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>
223 lines
13 KiB
Markdown
223 lines
13 KiB
Markdown
# 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/<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:
|
|
|
|
```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/<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.
|