feat(backups): добавить S3-хранилище и обновить экран бэкапов
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
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
Сохранять снимки в S3-compatible бакет, скачивать и удалять их вместе с локальными файлами. Привести /backups к DNA /servers: Frame, KPI, фильтры и реальное восстановление. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user