More clean up

This commit is contained in:
Joshen Lim committed 2025-06-10 17:53:29 +08:00
1 parent 01ebe33ccf
commit 4e6907f1bb
10 files changed
+27 -454

No files matched your search

@@ -1,18 +1,8 @@
import { useInfiniteQuery } from '@tanstack/react-query'
import { useParams } from 'next/navigation'
import { useQueryState, useQueryStates } from 'nuqs'
import { useQueryState } from 'nuqs'
import { useEffect, useMemo, useRef } from 'react'
import SuperJSON from 'superjson'
import { useHotKey } from 'hooks/ui/useHotKey'
import { createApiQueryString } from './QueryOptions'
import { SEARCH_PARAMS_PARSER } from './UnifiedLogs.constants'
import {
InfiniteQueryResponse,
PageParam,
UnifiedLogSchema,
UnifiedLogsMeta,
} from './UnifiedLogs.types'
export const useResetFocus = () => {
useHotKey(() => {
@@ -52,84 +42,3 @@ export const useLiveMode = <TData extends { date: Date }>(data: TData[]) => {
return { row: anchorRow, timestamp: liveTimestamp.current }
}
// [Joshen] This isn't currently being used - check if can deprecate
export function useUnifiedLogs() {
// Get project ref from URL params
const { ref: projectRef } = useParams<{ ref: string }>()
// Use your existing search params
const [search, setSearch] = useQueryStates(SEARCH_PARAMS_PARSER)
// Add log_type to search params if needed
const queryParams = useMemo(() => {
return {
...search,
// Add any unified logs specific params
}
}, [search])
// Create the query
// @ts-ignore
const result = useInfiniteQuery({
queryKey: ['unified-logs', projectRef, JSON.stringify(queryParams)],
queryFn: async ({ pageParam }) => {
const cursor = pageParam?.cursor ? new Date(pageParam.cursor) : undefined
const direction = pageParam?.direction
// Construct API params
const apiParams = {
...queryParams,
uuid: null, // Explicitly remove for API call
live: null, // Explicitly remove for API call
...(cursor && { cursor }),
...(direction && { direction }),
}
// Define endpoint for unified logs
const endpoint = `/api/projects/${projectRef}/logs/unified`
const queryString = createApiQueryString(apiParams)
const response = await fetch(`${endpoint}${queryString}`)
if (!response.ok) {
throw new Error('Failed to fetch unified logs')
}
const jsonString = await response.text()
return SuperJSON.parse<InfiniteQueryResponse<UnifiedLogSchema[], UnifiedLogsMeta>>(jsonString)
},
initialPageParam: { cursor: new Date().getTime(), direction: 'next' } as PageParam,
getPreviousPageParam: (firstPage) => {
if (!firstPage.prevCursor) return null
return { cursor: firstPage.prevCursor, direction: 'prev' } as PageParam
},
getNextPageParam: (lastPage) => {
if (!lastPage.nextCursor) return null
return { cursor: lastPage.nextCursor, direction: 'next' } as PageParam
},
refetchOnWindowFocus: false,
})
// Helper functions for the unified logs
const addLogTypeFilter = (logType: UnifiedLogSchema['log_type']) => {
const currentLogTypes = search.log_type || []
setSearch({
log_type: [...currentLogTypes, logType],
})
}
const removeLogTypeFilter = (logType: UnifiedLogSchema['log_type']) => {
const currentLogTypes = search.log_type || []
setSearch({
log_type: currentLogTypes.filter((type) => type !== logType),
})
}
// Return query results and helper functions
return {
...result,
addLogTypeFilter,
removeLogTypeFilter,
}
}
@@ -85,6 +85,7 @@ export const UnifiedLogsTable = () => {
[]
)
// [Joshen] This needs to move to the data folder to follow our proper RQ structure
const { data, isFetching, isLoading, fetchNextPage, hasNextPage, fetchPreviousPage, refetch } =
// @ts-ignore
useInfiniteQuery(dataOptions(search, projectRef ?? ''))
@@ -321,26 +321,23 @@ export function DataTableFilterCommand({ searchParamsParser }: DataTableFilterCo
</CommandGroup>
<CommandEmpty>No results found.</CommandEmpty>
</CommandList>
<div
className="bg-surface-100 flex flex-wrap justify-between gap-3 border-t bg-accent/50 px-2 py-1.5 text-sm text-accent-foreground"
cmdk-footer=""
>
<div className="bg-surface-100 flex flex-wrap justify-between gap-3 border-t bg-accent/50 px-2 py-1.5 text-sm text-accent-foreground">
<div className="flex flex-wrap gap-3">
<span>
Use <Kbd variant="outline">↑</Kbd> <Kbd variant="outline">↓</Kbd> to navigate
Use <Kbd>↑</Kbd> <Kbd>↓</Kbd> to navigate
</span>
<span>
<Kbd variant="outline">Enter</Kbd> to query
<Kbd>Enter</Kbd> to query
</span>
<span>
<Kbd variant="outline">Esc</Kbd> to close
<Kbd>Esc</Kbd> to close
</span>
<Separator orientation="vertical" className="my-auto h-3" />
<span>
Union: <Kbd variant="outline">regions:a,b</Kbd>
Union: <Kbd>regions:a,b</Kbd>
</span>
<span>
Range: <Kbd variant="outline">p95:59-340</Kbd>
Range: <Kbd>p95:59-340</Kbd>
</span>
</div>
{lastSearches.length ? (
@@ -93,7 +93,7 @@ export function DataTableSheetDetails({
</TooltipTrigger>
<TooltipContent>
<p>
Navigate <Kbd variant="outline">↑</Kbd>
Navigate <Kbd>↑</Kbd>
</p>
</TooltipContent>
</Tooltip>
@@ -112,7 +112,7 @@ export function DataTableSheetDetails({
</TooltipTrigger>
<TooltipContent>
<p>
Navigate <Kbd variant="outline">↓</Kbd>
Navigate <Kbd>↓</Kbd>
</p>
</TooltipContent>
</Tooltip>
@@ -24,7 +24,7 @@ import {
import { presets as defaultPresets } from './DataTable.constants'
import type { DatePreset } from './DataTable.types'
import { useDebounce } from './hooks/useDebounce'
import { kbdVariants } from './primitives/Kbd'
import { kdbClassName } from './primitives/Kbd'
interface DatePickerWithRangeProps extends HTMLAttributes<HTMLDivElement> {
date: DateRange | undefined
@@ -129,11 +129,10 @@ function DatePresets({
size="small"
onClick={() => onSelect({ from, to })}
className="text-left"
// className={cn(!isActive && 'border border-transparent')}
>
<div className="flex items-center justify-between gap-6">
<span className="mr-auto">{label}</span>
<span className={cn(kbdVariants(), 'uppercase')}>{shortcut}</span>
<span className={cn(kdbClassName, 'uppercase')}>{shortcut}</span>
</div>
</Button>
)
@@ -187,7 +186,7 @@ function DatePresetsSelect({
className="flex items-center justify-between [&>span:last-child]:flex [&>span:last-child]:w-full [&>span:last-child]:justify-between"
>
<span>{label}</span>
<span className={cn(kbdVariants(), 'ml-2 h-5 uppercase leading-snug')}>
<span className={cn(kdbClassName, 'ml-2 h-5 uppercase leading-snug')}>
{shortcut}
</span>
</SelectItem>
@@ -24,7 +24,7 @@ import {
import { presets as defaultPresets } from '../DataTable.constants'
import type { DatePreset } from '../DataTable.types'
import { useDebounce } from '../hooks/useDebounce'
import { kbdVariants } from './Kbd'
import { kdbClassName } from './Kbd'
interface DatePickerWithRangeProps extends HTMLAttributes<HTMLDivElement> {
date: DateRange | undefined
@@ -132,7 +132,7 @@ function DatePresets({
>
<div className="flex items-center justify-between gap-6">
<span className="mr-auto">{label}</span>
<span className={cn(kbdVariants(), 'uppercase')}>{shortcut}</span>
<span className={cn(kdbClassName, 'uppercase')}>{shortcut}</span>
</div>
</Button>
)
@@ -186,7 +186,7 @@ function DatePresetsSelect({
className="flex items-center justify-between [&>span:last-child]:flex [&>span:last-child]:w-full [&>span:last-child]:justify-between"
>
<span>{label}</span>
<span className={cn(kbdVariants(), 'ml-2 h-5 uppercase leading-snug')}>
<span className={cn(kdbClassName, 'ml-2 h-5 uppercase leading-snug')}>
{shortcut}
</span>
</SelectItem>
@@ -1,51 +1,21 @@
// Copy Pasta from: https://github.com/sadmann7/shadcn-table/blob/main/src/components/kbd.tsx#L54
import { type VariantProps, cva } from 'class-variance-authority'
import { forwardRef } from 'react'
import { ComponentPropsWithoutRef, forwardRef } from 'react'
import { cn } from 'ui'
export const kbdVariants = cva(
export const kdbClassName = cn(
'select-none rounded border px-1.5 py-px font-mono text-[0.7rem] font-normal font-mono shadow-sm disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-accent text-accent-foreground',
outline: 'bg-background text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
}
'bg-background text-foreground'
)
export interface KbdProps
extends React.ComponentPropsWithoutRef<'kbd'>,
VariantProps<typeof kbdVariants> {
/**
* The title of the `abbr` element inside the `kbd` element.
* @default undefined
* @type string | undefined
* @example title="Command"
*/
abbrTitle?: string
}
const Kbd = forwardRef<HTMLUnknownElement, KbdProps>(
({ abbrTitle, children, className, variant, ...props }, ref) => {
export const Kbd = forwardRef<HTMLUnknownElement, ComponentPropsWithoutRef<'kbd'>>(
({ children, className, ...props }, ref) => {
return (
<kbd className={cn(kbdVariants({ variant, className }))} ref={ref} {...props}>
{abbrTitle ? (
<abbr title={abbrTitle} className="no-underline">
{children}
</abbr>
) : (
children
)}
<kbd className={cn(kdbClassName, className)} ref={ref} {...props}>
{children}
</kbd>
)
}
)
Kbd.displayName = 'Kbd'
export { Kbd }
Kbd.displayName = 'Kbd'
+3 -14
View File
@@ -1,6 +1,6 @@
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { useMemo, useState, useCallback } from 'react'
import { useCallback, useMemo, useState } from 'react'
import {
LogsTableName,
@@ -22,8 +22,8 @@ import {
genDefaultQuery,
} from 'components/interfaces/Settings/Logs/Logs.utils'
import { get } from 'data/fetchers'
import { useLogsUrlState } from './useLogsUrlState'
import { useFillTimeseriesSorted } from './useFillTimeseriesSorted'
import { useLogsUrlState } from './useLogsUrlState'
import useTimeseriesUnixToIso from './useTimeseriesUnixToIso'
interface LogsPreviewHook {
@@ -92,10 +92,6 @@ function useLogsPreview({
const queryKey = useMemo(() => ['projects', projectRef, 'logs', params], [projectRef, params])
console.log('table:', table)
console.log('useLogsPreview SQL:', params.sql)
const {
data,
isSuccess,
@@ -151,17 +147,10 @@ function useLogsPreview({
}
})
// Log unified logs data for debugging
if (table === 'unified_logs' && logData.length > 0) {
console.log('Unified Logs Data:', logData)
console.log('Sample log record:', logData[0])
console.log('Sample log record metadata:', logData[0]?.metadata)
}
const oldestTimestamp = logData.length > 0 ? logData[logData.length - 1]?.timestamp : undefined
return { logData, error, oldestTimestamp }
}, [data?.pages, table])
}, [data?.pages])
const countQuerySql = useMemo(() => genCountQuery(table, mergedFilters), [table, mergedFilters])
const countQueryKey = useMemo(
@@ -1,291 +0,0 @@
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { useMemo, useState, useCallback } from 'react'
import {
LogsTableName,
PREVIEWER_DATEPICKER_HELPERS,
getDefaultHelper,
} from 'components/interfaces/Settings/Logs/Logs.constants'
import type {
Count,
EventChart,
EventChartData,
Filters,
LogData,
Logs,
LogsEndpointParams,
} from 'components/interfaces/Settings/Logs/Logs.types'
import {
genChartQuery,
genCountQuery,
genDefaultQuery,
} from 'components/interfaces/Settings/Logs/Logs.utils'
import { get } from 'data/fetchers'
import { useLogsUrlState } from './useLogsUrlState'
import { useFillTimeseriesSorted } from './useFillTimeseriesSorted'
import useTimeseriesUnixToIso from './useTimeseriesUnixToIso'
interface UnifiedLogsHook {
logData: LogData[]
error: string | Object | null
newCount: number
isSuccess: boolean
isLoading: boolean
isLoadingOlder: boolean
filters: Filters
params: LogsEndpointParams
oldestTimestamp?: string
eventChartData: EventChartData[]
loadOlder: () => void
refresh: () => void
}
function useUnifiedLogs({
projectRef,
filterOverride,
limit,
}: {
projectRef: string
filterOverride?: Filters
limit?: number
}): UnifiedLogsHook {
const defaultHelper = getDefaultHelper(PREVIEWER_DATEPICKER_HELPERS)
const [latestRefresh, setLatestRefresh] = useState(new Date().toISOString())
const {
timestampStart: urlTimestampStart,
timestampEnd: urlTimestampEnd,
filters: urlFilters,
search,
} = useLogsUrlState()
const timestampStart = useMemo(
() => urlTimestampStart || defaultHelper.calcFrom(),
[urlTimestampStart, defaultHelper]
)
const timestampEnd = useMemo(
() => urlTimestampEnd || defaultHelper.calcTo(),
[urlTimestampEnd, defaultHelper]
)
const mergedFilters = useMemo(
() => ({
...urlFilters,
...filterOverride,
...(search ? { search_query: search } : {}),
}),
[JSON.stringify(urlFilters), JSON.stringify(filterOverride), search]
)
const params: LogsEndpointParams = useMemo(() => {
const currentSql = genDefaultQuery(LogsTableName.UNIFIED, mergedFilters, limit)
return {
project: projectRef,
iso_timestamp_start: timestampStart,
iso_timestamp_end: timestampEnd,
sql: currentSql,
}
}, [projectRef, timestampStart, timestampEnd, mergedFilters, limit])
const queryKey = useMemo(
() => ['projects', projectRef, 'unified-logs', params],
[projectRef, params]
)
console.log('useUnifiedLogs SQL:', params.sql)
const {
data,
isSuccess,
isLoading,
isRefetching,
error: rqError,
fetchNextPage,
isFetchingNextPage,
refetch,
} = useInfiniteQuery(
queryKey,
async ({ signal, pageParam }) => {
const { data, error } = await get(`/platform/projects/{ref}/analytics/endpoints/logs.all`, {
params: {
path: { ref: projectRef },
query: {
...params,
iso_timestamp_end: pageParam || params.iso_timestamp_end,
},
},
signal,
})
if (error) {
throw error
}
return data as unknown as Logs
},
{
refetchOnWindowFocus: false,
getNextPageParam(lastPage) {
if ((lastPage.result?.length ?? 0) === 0) {
return undefined
}
const len = lastPage.result.length
const { timestamp: tsLimit }: LogData = lastPage.result[len - 1]
const isoTsLimit = dayjs.utc(Number(tsLimit / 1000)).toISOString()
return isoTsLimit
},
}
)
const { logData, error, oldestTimestamp } = useMemo(() => {
let logData: LogData[] = []
let error: null | string | object = rqError ? (rqError as any).message : null
data?.pages?.forEach((response) => {
if (response?.result) {
logData = [...logData, ...response.result]
}
if (!error && response?.error) {
error = response.error
}
})
// Log unified logs data for debugging
if (logData.length > 0) {
console.log('Unified Logs Data Sample:', logData.slice(0, 1))
}
const oldestTimestamp = logData.length > 0 ? logData[logData.length - 1]?.timestamp : undefined
return { logData, error, oldestTimestamp }
}, [data?.pages])
const countQuerySql = useMemo(
() => genCountQuery(LogsTableName.UNIFIED, mergedFilters),
[mergedFilters]
)
const countQueryKey = useMemo(
() => [
'projects',
projectRef,
'unified-logs-count',
{
projectRef,
sql: countQuerySql,
iso_timestamp_start: latestRefresh,
iso_timestamp_end: timestampEnd,
mergedFilters,
},
],
[projectRef, countQuerySql, latestRefresh, timestampEnd, mergedFilters]
)
const { data: countData } = useQuery(
countQueryKey,
async ({ signal }) => {
const { data, error } = await get(`/platform/projects/{ref}/analytics/endpoints/logs.all`, {
params: {
path: { ref: projectRef },
query: {
project: projectRef,
sql: countQuerySql,
iso_timestamp_start: latestRefresh,
iso_timestamp_end: timestampEnd,
},
},
signal,
})
if (error) {
throw error
}
return data as unknown as Count
},
{
refetchOnWindowFocus: false,
refetchInterval: 60000,
enabled: !error && data && data?.pages?.length > 0 ? true : false,
}
)
const newCount = countData?.result?.[0]?.count ?? 0
const chartQuery = useMemo(
() => genChartQuery(LogsTableName.UNIFIED, params, mergedFilters),
[params, mergedFilters]
)
const chartQueryKey = useMemo(
() => [
'projects',
projectRef,
'unified-logs-chart',
{
projectRef,
sql: chartQuery,
iso_timestamp_start: timestampStart,
iso_timestamp_end: timestampEnd,
},
],
[projectRef, chartQuery, timestampStart, timestampEnd]
)
const { data: eventChartResponse, refetch: refreshEventChart } = useQuery(
chartQueryKey,
async ({ signal }) => {
const { data, error } = await get(`/platform/projects/{ref}/analytics/endpoints/logs.all`, {
params: {
path: { ref: projectRef },
query: {
iso_timestamp_start: timestampStart,
iso_timestamp_end: timestampEnd,
project: projectRef,
sql: chartQuery,
},
},
signal,
})
if (error) {
throw error
}
return data as unknown as EventChart
},
{ refetchOnWindowFocus: false }
)
const refresh = useCallback(async () => {
setLatestRefresh(new Date().toISOString())
refreshEventChart()
refetch()
}, [refetch, refreshEventChart])
const normalizedEventChartData = useTimeseriesUnixToIso(
eventChartResponse?.result ?? [],
'timestamp'
)
const { data: eventChartData, error: eventChartError } = useFillTimeseriesSorted(
normalizedEventChartData,
'timestamp',
'count',
0,
timestampStart,
timestampEnd || new Date().toISOString()
)
return {
newCount,
logData,
isSuccess,
isLoading: isLoading || isRefetching,
isLoadingOlder: isFetchingNextPage,
error: error || eventChartError,
filters: mergedFilters,
params,
oldestTimestamp: oldestTimestamp ? String(oldestTimestamp) : undefined,
eventChartData,
refresh,
loadOlder: () => fetchNextPage(),
}
}
export default useUnifiedLogs
-1
View File
@@ -17,7 +17,6 @@ const HOSTED_SUPPORTED_API_URLS = [
'/get-utc-time',
'/edge-functions/test',
'/edge-functions/body',
'/api/data-table/light',
]
export function middleware(request: NextRequest) {