159 lines
8.2 KiB
TypeScript
159 lines
8.2 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
|
|
import { useAuthStore } from './stores/authStore';
|
|
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 AppointmentDetailPage from './pages/AppointmentDetailPage';
|
|
import PaymentsPage from './pages/PaymentsPage';
|
|
import PaymentDetailPage from './pages/PaymentDetailPage';
|
|
import SettlementsPage from './pages/SettlementsPage';
|
|
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 CategoriesPage from './pages/CategoriesPage';
|
|
import BlogsPage from './pages/BlogsPage';
|
|
import BlogFormPage from './pages/BlogFormPage';
|
|
import SecretariesPage from './pages/SecretariesPage';
|
|
import MyClinicPage from './pages/MyClinicPage';
|
|
import SettingsPage from './pages/SettingsPage';
|
|
import DoctorProfilePage from './pages/DoctorProfilePage';
|
|
import MyPatientsPage from './pages/MyPatientsPage';
|
|
import MyFinancialPage from './pages/MyFinancialPage';
|
|
import ClinicFormPage from './pages/ClinicFormPage';
|
|
import PreRegistrationsPage from './pages/PreRegistrationsPage';
|
|
import PwaInstallBanner from './components/ui/PwaInstallBanner';
|
|
|
|
// ── Guards ──────────────────────────────────────────────────────────────────
|
|
|
|
function PrivateRoute({ children }: { children: React.ReactNode }) {
|
|
const { isAuthenticated, primaryRole, availableContexts, dbUuid, fetchMe } = useAuthStore();
|
|
const location = useLocation();
|
|
|
|
useEffect(() => {
|
|
if (isAuthenticated && !primaryRole) {
|
|
fetchMe();
|
|
}
|
|
}, [isAuthenticated, primaryRole, fetchMe]);
|
|
|
|
if (!isAuthenticated) return <Navigate to="/admin/login" replace />;
|
|
|
|
// اگر context هنوز لود نشده — صبر کن
|
|
if (isAuthenticated && !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, children }: { roles: string[]; children: React.ReactNode }) {
|
|
const primaryRole = useAuthStore((s) => s.primaryRole);
|
|
if (!primaryRole) return <div style={{ padding: 40, textAlign: 'center' }}>در حال بارگذاری...</div>;
|
|
if (!roles.includes(primaryRole)) 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={<AppointmentsPage />} />
|
|
<Route path="appointments/:uuid" element={<AppointmentDetailPage />} />
|
|
|
|
{/* فقط ادمین */}
|
|
<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="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="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="secretaries" element={<RoleRoute roles={['admin']}><SecretariesPage /></RoleRoute>} />
|
|
<Route path="clinics" element={<RoleRoute roles={['admin']}><ClinicsPage /></RoleRoute>} />
|
|
<Route path="settings" element={<RoleRoute roles={['admin']}><SettingsPage /></RoleRoute>} />
|
|
|
|
{/* کلینیک من — fallback اگر dbUuid هنوز لود نشده */}
|
|
<Route path="my-clinic" element={<RoleRoute roles={['clinic']}><MyClinicPage /></RoleRoute>} />
|
|
|
|
{/* ادمین + کلینیک */}
|
|
<Route path="clinics/:uuid" element={<RoleRoute roles={['admin', 'clinic']}><ClinicDetailPage /></RoleRoute>} />
|
|
|
|
{/* فقط ادمین — کلینیک از طریق دعوتنامه در صفحه کلینیک خود دکتر اضافه میکند */}
|
|
<Route path="doctors" element={<RoleRoute roles={['admin']}><DoctorsPage /></RoleRoute>} />
|
|
<Route path="doctors/new" element={<RoleRoute roles={['admin']}><DoctorFormPage /></RoleRoute>} />
|
|
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor']}><DoctorDetailPage /></RoleRoute>} />
|
|
<Route path="profile" element={<RoleRoute roles={['doctor']}><DoctorProfilePage /></RoleRoute>} />
|
|
|
|
{/* دکتر / منشی / کلینیک */}
|
|
<Route path="my-patients" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']}><MyPatientsPage /></RoleRoute>} />
|
|
<Route path="my-financial" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']}><MyFinancialPage /></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 />
|
|
</>
|
|
);
|
|
}
|