refactor: unify /apps routing with application and route filtering
All checks were successful
Build & Publish / publish (push) Successful in 44s
All checks were successful
Build & Publish / publish (push) Successful in 44s
- Table columns: Status, Route, Application, Started (yyyy-mm-dd hh:mm:ss), Duration, Agent (removed Order ID and Customer) - /apps shows all exchanges, /apps/:id filters by application, /apps/:id/:routeId filters by application and route - Route paths changed from /routes/:id to /apps/:appId/:routeId across sidebar, search, breadcrumbs, metrics, and exchange detail - Added buildRouteToAppMap utility for route→application lookup Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,14 +69,9 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.routeGroup {
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* Customer text */
|
||||
.customerText {
|
||||
/* Application column */
|
||||
.appName {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProv
|
||||
// Mock data
|
||||
import { exchanges, type Exchange } from '../../mocks/exchanges'
|
||||
import { kpiMetrics } from '../../mocks/metrics'
|
||||
import { SIDEBAR_APPS } from '../../mocks/sidebar'
|
||||
import { SIDEBAR_APPS, buildRouteToAppMap } from '../../mocks/sidebar'
|
||||
|
||||
// Route → Application lookup
|
||||
const ROUTE_TO_APP = buildRouteToAppMap()
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
function formatDuration(ms: number): string {
|
||||
@@ -38,7 +41,13 @@ function formatDuration(ms: number): string {
|
||||
}
|
||||
|
||||
function formatTimestamp(date: Date): string {
|
||||
return date.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
|
||||
const y = date.getFullYear()
|
||||
const mo = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const d = String(date.getDate()).padStart(2, '0')
|
||||
const h = String(date.getHours()).padStart(2, '0')
|
||||
const mi = String(date.getMinutes()).padStart(2, '0')
|
||||
const s = String(date.getSeconds()).padStart(2, '0')
|
||||
return `${y}-${mo}-${d} ${h}:${mi}:${s}`
|
||||
}
|
||||
|
||||
function statusToVariant(status: Exchange['status']): 'success' | 'error' | 'running' | 'warning' {
|
||||
@@ -77,25 +86,15 @@ const COLUMNS: Column<Exchange>[] = [
|
||||
header: 'Route',
|
||||
sortable: true,
|
||||
render: (_, row) => (
|
||||
<div>
|
||||
<div className={styles.routeName}>{row.route}</div>
|
||||
<div className={styles.routeGroup}>{row.routeGroup}</div>
|
||||
</div>
|
||||
<span className={styles.routeName}>{row.route}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'orderId',
|
||||
header: 'Order ID',
|
||||
key: 'routeGroup',
|
||||
header: 'Application',
|
||||
sortable: true,
|
||||
render: (_, row) => (
|
||||
<MonoText size="sm">{row.orderId}</MonoText>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'customer',
|
||||
header: 'Customer',
|
||||
render: (_, row) => (
|
||||
<MonoText size="xs" className={styles.customerText}>{row.customer}</MonoText>
|
||||
<span className={styles.appName}>{ROUTE_TO_APP.get(row.route) ?? row.routeGroup}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -145,7 +144,7 @@ const SHORTCUTS = [
|
||||
|
||||
// ─── Dashboard component ──────────────────────────────────────────────────────
|
||||
export function Dashboard() {
|
||||
const { id: appId } = useParams<{ id: string }>()
|
||||
const { id: appId, routeId } = useParams<{ id: string; routeId: string }>()
|
||||
const [selectedId, setSelectedId] = useState<string | undefined>()
|
||||
const [panelOpen, setPanelOpen] = useState(false)
|
||||
const [selectedExchange, setSelectedExchange] = useState<Exchange | null>(null)
|
||||
@@ -160,11 +159,12 @@ export function Dashboard() {
|
||||
return new Set(app.routes.map((r) => r.id))
|
||||
}, [appId])
|
||||
|
||||
// Scope all data to the selected app
|
||||
// Scope all data to the selected app (and optionally route)
|
||||
const scopedExchanges = useMemo(() => {
|
||||
if (routeId) return exchanges.filter((e) => e.route === routeId)
|
||||
if (!appRouteIds) return exchanges
|
||||
return exchanges.filter((e) => appRouteIds.has(e.route))
|
||||
}, [appRouteIds])
|
||||
}, [appRouteIds, routeId])
|
||||
|
||||
// Filter exchanges (scoped + global filters)
|
||||
const filteredExchanges = useMemo(() => {
|
||||
@@ -313,9 +313,12 @@ export function Dashboard() {
|
||||
>
|
||||
{/* Top bar */}
|
||||
<TopBar
|
||||
breadcrumb={appId
|
||||
? [{ label: 'Applications', href: '/apps' }, { label: appId }]
|
||||
: [{ label: 'Applications' }]
|
||||
breadcrumb={
|
||||
routeId
|
||||
? [{ label: 'Applications', href: '/apps' }, { label: appId!, href: `/apps/${appId}` }, { label: routeId }]
|
||||
: appId
|
||||
? [{ label: 'Applications', href: '/apps' }, { label: appId }]
|
||||
: [{ label: 'Applications' }]
|
||||
}
|
||||
environment="PRODUCTION"
|
||||
user={{ name: 'hendrik' }}
|
||||
|
||||
Reference in New Issue
Block a user