# Admin Panel PWA

پنل ادمین (`/admin`) را به یک Progressive Web App تبدیل کن تا کاربران بتوانند آن را روی دستگاه خود نصب کنند و آفلاین هم shell اولیه را ببینند.

---

## وضعیت فعلی پروژه

- **Twig template**: `templates/admin/index.html.twig` — فایل HTML ورودی پنل ادمین
- **Webpack Encore** با `addEntry('admin', './assets/admin/index.tsx')`
- **Output path**: `public/build/`
- **Public path**: `/build`
- آیکون‌های موجود: `public/favicon.ico` و `public/favicon.png`
- دامنه local: `https://clinic-pro.ddev.site`

**قابلیت‌های ۱ تا ۴ قبلاً اجرا شده‌اند:**
- `public/manifest.json` ✅ موجود است
- `public/sw.js` ✅ موجود است
- ثبت SW در `assets/admin/index.tsx` ✅ انجام شده
- `assets/admin/components/ui/PwaInstallBanner.tsx` ✅ موجود است (برای کاربران logged-in)

---

## قابلیت‌های مورد نیاز

### قابلیت ۱ — Web App Manifest

فایل `public/manifest.json` بساز با این مشخصات:

```json
{
  "name": "ClinicPro Admin",
  "short_name": "ClinicPro",
  "description": "پنل مدیریت کلینیک پرو",
  "start_url": "/admin",
  "scope": "/admin",
  "display": "standalone",
  "orientation": "portrait-primary",
  "theme_color": "#6366f1",
  "background_color": "#f8fafc",
  "lang": "fa",
  "dir": "rtl",
  "icons": [
    { "src": "/favicon.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
    { "src": "/favicon.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
  ]
}
```

- رنگ `theme_color` را از CSS variable `--primary` پروژه بگیر (مقدار واقعی را از `assets/admin/styles.css` بخوان)
- در `templates/admin/index.html.twig` این تگ‌ها را به `<head>` اضافه کن:
  ```html
  <link rel="manifest" href="/manifest.json">
  <meta name="theme-color" content="#6366f1">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="default">
  <meta name="apple-mobile-web-app-title" content="ClinicPro">
  <link rel="apple-touch-icon" href="/favicon.png">
  ```

---

### قابلیت ۲ — Service Worker (Cache Shell)

یک service worker در `public/sw.js` بنویس. **بدون workbox** — native Service Worker API:

**استراتژی:**
- `install` event: کش کردن shell (فایل‌های ضروری برای render اولیه)
- `activate` event: پاک کردن کش‌های قدیمی
- `fetch` event:
  - درخواست‌های API (`/api/`) → **فقط Network** (هرگز کش نکن، اگر آفلاین بود خطا بده)
  - فایل‌های build (`/build/`) → **Cache First** (سریع‌تر، stale-while-revalidate)
  - navigation (`/admin*`) → **Network First با fallback** به shell کش‌شده

**فایل‌هایی که در install کش می‌شوند:**
```
/admin
/manifest.json
/favicon.png
```
فایل‌های `/build/` را در install کش **نکن** (hash در نام آن‌هاست و هر deploy عوض می‌شود) — آن‌ها را در fetch با Cache First مدیریت کن.

**Cache name** شامل version باشد تا activate بتواند قدیمی‌ها را پاک کند:
```js
const CACHE_NAME = 'clinicpro-admin-v1';
const BUILD_CACHE = 'clinicpro-build-v1';
```

---

### قابلیت ۳ — ثبت Service Worker در React

در `assets/admin/index.tsx` (فایل entry point) بعد از `ReactDOM.render` / `createRoot`:

```typescript
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .catch(() => { /* silent fail in dev */ });
  });
}
```

**نکته:** registration باید فقط در production یا به‌صورت همیشگی باشد — در هر دو حالت کار می‌کند چون sw.js در `/public` است و مستقیم serve می‌شود.

---

### قابلیت ۴ — Install Prompt Banner (اختیاری اما مطلوب)

یک کامپوننت کوچک React در `assets/admin/components/ui/PwaInstallBanner.tsx` بساز:

- `beforeinstallprompt` event را listen می‌کند
- اگر prompt در دسترس بود یک banner کوچک در پایین صفحه نشان می‌دهد:
  ```
  [آیکون] ClinicPro را نصب کنید — دسترسی سریع‌تر   [نصب] [×]
  ```
- بعد از dismiss در `localStorage` ذخیره کن (`pwa-dismissed`) تا دوباره نشان داده نشود
- در `assets/admin/App.tsx` یا `AdminLayout` این کامپوننت را اضافه کن

**طراحی**: از CSS variables پروژه استفاده کن (`--surface`, `--border`, `--primary`, `--text`). RTL رعایت شود.

---

## تست پس از هر قابلیت

```bash
# Build
ddev exec yarn dev

# بررسی manifest
curl https://clinic-pro.ddev.site/manifest.json

# بررسی sw.js
curl https://clinic-pro.ddev.site/sw.js | head -5

# بررسی Twig
curl https://clinic-pro.ddev.site/admin | grep manifest
```

**تست PWA در Chrome:**
1. مرورگر Chrome → `https://clinic-pro.ddev.site/admin`
2. DevTools → Application → Service Workers → باید registered باشد
3. DevTools → Application → Manifest → باید parse شده و installable باشد
4. آیکون نصب در address bar باید ظاهر شود

---

### قابلیت ۵ — نمایش پرامپت نصب در صفحه لاگین

در صفحه لاگین (`assets/admin/pages/LoginPage.tsx`) یک بخش نصب اپلیکیشن اضافه کن که **همیشه قابل مشاهده** باشد — نه فقط وقتی `beforeinstallprompt` فایر شده.

**منطق نمایش:**

- اگر app قبلاً نصب شده (`window.matchMedia('(display-mode: standalone)').matches`) → این بخش را نشان **نده**
- اگر `pwa-dismissed` در localStorage بود → این بخش را نشان **نده**
- در غیر این صورت همیشه نشان بده

**دو حالت:**

۱. **وقتی `beforeinstallprompt` در دسترس است** (Chrome/Edge desktop و Android):
   - دکمه «نصب اپلیکیشن» نشان بده
   - با کلیک روی دکمه، native install prompt مرورگر را باز کن

۲. **وقتی `beforeinstallprompt` در دسترس نیست** (Safari iOS، Firefox، یا قبل از fire شدن event):
   - یک راهنمای متنی نشان بده:
     - iOS: «در Safari: دکمه Share را بزن، سپس «Add to Home Screen» را انتخاب کن»
     - سایر: «در منوی مرورگر گزینه «Install app» یا «Add to Home Screen» را انتخاب کن»

**طراحی:**

یک card زیبا زیر فرم لاگین (یا بالای آن در موبایل) با این ظاهر:

```
┌─────────────────────────────────────────┐
│  [آیکون ClinicPro]                       │
│  ClinicPro را نصب کنید                  │
│  دسترسی سریع‌تر — بدون نیاز به مرورگر  │
│                                          │
│  [دکمه نصب / راهنما]    [بعداً]         │
└─────────────────────────────────────────┘
```

- از CSS variables پروژه استفاده کن (`--primary-soft`, `--primary-700`, `--surface`, `--border`)
- RTL رعایت شود
- دکمه «بعداً» مثل قابلیت ۴ عمل کند: `localStorage.setItem('pwa-dismissed', '1')` و بخش را مخفی کند
- کامپوننت جداگانه بساز: `assets/admin/components/ui/PwaLoginCard.tsx`
- در `LoginPage.tsx` این کامپوننت را import و زیر card اصلی فرم لاگین قرار بده

**نکته مهم:** این کامپوننت مستقل از `PwaInstallBanner` است — banner برای کاربران logged-in است، این card برای صفحه لاگین است. منطق `beforeinstallprompt` را در یک custom hook مشترک (`assets/admin/hooks/usePwaInstall.ts`) بگذار تا هر دو کامپوننت از آن استفاده کنند و event را دو بار capture نکنند.

---

## محدودیت‌ها و نکات

- **workbox اضافه نکن** — native API کافی است و dependency غیرضروری اضافه نمی‌کند
- **Webpack Encore را تغییر نده** — sw.js مستقیم در `public/` می‌نشیند و نیازی به bundle ندارد
- **DDEV HTTPS**: mkcert باید قبلاً نصب شده باشد (`mkcert -install`)؛ اگر نبود service worker register نمی‌شود
- **Scope**: `/admin` — service worker فقط این path را intercept می‌کند، `/api/` را تحت تأثیر قرار نمی‌دهد مگر از داخل scope فراخوانی شود
- **آیکون‌ها**: اگر `favicon.png` کوچک بود (زیر 192px)، یک آیکون مناسب در `public/icons/` بساز یا از همان استفاده کن — Chrome حتی با آیکون کوچک هم install prompt نشان می‌دهد
