Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
446 changes: 283 additions & 163 deletions README.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions frontend/student-management-ui/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Base URL of the API Gateway (see backend/api-gateway). Vite only exposes
# variables prefixed with VITE_ to client code.
VITE_API_BASE_URL=http://localhost:9000/api/v1
27 changes: 27 additions & 0 deletions frontend/student-management-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Environment files (see .env.example for the template)
.env

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
8 changes: 8 additions & 0 deletions frontend/student-management-ui/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "oxc"],
"rules": {
"react/rules-of-hooks": "error",
"react/only-export-components": ["warn", { "allowConstantExport": true }]
}
}
116 changes: 116 additions & 0 deletions frontend/student-management-ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# StudentHub Frontend

A React 19 + Vite + Material UI single-page app for the Student Management
System, talking to the backend exclusively through `api-gateway` (port 9000).

## Tech Stack

| Concern | Choice |
|---------------------|---------------------------------------|
| Framework | React 19 |
| Build tool | Vite 8 |
| UI library | Material UI (MUI) v9 |
| Routing | react-router-dom v7 |
| HTTP client | axios, with JWT auto-refresh interceptor |
| Notifications | notistack (toast snackbars) |

## Project Layout

```
src/
├── api/ API modules, one per backend service
│ ├── axiosClient.js axios instance + JWT attach/refresh interceptors
│ ├── authApi.js auth-service (register/login/refresh/logout) + admin user management
│ ├── studentApi.js student-service
│ ├── courseApi.js course-service
│ ├── enrollmentApi.js enrollment-service
│ └── gradeApi.js grade-service
├── context/
│ └── AuthContext.jsx current user, login/register/logout, session persistence
├── components/
│ ├── ProtectedRoute.jsx redirects to /login if not authenticated
│ ├── RoleRoute.jsx redirects to the user's own home if role doesn't match
│ ├── layout/AppLayout.jsx sidebar + top bar, role-based nav
│ └── common/ LoadingSpinner, ConfirmDialog
├── pages/
│ ├── Login.jsx, Register.jsx, NotFound.jsx
│ ├── admin/ AdminDashboard, ManageStudents, ManageCourses,
│ │ ManageEnrollments, ManageGrades, ManageUsers
│ └── student/ StudentDashboard, AvailableCourses, MyCourses,
│ MyGrades, Profile
├── theme/theme.js MUI theme customization
└── utils/ constants + localStorage helpers
```

## Running Locally

1. Make sure the backend is running (`api-gateway` on port 9000 at minimum
- see `../../backend/README.md` and its Docker Compose setup).
2. Install dependencies:
```bash
npm install
```
3. Copy the environment template if you haven't already (already done for
you as `.env` in this delivered copy, pointing at the default gateway URL):
```bash
cp .env.example .env
```
4. Start the dev server:
```bash
npm run dev
```
5. Open the printed local URL (typically `http://localhost:5173`).

## Key Design Decisions

### JWT auto-refresh (`api/axiosClient.js`)
A response interceptor watches for `401`s. On the first one, it calls
`/auth/refresh` with the stored refresh token, updates both tokens, and
retries the original request. If multiple requests 401 at the same time
(e.g. a dashboard firing several calls at once), they all queue behind a
**single** in-flight refresh call rather than each independently hitting
`/auth/refresh`. If the refresh itself fails, the session is cleared and
the user is redirected to `/login`.

### Response shape awareness
Not every backend service wraps its responses the same way:
- `auth-service` (including `/admin/users/**`) wraps everything in the
shared `ApiResponse<T>` envelope from `common-lib` -> the API modules
unwrap `response.data.data`.
- `student-service`, `course-service`, `enrollment-service`, and
`grade-service` all return raw DTOs directly -> the API modules use
`response.data`.

This is called out explicitly in `api/gradeApi.js` since it's the easiest
place to get this backwards.

### Route protection
`ProtectedRoute` guards everything behind a login check; `RoleRoute` then
splits `/admin/*` from `/student/*` so a STUDENT account can never even
render an admin page component (on top of the backend's own ownership
checks - this is a UX nicety, not the security boundary; the backend
remains the actual enforcement point).

### Grades workflow
Since grade-service requires an `enrollmentId` (not a raw student/course
pair) to assign a grade, `ManageGrades` lets an admin pick a course, see
its roster (via `enrollmentApi.getByCourse`), and assign/update a grade
per enrollment - mirroring exactly how the backend expects grades to be
created.

### No "list all enrollments" endpoint
The backend intentionally has no global "all enrollments" endpoint (only
by-student and by-course). `AdminDashboard`'s "Total Enrollments" card and
`ManageEnrollments`/`ManageGrades` therefore work by course selection
rather than a flat global list - this matches the real API surface rather
than assuming an endpoint that doesn't exist.

## Build

```bash
npm run build # outputs to dist/
npm run preview # serve the production build locally
```

This has been verified to build cleanly (`npm run build`) and lint cleanly
(`npm run lint`, 0 errors) in this environment.
13 changes: 13 additions & 0 deletions frontend/student-management-ui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>StudentHub — Student Management System</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
Loading
Loading