refactor: UI consistency — shared CSS, design system colors, no inline styles
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:
@@ -57,7 +57,7 @@
|
||||
|
||||
.stepIndicator {
|
||||
font-size: 12px;
|
||||
color: var(--accent, #6c7aff);
|
||||
color: var(--amber);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -143,8 +143,8 @@
|
||||
}
|
||||
|
||||
.subTabActive {
|
||||
color: var(--accent, #6c7aff);
|
||||
border-bottom-color: var(--accent, #6c7aff);
|
||||
color: var(--amber);
|
||||
border-bottom-color: var(--amber);
|
||||
}
|
||||
|
||||
/* Table */
|
||||
@@ -230,7 +230,7 @@
|
||||
|
||||
.editBannerActive {
|
||||
border-color: var(--warning);
|
||||
background: rgba(251, 191, 36, 0.06);
|
||||
background: color-mix(in srgb, var(--amber) 6%, transparent);
|
||||
}
|
||||
|
||||
.editBannerText {
|
||||
@@ -432,3 +432,44 @@
|
||||
.removeBtn:hover {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
/* Visually hidden file inputs */
|
||||
.visuallyHidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* Fixed-width inputs */
|
||||
.inputXs {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.inputSm {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.inputMd {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.inputLg {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.inputXl {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
/* Table cell flex layout */
|
||||
.cellFlex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user