128 lines
5.5 KiB
Markdown
128 lines
5.5 KiB
Markdown
# 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/<app>/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/<app>/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`)
|