Files
MikrotikManager/components/backups/backups-history.tsx
T
DenozordecandCursor cff26813b9
Docker images / prepare-release (push) Successful in 9s
Docker images / backend-test (push) Successful in 2m18s
Docker images / frontend-image (push) Successful in 4m37s
Docker images / updater-image (push) Successful in 46s
Docker images / backend-image (push) Successful in 3m6s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 16s
feat(backups): добавить S3-хранилище и обновить экран бэкапов
Сохранять снимки в S3-compatible бакет, скачивать и удалять их вместе с локальными файлами.
Привести /backups к DNA /servers: Frame, KPI, фильтры и реальное восстановление.

Co-authored-by: Cursor <[email protected]>
2026-09-08 14:06:27 +07:00

98 lines
3.2 KiB
TypeScript

"use client"
import type { Filter } from "@/components/reui/filters"
import type { Backup } from "@/lib/data"
import { applyReuiFilters } from "@/lib/data-filters/apply-reui-filters"
import {
BACKUP_FILTER_ACCESSORS,
BACKUP_FILTER_FIELDS,
} from "@/lib/data-filters/backup-filter-fields"
import { DataPageCard } from "@/components/data-page-card"
import { DataPageToolbar } from "@/components/data-page-toolbar"
import { BackupsDataGrid } from "@/components/data-grids/backups-data-grid"
import { Button } from "@/components/ui/button"
import { PlusIcon } from "lucide-react"
type KindFilter = "all" | "auto" | "manual"
export function BackupsHistory({
backups,
kindFilter,
onKindFilterChange,
search,
onSearchChange,
filters,
onFiltersChange,
onDownload,
onRestore,
onDelete,
onCreate,
}: {
backups: Backup[]
kindFilter: KindFilter
onKindFilterChange: (value: KindFilter) => void
search: string
onSearchChange: (value: string) => void
filters: Filter[]
onFiltersChange: (filters: Filter[]) => void
onDownload: (id: string, filename: string) => void
onRestore: (backup: Backup) => void
onDelete: (backup: Backup) => void
onCreate: () => void
}) {
const autoCount = backups.filter((b) => b.kind === "auto").length
const manualCount = backups.filter((b) => b.kind === "manual").length
const byKind = kindFilter === "all" ? backups : backups.filter((b) => b.kind === kindFilter)
const q = search.trim().toLowerCase()
const searched = q
? byKind.filter((b) =>
[b.filename, b.server, b.notes].some((v) => v.toLowerCase().includes(q)),
)
: byKind
const filtered = applyReuiFilters(searched, filters, BACKUP_FILTER_ACCESSORS)
const isEmptyAll = backups.length === 0
return (
<DataPageCard>
<DataPageToolbar
segmented={{
value: kindFilter,
onChange: onKindFilterChange,
options: [
{ value: "all", label: "Все", count: backups.length },
{ value: "auto", label: "Авто", count: autoCount },
{ value: "manual", label: "Вручную", count: manualCount },
],
}}
filters={filters}
onFiltersChange={onFiltersChange}
filterFields={BACKUP_FILTER_FIELDS}
search={search}
onSearchChange={onSearchChange}
searchPlaceholder="Поиск по файлу, серверу, заметке…"
countLabel={`${filtered.length} бэкапов`}
/>
<BackupsDataGrid
backups={filtered}
onDownload={onDownload}
onRestore={onRestore}
onDelete={onDelete}
emptyTitle={isEmptyAll ? "Нет бэкапов" : "Ничего не найдено"}
emptyDescription={
isEmptyAll
? "Создайте первый бэкап вручную или настройте расписание"
: "Измените фильтры или поисковый запрос"
}
emptyAction={
isEmptyAll ? (
<Button type="button" size="sm" onClick={onCreate}>
<PlusIcon className="size-4" />
Новый бэкап
</Button>
) : null
}
/>
</DataPageCard>
)
}