fix(ui): подтверждать удаление проб и изменение звезды

This commit is contained in:
Denozordec committed 2026-09-21 21:08:06 +07:00
1 parent 4f235058be
commit ce14f5de3e
4 files changed
+136 -47

No files matched your search

+57 -45
View File
@@ -13,6 +13,7 @@ import { FormField, FormToggle, SegmentedControl } from "@/components/form-kit"
import { Frame, FramePanel } from "@/components/reui/frame"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
import { Button } from "@/components/ui/button"
import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter } from "@/components/ui/alert-dialog"
import { Input } from "@/components/ui/input"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Flag } from "@/components/flag"
@@ -1056,6 +1057,7 @@ function ResourcesTab({ resources, serversList, liveApi }: { resources: ServerRe
function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId: string | null; allProbesHref: string }) {
const { mode, backendUrl, backendStatus, checkBackend } = useDataSource()
const [deleteTarget, setDeleteTarget] = useState<{ kind: "ping" | "speed"; id: string; label: string } | null>(null)
const draftProbeId = useRef("")
const [probeSaving, setProbeSaving] = useState(false)
const [probeSaveError, setProbeSaveError] = useState<string | null>(null)
@@ -1498,40 +1500,26 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
})
}, [apiFetch, liveApi])
const toggleDashboardStar = useCallback((id: string) => {
const toggleDashboardStar = (id: string) => {
if (!requireWrite()) return
const cur = probes.find((p) => p.id === id)
const cur = probes.find(p => p.id === id)
if (!cur) return
const nextVal = !cur.showOnDashboard
if (liveApi) {
overviewReqRef.current += 1
}
setProbes((prev) => prev.map((p) => (p.id === id ? { ...p, showOnDashboard: nextVal } : p)))
if (liveApi) {
void apiFetch(`/api/uptime/probes/${encodeURIComponent(id)}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
void saveProbeForm(async () => {
if (liveApi) await apiFetch("/api/uptime/probes/" + encodeURIComponent(id), {
method: "PATCH", signal: AbortSignal.timeout(30_000),
body: JSON.stringify({ showOnDashboard: nextVal }),
})
.then(() => loadLiveOverview())
.then(() => {
if (typeof window !== "undefined") {
window.dispatchEvent(new Event(UPTIME_PROBES_CHANGED))
}
})
.catch(() => {
setProbes((prev) => prev.map((p) => (p.id === id ? { ...p, showOnDashboard: cur.showOnDashboard } : p)))
})
} else {
const s = readMockDashboardStarIds()
if (nextVal) s.add(id)
else s.delete(id)
writeMockDashboardStarIds(s)
if (typeof window !== "undefined") {
window.dispatchEvent(new Event(UPTIME_PROBES_CHANGED))
}, () => {
setProbes(prev => prev.map(p => p.id === id ? { ...p, showOnDashboard: nextVal } : p))
if (!liveApi) {
const ids = readMockDashboardStarIds()
if (nextVal) ids.add(id); else ids.delete(id)
writeMockDashboardStarIds(ids)
}
}
}, [probes, liveApi, apiFetch, loadLiveOverview, requireWrite])
window.dispatchEvent(new Event(UPTIME_PROBES_CHANGED))
})
}
const persistSpeedProbes = useCallback(async (rows: SpeedProbeRow[]) => {
if (!liveApi) return
@@ -1584,18 +1572,12 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
void saveProbeForm(() => persistProbes(next), () => setProbes(next))
}
const deleteProbe = (id: string) => {
const deleteProbe = (id: string) => {
if (!requireWrite()) return
if (!liveApi) {
const s = readMockDashboardStarIds()
s.delete(id)
writeMockDashboardStarIds(s)
}
setProbes((prev) => {
const next = prev.filter((x) => x.id !== id)
void persistProbes(next).catch(error => setOpError(serverOperationError(error)))
return next
})
const probe = probes.find(p => p.id === id)
if (!probe) return
setProbeSaveError(null)
setDeleteTarget({ kind: "ping", id, label: probe.name + " → " + probe.target })
}
const toggleCollapse = (serverId: string) =>
@@ -1943,10 +1925,27 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
const deleteSpeedProbe = (id: string) => {
if (!requireWrite()) return
setSpeedProbes((prev) => {
const next = prev.filter((p) => p.id !== id)
void persistSpeedProbes(next).catch(error => setSpeedError(serverOperationError(error)))
return next
const probe = speedProbes.find(p => p.id === id)
if (!probe) return
const name = (id: string) => allServers.find(s => s.id === id)?.name ?? id
setProbeSaveError(null)
setDeleteTarget({ kind: "speed", id, label: name(probe.srcServerId) + " → " + name(probe.dstServerId) })
}
const confirmProbeDelete = () => {
if (!deleteTarget || !requireWrite()) return
const { kind, id } = deleteTarget
const nextPing = probes.filter(p => p.id !== id)
const nextSpeed = speedProbes.filter(p => p.id !== id)
void saveProbeForm(() => kind === "ping" ? persistProbes(nextPing) : persistSpeedProbes(nextSpeed), () => {
if (kind === "ping") {
setProbes(nextPing)
if (!liveApi) {
const ids = readMockDashboardStarIds(); ids.delete(id); writeMockDashboardStarIds(ids)
}
window.dispatchEvent(new Event(UPTIME_PROBES_CHANGED))
} else setSpeedProbes(nextSpeed)
setDeleteTarget(null)
})
}
@@ -2002,9 +2001,9 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
/>
{!canWrite && <ContextAlert variant="info" role="status"><ContextTitle>Только просмотр</ContextTitle><ContextDescription>Изменение проб и запуск измерений требуют разрешения на запись. Обновление данных загружает сохранённые результаты.</ContextDescription></ContextAlert>}
{permissionError && !sheetOpen && !speedSheetOpen && <ContextAlert variant="destructive" role="alert"><ContextDescription>{permissionError}</ContextDescription></ContextAlert>}
{permissionError && !sheetOpen && !speedSheetOpen && !deleteTarget && <ContextAlert variant="destructive" role="alert"><ContextDescription>{permissionError}</ContextDescription></ContextAlert>}
{probeSaveError && !sheetOpen && !speedSheetOpen && <ContextAlert variant="destructive" role="alert"><ContextDescription>{probeSaveError}</ContextDescription></ContextAlert>}
{probeSaveError && !sheetOpen && !speedSheetOpen && !deleteTarget && <ContextAlert variant="destructive" role="alert"><ContextDescription>{probeSaveError}</ContextDescription></ContextAlert>}
{/* ── Live indicator ── */}
{!isPaused && (
@@ -2957,6 +2956,19 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
</SheetFooter>
</SheetContent>
</Sheet>
<AlertDialog open={deleteTarget !== null} onOpenChange={open => { if (!open && !savingRef.current) setDeleteTarget(null) }}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Удалить пробу?</AlertDialogTitle>
<AlertDialogDescription>Проба «{deleteTarget?.label}» будет удалена из мониторинга.</AlertDialogDescription>
</AlertDialogHeader>
{(probeSaveError || permissionError) && <p role="alert" className="text-sm text-destructive">{probeSaveError || permissionError}</p>}
<AlertDialogFooter>
<Button type="button" variant="outline" disabled={probeSaving} onClick={() => setDeleteTarget(null)}>Отмена</Button>
<Button type="button" variant="destructive" disabled={probeSaving || !canWrite} onClick={confirmProbeDelete}>{probeSaving ? "Удаление…" : "Удалить"}</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
)
}