# Frontend Architecture ## Purpose The frontend uses a three-layer architecture: - View layer - Business logic layer - API/data access layer The goal is to keep UI components thin, keep business rules testable, and keep all backend communication centralized. ## Layer 1: View Location: - `frontend/src/components/` - `frontend/src/pages/` Responsibilities: - Render UI. - Own visual composition and layout. - Call business-layer hooks/actions. - Show loading, empty, and error states from business-layer state. - Keep form markup and user interaction wiring close to the component. - Compose shared UI primitives from `frontend/src/components/ui/` for common controls and repeated states. View components must not: - Call `fetch` directly. - Import backend API modules directly. - Contain tenant, role, permission, or workflow rules. - Transform backend DTOs into product state when that transformation belongs to business logic. - Hide failed persisted workflows with static data. ## Layer 2: Business Logic Location: - `frontend/src/business/` - `frontend/src/shared/business/` Responsibilities: - Own React Query hooks for server state. - Own local workflow state when it is not purely visual. - Transform backend DTOs into UI-facing view models. - Apply role, permission, tenant, campus, and workflow rules received from backend contracts. - Own calculations, filters, sorting, validation helpers, and derived state. - Coordinate multiple API calls for one user workflow. Business logic may import: - `frontend/src/shared/api/` - `frontend/src/shared/types/` - `frontend/src/shared/constants/` - `frontend/src/shared/business/` Business logic must not: - Render JSX. - Read or write secrets. - Duplicate backend authorization or tenant enforcement. - Create another HTTP client. Shared business helpers should be used for repeated cross-module mechanics. `frontend/src/shared/business/queryMutations.ts` centralizes React Query mutation invalidation, `frontend/src/shared/business/apiListRows.ts` centralizes `ApiListResponse.rows` extraction and mapping, and `frontend/src/shared/business/queryState.ts` centralizes multi-query loading/error aggregation. ## Layer 3: API/Data Access Location: - `frontend/src/shared/api/` - `frontend/src/shared/types/` Responsibilities: - Own all HTTP calls to `backend/`. - Send backend-owned auth cookies through the shared HTTP client. - Define request and response types. - Preserve backend errors and expose them to business logic. - Keep endpoint paths and payload shapes in one place. API/data access modules must not: - Render UI. - Own component state. - Apply product workflow decisions that belong to business logic. - Return mock/static data for persisted workflows. - Swallow backend failures. ## Import Direction Allowed direction: ```text View -> Business Logic -> API/Data Access -> Backend ``` Shared constants and shared types may be imported by any layer. ## Error Handling Backend/API errors are parsed in `frontend/src/shared/api/httpClient.ts` and normalized for UI rendering through `frontend/src/shared/errors/errorMessages.ts`. Feature hooks and components must use the shared error formatter instead of reading `.message` directly or creating local formatter helpers. Detailed rules live in `frontend/docs/error-handling.md`. `npm run test` includes `frontend/src/shared/architecture/import-boundaries.test.ts`, which enforces these import boundaries and keeps runtime data access centralized in the shared API layer. Disallowed direction: ```text API/Data Access -> Business Logic API/Data Access -> View Business Logic -> View ``` ## Feature Structure For new or updated product modules, use this shape: ```text frontend/src/business// hooks.ts mappers.ts selectors.ts validators.ts types.ts frontend/src/shared/api/.ts frontend/src/shared/types/.ts frontend/src/components// ``` Only create files that are actually needed for the module. ## Routing The frontend uses React Router. Top-level URL route declarations live in a typed object route config instead of inline JSX in `App.tsx`. Target structure: ```text frontend/src/app/AppProviders.tsx frontend/src/app/AppRouter.tsx frontend/src/app/appRoutes.tsx frontend/src/app/ModuleRouteGuard.tsx frontend/src/app/shellOutletContext.ts frontend/src/shared/constants/routes.ts frontend/src/shared/constants/moduleRoutes.ts ``` Rules: - `App.tsx` composes providers and renders the router only. - `appRoutes.tsx` owns top-level and product URL route objects. - Product routes are lazy-loaded route elements under the shared app shell layout. - Route page adapters stay thin and delegate product behavior to business hooks. - `AppRouter.tsx` uses `useRoutes(appRoutes)`. - `APP_ROUTE_PATHS` owns path constants and module route metadata maps each `ModuleId` to exactly one route path. - The browser URL is the source of truth for the active product module. - Sidebar, footer, dashboard actions, and other module navigation should navigate by route path instead of storing active module state. - `/login` remains the deterministic destination for expired access plus expired refresh sessions. - Restricted module routes redirect to `/dashboard`. ## Update Rule When updating an existing module: 1. Add or update backend API endpoints first. 2. Add typed API functions in `frontend/src/shared/api/`. 3. Add business hooks and mappers in `frontend/src/business//`. 4. Update view components to call the business layer. 5. Remove direct data access from the component. 6. Add or update module documentation. ## Current Baseline The active frontend already has: - React 19, Vite 8, TypeScript 6, Tailwind 4, Vitest 4, and ESLint 10 as the current active tooling baseline. - Current top-level URL routes are `/`, `/login`, and `*`; product module routes are nested under the shared shell layout and lazy-loaded from `frontend/src/pages/modules/`. - View components under `frontend/src/components/` and `frontend/src/pages/`. - Shared backend API foundation under `frontend/src/shared/api/`. - Shared frontend constants and types under `frontend/src/shared/`. - Cross-module UI-facing product types under `frontend/src/shared/types/app.ts`. - Static app navigation/media config under `frontend/src/shared/constants/appData.ts` and static personality catalog metadata under `frontend/src/shared/constants/personalityCatalog.ts`. - Backend-owned campus records and branding load through `frontend/src/shared/api/campuses.ts` and `frontend/src/business/campuses/`; frontend keeps only generic campus helpers and allowed Tailwind branding tokens. - Test-only seed records under `frontend/src/test-seeds/`; runtime code must not import that directory. - Theme names, default theme values, CSS class names, and media query constants under `frontend/src/shared/constants/theme.ts`; global light/dark CSS tokens remain in `frontend/src/index.css` and Tailwind maps to those variables in `frontend/tailwind.config.ts`. - React Query keys, UI timing values, storage keys, and sidebar runtime constants live in dedicated files under `frontend/src/shared/constants/`. - Auth/profile session logic under `frontend/src/business/auth/`, with `AuthContext` acting as a thin provider. The auth transport is backend-owned HttpOnly cookie auth documented in `backend/docs/cookie-auth.md` and `frontend/docs/auth-integration.md`. - App shell state, access selection, campus display lookup, mobile overlay visibility, shell outlet context, and prepared Sidebar/TopBar/GuestBanner/Footer props live under `frontend/src/business/app-shell/`. The shared shell layout remains a thin view composition in `frontend/src/components/AppLayout.tsx`. - Top bar shell state under `frontend/src/business/top-bar/`, with search, badges, notifications, profile menu, and sign-in modal composition split under `frontend/src/components/top-bar/`. - FRAME entries under `frontend/src/business/frame/`, with typed API calls in `frontend/src/shared/api/frame.ts` and explicit empty/error states in the view. - Current-user progress under `frontend/src/business/user-progress/`, with typed API calls in `frontend/src/shared/api/userProgress.ts` for learned signs and zone check-ins. - Safety quiz results under `frontend/src/business/safety-quiz/`, with typed API calls in `frontend/src/shared/api/safetyQuizResults.ts`. - Walk-through check-ins under `frontend/src/business/walkthrough/`, with typed API calls in `frontend/src/shared/api/walkthrough.ts`, shared constants in `frontend/src/shared/constants/walkthrough.ts`, and summary calculations in typed selectors. - Communications under `frontend/src/business/communications/`, with typed API calls in `frontend/src/shared/api/communications.ts` for parent messages, internal alerts, and dashboard upcoming events. - EI/personality results under `frontend/src/business/personality/`, with typed API calls in `frontend/src/shared/api/personality.ts`, DTO mappers, distribution selectors, workflow-specific hook files, and explicit loading/error states in the view. - Campus attendance config and daily summaries under `frontend/src/business/campus-attendance/`, with typed API calls in `frontend/src/shared/api/campusAttendance.ts`, DTO mappers, summary selectors, and explicit loading/error states in the view. - Staff attendance snapshot and director staff counts under `frontend/src/business/staff-attendance/`, with typed API calls in `frontend/src/shared/api/staffAttendance.ts`, DTO mappers, rollup selectors, and explicit loading/error states in the view. - Handbook policies under `frontend/src/business/policies/`, with typed document API calls in `frontend/src/shared/api/documents.ts`, DTO mappers, selectors, and explicit loading/error states in the view. - Classroom timer built-in sounds are local Web Audio behavior. AI-generated sounds are not exposed until a backend audio provider contract exists. - UI component variants live in dedicated non-component files such as `frontend/src/components/ui/button-variants.ts`, `badge-variants.ts`, `toggle-variants.ts`, and `navigation-menu-variants.ts`. - Loading, empty, and error state panels are centralized through `frontend/src/components/ui/state-panel.tsx`, with tone/size/alignment variants in `frontend/src/components/ui/state-panel-variants.ts`. - Repeated module headings use `frontend/src/components/ui/module-header.tsx`; simple native dropdowns use `frontend/src/components/ui/native-select.tsx`. - Reusable UI/context hooks live outside provider component files, including `frontend/src/components/theme-context.ts`, `frontend/src/components/ui/form-field-context.ts`, `frontend/src/components/ui/sidebar-context.ts`, and `frontend/src/contexts/auth-context.ts`. - The tracked large framework components have been split into thin wrappers and focused view pieces backed by business hooks/selectors. - Frontend TypeScript runs in strict mode through `npm run typecheck`; `npm run build` runs typecheck before Vite. - Frontend unit tests run through `npm run test` with Vitest. Current coverage includes business-layer selectors and mappers for app-shell/sidebar, auth, campuses, campus attendance, classroom support, classroom timer, communications, community, dashboard, director dashboard, ESA funding, FRAME, personality, policies, safety quiz, sign language, staff attendance, top bar, user progress, vocational, walk-through check-in/summary/form workflows, and zones. - `npm run test` also enforces API/business/view import boundaries through `frontend/src/shared/architecture/import-boundaries.test.ts`. - Frontend backend-free smoke tests run through `npm run test:e2e` with Playwright. Current smoke coverage verifies teacher, director, and superintendent guest navigation/access paths. - Frontend backend-seeded content tests run through `npm run test:e2e:content` with Playwright after backend migrations, seeders, and the backend server are running. - Frontend dependency verification is clean: `npm run lint`, `npm run test`, `npm run build`, `npm audit --audit-level=low`, and `npm outdated` pass for stable package releases. ## Known Remaining Gaps - New or changed framework wrappers should follow the same thin-view plus business-hook pattern. - New product routes should be added to module route metadata, `frontend/src/app/appRoutes.tsx`, and covered by route metadata tests. - TypeScript compiler strictness is enabled for the current baseline. Keep future slices compatible with `strict`, `noUnusedLocals`, and `noUnusedParameters`. - Unit test coverage exists for route config, module route metadata, API/data-access behavior, auth refresh/retry behavior, business-layer selector/mapper/report slices, and import-boundary guardrails. Guest-role Playwright smoke tests cover the current backend-free staff/director/superintendent paths.