Files
supabase/apps/studio/hooks/analytics/useFillTimeseriesSorted.ts
T
Jordi Enric db0a2ab752 refactor useFillTimeSeriesSorted hook (#42255)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Safer error rendering across analytics and reporting with a fallback
"Unknown error".

* **Tests**
* Added unit tests covering timeseries sorting and timestamp validation.

* **Refactor**
* Standardized timeseries hook and all callers to accept a single
options object and improved nullish handling.

* **New Features**
* Exposed timeseries utilities and explicit options/result types;
exported chart data type.

* **Chores**
  * Relaxed index signatures to allow dynamic metric keys.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-29 21:05:09 +01:00

127 lines
3.0 KiB
TypeScript

import { fillTimeseries } from 'components/interfaces/Settings/Logs/Logs.utils'
import type { Datum } from 'components/ui/Charts/Charts.types'
import { useMemo } from 'react'
export type FillTimeseriesOptions<T extends Datum = Datum> = {
/** The timeseries data to fill gaps in */
data: T[]
/** The key in each data object that contains the timestamp */
timestampKey: string
/** The key(s) to fill with default values when gaps exist */
valueKey: string | string[]
/** Default value to use for gaps */
defaultValue: number
/** Start of the time range (ISO string) */
startDate?: string
/** End of the time range (ISO string) */
endDate?: string
/** Minimum number of points before filling is applied */
minPointsToFill?: number
/** Optional interval specification (e.g., '5m', '1h') */
interval?: string
}
export type FillTimeseriesResult<T extends Datum = Datum> = {
data: T[]
error: Error | null
isError: boolean
}
/**
* Sorts timeseries data by timestamp in ascending order
* Returns a new sorted array without mutating the input
*/
export function sortByTimestamp<T extends Datum>(data: T[], timestampKey: string): T[] {
return [...data].sort((a, b) => {
return (
new Date(a[timestampKey] as string).getTime() - new Date(b[timestampKey] as string).getTime()
)
})
}
/**
* Validates that the data has a valid timestamp key
*/
export function hasValidTimestamp<T extends Datum>(data: T[], timestampKey: string): boolean {
return Boolean(data[0]?.[timestampKey])
}
/**
* Convenience hook for memoized filling of timeseries data.
*
* Fills gaps in timeseries data and sorts results by timestamp.
*
* @example
* ```ts
* const { data, error, isError } = useFillTimeseriesSorted({
* data: chartData,
* timestampKey: 'timestamp',
* valueKey: 'count',
* defaultValue: 0,
* startDate: startIso,
* endDate: endIso
* })
* ```
*/
export const useFillTimeseriesSorted = <T extends Datum = Datum>(
options: FillTimeseriesOptions<T>
): FillTimeseriesResult<T> => {
const {
data,
timestampKey,
valueKey,
defaultValue,
startDate,
endDate,
minPointsToFill = 20,
interval,
} = options
return useMemo(() => {
// Early return if no valid timestamp
if (!hasValidTimestamp(data, timestampKey)) {
return {
data,
error: null,
isError: false,
}
}
try {
const filled = fillTimeseries(
data,
timestampKey,
valueKey,
defaultValue,
startDate,
endDate,
minPointsToFill,
interval
) as T[]
const sorted = sortByTimestamp(filled, timestampKey)
return {
data: sorted,
error: null,
isError: false,
}
} catch (error: unknown) {
return {
data: [],
error: error instanceof Error ? error : new Error(String(error)),
isError: true,
}
}
}, [
JSON.stringify(data),
timestampKey,
JSON.stringify(valueKey),
defaultValue,
startDate,
endDate,
minPointsToFill,
interval,
])
}