feat: added initial implementation
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
# 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=<id>` booking creation.
|
||||
- `/pay?booking=<id>` 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=<query>`.
|
||||
- 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 `<html lang>`
|
||||
- set `<html dir>` (`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.
|
||||
Reference in New Issue
Block a user