feat(webhook): time-series histogram stats endpoint + frontend chart

This commit is contained in:
Александр Зимин
2026-05-12 14:30:22 +00:00
parent 91f4310a77
commit 5479142093
11 changed files with 1049 additions and 2 deletions
+18
View File
@@ -330,6 +330,24 @@ export type WebhookDeliveryPage = {
size: number
}
/** Один bucket histogram time-series stats. ISO ts + counts по status. */
export type WebhookStatsBucket = {
ts: string
pending: number
retrying: number
delivered: number
dlq: number
total: number
}
/** Response от GET /admin/webhooks/subscriptions/{id}/stats. */
export type WebhookDeliveryStats = {
bucketBy: 'hour' | 'day'
from: string
to: string
buckets: WebhookStatsBucket[]
}
// === Approval Workflow v2 ===
export type DraftStatus = 'PENDING' | 'APPROVED' | 'REJECTED' | 'WITHDRAWN'
+42
View File
@@ -21,6 +21,7 @@ import {
type SchemaDependent,
type SearchResponse,
type WebhookDeliveryPage,
type WebhookDeliveryStats,
type WebhookSubscription,
} from './client'
@@ -440,6 +441,47 @@ export const webhookDlqQuery = (page: number, size: number) =>
export const useWebhookDlq = (page: number, size: number) =>
useQuery(webhookDlqQuery(page, size))
export const webhookStatsQuery = (
id: string,
bucketBy: 'hour' | 'day',
fromIso?: string,
toIso?: string,
) =>
queryOptions({
queryKey: [
'webhooks',
'stats',
id,
bucketBy,
fromIso ?? null,
toIso ?? null,
] as const,
queryFn: async (): Promise<WebhookDeliveryStats> => {
const params: Record<string, string> = { bucketBy }
if (fromIso) params.from = fromIso
if (toIso) params.to = toIso
const { data } = await apiClient.get<WebhookDeliveryStats>(
`/admin/webhooks/subscriptions/${encodeURIComponent(id)}/stats`,
{ params },
)
return data
},
// Stats для admin dashboard — fresh data важна, но автополлинг мы
// отключаем (юзер сам refresh'ит / меняет range).
staleTime: 30 * 1000,
})
export const useWebhookStats = (
id: string,
bucketBy: 'hour' | 'day',
fromIso?: string,
toIso?: string,
) =>
useQuery({
...webhookStatsQuery(id, bucketBy, fromIso, toIso),
enabled: Boolean(id),
})
// === Dependents (Phase 1 dict-relationships-v2) ===
/**