Three pages, all on the existing design system: BranchesPage lists the current environment's booking locations with their working-hours and active-room counts, and two subpages edit the week and the rooms. The list page deliberately does not create or rename a branch — clinic and doctor detail pages already do that, and duplicating it would give one physical place two edit surfaces. Route permission reuses `appointment_settings` rather than inventing a new one. Two real bugs fell out of exercising this end to end: `days` was serialising as a JSON *array*, not an object keyed "0".."6" — keys 0..6 are sequential so json_encode collapses them to a list. The client reads days["0"] either way, so nothing looked broken, but the response shape was unstable: one missing day would flip the same field to an object. The controller now casts to stdClass and WorkingHoursTest::testDaysIsAJsonObjectNotAnArray pins it. Found by curling the endpoint for the docs, not by any test. `<input type="time">` caps at 23:59, so it can neither display nor produce the legal end value 1440. An all-day range would have vanished from the form and been corrupted by the first save. Ranges now carry an explicit end-of-day flag, with a round-trip test proving 1440 survives. docs/api/branch.md documents all eight endpoints with responses captured from real curl runs against ddev, including the 422 and 404 bodies. doctor.md records that active/timezone now appear on all nine existing address endpoints (additive), and tenancy.md gains the two lessons this task taught: an aggregate child whose root is itself declared global inherits no environment and needs a real pair, and TenantFilter is not a substitute for an explicit ownership check because hard isolation only applies to a *chosen* context. Verified: phpunit 1067 tests / 2974 assertions green; slot-mode frozen contract green; phpstan 14 errors before and after, none in touched files; tsc clean; vitest 87 files / 612 tests green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
299 lines
22 KiB
TypeScript
299 lines
22 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
|
|
import { useAuthStore } from './stores/authStore';
|
|
import { usePermissions } from './hooks/usePermissions';
|
|
import AdminLayout from './components/layout/AdminLayout';
|
|
import LoginPage from './pages/LoginPage';
|
|
import DashboardPage from './pages/DashboardPage';
|
|
import SelectContextPage from './pages/SelectContextPage';
|
|
import UsersPage from './pages/UsersPage';
|
|
import UserDetailPage from './pages/UserDetailPage';
|
|
import DoctorsPage from './pages/DoctorsPage';
|
|
import DoctorDetailPage from './pages/DoctorDetailPage';
|
|
import DoctorFormPage from './pages/DoctorFormPage';
|
|
import ClinicsPage from './pages/ClinicsPage';
|
|
import ClinicDetailPage from './pages/ClinicDetailPage';
|
|
import AppointmentsPage from './pages/AppointmentsPage';
|
|
import AppointmentCreatePage from './pages/AppointmentCreatePage';
|
|
import AppointmentDetailPage from './pages/AppointmentDetailPage';
|
|
import AppointmentEditPage from './pages/AppointmentEditPage';
|
|
import ReserveAppointmentsPage from './pages/ReserveAppointmentsPage';
|
|
import PaymentsPage from './pages/PaymentsPage';
|
|
import PaymentDetailPage from './pages/PaymentDetailPage';
|
|
import SettlementsPage from './pages/SettlementsPage';
|
|
import SettlementDetailPage from './pages/SettlementDetailPage';
|
|
import RepresentationsPage from './pages/RepresentationsPage';
|
|
import RepresentationDetailPage from './pages/RepresentationDetailPage';
|
|
import CommentsPage from './pages/CommentsPage';
|
|
import RatingsPage from './pages/RatingsPage';
|
|
import SmsPage from './pages/SmsPage';
|
|
import LogsPage from './pages/LogsPage';
|
|
import CategoriesPage from './pages/CategoriesPage';
|
|
import BlogsPage from './pages/BlogsPage';
|
|
import BlogFormPage from './pages/BlogFormPage';
|
|
import BlogReviewPage from './pages/BlogReviewPage';
|
|
import SecretariesPage from './pages/SecretariesPage';
|
|
import SecretaryDetailPage from './pages/SecretaryDetailPage';
|
|
import SecretaryEarningsPage from './pages/SecretaryEarningsPage';
|
|
import SecretarySettlementPage from './pages/SecretarySettlementPage';
|
|
import ClinicDoctorsPage from './pages/ClinicDoctorsPage';
|
|
import SettingsPage from './pages/SettingsPage';
|
|
import FinancialReportPage from './pages/FinancialReportPage';
|
|
import RepresentationSettlementPage from './pages/RepresentationSettlementPage';
|
|
import RepresentationFinancePage from './pages/RepresentationFinancePage';
|
|
import RepresentationProfilePage from './pages/RepresentationProfilePage';
|
|
import RepresentationBlogsPage from './pages/RepresentationBlogsPage';
|
|
import RepresentationBlogFormPage from './pages/RepresentationBlogFormPage';
|
|
import DoctorProfilePage from './pages/DoctorProfilePage';
|
|
import MyPatientsPage from './pages/MyPatientsPage';
|
|
import MyPaymentsPage from './pages/MyPaymentsPage';
|
|
import MyPaymentDetailPage from './pages/MyPaymentDetailPage';
|
|
import NewSessionPage from './pages/NewSessionPage';
|
|
import EditSessionPage from './pages/EditSessionPage';
|
|
import SessionPaymentPage from './pages/SessionPaymentPage';
|
|
import InsurancePricingPage from './pages/InsurancePricingPage';
|
|
import ClaimsPage from './pages/ClaimsPage';
|
|
import ClaimPatientDetailPage from './pages/ClaimPatientDetailPage';
|
|
import DoctorClaimsPage from './pages/DoctorClaimsPage';
|
|
import MyFinancialPage from './pages/MyFinancialPage';
|
|
import ClinicFormPage from './pages/ClinicFormPage';
|
|
import PreRegistrationsPage from './pages/PreRegistrationsPage';
|
|
import StaffPage from './pages/StaffPage';
|
|
import StaffMyServicesPage from './pages/StaffMyServicesPage';
|
|
import SubscriptionPage from './pages/SubscriptionPage';
|
|
import DiscountsPage from './pages/DiscountsPage';
|
|
import ClinicServicesPage from './pages/ClinicServicesPage';
|
|
import ServiceDetailPage from './pages/ServiceDetailPage';
|
|
import SmsWalletPage from './pages/SmsWalletPage';
|
|
import MySecretariesPage from './pages/MySecretariesPage';
|
|
import AdminSubscriptionPage from './pages/AdminSubscriptionPage';
|
|
import SettingsMenuPage from './pages/SettingsMenuPage';
|
|
import AccountSettingsPage from './pages/AccountSettingsPage';
|
|
import TagsSettingsPage from './pages/TagsSettingsPage';
|
|
import AppointmentSettingsPage from './pages/AppointmentSettingsPage';
|
|
import ClinicAppointmentSettingsPage from './pages/ClinicAppointmentSettingsPage';
|
|
import PatientsListPage from './pages/PatientsListPage';
|
|
import InventoryPage from './pages/InventoryPage';
|
|
import BranchesPage from './pages/BranchesPage';
|
|
import BranchWorkingHoursPage from './pages/BranchWorkingHoursPage';
|
|
import BranchRoomsPage from './pages/BranchRoomsPage';
|
|
import PatientRecordFormPage from './pages/PatientRecordFormPage';
|
|
import PatientDetailPage from './pages/PatientDetailPage';
|
|
import PaymentSuccessPage from './pages/PaymentSuccessPage';
|
|
import PwaInstallBanner from './components/ui/PwaInstallBanner';
|
|
|
|
// ── Guards ──────────────────────────────────────────────────────────────────
|
|
|
|
const ALLOWED_ROLES = ['admin', 'doctor', 'clinic', 'secretary', 'staff', 'representation'] as const;
|
|
|
|
function PrivateRoute({ children }: { children: React.ReactNode }) {
|
|
const { isAuthenticated, primaryRole, availableContexts, dbUuid, fetchMe, logout } = useAuthStore();
|
|
const location = useLocation();
|
|
const [accessDenied, setAccessDenied] = React.useState(false);
|
|
|
|
useEffect(() => {
|
|
if (isAuthenticated && !primaryRole) {
|
|
fetchMe();
|
|
}
|
|
}, [isAuthenticated, primaryRole, fetchMe]);
|
|
|
|
useEffect(() => {
|
|
if (isAuthenticated && primaryRole && !ALLOWED_ROLES.includes(primaryRole as any)) {
|
|
setAccessDenied(true);
|
|
logout();
|
|
}
|
|
}, [isAuthenticated, primaryRole, logout]);
|
|
|
|
if (accessDenied) {
|
|
return <Navigate to="/admin/login?error=access_denied" replace />;
|
|
}
|
|
|
|
if (!isAuthenticated) return <Navigate to="/admin/login" replace />;
|
|
|
|
// اگر context هنوز لود نشده — صبر کن
|
|
if (!primaryRole) {
|
|
return <div style={{ padding: 40, textAlign: 'center' }}>در حال بارگذاری...</div>;
|
|
}
|
|
|
|
// اگر چند context دارد و هنوز انتخاب نشده و روی صفحه انتخاب نیستیم
|
|
if (availableContexts.length > 1 && !dbUuid && location.pathname !== '/admin/select-context') {
|
|
return <Navigate to="/admin/select-context" replace />;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function PublicRoute({ children }: { children: React.ReactNode }) {
|
|
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
|
return isAuthenticated ? <Navigate to="/admin/dashboard" replace /> : <>{children}</>;
|
|
}
|
|
|
|
function RoleRoute({ roles, blockClinicScope, permission, children }: {
|
|
roles: string[];
|
|
blockClinicScope?: boolean;
|
|
/**
|
|
* [resource, action] — گیت مجوز صفحه.
|
|
* • پزشکِ مهمان در محیط کلینیک با داشتن این مجوز از blockClinicScope مستثنا میشود.
|
|
* • منشی فقط با داشتن این مجوز به صفحه دسترسی دارد (همراستا با enforcement سمت API).
|
|
*/
|
|
permission?: [string, string];
|
|
children: React.ReactNode;
|
|
}) {
|
|
const primaryRole = useAuthStore((s) => s.primaryRole);
|
|
const context = useAuthStore((s) => s.context);
|
|
const { can } = usePermissions();
|
|
if (!primaryRole) return <div style={{ padding: 40, textAlign: 'center' }}>در حال بارگذاری...</div>;
|
|
if (!roles.includes(primaryRole)) return <Navigate to="/admin/dashboard" replace />;
|
|
// منشیِ بدون مجوزِ این صفحه نباید با ورود مستقیم URL هم بازش کند.
|
|
if (primaryRole === 'secretary' && permission && !can(permission[0], permission[1])) {
|
|
return <Navigate to="/admin/dashboard" replace />;
|
|
}
|
|
// پزشکِ مهمان در محیط کلینیک فقط تا جایی که کلینیک مجوز داده دسترسی دارد.
|
|
if (blockClinicScope && primaryRole === 'doctor' && context?.scope === 'clinic') {
|
|
if (!permission || !can(permission[0], permission[1])) {
|
|
return <Navigate to="/admin/dashboard" replace />;
|
|
}
|
|
}
|
|
return <>{children}</>;
|
|
}
|
|
|
|
// ── App ──────────────────────────────────────────────────────────────────────
|
|
|
|
export default function App() {
|
|
return (
|
|
<>
|
|
<Routes>
|
|
{/* Public */}
|
|
<Route
|
|
path="/admin/login"
|
|
element={<PublicRoute><LoginPage /></PublicRoute>}
|
|
/>
|
|
|
|
{/* انتخاب محیط کاری — نیاز به auth دارد اما خارج از AdminLayout */}
|
|
<Route
|
|
path="/admin/select-context"
|
|
element={
|
|
<PrivateRoute>
|
|
<SelectContextPage />
|
|
</PrivateRoute>
|
|
}
|
|
/>
|
|
|
|
{/* Protected */}
|
|
<Route
|
|
path="/admin/*"
|
|
element={<PrivateRoute><AdminLayout /></PrivateRoute>}
|
|
>
|
|
<Route index element={<Navigate to="/admin/dashboard" replace />} />
|
|
|
|
{/* داشبورد — همه نقشها */}
|
|
<Route path="dashboard" element={<DashboardPage />} />
|
|
|
|
{/* نوبتها — همه نقشها بهجز نماینده */}
|
|
<Route path="appointments" element={<RoleRoute roles={['admin', 'clinic', 'doctor', 'secretary']}><AppointmentsPage /></RoleRoute>} />
|
|
<Route path="appointments/reserve" element={<RoleRoute roles={['admin', 'clinic', 'doctor', 'secretary']}><ReserveAppointmentsPage /></RoleRoute>} />
|
|
<Route path="appointments/new" element={<RoleRoute roles={['admin', 'clinic', 'doctor', 'secretary']}><AppointmentCreatePage /></RoleRoute>} />
|
|
<Route path="appointments/:uuid" element={<RoleRoute roles={['admin', 'clinic', 'doctor', 'secretary']}><AppointmentDetailPage /></RoleRoute>} />
|
|
<Route path="appointments/:uuid/edit" element={<RoleRoute roles={['admin', 'clinic', 'doctor', 'secretary']}><AppointmentEditPage /></RoleRoute>} />
|
|
|
|
{/* فقط ادمین */}
|
|
<Route path="users" element={<RoleRoute roles={['admin']}><UsersPage /></RoleRoute>} />
|
|
<Route path="users/:uuid" element={<RoleRoute roles={['admin']}><UserDetailPage /></RoleRoute>} />
|
|
<Route path="payments" element={<RoleRoute roles={['admin']}><PaymentsPage /></RoleRoute>} />
|
|
<Route path="payments/:uuid" element={<RoleRoute roles={['admin']}><PaymentDetailPage /></RoleRoute>} />
|
|
<Route path="settlements" element={<RoleRoute roles={['admin']}><SettlementsPage /></RoleRoute>} />
|
|
<Route path="settlements/:uuid" element={<RoleRoute roles={['admin']}><SettlementDetailPage /></RoleRoute>} />
|
|
<Route path="financial-report" element={<RoleRoute roles={['admin']}><FinancialReportPage /></RoleRoute>} />
|
|
<Route path="representations" element={<RoleRoute roles={['admin']}><RepresentationsPage /></RoleRoute>} />
|
|
<Route path="representations/:uuid" element={<RoleRoute roles={['admin']}><RepresentationDetailPage /></RoleRoute>} />
|
|
<Route path="comments" element={<RoleRoute roles={['admin']}><CommentsPage /></RoleRoute>} />
|
|
<Route path="ratings" element={<RoleRoute roles={['admin']}><RatingsPage /></RoleRoute>} />
|
|
<Route path="sms" element={<RoleRoute roles={['admin']}><SmsPage /></RoleRoute>} />
|
|
<Route path="logs" element={<RoleRoute roles={['admin']}><LogsPage /></RoleRoute>} />
|
|
<Route path="categories" element={<RoleRoute roles={['admin']}><CategoriesPage /></RoleRoute>} />
|
|
<Route path="blogs" element={<RoleRoute roles={['admin']}><BlogsPage /></RoleRoute>} />
|
|
<Route path="blogs/new" element={<RoleRoute roles={['admin']}><BlogFormPage /></RoleRoute>} />
|
|
<Route path="blogs/:uuid/edit" element={<RoleRoute roles={['admin']}><BlogFormPage /></RoleRoute>} />
|
|
<Route path="blog-review" element={<RoleRoute roles={['admin']}><BlogReviewPage /></RoleRoute>} />
|
|
<Route path="secretaries" element={<RoleRoute roles={['admin']}><SecretariesPage /></RoleRoute>} />
|
|
<Route path="secretaries/:uuid" element={<RoleRoute roles={['admin']}><SecretaryDetailPage /></RoleRoute>} />
|
|
<Route path="secretary-earnings" element={<RoleRoute roles={['secretary']}><SecretaryEarningsPage /></RoleRoute>} />
|
|
<Route path="secretary-settlement" element={<RoleRoute roles={['secretary']}><SecretarySettlementPage /></RoleRoute>} />
|
|
<Route path="clinics" element={<RoleRoute roles={['admin', 'representation']}><ClinicsPage /></RoleRoute>} />
|
|
<Route path="settings" element={<RoleRoute roles={['admin']}><SettingsPage /></RoleRoute>} />
|
|
|
|
{/* پزشکان کلینیک — تب تنظیماتِ مالک کلینیک */}
|
|
<Route path="settings/clinic-doctors" element={<RoleRoute roles={['clinic', 'secretary']} blockClinicScope permission={['clinic_doctors', 'view']}><ClinicDoctorsPage /></RoleRoute>} />
|
|
<Route path="settings/appointment-settings" element={<RoleRoute roles={['clinic', 'secretary']} permission={['appointment_settings', 'view']}><ClinicAppointmentSettingsPage /></RoleRoute>} />
|
|
{/* مسیر قدیمی «مدیریت مطب» → ریدایرکت به تب جدید */}
|
|
<Route path="my-clinic" element={<Navigate to="/admin/settings/clinic-doctors" replace />} />
|
|
|
|
{/* ادمین + کلینیک */}
|
|
<Route path="clinics/:uuid" element={<RoleRoute roles={['admin', 'clinic', 'representation']}><ClinicDetailPage /></RoleRoute>} />
|
|
|
|
{/* فقط ادمین — کلینیک از طریق دعوتنامه در صفحه کلینیک خود دکتر اضافه میکند */}
|
|
<Route path="representation-settlement" element={<RoleRoute roles={['representation']}><RepresentationSettlementPage /></RoleRoute>} />
|
|
<Route path="representation-finance" element={<RoleRoute roles={['representation']}><RepresentationFinancePage /></RoleRoute>} />
|
|
<Route path="representation-profile" element={<RoleRoute roles={['representation']}><RepresentationProfilePage /></RoleRoute>} />
|
|
<Route path="representation-blogs" element={<RoleRoute roles={['representation', 'admin']}><RepresentationBlogsPage /></RoleRoute>} />
|
|
<Route path="representation-blogs/new" element={<RoleRoute roles={['representation', 'admin']}><RepresentationBlogFormPage /></RoleRoute>} />
|
|
<Route path="representation-blogs/:uuid/edit" element={<RoleRoute roles={['representation', 'admin']}><RepresentationBlogFormPage /></RoleRoute>} />
|
|
<Route path="doctors" element={<RoleRoute roles={['admin', 'representation']}><DoctorsPage /></RoleRoute>} />
|
|
<Route path="doctor-claims" element={<RoleRoute roles={['admin']}><DoctorClaimsPage /></RoleRoute>} />
|
|
<Route path="doctors/new" element={<RoleRoute roles={['admin', 'representation']}><DoctorFormPage /></RoleRoute>} />
|
|
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor', 'clinic', 'representation']}><DoctorDetailPage /></RoleRoute>} />
|
|
<Route path="profile" element={<RoleRoute roles={['doctor']} blockClinicScope><DoctorProfilePage /></RoleRoute>} />
|
|
|
|
{/* دکتر / منشی / کلینیک */}
|
|
<Route path="my-patients" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><MyPatientsPage /></RoleRoute>} />
|
|
<Route path="my-payments" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope permission={['payments', 'view']}><MyPaymentsPage /></RoleRoute>} />
|
|
<Route path="my-payments/:patientUuid" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><MyPaymentDetailPage /></RoleRoute>} />
|
|
|
|
<Route path="patients" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope permission={['patients', 'view']}><PatientsListPage /></RoleRoute>} />
|
|
<Route path="patients/new" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope permission={['patients', 'create']}><PatientRecordFormPage /></RoleRoute>} />
|
|
<Route path="patients/:uuid/edit" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope permission={['patients', 'update']}><PatientRecordFormPage /></RoleRoute>} />
|
|
<Route path="patients/:uuid" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope permission={['patients', 'view']}><PatientDetailPage /></RoleRoute>} />
|
|
<Route path="patients/:recordUuid/session/new" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope permission={['patients', 'create']}><NewSessionPage /></RoleRoute>} />
|
|
<Route path="my-patients/:recordUuid/session/new" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><NewSessionPage /></RoleRoute>} />
|
|
<Route path="patients/:recordUuid/session/:sessionUuid/pay" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><SessionPaymentPage /></RoleRoute>} />
|
|
<Route path="my-patients/:recordUuid/session/:sessionUuid/pay" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><SessionPaymentPage /></RoleRoute>} />
|
|
<Route path="patients/:recordUuid/session/:sessionUuid/edit" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><EditSessionPage /></RoleRoute>} />
|
|
<Route path="my-patients/:recordUuid/session/:sessionUuid/edit" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><EditSessionPage /></RoleRoute>} />
|
|
<Route path="insurance-pricing" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['insurances', 'view']}><InsurancePricingPage /></RoleRoute>} />
|
|
<Route path="claims" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['insurances', 'view']}><ClaimsPage /></RoleRoute>} />
|
|
<Route path="claims/:patientUuid" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><ClaimPatientDetailPage /></RoleRoute>} />
|
|
<Route path="my-financial" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} permission={['payments', 'view']}><MyFinancialPage /></RoleRoute>} />
|
|
|
|
{/* فاز ۲ — دکتر / کلینیک */}
|
|
<Route path="staff" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['staff', 'view']}><StaffPage /></RoleRoute>} />
|
|
{/* پرسنل: تنها صفحهٔ دادهٔ این نقش، کنار داشبورد */}
|
|
<Route path="my-services" element={<RoleRoute roles={['staff']}><StaffMyServicesPage /></RoleRoute>} />
|
|
<Route path="settings-menu" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><SettingsMenuPage /></RoleRoute>} />
|
|
<Route path="account-settings" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']}><AccountSettingsPage /></RoleRoute>} />
|
|
<Route path="tags-settings" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['tags', 'view']}><TagsSettingsPage /></RoleRoute>} />
|
|
<Route path="appointment-settings" element={<RoleRoute roles={['doctor', 'secretary']} blockClinicScope permission={['appointment_settings', 'view']}><AppointmentSettingsPage /></RoleRoute>} />
|
|
<Route path="subscription" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['subscription', 'view']}><SubscriptionPage /></RoleRoute>} />
|
|
<Route path="discounts" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['discounts', 'view']}><DiscountsPage /></RoleRoute>} />
|
|
<Route path="subscription/success" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['subscription', 'view']}><PaymentSuccessPage /></RoleRoute>} />
|
|
<Route path="clinic-services" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['services', 'view']}><ClinicServicesPage /></RoleRoute>} />
|
|
<Route path="clinic-services/:uuid" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['services', 'view']}><ServiceDetailPage /></RoleRoute>} />
|
|
<Route path="inventory" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['inventory', 'view']}><InventoryPage /></RoleRoute>} />
|
|
<Route path="branches" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['appointment_settings', 'view']}><BranchesPage /></RoleRoute>} />
|
|
<Route path="branches/:branchUuid/working-hours" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['appointment_settings', 'view']}><BranchWorkingHoursPage /></RoleRoute>} />
|
|
<Route path="branches/:branchUuid/rooms" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['appointment_settings', 'view']}><BranchRoomsPage /></RoleRoute>} />
|
|
<Route path="sms-wallet" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['sms', 'view']}><SmsWalletPage /></RoleRoute>} />
|
|
<Route path="my-secretaries" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><MySecretariesPage /></RoleRoute>} />
|
|
<Route path="admin-subscription" element={<RoleRoute roles={['admin']}><AdminSubscriptionPage /></RoleRoute>} />
|
|
|
|
{/* فقط ادمین */}
|
|
<Route path="clinics/new" element={<RoleRoute roles={['admin']}><ClinicFormPage /></RoleRoute>} />
|
|
<Route path="pre-registrations" element={<RoleRoute roles={['admin']}><PreRegistrationsPage /></RoleRoute>} />
|
|
</Route>
|
|
|
|
<Route path="*" element={<Navigate to="/admin/dashboard" replace />} />
|
|
</Routes>
|
|
<PwaInstallBanner />
|
|
</>
|
|
);
|
|
}
|