Add stat card sparkline graphs with timeseries backend endpoint
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:
@@ -1,4 +1,4 @@
|
||||
import { useSearchExecutions, useExecutionStats } from '../../api/queries/executions';
|
||||
import { useSearchExecutions, useExecutionStats, useStatsTimeseries } from '../../api/queries/executions';
|
||||
import { useExecutionSearch } from './use-execution-search';
|
||||
import { StatCard } from '../../components/shared/StatCard';
|
||||
import { Pagination } from '../../components/shared/Pagination';
|
||||
@@ -11,6 +11,16 @@ export function ExecutionExplorer() {
|
||||
const searchRequest = toSearchRequest();
|
||||
const { data, isLoading, isFetching } = useSearchExecutions(searchRequest);
|
||||
const { data: stats } = useExecutionStats();
|
||||
const { data: timeseries } = useStatsTimeseries(
|
||||
searchRequest.timeFrom ?? undefined,
|
||||
searchRequest.timeTo ?? undefined,
|
||||
);
|
||||
|
||||
const sparkTotal = timeseries?.buckets.map((b) => b.totalCount) ?? [];
|
||||
const sparkFailed = timeseries?.buckets.map((b) => b.failedCount) ?? [];
|
||||
const sparkAvgDuration = timeseries?.buckets.map((b) => b.avgDurationMs) ?? [];
|
||||
const sparkP99 = timeseries?.buckets.map((b) => b.p99DurationMs) ?? [];
|
||||
const sparkActive = timeseries?.buckets.map((b) => b.activeCount) ?? [];
|
||||
|
||||
const total = data?.total ?? 0;
|
||||
const results = data?.data ?? [];
|
||||
@@ -40,11 +50,11 @@ export function ExecutionExplorer() {
|
||||
|
||||
{/* Stats Bar */}
|
||||
<div className={styles.statsBar}>
|
||||
<StatCard label="Total Matches" value={total.toLocaleString()} accent="amber" change={`from current search`} />
|
||||
<StatCard label="Avg Duration" value={`${avgDuration}ms`} accent="cyan" />
|
||||
<StatCard label="Failed (page)" value={failedCount.toString()} accent="rose" />
|
||||
<StatCard label="P99 Latency" value={stats ? `${stats.p99LatencyMs}ms` : '--'} accent="green" change="last hour" />
|
||||
<StatCard label="Active Now" value={stats ? stats.activeCount.toString() : '--'} accent="blue" change="running executions" />
|
||||
<StatCard label="Total Matches" value={total.toLocaleString()} accent="amber" change={`from current search`} sparkData={sparkTotal} />
|
||||
<StatCard label="Avg Duration" value={`${avgDuration}ms`} accent="cyan" sparkData={sparkAvgDuration} />
|
||||
<StatCard label="Failed (page)" value={failedCount.toString()} accent="rose" sparkData={sparkFailed} />
|
||||
<StatCard label="P99 Latency" value={stats ? `${stats.p99LatencyMs}ms` : '--'} accent="green" change="last hour" sparkData={sparkP99} />
|
||||
<StatCard label="Active Now" value={stats ? stats.activeCount.toString() : '--'} accent="blue" change="running executions" sparkData={sparkActive} />
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
|
||||
Reference in New Issue
Block a user