Files
wireguard-ui/src/pages/admin/setup/(partials)/firewall-rule-row.tsx
T
tbenandClaude Opus 5.5 cad1d32ae4 Redesign the frontend UI on twui
Rebuild every page on a shared set of twui-based primitives with a dense
ops-console look, WireGuard red accent and light/dark/system theming.

- Add design tokens (WireGuard red accent, zinc neutrals, theme-flipping
  semantic colours) and a no-flash theme cookie + toggle
- Add shared components: page header, panel, sub-nav, data table with
  mobile card list, stat tile, status dot, key-value list, copy button,
  inline alert, empty state, form section/actions, confirm dialog and
  toast providers
- New admin shell (sidebar, sticky header, mobile drawer) and a centered
  shell for auth and 404; strip the public template pages
- Unify adding clients at /admin/clients/add with a host picker; tabbed
  client detail (Overview / Rules / Config & QR) with config copy/download
- Re-run client setup after rule changes on the detail page so the
  firewall matches the DB
- Split Setup into status/maintenance/firewall and Settings into
  profile/password subpages
- Replace window.confirm/alert and page reloads with in-app dialogs,
  toasts and in-place list updates
- Fix nested rule form submitting the client form, prefilled signup
  passwords, hidden submit buttons, fields not updating after "next
  free" lookups, and TinyMCE white-on-white in dark mode

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 06:29:21 +01:00

44 lines
1.6 KiB
TypeScript

import CodeBlock from "@/src/components/twui/elements/CodeBlock";
import Row from "@/src/components/twui/layout/Row";
import Span from "@/src/components/twui/layout/Span";
import Stack from "@/src/components/twui/layout/Stack";
import StatusDot, { type StatusTone } from "@/src/components/general/status-dot";
import type {
FirewallRuleKind,
FirewallRuleInfo,
} from "@/src/functions/backend/firewall/grab-firewall-status";
const KIND_META: Record<FirewallRuleKind, { label: string; tone: StatusTone }> = {
required: { label: "Active", tone: "success" },
temporary: { label: "Temporary", tone: "warning" },
orphaned: { label: "Orphaned", tone: "error" },
other: { label: "Unmanaged", tone: "neutral" },
};
type Props = {
rule: FirewallRuleInfo;
};
export default function FirewallRuleRow({ rule }: Props) {
const meta = KIND_META[rule.kind];
return (
<Stack className="w-full items-stretch gap-2.5 px-4 py-3 border-t border-line first:border-t-0">
<Row className="w-full items-start justify-between gap-3 flex-nowrap">
<Span className="text-[15px] leading-5 min-w-0">{rule.description}</Span>
<StatusDot tone={meta.tone} label={meta.label} className="shrink-0 mt-0.5" />
</Row>
{rule.delete_command ? (
<CodeBlock
singleBlock
language="shell"
wrapperProps={{ className: "max-w-full!" }}
>
{`# remove rule\n${rule.delete_command}`}
</CodeBlock>
) : null}
</Stack>
);
}