Spaces:
Sleeping
Sleeping
| import React, { ReactNode, Suspense, lazy } from 'react'; | |
| import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; | |
| import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | |
| import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; | |
| import { AuthProvider, useAuth } from './lib/authContext'; | |
| import { LanguageProvider } from './lib/languageContext'; | |
| import { StudentProvider } from './lib/StudentContext'; | |
| import { ErrorBoundary } from './components/ErrorBoundary'; | |
| // โโโ Always-eager imports (tiny, needed on first render) โโโโโโโโโโโโโโโโโโโโโโ | |
| import { Navbar } from './components/Navbar'; | |
| import { RoleSwitcher } from './components/RoleSwitcher'; | |
| import { Toaster } from 'sonner'; | |
| // โโโ React Query Client with optimal configuration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const queryClient = new QueryClient({ | |
| defaultOptions: { | |
| queries: { | |
| staleTime: 0, // ุงูุจูุงูุงุช ุชุนุชุจุฑ ูุฏูู ุฉ ููุฑุงู ุนุดุงู ุชุธูุฑ ุงูุชุญุฏูุซุงุช ุจุณุฑุนุฉ | |
| gcTime: 10 * 60 * 1000, // 10 ุฏูุงุฆู - ูุญุชูุธ ุจุงูู cache | |
| retry: 1, // ู ุญุงููุฉ ูุงุญุฏุฉ ููุท ูู ุญุงูุฉ ุงูุฎุทุฃ | |
| refetchOnWindowFocus: true, // ูุนูุฏ ุงูุฌูุจ ุนูุฏ ุงูุนูุฏุฉ ููุชุทุจูู | |
| refetchOnMount: true, // ูุนูุฏ ุงูุฌูุจ ุนูุฏ ุชุญู ูู ุงูู ููู | |
| refetchOnReconnect: true, // ูุนูุฏ ุงูุฌูุจ ุนูุฏ ุงุณุชุนุงุฏุฉ ุงูุงุชุตุงู | |
| retryDelay: 1000, // ุชุฃุฎูุฑ ุซุงููุฉ ูุจู ุฅุนุงุฏุฉ ุงูู ุญุงููุฉ | |
| }, | |
| mutations: { | |
| retry: 1, | |
| retryDelay: 1000, | |
| }, | |
| }, | |
| }); | |
| // โโโ Lazy: public pages โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const LandingPage = lazy(() => import('./components/LandingPage').then(m => ({ default: m.LandingPage }))); | |
| const SignIn = lazy(() => import('./components/auth/SignIn').then(m => ({ default: m.SignIn }))); | |
| const Register = lazy(() => import('./components/auth/Register').then(m => ({ default: m.Register }))); | |
| const CompleteProfile = lazy(() => import('./components/auth/CompleteProfile').then(m => ({ default: m.CompleteProfile }))); | |
| const CompleteStudentProfile = lazy(() => import('./components/auth/CompleteStudentProfile')); | |
| const CompleteSheikhProfile = lazy(() => import('./components/auth/CompleteSheikhProfile')); | |
| const GoogleAuth = lazy(() => import('./components/auth/GoogleAuth')); | |
| const VerifyOtp = lazy(() => import('./components/auth/VerifyOtp').then(m => ({ default: m.VerifyOtp }))); | |
| const ResetPassword = lazy(() => import('./components/auth/ResetPassword').then(m => ({ default: m.ResetPassword }))); | |
| const ForgotPasswordRequest = lazy(() => import('./components/auth/ForgotPasswordRequest').then(m => ({ default: m.ForgotPasswordRequest }))); | |
| const InterviewSchedule = lazy(() => import('./components/auth/Interveiw').then(m => ({ default: m.InterviewSchedule }))); | |
| // โโโ Lazy: student โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const StudentDashboard = lazy(() => import('./components/dashboards/StudentDashboard').then(m => ({ default: m.StudentDashboard }))); | |
| const RecordingPractice = lazy(() => import('./components/practice/RecordingPractice').then(m => ({ default: m.RecordingPractice }))); | |
| const EvaluationReports = lazy(() => import('./components/practice/EvaluationReports').then(m => ({ default: m.EvaluationReports }))); | |
| const FindSheikh = lazy(() => import('./components/sheikh/FindSheikh').then(m => ({ default: m.FindSheikh }))); | |
| const SheikhProfile = lazy(() => import('./components/sheikh/SheikhProfile').then(m => ({ default: m.SheikhProfile }))); | |
| // โโโ Lazy: sheikh โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const SheikhDashboard = lazy(() => import('./components/dashboards/SheikhDashboard').then(m => ({ default: m.SheikhDashboard }))); | |
| const MyStudents = lazy(() => import('./components/sheikh/MyStudents').then(m => ({ default: m.MyStudents }))); | |
| const SheikhInterviewSession = lazy(() => import('./components/sessions/SheikhInterviewSession').then(m => ({ default: m.SheikhInterviewSession }))); | |
| // โโโ Lazy: admin โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const AdminDashboard = lazy(() => import('./components/dashboards/AdminDashboard').then(m => ({ default: m.AdminDashboard }))); | |
| const SheikhApproval = lazy(() => import('./components/admin/SheikhApproval').then(m => ({ default: m.SheikhApproval }))); | |
| const UserManagement = lazy(() => import('./components/admin/UserManagement').then(m => ({ default: m.UserManagement }))); | |
| const UserProfileView = lazy(() => import('./components/admin/UserProfileView').then(m => ({ default: m.UserProfileView }))); | |
| const InterviewSession = lazy(() => import('./components/admin/InterviewSession').then(m => ({ default: m.InterviewSession }))); | |
| // โโโ Lazy: sessions & shared โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const MySessions = lazy(() => import('./components/sessions/MySessions').then(m => ({ default: m.MySessions }))); | |
| const LiveSession = lazy(() => import('./components/sessions/LiveSession').then(m => ({ default: m.LiveSession }))); | |
| const WaitingRoom = lazy(() => import('./components/sessions/WaitingRoom').then(m => ({ default: m.WaitingRoom }))); | |
| const SessionReport = lazy(() => import('./components/sessions/SessionReport').then(m => ({ default: m.SessionReport }))); | |
| const SessionEndWaiting = lazy(() => import('./components/sessions/SessionEndWaiting').then(m => ({ default: m.SessionEndWaiting }))); | |
| const ProfilePage = lazy(() => import('./components/profile/ProfilePage').then(m => ({ default: m.ProfilePage }))); | |
| /* ========================= */ | |
| /* โณ Global Fallback */ | |
| /* ========================= */ | |
| function PageLoader() { | |
| return ( | |
| <div className="min-h-screen flex items-center justify-center"> | |
| <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-emerald-600" /> | |
| </div> | |
| ); | |
| } | |
| /* ========================= */ | |
| /* ๐ Protected Route */ | |
| /* ========================= */ | |
| function ProtectedRoute({ children }: { children: ReactNode }) { | |
| const { user, loading } = useAuth(); | |
| if (loading) return <PageLoader />; | |
| if (!user) return <Navigate to="/signin" replace />; | |
| if (user.role === 'admin') return <>{children}</>; | |
| if (!user.profileCompleted) { | |
| const redirect = user.role === 'sheikh' ? '/complete-profile/sheikh' : '/complete-profile/student'; | |
| return <Navigate to={redirect} replace />; | |
| } | |
| return <>{children}</>; | |
| } | |
| /* ========================= */ | |
| /* ๐ง App Layout */ | |
| /* ========================= */ | |
| function AppLayout() { | |
| return ( | |
| <div className="min-h-screen bg-gray-50"> | |
| <Navbar /> | |
| <RoleSwitcher /> | |
| <main> | |
| <Routes> | |
| {/* Student */} | |
| <Route path="/student/dashboard" element={<ErrorBoundary><StudentDashboard /></ErrorBoundary>} /> | |
| <Route path="/student/practice" element={<RecordingPractice />} /> | |
| <Route path="/student/find-sheikh" element={<FindSheikh />} /> | |
| <Route path="/student/sheikh/:id" element={<SheikhProfile />} /> | |
| <Route path="/student/sessions" element={<MySessions />} /> | |
| <Route path="/student/evaluations/:id?" element={<EvaluationReports />} /> | |
| <Route path="/waiting-room/:id" element={<WaitingRoom />} /> | |
| <Route path="/session-waiting/:sessionId" element={<SessionEndWaiting />} /> | |
| <Route path="/session-report/:id" element={<SessionReport />} /> | |
| {/* Sheikh */} | |
| <Route path="/sheikh/dashboard" element={<ErrorBoundary><SheikhDashboard /></ErrorBoundary>} /> | |
| <Route path="/sheikh/sessions" element={<MySessions />} /> | |
| <Route path="/sheikh/students" element={<MyStudents />} /> | |
| {/* Admin */} | |
| <Route path="/admin/dashboard" element={<ErrorBoundary><AdminDashboard /></ErrorBoundary>} /> | |
| <Route path="/admin/sheikh-approval" element={<SheikhApproval />} /> | |
| <Route path="/admin/users" element={<UserManagement />} /> | |
| <Route path="/admin/user/:userId" element={<UserProfileView />} /> | |
| <Route path="/admin/student/:userId" element={<UserProfileView />} /> | |
| <Route path="/admin/sheikh/:userId" element={<UserProfileView />} /> | |
| {/* Common */} | |
| <Route path="/profile" element={<ProfilePage />} /> | |
| </Routes> | |
| </main> | |
| </div> | |
| ); | |
| } | |
| /* ========================= */ | |
| /* ๐ App Root */ | |
| /* ========================= */ | |
| export default function App() { | |
| return ( | |
| <BrowserRouter> | |
| <QueryClientProvider client={queryClient}> | |
| <LanguageProvider> | |
| <AuthProvider> | |
| <StudentProvider> | |
| <Toaster | |
| richColors | |
| position="top-center" | |
| duration={3000} | |
| /> | |
| <Suspense fallback={<PageLoader />}> | |
| <Routes> | |
| {/* Public */} | |
| <Route path="/" element={<LandingPage />} /> | |
| <Route path="/signin" element={<SignIn />} /> | |
| <Route path="/register" element={<Register />} /> | |
| <Route path="/forgot-password" element={<ForgotPasswordRequest />} /> | |
| <Route path="/verify-otp" element={<VerifyOtp />} /> | |
| <Route path="/reset-password" element={<ResetPassword />} /> | |
| <Route path="/auth/google" element={<GoogleAuth />} /> | |
| <Route path="/complete-profile" element={<CompleteProfile />} /> | |
| <Route path="/complete-profile/student" element={<CompleteStudentProfile />} /> | |
| <Route path="/complete-profile/sheikh" element={<CompleteSheikhProfile />} /> | |
| <Route path="/sheikh/interview" element={<InterviewSchedule />} /> | |
| <Route path="/sheikh/interview-session/:sheikhId" element={<SheikhInterviewSession />} /> | |
| {/* Live Session */} | |
| <Route path="/session/:sessionId" element={ | |
| <ProtectedRoute><LiveSession /></ProtectedRoute> | |
| } /> | |
| {/* Admin Interview */} | |
| <Route path="/admin/interview/:sheikhId" element={ | |
| <ProtectedRoute><InterviewSession /></ProtectedRoute> | |
| } /> | |
| {/* Protected App */} | |
| <Route path="/*" element={ | |
| <ProtectedRoute><AppLayout /></ProtectedRoute> | |
| } /> | |
| </Routes> | |
| </Suspense> | |
| {/* {process.env.NODE_ENV === 'development' && <ReactQueryDevtools initialIsOpen={false} />} */} | |
| </StudentProvider> | |
| </AuthProvider> | |
| </LanguageProvider> | |
| </QueryClientProvider> | |
| </BrowserRouter> | |
| ); | |
| } |