Files
hamed ecdefa3c24 feat(tour): add onboarding tours for various admin pages
- Integrated TourButton component into SettingsMenuPage, SkillsPage, SmsWalletPage, StaffPage, StaffSessionDetailPage, StaffTreatmentSessionsPage, SubscriptionPage, TagsSettingsPage, TreatmentCasesPage to enhance user onboarding experience.
- Created new tour definitions for appointments, clinics, staff management, financial management, and patient management, ensuring comprehensive guidance for users navigating the admin panel.
- Updated documentation to reflect the addition of tours and their implementation details.
2026-08-10 10:10:20 +03:30

10 KiB

User Profile API

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.


POST /api/v1/user-profile

Create a medical profile for the authenticated user.

Permission: AUTH

Request Body (application/json)

{
  "name": "علی",
  "family": "احمدی",
  "fathers_name": "محمد",
  "national_code": "0012345678",
  "gender": "male",
  "blood_type": "A+",
  "marital_status": "married",
  "education": "لیسانس",
  "job": "مهندس",
  "address": "تهران، خیابان ولیعصر",
  "home_phone": "02112345678",
  "work_phone": "02198765432",
  "insurance_id": 1,
  "description": "توضیحات بیشتر",
  "sharing_with_user": true,
  "birthday": "1370-06-15",
  "date_of_birth": 648172800,
  "basic_insurance": "بیمه تأمین اجتماعی",
  "supplementary_insurance": "بیمه ایران",
  "other": {}
}
Field Type Required Description
name string First name
family string Last name
fathers_name string Father's name
national_code string National ID (10 digits)
gender string "male" or "female"
blood_type string "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"
marital_status string "single" or "married"
education string Education level
job string Occupation
address string Home address
home_phone string Home phone number
work_phone string Work phone number
insurance_id integer Insurance ID
description string Additional notes
sharing_with_user boolean Allow doctors to view this profile
birthday string Birthday in Jalali format YYYY-MM-DD
date_of_birth integer Birthday as Unix timestamp
basic_insurance string Basic insurance name
supplementary_insurance string Supplementary insurance name
other object Extra metadata

Response 201

{
  "success": true,
  "data": {
    "uuid": "profile-uuid-...",
    "name": "علی",
    "family": "احمدی",
    "gender": "male",
    "blood_type": "A+",
    "national_code": "0012345678",
    "sharing_with_user": true,
    "created_at": 1717000000
  }
}

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_VALIDATION_001 422 national_code نامعتبر (رقم کنترلی/طول غلط) (field: national_code)
ERR_PROFILE_001 409 کد ملی قبلاً برای پروفایل کاربر دیگری ثبت شده (field: national_code)
ERR_CONFLICT_001 409 Profile already exists for this user

GET /api/v1/user-profile/{uuid}

Get a user profile.

Permission: AUTH — must be the profile owner, or a doctor if sharing_with_user: true, or ROLE_ADMIN

Path Parameters

Param Type Description
uuid string (UUID) Profile UUID or the owning User UUID — both are accepted

Auto-resolve & lazy-create: {uuid} is first looked up as a profile uuid, then as a user uuid. If it is the current user's own uuid and they have no profile yet, an empty profile is created and returned (HTTP 200) — so a freshly registered user always gets an editable profile instead of a 404. An admin reading another user's missing profile gets 404 (no profile is created as a side effect of reading). The response includes both the profile uuid and user_uuid; keep the profile uuid for subsequent PATCHes.

Response 200

Full profile object including all fields (all null for a newly created empty profile). Response is double-nested: extract with data.data.

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_FORBIDDEN_001 403 Not authorized to view this profile
ERR_VALIDATION_002 404 No profile or user matches the uuid

PATCH /api/v1/user-profile/{uuid}

Update a user profile.

Permission: AUTH — must be the profile owner

Path Parameters

Param Type Description
uuid string (UUID) Profile UUID or the owning User UUID — both accepted; the profile is created if the current user has none yet

Request Body

Same fields as POST — all optional.

national_code server-side validation: اگر national_code ارسال شود و غیرخالی باشد، با الگوریتم رقم کنترلیِ کد ملی ایران اعتبارسنجی می‌شود (ارقام فارسی/عربی به انگلیسی نرمال و به‌صورت لاتین ذخیره می‌شوند). مقدارِ نامعتبر با 422 رد می‌شود. ارسال null یا رشته‌ی خالی مجاز است (کد ملی اختیاری) و فیلد را پاک می‌کند.

یکتایی کد ملی: کد ملی در کل سیستم یکتاست (یک کد ملی = یک بیمار). اگر کد ملی ارسالی قبلاً به پروفایل کاربر دیگری تعلق داشته باشد، با 409 و کد ERR_PROFILE_001 رد می‌شود (field: national_code). پیام خطا شامل شماره موبایلِ ماسک‌شده‌ی صاحب کد است (مثلاً «این کد ملی قبلاً با شماره 0912****56 ثبت شده است»). در سطح دیتابیس هم با UNIQUE INDEX uniq_profiles_national_code تضمین شده (مقادیر NULL آزادند). همین قید روی مسیر ساخت بیمار توسط منشی (POST /api/v1/patient) نیز اعمال می‌شود.

Response 200

Updated profile object.

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_VALIDATION_001 422 national_code نامعتبر (رقم کنترلی/طول غلط) (field: national_code)
ERR_PROFILE_001 409 کد ملی قبلاً برای پروفایل کاربر دیگری ثبت شده (field: national_code)
ERR_FORBIDDEN_001 403 Not the profile owner
ERR_NOT_FOUND_001 404 Profile not found

DELETE /api/v1/user-profile/{uuid}

Delete a user profile.

Permission: ROLE_ADMIN

Response 200

{ "success": true, "data": { "message": "پروفایل حذف شد" } }

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_AUTH_006 403 Not admin
ERR_NOT_FOUND_001 404 Profile not found

POST /api/v1/user-profile/avatar

Upload the current user's profile avatar. Creates the profile if it doesn't exist yet.

Permission: IS_AUTHENTICATED_FULLY

Request

Raw file body with Content-Disposition: filename="..." and Content-Type: application/octet-stream.

Response 200

{ "success": true, "data": { "avatar": "/uploads/avatars/2026-06/xxx.jpg", "url": "/uploads/avatars/2026-06/xxx.jpg" } }

The stored path is also returned as avatar in the profile GET/PATCH responses.

Errors

Code HTTP Description
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.

Every non-admin page of the panel ships a tour. Adding one to a new page means: a file under assets/admin/lib/tour/tours/, one line in registry.ts, and tourId on the page's PageHeader — the shared data-tour anchors (page-title, page-action, page-search, page-table, page-pagination) already come from PageHeader, DataTable and Pagination. A step whose anchor is not on screen is dropped silently, so role-specific pages need no branching. PageHeader auto-runs the tour once the page is ready; pass tourReady={!isLoading} when the data arrives late.

GET /api/v1/my/tours

Tours the current user has already been through.

Permission: IS_AUTHENTICATED_FULLY

Response 200

{ "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)

{ "version": 1 }
Field Type Required Description
version integer ≥ 1 Version of the tour definition that was shown

Response 200

{ "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

{ "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