-
Notifications
You must be signed in to change notification settings - Fork 126
Refactor/router create browser router #1694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
13a32b6
1506b23
f7e30cf
9f3294d
4ddd2ba
e210660
bdc5513
a862c2b
c28c52f
ae441cc
e2a8b7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import React from "react"; | ||
| import AdminRoute from "@/components/AdminRoute"; | ||
| import MainLayout from "./MainLayout"; | ||
|
|
||
| export default function AdminLayout() { | ||
| return ( | ||
| <AdminRoute> | ||
| <MainLayout /> | ||
| </AdminRoute> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import React from "react"; | ||
| import { Outlet } from "react-router-dom"; | ||
| import Navbar from "@/components/Navbar/Navbar"; | ||
| import StreakBadge from "@/components/StreakBadge"; | ||
| import { useAuth } from "@/contexts/useAuth"; | ||
|
|
||
| export default function MainLayout() { | ||
| const { user } = useAuth(); | ||
| return ( | ||
| <> | ||
| <Navbar /> | ||
| {user && <StreakBadge />} | ||
| <Outlet /> | ||
| </> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import React from "react"; | ||
| import ProtectedRoute from "@/components/ProtectedRoute"; | ||
| import MainLayout from "./MainLayout"; | ||
|
|
||
| export default function ProtectedLayout() { | ||
| return ( | ||
| <ProtectedRoute> | ||
| <MainLayout /> | ||
| </ProtectedRoute> | ||
|
Comment on lines
+7
to
+9
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift Prevent layout remounts and duplicate API calls by centralizing Because To fix this, render
📍 Affects 4 files
🤖 Prompt for AI Agents |
||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import React from "react"; | ||
| import ProtectedMentorRoute from "@/components/ProtectedMentorRoute"; | ||
| import MainLayout from "./MainLayout"; | ||
|
|
||
| export default function ProtectedMentorLayout() { | ||
| return ( | ||
| <ProtectedMentorRoute> | ||
| <MainLayout /> | ||
| </ProtectedMentorRoute> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import React, { useState, useEffect, Suspense } from "react"; | ||
| import { Outlet } from "react-router-dom"; | ||
| import { useAuth } from "@/contexts/useAuth"; | ||
|
|
||
| import SplashScreen from "@/components/SplashScreen"; | ||
| import MouseSparkles from "@/components/MouseSparkles"; | ||
| import CookieConsentBanner from "@/components/CookieConsentBanner"; | ||
| import Chatbot from "@/components/Chatbot/Chatbot"; | ||
| import FloatingAI from "@/components/FloatingAI"; | ||
| import BackToTop from "@/components/BackToTop"; | ||
|
|
||
| export default function RootLayout() { | ||
| const { user } = useAuth(); | ||
| const [loading, setLoading] = useState(true); | ||
|
|
||
| useEffect(() => { | ||
| const timer = setTimeout(() => setLoading(false), 2500); | ||
| return () => clearTimeout(timer); | ||
| }, []); | ||
|
|
||
| if (loading) { | ||
| return <SplashScreen />; | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <MouseSparkles /> | ||
| <CookieConsentBanner /> | ||
|
|
||
| <Suspense | ||
| fallback={ | ||
| <div className="flex min-h-screen items-center justify-center bg-[#020617]"> | ||
| <div className="h-10 w-10 animate-spin rounded-full border-4 border-cyan-400 border-t-transparent" /> | ||
| </div> | ||
| } | ||
| > | ||
| <Outlet /> | ||
| </Suspense> | ||
|
|
||
| {user && ( | ||
| <> | ||
| <Chatbot /> | ||
| <FloatingAI /> | ||
| </> | ||
| )} | ||
|
|
||
| <BackToTop /> | ||
| </> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import React from "react"; | ||
| import { RouteObject } from "react-router-dom"; | ||
| import AdminLayout from "@/layouts/AdminLayout"; | ||
|
|
||
| const Admin = React.lazy(() => import("@/pages/Admin")); | ||
|
|
||
| export const adminRoutes: RouteObject[] = [ | ||
| { | ||
| element: <AdminLayout />, | ||
| children: [ | ||
| { path: "/admin", element: <Admin /> }, | ||
| ], | ||
| }, | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import React from "react"; | ||
| import { RouteObject } from "react-router-dom"; | ||
|
|
||
| const Login = React.lazy(() => import("@/pages/Login")); | ||
| const Signup = React.lazy(() => import("@/pages/Signup")); | ||
| const ForgotPassword = React.lazy(() => import("@/pages/ForgotPassword")); | ||
| const ResetPassword = React.lazy(() => import("@/pages/ResetPassword")); | ||
| const AuthCallback = React.lazy(() => import("@/pages/AuthCallback")); | ||
| const Onboarding = React.lazy(() => import("@/pages/Onboarding")); | ||
| const PublicPortfolio = React.lazy(() => import("@/pages/PublicPortfolio")); | ||
| const BecomeMentor = React.lazy(() => import("@/pages/BecomeMentor")); | ||
|
|
||
| export const authRoutes: RouteObject[] = [ | ||
| { path: "/login", element: <Login /> }, | ||
| { path: "/signup", element: <Signup /> }, | ||
| { path: "/forgot-password", element: <ForgotPassword /> }, | ||
| { path: "/reset-password", element: <ResetPassword /> }, | ||
| { path: "/auth/callback", element: <AuthCallback /> }, | ||
| { path: "/onboarding", element: <Onboarding /> }, | ||
| { path: "/portfolio/:slug", element: <PublicPortfolio /> }, | ||
| { path: "/become-mentor", element: <BecomeMentor /> }, | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import React from "react"; | ||
| import { createBrowserRouter } from "react-router-dom"; | ||
| import RootLayout from "@/layouts/RootLayout"; | ||
| import { publicRoutes } from "./public.routes"; | ||
| import { authRoutes } from "./auth.routes"; | ||
| import { protectedRoutes } from "./protected.routes"; | ||
| import { settingsRoutes } from "./settings.routes"; | ||
| import { mentorRoutes } from "./mentor.routes"; | ||
| import { adminRoutes } from "./admin.routes"; | ||
|
|
||
| export const router = createBrowserRouter([ | ||
| { | ||
| element: <RootLayout />, | ||
| children: [ | ||
| ...publicRoutes, | ||
| ...authRoutes, | ||
| ...protectedRoutes, | ||
| ...settingsRoutes, | ||
| ...mentorRoutes, | ||
| ...adminRoutes, | ||
| ], | ||
| }, | ||
| ]); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import React from "react"; | ||
| import { RouteObject } from "react-router-dom"; | ||
| import ProtectedMentorLayout from "@/layouts/ProtectedMentorLayout"; | ||
|
|
||
| const MentorDashboard = React.lazy(() => import("@/pages/MentorDashboard")); | ||
|
|
||
| export const mentorRoutes: RouteObject[] = [ | ||
| { | ||
| element: <ProtectedMentorLayout />, | ||
| children: [ | ||
| { path: "/mentor-dashboard", element: <MentorDashboard /> }, | ||
| ], | ||
| }, | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import React from "react"; | ||
| import { RouteObject } from "react-router-dom"; | ||
| import ProtectedLayout from "@/layouts/ProtectedLayout"; | ||
| import { useAuth } from "@/contexts/useAuth"; | ||
|
|
||
| const Dashboard = React.lazy(() => import("@/pages/Dashboard")); | ||
| const LearnerDashboard = React.lazy(() => import("@/pages/LearnerDashboard")); | ||
| const Discover = React.lazy(() => import("@/pages/Discover")); | ||
| const Sessions = React.lazy(() => import("@/pages/Sessions")); | ||
| const Messages = React.lazy(() => import("@/pages/Messages")); | ||
| const Chat = React.lazy(() => import("@/pages/Chat")); | ||
| const Notifications = React.lazy(() => import("@/pages/Notifications")); | ||
| const Leaderboard = React.lazy(() => import("@/pages/Leaderboard")); | ||
| const ResourceHub = React.lazy(() => import("@/pages/ResourceHub")); | ||
| const Portfolio = React.lazy(() => import("@/pages/Portfolio")); | ||
| const PeerReviewDashboard = React.lazy(() => import("@/pages/PeerReviewDashboard")); | ||
| const SubmitForReview = React.lazy(() => import("@/pages/SubmitForReview")); | ||
| const ReviewSubmission = React.lazy(() => import("@/pages/ReviewSubmission")); | ||
| const MockInterview = React.lazy(() => import("@/pages/MockInterview")); | ||
| const AnonymousDoubts = React.lazy(() => import("@/pages/AnonymousDoubts")); | ||
| const ContributorDashboard = React.lazy(() => import("@/pages/ContributorDashboard")); | ||
| const AIPage = React.lazy(() => import("@/pages/aipage")); | ||
| const StudyRooms = React.lazy(() => import("@/components/StudyRooms")); | ||
| const Room = React.lazy(() => import("@/components/Room/Room")); | ||
|
|
||
| const MessagesRoute = () => { | ||
| const { user } = useAuth(); | ||
| return <Messages user={user} />; | ||
| }; | ||
|
|
||
| export const protectedRoutes: RouteObject[] = [ | ||
| { | ||
| element: <ProtectedLayout />, | ||
| children: [ | ||
| { path: "/dashboard", element: <Dashboard /> }, | ||
| { path: "/learner-dashboard", element: <LearnerDashboard /> }, | ||
| { path: "/discover", element: <Discover /> }, | ||
| { path: "/sessions", element: <Sessions /> }, | ||
| { path: "/messages", element: <MessagesRoute /> }, | ||
| { path: "/chat", element: <Chat /> }, | ||
| { path: "/notifications", element: <Notifications /> }, | ||
| { path: "/leaderboard", element: <Leaderboard /> }, | ||
| { path: "/resources", element: <ResourceHub /> }, | ||
| { path: "/portfolio", element: <Portfolio /> }, | ||
| { path: "/peer-review", element: <PeerReviewDashboard /> }, | ||
| { path: "/peer-review/new", element: <SubmitForReview /> }, | ||
| { path: "/peer-review/:id", element: <ReviewSubmission /> }, | ||
| { path: "/mock-interview", element: <MockInterview /> }, | ||
| { path: "/anonymous-doubts", element: <AnonymousDoubts /> }, | ||
| { path: "/contributor-dashboard", element: <ContributorDashboard /> }, | ||
| { path: "/ai", element: <AIPage /> }, | ||
| { path: "/rooms", element: <StudyRooms /> }, | ||
| { path: "/rooms/:id", element: <Room /> }, | ||
| ], | ||
| }, | ||
| ]; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||||||||||||||||||||||||||||
| import React from "react"; | ||||||||||||||||||||||||||||||||||||
| import { Navigate, RouteObject } from "react-router-dom"; | ||||||||||||||||||||||||||||||||||||
| import MainLayout from "@/layouts/MainLayout"; | ||||||||||||||||||||||||||||||||||||
| import { useAuth } from "@/contexts/useAuth"; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const Index = React.lazy(() => import("@/pages/Index")); | ||||||||||||||||||||||||||||||||||||
| const Contact = React.lazy(() => import("@/pages/Contact")); | ||||||||||||||||||||||||||||||||||||
| const PrivacyPolicy = React.lazy(() => import("@/pages/privacy")); | ||||||||||||||||||||||||||||||||||||
| const CookiesPolicy = React.lazy(() => import("@/pages/cookies-policy")); | ||||||||||||||||||||||||||||||||||||
| const TermsAndConditions = React.lazy(() => import("@/pages/TermsAndConditions")); | ||||||||||||||||||||||||||||||||||||
| const NotFound = React.lazy(() => import("@/pages/NotFound")); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const IndexRoute = () => { | ||||||||||||||||||||||||||||||||||||
| const { user } = useAuth(); | ||||||||||||||||||||||||||||||||||||
| return user ? <Navigate to="/dashboard" replace /> : <Index />; | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+13
to
+16
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Handle the loading state to prevent unauthenticated UI flashing. Because Check the 🛠️ Proposed fix const IndexRoute = () => {
- const { user } = useAuth();
- return user ? <Navigate to="/dashboard" replace /> : <Index />;
+ const { user, loading } = useAuth();
+
+ if (loading) {
+ return (
+ <div className="flex min-h-screen items-center justify-center">
+ <div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
+ </div>
+ );
+ }
+
+ return user ? <Navigate to="/dashboard" replace /> : <Index />;
};📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: test[warning] 13-13: 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export const publicRoutes: RouteObject[] = [ | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| element: <MainLayout />, | ||||||||||||||||||||||||||||||||||||
| children: [ | ||||||||||||||||||||||||||||||||||||
| { path: "/", element: <IndexRoute /> }, | ||||||||||||||||||||||||||||||||||||
| { path: "/contact", element: <Contact /> }, | ||||||||||||||||||||||||||||||||||||
| { path: "/privacy-policy", element: <PrivacyPolicy /> }, | ||||||||||||||||||||||||||||||||||||
| { path: "/cookies-policy", element: <CookiesPolicy /> }, | ||||||||||||||||||||||||||||||||||||
| { path: "/terms-and-conditions", element: <TermsAndConditions /> }, | ||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| path: "*", | ||||||||||||||||||||||||||||||||||||
| element: <NotFound />, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Wrap
<Outlet />with<Suspense>to handle lazy-loaded routes without unmounting the navigation.The feature route modules use
React.lazy()for page components. Without a<Suspense>boundary insideMainLayout, navigating to a lazy-loaded route will suspend the layout hierarchy up to the nearest root boundary (likely inApp.tsx), causing theNavbarto completely unmount and flash during page transitions.Wrapping
<Outlet />ensures the navigation UI remains visible while the lazy page loads.🛠️ Proposed fix
Also applies to: 10-14
🤖 Prompt for AI Agents