feat(mcp): подготовить сервер и доступ к приёмке

This commit is contained in:
Denozordec committed 2026-11-03 03:12:03 +07:00
commit bf3d50e1ff
3835 files changed
+537122

No files matched your search

@@ -0,0 +1,110 @@
"use client"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import { Alert, AlertDescription, AlertTitle } from "@/components/reui/alert"
import type { Backup } from "@/lib/data"
import { backupFileMissing } from "@/lib/backup-file-state"
import { AlertCircleIcon, LoaderCircleIcon, TriangleAlertIcon } from "lucide-react"
export function BackupDeleteDialog({
backup,
busy,
error,
onConfirm,
onCancel,
returnFocus,
}: {
backup: Backup | null
busy: boolean
error?: string | null
onConfirm: () => void
onCancel: () => void
returnFocus?: () => HTMLElement | null
}) {
return (
<AlertDialog open={Boolean(backup)} onOpenChange={(v) => { if (!v && !busy) onCancel() }}>
<AlertDialogContent finalFocus={returnFocus}>
<AlertDialogHeader>
<AlertDialogMedia className="bg-destructive/10 text-destructive">
<AlertCircleIcon />
</AlertDialogMedia>
<AlertDialogTitle>{backup && backupFileMissing(backup) ? "Удалить запись о бэкапе?" : "Удалить бэкап?"}</AlertDialogTitle>
<AlertDialogDescription className="[overflow-wrap:anywhere]">
{backup && backupFileMissing(backup) ? <>Локальный файл <span className="font-mono text-foreground">{backup.filename}</span> отсутствует. Будет удалена только запись из истории.</> : <>Файл <span className="font-mono text-foreground">{backup?.filename}</span> будет удалён
{backup?.storage === "s3" || backup?.storage === "both" ? " локально; объект S3 — через текущее подключение к хранилищу" : " с диска"}.</>}
</AlertDialogDescription>
</AlertDialogHeader>
{error && <p role="alert" className="text-sm text-destructive break-words">{error}</p>}
<AlertDialogFooter>
<AlertDialogCancel disabled={busy} onClick={onCancel}>Отмена</AlertDialogCancel>
<AlertDialogAction variant="destructive" disabled={busy} onClick={onConfirm}>
{busy ? <LoaderCircleIcon className="size-4 animate-spin" /> : null}
Удалить
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}
export function BackupRestoreDialog({
backup,
busy,
blocked = false,
error,
onConfirm,
onCancel,
returnFocus,
}: {
backup: Backup | null
busy: boolean
blocked?: boolean
error?: string | null
onConfirm: () => void
onCancel: () => void
returnFocus?: () => HTMLElement | null
}) {
return (
<AlertDialog open={Boolean(backup)} onOpenChange={(v) => { if (!v && !busy) onCancel() }}>
<AlertDialogContent finalFocus={returnFocus}>
<AlertDialogHeader>
<AlertDialogMedia className="bg-warning/10 text-warning">
<TriangleAlertIcon />
</AlertDialogMedia>
<AlertDialogTitle>Восстановить конфигурацию?</AlertDialogTitle>
<AlertDialogDescription className="[overflow-wrap:anywhere]">
<span>
Файл <span className="font-mono text-foreground">{backup?.filename}</span> будет загружен
на <span className="text-foreground">{backup?.server}</span> и импортирован.
</span>
</AlertDialogDescription>
<Alert variant="warning">
<TriangleAlertIcon />
<AlertTitle className="line-clamp-none">Это изменит рабочую конфигурацию роутера</AlertTitle>
<AlertDescription>
Сессия может оборваться. Убедитесь, что выбран именно этот сервер и этот снимок.
</AlertDescription>
</Alert>
</AlertDialogHeader>
{error && <p role="alert" className="text-sm text-destructive break-words">{error}</p>}
<AlertDialogFooter>
<AlertDialogCancel disabled={busy} onClick={onCancel}>{blocked && !busy ? "Закрыть" : "Отмена"}</AlertDialogCancel>
<AlertDialogAction disabled={busy || blocked} onClick={onConfirm}>
{busy ? <LoaderCircleIcon className="size-4 animate-spin" /> : null}
Восстановить
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}