Implement SMS panel user flow and patient records system; add wallet charging, automatic reminders, and patient session management with detailed database schema and user flows.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# معماری — تسک ۱۲: تکمیل منشی
|
||||
|
||||
## فایلهای موجود که تغییر میکنند
|
||||
|
||||
```
|
||||
src/Secretary/Controller/SecretaryController.php ← inject SubscriptionService + limit check
|
||||
src/Shared/Constant/ErrorCodes.php ← ثابت جدید ERR_SECRETARY_LIMIT_REACHED
|
||||
assets/admin/pages/SecretariesPage.tsx ← Modal permissions checkbox matrix
|
||||
```
|
||||
|
||||
## تغییرات Backend
|
||||
|
||||
### SecretaryController — constructor injection
|
||||
```php
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly DoctorSecretaryRepository $secretaryRepo,
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly SubscriptionService $subscriptionService, // ← جدید
|
||||
) {}
|
||||
```
|
||||
|
||||
### SecretaryRepository — متد جدید countActive
|
||||
```php
|
||||
// src/Secretary/Repository/DoctorSecretaryRepository.php
|
||||
public function countActive(int $doctorId): int
|
||||
{
|
||||
return (int) $this->createQueryBuilder('s')
|
||||
->select('COUNT(s.id)')
|
||||
->where('s.doctor = :doctor')
|
||||
->andWhere('s.active = true')
|
||||
->setParameter('doctor', $doctorId)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
```
|
||||
|
||||
## تغییرات Frontend
|
||||
|
||||
### SecretariesPage.tsx — ساختار Modal permissions
|
||||
|
||||
```tsx
|
||||
// types جدید
|
||||
interface PermissionsMatrix {
|
||||
version: number;
|
||||
resources: {
|
||||
appointments?: { view: boolean; create: boolean; cancel: boolean; update_status: boolean };
|
||||
addresses?: { view: boolean; create: boolean; update: boolean; delete: boolean };
|
||||
clinic_info?: { view: boolean; update: boolean };
|
||||
insurances?: { view: boolean; create: boolean; update: boolean; delete: boolean };
|
||||
};
|
||||
}
|
||||
|
||||
// component
|
||||
function PermissionsEditor({ value, onChange }: {
|
||||
value: PermissionsMatrix;
|
||||
onChange: (v: PermissionsMatrix) => void;
|
||||
}) {
|
||||
// رندر جدول checkbox
|
||||
// هر ردیف: نام بخش + چکباکسهای action
|
||||
// onChange ساختار را update میکند
|
||||
}
|
||||
```
|
||||
|
||||
### نکته: مدیریت خطای 403 در فرم ایجاد منشی
|
||||
|
||||
```tsx
|
||||
// وقتی POST /api/v1/secretary با 403 برمیگردد:
|
||||
// نمایش toast: "سقف منشی پنل رسیده است. برای افزایش به صفحه اشتراک بروید"
|
||||
// دکمه "ارتقاء پنل" → navigate('/admin/subscription')
|
||||
```
|
||||
@@ -0,0 +1,59 @@
|
||||
# پایگاه داده — تسک ۱۲: تکمیل منشی
|
||||
|
||||
## هیچ migration لازم نیست
|
||||
|
||||
جدول `doctor_secretaries` کامل است. فقط باید مطمئن شویم ستون `permissions` درست ذخیره میشود.
|
||||
|
||||
## ستون موجود: permissions در doctor_secretaries
|
||||
|
||||
| ستون | نوع | مقدار پیشفرض |
|
||||
|------|-----|--------------|
|
||||
| permissions | JSON | `DEFAULT_PERMISSIONS` از DoctorSecretary entity |
|
||||
|
||||
### ساختار JSON ذخیرهشده:
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": {
|
||||
"view": true,
|
||||
"create": true,
|
||||
"cancel": false,
|
||||
"update_status": true
|
||||
},
|
||||
"addresses": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
},
|
||||
"clinic_info": {
|
||||
"view": true,
|
||||
"update": false
|
||||
},
|
||||
"insurances": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### نکته `version`:
|
||||
- `version: 1` — اگر در آینده ساختار permissions تغییر کرد، migration به version بعدی انجام میشود
|
||||
- همیشه هنگام خواندن permissions، `version` بررسی شود و اگر قدیمی بود با DEFAULT_PERMISSIONS merge شود
|
||||
|
||||
## جدول doctor_secretaries (مرجع)
|
||||
|
||||
| ستون | نوع | توضیح |
|
||||
|------|-----|-------|
|
||||
| id | INT PK | |
|
||||
| uuid | CHAR(36) UNIQUE | |
|
||||
| doctor_id | INT FK→doctors.id | |
|
||||
| secretary_id | INT FK→users.id | |
|
||||
| permissions | JSON | ساختار بالا |
|
||||
| active | TINYINT(1) DEFAULT 1 | |
|
||||
| created_at | INT | |
|
||||
| updated_at | INT | |
|
||||
@@ -0,0 +1,134 @@
|
||||
# تسک ۱۲: تکمیل منشی — محدودیت پنل + UI Permissions
|
||||
|
||||
## توضیح
|
||||
Entity `DoctorSecretary` و API کامل موجود است (`src/Secretary/`). این تسک فقط دو چیز اضافه میکند:
|
||||
1. Backend: بررسی سقف تعداد منشی بر اساس پنل اشتراکی
|
||||
2. Frontend: Modal با checkbox matrix برای ویرایش permissions
|
||||
|
||||
**هیچ endpoint جدیدی ایجاد نمیشود.**
|
||||
|
||||
## فایلهای موجود که تغییر میکنند
|
||||
|
||||
| فایل | تغییر |
|
||||
|------|-------|
|
||||
| `src/Secretary/Controller/SecretaryController.php` | inject `SubscriptionService`، اضافه کردن limit check در `create()` |
|
||||
| `assets/admin/pages/SecretariesPage.tsx` | Modal ویرایش با checkbox matrix برای permissions |
|
||||
|
||||
## API موجود (بدون تغییر)
|
||||
|
||||
| متد | مسیر | توضیح |
|
||||
|-----|------|-------|
|
||||
| GET | `/api/v1/secretaries/{doctorUuid}` | لیست منشیان |
|
||||
| POST | `/api/v1/secretary` | ایجاد منشی ← **اینجا limit check اضافه میشود** |
|
||||
| PATCH | `/api/v1/secretary/{uuid}` | ویرایش ← **permissions هم قابل ویرایش میشود** |
|
||||
| DELETE | `/api/v1/secretary/{uuid}` | غیرفعالسازی (soft delete) |
|
||||
|
||||
## پیشنیازها
|
||||
- تسک ۱۴ (Secretary — موجود در کد)
|
||||
- **تسک ۱۱** (Subscription — `SubscriptionService` باید موجود باشد)
|
||||
|
||||
## زمان تخمینی
|
||||
۴ تا ۵ ساعت
|
||||
|
||||
## تغییر Backend — SecretaryController::create()
|
||||
|
||||
### وضعیت فعلی (قبل):
|
||||
```php
|
||||
public function create(Request $request): JsonResponse
|
||||
{
|
||||
// validate fields
|
||||
// create DoctorSecretary
|
||||
// persist
|
||||
return $this->success($secretary->toArray());
|
||||
}
|
||||
```
|
||||
|
||||
### وضعیت جدید (بعد):
|
||||
```php
|
||||
public function create(Request $request): JsonResponse
|
||||
{
|
||||
// 1. پیدا کردن دکتر از doctorUuid در body
|
||||
$doctor = $this->doctorRepo->findByUuid($request->get('doctor_uuid'));
|
||||
|
||||
// 2. بررسی سقف پنل
|
||||
$entityType = 'doctor';
|
||||
$entityId = $doctor->getId();
|
||||
$limit = $this->subscriptionService->getSecretaryLimit($entityType, $entityId);
|
||||
$current = $this->secretaryRepo->countActive($doctor->getId());
|
||||
if ($current >= $limit) {
|
||||
return $this->error(
|
||||
ErrorCodes::ERR_SECRETARY_LIMIT_REACHED,
|
||||
'سقف تعداد منشی پنل اشتراکی شما رسیده است',
|
||||
Response::HTTP_FORBIDDEN
|
||||
);
|
||||
}
|
||||
|
||||
// 3. ادامه ایجاد منشی ...
|
||||
}
|
||||
```
|
||||
|
||||
### خطای جدید که باید در ErrorCodes.php اضافه شود:
|
||||
```php
|
||||
// src/Shared/Constant/ErrorCodes.php
|
||||
const ERR_SECRETARY_LIMIT_REACHED = 'ERR_SECRETARY_LIMIT_REACHED';
|
||||
// messages: 'سقف تعداد منشی پنل اشتراکی شما رسیده است'
|
||||
```
|
||||
|
||||
## تغییر Frontend — SecretariesPage.tsx
|
||||
|
||||
### ساختار DEFAULT_PERMISSIONS (از DoctorSecretary::DEFAULT_PERMISSIONS):
|
||||
```php
|
||||
[
|
||||
'version' => 1,
|
||||
'resources' => [
|
||||
'appointments' => ['view' => true, 'create' => true, 'cancel' => false, 'update_status' => true],
|
||||
'addresses' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
|
||||
'clinic_info' => ['view' => true, 'update' => false],
|
||||
'insurances' => ['view' => true, 'create' => false, 'update' => false, 'delete' => false],
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
### نمونه UI Permissions checkbox matrix در Modal:
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ دسترسیهای منشی │
|
||||
├──────────────┬───────┬────────┬────────┬────────────┤
|
||||
│ بخش │ مشاهده│ ایجاد │ ویرایش │ حذف/لغو │
|
||||
├──────────────┼───────┼────────┼────────┼────────────┤
|
||||
│ نوبتها │ ☑ │ ☑ │ ☑ │ ☐ │
|
||||
│ آدرسها │ ☑ │ ☐ │ ☐ │ ☐ │
|
||||
│ اطلاعات کلینیک│ ☑ │ - │ ☐ │ - │
|
||||
│ بیمهها │ ☑ │ ☐ │ ☐ │ ☐ │
|
||||
└──────────────┴───────┴────────┴────────┴────────────┘
|
||||
```
|
||||
|
||||
### PATCH /api/v1/secretary/{uuid} با permissions:
|
||||
```json
|
||||
{
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": { "view": true, "create": true, "cancel": true, "update_status": true },
|
||||
"addresses": { "view": true, "create": false, "update": false, "delete": false }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## کدهای خطای جدید
|
||||
|
||||
| کد | پیام فارسی |
|
||||
|----|-----------|
|
||||
| `ERR_SECRETARY_LIMIT_REACHED` | سقف تعداد منشی پنل اشتراکی شما رسیده است |
|
||||
|
||||
## Response خطا (403)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"errors": [{
|
||||
"code": "ERR_SECRETARY_LIMIT_REACHED",
|
||||
"message": "سقف تعداد منشی پنل اشتراکی شما رسیده است. برای افزودن منشی بیشتر پنل را ارتقاء دهید."
|
||||
}]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
# جریان کاربری — تسک ۱۲: تکمیل منشی
|
||||
|
||||
## جریان افزودن منشی جدید با بررسی سقف پنل
|
||||
|
||||
```
|
||||
دکتر روی «افزودن منشی» کلیک میکند → Modal باز میشود
|
||||
│
|
||||
▼
|
||||
POST /api/v1/secretary { phone, ... }
|
||||
│
|
||||
├─► 403 ERR_SECRETARY_LIMIT_REACHED:
|
||||
│ ┌─────────────────────────────────────────┐
|
||||
│ │ ⚠️ سقف منشی پنل شما تکمیل شده است │
|
||||
│ │ پنل Free: حداکثر ۱ منشی │
|
||||
│ │ │
|
||||
│ │ [ارتقاء به Basic] [بستن] │
|
||||
│ └─────────────────────────────────────────┘
|
||||
│
|
||||
└─► 201: منشی ایجاد شد → لیست refresh
|
||||
```
|
||||
|
||||
## جریان ویرایش permissions منشی
|
||||
|
||||
```
|
||||
دکتر روی آیکن تنظیمات کنار منشی کلیک میکند
|
||||
│
|
||||
▼
|
||||
GET /api/v1/secretary/{uuid}
|
||||
→ بارگذاری permissions فعلی در Modal
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ دسترسیهای علی رضایی │
|
||||
├──────────────────┬──────────┬────────┬────────┬─────────┤
|
||||
│ بخش │ مشاهده │ ایجاد │ ویرایش │ لغو/حذف│
|
||||
├──────────────────┼──────────┼────────┼────────┼─────────┤
|
||||
│ نوبتها │ ✅ │ ✅ │ ✅ │ ☐ │
|
||||
│ آدرسها │ ✅ │ ☐ │ ☐ │ ☐ │
|
||||
│ اطلاعات کلینیک │ ✅ │ - │ ☐ │ - │
|
||||
│ بیمهها │ ✅ │ ☐ │ ☐ │ ☐ │
|
||||
└──────────────────┴──────────┴────────┴────────┴─────────┘
|
||||
│ │
|
||||
│ [لغو] [ذخیره دسترسیها] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
PATCH /api/v1/secretary/{uuid}
|
||||
{ permissions: { version: 1, resources: { ... } } }
|
||||
│
|
||||
▼
|
||||
200: «دسترسیهای منشی بروزرسانی شد» → Modal بسته میشود
|
||||
```
|
||||
|
||||
## ترتیب سطوح سقف منشی
|
||||
|
||||
```
|
||||
اشتراک → سقف → اگر افزودن فراتر رود
|
||||
─────────────────────────────────────────
|
||||
Free → 1 → 403
|
||||
Basic → 2 → 403
|
||||
Professional → 5 → 403
|
||||
```
|
||||
|
||||
## نکته: منشی غیرفعال از سقف کم نمیشود
|
||||
|
||||
```
|
||||
countActive() فقط منشیان active=true را میشمارد
|
||||
یعنی: اگر 2 منشی داری و یکی را غیرفعال کنی → میتوانی دوباره 1 منشی جدید اضافه کنی
|
||||
(مادامی که در پنل Basic هستی و سقف 2 داری)
|
||||
```
|
||||
Reference in New Issue
Block a user