refactor: UI consistency — shared CSS, design system colors, no inline styles
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m22s
CI / docker (push) Successful in 1m9s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 36s

Phase 1: Extract 6 shared CSS modules (table-section, log-panel,
rate-colors, refresh-indicator, chart-card, section-card) eliminating
~135 duplicate class definitions across 11 files.

Phase 2: Replace all hardcoded hex colors in CSS modules with design
system variables. Strip ~55 hex fallbacks from var() patterns. Fix 4
undefined variable names (--accent, --bg-base, --surface, --bg-surface-raised).

Phase 3: Replace ~45 hardcoded hex values in ProcessDiagram SVG
components with var() CSS custom properties. Fix Dashboard.tsx color prop.

Phase 4: Create CSS modules for AdminLayout, DatabaseAdminPage,
OidcCallback (previously 100% inline). Extract shared PageLoader
component (replaces 3 copy-pasted spinner patterns). Move AppsTab
static inline styles to CSS classes. Extract LayoutShell StarredList styles.

58 files changed, net -219 lines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-09 14:55:54 +02:00
parent bfed8174ca
commit ff62a34d89
58 changed files with 770 additions and 989 deletions

View File

@@ -301,7 +301,7 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
<span className={styles.configLabel}>Application JAR</span>
<div className={styles.fileRow}>
<input ref={fileInputRef} type="file" accept=".jar"
style={{ position: 'absolute', width: 1, height: 1, margin: -1, padding: 0, overflow: 'hidden', clip: 'rect(0,0,0,0)', border: 0 }}
className={styles.visuallyHidden}
onChange={(e) => setFile(e.target.files?.[0] ?? null)} />
<Button size="sm" variant="secondary" type="button" onClick={() => fileInputRef.current?.click()} disabled={busy}>
{file ? 'Change file' : 'Select JAR'}
@@ -355,8 +355,8 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
<span className={styles.configLabel}>Max Payload Size</span>
<div className={styles.configInline}>
<Input disabled={busy} value={payloadSize} onChange={(e) => setPayloadSize(e.target.value)} style={{ width: 70 }} />
<Select disabled={busy} value={payloadUnit} onChange={(e) => setPayloadUnit(e.target.value)} style={{ width: 90 }}
<Input disabled={busy} value={payloadSize} onChange={(e) => setPayloadSize(e.target.value)} className={styles.inputMd} />
<Select disabled={busy} value={payloadUnit} onChange={(e) => setPayloadUnit(e.target.value)} className={styles.inputXl}
options={[{ value: 'bytes', label: 'bytes' }, { value: 'KB', label: 'KB' }, { value: 'MB', label: 'MB' }]} />
</div>
@@ -373,12 +373,12 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
<Toggle checked={metricsEnabled} onChange={() => !busy && setMetricsEnabled(!metricsEnabled)} disabled={busy} />
<span className={metricsEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{metricsEnabled ? 'Enabled' : 'Disabled'}</span>
<span className={styles.cellMeta} style={{ marginLeft: 8 }}>Interval</span>
<Input disabled={busy} value={metricsInterval} onChange={(e) => setMetricsInterval(e.target.value)} style={{ width: 50 }} />
<Input disabled={busy} value={metricsInterval} onChange={(e) => setMetricsInterval(e.target.value)} className={styles.inputXs} />
<span className={styles.cellMeta}>s</span>
</div>
<span className={styles.configLabel}>Sampling Rate</span>
<Input disabled={busy} value={samplingRate} onChange={(e) => setSamplingRate(e.target.value)} style={{ width: 80 }} />
<Input disabled={busy} value={samplingRate} onChange={(e) => setSamplingRate(e.target.value)} className={styles.inputLg} />
<span className={styles.configLabel}>Compress Success</span>
<div className={styles.configInline}>
@@ -406,25 +406,25 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
<div className={styles.configGrid}>
<span className={styles.configLabel}>Memory Limit</span>
<div className={styles.configInline}>
<Input disabled={busy} value={memoryLimit} onChange={(e) => setMemoryLimit(e.target.value)} style={{ width: 80 }} />
<Input disabled={busy} value={memoryLimit} onChange={(e) => setMemoryLimit(e.target.value)} className={styles.inputLg} />
<span className={styles.cellMeta}>MB</span>
</div>
<span className={styles.configLabel}>Memory Reserve</span>
<div>
<div className={styles.configInline}>
<Input disabled={!isProd || busy} value={memoryReserve} onChange={(e) => setMemoryReserve(e.target.value)} placeholder="---" style={{ width: 80 }} />
<Input disabled={!isProd || busy} value={memoryReserve} onChange={(e) => setMemoryReserve(e.target.value)} placeholder="---" className={styles.inputLg} />
<span className={styles.cellMeta}>MB</span>
</div>
{!isProd && <span className={styles.configHint}>Available in production environments only</span>}
</div>
<span className={styles.configLabel}>CPU Request</span>
<Input disabled={busy} value={cpuRequest} onChange={(e) => setCpuRequest(e.target.value)} style={{ width: 80 }} />
<Input disabled={busy} value={cpuRequest} onChange={(e) => setCpuRequest(e.target.value)} className={styles.inputLg} />
<span className={styles.configLabel}>CPU Limit</span>
<div className={styles.configInline}>
<Input disabled={busy} value={cpuLimit} onChange={(e) => setCpuLimit(e.target.value)} placeholder="e.g. 1000" style={{ width: 80 }} />
<Input disabled={busy} value={cpuLimit} onChange={(e) => setCpuLimit(e.target.value)} placeholder="e.g. 1000" className={styles.inputLg} />
<span className={styles.cellMeta}>millicores</span>
</div>
@@ -443,10 +443,10 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
</div>
<span className={styles.configLabel}>App Port</span>
<Input disabled={busy} value={appPort} onChange={(e) => setAppPort(e.target.value)} style={{ width: 80 }} />
<Input disabled={busy} value={appPort} onChange={(e) => setAppPort(e.target.value)} className={styles.inputLg} />
<span className={styles.configLabel}>Replicas</span>
<Input disabled={busy} value={replicas} onChange={(e) => setReplicas(e.target.value)} style={{ width: 60 }} type="number" />
<Input disabled={busy} value={replicas} onChange={(e) => setReplicas(e.target.value)} className={styles.inputSm} type="number" />
<span className={styles.configLabel}>Deploy Strategy</span>
<Select disabled={busy} value={deployStrategy} onChange={(e) => setDeployStrategy(e.target.value)}
@@ -551,7 +551,7 @@ function AppDetailView({ appId: appSlug, environments, selectedEnv }: { appId: s
</div>
<div className={styles.detailActions}>
<input ref={fileInputRef} type="file" accept=".jar"
style={{ position: 'absolute', width: 1, height: 1, margin: -1, padding: 0, overflow: 'hidden', clip: 'rect(0,0,0,0)', border: 0 }}
className={styles.visuallyHidden}
onChange={handleUpload} />
<Button size="sm" variant="primary" type="button" onClick={() => fileInputRef.current?.click()} loading={uploadJar.isPending}>Upload JAR</Button>
<Button size="sm" variant="danger" onClick={() => setDeleteConfirm(true)}>Delete App</Button>
@@ -636,7 +636,7 @@ function OverviewSubTab({ app, deployments, versions, environments, envMap, sele
<Badge label={dEnv?.displayName ?? '?'} color={dEnv?.production ? 'error' : 'auto'} />
</td>
<td><Badge label={version ? `v${version.version}` : '?'} color="auto" /></td>
<td style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<td className={styles.cellFlex}>
<StatusDot variant={DEPLOY_STATUS_DOT[d.status] ?? 'dead'} />
<Badge label={d.status} color={STATUS_COLORS[d.status] ?? 'auto'} />
</td>
@@ -962,8 +962,8 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
<span className={styles.configLabel}>Max Payload Size</span>
<div className={styles.configInline}>
<Input disabled={!editing} value={payloadSize} onChange={(e) => setPayloadSize(e.target.value)} style={{ width: 70 }} />
<Select disabled={!editing} value={payloadUnit} onChange={(e) => setPayloadUnit(e.target.value)} style={{ width: 90 }}
<Input disabled={!editing} value={payloadSize} onChange={(e) => setPayloadSize(e.target.value)} className={styles.inputMd} />
<Select disabled={!editing} value={payloadUnit} onChange={(e) => setPayloadUnit(e.target.value)} className={styles.inputXl}
options={[{ value: 'bytes', label: 'bytes' }, { value: 'KB', label: 'KB' }, { value: 'MB', label: 'MB' }]} />
</div>
@@ -980,12 +980,12 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
<Toggle checked={metricsEnabled} onChange={() => editing && setMetricsEnabled(!metricsEnabled)} disabled={!editing} />
<span className={metricsEnabled ? styles.toggleEnabled : styles.toggleDisabled}>{metricsEnabled ? 'Enabled' : 'Disabled'}</span>
<span className={styles.cellMeta} style={{ marginLeft: 8 }}>Interval</span>
<Input disabled={!editing} value={metricsInterval} onChange={(e) => setMetricsInterval(e.target.value)} style={{ width: 50 }} />
<Input disabled={!editing} value={metricsInterval} onChange={(e) => setMetricsInterval(e.target.value)} className={styles.inputXs} />
<span className={styles.cellMeta}>s</span>
</div>
<span className={styles.configLabel}>Sampling Rate</span>
<Input disabled={!editing} value={samplingRate} onChange={(e) => setSamplingRate(e.target.value)} style={{ width: 80 }} />
<Input disabled={!editing} value={samplingRate} onChange={(e) => setSamplingRate(e.target.value)} className={styles.inputLg} />
<span className={styles.configLabel}>Compress Success</span>
<div className={styles.configInline}>
@@ -1032,25 +1032,25 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
<div className={styles.configGrid}>
<span className={styles.configLabel}>Memory Limit</span>
<div className={styles.configInline}>
<Input disabled={!editing} value={memoryLimit} onChange={(e) => setMemoryLimit(e.target.value)} style={{ width: 80 }} />
<Input disabled={!editing} value={memoryLimit} onChange={(e) => setMemoryLimit(e.target.value)} className={styles.inputLg} />
<span className={styles.cellMeta}>MB</span>
</div>
<span className={styles.configLabel}>Memory Reserve</span>
<div>
<div className={styles.configInline}>
<Input disabled value={memoryReserve} onChange={(e) => setMemoryReserve(e.target.value)} placeholder="---" style={{ width: 80 }} />
<Input disabled value={memoryReserve} onChange={(e) => setMemoryReserve(e.target.value)} placeholder="---" className={styles.inputLg} />
<span className={styles.cellMeta}>MB</span>
</div>
{!isProd && <span className={styles.configHint}>Available in production environments only</span>}
</div>
<span className={styles.configLabel}>CPU Request</span>
<Input disabled={!editing} value={cpuRequest} onChange={(e) => setCpuRequest(e.target.value)} style={{ width: 80 }} />
<Input disabled={!editing} value={cpuRequest} onChange={(e) => setCpuRequest(e.target.value)} className={styles.inputLg} />
<span className={styles.configLabel}>CPU Limit</span>
<div className={styles.configInline}>
<Input disabled={!editing} value={cpuLimit} onChange={(e) => setCpuLimit(e.target.value)} placeholder="e.g. 1000" style={{ width: 80 }} />
<Input disabled={!editing} value={cpuLimit} onChange={(e) => setCpuLimit(e.target.value)} placeholder="e.g. 1000" className={styles.inputLg} />
<span className={styles.cellMeta}>millicores</span>
</div>
@@ -1069,10 +1069,10 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
</div>
<span className={styles.configLabel}>App Port</span>
<Input disabled={!editing} value={appPort} onChange={(e) => setAppPort(e.target.value)} style={{ width: 80 }} />
<Input disabled={!editing} value={appPort} onChange={(e) => setAppPort(e.target.value)} className={styles.inputLg} />
<span className={styles.configLabel}>Replicas</span>
<Input disabled={!editing} value={replicas} onChange={(e) => setReplicas(e.target.value)} style={{ width: 60 }} type="number" />
<Input disabled={!editing} value={replicas} onChange={(e) => setReplicas(e.target.value)} className={styles.inputSm} type="number" />
<span className={styles.configLabel}>Deploy Strategy</span>
<Select disabled={!editing} value={deployStrategy} onChange={(e) => setDeployStrategy(e.target.value)}