refactor: strip AppShell+Sidebar wrappers from all page components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-02 18:09:16 +02:00
parent 8cd3c3a99d
commit 9fa7eb129d
8 changed files with 129 additions and 164 deletions

View File

@@ -1,9 +1,6 @@
import { useNavigate, useLocation } from 'react-router-dom'
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
import { Tabs } from '../../design-system/composites/Tabs/Tabs'
import { SIDEBAR_APPS } from '../../mocks/sidebar'
import styles from './Admin.module.css'
import type { ReactNode } from 'react'
@@ -23,7 +20,7 @@ export function AdminLayout({ title, children }: AdminLayoutProps) {
const location = useLocation()
return (
<AppShell sidebar={<Sidebar apps={SIDEBAR_APPS} />}>
<>
<TopBar
breadcrumb={[
{ label: 'Admin', href: '/admin' },
@@ -40,6 +37,6 @@ export function AdminLayout({ title, children }: AdminLayoutProps) {
<div className={styles.adminContent}>
{children}
</div>
</AppShell>
</>
)
}

View File

@@ -4,8 +4,6 @@ import { ChevronRight } from 'lucide-react'
import styles from './AgentHealth.module.css'
// Layout
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
// Composites
@@ -28,7 +26,6 @@ import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProv
// Mock data
import { agents, type AgentHealth as AgentHealthData } from '../../mocks/agents'
import { SIDEBAR_APPS } from '../../mocks/sidebar'
import { agentEvents } from '../../mocks/agentEvents'
// ── URL scope parsing ────────────────────────────────────────────────────────
@@ -317,19 +314,7 @@ export function AgentHealth() {
const isFullWidth = scope.level !== 'all'
return (
<AppShell
sidebar={<Sidebar apps={SIDEBAR_APPS} />}
detail={
selectedInstance ? (
<DetailPanel
open={panelOpen}
onClose={() => setPanelOpen(false)}
title={selectedInstance.name}
tabs={detailTabs}
/>
) : undefined
}
>
<>
<TopBar
breadcrumb={buildBreadcrumb(scope)}
environment="PRODUCTION"
@@ -454,6 +439,16 @@ export function AgentHealth() {
</div>
)}
</div>
</AppShell>
{/* Detail panel (portals itself) */}
{selectedInstance && (
<DetailPanel
open={panelOpen}
onClose={() => setPanelOpen(false)}
title={selectedInstance.name}
tabs={detailTabs}
/>
)}
</>
)
}

View File

@@ -4,8 +4,6 @@ import { ChevronRight } from 'lucide-react'
import styles from './AgentInstance.module.css'
// Layout
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
// Composites
@@ -28,7 +26,6 @@ import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProv
// Data
import { agents } from '../../mocks/agents'
import { SIDEBAR_APPS } from '../../mocks/sidebar'
import { agentEvents } from '../../mocks/agentEvents'
import { useState } from 'react'
@@ -127,12 +124,12 @@ export function AgentInstance() {
if (!agent) {
return (
<AppShell sidebar={<Sidebar apps={SIDEBAR_APPS} />}>
<>
<TopBar breadcrumb={[{ label: 'Agents', href: '/agents' }, { label: 'Not Found' }]} environment="PRODUCTION" user={{ name: 'hendrik' }} />
<div className={styles.content}>
<div className={styles.notFound}>Agent instance not found.</div>
</div>
</AppShell>
</>
)
}
@@ -153,7 +150,7 @@ export function AgentInstance() {
const statusColor = agent.status === 'live' ? 'success' : agent.status === 'stale' ? 'warning' : 'error'
return (
<AppShell sidebar={<Sidebar apps={SIDEBAR_APPS} />}>
<>
<TopBar
breadcrumb={[
{ label: 'Applications', href: '/apps' },
@@ -302,6 +299,6 @@ export function AgentInstance() {
</div>
</div>
</div>
</AppShell>
</>
)
}

View File

@@ -1,12 +1,9 @@
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
import { EmptyState } from '../../design-system/primitives/EmptyState/EmptyState'
import { SIDEBAR_APPS } from '../../mocks/sidebar'
export function ApiDocs() {
return (
<AppShell sidebar={<Sidebar apps={SIDEBAR_APPS} />}>
<>
<TopBar
breadcrumb={[{ label: 'API Documentation' }]}
environment="PRODUCTION"
@@ -17,6 +14,6 @@ export function ApiDocs() {
title="API Documentation"
description="API documentation coming soon."
/>
</AppShell>
</>
)
}

View File

@@ -1,15 +1,12 @@
import { useParams } from 'react-router-dom'
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
import { EmptyState } from '../../design-system/primitives/EmptyState/EmptyState'
import { SIDEBAR_APPS } from '../../mocks/sidebar'
export function AppDetail() {
const { id } = useParams<{ id: string }>()
return (
<AppShell sidebar={<Sidebar apps={SIDEBAR_APPS} />}>
<>
<TopBar
breadcrumb={[
{ label: 'Applications', href: '/apps' },
@@ -23,6 +20,6 @@ export function AppDetail() {
title="Application Detail"
description="Application detail view coming soon."
/>
</AppShell>
</>
)
}

View File

@@ -4,8 +4,6 @@ import { TrendingUp, TrendingDown, ArrowRight, ExternalLink, AlertTriangle } fro
import styles from './Dashboard.module.css'
// Layout
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
// Composites
@@ -287,106 +285,7 @@ export function Dashboard() {
const totalErrors = processorErrors.length + (hasExchangeError && processorErrors.length === 0 ? 1 : 0)
return (
<AppShell
sidebar={
<Sidebar apps={SIDEBAR_APPS} />
}
detail={
selectedExchange ? (
<DetailPanel
open={panelOpen}
onClose={() => setPanelOpen(false)}
title={`${selectedExchange.orderId}${selectedExchange.route}`}
>
{/* Link to full detail page */}
<div className={styles.panelSection}>
<button
className={styles.openDetailLink}
onClick={() => navigate(`/exchanges/${selectedExchange.id}`)}
>
Open full details <ArrowRight size={14} style={{ verticalAlign: 'middle' }} />
</button>
</div>
{/* Overview */}
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>Overview</div>
<div className={styles.overviewGrid}>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Status</span>
<span className={styles.statusCell}>
<StatusDot variant={statusToVariant(selectedExchange.status)} />
<span>{statusLabel(selectedExchange.status)}</span>
</span>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Duration</span>
<MonoText size="sm">{formatDuration(selectedExchange.durationMs)}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Route</span>
<span>{selectedExchange.route}</span>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Customer</span>
<MonoText size="sm">{selectedExchange.customer}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Agent</span>
<MonoText size="sm">{selectedExchange.agent}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Correlation</span>
<MonoText size="xs">{selectedExchange.correlationId}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Timestamp</span>
<MonoText size="xs">{selectedExchange.timestamp.toISOString()}</MonoText>
</div>
</div>
</div>
{/* Errors */}
{totalErrors > 0 && (
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>
Errors
{totalErrors > 1 && (
<Badge label={`+${totalErrors - 1} more`} color="error" variant="outlined" />
)}
</div>
<div className={styles.errorBlock}>
<div className={styles.errorClass}>
{selectedExchange.errorClass ?? processorErrors[0]?.name}
</div>
<div className={styles.errorMessage}>
{selectedExchange.errorMessage ?? `Failed at processor: ${processorErrors[0]?.name}`}
</div>
</div>
</div>
)}
{/* Route Flow */}
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>Route Flow</div>
<RouteFlow nodes={routeNodes} />
</div>
{/* Processor Timeline */}
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>
Processor Timeline
<span className={styles.panelSectionMeta}>{formatDuration(selectedExchange.durationMs)}</span>
</div>
<ProcessorTimeline
processors={selectedExchange.processors}
totalMs={selectedExchange.durationMs}
/>
</div>
</DetailPanel>
) : undefined
}
>
<>
{/* Top bar */}
<TopBar
breadcrumb={
@@ -444,6 +343,101 @@ export function Dashboard() {
{/* Shortcuts bar */}
<ShortcutsBar shortcuts={SHORTCUTS} />
</AppShell>
{/* Detail panel (portals itself) */}
{selectedExchange && (
<DetailPanel
open={panelOpen}
onClose={() => setPanelOpen(false)}
title={`${selectedExchange.orderId}${selectedExchange.route}`}
>
{/* Link to full detail page */}
<div className={styles.panelSection}>
<button
className={styles.openDetailLink}
onClick={() => navigate(`/exchanges/${selectedExchange.id}`)}
>
Open full details <ArrowRight size={14} style={{ verticalAlign: 'middle' }} />
</button>
</div>
{/* Overview */}
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>Overview</div>
<div className={styles.overviewGrid}>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Status</span>
<span className={styles.statusCell}>
<StatusDot variant={statusToVariant(selectedExchange.status)} />
<span>{statusLabel(selectedExchange.status)}</span>
</span>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Duration</span>
<MonoText size="sm">{formatDuration(selectedExchange.durationMs)}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Route</span>
<span>{selectedExchange.route}</span>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Customer</span>
<MonoText size="sm">{selectedExchange.customer}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Agent</span>
<MonoText size="sm">{selectedExchange.agent}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Correlation</span>
<MonoText size="xs">{selectedExchange.correlationId}</MonoText>
</div>
<div className={styles.overviewRow}>
<span className={styles.overviewLabel}>Timestamp</span>
<MonoText size="xs">{selectedExchange.timestamp.toISOString()}</MonoText>
</div>
</div>
</div>
{/* Errors */}
{totalErrors > 0 && (
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>
Errors
{totalErrors > 1 && (
<Badge label={`+${totalErrors - 1} more`} color="error" variant="outlined" />
)}
</div>
<div className={styles.errorBlock}>
<div className={styles.errorClass}>
{selectedExchange.errorClass ?? processorErrors[0]?.name}
</div>
<div className={styles.errorMessage}>
{selectedExchange.errorMessage ?? `Failed at processor: ${processorErrors[0]?.name}`}
</div>
</div>
</div>
)}
{/* Route Flow */}
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>Route Flow</div>
<RouteFlow nodes={routeNodes} />
</div>
{/* Processor Timeline */}
<div className={styles.panelSection}>
<div className={styles.panelSectionTitle}>
Processor Timeline
<span className={styles.panelSectionMeta}>{formatDuration(selectedExchange.durationMs)}</span>
</div>
<ProcessorTimeline
processors={selectedExchange.processors}
totalMs={selectedExchange.durationMs}
/>
</div>
</DetailPanel>
)}
</>
)
}

View File

@@ -3,8 +3,6 @@ import { useParams, useNavigate } from 'react-router-dom'
import styles from './ExchangeDetail.module.css'
// Layout
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
// Composites
@@ -22,7 +20,7 @@ import { InfoCallout } from '../../design-system/primitives/InfoCallout/InfoCall
// Mock data
import { exchanges } from '../../mocks/exchanges'
import { SIDEBAR_APPS, buildRouteToAppMap } from '../../mocks/sidebar'
import { buildRouteToAppMap } from '../../mocks/sidebar'
const ROUTE_TO_APP = buildRouteToAppMap()
@@ -196,11 +194,7 @@ export function ExchangeDetail() {
// Not found state
if (!exchange) {
return (
<AppShell
sidebar={
<Sidebar apps={SIDEBAR_APPS} />
}
>
<>
<TopBar
breadcrumb={[
{ label: 'Applications', href: '/apps' },
@@ -213,7 +207,7 @@ export function ExchangeDetail() {
<div className={styles.content}>
<InfoCallout variant="warning">Exchange "{id}" not found in mock data.</InfoCallout>
</div>
</AppShell>
</>
)
}
@@ -229,11 +223,7 @@ export function ExchangeDetail() {
const isSelectedFailed = selectedProc?.status === 'fail'
return (
<AppShell
sidebar={
<Sidebar apps={SIDEBAR_APPS} />
}
>
<>
{/* Top bar */}
<TopBar
breadcrumb={[
@@ -454,6 +444,6 @@ export function ExchangeDetail() {
)}
</div>
</AppShell>
</>
)
}

View File

@@ -3,8 +3,6 @@ import { useNavigate, useParams } from 'react-router-dom'
import styles from './Routes.module.css'
// Layout
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
// Composites
@@ -33,7 +31,7 @@ import {
type RouteMetricRow,
} from '../../mocks/metrics'
import { routes } from '../../mocks/routes'
import { SIDEBAR_APPS, buildRouteToAppMap } from '../../mocks/sidebar'
import { buildRouteToAppMap } from '../../mocks/sidebar'
const ROUTE_TO_APP = buildRouteToAppMap()
@@ -410,7 +408,7 @@ export function Routes() {
// ── Route detail view ───────────────────────────────────────────────────────
if (routeId && appId && routeDef) {
return (
<AppShell sidebar={<Sidebar apps={SIDEBAR_APPS} />}>
<>
<TopBar
breadcrumb={breadcrumb}
environment="PRODUCTION"
@@ -448,13 +446,13 @@ export function Routes() {
<RouteFlow nodes={routeFlowNodes} />
</div>
</div>
</AppShell>
</>
)
}
// ── Top level / Application level view ──────────────────────────────────────
return (
<AppShell sidebar={<Sidebar apps={SIDEBAR_APPS} />}>
<>
<TopBar
breadcrumb={breadcrumb}
environment="PRODUCTION"
@@ -533,6 +531,6 @@ export function Routes() {
</Card>
</div>
</div>
</AppShell>
</>
)
}