|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Talo is a self-hostable game dev dashboard. This is the frontend React application that provides a web interface for managing players, leaderboards, events, stats, game saves, and other game backend features. The frontend communicates with the Talo backend API. |
| 8 | + |
| 9 | +## Development Commands |
| 10 | + |
| 11 | +### Running the application |
| 12 | +```bash |
| 13 | +npm run dev # Start dev server on http://localhost:8080 |
| 14 | +npm run dev:e2e # Start dev server in e2e mode for Cypress tests |
| 15 | +npm run build # Build for production (runs tsc + vite build) |
| 16 | +``` |
| 17 | + |
| 18 | +### Testing |
| 19 | +```bash |
| 20 | +npm test # Run all Vitest unit tests (runs with TZ=UTC) |
| 21 | +npm test -- <pattern> # Run specific test file(s) matching pattern |
| 22 | +npm run test:e2e # Start dev server and open Cypress for e2e tests |
| 23 | +npm run cypress:open # Open Cypress (dev server must be running) |
| 24 | +``` |
| 25 | + |
| 26 | +### Code Quality |
| 27 | +```bash |
| 28 | +npm run lint # Run ESLint on src/**/*.{js,jsx,ts,tsx} |
| 29 | +``` |
| 30 | + |
| 31 | +### Testing Notes |
| 32 | +- Unit tests use Vitest with jsdom environment |
| 33 | +- Test files are co-located with source in `__tests__/` directories |
| 34 | +- E2E tests are in `cypress/e2e/pages/` and use Cypress |
| 35 | +- Coverage excludes `src/api/`, `src/entities/`, `src/constants/`, and `src/utils/canViewPage.ts` |
| 36 | +- Tests must run with `TZ=UTC` for consistent date handling |
| 37 | + |
| 38 | +## Architecture Overview |
| 39 | + |
| 40 | +### Tech Stack |
| 41 | +- React 18 with TypeScript |
| 42 | +- Vite for build tooling |
| 43 | +- React Router v6 for routing |
| 44 | +- Recoil for global state management |
| 45 | +- Axios for HTTP requests |
| 46 | +- SWR for data fetching and caching |
| 47 | +- Zod for runtime validation |
| 48 | +- Tailwind CSS v4 for styling |
| 49 | +- Recharts for charts |
| 50 | +- React Hook Form for forms |
| 51 | + |
| 52 | +### Directory Structure |
| 53 | + |
| 54 | +``` |
| 55 | +src/ |
| 56 | +├── api/ # SWR hooks and API functions (91 files) |
| 57 | +├── components/ # Reusable UI components |
| 58 | +│ ├── billing/ |
| 59 | +│ ├── charts/ |
| 60 | +│ ├── events/ |
| 61 | +│ ├── saves/ |
| 62 | +│ ├── tables/ |
| 63 | +│ ├── toast/ |
| 64 | +│ └── toggles/ |
| 65 | +├── constants/ # App constants (routes, nav, user types) |
| 66 | +├── entities/ # TypeScript types and Zod schemas (29 domain models) |
| 67 | +├── modals/ # Modal dialog components |
| 68 | +├── pages/ # Page components (39 pages) |
| 69 | +├── services/ # Business logic (AuthService) |
| 70 | +├── state/ # Recoil atoms (8 state files) |
| 71 | +├── styles/ # CSS files |
| 72 | +├── utils/ # Utilities and custom hooks |
| 73 | +│ ├── group-rules/ |
| 74 | +│ └── validation/ |
| 75 | +├── App.tsx # Main app component (handles auth and routing) |
| 76 | +├── Router.tsx # Route definitions with lazy loading |
| 77 | +└── index.tsx # App entry point |
| 78 | +``` |
| 79 | + |
| 80 | +### Routing |
| 81 | +- Routes are defined in [Router.tsx](src/Router.tsx) using React Router v6 |
| 82 | +- Route paths are constants in [src/constants/routes.ts](src/constants/routes.ts) |
| 83 | +- All pages use lazy loading via `React.lazy()` for code splitting |
| 84 | +- Routes are split into unauthenticated (login, register) and authenticated sections |
| 85 | +- Permission checks use `canViewPage()` utility before rendering routes |
| 86 | +- Many routes only render when an `activeGame` is selected |
| 87 | + |
| 88 | +### State Management |
| 89 | +- Global state uses Recoil with atoms in [src/state/](src/state/) |
| 90 | +- Key atoms: |
| 91 | + - `userState` - Current authenticated user |
| 92 | + - `activeGameState` - Currently selected game (persisted to localStorage) |
| 93 | + - `gamesState` - User's games list |
| 94 | + - `organisationState` - Organization data |
| 95 | + - `devDataState` - Dev data inclusion flag |
| 96 | +- State consumed via `useRecoilValue()`, `useRecoilState()`, `useSetRecoilState()` |
| 97 | + |
| 98 | +### API Layer |
| 99 | +- Base Axios instance configured in [src/api/api.ts](src/api/api.ts) |
| 100 | +- Request interceptor adds Bearer token and dev data header |
| 101 | +- Response interceptor handles 401s with automatic token refresh |
| 102 | +- Base URL from `VITE_API_URL` environment variable |
| 103 | + |
| 104 | +**Data fetching patterns:** |
| 105 | + |
| 106 | +1. **SWR hooks** (23 hooks): Pattern is `use{Entity}(game, params)` returning `{ data, loading, error }` |
| 107 | + - Examples: `useEvents`, `useLeaderboards`, `useStats` |
| 108 | + - Uses `makeValidatedGetRequest()` for Zod validation |
| 109 | + - Automatic caching, revalidation, and deduplication |
| 110 | + |
| 111 | +2. **Mutation functions** (68 functions): Named after action (e.g., `createLeaderboard`, `updatePlayer`) |
| 112 | + - Uses `makeValidatedRequest()` wrapper for Zod validation |
| 113 | + - Returns validated, typed responses |
| 114 | + |
| 115 | +### Component Patterns |
| 116 | +- Functional components with TypeScript |
| 117 | +- Composition over inheritance |
| 118 | +- Context for cross-cutting concerns (EventsContext, ToastProvider) |
| 119 | +- Form handling with React Hook Form |
| 120 | +- Tables use composable system: `Table` > `TableHeader`/`TableBody` > `TableCell` |
| 121 | + |
| 122 | +### Key Utilities and Hooks |
| 123 | +Located in [src/utils/](src/utils/): |
| 124 | + |
| 125 | +- `useTimePeriod` - Date range calculation from period strings ('1d', '7d', '30d', 'w', 'm', 'y') |
| 126 | +- `useTimePeriodAndDates` - Combined time period and date management |
| 127 | +- `useLocalStorage` - localStorage with React state sync |
| 128 | +- `usePlayer` - Extract player from URL params |
| 129 | +- `useSortedItems` - Client-side sorting |
| 130 | +- `useSearch` - Client-side search filtering |
| 131 | +- `useNodeGraph` - Graph visualization for save data (using @xyflow/react) |
| 132 | +- `buildError` - Normalize error objects |
| 133 | +- `canPerformAction` / `canViewPage` - Permission checking |
| 134 | +- `getEventColour` - Consistent color assignment for events |
| 135 | + |
| 136 | +## Important Patterns |
| 137 | + |
| 138 | +### Authentication |
| 139 | +- Managed by [AuthService](src/services/AuthService.ts) singleton |
| 140 | +- Token-based with automatic refresh on 401 responses |
| 141 | +- Token stored in memory (not localStorage for security) |
| 142 | +- Login redirects handled via `useIntendedRoute` hook |
| 143 | + |
| 144 | +### Authorization |
| 145 | +- Route-level checks with `canViewPage()` in Router |
| 146 | +- Action-level checks with `canPerformAction()` throughout components |
| 147 | +- Based on user type (ADMIN, OWNER, DEV, DEMO) |
| 148 | + |
| 149 | +### Validation |
| 150 | +- Zod schemas defined alongside entities in [src/entities/](src/entities/) |
| 151 | +- `makeValidatedRequest()` and `makeValidatedGetRequest()` wrappers ensure type safety |
| 152 | +- Validation errors logged and sent to Sentry |
| 153 | + |
| 154 | +### Error Handling |
| 155 | +- Centralized error normalization via `buildError()` |
| 156 | +- API errors show user-friendly messages via toast notifications |
| 157 | +- Sentry integration for error tracking |
| 158 | + |
| 159 | +### Date Handling |
| 160 | +- All API dates must be UTC |
| 161 | +- Use `convertDateToUTC()` when sending dates to API |
| 162 | +- Tests run with `TZ=UTC` to ensure consistency |
| 163 | + |
| 164 | +### Active Game Context |
| 165 | +- Most features require an active game to be selected |
| 166 | +- Active game stored in Recoil state and persisted to localStorage |
| 167 | +- Routes conditionally render based on `activeGame` availability |
| 168 | + |
| 169 | +## Environment Variables |
| 170 | + |
| 171 | +Required environment variables (set via `.env` files): |
| 172 | +- `VITE_API_URL` - Backend API base URL (e.g., `http://localhost:3000`) |
| 173 | + |
| 174 | +## Node Version |
| 175 | + |
| 176 | +This project requires Node.js 22.x (see package.json engines). |
| 177 | + |
| 178 | +## Git Workflow |
| 179 | + |
| 180 | +Main branch for PRs: `develop` |
0 commit comments