Skip to content

Commit eca4353

Browse files
authored
perf: lazy-load list pages only; wrap AccountNotFound in PageShell (#1672)
Eagerly loading detail pages avoids timing-sensitive E2E failures where structural conformance tests (breadcrumbs, PageShell wrapper) catch the page mid-Suspense and find both loaded and loading elements simultaneously. List pages are larger due to column/filter definitions and DataTable deps, making them the higher-value lazy-loading targets. Also wraps AccountNotFound in PageShell so the not-found state maintains consistent layout structure (space-y-6 with breadcrumb). Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent d461c7f commit eca4353

2 files changed

Lines changed: 27 additions & 53 deletions

File tree

frontend/src/App.tsx

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,71 +20,52 @@ import { CallbackPage } from '@/pages/callback'
2020
import { ProviderButton } from '@/components/auth/provider-button'
2121
import { AuthDivider } from '@/components/auth/auth-divider'
2222
import { DashboardPage } from '@/features/dashboard'
23-
24-
// Feature pages (lazy-loaded: split per route for faster initial load)
23+
import { AccountDetailPage } from '@/features/accounts/pages/[accountId]'
24+
import { PaymentDetailPage } from '@/features/payments/pages/payment-detail'
25+
import { BookingLogDetailPage } from '@/features/ledger/pages/booking-log-detail'
26+
import { PositionDetailPage } from '@/features/positions/pages/detail'
27+
import { PartyDetailPage } from '@/features/parties/pages/[partyId]'
28+
import { TenantDetailPage } from '@/features/tenants/pages/[tenantId]'
29+
import { ReconciliationDetailPage } from '@/features/reconciliation/pages/detail'
30+
import { StarlarkDetailPage } from '@/features/sagas/pages/detail'
31+
import { MappingDetailPage } from '@/features/mappings/pages/[mappingId]'
32+
import { InternalAccountDetailPage } from '@/features/internal-accounts/pages/[accountId]'
33+
import { UserDetailPage } from '@/features/identity/pages/user-detail-page'
34+
35+
// List pages (lazy-loaded: split per route for faster initial load)
2536
const AccountsPage = lazy(() =>
2637
import('@/features/accounts/pages/index').then((m) => ({ default: m.AccountsPage })),
2738
)
28-
const AccountDetailPage = lazy(() =>
29-
import('@/features/accounts/pages/[accountId]').then((m) => ({ default: m.AccountDetailPage })),
30-
)
3139
const PaymentsPage = lazy(() =>
3240
import('@/features/payments/pages/index').then((m) => ({ default: m.PaymentsPage })),
3341
)
34-
const PaymentDetailPage = lazy(() =>
35-
import('@/features/payments/pages/payment-detail').then((m) => ({ default: m.PaymentDetailPage })),
36-
)
3742
const LedgerPage = lazy(() =>
3843
import('@/features/ledger/pages/index').then((m) => ({ default: m.LedgerPage })),
3944
)
40-
const BookingLogDetailPage = lazy(() =>
41-
import('@/features/ledger/pages/booking-log-detail').then((m) => ({ default: m.BookingLogDetailPage })),
42-
)
4345
const PositionsPage = lazy(() =>
4446
import('@/features/positions/pages/index').then((m) => ({ default: m.PositionsPage })),
4547
)
46-
const PositionDetailPage = lazy(() =>
47-
import('@/features/positions/pages/detail').then((m) => ({ default: m.PositionDetailPage })),
48-
)
4948
const PartiesPage = lazy(() =>
5049
import('@/features/parties/pages/index').then((m) => ({ default: m.PartiesPage })),
5150
)
52-
const PartyDetailPage = lazy(() =>
53-
import('@/features/parties/pages/[partyId]').then((m) => ({ default: m.PartyDetailPage })),
54-
)
5551
const TenantsPage = lazy(() =>
5652
import('@/features/tenants/pages/index').then((m) => ({ default: m.TenantsPage })),
5753
)
58-
const TenantDetailPage = lazy(() =>
59-
import('@/features/tenants/pages/[tenantId]').then((m) => ({ default: m.TenantDetailPage })),
60-
)
6154
const ReconciliationPage = lazy(() =>
6255
import('@/features/reconciliation/pages/index').then((m) => ({ default: m.ReconciliationPage })),
6356
)
64-
const ReconciliationDetailPage = lazy(() =>
65-
import('@/features/reconciliation/pages/detail').then((m) => ({ default: m.ReconciliationDetailPage })),
66-
)
6757
const AuditLogPage = lazy(() =>
6858
import('@/features/audit/pages/index').then((m) => ({ default: m.AuditLogPage })),
6959
)
7060
const StarlarkConfigPage = lazy(() =>
7161
import('@/features/sagas/pages/index').then((m) => ({ default: m.StarlarkConfigPage })),
7262
)
73-
const StarlarkDetailPage = lazy(() =>
74-
import('@/features/sagas/pages/detail').then((m) => ({ default: m.StarlarkDetailPage })),
75-
)
7663
const MappingsPage = lazy(() =>
7764
import('@/features/mappings/pages/index').then((m) => ({ default: m.MappingsPage })),
7865
)
79-
const MappingDetailPage = lazy(() =>
80-
import('@/features/mappings/pages/[mappingId]').then((m) => ({ default: m.MappingDetailPage })),
81-
)
8266
const InternalAccountsPage = lazy(() =>
8367
import('@/features/internal-accounts/pages/index').then((m) => ({ default: m.InternalAccountsPage })),
8468
)
85-
const InternalAccountDetailPage = lazy(() =>
86-
import('@/features/internal-accounts/pages/[accountId]').then((m) => ({ default: m.InternalAccountDetailPage })),
87-
)
8869
const McpConfigPage = lazy(() =>
8970
import('@/features/mcp-config/pages/index').then((m) => ({ default: m.McpConfigPage })),
9071
)
@@ -94,9 +75,6 @@ const TransactionsPage = lazy(() =>
9475
const UsersListPage = lazy(() =>
9576
import('@/features/identity/pages/users-list-page').then((m) => ({ default: m.UsersListPage })),
9677
)
97-
const UserDetailPage = lazy(() =>
98-
import('@/features/identity/pages/user-detail-page').then((m) => ({ default: m.UserDetailPage })),
99-
)
10078
const CookbookPage = lazy(() =>
10179
import('@/features/cookbook/pages/index').then((m) => ({ default: m.CookbookPage })),
10280
)
@@ -391,25 +369,25 @@ function AppShellLayout() {
391369
{/* Tenant-scoped routes */}
392370
<Route path="/" element={guarded(<DashboardPage />)} />
393371
<Route path="/accounts" element={<FeatureGuard feature="accounts"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<AccountsPage />)}</Suspense></FeatureGuard>} />
394-
<Route path="/accounts/:accountId" element={<FeatureGuard feature="accounts"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<AccountDetailPage />)}</Suspense></FeatureGuard>} />
372+
<Route path="/accounts/:accountId" element={<FeatureGuard feature="accounts">{guarded(<AccountDetailPage />)}</FeatureGuard>} />
395373
<Route path="/internal-accounts" element={<FeatureGuard feature="internal-accounts"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<InternalAccountsPage />)}</Suspense></FeatureGuard>} />
396-
<Route path="/internal-accounts/:accountId" element={<FeatureGuard feature="internal-accounts"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<InternalAccountDetailPage />)}</Suspense></FeatureGuard>} />
374+
<Route path="/internal-accounts/:accountId" element={<FeatureGuard feature="internal-accounts">{guarded(<InternalAccountDetailPage />)}</FeatureGuard>} />
397375
<Route path="/payments" element={<FeatureGuard feature="payments"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<PaymentsPage />)}</Suspense></FeatureGuard>} />
398-
<Route path="/payments/:paymentOrderId" element={<FeatureGuard feature="payments"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<PaymentDetailPage />)}</Suspense></FeatureGuard>} />
376+
<Route path="/payments/:paymentOrderId" element={<FeatureGuard feature="payments">{guarded(<PaymentDetailPage />)}</FeatureGuard>} />
399377
<Route path="/transactions" element={<Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<TransactionsPage />)}</Suspense>} />
400378
<Route path="/positions" element={<FeatureGuard feature="positions"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<PositionsPage />)}</Suspense></FeatureGuard>} />
401-
<Route path="/positions/:logId" element={<FeatureGuard feature="positions"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<PositionDetailPage />)}</Suspense></FeatureGuard>} />
379+
<Route path="/positions/:logId" element={<FeatureGuard feature="positions">{guarded(<PositionDetailPage />)}</FeatureGuard>} />
402380
<Route path="/ledger" element={<FeatureGuard feature="ledger"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<LedgerPage />)}</Suspense></FeatureGuard>} />
403-
<Route path="/ledger/:bookingLogId" element={<FeatureGuard feature="ledger"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<BookingLogDetailPage />)}</Suspense></FeatureGuard>} />
381+
<Route path="/ledger/:bookingLogId" element={<FeatureGuard feature="ledger">{guarded(<BookingLogDetailPage />)}</FeatureGuard>} />
404382
<Route path="/parties" element={<FeatureGuard feature="parties"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<PartiesPage />)}</Suspense></FeatureGuard>} />
405-
<Route path="/parties/:partyId" element={<FeatureGuard feature="parties"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<PartyDetailPage />)}</Suspense></FeatureGuard>} />
383+
<Route path="/parties/:partyId" element={<FeatureGuard feature="parties">{guarded(<PartyDetailPage />)}</FeatureGuard>} />
406384
<Route path="/reconciliation" element={<FeatureGuard feature="reconciliation"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<ReconciliationPage />)}</Suspense></FeatureGuard>} />
407-
<Route path="/reconciliation/:runId" element={<FeatureGuard feature="reconciliation"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<ReconciliationDetailPage />)}</Suspense></FeatureGuard>} />
385+
<Route path="/reconciliation/:runId" element={<FeatureGuard feature="reconciliation">{guarded(<ReconciliationDetailPage />)}</FeatureGuard>} />
408386
<Route
409387
path="/starlark-config"
410388
element={<FeatureGuard feature="sagas"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<StarlarkConfigPage isPlatformAdmin={isPlatformAdmin} />)}</Suspense></FeatureGuard>}
411389
/>
412-
<Route path="/starlark-config/:definitionId" element={<FeatureGuard feature="sagas"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<StarlarkDetailPage />)}</Suspense></FeatureGuard>} />
390+
<Route path="/starlark-config/:definitionId" element={<FeatureGuard feature="sagas">{guarded(<StarlarkDetailPage />)}</FeatureGuard>} />
413391
<Route path="/market-data" element={<FeatureGuard feature="market-data"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<MarketDataPage />)}</Suspense></FeatureGuard>} />
414392
<Route path="/market-data/:datasetCode" element={<FeatureGuard feature="market-data"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<DatasetDetailPage />)}</Suspense></FeatureGuard>} />
415393
<Route path="/forecasting" element={<FeatureGuard feature="forecasting"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<ForecastingPage />)}</Suspense></FeatureGuard>} />
@@ -419,7 +397,7 @@ function AppShellLayout() {
419397
<Route path="/reference-data/nodes" element={<FeatureGuard feature="reference-data"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<NodesPage />)}</Suspense></FeatureGuard>} />
420398
<Route path="/reference-data/valuation-rules" element={<FeatureGuard feature="reference-data"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<ValuationRulesPage />)}</Suspense></FeatureGuard>} />
421399
<Route path="/gateway-mappings" element={<FeatureGuard feature="mappings"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<MappingsPage />)}</Suspense></FeatureGuard>} />
422-
<Route path="/gateway-mappings/:mappingId" element={<FeatureGuard feature="mappings"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<MappingDetailPage />)}</Suspense></FeatureGuard>} />
400+
<Route path="/gateway-mappings/:mappingId" element={<FeatureGuard feature="mappings">{guarded(<MappingDetailPage />)}</FeatureGuard>} />
423401
<Route path="/economy" element={<FeatureGuard feature="economy"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<EconomyOverviewPage />)}</Suspense></FeatureGuard>} />
424402
<Route path="/economy/create" element={<FeatureGuard feature="economy"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<EconomyCreatePage />)}</Suspense></FeatureGuard>} />
425403
<Route path="/economy/edit" element={<FeatureGuard feature="economy"><Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>{guarded(<EconomyEditPage />)}</Suspense></FeatureGuard>} />
@@ -448,9 +426,7 @@ function AppShellLayout() {
448426
path="/users/:userId"
449427
element={
450428
<AdminOnlyRoute>
451-
<Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>
452-
{guarded(<UserDetailPage />)}
453-
</Suspense>
429+
{guarded(<UserDetailPage />)}
454430
</AdminOnlyRoute>
455431
}
456432
/>
@@ -470,9 +446,7 @@ function AppShellLayout() {
470446
path="/tenants/:tenantId"
471447
element={
472448
<PlatformOnlyRoute>
473-
<Suspense fallback={<div className="h-96 animate-pulse rounded bg-muted" />}>
474-
{guarded(<TenantDetailPage />)}
475-
</Suspense>
449+
{guarded(<TenantDetailPage />)}
476450
</PlatformOnlyRoute>
477451
}
478452
/>

frontend/src/features/accounts/pages/[accountId].tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ import { useAccountDetail, useAccountPostings, useAccountLiens } from '../hooks'
2424

2525
function AccountNotFound({ accountId }: { accountId?: string }) {
2626
return (
27-
<div data-testid="account-not-found" className="p-6">
27+
<PageShell>
2828
<Breadcrumbs items={[{ label: 'Accounts', href: '/accounts' }, { label: 'Not found' }]} />
29-
<div className="mt-8 text-center">
29+
<div data-testid="account-not-found" className="mt-8 text-center">
3030
<h2 className="text-xl font-semibold">Account not found</h2>
3131
<p className="mt-2 text-sm text-muted-foreground">
3232
{accountId
3333
? `Account "${accountId}" was not found in any account service.`
3434
: 'The account you are looking for does not exist or has been removed.'}
3535
</p>
3636
</div>
37-
</div>
37+
</PageShell>
3838
)
3939
}
4040

0 commit comments

Comments
 (0)