mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-24 05:43:58 +00:00
Major additions: - Complete Next.js studio application with 1600+ components - Docker support (Dockerfile.combined, docker-compose.yml) - GCP deployment documentation and benchmarks - SQL benchmark scripts for performance testing - Sentry integration for monitoring - Comprehensive test suite and mocks Studio features: - Dashboard and admin interfaces - Data visualization components - Authentication and user management - API integration with RuVector backend - Static data and public assets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
247 lines
9.4 KiB
TypeScript
247 lines
9.4 KiB
TypeScript
import { useState } from 'react'
|
|
|
|
import { useParams } from 'common'
|
|
import { useDataTable } from 'components/ui/DataTable/providers/DataTableProvider'
|
|
import {
|
|
ServiceFlowType,
|
|
useUnifiedLogInspectionQuery,
|
|
} from 'data/logs/unified-log-inspection-query'
|
|
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
|
|
import {
|
|
CodeBlock,
|
|
ResizableHandle,
|
|
ResizablePanel,
|
|
Skeleton,
|
|
Tabs_Shadcn_ as Tabs,
|
|
TabsContent_Shadcn_ as TabsContent,
|
|
TabsList_Shadcn_ as TabsList,
|
|
TabsTrigger_Shadcn_ as TabsTrigger,
|
|
cn,
|
|
} from 'ui'
|
|
import {
|
|
MemoizedEdgeFunctionBlock,
|
|
MemoizedGoTrueBlock,
|
|
MemoizedNetworkBlock,
|
|
MemoizedPostgRESTBlock,
|
|
MemoizedPostgresBlock,
|
|
MemoizedStorageBlock,
|
|
} from './ServiceFlow/components/ServiceBlocks'
|
|
import { ServiceFlowHeader } from './ServiceFlow/components/ServiceFlowHeader'
|
|
import { MemoizedRequestStartedBlock } from './ServiceFlow/components/blocks/RequestStartedBlock'
|
|
import { MemoizedResponseCompletedBlock } from './ServiceFlow/components/blocks/ResponseCompletedBlock'
|
|
import { ColumnSchema } from './UnifiedLogs.schema'
|
|
import { QuerySearchParamsType } from './UnifiedLogs.types'
|
|
|
|
interface ServiceFlowPanelProps {
|
|
selectedRow: ColumnSchema
|
|
selectedRowKey: string
|
|
searchParameters: QuerySearchParamsType
|
|
}
|
|
|
|
export function ServiceFlowPanel({
|
|
selectedRow,
|
|
selectedRowKey,
|
|
searchParameters,
|
|
}: ServiceFlowPanelProps) {
|
|
const { table, filterFields } = useDataTable()
|
|
const { ref: projectRef } = useParams()
|
|
const [activeTab, setActiveTab] = useState('service-flow')
|
|
|
|
const { logsMetadata } = useIsFeatureEnabled(['logs:metadata'])
|
|
|
|
const logType = selectedRow?.log_type
|
|
const serviceFlowType: ServiceFlowType | undefined =
|
|
logType === 'edge function' ? 'edge-function' : (logType as ServiceFlowType)
|
|
const shouldShowServiceFlow = !!serviceFlowType
|
|
|
|
// Query the logs API directly
|
|
const {
|
|
data: serviceFlowData,
|
|
isLoading,
|
|
error,
|
|
} = useUnifiedLogInspectionQuery(
|
|
{
|
|
projectRef: projectRef,
|
|
logId: selectedRow?.id,
|
|
type: serviceFlowType,
|
|
search: searchParameters,
|
|
},
|
|
{
|
|
enabled: Boolean(projectRef) && Boolean(selectedRow?.id) && Boolean(serviceFlowType),
|
|
}
|
|
)
|
|
|
|
// Prepare JSON data for Raw JSON tab
|
|
const jsonData =
|
|
shouldShowServiceFlow && serviceFlowData?.result?.[0] ? serviceFlowData.result[0] : selectedRow
|
|
|
|
const formattedJsonData =
|
|
!logsMetadata && 'raw_log_data' in jsonData && 'metadata' in jsonData.raw_log_data
|
|
? {
|
|
...jsonData,
|
|
raw_log_data: { ...jsonData.raw_log_data, metadata: undefined },
|
|
}
|
|
: jsonData
|
|
|
|
if (selectedRowKey) {
|
|
return (
|
|
<>
|
|
<ResizableHandle withHandle className="z-10" />
|
|
<ResizablePanel
|
|
id="log-sidepanel"
|
|
order={2}
|
|
defaultSize={1}
|
|
maxSize={40}
|
|
className={cn(
|
|
'bg-dash-sidebar',
|
|
'z-40',
|
|
'border-l fixed right-0 top-0 bottom-0',
|
|
'md:absolute md:h-auto',
|
|
// ' md:w-3/4',
|
|
'xl:z-[1]',
|
|
'min-w-[28rem] max-w-[45rem]',
|
|
'xl:relative xl:border-l-0'
|
|
)}
|
|
>
|
|
<div className="h-full overflow-auto">
|
|
{/* Service Flow Header with navigation */}
|
|
<ServiceFlowHeader
|
|
selectedRow={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
/>
|
|
|
|
<div className="relative">
|
|
<Tabs
|
|
defaultValue="service-flow"
|
|
value={activeTab}
|
|
onValueChange={setActiveTab}
|
|
className="w-full h-full flex flex-col pt-2"
|
|
>
|
|
<TabsList className="flex gap-3 px-5">
|
|
{shouldShowServiceFlow && (
|
|
<TabsTrigger value="service-flow">Overview</TabsTrigger>
|
|
)}
|
|
<TabsTrigger value="raw-json">Raw JSON</TabsTrigger>
|
|
</TabsList>
|
|
|
|
{shouldShowServiceFlow && (
|
|
<TabsContent value="service-flow">
|
|
<div className="p-4">
|
|
{error ? (
|
|
<div className="text-center py-8 text-destructive">
|
|
Error: {error.toString()}
|
|
</div>
|
|
) : (
|
|
<>
|
|
{serviceFlowType === 'postgres' ? (
|
|
// Postgres flows: Connection Started -> Postgres -> Response
|
|
<>
|
|
<MemoizedRequestStartedBlock data={selectedRow} />
|
|
|
|
<MemoizedPostgresBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
isLoading={isLoading}
|
|
isLast={false}
|
|
filterFields={filterFields}
|
|
table={table}
|
|
/>
|
|
|
|
<MemoizedResponseCompletedBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
/>
|
|
</>
|
|
) : (
|
|
// HTTP-based flows: Request Started -> Network -> Service -> Response
|
|
<>
|
|
<MemoizedRequestStartedBlock data={selectedRow} />
|
|
|
|
<MemoizedNetworkBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
isLoading={isLoading}
|
|
filterFields={filterFields}
|
|
table={table}
|
|
/>
|
|
|
|
{serviceFlowType === 'auth' ? (
|
|
<MemoizedGoTrueBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
isLoading={isLoading}
|
|
filterFields={filterFields}
|
|
table={table}
|
|
/>
|
|
) : serviceFlowType === 'edge-function' ? (
|
|
<MemoizedEdgeFunctionBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
isLoading={isLoading}
|
|
filterFields={filterFields}
|
|
table={table}
|
|
/>
|
|
) : serviceFlowType === 'storage' ? (
|
|
<MemoizedStorageBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
isLoading={isLoading}
|
|
filterFields={filterFields}
|
|
table={table}
|
|
/>
|
|
) : (
|
|
<>
|
|
<MemoizedPostgRESTBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
isLoading={isLoading}
|
|
filterFields={filterFields}
|
|
table={table}
|
|
/>
|
|
|
|
<MemoizedPostgresBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
isLoading={isLoading}
|
|
filterFields={filterFields}
|
|
table={table}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
<MemoizedResponseCompletedBlock
|
|
data={selectedRow}
|
|
enrichedData={serviceFlowData?.result?.[0]}
|
|
/>
|
|
</>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</TabsContent>
|
|
)}
|
|
|
|
<TabsContent value="raw-json" className="flex-grow overflow-auto">
|
|
{isLoading && shouldShowServiceFlow && (
|
|
<div className="flex items-center gap-3 text-foreground-light p-3 bg-surface-100 border-b border-border">
|
|
<Skeleton className="h-4 w-4 rounded-full animate-pulse" />
|
|
<span className="text-sm">Enriching log...</span>
|
|
</div>
|
|
)}
|
|
<CodeBlock
|
|
language="json"
|
|
className="max-h-[800px] overflow-auto border-none rounded-none [&_pre]:!leading-tight [&_code]:!leading-tight"
|
|
>
|
|
{JSON.stringify(formattedJsonData, null, 2)}
|
|
</CodeBlock>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
</div>
|
|
</ResizablePanel>
|
|
</>
|
|
)
|
|
}
|
|
|
|
return null
|
|
}
|