feat(mcp): подготовить сервер и доступ к приёмке
This commit is contained in:
commit
bf3d50e1ff
3835 files changed
+537122
No files matched your search
@@ -0,0 +1,90 @@
|
||||
"use client"
|
||||
|
||||
import { useMemo, useState } from "react"
|
||||
import { PageHeader } from "@/components/page-header"
|
||||
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 } from "lucide-react"
|
||||
import { useEvoCatalogView, EvoBgpCatalogStatus, EvoBgpCatalogUnavailable } from "@/components/settings/evobgp-catalog-status"
|
||||
import { toast } from "sonner"
|
||||
|
||||
export default function IpRangesPage() {
|
||||
const { mode, snapshot, loading, known } = useEvoCatalogView()
|
||||
const [importOpen, setImportOpen] = useState(false)
|
||||
const [search, setSearch] = useState("")
|
||||
|
||||
const useEvoCatalog = mode === "live"
|
||||
|
||||
const rows = useMemo(() => {
|
||||
if (mode === "mock") return mockIpRanges
|
||||
if (loading && !snapshot) return []
|
||||
return snapshot?.ipRanges ?? []
|
||||
}, [mode, 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
|
||||
crumbs={[{ label: "Данные" }, { label: "IP-диапазоны" }]}
|
||||
actions={mode === "live" ? null : <>
|
||||
<Button variant="outline" size="sm" onClick={() => setImportOpen(true)}>
|
||||
<UploadIcon className="size-4" />Импорт
|
||||
</Button>
|
||||
<Button variant="outline" size="sm"><DownloadIcon className="size-4" />Экспорт</Button>
|
||||
<Button size="sm"><PlusIcon className="size-4" />Добавить диапазон</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight">IP-диапазоны</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
CIDR-блоки из каталога для сетевых фильтров
|
||||
</p>
|
||||
<div className="mt-3"><EvoBgpCatalogStatus /></div>
|
||||
</div>
|
||||
<DataPageCard>
|
||||
<DataPageToolbar
|
||||
search={search}
|
||||
onSearchChange={setSearch}
|
||||
searchPlaceholder="Поиск по CIDR, ASN…"
|
||||
countLabel={known ? `${filtered.length} диапазонов` : "Количество: —"}
|
||||
/>
|
||||
{known || loading ? <IpRangesDataGrid
|
||||
ipRanges={filtered}
|
||||
isLoading={useEvoCatalog && loading && !snapshot}
|
||||
pagination={useEvoCatalog}
|
||||
emptyDescription={search ? "Измените поисковый запрос" : useEvoCatalog ? "В подтверждённом каталоге нет записей этого типа" : undefined}
|
||||
/> : <EvoBgpCatalogUnavailable />}
|
||||
</DataPageCard>
|
||||
</div>
|
||||
</div>
|
||||
<FileImportDialog
|
||||
open={importOpen}
|
||||
onOpenChange={setImportOpen}
|
||||
title="Импорт IP-диапазонов"
|
||||
description="Загрузите CSV или JSON со списком CIDR-блоков"
|
||||
accept=".csv,.json,text/csv,application/json"
|
||||
onImport={async (files) => {
|
||||
toast.info(`Выбран файл: ${files[0]?.name ?? "—"}`)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user