fix(ui): разделить время опроса и подтверждения OSPF
This commit is contained in:
1 parent
baa43b7ad4
commit
c18c148a23
5 files changed
+65
-4
No files matched your search
@@ -660,6 +660,7 @@ function InterfacesTab({
|
||||
dataReady,
|
||||
dataIncomplete,
|
||||
onOperationBusy,
|
||||
onOperationConfirmed,
|
||||
}: {
|
||||
items: OspfItem[]
|
||||
isLive: boolean
|
||||
@@ -668,6 +669,7 @@ function InterfacesTab({
|
||||
onLiveDataRefresh: () => void
|
||||
dataReady: boolean
|
||||
dataIncomplete: boolean
|
||||
onOperationConfirmed: (id: number) => void
|
||||
onOperationBusy: (busy: boolean) => void
|
||||
}) {
|
||||
const [items, setItems] = useState<OspfItem[]>(initialItems)
|
||||
@@ -750,6 +752,7 @@ function InterfacesTab({
|
||||
byKey[`${data.serverId}-${row.id}`] = row.optimalCost
|
||||
})
|
||||
setLiveOptimalCost(byKey)
|
||||
onOperationConfirmed(data.serverId)
|
||||
toast.success(`API подтвердил оптимизацию OSPF: ${data.optimizedCount} интерфейсов`)
|
||||
onLiveDataRefresh()
|
||||
} catch (err) {
|
||||
@@ -1151,6 +1154,8 @@ function OspfPageContent() {
|
||||
const [fetchedAt, setFetchedAt] = useState<Date | null>(null)
|
||||
const [liveError, setLiveError] = useState<string | null>(null)
|
||||
const [fetchTick, setFetchTick] = useState(0)
|
||||
const [readReceipts, setReadReceipts] = useState<Record<string, string>>({})
|
||||
const [writeReceipt, setWriteReceipt] = useState<{ serverId: number; at: string } | null>(null)
|
||||
const operationBusy = useRef(false)
|
||||
const [writeBusy, setWriteBusy] = useState(false)
|
||||
function refresh() { if (operationBusy.current) return; setLoading(true); setFetchTick(t => t + 1) }
|
||||
@@ -1180,7 +1185,9 @@ function OspfPageContent() {
|
||||
const failed = new Set(data.failures.map(f => f.serverId))
|
||||
setLiveData(previous => ({ ...data, ...Object.fromEntries(sections.map(section => [section, [...data[section], ...(previous?.[section] ?? []).filter(row => enabled.has(row.serverId) && failed.has(row.serverId))]])) }))
|
||||
setLiveServers(inventory.filter(s => s.enabled))
|
||||
setLiveError(null); setFetchedAt(new Date()); setLoading(false)
|
||||
const at = new Date()
|
||||
setReadReceipts(previous => ({ ...Object.fromEntries(Object.entries(previous).filter(([id]) => enabled.has(Number(id)))), ...Object.fromEntries([...enabled].filter(id => !failed.has(id)).map(id => [String(id), at.toISOString()])) }))
|
||||
setLiveError(null); setFetchedAt(at); setLoading(false)
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
if (cancelled) return
|
||||
@@ -1346,7 +1353,11 @@ function OspfPageContent() {
|
||||
<RefreshCwIcon className="size-3.5 animate-spin" />Загрузка OSPF данных…
|
||||
</div>
|
||||
)}
|
||||
{isLive && fetchedAt && <p role="group" aria-label="Свежесть OSPF" className="text-xs text-muted-foreground">Ответ каталога получен <time dateTime={fetchedAt.toISOString()}>{fetchedAt.toLocaleTimeString("ru")}</time>. Время получения браузером, не измерения RouterOS.</p>}
|
||||
{isLive && fetchedAt && <div role="group" aria-label="Свежесть OSPF" className="text-xs text-muted-foreground">
|
||||
<p>Ответ каталога получен <time dateTime={fetchedAt.toISOString()}>{fetchedAt.toLocaleTimeString("ru")}</time>. Время получения браузером, не измерения RouterOS.</p>
|
||||
{liveServers.filter(s => filterServerId === ALL_SERVERS_ID || String(s.id) === filterServerId).map(s => <p key={s.id}>{s.name}: {readReceipts[s.id] ? <time data-ospf-server={s.id} dateTime={readReceipts[s.id]}>{new Date(readReceipts[s.id]).toLocaleTimeString("ru")}</time> : "успешный опрос не получен"}{unknownIds.has(s.id) ? " · данные устарели" : ""}</p>)}
|
||||
{writeReceipt && (filterServerId === ALL_SERVERS_ID || String(writeReceipt.serverId) === filterServerId) && <p data-ospf-time="write">API подтвердил оптимизацию устройства {writeReceipt.serverId}: <time dateTime={writeReceipt.at}>{new Date(writeReceipt.at).toLocaleTimeString("ru")}</time></p>}
|
||||
</div>}
|
||||
{isLive && (liveError || Boolean(liveData?.failures.length)) && <Alert variant="warning"><AlertCircleIcon /><AlertTitle>{liveError ? "Не удалось загрузить OSPF" : "Не удалось опросить часть роутеров OSPF"}</AlertTitle><AlertDescription>{liveError ?? liveData?.failures.map(f => f.serverName + ": " + f.error).join("; ")}. {liveData ? "Показаны последние полученные данные; состояния отказавших устройств неизвестны." : "Данные пока неизвестны."}<Button variant="outline" size="sm" disabled={loading || writeBusy} onClick={refresh} aria-label="Повторить загрузку OSPF">Повторить</Button></AlertDescription></Alert>}
|
||||
{incomplete && (activeTab === "interfaces" ? displayItems.length === 0 : activeTab === "neighbors" ? displayNeighbors.length === 0 : activeTab === "bfd" ? displayBfdSessions.length === 0 : displayRoutes.length === 0) && <p role="status">Данные выбранного раздела OSPF пока неизвестны.</p>}
|
||||
{mode === "mock" && (
|
||||
@@ -1392,6 +1403,7 @@ function OspfPageContent() {
|
||||
onLiveDataRefresh={() => { setLoading(true); setFetchTick(t => t + 1) }}
|
||||
dataReady={dataReady}
|
||||
dataIncomplete={incomplete}
|
||||
onOperationConfirmed={serverId => setWriteReceipt({ serverId, at: new Date().toISOString() })}
|
||||
onOperationBusy={busy => { operationBusy.current = busy; setWriteBusy(busy) }}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user