Files
Ivan Vasilov 09a62662d8 chore: Clean up ui-patterns imports (#29132)
* Switch all top-level imports for ui-patterns components to use specific components when importing.

* Remove the @ui-patterns shorthand since it works exactly the same without the @.

* More import fixes.

* Fix the ui-patterns tests.
2024-09-30 21:41:26 +02:00

251 lines
8.3 KiB
TypeScript

import { MousePointerClick, X } from 'lucide-react'
import { useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'
import { Loading } from 'components/ui/Loading'
import { useWarehouseQueryQuery } from 'data/analytics/warehouse-query'
import useSingleLog from 'hooks/analytics/useSingleLog'
import {
Button,
CodeBlock,
TabsContent_Shadcn_,
TabsList_Shadcn_,
TabsTrigger_Shadcn_,
Tabs_Shadcn_,
cn,
} from 'ui'
import AuthSelectionRenderer from './LogSelectionRenderers/AuthSelectionRenderer'
import DatabaseApiSelectionRender from './LogSelectionRenderers/DatabaseApiSelectionRender'
import DatabasePostgresSelectionRender from './LogSelectionRenderers/DatabasePostgresSelectionRender'
import DefaultExplorerSelectionRenderer from './LogSelectionRenderers/DefaultExplorerSelectionRenderer'
import DefaultPreviewSelectionRenderer from './LogSelectionRenderers/DefaultPreviewSelectionRenderer'
import FunctionInvocationSelectionRender from './LogSelectionRenderers/FunctionInvocationSelectionRender'
import FunctionLogsSelectionRender from './LogSelectionRenderers/FunctionLogsSelectionRender'
import type { LogData, LogsEndpointParams, QueryType } from './Logs.types'
import { isDefaultLogPreviewFormat, isUnixMicro, unixMicroToIsoTimestamp } from './Logs.utils'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
export interface LogSelectionProps {
log: LogData | null
onClose: () => void
queryType?: QueryType
projectRef: string
params: Partial<LogsEndpointParams>
collectionName?: string
}
const LogSelection = ({
projectRef,
log: partialLog,
onClose,
queryType,
params = {},
collectionName,
}: LogSelectionProps) => {
const { logData: fullLog, isLoading } = useSingleLog(
projectRef,
queryType,
params,
partialLog?.id
)
const [sql, setSql] = useState('')
const warehouseQueryEnabled = queryType === 'warehouse'
const {
refetch: refetchWarehouseData,
data: warehouseQueryData,
isLoading: warehouseQueryLoading,
} = useWarehouseQueryQuery(
{
ref: projectRef,
sql,
},
{
enabled: queryType === 'warehouse',
onError: (error) => {
toast.error(error.message)
},
}
)
useEffect(() => {
const newSql = `select id, timestamp, event_message, metadata from \`${collectionName}\`
where id = '${partialLog?.id}' and timestamp > '2024-01-01' limit 1`
setSql(newSql)
}, [collectionName, partialLog?.id])
useEffect(() => {
if (queryType === 'warehouse') {
refetchWarehouseData()
}
}, [
warehouseQueryData,
collectionName,
projectRef,
partialLog?.id,
refetchWarehouseData,
queryType,
])
const Formatter = () => {
switch (queryType) {
case 'warehouse':
if (!warehouseQueryData) return null
return <DefaultPreviewSelectionRenderer log={warehouseQueryData.result?.[0] || {}} />
case 'api':
if (!fullLog) return null
if (!fullLog.metadata) return <DefaultPreviewSelectionRenderer log={fullLog} />
return <DatabaseApiSelectionRender log={fullLog} />
case 'database':
if (!fullLog) return null
if (!fullLog.metadata) return <DefaultPreviewSelectionRenderer log={fullLog} />
return <DatabasePostgresSelectionRender log={fullLog} />
case 'fn_edge':
if (!fullLog) return null
if (!fullLog.metadata) return <DefaultPreviewSelectionRenderer log={fullLog} />
return <FunctionInvocationSelectionRender log={fullLog} />
case 'functions':
if (!fullLog) return null
if (!fullLog.metadata) return <DefaultPreviewSelectionRenderer log={fullLog} />
return <FunctionLogsSelectionRender log={fullLog} />
case 'auth':
if (!fullLog) return null
if (!fullLog.metadata) return <DefaultPreviewSelectionRenderer log={fullLog} />
return <AuthSelectionRenderer log={fullLog} />
default:
if (queryType && fullLog && isDefaultLogPreviewFormat(fullLog)) {
return <DefaultPreviewSelectionRenderer log={fullLog} />
}
if (queryType && !fullLog) {
return null
}
if (!partialLog) return null
return <DefaultExplorerSelectionRenderer log={partialLog} />
}
}
const selectionText = useMemo(() => {
if (fullLog && queryType) {
return `Log ID
${fullLog.id}\n
Log Timestamp (UTC)
${isUnixMicro(fullLog.timestamp) ? unixMicroToIsoTimestamp(fullLog.timestamp) : fullLog.timestamp}\n
Log Event Message
${fullLog.event_message}\n
Log Metadata
${JSON.stringify(fullLog.metadata, null, 2)}
`
}
return JSON.stringify(fullLog || partialLog, null, 2)
}, [fullLog, partialLog, queryType])
const rawLog = useMemo(() => {
if (queryType === 'warehouse') {
return warehouseQueryData?.result?.[0] || {}
}
return fullLog || partialLog
}, [queryType, warehouseQueryData, fullLog, partialLog])
return (
<div
className={cn(
'relative flex h-full flex-grow flex-col border border-l border-overlay',
'overflow-y-scroll bg-studio'
)}
>
<div
className={cn(
'absolute flex h-full w-full flex-col items-center justify-center gap-2 overflow-y-scroll bg-studio text-center opacity-0 transition-all',
{
'z-0 opacity-0': partialLog,
'z-10 opacity-100': !partialLog,
}
)}
>
<div
className={
`flex
w-full
max-w-sm
scale-95
flex-col
items-center
justify-center
gap-6
text-center
opacity-0
transition-all delay-300 duration-500 ` +
(partialLog || isLoading ? 'mt-0 scale-95 opacity-0' : 'mt-8 scale-100 opacity-100')
}
>
<div className="relative flex h-4 w-32 items-center rounded border border-control px-2">
<div className="h-0.5 w-2/3 rounded-full bg-surface-300"></div>
<div className="absolute right-1 -bottom-4">
<MousePointerClick size="24" strokeWidth={1} />
</div>
</div>
<div className="flex flex-col gap-1">
<h3 className="text-sm text-foreground">Select an Event</h3>
<p className="text-xs text-foreground-lighter">
{queryType === 'warehouse'
? 'Select an Event to view the complete JSON payload'
: 'Select an Event to view the code snippet (pretty view) or complete JSON payload (raw view).'}
</p>
</div>
</div>
</div>
<div className="relative flex-grow flex flex-col bg-surface-100">
<Tabs_Shadcn_ defaultValue="details" className="flex flex-col">
<TabsList_Shadcn_ className="px-2 pt-2">
<TabsTrigger_Shadcn_ className="px-3" value="details">
Details
</TabsTrigger_Shadcn_>
<TabsTrigger_Shadcn_ className="px-3" value="raw">
Raw
</TabsTrigger_Shadcn_>
<Button
type="text"
className="ml-auto absolute top-2 right-2 cursor-pointer transition hover:text-foreground h-6 w-6 px-0 py-0 flex items-center justify-center"
onClick={onClose}
>
<X size={14} strokeWidth={2} className="text-foreground-lighter" />
</Button>
</TabsList_Shadcn_>
<div className="flex-grow">
{isLoading || (warehouseQueryEnabled && warehouseQueryLoading) ? (
<div className="p-4">
<GenericSkeletonLoader />
</div>
) : (
<>
<TabsContent_Shadcn_ className="bg-surface-100 space-y-6" value="details">
<Formatter />
</TabsContent_Shadcn_>
<TabsContent_Shadcn_ value="raw">
{isLoading && 'Loading...'}
<CodeBlock
hideLineNumbers
language="json"
className="prose w-full pt-0 max-w-full border-none"
>
{JSON.stringify(rawLog, null, 2)}
</CodeBlock>
</TabsContent_Shadcn_>
</>
)}
</div>
</Tabs_Shadcn_>
</div>
</div>
)
}
export default LogSelection