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:
@@ -48,7 +48,9 @@ class ErrorCodes
|
||||
public const ERR_SECRETARY_001 = 'ERR_SECRETARY_001';
|
||||
|
||||
// Staff
|
||||
public const ERR_STAFF_NOT_FOUND = 'ERR_STAFF_NOT_FOUND';
|
||||
public const ERR_STAFF_NOT_FOUND = 'ERR_STAFF_NOT_FOUND';
|
||||
public const ERR_STAFF_MOBILE_INVALID = 'ERR_STAFF_MOBILE_INVALID';
|
||||
public const ERR_STAFF_MOBILE_TAKEN = 'ERR_STAFF_MOBILE_TAKEN';
|
||||
|
||||
// Subscription
|
||||
public const ERR_SUBSCRIPTION_REQUIRED = 'ERR_SUBSCRIPTION_REQUIRED';
|
||||
@@ -141,6 +143,8 @@ class ErrorCodes
|
||||
self::ERR_RATE_LIMIT_001 => 'درخواستهای زیاد. لطفاً بعداً تلاش کنید',
|
||||
self::ERR_CAPTCHA_001 => 'تأیید امنیتی ناموفق بود. لطفاً صفحه را رفرش کنید و دوباره تلاش کنید',
|
||||
self::ERR_STAFF_NOT_FOUND => 'پرسنل یافت نشد',
|
||||
self::ERR_STAFF_MOBILE_INVALID => 'شماره موبایل پرسنل معتبر نیست',
|
||||
self::ERR_STAFF_MOBILE_TAKEN => 'برای این شماره قبلاً پرسنلی ثبت شده است',
|
||||
self::ERR_SUBSCRIPTION_REQUIRED => 'این قابلیت نیاز به پنل Basic یا بالاتر دارد',
|
||||
self::ERR_TRIAL_ALREADY_USED => 'قبلاً از تریال استفاده کردهاید',
|
||||
self::ERR_TRIAL_DISABLED => 'تریال در حال حاضر غیرفعال است',
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Secretary\Repository\DoctorSecretaryRepository;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Exception\AppException;
|
||||
use App\Staff\Repository\ClinicStaffRepository;
|
||||
|
||||
/**
|
||||
* تنها نقطهٔ تصمیمگیری دربارهٔ «این درخواست در کدام محیط اجرا میشود؟».
|
||||
@@ -34,6 +35,7 @@ class EntityContextResolver
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly UserActiveContextRepository $activeContextRepo,
|
||||
private readonly DoctorSecretaryRepository $secretaryRepo,
|
||||
private readonly ClinicStaffRepository $staffRepo,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -92,7 +94,13 @@ class EntityContextResolver
|
||||
return $clinic !== null ? EntityContext::forClinic($clinic) : EntityContext::unknown();
|
||||
}
|
||||
|
||||
/** مالک کلینیک، ادمین، پزشکِ عضو همان کلینیک، یا منشیِ دارای رابطهٔ فعال در آن. */
|
||||
/**
|
||||
* مالک کلینیک، ادمین، پزشکِ عضو همان کلینیک، منشیِ دارای رابطهٔ فعال در آن، یا
|
||||
* پرسنلِ فعالِ همان کلینیک.
|
||||
*
|
||||
* «میتواند در این محیط بایستد» یعنی محیطش حل میشود — نه اینکه هر کاری در آن
|
||||
* مجاز است؛ محدودهٔ پرسنل را StaffRouteGuardSubscriber تعیین میکند.
|
||||
*/
|
||||
public function canActInClinic(User $user, Clinic $clinic): bool
|
||||
{
|
||||
if ($user->hasRole('ROLE_ADMIN') || $clinic->getUser()->getId() === $user->getId()) {
|
||||
@@ -104,6 +112,10 @@ class EntityContextResolver
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->staffRepo->findActiveByUserAndEntity($user, EntityContext::TYPE_CLINIC, $clinic->getId()) !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->secretaryRepo->findActiveBySecretaryForClinic($user, $clinic) !== null;
|
||||
}
|
||||
|
||||
@@ -148,6 +160,10 @@ class EntityContextResolver
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->staffRepo->findActiveByUserAndEntity($user, EntityContext::TYPE_DOCTOR, $doctor->getId()) !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->secretaryRepo->findActiveBySecretaryForDoctor($user, $doctor) !== null;
|
||||
}
|
||||
|
||||
@@ -163,9 +179,9 @@ class EntityContextResolver
|
||||
return $clinic !== null ? EntityContext::forClinic($clinic) : EntityContext::unknown();
|
||||
}
|
||||
|
||||
// منشی fallback نقشی ندارد: محیطش فقط از UserActiveContext میآید، چون یک
|
||||
// منشی میتواند همزمان به چند پزشک و کلینیک وصل باشد و نقش تنها، انتخاب
|
||||
// بین آنها را تعیین نمیکند.
|
||||
// منشی و پرسنل fallback نقشی ندارند: محیطشان فقط از UserActiveContext میآید،
|
||||
// چون هر دو میتوانند همزمان به چند پزشک و کلینیک وصل باشند و نقش تنها،
|
||||
// انتخاب بین آنها را تعیین نمیکند.
|
||||
return EntityContext::unknown();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user