aboalaa147's picture
Initial deployment
eb6a2f9
Raw
History Blame Contribute Delete
11.7 kB
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>
);
}