useFillTimeseriesSorted.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { useMemo } from 'react'
  2. import { fillTimeseries } from '@/components/interfaces/Settings/Logs/Logs.utils'
  3. import type { Datum } from '@/components/ui/Charts/Charts.types'
  4. export type FillTimeseriesOptions<T extends Datum = Datum> = {
  5. /** The timeseries data to fill gaps in */
  6. data: T[]
  7. /** The key in each data object that contains the timestamp */
  8. timestampKey: string
  9. /** The key(s) to fill with default values when gaps exist */
  10. valueKey: string | string[]
  11. /** Default value to use for gaps */
  12. defaultValue: number
  13. /** Start of the time range (ISO string) */
  14. startDate?: string
  15. /** End of the time range (ISO string) */
  16. endDate?: string
  17. /** Minimum number of points before filling is applied */
  18. minPointsToFill?: number
  19. /** Optional interval specification (e.g., '5m', '1h') */
  20. interval?: string
  21. }
  22. export type FillTimeseriesResult<T extends Datum = Datum> = {
  23. data: T[]
  24. error: Error | null
  25. isError: boolean
  26. }
  27. /**
  28. * Sorts timeseries data by timestamp in ascending order
  29. * Returns a new sorted array without mutating the input
  30. */
  31. export function sortByTimestamp<T extends Datum>(data: T[], timestampKey: string): T[] {
  32. return [...data].sort((a, b) => {
  33. return (
  34. new Date(a[timestampKey] as string).getTime() - new Date(b[timestampKey] as string).getTime()
  35. )
  36. })
  37. }
  38. /**
  39. * Validates that the data has a valid timestamp key
  40. */
  41. export function hasValidTimestamp<T extends Datum>(data: T[], timestampKey: string): boolean {
  42. return Boolean(data[0]?.[timestampKey])
  43. }
  44. /**
  45. * Convenience hook for memoized filling of timeseries data.
  46. *
  47. * Fills gaps in timeseries data and sorts results by timestamp.
  48. *
  49. * @example
  50. * ```ts
  51. * const { data, error, isError } = useFillTimeseriesSorted({
  52. * data: chartData,
  53. * timestampKey: 'timestamp',
  54. * valueKey: 'count',
  55. * defaultValue: 0,
  56. * startDate: startIso,
  57. * endDate: endIso
  58. * })
  59. * ```
  60. */
  61. export const useFillTimeseriesSorted = <T extends Datum = Datum>(
  62. options: FillTimeseriesOptions<T>
  63. ): FillTimeseriesResult<T> => {
  64. const {
  65. data,
  66. timestampKey,
  67. valueKey,
  68. defaultValue,
  69. startDate,
  70. endDate,
  71. minPointsToFill = 20,
  72. interval,
  73. } = options
  74. return useMemo(() => {
  75. // Early return if no valid timestamp
  76. if (!hasValidTimestamp(data, timestampKey)) {
  77. return {
  78. data,
  79. error: null,
  80. isError: false,
  81. }
  82. }
  83. try {
  84. const filled = fillTimeseries(
  85. data,
  86. timestampKey,
  87. valueKey,
  88. defaultValue,
  89. startDate,
  90. endDate,
  91. minPointsToFill,
  92. interval
  93. ) as T[]
  94. const sorted = sortByTimestamp(filled, timestampKey)
  95. return {
  96. data: sorted,
  97. error: null,
  98. isError: false,
  99. }
  100. } catch (error: unknown) {
  101. return {
  102. data: [],
  103. error: error instanceof Error ? error : new Error(String(error)),
  104. isError: true,
  105. }
  106. }
  107. }, [
  108. JSON.stringify(data),
  109. timestampKey,
  110. JSON.stringify(valueKey),
  111. defaultValue,
  112. startDate,
  113. endDate,
  114. minPointsToFill,
  115. interval,
  116. ])
  117. }