40227-vm/frontend/docs/frontend-architecture.md
Dmitri d4a5378adf Refactor: migrate frontend to Vite/React, add product backend modules
Frontend:
- Replace Next.js with Vite + React + TypeScript
- Add new component architecture (app-shell, sidebar, dashboard modules)
- Implement product modules: FRAME, safety protocols, walkthrough checkin,
  campus/staff attendance, personality quiz, sign language, classroom timer
- Add shadcn/ui component library with Tailwind CSS
- Remove legacy generated components, stores, and pages

Backend:
- Add product migrations: frame_entries, user_progress, safety_quiz_results,
  walkthrough_checkins, communication_events, personality_quiz_results,
  campus_attendance_config/summaries, staff_attendance_records, content_catalog
- Add corresponding models, services, and routes
- Implement cookie-based auth with refresh token rotation
- Add content catalog seeder with product content
- Migrate to ESLint flat config
- Switch from yarn to npm

Infrastructure:
- Update .gitignore for new tooling
- Add project documentation (CLAUDE.md, docs/)
- Remove deprecated config files and yarn.lock

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-09 15:18:23 +02:00

13 KiB

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:

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:

API/Data Access -> Business Logic
API/Data Access -> View
Business Logic -> View

Feature Structure

For new or updated product modules, use this shape:

frontend/src/business/<module>/
  hooks.ts
  mappers.ts
  selectors.ts
  validators.ts
  types.ts

frontend/src/shared/api/<module>.ts
frontend/src/shared/types/<module>.ts
frontend/src/components/<module>/

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:

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/<module>/.
  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.