mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 04:15:04 +03:00
180 lines
5.9 KiB
TypeScript
180 lines
5.9 KiB
TypeScript
import { FC } from 'react'
|
|
import { Button, Divider, IconX } from 'ui'
|
|
|
|
import { LogData, QueryType } from './Logs.types'
|
|
|
|
import DatabaseApiSelectionRender from './LogSelectionRenderers/DatabaseApiSelectionRender'
|
|
import DatabasePostgresSelectionRender from './LogSelectionRenderers/DatabasePostgresSelectionRender'
|
|
import FunctionInvocationSelectionRender from './LogSelectionRenderers/FunctionInvocationSelectionRender'
|
|
import FunctionLogsSelectionRender from './LogSelectionRenderers/FunctionLogsSelectionRender'
|
|
import DefaultExplorerSelectionRenderer from './LogSelectionRenderers/DefaultExplorerSelectionRenderer'
|
|
import DefaultPreviewSelectionRenderer from './LogSelectionRenderers/DefaultPreviewSelectionRenderer'
|
|
import {
|
|
isDefaultLogPreviewFormat,
|
|
isUnixMicro,
|
|
LogsEndpointParams,
|
|
unixMicroToIsoTimestamp,
|
|
} from '.'
|
|
import useSingleLog from 'hooks/analytics/useSingleLog'
|
|
import Connecting from 'components/ui/Loading/Loading'
|
|
import CopyButton from 'components/ui/CopyButton'
|
|
import AuthSelectionRenderer from './LogSelectionRenderers/AuthSelectionRenderer'
|
|
|
|
export interface LogSelectionProps {
|
|
log: LogData | null
|
|
onClose: () => void
|
|
queryType?: QueryType
|
|
projectRef: string
|
|
params: Partial<LogsEndpointParams>
|
|
}
|
|
|
|
const LogSelection: FC<LogSelectionProps> = ({
|
|
projectRef,
|
|
log: partialLog,
|
|
onClose,
|
|
queryType,
|
|
params = {},
|
|
}) => {
|
|
const [{ logData: fullLog, isLoading }] = useSingleLog(
|
|
projectRef,
|
|
queryType,
|
|
params,
|
|
partialLog?.id
|
|
)
|
|
const Formatter = () => {
|
|
switch (queryType) {
|
|
case 'api':
|
|
if (!fullLog) return null
|
|
return <DatabaseApiSelectionRender log={fullLog} />
|
|
|
|
case 'database':
|
|
if (!fullLog) return null
|
|
return <DatabasePostgresSelectionRender log={fullLog} />
|
|
|
|
case 'fn_edge':
|
|
if (!fullLog) return null
|
|
return <FunctionInvocationSelectionRender log={fullLog} />
|
|
|
|
case 'functions':
|
|
if (!fullLog) return null
|
|
return <FunctionLogsSelectionRender log={fullLog} />
|
|
|
|
case 'auth':
|
|
if (!fullLog) return null
|
|
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 = () => {
|
|
if (!fullLog) return ''
|
|
if (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, null, 2)
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={[
|
|
'relative flex h-full flex-grow flex-col border border-l',
|
|
'border-panel-border-light dark:border-panel-border-dark',
|
|
'overflow-y-scroll bg-gray-200',
|
|
].join(' ')}
|
|
>
|
|
<div
|
|
className={
|
|
`absolute flex
|
|
h-full w-full flex-col items-center justify-center gap-2 overflow-y-scroll bg-scale-200 text-center opacity-0 transition-all ` +
|
|
(partialLog ? 'z-0 opacity-0' : 'z-10 opacity-100')
|
|
}
|
|
>
|
|
<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-scale-600 px-2 dark:border-scale-400">
|
|
<div className="h-0.5 w-2/3 rounded-full bg-scale-600 dark:bg-scale-500"></div>
|
|
<div className="absolute right-1 -bottom-4">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
className="h-6 w-6"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth="1"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-1">
|
|
<h3 className="text-sm text-scale-1200">Select an Event</h3>
|
|
<p className="text-xs text-scale-900">
|
|
Select an Event to view the code snippet (pretty view) or complete JSON payload (raw
|
|
view).
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="relative h-px flex-grow bg-scale-300">
|
|
<div className="pt-4 px-4 flex flex-col gap-4">
|
|
<div className="flex flex-row justify-between items-center">
|
|
<div className={`transition ${!isLoading ? 'opacity-100' : 'opacity-0'}`}>
|
|
<CopyButton text={selectionText()} type="default" title="Copy log to clipboard" />
|
|
</div>
|
|
<Button
|
|
type="text"
|
|
className="cursor-pointer transition hover:text-scale-1200 h-8 w-8 px-0 py-0 flex items-center justify-center"
|
|
onClick={onClose}
|
|
>
|
|
<IconX size={14} strokeWidth={2} className="text-scale-900" />
|
|
</Button>
|
|
</div>
|
|
<div className="h-px w-full bg-scale-600 rounded " />
|
|
</div>
|
|
{isLoading && <Connecting />}
|
|
<div className="flex flex-col space-y-6 bg-scale-300 py-4">
|
|
{!isLoading && <Formatter />}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default LogSelection
|