feat: DataTable scrollable layout with 200+ mock exchanges
Make Dashboard table fill viewport height with sticky header/footer and internal scrolling. Expand mock exchange data from 15 to 200 records and Inventory showcase from 5 to 500 records. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -313,3 +313,74 @@ export const exchanges: Exchange[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// ── Generate additional exchanges to reach ~200 total ────────────────────────
|
||||||
|
|
||||||
|
const GEN_ROUTES = [
|
||||||
|
'order-intake', 'payment-validate', 'order-enrichment', 'shipment-dispatch',
|
||||||
|
'payment-process', 'shipment-track', 'inventory-sync', 'notification-send',
|
||||||
|
'audit-log', 'customer-update', 'refund-process', 'invoice-generate',
|
||||||
|
]
|
||||||
|
const GEN_ROUTE_GROUPS = [
|
||||||
|
'order-flow', 'payment-flow', 'shipment-flow', 'inventory-flow',
|
||||||
|
'notification-flow', 'audit-flow', 'customer-flow', 'billing-flow',
|
||||||
|
]
|
||||||
|
const GEN_AGENTS = ['prod-1', 'prod-2', 'prod-3', 'prod-4']
|
||||||
|
const GEN_STATUSES: Exchange['status'][] = [
|
||||||
|
'completed', 'completed', 'completed', 'completed', 'completed',
|
||||||
|
'completed', 'failed', 'warning', 'running',
|
||||||
|
]
|
||||||
|
const GEN_PROCESSORS: ProcessorData[][] = [
|
||||||
|
[
|
||||||
|
{ name: 'from(jms:queue)', type: 'consumer', durationMs: 4, status: 'ok', startMs: 0 },
|
||||||
|
{ name: 'unmarshal(json)', type: 'transform', durationMs: 6, status: 'ok', startMs: 4 },
|
||||||
|
{ name: 'validate(schema)', type: 'process', durationMs: 10, status: 'ok', startMs: 10 },
|
||||||
|
{ name: 'to(target)', type: 'to', durationMs: 15, status: 'ok', startMs: 20 },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ name: 'from(jms:queue)', type: 'consumer', durationMs: 5, status: 'ok', startMs: 0 },
|
||||||
|
{ name: 'enrich(external-api)', type: 'enrich', durationMs: 120, status: 'slow', startMs: 5 },
|
||||||
|
{ name: 'to(target)', type: 'to', durationMs: 20, status: 'ok', startMs: 125 },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ name: 'from(jms:queue)', type: 'consumer', durationMs: 3, status: 'ok', startMs: 0 },
|
||||||
|
{ name: 'to(external-gateway)', type: 'to', durationMs: 400, status: 'fail', startMs: 3 },
|
||||||
|
],
|
||||||
|
]
|
||||||
|
const GEN_ERRORS = [
|
||||||
|
{ msg: 'org.apache.camel.CamelExecutionException: Timeout after 5000ms', cls: 'org.apache.camel.CamelExecutionException' },
|
||||||
|
{ msg: 'java.sql.SQLTransientConnectionException: Connection pool exhausted', cls: 'java.sql.SQLTransientConnectionException' },
|
||||||
|
{ msg: 'org.apache.camel.component.http.HttpOperationFailedException: HTTP 502 Bad Gateway', cls: 'org.apache.camel.component.http.HttpOperationFailedException' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const BASE_TIME = new Date('2026-03-18T08:00:00').getTime()
|
||||||
|
|
||||||
|
for (let i = 0; i < 185; i++) {
|
||||||
|
const idx = exchanges.length
|
||||||
|
const status = GEN_STATUSES[i % GEN_STATUSES.length]
|
||||||
|
const route = GEN_ROUTES[i % GEN_ROUTES.length]
|
||||||
|
const routeGroup = GEN_ROUTE_GROUPS[i % GEN_ROUTE_GROUPS.length]
|
||||||
|
const isFailed = status === 'failed'
|
||||||
|
const durationMs = isFailed
|
||||||
|
? 1000 + ((i * 37) % 4000)
|
||||||
|
: status === 'running'
|
||||||
|
? 10000 + ((i * 53) % 20000)
|
||||||
|
: 30 + ((i * 73) % 400)
|
||||||
|
const err = isFailed ? GEN_ERRORS[i % GEN_ERRORS.length] : undefined
|
||||||
|
|
||||||
|
exchanges.push({
|
||||||
|
id: `E-2026-03-18-${String(idx + 200).padStart(5, '0')}`,
|
||||||
|
orderId: `OP-${80000 + idx}`,
|
||||||
|
customer: `CUST-${10000 + ((i * 131) % 90000)}`,
|
||||||
|
route,
|
||||||
|
routeGroup,
|
||||||
|
status,
|
||||||
|
durationMs,
|
||||||
|
timestamp: new Date(BASE_TIME - i * 2 * 60 * 1000),
|
||||||
|
correlationId: `cmr-${i.toString(16).padStart(8, '0')}-gen`,
|
||||||
|
agent: GEN_AGENTS[i % GEN_AGENTS.length],
|
||||||
|
...(err ? { errorMessage: err.msg, errorClass: err.cls } : {}),
|
||||||
|
processors: GEN_PROCESSORS[i % GEN_PROCESSORS.length].map((p) => ({ ...p })),
|
||||||
|
...(i % 3 === 0 ? { correlationGroup: `${routeGroup}-${String(Math.floor(i / 3)).padStart(3, '0')}` } : {}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 20px 24px 40px;
|
padding: 20px 24px 40px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
background: var(--bg-body);
|
background: var(--bg-body);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Filter bar spacing */
|
/* Filter bar spacing */
|
||||||
@@ -19,6 +22,10 @@
|
|||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
box-shadow: var(--shadow-card);
|
box-shadow: var(--shadow-card);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableHeader {
|
.tableHeader {
|
||||||
|
|||||||
@@ -425,6 +425,7 @@ export function Dashboard() {
|
|||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
sortable
|
sortable
|
||||||
flush
|
flush
|
||||||
|
fillHeight
|
||||||
rowAccent={handleRowAccent}
|
rowAccent={handleRowAccent}
|
||||||
expandedContent={(row) =>
|
expandedContent={(row) =>
|
||||||
row.errorMessage ? (
|
row.errorMessage ? (
|
||||||
|
|||||||
@@ -135,13 +135,27 @@ interface TableRow {
|
|||||||
exchanges: number
|
exchanges: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const TABLE_DATA: TableRow[] = [
|
const ROUTE_PREFIXES = [
|
||||||
{ id: '1', name: 'order-ingest', method: 'POST', status: 'live', exchanges: 1243 },
|
'order', 'payment', 'inventory', 'customer', 'shipment', 'user', 'cart',
|
||||||
{ id: '2', name: 'payment-validate', method: 'POST', status: 'live', exchanges: 987 },
|
'refund', 'stock', 'email', 'webhook', 'cache', 'log', 'rate', 'health',
|
||||||
{ id: '3', name: 'inventory-check', method: 'GET', status: 'stale', exchanges: 432 },
|
'session', 'config', 'metrics', 'batch', 'audit', 'invoice', 'product',
|
||||||
{ id: '4', name: 'notify-customer', method: 'POST', status: 'live', exchanges: 876 },
|
'catalog', 'search', 'report', 'export', 'import', 'sync', 'alert', 'ticket',
|
||||||
{ id: '5', name: 'archive-order', method: 'PUT', status: 'dead', exchanges: 54 },
|
|
||||||
]
|
]
|
||||||
|
const ROUTE_SUFFIXES = [
|
||||||
|
'ingest', 'validate', 'check', 'notify', 'archive', 'track', 'auth',
|
||||||
|
'update', 'process', 'sync', 'dispatch', 'relay', 'invalidate', 'aggregate',
|
||||||
|
'limit', 'refresh', 'reload', 'export', 'import', 'purge',
|
||||||
|
]
|
||||||
|
const METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
|
||||||
|
const STATUSES = ['live', 'live', 'live', 'live', 'stale', 'stale', 'dead']
|
||||||
|
|
||||||
|
const TABLE_DATA: TableRow[] = Array.from({ length: 500 }, (_, i) => ({
|
||||||
|
id: String(i + 1),
|
||||||
|
name: `${ROUTE_PREFIXES[i % ROUTE_PREFIXES.length]}-${ROUTE_SUFFIXES[(i * 7) % ROUTE_SUFFIXES.length]}`,
|
||||||
|
method: METHODS[i % METHODS.length],
|
||||||
|
status: STATUSES[i % STATUSES.length],
|
||||||
|
exchanges: ((i * 1337 + 421) % 9990) + 10,
|
||||||
|
}))
|
||||||
|
|
||||||
const NOW = new Date()
|
const NOW = new Date()
|
||||||
const minsAgo = (n: number) => new Date(NOW.getTime() - n * 60 * 1000)
|
const minsAgo = (n: number) => new Date(NOW.getTime() - n * 60 * 1000)
|
||||||
@@ -468,12 +482,13 @@ export function CompositesSection() {
|
|||||||
title="DataTable"
|
title="DataTable"
|
||||||
description="Sortable, paginated table with row click, accent rows, and page size selector."
|
description="Sortable, paginated table with row click, accent rows, and page size selector."
|
||||||
>
|
>
|
||||||
<div style={{ width: '100%' }}>
|
<div style={{ width: '100%', height: 500, display: 'flex', flexDirection: 'column' }}>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
data={TABLE_DATA}
|
data={TABLE_DATA}
|
||||||
sortable
|
sortable
|
||||||
pageSize={5}
|
pageSize={25}
|
||||||
|
fillHeight
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</DemoCard>
|
</DemoCard>
|
||||||
|
|||||||
Reference in New Issue
Block a user