style: add CSS modules to all pages matching design system mock layouts
Replace inline styles with semantic CSS module classes for proper visual structure: card wrappers with borders/shadows, grid layouts for stat strips and charts, section headers, and typography classes. Pages updated: Dashboard, ExchangeDetail, RoutesMetrics, AgentHealth, AgentInstance. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
143
ui/src/pages/ExchangeDetail/ExchangeDetail.module.css
Normal file
143
ui/src/pages/ExchangeDetail/ExchangeDetail.module.css
Normal file
@@ -0,0 +1,143 @@
|
||||
.exchangeHeader {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.headerRow {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.headerStat {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.headerStatLabel {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.6px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.headerStatValue {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.timelineSection {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.timelineHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.timelineTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.timelineBody {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.detailSplit {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detailPanel {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panelHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
background: var(--bg-raised);
|
||||
}
|
||||
|
||||
.panelTitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.panelBody {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.headerKvRow {
|
||||
display: grid;
|
||||
grid-template-columns: 140px 1fr;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.headerKvRow:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.headerKey {
|
||||
font-family: var(--font-mono);
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.headerValue {
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sectionLabel {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.6px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router';
|
||||
import {
|
||||
Card, Badge, StatusDot, MonoText, CodeBlock, InfoCallout,
|
||||
Badge, StatusDot, MonoText, CodeBlock, InfoCallout,
|
||||
ProcessorTimeline, Breadcrumb, Spinner,
|
||||
} from '@cameleer/design-system';
|
||||
import { useExecutionDetail, useProcessorSnapshot } from '../../api/queries/executions';
|
||||
import styles from './ExchangeDetail.module.css';
|
||||
|
||||
export default function ExchangeDetail() {
|
||||
const { id } = useParams();
|
||||
@@ -43,88 +44,96 @@ export default function ExchangeDetail() {
|
||||
{ label: id?.slice(0, 12) || '' },
|
||||
]} />
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1rem', margin: '1.5rem 0' }}>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>Status</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginTop: '0.25rem' }}>
|
||||
<StatusDot variant={detail.status === 'COMPLETED' ? 'success' : detail.status === 'FAILED' ? 'error' : 'running'} />
|
||||
<div className={styles.exchangeHeader}>
|
||||
<div className={styles.headerRow}>
|
||||
<div className={styles.headerLeft}>
|
||||
<StatusDot variant={detail.status === 'COMPLETED' ? 'success' : detail.status === 'FAILED' ? 'error' : 'running'} />
|
||||
<div>
|
||||
<Badge label={detail.status} color={detail.status === 'COMPLETED' ? 'success' : 'error'} />
|
||||
<MonoText>{id}</MonoText>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>Duration</div>
|
||||
<div style={{ fontSize: '1.25rem', fontWeight: 600 }}>{detail.durationMs}ms</div>
|
||||
<div className={styles.headerRight}>
|
||||
<div className={styles.headerStat}>
|
||||
<div className={styles.headerStatLabel}>Duration</div>
|
||||
<div className={styles.headerStatValue}>{detail.durationMs}ms</div>
|
||||
</div>
|
||||
<div className={styles.headerStat}>
|
||||
<div className={styles.headerStatLabel}>Route</div>
|
||||
<div className={styles.headerStatValue}>{detail.routeId}</div>
|
||||
</div>
|
||||
<div className={styles.headerStat}>
|
||||
<div className={styles.headerStatLabel}>Application</div>
|
||||
<div className={styles.headerStatValue}>{detail.groupName || 'unknown'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>Route</div>
|
||||
<MonoText>{detail.routeId}</MonoText>
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>Application</div>
|
||||
<Badge label={detail.groupName || 'unknown'} color="auto" />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{detail.errorMessage && (
|
||||
<div style={{ marginBottom: '1.5rem' }}>
|
||||
<InfoCallout variant="error">
|
||||
{detail.errorMessage}
|
||||
</InfoCallout>
|
||||
</div>
|
||||
<InfoCallout variant="error">
|
||||
{detail.errorMessage}
|
||||
</InfoCallout>
|
||||
)}
|
||||
|
||||
<h3 style={{ marginBottom: '0.75rem' }}>Processor Timeline</h3>
|
||||
{processors.length > 0 ? (
|
||||
<ProcessorTimeline
|
||||
processors={processors}
|
||||
totalMs={detail.durationMs}
|
||||
onProcessorClick={(_p, i) => setSelectedProcessor(i)}
|
||||
selectedIndex={selectedProcessor ?? undefined}
|
||||
/>
|
||||
) : (
|
||||
<InfoCallout>No processor data available</InfoCallout>
|
||||
)}
|
||||
<div className={styles.timelineSection}>
|
||||
<div className={styles.timelineHeader}>
|
||||
<span className={styles.timelineTitle}>Processor Timeline</span>
|
||||
</div>
|
||||
<div className={styles.timelineBody}>
|
||||
{processors.length > 0 ? (
|
||||
<ProcessorTimeline
|
||||
processors={processors}
|
||||
totalMs={detail.durationMs}
|
||||
onProcessorClick={(_p, i) => setSelectedProcessor(i)}
|
||||
selectedIndex={selectedProcessor ?? undefined}
|
||||
/>
|
||||
) : (
|
||||
<InfoCallout>No processor data available</InfoCallout>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{snapshot && (
|
||||
<div style={{ marginTop: '1.5rem' }}>
|
||||
<h3 style={{ marginBottom: '0.75rem' }}>Exchange Snapshot</h3>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>Input Body</h4>
|
||||
<>
|
||||
<div className={styles.sectionLabel}>Exchange Snapshot</div>
|
||||
<div className={styles.detailSplit}>
|
||||
<div className={styles.detailPanel}>
|
||||
<div className={styles.panelHeader}>
|
||||
<span className={styles.panelTitle}>Input Body</span>
|
||||
</div>
|
||||
<div className={styles.panelBody}>
|
||||
<CodeBlock content={String(snapshot.inputBody ?? 'null')} />
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>Output Body</h4>
|
||||
</div>
|
||||
<div className={styles.detailPanel}>
|
||||
<div className={styles.panelHeader}>
|
||||
<span className={styles.panelTitle}>Output Body</span>
|
||||
</div>
|
||||
<div className={styles.panelBody}>
|
||||
<CodeBlock content={String(snapshot.outputBody ?? 'null')} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', marginTop: '1rem' }}>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>Input Headers</h4>
|
||||
<div className={styles.detailSplit}>
|
||||
<div className={styles.detailPanel}>
|
||||
<div className={styles.panelHeader}>
|
||||
<span className={styles.panelTitle}>Input Headers</span>
|
||||
</div>
|
||||
<div className={styles.panelBody}>
|
||||
<CodeBlock content={JSON.stringify(snapshot.inputHeaders ?? {}, null, 2)} />
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<div style={{ padding: '1rem' }}>
|
||||
<h4 style={{ marginBottom: '0.5rem' }}>Output Headers</h4>
|
||||
</div>
|
||||
<div className={styles.detailPanel}>
|
||||
<div className={styles.panelHeader}>
|
||||
<span className={styles.panelTitle}>Output Headers</span>
|
||||
</div>
|
||||
<div className={styles.panelBody}>
|
||||
<CodeBlock content={JSON.stringify(snapshot.outputHeaders ?? {}, null, 2)} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user