Add device traffic reset with outbound baselines
This commit is contained in:
@@ -19,6 +19,7 @@ interface DevicesFeatureOptions {
|
||||
isGateway: boolean;
|
||||
listDevices: () => Promise<unknown>;
|
||||
refreshDevices: () => Promise<unknown>;
|
||||
resetDeviceTraffic: (expectedRevision: number) => Promise<unknown>;
|
||||
updateDevice: (id: string, patch: Record<string, unknown>, expectedRevision: number) => Promise<unknown>;
|
||||
setDevicePolicy: (id: string, mode: DevicePolicy, expectedRevision: number) => Promise<unknown>;
|
||||
}
|
||||
@@ -40,6 +41,7 @@ export function useDevicesFeature({
|
||||
isGateway,
|
||||
listDevices,
|
||||
refreshDevices,
|
||||
resetDeviceTraffic,
|
||||
updateDevice: requestDeviceUpdate,
|
||||
setDevicePolicy,
|
||||
}: DevicesFeatureOptions) {
|
||||
@@ -50,6 +52,8 @@ export function useDevicesFeature({
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [refreshCycle, setRefreshCycle] = useState(0);
|
||||
const [savingId, setSavingId] = useState('');
|
||||
const [resetOpen, setResetOpen] = useState(false);
|
||||
const [resetting, setResetting] = useState(false);
|
||||
const panelRef = useRef<HTMLElement>(null);
|
||||
const toggleRef = useRef<HTMLButtonElement>(null);
|
||||
const closeRef = useRef<HTMLButtonElement>(null);
|
||||
@@ -136,6 +140,29 @@ export function useDevicesFeature({
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmResetTraffic() {
|
||||
if (!snapshot) return;
|
||||
setResetting(true);
|
||||
try {
|
||||
let next: DeviceSnapshot;
|
||||
try {
|
||||
next = parseDeviceSnapshot(await resetDeviceTraffic(snapshot.revision));
|
||||
} catch (caught) {
|
||||
if (requestError(caught).code !== 'STATE_CONFLICT') throw caught;
|
||||
const latest = parseDeviceSnapshot(await listDevices());
|
||||
publish(latest);
|
||||
next = parseDeviceSnapshot(await resetDeviceTraffic(latest.revision));
|
||||
}
|
||||
publish(next);
|
||||
setError(null);
|
||||
setResetOpen(false);
|
||||
} catch (caught) {
|
||||
setError(caught);
|
||||
} finally {
|
||||
setResetting(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isGateway) return undefined;
|
||||
load();
|
||||
@@ -152,6 +179,7 @@ export function useDevicesFeature({
|
||||
if (!isOpen) return undefined;
|
||||
const frame = requestAnimationFrame(() => closeRef.current?.focus());
|
||||
const closeDevices = (event: PointerEvent | KeyboardEvent) => {
|
||||
if (resetOpen) return;
|
||||
if (event.type === 'keydown' && (event as KeyboardEvent).key !== 'Escape') return;
|
||||
if (event.type !== 'keydown' && (
|
||||
panelRef.current?.contains(event.target as Node) || toggleRef.current?.contains(event.target as Node)
|
||||
@@ -168,7 +196,7 @@ export function useDevicesFeature({
|
||||
if (panelRef.current?.contains(document.activeElement)) toggleRef.current?.focus();
|
||||
});
|
||||
};
|
||||
}, [isOpen]);
|
||||
}, [isOpen, resetOpen]);
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
@@ -178,12 +206,17 @@ export function useDevicesFeature({
|
||||
refreshing,
|
||||
refreshCycle,
|
||||
savingId,
|
||||
resetOpen,
|
||||
resetting,
|
||||
panelRef,
|
||||
toggleRef,
|
||||
closeRef,
|
||||
load,
|
||||
updateDevice,
|
||||
updatePolicy,
|
||||
requestTrafficReset: () => setResetOpen(true),
|
||||
cancelTrafficReset: () => setResetOpen(false),
|
||||
confirmResetTraffic,
|
||||
close: () => setIsOpen(false),
|
||||
toggle: () => setIsOpen((open) => !open),
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
type CSSProperties,
|
||||
} from 'react';
|
||||
import { Drawer } from '../../ui/Drawer.js';
|
||||
import { ConfirmationDialog } from '../../ui/ConfirmationDialog.js';
|
||||
import { Tooltip } from '../../ui/Tooltip.js';
|
||||
import { copyText } from '../../utils/clientControls.js';
|
||||
import {
|
||||
@@ -70,9 +71,14 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
|
||||
refreshing,
|
||||
refreshCycle,
|
||||
savingId,
|
||||
resetOpen,
|
||||
resetting,
|
||||
load: onLoad,
|
||||
updateDevice,
|
||||
updatePolicy,
|
||||
requestTrafficReset,
|
||||
cancelTrafficReset,
|
||||
confirmResetTraffic,
|
||||
close: onClose,
|
||||
} = feature;
|
||||
const [editingId, setEditingId] = useState('');
|
||||
@@ -278,7 +284,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
return <>
|
||||
<Drawer
|
||||
panelRef={panelRef}
|
||||
closeRef={closeRef}
|
||||
@@ -333,6 +339,17 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
|
||||
<button type="button" aria-pressed={trafficScale === 'linear'} onClick={() => setTrafficScale('linear')}>Лин</button>
|
||||
<button type="button" aria-pressed={trafficScale === 'log'} onClick={() => setTrafficScale('log')}>Лог</button>
|
||||
</span>
|
||||
<button
|
||||
className="client-devices-reset"
|
||||
type="button"
|
||||
disabled={!snapshot || resetting}
|
||||
onClick={requestTrafficReset}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M4 12a8 8 0 1 0 2.3-5.7M4 5v7h7" />
|
||||
</svg>
|
||||
<span>Сбросить данные</span>
|
||||
</button>
|
||||
</div>
|
||||
<h2 id="client-devices-title">Устройства</h2>
|
||||
<div className="client-instructions-intro">
|
||||
@@ -623,5 +640,17 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
|
||||
})}
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
<ConfirmationDialog
|
||||
open={resetOpen}
|
||||
id="client-devices-reset"
|
||||
kicker="Данные устройств"
|
||||
title="Сбросить статистику?"
|
||||
description="Вход и выход для всех устройств начнут считаться заново. Общая скорость и история Prometheus/Grafana останутся без изменений."
|
||||
cancelLabel="Оставить данные"
|
||||
confirmLabel="Сбросить"
|
||||
busy={resetting}
|
||||
onCancel={cancelTrafficReset}
|
||||
onConfirm={confirmResetTraffic}
|
||||
/>
|
||||
</>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user