feat: add per-doctor permissions management in clinics

- Implement DoctorPermissionsModal for managing doctor permissions in clinics.
- Create usePermissions hook to handle user permissions context.
- Add migration for clinic_doctor_permissions table with default permissions.
- Develop ClinicDoctorPermissionController for handling permissions API.
- Create ClinicDoctorPermission entity to manage permissions data.
- Implement ClinicDoctorPermissionRepository for database interactions.
- Add ClinicDoctorPermissionChecker for permission validation logic.
- Write tests for clinic doctor permissions functionality.
This commit is contained in:
hamed
2026-07-18 09:44:13 +03:30
parent 3a23aa242e
commit e4ddd38f0c
17 changed files with 1504 additions and 44 deletions
+26 -20
View File
@@ -29,6 +29,7 @@ import {
import { useState } from "react";
import { NavLink, useLocation, useNavigate } from "react-router-dom";
import { useSubscription } from "../../hooks/useSubscription";
import { usePermissions } from "../../hooks/usePermissions";
import { useAuthStore } from "../../stores/authStore";
import { useUiStore } from "../../stores/uiStore";
@@ -53,28 +54,32 @@ function buildSections(
primaryRole: string | null,
dbUuid: string | null,
scope: string | null,
can: (resource: string, action: string) => boolean,
): Section[] {
// پزشکِ مهمان در محیط کلینیک (scope=clinic): فقط داشبورد و نوبت‌های خودش؛
// ابزارهای مدیریتی مطب/کلینیک نمایش داده نمی‌شوند.
// پزشکِ مهمان در محیط کلینیک (scope=clinic): منو از روی مجوزهایی که کلینیک
// برایش تعیین کرده ساخته می‌شود، نه به‌صورت hardcode.
if (primaryRole === "doctor" && scope === "clinic") {
return [
{
label: "عمومی",
items: [
{
to: "/admin/dashboard",
icon: ChartBarIcon,
label: "داشبورد",
},
{
to: "/admin/appointments",
icon: CalendarDaysIcon,
label: "نوبت‌های من",
children: APPOINTMENTS_CHILDREN,
},
],
},
const items: SectionItem[] = [
{ to: "/admin/dashboard", icon: ChartBarIcon, label: "داشبورد" },
];
if (can("appointments", "view")) {
items.push({
to: "/admin/appointments",
icon: CalendarDaysIcon,
label: "نوبت‌های من",
children: APPOINTMENTS_CHILDREN,
});
}
if (can("patients", "view")) {
items.push({
to: "/admin/patients",
icon: FolderOpenIcon,
label: "پرونده بیماران",
feature: "patient_records",
});
}
return [{ label: "عمومی", items }];
}
if (primaryRole === "admin") {
@@ -625,7 +630,8 @@ export default function Sidebar({ mobileOpen: _m, onMobileClose: _c }: Props) {
const { hasFeature } = useSubscription();
const navigate = useNavigate();
const sections = buildSections(primaryRole, dbUuid, context?.scope ?? null);
const { can } = usePermissions();
const sections = buildSections(primaryRole, dbUuid, context?.scope ?? null, can);
const initials = (userName ?? "U").charAt(0).toUpperCase();
return (