"use client" import { useMemo, useState } from "react" import { PageHeader } from "@/components/page-header" import { StatusBadge } from "@/components/status-badge" import { backups as initialBackups, servers } from "@/lib/data" import type { Backup } from "@/lib/data" import { Card, CardContent } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter, SheetClose, } from "@/components/ui/sheet" import { RefreshCwIcon, PlusIcon, DownloadIcon, Trash2Icon, HardDriveIcon, ClockIcon, ServerIcon, CheckCircleIcon, FolderIcon, } from "lucide-react" import { cn } from "@/lib/utils" // ─── small UI helpers ───────────────────────────────────────────────────────── function Toggle({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) { return ( ) } function SectionTitle({ icon, children }: { icon: React.ReactNode; children: React.ReactNode }) { return (
{icon} {children}
) } function Field({ label, hint, children }: { label: string; hint?: string; children: React.ReactNode }) { return (
{children} {hint &&

{hint}

}
) } function SegmentedControl({ value, onChange, options }: { value: T; onChange: (v: T) => void; options: { value: T; label: string }[] }) { return (
{options.map((o) => ( ))}
) } // ─── types ──────────────────────────────────────────────────────────────────── type PageTab = "history" | "settings" type KindFilter = "all" | "auto" | "manual" type BackupFreq = "daily" | "weekly" | "monthly" type StorageType = "local" | "ftp" | "scp" | "smb" type BackupFormat = "rsc" | "backup" const WEEK_DAYS = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"] // ─── defaults ───────────────────────────────────────────────────────────────── const defaultSchedule = { enabled: true, frequency: "daily" as BackupFreq, hour: 3, minute: 0, weekDay: 0, monthDay: 1, keepCount: 7, format: "rsc" as BackupFormat, } const defaultStorage = { type: "local" as StorageType, localPath: "/var/backup/mikrotik", host: "", port: "", username: "", password: "", remotePath: "/mikrotik-backups", share: "backups", showPassword: false, } // ════════════════════════════════════════════════════════════════════════════ export default function BackupsPage() { // ── state ────────────────────────────────────────────────────────────── const [tab, setTab] = useState("history") const [backupList, setBackupList] = useState(initialBackups) const [kindFilter, setKindFilter] = useState("all") // Schedule const [schedule, setSchedule] = useState(defaultSchedule) const setSched = (k: K, v: (typeof defaultSchedule)[K]) => setSchedule((s) => ({ ...s, [k]: v })) // Storage const [storage, setStorage] = useState(defaultStorage) const setStore = (k: K, v: (typeof defaultStorage)[K]) => setStorage((s) => ({ ...s, [k]: v })) // Server selection (all enabled by default) const [selectedServers, setSelectedServers] = useState>( new Set(servers.map((s) => s.id)) ) function toggleServer(id: string) { setSelectedServers((prev) => { const next = new Set(prev) if (next.has(id)) next.delete(id); else next.add(id) return next }) } // Settings saved flash const [saved, setSaved] = useState(false) function handleSave() { setSaved(true) setTimeout(() => setSaved(false), 2000) } // Manual backup sheet const [manualOpen, setManualOpen] = useState(false) const [manualServers, setManualServers] = useState>(new Set()) const [manualNotes, setManualNotes] = useState("") function toggleManualServer(id: string) { setManualServers((prev) => { const next = new Set(prev) if (next.has(id)) next.delete(id); else next.add(id) return next }) } function handleManualBackup() { const now = new Date() const ts = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,"0")}-${String(now.getDate()).padStart(2,"0")}_${String(now.getHours()).padStart(2,"0")}-${String(now.getMinutes()).padStart(2,"0")}` const newBackups: Backup[] = Array.from(manualServers).map((sid, i) => { const sv = servers.find((s) => s.id === sid)! return { id: `b${Date.now()}${i}`, server: sv.name, filename: `${sv.name}_${ts}_manual.rsc`, size: `${Math.floor(60 + Math.random()*80)} КБ`, created: "Только что", kind: "manual", notes: manualNotes || "", } }) setBackupList((list) => [...newBackups, ...list]) setManualOpen(false) setManualServers(new Set()) setManualNotes("") setTab("history") } // Delete backup function handleDelete(id: string) { setBackupList((list) => list.filter((b) => b.id !== id)) } // ── derived ──────────────────────────────────────────────────────────── const filtered = useMemo(() => backupList.filter((b) => kindFilter === "all" || b.kind === kindFilter), [backupList, kindFilter] ) const autoCount = backupList.filter((b) => b.kind === "auto").length const manualCount = backupList.filter((b) => b.kind === "manual").length const serverCount = new Set(backupList.map((b) => b.server)).size // Schedule summary string const schedSummary = (() => { if (!schedule.enabled) return "Отключено" const t = `${String(schedule.hour).padStart(2,"0")}:${String(schedule.minute).padStart(2,"0")}` if (schedule.frequency === "daily") return `Каждый день в ${t}` if (schedule.frequency === "weekly") return `Каждую неделю (${WEEK_DAYS[schedule.weekDay]}) в ${t}` return `${schedule.monthDay}-го числа каждого месяца в ${t}` })() // Storage path summary const pathSummary = storage.type === "local" ? storage.localPath || "/var/backup/mikrotik" : `${storage.type.toUpperCase()}://${storage.host || "host"}${storage.remotePath || "/"}` // ── render ───────────────────────────────────────────────────────────── return (
} />
{/* Stats */}
{[ { label: "Всего бэкапов", value: backupList.length, icon: }, { label: "Авто", value: autoCount, icon: }, { label: "Вручную", value: manualCount, icon: }, { label: "Серверов охвачено",value: serverCount, icon: }, ].map((s) => (

{s.label}

{s.value}

{s.icon}
))}
{/* Info bar */}
{schedSummary} {pathSummary} {selectedServers.size} из {servers.length} серверов
{/* Tabs */}
{([ { id: "history", label: "История бэкапов" }, { id: "settings", label: "Настройки" }, ] as { id: PageTab; label: string }[]).map((t) => ( ))}
{/* ── История ──────────────────────────────────────────────────── */} {tab === "history" && (
{([ { value: "all", label: "Все", count: backupList.length }, { value: "auto", label: "Авто", count: autoCount }, { value: "manual", label: "Вручную", count: manualCount }, ] as { value: KindFilter; label: string; count: number }[]).map((t) => ( ))}
{filtered.length} бэкапов
{filtered.length === 0 && ( )} {filtered.map((b) => ( ))}
Файл Сервер Размер Тип Заметки Создан
Нет бэкапов
{b.filename} {b.server} {b.size} {b.kind === "auto" ? "авто" : "вручную"} {b.notes || "—"} {b.created}
)} {/* ── Настройки ────────────────────────────────────────────────── */} {tab === "settings" && (
{/* Расписание */} }>Расписание

Автоматический бэкап

Создавать бэкапы по расписанию

setSched("enabled", v)} />
setSched("frequency", v)} options={[ { value: "daily", label: "Ежедневно" }, { value: "weekly", label: "Еженедельно" }, { value: "monthly", label: "Ежемесячно" }, ]} /> {schedule.frequency === "weekly" && (
{WEEK_DAYS.map((d, i) => ( ))}
)} {schedule.frequency === "monthly" && ( setSched("monthDay", Math.min(28, Math.max(1, Number(e.target.value))))} /> )}
setSched("hour", Math.min(23, Math.max(0, Number(e.target.value))))} />
:
{[0, 15, 30, 45].map((m) => ( ))}
setSched("keepCount", Math.max(1, Number(e.target.value)))} /> setSched("format", v)} options={[ { value: "rsc", label: ".rsc" }, { value: "backup", label: ".backup" }, ]} />
{/* Хранилище */} }>Хранилище setStore("type", v)} options={[ { value: "local", label: "Локально" }, { value: "ftp", label: "FTP" }, { value: "scp", label: "SCP" }, { value: "smb", label: "SMB" }, ]} /> {storage.type === "local" && ( setStore("localPath", e.target.value)} /> )} {storage.type !== "local" && ( <>
setStore("host", e.target.value)} />
setStore("port", e.target.value)} />
{storage.type === "smb" && ( setStore("share", e.target.value)} /> )}
setStore("username", e.target.value)} />
setStore("password", e.target.value)} />
setStore("remotePath", e.target.value)} /> )} {/* Preview */}

Путь сохранения

{pathSummary}

{storage.type !== "local" && (

Файлы: {pathSummary}/{""}_{"{date}"}.{schedule.format}

)} {storage.type === "local" && (

Файлы: {storage.localPath || "/var/backup/mikrotik"}/{""}_{"{date}"}.{schedule.format}

)}
{/* Серверы */}
}>Серверы для бэкапа
·
{servers.map((s) => { const checked = selectedServers.has(s.id) return ( ) })}

Выбрано {selectedServers.size} из {servers.length} серверов

{/* Save button */}
{saved && ( Настройки сохранены )}
)}
{/* ══ Sheet: Manual backup ══════════════════════════════════════════════ */} Новый бэкап Снять конфигурацию вручную с выбранных серверов

Выберите серверы

·
{servers.map((s) => { const checked = manualServers.has(s.id) return ( ) })}
setManualNotes(e.target.value)} />
}>Отмена
) }