Add stat card sparkline graphs with timeseries backend endpoint
All checks were successful
CI / build (push) Successful in 1m0s
CI / docker (push) Successful in 45s
CI / deploy (push) Successful in 23s

New /search/stats/timeseries endpoint returns bucketed counts/metrics
over a time window using ClickHouse toStartOfInterval(). Frontend
Sparkline component renders SVG polyline + gradient fill on each
stat card, driven by a useStatsTimeseries query hook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-13 18:20:08 +01:00
parent cccd3f07be
commit 9e6e1b350a
10 changed files with 217 additions and 7 deletions

View File

@@ -28,6 +28,27 @@ export function useSearchExecutions(filters: SearchRequest) {
});
}
export function useStatsTimeseries(timeFrom: string | undefined, timeTo: string | undefined) {
return useQuery({
queryKey: ['executions', 'timeseries', timeFrom, timeTo],
queryFn: async () => {
const { data, error } = await api.GET('/search/stats/timeseries', {
params: {
query: {
from: timeFrom!,
to: timeTo || undefined,
buckets: 24,
},
},
});
if (error) throw new Error('Failed to load timeseries');
return data!;
},
enabled: !!timeFrom,
refetchInterval: 30_000,
});
}
export function useExecutionDetail(executionId: string | null) {
return useQuery({
queryKey: ['executions', 'detail', executionId],

View File

@@ -106,6 +106,24 @@ export interface paths {
};
};
};
'/search/stats/timeseries': {
get: {
parameters: {
query: {
from: string;
to?: string;
buckets?: number;
};
};
responses: {
200: {
content: {
'application/json': StatsTimeseries;
};
};
};
};
};
'/agents': {
get: {
parameters: {
@@ -197,6 +215,19 @@ export interface ExecutionStats {
activeCount: number;
}
export interface StatsTimeseries {
buckets: TimeseriesBucket[];
}
export interface TimeseriesBucket {
time: string;
totalCount: number;
failedCount: number;
avgDurationMs: number;
p99DurationMs: number;
activeCount: number;
}
export interface AgentInstance {
id: string;
name: string;