refactor(ui): remove KPI strip from Dashboard — metrics now in tab bar
This commit is contained in:
@@ -4,17 +4,14 @@ import { AlertTriangle, X, Search } from 'lucide-react'
|
||||
import {
|
||||
DataTable,
|
||||
ShortcutsBar,
|
||||
KpiStrip,
|
||||
StatusDot,
|
||||
MonoText,
|
||||
Badge,
|
||||
useGlobalFilters,
|
||||
} from '@cameleer/design-system'
|
||||
import type { Column, KpiItem } from '@cameleer/design-system'
|
||||
import type { Column } from '@cameleer/design-system'
|
||||
import {
|
||||
useSearchExecutions,
|
||||
useExecutionStats,
|
||||
useStatsTimeseries,
|
||||
} from '../../api/queries/executions'
|
||||
import type { ExecutionSummary } from '../../api/types'
|
||||
import styles from './Dashboard.module.css'
|
||||
@@ -199,16 +196,12 @@ export default function Dashboard() {
|
||||
const { timeRange, statusFilters } = useGlobalFilters()
|
||||
const timeFrom = timeRange.start.toISOString()
|
||||
const timeTo = timeRange.end.toISOString()
|
||||
const timeWindowSeconds = (timeRange.end.getTime() - timeRange.start.getTime()) / 1000
|
||||
|
||||
const handleSortChange = useCallback((key: string, dir: 'asc' | 'desc') => {
|
||||
setSortField(key)
|
||||
setSortDir(dir)
|
||||
}, [])
|
||||
|
||||
// ─── API hooks ───────────────────────────────────────────────────────────
|
||||
const { data: stats } = useExecutionStats(timeFrom, timeTo, routeId, appId)
|
||||
const { data: timeseries } = useStatsTimeseries(timeFrom, timeTo, routeId, appId)
|
||||
// Convert design-system status filters (lowercase) to API status param (uppercase)
|
||||
const statusParam = statusFilters.size > 0
|
||||
? [...statusFilters].map(s => s.toUpperCase()).join(',')
|
||||
@@ -236,94 +229,6 @@ export default function Dashboard() {
|
||||
[searchResult],
|
||||
)
|
||||
|
||||
// ─── KPI items ───────────────────────────────────────────────────────────
|
||||
const totalCount = stats?.totalCount ?? 0
|
||||
const failedCount = stats?.failedCount ?? 0
|
||||
const successRate = totalCount > 0 ? ((totalCount - failedCount) / totalCount) * 100 : 100
|
||||
const throughput = timeWindowSeconds > 0 ? totalCount / timeWindowSeconds : 0
|
||||
|
||||
const prevTotal = stats?.prevTotalCount ?? 0
|
||||
const prevFailed = stats?.prevFailedCount ?? 0
|
||||
const exchangeTrend = prevTotal > 0 ? ((totalCount - prevTotal) / prevTotal) * 100 : 0
|
||||
const prevSuccessRate = prevTotal > 0 ? ((prevTotal - prevFailed) / prevTotal) * 100 : 100
|
||||
const successRateDelta = successRate - prevSuccessRate
|
||||
const errorDelta = failedCount - prevFailed
|
||||
|
||||
const sparkExchanges = useMemo(
|
||||
() => (timeseries?.buckets || []).map((b: any) => b.totalCount as number),
|
||||
[timeseries],
|
||||
)
|
||||
const sparkErrors = useMemo(
|
||||
() => (timeseries?.buckets || []).map((b: any) => b.failedCount as number),
|
||||
[timeseries],
|
||||
)
|
||||
const sparkLatency = useMemo(
|
||||
() => (timeseries?.buckets || []).map((b: any) => b.p99DurationMs as number),
|
||||
[timeseries],
|
||||
)
|
||||
const sparkThroughput = useMemo(
|
||||
() =>
|
||||
(timeseries?.buckets || []).map((b: any) => {
|
||||
const bucketSeconds = timeWindowSeconds / Math.max((timeseries?.buckets || []).length, 1)
|
||||
return bucketSeconds > 0 ? (b.totalCount as number) / bucketSeconds : 0
|
||||
}),
|
||||
[timeseries, timeWindowSeconds],
|
||||
)
|
||||
|
||||
const kpiItems: KpiItem[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: 'Exchanges',
|
||||
value: totalCount.toLocaleString(),
|
||||
trend: {
|
||||
label: `${exchangeTrend > 0 ? '\u2191' : exchangeTrend < 0 ? '\u2193' : '\u2192'} ${exchangeTrend > 0 ? '+' : ''}${exchangeTrend.toFixed(0)}%`,
|
||||
variant: (exchangeTrend > 0 ? 'success' : exchangeTrend < 0 ? 'error' : 'muted') as 'success' | 'error' | 'muted',
|
||||
},
|
||||
subtitle: `${successRate.toFixed(1)}% success rate`,
|
||||
sparkline: sparkExchanges,
|
||||
borderColor: 'var(--amber)',
|
||||
},
|
||||
{
|
||||
label: 'Success Rate',
|
||||
value: `${successRate.toFixed(1)}%`,
|
||||
trend: {
|
||||
label: `${successRateDelta >= 0 ? '\u2191' : '\u2193'} ${successRateDelta >= 0 ? '+' : ''}${successRateDelta.toFixed(1)}%`,
|
||||
variant: (successRateDelta >= 0 ? 'success' : 'error') as 'success' | 'error',
|
||||
},
|
||||
subtitle: `${(totalCount - failedCount).toLocaleString()} ok / ${failedCount} error`,
|
||||
borderColor: 'var(--success)',
|
||||
},
|
||||
{
|
||||
label: 'Errors',
|
||||
value: failedCount,
|
||||
trend: {
|
||||
label: `${errorDelta > 0 ? '\u2191' : errorDelta < 0 ? '\u2193' : '\u2192'} ${errorDelta > 0 ? '+' : ''}${errorDelta}`,
|
||||
variant: (errorDelta > 0 ? 'error' : errorDelta < 0 ? 'success' : 'muted') as 'success' | 'error' | 'muted',
|
||||
},
|
||||
subtitle: `${failedCount} errors in selected period`,
|
||||
sparkline: sparkErrors,
|
||||
borderColor: 'var(--error)',
|
||||
},
|
||||
{
|
||||
label: 'Throughput',
|
||||
value: `${throughput.toFixed(1)} msg/s`,
|
||||
trend: { label: '\u2192', variant: 'muted' as const },
|
||||
subtitle: `${throughput.toFixed(1)} msg/s`,
|
||||
sparkline: sparkThroughput,
|
||||
borderColor: 'var(--running)',
|
||||
},
|
||||
{
|
||||
label: 'Latency p99',
|
||||
value: `${(stats?.p99LatencyMs ?? 0).toLocaleString()} ms`,
|
||||
trend: { label: '', variant: 'muted' as const },
|
||||
subtitle: `${(stats?.p99LatencyMs ?? 0).toLocaleString()}ms`,
|
||||
sparkline: sparkLatency,
|
||||
borderColor: 'var(--warning)',
|
||||
},
|
||||
],
|
||||
[totalCount, failedCount, successRate, throughput, exchangeTrend, successRateDelta, errorDelta, sparkExchanges, sparkErrors, sparkLatency, sparkThroughput, stats?.p99LatencyMs],
|
||||
)
|
||||
|
||||
// ─── Table columns ──────────────────────────────────────────────────────
|
||||
const columns: Column<Row>[] = useMemo(() => buildBaseColumns(), [])
|
||||
|
||||
@@ -344,9 +249,6 @@ export default function Dashboard() {
|
||||
<>
|
||||
{/* Scrollable content */}
|
||||
<div className={styles.content}>
|
||||
{/* KPI strip */}
|
||||
<KpiStrip items={kpiItems} />
|
||||
|
||||
{/* Exchanges table */}
|
||||
<div className={styles.tableSection}>
|
||||
<div className={styles.tableHeader}>
|
||||
|
||||
Reference in New Issue
Block a user