mirror of
https://github.com/supabase/supabase.git
synced 2026-09-08 19:08:44 +08:00
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 -->
134 lines
3.2 KiB
TypeScript
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,
|
|
}
|
|
}
|
|
}
|