feat(ui): sweep remaining components for Deep Space consistency

Replace old design tokens and hardcoded colors across 29 files:
- bg-primary/text-primary-foreground -> bg-accent/text-white
- text-muted-foreground -> text-text-muted
- text-destructive/bg-destructive -> text-error/bg-error
- bg-muted -> bg-elevated (background usage)
- Hardcoded green/red/yellow/emerald/amber/slate -> semantic tokens
- Remove shadow-md/lg from cards, tooltips, topology nodes
- rounded-xl -> rounded-lg on cards/panels
- focus:ring-1 focus:ring-ring -> focus:border-accent on inputs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jason Staack
2026-03-16 19:00:36 -05:00
parent 25d4a80b73
commit c455fe4ed5
29 changed files with 107 additions and 107 deletions

View File

@@ -461,7 +461,7 @@ function ChannelFormDialog({
<select <select
value={smtpProvider} value={smtpProvider}
onChange={(e) => handleProviderChange(e.target.value)} onChange={(e) => handleProviderChange(e.target.value)}
className="w-full rounded-md bg-slate-700 border border-slate-600 text-white px-3 py-2 text-sm" className="w-full rounded-md bg-elevated border border-border text-text-primary px-3 py-2 text-sm"
> >
{SMTP_PRESETS.map((p) => ( {SMTP_PRESETS.map((p) => (
<option key={p.id} value={p.id}> <option key={p.id} value={p.id}>
@@ -544,12 +544,12 @@ function ChannelFormDialog({
type="button" type="button"
onClick={handleTestSmtp} onClick={handleTestSmtp}
disabled={testing || !smtpHost || !toAddress} disabled={testing || !smtpHost || !toAddress}
className="px-4 py-2 rounded-md bg-slate-600 text-white text-sm hover:bg-slate-500 disabled:opacity-50" className="px-4 py-2 rounded-md bg-elevated text-text-primary text-sm hover:bg-elevated/80 disabled:opacity-50"
> >
{testing ? 'Testing...' : 'Test Connection'} {testing ? 'Testing...' : 'Test Connection'}
</button> </button>
{testResult && ( {testResult && (
<p className={`text-sm ${testResult.success ? 'text-green-400' : 'text-red-400'}`}> <p className={`text-sm ${testResult.success ? 'text-success' : 'text-error'}`}>
{testResult.message} {testResult.message}
</p> </p>
)} )}

View File

@@ -224,7 +224,7 @@ export function EmergencyKitDialog({
> >
{copied ? ( {copied ? (
<> <>
<Check className="h-4 w-4 text-green-500" /> <Check className="h-4 w-4 text-success" />
Copied Copied
</> </>
) : ( ) : (

View File

@@ -56,18 +56,18 @@ const SCORE_CONFIG: Record<
}, },
2: { 2: {
label: 'Fair', label: 'Fair',
color: 'text-yellow-500', color: 'text-warning',
barColor: 'bg-yellow-500', barColor: 'bg-warning',
}, },
3: { 3: {
label: 'Strong', label: 'Strong',
color: 'text-green-500', color: 'text-success',
barColor: 'bg-green-500', barColor: 'bg-success',
}, },
4: { 4: {
label: 'Very Strong', label: 'Very Strong',
color: 'text-green-400', color: 'text-success',
barColor: 'bg-green-400', barColor: 'bg-success',
}, },
} }

View File

@@ -168,7 +168,7 @@ export function BulkDeployDialog({
{availableDevices.length === 0 ? ( {availableDevices.length === 0 ? (
<div className="rounded-lg border border-border bg-elevated/50 p-4 text-center"> <div className="rounded-lg border border-border bg-elevated/50 p-4 text-center">
<CheckCircle className="h-6 w-6 text-green-500 mx-auto mb-2" /> <CheckCircle className="h-6 w-6 text-success mx-auto mb-2" />
<p className="text-sm font-medium text-text-primary"> <p className="text-sm font-medium text-text-primary">
All devices have certificates All devices have certificates
</p> </p>
@@ -222,7 +222,7 @@ export function BulkDeployDialog({
className={cn( className={cn(
'text-[10px] uppercase px-1.5 py-0.5 rounded', 'text-[10px] uppercase px-1.5 py-0.5 rounded',
d.status === 'online' d.status === 'online'
? 'bg-green-500/10 text-green-500' ? 'bg-success/10 text-success'
: 'bg-text-muted/10 text-text-muted', : 'bg-text-muted/10 text-text-muted',
)} )}
> >
@@ -263,9 +263,9 @@ export function BulkDeployDialog({
<div className="space-y-4"> <div className="space-y-4">
{/* Summary */} {/* Summary */}
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="rounded-lg border border-green-500/30 bg-green-500/5 p-4 text-center"> <div className="rounded-lg border border-success/30 bg-success/5 p-4 text-center">
<CheckCircle className="h-6 w-6 text-green-500 mx-auto mb-1" /> <CheckCircle className="h-6 w-6 text-success mx-auto mb-1" />
<p className="text-2xl font-bold text-green-500"> <p className="text-2xl font-bold text-success">
{result.success} {result.success}
</p> </p>
<p className="text-xs text-text-muted">Succeeded</p> <p className="text-xs text-text-muted">Succeeded</p>

View File

@@ -76,7 +76,7 @@ export function CAStatusCard({ ca, canWrite: writable, tenantId }: CAStatusCardP
if (!ca) { if (!ca) {
return ( return (
<div className="max-w-lg mx-auto"> <div className="max-w-lg mx-auto">
<div className="rounded-xl border border-border bg-surface p-8 text-center space-y-6"> <div className="rounded-lg border border-border bg-surface 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"> <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" /> <Shield className="h-8 w-8 text-accent" />
</div> </div>
@@ -118,8 +118,8 @@ export function CAStatusCard({ ca, canWrite: writable, tenantId }: CAStatusCardP
return ( return (
<div <div
className={cn( className={cn(
'rounded-xl border bg-surface p-6 space-y-4', 'rounded-lg border bg-surface p-6 space-y-4',
isExpired ? 'border-error/40' : 'border-green-500/30', isExpired ? 'border-error/40' : 'border-success/30',
)} )}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@@ -127,13 +127,13 @@ export function CAStatusCard({ ca, canWrite: writable, tenantId }: CAStatusCardP
<div <div
className={cn( className={cn(
'w-10 h-10 rounded-xl flex items-center justify-center', 'w-10 h-10 rounded-xl flex items-center justify-center',
isExpired ? 'bg-error/10' : 'bg-green-500/10', isExpired ? 'bg-error/10' : 'bg-success/10',
)} )}
> >
<ShieldCheck <ShieldCheck
className={cn( className={cn(
'h-5 w-5', 'h-5 w-5',
isExpired ? 'text-error' : 'text-green-500', isExpired ? 'text-error' : 'text-success',
)} )}
/> />
</div> </div>
@@ -146,7 +146,7 @@ export function CAStatusCard({ ca, canWrite: writable, tenantId }: CAStatusCardP
'inline-flex items-center gap-1 text-[10px] font-medium uppercase px-1.5 py-0.5 rounded border mt-0.5', 'inline-flex items-center gap-1 text-[10px] font-medium uppercase px-1.5 py-0.5 rounded border mt-0.5',
isExpired isExpired
? 'bg-error/20 text-error border-error/40' ? 'bg-error/20 text-error border-error/40'
: 'bg-green-500/20 text-green-500 border-green-500/40', : 'bg-success/20 text-success border-success/40',
)} )}
> >
{isExpired ? 'Expired' : 'Active'} {isExpired ? 'Expired' : 'Active'}
@@ -175,7 +175,7 @@ export function CAStatusCard({ ca, canWrite: writable, tenantId }: CAStatusCardP
title="Copy fingerprint" title="Copy fingerprint"
> >
{copied ? ( {copied ? (
<CheckCircle className="h-3.5 w-3.5 text-green-500" /> <CheckCircle className="h-3.5 w-3.5 text-success" />
) : ( ) : (
<Copy className="h-3.5 w-3.5" /> <Copy className="h-3.5 w-3.5" />
)} )}

View File

@@ -148,7 +148,7 @@ export function DeployCertDialog({
{availableDevices.length === 0 ? ( {availableDevices.length === 0 ? (
<div className="rounded-lg border border-border bg-elevated/50 p-4 text-center"> <div className="rounded-lg border border-border bg-elevated/50 p-4 text-center">
<CheckCircle className="h-6 w-6 text-green-500 mx-auto mb-2" /> <CheckCircle className="h-6 w-6 text-success mx-auto mb-2" />
<p className="text-sm font-medium text-text-primary"> <p className="text-sm font-medium text-text-primary">
All devices have certificates All devices have certificates
</p> </p>
@@ -231,7 +231,7 @@ export function DeployCertDialog({
{step === 'done' && ( {step === 'done' && (
<div className="py-8 text-center space-y-3"> <div className="py-8 text-center space-y-3">
<CheckCircle className="h-8 w-8 text-green-500 mx-auto" /> <CheckCircle className="h-8 w-8 text-success mx-auto" />
<p className="text-sm font-medium text-text-primary"> <p className="text-sm font-medium text-text-primary">
Certificate deployed successfully Certificate deployed successfully
</p> </p>

View File

@@ -54,11 +54,11 @@ const STATUS_CONFIG: Record<
}, },
deployed: { deployed: {
label: 'Deployed', label: 'Deployed',
className: 'bg-green-500/20 text-green-500 border-green-500/40', className: 'bg-success/20 text-success border-success/40',
}, },
expiring: { expiring: {
label: 'Expiring Soon', label: 'Expiring Soon',
className: 'bg-yellow-500/20 text-yellow-500 border-yellow-500/40', className: 'bg-warning/20 text-warning border-warning/40',
}, },
expired: { expired: {
label: 'Expired', label: 'Expired',
@@ -236,7 +236,7 @@ export function DeviceCertTable({
{/* Empty state */} {/* Empty state */}
{filteredCerts.length === 0 ? ( {filteredCerts.length === 0 ? (
<div className="rounded-xl border border-dashed border-accent/30 bg-accent/5 p-8 text-center space-y-3"> <div className="rounded-lg border border-dashed border-accent/30 bg-accent/5 p-8 text-center space-y-3">
<ShieldCheck className="h-10 w-10 text-accent mx-auto" /> <ShieldCheck className="h-10 w-10 text-accent mx-auto" />
<h3 className="text-base font-semibold text-text-primary"> <h3 className="text-base font-semibold text-text-primary">
No device certificates yet No device certificates yet

View File

@@ -25,7 +25,7 @@ function triggerBadgeClass(type: ConfigBackupEntry['trigger_type']) {
case 'config-change': case 'config-change':
return 'border-orange-500/50 bg-orange-500/10 text-orange-500' return 'border-orange-500/50 bg-orange-500/10 text-orange-500'
default: default:
return 'border-muted bg-muted/10 text-muted-foreground' return 'border-muted bg-muted/10 text-text-muted'
} }
} }

View File

@@ -67,8 +67,8 @@ export function ConfigHistorySection({ tenantId, deviceId, deviceName }: ConfigH
return ( return (
<div className="rounded-lg border border-border bg-surface p-4"> <div className="rounded-lg border border-border bg-surface p-4">
<div className="flex items-center gap-2 mb-3"> <div className="flex items-center gap-2 mb-3">
<History className="h-4 w-4 text-muted-foreground" /> <History className="h-4 w-4 text-text-muted" />
<h3 className="text-sm font-medium text-muted-foreground">Configuration History</h3> <h3 className="text-sm font-medium text-text-muted">Configuration History</h3>
</div> </div>
{selectedSnapshotId && ( {selectedSnapshotId && (

View File

@@ -28,7 +28,7 @@ export function DiffViewer({ tenantId, deviceId, snapshotId, onClose }: DiffView
{/* Header */} {/* Header */}
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<h3 className="text-sm font-medium text-muted-foreground">Config Diff</h3> <h3 className="text-sm font-medium text-text-muted">Config Diff</h3>
{diff && ( {diff && (
<span className="text-xs font-mono"> <span className="text-xs font-mono">
<span className="text-success">+{diff.lines_added}</span> <span className="text-success">+{diff.lines_added}</span>

View File

@@ -20,10 +20,10 @@ interface RestorePreviewProps {
} }
const riskBadgeColors = { const riskBadgeColors = {
none: 'bg-muted text-text-secondary', none: 'bg-elevated text-text-secondary',
low: 'bg-success/10 text-success border-success/30', low: 'bg-success/10 text-success border-success/30',
medium: 'bg-warning/10 text-warning border-warning/30', medium: 'bg-warning/10 text-warning border-warning/30',
high: 'bg-destructive/10 text-destructive border-destructive/30', high: 'bg-error/10 text-error border-error/30',
} as const } as const
export function RestorePreview({ export function RestorePreview({
@@ -53,9 +53,9 @@ export function RestorePreview({
if (isLoading) { if (isLoading) {
return ( return (
<div className="space-y-4 p-4"> <div className="space-y-4 p-4">
<div className="h-12 rounded-lg bg-muted animate-pulse" /> <div className="h-12 rounded-lg bg-elevated animate-pulse" />
<div className="h-32 rounded-lg bg-muted animate-pulse" /> <div className="h-32 rounded-lg bg-elevated animate-pulse" />
<div className="h-16 rounded-lg bg-muted animate-pulse" /> <div className="h-16 rounded-lg bg-elevated animate-pulse" />
</div> </div>
) )
} }
@@ -63,10 +63,10 @@ export function RestorePreview({
if (error || !preview) { if (error || !preview) {
return ( return (
<div className="p-4 space-y-4"> <div className="p-4 space-y-4">
<div className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 flex items-start gap-3"> <div className="rounded-lg border border-error/30 bg-error/5 p-4 flex items-start gap-3">
<XCircle className="h-5 w-5 text-destructive shrink-0 mt-0.5" /> <XCircle className="h-5 w-5 text-error shrink-0 mt-0.5" />
<div> <div>
<p className="text-sm font-medium text-destructive">Preview failed</p> <p className="text-sm font-medium text-error">Preview failed</p>
<p className="text-xs text-text-secondary mt-1"> <p className="text-xs text-text-secondary mt-1">
Could not analyze the config. You may still proceed manually. Could not analyze the config. You may still proceed manually.
</p> </p>
@@ -88,13 +88,13 @@ export function RestorePreview({
<div className="space-y-4 p-4"> <div className="space-y-4 p-4">
{/* Validation errors */} {/* Validation errors */}
{!validation.valid && ( {!validation.valid && (
<div className="rounded-lg border border-destructive/30 bg-destructive/5 p-3 space-y-1"> <div className="rounded-lg border border-error/30 bg-error/5 p-3 space-y-1">
<p className="text-sm font-medium text-destructive flex items-center gap-2"> <p className="text-sm font-medium text-error flex items-center gap-2">
<XCircle className="h-4 w-4" /> <XCircle className="h-4 w-4" />
Validation errors found Validation errors found
</p> </p>
{validation.errors.map((err, i) => ( {validation.errors.map((err, i) => (
<p key={i} className="text-xs text-destructive/80 ml-6">{err}</p> <p key={i} className="text-xs text-error/80 ml-6">{err}</p>
))} ))}
</div> </div>
)} )}
@@ -103,13 +103,13 @@ export function RestorePreview({
<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-surface-raised p-3 flex items-center justify-between">
<div className="flex items-center gap-4 text-sm"> <div className="flex items-center gap-4 text-sm">
<span className="text-success font-mono">+{diff.added}</span> <span className="text-success font-mono">+{diff.added}</span>
<span className="text-destructive font-mono">-{diff.removed}</span> <span className="text-error font-mono">-{diff.removed}</span>
<span className="text-text-secondary"> <span className="text-text-secondary">
across {changedCategories.length} categor{changedCategories.length === 1 ? 'y' : 'ies'} across {changedCategories.length} categor{changedCategories.length === 1 ? 'y' : 'ies'}
</span> </span>
</div> </div>
{hasHighRisk ? ( {hasHighRisk ? (
<span className="text-xs font-medium text-destructive flex items-center gap-1"> <span className="text-xs font-medium text-error flex items-center gap-1">
<Shield className="h-3 w-3" /> High risk <Shield className="h-3 w-3" /> High risk
</span> </span>
) : ( ) : (
@@ -138,7 +138,7 @@ export function RestorePreview({
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{cat.adds > 0 && <span className="text-xs text-success">+{cat.adds}</span>} {cat.adds > 0 && <span className="text-xs text-success">+{cat.adds}</span>}
{cat.removes > 0 && <span className="text-xs text-destructive">-{cat.removes}</span>} {cat.removes > 0 && <span className="text-xs text-error">-{cat.removes}</span>}
<span className={cn( <span className={cn(
'text-xs px-1.5 py-0.5 rounded border', 'text-xs px-1.5 py-0.5 rounded border',
riskBadgeColors[cat.risk], riskBadgeColors[cat.risk],

View File

@@ -41,11 +41,11 @@ export function RollbackAlert({
} }
return ( return (
<div className="rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3 flex items-center justify-between"> <div className="rounded-lg border border-error/30 bg-error/5 px-4 py-3 flex items-center justify-between">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<AlertTriangle className="h-5 w-5 text-destructive shrink-0" /> <AlertTriangle className="h-5 w-5 text-error shrink-0" />
<div> <div>
<p className="text-sm font-medium text-destructive"> <p className="text-sm font-medium text-error">
Device went offline after config change Device went offline after config change
</p> </p>
<p className="text-xs text-text-secondary mt-0.5"> <p className="text-xs text-text-secondary mt-0.5">

View File

@@ -166,7 +166,7 @@ export function RemoteWinBoxButton({ tenantId, deviceId }: RemoteWinBoxButtonPro
<button <button
onClick={handleOpen} onClick={handleOpen}
disabled={createMutation.isPending} disabled={createMutation.isPending}
className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50" className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-accent text-white hover:bg-accent/90 disabled:opacity-50"
> >
{createMutation.isPending ? ( {createMutation.isPending ? (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
@@ -186,11 +186,11 @@ export function RemoteWinBoxButton({ tenantId, deviceId }: RemoteWinBoxButtonPro
</div> </div>
{state === 'failed' && error && ( {state === 'failed' && error && (
<div className="mt-2 flex items-center gap-2"> <div className="mt-2 flex items-center gap-2">
<p className="text-sm text-destructive">{error}</p> <p className="text-sm text-error">{error}</p>
</div> </div>
)} )}
{state === 'terminated' && ( {state === 'terminated' && (
<p className="mt-2 text-sm text-muted-foreground">Session ended</p> <p className="mt-2 text-sm text-text-muted">Session ended</p>
)} )}
</div> </div>
) )
@@ -206,7 +206,7 @@ export function RemoteWinBoxButton({ tenantId, deviceId }: RemoteWinBoxButtonPro
{state === 'requesting' ? 'Requesting session...' : 'Provisioning WinBox container...'} {state === 'requesting' ? 'Requesting session...' : 'Provisioning WinBox container...'}
</p> </p>
</div> </div>
<p className="text-xs text-muted-foreground">This may take a few seconds</p> <p className="text-xs text-text-muted">This may take a few seconds</p>
</div> </div>
) )
} }
@@ -234,12 +234,12 @@ export function RemoteWinBoxButton({ tenantId, deviceId }: RemoteWinBoxButtonPro
} }
> >
{/* Header bar */} {/* Header bar */}
<div className="flex items-center justify-between px-3 py-2 border-b bg-muted/50"> <div className="flex items-center justify-between px-3 py-2 border-b bg-elevated/50">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Globe className="h-4 w-4 text-primary" /> <Globe className="h-4 w-4 text-accent" />
<span className="text-sm font-medium">Remote WinBox</span> <span className="text-sm font-medium">Remote WinBox</span>
{countdown && ( {countdown && (
<span className="text-xs text-muted-foreground"> <span className="text-xs text-text-muted">
Expires in {countdown} Expires in {countdown}
</span> </span>
)} )}
@@ -281,7 +281,7 @@ export function RemoteWinBoxButton({ tenantId, deviceId }: RemoteWinBoxButtonPro
// Active but no iframe URL (missing xpra_ws_port) — show reset option // Active but no iframe URL (missing xpra_ws_port) — show reset option
return ( return (
<div className="rounded-md border p-4 space-y-2"> <div className="rounded-md border p-4 space-y-2">
<p className="text-sm text-destructive">Session active but display unavailable</p> <p className="text-sm text-error">Session active but display unavailable</p>
<button <button
onClick={handleReset} onClick={handleReset}
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-md border border-input bg-background hover:bg-accent text-sm" className="inline-flex items-center gap-2 px-3 py-1.5 rounded-md border border-input bg-background hover:bg-accent text-sm"

View File

@@ -162,7 +162,7 @@ export function SSHTerminal({ tenantId, deviceId, deviceName }: SSHTerminalProps
return ( return (
<button <button
onClick={handleOpen} onClick={handleOpen}
className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-primary text-primary-foreground hover:bg-primary/90" className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-accent text-white hover:bg-accent/90"
> >
<TerminalIcon className="h-4 w-4" /> <TerminalIcon className="h-4 w-4" />
SSH Terminal SSH Terminal
@@ -172,14 +172,14 @@ export function SSHTerminal({ tenantId, deviceId, deviceName }: SSHTerminalProps
return ( return (
<div className={`rounded-md border overflow-hidden ${expanded ? 'fixed inset-4 z-50 bg-background' : ''}`}> <div className={`rounded-md border overflow-hidden ${expanded ? 'fixed inset-4 z-50 bg-background' : ''}`}>
<div className="flex items-center justify-between px-3 py-2 bg-muted/50 border-b"> <div className="flex items-center justify-between px-3 py-2 bg-elevated/50 border-b">
<span className="text-sm font-medium">SSH: {deviceName}</span> <span className="text-sm font-medium">SSH: {deviceName}</span>
<div className="flex gap-1"> <div className="flex gap-1">
<button onClick={() => setExpanded(!expanded)} className="p-1 hover:bg-accent rounded"> <button onClick={() => setExpanded(!expanded)} className="p-1 hover:bg-accent rounded">
{expanded ? <Minimize2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />} {expanded ? <Minimize2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />}
</button> </button>
{state === 'disconnected' ? ( {state === 'disconnected' ? (
<button onClick={handleReconnect} className="px-2 py-1 text-xs rounded bg-primary text-primary-foreground"> <button onClick={handleReconnect} className="px-2 py-1 text-xs rounded bg-accent text-white">
Reconnect Reconnect
</button> </button>
) : ( ) : (
@@ -191,7 +191,7 @@ export function SSHTerminal({ tenantId, deviceId, deviceName }: SSHTerminalProps
</div> </div>
<div ref={termRef} className="h-80" tabIndex={0} style={expanded ? { height: 'calc(100% - 40px)' } : {}} /> <div ref={termRef} className="h-80" tabIndex={0} style={expanded ? { height: 'calc(100% - 40px)' } : {}} />
{state === 'connected' && ( {state === 'connected' && (
<div className="px-3 py-1 text-xs text-muted-foreground border-t"> <div className="px-3 py-1 text-xs text-text-muted border-t">
SSH session active idle timeout: 15 min SSH session active idle timeout: 15 min
</div> </div>
)} )}

View File

@@ -78,7 +78,7 @@ export function WinBoxButton({ tenantId, deviceId }: WinBoxButtonProps) {
openMutation.mutate() openMutation.mutate()
}} }}
disabled={openMutation.isPending} disabled={openMutation.isPending}
className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50" className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-accent text-white hover:bg-accent/90 disabled:opacity-50"
> >
{openMutation.isPending ? ( {openMutation.isPending ? (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
@@ -87,7 +87,7 @@ export function WinBoxButton({ tenantId, deviceId }: WinBoxButtonProps) {
)} )}
{openMutation.isPending ? 'Connecting...' : 'Open WinBox'} {openMutation.isPending ? 'Connecting...' : 'Open WinBox'}
</button> </button>
{error && <p className="mt-2 text-sm text-destructive">{error}</p>} {error && <p className="mt-2 text-sm text-error">{error}</p>}
</div> </div>
) )
} }
@@ -96,7 +96,7 @@ export function WinBoxButton({ tenantId, deviceId }: WinBoxButtonProps) {
return ( return (
<div className="rounded-md border p-4 space-y-3"> <div className="rounded-md border p-4 space-y-3">
<p className="font-medium text-sm">WinBox tunnel ready</p> <p className="font-medium text-sm">WinBox tunnel ready</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-text-muted">
Connect to: <code className="font-mono">{tunnelInfo.host}:{tunnelInfo.port}</code> Connect to: <code className="font-mono">{tunnelInfo.host}:{tunnelInfo.port}</code>
</p> </p>
<div className="flex gap-2"> <div className="flex gap-2">
@@ -119,7 +119,7 @@ export function WinBoxButton({ tenantId, deviceId }: WinBoxButtonProps) {
Close Tunnel Close Tunnel
</button> </button>
</div> </div>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-text-muted">
Tunnel closes after 5 min of inactivity Tunnel closes after 5 min of inactivity
</p> </p>
</div> </div>

View File

@@ -36,8 +36,8 @@ const CONNECTION_LABELS: Record<ConnectionState, string> = {
// Generate a deterministic color from a string // Generate a deterministic color from a string
function tenantColor(name: string): string { function tenantColor(name: string): string {
const colors = [ const colors = [
'bg-blue-500', 'bg-emerald-500', 'bg-violet-500', 'bg-amber-500', 'bg-info', 'bg-success', 'bg-accent', 'bg-warning',
'bg-rose-500', 'bg-cyan-500', 'bg-pink-500', 'bg-teal-500', 'bg-error', 'bg-info', 'bg-accent', 'bg-success',
] ]
let hash = 0 let hash = 0
for (let i = 0; i < name.length; i++) { for (let i = 0; i < name.length; i++) {

View File

@@ -298,7 +298,7 @@ export function Sidebar() {
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-label="Navigation" aria-label="Navigation"
className="lg:hidden fixed inset-y-0 left-0 z-50 w-[180px] flex flex-col bg-sidebar border-r border-border shadow-xl" className="lg:hidden fixed inset-y-0 left-0 z-50 w-[180px] flex flex-col bg-sidebar border-r border-border"
> >
{sidebarContent(false)} {sidebarContent(false)}
</aside> </aside>

View File

@@ -294,7 +294,7 @@ export function MaintenanceForm({
onChange={(e) => setNotes(e.target.value)} onChange={(e) => setNotes(e.target.value)}
placeholder="Reason for maintenance, ticket number, etc." placeholder="Reason for maintenance, ticket number, etc."
rows={2} rows={2}
className="w-full rounded-md border border-border bg-elevated/50 px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-1 focus:ring-ring resize-none" className="w-full rounded-md border border-border bg-elevated/50 px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-accent focus:outline-none resize-none"
/> />
</div> </div>

View File

@@ -33,7 +33,7 @@ function CustomTooltip({
}: { active?: boolean; payload?: Array<{ value?: number }>; label?: string; unit: string }) { }: { active?: boolean; payload?: Array<{ value?: number }>; label?: string; unit: string }) {
if (!active || !payload?.length) return null if (!active || !payload?.length) return null
return ( return (
<div className="rounded border border-border bg-surface px-2 py-1.5 text-xs text-text-primary shadow-lg"> <div className="rounded border border-border bg-surface px-2 py-1.5 text-xs text-text-primary">
<div className="mb-1 text-text-muted">{label}</div> <div className="mb-1 text-text-muted">{label}</div>
<div> <div>
{(payload[0].value ?? 0).toFixed(1)} {(payload[0].value ?? 0).toFixed(1)}

View File

@@ -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 }) { 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 if (!active || !payload?.length) return null
return ( return (
<div className="rounded border border-border bg-surface px-2 py-1.5 text-xs text-text-primary shadow-lg"> <div className="rounded border border-border bg-surface px-2 py-1.5 text-xs text-text-primary">
<div className="mb-1 text-text-muted">{label}</div> <div className="mb-1 text-text-muted">{label}</div>
{payload.map((entry) => ( {payload.map((entry) => (
<div key={entry.dataKey} className="flex items-center gap-2"> <div key={entry.dataKey} className="flex items-center gap-2">

View File

@@ -99,8 +99,8 @@ function DeviceNode({ data }: NodeProps<DeviceNodeData>) {
return ( return (
<div <div
className={cn( className={cn(
'rounded-lg border bg-surface shadow-md px-3 py-2 min-w-[180px]', 'rounded-lg border bg-surface px-3 py-2 min-w-[180px]',
'transition-shadow hover:shadow-lg', 'transition-colors',
isOnline ? 'border-border' : 'border-error/30', isOnline ? 'border-border' : 'border-error/30',
)} )}
> >
@@ -152,7 +152,7 @@ interface TooltipData {
function NodeTooltip({ data }: { data: TooltipData; onClose?: () => void }) { function NodeTooltip({ data }: { data: TooltipData; onClose?: () => void }) {
return ( return (
<div <div
className="absolute z-50 rounded-lg border border-border bg-elevated shadow-lg px-3 py-2 text-xs pointer-events-none" className="absolute z-50 rounded-lg border border-border bg-elevated px-3 py-2 text-xs pointer-events-none"
style={{ left: data.x + 10, top: data.y - 10 }} style={{ left: data.x + 10, top: data.y - 10 }}
> >
<div className="font-medium text-text-primary">{data.hostname}</div> <div className="font-medium text-text-primary">{data.hostname}</div>
@@ -339,7 +339,7 @@ export function TopologyMap({ tenantId }: TopologyMapProps) {
> >
<Background color="hsl(var(--muted))" gap={20} size={1} /> <Background color="hsl(var(--muted))" gap={20} size={1} />
<Controls <Controls
className="!bg-surface !border-border !shadow-md [&>button]:!bg-surface [&>button]:!border-border [&>button]:!text-text-secondary [&>button:hover]:!bg-elevated" className="!bg-surface !border-border [&>button]:!bg-surface [&>button]:!border-border [&>button]:!text-text-secondary [&>button:hover]:!bg-elevated"
/> />
<MiniMap <MiniMap
nodeColor={(node) => { nodeColor={(node) => {

View File

@@ -232,7 +232,7 @@ export function ReportsPage({ tenantId }: ReportsPageProps) {
disabled={generateMutation.isPending || !tenantId} disabled={generateMutation.isPending || !tenantId}
className={cn( className={cn(
'inline-flex items-center gap-2 px-6 py-2.5 rounded-md text-sm font-medium transition-colors', 'inline-flex items-center gap-2 px-6 py-2.5 rounded-md text-sm font-medium transition-colors',
'bg-primary text-primary-foreground hover:bg-primary/90', 'bg-accent text-white hover:bg-accent/90',
'disabled:opacity-50 disabled:cursor-not-allowed', 'disabled:opacity-50 disabled:cursor-not-allowed',
)} )}
> >

View File

@@ -264,7 +264,7 @@ export function ApiKeysPage({ tenantId }: ApiKeysPageProps) {
</label> </label>
<input <input
type="text" type="text"
className="w-full rounded-md border border-border-bright bg-elevated/50 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-ring" className="w-full rounded-md border border-border-bright bg-elevated/50 px-3 py-2 text-sm focus:border-accent focus:outline-none"
placeholder="e.g. Monitoring Integration" placeholder="e.g. Monitoring Integration"
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
@@ -300,7 +300,7 @@ export function ApiKeysPage({ tenantId }: ApiKeysPageProps) {
</label> </label>
<input <input
type="date" type="date"
className="w-full rounded-md border border-border-bright bg-elevated/50 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-ring" className="w-full rounded-md border border-border-bright bg-elevated/50 px-3 py-2 text-sm focus:border-accent focus:outline-none"
value={expiresAt} value={expiresAt}
onChange={(e) => setExpiresAt(e.target.value)} onChange={(e) => setExpiresAt(e.target.value)}
min={new Date().toISOString().split('T')[0]} min={new Date().toISOString().split('T')[0]}

View File

@@ -215,13 +215,13 @@ export function SettingsPage() {
{/* Delete Account */} {/* Delete Account */}
<div className="flex items-center justify-between py-2 border-t border-border/50"> <div className="flex items-center justify-between py-2 border-t border-border/50">
<div> <div>
<span className="text-sm text-destructive">Delete Account</span> <span className="text-sm text-error">Delete Account</span>
<p className="text-xs text-text-muted">Permanently delete your account and all personal data</p> <p className="text-xs text-text-muted">Permanently delete your account and all personal data</p>
</div> </div>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="text-destructive border-destructive/30 hover:bg-destructive/10" className="text-error border-error/30 hover:bg-error/10"
onClick={() => setShowDeleteDialog(true)} onClick={() => setShowDeleteDialog(true)}
> >
<Trash2 className="h-3.5 w-3.5 mr-1.5" /> <Trash2 className="h-3.5 w-3.5 mr-1.5" />
@@ -237,7 +237,7 @@ export function SettingsPage() {
}}> }}>
<DialogContent className="sm:max-w-md"> <DialogContent className="sm:max-w-md">
<DialogHeader> <DialogHeader>
<DialogTitle className="flex items-center gap-2 text-destructive"> <DialogTitle className="flex items-center gap-2 text-error">
<AlertTriangle className="h-5 w-5" /> <AlertTriangle className="h-5 w-5" />
Delete Account Delete Account
</DialogTitle> </DialogTitle>
@@ -247,8 +247,8 @@ export function SettingsPage() {
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
<div className="rounded-md bg-destructive/10 border border-destructive/20 p-3"> <div className="rounded-md bg-error/10 border border-error/20 p-3">
<p className="text-sm text-destructive font-medium">This will permanently:</p> <p className="text-sm text-error font-medium">This will permanently:</p>
<ul className="text-sm text-text-secondary mt-1 space-y-1 list-disc pl-4"> <ul className="text-sm text-text-secondary mt-1 space-y-1 list-disc pl-4">
<li>Delete your user account</li> <li>Delete your user account</li>
<li>Remove all your API keys</li> <li>Remove all your API keys</li>

View File

@@ -484,7 +484,7 @@ export function SetupWizard() {
<StepIndicator currentStep={step} /> <StepIndicator currentStep={step} />
{/* Card */} {/* Card */}
<div className="bg-surface border border-border rounded-xl shadow-lg p-8"> <div className="bg-surface border border-border rounded-lg p-8">
{step === 1 && ( {step === 1 && (
<CreateTenantStep <CreateTenantStep
onComplete={(tenant) => { onComplete={(tenant) => {

View File

@@ -201,7 +201,7 @@ export function VpnPage() {
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<h1 className="text-2xl font-bold text-text-primary">VPN</h1> <h1 className="text-2xl font-bold text-text-primary">VPN</h1>
<div className="max-w-lg mx-auto mt-12"> <div className="max-w-lg mx-auto mt-12">
<div className="rounded-xl border border-border bg-surface p-8 text-center space-y-6"> <div className="rounded-lg border border-border bg-surface 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"> <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" /> <Shield className="h-8 w-8 text-accent" />
</div> </div>
@@ -244,8 +244,8 @@ export function VpnPage() {
className={cn( className={cn(
'inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium', 'inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium',
config.is_enabled config.is_enabled
? 'bg-green-500/10 text-green-500' ? 'bg-success/10 text-success'
: 'bg-yellow-500/10 text-yellow-500', : 'bg-warning/10 text-warning',
)} )}
> >
{config.is_enabled ? ( {config.is_enabled ? (
@@ -271,7 +271,7 @@ export function VpnPage() {
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="text-red-400 border-red-800 hover:bg-red-900/30" className="text-error border-error/30 hover:bg-error/10"
onClick={() => { onClick={() => {
if (confirm('Delete VPN configuration? All peers will be removed.')) { if (confirm('Delete VPN configuration? All peers will be removed.')) {
deleteMutation.mutate() deleteMutation.mutate()
@@ -310,7 +310,7 @@ export function VpnPage() {
{peersLoading ? ( {peersLoading ? (
<TableSkeleton rows={3} /> <TableSkeleton rows={3} />
) : peers.length === 0 ? ( ) : peers.length === 0 ? (
<div className="rounded-xl border border-dashed border-accent/30 bg-accent/5 p-8 text-center space-y-3"> <div className="rounded-lg border border-dashed border-accent/30 bg-accent/5 p-8 text-center space-y-3">
<ShieldCheck className="h-10 w-10 text-accent mx-auto" /> <ShieldCheck className="h-10 w-10 text-accent mx-auto" />
<h3 className="text-base font-semibold text-text-primary">VPN is ready</h3> <h3 className="text-base font-semibold text-text-primary">VPN is ready</h3>
<p className="text-sm text-text-secondary max-w-md mx-auto"> <p className="text-sm text-text-secondary max-w-md mx-auto">
@@ -346,7 +346,7 @@ export function VpnPage() {
</td> </td>
<td className="px-4 py-3"> <td className="px-4 py-3">
{peer.last_handshake ? ( {peer.last_handshake ? (
<span className="inline-flex items-center gap-1 text-green-500 text-xs"> <span className="inline-flex items-center gap-1 text-success text-xs">
<Wifi className="h-3 w-3" /> Connected <Wifi className="h-3 w-3" /> Connected
</span> </span>
) : ( ) : (
@@ -373,7 +373,7 @@ export function VpnPage() {
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => removePeerMutation.mutate(peer.id)} onClick={() => removePeerMutation.mutate(peer.id)}
className="text-red-400 hover:text-red-300" className="text-error hover:text-error/80"
title="Remove from VPN" title="Remove from VPN"
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
@@ -400,7 +400,7 @@ export function VpnPage() {
</p> </p>
{availableDevices.length === 0 ? ( {availableDevices.length === 0 ? (
<div className="rounded-lg border border-border bg-elevated/50 p-4 text-center"> <div className="rounded-lg border border-border bg-elevated/50 p-4 text-center">
<CheckCircle className="h-6 w-6 text-green-500 mx-auto mb-2" /> <CheckCircle className="h-6 w-6 text-success mx-auto mb-2" />
<p className="text-sm font-medium text-text-primary">All devices are on VPN</p> <p className="text-sm font-medium text-text-primary">All devices are on VPN</p>
<p className="text-xs text-text-muted mt-1"> <p className="text-xs text-text-muted mt-1">
Every device in your fleet is already connected. Add more devices to your fleet first. Every device in your fleet is already connected. Add more devices to your fleet first.
@@ -454,7 +454,7 @@ export function VpnPage() {
className="absolute top-2 right-2" className="absolute top-2 right-2"
onClick={() => copyToClipboard(peerConfig.routeros_commands.join('\n'))} onClick={() => copyToClipboard(peerConfig.routeros_commands.join('\n'))}
> >
{copied ? <CheckCircle className="h-4 w-4 text-green-500" /> : <Copy className="h-4 w-4" />} {copied ? <CheckCircle className="h-4 w-4 text-success" /> : <Copy className="h-4 w-4" />}
</Button> </Button>
</div> </div>
<div className="grid grid-cols-2 gap-3 text-sm"> <div className="grid grid-cols-2 gap-3 text-sm">

View File

@@ -672,7 +672,7 @@ function DeviceDetailPage() {
{/* Interface Utilization */} {/* Interface Utilization */}
<div className="rounded-lg border border-border bg-surface p-4"> <div className="rounded-lg border border-border bg-surface p-4">
<h3 className="text-sm font-medium text-muted-foreground mb-3">Interface Utilization</h3> <h3 className="text-sm font-medium text-text-muted mb-3">Interface Utilization</h3>
<InterfaceGauges tenantId={tenantId} deviceId={deviceId} active={activeTab === 'overview'} /> <InterfaceGauges tenantId={tenantId} deviceId={deviceId} active={activeTab === 'overview'} />
</div> </div>

View File

@@ -13,25 +13,25 @@ export const Route = createFileRoute('/_authenticated/traffic')({
function cpuColor(cpu: number | null): string { function cpuColor(cpu: number | null): string {
if (cpu === null) return 'text-text-muted' if (cpu === null) return 'text-text-muted'
if (cpu < 50) return 'text-emerald-400' if (cpu < 50) return 'text-success'
if (cpu < 80) return 'text-yellow-400' if (cpu < 80) return 'text-warning'
return 'text-red-400' return 'text-error'
} }
function memColor(mem: number | null): string { function memColor(mem: number | null): string {
if (mem === null) return 'text-text-muted' if (mem === null) return 'text-text-muted'
if (mem < 60) return 'text-emerald-400' if (mem < 60) return 'text-success'
if (mem < 85) return 'text-yellow-400' if (mem < 85) return 'text-warning'
return 'text-red-400' return 'text-error'
} }
function statusDot(status: string) { function statusDot(status: string) {
const color = const color =
status === 'online' status === 'online'
? 'bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.6)]' ? 'bg-success'
: status === 'degraded' : status === 'degraded'
? 'bg-yellow-400 shadow-[0_0_6px_rgba(250,204,21,0.6)]' ? 'bg-warning'
: 'bg-red-400 shadow-[0_0_6px_rgba(248,113,113,0.6)]' : 'bg-error'
return <span className={cn('inline-block h-2 w-2 rounded-full', color)} /> return <span className={cn('inline-block h-2 w-2 rounded-full', color)} />
} }

View File

@@ -13,9 +13,9 @@ export const Route = createFileRoute('/_authenticated/wireless')({
function signalColor(signal: number | null): string { function signalColor(signal: number | null): string {
if (signal === null) return 'text-text-muted' if (signal === null) return 'text-text-muted'
if (signal > -60) return 'text-emerald-400' if (signal > -60) return 'text-success'
if (signal > -70) return 'text-yellow-400' if (signal > -70) return 'text-warning'
return 'text-red-400' return 'text-error'
} }
function WirelessPage() { function WirelessPage() {
@@ -105,7 +105,7 @@ function WirelessPage() {
) : issues.length === 0 ? ( ) : issues.length === 0 ? (
<Card className="border-border bg-surface"> <Card className="border-border bg-surface">
<CardContent className="flex flex-col items-center justify-center gap-3 p-12"> <CardContent className="flex flex-col items-center justify-center gap-3 p-12">
<CheckCircle2 className="h-10 w-10 text-emerald-400" /> <CheckCircle2 className="h-10 w-10 text-success" />
<p className="text-sm font-medium text-text-secondary"> <p className="text-sm font-medium text-text-secondary">
All Clear no wireless issues detected All Clear no wireless issues detected
</p> </p>
@@ -153,7 +153,7 @@ function WirelessPage() {
> >
<td className="px-4 py-3 text-sm text-text-secondary"> <td className="px-4 py-3 text-sm text-text-secondary">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="inline-block h-2 w-2 rounded-full bg-red-400 shadow-[0_0_6px_rgba(248,113,113,0.6)]" /> <span className="inline-block h-2 w-2 rounded-full bg-error" />
{issue.hostname} {issue.hostname}
</div> </div>
</td> </td>