This document gives a concise, accurate overview of how the Disciplr frontend is wired together: routing, provider/context nesting, global stores, the utility layer, and the data seam where mock data will be swapped for a real backend.
It is meant as a map. For deeper detail on specific subsystems, follow the cross-links to the focused docs.
The app boots from src/main.tsx, which renders
src/App.tsx. App.tsx defines the provider tree and the
route table.
Provider/wrapper nesting, outermost to innermost (verified against
src/App.tsx and src/components/Layout.tsx):
ThemeProvider (src/context/ThemeContext.tsx)
WalletProvider (src/context/WalletContext.tsx)
BrowserRouter (react-router-dom)
ErrorBoundary (src/components/ErrorBoundary.tsx)
Layout (src/components/Layout.tsx) — header, nav, TrustlineBanner, <main>
Routes (route table below)
Route element lazy routes are individually wrapped in <Suspense>
Layout renders the shared chrome (site header, desktop nav, mobile drawer,
notification bell, wallet connect button, and the TrustlineBanner) and renders
the active route inside <main> via its children prop.
All routes are mounted in src/App.tsx. Most page components
are eagerly imported at the top of the file. Analytics and Notification
are lazy (React.lazy(() => import(...))) and each is wrapped in its own
<Suspense> with a full-height Skeleton fallback.
| Path | Page component | Loading | Purpose |
|---|---|---|---|
/ |
Home |
eager | Landing overview and vault entry points |
/dashboard |
Dashboard |
eager | Vault and activity dashboard |
/vaults |
Vaults |
eager | Vault listing |
/vaults/create |
CreateVault |
eager | Vault creation form |
/vaults/:id |
VaultDetail |
eager | Milestones, addresses, status, transactions for one vault |
/vaults/:id/transactions |
VaultTransactions |
eager | Per-vault transaction explorer |
/transactions |
VaultTransactions |
eager | All-vaults transaction explorer (same component) |
/verifier |
VerifierDashboard |
eager | Verifier overview |
/verifier/queue |
PendingValidations |
eager | Pending validation queue |
/verifier/queue/:vaultId |
ValidationDetail |
eager | Validation decision details |
/verifier/history |
ValidationHistory |
eager | Historical validation records |
/analytics |
Analytics |
lazy | Analytics charts and export workflows |
/notifications |
Notification |
lazy | Notification feed |
* |
NotFound |
eager | Catch-all fallback |
Note that /vaults/:id/transactions and /transactions both render the same
VaultTransactions page component.
src/pages/NotificationSettings.tsxexists but is not registered as a route inApp.tsx. TheNotificationpage links to/notification/settings, which currently has no matching route.
Two React contexts hold app-wide state that is not appropriate for Zustand (theme and wallet session).
ThemeContext — src/context/ThemeContext.tsx
- State:
theme('light' | 'dark'), plustoggleTheme()andsetTheme(theme). - Initial theme reads
localStorage(disciplr-themekey), falling back to the OSprefers-color-scheme. Storage failures fall back to an in-memory value. - Writes the active theme to
document.documentElement[data-theme]and persists it. Auto-follows the system theme only while the user has not made a manual choice. - Consume via the
useTheme()hook (throws outside the provider).
WalletContext — src/context/WalletContext.tsx
- State:
address,network('TESTNET' | 'PUBLIC'),balance,balanceStatus(idle | loading | success | no_trustline | error),balanceError,isConnecting,error. - Actions:
connect(),disconnect(),checkConnection(). - Integrates Freighter through
@stellar/freighter-api(isAllowed,setAllowed,requestAccess,getAddress,getNetworkDetails) and fetches the USDC balance viasrc/utils/horizon.ts. In-flight balance fetches are cancelled with anAbortController. - Consume via the
useWallet()hook (throws outside the provider).
Global client state that is shared across unrelated routes lives in Zustand
stores. See docs/STORES.md for the full store contracts.
useNotification— the notification feed:notification[], derivedunreadCount, andsetNotification,markRead(id),markAllRead(). Seeded fromgetNotifications()example data.useVerifierStore— verifier workflow state:pendingValidations[]andvalidationHistory[](ValidationTaskrecords), withapproveValidation,rejectValidation, and thebatchApprove/batchRejectmutators. Batch mutators are implemented in terms of the single-task mutators. Seeded with mock pending/history data.- Re-exports everything from
notificationPreferences.ts.
useNotificationPreferences— user notification settings (email,push,frequency,quietHours) with per-field setters andreset(). Uses Zustand'spersistmiddleware (localStoragekeynotification-preferences).
Utility Layer — src/utils/
Pure helpers and small framework-agnostic modules. Notable ones:
horizon.ts— Stellar Horizon access: per-network Horizon URLs and USDC issuers, plusfetchUsdcBalance(with theHorizonBalanceErrortype for account-not-found / request / response errors).csv.ts— CSV export helpers for validation tasks, transactions, and analytics rows, including numeric-cell normalization and cell escaping.paginate.ts— validation-history filtering (status / query / date range / milestone) and generic pagination (PaginationResult<T>).windowRange.ts— lightweight virtual-list math (windowRange); windowing only engages pastWINDOW_THRESHOLD(50) rows, rendering at mostWINDOW_SIZE(40) rows.vaultValidation.ts— create-vault form validation: Stellar address, USDC amount, and future-deadline checks (validateCreateVault).
Other helpers in this folder cover charting/typography, motion/reduced-motion, explorer links, address formatting, and logging.
Page components do not read mock data directly. They depend on a single, Promise-based service so the mock backend can later be replaced with real Soroban/Horizon calls without touching the pages.
src/services/vaultService.ts— exposeslistVaults(),getVault(id),getTransactions(id), andlistAllActivity(). Mock datasets (MASTER_VAULTS,MASTER_ACTIVITY) live inside this file; each function is marked with aSEAMcomment indicating the Soroban/Horizon call that should replace its internals.- Shared domain types live in
src/types/vault.ts(Vault,VaultStatus,MilestoneStatus,TxType,TxStatus, etc.).
See docs/VAULT_DATA_LAYER.md for the full data-layer
contract and migration guide.
src/
|-- App.tsx # Provider tree + route table
|-- main.tsx # ReactDOM entry point
|-- context/ # React contexts (ThemeContext, WalletContext)
|-- Zustand/ # Global stores (Store.ts, notificationPreferences.ts)
|-- pages/ # Route-level page components
|-- components/ # Reusable UI (Layout, Wallet/, Notification/, etc.)
|-- services/ # vaultService.ts — the data seam
|-- types/ # Shared domain types (vault.ts)
`-- utils/ # Pure helpers (horizon, csv, paginate, windowRange, ...)
docs/STORES.md— Zustand store contracts.docs/VAULT_DATA_LAYER.md— data layer and backend migration guide.docs/TESTING.md— contributor testing guide (Vitest + Jest).design-system/documentation/getting-started.md— design-system tokens and how the app consumes them.design-system/README.md— design-system package entry point.