4.0 KiB
4.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Gravl is a fitness/workout tracking app (PPL - Push/Pull/Legs) with progression tracking. The UI is in Swedish. It uses a React frontend, Express backend, and PostgreSQL database, deployed via Docker with nginx and Traefik.
Commands
Frontend (frontend/)
npm run dev # Vite dev server on port 5173
npm run build # Production build -> dist/
npm run preview # Preview production build
Backend (backend/)
npm start # node src/index.js
npm run dev # nodemon with auto-reload
Docker
docker compose up -d --build # Build and run all services
Database
psql -h localhost -U postgres -d gravl -f db/init.sql # Initialize schema + seed data
There are no test or lint configurations.
Architecture
Frontend (React 18 + Vite, no TypeScript)
- Entry:
main.jsxsets up React Router v6 withAuthProvidercontext - Top-level routing (
main.jsx):/login,/register,/onboardinguse route guards (AuthRoute,ProtectedRoute) - In-app navigation (
App.jsx): UsesuseStateview switching (not URL routes) between'dashboard','profile','progress','select-workout','workout' - State:
AuthContextis the only shared state (token in localStorage, user profile). No Redux or other state libraries. Component-level state viauseState - API calls: Direct
fetch()in components withAPI_URL = '/api'constant. No shared API service layer - Styling: Plain CSS with custom properties for theming. Two files:
index.css(globals) andApp.css(~1900 lines, organized by component sections). Dark theme with orange accent (#ff6b35). Mobile-first, max-width 600px - Icons: Custom SVG icon library in
components/Icons.jsx(no emoji usage per design decision) - Pages directory:
src/pages/holds full-page components (Dashboard.jsx,WorkoutPage.jsx,LoginPage.jsx,RegisterPage.jsx,OnboardingWizard.jsx,ProfilePage.jsx,ProgressPage.jsx,WorkoutSelectPage.jsx) - Input components:
components/StepperInput.jsx(pure controlled — no internal useState),WeightInput.jsx(2.5kg steps, kg suffix),RepsInput.jsx(1-rep steps). Used in workout set rows.
Backend (Express, single-file)
- All routes in
src/index.js— no separation into route files or controllers - Auth: JWT with 30-day expiry,
bcryptjsfor passwords,authMiddlewarefor protected routes - Database:
pgwith parameterized queries ($1, $2placeholders) - Currently hardcodes program ID=1 in many queries
- Env vars (all have defaults):
JWT_SECRET,DB_HOST,DB_PORT,DB_USER,DB_PASSWORD,DB_NAME
Database (PostgreSQL)
- Schema in
db/init.sql:users,programs,program_days,exercises,program_exercises,workout_logs - Seeded with one PPL program (Push A/B, Pull A/B, Legs A/B) and 18 exercises
Deployment
- Frontend: multi-stage Docker build (node -> nginx), nginx proxies
/apitogravl-backend:3001 - Backend: node:20-alpine container on port 3001
- External PostgreSQL on
homelabDocker network - Traefik reverse proxy at
gravl.homelab.local
Conventions
- Swedish language for all UI text, some variable names and comments
- Functional components only, hooks throughout
- Workout-type CSS color variables:
--workout-push,--workout-pull,--workout-legs - Progression logic: increase weight by 2.5kg when all sets hit max reps
- StepperInput is a pure controlled component — no internal useState, all state in parent
- 44px minimum touch targets on all interactive elements (stepper buttons, inputs)
- Input font-size ≥ 16px everywhere (prevents iOS auto-zoom on focus)
agents/ Directory
Contains AI agent persona definitions (SOUL.md files) for different roles (architect, backend-dev, frontend-dev, coach, nutritionist, reviewer). The coach/ directory also has exercise data, program definitions (beginner/hypertrophy/strength), and foods data as JSON.