feat: implement service mode completion for nobat724_front
- Add task for completing service mode in clinicpro with detailed objectives and acceptance criteria. - Create architecture documentation for task 00b, outlining involved components and necessary changes. - Develop checklist for task 00b to ensure all requirements are met. - Document implementation notes for task 00b, emphasizing API contract checks and design system adherence. - Update task documentation for task 00b, specifying goals and current issues with service mode.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# تعریف «تمامشده» و قالب چکلیست
|
||||
|
||||
هر تسک یک `checklist.md` دارد. **پیش از اعلام پایان تسک، همهٔ ردیفها بازبینی میشوند و
|
||||
هیچ ردیفی در `🔄` یا `⏳` نمیماند.**
|
||||
|
||||
---
|
||||
|
||||
## نمادها
|
||||
|
||||
| نماد | معنی | اجازهٔ باقیماندن در پایان تسک |
|
||||
|---|---|---|
|
||||
| ✅ | انجامشده و تأییدشده | بله |
|
||||
| 🔄 | در حال انجام | **نه** — یا ✅ شود یا با دلیل صریح به ⏳ منتقل شود |
|
||||
| ⏳ | انجامنشده | **نه** — یا ✅ شود یا با دلیل مکتوب و تسک مقصد به تعویق برود |
|
||||
| ⚠️ | نیازمند بررسی یا تست | **نه** — باید تعیین تکلیف شود |
|
||||
|
||||
`⏳` تنها وقتی در پایان مجاز است که کنارش نوشته شده باشد: **چرا** به تعویق افتاد و
|
||||
**کدام تسک** آن را برمیدارد. `⏳ بدون دلیل = تسک تمام نشده.`
|
||||
|
||||
---
|
||||
|
||||
## قالب `checklist.md`
|
||||
|
||||
```markdown
|
||||
# چکلیست — تسک XX
|
||||
|
||||
وضعیت کلی: ⏳ شروع نشده | 🔄 در حال انجام | ✅ تمامشده
|
||||
آخرین بازبینی: —
|
||||
|
||||
## ۰. خط سرخها (رجوع: _shared/red-lines.md)
|
||||
| # | مورد | وضعیت | یادداشت |
|
||||
|---|---|---|---|
|
||||
| ۰.۱ | منطق اسلاتی دستکاری نشد · `--group=slot-mode-frozen` سبز | ⏳ | |
|
||||
| ۰.۲ | هیچ متد موجود `SlotCalculatorService` ویرایش نشد | ⏳ | |
|
||||
| ۰.۳ | قرارداد `appointment-slots` و `month-availability` دستنخورده | ⏳ | |
|
||||
|
||||
## ۱. بکاند
|
||||
## ۲. دیتابیس و مهاجرت
|
||||
## ۳. UI (رجوع: _shared/ui-conventions.md)
|
||||
## ۴. تست
|
||||
## ۵. مستندات
|
||||
## ۶. بازبینی پایانی
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## بخش ۶ — بازبینی پایانی، یکسان در همهٔ تسکها
|
||||
|
||||
```markdown
|
||||
| # | مورد | وضعیت | یادداشت |
|
||||
|---|---|---|---|
|
||||
| ۶.۱ | همهٔ ردیفهای بالا وضعیت نهایی دارند (هیچ 🔄 و ⏳ بیدلیل) | ⏳ | |
|
||||
| ۶.۲ | `ddev exec php bin/phpunit` کامل سبز | ⏳ | |
|
||||
| ۶.۳ | `ddev exec php bin/phpunit --group=slot-mode-frozen` سبز | ⏳ | |
|
||||
| ۶.۴ | `ddev exec php vendor/bin/phpstan analyse` بدون خطای جدید | ⏳ | |
|
||||
| ۶.۵ | `npx tsc --noEmit` بدون خطا | ⏳ | |
|
||||
| ۶.۶ | `yarn test` سبز | ⏳ | |
|
||||
| ۶.۷ | `TenantSchemaCoverageTest` و `TenantLookupInventoryTest` سبز | ⏳ | |
|
||||
| ۶.۸ | `docs/api/*` بهروز شد (قاعدهٔ ثابت پروژه) | ⏳ | |
|
||||
| ۶.۹ | چکلیست UI کامل شد (اگر تسک صفحه/کامپوننت دارد) | ⏳ | |
|
||||
| ۶.۱۰ | مصرفکنندگان دیگر دستی بررسی شدند: `nobat724_front` · `clinic-pro-tauri` | ⏳ | |
|
||||
| ۶.۱۱ | تغییرات commit شد، سپس `graphify update .` اجرا شد | ⏳ | |
|
||||
| ۶.۱۲ | موارد بهتعویقافتاده با دلیل و تسک مقصد ثبت شدند | ⏳ | |
|
||||
```
|
||||
|
||||
ردیف ۶.۱۰ در build هیچکدام از آن دو ریپو خطا نمیدهد — بررسی فقط دستی ممکن است.
|
||||
ردیف ۶.۱۱ ترتیبش مهم است: اول commit، بعد `graphify update`.
|
||||
|
||||
---
|
||||
|
||||
## قواعد ثابت پروژه که در هر تسک اعمال میشوند
|
||||
|
||||
از `CLAUDE.md` و `docs/architecture/tenancy.md`:
|
||||
|
||||
1. entity جدید یا `TenantOwnedTrait` میگیرد یا با دلیل در `GlobalTables` ثبت میشود
|
||||
2. `entity_type, entity_id` ستونهای **اول** هر ایندکس ترکیبیِ لیست
|
||||
3. هر uuid از request با `TenantOwnershipChecker` سنجیده میشود
|
||||
4. timestamp ها `int` یونیکس، نه `DateTime` · نمایش شمسی فقط در UI
|
||||
5. کنترلر نازک · `extends BaseController` · `success()/paginated()/error()`
|
||||
6. منطق در Service، کوئری در Repository، وابستگی با constructor injection
|
||||
7. لیستهای ادمین با `getArrayResult()`
|
||||
8. API جدید فقط وقتی هیچ endpoint موجودی — حتی با توسعه — کافی نباشد؛ **دلیلش نوشته شود**
|
||||
9. تست موفق + خطا + مرزی · بدون اجرای موفق تست، تسک تمام نیست
|
||||
10. کد و کامیت و مستندات انگلیسی · رشتههای UI فارسی از i18n
|
||||
11. SOLID · کلاس/کامپوننت چندمسئولیتی ننویس
|
||||
@@ -0,0 +1,96 @@
|
||||
# خط سرخها — قواعدی که هیچ تسکی نمیتواند نقض کند
|
||||
|
||||
این فایل بالای هر تسک حاکم است. اگر تسکی با اینها تناقض داشت، **این فایل برنده است** و
|
||||
تسک باید اصلاح شود، نه این فایل.
|
||||
|
||||
---
|
||||
|
||||
## ۱. ⛔ نوبتدهی اسلاتی به هیچ عنوان دستکاری نمیشود
|
||||
|
||||
`booking_mode = 'slot'` منطق تولیدیِ زنده است. در **هیچ تسکی از این فاز** نه رفتارش،
|
||||
نه امضایش، نه خروجیاش تغییر نمیکند.
|
||||
|
||||
### فایلها و مسیرهای قفلشده
|
||||
|
||||
| فایل / مسیر | چه چیزی قفل است |
|
||||
|---|---|
|
||||
| `src/Appointment/Service/SlotCalculatorService.php` | متدهای `getAvailableSlots`، `getAllSlotsWithAvailability`، `hasAnyAvailability`، `findNextAvailableStart`، `buildSessionSlots`، `buildAllSessions`، `filterBookedSlots`، `isWithinBookingWindow` — **هیچکدام** ویرایش نمیشوند |
|
||||
| `GET /api/v1/appointment-slots` | قرارداد request/response |
|
||||
| `GET /api/v1/appointment-settings/month-availability/{doctorUuid}` | قرارداد |
|
||||
| `Appointment::active_slot_key` و `refreshActiveSlotKey()` | مکانیزم یکتایی موجود |
|
||||
| `AppointmentRepository::isSlotTaken` | امضا و معنا |
|
||||
| `WeeklySchedule::MODE_SLOT` و `DEFAULT_META['booking_mode']` | مقدار پیشفرض `slot` میماند |
|
||||
|
||||
### چه چیزی مجاز است
|
||||
|
||||
- **افزودن** متد جدید به `SlotCalculatorService` — بدون تغییر متدهای موجود
|
||||
- **افزودن** کلاس/سرویس موازی (مثل `AvailabilityEngine` تسک ۰۶)
|
||||
- **افزودن** کلید جدید به `WeeklySchedule.meta` — با حفظ پیشفرضهای موجود
|
||||
- **افزودن** ستون تهیپذیر به `appointments`
|
||||
|
||||
### چه چیزی ممنوع است
|
||||
|
||||
- تغییر امضای هر متد موجود در `SlotCalculatorService`
|
||||
- تغییر شکل خروجی `appointment-slots` (حتی افزودن فیلد، اگر ترتیب/نوع فیلدهای موجود عوض شود)
|
||||
- «یکدستسازی» یا refactor مسیر اسلاتی
|
||||
- حذف `active_slot_key` یا `is_reserve`
|
||||
- اعمال قوانین جدید (تسک ۰۹) روی حالت `slot` — حتی اگر منطقی به نظر برسد
|
||||
|
||||
### اجبار خودکار
|
||||
|
||||
هر تسک باید این تست را سبز نگه دارد:
|
||||
|
||||
```bash
|
||||
ddev exec php bin/phpunit --group=slot-mode-frozen
|
||||
```
|
||||
|
||||
و در `tests/Appointment/SlotModeFrozenTest.php`:
|
||||
|
||||
```php
|
||||
/** @group slot-mode-frozen */
|
||||
public function testSlotModeContractUnchanged(): void
|
||||
{
|
||||
// snapshot خروجی appointment-slots برای یک برنامهٔ ثابت
|
||||
// هر تغییری در شکل پاسخ، این تست را قرمز میکند
|
||||
self::assertJsonStringEqualsJsonFile(
|
||||
__DIR__ . '/fixtures/slot-mode-contract.json',
|
||||
$this->client->getResponse()->getContent()
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
fixture در تسک ۰۰ ساخته میشود و **هیچ تسکی اجازهٔ بهروزرسانیاش را ندارد**.
|
||||
|
||||
---
|
||||
|
||||
## ۲. ✅ نوبتدهی سرویسی در همین فاز کامل میشود
|
||||
|
||||
`booking_mode = 'service'` نیمهکاره است: در مسیر رزرو (سایت و درawer پنل) کار میکند، ولی
|
||||
در ویرایش نوبت، نوبت رزرو، و بخشی از پنل بیمار غایب است.
|
||||
|
||||
تکمیلش **پیشنیاز** بقیهٔ فاز است، نه موازی با آن:
|
||||
|
||||
```
|
||||
تسک ۰۰ تکمیل نوبتدهی سرویسی در clinicpro
|
||||
تسک ۰۰ب سازگارسازی nobat724_front با وضعیت فعلی
|
||||
↓
|
||||
تسک ۰۱ به بعد (موتور چندمنبعی)
|
||||
```
|
||||
|
||||
دلیل ترتیب: اگر حالت `resource` (تسک ۰۶) روی حالت `service` نیمهکاره ساخته شود، هر باگ
|
||||
موجود سرویسی به موتور جدید ارث میرسد و تشخیص منبعش غیرممکن میشود.
|
||||
|
||||
---
|
||||
|
||||
## ۳. 🎨 هر صفحه یا بخش جدید، عیناً با دیزاینسیستم موجود
|
||||
|
||||
هیچ طراحی جدید، هیچ کامپوننت موازی، هیچ رنگ hard-code.
|
||||
جزئیات کامل و چکلیست: [ui-conventions.md](ui-conventions.md)
|
||||
|
||||
---
|
||||
|
||||
## ۴. ☑️ هیچ تسکی بدون تکمیل چکلیستش تمام نیست
|
||||
|
||||
هر تسک یک `checklist.md` دارد. پیش از اعلام پایان، **همهٔ** ردیفها باید وضعیت نهایی
|
||||
داشته باشند و هیچ ردیفی در `🔄` یا `⏳` نماند.
|
||||
قالب و قواعد: [definition-of-done.md](definition-of-done.md)
|
||||
@@ -0,0 +1,126 @@
|
||||
# قواعد UI — هر صفحه و بخش جدید عیناً مطابق سیستم موجود
|
||||
|
||||
**الزامی برای همهٔ تسکها.** هیچ طراحی جدید، هیچ تم جدید، هیچ کامپوننت موازی.
|
||||
منبع حقیقت: کدِ موجود، نه سلیقه و نه `docs/admin-ui/ui-design-spec.md` (که draft قدیمی
|
||||
با پالت بنفش است و با کد شیپشده نمیخواند).
|
||||
|
||||
---
|
||||
|
||||
## پنل ادمین `clinicpro` — React 19 + Webpack Encore + Tailwind v4
|
||||
|
||||
### توکنها — هرگز مقدار hard-code
|
||||
|
||||
منبع: `assets/admin/styles.css`، بلوک `:root`.
|
||||
|
||||
```tsx
|
||||
// ❌
|
||||
<div style={{ background: '#5559CE', borderRadius: 14 }}>
|
||||
|
||||
// ✅
|
||||
<div className="bg-[var(--primary)] rounded-[var(--r)]">
|
||||
```
|
||||
|
||||
| گروه | توکن |
|
||||
|---|---|
|
||||
| برند | `--primary` `#5559CE` · `--primary-600/700` · `--primary-soft/soft2` · `--on-primary` |
|
||||
| اکسنت | `--accent` `#f0682a` · `--accent-600` · `--accent-bg` |
|
||||
| سطوح | `--bg` `--bg-2` `--surface` `--surface-2/3` `--border` `--border-2` |
|
||||
| متن | `--text` `--text-2` `--text-3` |
|
||||
| وضعیت | `--success/-bg` `--warning/-bg` `--danger/-bg` `--info/-bg` `--violet/-bg` |
|
||||
| کارت آمار | `--stat-{amber,violet,green,pink}-{bg,fg}` |
|
||||
| شعاع | `--r-xs:7` `--r-sm:8` `--r:14` `--r-lg:18` `--r-xl:24` `--r-pill:999` |
|
||||
| سایه | `--shadow-sm` `--shadow` `--shadow-lg` |
|
||||
| چیدمان | `--sidebar-w:243` `--collapsed-w:90` `--topbar-h:64` `--gap:20` `--card-pad:22` `--row-h:56` |
|
||||
| حرکت | `--ease: cubic-bezier(.22,.61,.36,1)` |
|
||||
|
||||
دارکمود با `[data-theme="dark"]` و حالت فشرده با `[data-density="compact"]` خودکار
|
||||
اعمال میشوند — **اگر** از توکن استفاده کرده باشی. مقدار hard-code در دارکمود میشکند.
|
||||
|
||||
### کامپوننتها — اول جستوجو، بعد ساخت
|
||||
|
||||
`assets/admin/components/ui/` اینها را دارد. ساختن نسخهٔ موازی از هر کدام **رد** میشود:
|
||||
|
||||
```
|
||||
DataTable (مرتبسازی، جستجو، skeleton، empty state، bulk) · Modal · ConfirmDialog
|
||||
PageHeader (عنوان + breadcrumb + action + backTo) · BackButton · StatCard · StatusBadge
|
||||
Pagination · SearchableSelect · AppointmentStatusDropdown
|
||||
PersianDateInput / PersianDatePicker / PersianCalendar
|
||||
MobileInput · PriceInput · Portal · FeatureGate · Altcha
|
||||
```
|
||||
|
||||
### پنج قاعدهٔ غیرقابلمذاکره
|
||||
|
||||
1. **`SearchableSelect`، هرگز `<select>` بومی.** بیاستثنا.
|
||||
2. **دکمهٔ بازگشت در هر زیرصفحه.** صفحهای که از دل صفحهٔ دیگر باز میشود:
|
||||
- با `PageHeader` → فقط `backTo="/admin/…"` بده
|
||||
- بی `PageHeader` → `<BackButton fallback="/admin/…" />` بالای هدر
|
||||
- دکمهٔ دستساز نساز. رفتار در `hooks/useGoBack.ts` متمرکز است.
|
||||
3. **وضعیت لیست در URL، نه در `useState`.** جستجو، فیلتر، شمارهٔ صفحه، نما — همه با
|
||||
`hooks/useUrlState.ts`:
|
||||
```tsx
|
||||
const [urlState, setUrlState] = useUrlState({ page: '1', search: '', status: '' });
|
||||
const setSearch = (v: string) => setUrlState({ search: v, page: '1' });
|
||||
```
|
||||
دلیلش «بازگشت» است: `navigate(-1)` همان URL را برمیگرداند. فیلد جستجوی debounceشده
|
||||
local میماند، فقط مقدار نهایی به URL میرود.
|
||||
4. **داده فقط با TanStack Query.** `useQuery`/`useMutation`، کلید `['resource', page, filters]`،
|
||||
همهٔ HTTP از `lib/api.ts`. استخراج: paginated → `data?.data` + `data?.meta?.totalRecords`؛
|
||||
single → `data?.data`؛ Category → `data?.data?.data ?? []`.
|
||||
5. **فرم با React Hook Form + Zod.** `z.object({...})` و تایپ با `z.infer`.
|
||||
|
||||
### فارسی، RTL، شمسی
|
||||
|
||||
- همهٔ رشتههای UI فارسی — از فایل i18n، نه inline
|
||||
- تاریخها شمسی با `formatDate`/`formatDateTime` از `lib/utils.ts` (پشتصحنه `jalaali-js`)
|
||||
- مبالغ با `formatRial`/`formatNumber`
|
||||
- جهت RTL — `ms-*`/`me-*` بهجای `ml-*`/`mr-*`
|
||||
- فونت `Vazirmatn` از `@fontsource/vazirmatn`
|
||||
- آیکنها Heroicons v2 · نمودار Recharts · تُست `sonner`
|
||||
|
||||
### نامگذاری و مسیر
|
||||
|
||||
- صفحه: `assets/admin/pages/XxxPage.tsx` · کامپوننت PascalCase · هوک `useXxx.ts`
|
||||
- کامپوننت عمومی → `components/ui/` · کامپوزیت مخصوص فیچر → `components/*.tsx`
|
||||
- تایپهای مشترک → `types/index.ts`
|
||||
- مسیر در `App.tsx` (React Router v7)، زیر `/admin/*`
|
||||
|
||||
---
|
||||
|
||||
## سایت عمومی `nobat724_front` — Next.js 15 App Router + MUI v5 + Tailwind
|
||||
|
||||
- تم MUI در `mui/index.js` با `direction: rtl` و فونت Vazir — تم جدید نساز
|
||||
- Tailwind با `darkMode: "class"`؛ صفحات عمومی `data-theme`، پنل `class`
|
||||
(`next-themes` در `app/Providers.js`)
|
||||
- **فونت فقط Vazir** — در `app/globals.css` با `@font-face`. فونت دیگر اضافه نکن.
|
||||
- هر صفحه باید `generateMetadata` صادر کند · همیشه `await params`
|
||||
- شهر از subdomain: server-side `lib/getStateInfo.js` · client-side `useProvince()`
|
||||
- فراخوانی API: `services/response.js` → `request.*`؛ برای auth `{ requireAuth: true }`
|
||||
- داده server-side: `lib/req.js` → `fetchReq(url)`
|
||||
- slug پزشک/کلینیک = `uuid`
|
||||
- JSON-LD مستقیم در JSX صفحات doctor/clinic/blog
|
||||
- تاریخ شمسی با `jalali-moment` / `dayjs`
|
||||
- کامپوننتهای موجود `components/appointment/*` را توسعه بده، مسیر موازی نساز
|
||||
|
||||
---
|
||||
|
||||
## چکلیست UI — در `checklist.md` هر تسکی که صفحه یا کامپوننت میسازد
|
||||
|
||||
هر ردیف باید یکی از ✅ / 🔄 / ⏳ / ⚠️ بگیرد:
|
||||
|
||||
```
|
||||
□ هیچ رنگ/شعاع/سایهٔ hard-code نیست — همه از توکنهای styles.css
|
||||
□ دارکمود بررسی شد (data-theme="dark") و چیزی نمیشکند
|
||||
□ حالت فشرده بررسی شد (data-density="compact")
|
||||
□ همهٔ select ها SearchableSelect اند، هیچ <select> بومی نیست
|
||||
□ زیرصفحهها backTo یا <BackButton /> دارند
|
||||
□ وضعیت لیست (جستجو/فیلتر/صفحه) در URL است با useUrlState
|
||||
□ لیستها از DataTable استفاده میکنند با skeleton و empty state فارسی
|
||||
□ هیچ کامپوننت موازیِ چیزی که در components/ui/ هست ساخته نشد
|
||||
□ همهٔ رشتهها فارسی و از i18n
|
||||
□ تاریخها شمسی با formatDate · مبالغ با formatRial
|
||||
□ RTL بررسی شد (ms/me نه ml/mr)
|
||||
□ موبایل بررسی شد (بدون اسکرول افقی)
|
||||
□ فرمها با React Hook Form + Zod
|
||||
□ داده با TanStack Query و استخراج envelope درست
|
||||
□ خطاها با پیام فارسی از ErrorCodes نمایش داده میشوند
|
||||
```
|
||||
Reference in New Issue
Block a user