4.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project
aplp.frontend.react — React frontend for APLP (Adaptive Personal Learning Platform), the learner-facing app (aplp-web). Thin client only: it calls the aplp.backend.spring REST API and holds no domain business rules (mastery/progress/adaptive decisions all come from the backend).
Commands
npm run dev # Vite dev server
npm run build # tsc -b (typecheck) + vite build
npm run typecheck # tsc -b --noEmit
npm run lint # oxlint
npm run lint:fix
npm run format # prettier --write .
npm run format:check
npm test # vitest run (single run)
npm run test:watch # vitest watch mode
npm run test:coverage
Run a single test file: npx vitest run src/features/auth/store/authStore.test.ts (or npx vitest src/path/to/File.test.tsx for watch mode on one file).
Backend must be running locally for the app to function against real APIs (default VITE_API_BASE_URL=http://localhost:8080/api/v1, see .env.example).
Architecture
src/
├── app/ # entry, router (App.tsx), layouts, top-level pages, providers (QueryProvider)
├── api/ # axios client (src/api/client.ts), tokenStorage
├── features/ # vertical feature modules: auth (built), learner/course/learning/assessment/adaptive/... (planned per docs/PHASES.md)
│ └── <feature>/
│ ├── api/ # calls to backend endpoints for this feature
│ ├── components/
│ ├── hooks/
│ ├── pages/ # route-level views
│ ├── store/ # zustand store, if the feature needs one
│ └── types.ts
├── shared/ # shared components (src/shared/components), config (src/shared/config/env.ts)
└── types/ # cross-feature types / DTO contracts (src/types/api.ts)
Path alias @/ → src/ (configured in vite.config.ts and tsconfig).
Data flow
UI component → hooks (React Query for server state, Zustand for cross-app client state, useState for local state) → src/api axios wrapper → backend REST API.
- Server state (fetched/cached data) always goes through TanStack Query — never duplicate it into a Zustand store.
- All backend calls go through
src/api; don't call axios/fetch directly from components.
Auth flow
- Access token: in-memory only (
src/api/tokenStorage.ts), never persisted — XSS mitigation. Cleared on reload. - Refresh token: currently stored in
localStoragebytokenStorage.ts(note: this differs fromdocs/ARCHITECTURE.md, which documents the original design as an HttpOnly cookie managed entirely by the backend — treat the code as source of truth, not the doc, if the two disagree). - The axios client (
src/api/client.ts) auto-handles401s: on a non-auth-endpoint 401 it calls the registeredRefreshHandleronce (deduped via a sharedrefreshPromise), retries the original request with the new token, and callsUnauthorizedHandler(logout) if refresh fails. Auth endpoints (/auth/*) are excluded from this retry to avoid loops. registerAuthHandlers()wires the auth feature's refresh/logout logic into the generic api client — the api layer stays auth-feature-agnostic.- Route protection via
ProtectedRoute(src/features/auth/components/ProtectedRoute.tsx), wrapping routes inApp.tsx. useAuthStore.getState().init()runs once on app mount (App.tsx) to hydrate auth state.
Conventions (from docs/CONVENTIONS.md)
- TypeScript everywhere; avoid
any. Props viainterface. - Naming: components PascalCase, hooks
useXcamelCase, API functions camelCase, types/interfaces PascalCase. - Test files sit next to source:
Component.test.tsx. - React Query keys: array form, e.g.
['course', id]. - Every fetch/save view handles loading, empty, error, success states, using the shared components in
src/shared/components(Spinner,EmptyState,ErrorState,ErrorBoundary). - Errors surfaced to users must be friendly messages derived from the backend's
ApiErrorshape (src/types/api.ts) — never raw stack traces.
Docs
docs/ARCHITECTURE.md, docs/CONVENTIONS.md, docs/SETUP.md, docs/PHASES.md — the latter maps each upcoming feature phase (learner profile, course, learning, assessment, adaptive, recommendation, assistance, account) to frontend work; check it before scaffolding a new features/<name> module.
Design
Alway using docs/DESIGN.md file to code everything relevant to style and UI