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:
@@ -136,23 +136,6 @@
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* Rate color classes */
|
||||
.rateGood {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.rateWarn {
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.rateBad {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.rateNeutral {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Route name in table */
|
||||
.routeNameCell {
|
||||
font-size: 12px;
|
||||
@@ -161,42 +144,10 @@
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* Table section (reused for processor table) */
|
||||
.tableSection {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tableHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.tableTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tableRight {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tableMeta {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* Chart grid */
|
||||
.chartGrid {
|
||||
display: grid;
|
||||
@@ -204,15 +155,6 @@
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.chartCard {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chartTitle {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
@@ -370,35 +312,6 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.typeSelector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.typeOption {
|
||||
padding: 4px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-subtle);
|
||||
background: var(--bg-surface);
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-body);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.typeOption:hover {
|
||||
border-color: var(--border);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.typeOptionActive {
|
||||
background: var(--amber-bg);
|
||||
color: var(--amber-deep);
|
||||
border-color: var(--amber);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tapModalFooter {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -408,56 +321,3 @@
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
/* Test expression */
|
||||
.testSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.testTabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.testTabBtn {
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-subtle);
|
||||
background: var(--bg-surface);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.testTabBtnActive {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.testBody {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.testResult {
|
||||
padding: 10px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
margin-top: 8px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.testSuccess {
|
||||
background: var(--success-bg);
|
||||
border: 1px solid var(--success-border);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.testError {
|
||||
background: var(--error-bg);
|
||||
border: 1px solid var(--error-border);
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ import type { ExecutionSummary } from '../../api/types';
|
||||
import type { CatalogApp, CatalogRoute } from '../../api/queries/catalog';
|
||||
import { buildFlowSegments } from '../../utils/diagram-mapping';
|
||||
import styles from './RouteDetail.module.css';
|
||||
import tableStyles from '../../styles/table-section.module.css';
|
||||
import rateStyles from '../../styles/rate-colors.module.css';
|
||||
import chartCardStyles from '../../styles/chart-card.module.css';
|
||||
import tapModalStyles from '../../components/TapConfigModal.module.css';
|
||||
|
||||
// ── Row types ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -714,11 +718,11 @@ export default function RouteDetail() {
|
||||
</div>
|
||||
|
||||
{/* Processor Performance table (full width) */}
|
||||
<div className={styles.tableSection}>
|
||||
<div className={styles.tableHeader}>
|
||||
<span className={styles.tableTitle}>Processor Performance</span>
|
||||
<div className={styles.tableRight}>
|
||||
<span className={styles.tableMeta}>{processorRows.length} processors</span>
|
||||
<div className={`${tableStyles.tableSection} ${styles.tableSection}`}>
|
||||
<div className={tableStyles.tableHeader}>
|
||||
<span className={tableStyles.tableTitle}>Processor Performance</span>
|
||||
<div className={tableStyles.tableRight}>
|
||||
<span className={tableStyles.tableMeta}>{processorRows.length} processors</span>
|
||||
<Badge label="AUTO" color="success" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -732,8 +736,8 @@ export default function RouteDetail() {
|
||||
{/* Route Flow section */}
|
||||
{diagramFlows.length > 0 && (
|
||||
<div className={styles.routeFlowSection}>
|
||||
<div className={styles.tableHeader}>
|
||||
<span className={styles.tableTitle}>Route Flow</span>
|
||||
<div className={tableStyles.tableHeader}>
|
||||
<span className={tableStyles.tableTitle}>Route Flow</span>
|
||||
</div>
|
||||
<RouteFlow flows={diagramFlows} />
|
||||
</div>
|
||||
@@ -745,7 +749,7 @@ export default function RouteDetail() {
|
||||
|
||||
{activeTab === 'performance' && (
|
||||
<div className={styles.chartGrid} style={{ marginTop: 16 }}>
|
||||
<div className={styles.chartCard}>
|
||||
<div className={chartCardStyles.chartCard}>
|
||||
<div className={styles.chartTitle}>Throughput</div>
|
||||
<AreaChart
|
||||
series={[{
|
||||
@@ -755,7 +759,7 @@ export default function RouteDetail() {
|
||||
height={200}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.chartCard}>
|
||||
<div className={chartCardStyles.chartCard}>
|
||||
<div className={styles.chartTitle}>Latency</div>
|
||||
<LineChart
|
||||
series={[{
|
||||
@@ -766,7 +770,7 @@ export default function RouteDetail() {
|
||||
threshold={{ value: 300, label: 'SLA 300ms' }}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.chartCard}>
|
||||
<div className={chartCardStyles.chartCard}>
|
||||
<div className={styles.chartTitle}>Errors</div>
|
||||
<BarChart
|
||||
series={[{
|
||||
@@ -776,7 +780,7 @@ export default function RouteDetail() {
|
||||
height={200}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.chartCard}>
|
||||
<div className={chartCardStyles.chartCard}>
|
||||
<div className={styles.chartTitle}>Success Rate</div>
|
||||
<AreaChart
|
||||
series={[{
|
||||
@@ -890,13 +894,13 @@ export default function RouteDetail() {
|
||||
</FormField>
|
||||
|
||||
<FormField label="Type">
|
||||
<div className={styles.typeSelector}>
|
||||
<div className={tapModalStyles.typeSelector}>
|
||||
{typeChoices.map(tc => (
|
||||
<button
|
||||
key={tc.value}
|
||||
type="button"
|
||||
title={tc.tooltip}
|
||||
className={`${styles.typeOption} ${tapType === tc.value ? styles.typeOptionActive : ''}`}
|
||||
className={`${tapModalStyles.typeOption} ${tapType === tc.value ? tapModalStyles.typeOptionActive : ''}`}
|
||||
onClick={() => setTapType(tc.value)}
|
||||
>
|
||||
{tc.label}
|
||||
@@ -913,18 +917,18 @@ export default function RouteDetail() {
|
||||
|
||||
{/* Test Expression */}
|
||||
<Collapsible title="Test Expression" defaultOpen>
|
||||
<div className={styles.testSection}>
|
||||
<div className={styles.testTabs}>
|
||||
<div className={tapModalStyles.testSection}>
|
||||
<div className={tapModalStyles.testTabs}>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.testTabBtn} ${testTab === 'recent' ? styles.testTabBtnActive : ''}`}
|
||||
className={`${tapModalStyles.testTabBtn} ${testTab === 'recent' ? tapModalStyles.testTabBtnActive : ''}`}
|
||||
onClick={() => setTestTab('recent')}
|
||||
>
|
||||
Recent Exchange
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.testTabBtn} ${testTab === 'custom' ? styles.testTabBtnActive : ''}`}
|
||||
className={`${tapModalStyles.testTabBtn} ${testTab === 'custom' ? tapModalStyles.testTabBtnActive : ''}`}
|
||||
onClick={() => setTestTab('custom')}
|
||||
>
|
||||
Custom Payload
|
||||
@@ -932,7 +936,7 @@ export default function RouteDetail() {
|
||||
</div>
|
||||
|
||||
{testTab === 'recent' && (
|
||||
<div className={styles.testBody}>
|
||||
<div className={tapModalStyles.testBody}>
|
||||
<Select
|
||||
options={recentExchangeOptions.length > 0 ? recentExchangeOptions : [{ value: '', label: 'No recent exchanges' }]}
|
||||
value={testExchangeId}
|
||||
@@ -942,7 +946,7 @@ export default function RouteDetail() {
|
||||
)}
|
||||
|
||||
{testTab === 'custom' && (
|
||||
<div className={styles.testBody}>
|
||||
<div className={tapModalStyles.testBody}>
|
||||
<Textarea
|
||||
className={styles.monoTextarea}
|
||||
value={testPayload}
|
||||
@@ -953,7 +957,7 @@ export default function RouteDetail() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.testBody}>
|
||||
<div className={tapModalStyles.testBody}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
@@ -966,7 +970,7 @@ export default function RouteDetail() {
|
||||
</div>
|
||||
|
||||
{testResult && (
|
||||
<div className={`${styles.testResult} ${testResult.error ? styles.testError : styles.testSuccess}`}>
|
||||
<div className={`${tapModalStyles.testResult} ${testResult.error ? tapModalStyles.testError : tapModalStyles.testSuccess}`}>
|
||||
{testResult.error ?? testResult.result ?? 'No result'}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -5,68 +5,6 @@
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.refreshIndicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.refreshDot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--success);
|
||||
box-shadow: 0 0 4px rgba(61, 124, 71, 0.5);
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.refreshText {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* Route performance table */
|
||||
.tableSection {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tableHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.tableTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tableRight {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tableMeta {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* Route name in table */
|
||||
.routeNameCell {
|
||||
font-size: 12px;
|
||||
@@ -81,23 +19,6 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Rate color classes */
|
||||
.rateGood {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.rateWarn {
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.rateBad {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.rateNeutral {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 2x2 chart grid */
|
||||
.chartGrid {
|
||||
display: grid;
|
||||
|
||||
@@ -17,6 +17,9 @@ import { useRouteMetrics } from '../../api/queries/catalog';
|
||||
import { useExecutionStats, useStatsTimeseries } from '../../api/queries/executions';
|
||||
import type { RouteMetrics } from '../../api/types';
|
||||
import styles from './RoutesMetrics.module.css';
|
||||
import tableStyles from '../../styles/table-section.module.css';
|
||||
import refreshStyles from '../../styles/refresh-indicator.module.css';
|
||||
import rateStyles from '../../styles/rate-colors.module.css';
|
||||
|
||||
interface RouteRow {
|
||||
id: string;
|
||||
@@ -64,7 +67,7 @@ const ROUTE_COLUMNS: Column<RouteRow>[] = [
|
||||
sortable: true,
|
||||
render: (_, row) => {
|
||||
const pct = row.successRate * 100;
|
||||
const cls = pct >= 99 ? styles.rateGood : pct >= 97 ? styles.rateWarn : styles.rateBad;
|
||||
const cls = pct >= 99 ? rateStyles.rateGood : pct >= 97 ? rateStyles.rateWarn : rateStyles.rateBad;
|
||||
return <MonoText size="sm" className={cls}>{pct.toFixed(1)}%</MonoText>;
|
||||
},
|
||||
},
|
||||
@@ -81,7 +84,7 @@ const ROUTE_COLUMNS: Column<RouteRow>[] = [
|
||||
header: 'p99 Duration',
|
||||
sortable: true,
|
||||
render: (_, row) => {
|
||||
const cls = row.p99DurationMs > 300 ? styles.rateBad : row.p99DurationMs > 200 ? styles.rateWarn : styles.rateGood;
|
||||
const cls = row.p99DurationMs > 300 ? rateStyles.rateBad : row.p99DurationMs > 200 ? rateStyles.rateWarn : rateStyles.rateGood;
|
||||
return <MonoText size="sm" className={cls}>{Math.round(row.p99DurationMs)}ms</MonoText>;
|
||||
},
|
||||
},
|
||||
@@ -91,7 +94,7 @@ const ROUTE_COLUMNS: Column<RouteRow>[] = [
|
||||
sortable: true,
|
||||
render: (_, row) => {
|
||||
const pct = row.errorRate * 100;
|
||||
const cls = pct > 5 ? styles.rateBad : pct > 1 ? styles.rateWarn : styles.rateGood;
|
||||
const cls = pct > 5 ? rateStyles.rateBad : pct > 1 ? rateStyles.rateWarn : rateStyles.rateGood;
|
||||
return <MonoText size="sm" className={cls}>{pct.toFixed(1)}%</MonoText>;
|
||||
},
|
||||
},
|
||||
@@ -281,20 +284,20 @@ export default function RoutesMetrics() {
|
||||
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
<div className={styles.refreshIndicator}>
|
||||
<span className={styles.refreshDot} />
|
||||
<span className={styles.refreshText}>Auto-refresh: 30s</span>
|
||||
<div className={refreshStyles.refreshIndicator}>
|
||||
<span className={refreshStyles.refreshDot} />
|
||||
<span className={refreshStyles.refreshText}>Auto-refresh: 30s</span>
|
||||
</div>
|
||||
|
||||
{/* KPI header cards */}
|
||||
<KpiStrip items={kpiItems} />
|
||||
|
||||
{/* Per-route performance table */}
|
||||
<div className={styles.tableSection}>
|
||||
<div className={styles.tableHeader}>
|
||||
<span className={styles.tableTitle}>Per-Route Performance</span>
|
||||
<div className={styles.tableRight}>
|
||||
<span className={styles.tableMeta}>{rows.length} routes</span>
|
||||
<div className={tableStyles.tableSection}>
|
||||
<div className={tableStyles.tableHeader}>
|
||||
<span className={tableStyles.tableTitle}>Per-Route Performance</span>
|
||||
<div className={tableStyles.tableRight}>
|
||||
<span className={tableStyles.tableMeta}>{rows.length} routes</span>
|
||||
<Badge label="AUTO" color="success" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user