# Frontend Technical Specs and Requirements (MVP) ## Purpose Define the implementation contract for the React frontend against the current backend REST API so the customer flows (auth, search, booking, payment, profile) can ship reliably for KSA. ## Scope In scope: - Customer web app (React + Vite) for Phase 1 flows. - API integration contracts for current backend endpoints. - UX/error/loading behavior and test requirements. - i18n/RTL and KSA timezone handling. Out of scope (Phase 2+): - Manager/staff admin dashboards. - Advanced reporting/reviews moderation tools. - Full observability product dashboards. ## Product and Platform Constraints - Market default: KSA first. - Default locale: `ar-sa`; fallback: `en`. - Timezone baseline: `Asia/Riyadh` (`+03:00`). - API error contract: HTTP status + `detail` where applicable. - Booking/payment operations must avoid duplicate side effects. ## UX Priorities (Primary Contract) - First screen for authenticated and guest users MUST be a feed of nearby/available salons. - The feed is the default home experience and primary entry to booking. - Main customer navigation MUST be bottom tabs (mobile-first), not header-first navigation. - Bottom tabs MUST include at minimum: - Home/Feed - Bookings - Profile - Optional tabs (when implemented) may include Search/Explore and Payments, but must not displace Home/Feed as default. ## Frontend Architecture Requirements ## Runtime and Stack - React 18 + Vite. - React Router (`BrowserRouter`) for route navigation. - `i18next` + `react-i18next` for translations and direction switching. - `fetch` wrapper in `src/api/client.js` as single API boundary. - Auth/session state in `AuthContext`. ## Route Map (Customer) - `/` home feed (nearby/available salons) + search/filter. - `/salon/:id` salon detail. - `/login` phone OTP login. - `/book?salon=` booking creation. - `/pay?booking=` payment initiation. - `/pay/return` payment callback/return surface. - `/bookings` customer booking history. - `/profile` customer profile summary. ## Module Boundaries - `src/api/`: all HTTP logic, standardized errors. - `src/contexts/`: auth/session lifecycle only. - `src/hooks/`: domain-side UI logic (`useSalonSearch`, `usePaymentForm`). - `src/pages/`: route-level composition. - `src/components/`: reusable presentation and guarded wrappers. - `src/i18n/`: locale dictionaries and locale/direction state. ## Functional Requirements ### FR-1 Phone-First Authentication - Login MUST use: - `POST /api/auth/phone/request/` - `POST /api/auth/phone/verify/` - Password auth endpoint (`/api/auth/token/`) MUST NOT be used (returns 410). - Login request form MUST collect: - `phone_number` (accept KSA local or E.164 input) - `channel` (`sms` or `whatsapp`) - optional: `device_id` (recommended for abuse controls) - Verify step MUST submit `request_id` + 6-digit `code`. - On success, frontend MUST persist `access` and `refresh` tokens and user payload. ### FR-2 Session Restore and Token Refresh - On app boot, if `access` exists: - call `GET /api/auth/me/`. - If `401`/token invalid: - call `POST /api/auth/token/refresh/` once. - retry `GET /api/auth/me/` with new access token. - If refresh fails, frontend MUST clear tokens and require re-login. ### FR-3 Salon Discovery - Home MUST render a salon feed by default on first load. - Feed data MUST come from `GET /api/salons/` and support query params for discovery. - Home search MUST call `GET /api/salons/?q=`. - Search SHOULD support additional filters when UI is added: - `city` - `service` - Nearby/available ranking can be client-side initially, but server response MUST remain source of truth. - Result cards MUST link to `/salon/:id`. ### FR-4 Salon Detail - Detail page MUST call `GET /api/salons/:id/`. - UI MUST render: - salon base info - services (duration, amount, currency) - staff list - optional reviews/photos if present - CTA MUST deep-link to booking flow with salon id. ### FR-5 Booking Creation - Booking page MUST require authenticated user. - Create booking with `POST /api/bookings/` using: - `service` - `staff` (required) - `start_time` - `end_time` - optional `notes` - `end_time` MUST match service duration exactly. - Datetime submitted to backend MUST include explicit offset (`+03:00` for KSA baseline). - On success (`201`), frontend MUST navigate to payment flow with booking id. ### FR-6 Booking History - `/bookings` MUST call authenticated `GET /api/bookings/`. - List MUST show booking id, status, salon/service labels, datetime, and price. - Datetime rendering MUST use active locale formatting. ### FR-7 Payment Initiation (Idempotent) - Payment submission MUST call `POST /api/payments/`. - Payload requirements: - `booking_id` (number) - `provider` = `moyasar` - `idempotency_key` (UUID) - `source` object with supported type (`stcpay`, `token`, `applepay`, `samsungpay`) - `callback_url` required for `source.type=token` - Frontend MUST disable duplicate submits while request is in-flight. - Same payment attempt retry MUST reuse the same `idempotency_key`. - New attempt MUST generate a new key. - If response includes `redirect_url`, frontend MUST redirect. ### FR-8 Payment Return Handling - `/pay/return` MUST parse query params: - `status` - `id` - Success statuses shown as success UX: `paid`, `captured`, `authorized`. - Non-success statuses MUST show neutral/pending/failure guidance and link to profile/bookings. ### FR-9 Locale and Direction - App MUST allow switching between `ar-sa` and `en`. - Locale switch MUST: - persist preference in local storage - set `` - set `` (`rtl` for `ar-sa`, `ltr` for `en`) - API calls MUST include `Accept-Language` header with active locale. ## API Contract Requirements | Endpoint | Auth | Request | Success | Error handling | |---|---|---|---|---| | `POST /api/auth/phone/request/` | No | `phone_number`, `channel`, optional profile fields | `201` with `request_id`, `expires_at` | `429` may include `retry_after_seconds`; show wait message | | `POST /api/auth/phone/verify/` | No | `request_id`, `code` | `200` with `access`, `refresh`, `user` | `400` invalid/expired code | | `POST /api/auth/token/refresh/` | No | `refresh` | `200` with new `access` | logout on failure | | `GET /api/auth/me/` | Bearer | - | `200` user payload | `401` triggers refresh flow | | `GET /api/salons/` | No | `q`, optional `city`, `service` | `200` list | show localized generic fetch error | | `GET /api/salons/:id/` | No | - | `200` detail object | show detail/fallback | | `POST /api/bookings/` | Bearer | booking payload | `201` booking | `400` field validation errors | | `GET /api/bookings/` | Bearer | - | `200` list | auth + generic errors | | `POST /api/payments/` | Bearer | payment payload | `201` created or `200` reused idempotent record | `400/403` with details; never auto-retry with new key | ## Error and State Handling Requirements - API wrapper MUST throw structured errors with: - HTTP status - parsed response body - best message (`detail` first, fallback to response text) - For validation objects (`{field: [msg]}`), UI SHOULD render first field message near form and keep raw object in debug logs. - For `429` with `retry_after_seconds`, UI MUST display server-provided cooldown. - All mutating forms MUST expose: - idle/loading/error/success states - submit button disabled while loading ## Security and Abuse-Resistance Requirements - Use Bearer access token for authenticated endpoints only. - Include optional `device_id` during phone auth request to strengthen backend abuse controls. - Never send raw card PAN/CVV data to backend; use tokenized sources only. - On logout, clear user and both tokens from memory + storage. ## Accessibility and UX Requirements - All interactive controls MUST have accessible labels. - Auth/booking/payment forms MUST be keyboard usable. - Error text MUST be visible and associated with active form context. - Layout MUST remain usable on mobile widths (`>=320px`) and desktop. ## Non-Functional Requirements - Reliability: no duplicate payment submission side effects for one attempt. - Consistency: API errors surfaced predictably and localized where available. - Maintainability: domain behavior in hooks/services, not route components. - Extensibility: route/module structure must support manager/staff pages later without rewrite. ## Test Requirements (Frontend) - Test stack: `vitest` + Testing Library. - Required coverage for release: - phone login request + verify success/failure + 429 cooldown message - auth restore and refresh-token fallback - protected route redirect behavior - salon search loading/empty/results/error - booking form validation + API error mapping + success redirect - payment form source validation + idempotency key reuse on retry + redirect behavior - locale switching persists and sets `lang`/`dir` - bookings list rendering and localized datetime output Run: - `cd frontend && npm run test` ## Definition of Done (Frontend) - All FR requirements implemented for in-scope routes. - API integrations match endpoint/payload contract above. - No use of deprecated password login API. - All listed frontend tests pass. - `ar-sa` and `en` UX verified on mobile + desktop. ## Known Dependencies and Open Decisions - OAuth/social-linking policy is not finalized; keep social login UI hidden for now. - Cancellation and refund policies are not finalized; do not ship irreversible customer actions until policy finalization. - Detailed business-hours/timezone policy beyond current backend validation remains open; keep KSA-offset submission and avoid client-side assumptions that override server validation.