# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Goal A salon booking platform for KSA (Saudi Arabia) with Django REST API backend and React/Vite frontend. Optimized for phone-first auth (OTP via SMS/WhatsApp), Moyasar payments, Arabic locale (ar-sa), and Riyadh timezone. ## Commands ### Backend ```bash # Setup (from repo root) python3 -m venv venv && source venv/bin/activate pip install -r backend/requirements.txt -r backend/requirements-dev.txt cp backend/.env.example backend/.env # Migrations and dev server (from backend/) cd backend python3 manage.py migrate python3 manage.py runserver # Seed demo data python3 manage.py seed_demo # Run all tests (from backend/ with venv active) cd backend python3 -m pytest # Run a single test file python3 -m pytest apps/accounts/tests/test_otp_limits.py # Run a single test python3 -m pytest apps/accounts/tests/test_otp_limits.py::TestClassName::test_method_name # Run external/integration tests (hits real third-party services) PYTEST_ADDOPTS='' python3 -m pytest -m external ``` ### Authentica E2E Testing Set these env vars before running external tests: - `AUTHENTICA_E2E=1` - `AUTHENTICA_API_KEY=...` - `AUTHENTICA_E2E_PHONE=...` (phone that will receive OTP) - `AUTHENTICA_E2E_CODE=...` (OTP code received) ### Frontend ```bash # From frontend/ cd frontend npm install npm run dev # dev server at localhost:5173, proxies /api to localhost:8000 npm run test # vitest npm run build ``` ## Architecture ### Backend (`backend/`) Django project lives in `backend/salon_api/` (settings, root urls, wsgi/asgi). All domain apps are under `backend/apps/`: | App | Responsibility | |-----|----------------| | `accounts` | Custom User model, phone/OTP auth, JWT tokens, locale preferences | | `salons` | Salon catalog, services, staff profiles, availability windows, reviews | | `bookings` | Booking lifecycle, availability/overlap validation, status transitions | | `payments` | Moyasar integration (create, capture, refund), webhook reconciliation, idempotency | | `notifications` | Booking lifecycle SMS/WhatsApp messages, stored for auditability | **Service layer pattern:** Business logic lives in `apps//services/` (not in views). Views are thin — they validate input, call services, return responses. Keep it this way. **OTP providers** (`apps/accounts/services/otp.py`): pluggable via `OTP_PROVIDER` env var. Active providers: `console` (dev), `twilio`, `authentica`. `unifonic` is a scaffold. Authentica is the recommended production provider and uses a server-side OTP flow (`uses_provider_otp = True`) — it generates and verifies the code itself, so the DB stores a placeholder hash. **Payment gateway** (`apps/payments/services/gateway.py`): `MoyasarGateway` implements `BasePaymentGateway`. Amounts are always in minor units (halalas). `MOYASAR_SECRET_KEY` and `MOYASAR_PUBLISHABLE_KEY` are required. **Sync-only (MVP):** All external calls (OTP sends, notifications, payment gateway) run synchronously in the request path. No task queue. See `docs/adr/0001-synchronous-external-calls-mvp.md`. **Database:** SQLite for local dev (default), PostgreSQL via `DATABASE_URL` env var for production. Tests use `TEST_DATABASE_URL` if set. **Localization:** Default language `ar-sa`, timezone `Asia/Riyadh`. `UserLocaleMiddleware` applies per-user locale preference. ### Frontend (`frontend/`) React 18 + Vite app. Entry: `src/main.jsx` → `AuthProvider` wraps `App`. - **Routing:** `react-router-dom` v7 with pages in `src/pages/` - **Auth:** JWT tokens managed via `src/contexts/AuthContext.jsx`; `src/components/ProtectedRoute.jsx` guards private pages - **API:** `src/api/client.js` is the axios/fetch wrapper - **Hooks:** Domain logic extracted into `src/hooks/` (e.g., `useSalonSearch`, `usePaymentForm`) - **i18n:** `react-i18next` configured in `src/i18n/index.js`; supports `ar-sa` and `en` Tests use Vitest + Testing Library. Setup in `src/test/setupTests.js`. ## Key Env Vars Backend (`backend/.env`): - `DJANGO_SECRET_KEY`, `DJANGO_DEBUG`, `DATABASE_URL` - `OTP_PROVIDER` — `console` | `twilio` | `authentica` | `unifonic` - `AUTHENTICA_API_KEY`, `AUTHENTICA_BASE_URL`, `AUTHENTICA_SENDER_NAME` - `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_FROM_NUMBER`, `TWILIO_WHATSAPP_FROM` - `MOYASAR_SECRET_KEY`, `MOYASAR_PUBLISHABLE_KEY` - `NOTIFICATION_PROVIDER` — defaults to `OTP_PROVIDER` - `OTP_EXPIRY_MINUTES`, `OTP_MAX_PER_WINDOW`, `OTP_WINDOW_MINUTES`, `OTP_RESEND_COOLDOWN_SECONDS` - `CORS_ALLOWED_ORIGINS` ## Testing Conventions - Backend tests live beside their apps: `apps//tests/test_*.py` - `pytest.ini` marks `external` tests as opt-in; default runs skip them - Frontend tests: Vitest + Testing Library; test files colocated with source (`*.test.jsx`) - Minimum coverage: auth flows, booking validation, payment state transitions ## ExecPlans For complex features, use an ExecPlan (see `PLANS.md` for the full spec and active plan pointer). ExecPlans are living documents in `docs/execplans/`. The active plan is listed in `PLANS.md`. Update `docs/risks.md` when opening or closing a significant gap. ## Coding Conventions - Business logic in service layers; keep views thin - Predictable error responses: HTTP status code + `detail` field - Comment intent, edge cases, and non-obvious business rules; skip obvious comments - Payment and booking flows must be idempotent and auditable - Phone auth must be rate-limited (enforced in `otp.py` via `OtpRateLimitError` / `OtpCooldownError`)