# Frontend integration notes — Clarity Clinic API

The one page to read before you build a frontend against this API. It captures the things that are easy to
get wrong and aren't obvious from the endpoint list. Full reference: [`API.md`](API.md) · live explorer: `/swagger`.

## 1. Base URL & CORS
- The API is at `http://<host>:5180` locally (`http://<vps>:8080` deployed). Every route is under `/api`
  (e.g. `http://<host>:5180/api/doctors`).
- Configure your app's base URL: **Patient app** → `VITE_API_URL` (`.env`); **Staff app** →
  `API_BASE` in `src/app/core/config.ts`.
- **CORS:** the API only answers browsers whose origin is in the server's allow-list
  (`Clients:PatientApp` / `Clients:StaffApp`, comma-separated). If you get a CORS error, your dev origin
  (e.g. `http://localhost:5173`) isn't listed — ask for it to be added.

## 2. Auth lifecycle (important)
- `POST /api/auth/register` (patients only) and `POST /api/auth/login` return `{ token, user }`.
- Send the token on every authenticated request: `Authorization: Bearer <token>`.
- **The token is valid for 7 days. There is NO refresh token and NO logout endpoint.**
  - On **`401`** (expired/invalid token), clear the stored token and redirect to login. Don't try to refresh.
  - **Logout** = delete the token from your store client-side. Nothing to call server-side (JWT is stateless).
- Optional **`Accept-Language: en` | `ar`** header localizes server messages (defaults to English).
- `GET /api/auth/me` re-fetches the current user; `PUT /api/auth/profile` returns the **updated** user.

## 3. Errors — there are NO per-field keys
Every failure returns the same envelope:
```json
{ "error": "conflict", "message": "That slot was just booked. Please pick another slot.", "details": [] }
```
- `details` is a **flat string array** of validation messages — it is **not** keyed by field. You cannot map
  an error to a specific input reliably; show `message` (and list `details` if present).
- Status codes: **400** validation · **401** missing/invalid token · **403** wrong role · **404** not found ·
  **409** conflict (slot taken, or a business rule like deactivating a doctor with future appointments).

## 4. Response shapes & gotchas
- **Only `POST /api/appointments` wraps its result** as `{ appointment, payment }`. **Every other**
  appointment endpoint (`walk-in`, `GET /appointments/{id}`, `cancel`, `reschedule`, `arrived`, `no-show`,
  `cash-paid`, `complete`) returns the **`Appointment` object directly** — don't reach for `.appointment`.
- **Booking outcomes:** `mode: Online` + `paymentMethod: Online` → `201` status `PendingPayment` with
  `payment.checkoutUrl` + a `meetingLink`. `mode: InClinic` + `paymentMethod: Cash` → `201` status
  `Confirmed`, `payment: null` (paid at the desk).
- **Pagination** (`GET /api/doctors`) returns `{ items, page, pageSize, total }`. There is **no `totalPages`
  or `hasMore`** — compute from `total / pageSize`. Default `pageSize` is **10**; pass `pageSize` for more.
- **Slots & duration:** slots are a 15-min grid, but a slot is only offered if the whole service fits without
  overlap — booking a 30-min service at `09:00` also removes `09:15`. **Re-fetch slots after every booking**,
  and on **`409`** ("slot just taken") re-fetch and ask the user to re-pick.

## 5. Enums are case-sensitive
Send these exact strings:
- `AppointmentMode`: `Online` | `InClinic`
- `PaymentMethod`: `Online` | `Cash`
- `AppointmentStatus`: `PendingPayment` | `Confirmed` | `Arrived` | `Completed` | `Cancelled` | `NoShow`
- `PaymentStatus`: `Pending` | `Paid` | `Failed` | `Refunded`
- `UserRole`: `Patient` | `Doctor` | `Receptionist` | `Admin`

(Numbers tolerate JSON strings — `serviceId: "1"` works — but **enum strings must match exactly**, casing included.)

## 6. Formats & timezone
- **Dates:** `yyyy-MM-dd`. **Times:** `HH:mm` (24-hour). **Money:** EGP (decimal).
- **All clinic time is `Africa/Cairo`.** "Today", past-slot filtering, and the day calendar are computed in
  Cairo time on the server — send/expect dates accordingly (don't convert to the browser's timezone).

## 7. No file uploads
`avatarUrl` (profile) and `photoUrl` (add-doctor) are **plain URL strings** — there is no upload endpoint.
To show an image, host it somewhere (CDN, object storage, a static path) and pass its URL.

## 8. Who uses which app
- **Patient app** → role `Patient` (self-register or `patient@clinic.local`). Browse, book, pay, view
  appointments & prescriptions.
- **Staff app** → roles `Admin` / `Receptionist` / `Doctor`. The UI hides controls by role, but the **server
  enforces permissions** — a 403 means the role isn't allowed, period.

## 9. Trying it
- **Swagger** (`/swagger`): click **Authorize**, paste the token from `POST /auth/login`, and call any endpoint.
- **Postman**: import `docs/postman/Clarity-Clinic.postman_collection.json` — logins auto-store the token. See
  the **🚀 Full Patient Journey** folder for the end-to-end flow.
- **Health**: `GET /health` → `{"status":"healthy"}` (no auth) for uptime checks.

## 10. Seeded logins
| Role | Email | Password |
|------|-------|----------|
| Admin | `admin@clinic.local` | `Admin#123` |
| Doctor | `doctor@clinic.local` | `Doc#123` |
| Receptionist | `reception@clinic.local` | `Recep#123` |
| Patient | `patient@clinic.local` | `Pat#123` |
