feat: restrict guest doctor access in clinic context by updating role-based routing and sidebar menu
This commit is contained in:
+13
-8
@@ -87,10 +87,15 @@ function PublicRoute({ children }: { children: React.ReactNode }) {
|
||||
return isAuthenticated ? <Navigate to="/admin/dashboard" replace /> : <>{children}</>;
|
||||
}
|
||||
|
||||
function RoleRoute({ roles, children }: { roles: string[]; children: React.ReactNode }) {
|
||||
function RoleRoute({ roles, blockClinicScope, children }: { roles: string[]; blockClinicScope?: boolean; children: React.ReactNode }) {
|
||||
const primaryRole = useAuthStore((s) => s.primaryRole);
|
||||
const context = useAuthStore((s) => s.context);
|
||||
if (!primaryRole) return <div style={{ padding: 40, textAlign: 'center' }}>در حال بارگذاری...</div>;
|
||||
if (!roles.includes(primaryRole)) return <Navigate to="/admin/dashboard" replace />;
|
||||
// پزشکِ مهمان در محیط کلینیک به ابزارهای مدیریتی دسترسی ندارد.
|
||||
if (blockClinicScope && primaryRole === 'doctor' && context?.scope === 'clinic') {
|
||||
return <Navigate to="/admin/dashboard" replace />;
|
||||
}
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@@ -159,18 +164,18 @@ export default function App() {
|
||||
<Route path="doctors" element={<RoleRoute roles={['admin', 'representation']}><DoctorsPage /></RoleRoute>} />
|
||||
<Route path="doctors/new" element={<RoleRoute roles={['admin', 'representation']}><DoctorFormPage /></RoleRoute>} />
|
||||
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor', 'clinic']}><DoctorDetailPage /></RoleRoute>} />
|
||||
<Route path="profile" element={<RoleRoute roles={['doctor']}><DoctorProfilePage /></RoleRoute>} />
|
||||
<Route path="profile" element={<RoleRoute roles={['doctor']} blockClinicScope><DoctorProfilePage /></RoleRoute>} />
|
||||
|
||||
{/* دکتر / منشی / کلینیک */}
|
||||
<Route path="my-patients" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']}><MyPatientsPage /></RoleRoute>} />
|
||||
<Route path="my-patients" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><MyPatientsPage /></RoleRoute>} />
|
||||
<Route path="my-financial" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']}><MyFinancialPage /></RoleRoute>} />
|
||||
|
||||
{/* فاز ۲ — دکتر / کلینیک */}
|
||||
<Route path="staff" element={<RoleRoute roles={['doctor', 'clinic']}><StaffPage /></RoleRoute>} />
|
||||
<Route path="subscription" element={<RoleRoute roles={['doctor', 'clinic']}><SubscriptionPage /></RoleRoute>} />
|
||||
<Route path="clinic-services" element={<RoleRoute roles={['doctor', 'clinic']}><ClinicServicesPage /></RoleRoute>} />
|
||||
<Route path="sms-wallet" element={<RoleRoute roles={['doctor', 'clinic']}><SmsWalletPage /></RoleRoute>} />
|
||||
<Route path="my-secretaries" element={<RoleRoute roles={['doctor', 'clinic']}><MySecretariesPage /></RoleRoute>} />
|
||||
<Route path="staff" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><StaffPage /></RoleRoute>} />
|
||||
<Route path="subscription" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><SubscriptionPage /></RoleRoute>} />
|
||||
<Route path="clinic-services" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><ClinicServicesPage /></RoleRoute>} />
|
||||
<Route path="sms-wallet" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><SmsWalletPage /></RoleRoute>} />
|
||||
<Route path="my-secretaries" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><MySecretariesPage /></RoleRoute>} />
|
||||
<Route path="admin-subscription" element={<RoleRoute roles={['admin']}><AdminSubscriptionPage /></RoleRoute>} />
|
||||
|
||||
{/* فقط ادمین */}
|
||||
|
||||
@@ -40,7 +40,22 @@ type Section = { label: string; items: SectionItem[] };
|
||||
function buildSections(
|
||||
primaryRole: string | null,
|
||||
dbUuid: string | null,
|
||||
scope: string | null,
|
||||
): Section[] {
|
||||
// پزشکِ مهمان در محیط کلینیک (scope=clinic): فقط داشبورد و نوبتهای خودش؛
|
||||
// ابزارهای مدیریتی مطب/کلینیک نمایش داده نمیشوند.
|
||||
if (primaryRole === "doctor" && scope === "clinic") {
|
||||
return [
|
||||
{
|
||||
label: "عمومی",
|
||||
items: [
|
||||
{ to: "/admin/dashboard", icon: ChartBarIcon, label: "داشبورد" },
|
||||
{ to: "/admin/appointments", icon: CalendarDaysIcon, label: "نوبتهای من" },
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (primaryRole === "admin") {
|
||||
return [
|
||||
{
|
||||
@@ -366,12 +381,12 @@ interface Props {
|
||||
|
||||
export default function Sidebar({ mobileOpen: _m, onMobileClose: _c }: Props) {
|
||||
const sidebarOpen = useUiStore((s) => s.sidebarOpen);
|
||||
const { logout, primaryRole, userName, availableContexts, dbUuid } =
|
||||
const { logout, primaryRole, userName, availableContexts, dbUuid, context } =
|
||||
useAuthStore();
|
||||
const { hasFeature } = useSubscription();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const sections = buildSections(primaryRole, dbUuid);
|
||||
const sections = buildSections(primaryRole, dbUuid, context?.scope ?? null);
|
||||
const initials = (userName ?? "U").charAt(0).toUpperCase();
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface ContextItem {
|
||||
db_uuid: string;
|
||||
name: string;
|
||||
role: 'admin' | 'clinic' | 'doctor' | 'secretary' | 'representation' | 'user';
|
||||
scope?: string | null;
|
||||
doctor_uuid?: string;
|
||||
permissions?: Record<string, any>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user