application structure and authentication
App owns auth. Real and demo login both land in AuthenticatedApp, which wires routes and feature modules.
Related: State Management Strategy, Data Layer & API Integration.
Application entry point#
src/App.jsx exports App. src/main.jsx mounts it. Providers and the router wrap the tree inside App.
Core component structure#
flowchart TD main["main.jsx createRoot"] --> App["App"] App --> Script["ThemeScript"] App --> Provider["ThemeProvider"] Provider --> Fonts["DynamicFontLoader"] Provider --> QC["QueryClientProvider"] QC --> Toast["Toaster"] QC --> Router["HashRouter"] Router --> Public["Route /stats Cloudflare"] Router --> AuthGate["isAuthenticated ? AuthenticatedApp : LoginWrapper"]
Global providers#
| Provider | Purpose |
|---|---|
ThemeScript |
Writes theme class before paint |
ThemeProvider |
Light/dark and CSS variables |
DynamicFontLoader |
Google Fonts for the active preset |
QueryClientProvider |
Shared QueryClient for stats |
Toaster |
sonner, popover colors |
HashRouter |
Client routes for GitHub Pages |
Authentication system#
Real login hits JIIT through jsjiit. Demo login never calls the network.
Portal instance architecture#
flowchart TD CDN["[email protected] WebPortal LoginError"] --> Real["realPortal"] Real --> Cfg["useProxy true"] Cfg --> Url["proxyUrl https://jportal-cors-proxy.onrender.com"] MockFile["MockWebPortal.js"] --> Mock["mockPortal"] Demo{"isDemoMode"} -->|false| Real Demo -->|true| Mock Real --> W["w"] Mock --> W
Authentication state management#
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isDemoMode, setIsDemoMode] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
| Variable | Role |
|---|---|
isAuthenticated |
Protected tree vs login |
isDemoMode |
mockPortal vs realPortal |
isLoading |
“Signing in…” splash during auto-login |
error |
Auto-login failure text |
Auto-login flow#
sequenceDiagram
participant App
participant LS as localStorage
participant RP as realPortal
participant UI
App->>LS: username password
alt missing
App->>UI: LoginWrapper isLoading false
else present
App->>RP: student_login
alt RP.session
App->>UI: AuthenticatedApp
else LoginError unavailable
App->>UI: error banner + LoginWrapper
else Failed to fetch
App->>UI: network error + LoginWrapper
else other
App->>LS: remove credentials
App->>UI: Auto-login failed
end
endLogin component#
src/components/Login.jsx is the form. Zod + react-hook-form. LoginError from the same jsjiit CDN import.
flowchart TD Form["Login form"] --> Schema["zod enrollmentNumber password"] Form --> Submit["onSubmit sets loginStatus.credentials"] Submit --> Effect["useEffect student_login"] Effect --> Store["localStorage username password"] Effect --> Success["onLoginSuccess"] Effect --> Toast["sonner on LoginError"] DemoBtn["Try Demo"] --> Demo["onDemoLogin"]
Login process:
- Submit sets
loginStatus.credentials. - Effect calls
w.student_login(enrollmentNumber, password). - Success writes
localStorageandonLoginSuccess(). LoginWrapperwaits 100ms thennavigate("/attendance").
Routing architecture#
Route guard implementation#
flowchart TD
Router["HashRouter"] --> Stats["/stats always Cloudflare"]
Router --> Auth{"isAuthenticated"}
Auth -->|false| Star["path * LoginWrapper"]
Auth -->|true| Rest["path /* AuthenticatedApp"]| Condition | Pattern | Component |
|---|---|---|
| Always | /stats |
Cloudflare |
| Logged out | * |
LoginWrapper |
| Logged in | /* |
AuthenticatedApp |
AuthenticatedApp route definitions#
flowchart TD AA["AuthenticatedApp"] --> N1["Navigate / -> /attendance"] AA --> N2["Navigate /login -> /attendance"] AA --> A["/attendance Attendance"] AA --> G["/grades Grades"] AA --> E["/exams Exams"] AA --> S["/subjects Subjects"] AA --> P["/profile Profile"]
AuthenticatedApp wrapper#
AuthenticatedApp is the layout and the state hub.
Component responsibilities#
flowchart TD AA["AuthenticatedApp"] --> Measure["headerRef --header-height"] AA --> Header["Header logout + theme"] AA --> Routes["feature Routes"] AA --> Nav["Navbar"] AA --> Persist["attendanceGoal localStorage"]
State organization#
| Feature | Examples |
|---|---|
| Attendance | attendanceData, selectedAttendanceSem, attendanceGoal, daily cache, tabs |
| Grades | gradesData, gradeCards, marksData, loading flags |
| Exams | examSchedule, examSemesters, selectedExamSem, selectedExamEvent |
| Subjects | subjectData, subjectChoices, tabs registered / choices |
| Profile | profileData |
| Shared | w |
Each route passes a long props list. Example Attendance: w, caches, goal, subject daily data, loading flags, tab, calendar, tracker.
LocalStorage integration#
const [attendanceGoal, setAttendanceGoal] = useState(() => {
const savedGoal = localStorage.getItem("attendanceGoal");
return savedGoal ? parseInt(savedGoal) : 75;
});
A useEffect writes the goal back on change.
Navigation system#
Header component#
Sticky top-0 z-30. Title, /stats icon, ThemeSelectorDialog, logout. Logout clears username, password, attendanceData, sets auth and demo false, navigates to /login.
Navbar component#
flowchart LR Nav["Navbar fixed bottom"] --> A["/attendance"] Nav --> G["/grades"] Nav --> E["/exams"] Nav --> S["/subjects"] Nav --> P["/profile"]
Icons come from public/icons/*.svg?react. Active NavLink uses bg-primary and fill-primary-foreground.
Component lifecycle#
stateDiagram-v2 [*] --> Loading: mount performLogin Loading --> Login: no session Loading --> Authenticated: session Login --> Authenticated: real or demo success Authenticated --> Login: Header handleLogout