added claude.md
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# 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`)
|
||||
Reference in New Issue
Block a user