pwa setting
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
# 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 نشان میدهد
|
||||
Reference in New Issue
Block a user