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

This commit is contained in:
Denozordec
2026-06-30 22:36:00 +07:00
parent d3a2d38b37
commit d2de8d3188
16 changed files with 162 additions and 214 deletions
+45 -52
View File
@@ -9,11 +9,17 @@ import {
} from "@/components/data-grids/firewall-rules-data-grid"
import { FirewallScenarioRulesDataGrid } from "@/components/data-grids/firewall-scenario-rules-data-grid"
import { DataPageCard } from "@/components/data-page-card"
import { FormField, FormToggle, SectionTitle } from "@/components/form-kit"
import { DataPageToolbarFrame } from "@/components/data-page-toolbar"
import { FormField, FormToggle, SectionTitle, SegmentedControl } from "@/components/form-kit"
import { firewallRules, type FirewallRule } from "@/lib/data"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group"
import {
Sheet, SheetContent, SheetHeader, SheetTitle,
SheetDescription, SheetFooter, SheetClose,
@@ -1133,15 +1139,13 @@ function ScenarioSheet({ open, onClose, initial, onSave }: {
{/* Rules table */}
{rules.length > 0 ? (
<div className="rounded-lg border overflow-hidden">
<FirewallScenarioRulesDataGrid
rules={rules}
onToggleEnabled={toggleEnabled}
onMoveUp={(id) => moveRule(id, -1)}
onMoveDown={(id) => moveRule(id, 1)}
onRemove={removeRule}
/>
</div>
<FirewallScenarioRulesDataGrid
rules={rules}
onToggleEnabled={toggleEnabled}
onMoveUp={(id) => moveRule(id, -1)}
onMoveDown={(id) => moveRule(id, 1)}
onRemove={removeRule}
/>
) : (
!addOpen && (
<div className="text-center py-6 text-sm text-muted-foreground border rounded-lg border-dashed">
@@ -1784,52 +1788,41 @@ export default function FirewallPage() {
<SimulatorTab rules={rules} />
) : (
<DataPageCard>
{/* toolbar */}
<div className="flex items-center gap-3 px-5 py-3 border-b flex-wrap">
{/* IP family selector */}
<div className="flex items-center gap-1 rounded-md border border-border bg-muted/40 p-0.5">
{(["all", "ip", "ip6"] as IpFamily[]).map((f) => (
<button key={f} onClick={() => setIpFamily(f)}
className={cn(
"flex items-center gap-1.5 rounded px-3 py-1 text-xs transition-colors whitespace-nowrap",
ipFamily === f
? "bg-background text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}>
{IP_FAMILY_LABELS[f]}
</button>
))}
</div>
{/* chain sub-tabs */}
<div className="flex items-center gap-1 rounded-md border border-border bg-muted/40 p-0.5">
{[{ value: "all", label: "Все" }, ...chainsInGroup.map((c) => ({ value: c, label: c }))].map((t) => (
<button key={t.value} onClick={() => setChainFilter(t.value)}
className={cn(
"flex items-center gap-1.5 rounded px-3 py-1 text-xs transition-colors",
chainFilter === t.value
? "bg-background text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}>
{t.label}
<span className="opacity-50">{chainCounts[t.value] ?? 0}</span>
</button>
))}
</div>
{/* search */}
<div className="flex items-center gap-2 h-8 px-3 border border-input rounded-md bg-background min-w-[220px]">
<SearchIcon className="size-3.5 text-muted-foreground shrink-0" />
<input
className="flex-1 bg-transparent outline-none placeholder:text-muted-foreground text-sm"
<DataPageToolbarFrame>
<SegmentedControl
value={ipFamily}
onChange={setIpFamily}
options={(["all", "ip", "ip6"] as IpFamily[]).map((f) => ({
value: f,
label: IP_FAMILY_LABELS[f],
}))}
/>
<SegmentedControl
value={chainFilter}
onChange={setChainFilter}
options={[
{ value: "all", label: "Все", count: chainCounts.all ?? 0 },
...chainsInGroup.map((c) => ({
value: c,
label: c,
count: chainCounts[c] ?? 0,
})),
]}
/>
<InputGroup className="min-w-[220px] max-w-sm">
<InputGroupAddon>
<SearchIcon className="size-3.5" />
</InputGroupAddon>
<InputGroupInput
placeholder="Поиск по адресу, действию…"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<span className="text-sm text-muted-foreground ml-auto">{filteredRules.length} правил</span>
</div>
</InputGroup>
<span className="text-sm text-muted-foreground ml-auto">
{filteredRules.length} правил
</span>
</DataPageToolbarFrame>
<FirewallRulesDataGrid rules={filteredRules} onToggle={toggleRule} onEdit={openEdit} />
</DataPageCard>