---
name: redesign-page
description: نقد و بازطراحی حرفه‌ای UI/UX یک صفحه از پنل ادمین ClinicPro از روی URL آن — اسکرین‌شات در چهار نما (روشن، تیره، فشرده، موبایل)، پروبِ دسترسی‌پذیری روی DOM زنده، نگاشت URL به فایل سورس، آدیت انحراف از دیزاین‌سیستم، و بازنویسی با کامپوننت‌ها و توکن‌های موجود. استفاده کن وقتی کاربر یک URL از /admin می‌دهد و می‌گوید «این صفحه ui/ux خوبی ندارد»، «این صفحه را بازطراحی کن»، «این صفحه را نقد کن»، «redesign this page»، «UI/UX review»، «این قسمت را درست کن»، یا «screenshot این صفحه».
---

# نقد و بازطراحی صفحهٔ پنل ادمین ClinicPro

نقش: متخصص ارشد UI/UX. هدف **بهبود تجربهٔ کاربری در چهارچوب تم فعلی** است، نه ساختن
هویت بصری جدید. هر تغییری که با دیزاین‌سیستم فعلی ناسازگار باشد، رد است.

پنل ادمین یک SPA کلاینت‌ساید است (React 19 + Webpack Encore، سرو شده از `/admin/*`).
یعنی `curl` و فلگ `--screenshot` کروم به درد نمی‌خورند: هر دو روی فرم لاگین می‌نشینند،
چون توکن JWT در `localStorage['clinicpro-auth']` است. درایور این skill آن کار را
می‌کند: با API لاگین می‌کند، `localStorage` را seed می‌کند، تم/تراکم را می‌نشاند، بعد
ناوبری و اسکرین‌شات می‌گیرد — با CDP روی `WebSocket` نیتیو Node 22، **بدون هیچ وابستگی
npm** (نه playwright، نه puppeteer).

مسیرها نسبت به `clinicpro/` هستند.

## پیش‌نیازها

هیچ نصبی لازم نیست:

```bash
ddev describe | head -3          # باید بالا باشد: https://clinic-pro.ddev.site
ls "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
```

کروم جای دیگری است؟ `CHROME_BIN` را ست کن.

## گردش کار

### ۰. اول دیزاین‌سیستم را بخوان — قبل از هر چیز

**منبع حقیقتِ توکن‌ها `assets/admin/styles.css` است**، نه `docs/admin-ui/ui-design-spec.md`
(آن سند قدیمی است و پالت بنفشش با کد شیپ‌شده نمی‌خواند).

```bash
node .claude/skills/redesign-page/driver.mjs ds            # توکن‌ها + کلاس‌ها + کامپوننت‌ها
node .claude/skills/redesign-page/driver.mjs ds tokens     # فقط توکن‌ها
node .claude/skills/redesign-page/driver.mjs ds components # فقط کامپوننت‌های مشترک با Props
```

خروجی واقعی: `TOKENS (57)` و `SHARED COMPONENTS (26)`. قانون ترتیب:
**اول کامپوننت موجود، بعد توسعه/عمومی‌کردنش، در آخر ساخت کامپوننت جدید** — و دلیلش را بنویس.

### ۱. چهار نمای اجباری

```bash
node .claude/skills/redesign-page/driver.mjs variants \
  "https://clinic-pro.ddev.site/admin/resources" --dir /tmp/clinicpro-review
```

چهار فایل می‌سازد: `-light` · `-dark` · `-compact` · `-mobile`. **هر چهار را با ابزار
Read باز کن و نگاه کن.** قضاوت با یک اسکرین‌شات یعنی صفحه‌ای که در سه نمای دیگر خراب است.
تم تیره و تراکم فشرده در این پنل تنظیمات واقعی کاربرند، نه فرض.

تک‌نما:

```bash
node .claude/skills/redesign-page/driver.mjs shot "<url>" --out /tmp/x.png \
  --theme dark --density compact --w 390 --h 844 --full --wait 6000
```

فلگ‌ها: `--w/--h` ویوپورت · `--wait` میلی‌ثانیه · `--full` کل صفحه ·
`--theme light|dark` · `--density comfortable|compact` · `--context clinic|personal` ·
`--no-probe`.

هر `shot` یک **پروب رانتایم** هم می‌زند که فقط روی DOM رندرشده دیدنی است:

```
RUNTIME
  ⚠ 2 form field(s) with no label
```

چه چیزهایی می‌گیرد: سرریز افقی، دکمهٔ آیکونیِ بی‌نام (بدون `aria-label`/`title`)،
فیلد بدون لیبل، و کنترل کوتاه‌تر از ۳۲px (هدف لمسی ۴۴px است).

### ۲. نگاشت URL به سورس + آدیت ایستا

```bash
node .claude/skills/redesign-page/driver.mjs inspect "https://clinic-pro.ddev.site/admin/resources"
```

خروجی واقعی:

```
route     resources
component ResourcesPage
file      assets/admin/pages/ResourcesPage.tsx
components PageHeader, DataTable, ResourceBlocksModal, ConfirmDialog, SearchableSelect, …
lines     278
test      assets/admin/pages/ResourcesPage.test.tsx

AUDIT  clean
```

روی هر فایل مستقیم:

```bash
node .claude/skills/redesign-page/driver.mjs audit assets/admin/pages/ClinicDetailPage.tsx
# AUDIT
#   assets/admin/pages/ClinicDetailPage.tsx:242  hand-rolled overlay — use the shared <Modal>
```

آدیت این‌ها را می‌گیرد: `<select>` نیتیو، `.btn` بدون واریانت، هگز هاردکد، overlay دستی،
`<label>` داخل `.field`، تاریخ میلادی، `type="date"`، دکمهٔ آیکونی بدون `aria-label`،
توکن تعریف‌نشده، و `.seg` بدون کلاس `on/active`.

### ۳. گزارش — همیشه با این ۹ بخش

```
۱. تحلیل صفحه            چه کاری برای چه کاربری؛ جریان اصلی
۲. مشکلات UI             سلسله‌مراتب بصری، فاصله، تایپوگرافی، رنگ، انحراف از DS
۳. مشکلات UX             جریان کار، تعداد کلیک، حالت‌های Loading/Empty/Error، ریسپانسیو، دسترسی‌پذیری
۴. پیشنهادهای بهبود      برای هر مشکل، یک راه‌حل مشخص و قابل اجرا
۵. ساختار جدید صفحه      چیدمان پیشنهادی، در چهارچوب همین تم
۶. کامپوننت‌های قابل استفادهٔ مجدد   از components/ui که همین حالا جواب می‌دهند
۷. کامپوننت‌های نیازمند بهبود        کدام Props/API باید عمومی‌تر شود و چرا
۸. کامپوننت‌های جدید      فقط در صورت ضرورت، با دلیل نبودِ جایگزین
۹. دلیل هر تغییر         چرا این تغییر تجربه را بهتر می‌کند
```

هر یافته باید به `file:line` وصل باشد یا به یکی از اسکرین‌شات‌ها. یافتهٔ بی‌ارجاع، حدس است.

### ۴. بازنویسی، سپس مقایسه

```bash
ddev exec yarn dev
node .claude/skills/redesign-page/driver.mjs variants "<همان url>" --dir /tmp/clinicpro-review-after
```

before/after را کنار هم بگذار. اگر تفاوتی دیده نمی‌شود، باندل قدیمی است.

### ۵. تست + تایپ‌چک (بدون این، تسک تمام نیست)

```bash
ddev exec npx tsc --noEmit --project tsconfig.json
npx vitest run                                   # روی هاست، نه داخل ddev
```

خط پایه در ۲۰۲۶-۰۸-۰۲: **۱۰۰ فایل، ۶۶۰ تست، همه سبز.** هر شکستی مالِ توست.
(نسخهٔ قبلی این سند از «۲۱ تست از پیش شکسته» می‌گفت — دیگر درست نیست.)

## چک‌لیست بازطراحی

درایور موارد گرپ‌شدنی و DOMی را می‌گیرد؛ این‌ها را باید خودت با چشم ببینی:

- **`.field` در مقابل `.field-block`** — `.field` خودش باکسِ بوردردار اینپوت است. لیبل
  داخلش یعنی لیبل چسبیده به اینپوت؛ `SearchableSelect` داخلش یعنی دو باکس تودرتو.
  «لیبل بالای فیلد» → `.field-block`.
- **`.card` پدینگ ندارد** — `card-pad` آن را می‌دهد.
- **`className="btn"` بدون واریانت** بی‌رنگ و بی‌بوردر رندر می‌شود، عملاً نامرئی.
  همیشه `btn primary` / `btn secondary` / `btn ghost` / `btn danger`.
- **دکمهٔ فقط-آیکون** → `mini-btn`، نه `btn ghost sm` با پدینگ دستی.
- **`.seg`** فقط `button` و `a` را می‌شناسد و کلاس فعالش `on` است (`active` هم alias شد).
  تبِ فعالِ بی‌کلاس یعنی هیچ نشانه‌ای ندارد.
- **سلسله‌مراتب** — عنوان در `PageHeader` بیاید و در کارت زیرش تکرار نشود.
- **صفحهٔ زیرمجموعه** حتماً `backTo` یا `<BackButton fallback=…>` دارد.
- **وضعیت لیست در URL** — جستجو/فیلتر/صفحه با `useUrlState`، نه `useState`؛ وگرنه
  «بازگشت» نما را می‌پراند.
- **RTL/جلالی** — رشته‌های جدید فارسی، تاریخ‌ها جلالی، اعداد با `formatNumber`/`formatRial`.
- **دارک‌مود** — با توکن خودکار درست است؛ یک هگز هاردکد آن را می‌شکند.
- **سه حالت داده** — Loading / Empty / Error هر سه باید طراحی داشته باشند، نه فقط حالت پر.

## Gotchas

- **کاربر پیش‌فرض درایور با دیتابیس فعلی هماهنگ است، ولی دیتابیس عوض می‌شود.**
  پیش‌فرض `0912000201` / `QaTest@1234` (پزشکِ مالک کلینیک) است. اگر لاگین `ERR_AUTH_005`
  داد، دیتابیس دوباره seed شده: `ddev exec php bin/console app:seed-scenarios --reset -n`.
  در اجرای ۲۰۲۶-۰۸-۰۲ کاربر `0912000301` (مالک غیرپزشک) **رمز نداشت** و لاگینش رد شد؛
  از `0912000201` یا `0912000101` استفاده کن.

- **ریدایرکت خاموش نقش‌ها.** `RoleRoute` کاربرِ بی‌مجوز را بی‌صدا به `/admin/dashboard`
  می‌برد — یعنی یک اسکرین‌شات کاملاً سالم از **صفحهٔ اشتباه**. درایور مقایسه می‌کند:

  ```
  ⚠ WRONG PAGE: asked for /admin/clinics/…, landed on /admin/dashboard
  ```

- **محیط کاری، نه نقش.** کاربری که هم مطب شخصی دارد هم کلینیک، پیش‌فرض روی مطب می‌نشیند و
  صفحهٔ منابع/سرویس‌های کلینیک **خالی** می‌آید. این باگ نیست: `--context clinic` بده.

- **مودال نصب PWA جلوی صفحه را می‌گیرد.** درایور `pwa-dismissed=1` را seed می‌کند. با
  کروم خام، این مودال وسط تصویر است.

- **تم فقط با صفت `data-theme` نمی‌ماند** — بعد از hydrate از `localStorage['clinicpro-ui']`
  دوباره خوانده می‌شود. درایور هر دو را می‌نویسد و بعد از رندر یک بار دیگر صفت را می‌گذارد.

- **موبایل بدون `Emulation.setDeviceMetricsOverride` فقط «پنجرهٔ باریک» است** — مدیا
  کوئری‌های `pointer: coarse` خاموش می‌مانند و ارتفاع لمسی ۴۴px دیده نمی‌شود. درایور برای
  عرض ≤۴۸۰ خودش این را روشن می‌کند.

- **کپچا (altcha) لوکال اجباری نیست.** `POST /api/v1/user/login` بدون فیلد `altcha` هم
  توکن می‌دهد؛ درایور به همین تکیه می‌کند و روی محیطی که کپچا را اجبار کند می‌شکند.

- **سرت ddev را Node رد می‌کند** (`UNABLE_TO_VERIFY_LEAF_SIGNATURE`). درایور فقط برای
  `*.ddev.site` / `localhost` تأیید TLS را خاموش می‌کند، نه برای هر مبدأ.

- **صفحهٔ نوبت‌ها خودش تا ساعت جاری اسکرول می‌کند**، پس ویوپورت وسط تایم‌لاین می‌افتد.
  برای دیدن هدر `--full` بده.

- **منوی تنظیمات دو مصرف‌کننده دارد** — `settingsMenu.ts` منبع واحد است؛ سایدبار دسکتاپ و
  فهرست موبایل هر دو از آن می‌خوانند. در موبایل کل منو **بالای** محتوا می‌نشیند، پس صفحهٔ
  تنظیماتی در نمای ۳۹۰px یعنی ۱۵ آیتم منو قبل از رسیدن به محتوا. در نقد موبایل حتماً ببینش.

- **بیلد CSS داخل ddev خطای نیتیو `lightningcss` می‌دهد** — از قبل هست و جلوی کامپایل
  JS/TS را نمی‌گیرد؛ خطاهای TypeScript همچنان در خروجی `tsc` می‌آیند.

## Troubleshooting

| علامت | علت / راه‌حل |
|---|---|
| `login failed for 0912000201: … ERR_AUTH_005` | دیتابیس دوباره seed شده. `ddev exec php bin/console app:seed-scenarios --reset -n` یا `CLINICPRO_USER`/`CLINICPRO_PASS` را ست کن. |
| `Chrome did not expose CDP on :9333` | نمونهٔ کروم قبلی زنده مانده. `CDP_PORT=9444` بده یا پروسه را بکش. |
| `⚠ redirected to /login` | توکن رد شد؛ معمولاً JWT منقضی شده — دوباره اجرا کن. |
| `⚠ WRONG PAGE` | نقشِ کاربر اجازه ندارد، یا محیط اشتباه است (`--context clinic`). |
| `⚠ page text is only N chars` | صفحه خالی رندر شده. `--wait 8000` بده یا کنسول را چک کن. |
| اسکرین‌شات تغییرات را نشان نمی‌دهد | باندل قدیمی است. `ddev exec yarn dev` (یا `yarn watch` روشن). |
| صفحهٔ کلینیک خالی است ولی خطا ندارد | محیط روی مطب شخصی است. `--context clinic`. |
