feat(panel): connect representative panel and user dashboard to real API, removing mock data

This commit is contained in:
hamed
2026-06-15 09:12:51 +03:30
parent d9dc976ab3
commit 4374ed5043
@@ -0,0 +1,164 @@
# اتصال پنل نماینده و داشبورد کاربر به API واقعی (حذف داده‌های mock)
## پروژه
`nobat724_front` — سایت عمومی نوبت‌دهی. این کار صرفاً frontend است و backend (`clinicpro`) تغییر نمی‌کند؛ همه‌ی endpointهای لازم از قبل در `clinicpro/docs/api/` مستند و آماده‌اند.
## زمینه
بخش عمده‌ی سایت عمومی (لیست/جزئیات پزشک، کلینیک، بلاگ، فرایند نوبت‌گیری، پرداخت، لاگین OTP) قبلاً از طریق `services/response.js``request.*` به API واقعی متصل است. اما دو ناحیه هنوز روی داده‌ی mock محلی کار می‌کنند و عملاً برای همه‌ی کاربران یک داده‌ی ثابت و ساختگی نشان می‌دهند:
1. **پنل نماینده** (`app/panel/(layout)/*`) — از `data/information.json` تغذیه می‌شود (نام، آمار، نمودار درآمد، لیست نوبت پزشکان).
2. **داشبورد/حساب کاربری بیمار** (`components/dashboard/*`) — از `data/userData.json` و `data/bank.json` تغذیه می‌شود (پروفایل، نوبت‌ها، تراکنش‌ها).
نکته‌ی مهم: layout پنل (`app/panel/(layout)/layout.js`) **همین حالا** `representationInfo` واقعی را server-side از `GET /api/v1/representation/{uuid}` می‌گیرد و به `Content` پاس می‌دهد، ولی `Content` و صفحات داخلی این prop را نادیده می‌گیرند و به‌جایش `information.json` را import می‌کنند. پس بخشی از کار «وصل‌کردن سیم‌های قطع‌شده»ی موجود است، نه ساختن از صفر.
## مشکل / هدف
پنل نماینده و داشبورد کاربر را به API واقعی وصل کن تا داده‌ی هر کاربر واقعی و مخصوص خودش باشد، و importهای `data/information.json`، `data/userData.json`، `data/bank.json` از مسیر render حذف شوند.
این کار باید **مرحله‌به‌مرحله** انجام شود (یک قابلیت در هر مرحله: پیاده‌سازی → build/lint → تأیید → commit)، چون نواحی مستقل‌اند و ریسک رگرسیون در صفحات mock بالاست.
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `services/response.js` | لایه‌ی `request.*`؛ توابع موجود استفاده می‌شوند و توابع جدید representation dashboard اضافه می‌شود |
| `app/panel/(layout)/layout.js` | server-side؛ `representationInfo` را از API می‌گیرد و پاس می‌دهد (موجود — تغییر نمی‌کند) |
| `components/panel/Content.js` | `import Information from "@/data/information.json"` را حذف و `representationInfo` را مصرف کن |
| `app/panel/(layout)/dashboard/page.js` | mock `Information` را با داده‌ی واقعی representation dashboard جایگزین کن |
| `app/panel/(layout)/turns/page.js` | mock `Information` → نوبت‌های واقعی |
| `app/panel/(layout)/user-account/page.js` | mock `Information` + `Bank` → داده‌ی واقعی نماینده + حساب بانکی |
| `app/panel/(layout)/add-doctor/page.js` | mock `Information``representationInfo` |
| `components/dashboard/Content.js` | `import userData from "@/data/userData.json"` را با `user-profile` + appointments واقعی جایگزین کن |
| `components/dashboard/userAccount/sidebars/turns/index.js` | از قبل `request.*` دارد — صرفاً بررسی اتصال |
| `components/dashboard/userAccount/sidebars/transactions/index.js` | از قبل `request.*` دارد — صرفاً بررسی اتصال |
| `lib/auth.js` | `getUser()` server-side از کوکی (برای uuid/representation_uuid) |
| `clinicpro/docs/api/representation.md` | قرارداد endpointهای representation و dashboard ماهانه/سالانه |
| `clinicpro/docs/api/user-profile.md` | قرارداد پروفایل کاربر |
## وضعیت فعلی
### پنل: داده‌ی واقعی موجود است ولی نادیده گرفته می‌شود
`app/panel/(layout)/layout.js` — این بخش درست است و حفظ می‌شود:
```js
let representationInfo = null;
// ...
representationInfo = await fetchReq(
`${API_URL}/api/v1/representation/${representationUuid}`,
{ headers: { Authorization: `Bearer ${accessToken.value}` } }
);
return <Content children={children} representationInfo={representationInfo} />;
```
اما `components/panel/Content.js` آن را مصرف نمی‌کند و mock import می‌کند:
```js
import Information from "@/data/information.json";
// ...
function Content({ children, representationInfo }) {
// representationInfo دریافت می‌شود ولی فقط به UserDetail پاس داده می‌شود
// Header همچنان data={Information} (mock) می‌گیرد
```
و `app/panel/(layout)/dashboard/page.js` کاملاً mock است:
```js
import DashboardPage from "@/components/panel/dashboard";
import Information from "@/data/information.json";
function Dashboard() {
return <DashboardPage data={Information} />;
}
```
### داشبورد کاربر: کاملاً mock
`components/dashboard/Content.js`:
```js
import userData from "@/data/userData.json";
// ...
<DashboardPage user={userData.data} ...>
<UserAccountPage user={userData.data} ... />
</DashboardPage>
```
### لایه‌ی API موجود (در `services/response.js`)
این توابع از قبل آماده‌اند و باید استفاده شوند:
```js
getRepresentationInfo: (uuid) => api.get(`api/v1/representation/${uuid}`, { requireAuth: true }),
getUserProfile: (uuid) => api.get(`/api/v1/user-profile/${uuid}`, { requireAuth: true }),
getMyAppointments: (userId, params) => api.get(`api/v1/appointment/my-appointments/${userId}`, { params, requireAuth: true }),
getMyPayments: (userId, params) => api.get(`api/v1/payment/my-payments/${userId}`, { params, requireAuth: true }),
```
توابع dashboard ماهانه/سالانه نماینده **هنوز نیستند** و باید اضافه شوند (endpointها در `representation.md` مستند است).
## وظایف
اجرای مرحله‌به‌مرحله؛ بعد از هر قابلیت `npm run lint && npm run build` و سپس commit جدا.
### ۱. افزودن توابع representation dashboard به `services/response.js`
طبق `clinicpro/docs/api/representation.md`:
```js
getRepresentationDashboardMonthly: (uuid, year, month) =>
api.get(`api/v1/representation/${uuid}/dashboard/monthly`, {
params: { year, month },
requireAuth: true,
}),
getRepresentationDashboardYearly: (uuid, year) =>
api.get(`api/v1/representation/${uuid}/dashboard/yearly`, {
params: { year },
requireAuth: true,
}),
```
> پاسخ تک‌منبعی است: داده در `response.data` (پس از interceptor در `api.js` که `response.data` را برمی‌گرداند) → سپس `result.data` طبق envelope `{ success, data }`.
### ۲. وصل‌کردن Header و UserDetail پنل به `representationInfo`
در `components/panel/Content.js`:
- `import Information from "@/data/information.json"` را حذف کن.
- `representationInfo` (که از layout می‌آید) را به `<Header data={...} />` و `<UserDetail data={...} />` پاس بده.
- چون شکل `representationInfo` (فیلدهای `full_name`, `city_name`, `commission_percent`, `bank_account`, ...) با شکل mock (`name`, `detail`, `expertise`, `all_patients`, ...) فرق دارد، یک adapter کوچک بنویس که فیلدهای واقعی را به همان propهایی که `Header`/`UserDetail` انتظار دارند map کند. فیلدهای آماری که در API نیستند (مثل `todays_activities`) را از dashboard ماهانه پر کن یا حذف کن — توهم‌سازی داده ممنوع.
### ۳. صفحه‌ی dashboard پنل با داده‌ی واقعی
`app/panel/(layout)/dashboard/page.js`:
- این صفحه Server Component است. `representation_uuid` را از کوکی `userInfo` بخوان (مثل `layout.js`).
- `GET /api/v1/representation/{uuid}/dashboard/monthly?year=&month=` را با سال/ماه جلالی جاری فراخوانی کن (تبدیل به سال/ماه میلادی یا طبق چیزی که backend انتظار دارد — در `representation.md` پارامتر `year`/`month` عددی است؛ بررسی کن backend جلالی می‌خواهد یا میلادی، اگر مبهم بود همین‌جا توقف کن و بپرس).
- خروجی `stats` (`total_appointments`, `total_revenue_rials`, `commission_rials`, `daily[]`) را به `DashboardPage` بده.
- نمودار درآمد (`chart_amount_income` در mock) را از `daily[].revenue` یا از dashboard سالانه (`months[].revenue_rials`) بساز.
### ۴. صفحات turns و user-account و add-doctor پنل
- `turns/page.js`: لیست نوبت پزشکانِ نماینده — اگر endpoint اختصاصی نیست، از همان dashboard ماهانه (`daily`) یا توقف و پرسش. mock حذف شود.
- `user-account/page.js`: `bank_account` واقعی از `representationInfo` به‌جای `data/bank.json`؛ اطلاعات نماینده به‌جای `information.json`.
- `add-doctor/page.js`: `Information` mock فقط برای نمایش هدر استفاده می‌شود → با `representationInfo` جایگزین کن.
### ۵. داشبورد/حساب کاربری بیمار با داده‌ی واقعی
`components/dashboard/Content.js`:
- `import userData from "@/data/userData.json"` را حذف کن.
- `uuid` کاربر را از کوکی بخوان (یا از prop `logged`/`getUser`).
- پروفایل را با `request.getUserProfile(uuid)` بگیر (طبق `user-profile.md`).
- نوبت‌ها و تراکنش‌ها از قبل در `sidebars/turns/index.js` و `sidebars/transactions/index.js` با `request.getMyAppointments` / `request.getMyPayments` گرفته می‌شوند — صرفاً تأیید کن که `userId` درست پاس می‌شود و دیگر به `userData.json` وابسته نیستند.
- فیلدهای mock که معادل API ندارند را حذف کن (نه ساختن داده‌ی جعلی).
## نکات مهم
- **هیچ تغییری در backend لازم نیست.** اگر حین کار به endpoint غایبی برخوردی (مثلاً «لیست نوبت پزشکان یک نماینده»)، **متوقف شو و بپرس** — پرامپت جداگانه برای backend لازم می‌شود (cross-repo)؛ داده‌ی جعلی جایگزین نکن.
- **App Router:** صفحات پنل/داشبورد که داده‌ی server-side می‌خواهند باید Server Component بمانند و کوکی را با `cookies()` از `next/headers` بخوانند؛ کامپوننت‌های `"use client"` که `request.*` صدا می‌زنند uuid را به‌صورت prop بگیرند. همیشه `await params`/`await cookies()`.
- **Auth:** همه‌ی این endpointها `requireAuth` می‌خواهند؛ توابع `request.*` خودشان کوکی `access_token` را به‌صورت Bearer ضمیمه می‌کنند. در 401، `api.js` به‌صورت خودکار logout و redirect به `/login` می‌کند — رفتار موجود را خراب نکن.
- **Multi-domain:** این صفحات هم زیر subdomain شهر اجرا می‌شوند؛ هر `matchedCity`/`getStateInfo` موجود را حذف نکن.
- **شکل پاسخ:** envelope بک‌اند `{ success, data }` است. interceptor در `services/api.js` یک‌بار `response.data` را برمی‌گرداند، پس در فراخوانی‌ها داده‌ی واقعی در `result.data` است. این را با یک endpoint واقعی تأیید کن، نه با حدس.
- **Jalali/RTL:** تاریخ‌ها با `jalali-moment`/`dayjs` به شمسی نمایش داده شوند؛ مبالغ به ریال با جداکننده‌ی هزارگان فارسی. timestampهای API یونیکس هستند.
- **adapter جدا:** برای هر ناحیه یک تابع map کوچک بنویس (API → propهای کامپوننت موجود) تا کامپوننت‌های نمایشی دست‌نخورده بمانند و دیف کوچک شود.
- **تست:** بعد از هر قابلیت `npm run lint` و `npm run build`؛ build کامل خطاهای صفحه/متادیتا را آشکار می‌کند. سپس commit با پیام توصیفی فارسی/انگلیسی برای همان قابلیت.
- **فایل‌های mock:** فعلاً فایل‌های `data/*.json` را پاک نکن؛ فقط importشان را از مسیر render حذف کن (ممکن است جای دیگری مرجع داشته باشند). حذف نهایی فایل‌ها در یک commit جدا بعد از تأیید عدم استفاده.