refactor(ui): migrate all components to Warm Precision token names
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -146,7 +146,7 @@ export function AnsiNfoModal({ open, onOpenChange }: AnsiNfoModalProps) {
|
||||
}}
|
||||
>
|
||||
{/* Retro title bar */}
|
||||
<div className="flex items-center justify-between px-3 pr-10 py-1.5 bg-surface border-b border-border font-mono text-xs">
|
||||
<div className="flex items-center justify-between px-3 pr-10 py-1.5 bg-panel border-b border-border font-mono text-xs">
|
||||
<DialogTitle id="ansi-nfo-title" className="text-text-muted text-xs font-normal font-mono">
|
||||
TOD.NFO — ACiD View v1.0
|
||||
</DialogTitle>
|
||||
|
||||
@@ -698,7 +698,7 @@ export function AlertRulesPage() {
|
||||
No alert rules configured.
|
||||
</p>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3 px-4 py-2 border-b border-border text-xs text-text-muted font-medium">
|
||||
<span className="flex-1">Name</span>
|
||||
@@ -710,7 +710,7 @@ export function AlertRulesPage() {
|
||||
{rules.map((rule) => (
|
||||
<div
|
||||
key={rule.id}
|
||||
className="flex items-center gap-3 px-4 py-2.5 border-b border-border/50 hover:bg-surface text-sm"
|
||||
className="flex items-center gap-3 px-4 py-2.5 border-b border-border/50 hover:bg-panel text-sm"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="text-text-primary truncate block">
|
||||
@@ -809,7 +809,7 @@ export function AlertRulesPage() {
|
||||
{channels.map((ch) => (
|
||||
<div
|
||||
key={ch.id}
|
||||
className="rounded-lg border border-border bg-surface p-4 space-y-2"
|
||||
className="rounded-lg border border-border bg-panel p-4 space-y-2"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -89,7 +89,7 @@ function AlertRow({
|
||||
alert.silenced_until && new Date(alert.silenced_until) > new Date()
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3 px-4 py-3 border-b border-border/50 hover:bg-surface transition-colors">
|
||||
<div className="flex items-center gap-3 px-4 py-3 border-b border-border/50 hover:bg-panel transition-colors">
|
||||
<StatusIcon status={alert.status} />
|
||||
<SeverityBadge severity={alert.severity} />
|
||||
|
||||
@@ -271,7 +271,7 @@ export function AlertsPage() {
|
||||
description="All clear! No alerts have been triggered."
|
||||
/>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{alerts.map((alert) => (
|
||||
<AlertRow
|
||||
key={alert.id}
|
||||
@@ -302,7 +302,7 @@ export function AlertsPage() {
|
||||
description="Alert events will appear here as they are triggered and resolved."
|
||||
/>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{/* Table header */}
|
||||
<div className="flex items-center gap-3 px-4 py-2 border-b border-border text-[10px] uppercase tracking-wider text-text-muted font-semibold">
|
||||
<span className="w-5" />
|
||||
@@ -315,7 +315,7 @@ export function AlertsPage() {
|
||||
{alerts.map((alert) => (
|
||||
<div
|
||||
key={alert.id}
|
||||
className="flex items-center gap-3 px-4 py-2.5 border-b border-border/50 hover:bg-surface text-sm"
|
||||
className="flex items-center gap-3 px-4 py-2.5 border-b border-border/50 hover:bg-panel text-sm"
|
||||
>
|
||||
<StatusIcon status={alert.status} />
|
||||
<span className="w-16">
|
||||
|
||||
@@ -143,7 +143,7 @@ export function AuditLogTable({ tenantId }: AuditLogTableProps) {
|
||||
value={actionFilter}
|
||||
onChange={(e) => { setActionFilter(e.target.value); setPage(1) }}
|
||||
aria-label="Filter by action"
|
||||
className="h-8 rounded-md border border-border bg-surface px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-8 rounded-md border border-border bg-panel px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
>
|
||||
{ACTION_TYPES.map((a) => (
|
||||
<option key={a.value} value={a.value}>
|
||||
@@ -160,7 +160,7 @@ export function AuditLogTable({ tenantId }: AuditLogTableProps) {
|
||||
value={dateFrom}
|
||||
onChange={(e) => { setDateFrom(e.target.value); setPage(1) }}
|
||||
aria-label="Filter from date"
|
||||
className="h-8 rounded-md border border-border bg-surface px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-8 rounded-md border border-border bg-panel px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -172,7 +172,7 @@ export function AuditLogTable({ tenantId }: AuditLogTableProps) {
|
||||
value={dateTo}
|
||||
onChange={(e) => { setDateTo(e.target.value); setPage(1) }}
|
||||
aria-label="Filter to date"
|
||||
className="h-8 rounded-md border border-border bg-surface px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-8 rounded-md border border-border bg-panel px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -185,7 +185,7 @@ export function AuditLogTable({ tenantId }: AuditLogTableProps) {
|
||||
value={userSearch}
|
||||
onChange={(e) => setUserSearch(e.target.value)}
|
||||
aria-label="Filter by user"
|
||||
className="h-8 rounded-md border border-border bg-surface pl-7 pr-2 text-xs text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-1 focus:ring-accent w-40"
|
||||
className="h-8 rounded-md border border-border bg-panel pl-7 pr-2 text-xs text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-1 focus:ring-accent w-40"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -196,7 +196,7 @@ export function AuditLogTable({ tenantId }: AuditLogTableProps) {
|
||||
<button
|
||||
onClick={handleExport}
|
||||
disabled={exporting || !data?.total}
|
||||
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-surface px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-elevated hover:text-text-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-panel px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-elevated hover:text-text-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
{exporting ? 'Exporting...' : 'Export CSV'}
|
||||
@@ -204,7 +204,7 @@ export function AuditLogTable({ tenantId }: AuditLogTableProps) {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{isLoading ? (
|
||||
<div className="p-8 text-center">
|
||||
<div className="inline-block h-6 w-6 animate-spin rounded-full border-2 border-accent border-t-transparent" />
|
||||
@@ -278,7 +278,7 @@ export function AuditLogTable({ tenantId }: AuditLogTableProps) {
|
||||
setPage(1)
|
||||
}}
|
||||
aria-label="Rows per page"
|
||||
className="h-7 rounded border border-border bg-surface px-1.5 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-7 rounded border border-border bg-panel px-1.5 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
>
|
||||
{PER_PAGE_OPTIONS.map((n) => (
|
||||
<option key={n} value={n}>
|
||||
|
||||
@@ -220,7 +220,7 @@ export function EmergencyKitDialog({
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="flex flex-1 items-center justify-center gap-2 rounded-md border border-border bg-surface px-4 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-surface-hover"
|
||||
className="flex flex-1 items-center justify-center gap-2 rounded-md border border-border bg-panel px-4 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-panel-hover"
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
@@ -245,7 +245,7 @@ export function EmergencyKitDialog({
|
||||
</div>
|
||||
|
||||
{/* Instructions */}
|
||||
<div className="mt-3 rounded-md bg-surface-secondary p-3 text-xs text-text-secondary leading-relaxed">
|
||||
<div className="mt-3 rounded-md bg-panel-secondary p-3 text-xs text-text-secondary leading-relaxed">
|
||||
The PDF includes your Secret Key. Print it or save it securely.
|
||||
You can also store the key in your password manager. Do NOT store it alongside your password.
|
||||
</div>
|
||||
|
||||
@@ -134,7 +134,7 @@ export function SrpUpgradeDialog({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="rounded-md bg-surface-secondary p-4 text-sm text-text-secondary leading-relaxed space-y-3">
|
||||
<div className="rounded-md bg-panel-secondary p-4 text-sm text-text-secondary leading-relaxed space-y-3">
|
||||
<p>
|
||||
<strong>What happens:</strong>
|
||||
</p>
|
||||
|
||||
@@ -275,7 +275,7 @@ export function BulkDeployDialog({
|
||||
'rounded-lg border p-4 text-center',
|
||||
result.failed > 0
|
||||
? 'border-error/30 bg-error/5'
|
||||
: 'border-border bg-surface',
|
||||
: 'border-border bg-panel',
|
||||
)}
|
||||
>
|
||||
<XCircle
|
||||
|
||||
@@ -76,7 +76,7 @@ export function CAStatusCard({ ca, canWrite: writable, tenantId }: CAStatusCardP
|
||||
if (!ca) {
|
||||
return (
|
||||
<div className="max-w-lg mx-auto">
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center space-y-6">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center space-y-6">
|
||||
<div className="mx-auto w-16 h-16 rounded-2xl bg-accent/10 flex items-center justify-center">
|
||||
<Shield className="h-8 w-8 text-accent" />
|
||||
</div>
|
||||
@@ -118,7 +118,7 @@ export function CAStatusCard({ ca, canWrite: writable, tenantId }: CAStatusCardP
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-lg border bg-surface p-6 space-y-4',
|
||||
'rounded-lg border bg-panel p-6 space-y-4',
|
||||
isExpired ? 'border-error/40' : 'border-success/30',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -204,7 +204,7 @@ export function CommandPalette() {
|
||||
const visiblePages = pageCommands.filter((p) => p.visible)
|
||||
|
||||
const itemClass =
|
||||
'flex items-center gap-3 px-2 py-2 rounded-lg text-sm text-text-secondary cursor-pointer data-[selected=true]:bg-accent-muted data-[selected=true]:text-accent'
|
||||
'flex items-center gap-3 px-2 py-2 rounded-lg text-sm text-text-secondary cursor-pointer data-[selected=true]:bg-accent-soft data-[selected=true]:text-accent'
|
||||
const groupHeadingClass =
|
||||
'[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:text-text-muted [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider'
|
||||
|
||||
@@ -216,7 +216,7 @@ export function CommandPalette() {
|
||||
overlayClassName="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm"
|
||||
contentClassName="fixed left-1/2 top-[20%] -translate-x-1/2 z-50 w-full max-w-lg"
|
||||
>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center gap-2 px-4 border-b border-border">
|
||||
<Search className="h-4 w-4 text-text-muted flex-shrink-0" />
|
||||
<Command.Input
|
||||
|
||||
@@ -136,7 +136,7 @@ export function CommandExecutor({ tenantId, deviceId, currentPath }: CommandExec
|
||||
{results.length > 0 && (
|
||||
<div className="max-h-48 overflow-y-auto space-y-2">
|
||||
{results.map((r, i) => (
|
||||
<div key={i} className="bg-surface border-border rounded-md font-mono text-xs p-2 border">
|
||||
<div key={i} className="bg-panel border-border rounded-md font-mono text-xs p-2 border">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-[10px] text-text-muted">{r.timestamp}</span>
|
||||
<span className="text-xs font-mono text-text-secondary">{r.command}</span>
|
||||
|
||||
@@ -325,7 +325,7 @@ export function ConfigEditorPage() {
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog open={deleteConfirmOpen} onOpenChange={(o) => !o && setDeleteConfirmOpen(false)}>
|
||||
<DialogContent className="max-w-sm bg-surface border-border text-text-primary">
|
||||
<DialogContent className="max-w-sm bg-panel border-border text-text-primary">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-sm">Confirm Delete</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -143,7 +143,7 @@ export function EntryForm({ open, onClose, mode, entry, columns, onSubmit }: Ent
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(o) => !o && onClose()}>
|
||||
<DialogContent className="max-w-lg max-h-[80vh] overflow-y-auto bg-surface border-border text-text-primary">
|
||||
<DialogContent className="max-w-lg max-h-[80vh] overflow-y-auto bg-panel border-border text-text-primary">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-sm">
|
||||
{mode === 'add' ? 'Add New Entry' : 'Edit Entry'}
|
||||
|
||||
@@ -42,7 +42,7 @@ export function EntryTable({
|
||||
className={cn(
|
||||
'rounded-lg border p-4 text-sm',
|
||||
isContainerPath
|
||||
? 'border-border bg-surface text-text-secondary'
|
||||
? 'border-border bg-panel text-text-secondary'
|
||||
: 'border-error/30 bg-error/10 text-error',
|
||||
)}
|
||||
>
|
||||
@@ -70,7 +70,7 @@ export function EntryTable({
|
||||
<div className="h-8 w-24 bg-elevated/50 rounded animate-pulse" />
|
||||
</div>
|
||||
{Array.from({ length: 5 }).map((_, i) => (
|
||||
<div key={i} className="h-10 bg-surface rounded animate-pulse" />
|
||||
<div key={i} className="h-10 bg-panel rounded animate-pulse" />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
@@ -128,7 +128,7 @@ export function EntryTable({
|
||||
<tr
|
||||
key={entry['.id'] || i}
|
||||
className={cn(
|
||||
'border-b border-border/50 hover:bg-surface transition-colors',
|
||||
'border-b border-border/50 hover:bg-panel transition-colors',
|
||||
entry['dynamic'] === 'true' && 'text-text-muted',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -265,7 +265,7 @@ function TreeItem({
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 w-full px-2 py-1 text-xs rounded transition-colors',
|
||||
isActive
|
||||
? 'bg-[hsl(var(--accent-muted))] text-accent'
|
||||
? 'bg-[hsl(var(--accent-soft))] text-accent'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-elevated/50',
|
||||
)}
|
||||
style={{ paddingLeft: `${depth * 12 + 8}px` }}
|
||||
|
||||
@@ -145,7 +145,7 @@ export function AddressListPanel({ tenantId, deviceId, active }: ConfigPanelProp
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<List className="h-4 w-4" />
|
||||
|
||||
@@ -282,7 +282,7 @@ function AddressTable({
|
||||
return (
|
||||
<>
|
||||
{/* Table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Network className="h-4 w-4" />
|
||||
|
||||
@@ -193,8 +193,8 @@ export function ArpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
|
||||
filterTab === tab.key
|
||||
? 'bg-surface text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-surface/50',
|
||||
? 'bg-panel text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-panel/50',
|
||||
)}
|
||||
>
|
||||
{tab.label}
|
||||
@@ -290,7 +290,7 @@ function ArpTable({
|
||||
return (
|
||||
<>
|
||||
{/* Table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Network className="h-4 w-4" />
|
||||
|
||||
@@ -82,7 +82,7 @@ export function BandwidthTestTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
||||
<div className="space-y-1 col-span-2 sm:col-span-1">
|
||||
<Label className="text-xs text-text-secondary">Target Address</Label>
|
||||
@@ -99,7 +99,7 @@ export function BandwidthTestTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
<select
|
||||
value={direction}
|
||||
onChange={(e) => setDirection(e.target.value)}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="both">Both</option>
|
||||
<option value="send">Send</option>
|
||||
@@ -111,7 +111,7 @@ export function BandwidthTestTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
<select
|
||||
value={protocol}
|
||||
onChange={(e) => setProtocol(e.target.value)}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="tcp">TCP</option>
|
||||
<option value="udp">UDP</option>
|
||||
@@ -170,7 +170,7 @@ export function BandwidthTestTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
)}
|
||||
|
||||
{results.length > 0 && (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50 flex items-center gap-2">
|
||||
<Gauge className="h-4 w-4 text-accent" />
|
||||
<span className="text-sm font-medium text-text-secondary">Bandwidth Test Results</span>
|
||||
|
||||
@@ -184,7 +184,7 @@ function DeviceSelector({
|
||||
|
||||
if (devices.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center">
|
||||
<p className="text-text-muted text-sm">No devices found for this tenant.</p>
|
||||
</div>
|
||||
)
|
||||
@@ -208,7 +208,7 @@ function DeviceSelector({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/50">
|
||||
@@ -327,7 +327,7 @@ function ChangeDefiner({
|
||||
</div>
|
||||
|
||||
{operationType && (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
{operationType === 'add-firewall-rule' && (
|
||||
<>
|
||||
<h4 className="text-sm font-medium text-text-secondary">Firewall Rule</h4>
|
||||
@@ -605,7 +605,7 @@ function ExecutionPanel({
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Change description */}
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<h4 className="text-sm font-medium text-text-secondary mb-1">Change to Apply</h4>
|
||||
<p className="text-sm text-text-primary">{change.description}</p>
|
||||
<p className="text-xs text-text-muted mt-1 font-mono">
|
||||
@@ -626,7 +626,7 @@ function ExecutionPanel({
|
||||
|
||||
{/* Summary */}
|
||||
{isComplete && (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 flex items-center gap-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-success">
|
||||
<CheckCircle className="h-5 w-5" />
|
||||
<span className="text-sm font-medium">{successCount} succeeded</span>
|
||||
@@ -641,7 +641,7 @@ function ExecutionPanel({
|
||||
)}
|
||||
|
||||
{/* Device status table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/50">
|
||||
|
||||
@@ -138,11 +138,11 @@ export function BridgePortPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
</div>
|
||||
|
||||
{ports.entries.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 text-center text-sm text-text-muted">
|
||||
No bridge ports configured.
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
@@ -212,7 +212,7 @@ export function BridgePortPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
<select
|
||||
value={formData['interface'] || ''}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, interface: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary font-mono"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary font-mono"
|
||||
>
|
||||
<option value="">Select...</option>
|
||||
{ifaceNames.map((name) => <option key={name} value={name}>{name}</option>)}
|
||||
@@ -223,7 +223,7 @@ export function BridgePortPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
<select
|
||||
value={formData['bridge'] || ''}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, bridge: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary font-mono"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary font-mono"
|
||||
>
|
||||
{bridgeNames.map((name) => <option key={name} value={name}>{name}</option>)}
|
||||
</select>
|
||||
@@ -243,7 +243,7 @@ export function BridgePortPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
<select
|
||||
value={formData['frame-types'] || 'admit-all'}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, 'frame-types': e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
{FRAME_TYPES.map((ft) => <option key={ft} value={ft}>{ft}</option>)}
|
||||
</select>
|
||||
@@ -253,7 +253,7 @@ export function BridgePortPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
<select
|
||||
value={formData['ingress-filtering'] || 'no'}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, 'ingress-filtering': e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="yes">Yes</option>
|
||||
<option value="no">No</option>
|
||||
@@ -264,7 +264,7 @@ export function BridgePortPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
<select
|
||||
value={formData['hw'] || 'yes'}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, hw: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="yes">Yes</option>
|
||||
<option value="no">No</option>
|
||||
@@ -297,7 +297,7 @@ export function BridgePortPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
<select
|
||||
value={formData['edge'] || 'auto'}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, edge: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="auto">Auto</option>
|
||||
<option value="yes">Yes</option>
|
||||
|
||||
@@ -140,7 +140,7 @@ export function BridgeVlanPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
|
||||
{/* VLAN Filtering status per bridge */}
|
||||
{bridges.entries.length > 0 && (
|
||||
<div className="rounded-lg border border-border bg-surface p-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Network className="h-4 w-4 text-accent" />
|
||||
<span className="text-sm font-medium text-text-secondary">Bridge VLAN Filtering</span>
|
||||
@@ -170,11 +170,11 @@ export function BridgeVlanPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
|
||||
{/* VLAN Table */}
|
||||
{vlans.entries.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 text-center text-sm text-text-muted">
|
||||
No bridge VLAN entries configured.
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
@@ -247,7 +247,7 @@ export function BridgeVlanPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
<select
|
||||
value={formData['bridge'] || ''}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, bridge: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary font-mono"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary font-mono"
|
||||
>
|
||||
{bridgeNames.map((name) => <option key={name} value={name}>{name}</option>)}
|
||||
</select>
|
||||
|
||||
@@ -81,9 +81,9 @@ export function ConfigDiffViewer({
|
||||
const isLoading = loadingOld || loadingNew
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-4 py-2.5 border-b border-border bg-surface">
|
||||
<div className="flex items-center justify-between px-4 py-2.5 border-b border-border bg-panel">
|
||||
<div className="flex items-center gap-2 text-xs text-text-muted">
|
||||
{isEncrypted && (
|
||||
<span className="inline-flex items-center gap-1 text-info" title="Decrypted from encrypted backup">
|
||||
|
||||
@@ -65,7 +65,7 @@ export function ConfigHistorySection({ tenantId, deviceId, deviceName }: ConfigH
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<History className="h-4 w-4 text-text-muted" />
|
||||
<h3 className="text-sm font-medium text-text-muted">Configuration History</h3>
|
||||
|
||||
@@ -162,12 +162,12 @@ export function ConfigTab({
|
||||
{[0, 1, 2].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-14 rounded-lg border border-border bg-surface animate-pulse"
|
||||
className="h-14 rounded-lg border border-border bg-panel animate-pulse"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : !backups || backups.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 text-center text-sm text-text-muted">
|
||||
No backups yet. Click ‘Backup Now’ to create the first backup.
|
||||
</div>
|
||||
) : (
|
||||
@@ -201,7 +201,7 @@ export function ConfigTab({
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-sm text-text-muted h-full flex items-center justify-center min-h-32">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-sm text-text-muted h-full flex items-center justify-center min-h-32">
|
||||
{selectedShas.length < 2
|
||||
? 'Select two backups from the timeline to compare'
|
||||
: 'Click "Compare selected" to view the diff'}
|
||||
|
||||
@@ -104,7 +104,7 @@ export function ConnTrackPanel({ tenantId, deviceId, active }: ConfigPanelProps)
|
||||
</div>
|
||||
|
||||
{/* Active connections count */}
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Activity className="h-5 w-5 text-accent" />
|
||||
<div>
|
||||
@@ -120,7 +120,7 @@ export function ConnTrackPanel({ tenantId, deviceId, active }: ConfigPanelProps)
|
||||
</div>
|
||||
|
||||
{/* Tracking settings */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">Connection Tracking Settings</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={handleEdit}>
|
||||
@@ -154,7 +154,7 @@ export function ConnTrackPanel({ tenantId, deviceId, active }: ConfigPanelProps)
|
||||
<select
|
||||
value={formData['enabled'] || 'auto'}
|
||||
onChange={(e) => setFormData((f) => ({ ...f, enabled: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="auto">Auto</option>
|
||||
<option value="yes">Yes</option>
|
||||
|
||||
@@ -206,7 +206,7 @@ export function DhcpClientPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
</div>
|
||||
|
||||
{/* DHCP Client table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Globe className="h-4 w-4" />
|
||||
|
||||
@@ -240,8 +240,8 @@ export function DhcpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
|
||||
activeTab === tab.key
|
||||
? 'bg-surface text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-surface/50',
|
||||
? 'bg-panel text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-panel/50',
|
||||
)}
|
||||
>
|
||||
{tab.icon}
|
||||
@@ -400,7 +400,7 @@ function ServersTab({
|
||||
}, [form, editing, panel])
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-medium text-text-primary">DHCP Servers</h3>
|
||||
<Button variant="outline" size="sm" onClick={handleAdd} className="gap-1.5">
|
||||
@@ -651,7 +651,7 @@ function PoolsTab({
|
||||
}, [form, editing, panel])
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-medium text-text-primary">Address Pools</h3>
|
||||
<Button variant="outline" size="sm" onClick={handleAdd} className="gap-1.5">
|
||||
@@ -860,7 +860,7 @@ function LeasesTab({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-medium text-text-primary">DHCP Leases</h3>
|
||||
<Button variant="outline" size="sm" onClick={handleAddStatic} className="gap-1.5">
|
||||
@@ -1112,7 +1112,7 @@ function NetworksTab({
|
||||
}, [form, editing, panel])
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-medium text-text-primary">DHCP Networks</h3>
|
||||
<Button variant="outline" size="sm" onClick={handleAdd} className="gap-1.5">
|
||||
|
||||
@@ -24,7 +24,7 @@ export function DiffViewer({ tenantId, deviceId, snapshotId, onClose }: DiffView
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
@@ -302,7 +302,7 @@ export function DnsPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
</div>
|
||||
|
||||
{/* Section 1: Resolver Settings */}
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Server className="h-4 w-4 text-accent" />
|
||||
@@ -393,7 +393,7 @@ export function DnsPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
</div>
|
||||
|
||||
{/* Section 2: Static DNS Entries */}
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Globe className="h-4 w-4 text-accent" />
|
||||
|
||||
@@ -643,7 +643,7 @@ function FilterRulesTable({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
@@ -836,7 +836,7 @@ function NatRulesTable({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
|
||||
@@ -233,7 +233,7 @@ export function InterfacesPanel({ tenantId, deviceId, active }: ConfigPanelProps
|
||||
|
||||
function TableSkeleton({ rows = 5 }: { rows?: number }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface">
|
||||
<div className="rounded-lg border border-border bg-panel">
|
||||
<div className="p-3 border-b border-border">
|
||||
<Skeleton className="h-4 w-32" />
|
||||
</div>
|
||||
@@ -264,14 +264,14 @@ function InterfacesTable({
|
||||
|
||||
if (entries.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-text-secondary text-sm">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-text-secondary text-sm">
|
||||
No interfaces found on this device.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/30">
|
||||
@@ -374,11 +374,11 @@ function IpAddressesTab({ entries, isLoading, interfaceNames, addChange }: IpAdd
|
||||
</div>
|
||||
|
||||
{entries.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-text-secondary text-sm">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-text-secondary text-sm">
|
||||
No IP addresses configured.
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/30">
|
||||
@@ -633,11 +633,11 @@ function VlansTab({ entries, isLoading, interfaceNames, addChange }: VlansTabPro
|
||||
</div>
|
||||
|
||||
{entries.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-text-secondary text-sm">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-text-secondary text-sm">
|
||||
No VLANs configured.
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/30">
|
||||
@@ -941,11 +941,11 @@ function BridgesTab({
|
||||
</div>
|
||||
|
||||
{bridges.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 text-center text-text-secondary text-sm">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 text-center text-text-secondary text-sm">
|
||||
No bridges configured.
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/30">
|
||||
@@ -1015,11 +1015,11 @@ function BridgesTab({
|
||||
</div>
|
||||
|
||||
{bridgePorts.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 text-center text-text-secondary text-sm">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 text-center text-text-secondary text-sm">
|
||||
No bridge ports configured.
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/30">
|
||||
|
||||
@@ -87,7 +87,7 @@ export function IpsecPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
{SUB_TABS.map((tab) => (
|
||||
<button key={tab.key} onClick={() => setActiveTab(tab.key)}
|
||||
className={cn('flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
|
||||
activeTab === tab.key ? 'bg-surface text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary hover:bg-surface/50')}>
|
||||
activeTab === tab.key ? 'bg-panel text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary hover:bg-panel/50')}>
|
||||
{tab.icon}{tab.label}
|
||||
</button>
|
||||
))}
|
||||
@@ -147,11 +147,11 @@ function PeersTab({ entries, panel }: { entries: PeerEntry[]; panel: PanelHook }
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Address</Label><Input value={form.address} onChange={(e) => setForm((f) => ({ ...f, address: e.target.value }))} placeholder="0.0.0.0/0" className="h-8 text-sm font-mono" /></div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Auth Method</Label>
|
||||
<select value={form['auth-method']} onChange={(e) => setForm((f) => ({ ...f, 'auth-method': e.target.value }))} className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary">
|
||||
<select value={form['auth-method']} onChange={(e) => setForm((f) => ({ ...f, 'auth-method': e.target.value }))} className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary">
|
||||
<option value="pre-shared-key">Pre-Shared Key</option><option value="rsa-key">RSA Key</option><option value="rsa-signature">RSA Signature</option>
|
||||
</select></div>
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Exchange Mode</Label>
|
||||
<select value={form['exchange-mode']} onChange={(e) => setForm((f) => ({ ...f, 'exchange-mode': e.target.value }))} className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary">
|
||||
<select value={form['exchange-mode']} onChange={(e) => setForm((f) => ({ ...f, 'exchange-mode': e.target.value }))} className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary">
|
||||
<option value="main">Main</option><option value="aggressive">Aggressive</option><option value="ike2">IKEv2</option>
|
||||
</select></div>
|
||||
</div>
|
||||
@@ -208,8 +208,8 @@ function PoliciesTab({ entries, panel }: { entries: PolicyEntry[]; panel: PanelH
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Dst Address</Label><Input value={form['dst-address']} onChange={(e) => setForm((f) => ({ ...f, 'dst-address': e.target.value }))} placeholder="10.0.0.0/24" className="h-8 text-sm font-mono" /></div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Tunnel</Label><select value={form.tunnel} onChange={(e) => setForm((f) => ({ ...f, tunnel: e.target.value }))} className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"><option value="yes">Yes</option><option value="no">No</option></select></div>
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Action</Label><select value={form.action} onChange={(e) => setForm((f) => ({ ...f, action: e.target.value }))} className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"><option value="encrypt">Encrypt</option><option value="none">None</option></select></div>
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Tunnel</Label><select value={form.tunnel} onChange={(e) => setForm((f) => ({ ...f, tunnel: e.target.value }))} className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"><option value="yes">Yes</option><option value="no">No</option></select></div>
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Action</Label><select value={form.action} onChange={(e) => setForm((f) => ({ ...f, action: e.target.value }))} className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"><option value="encrypt">Encrypt</option><option value="none">None</option></select></div>
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Proposal</Label><Input value={form.proposal} onChange={(e) => setForm((f) => ({ ...f, proposal: e.target.value }))} className="h-8 text-sm" /></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -275,7 +275,7 @@ function ProposalsTab({ entries, panel }: { entries: ProposalEntry[]; panel: Pan
|
||||
|
||||
function SasTab({ entries }: { entries: SaEntry[] }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50"><span className="text-sm font-medium text-text-secondary">Installed SAs ({entries.length})</span></div>
|
||||
{entries.length === 0 ? <div className="px-4 py-8 text-center text-sm text-text-muted">No active security associations.</div> : (
|
||||
<div className="overflow-x-auto"><table className="w-full text-sm"><thead><tr className="border-b border-border/50 text-text-secondary text-xs">
|
||||
@@ -301,7 +301,7 @@ function SasTab({ entries }: { entries: SaEntry[] }) {
|
||||
|
||||
function TableWrapper({ title, count, onAdd, children }: { title: string; count: number; onAdd: () => void; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">{title} ({count})</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={onAdd}><Plus className="h-3.5 w-3.5" />Add</Button>
|
||||
|
||||
@@ -127,7 +127,7 @@ export function ManglePanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
onClick={() => setChainFilter(chain)}
|
||||
className={cn(
|
||||
'px-3 py-1.5 rounded-md text-sm font-medium transition-colors capitalize',
|
||||
chainFilter === chain ? 'bg-surface text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary hover:bg-surface/50',
|
||||
chainFilter === chain ? 'bg-panel text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary hover:bg-panel/50',
|
||||
)}
|
||||
>
|
||||
{chain}
|
||||
@@ -193,7 +193,7 @@ function MangleTable({ entries, panel }: { entries: MangleEntry[]; panel: PanelH
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Filter className="h-4 w-4" />
|
||||
|
||||
@@ -90,7 +90,7 @@ export function PingTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Input form */}
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<div className="space-y-1 col-span-2">
|
||||
<Label className="text-xs text-text-secondary">Target IP / Hostname</Label>
|
||||
@@ -166,7 +166,7 @@ export function PingTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
)}
|
||||
|
||||
{results.length > 0 && (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50 flex items-center gap-2">
|
||||
<Activity className="h-4 w-4 text-accent" />
|
||||
<span className="text-sm font-medium text-text-secondary">Ping Results</span>
|
||||
@@ -189,7 +189,7 @@ export function PingTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
|
||||
{/* Stats summary */}
|
||||
{stats && (
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="grid grid-cols-3 sm:grid-cols-6 gap-4 text-center">
|
||||
<StatBox label="Sent" value={String(stats.sent)} />
|
||||
<StatBox label="Received" value={String(stats.received)} />
|
||||
|
||||
@@ -275,7 +275,7 @@ function PoolTable({
|
||||
return (
|
||||
<>
|
||||
{/* Table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Layers className="h-4 w-4" />
|
||||
|
||||
@@ -98,7 +98,7 @@ export function PppPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
{SUB_TABS.map((tab) => (
|
||||
<button key={tab.key} onClick={() => setActiveTab(tab.key)}
|
||||
className={cn('flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
|
||||
activeTab === tab.key ? 'bg-surface text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary hover:bg-surface/50')}>
|
||||
activeTab === tab.key ? 'bg-panel text-text-primary shadow-sm' : 'text-text-secondary hover:text-text-primary hover:bg-panel/50')}>
|
||||
{tab.icon}{tab.label}
|
||||
{tab.key === 'active' && activeConns.entries.length > 0 && (
|
||||
<span className="text-xs bg-accent/20 text-accent px-1 rounded">{activeConns.entries.length}</span>
|
||||
@@ -146,7 +146,7 @@ function ProfilesTab({ entries, panel }: { entries: ProfileEntry[]; panel: Panel
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">PPP Profiles ({entries.length})</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={handleAdd}><Plus className="h-3.5 w-3.5" />Add Profile</Button>
|
||||
@@ -219,7 +219,7 @@ function SecretsTab({ entries, panel, profileNames }: { entries: SecretEntry[];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">PPP Secrets ({entries.length})</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={handleAdd}><Plus className="h-3.5 w-3.5" />Add Secret</Button>
|
||||
@@ -252,7 +252,7 @@ function SecretsTab({ entries, panel, profileNames }: { entries: SecretEntry[];
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Service</Label>
|
||||
<select value={form.service} onChange={(e) => setForm((f) => ({ ...f, service: e.target.value }))} className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary">
|
||||
<select value={form.service} onChange={(e) => setForm((f) => ({ ...f, service: e.target.value }))} className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary">
|
||||
{PPP_SERVICES.map((s) => <option key={s} value={s}>{s}</option>)}
|
||||
</select></div>
|
||||
<div className="space-y-1"><Label className="text-xs text-text-secondary">Profile</Label><Input value={form.profile} onChange={(e) => setForm((f) => ({ ...f, profile: e.target.value }))} placeholder="default" className="h-8 text-sm" list="profile-names" />
|
||||
@@ -281,7 +281,7 @@ function ActiveTab({ entries, tenantId, deviceId, refetch }: { entries: ActiveEn
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">Active Connections ({entries.length})</span>
|
||||
</div>
|
||||
|
||||
@@ -100,7 +100,7 @@ export function RestorePreview({
|
||||
)}
|
||||
|
||||
{/* Summary bar */}
|
||||
<div className="rounded-lg border border-border bg-surface-raised p-3 flex items-center justify-between">
|
||||
<div className="rounded-lg border border-border bg-panel-raised p-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4 text-sm">
|
||||
<span className="text-success font-mono">+{diff.added}</span>
|
||||
<span className="text-error font-mono">-{diff.removed}</span>
|
||||
@@ -125,7 +125,7 @@ export function RestorePreview({
|
||||
{changedCategories.map((cat) => (
|
||||
<button
|
||||
key={cat.path}
|
||||
className="w-full px-3 py-2 flex items-center justify-between hover:bg-surface-raised/50 transition-colors"
|
||||
className="w-full px-3 py-2 flex items-center justify-between hover:bg-panel-raised/50 transition-colors"
|
||||
onClick={() => togglePath(cat.path)}
|
||||
>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
|
||||
@@ -170,8 +170,8 @@ export function RoutesPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
|
||||
filterTab === tab.key
|
||||
? 'bg-surface text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-surface/50',
|
||||
? 'bg-panel text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-panel/50',
|
||||
)}
|
||||
>
|
||||
{tab.label}
|
||||
@@ -313,7 +313,7 @@ function RoutesTable({
|
||||
return (
|
||||
<>
|
||||
{/* Table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Route className="h-4 w-4" />
|
||||
|
||||
@@ -148,8 +148,8 @@ export function ScriptsPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
|
||||
activeTab === tab.key
|
||||
? 'bg-surface text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-surface/50',
|
||||
? 'bg-panel text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-panel/50',
|
||||
)}
|
||||
>
|
||||
{tab.icon}
|
||||
@@ -275,7 +275,7 @@ function ScriptsTab({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Code className="h-4 w-4" />
|
||||
@@ -477,7 +477,7 @@ function SchedulerTab({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Clock className="h-4 w-4" />
|
||||
|
||||
@@ -230,7 +230,7 @@ function ServiceTable({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Shield className="h-4 w-4" />
|
||||
@@ -337,7 +337,7 @@ function ServiceTable({
|
||||
<select
|
||||
value={form.disabled}
|
||||
onChange={(e) => setForm((f) => ({ ...f, disabled: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="false">Enabled</option>
|
||||
<option value="true">Disabled</option>
|
||||
|
||||
@@ -158,7 +158,7 @@ export function SnmpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
</div>
|
||||
|
||||
{/* SNMP Status + Settings */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2">
|
||||
<Radio className="h-4 w-4 text-accent" />
|
||||
@@ -192,7 +192,7 @@ export function SnmpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
</div>
|
||||
|
||||
{/* Communities */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">SNMP Communities</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={handleAddCommunity}>
|
||||
@@ -279,7 +279,7 @@ export function SnmpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-text-secondary">Trap Version</Label>
|
||||
<select value={settingsForm['trap-version'] || '1'} onChange={(e) => setSettingsForm((f) => ({ ...f, 'trap-version': e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary">
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary">
|
||||
<option value="1">v1</option>
|
||||
<option value="2">v2c</option>
|
||||
<option value="3">v3</option>
|
||||
@@ -313,7 +313,7 @@ export function SnmpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-text-secondary">Read Access</Label>
|
||||
<select value={communityForm['read-access'] || 'yes'} onChange={(e) => setCommunityForm((f) => ({ ...f, 'read-access': e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary">
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary">
|
||||
<option value="yes">Yes</option>
|
||||
<option value="no">No</option>
|
||||
</select>
|
||||
@@ -321,7 +321,7 @@ export function SnmpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-text-secondary">Write Access</Label>
|
||||
<select value={communityForm['write-access'] || 'no'} onChange={(e) => setCommunityForm((f) => ({ ...f, 'write-access': e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary">
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary">
|
||||
<option value="yes">Yes</option>
|
||||
<option value="no">No</option>
|
||||
</select>
|
||||
@@ -329,7 +329,7 @@ export function SnmpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-text-secondary">Security</Label>
|
||||
<select value={communityForm['security'] || 'none'} onChange={(e) => setCommunityForm((f) => ({ ...f, security: e.target.value }))}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary">
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary">
|
||||
<option value="none">None</option>
|
||||
<option value="authorized">Authorized</option>
|
||||
<option value="private">Private</option>
|
||||
|
||||
@@ -134,7 +134,7 @@ export function SwitchPortManager({ tenantId, deviceId, active }: ConfigPanelPro
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from({ length: 8 }).map((_, i) => (
|
||||
<Skeleton key={i} className="h-20 w-16 rounded-md" />
|
||||
@@ -146,7 +146,7 @@ export function SwitchPortManager({ tenantId, deviceId, active }: ConfigPanelPro
|
||||
|
||||
if (etherPorts.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-text-secondary text-sm">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-text-secondary text-sm">
|
||||
<Network className="h-8 w-8 mx-auto mb-2 opacity-40" />
|
||||
No ethernet ports detected on this device.
|
||||
</div>
|
||||
@@ -156,7 +156,7 @@ export function SwitchPortManager({ tenantId, deviceId, active }: ConfigPanelPro
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Port grid */}
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<h3 className="text-sm font-medium text-text-primary mb-3">Switch Ports</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{etherPorts.map((port) => {
|
||||
@@ -177,7 +177,7 @@ export function SwitchPortManager({ tenantId, deviceId, active }: ConfigPanelPro
|
||||
</div>
|
||||
|
||||
{/* VLAN Legend */}
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<h3 className="text-sm font-medium text-text-primary mb-2">VLAN Legend</h3>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<LegendItem color={UNASSIGNED_COLOR} label="Unassigned" />
|
||||
|
||||
@@ -104,8 +104,8 @@ export function SystemPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
|
||||
activeTab === tab.key
|
||||
? 'bg-surface text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-surface/50',
|
||||
? 'bg-panel text-text-primary shadow-sm'
|
||||
: 'text-text-secondary hover:text-text-primary hover:bg-panel/50',
|
||||
)}
|
||||
>
|
||||
{tab.icon}
|
||||
@@ -179,7 +179,7 @@ function IdentityTab({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">System Identity</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={handleEdit}>
|
||||
@@ -283,7 +283,7 @@ function ClockTab({
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Clock info */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">Clock Settings</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={handleEditClock}>
|
||||
@@ -300,7 +300,7 @@ function ClockTab({
|
||||
</div>
|
||||
|
||||
{/* NTP info */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">NTP Client</span>
|
||||
<Button size="sm" variant="outline" className="gap-1" onClick={handleEditNtp}>
|
||||
@@ -351,7 +351,7 @@ function ClockTab({
|
||||
<select
|
||||
value={ntpEnabled}
|
||||
onChange={(e) => setNtpEnabled(e.target.value)}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="yes">Yes</option>
|
||||
<option value="no">No</option>
|
||||
@@ -394,7 +394,7 @@ function ResourcesTab({ data }: { data: Record<string, string> }) {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50">
|
||||
<span className="text-sm font-medium text-text-secondary">System Resources</span>
|
||||
</div>
|
||||
|
||||
@@ -106,14 +106,14 @@ export function TorchTool({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-5">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-text-secondary">Interface</Label>
|
||||
<select
|
||||
value={iface}
|
||||
onChange={(e) => setIface(e.target.value)}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary font-mono"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary font-mono"
|
||||
>
|
||||
{ifaceNames.length > 0
|
||||
? ifaceNames.map((name) => (
|
||||
@@ -190,7 +190,7 @@ export function TorchTool({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
)}
|
||||
|
||||
{entries.length > 0 && (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50 flex items-center gap-2">
|
||||
<Flame className="h-4 w-4 text-accent" />
|
||||
<span className="text-sm font-medium text-text-secondary">
|
||||
@@ -229,7 +229,7 @@ export function TorchTool({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
)}
|
||||
|
||||
{entries.length === 0 && !torchMutation.isPending && !torchMutation.isIdle && (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 text-center text-sm text-text-muted">
|
||||
No traffic captured. Try a different interface or remove filters.
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -69,7 +69,7 @@ export function TracerouteTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-5">
|
||||
<div className="space-y-1 col-span-2">
|
||||
<Label className="text-xs text-text-secondary">Target IP / Hostname</Label>
|
||||
@@ -108,7 +108,7 @@ export function TracerouteTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
<select
|
||||
value={protocol}
|
||||
onChange={(e) => setProtocol(e.target.value)}
|
||||
className="h-8 w-full rounded-md border border-border bg-surface px-3 text-sm text-text-primary"
|
||||
className="h-8 w-full rounded-md border border-border bg-panel px-3 text-sm text-text-primary"
|
||||
>
|
||||
<option value="icmp">ICMP</option>
|
||||
<option value="udp">UDP</option>
|
||||
@@ -137,7 +137,7 @@ export function TracerouteTool({ tenantId, deviceId }: ConfigPanelProps) {
|
||||
)}
|
||||
|
||||
{hops.length > 0 && (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50 flex items-center gap-2">
|
||||
<Route className="h-4 w-4 text-accent" />
|
||||
<span className="text-sm font-medium text-text-secondary">
|
||||
|
||||
@@ -148,7 +148,7 @@ export function UsersPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
|
||||
{/* Groups overview */}
|
||||
{groups.length > 0 && (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="px-4 py-2 border-b border-border/50 flex items-center gap-2">
|
||||
<Shield className="h-4 w-4 text-text-muted" />
|
||||
<span className="text-sm font-medium text-text-secondary">User Groups</span>
|
||||
@@ -266,7 +266,7 @@ function UsersTable({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-border/50">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-text-secondary">
|
||||
<Users className="h-4 w-4" />
|
||||
|
||||
@@ -69,7 +69,7 @@ export function AlertSummary({
|
||||
}))
|
||||
|
||||
return (
|
||||
<Card className="bg-surface border-border">
|
||||
<Card className="bg-panel border-border">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-text-secondary">
|
||||
Alert Summary
|
||||
|
||||
@@ -45,7 +45,7 @@ function BwTooltip({ active, payload }: CustomTooltipProps) {
|
||||
if (!active || !payload || payload.length === 0) return null
|
||||
const item = payload[0]
|
||||
return (
|
||||
<div className="rounded-md border border-border bg-surface px-3 py-2 text-xs">
|
||||
<div className="rounded-md border border-border bg-panel px-3 py-2 text-xs">
|
||||
<p className="font-medium text-text-primary">{item.payload.hostname}</p>
|
||||
<p className="text-text-secondary">{formatBw(item.value)}</p>
|
||||
</div>
|
||||
@@ -56,7 +56,7 @@ export function BandwidthChart({ devices }: BandwidthChartProps) {
|
||||
const chartHeight = Math.max(200, devices.length * 36)
|
||||
|
||||
return (
|
||||
<Card className="bg-surface border-border">
|
||||
<Card className="bg-panel border-border">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-text-secondary">
|
||||
Top Bandwidth Consumers
|
||||
|
||||
@@ -115,7 +115,7 @@ export function EventsTimeline({ tenantId, isSuperAdmin }: EventsTimelineProps)
|
||||
})
|
||||
|
||||
return (
|
||||
<Card className="bg-surface border-border">
|
||||
<Card className="bg-panel border-border">
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<CardTitle className="text-sm font-medium text-text-secondary">
|
||||
@@ -159,7 +159,7 @@ export function EventsTimeline({ tenantId, isSuperAdmin }: EventsTimelineProps)
|
||||
className="relative flex items-start gap-3 pb-3 pl-4 last:pb-0"
|
||||
>
|
||||
{/* Icon positioned over the timeline line */}
|
||||
<div className="absolute -left-[9px] top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-surface">
|
||||
<div className="absolute -left-[9px] top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-panel">
|
||||
<EventIcon event={event} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ export function HealthScore({
|
||||
const dashOffset = CIRCUMFERENCE * (1 - progress)
|
||||
|
||||
return (
|
||||
<Card className="bg-surface border-border">
|
||||
<Card className="bg-panel border-border">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-text-secondary">
|
||||
Network Health
|
||||
|
||||
@@ -72,7 +72,7 @@ export function QuickActions({ tenantId, isSuperAdmin }: QuickActionsProps) {
|
||||
const actions = getActions(tenantId, isSuperAdmin)
|
||||
|
||||
return (
|
||||
<Card className="bg-surface border-border">
|
||||
<Card className="bg-panel border-border">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-text-secondary">
|
||||
Quick Actions
|
||||
|
||||
@@ -25,7 +25,7 @@ export function WirelessIssues({ tenantId }: WirelessIssuesProps) {
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-5">
|
||||
<div className="rounded-lg border border-border bg-panel p-5">
|
||||
<h3 className="text-sm font-semibold text-text-primary mb-4 flex items-center gap-2">
|
||||
<Wifi className="h-4 w-4 text-text-muted" />
|
||||
APs Needing Attention
|
||||
|
||||
@@ -50,7 +50,7 @@ function StatCard({
|
||||
color: string
|
||||
}) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className={cn('text-2xl font-bold', color)}>{value}</div>
|
||||
<div className="text-xs text-text-muted mt-1">{label}</div>
|
||||
</div>
|
||||
@@ -184,7 +184,7 @@ function UpgradeDialog({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="rounded border border-border bg-surface p-3 text-xs text-text-secondary">
|
||||
<div className="rounded border border-border bg-panel p-3 text-xs text-text-secondary">
|
||||
A mandatory config backup will be taken before upgrading each device.
|
||||
</div>
|
||||
|
||||
@@ -254,10 +254,10 @@ function VersionGroupCard({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<button
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
className="flex items-center gap-3 px-4 py-3 w-full text-left hover:bg-surface transition-colors"
|
||||
className="flex items-center gap-3 px-4 py-3 w-full text-left hover:bg-panel transition-colors"
|
||||
>
|
||||
{expanded ? (
|
||||
<ChevronDown className="h-4 w-4 text-text-muted" />
|
||||
|
||||
@@ -256,7 +256,7 @@ function MassUpgradeProgress({
|
||||
)}
|
||||
|
||||
{/* Device list */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden max-h-48 overflow-y-auto">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden max-h-48 overflow-y-auto">
|
||||
{rollout.jobs.map((job) => {
|
||||
const config = STATUS_CONFIG[job.status] ?? STATUS_CONFIG.pending
|
||||
const Icon = config.icon
|
||||
|
||||
@@ -231,7 +231,7 @@ export function AdoptionWizard({ tenantId }: AdoptionWizardProps) {
|
||||
|
||||
{/* Step 3: Configure Credentials */}
|
||||
{step === 3 && (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Configure Credentials</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">
|
||||
@@ -253,7 +253,7 @@ export function AdoptionWizard({ tenantId }: AdoptionWizardProps) {
|
||||
className={cn(
|
||||
'flex-1 px-3 py-1.5 rounded text-xs font-medium transition-colors',
|
||||
credMode === opt.value
|
||||
? 'bg-surface text-text-primary shadow-sm'
|
||||
? 'bg-panel text-text-primary shadow-sm'
|
||||
: 'text-text-muted hover:text-text-secondary',
|
||||
)}
|
||||
>
|
||||
@@ -389,7 +389,7 @@ export function AdoptionWizard({ tenantId }: AdoptionWizardProps) {
|
||||
|
||||
{/* Step 4: Assign Groups & Tags */}
|
||||
{step === 4 && (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Assign Groups & Tags</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">
|
||||
@@ -584,7 +584,7 @@ function SubnetStep({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Enter Subnet</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">
|
||||
@@ -682,7 +682,7 @@ function ScanResultsStep({
|
||||
).length
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Scan Results</h3>
|
||||
@@ -703,7 +703,7 @@ function ScanResultsStep({
|
||||
<div className="rounded-md border border-border/50 overflow-hidden max-h-72 overflow-y-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="sticky top-0">
|
||||
<tr className="border-b border-border bg-surface">
|
||||
<tr className="border-b border-border bg-panel">
|
||||
<th className="px-3 py-2 w-8">
|
||||
<Checkbox
|
||||
checked={allNewSelected}
|
||||
@@ -959,7 +959,7 @@ function ImportVerifyStep({
|
||||
])
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Import & Verify</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">
|
||||
@@ -1013,7 +1013,7 @@ function ImportVerifyStep({
|
||||
<div className="rounded-md border border-border/50 overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-surface">
|
||||
<tr className="border-b border-border bg-panel">
|
||||
<th className="text-left px-3 py-2 text-xs font-medium text-text-muted">
|
||||
Device
|
||||
</th>
|
||||
|
||||
@@ -213,7 +213,7 @@ export function FleetDashboard() {
|
||||
</span>
|
||||
)}
|
||||
{/* Refresh interval selector */}
|
||||
<div className="flex items-center rounded-md border border-border bg-surface">
|
||||
<div className="flex items-center rounded-md border border-border bg-panel">
|
||||
{REFRESH_OPTIONS.map((opt) => (
|
||||
<button
|
||||
key={opt.label}
|
||||
|
||||
@@ -92,7 +92,7 @@ function SortHeader({ column, label, currentSort, currentDir, onSort, className
|
||||
function DeviceCard({ device, tenantId }: { device: DeviceResponse; tenantId: string }) {
|
||||
return (
|
||||
<div
|
||||
className="w-full text-left rounded-lg border border-border bg-surface p-3 hover:bg-elevated/50 transition-colors min-h-[44px]"
|
||||
className="w-full text-left rounded-lg border border-border bg-panel p-3 hover:bg-elevated/50 transition-colors min-h-[44px]"
|
||||
data-testid={`device-card-${device.hostname}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
|
||||
@@ -104,7 +104,7 @@ export function ScanResultsList({ tenantId, results, onDone }: Props) {
|
||||
<div className="rounded-lg border border-border overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-surface">
|
||||
<tr className="border-b border-border bg-panel">
|
||||
<th className="px-3 py-2 w-8">
|
||||
<Checkbox
|
||||
checked={allSelected}
|
||||
@@ -121,7 +121,7 @@ export function ScanResultsList({ tenantId, results, onDone }: Props) {
|
||||
{results.discovered.map((device) => (
|
||||
<tr
|
||||
key={device.ip_address}
|
||||
className="border-b border-border/50 hover:bg-surface cursor-pointer"
|
||||
className="border-b border-border/50 hover:bg-panel cursor-pointer"
|
||||
onClick={() => toggleSelect(device.ip_address)}
|
||||
>
|
||||
<td className="px-3 py-2" onClick={(e) => e.stopPropagation()}>
|
||||
@@ -154,7 +154,7 @@ export function ScanResultsList({ tenantId, results, onDone }: Props) {
|
||||
|
||||
{/* Credentials */}
|
||||
{selected.size > 0 && (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-sm font-medium">
|
||||
Credentials for {selected.size} selected device{selected.size !== 1 ? 's' : ''}
|
||||
|
||||
@@ -321,7 +321,7 @@ export function Sidebar() {
|
||||
className={cn(
|
||||
'flex items-center gap-2.5 px-3 py-2 mx-1 rounded-md text-sm transition-colors min-h-[44px]',
|
||||
active
|
||||
? 'bg-[hsl(var(--accent-muted))] text-accent rounded-md'
|
||||
? 'bg-[hsl(var(--accent-soft))] text-accent rounded-md'
|
||||
: 'text-text-muted hover:text-text-primary hover:bg-elevated/50 rounded-md',
|
||||
showCollapsed && 'justify-center px-0',
|
||||
)}
|
||||
|
||||
@@ -103,7 +103,7 @@ export function MaintenanceList({ tenantId }: MaintenanceListProps) {
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-20 rounded-lg border border-border bg-surface animate-pulse"
|
||||
className="h-20 rounded-lg border border-border bg-panel animate-pulse"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -266,7 +266,7 @@ function WindowCard({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`rounded-lg border border-border bg-surface p-3 ${
|
||||
className={`rounded-lg border border-border bg-panel p-3 ${
|
||||
isPast ? 'opacity-60' : ''
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -88,7 +88,7 @@ export function MapPage() {
|
||||
<select
|
||||
value={selectedTenant}
|
||||
onChange={(e) => setSelectedTenant(e.target.value)}
|
||||
className="text-xs bg-elevated/50 border border-border text-text-primary rounded px-2 py-1 focus:outline-none focus:ring-1 focus:ring-border-bright"
|
||||
className="text-xs bg-elevated/50 border border-border text-text-primary rounded px-2 py-1 focus:outline-none focus:ring-1 focus:ring-border-default"
|
||||
>
|
||||
<option value="all">All Organizations</option>
|
||||
{tenants.map((t) => (
|
||||
|
||||
@@ -33,7 +33,7 @@ function CustomTooltip({
|
||||
}: { active?: boolean; payload?: Array<{ value?: number }>; label?: string; unit: string }) {
|
||||
if (!active || !payload?.length) return null
|
||||
return (
|
||||
<div className="rounded border border-border bg-surface px-2 py-1.5 text-xs text-text-primary">
|
||||
<div className="rounded border border-border bg-panel px-2 py-1.5 text-xs text-text-primary">
|
||||
<div className="mb-1 text-text-muted">{label}</div>
|
||||
<div>
|
||||
{(payload[0].value ?? 0).toFixed(1)}
|
||||
|
||||
@@ -43,16 +43,16 @@ export function HealthTab({ tenantId, deviceId, active = true }: HealthTabProps)
|
||||
{isLoading ? (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{[0, 1, 2, 3].map((i) => (
|
||||
<div key={i} className="rounded-lg border border-border bg-surface p-4 h-44 animate-pulse" />
|
||||
<div key={i} className="rounded-lg border border-border bg-panel p-4 h-44 animate-pulse" />
|
||||
))}
|
||||
</div>
|
||||
) : !data || data.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-sm text-text-muted">
|
||||
No health metrics data available for the selected time range.
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<HealthChart
|
||||
data={data}
|
||||
metric="avg_cpu"
|
||||
@@ -62,7 +62,7 @@ export function HealthTab({ tenantId, deviceId, active = true }: HealthTabProps)
|
||||
maxY={100}
|
||||
/>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<HealthChart
|
||||
data={data}
|
||||
metric="avg_mem_pct"
|
||||
@@ -72,7 +72,7 @@ export function HealthTab({ tenantId, deviceId, active = true }: HealthTabProps)
|
||||
maxY={100}
|
||||
/>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<HealthChart
|
||||
data={data}
|
||||
metric="avg_disk_pct"
|
||||
@@ -82,7 +82,7 @@ export function HealthTab({ tenantId, deviceId, active = true }: HealthTabProps)
|
||||
maxY={100}
|
||||
/>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<HealthChart
|
||||
data={data}
|
||||
metric="avg_temp"
|
||||
|
||||
@@ -89,11 +89,11 @@ export function InterfacesTab({ tenantId, deviceId, active = true }: InterfacesT
|
||||
{isLoading ? (
|
||||
<div className="space-y-4">
|
||||
{[0, 1, 2].map((i) => (
|
||||
<div key={i} className="rounded-lg border border-border bg-surface p-4 h-56 animate-pulse" />
|
||||
<div key={i} className="rounded-lg border border-border bg-panel p-4 h-56 animate-pulse" />
|
||||
))}
|
||||
</div>
|
||||
) : !trafficData || trafficData.length === 0 ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-sm text-text-muted">
|
||||
{interfaces && interfaces.length === 0
|
||||
? 'No interfaces discovered for this device.'
|
||||
: 'No traffic data available for the selected time range.'}
|
||||
@@ -103,7 +103,7 @@ export function InterfacesTab({ tenantId, deviceId, active = true }: InterfacesT
|
||||
{interfaceNames.map((ifaceName) => {
|
||||
const ifaceData = byInterface.get(ifaceName) ?? []
|
||||
return (
|
||||
<div key={ifaceName} className="rounded-lg border border-border bg-surface p-4">
|
||||
<div key={ifaceName} className="rounded-lg border border-border bg-panel p-4">
|
||||
<TrafficChart data={ifaceData} interfaceName={ifaceName} />
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -101,7 +101,7 @@ export function TimeRangeSelector({
|
||||
className={cn(
|
||||
'px-2.5 py-1 text-xs rounded border transition-colors',
|
||||
value === preset
|
||||
? 'bg-elevated border-border-bright text-text-primary'
|
||||
? 'bg-elevated border-border-default text-text-primary'
|
||||
: 'bg-transparent border-border/50 text-text-primary/40 hover:text-text-primary/60 hover:border-border',
|
||||
)}
|
||||
>
|
||||
@@ -113,7 +113,7 @@ export function TimeRangeSelector({
|
||||
className={cn(
|
||||
'px-2.5 py-1 text-xs rounded border transition-colors',
|
||||
value === 'custom'
|
||||
? 'bg-elevated border-border-bright text-text-primary'
|
||||
? 'bg-elevated border-border-default text-text-primary'
|
||||
: 'bg-transparent border-border/50 text-text-primary/40 hover:text-text-primary/60 hover:border-border',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -36,7 +36,7 @@ function formatBucket(bucket: string, useDate: boolean): string {
|
||||
function CustomTooltip({ active, payload, label }: { active?: boolean; payload?: Array<{ value?: number; dataKey?: string; name?: string; color?: string }>; label?: string }) {
|
||||
if (!active || !payload?.length) return null
|
||||
return (
|
||||
<div className="rounded border border-border bg-surface px-2 py-1.5 text-xs text-text-primary">
|
||||
<div className="rounded border border-border bg-panel px-2 py-1.5 text-xs text-text-primary">
|
||||
<div className="mb-1 text-text-muted">{label}</div>
|
||||
{payload.map((entry) => (
|
||||
<div key={entry.dataKey} className="flex items-center gap-2">
|
||||
|
||||
@@ -57,7 +57,7 @@ function WirelessInterfaceCard({ section }: { section: WirelessInterfaceSection
|
||||
const { interfaceName, latest, history } = section
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-3">
|
||||
{/* Interface name header */}
|
||||
<h3 className="text-sm font-medium text-text-primary">{interfaceName}</h3>
|
||||
|
||||
@@ -161,11 +161,11 @@ export function WirelessTab({ tenantId, deviceId, active = true }: WirelessTabPr
|
||||
{isLoading ? (
|
||||
<div className="space-y-4">
|
||||
{[0, 1].map((i) => (
|
||||
<div key={i} className="rounded-lg border border-border bg-surface p-4 h-48 animate-pulse" />
|
||||
<div key={i} className="rounded-lg border border-border bg-panel p-4 h-48 animate-pulse" />
|
||||
))}
|
||||
</div>
|
||||
) : hasNoWireless ? (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center text-sm text-text-muted">
|
||||
No wireless interfaces detected on this device.
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -246,7 +246,7 @@ export function ClientsTab({ tenantId, deviceId, active }: ClientsTabProps) {
|
||||
<div className="overflow-x-auto rounded-lg border border-border">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-surface text-text-secondary text-left">
|
||||
<tr className="bg-panel text-text-secondary text-left">
|
||||
{/* Expand chevron column */}
|
||||
<th className="w-8 px-3 py-2.5" />
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ export function InterfaceGauges({ tenantId, deviceId, active }: InterfaceGaugesP
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{[0, 1, 2].map((i) => (
|
||||
<div key={i} className="rounded-lg border border-border bg-surface p-3">
|
||||
<div key={i} className="rounded-lg border border-border bg-panel p-3">
|
||||
<Skeleton className="h-4 w-24 mb-2" />
|
||||
<Skeleton className="h-3 w-full mb-1" />
|
||||
<Skeleton className="h-3 w-full" />
|
||||
@@ -115,7 +115,7 @@ export function InterfaceGauges({ tenantId, deviceId, active }: InterfaceGaugesP
|
||||
|
||||
if (!interfaces || interfaces.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 text-center text-sm text-text-muted">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 text-center text-sm text-text-muted">
|
||||
No interface data available.
|
||||
</div>
|
||||
)
|
||||
@@ -149,7 +149,7 @@ export function InterfaceGauges({ tenantId, deviceId, active }: InterfaceGaugesP
|
||||
const values = latestByIface.get(ifaceName) ?? { rx: 0, tx: 0 }
|
||||
|
||||
return (
|
||||
<div key={ifaceName} className="rounded-lg border border-border bg-surface p-3">
|
||||
<div key={ifaceName} className="rounded-lg border border-border bg-panel p-3">
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<span className="text-sm font-medium text-text-primary">{ifaceName}</span>
|
||||
<span className="text-[10px] text-text-muted">
|
||||
|
||||
@@ -187,7 +187,7 @@ export function LogsTab({ tenantId, deviceId, active }: LogsTabProps) {
|
||||
</div>
|
||||
|
||||
{/* Log table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{isLoading ? (
|
||||
<TableSkeleton />
|
||||
) : error ? (
|
||||
|
||||
@@ -99,7 +99,7 @@ function DeviceNode({ data }: NodeProps<DeviceNodeData>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-lg border bg-surface px-3 py-2 min-w-[180px]',
|
||||
'rounded-lg border bg-panel px-3 py-2 min-w-[180px]',
|
||||
'transition-colors',
|
||||
isOnline ? 'border-border' : 'border-error/30',
|
||||
)}
|
||||
@@ -339,7 +339,7 @@ export function TopologyMap({ tenantId }: TopologyMapProps) {
|
||||
>
|
||||
<Background color="hsl(var(--muted))" gap={20} size={1} />
|
||||
<Controls
|
||||
className="!bg-surface !border-border [&>button]:!bg-surface [&>button]:!border-border [&>button]:!text-text-secondary [&>button:hover]:!bg-elevated"
|
||||
className="!bg-panel !border-border [&>button]:!bg-panel [&>button]:!border-border [&>button]:!text-text-secondary [&>button:hover]:!bg-elevated"
|
||||
/>
|
||||
<MiniMap
|
||||
nodeColor={(node) => {
|
||||
@@ -349,7 +349,7 @@ export function TopologyMap({ tenantId }: TopologyMapProps) {
|
||||
: 'hsl(var(--error))'
|
||||
}}
|
||||
maskColor="hsl(var(--background) / 0.7)"
|
||||
className="!bg-surface !border-border"
|
||||
className="!bg-panel !border-border"
|
||||
/>
|
||||
</ReactFlow>
|
||||
|
||||
@@ -357,7 +357,7 @@ export function TopologyMap({ tenantId }: TopologyMapProps) {
|
||||
{tooltip && <NodeTooltip data={tooltip} onClose={() => setTooltip(null)} />}
|
||||
|
||||
{/* Legend */}
|
||||
<div className="absolute bottom-4 left-4 rounded-lg border border-border bg-surface/90 backdrop-blur-sm px-3 py-2 text-xs text-text-muted">
|
||||
<div className="absolute bottom-4 left-4 rounded-lg border border-border bg-panel/90 backdrop-blur-sm px-3 py-2 text-xs text-text-muted">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<span className="h-2 w-2 rounded-full bg-success" /> Online
|
||||
|
||||
@@ -112,7 +112,7 @@ export function VpnTab({ tenantId, deviceId, active }: VpnTabProps) {
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="mt-4 rounded-lg border border-border bg-surface p-6 text-center text-sm text-error">
|
||||
<div className="mt-4 rounded-lg border border-border bg-panel p-6 text-center text-sm text-error">
|
||||
Failed to load VPN tunnels. The device may not support this feature.
|
||||
</div>
|
||||
)
|
||||
@@ -120,7 +120,7 @@ export function VpnTab({ tenantId, deviceId, active }: VpnTabProps) {
|
||||
|
||||
if (!data || data.tunnels.length === 0) {
|
||||
return (
|
||||
<div className="mt-4 rounded-lg border border-border bg-surface p-8 text-center">
|
||||
<div className="mt-4 rounded-lg border border-border bg-panel p-8 text-center">
|
||||
<Shield className="w-10 h-10 mx-auto mb-3 text-text-muted opacity-40" />
|
||||
<p className="text-sm font-medium text-text-primary mb-1">
|
||||
No active VPN tunnels
|
||||
@@ -134,7 +134,7 @@ export function VpnTab({ tenantId, deviceId, active }: VpnTabProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-4 rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="mt-4 rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-left">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/50">
|
||||
|
||||
@@ -251,7 +251,7 @@ export function BulkCommandWizard({ tenantId }: BulkCommandWizardProps) {
|
||||
|
||||
{/* Step 2: Enter Command */}
|
||||
{step === 2 && (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Enter RouterOS Command</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">
|
||||
@@ -294,7 +294,7 @@ export function BulkCommandWizard({ tenantId }: BulkCommandWizardProps) {
|
||||
|
||||
{/* Step 3: Review & Execute */}
|
||||
{step === 3 && (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Review & Execute</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">
|
||||
@@ -336,7 +336,7 @@ export function BulkCommandWizard({ tenantId }: BulkCommandWizardProps) {
|
||||
<div className="rounded-lg border border-border overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-surface">
|
||||
<tr className="border-b border-border bg-panel">
|
||||
<th className="text-left px-3 py-2 text-xs font-medium text-text-muted">
|
||||
Device
|
||||
</th>
|
||||
@@ -615,7 +615,7 @@ function DeviceSelectionStep({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-6 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">Select Target Devices</h3>
|
||||
<p className="text-xs text-text-muted mt-0.5">
|
||||
@@ -641,7 +641,7 @@ function DeviceSelectionStep({
|
||||
className={cn(
|
||||
'flex-1 px-3 py-1.5 rounded text-xs font-medium transition-colors',
|
||||
mode === opt.value
|
||||
? 'bg-surface text-text-primary shadow-sm'
|
||||
? 'bg-panel text-text-primary shadow-sm'
|
||||
: 'text-text-muted hover:text-text-secondary',
|
||||
)}
|
||||
>
|
||||
@@ -699,7 +699,7 @@ function DeviceSelectionStep({
|
||||
<div className="rounded-md border border-border/50 overflow-hidden max-h-72 overflow-y-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="sticky top-0">
|
||||
<tr className="border-b border-border bg-surface">
|
||||
<tr className="border-b border-border bg-panel">
|
||||
<th className="px-3 py-2 w-8">
|
||||
<Checkbox
|
||||
checked={
|
||||
|
||||
@@ -122,8 +122,8 @@ export function ReportsPage({ tenantId }: ReportsPageProps) {
|
||||
className={cn(
|
||||
'flex items-start gap-3 p-4 rounded-lg border text-left transition-all',
|
||||
isSelected
|
||||
? 'border-accent bg-accent-muted/30 ring-1 ring-accent'
|
||||
: 'border-border bg-surface hover:border-text-muted',
|
||||
? 'border-accent bg-accent-soft/30 ring-1 ring-accent'
|
||||
: 'border-border bg-panel hover:border-text-muted',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -172,7 +172,7 @@ export function ReportsPage({ tenantId }: ReportsPageProps) {
|
||||
type="date"
|
||||
value={dateFrom}
|
||||
onChange={(e) => setDateFrom(e.target.value)}
|
||||
className="w-full h-9 rounded-md border border-border bg-surface px-3 text-sm text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="w-full h-9 rounded-md border border-border bg-panel px-3 text-sm text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -187,7 +187,7 @@ export function ReportsPage({ tenantId }: ReportsPageProps) {
|
||||
type="date"
|
||||
value={dateTo}
|
||||
onChange={(e) => setDateTo(e.target.value)}
|
||||
className="w-full h-9 rounded-md border border-border bg-surface px-3 text-sm text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="w-full h-9 rounded-md border border-border bg-panel px-3 text-sm text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -166,7 +166,7 @@ export function ApiKeysPage({ tenantId }: ApiKeysPageProps) {
|
||||
action={{ label: 'Create API Key', onClick: () => setShowCreateDialog(true) }}
|
||||
/>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/30">
|
||||
@@ -264,7 +264,7 @@ export function ApiKeysPage({ tenantId }: ApiKeysPageProps) {
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full rounded-md border border-border-bright bg-elevated/50 px-3 py-2 text-sm focus:border-accent focus:outline-none"
|
||||
className="w-full rounded-md border border-border-default bg-elevated/50 px-3 py-2 text-sm focus:border-accent focus:outline-none"
|
||||
placeholder="e.g. Monitoring Integration"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
@@ -300,7 +300,7 @@ export function ApiKeysPage({ tenantId }: ApiKeysPageProps) {
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
className="w-full rounded-md border border-border-bright bg-elevated/50 px-3 py-2 text-sm focus:border-accent focus:outline-none"
|
||||
className="w-full rounded-md border border-border-default bg-elevated/50 px-3 py-2 text-sm focus:border-accent focus:outline-none"
|
||||
value={expiresAt}
|
||||
onChange={(e) => setExpiresAt(e.target.value)}
|
||||
min={new Date().toISOString().split('T')[0]}
|
||||
|
||||
@@ -78,7 +78,7 @@ export function SettingsPage() {
|
||||
</div>
|
||||
|
||||
{/* Account section */}
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-1">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-1">
|
||||
<SectionHeader icon={User} title="Account" />
|
||||
<InfoRow label="Email" value={user?.email} />
|
||||
<InfoRow label="Role" value={
|
||||
@@ -94,13 +94,13 @@ export function SettingsPage() {
|
||||
</div>
|
||||
|
||||
{/* Password & Security section */}
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-1">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-1">
|
||||
<SectionHeader icon={Lock} title="Password & Security" />
|
||||
<ChangePasswordForm />
|
||||
</div>
|
||||
|
||||
{/* Permissions section */}
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-1">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-1">
|
||||
<SectionHeader icon={Shield} title="Permissions" />
|
||||
<InfoRow label="Read devices" value="Yes" />
|
||||
<InfoRow
|
||||
@@ -118,7 +118,7 @@ export function SettingsPage() {
|
||||
</div>
|
||||
|
||||
{/* System info section */}
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-1">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-1">
|
||||
<SectionHeader icon={Info} title="System" />
|
||||
<InfoRow label="API" value={
|
||||
<a
|
||||
@@ -135,7 +135,7 @@ export function SettingsPage() {
|
||||
|
||||
{/* Quick links */}
|
||||
{isTenantAdmin(user) && (
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-1">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-1">
|
||||
<SectionHeader icon={Key} title="Integrations" />
|
||||
<Link
|
||||
to="/settings/api-keys"
|
||||
@@ -152,7 +152,7 @@ export function SettingsPage() {
|
||||
|
||||
{/* Maintenance — super_admin only */}
|
||||
{isSuperAdmin(user) && (
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-1">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-1">
|
||||
<SectionHeader icon={Monitor} title="Maintenance" />
|
||||
<div className="flex items-center justify-between py-2">
|
||||
<div>
|
||||
@@ -182,7 +182,7 @@ export function SettingsPage() {
|
||||
{isSuperAdmin(user) && <SMTPSettingsSection />}
|
||||
|
||||
{/* Data & Privacy section */}
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-3">
|
||||
<SectionHeader icon={Shield} title="Data & Privacy" />
|
||||
|
||||
{/* Export Data */}
|
||||
@@ -377,7 +377,7 @@ function SMTPSettingsSection() {
|
||||
if (isLoading) return null
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface px-4 py-3 space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel px-4 py-3 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<SectionHeader icon={Mail} title="System Email (SMTP)" />
|
||||
<span className={`text-[10px] font-medium px-2 py-0.5 rounded-full ${
|
||||
|
||||
@@ -484,7 +484,7 @@ export function SetupWizard() {
|
||||
<StepIndicator currentStep={step} />
|
||||
|
||||
{/* Card */}
|
||||
<div className="bg-surface border border-border rounded-lg p-8">
|
||||
<div className="bg-panel border border-border rounded-lg p-8">
|
||||
{step === 1 && (
|
||||
<CreateTenantStep
|
||||
onComplete={(tenant) => {
|
||||
|
||||
@@ -19,7 +19,7 @@ export function SimpleFormSection({
|
||||
children,
|
||||
}: SimpleFormSectionProps) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4 space-y-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4 space-y-4">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Icon className="h-4.5 w-4.5 text-accent flex-shrink-0" />
|
||||
<div>
|
||||
|
||||
@@ -16,7 +16,7 @@ export function SimpleStatusBanner({ items, isLoading }: SimpleStatusBannerProps
|
||||
<div key={i} className="flex flex-col">
|
||||
<span className="text-xs text-text-muted">{item.label}</span>
|
||||
{isLoading ? (
|
||||
<div className="h-5 w-24 mt-0.5 rounded bg-elevated animate-shimmer" />
|
||||
<div className="h-5 w-24 mt-0.5 rounded bg-elevated animate-pulse" />
|
||||
) : (
|
||||
<span className="text-sm font-medium text-text-primary">
|
||||
{item.value || '\u2014'}
|
||||
|
||||
@@ -180,7 +180,7 @@ export function LanDhcpPanel({ tenantId, deviceId, active }: ConfigPanelProps) {
|
||||
{activeLeases.length === 0 ? (
|
||||
<p className="text-xs text-text-muted">No active DHCP leases</p>
|
||||
) : (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-elevated/30">
|
||||
|
||||
@@ -126,7 +126,7 @@ export function WifiSimplePanel({ tenantId, deviceId, active, routerosVersion }:
|
||||
// No wireless hardware
|
||||
if (wireless.entries.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-12 text-center">
|
||||
<div className="rounded-lg border border-border bg-panel p-12 text-center">
|
||||
<Wifi className="h-8 w-8 text-text-muted/50 mx-auto mb-3" />
|
||||
<p className="text-sm font-medium text-text-secondary">
|
||||
This device does not have wireless hardware
|
||||
|
||||
@@ -59,7 +59,7 @@ export function SiteHealthGrid({ tenantId, siteId }: SiteHealthGridProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-3">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<div key={i} className="rounded-lg border border-border bg-surface p-4 space-y-3 animate-pulse">
|
||||
<div key={i} className="rounded-lg border border-border bg-panel p-4 space-y-3 animate-pulse">
|
||||
<div className="h-4 w-24 bg-elevated rounded" />
|
||||
<div className="h-1.5 w-full bg-elevated rounded-full" />
|
||||
<div className="h-1.5 w-full bg-elevated rounded-full" />
|
||||
@@ -74,7 +74,7 @@ export function SiteHealthGrid({ tenantId, siteId }: SiteHealthGridProps) {
|
||||
|
||||
if (devices.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center">
|
||||
<p className="text-sm text-text-muted">
|
||||
No devices assigned to this site. Assign devices from the fleet page.
|
||||
</p>
|
||||
@@ -103,7 +103,7 @@ export function SiteHealthGrid({ tenantId, siteId }: SiteHealthGridProps) {
|
||||
to="/tenants/$tenantId/devices/$deviceId"
|
||||
params={{ tenantId, deviceId: device.id }}
|
||||
className={cn(
|
||||
'rounded-lg border bg-surface p-4 space-y-2 hover:bg-elevated/50 transition-colors block',
|
||||
'rounded-lg border bg-panel p-4 space-y-2 hover:bg-elevated/50 transition-colors block',
|
||||
borderColor(device.status),
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -153,7 +153,7 @@ export function SiteSectorView({ tenantId, siteId }: SiteSectorViewProps) {
|
||||
|
||||
if (sectors.length === 0 && unassignedDevices.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center space-y-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center space-y-3">
|
||||
<p className="text-sm text-text-muted">
|
||||
No sectors defined. Create sectors to organize APs by direction.
|
||||
</p>
|
||||
@@ -315,7 +315,7 @@ function SectorSection({
|
||||
const isUnassigned = !sector
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{/* Section header */}
|
||||
<button
|
||||
className="w-full flex items-center gap-2 px-4 py-3 hover:bg-elevated/50 transition-colors text-left"
|
||||
|
||||
@@ -101,7 +101,7 @@ export function PushProgressPanel({ tenantId, rolloutId, onClose }: PushProgress
|
||||
return (
|
||||
<div
|
||||
key={job.device_id}
|
||||
className="flex items-center gap-3 rounded-lg border border-border/50 bg-surface/50 px-3 py-2"
|
||||
className="flex items-center gap-3 rounded-lg border border-border/50 bg-panel/50 px-3 py-2"
|
||||
>
|
||||
<Icon
|
||||
className={cn(
|
||||
|
||||
@@ -164,7 +164,7 @@ export function TemplateEditor({ template, onSave, onCancel }: TemplateEditorPro
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="What does this template configure?"
|
||||
rows={2}
|
||||
className="w-full px-3 py-2 text-sm rounded-md bg-elevated/50 border border-border text-text-primary placeholder:text-text-muted resize-none focus:outline-none focus:ring-1 focus:ring-border-bright"
|
||||
className="w-full px-3 py-2 text-sm rounded-md bg-elevated/50 border border-border text-text-primary placeholder:text-text-muted resize-none focus:outline-none focus:ring-1 focus:ring-border-default"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -176,7 +176,7 @@ export function TemplateEditor({ template, onSave, onCancel }: TemplateEditorPro
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
placeholder={`# Example: Set system identity\n/system identity set name={{ device.hostname }}-{{ site_name }}\n\n# Add IP address\n/ip address add address={{ mgmt_ip }}/24 interface=ether1`}
|
||||
rows={16}
|
||||
className="w-full px-3 py-2 text-sm rounded-md bg-background border border-border text-success placeholder:text-text-muted font-mono resize-y focus:outline-none focus:ring-1 focus:ring-border-bright leading-relaxed"
|
||||
className="w-full px-3 py-2 text-sm rounded-md bg-background border border-border text-success placeholder:text-text-muted font-mono resize-y focus:outline-none focus:ring-1 focus:ring-border-default leading-relaxed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -247,7 +247,7 @@ export function TemplateEditor({ template, onSave, onCancel }: TemplateEditorPro
|
||||
<div className="rounded-lg border border-border overflow-hidden">
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
<tr className="bg-surface border-b border-border">
|
||||
<tr className="bg-panel border-b border-border">
|
||||
<th className="text-left px-3 py-1.5 text-[10px] uppercase tracking-wider font-semibold text-text-muted">Name</th>
|
||||
<th className="text-left px-3 py-1.5 text-[10px] uppercase tracking-wider font-semibold text-text-muted w-28">Type</th>
|
||||
<th className="text-left px-3 py-1.5 text-[10px] uppercase tracking-wider font-semibold text-text-muted">Default</th>
|
||||
|
||||
@@ -150,7 +150,7 @@ export function TemplatePushWizard({ open, onClose, tenantId, template }: Templa
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(o) => !o && handleClose()}>
|
||||
<DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto bg-surface border-border text-text-primary">
|
||||
<DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto bg-panel border-border text-text-primary">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-sm flex items-center gap-2">
|
||||
Push Template: {template.name}
|
||||
@@ -193,7 +193,7 @@ export function TemplatePushWizard({ open, onClose, tenantId, template }: Templa
|
||||
{devices?.map((device) => (
|
||||
<label
|
||||
key={device.id}
|
||||
className="flex items-center gap-3 px-3 py-2 hover:bg-surface cursor-pointer"
|
||||
className="flex items-center gap-3 px-3 py-2 hover:bg-panel cursor-pointer"
|
||||
>
|
||||
<Checkbox
|
||||
checked={selectedDeviceIds.has(device.id)}
|
||||
@@ -237,7 +237,7 @@ export function TemplatePushWizard({ open, onClose, tenantId, template }: Templa
|
||||
Provide values for template variables. Built-in device variables are auto-populated per device.
|
||||
</div>
|
||||
|
||||
<div className="text-[10px] text-text-muted bg-surface rounded px-3 py-2">
|
||||
<div className="text-[10px] text-text-muted bg-panel rounded px-3 py-2">
|
||||
Auto-populated: {'{{ device.hostname }}'}, {'{{ device.ip }}'}, {'{{ device.model }}'}
|
||||
</div>
|
||||
|
||||
@@ -321,7 +321,7 @@ export function TemplatePushWizard({ open, onClose, tenantId, template }: Templa
|
||||
'text-xs px-2 py-1 rounded transition-colors',
|
||||
previewDevice === d.id
|
||||
? 'bg-elevated text-text-primary'
|
||||
: 'bg-surface text-text-secondary hover:text-text-secondary',
|
||||
: 'bg-panel text-text-secondary hover:text-text-secondary',
|
||||
)}
|
||||
>
|
||||
{d.hostname}
|
||||
|
||||
@@ -229,7 +229,7 @@ export function TemplatesPage() {
|
||||
{templates.map((template) => (
|
||||
<div
|
||||
key={template.id}
|
||||
className="rounded-lg border border-border bg-surface/50 p-4 hover:bg-surface transition-colors"
|
||||
className="rounded-lg border border-border bg-panel/50 p-4 hover:bg-panel transition-colors"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -313,7 +313,7 @@ export function TemplatesPage() {
|
||||
open={!!deleteConfirmId}
|
||||
onOpenChange={(o) => !o && setDeleteConfirmId(null)}
|
||||
>
|
||||
<DialogContent className="max-w-sm bg-surface border-border text-text-primary">
|
||||
<DialogContent className="max-w-sm bg-panel border-border text-text-primary">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-sm">Delete Template</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -60,7 +60,7 @@ export function TenantList() {
|
||||
<div className="rounded-lg border border-border overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-surface">
|
||||
<tr className="border-b border-border bg-panel">
|
||||
<th className="text-left px-3 py-2 text-xs font-medium text-text-muted">Name</th>
|
||||
<th className="text-right px-3 py-2 text-xs font-medium text-text-muted">Users</th>
|
||||
<th className="text-right px-3 py-2 text-xs font-medium text-text-muted">Devices</th>
|
||||
@@ -87,7 +87,7 @@ export function TenantList() {
|
||||
tenants?.map((tenant) => (
|
||||
<tr
|
||||
key={tenant.id}
|
||||
className="border-b border-border/50 hover:bg-surface transition-colors"
|
||||
className="border-b border-border/50 hover:bg-panel transition-colors"
|
||||
>
|
||||
<td className="px-3 py-2.5">
|
||||
<Link
|
||||
|
||||
@@ -195,7 +195,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
label="Unique Devices"
|
||||
value={stats.unique_devices.toLocaleString()}
|
||||
/>
|
||||
<div className="rounded-lg border border-border bg-surface p-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-3">
|
||||
<div className="text-[10px] font-medium uppercase tracking-wider text-text-muted mb-2">
|
||||
By Justification
|
||||
</div>
|
||||
@@ -228,7 +228,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
setJustificationFilter(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
className="h-8 rounded-md border border-border bg-surface px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-8 rounded-md border border-border bg-panel px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
>
|
||||
{JUSTIFICATION_TYPES.map((j) => (
|
||||
<option key={j.value} value={j.value}>
|
||||
@@ -244,7 +244,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
setActionFilter(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
className="h-8 rounded-md border border-border bg-surface px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-8 rounded-md border border-border bg-panel px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
>
|
||||
{ACTION_TYPES.map((a) => (
|
||||
<option key={a.value} value={a.value}>
|
||||
@@ -263,7 +263,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
setDateFrom(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
className="h-8 rounded-md border border-border bg-surface px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-8 rounded-md border border-border bg-panel px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -277,7 +277,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
setDateTo(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
className="h-8 rounded-md border border-border bg-surface px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-8 rounded-md border border-border bg-panel px-2 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -288,7 +288,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
<button
|
||||
onClick={handleExport}
|
||||
disabled={exporting || !data?.total}
|
||||
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-surface px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-elevated hover:text-text-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-panel px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-elevated hover:text-text-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
{exporting ? 'Exporting...' : 'Export CSV'}
|
||||
@@ -296,7 +296,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="rounded-lg border border-border bg-surface overflow-hidden">
|
||||
<div className="rounded-lg border border-border bg-panel overflow-hidden">
|
||||
{isLoading ? (
|
||||
<div className="p-8 text-center">
|
||||
<div className="inline-block h-6 w-6 animate-spin rounded-full border-2 border-accent border-t-transparent" />
|
||||
@@ -373,7 +373,7 @@ export function TransparencyLogTable({ tenantId }: TransparencyLogTableProps) {
|
||||
setPerPage(Number(e.target.value))
|
||||
setPage(1)
|
||||
}}
|
||||
className="h-7 rounded border border-border bg-surface px-1.5 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
className="h-7 rounded border border-border bg-panel px-1.5 text-xs text-text-primary focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
>
|
||||
{PER_PAGE_OPTIONS.map((n) => (
|
||||
<option key={n} value={n}>
|
||||
@@ -438,7 +438,7 @@ interface StatsCardProps {
|
||||
|
||||
function StatsCard({ icon: Icon, label, value }: StatsCardProps) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-3">
|
||||
<div className="rounded-lg border border-border bg-panel p-3">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Icon className="h-3.5 w-3.5 text-text-muted" />
|
||||
<span className="text-[10px] font-medium uppercase tracking-wider text-text-muted">
|
||||
|
||||
@@ -8,8 +8,8 @@ const buttonVariants = cva(
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-[hsl(var(--accent-muted))] text-accent hover:bg-accent/20',
|
||||
solid: 'bg-accent text-white hover:bg-accent-hover',
|
||||
default: 'bg-[hsl(var(--accent-soft))] text-accent hover:bg-accent/20',
|
||||
solid: 'bg-accent text-white hover:bg-accent',
|
||||
destructive: 'bg-error/15 text-error hover:bg-error/20',
|
||||
outline:
|
||||
'border border-border bg-transparent text-text-secondary hover:bg-elevated hover:text-text-primary',
|
||||
|
||||
@@ -6,7 +6,7 @@ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElemen
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'rounded-lg border border-border bg-surface text-text-primary',
|
||||
'rounded-lg border border-border bg-panel text-text-primary',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -32,7 +32,7 @@ const DialogContent = React.forwardRef<
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%] w-full max-w-lg rounded-lg border border-border bg-surface text-text-primary p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
||||
'fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%] w-full max-w-lg rounded-lg border border-border bg-panel text-text-primary p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -38,7 +38,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-surface p-1 text-text-primary',
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-panel p-1 text-text-primary',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -55,7 +55,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-surface p-1 text-text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-panel p-1 text-text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -16,7 +16,7 @@ const PopoverContent = React.forwardRef<
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 w-72 rounded-lg border border-border bg-surface p-4 text-text-primary outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'z-50 w-72 rounded-lg border border-border bg-panel p-4 text-text-primary outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -63,7 +63,7 @@ const SelectContent = React.forwardRef<
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-border bg-surface text-text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-border bg-panel text-text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className,
|
||||
|
||||
@@ -6,7 +6,7 @@ export function Skeleton({ className, ...props }: SkeletonProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'animate-shimmer rounded-md bg-elevated bg-shimmer bg-shimmer relative overflow-hidden',
|
||||
'animate-pulse rounded-md bg-elevated relative overflow-hidden',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -26,7 +26,7 @@ const TabsTrigger = React.forwardRef<
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-[hsl(var(--accent-muted))] data-[state=active]:text-accent data-[state=active]:font-semibold data-[state=inactive]:text-text-muted data-[state=inactive]:hover:text-text-secondary',
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-[hsl(var(--accent-soft))] data-[state=active]:text-accent data-[state=active]:font-semibold data-[state=inactive]:text-text-muted data-[state=inactive]:hover:text-text-secondary',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -8,7 +8,7 @@ export function Toaster() {
|
||||
<SonnerToaster
|
||||
position="bottom-right"
|
||||
toastOptions={{
|
||||
className: 'bg-surface border-border text-text-primary',
|
||||
className: 'bg-panel border-border text-text-primary',
|
||||
descriptionClassName: 'text-text-secondary',
|
||||
}}
|
||||
theme={theme}
|
||||
|
||||
@@ -76,7 +76,7 @@ export function UserList({ tenantId }: Props) {
|
||||
<div className="rounded-lg border border-border overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border bg-surface">
|
||||
<tr className="border-b border-border bg-panel">
|
||||
<th scope="col" className="text-left px-3 py-2 text-xs font-medium text-text-muted">Name</th>
|
||||
<th scope="col" className="text-left px-3 py-2 text-xs font-medium text-text-muted">Email</th>
|
||||
<th scope="col" className="text-left px-3 py-2 text-xs font-medium text-text-muted">Role</th>
|
||||
@@ -104,7 +104,7 @@ export function UserList({ tenantId }: Props) {
|
||||
users?.map((u) => (
|
||||
<tr
|
||||
key={u.id}
|
||||
className="border-b border-border/50 hover:bg-surface transition-colors"
|
||||
className="border-b border-border/50 hover:bg-panel transition-colors"
|
||||
>
|
||||
<td className="px-3 py-2.5 font-medium">{u.name}</td>
|
||||
<td className="px-3 py-2.5 text-text-secondary">{u.email}</td>
|
||||
|
||||
@@ -202,7 +202,7 @@ export function VpnPage() {
|
||||
<div className="p-6 space-y-6">
|
||||
<h1 className="text-2xl font-bold text-text-primary">VPN</h1>
|
||||
<div className="max-w-lg mx-auto mt-12">
|
||||
<div className="rounded-lg border border-border bg-surface p-8 text-center space-y-6">
|
||||
<div className="rounded-lg border border-border bg-panel p-8 text-center space-y-6">
|
||||
<div className="mx-auto w-16 h-16 rounded-2xl bg-accent/10 flex items-center justify-center">
|
||||
<Shield className="h-8 w-8 text-accent" />
|
||||
</div>
|
||||
@@ -496,7 +496,7 @@ function InfoCard({
|
||||
muted?: boolean
|
||||
}) {
|
||||
return (
|
||||
<div className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="rounded-lg border border-border bg-panel p-4">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Icon className="h-4 w-4 text-text-muted" />
|
||||
<span className="text-xs text-text-muted uppercase tracking-wider">{label}</span>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user