feat: add staff role functionality with dashboard access and service management
- Implemented SidebarStaff component tests to ensure staff users see only their dashboard and services. - Created StaffMyServicesPage to display assigned services for staff users. - Added migration to link clinic staff rows to user accounts for ROLE_STAFF access. - Defined StaffPermissions class for static permissions related to staff role. - Introduced StaffRouteGuardSubscriber to restrict API access for staff users. - Developed StaffAccountService for managing staff user accounts and linking them to clinic staff. - Added comprehensive tests for StaffAccountService to validate user creation, mobile number handling, and account attachment. - Implemented tests for staff dashboard access to ensure proper permissions and access control. - Created tests for staff login context to verify correct environment visibility based on user roles.
This commit is contained in:
+14
-1
@@ -309,7 +309,7 @@ Authorization: Bearer <token>
|
||||
|
||||
| فیلد | نوع | توضیح |
|
||||
|------|-----|-------|
|
||||
| `primary_role` | string | نقش اصلی: `admin` \| `clinic` \| `doctor` \| `secretary` \| `representation` \| `user` |
|
||||
| `primary_role` | string | نقش اصلی: `admin` \| `clinic` \| `doctor` \| `secretary` \| `staff` \| `representation` \| `user` |
|
||||
| `db_uuid` | string\|null | UUID موجودیت فعال (null = هنوز context انتخاب نشده) |
|
||||
| `db_key` | string\|null | `HMAC-SHA256(db_uuid, APP_SECRET)` برای اعتبارسنجی |
|
||||
| `doctor_uuid` | string\|null | UUID دکتر — ثابت است حتی در context کلینیک که `db_uuid` برابر UUID کلینیک است. برای کاربران غیر دکتر: `null` |
|
||||
@@ -327,6 +327,7 @@ Authorization: Bearer <token>
|
||||
| پزشکِ عضو کلینیک (`role: doctor`، `scope: clinic`) | envelope کامل `{version, resources}` از `clinic_doctor_permissions` |
|
||||
| پزشکِ عضوی که دسترسیاش غیرفعال شده | `{version: 1, resources: {}}` — یعنی هیچ دسترسی |
|
||||
| منشی (`role: secretary`) | envelope کامل از `doctor_secretaries` |
|
||||
| پرسنل (`role: staff`) | envelope ثابت `{"version":1,"resources":{"services":{"view":true},"appointments":{"view":true}}}` — قابل ویرایش نیست |
|
||||
|
||||
نکتهٔ مهم برای کلاینت: **نبودِ `permissions` (یا `null`) یعنی «بدون محدودیت»، نه «بدون دسترسی».** ساختار و کلیدهای مجوز پزشکِ عضو کلینیک در `docs/api/clinic.md` → بخش *Clinic Doctor Permissions* آمده است.
|
||||
|
||||
@@ -335,14 +336,26 @@ Authorization: Bearer <token>
|
||||
- `ROLE_CLINIC` → `"clinic"`
|
||||
- `ROLE_DOCTOR` → `"doctor"`
|
||||
- `ROLE_SECRETARY` → `"secretary"`
|
||||
- `ROLE_STAFF` → `"staff"` (پرسنل کلینیک/مطب؛ عمداً بعد از منشی: کاربری که هر دو نقش را دارد منشی میماند)
|
||||
- `ROLE_REPRESENTATION` → `"representation"` (نماینده؛ دسترسی محدود به پنل ادمین: افزودن پزشک/کلینیک، نوبتهای پزشکانِ زیرمجموعه، داشبورد نماینده)
|
||||
- بقیه → `"user"`
|
||||
|
||||
**نقش `staff`** — کاربری که از `POST /api/v1/staff` با `has_account: true` ساخته شده
|
||||
(رجوع به [staff.md](staff.md)):
|
||||
|
||||
- هر ردیف **فعالِ** `clinic_staff` که به این کاربر وصل است، یک context با `role: "staff"` و
|
||||
`scope` برابر `doctor` یا `clinic` میسازد. پرسنلِ غیرفعال هیچ context نمیگیرد.
|
||||
- ورود با رمز مجاز است (`User::isStaff()` شامل `ROLE_STAFF` است).
|
||||
- دسترسی API این کاربر **پیشفرض بسته** است: فقط `GET /api/v1/dashboard/staff`،
|
||||
`POST /api/v1/auth/switch-context`، `POST /api/v1/user/change-password` و مسیرهای `/oauth/*`؛
|
||||
بقیهٔ `/api/v1/*` با `ERR_FORBIDDEN_001` و ۴۰۳ رد میشود (`StaffRouteGuardSubscriber`).
|
||||
|
||||
**قانون `context.role`** — نقشی که در آن محیط کاری فعال است:
|
||||
- context مطب شخصی دکتر: `"doctor"`
|
||||
- context کلینیک که دکتر **عضو** آن است (مالک نیست): `"doctor"` + `"scope": "clinic"` — پزشک میماند و فقط نوبتهای خودش در آن کلینیک را میبیند؛ دسترسی مدیریتی پنل کلینیک ندارد
|
||||
- context کلینیک که دکتر **صاحب** آن است: `"clinic"` (دسترسی کامل مالک)
|
||||
- context منشی: `"secretary"`
|
||||
- context پرسنل: `"staff"` + `scope` برابر نوع محیط (`doctor` یا `clinic`)
|
||||
|
||||
> **نکته frontend:** پس از `switchContext`، `primaryRole` در store از `context.role` و `scope` از `context.scope` آپدیت میشود. وقتی `role:"doctor"` و `scope:"clinic"` است (پزشکِ مهمان)، Sidebar فقط «داشبورد» و «نوبتها» را نشان میدهد و مسیرهای مدیریتی (`staff`, `clinic-services`, `subscription`, `my-secretaries`, `my-patients`, `profile`) به داشبورد ریدایرکت میشوند. در سمت backend هم endpointهای مدیریتی برای پزشک فقط scope **شخصیِ** خودش را برمیگردانند (نه کلینیک) و endpointهای ویرایش کلینیک مالکیت را چک میکنند (۴۰۳).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user