Files
supabase/apps/studio/components/ui/DataTable/providers/DataTableProvider.tsx
Jordi Enric d2ffd76395 perf(logs): load pathname facet counts on demand DEBUG-230 (#51112)
## Problem

Unified Logs included a pathname aggregation in every initial sidebar
count query, even when the pathname filter was closed. This issue is
tracked in
[DEBUG-230](https://linear.app/supabase/issue/DEBUG-230/fetch-sidebar-counts-only-when-needed).

## Fix

Remove pathname aggregation from the initial ClickHouse and BigQuery
count queries while keeping the existing shared count scans. Fetch
scoped pathname options through the existing facet query when the filter
opens or its search changes. Order limited pathname results by count,
validate response rows with Zod, and retain selected paths during
loading and validation errors. Use live sidebar filters while their URL
update is pending and URL filters after navigation. Cache results by
project and filter scope, and wait for feature flags before requesting
options.

## How to test

- Open Unified Logs, then open Pathname and search. Confirm options load
on demand.
- Change the time range, another filter, or navigate through browser
history. Confirm the options reflect the current scope.
- Close and reopen Pathname without changing the scope. Confirm cached
options return.
- Run the pathname filter component tests and Studio typecheck.
2026-10-06 16:38:30 +02:00

88 lines
3.0 KiB
TypeScript

import type {
ColumnDef,
ColumnFiltersState,
PaginationState,
RowSelectionState,
SortingState,
Table,
VisibilityState,
} from '@tanstack/react-table'
import { createContext, ReactNode, useContext, useMemo } from 'react'
import { DataTableFilterField } from '../DataTable.types'
import { RowSelectionModifiers } from '../rowSelection.utils'
import { ResponseError } from '@/types'
// REMINDER: read about how to move controlled state out of the useReactTable hook
// https://github.com/TanStack/table/discussions/4005#discussioncomment-7303569
interface DataTableStateContextType<TSearchParams = unknown> {
columnFilters: ColumnFiltersState
sorting: SortingState
rowSelection: RowSelectionState
columnOrder: string[]
columnVisibility: VisibilityState
pagination: PaginationState
enableColumnOrdering: boolean
searchParameters: TSearchParams
hasPendingFilterChange?: boolean
openRowId: string | undefined
setOpenRowId: (id: string | undefined) => void
onSelectRow?: (id: string, modifiers?: RowSelectionModifiers) => void
}
interface DataTableBaseContextType<TData = unknown, TValue = unknown> {
table: Table<TData>
error: ResponseError | null
filterFields: DataTableFilterField<TData>[]
columns: ColumnDef<TData, TValue>[]
isFetching: boolean
isError: boolean
isLoading: boolean
isLoadingCounts: boolean
getFacetedUniqueValues?: (table: Table<TData>, columnId: string) => Map<string, number>
getFacetedMinMaxValues?: (table: Table<TData>, columnId: string) => undefined | [number, number]
}
interface DataTableContextType<TData = unknown, TValue = unknown, TSearchParams = unknown>
extends DataTableStateContextType<TSearchParams>, DataTableBaseContextType<TData, TValue> {}
export const DataTableContext = createContext<DataTableContextType<any, any, any> | null>(null)
export function DataTableProvider<TData, TValue, TSearchParams = unknown>({
children,
...props
}: Partial<DataTableStateContextType<TSearchParams>> &
DataTableBaseContextType<TData, TValue> & {
children: ReactNode
}) {
const value = useMemo(
() => ({
...props,
columnFilters: props.columnFilters ?? [],
sorting: props.sorting ?? [],
rowSelection: props.rowSelection ?? {},
columnOrder: props.columnOrder ?? [],
columnVisibility: props.columnVisibility ?? {},
pagination: props.pagination ?? { pageIndex: 0, pageSize: 10 },
enableColumnOrdering: props.enableColumnOrdering ?? false,
searchParameters: props.searchParameters ?? ({} as any),
openRowId: props.openRowId,
setOpenRowId: props.setOpenRowId ?? (() => {}),
}),
[props]
)
return <DataTableContext.Provider value={value}>{children}</DataTableContext.Provider>
}
export function useDataTable<TData, TValue, TSearchParams = unknown>() {
const context = useContext(DataTableContext)
if (!context) {
throw new Error('useDataTable must be used within a DataTableProvider')
}
return context as DataTableContextType<TData, TValue, TSearchParams>
}