feat: add TourProgressController and related entities for user tour progress tracking
- Implemented TourProgressController to handle API endpoints for tracking guided tours seen by users. - Created UserTourProgress entity to store the highest version of tours seen by each user. - Developed UserTourProgressRepository for database interactions related to user tour progress. - Introduced TourProgressService to manage business logic for marking tours as seen and retrieving seen maps. - Added comprehensive tests for API endpoints and entity behavior to ensure functionality and data integrity.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# User Profile API
|
||||
|
||||
> **Prefix:** `/api/v1/user-profile`
|
||||
> **Prefix:** `/api/v1/user-profile` (plus the per-user tour endpoints under `/api/v1/my/tours`)
|
||||
|
||||
User medical profiles store health information that can be shared with doctors.
|
||||
|
||||
@@ -186,3 +186,79 @@ The stored path is also returned as `avatar` in the profile GET/PATCH responses.
|
||||
|------|------|-------------|
|
||||
| `ERR_VALIDATION_001` | 422 | Missing or invalid file |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
|
||||
---
|
||||
|
||||
# Guided tours (admin panel)
|
||||
|
||||
The admin SPA ships a step-by-step tour per page. A tour runs automatically the first time a
|
||||
user opens that page, and is available afterwards from the `?` button in the page header.
|
||||
Which tours a user has already been through is stored server-side, so it follows the account
|
||||
across browsers and devices.
|
||||
|
||||
Each tour carries a `version` in the frontend registry (`assets/admin/lib/tour/tours/*.ts`).
|
||||
The tour auto-runs again when its version is higher than the stored one, which is how a rewritten
|
||||
tour reaches users who already saw the old text.
|
||||
|
||||
## GET `/api/v1/my/tours`
|
||||
|
||||
Tours the current user has already been through.
|
||||
|
||||
**Permission:** `IS_AUTHENTICATED_FULLY`
|
||||
|
||||
### Response `200`
|
||||
```json
|
||||
{ "success": true, "data": { "seen": { "appointments": 1 } } }
|
||||
```
|
||||
|
||||
`seen` is always an object; it is `{}` for a user who has not finished any tour yet.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `seen` | object | tour id → highest version the user has seen |
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
|
||||
---
|
||||
|
||||
## POST `/api/v1/my/tours/{tourId}/seen`
|
||||
|
||||
Record that the current user has been through a tour. Closing a tour part-way counts as seen —
|
||||
the client sends this on tour close as well as on completion.
|
||||
|
||||
**Permission:** `IS_AUTHENTICATED_FULLY`
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `tourId` | string | Route-constrained to `[a-z0-9-]{1,64}`; anything else is a 404 |
|
||||
|
||||
### Request Body (`application/json`)
|
||||
```json
|
||||
{ "version": 1 }
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `version` | integer ≥ 1 | ✅ | Version of the tour definition that was shown |
|
||||
|
||||
### Response `200`
|
||||
```json
|
||||
{ "success": true, "data": { "tourId": "appointments", "version": 1 } }
|
||||
```
|
||||
|
||||
Repeat calls update the same row. The stored version only ever moves forward, so replaying an
|
||||
old tour from the `?` button cannot re-trigger a newer one.
|
||||
|
||||
### Errors
|
||||
```json
|
||||
{ "success": false, "data": null, "errors": [ { "code": "ERR_VALIDATION_001", "message": "نسخهٔ راهنما باید عددی بزرگتر از صفر باشد", "field": "version" } ] }
|
||||
```
|
||||
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_VALIDATION_001` | 422 | `version` missing, not an integer, or below 1 |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| — | 404 | `tourId` does not match the route constraint |
|
||||
|
||||
Reference in New Issue
Block a user