Files
supabase/apps/studio/components/interfaces/UnifiedLogs/ServiceFlow/components/ServiceFlowHeader.tsx
T
Danny WhiteandJoshen Lim 031b227165 studio(chore): badge component defrag (#40118)
* component clean up

* optically center

* docs and type size

* code badge variant

* sensible defaults

* fix product menu flex

* badge sweep

* new project badges

* logs

* compute badge

* studio badge sweep

* www sweep

* docs sweep

* clean up

* fixes

* cleanup

* fixes

* better docs

* fixes

* misc fixes

* consistency

* Minor fixes for issues i found

* simplify mt-0

* mt simplification

* remaining optical alignment

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2025-12-02 11:15:50 +11:00

169 lines
5.5 KiB
TypeScript

import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DataTableColumnStatusCode } from 'components/ui/DataTable/DataTableColumn/DataTableColumnStatusCode'
import { Kbd } from 'components/ui/DataTable/primitives/Kbd'
import { useDataTable } from 'components/ui/DataTable/providers/DataTableProvider'
import { ChevronDown, ChevronUp, X } from 'lucide-react'
import { useCallback, useEffect, useMemo } from 'react'
import { Badge, Button, Separator } from 'ui'
import { LogTypeIcon } from '../../components/LogTypeIcon'
import { ColumnSchema } from '../../UnifiedLogs.schema'
import { getStatusLevel } from '../../UnifiedLogs.utils'
import { TruncatedTextWithPopover } from './shared/TruncatedTextWithPopover'
interface ServiceFlowHeaderProps {
selectedRow: ColumnSchema
enrichedData?: Record<string, any>
}
export const ServiceFlowHeader = ({ selectedRow, enrichedData }: ServiceFlowHeaderProps) => {
const { table, rowSelection, isLoading } = useDataTable()
const method = selectedRow?.method
const logType = selectedRow?.log_type
const status = selectedRow?.status
// Display logic: path → event_message → grayed out "/"
const displayPath = useMemo(() => {
const actualPath = enrichedData?.request_path || selectedRow?.pathname
if (actualPath && actualPath !== '/') {
return { text: actualPath, isDefault: false, isTruncatable: false }
}
const eventMessage = enrichedData?.event_message || selectedRow?.event_message
if (eventMessage) {
return { text: eventMessage, isDefault: false, isTruncatable: true }
}
return { text: '/', isDefault: true, isTruncatable: false }
}, [enrichedData, selectedRow])
// Navigation logic (same as DataTableSheetDetails)
const selectedRowKey = Object.keys(rowSelection)?.[0]
const selectedRowData = useMemo(() => {
if (isLoading && !selectedRowKey) return
return table.getCoreRowModel().flatRows.find((row) => row.id === selectedRowKey)
}, [selectedRowKey, isLoading, table])
const index = table.getCoreRowModel().flatRows.findIndex((row) => row.id === selectedRowData?.id)
// Get the ID of the next row in the table for navigation
const nextId = useMemo(
() => table.getCoreRowModel().flatRows[index + 1]?.id,
[index, isLoading, table]
)
// Get the ID of the previous row in the table for navigation
const prevId = useMemo(
() => table.getCoreRowModel().flatRows[index - 1]?.id,
[index, isLoading, table]
)
// Navigate to the previous row when called
const onPrev = useCallback(() => {
if (prevId) table.setRowSelection({ [prevId]: true })
}, [prevId, table])
// Navigate to the next row when called
const onNext = useCallback(() => {
if (nextId) table.setRowSelection({ [nextId]: true })
}, [nextId, table])
// Close the current row selection
const onClose = useCallback(() => {
table.resetRowSelection()
}, [table])
useEffect(() => {
const down = (e: KeyboardEvent) => {
if (!selectedRowKey) return
// REMINDER: prevent dropdown navigation inside of sheet to change row selection
const activeElement = document.activeElement
const isMenuActive = activeElement?.closest('[role="menu"]')
if (isMenuActive) return
if (e.key === 'ArrowUp') {
e.preventDefault()
onPrev()
}
if (e.key === 'ArrowDown') {
e.preventDefault()
onNext()
}
}
document.addEventListener('keydown', down)
return () => document.removeEventListener('keydown', down)
}, [selectedRowKey, onNext, onPrev])
return (
<div className="flex items-center justify-between px-4 py-3">
<div className="flex items-center gap-3 min-w-0 flex-1 mr-6">
{logType && <LogTypeIcon type={logType} size={16} className="text-foreground/70" />}
{method && <Badge variant="default">{method}</Badge>}
{displayPath.isTruncatable ? (
<TruncatedTextWithPopover
text={displayPath.text}
maxLength={60}
className={`font-mono text-sm truncate ${displayPath.isDefault ? 'text-foreground-muted' : 'text-foreground'}`}
/>
) : (
<span
className={`font-mono text-sm truncate ${displayPath.isDefault ? 'text-foreground-muted' : 'text-foreground'}`}
>
{displayPath.text}
</span>
)}
</div>
<div className="flex h-7 items-center gap-1">
{status && (
<DataTableColumnStatusCode
value={status}
level={getStatusLevel(status)}
className="text-xs"
/>
)}
<ButtonTooltip
size="tiny"
type="text"
disabled={!prevId}
onClick={onPrev}
className="px-1"
icon={<ChevronUp />}
tooltip={{
content: {
text: (
<p>
Navigate <Kbd>↑</Kbd>
</p>
),
},
}}
/>
<ButtonTooltip
size="tiny"
type="text"
disabled={!nextId}
onClick={onNext}
className="px-1"
icon={<ChevronDown />}
tooltip={{
content: {
text: (
<p>
Navigate <Kbd>↓</Kbd>
</p>
),
},
}}
/>
<Separator orientation="vertical" className="mx-1" />
<Button size="tiny" type="text" onClick={onClose} className="px-1" icon={<X />} />
</div>
</div>
)
}