Files
clinicpro/docs/new_feture/taskes/task-05-appointment-plan/user_flow.md
T
hamed 021d0eb6b2 feat: implement cancellation policy, no-show tracking, and waitlist management
- Add implementation notes for cancellation and waitlist features.
- Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting.
- Establish architecture for domain events and outbox pattern to ensure reliable event publishing.
- Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports.
- Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.
2026-07-30 11:43:58 +03:30

5.9 KiB
Raw Blame History

جریان کاربری — تسک ۰۵

الف) کلینیک الگوی بخش‌های یک سرویس را تعریف می‌کند

پنل › خدمات › لیزر کندلا › تب «بخش‌های نوبت»
    │
    ├─ «افزودن بخش»
    │     نام: مالیدن کرم بی‌حسی
    │     نوع (کلید ادغام): آماده‌سازی
    │     مدت: ثابت — ۵ دقیقه
    │     بیمار حاضر است: بله
    │     با بخش‌های هم‌نوع ادغام شود: بله
    │     └─ نیازمندی‌ها:
    │           [اتاق] × ۱ — انحصاری
    │           [اپراتور] × ۱ — انحصاری — مهارت: لیزر آلکساندرایت — قید: هم‌جنس با بیمار
    │
    ├─ بخش ۲: انتظار اثر بی‌حسی — ثابت ۳۰ — ادغام‌پذیر
    │     نیازمندی: فقط [اتاق] × ۱ انحصاری        ← اپراتور اینجا آزاد است
    │
    ├─ بخش ۳: خود لیزر — سهمی ۱۰۰٪
    │     نیازمندی: [اتاق] × ۱ · [اپراتور] × ۱ · [دستگاه] × ۱ از استخر «لیزرهای آلکساندرایت»
    │
    └─ بخش ۴: مراقبت بعد — ثابت ۵
          نیازمندی: [اتاق] × ۱ · [اپراتور] × ۱
    │
    ▼
نوار پیش‌نمایش زمانی (زیر فرم، زنده):

  ┌─────┬───────────────────┬─────────────┬─────┐
  │ ۵'  │        ۳۰'        │     ۲۰'     │ ۵'  │
  │🏠👤 │        🏠         │  🏠 👤 🔧   │🏠👤 │
  └─────┴───────────────────┴─────────────┴─────┘
   کل: ۶۰ دقیقه · اپراتور واقعاً درگیر: ۳۰ دقیقه

    │
    ▼
«ذخیره» → PUT /api/v1/service-item/{uuid}/segments

خط «اپراتور واقعاً درگیر: ۳۰ دقیقه» مهم‌ترین بازخورد این صفحه است: کلینیک آنجا می‌فهمد چرا این کار ارزشش را دارد.


ب) بیمار سرویس و آیتم انتخاب می‌کند (سایت عمومی)

انتخاب پزشک/کلینیک
    │
    ▼
GET /api/v1/appointment-booking-services/{doctorUuid}
    → booking_mode = "resource"        ← حالت جدید
    → services[] با گروه‌های آیتم
    │
    ▼
بیمار انتخاب می‌کند: ناحیه = صورت + بیکینی · سطح انرژی = ۱۶
    │
    ▼
POST /api/v1/service-selection/validate        (تسک ۰۴، debounce)
    → valid: true · total_duration_minutes: 23 · total_price_rials: …
    │
    │ اگر valid=false:
    │     خطاها زیر همان گروه نمایش داده می‌شوند
    │     «انتخاب حداقل یک مورد از نواحی الزامی است»
    │     «صورت و فول‌بادی با هم قابل انتخاب نیستند»
    │     و دکمهٔ «ادامه» غیرفعال می‌ماند
    ▼
POST /api/v1/appointment-plan/preview
    → total_minutes: 68
      segments: [آماده‌سازی ۵ · انتظار ۳۰ · لیزر ۲۳ · مراقبت ۵ · تمیزکاری ۵]
    │
    │ اگر NoEligibleResourceException:
    │     «هیچ اپراتور خانمی با مهارت لیزر آلکساندرایت در شعبهٔ مرکزی موجود نیست»
    │     + پیشنهاد شعبهٔ دیگر (اگر داشته باشد)
    ▼
مرحلهٔ انتخاب زمان  →  تسک ۰۶

نکته UX: برنامهٔ نوبت به بیمار نمایش داده نمی‌شود. بیمار فقط «۶۸ دقیقه» و «توضیحات آماده‌سازی» را می‌بیند. بخش‌ها جزئیات عملیاتی کلینیک‌اند؛ نشان دادنشان به بیمار فقط سؤال می‌سازد.

استثنا: بخش‌هایی با patient_present = false نباید در مدت اعلامی به بیمار بیایند («تمیزکاری یونیت» ۵ دقیقهٔ بعد از رفتن بیمار است). پس دو عدد وجود دارد:

  • total_minutes = ۶۸ (اشغال کلینیک) — برای موتور
  • patient_facing_minutes = ۶۳ — برای نمایش

هر دو در پاسخ preview برگردند.


ج) منشی از پنل نوبت می‌سازد

همان جریان ب، با دو تفاوت:

  1. forManagement = true — بازهٔ رزرو و خاموش بودن نوبت‌دهی آنلاین اعمال نمی‌شود (همان رفتاری که SlotCalculatorService::isWithinBookingWindow() امروز دارد)
  2. منشی می‌تواند منبع را دستی انتخاب کند: پاسخ preview برای هر نیازمندی candidates را با نام برمی‌گرداند و پنل یک SearchableSelect اختیاری نشان می‌دهد. خالی گذاشتن یعنی «تو انتخاب کن» (استراتژی تسک ۰۶).

د) حالت خطا — هیچ منبعی موجود نیست

POST /appointment-plan/preview
    ▼
422 {
  "success": false,
  "errors": [{
    "code": "ERR_NO_ELIGIBLE_RESOURCE",
    "message": "هیچ اپراتور خانمی با مهارت لیزر آلکساندرایت در شعبهٔ مرکزی موجود نیست",
    "meta": { "segment": "خود لیزر", "role": "اپراتور", "branch_uuid": "…" }
  }]
}

meta اجباری است: پنل با آن می‌تواند مستقیم به صفحهٔ منابع همان شعبه لینک بدهد («افزودن اپراتور») و کلینیک در سه کلیک مشکل را حل کند، به‌جای اینکه با یک پیام بن‌بست بماند.