mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 09:50:33 -04:00
b2e5476146
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Tidying up the bottom panel in unified logs. Taking care of some visual quirks etc. Also preparing this area to house some other future concepts via tabs. | Before | After | |--------|--------| | <img width="828" height="384" alt="Screenshot 2026-04-30 at 11 24 09" src="https://github.com/user-attachments/assets/804bdf1c-7cdb-4dd8-bf1e-31c434ef1436" /> | <img width="830" height="407" alt="Screenshot 2026-04-30 at 11 22 53" src="https://github.com/user-attachments/assets/28555efe-f893-4bae-bcb0-284e6db733e6" /> | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Redesigned service flow panel with Overview and Raw JSON tabs * Added Previous/Next navigation controls with Arrow Up/Down keyboard support * New detail components and section headers with icons for clearer organization * Improved Postgres detail view and message/session display * **Bug Fixes / Changes** * Removed legacy header UI and related controls * **UI / Style** * Enhanced copy-to-clipboard feedback animation * Updated "Load more" button styling * Adjusted panel sizing for improved resizing behavior <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ali Waseem <waseema393@gmail.com>
191 lines
5.8 KiB
TypeScript
191 lines
5.8 KiB
TypeScript
import { Table } from '@tanstack/react-table'
|
|
import { endOfDay, endOfHour, startOfDay, startOfHour } from 'date-fns'
|
|
import {
|
|
CalendarClock,
|
|
CalendarDays,
|
|
CalendarSearch,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
Copy,
|
|
Equal,
|
|
Search,
|
|
} from 'lucide-react'
|
|
import { ComponentPropsWithRef } from 'react'
|
|
import {
|
|
cn,
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuGroup,
|
|
DropdownMenuItem,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from 'ui'
|
|
|
|
import { DataTableFilterField } from '@/components/ui/DataTable/DataTable.types'
|
|
import { useCopyToClipboard } from '@/hooks/ui/useCopyToClipboard'
|
|
|
|
interface DataTableSheetRowActionProps<
|
|
TData,
|
|
TFields extends DataTableFilterField<TData>,
|
|
> extends ComponentPropsWithRef<typeof DropdownMenuTrigger> {
|
|
fieldValue: TFields['value']
|
|
filterFields: TFields[]
|
|
value: string | number
|
|
table: Table<TData>
|
|
}
|
|
|
|
export function DataTableSheetRowAction<TData, TFields extends DataTableFilterField<TData>>({
|
|
fieldValue,
|
|
filterFields,
|
|
value,
|
|
children,
|
|
className,
|
|
table,
|
|
onKeyDown,
|
|
...props
|
|
}: DataTableSheetRowActionProps<TData, TFields>) {
|
|
const { copy, isCopied } = useCopyToClipboard()
|
|
const field = filterFields.find((field) => field.value === fieldValue)
|
|
const column = table.getColumn(fieldValue.toString())
|
|
|
|
if (!field || !column) return null
|
|
|
|
function renderOptions() {
|
|
if (!field) return null
|
|
switch (field.type) {
|
|
case 'checkbox':
|
|
return (
|
|
<DropdownMenuItem
|
|
onClick={() => {
|
|
const filterValue = column?.getFilterValue() as undefined | Array<unknown>
|
|
const newValue = filterValue?.includes(value)
|
|
? filterValue
|
|
: [...(filterValue || []), value]
|
|
|
|
column?.setFilterValue(newValue)
|
|
}}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Search size={16} />
|
|
Include
|
|
</DropdownMenuItem>
|
|
)
|
|
case 'input':
|
|
return (
|
|
<DropdownMenuItem
|
|
onClick={() => column?.setFilterValue(value)}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Search size={16} />
|
|
Include
|
|
</DropdownMenuItem>
|
|
)
|
|
case 'slider':
|
|
return (
|
|
<DropdownMenuGroup>
|
|
<DropdownMenuItem
|
|
onClick={() => column?.setFilterValue([0, value])}
|
|
className="flex items-center gap-2"
|
|
>
|
|
{/* FIXME: change icon as it is not clear */}
|
|
<ChevronLeft size={16} />
|
|
Less or equal than
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => column?.setFilterValue([value, 5000])}
|
|
className="flex items-center gap-2"
|
|
>
|
|
{/* FIXME: change icon as it is not clear */}
|
|
<ChevronRight size={16} />
|
|
Greater or equal than
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => column?.setFilterValue([value])}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Equal size={16} />
|
|
Equal to
|
|
</DropdownMenuItem>
|
|
</DropdownMenuGroup>
|
|
)
|
|
case 'timerange':
|
|
const date = new Date(value)
|
|
return (
|
|
<DropdownMenuGroup>
|
|
<DropdownMenuItem
|
|
onClick={() => column?.setFilterValue([date])}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<CalendarSearch size={16} />
|
|
Exact timestamp
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => {
|
|
const start = startOfHour(date)
|
|
const end = endOfHour(date)
|
|
column?.setFilterValue([start, end])
|
|
}}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<CalendarClock size={16} />
|
|
Same hour
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => {
|
|
const start = startOfDay(date)
|
|
const end = endOfDay(date)
|
|
column?.setFilterValue([start, end])
|
|
}}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<CalendarDays size={16} />
|
|
Same day
|
|
</DropdownMenuItem>
|
|
</DropdownMenuGroup>
|
|
)
|
|
default:
|
|
return null
|
|
}
|
|
}
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger
|
|
className={cn(
|
|
'rounded-md ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
'relative',
|
|
className
|
|
)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'ArrowDown') {
|
|
// REMINDER: default behavior is to open the dropdown menu
|
|
// But because we use it to navigate between rows, we need to prevent it
|
|
// and only use "Enter" to select the option
|
|
e.preventDefault()
|
|
}
|
|
onKeyDown?.(e)
|
|
}}
|
|
{...props}
|
|
>
|
|
{children}
|
|
{isCopied ? (
|
|
<div className="absolute inset-0 flex items-center justify-center rounded-md bg-surface-100/80 backdrop-blur-sm animate-in fade-in duration-150">
|
|
<span className="font-mono text-xs text-foreground-light">Copied</span>
|
|
</div>
|
|
) : null}
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" side="bottom" className="w-40">
|
|
{renderOptions()}
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem
|
|
onClick={() => copy(String(value), { timeout: 1000 })}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Copy size={16} />
|
|
Copy value
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|