Files
supabase/apps/studio/hooks/analytics/useFillTimeseriesSorted.ts
Ivan Vasilov 92e79e67d9 feat: Add error state to charts (#46991)
This PR adds an error state to the usage charts on the Project home
page.

<img width="1708" height="471" alt="Screenshot 2026-06-16 at 18 10 20"
src="https://github.com/user-attachments/assets/cba87dad-5e23-4cd1-b787-0ea699445d7f"
/>

I also added an error state to the charts in the [design
system](https://design-system-git-feat-chart-error-state-supabase.vercel.app/design-system/docs/ui-patterns/charts#chart-states).


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Charts now display error states when data retrieval or processing
fails, providing users with clear feedback about issues.
* Improved error visibility and handling across analytics and charting
components for better transparency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-16 16:18:17 +00:00

134 lines
3.2 KiB
TypeScript

import { useMemo } from 'react'
import { fillTimeseries } from '@/components/interfaces/Settings/Logs/Logs.utils'
import type { Datum } from '@/components/ui/Charts/Charts.types'
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> => {
return useMemo(() => {
return fillTimeseriesSorted(options)
}, [
JSON.stringify(options.data),
options.timestampKey,
JSON.stringify(options.valueKey),
options.defaultValue,
options.startDate,
options.endDate,
options.minPointsToFill,
options.interval,
])
}
export const fillTimeseriesSorted = <T extends Datum = Datum>(
options: FillTimeseriesOptions<T>
): FillTimeseriesResult<T> => {
const {
data,
timestampKey,
valueKey,
defaultValue,
startDate,
endDate,
minPointsToFill = 20,
interval,
} = options
// 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,
}
}
}