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 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 ; } if (!isAuthenticated) return ; // اگر context هنوز لود نشده — صبر کن if (!primaryRole) { return
در حال بارگذاری...
; } // اگر چند context دارد و هنوز انتخاب نشده و روی صفحه انتخاب نیستیم if (availableContexts.length > 1 && !dbUuid && location.pathname !== '/admin/select-context') { return ; } return <>{children}; } function PublicRoute({ children }: { children: React.ReactNode }) { const isAuthenticated = useAuthStore((s) => s.isAuthenticated); return isAuthenticated ? : <>{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
در حال بارگذاری...
; if (!roles.includes(primaryRole)) return ; // منشیِ بدون مجوزِ این صفحه نباید با ورود مستقیم URL هم بازش کند. if (primaryRole === 'secretary' && permission && !can(permission[0], permission[1])) { return ; } // پزشکِ مهمان در محیط کلینیک فقط تا جایی که کلینیک مجوز داده دسترسی دارد. if (blockClinicScope && primaryRole === 'doctor' && context?.scope === 'clinic') { if (!permission || !can(permission[0], permission[1])) { return ; } } return <>{children}; } // ── App ────────────────────────────────────────────────────────────────────── export default function App() { return ( <> {/* Public */} } /> {/* انتخاب محیط کاری — نیاز به auth دارد اما خارج از AdminLayout */} } /> {/* Protected */} } > } /> {/* داشبورد — همه نقش‌ها */} } /> {/* نوبت‌ها — همه نقش‌ها به‌جز نماینده */} } /> } /> } /> } /> } /> {/* فقط ادمین */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> {/* پزشکان کلینیک — تب تنظیماتِ مالک کلینیک */} } /> } /> {/* مسیر قدیمی «مدیریت مطب» → ریدایرکت به تب جدید */} } /> {/* ادمین + کلینیک */} } /> {/* فقط ادمین — کلینیک از طریق دعوتنامه در صفحه کلینیک خود دکتر اضافه می‌کند */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> {/* دکتر / منشی / کلینیک */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> {/* فاز ۲ — دکتر / کلینیک */} } /> {/* پرسنل: تنها صفحهٔ دادهٔ این نقش، کنار داشبورد */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> {/* فقط ادمین */} } /> } /> } /> ); }