fix(ui): выбирать неактивные серверы для месячного отчёта
This commit is contained in:
1 parent
7dd377d7c3
commit
460cf67313
6 files changed
+76
-33
No files matched your search
+27
-12
@@ -797,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; receivedAt?: number; stats?: FlowStatsDto; exporters?: FlowEntityCard[]; clients?: FlowEntityCard[]; error?: string } | null>(null)
|
||||
const [flowResult, setFlowResult] = useState<{ key: string; receivedAt?: number; monthlyServers?: ServerRead[]; 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
|
||||
@@ -879,10 +879,20 @@ export default function TrafficPage() {
|
||||
useEffect(() => {
|
||||
if (!isLive || effectiveMode !== "flows") return
|
||||
let cancelled = false
|
||||
if (range === "30d") {
|
||||
void listServers(backendUrl).then(servers => {
|
||||
if (cancelled) return
|
||||
setFlowResult({ key: flowKey, receivedAt: Date.now(), monthlyServers: servers })
|
||||
setSelectedId(prev => servers.some(server => String(server.id) === prev) ? prev : String(servers[0]?.id ?? ""))
|
||||
}).catch(error => {
|
||||
if (!cancelled) setFlowResult({ key: flowKey, error: error instanceof Error ? error.message : "Не удалось загрузить каталог серверов" })
|
||||
})
|
||||
return () => { cancelled = true }
|
||||
}
|
||||
void Promise.all([
|
||||
getTrafficFlows(backendUrl, range === "30d" ? "24h" : range),
|
||||
getFlowExporters(backendUrl, range === "30d" ? "24h" : range),
|
||||
getFlowClients(backendUrl, range === "30d" ? "24h" : range),
|
||||
getTrafficFlows(backendUrl, range),
|
||||
getFlowExporters(backendUrl, range),
|
||||
getFlowClients(backendUrl, range),
|
||||
]).then(([stats, exporters, clients]) => {
|
||||
if (cancelled) return
|
||||
setFlowResult({ key: flowKey, receivedAt: Date.now(), stats, exporters: exporters.exporters, clients: clients.clients })
|
||||
@@ -1049,7 +1059,12 @@ export default function TrafficPage() {
|
||||
return sortDir === "desc" ? -v : v
|
||||
})
|
||||
}, [flowCards, q, sortField, sortDir])
|
||||
const selFlowCard = sortedFlowCards.find((c) => c.id === selectedId) ?? sortedFlowCards[0] ?? null
|
||||
const monthlyServers = currentFlows?.monthlyServers ?? []
|
||||
const visibleMonthlyServers = monthlyServers.filter(server => !q || server.name.toLowerCase().includes(q)).sort((a, b) => a.name.localeCompare(b.name))
|
||||
const monthlyServer = monthlyServers.find(server => String(server.id) === selectedId)
|
||||
const selFlowCard = range === "30d"
|
||||
? (monthlyServer ? { id: String(monthlyServer.id), name: monthlyServer.name, country: monthlyServer.country, site: monthlyServer.site } : null)
|
||||
: flowCards.find((c) => c.id === selectedId) ?? null
|
||||
const displayedFlow = currentFlows && !liveError ? (flowLiveSample ?? currentAnalytics?.data ?? null) : null
|
||||
|
||||
const selServer = useMemo(() => activeServerTraffic.find(s => s.id === selectedId) ?? activeServerTraffic[0], [selectedId, activeServerTraffic])
|
||||
@@ -1242,7 +1257,7 @@ export default function TrafficPage() {
|
||||
className="w-full pl-8 pr-3 h-8 text-xs bg-muted/50 border border-border rounded-md outline-none focus:border-primary transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
<div className={cn("flex gap-1 flex-wrap", range === "30d" && "hidden")}>
|
||||
<span className="text-[10px] text-muted-foreground self-center mr-0.5">Сортировка:</span>
|
||||
{visibleSortFields.map(({ field, label }) => (
|
||||
<button
|
||||
@@ -1260,9 +1275,9 @@ export default function TrafficPage() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{range === "30d" && <p className="text-xs text-muted-foreground">Выбор сервера: активность за последние 24 часа. Месячный отчёт — в деталях сервера.</p>}
|
||||
{range === "30d" && <p className="text-xs text-muted-foreground">Серверы из каталога, включая неактивные. Удалённые серверы в список не входят.</p>}
|
||||
<div className="flex flex-col gap-2">
|
||||
{sortedFlowCards.map((card) => (
|
||||
{range === "30d" ? visibleMonthlyServers.map(server => <Button key={server.id} variant="outline" className="justify-start whitespace-normal break-all h-auto py-2" aria-pressed={String(server.id) === selectedId} onClick={() => setSelectedId(String(server.id))}>{server.name}</Button>) : sortedFlowCards.map((card) => (
|
||||
<FlowEntityCardView
|
||||
key={card.id}
|
||||
card={card}
|
||||
@@ -1270,9 +1285,9 @@ export default function TrafficPage() {
|
||||
onClick={() => { setFlowIface("__all__"); setSelectedId(card.id) }}
|
||||
/>
|
||||
))}
|
||||
{!liveBusy && !liveError && sortedFlowCards.length === 0 ? (
|
||||
{!liveBusy && !liveError && (range === "30d" ? visibleMonthlyServers.length : sortedFlowCards.length) === 0 ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{flowEmptyHint(flowStats, collectorAlive) ?? "Нет экспортёров IPFIX. Подключите jump-host."}
|
||||
{range === "30d" ? (q ? "Нет серверов по этому запросу" : "В каталоге нет серверов") : flowEmptyHint(flowStats, collectorAlive) ?? "Нет экспортёров IPFIX. Подключите jump-host."}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -1281,11 +1296,11 @@ 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"} />}
|
||||
{isLive && (displayedFlow || (monthlyServer && currentAnalytics?.monthly)) && <TrafficDataTime label="Получение аналитики потоков" receivedAt={flowLiveSample ? flowLiveReceivedAt : currentAnalytics?.receivedAt} source={flowLiveSample ? "live-поток" : "API"} />}
|
||||
<FlowAnalyticsDetail
|
||||
card={selFlowCard}
|
||||
analytics={displayedFlow}
|
||||
monthly={currentAnalytics?.monthly}
|
||||
monthly={monthlyServer ? currentAnalytics?.monthly : undefined}
|
||||
monthlyAvailable={flowScope === "servers"}
|
||||
month={flowMonth}
|
||||
onMonth={setFlowMonth}
|
||||
|
||||
Reference in new issue
Block a user