fix(ui): показывать время получения аналитики потоков

This commit is contained in:
Denozordec committed 2026-09-28 11:22:20 +07:00
1 parent 7318c5286b
commit 65241c94e9
6 files changed
+70 -12

No files matched your search

+8 -5
View File
@@ -2,6 +2,7 @@
import { useState, useMemo, useEffect, type ReactNode } from "react"
import { Alert, AlertTitle, AlertDescription } from "@/components/reui/alert"
import { TrafficDataTime } from "@/components/traffic/traffic-data-time"
import { PageHeader } from "@/components/page-header"
import { Frame, FramePanel } from "@/components/reui/frame"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
@@ -796,7 +797,7 @@ export default function TrafficPage() {
const [flowScope, setFlowScope] = useState<FlowScope>("servers")
const [flowRetry, setFlowRetry] = useState(0)
const flowKey = JSON.stringify([isLive, groupMode, backendUrl, range, flowScope, flowRetry])
const [flowResult, setFlowResult] = useState<{ key: string; stats?: FlowStatsDto; exporters?: FlowEntityCard[]; clients?: FlowEntityCard[]; error?: string } | null>(null)
const [flowResult, setFlowResult] = useState<{ key: string; receivedAt?: number; stats?: FlowStatsDto; exporters?: FlowEntityCard[]; clients?: FlowEntityCard[]; error?: string } | null>(null)
const currentFlows = flowResult?.key === flowKey ? flowResult : null
const liveBusy = isLive && groupMode === "flows" && !currentFlows
const liveError = currentFlows?.error
@@ -804,7 +805,7 @@ export default function TrafficPage() {
const flowExporters = useMemo(() => currentFlows?.exporters ?? [], [currentFlows])
const flowClients = useMemo(() => currentFlows?.clients ?? [], [currentFlows])
const [analyticsRetry, setAnalyticsRetry] = useState(0)
const [analyticsResult, setAnalyticsResult] = useState<{ key: string; data?: FlowAnalyticsDto; monthly?: FlowMonthlyDto; error?: string } | null>(null)
const [analyticsResult, setAnalyticsResult] = useState<{ key: string; receivedAt?: number; data?: FlowAnalyticsDto; monthly?: FlowMonthlyDto; error?: string } | null>(null)
const flowIfaceOwner = JSON.stringify([isLive, backendUrl, groupMode, flowScope, selectedId])
const [flowIfaceSelection, setFlowIfaceSelection] = useState<{ owner: string; iface: string } | null>(null)
const flowIface = flowIfaceSelection?.owner === flowIfaceOwner ? flowIfaceSelection.iface : "__all__"
@@ -825,7 +826,7 @@ export default function TrafficPage() {
iface: selectedIface,
})
const flowLiveEnabled = isLive && effectiveMode === "flows" && Boolean(selectedId) && range === "5m"
const { sample: flowLiveSample, error: flowLiveError } = useFlowLive({
const { sample: flowLiveSample, error: flowLiveError, receivedAt: flowLiveReceivedAt } = useFlowLive({
enabled: flowLiveEnabled,
backendUrl,
range,
@@ -882,7 +883,7 @@ export default function TrafficPage() {
getFlowClients(backendUrl, range === "30d" ? "24h" : range),
]).then(([stats, exporters, clients]) => {
if (cancelled) return
setFlowResult({ key: flowKey, stats, exporters: exporters.exporters, clients: clients.clients })
setFlowResult({ key: flowKey, receivedAt: Date.now(), stats, exporters: exporters.exporters, clients: clients.clients })
setSelectedId(prev => {
const list = flowScope === "users" ? clients.clients : exporters.exporters
return list.some(x => x.id === prev) ? prev : (list[0]?.id ?? "")
@@ -907,7 +908,7 @@ export default function TrafficPage() {
iface: flowIface, dedup: flowDedup, excludeMesh: flowExcludeMesh, excludeOverlay: flowExcludeOverlay,
}).then(data => ({ data }))
void request.then(result => {
if (!cancelled) setAnalyticsResult({ key: analyticsKey, ...result })
if (!cancelled) setAnalyticsResult({ key: analyticsKey, receivedAt: Date.now(), ...result })
}).catch(e => {
if (!cancelled) setAnalyticsResult({ key: analyticsKey, error: e instanceof Error ? e.message : "Не удалось загрузить аналитику" })
})
@@ -1193,6 +1194,7 @@ export default function TrafficPage() {
Подключить JH
</Button>
</div>
{isLive && <TrafficDataTime label="Получение списка потоков" receivedAt={currentFlows?.receivedAt} source="API" />}
{liveBusy && <p role="status">Загрузка потоков…</p>}
{liveError && <Alert variant="destructive" role="alert"><AlertTitle>Потоки недоступны</AlertTitle><AlertDescription>{liveError}<Button size="sm" variant="outline" onClick={() => setFlowRetry(value => value + 1)}>Повторить загрузку потоков</Button></AlertDescription></Alert>}
{flowError && !liveError && (
@@ -1277,6 +1279,7 @@ export default function TrafficPage() {
<FramePanel className="flex-1 min-w-0 p-3 sm:p-5">
{analyticsLoading && <p role="status">Загрузка аналитики…</p>}
{currentAnalytics?.error && <Alert variant="destructive" role="alert"><AlertTitle>Аналитика недоступна</AlertTitle><AlertDescription>{currentAnalytics.error}<Button size="sm" variant="outline" onClick={() => setAnalyticsRetry(value => value + 1)}>Повторить аналитику</Button></AlertDescription></Alert>}
{isLive && (displayedFlow || currentAnalytics?.monthly) && <TrafficDataTime label="Получение аналитики потоков" receivedAt={flowLiveSample ? flowLiveReceivedAt : currentAnalytics?.receivedAt} source={flowLiveSample ? "live-поток" : "API"} />}
<FlowAnalyticsDetail
card={selFlowCard}
analytics={displayedFlow}