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
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user