fix(ui): заменить таблицы на карточки данных и улучшить функциональность поиска
Docker images / prepare-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 1m37s
Docker images / frontend-image (push) Successful in 1m50s
Docker images / updater-image (push) Successful in 44s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s

This commit is contained in:
Denozordec
2026-06-30 22:22:51 +07:00
parent a1a9124f3d
commit d3a2d38b37
63 changed files with 8509 additions and 3652 deletions
+30 -53
View File
@@ -2,11 +2,13 @@
import { useMemo, useState } from "react"
import { PageHeader } from "@/components/page-header"
import { DataTable } from "@/components/data-table"
import { DataPageCard } from "@/components/data-page-card"
import { DataPageToolbar } from "@/components/data-page-toolbar"
import { IpRangesDataGrid } from "@/components/data-grids/ip-ranges-data-grid"
import { FileImportDialog } from "@/components/file-import-dialog"
import { ipRanges as mockIpRanges } from "@/lib/data"
import { Button } from "@/components/ui/button"
import { UploadIcon, DownloadIcon, PlusIcon, FilterIcon, LoaderCircleIcon } from "lucide-react"
import { UploadIcon, DownloadIcon, PlusIcon, LoaderCircleIcon } from "lucide-react"
import { useDataSource } from "@/lib/data-source"
import { useEvoBGP } from "@/lib/evobgp-context"
import { cn } from "@/lib/utils"
@@ -16,6 +18,7 @@ export default function IpRangesPage() {
const { mode } = useDataSource()
const { enabled, snapshot, loading, error } = useEvoBGP()
const [importOpen, setImportOpen] = useState(false)
const [search, setSearch] = useState("")
const useEvoCatalog = mode === "live" && enabled
@@ -25,6 +28,18 @@ export default function IpRangesPage() {
return snapshot?.ipRanges ?? []
}, [useEvoCatalog, loading, snapshot])
const filtered = useMemo(() => {
if (!search) return rows
const q = search.toLowerCase()
return rows.filter(
(r) =>
r.cidr.toLowerCase().includes(q) ||
r.asn.toLowerCase().includes(q) ||
r.country.toLowerCase().includes(q) ||
r.filter.toLowerCase().includes(q),
)
}, [rows, search])
return (
<div className="flex flex-col h-full">
<PageHeader
@@ -60,57 +75,19 @@ export default function IpRangesPage() {
</p>
)}
</div>
<DataTable
data={rows}
isLoading={useEvoCatalog && loading && !snapshot}
searchPlaceholder="Поиск по CIDR, ASN…"
searchKeys={["cidr", "asn", "country", "filter"]}
columns={[
{
key: "cidr",
label: "CIDR",
render: (d) => <span className="font-mono font-medium">{d.cidr}</span>,
},
{
key: "asn",
label: "ASN",
render: (d) => <span className="font-mono text-xs text-muted-foreground">{d.asn}</span>,
},
{
key: "country",
label: "Страна",
render: (d) => <span className="text-xs border border-border rounded px-2 py-0.5">{d.country}</span>,
},
{
key: "purpose",
label: "Назначение",
render: (d) => <span className="text-xs border border-border rounded px-2 py-0.5">{d.purpose}</span>,
},
{
key: "filter",
label: "Фильтр",
render: (d) => (
<span className="inline-flex items-center gap-1 text-xs bg-muted rounded px-2 py-0.5">
<FilterIcon className="size-3 text-muted-foreground" />{d.filter}
</span>
),
},
{
key: "updated",
label: "Обновлён",
render: (d) => <span className="text-xs text-muted-foreground">{d.updated}</span>,
},
{
key: "enabled",
label: "Статус",
render: (d) => (
<span className={`text-xs font-medium ${d.enabled ? "text-emerald-600" : "text-muted-foreground"}`}>
{d.enabled ? "Активен" : "Отключён"}
</span>
),
},
]}
/>
<DataPageCard>
<DataPageToolbar
search={search}
onSearchChange={setSearch}
searchPlaceholder="Поиск по CIDR, ASN…"
countLabel={`${filtered.length} диапазонов`}
/>
<IpRangesDataGrid
ipRanges={filtered}
isLoading={useEvoCatalog && loading && !snapshot}
pagination={useEvoCatalog}
/>
</DataPageCard>
</div>
</div>
<FileImportDialog