fix(ui): ограничить отображение матрицы WAN и Jump Host
This commit is contained in:
1 parent
b3da4216e6
commit
1ad6b4a6b6
8 files changed
+132
-14
No files matched your search
@@ -2,7 +2,10 @@
|
||||
|
||||
import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
|
||||
|
||||
import { useMemo } from "react"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { DataGridContainer, DataGridScrollArea, DataGridTable } from "@/components/reui/data-grid"
|
||||
import {
|
||||
type ColumnDef,
|
||||
useTable,
|
||||
@@ -38,12 +41,35 @@ function RouteOptimizerWanMatrixDataGrid({
|
||||
legs,
|
||||
jumpHosts,
|
||||
}: RouteOptimizerWanMatrixDataGridProps) {
|
||||
const bestScore = legs.length ? Math.max(...legs.map((l) => l.score)) : 0
|
||||
const [wanQuery, setWanQuery] = useState("")
|
||||
const [jhQuery, setJhQuery] = useState("")
|
||||
const [jhPage, setJhPage] = useState(0)
|
||||
const filteredWans = useMemo(() => home.wans.filter(wan =>
|
||||
[wan.name, wan.iface, wan.isp, wan.ip, wan.id].join(" ").toLocaleLowerCase().includes(wanQuery.trim().toLocaleLowerCase())), [home.wans, wanQuery])
|
||||
const filteredHosts = useMemo(() => jumpHosts.filter(jh =>
|
||||
[jh.label, jh.site, jh.ip, jh.country, jh.id].join(" ").toLocaleLowerCase().includes(jhQuery.trim().toLocaleLowerCase())), [jumpHosts, jhQuery])
|
||||
const lastHostPage = Math.max(0, Math.ceil(filteredHosts.length / 5) - 1)
|
||||
const hostPage = Math.min(jhPage, lastHostPage)
|
||||
if (jhPage !== hostPage) setJhPage(hostPage)
|
||||
const visibleHosts = useMemo(() => filteredHosts.slice(hostPage * 5, hostPage * 5 + 5), [filteredHosts, hostPage])
|
||||
const { bestScore, legMap } = useMemo(() => {
|
||||
let bestScore = legs.length ? -Infinity : 0
|
||||
const legMap = new Map<string, Map<string, WanJhLeg>>()
|
||||
for (const leg of legs) {
|
||||
bestScore = Math.max(bestScore, leg.score)
|
||||
let byHost = legMap.get(leg.wanId)
|
||||
if (!byHost) { byHost = new Map(); legMap.set(leg.wanId, byHost) }
|
||||
// Preserve the first matching leg, as the former Array.find did.
|
||||
if (!byHost.has(leg.jhId)) byHost.set(leg.jhId, leg)
|
||||
}
|
||||
return { bestScore, legMap }
|
||||
}, [legs])
|
||||
|
||||
const columns = useMemo<ColumnDef<DataGridFeatures, HomeRouter["wans"][number]>[]>(() => {
|
||||
const base: ColumnDef<DataGridFeatures, HomeRouter["wans"][number]>[] = [
|
||||
{
|
||||
id: "wan",
|
||||
size: 180,
|
||||
accessorKey: "name",
|
||||
header: () => <span className="text-[11px] font-medium text-muted-foreground">WAN-аплинк</span>,
|
||||
cell: ({ row }) => {
|
||||
@@ -62,6 +88,7 @@ function RouteOptimizerWanMatrixDataGrid({
|
||||
},
|
||||
{
|
||||
id: "isp",
|
||||
size: 160,
|
||||
header: () => <span className="text-[11px] font-medium text-muted-foreground">ISP / IP</span>,
|
||||
cell: ({ row }) => {
|
||||
const wan = row.original
|
||||
@@ -76,6 +103,7 @@ function RouteOptimizerWanMatrixDataGrid({
|
||||
},
|
||||
{
|
||||
id: "bandwidth",
|
||||
size: 112,
|
||||
header: () => (
|
||||
<span className="text-[11px] font-medium text-muted-foreground block text-right">
|
||||
Лимит модели
|
||||
@@ -97,8 +125,9 @@ function RouteOptimizerWanMatrixDataGrid({
|
||||
},
|
||||
]
|
||||
|
||||
const jhCols: ColumnDef<DataGridFeatures, HomeRouter["wans"][number]>[] = jumpHosts.map((jh) => ({
|
||||
const jhCols: ColumnDef<DataGridFeatures, HomeRouter["wans"][number]>[] = visibleHosts.map((jh) => ({
|
||||
id: `jh-${jh.id}`,
|
||||
size: 200,
|
||||
header: () => (
|
||||
<div className="text-center">
|
||||
<div className="text-[11px] font-medium text-muted-foreground">{jh.label}</div>
|
||||
@@ -110,7 +139,7 @@ function RouteOptimizerWanMatrixDataGrid({
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const wan = row.original
|
||||
const leg = legs.find((l) => l.wanId === wan.id && l.jhId === jh.id)
|
||||
const leg = legMap.get(wan.id)?.get(jh.id)
|
||||
if (!leg) return <span className="text-center text-muted-foreground text-xs block">—</span>
|
||||
const isBest = leg.score === bestScore
|
||||
return (
|
||||
@@ -165,24 +194,55 @@ function RouteOptimizerWanMatrixDataGrid({
|
||||
}
|
||||
|
||||
return [...base, ...jhCols]
|
||||
}, [bestScore, jumpHosts, legs])
|
||||
}, [bestScore, visibleHosts, legMap])
|
||||
|
||||
const table = useTable({
|
||||
features: dataGridFeatures,
|
||||
manualPagination: true,
|
||||
manualPagination: false,
|
||||
autoResetPageIndex: false,
|
||||
initialState: { pagination: { pageIndex: 0, pageSize: 50 }, columnPinning: { start: ["wan"], end: [] } },
|
||||
manualSorting: true,
|
||||
data: home.wans,
|
||||
data: filteredWans,
|
||||
columns,
|
||||
getRowId: (row) => row.id,
|
||||
})
|
||||
|
||||
const { pageIndex, pageSize } = table.state.pagination
|
||||
useEffect(() => {
|
||||
const lastPage = Math.max(0, Math.ceil(filteredWans.length / pageSize) - 1)
|
||||
if (pageIndex > lastPage) table.setPageIndex(lastPage)
|
||||
}, [filteredWans.length, pageIndex, pageSize, table])
|
||||
|
||||
return (
|
||||
<section aria-label="Матрица WAN и Jump Host" className="min-w-0">
|
||||
<div className="flex flex-wrap gap-3 px-5 py-3">
|
||||
<label className="min-w-0 flex-1 basis-48 text-xs">Поиск WAN
|
||||
<Input className="mt-1" value={wanQuery} onChange={event => { setWanQuery(event.target.value); table.setPageIndex(0) }} placeholder="Название, интерфейс, ISP или IP" />
|
||||
</label>
|
||||
<label className="min-w-0 flex-1 basis-48 text-xs">Поиск Jump Host
|
||||
<Input className="mt-1" value={jhQuery} onChange={event => { setJhQuery(event.target.value); setJhPage(0) }} placeholder="Название, площадка или IP" />
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2 px-5 pb-3 text-xs text-muted-foreground">
|
||||
<span>WAN: {filteredWans.length} из {home.wans.length}. Jump Host: {filteredHosts.length} из {jumpHosts.length}.</span>
|
||||
{filteredHosts.length > 5 && <>
|
||||
<span>Столбцы {hostPage * 5 + 1}–{Math.min((hostPage + 1) * 5, filteredHosts.length)} из {filteredHosts.length}</span>
|
||||
<Button variant="outline" size="sm" aria-label="Предыдущие Jump Host" disabled={hostPage === 0} onClick={() => setJhPage(hostPage - 1)}>Назад</Button>
|
||||
<Button variant="outline" size="sm" aria-label="Следующие Jump Host" disabled={hostPage === lastHostPage} onClick={() => setJhPage(hostPage + 1)}>Далее</Button>
|
||||
</>}
|
||||
</div>
|
||||
<DataGridShell
|
||||
table={table}
|
||||
recordCount={home.wans.length}
|
||||
recordCount={filteredWans.length}
|
||||
pagination={filteredWans.length > 50 || pageSize !== 50}
|
||||
tableClassNames={{ headerRow: "border-b bg-muted/20", bodyRow: "group/row hover:bg-muted/30" }}
|
||||
tableLayout={{ dense: true }}
|
||||
/>
|
||||
tableLayout={{ dense: true, width: "fixed", columnsPinnable: true }}
|
||||
>
|
||||
<DataGridContainer border={false}>
|
||||
<DataGridScrollArea orientation="horizontal"><DataGridTable /></DataGridScrollArea>
|
||||
</DataGridContainer>
|
||||
</DataGridShell>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user