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:
@@ -4,6 +4,8 @@ namespace App\Dashboard\Controller;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Clinic\Repository\ClinicRepository;
|
||||
use App\ClinicService\Entity\ServiceItem;
|
||||
use App\ClinicService\Repository\ServiceItemRepository;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Patient\Repository\PatientRecordRepository;
|
||||
use App\Patient\Repository\PatientSessionRepository;
|
||||
@@ -13,6 +15,8 @@ use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Context\EntityContextResolver;
|
||||
use App\Shared\Controller\BaseController;
|
||||
use App\Sms\Service\SmsWalletService;
|
||||
use App\Staff\Repository\ClinicStaffRepository;
|
||||
use App\Staff\Security\StaffPermissions;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -35,6 +39,8 @@ class DashboardController extends BaseController
|
||||
private readonly EntityContextResolver $contextResolver,
|
||||
private readonly \App\Clinic\Security\ClinicDoctorPermissionChecker $permChecker,
|
||||
private readonly \App\Doctor\Repository\DoctorAddressRepository $addressRepo,
|
||||
private readonly ClinicStaffRepository $staffRepo,
|
||||
private readonly ServiceItemRepository $serviceItemRepo,
|
||||
) {}
|
||||
|
||||
// ── Clinic Dashboard ────────────────────────────────────────────────────
|
||||
@@ -668,5 +674,75 @@ class DashboardController extends BaseController
|
||||
'today_appointments' => $todayAppts,
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Staff Dashboard ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* داشبورد پرسنل: سرویسهایی که به او تخصیص یافته و نوبتهای امروزِ خودش.
|
||||
*
|
||||
* نقش تنها کافی نیست — ردیف فعالِ پرسنل در محیط فعال هم باید وجود داشته باشد،
|
||||
* چون توکنِ صادرشده تا انقضا معتبر میماند و غیرفعالشدنِ پرسنل باید همان لحظه
|
||||
* دسترسی را ببندد.
|
||||
*/
|
||||
#[Route('/api/v1/dashboard/staff', methods: ['GET'])]
|
||||
#[IsGranted('ROLE_STAFF')]
|
||||
public function staff(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$context = $this->contextResolver->resolve($user);
|
||||
if (!$context->isResolved()) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'محیط کاری پرسنل تنظیم نشده', 403);
|
||||
}
|
||||
|
||||
[$entityType, $entityId] = $context->toEntityPair();
|
||||
$staff = $this->staffRepo->findActiveByUserAndEntity($user, $entityType, $entityId);
|
||||
if ($staff === null) {
|
||||
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی پرسنل تنظیم نشده', 403);
|
||||
}
|
||||
|
||||
$todayStart = strtotime('today midnight');
|
||||
$todayEnd = strtotime('tomorrow midnight') - 1;
|
||||
|
||||
$todayAppts = $this->em->createQuery('
|
||||
SELECT a.uuid, u.realName AS patient_name, u.mobileNumber AS patient_mobile,
|
||||
a.slotStart AS slot_start, a.status
|
||||
FROM App\Appointment\Entity\Appointment a
|
||||
JOIN a.user u
|
||||
WHERE a.staff = :staff AND a.slotStart >= :s AND a.slotStart <= :e
|
||||
ORDER BY a.slotStart ASC
|
||||
')->setParameters(['staff' => $staff, 's' => $todayStart, 'e' => $todayEnd])
|
||||
->getArrayResult();
|
||||
|
||||
$services = array_map(
|
||||
static fn(ServiceItem $item) => [
|
||||
'uuid' => $item->getUuid(),
|
||||
'name' => $item->getName(),
|
||||
'section_name' => $item->getSection()->getName(),
|
||||
'price_rials' => $item->getPriceRials(),
|
||||
'duration_minutes' => $item->getDurationMinutes(),
|
||||
],
|
||||
$this->serviceItemRepo->findByStaff($staff),
|
||||
);
|
||||
|
||||
return $this->success([
|
||||
'scope' => $entityType,
|
||||
'staff' => [
|
||||
'uuid' => $staff->getUuid(),
|
||||
'full_name' => $staff->getFullName(),
|
||||
'job_title' => $staff->getJobTitle(),
|
||||
],
|
||||
'owner' => [
|
||||
'name' => $context->isClinic()
|
||||
? ($context->clinic?->getName() ?? '')
|
||||
: ($context->doctor?->getName() ?? ''),
|
||||
],
|
||||
'permissions' => StaffPermissions::DEFAULT,
|
||||
'stats' => [
|
||||
'today_appointments' => count($todayAppts),
|
||||
'services' => count($services),
|
||||
],
|
||||
'services' => $services,
|
||||
'today_appointments' => $todayAppts,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user