mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-25 06:36:37 +00:00
Major additions: - Complete Next.js studio application with 1600+ components - Docker support (Dockerfile.combined, docker-compose.yml) - GCP deployment documentation and benchmarks - SQL benchmark scripts for performance testing - Sentry integration for monitoring - Comprehensive test suite and mocks Studio features: - Dashboard and admin interfaces - Data visualization components - Authentication and user management - API integration with RuVector backend - Static data and public assets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
import { useProjectMetricsQuery } from 'data/analytics/project-metrics-query'
|
|
import type { ProjectMetricsRow } from 'data/analytics/project-metrics-query'
|
|
|
|
type ServiceKey = 'db' | 'functions' | 'auth' | 'storage' | 'realtime'
|
|
|
|
export type StatsLike = {
|
|
error: unknown | null
|
|
isLoading: boolean
|
|
eventChartData: Array<{
|
|
timestamp: string
|
|
ok_count: number
|
|
warning_count: number
|
|
error_count: number
|
|
}>
|
|
refresh: () => void
|
|
}
|
|
|
|
type ServiceStatsMap = Record<
|
|
ServiceKey,
|
|
{
|
|
current: StatsLike
|
|
previous: StatsLike
|
|
}
|
|
>
|
|
|
|
/**
|
|
* Transform backend project metrics into a UI-friendly structure with consistent
|
|
* loading/error/refresh state per service.
|
|
*
|
|
* Why this exists
|
|
* - Backend returns flat rows: one record per (time_window, service, bucket_ts).
|
|
* - UI needs per-service objects with two series (current/previous) to drive 5 cards and compute per-service totals.
|
|
* - Charts expect ISO string timestamps; backend gives TIMESTAMP (coming to client as microseconds). We convert to ISO.
|
|
* - We also need stable sorting and consistent empty arrays when a series has no points.
|
|
* - We attach loading/error/refresh per service to keep UI simple.
|
|
*/
|
|
export const toServiceStatsMap = (args: {
|
|
data?: ProjectMetricsRow[]
|
|
isLoading: boolean
|
|
error?: unknown
|
|
onRefresh: () => void
|
|
}): ServiceStatsMap => {
|
|
const { data, isLoading, error, onRefresh } = args
|
|
|
|
const base = {
|
|
error: error ?? null,
|
|
isLoading,
|
|
refresh: () => {
|
|
onRefresh()
|
|
},
|
|
}
|
|
|
|
const empty: StatsLike = { ...base, eventChartData: [] }
|
|
|
|
const grouped: Record<
|
|
ServiceKey,
|
|
{ current: StatsLike['eventChartData']; previous: StatsLike['eventChartData'] }
|
|
> = {
|
|
db: { current: [], previous: [] },
|
|
functions: { current: [], previous: [] },
|
|
auth: { current: [], previous: [] },
|
|
storage: { current: [], previous: [] },
|
|
realtime: { current: [], previous: [] },
|
|
}
|
|
|
|
const toIso = (microseconds: number) => new Date(microseconds / 1000).toISOString()
|
|
|
|
for (const r of data ?? []) {
|
|
const bucket = grouped[r.service as ServiceKey]
|
|
const target = r.time_window === 'current' ? bucket.current : bucket.previous
|
|
target.push({
|
|
timestamp: toIso(r.timestamp),
|
|
ok_count: r.ok_count,
|
|
warning_count: r.warning_count,
|
|
error_count: r.error_count,
|
|
})
|
|
}
|
|
|
|
const byTime = (a: { timestamp: string }, b: { timestamp: string }) =>
|
|
Date.parse(a.timestamp) - Date.parse(b.timestamp)
|
|
for (const key of Object.keys(grouped) as ServiceKey[]) {
|
|
grouped[key].current.sort(byTime)
|
|
grouped[key].previous.sort(byTime)
|
|
}
|
|
|
|
const toStats = (rows: StatsLike['eventChartData'] | undefined): StatsLike =>
|
|
rows ? { ...base, eventChartData: rows } : empty
|
|
|
|
return {
|
|
db: { current: toStats(grouped.db.current), previous: toStats(grouped.db.previous) },
|
|
functions: {
|
|
current: toStats(grouped.functions.current),
|
|
previous: toStats(grouped.functions.previous),
|
|
},
|
|
auth: { current: toStats(grouped.auth.current), previous: toStats(grouped.auth.previous) },
|
|
storage: {
|
|
current: toStats(grouped.storage.current),
|
|
previous: toStats(grouped.storage.previous),
|
|
},
|
|
realtime: {
|
|
current: toStats(grouped.realtime.current),
|
|
previous: toStats(grouped.realtime.previous),
|
|
},
|
|
}
|
|
}
|
|
|
|
export const useServiceStats = (
|
|
projectRef: string,
|
|
interval: '1hr' | '1day' | '7day'
|
|
): ServiceStatsMap => {
|
|
const { data, isLoading, error, refetch } = useProjectMetricsQuery({ projectRef, interval })
|
|
|
|
return toServiceStatsMap({
|
|
data,
|
|
isLoading,
|
|
error,
|
|
onRefresh: () => {
|
|
void refetch()
|
|
},
|
|
})
|
|
}
|