From dfb2f126b973732f14dc513ec317a30fa8698702 Mon Sep 17 00:00:00 2001
From: Muhammad Asad <148639150+ASAD2204@users.noreply.github.com>
Date: Sun, 25 Jan 2026 02:17:13 +0500
Subject: [PATCH 1/7] new features addon
---
.github/workflows/deploy.yml | 49 +
DEPLOYMENT.md | 65 +
package-lock.json | 561 ++++++
package.json | 6 +-
src/App.jsx | 98 +-
src/components/Chat/ChatWidget.jsx | 721 +++++++
src/components/Common/EmptyState.jsx | 125 ++
src/components/Common/LoadingSkeleton.jsx | 24 +
src/components/Common/PageTransition.jsx | 33 +
src/components/Layout/MainLayout.jsx | 74 +-
src/components/Layout/Sidebar.jsx | 175 +-
src/components/Layout/TopBar.jsx | 31 +-
src/contexts/SnackbarContext.jsx | 77 +
src/contexts/ThemeContext.jsx | 6 +-
src/data/dummyData.js | 374 +++-
src/main.jsx | 13 +-
src/pages/Admin/AlumniManagement.jsx | 532 +++++
src/pages/Admin/Dashboard.jsx | 595 ++++--
src/pages/Admin/DepartmentManagement.jsx | 634 ++++++
src/pages/Admin/FinanceManagement.jsx | 538 ++++++
src/pages/Admin/GrievanceManagement.jsx | 669 +++++++
src/pages/Admin/Reports.jsx | 141 +-
src/pages/Admin/Settings.jsx | 446 +++++
src/pages/Alumni/AlumniEvents.jsx | 439 +++++
src/pages/Alumni/AlumniNetwork.jsx | 361 ++++
src/pages/Alumni/JobBoard.jsx | 360 ++++
src/pages/Alumni/Mentorship.jsx | 374 ++++
src/pages/Alumni/SuccessStories.jsx | 366 ++++
src/pages/Attendance/AttendanceSuccess.jsx | 155 ++
src/pages/Attendance/Confirmation.jsx | 215 +++
src/pages/Attendance/CourseSelection.jsx | 197 ++
src/pages/Attendance/FaceCapture.jsx | 375 ++++
src/pages/Attendance/GPSVerification.jsx | 275 +++
src/pages/Attendance/LivenessDetection.jsx | 557 ++++++
src/pages/Attendance/SmartAttendance.jsx | 1153 ++++-------
src/pages/Auth/ForgotPassword.jsx | 37 +-
src/pages/Auth/Login.jsx | 313 +--
src/pages/Auth/OTP.jsx | 39 +-
src/pages/Chat/ChatPortal.jsx | 1924 ++++++++-----------
src/pages/Chat/ChatPortal.jsx.bak | 1463 ++++++++++++++
src/pages/Grievances/EnhancedGrievances.jsx | 569 ++++++
src/pages/Grievances/Grievances.jsx | 704 ++++++-
src/pages/LMS/CourseClassroom.jsx | 41 +-
src/pages/Library/BookManagement.jsx | 383 ++++
src/pages/Library/IssuedBooks.jsx | 370 ++++
src/pages/Library/LibrarianDashboard.jsx | 973 ++++++++++
src/pages/Library/LibrarianGrievances.jsx | 507 +++++
src/pages/Library/LibrarianReports.jsx | 392 ++++
src/pages/Library/Library.jsx | 755 +++++++-
src/pages/Library/LibraryCatalog.jsx | 864 ++++++---
src/pages/Library/Reservations.jsx | 356 ++++
src/pages/Student/AlumniDirectory.jsx | 377 ++++
src/pages/Student/Dashboard.jsx | 139 +-
src/pages/Student/MyAssignments.jsx | 411 ++++
src/pages/Student/MyTickets.jsx | 484 +++++
src/pages/Student/Notifications.jsx | 361 ++++
src/pages/Teacher/Assignments.jsx | 522 +++++
src/pages/Teacher/CourseManagement.jsx | 906 +++++++++
src/pages/Teacher/CreateAssignment.jsx | 289 +++
src/pages/Teacher/Dashboard.jsx | 40 +-
src/pages/Teacher/GrievanceManagement.jsx | 540 ++++++
src/pages/Teacher/MyCourses.jsx | 8 +-
src/pages/Teacher/Quizzes.jsx | 458 +++++
src/pages/Teacher/Reports.jsx | 484 +++++
src/pages/Teacher/StudentManagement.jsx | 83 +-
src/pages/Teacher/TeacherAttendance.jsx | 436 +++++
src/pages/Teacher/ViewSubmissions.jsx | 460 +++++
src/styles/globalStyles.js | 37 +-
src/theme.js | 92 +-
vite.config.js | 1 +
70 files changed, 23756 insertions(+), 2876 deletions(-)
create mode 100644 .github/workflows/deploy.yml
create mode 100644 DEPLOYMENT.md
create mode 100644 src/components/Chat/ChatWidget.jsx
create mode 100644 src/components/Common/EmptyState.jsx
create mode 100644 src/components/Common/PageTransition.jsx
create mode 100644 src/contexts/SnackbarContext.jsx
create mode 100644 src/pages/Admin/AlumniManagement.jsx
create mode 100644 src/pages/Admin/DepartmentManagement.jsx
create mode 100644 src/pages/Admin/FinanceManagement.jsx
create mode 100644 src/pages/Admin/GrievanceManagement.jsx
create mode 100644 src/pages/Admin/Settings.jsx
create mode 100644 src/pages/Alumni/AlumniEvents.jsx
create mode 100644 src/pages/Alumni/AlumniNetwork.jsx
create mode 100644 src/pages/Alumni/JobBoard.jsx
create mode 100644 src/pages/Alumni/Mentorship.jsx
create mode 100644 src/pages/Alumni/SuccessStories.jsx
create mode 100644 src/pages/Attendance/AttendanceSuccess.jsx
create mode 100644 src/pages/Attendance/Confirmation.jsx
create mode 100644 src/pages/Attendance/CourseSelection.jsx
create mode 100644 src/pages/Attendance/FaceCapture.jsx
create mode 100644 src/pages/Attendance/GPSVerification.jsx
create mode 100644 src/pages/Attendance/LivenessDetection.jsx
create mode 100644 src/pages/Chat/ChatPortal.jsx.bak
create mode 100644 src/pages/Grievances/EnhancedGrievances.jsx
create mode 100644 src/pages/Library/BookManagement.jsx
create mode 100644 src/pages/Library/IssuedBooks.jsx
create mode 100644 src/pages/Library/LibrarianDashboard.jsx
create mode 100644 src/pages/Library/LibrarianGrievances.jsx
create mode 100644 src/pages/Library/LibrarianReports.jsx
create mode 100644 src/pages/Library/Reservations.jsx
create mode 100644 src/pages/Student/AlumniDirectory.jsx
create mode 100644 src/pages/Student/MyAssignments.jsx
create mode 100644 src/pages/Student/MyTickets.jsx
create mode 100644 src/pages/Student/Notifications.jsx
create mode 100644 src/pages/Teacher/Assignments.jsx
create mode 100644 src/pages/Teacher/CourseManagement.jsx
create mode 100644 src/pages/Teacher/CreateAssignment.jsx
create mode 100644 src/pages/Teacher/GrievanceManagement.jsx
create mode 100644 src/pages/Teacher/Quizzes.jsx
create mode 100644 src/pages/Teacher/Reports.jsx
create mode 100644 src/pages/Teacher/TeacherAttendance.jsx
create mode 100644 src/pages/Teacher/ViewSubmissions.jsx
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..1f8deee
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,49 @@
+name: Deploy to GitHub Pages
+
+on:
+ push:
+ branches:
+ - main
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: true
+
+jobs:
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build
+ run: npm run build
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v4
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: './dist'
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
new file mode 100644
index 0000000..a35474a
--- /dev/null
+++ b/DEPLOYMENT.md
@@ -0,0 +1,65 @@
+# GitHub Pages Deployment Guide
+
+## Setup Instructions
+
+### 1. Update Repository Information
+In `package.json`, replace the homepage URL:
+```json
+"homepage": "https://YOUR_GITHUB_USERNAME.github.io/Project_Nexus"
+```
+Change `YOUR_GITHUB_USERNAME` to your actual GitHub username.
+
+### 2. GitHub Repository Settings
+1. Go to your repository on GitHub
+2. Navigate to **Settings** → **Pages**
+3. Under **Source**, select **GitHub Actions**
+
+### 3. Deploy Options
+
+#### Option A: Automatic Deployment (Recommended)
+Push your code to the `main` branch:
+```bash
+git add .
+git commit -m "Configure GitHub Pages deployment"
+git push origin main
+```
+The GitHub Action will automatically build and deploy your site.
+
+#### Option B: Manual Deployment
+Run the deployment script:
+```bash
+npm run deploy
+```
+
+### 4. Access Your Site
+After deployment, your site will be available at:
+```
+https://YOUR_GITHUB_USERNAME.github.io/Project_Nexus
+```
+
+## Important Notes
+
+- **Base Path**: The `base` property in `vite.config.js` is set to `/Project_Nexus/`. This must match your repository name.
+- **Build Folder**: Vite builds to the `dist` folder by default.
+- **First Deployment**: Initial deployment may take 2-5 minutes.
+
+## Troubleshooting
+
+### 404 Errors
+- Verify the `base` path in `vite.config.js` matches your repository name
+- Check that GitHub Pages is enabled in repository settings
+
+### Build Fails
+- Run `npm run build` locally to check for errors
+- Ensure all dependencies are installed: `npm install`
+
+### Assets Not Loading
+- Confirm the `homepage` in `package.json` is correct
+- Check browser console for CORS or path errors
+
+## Development vs Production
+
+- **Development**: `npm run dev` (runs on http://localhost:5173)
+- **Build**: `npm run build` (creates production build in `dist/`)
+- **Preview**: `npm run preview` (preview production build locally)
+- **Deploy**: `npm run deploy` (deploy to GitHub Pages)
diff --git a/package-lock.json b/package-lock.json
index a0f64c6..d7a092c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,6 +28,7 @@
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
+ "gh-pages": "^6.3.0",
"globals": "^16.5.0",
"vite": "^7.2.4"
}
@@ -1479,6 +1480,44 @@
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -2281,6 +2320,23 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/babel-plugin-macros": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
@@ -2324,6 +2380,19 @@
"concat-map": "0.0.1"
}
},
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
@@ -2434,6 +2503,23 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/commander": {
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2658,6 +2744,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@@ -2675,6 +2774,13 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/email-addresses": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
+ "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/error-ex": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
@@ -2955,6 +3061,36 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -2969,6 +3105,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -3000,6 +3146,65 @@
"node": ">=16.0.0"
}
},
+ "node_modules/filename-reserved-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
+ "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/filenamify": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
+ "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "filename-reserved-regex": "^2.0.0",
+ "strip-outer": "^1.0.1",
+ "trim-repeated": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-cache-dir": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
+ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+ }
+ },
"node_modules/find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
@@ -3071,6 +3276,21 @@
}
}
},
+ "node_modules/fs-extra": {
+ "version": "11.3.3",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz",
+ "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -3105,6 +3325,29 @@
"node": ">=6.9.0"
}
},
+ "node_modules/gh-pages": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz",
+ "integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async": "^3.2.4",
+ "commander": "^13.0.0",
+ "email-addresses": "^5.0.0",
+ "filenamify": "^4.3.0",
+ "find-cache-dir": "^3.3.1",
+ "fs-extra": "^11.1.1",
+ "globby": "^11.1.0"
+ },
+ "bin": {
+ "gh-pages": "bin/gh-pages.js",
+ "gh-pages-clean": "bin/gh-pages-clean.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -3131,6 +3374,34 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3284,6 +3555,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -3362,6 +3643,19 @@
"node": ">=6"
}
},
+ "node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -3437,6 +3731,59 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3563,6 +3910,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -3647,6 +4004,75 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pkg-dir/node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -3713,6 +4139,27 @@
"node": ">=6"
}
},
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/react": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
@@ -3897,6 +4344,17 @@
"node": ">=4"
}
},
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/rollup": {
"version": "4.54.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
@@ -3939,6 +4397,30 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
"node_modules/scheduler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
@@ -3984,6 +4466,16 @@
"node": ">=8"
}
},
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -4016,6 +4508,29 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strip-outer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
+ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-outer/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
@@ -4070,6 +4585,42 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/trim-repeated": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
+ "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/trim-repeated/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -4089,6 +4640,16 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
diff --git a/package.json b/package.json
index afe6440..d470a5d 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,14 @@
"private": true,
"version": "0.0.0",
"type": "module",
+ "homepage": "https://YOUR_GITHUB_USERNAME.github.io/Project_Nexus",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "predeploy": "npm run build",
+ "deploy": "gh-pages -d dist"
},
"dependencies": {
"@emotion/react": "^11.14.0",
@@ -30,6 +33,7 @@
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
+ "gh-pages": "^6.3.0",
"globals": "^16.5.0",
"vite": "^7.2.4"
}
diff --git a/src/App.jsx b/src/App.jsx
index 0cb9d03..c27a622 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -14,21 +14,42 @@ import OTP from './pages/Auth/OTP';
import Dashboard from './pages/Student/Dashboard';
import Profile from './pages/Student/Profile';
import Transcript from './pages/Student/Transcript';
+import MyAssignments from './pages/Student/MyAssignments';
+import MyTickets from './pages/Student/MyTickets';
+import Notifications from './pages/Student/Notifications';
+import AlumniDirectory from './pages/Student/AlumniDirectory';
// Admin Pages
import AdminDashboard from './pages/Admin/Dashboard';
import UserManagement from './pages/Admin/UserManagement';
-import CourseManagement from './pages/Admin/CourseManagement';
+import AdminCourseManagement from './pages/Admin/CourseManagement';
import AdminReports from './pages/Admin/Reports';
+import AdminFinance from './pages/Admin/FinanceManagement';
+import AdminGrievanceManagement from './pages/Admin/GrievanceManagement';
+import AdminSettings from './pages/Admin/Settings';
+import AlumniManagement from './pages/Admin/AlumniManagement';
+import DepartmentManagement from './pages/Admin/DepartmentManagement';
// Teacher Pages
import TeacherDashboard from './pages/Teacher/Dashboard';
import TeacherCourses from './pages/Teacher/MyCourses';
import StudentManagement from './pages/Teacher/StudentManagement';
+import TeacherReports from './pages/Teacher/Reports';
+import TeacherCourseManagement from './pages/Teacher/CourseManagement';
+import CreateAssignment from './pages/Teacher/CreateAssignment';
+import ViewSubmissions from './pages/Teacher/ViewSubmissions';
+import Assignments from './pages/Teacher/Assignments';
+import Quizzes from './pages/Teacher/Quizzes';
+import TeacherAttendance from './pages/Teacher/TeacherAttendance';
// Attendance Pages
import SmartAttendance from './pages/Attendance/SmartAttendance';
import AttendanceHistory from './pages/Attendance/History';
+import GPSVerification from './pages/Attendance/GPSVerification';
+import LivenessDetection from './pages/Attendance/LivenessDetection';
+import FaceCapture from './pages/Attendance/FaceCapture';
+import Confirmation from './pages/Attendance/Confirmation';
+import AttendanceSuccess from './pages/Attendance/AttendanceSuccess';
// LMS Pages
import CourseList from './pages/LMS/CourseList';
@@ -43,7 +64,19 @@ import ChatPortal from './pages/Chat/ChatPortal';
// Other Pages
import Library from './pages/Library/Library';
+import LibrarianDashboard from './pages/Library/LibrarianDashboard';
+import BookManagement from './pages/Library/BookManagement';
+import IssuedBooks from './pages/Library/IssuedBooks';
+import Reservations from './pages/Library/Reservations';
+import LibrarianReports from './pages/Library/LibrarianReports';
+import LibrarianGrievances from './pages/Library/LibrarianGrievances';
import Grievances from './pages/Grievances/Grievances';
+import AlumniNetwork from './pages/Alumni/AlumniNetwork';
+import AlumniEvents from './pages/Alumni/AlumniEvents';
+import JobBoard from './pages/Alumni/JobBoard';
+import Mentorship from './pages/Alumni/Mentorship';
+import SuccessStories from './pages/Alumni/SuccessStories';
+import TeacherGrievanceManagement from './pages/Teacher/GrievanceManagement';
// Protected Route Component
const ProtectedRoute = ({ children }) => {
@@ -61,6 +94,10 @@ function App() {
return '/admin/dashboard';
case 'teacher':
return '/teacher/dashboard';
+ case 'librarian':
+ return '/librarian/dashboard';
+ case 'alumni':
+ return '/alumni/network';
default:
return '/dashboard';
}
@@ -88,38 +125,85 @@ function App() {
} />
} />
} />
+ } />
+ } />
{/* Admin Routes */}
} />
} />
- } />
+ } />
+ } />
+ } />
} />
+ } />
+ } />
+ } />
{/* Teacher Routes */}
} />
} />
} />
} />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
{/* Attendance Routes */}
- } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
} />
{/* LMS Routes */}
} />
} />
} />
- } />
+ } />
+ } />
+
+ {/* Support Routes */}
+ } />
+ } />
{/* Finance Routes */}
} />
-
- {/* Chat Routes */}
+ {/* Student Alumni Interaction Routes */}
+ } />
+ {/* Chat Routes */}
} />
- {/* Other Routes */}
+ {/* Library Routes */}
} />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+ {/* Alumni Routes */}
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+ {/* Grievances */}
} />
+ } />
{/* Fallback */}
diff --git a/src/components/Chat/ChatWidget.jsx b/src/components/Chat/ChatWidget.jsx
new file mode 100644
index 0000000..cc1930b
--- /dev/null
+++ b/src/components/Chat/ChatWidget.jsx
@@ -0,0 +1,721 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ Box,
+ Paper,
+ Typography,
+ TextField,
+ IconButton,
+ Avatar,
+ List,
+ ListItem,
+ ListItemAvatar,
+ ListItemText,
+ ListItemButton,
+ Badge,
+ Divider,
+ Tab,
+ Tabs,
+ Stack,
+ Chip,
+ InputAdornment,
+ useTheme,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Button,
+} from '@mui/material';
+import {
+ Close,
+ OpenInFull,
+ Send,
+ Mic,
+ EmojiEmotions,
+ Search,
+ SmartToy,
+ Person,
+ Groups,
+ Circle,
+ AttachFile,
+ MoreVert,
+ Add,
+ Check,
+} from '@mui/icons-material';
+import { motion, AnimatePresence } from 'framer-motion';
+import { useNavigate } from 'react-router-dom';
+
+const ChatWidget = ({ open, onClose, greetingMessage }) => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const [mode, setMode] = useState('ai'); // 'ai' or 'human'
+ const [view, setView] = useState('chat'); // 'contacts', 'groups', 'chat'
+ const [chatInput, setChatInput] = useState('');
+ const [isTyping, setIsTyping] = useState(false);
+ const [selectedContact, setSelectedContact] = useState(null);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [createGroupOpen, setCreateGroupOpen] = useState(false);
+ const [newGroupName, setNewGroupName] = useState('');
+ const [newGroupDescription, setNewGroupDescription] = useState('');
+ const [selectedMembers, setSelectedMembers] = useState([]);
+ const [groups, setGroups] = useState([
+ { id: 1, name: 'BSIT Batch 2024', members: 48, avatar: 'B', lastMsg: 'Quiz on Friday', time: '10m' },
+ { id: 2, name: 'Database Project', members: 5, avatar: 'D', lastMsg: 'Schema finalized', time: '1h' },
+ { id: 3, name: 'Study Group A', members: 8, avatar: 'S', lastMsg: 'Tomorrow at library', time: '4h' },
+ ]);
+ const messagesEndRef = useRef(null);
+
+ const [messages, setMessages] = useState([
+ {
+ id: 1,
+ sender: 'ai',
+ text: greetingMessage || 'How can I help you today?',
+ timestamp: new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }),
+ },
+ ]);
+
+ const contacts = [
+ { id: 1, name: 'Ayesha Khan', role: 'Class Rep', status: 'online', avatar: 'A', lastMsg: 'Can you share notes?', time: '5m' },
+ { id: 2, name: 'Dr. Sarah Ahmed', role: 'Advisor', status: 'online', avatar: 'S', lastMsg: 'Meeting at 3 PM', time: '1h' },
+ { id: 3, name: 'Ali Hassan', role: 'Peer', status: 'away', avatar: 'A', lastMsg: 'Thanks!', time: '2h' },
+ { id: 4, name: 'Maria Khan', role: 'Study Group', status: 'online', avatar: 'M', lastMsg: 'Assignment due tomorrow', time: '3h' },
+ ];
+
+ const handleCreateGroup = () => {
+ if (!newGroupName.trim() || selectedMembers.length < 2) return;
+
+ const newGroup = {
+ id: groups.length + 1,
+ name: newGroupName,
+ members: selectedMembers.length + 1,
+ avatar: newGroupName[0].toUpperCase(),
+ lastMsg: 'Group created',
+ time: 'Now',
+ };
+
+ setGroups((prev) => [...prev, newGroup]);
+ setCreateGroupOpen(false);
+ setNewGroupName('');
+ setNewGroupDescription('');
+ setSelectedMembers([]);
+ setView('groups');
+ };
+
+ const toggleMemberSelection = (contactId) => {
+ setSelectedMembers((prev) =>
+ prev.includes(contactId) ? prev.filter((id) => id !== contactId) : [...prev, contactId]
+ );
+ };
+
+ const handleSendMessage = () => {
+ if (!chatInput.trim()) return;
+
+ const newMessage = {
+ id: Date.now(),
+ sender: 'user',
+ text: chatInput,
+ timestamp: new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }),
+ };
+ setMessages((prev) => [...prev, newMessage]);
+ setChatInput('');
+
+ if (mode === 'ai') {
+ setIsTyping(true);
+ setTimeout(() => {
+ setMessages((prev) => [
+ ...prev,
+ {
+ id: Date.now() + 1,
+ sender: 'ai',
+ text: 'I understand your query. Let me help you with that!',
+ timestamp: new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }),
+ },
+ ]);
+ setIsTyping(false);
+ }, 1000);
+ }
+ };
+
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ };
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages]);
+
+ const filteredContacts = contacts.filter((c) =>
+ c.name.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ const filteredGroups = groups.filter((g) =>
+ g.name.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ if (!open) return null;
+
+ return (
+
+
+ {/* Header */}
+
+
+
+ Nexus Chat
+
+
+ navigate('/chat')} sx={{ color: 'white' }}>
+
+
+
+
+
+
+
+
+ {/* Mode Toggle */}
+
+ { setMode('ai'); setView('chat'); }}
+ sx={{
+ flex: 1,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ gap: 0.5,
+ py: 0.75,
+ px: 1.5,
+ borderRadius: '10px',
+ backgroundColor: mode === 'ai' ? 'rgba(255,255,255,0.25)' : 'transparent',
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+ }}
+ >
+
+ AI Assistant
+
+ { setMode('human'); setView('contacts'); }}
+ sx={{
+ flex: 1,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ gap: 0.5,
+ py: 0.75,
+ px: 1.5,
+ borderRadius: '10px',
+ backgroundColor: mode === 'human' ? 'rgba(255,255,255,0.25)' : 'transparent',
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+ }}
+ >
+
+ Contacts
+
+
+
+
+ {/* Content Area */}
+ {mode === 'ai' ? (
+ // AI Chat View
+ <>
+
+
+ {messages.map((msg) => (
+
+
+
+
+ {msg.text}
+
+
+ {msg.timestamp}
+
+
+
+
+ ))}
+ {isTyping && (
+
+
+
+
+
+ )}
+
+
+
+
+ {/* AI Input */}
+
+
+
+
+
+ setChatInput(e.target.value)}
+ onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
+ variant="standard"
+ InputProps={{ disableUnderline: true }}
+ sx={{ '& .MuiInputBase-input': { fontSize: '0.9rem', py: 0.5 } }}
+ />
+ {chatInput.trim() ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+ >
+ ) : (
+ // Human Chat View
+ <>
+ {view === 'contacts' || view === 'groups' ? (
+ <>
+ {/* Tabs */}
+
+ setView(v === 0 ? 'contacts' : 'groups')}>
+
+
+
+
+
+ {/* Search */}
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ backgroundColor: 'background.paper',
+ borderRadius: '12px',
+ },
+ }}
+ />
+
+
+ {/* List */}
+
+ {(view === 'contacts' ? filteredContacts : filteredGroups).map((item) => (
+
+ {
+ setSelectedContact(item);
+ setView('chat');
+ setMessages([{
+ id: 1,
+ sender: 'other',
+ text: item.lastMsg,
+ timestamp: item.time,
+ }]);
+ }}
+ sx={{ py: 1.5 }}
+ >
+
+
+ ) : null
+ }
+ >
+
+ {view === 'contacts' ? item.avatar : }
+
+
+
+
+ {item.name}
+
+ }
+ secondary={
+
+ {item.lastMsg}
+
+ }
+ />
+
+
+ {item.time}
+
+ {view === 'groups' && (
+
+ )}
+
+
+
+
+ ))}
+
+
+ {/* Create Group Button */}
+ {view === 'groups' && (
+
+ }
+ onClick={() => setCreateGroupOpen(true)}
+ sx={{
+ bgcolor: '#128C7E',
+ color: 'white',
+ fontWeight: 600,
+ borderRadius: '20px',
+ py: 1,
+ textTransform: 'none',
+ '&:hover': {
+ bgcolor: '#0F7A6F',
+ },
+ }}
+ >
+ Create New Group
+
+
+ )}
+ >
+ ) : (
+ // Individual Chat View
+ <>
+ {/* Chat Header */}
+
+ setView('contacts')}>
+
+
+
+ {selectedContact?.avatar}
+
+
+
+ {selectedContact?.name}
+
+
+ {selectedContact?.status === 'online' ? 'online' : 'offline'}
+
+
+
+
+
+
+
+ {/* Messages */}
+
+
+ {messages.map((msg) => (
+
+
+ {msg.text}
+
+ {msg.timestamp}
+
+
+
+ ))}
+
+
+
+
+ {/* Input */}
+
+
+
+
+
+
+
+
+ setChatInput(e.target.value)}
+ onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
+ variant="standard"
+ InputProps={{ disableUnderline: true }}
+ sx={{ '& .MuiInputBase-input': { fontSize: '0.9rem', py: 0.5 } }}
+ />
+ {chatInput.trim() ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+ >
+ )}
+ >
+ )}
+
+
+ {/* Create Group Dialog */}
+
+
+ );
+};
+
+export default ChatWidget;
diff --git a/src/components/Common/EmptyState.jsx b/src/components/Common/EmptyState.jsx
new file mode 100644
index 0000000..701b06a
--- /dev/null
+++ b/src/components/Common/EmptyState.jsx
@@ -0,0 +1,125 @@
+import { Box, Typography, Button, Paper } from '@mui/material';
+import PropTypes from 'prop-types';
+import {
+ Assignment as AssignmentIcon,
+ School as SchoolIcon,
+ LibraryBooks as LibraryIcon,
+ Message as MessageIcon,
+ Notifications as NotificationsIcon,
+ Search as SearchIcon,
+ Inbox as InboxIcon,
+ Event as EventIcon,
+} from '@mui/icons-material';
+
+/**
+ * Empty State Component
+ * Displays when there are no items to show in a list/table
+ * Provides visual feedback with icons, messages, and optional actions
+ */
+
+const iconMap = {
+ assignments: AssignmentIcon,
+ courses: SchoolIcon,
+ books: LibraryIcon,
+ messages: MessageIcon,
+ notifications: NotificationsIcon,
+ search: SearchIcon,
+ inbox: InboxIcon,
+ events: EventIcon,
+};
+
+const EmptyState = ({
+ icon = 'inbox',
+ title = 'No items found',
+ message = 'There are no items to display at this time.',
+ actionLabel,
+ onAction,
+ sx = {},
+}) => {
+ const IconComponent = iconMap[icon] || InboxIcon;
+
+ return (
+
+
+ theme.palette.mode === 'light'
+ ? 'rgba(25, 118, 210, 0.08)'
+ : 'rgba(33, 150, 243, 0.12)',
+ mb: 3,
+ }}
+ >
+
+
+
+
+ {title}
+
+
+
+ {message}
+
+
+ {actionLabel && onAction && (
+
+ )}
+
+ );
+};
+
+EmptyState.propTypes = {
+ icon: PropTypes.oneOf([
+ 'assignments',
+ 'courses',
+ 'books',
+ 'messages',
+ 'notifications',
+ 'search',
+ 'inbox',
+ 'events',
+ ]),
+ title: PropTypes.string,
+ message: PropTypes.string,
+ actionLabel: PropTypes.string,
+ onAction: PropTypes.func,
+ sx: PropTypes.object,
+};
+
+export default EmptyState;
diff --git a/src/components/Common/LoadingSkeleton.jsx b/src/components/Common/LoadingSkeleton.jsx
index 298d168..a594acf 100644
--- a/src/components/Common/LoadingSkeleton.jsx
+++ b/src/components/Common/LoadingSkeleton.jsx
@@ -143,6 +143,30 @@ export const DashboardSkeleton = () => {
);
};
+// Course Card Skeleton - for grid cards
+export const CourseCardSkeleton = ({ count = 3 }) => {
+ return (
+
+ {[...Array(count)].map((_, index) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
// Profile Skeleton - for profile page loading
export const ProfileSkeleton = () => {
return (
diff --git a/src/components/Common/PageTransition.jsx b/src/components/Common/PageTransition.jsx
new file mode 100644
index 0000000..736a079
--- /dev/null
+++ b/src/components/Common/PageTransition.jsx
@@ -0,0 +1,33 @@
+import { motion } from 'framer-motion';
+import PropTypes from 'prop-types';
+
+/**
+ * Page Transition Wrapper
+ * Adds smooth fade-up animation to page content
+ * Used globally to wrap all main page content
+ */
+
+const PageTransition = ({ children, delay = 0 }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+PageTransition.propTypes = {
+ children: PropTypes.node.isRequired,
+ delay: PropTypes.number,
+};
+
+export default PageTransition;
diff --git a/src/components/Layout/MainLayout.jsx b/src/components/Layout/MainLayout.jsx
index 5a591cd..a5a45f9 100644
--- a/src/components/Layout/MainLayout.jsx
+++ b/src/components/Layout/MainLayout.jsx
@@ -1,18 +1,48 @@
-import React, { useState } from 'react';
-import { Box, CssBaseline } from '@mui/material';
-import { Outlet } from 'react-router-dom';
+import React, { useEffect, useMemo, useState } from 'react';
+import {
+ Box,
+ CssBaseline,
+ Fab,
+ Badge,
+} from '@mui/material';
+import {
+ ChatBubbleOutline,
+} from '@mui/icons-material';
+import { Outlet, useLocation } from 'react-router-dom';
import Sidebar from './Sidebar';
import TopBar from './TopBar';
+import ChatWidget from '../Chat/ChatWidget';
const DRAWER_WIDTH = 260;
const MainLayout = () => {
const [mobileOpen, setMobileOpen] = useState(false);
+ const [chatOpen, setChatOpen] = useState(false);
+ const [unreadCount, setUnreadCount] = useState(1);
+ const location = useLocation();
+
+ const contextGreeting = useMemo(() => {
+ const path = location.pathname;
+ if (path.startsWith('/finance')) return 'Do you have questions about your fee voucher?';
+ if (path.startsWith('/lms')) return 'Need help with your course or assignment?';
+ if (path.startsWith('/attendance')) return 'Want help with attendance check-in?';
+ if (path.startsWith('/library')) return 'Looking for a book or reservation help?';
+ if (path.startsWith('/grievances')) return 'Need help submitting a grievance?';
+ if (path.startsWith('/chat')) return 'Want to continue in full chat?';
+ return 'How can I assist you right now?';
+ }, [location.pathname]);
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
+ const handleChatToggle = () => {
+ setChatOpen((prev) => !prev);
+ if (!chatOpen) {
+ setUnreadCount(0);
+ }
+ };
+
return (
@@ -37,6 +67,44 @@ const MainLayout = () => {
>
+
+ {/* Floating Chat Widget - Hidden on Chat Portal */}
+ {!location.pathname.startsWith('/chat') && (
+
+ {!chatOpen && (
+
+ 0 ? 'pulse 2s infinite' : 'none',
+ }}
+ >
+
+
+
+ )}
+
+ {/* Chat Widget */}
+
+
+ )}
);
};
diff --git a/src/components/Layout/Sidebar.jsx b/src/components/Layout/Sidebar.jsx
index 00f2932..235b7fc 100644
--- a/src/components/Layout/Sidebar.jsx
+++ b/src/components/Layout/Sidebar.jsx
@@ -41,6 +41,11 @@ import {
Assessment as AssessmentIcon,
Group as GroupIcon,
People as PeopleIcon,
+ Event as EventIcon,
+ LocalLibrary as LocalLibraryIcon,
+ Quiz as QuizIcon,
+ AccountBalance as AccountBalanceIcon,
+ CardMembership as CardMembershipIcon,
} from '@mui/icons-material';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
@@ -70,7 +75,7 @@ const studentMenuItems = [
{
text: 'Attendance',
icon: HowToRegIcon,
- path: '/attendance',
+ path: '/attendance/smart-attendance',
badge: !isAttendanceMarkedToday() ? 'Mark Now' : null,
badgeColor: 'warning'
},
@@ -92,13 +97,15 @@ const studentMenuItems = [
text: 'Nexus Chat',
icon: ChatIcon,
path: '/chat',
- badge: null,
badgeColor: 'primary'
},
{ text: 'Profile', icon: PersonIcon, path: '/profile', divider: true },
{ text: 'Transcript', icon: DescriptionIcon, path: '/transcript' },
{ text: 'Library', icon: MenuBookIcon, path: '/library' },
+ { text: 'Alumni Directory', icon: PeopleIcon, path: '/student/alumni-directory' },
{ text: 'Grievances', icon: SupportAgentIcon, path: '/grievances' },
+ { text: 'My Tickets', icon: SupportAgentIcon, path: '/support' },
+ { text: 'Notifications', icon: AssessmentIcon, path: '/notifications' },
];
// Admin menu items
@@ -106,11 +113,14 @@ const adminMenuItems = [
{ text: 'Dashboard', icon: DashboardIcon, path: '/admin/dashboard' },
{ text: 'User Management', icon: PeopleIcon, path: '/admin/users' },
{ text: 'Course Management', icon: SchoolIcon, path: '/admin/courses' },
- { text: 'Fee Management', icon: PaymentIcon, path: '/finance' },
- { text: 'Reports', icon: AssessmentIcon, path: '/admin/reports', divider: true },
+ { text: 'Departments', icon: AccountBalanceIcon, path: '/admin/departments' },
+ { text: 'Alumni Management', icon: CardMembershipIcon, path: '/admin/alumni' },
+ { text: 'Finance Management', icon: PaymentIcon, path: '/admin/finance' },
+ { text: 'Grievances', icon: SupportAgentIcon, path: '/admin/grievances' },
+ { text: 'Reports', icon: AssessmentIcon, path: '/admin/reports' },
+ { text: 'Settings', icon: SettingsIcon, path: '/admin/settings', divider: true },
{ text: 'Library', icon: MenuBookIcon, path: '/library' },
- { text: 'Grievances', icon: SupportAgentIcon, path: '/grievances' },
- { text: 'Settings', icon: SettingsIcon, path: '/admin/settings' },
+ { text: 'Nexus Chat', icon: ChatIcon, path: '/chat' },
];
// Teacher menu items
@@ -118,26 +128,48 @@ const teacherMenuItems = [
{ text: 'Dashboard', icon: DashboardIcon, path: '/teacher/dashboard' },
{ text: 'My Courses', icon: SchoolIcon, path: '/teacher/courses' },
{ text: 'Students', icon: GroupIcon, path: '/teacher/students' },
+ { text: 'Assignments', icon: AssignmentIcon, path: '/teacher/assignments' },
+ { text: 'Quizzes', icon: QuizIcon, path: '/teacher/quizzes' },
{
text: 'Attendance',
icon: HowToRegIcon,
- path: '/attendance',
- },
- {
- text: 'Assignments',
- icon: AssignmentIcon,
- path: '/assignments',
- badge: '23',
- badgeColor: 'warning'
+ path: '/teacher/attendance',
},
+ { text: 'Reports', icon: AssessmentIcon, path: '/teacher/reports', divider: true },
+ { text: 'Profile', icon: PersonIcon, path: '/profile' },
+ { text: 'Library', icon: MenuBookIcon, path: '/library' },
{
text: 'Nexus Chat',
icon: ChatIcon,
path: '/chat',
},
- { text: 'Profile', icon: PersonIcon, path: '/profile', divider: true },
+ { text: 'Grievances', icon: SupportAgentIcon, path: '/teacher/grievances' },
+];
+
+// Librarian menu items
+const librarianMenuItems = [
+ { text: 'Dashboard', icon: DashboardIcon, path: '/librarian/dashboard' },
+ { text: 'Library Catalog', icon: LocalLibraryIcon, path: '/library' },
+ { text: 'Book Management', icon: MenuBookIcon, path: '/librarian/books' },
+ { text: 'Issued Books', icon: AssignmentIcon, path: '/librarian/issued' },
+ { text: 'Reservations', icon: EventIcon, path: '/librarian/reservations' },
+ { text: 'Reports', icon: AssessmentIcon, path: '/librarian/reports', divider: true },
+ { text: 'Nexus Chat', icon: ChatIcon, path: '/chat' },
+ { text: 'Grievances', icon: SupportAgentIcon, path: '/librarian/grievances' },
+ { text: 'Settings', icon: SettingsIcon, path: '/admin/settings' },
+];
+
+// Alumni menu items
+const alumniMenuItems = [
+ { text: 'Alumni Network', icon: PeopleIcon, path: '/alumni/network' },
+ { text: 'Events', icon: EventIcon, path: '/alumni/events' },
+ { text: 'Job Board', icon: AssignmentIcon, path: '/alumni/jobs' },
+ { text: 'Mentorship', icon: GroupIcon, path: '/alumni/mentorship' },
+ { text: 'Success Stories', icon: SchoolIcon, path: '/alumni/stories', divider: true },
+ { text: 'Profile', icon: PersonIcon, path: '/profile' },
{ text: 'Library', icon: MenuBookIcon, path: '/library' },
- { text: 'Reports', icon: AssessmentIcon, path: '/teacher/reports' },
+ { text: 'Nexus Chat', icon: ChatIcon, path: '/chat' },
+ { text: 'Grievances', icon: SupportAgentIcon, path: '/alumni/grievances' },
];
const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
@@ -156,6 +188,10 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
return adminMenuItems;
case 'teacher':
return teacherMenuItems;
+ case 'librarian':
+ return librarianMenuItems;
+ case 'alumni':
+ return alumniMenuItems;
default:
return studentMenuItems;
}
@@ -190,19 +226,24 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
display: 'flex',
flexDirection: 'column',
minHeight: 0,
- background: `linear-gradient(180deg, ${theme.palette.primary.dark} 0%, ${theme.palette.primary.main} 100%)`,
+ background: theme.palette.mode === 'dark'
+ ? 'linear-gradient(180deg, #1A1A1A 0%, #0A0A0A 100%)'
+ : 'linear-gradient(180deg, #1976D2 0%, #00796B 100%)',
}}
>
{/* Logo Area */}
{!collapsed ? (
@@ -211,16 +252,21 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
sx={{
width: 150,
height: 50,
- backgroundColor: 'rgba(255, 255, 255, 0.2)',
+ background: theme.palette.mode === 'dark'
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.2) 0%, rgba(33,150,243,0.2) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.15) 100%)',
borderRadius: 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mx: 'auto',
mb: 1,
+ boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
+ border: '1px solid',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.15)' : 'rgba(255,255,255,0.3)',
}}
>
-
+
NEXUS
@@ -259,20 +305,34 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
navigate(item.path)}
sx={{
- borderRadius: collapsed ? '8px' : '12px',
- backgroundColor: isActive ? 'rgba(255, 255, 255, 0.16)' : 'transparent',
+ borderRadius: collapsed ? '12px' : '16px',
+ background: isActive
+ ? theme.palette.mode === 'dark'
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.25) 0%, rgba(33,150,243,0.25) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.15) 100%)'
+ : 'transparent',
color: 'white',
'&:hover': {
- backgroundColor: isActive
- ? 'rgba(255, 255, 255, 0.2)'
- : 'rgba(255, 255, 255, 0.1)',
+ background: isActive
+ ? theme.palette.mode === 'dark'
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.3) 0%, rgba(33,150,243,0.3) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.2) 100%)'
+ : theme.palette.mode === 'dark'
+ ? 'rgba(255,255,255,0.08)'
+ : 'rgba(255,255,255,0.12)',
},
border: '1px solid',
- borderColor: isActive ? 'rgba(255, 255, 255, 0.18)' : 'transparent',
+ borderColor: isActive
+ ? theme.palette.mode === 'dark'
+ ? 'rgba(76,175,80,0.3)'
+ : 'rgba(255,255,255,0.25)'
+ : 'transparent',
py: collapsed ? 1.5 : 1.2,
px: collapsed ? 1.5 : 2,
+ mx: collapsed ? 0 : 0.5,
justifyContent: collapsed ? 'center' : 'flex-start',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
+ boxShadow: isActive ? '0 4px 12px rgba(0,0,0,0.15)' : 'none',
}}
>
{
{item.badge && (
)}
@@ -350,7 +418,7 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
)}
{item.divider && (
-
+
)}
);
@@ -360,14 +428,18 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
{/* Footer (always sits at the bottom; never overlaps nav) */}
-
+
{/* User Info Card */}
{!collapsed ? (
@@ -392,9 +464,14 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
sx={{
height: 18,
fontSize: '0.65rem',
- backgroundColor: 'rgba(255, 255, 255, 0.2)',
+ fontWeight: 600,
+ background: theme.palette.mode === 'dark'
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.3) 0%, rgba(33,150,243,0.3) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.15) 100%)',
color: 'white',
mt: 0.5,
+ border: '1px solid',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.15)' : 'rgba(255,255,255,0.3)',
}}
/>
@@ -423,10 +500,16 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
onClick={handleLogoutClick}
sx={{
color: 'white',
- borderColor: 'rgba(255, 255, 255, 0.3)',
+ fontWeight: 700,
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.35)',
+ background: theme.palette.mode === 'dark'
+ ? 'rgba(244,67,54,0.15)'
+ : 'rgba(211,47,47,0.08)',
'&:hover': {
- borderColor: 'rgba(255, 255, 255, 0.5)',
- backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.4)' : 'rgba(255,255,255,0.6)',
+ background: theme.palette.mode === 'dark'
+ ? 'rgba(244,67,54,0.25)'
+ : 'rgba(211,47,47,0.15)',
},
py: collapsed ? 1.5 : 0.75,
justifyContent: 'center',
@@ -443,7 +526,9 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
display: 'flex',
justifyContent: 'center',
p: 0.5,
- backgroundColor: 'rgba(255, 255, 255, 0.05)',
+ background: theme.palette.mode === 'dark'
+ ? 'rgba(255,255,255,0.03)'
+ : 'rgba(255,255,255,0.08)',
}}
>
{
sx={{
color: 'white',
'&:hover': {
- backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ background: theme.palette.mode === 'dark'
+ ? 'rgba(255,255,255,0.08)'
+ : 'rgba(255,255,255,0.15)',
},
}}
>
diff --git a/src/components/Layout/TopBar.jsx b/src/components/Layout/TopBar.jsx
index cb2f9f2..cc4a2fd 100644
--- a/src/components/Layout/TopBar.jsx
+++ b/src/components/Layout/TopBar.jsx
@@ -132,6 +132,29 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
chat: 'Nexus Chat',
library: 'Library',
grievances: 'Grievances',
+ admin: 'Admin',
+ users: 'User Management',
+ courses: 'Course Management',
+ departments: 'Departments',
+ alumni: 'Alumni Management',
+ reports: 'Reports',
+ settings: 'Settings',
+ teacher: 'Teacher',
+ students: 'Students',
+ quizzes: 'Quizzes',
+ librarian: 'Librarian',
+ books: 'Book Management',
+ issued: 'Issued Books',
+ reservations: 'Reservations',
+ network: 'Alumni Network',
+ events: 'Events',
+ jobs: 'Job Board',
+ mentorship: 'Mentorship Program',
+ stories: 'Success Stories',
+ support: 'My Tickets',
+ notifications: 'Notifications',
+ 'alumni-directory': 'Alumni Directory',
+ 'smart-attendance': 'Smart Attendance',
};
pathnames.forEach((value, index) => {
@@ -251,10 +274,12 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
sx={{
width: { sm: `calc(100% - ${drawerWidth}px)` },
ml: { sm: `${drawerWidth}px` },
- backgroundColor: 'white',
+ backgroundColor: 'background.paper',
color: 'text.primary',
+ borderBottom: '1px solid',
+ borderColor: 'divider',
}}
- elevation={1}
+ elevation={0}
>
{/* Left: Hamburger Menu + Breadcrumbs */}
@@ -566,7 +591,7 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
variant="body2"
onClick={() => {
handleNotificationsClose();
- // Navigate to notifications page
+ navigate('/notifications');
}}
sx={{
fontWeight: 600,
diff --git a/src/contexts/SnackbarContext.jsx b/src/contexts/SnackbarContext.jsx
new file mode 100644
index 0000000..f8b99a2
--- /dev/null
+++ b/src/contexts/SnackbarContext.jsx
@@ -0,0 +1,77 @@
+import { createContext, useContext, useState, useCallback } from 'react';
+import { Snackbar, Alert, Slide } from '@mui/material';
+import PropTypes from 'prop-types';
+
+/**
+ * Snackbar Context
+ * Provides global toast notifications throughout the application
+ * Usage: const { showSnackbar } = useSnackbar();
+ * showSnackbar('Success!', 'success');
+ */
+
+const SnackbarContext = createContext();
+
+export const useSnackbar = () => {
+ const context = useContext(SnackbarContext);
+ if (!context) {
+ throw new Error('useSnackbar must be used within SnackbarProvider');
+ }
+ return context;
+};
+
+function SlideTransition(props) {
+ return ;
+}
+
+export const SnackbarProvider = ({ children }) => {
+ const [snackbar, setSnackbar] = useState({
+ open: false,
+ message: '',
+ severity: 'success', // 'success' | 'error' | 'warning' | 'info'
+ });
+
+ const showSnackbar = useCallback((message, severity = 'success') => {
+ setSnackbar({
+ open: true,
+ message,
+ severity,
+ });
+ }, []);
+
+ const hideSnackbar = useCallback(() => {
+ setSnackbar((prev) => ({ ...prev, open: false }));
+ }, []);
+
+ return (
+
+ {children}
+
+
+ {snackbar.message}
+
+
+
+ );
+};
+
+SnackbarProvider.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+export default SnackbarContext;
diff --git a/src/contexts/ThemeContext.jsx b/src/contexts/ThemeContext.jsx
index 9bf5a22..280e94e 100644
--- a/src/contexts/ThemeContext.jsx
+++ b/src/contexts/ThemeContext.jsx
@@ -7,7 +7,7 @@ const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
// Initialize theme mode from localStorage or default to 'light'
const [mode, setMode] = useState(() => {
- const savedMode = localStorage.getItem('themeMode');
+ const savedMode = localStorage.getItem('nexus-theme');
return savedMode || 'light';
});
@@ -15,14 +15,14 @@ export const ThemeProvider = ({ children }) => {
const toggleTheme = () => {
setMode((prevMode) => {
const newMode = prevMode === 'light' ? 'dark' : 'light';
- localStorage.setItem('themeMode', newMode);
+ localStorage.setItem('nexus-theme', newMode);
return newMode;
});
};
// Save mode to localStorage whenever it changes
useEffect(() => {
- localStorage.setItem('themeMode', mode);
+ localStorage.setItem('nexus-theme', mode);
}, [mode]);
return (
diff --git a/src/data/dummyData.js b/src/data/dummyData.js
index c35726a..d66df5f 100644
--- a/src/data/dummyData.js
+++ b/src/data/dummyData.js
@@ -421,7 +421,7 @@ export const libraryBooks = [
pages: 1248,
totalCopies: 5,
availableCopies: 3,
- coverImage: "https://via.placeholder.com/200x300/1976d2/ffffff?text=Database+Systems",
+ coverImage: "https://images.unsplash.com/photo-1589998059171-988d887df646?w=400&h=600&fit=crop",
shelfLocation: "CS-A-104",
description: "Comprehensive guide to database systems covering data models, relational algebra, SQL, database design, query processing, transaction management, and more. Essential reading for computer science students.",
language: "English",
@@ -439,7 +439,7 @@ export const libraryBooks = [
pages: 1312,
totalCopies: 8,
availableCopies: 5,
- coverImage: "https://via.placeholder.com/200x300/2e7d32/ffffff?text=Algorithms",
+ coverImage: "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?w=400&h=600&fit=crop",
shelfLocation: "CS-A-215",
description: "The leading introduction to algorithms. This book provides a comprehensive introduction to the modern study of computer algorithms covering analysis, design, and implementation.",
language: "English",
@@ -457,7 +457,7 @@ export const libraryBooks = [
pages: 944,
totalCopies: 6,
availableCopies: 2,
- coverImage: "https://via.placeholder.com/200x300/d32f2f/ffffff?text=OS+Concepts",
+ coverImage: "https://images.unsplash.com/photo-1497633762265-9d179a990aa6?w=400&h=600&fit=crop",
shelfLocation: "CS-B-088",
description: "Operating System Concepts continues to provide a solid theoretical foundation for understanding operating systems, covering process management, memory management, storage management, protection and security.",
language: "English",
@@ -475,7 +475,7 @@ export const libraryBooks = [
pages: 960,
totalCopies: 7,
availableCopies: 7,
- coverImage: "https://via.placeholder.com/200x300/7b1fa2/ffffff?text=Networks",
+ coverImage: "https://images.unsplash.com/photo-1481627834876-b7833e8f5570?w=400&h=600&fit=crop",
shelfLocation: "IT-C-142",
description: "This classic best seller has been thoroughly updated to reflect the newest and most exciting advances in networking. Features coverage of wireless networks, 3G cellular, Gigabit Ethernet, and more.",
language: "English",
@@ -493,7 +493,7 @@ export const libraryBooks = [
pages: 1152,
totalCopies: 4,
availableCopies: 0,
- coverImage: "https://via.placeholder.com/200x300/f57c00/ffffff?text=AI+Modern",
+ coverImage: "https://images.unsplash.com/photo-1512820790803-83ca734da794?w=400&h=600&fit=crop",
shelfLocation: "CS-D-225",
description: "The long-anticipated revision of this best-selling text offers the most comprehensive, up-to-date introduction to the theory and practice of artificial intelligence.",
language: "English",
@@ -511,7 +511,7 @@ export const libraryBooks = [
pages: 816,
totalCopies: 5,
availableCopies: 4,
- coverImage: "https://via.placeholder.com/200x300/0288d1/ffffff?text=Software+Eng",
+ coverImage: "https://images.unsplash.com/photo-1516259762381-22954d7d3ad2?w=400&h=600&fit=crop",
shelfLocation: "CS-A-301",
description: "For courses in computer science and software engineering. The fundamental practice of software engineering. Presents a broad perspective on software systems engineering.",
language: "English",
@@ -529,7 +529,7 @@ export const libraryBooks = [
pages: 800,
totalCopies: 6,
availableCopies: 3,
- coverImage: "https://via.placeholder.com/200x300/388e3c/ffffff?text=DS+Java",
+ coverImage: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=400&h=600&fit=crop",
shelfLocation: "CS-B-156",
description: "Data Structures and Algorithms in Java provides an introduction to data structures and algorithms, including their design, analysis, and implementation.",
language: "English",
@@ -547,7 +547,7 @@ export const libraryBooks = [
pages: 972,
totalCopies: 10,
availableCopies: 8,
- coverImage: "https://via.placeholder.com/200x300/c62828/ffffff?text=Discrete+Math",
+ coverImage: "https://images.unsplash.com/photo-1635070041078-e363dbe005cb?w=400&h=600&fit=crop",
shelfLocation: "MATH-A-045",
description: "Discrete Mathematics and its Applications is a focused introduction to the primary themes in a discrete mathematics course.",
language: "English",
@@ -565,7 +565,7 @@ export const libraryBooks = [
pages: 720,
totalCopies: 4,
availableCopies: 4,
- coverImage: "https://via.placeholder.com/200x300/00796b/ffffff?text=BI",
+ coverImage: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?w=400&h=600&fit=crop",
shelfLocation: "BUS-C-089",
description: "The book presents the fundamentals of business intelligence in a user-friendly format with real-world examples and case studies.",
language: "English",
@@ -583,7 +583,7 @@ export const libraryBooks = [
pages: 800,
totalCopies: 5,
availableCopies: 2,
- coverImage: "https://via.placeholder.com/200x300/5e35b1/ffffff?text=CO+Design",
+ coverImage: "https://images.unsplash.com/photo-1518770660439-4636190af475?w=400&h=600&fit=crop",
shelfLocation: "CS-A-178",
description: "The classic textbook that builds a strong foundation in the fundamentals of computer organization and design.",
language: "English",
@@ -601,7 +601,7 @@ export const libraryBooks = [
pages: 1616,
totalCopies: 8,
availableCopies: 6,
- coverImage: "https://via.placeholder.com/200x300/1565c0/ffffff?text=Physics",
+ coverImage: "https://images.unsplash.com/photo-1636466497217-26a8cbeaf0aa?w=400&h=600&fit=crop",
shelfLocation: "SCI-A-234",
description: "Achieve success in your physics course by making the most of what this best-selling physics text has to offer.",
language: "English",
@@ -619,7 +619,7 @@ export const libraryBooks = [
pages: 392,
totalCopies: 3,
availableCopies: 1,
- coverImage: "https://via.placeholder.com/200x300/558b2f/ffffff?text=Node.js",
+ coverImage: "https://images.unsplash.com/photo-1627398242454-45a1465c2479?w=400&h=600&fit=crop",
shelfLocation: "IT-B-267",
description: "Learn to build fast and scalable web applications with Node.js. This book covers Express.js, MongoDB, and modern JavaScript.",
language: "English",
@@ -628,6 +628,74 @@ export const libraryBooks = [
},
];
+export const libraryTransactions = [
+ {
+ id: 'LTX-1001',
+ studentId: 'STU001',
+ studentName: 'Muhammad Asad',
+ isbn: '978-0131873254',
+ bookTitle: 'Database Systems: The Complete Book',
+ issuedOn: '2026-01-05',
+ dueDate: '2026-02-04',
+ status: 'Issued',
+ condition: 'Good',
+ fine: 0,
+ },
+ {
+ id: 'LTX-1002',
+ studentId: 'STU002',
+ studentName: 'Ayesha Khan',
+ isbn: '978-1118063330',
+ bookTitle: 'Operating System Concepts',
+ issuedOn: '2026-01-10',
+ dueDate: '2026-02-09',
+ status: 'Issued',
+ condition: 'Good',
+ fine: 0,
+ },
+];
+
+export const issueBookTransaction = (studentId, studentName, isbn) => {
+ const book = libraryBooks.find((b) => b.isbn === isbn);
+ if (!book || book.availableCopies < 1) {
+ return { success: false, message: 'Book not available.' };
+ }
+ const issuedOn = new Date().toISOString().split('T')[0];
+ const dueDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
+ .toISOString()
+ .split('T')[0];
+ const transaction = {
+ id: `LTX-${String(libraryTransactions.length + 1000)}`,
+ studentId,
+ studentName,
+ isbn,
+ bookTitle: book.title,
+ issuedOn,
+ dueDate,
+ status: 'Issued',
+ condition: 'Good',
+ fine: 0,
+ };
+ libraryTransactions.unshift(transaction);
+ book.availableCopies -= 1;
+ return { success: true, transaction };
+};
+
+export const returnBookTransaction = (transactionId, condition) => {
+ const transaction = libraryTransactions.find((t) => t.id === transactionId);
+ if (!transaction) {
+ return { success: false, message: 'Transaction not found.' };
+ }
+ const book = libraryBooks.find((b) => b.isbn === transaction.isbn);
+ if (book) {
+ book.availableCopies += 1;
+ }
+ transaction.status = 'Returned';
+ transaction.condition = condition;
+ transaction.fine = condition === 'Damaged' ? 500 : condition === 'Lost' ? 2000 : 0;
+ return { success: true, transaction };
+};
+
export const myIssuedBooks = [
{
id: 1,
@@ -688,21 +756,47 @@ export const readingHistory = [
];
export const reserveBook = (bookId) => {
- const book = libraryBooks.find(b => b.id === bookId);
- if (book && book.availableCopies > 0) {
- const reservation = {
- id: myReservedBooks.length + 1,
- bookId: bookId,
- bookTitle: book.title,
- reservedDate: new Date().toISOString().split('T')[0],
- expiresOn: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
- status: 'reserved',
+ // Check eligibility: User must have < 3 books issued
+ if (myIssuedBooks.length >= 3) {
+ return {
+ success: false,
+ message: '❌ Cannot reserve: You have reached the maximum limit of 3 issued books.'
};
- myReservedBooks.push(reservation);
- book.availableCopies--;
- return { success: true, message: 'Book reserved successfully!' };
}
- return { success: false, message: 'Book not available for reservation.' };
+
+ const book = libraryBooks.find(b => b.id === bookId);
+
+ if (!book) {
+ return { success: false, message: '❌ Book not found.' };
+ }
+
+ if (book.availableCopies <= 0) {
+ return { success: false, message: '❌ Book not available for reservation.' };
+ }
+
+ // Create reservation with 24-hour timer
+ const now = new Date();
+ const expiresOn = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24 hours from now
+
+ const reservation = {
+ id: myReservedBooks.length + 1,
+ bookId: bookId,
+ bookTitle: book.title,
+ reservedDate: now.toISOString().split('T')[0],
+ reservedTime: now.toLocaleTimeString(),
+ expiresOn: expiresOn.toISOString().split('T')[0],
+ expiresTime: expiresOn.toLocaleTimeString(),
+ status: 'reserved',
+ };
+
+ // Decrement available copies and set status to Reserved
+ myReservedBooks.push(reservation);
+ book.availableCopies--;
+
+ return {
+ success: true,
+ message: `✅ Book reserved successfully! Please pick up from library within 24 hours. Reservation expires on ${expiresOn.toLocaleDateString()} at ${expiresOn.toLocaleTimeString()}.`
+ };
};
export const returnBook = (issuedBookId) => {
@@ -932,3 +1026,233 @@ export const addChatMessage = (text, isAi = false, citations = null) => {
chatMessages.push(newMessage);
return newMessage;
};
+
+// Alumni Data
+export const alumni = [
+ {
+ id: 'ALU001',
+ name: 'Dr. Zainab Ahmed',
+ email: 'zainab.ahmed@techcorp.com',
+ graduationYear: 2018,
+ degree: 'BS Computer Science',
+ photoUrl: 'https://i.pravatar.cc/150?img=45',
+ currentCompany: 'Microsoft',
+ companyLogo: 'https://img.icons8.com/color/96/microsoft.png',
+ position: 'Senior Software Engineer',
+ location: 'Seattle, USA',
+ linkedIn: 'https://linkedin.com/in/zainab-ahmed',
+ achievements: ['Published 3 research papers', 'Speaker at Tech Summit 2024'],
+ expertise: ['Cloud Computing', 'AI/ML', 'System Design'],
+ },
+ {
+ id: 'ALU002',
+ name: 'Muhammad Hassan',
+ email: 'hassan@google.com',
+ graduationYear: 2019,
+ degree: 'BS Software Engineering',
+ photoUrl: 'https://i.pravatar.cc/150?img=12',
+ currentCompany: 'Google',
+ companyLogo: 'https://img.icons8.com/color/96/google-logo.png',
+ position: 'Product Manager',
+ location: 'Mountain View, USA',
+ linkedIn: 'https://linkedin.com/in/muhammad-hassan',
+ achievements: ['Led 5+ product launches', 'Google Cloud Certified'],
+ expertise: ['Product Management', 'Data Analytics', 'UX Research'],
+ },
+ {
+ id: 'ALU003',
+ name: 'Ayesha Malik',
+ email: 'ayesha@amazon.com',
+ graduationYear: 2017,
+ degree: 'BS Information Technology',
+ photoUrl: 'https://i.pravatar.cc/150?img=32',
+ currentCompany: 'Amazon',
+ companyLogo: 'https://img.icons8.com/color/96/amazon.png',
+ position: 'Solutions Architect',
+ location: 'Dubai, UAE',
+ linkedIn: 'https://linkedin.com/in/ayesha-malik',
+ achievements: ['AWS Certified Solutions Architect', 'Built scalable systems for 100M+ users'],
+ expertise: ['Cloud Architecture', 'DevOps', 'Microservices'],
+ },
+ {
+ id: 'ALU004',
+ name: 'Ali Raza',
+ email: 'ali@startup.io',
+ graduationYear: 2020,
+ degree: 'BS Computer Science',
+ photoUrl: 'https://i.pravatar.cc/150?img=51',
+ currentCompany: 'TechVenture (Founder)',
+ companyLogo: 'https://img.icons8.com/color/96/rocket.png',
+ position: 'CEO & Co-Founder',
+ location: 'Karachi, Pakistan',
+ linkedIn: 'https://linkedin.com/in/ali-raza',
+ achievements: ['Raised $2M in funding', 'Forbes 30 Under 30'],
+ expertise: ['Entrepreneurship', 'FinTech', 'Business Strategy'],
+ },
+ {
+ id: 'ALU005',
+ name: 'Sara Khan',
+ email: 'sara@meta.com',
+ graduationYear: 2016,
+ degree: 'BS Information Technology',
+ photoUrl: 'https://i.pravatar.cc/150?img=25',
+ currentCompany: 'Meta (Facebook)',
+ companyLogo: 'https://img.icons8.com/color/96/meta.png',
+ position: 'Engineering Manager',
+ location: 'London, UK',
+ linkedIn: 'https://linkedin.com/in/sara-khan',
+ achievements: ['Led team of 15 engineers', 'Meta Bootcamp Mentor'],
+ expertise: ['Engineering Leadership', 'React', 'Mobile Development'],
+ },
+ {
+ id: 'ALU006',
+ name: 'Usman Tariq',
+ email: 'usman@ibm.com',
+ graduationYear: 2021,
+ degree: 'BS Software Engineering',
+ photoUrl: 'https://i.pravatar.cc/150?img=33',
+ currentCompany: 'IBM',
+ companyLogo: 'https://img.icons8.com/color/96/ibm.png',
+ position: 'Data Scientist',
+ location: 'Toronto, Canada',
+ linkedIn: 'https://linkedin.com/in/usman-tariq',
+ achievements: ['Published AI research', 'Kaggle Competitions Master'],
+ expertise: ['Machine Learning', 'Deep Learning', 'NLP'],
+ },
+ {
+ id: 'ALU007',
+ name: 'Fatima Noor',
+ email: 'fatima@oracle.com',
+ graduationYear: 2019,
+ degree: 'BS Computer Science',
+ photoUrl: 'https://i.pravatar.cc/150?img=44',
+ currentCompany: 'Oracle',
+ companyLogo: 'https://img.icons8.com/color/96/oracle-logo.png',
+ position: 'Database Architect',
+ location: 'Singapore',
+ linkedIn: 'https://linkedin.com/in/fatima-noor',
+ achievements: ['Oracle Certified Master', 'Database performance optimization expert'],
+ expertise: ['Database Design', 'SQL', 'Performance Tuning'],
+ },
+ {
+ id: 'ALU008',
+ name: 'Ahmed Siddiqui',
+ email: 'ahmed@salesforce.com',
+ graduationYear: 2018,
+ degree: 'BS Information Technology',
+ photoUrl: 'https://i.pravatar.cc/150?img=60',
+ currentCompany: 'Salesforce',
+ companyLogo: 'https://img.icons8.com/color/96/salesforce.png',
+ position: 'Technical Lead',
+ location: 'San Francisco, USA',
+ linkedIn: 'https://linkedin.com/in/ahmed-siddiqui',
+ achievements: ['15x Salesforce Certified', 'Community Speaker'],
+ expertise: ['CRM', 'Apex', 'Lightning Web Components'],
+ },
+];
+
+export const alumniEvents = [
+ {
+ id: 'EVT001',
+ title: 'Annual Alumni Reunion 2026',
+ description: 'Join us for our annual reunion! Network with fellow alumni, share experiences, and reconnect with your batch mates.',
+ date: '2026-03-15',
+ time: '06:00 PM',
+ venue: 'University Main Auditorium',
+ type: 'Reunion',
+ capacity: 500,
+ registered: 234,
+ fee: 2000, // PKR
+ organizer: 'Alumni Relations Office',
+ coverImage: 'https://images.unsplash.com/photo-1511578314322-379afb476865?w=600',
+ features: ['Networking Dinner', 'Live Music', 'Photo Booth', 'Awards Ceremony'],
+ speakers: ['Dr. Zainab Ahmed', 'Muhammad Hassan'],
+ status: 'Upcoming',
+ },
+ {
+ id: 'EVT002',
+ title: 'Tech Career Fair 2026',
+ description: 'Exclusive career fair featuring top tech companies. Get interview opportunities, resume reviews, and career guidance.',
+ date: '2026-02-28',
+ time: '10:00 AM',
+ venue: 'Campus Expo Center',
+ type: 'Career Fair',
+ capacity: 300,
+ registered: 178,
+ fee: 0,
+ organizer: 'Career Services',
+ coverImage: 'https://images.unsplash.com/photo-1540575467063-178a50c2df87?w=600',
+ features: ['Company Booths', 'Mock Interviews', 'Resume Clinic', 'Panel Discussions'],
+ speakers: ['Ayesha Malik', 'Ali Raza', 'Sara Khan'],
+ status: 'Upcoming',
+ },
+ {
+ id: 'EVT003',
+ title: 'Entrepreneurship Workshop',
+ description: 'Learn from successful alumni entrepreneurs. Discover how to start your own venture and navigate the startup ecosystem.',
+ date: '2026-04-10',
+ time: '02:00 PM',
+ venue: 'Innovation Hub, Block C',
+ type: 'Workshop',
+ capacity: 100,
+ registered: 67,
+ fee: 500,
+ organizer: 'Entrepreneurship Cell',
+ coverImage: 'https://images.unsplash.com/photo-1559136555-9303baea8ebd?w=600',
+ features: ['Interactive Sessions', 'Networking', 'Pitch Practice', 'Mentorship'],
+ speakers: ['Ali Raza'],
+ status: 'Upcoming',
+ },
+ {
+ id: 'EVT004',
+ title: 'AI & Machine Learning Webinar',
+ description: 'Virtual webinar on latest trends in AI/ML. Get insights from industry experts working at top tech companies.',
+ date: '2026-02-20',
+ time: '07:00 PM',
+ venue: 'Online (Zoom)',
+ type: 'Webinar',
+ capacity: 1000,
+ registered: 543,
+ fee: 0,
+ organizer: 'CS Department',
+ coverImage: 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=600',
+ features: ['Live Q&A', 'Recording Access', 'Certificate', 'Resource Materials'],
+ speakers: ['Dr. Zainab Ahmed', 'Usman Tariq'],
+ status: 'Upcoming',
+ },
+ {
+ id: 'EVT005',
+ title: 'Sports Gala 2025',
+ description: 'Annual sports meet for alumni and students. Participate in cricket, football, badminton and more!',
+ date: '2025-12-20',
+ time: '08:00 AM',
+ venue: 'University Sports Complex',
+ type: 'Sports',
+ capacity: 400,
+ registered: 312,
+ fee: 1000,
+ organizer: 'Sports Department',
+ coverImage: 'https://images.unsplash.com/photo-1461896836934-ffe607ba8211?w=600',
+ features: ['Multiple Sports', 'Refreshments', 'Prizes', 'Team Building'],
+ speakers: [],
+ status: 'Completed',
+ },
+];
+
+export const registerForEvent = (eventId) => {
+ const event = alumniEvents.find(e => e.id === eventId);
+ if (event && event.registered < event.capacity) {
+ event.registered++;
+ return { success: true, message: 'Successfully registered for the event!', event };
+ }
+ return { success: false, message: 'Event is full or not found.' };
+};
+
+export const connectWithAlumni = (alumniId) => {
+ const alumnus = alumni.find(a => a.id === alumniId);
+ if (alumnus) {
+ // Simulate LinkedIn connection
+ return { success: true, message: `Connection request sent to ${alumnus.name}!`, linkedIn: alumnus.linkedIn };
+ }
+ return { success: false, message: 'Alumni not found.' };
+};
diff --git a/src/main.jsx b/src/main.jsx
index 243c03b..4c59546 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -8,6 +8,7 @@ import App from './App.jsx';
import { getTheme } from './theme.js';
import { ThemeProvider, useThemeMode } from './contexts/ThemeContext.jsx';
import { AuthProvider } from './contexts/AuthContext.jsx';
+import { SnackbarProvider } from './contexts/SnackbarContext.jsx';
import globalStyles from './styles/globalStyles.js';
// Theme Wrapper Component
@@ -19,11 +20,13 @@ const ThemedApp = () => {
-
-
-
-
-
+
+
+
+
+
+
+
);
};
diff --git a/src/pages/Admin/AlumniManagement.jsx b/src/pages/Admin/AlumniManagement.jsx
new file mode 100644
index 0000000..1892276
--- /dev/null
+++ b/src/pages/Admin/AlumniManagement.jsx
@@ -0,0 +1,532 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ TextField,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Chip,
+ Avatar,
+ InputAdornment,
+ Stack,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Select,
+ Tooltip,
+ Alert,
+ Paper,
+ alpha,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Add,
+ Edit,
+ Delete,
+ Search,
+ Email,
+ Phone,
+ Business,
+ LocationOn,
+ School,
+ LinkedIn,
+ CheckCircle,
+ Cancel,
+ Save,
+ PersonAdd,
+} from '@mui/icons-material';
+import { useTheme } from '@mui/material/styles';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { pageTransition, staggerContainer, fadeInUp } from '../../utils/animations';
+
+const AlumniManagement = () => {
+ const theme = useTheme();
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterYear, setFilterYear] = useState('all');
+ const [filterProgram, setFilterProgram] = useState('all');
+ const [openDialog, setOpenDialog] = useState(false);
+ const [editMode, setEditMode] = useState(false);
+ const [selectedAlumni, setSelectedAlumni] = useState(null);
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+
+ // Mock alumni data
+ const [alumniList, setAlumniList] = useState([
+ {
+ id: 1,
+ name: 'Ali Hassan',
+ rollNo: 'CS-2019-001',
+ email: 'ali.hassan@gmail.com',
+ phone: '+92 300 1234567',
+ program: 'BS Computer Science',
+ graduationYear: 2023,
+ currentCompany: 'Google',
+ designation: 'Software Engineer',
+ location: 'Karachi, Pakistan',
+ linkedIn: 'linkedin.com/in/alihassan',
+ verified: true,
+ registrationDate: '2023-08-15',
+ },
+ {
+ id: 2,
+ name: 'Fatima Noor',
+ rollNo: 'CS-2020-045',
+ email: 'fatima.noor@outlook.com',
+ phone: '+92 321 9876543',
+ program: 'BS Software Engineering',
+ graduationYear: 2024,
+ currentCompany: 'Microsoft',
+ designation: 'Product Manager',
+ location: 'Lahore, Pakistan',
+ linkedIn: 'linkedin.com/in/fatimanoor',
+ verified: true,
+ registrationDate: '2024-07-20',
+ },
+ {
+ id: 3,
+ name: 'Ahmed Khan',
+ rollNo: 'BBA-2018-089',
+ email: 'ahmed.khan@yahoo.com',
+ phone: '+92 333 5551234',
+ program: 'BBA',
+ graduationYear: 2022,
+ currentCompany: 'Unilever',
+ designation: 'Marketing Manager',
+ location: 'Islamabad, Pakistan',
+ linkedIn: 'linkedin.com/in/ahmedkhan',
+ verified: false,
+ registrationDate: '2022-09-10',
+ },
+ ]);
+
+ const [formData, setFormData] = useState({
+ name: '',
+ rollNo: '',
+ email: '',
+ phone: '',
+ program: '',
+ graduationYear: new Date().getFullYear(),
+ currentCompany: '',
+ designation: '',
+ location: '',
+ linkedIn: '',
+ verified: false,
+ });
+
+ // Stats
+ const stats = {
+ total: alumniList.length,
+ verified: alumniList.filter((a) => a.verified).length,
+ thisYear: alumniList.filter((a) => a.graduationYear === new Date().getFullYear()).length,
+ employed: alumniList.filter((a) => a.currentCompany).length,
+ };
+
+ const programs = ['BS Computer Science', 'BS Software Engineering', 'BBA', 'BS Data Science', 'MBA'];
+ const years = Array.from({ length: 10 }, (_, i) => new Date().getFullYear() - i);
+
+ // Filter alumni
+ const filteredAlumni = alumniList.filter((alumni) => {
+ const matchesSearch =
+ alumni.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ alumni.rollNo.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ alumni.email.toLowerCase().includes(searchQuery.toLowerCase());
+ const matchesYear = filterYear === 'all' || alumni.graduationYear === parseInt(filterYear);
+ const matchesProgram = filterProgram === 'all' || alumni.program === filterProgram;
+ return matchesSearch && matchesYear && matchesProgram;
+ });
+
+ const handleOpenDialog = (alumni = null) => {
+ if (alumni) {
+ setEditMode(true);
+ setSelectedAlumni(alumni);
+ setFormData(alumni);
+ } else {
+ setEditMode(false);
+ setSelectedAlumni(null);
+ setFormData({
+ name: '',
+ rollNo: '',
+ email: '',
+ phone: '',
+ program: '',
+ graduationYear: new Date().getFullYear(),
+ currentCompany: '',
+ designation: '',
+ location: '',
+ linkedIn: '',
+ verified: false,
+ });
+ }
+ setOpenDialog(true);
+ };
+
+ const handleCloseDialog = () => {
+ setOpenDialog(false);
+ setEditMode(false);
+ setSelectedAlumni(null);
+ };
+
+ const handleSave = () => {
+ if (!formData.name || !formData.email || !formData.rollNo) {
+ setSnackbar({ open: true, message: 'Please fill required fields', severity: 'error' });
+ return;
+ }
+
+ if (editMode) {
+ setAlumniList(alumniList.map((a) => (a.id === selectedAlumni.id ? { ...formData, id: a.id } : a)));
+ setSnackbar({ open: true, message: 'Alumni updated successfully', severity: 'success' });
+ } else {
+ const newAlumni = {
+ ...formData,
+ id: alumniList.length + 1,
+ registrationDate: new Date().toISOString().split('T')[0],
+ };
+ setAlumniList([...alumniList, newAlumni]);
+ setSnackbar({ open: true, message: 'Alumni registered successfully', severity: 'success' });
+ }
+ handleCloseDialog();
+ };
+
+ const handleDelete = (id) => {
+ if (window.confirm('Are you sure you want to delete this alumni?')) {
+ setAlumniList(alumniList.filter((a) => a.id !== id));
+ setSnackbar({ open: true, message: 'Alumni deleted successfully', severity: 'info' });
+ }
+ };
+
+ const handleToggleVerify = (id) => {
+ setAlumniList(
+ alumniList.map((a) => (a.id === id ? { ...a, verified: !a.verified } : a))
+ );
+ setSnackbar({ open: true, message: 'Verification status updated', severity: 'success' });
+ };
+
+ return (
+
+
+ } onClick={() => handleOpenDialog()}>
+ Register Alumni
+
+ }
+ />
+
+ {/* Stats */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Filters */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Graduation Year
+
+
+
+
+
+ Program
+
+
+
+
+
+
+
+ {/* Alumni Table */}
+
+
+
+ Alumni Records ({filteredAlumni.length})
+
+
+
+
+
+ Alumni
+ Program
+ Graduation Year
+ Current Position
+ Location
+ Status
+ Actions
+
+
+
+ {filteredAlumni.map((alumni) => (
+
+
+
+ {alumni.name[0]}
+
+
+ {alumni.name}
+
+
+ {alumni.rollNo}
+
+
+
+ {alumni.email}
+
+
+
+
+ {alumni.program}
+ {alumni.graduationYear}
+
+
+ {alumni.designation || '-'}
+
+
+ {alumni.currentCompany || 'Not specified'}
+
+
+
+
+ {alumni.location || '-'}
+
+
+
+ : }
+ onClick={() => handleToggleVerify(alumni.id)}
+ sx={{ cursor: 'pointer' }}
+ />
+
+
+
+ handleOpenDialog(alumni)}>
+
+
+
+
+ handleDelete(alumni.id)}>
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+ {/* Add/Edit Dialog */}
+
+
+ {/* Snackbar */}
+ {snackbar.open && (
+ setSnackbar({ ...snackbar, open: false })}
+ sx={{ position: 'fixed', bottom: 24, right: 24, zIndex: 9999 }}
+ >
+ {snackbar.message}
+
+ )}
+
+
+ );
+};
+
+export default AlumniManagement;
diff --git a/src/pages/Admin/Dashboard.jsx b/src/pages/Admin/Dashboard.jsx
index 2d7b40d..9d3d588 100644
--- a/src/pages/Admin/Dashboard.jsx
+++ b/src/pages/Admin/Dashboard.jsx
@@ -55,7 +55,7 @@ const AdminDashboard = () => {
change: '+12.5%',
trend: 'up',
icon: People,
- color: theme.palette.primary.main,
+ color: 'primary',
},
{
title: 'Faculty Members',
@@ -63,7 +63,7 @@ const AdminDashboard = () => {
change: '+3.2%',
trend: 'up',
icon: School,
- color: theme.palette.success.main,
+ color: 'success',
},
{
title: 'Active Courses',
@@ -71,7 +71,7 @@ const AdminDashboard = () => {
change: '+8.1%',
trend: 'up',
icon: Assignment,
- color: theme.palette.info.main,
+ color: 'info',
},
{
title: 'Revenue (This Month)',
@@ -79,7 +79,7 @@ const AdminDashboard = () => {
change: '+15.3%',
trend: 'up',
icon: AccountBalance,
- color: theme.palette.warning.main,
+ color: 'warning',
},
];
@@ -157,112 +157,85 @@ const AdminDashboard = () => {
))}
- {/* Enrollment & Attendance Overview */}
+ {/* Pending Approvals - Full Width */}
- {/* Enrollment Overview */}
-
-
-
-
-
- Enrollment Overview
-
- }>
- View Details
-
-
-
- {['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'].map((month, idx) => {
- const value = [120, 150, 180, 220, 280, 320][idx];
- return (
-
-
- {month}
- {value} students
-
-
-
- );
- })}
-
-
-
-
-
- {/* Attendance Overview */}
-
+
- Today's Attendance
+ Pending Approvals
-
-
- 85%
- Present
-
-
-
-
- 10%
- Absent
-
-
-
-
- 5%
- Leave
-
-
-
-
-
-
-
-
- {/* Revenue & Department Overview */}
-
- {/* Revenue Overview */}
-
-
-
-
- Monthly Revenue
-
-
- {['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'].map((month, idx) => {
- const revenue = [6.5, 7.2, 6.8, 7.5, 8.1, 8.5][idx];
+ {pendingApprovals.map((item, index) => {
+ const Icon = item.icon;
return (
-
- {month}
-
-
-
- ₨{revenue}M
-
+
+
+
+
+
+
+
+ {item.count}
+
+
+
+ {item.type}
+
+
+
);
})}
-
+
+
- {/* Departments Overview */}
-
-
+ {/* Main Content Grid - Better Layout */}
+
+ {/* Departments Overview - Takes Priority */}
+
+
-
- Departments Overview
-
-
-
-
+
+
+ Departments Overview
+
+
+ Top performing departments
+
+
+ }
+ onClick={() => navigate('/admin/departments')}
+ >
+ View All
+
{departments.map((dept, index) => (
@@ -270,46 +243,61 @@ const AdminDashboard = () => {
key={index}
elevation={0}
sx={{
- p: 2,
- backgroundColor: alpha(theme.palette.primary.main, 0.05),
+ p: 2.5,
+ backgroundColor: alpha(theme.palette.primary.main, 0.04),
borderRadius: 2,
+ border: '1px solid',
+ borderColor: 'divider',
+ transition: 'all 0.3s',
+ '&:hover': {
+ borderColor: theme.palette.primary.main,
+ backgroundColor: alpha(theme.palette.primary.main, 0.08),
+ transform: 'translateX(4px)',
+ },
}}
>
-
-
+
+
{dept.name}
}
label={`+${dept.growth}%`}
size="small"
color="success"
- sx={{ height: 20, fontSize: '0.7rem' }}
+ sx={{ fontWeight: 700 }}
/>
-
+
-
- Students
-
-
- {dept.students}
-
+
+
+ {dept.students}
+
+
+ Students
+
+
-
- Faculty
-
-
- {dept.faculty}
-
+
+
+ {dept.faculty}
+
+
+ Faculty
+
+
-
- Courses
-
-
- {dept.courses}
-
+
+
+ {dept.courses}
+
+
+ Courses
+
+
@@ -318,111 +306,316 @@ const AdminDashboard = () => {
-
- {/* Pending Approvals & Recent Activities */}
-
- {/* Pending Approvals */}
-
-
+ {/* Today's Attendance - Sidebar */}
+
+
- Pending Approvals
+ Today's Attendance
-
- {pendingApprovals.map((item, index) => {
- const Icon = item.icon;
+
+
+
+
+ 85%
+
+
+ Present Students
+
+
+ 2,420 out of 2,847 students
+
+
+
+
+
+
+
+ 10%
+
+
+ Absent (285)
+
+
+
+
+
+
+ 5%
+
+
+ On Leave (142)
+
+
+
+
+
+
+
+
+ {/* Quick Actions */}
+
+
+
+ Quick Actions
+
+
+ }
+ onClick={() => navigate('/admin/users')}
+ sx={{ justifyContent: 'flex-start', py: 1.5 }}
+ >
+ Add New User
+
+ }
+ onClick={() => navigate('/admin/courses')}
+ sx={{ justifyContent: 'flex-start', py: 1.5 }}
+ >
+ Manage Courses
+
+ }
+ onClick={() => navigate('/admin/finance')}
+ sx={{ justifyContent: 'flex-start', py: 1.5 }}
+ >
+ View Finances
+
+ }
+ onClick={() => navigate('/admin/reports')}
+ sx={{ justifyContent: 'flex-start', py: 1.5 }}
+ >
+ Generate Report
+
+
+
+
+
+
+
+ {/* Bottom Section - Enrollment & Revenue */}
+
+ {/* Monthly Enrollment Trend */}
+
+
+
+
+
+
+ Monthly Enrollment
+
+
+ New student registrations per month
+
+
+
+
+
+ {['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'].map((month, idx) => {
+ const value = [120, 150, 180, 220, 280, 320][idx];
+ const percentage = (value / 320) * 100;
return (
-
-
-
-
-
-
-
-
- {item.count}
-
-
- {item.type}
-
-
-
-
-
+
+
+
+ {month}
+
+
+ {value} students
+
+
+
+
);
})}
-
+
- {/* Recent Activities */}
-
+ {/* Monthly Revenue */}
+
-
- Recent Activities
-
+
+
+
+ Monthly Revenue
+
+
+ Total revenue collected per month
+
+
+
+
- {recentActivities.map((activity) => (
-
-
-
-
-
- {activity.title}
-
-
- {activity.description}
+ {['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'].map((month, idx) => {
+ const revenue = [6.5, 7.2, 6.8, 7.5, 8.1, 8.5][idx];
+ const percentage = (revenue / 8.5) * 100;
+ return (
+
+
+
+ {month}
-
- {activity.time}
+
+ ₨{revenue}M
+
-
- ))}
+ );
+ })}
+
+ {/* Recent Activities - Full Width */}
+
+
+
+
+
+
+ Recent Activities
+
+ }>
+ View All
+
+
+
+ {recentActivities.map((activity) => (
+
+
+
+
+
+
+ {activity.title}
+
+
+ {activity.description}
+
+
+
+
+
+
+ ))}
+
+
+
+
+
);
diff --git a/src/pages/Admin/DepartmentManagement.jsx b/src/pages/Admin/DepartmentManagement.jsx
new file mode 100644
index 0000000..9618096
--- /dev/null
+++ b/src/pages/Admin/DepartmentManagement.jsx
@@ -0,0 +1,634 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ TextField,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Chip,
+ InputAdornment,
+ Stack,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Select,
+ Tooltip,
+ Alert,
+ Divider,
+ alpha,
+ Paper,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Add,
+ Edit,
+ Delete,
+ Search,
+ School,
+ People,
+ Description,
+ Cancel,
+ Save,
+ Visibility,
+ MenuBook,
+ TrendingUp,
+} from '@mui/icons-material';
+import { useTheme } from '@mui/material/styles';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { pageTransition } from '../../utils/animations';
+
+const DepartmentManagement = () => {
+ const theme = useTheme();
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterDepartment, setFilterDepartment] = useState('all');
+ const [filterLevel, setFilterLevel] = useState('all');
+ const [openDialog, setOpenDialog] = useState(false);
+ const [editMode, setEditMode] = useState(false);
+ const [selectedProgram, setSelectedProgram] = useState(null);
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+
+ // Mock programs data
+ const [programs, setPrograms] = useState([
+ {
+ id: 1,
+ name: 'Bachelor of Science in Computer Science',
+ shortName: 'BS CS',
+ code: 'CS-001',
+ department: 'Computer Science',
+ level: 'Undergraduate',
+ duration: '4 years',
+ totalCredits: 132,
+ semesters: 8,
+ tuitionFee: 450000,
+ enrolledStudents: 856,
+ faculty: 45,
+ status: 'Active',
+ accreditation: 'HEC Recognized',
+ startYear: 2015,
+ },
+ {
+ id: 2,
+ name: 'Bachelor of Science in Software Engineering',
+ shortName: 'BS SE',
+ code: 'SE-001',
+ department: 'Computer Science',
+ level: 'Undergraduate',
+ duration: '4 years',
+ totalCredits: 132,
+ semesters: 8,
+ tuitionFee: 480000,
+ enrolledStudents: 642,
+ faculty: 38,
+ status: 'Active',
+ accreditation: 'PEC & HEC Recognized',
+ startYear: 2016,
+ },
+ {
+ id: 3,
+ name: 'Bachelor of Business Administration',
+ shortName: 'BBA',
+ code: 'BBA-001',
+ department: 'Business Administration',
+ level: 'Undergraduate',
+ duration: '4 years',
+ totalCredits: 120,
+ semesters: 8,
+ tuitionFee: 350000,
+ enrolledStudents: 743,
+ faculty: 32,
+ status: 'Active',
+ accreditation: 'HEC Recognized',
+ startYear: 2012,
+ },
+ {
+ id: 4,
+ name: 'Master of Business Administration',
+ shortName: 'MBA',
+ code: 'MBA-001',
+ department: 'Business Administration',
+ level: 'Graduate',
+ duration: '2 years',
+ totalCredits: 72,
+ semesters: 4,
+ tuitionFee: 600000,
+ enrolledStudents: 234,
+ faculty: 28,
+ status: 'Active',
+ accreditation: 'HEC Recognized',
+ startYear: 2010,
+ },
+ {
+ id: 5,
+ name: 'Bachelor of Science in Data Science',
+ shortName: 'BS DS',
+ code: 'DS-001',
+ department: 'Computer Science',
+ level: 'Undergraduate',
+ duration: '4 years',
+ totalCredits: 132,
+ semesters: 8,
+ tuitionFee: 500000,
+ enrolledStudents: 312,
+ faculty: 26,
+ status: 'Active',
+ accreditation: 'HEC Recognized',
+ startYear: 2020,
+ },
+ ]);
+
+ const [formData, setFormData] = useState({
+ name: '',
+ shortName: '',
+ code: '',
+ department: '',
+ level: 'Undergraduate',
+ duration: '',
+ totalCredits: '',
+ semesters: '',
+ tuitionFee: '',
+ enrolledStudents: 0,
+ faculty: 0,
+ status: 'Active',
+ accreditation: '',
+ startYear: new Date().getFullYear(),
+ });
+
+ // Stats
+ const stats = {
+ totalPrograms: programs.length,
+ activePrograms: programs.filter((p) => p.status === 'Active').length,
+ totalStudents: programs.reduce((sum, p) => sum + p.enrolledStudents, 0),
+ totalFaculty: programs.reduce((sum, p) => sum + p.faculty, 0),
+ };
+
+ const departments = [...new Set(programs.map((p) => p.department))];
+ const levels = ['Undergraduate', 'Graduate', 'Postgraduate'];
+
+ // Filter programs
+ const filteredPrograms = programs.filter((program) => {
+ const matchesSearch =
+ program.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ program.shortName.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ program.code.toLowerCase().includes(searchQuery.toLowerCase());
+ const matchesDepartment = filterDepartment === 'all' || program.department === filterDepartment;
+ const matchesLevel = filterLevel === 'all' || program.level === filterLevel;
+ return matchesSearch && matchesDepartment && matchesLevel;
+ });
+
+ const handleOpenDialog = (program = null) => {
+ if (program) {
+ setEditMode(true);
+ setSelectedProgram(program);
+ setFormData(program);
+ } else {
+ setEditMode(false);
+ setSelectedProgram(null);
+ setFormData({
+ name: '',
+ shortName: '',
+ code: '',
+ department: '',
+ level: 'Undergraduate',
+ duration: '',
+ totalCredits: '',
+ semesters: '',
+ tuitionFee: '',
+ enrolledStudents: 0,
+ faculty: 0,
+ status: 'Active',
+ accreditation: '',
+ startYear: new Date().getFullYear(),
+ });
+ }
+ setOpenDialog(true);
+ };
+
+ const handleCloseDialog = () => {
+ setOpenDialog(false);
+ setEditMode(false);
+ setSelectedProgram(null);
+ };
+
+ const handleSave = () => {
+ if (!formData.name || !formData.code || !formData.department) {
+ setSnackbar({ open: true, message: 'Please fill required fields', severity: 'error' });
+ return;
+ }
+
+ if (editMode) {
+ setPrograms(programs.map((p) => (p.id === selectedProgram.id ? { ...formData, id: p.id } : p)));
+ setSnackbar({ open: true, message: 'Program updated successfully', severity: 'success' });
+ } else {
+ const newProgram = {
+ ...formData,
+ id: programs.length + 1,
+ enrolledStudents: parseInt(formData.enrolledStudents) || 0,
+ faculty: parseInt(formData.faculty) || 0,
+ totalCredits: parseInt(formData.totalCredits) || 0,
+ semesters: parseInt(formData.semesters) || 0,
+ tuitionFee: parseInt(formData.tuitionFee) || 0,
+ };
+ setPrograms([...programs, newProgram]);
+ setSnackbar({ open: true, message: 'Program created successfully', severity: 'success' });
+ }
+ handleCloseDialog();
+ };
+
+ const handleDelete = (id) => {
+ if (window.confirm('Are you sure you want to delete this program?')) {
+ setPrograms(programs.filter((p) => p.id !== id));
+ setSnackbar({ open: true, message: 'Program deleted successfully', severity: 'info' });
+ }
+ };
+
+ const handleToggleStatus = (id) => {
+ setPrograms(
+ programs.map((p) =>
+ p.id === id ? { ...p, status: p.status === 'Active' ? 'Inactive' : 'Active' } : p
+ )
+ );
+ };
+
+ return (
+
+
+ } onClick={() => handleOpenDialog()}>
+ Add New Program
+
+ }
+ />
+
+ {/* Stats */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Filters */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Department
+
+
+
+
+
+ Level
+
+
+
+
+
+
+
+ {/* Programs Table */}
+
+
+
+ Degree Programs ({filteredPrograms.length})
+
+
+
+
+
+ Program
+ Department
+ Level
+ Duration
+ Credits
+ Students
+ Tuition Fee
+ Status
+ Actions
+
+
+
+ {filteredPrograms.map((program) => (
+
+
+
+
+ {program.name}
+
+
+ {program.shortName} ({program.code})
+
+
+
+ {program.department}
+
+
+
+
+ {program.duration}
+
+
+ {program.semesters} semesters
+
+
+ {program.totalCredits} hrs
+
+
+ {program.enrolledStudents}
+
+
+ {program.faculty} faculty
+
+
+ PKR {(program.tuitionFee / 1000).toFixed(0)}K
+
+ handleToggleStatus(program.id)}
+ sx={{ cursor: 'pointer' }}
+ />
+
+
+
+ handleOpenDialog(program)}>
+
+
+
+
+ handleDelete(program.id)}>
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+ {/* Add/Edit Dialog */}
+
+
+ {/* Snackbar */}
+ {snackbar.open && (
+ setSnackbar({ ...snackbar, open: false })}
+ sx={{ position: 'fixed', bottom: 24, right: 24, zIndex: 9999 }}
+ >
+ {snackbar.message}
+
+ )}
+
+
+ );
+};
+
+export default DepartmentManagement;
diff --git a/src/pages/Admin/FinanceManagement.jsx b/src/pages/Admin/FinanceManagement.jsx
new file mode 100644
index 0000000..da23842
--- /dev/null
+++ b/src/pages/Admin/FinanceManagement.jsx
@@ -0,0 +1,538 @@
+import { useMemo, useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ TextField,
+ InputAdornment,
+ Button,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Chip,
+ Stack,
+ Tabs,
+ Tab,
+ Paper,
+ Divider,
+ IconButton,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ alpha,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Search,
+ FilterList,
+ Download,
+ Assessment,
+ TrendingUp,
+ AttachMoney,
+ Warning,
+ Receipt,
+ Save,
+ Edit,
+ Email,
+} from '@mui/icons-material';
+import { useTheme } from '@mui/material/styles';
+import PageHeader from '../../components/Common/PageHeader';
+import PageTransition from '../../components/Common/PageTransition';
+import StatCard from '../../components/Common/StatCard';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const mockLedger = [
+ { id: 'TXN-1001', student: 'Muhammad Asad', rollNo: 'CS-2023-001', amount: 45000, status: 'Paid', date: '2026-01-10', method: 'Card', semester: 'Fall 2025' },
+ { id: 'TXN-1002', student: 'Ayesha Khan', rollNo: 'BBA-2022-045', amount: 2000, status: 'Paid', date: '2026-01-12', method: 'Bank', semester: 'Fall 2025' },
+ { id: 'TXN-1003', student: 'Ali Ahmed', rollNo: 'ENG-2023-112', amount: 45000, status: 'Overdue', date: '2025-12-10', method: 'Voucher', semester: 'Fall 2025' },
+ { id: 'TXN-1004', student: 'Hassan Raza', rollNo: 'CS-2024-089', amount: 45000, status: 'Unpaid', date: '2025-11-25', method: 'Voucher', semester: 'Fall 2025' },
+ { id: 'TXN-1005', student: 'Sara Khan', rollNo: 'BBA-2023-067', amount: 45000, status: 'Paid', date: '2026-01-19', method: 'Card', semester: 'Fall 2025' },
+ { id: 'TXN-1006', student: 'Bilal Ahmad', rollNo: 'CS-2023-045', amount: 45000, status: 'Overdue', date: '2025-12-05', method: 'Bank', semester: 'Fall 2025' },
+ { id: 'TXN-1007', student: 'Fatima Noor', rollNo: 'ENG-2024-023', amount: 45000, status: 'Unpaid', date: '2025-12-01', method: 'Voucher', semester: 'Fall 2025' },
+];
+
+const FinanceManagement = () => {
+ const theme = useTheme();
+ const { showSnackbar } = useSnackbar();
+ const [activeTab, setActiveTab] = useState(0);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [openFeeDialog, setOpenFeeDialog] = useState(false);
+ const [feeStructure, setFeeStructure] = useState({
+ tuitionFee: 45000,
+ labFee: 5000,
+ libraryFee: 2000,
+ sportsFee: 1500,
+ examFee: 3000,
+ });
+
+ // Calculate statistics
+ const stats = useMemo(() => {
+ const totalRevenue = mockLedger
+ .filter(t => t.status === 'Paid')
+ .reduce((sum, t) => sum + t.amount, 0);
+
+ const pendingDues = mockLedger
+ .filter(t => t.status === 'Unpaid' || t.status === 'Overdue')
+ .reduce((sum, t) => sum + t.amount, 0);
+
+ const overdueCount = mockLedger.filter(t => t.status === 'Overdue').length;
+ const unpaidCount = mockLedger.filter(t => t.status === 'Unpaid').length;
+
+ return { totalRevenue, pendingDues, overdueCount, unpaidCount };
+ }, []);
+
+ // Get defaulters (unpaid for > 30 days)
+ const defaulters = useMemo(() => {
+ const today = new Date();
+ return mockLedger.filter(t => {
+ if (t.status === 'Overdue' || t.status === 'Unpaid') {
+ const transactionDate = new Date(t.date);
+ const daysDiff = Math.floor((today - transactionDate) / (1000 * 60 * 60 * 24));
+ return daysDiff > 30;
+ }
+ return false;
+ });
+ }, []);
+
+ const filtered = useMemo(() => {
+ if (!searchQuery) return mockLedger;
+ return mockLedger.filter(
+ (item) =>
+ item.student.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ item.rollNo.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ item.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ item.status.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+ }, [searchQuery]);
+
+ const handleGenerateReport = () => {
+ showSnackbar('Monthly report generated successfully', 'success');
+ };
+
+ const handleSaveFeeStructure = () => {
+ showSnackbar('Fee structure updated successfully', 'success');
+ setOpenFeeDialog(false);
+ };
+
+ const handleSendReminder = (student) => {
+ showSnackbar(`Reminder sent to ${student}`, 'success');
+ };
+
+ return (
+
+
+
+
+ {/* Summary Statistics */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Tabs */}
+
+
+ setActiveTab(val)}>
+
+
+
+
+
+
+
+
+ {/* Tab 1: Transaction Log */}
+ {activeTab === 0 && (
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+ }>
+ Filter
+
+ } onClick={handleGenerateReport}>
+ Generate Report
+
+
+
+
+
+
+
+ Transaction ID
+ Student
+ Roll Number
+ Amount (PKR)
+ Status
+ Date
+ Method
+
+
+
+ {filtered.map((row) => (
+
+ {row.id}
+ {row.student}
+ {row.rollNo}
+ {row.amount.toLocaleString()}
+
+
+
+ {row.date}
+ {row.method}
+
+ ))}
+
+
+
+
+
+
+ )}
+
+ {/* Tab 2: Defaulters List */}
+ {activeTab === 1 && (
+
+
+
+
+
+
+ Students with Unpaid Dues (> 30 Days)
+
+
+ {defaulters.length} students require immediate attention
+
+
+ }>
+ Send Bulk Reminder
+
+
+
+
+
+
+
+ Student Name
+ Roll Number
+ Amount Due (PKR)
+ Due Date
+ Days Overdue
+ Semester
+ Actions
+
+
+
+ {defaulters.map((row) => {
+ const daysOverdue = Math.floor((new Date() - new Date(row.date)) / (1000 * 60 * 60 * 24));
+ return (
+
+ {row.student}
+ {row.rollNo}
+
+
+ {row.amount.toLocaleString()}
+
+
+ {row.date}
+
+
+
+ {row.semester}
+
+ handleSendReminder(row.student)}
+ >
+
+
+
+
+ );
+ })}
+
+
+
+
+
+
+ )}
+
+ {/* Tab 3: Fee Structure */}
+ {activeTab === 2 && (
+
+
+
+
+
+
+ Semester Fee Structure
+
+
+ Configure tuition and other fees per semester
+
+
+ }
+ onClick={() => setOpenFeeDialog(true)}
+ >
+ Edit Structure
+
+
+
+
+
+
+
+
+ Tuition Fee
+
+ ₨ {feeStructure.tuitionFee.toLocaleString()}
+
+
+
+
+ Lab Fee
+
+ ₨ {feeStructure.labFee.toLocaleString()}
+
+
+
+
+ Library Fee
+
+ ₨ {feeStructure.libraryFee.toLocaleString()}
+
+
+
+
+ Sports Fee
+
+ ₨ {feeStructure.sportsFee.toLocaleString()}
+
+
+
+
+ Exam Fee
+
+ ₨ {feeStructure.examFee.toLocaleString()}
+
+
+
+
+ Total Per Semester
+
+ ₨ {Object.values(feeStructure).reduce((a, b) => a + b, 0).toLocaleString()}
+
+
+
+
+
+
+
+
+
+ Fee Structure Guidelines
+
+
+
+ • Tuition fees cover all academic courses and instruction
+
+
+ • Lab fees apply only to students enrolled in lab-based courses
+
+
+ • Library fee provides access to physical and digital resources
+
+
+ • Sports fee covers athletic facilities and intramural programs
+
+
+ • Exam fee includes mid-term and final examinations
+
+
+ Note: Fee structure can be updated at the start of each semester
+
+
+
+
+
+
+
+
+ )}
+
+
+ {/* Edit Fee Structure Dialog */}
+
+
+
+ );
+};
+
+export default FinanceManagement;
diff --git a/src/pages/Admin/GrievanceManagement.jsx b/src/pages/Admin/GrievanceManagement.jsx
new file mode 100644
index 0000000..8f6281e
--- /dev/null
+++ b/src/pages/Admin/GrievanceManagement.jsx
@@ -0,0 +1,669 @@
+import React, { useMemo, useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ List,
+ ListItem,
+ ListItemButton,
+ ListItemText,
+ Chip,
+ Divider,
+ Button,
+ TextField,
+ Stack,
+ Avatar,
+ IconButton,
+ Paper,
+ useTheme,
+ alpha,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Mail,
+ CheckCircle,
+ Warning,
+ ArrowUpward,
+ Send,
+ Person,
+ School,
+ AccessTime,
+ ConfirmationNumber,
+ PendingActions,
+ Category,
+} from '@mui/icons-material';
+import { grievances } from '../../data/dummyData';
+import PageTransition from '../../components/Common/PageTransition';
+import EmptyState from '../../components/Common/EmptyState';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const GrievanceManagement = () => {
+ const theme = useTheme();
+ const { showSnackbar } = useSnackbar();
+ const [selectedId, setSelectedId] = useState(grievances[0]?.id || null);
+ const [replyText, setReplyText] = useState('');
+ const [filter, setFilter] = useState('all');
+
+ const filtered = useMemo(() => {
+ let data = [...grievances];
+ if (filter === 'pending') {
+ data = data.filter((g) => g.status === 'Pending' || g.status === 'In Progress');
+ }
+ if (filter === 'urgent') {
+ data = data.filter((g) => g.priority === 'High');
+ }
+ return data;
+ }, [filter]);
+
+ const selected = filtered.find((g) => g.id === selectedId) || filtered[0];
+ const pendingCount = grievances.filter((g) => g.status === 'Pending' || g.status === 'In Progress').length;
+ const highPriorityCount = grievances.filter((g) => g.priority === 'High' && g.status !== 'Resolved').length;
+
+ const handleResolve = () => {
+ if (!selected) return;
+ showSnackbar(`Marked ${selected.ticketId} as resolved`, 'success');
+ };
+
+ const handleEscalate = () => {
+ if (!selected) return;
+ showSnackbar(`Escalated ${selected.ticketId} to higher authority`, 'warning');
+ };
+
+ const handleReply = () => {
+ if (!replyText.trim()) {
+ showSnackbar('Reply cannot be empty', 'error');
+ return;
+ }
+ showSnackbar('Reply sent. Status updated to In Progress.', 'success');
+ setReplyText('');
+ };
+
+ const getTimeAgo = (dateString) => {
+ const date = new Date(dateString);
+ const diffMs = Date.now() - date.getTime();
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
+ if (diffDays <= 0) return 'Today';
+ if (diffDays === 1) return '1 day ago';
+ return `${diffDays} days ago`;
+ };
+
+ return (
+
+
+ {/* HEADER */}
+
+
+
+
+
+
+
+ Grievance Management
+
+
+ Review, respond, and resolve student grievances
+
+
+
+
+
+ {/* TOP STATS BAR */}
+
+
+
+
+
+
+
+
+
+
+ Total Pending
+
+
+ {pendingCount}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ High Priority Open
+
+
+ {highPriorityCount}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Avg Resolution Time
+
+
+ 2.4d
+
+
+
+
+
+
+
+
+ {/* MASTER-DETAIL SPLIT VIEW */}
+
+ {/* LEFT PANEL - INBOX LIST (35%) */}
+
+
+
+ {/* FILTER CHIPS */}
+
+ setFilter('all')}
+ sx={{
+ fontWeight: 600,
+ ...(filter === 'all' && {
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ color: 'white',
+ }),
+ }}
+ />
+ setFilter('pending')}
+ sx={{
+ fontWeight: 600,
+ ...(filter === 'pending' && {
+ background: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)',
+ color: 'white',
+ }),
+ }}
+ />
+ setFilter('urgent')}
+ sx={{
+ fontWeight: 600,
+ ...(filter === 'urgent' && {
+ background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
+ color: 'white',
+ }),
+ }}
+ />
+
+
+ {filtered.length} {filtered.length === 1 ? 'Ticket' : 'Tickets'}
+
+
+
+
+
+ {/* TICKET LIST */}
+
+ {filtered.length === 0 ? (
+
+ ) : (
+
+ {filtered.map((item) => (
+
+
+ setSelectedId(item.id)}
+ sx={{
+ py: 2,
+ px: 2,
+ '&.Mui-selected': {
+ background: theme.palette.mode === 'dark'
+ ? 'rgba(102,126,234,0.15)'
+ : 'rgba(102,126,234,0.08)',
+ borderLeft: '4px solid',
+ borderColor: 'primary.main',
+ },
+ }}
+ >
+
+ {/* HEADER */}
+
+ {/* CATEGORY ICON */}
+
+
+
+
+
+ {item.subject}
+
+
+ {item.comments?.[0]?.author || 'Student'} • {getTimeAgo(item.submittedAt)}
+
+
+
+
+ {/* CHIPS */}
+
+
+ {item.priority === 'High' && (
+
+ )}
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+
+
+ {/* RIGHT PANEL - DETAIL VIEW (65%) */}
+
+ {!selected ? (
+
+
+
+ ) : (
+
+ {/* STUDENT PROFILE CARD */}
+
+
+
+
+
+
+
+ {selected.comments?.[0]?.author || 'Student Name'}
+
+
+
+
+ Computer Science • {selected.ticketId}
+
+
+
+ {selected.priority === 'High' && (
+ }
+ label="High Priority"
+ color="error"
+ sx={{
+ fontWeight: 'bold',
+ '@keyframes pulse': {
+ '0%': { boxShadow: '0 0 0 0 rgba(211,47,47,0.4)' },
+ '70%': { boxShadow: '0 0 0 10px rgba(211,47,47,0)' },
+ '100%': { boxShadow: '0 0 0 0 rgba(211,47,47,0)' },
+ },
+ animation: 'pulse 2s infinite',
+ }}
+ />
+ )}
+
+
+ {/* TICKET INFO */}
+
+
+ {selected.subject}
+
+
+
+ {selected.description}
+
+
+ }
+ label={selected.category}
+ size="small"
+ sx={{ fontWeight: 600 }}
+ />
+ }
+ label={selected.ticketId}
+ size="small"
+ variant="outlined"
+ sx={{ fontWeight: 600 }}
+ />
+ }
+ label={new Date(selected.submittedAt).toLocaleDateString()}
+ size="small"
+ variant="outlined"
+ sx={{ fontWeight: 600 }}
+ />
+
+
+
+ {/* CONVERSATION THREAD */}
+ {selected.comments?.length > 0 && (
+
+
+ Conversation
+
+
+ {selected.comments.map((comment) => (
+
+
+ {comment.author?.[0] || 'U'}
+
+
+
+ {comment.author} ({comment.role})
+
+
+ {new Date(comment.timestamp).toLocaleString()}
+
+
+ {comment.text}
+
+
+
+ ))}
+
+
+ )}
+
+
+
+
+ {/* REPLY BOX AT BOTTOM */}
+
+ setReplyText(e.target.value)}
+ sx={{
+ mb: 2,
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ />
+
+ {/* ACTION BUTTONS */}
+
+ }
+ onClick={handleReply}
+ sx={{
+ borderRadius: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ boxShadow: '0 4px 12px rgba(102,126,234,0.4)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #5568d3 0%, #6a3f8f 100%)',
+ boxShadow: '0 6px 16px rgba(102,126,234,0.5)',
+ },
+ }}
+ >
+ Send Reply
+
+
+ }
+ color="warning"
+ onClick={handleEscalate}
+ sx={{
+ borderRadius: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ }}
+ >
+ Escalate
+
+ }
+ onClick={handleResolve}
+ sx={{
+ borderRadius: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ background: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
+ boxShadow: '0 4px 12px rgba(17,153,142,0.4)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #0e7d72 0%, #2ed665 100%)',
+ boxShadow: '0 6px 16px rgba(17,153,142,0.5)',
+ },
+ }}
+ >
+ Mark Resolved
+
+
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default GrievanceManagement;
diff --git a/src/pages/Admin/Reports.jsx b/src/pages/Admin/Reports.jsx
index b9ca525..0e3ea06 100644
--- a/src/pages/Admin/Reports.jsx
+++ b/src/pages/Admin/Reports.jsx
@@ -28,6 +28,17 @@ import {
Share,
} from '@mui/icons-material';
import { useTheme } from '@mui/material/styles';
+import {
+ LineChart,
+ Line,
+ BarChart,
+ Bar,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip,
+ ResponsiveContainer,
+} from 'recharts';
import PageHeader from '../../components/Common/PageHeader';
import { pageTransition } from '../../utils/animations';
@@ -52,30 +63,22 @@ const AdminReports = () => {
{ label: 'Course Completion', value: '94%', change: '+3.2%', color: theme.palette.warning.main },
];
- const enrollmentData = {
- labels: ['CS', 'Business', 'Engineering', 'Medical', 'Arts'],
- datasets: [
- {
- label: 'Students',
- data: [852, 743, 621, 431, 200],
- backgroundColor: theme.palette.primary.main,
- },
- ],
- };
+ const enrollmentData = [
+ { department: 'CS', students: 852 },
+ { department: 'Business', students: 743 },
+ { department: 'Engineering', students: 621 },
+ { department: 'Medical', students: 431 },
+ { department: 'Arts', students: 200 },
+ ];
- const revenueData = {
- labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
- datasets: [
- {
- label: 'Revenue (Million PKR)',
- data: [6.5, 7.2, 6.8, 7.5, 8.1, 8.5],
- borderColor: theme.palette.success.main,
- backgroundColor: alpha(theme.palette.success.main, 0.1),
- fill: true,
- tension: 0.4,
- },
- ],
- };
+ const revenueData = [
+ { month: 'Jan', revenue: 6.5 },
+ { month: 'Feb', revenue: 7.2 },
+ { month: 'Mar', revenue: 6.8 },
+ { month: 'Apr', revenue: 7.5 },
+ { month: 'May', revenue: 8.1 },
+ { month: 'Jun', revenue: 8.5 },
+ ];
return (
@@ -188,14 +191,45 @@ const AdminReports = () => {
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -207,14 +241,43 @@ const AdminReports = () => {
Department Enrollment
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/Admin/Settings.jsx b/src/pages/Admin/Settings.jsx
new file mode 100644
index 0000000..a423776
--- /dev/null
+++ b/src/pages/Admin/Settings.jsx
@@ -0,0 +1,446 @@
+import { useState, useRef } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ TextField,
+ Switch,
+ FormControlLabel,
+ Button,
+ Divider,
+ Stack,
+ Avatar,
+ IconButton,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Chip,
+ Paper,
+ alpha,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Upload,
+ Delete,
+ Image as ImageIcon,
+ Save,
+ Notifications,
+ Security,
+ School,
+} from '@mui/icons-material';
+import { useTheme } from '@mui/material/styles';
+import PageHeader from '../../components/Common/PageHeader';
+import PageTransition from '../../components/Common/PageTransition';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const Settings = () => {
+ const theme = useTheme();
+ const { showSnackbar } = useSnackbar();
+ const fileInputRef = useRef(null);
+ const [logoPreview, setLogoPreview] = useState(null);
+ const [settings, setSettings] = useState({
+ campusName: 'Project Nexus University',
+ campusAddress: 'Karachi, Pakistan',
+ campusEmail: 'info@nexus.edu.pk',
+ campusPhone: '+92 21 1234567',
+ passwordMinLength: 8,
+ requireSpecialChar: true,
+ requireUppercase: true,
+ requireNumber: true,
+ requireTwoFactor: false,
+ sessionTimeout: 30,
+ maxLoginAttempts: 5,
+ emailNotifications: true,
+ smsNotifications: false,
+ pushNotifications: true,
+ emailTemplate: 'Dear {name}, your request has been received.',
+ smsTemplate: 'Hi {name}, {message}',
+ notifyOnFeePayment: true,
+ notifyOnAttendance: true,
+ notifyOnGrades: true,
+ });
+
+ const handleSave = () => {
+ showSnackbar('Settings saved successfully', 'success');
+ };
+
+ const handleLogoUpload = (event) => {
+ const file = event.target.files[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ setLogoPreview(reader.result);
+ showSnackbar('Logo uploaded successfully', 'success');
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+
+ const handleRemoveLogo = () => {
+ setLogoPreview(null);
+ showSnackbar('Logo removed', 'info');
+ };
+
+ return (
+
+
+
+
+
+ {/* General Settings */}
+
+
+
+
+
+
+ General Settings
+
+
+
+ {/* Logo Upload */}
+
+
+ University Logo
+
+
+
+ {logoPreview ? (
+
+ ) : (
+
+ )}
+
+
+
+ }
+ onClick={() => fileInputRef.current?.click()}
+ >
+ Upload Logo
+
+ {logoPreview && (
+ }
+ onClick={handleRemoveLogo}
+ >
+ Remove
+
+ )}
+
+
+
+
+
+
+ setSettings({ ...settings, campusName: e.target.value })}
+ fullWidth
+ />
+ setSettings({ ...settings, campusAddress: e.target.value })}
+ fullWidth
+ />
+ setSettings({ ...settings, campusEmail: e.target.value })}
+ fullWidth
+ />
+ setSettings({ ...settings, campusPhone: e.target.value })}
+ fullWidth
+ />
+ }>
+ Save General Settings
+
+
+
+
+
+
+ {/* Security Settings */}
+
+
+
+
+
+
+ Security Settings
+
+
+
+
+ Password Policy
+
+ setSettings({ ...settings, passwordMinLength: parseInt(e.target.value, 10) })}
+ fullWidth
+ helperText="Recommended: 8 or more characters"
+ />
+ setSettings({ ...settings, requireSpecialChar: e.target.checked })}
+ />
+ }
+ label="Require Special Characters (!@#$%)"
+ />
+ setSettings({ ...settings, requireUppercase: e.target.checked })}
+ />
+ }
+ label="Require Uppercase Letters"
+ />
+ setSettings({ ...settings, requireNumber: e.target.checked })}
+ />
+ }
+ label="Require Numbers"
+ />
+
+
+
+
+ Session Management
+
+
+ Session Timeout
+
+
+ setSettings({ ...settings, maxLoginAttempts: parseInt(e.target.value, 10) })}
+ fullWidth
+ helperText="Lock account after this many failed attempts"
+ />
+
+
+
+ setSettings({ ...settings, requireTwoFactor: e.target.checked })}
+ />
+ }
+ label="Enable Two-Factor Authentication"
+ />
+ }>
+ Save Security Settings
+
+
+
+
+
+
+ {/* Notification Settings */}
+
+
+
+
+
+
+ Notification Settings
+
+
+
+
+
+
+ Notification Channels
+
+ setSettings({ ...settings, emailNotifications: e.target.checked })}
+ />
+ }
+ label="Enable Email Notifications"
+ />
+ setSettings({ ...settings, smsNotifications: e.target.checked })}
+ />
+ }
+ label="Enable SMS Notifications"
+ />
+ setSettings({ ...settings, pushNotifications: e.target.checked })}
+ />
+ }
+ label="Enable Push Notifications"
+ />
+
+
+
+
+ Automatic Alerts
+
+ setSettings({ ...settings, notifyOnFeePayment: e.target.checked })}
+ />
+ }
+ label="Notify on Fee Payments"
+ />
+ setSettings({ ...settings, notifyOnAttendance: e.target.checked })}
+ />
+ }
+ label="Notify on Attendance Marks"
+ />
+ setSettings({ ...settings, notifyOnGrades: e.target.checked })}
+ />
+ }
+ label="Notify on Grade Uploads"
+ />
+
+
+
+
+
+
+ Message Templates
+
+ setSettings({ ...settings, emailTemplate: e.target.value })}
+ fullWidth
+ helperText="Use {name} for student name, {message} for content"
+ />
+ setSettings({ ...settings, smsTemplate: e.target.value })}
+ fullWidth
+ helperText="Keep SMS messages short (160 characters)"
+ />
+
+
+
+ Available Variables:
+ • {'{name}'} - Student/User name
+ • {'{rollNo}'} - Roll number
+ • {'{message}'} - Dynamic message content
+ • {'{date}'} - Current date
+ • {'{amount}'} - Fee amount
+
+
+
+
+
+
+ }>
+ Save Notification Settings
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Settings;
diff --git a/src/pages/Alumni/AlumniEvents.jsx b/src/pages/Alumni/AlumniEvents.jsx
new file mode 100644
index 0000000..ae613bb
--- /dev/null
+++ b/src/pages/Alumni/AlumniEvents.jsx
@@ -0,0 +1,439 @@
+import { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ CardMedia,
+ Typography,
+ Button,
+ Chip,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ TextField,
+ LinearProgress,
+ IconButton,
+ Stack,
+ Divider,
+ Avatar,
+ AvatarGroup,
+ Alert,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Event,
+ LocationOn,
+ AccessTime,
+ People,
+ CheckCircle,
+ AttachMoney,
+ Close,
+ CalendarMonth,
+ PersonAdd,
+ EmojiEvents,
+ VideoCall,
+ BusinessCenter,
+ School,
+} from '@mui/icons-material';
+import { alumniEvents, registerForEvent } from '../../data/dummyData';
+import PageHeader from '../../components/Common/PageHeader';
+import PageTransition from '../../components/Common/PageTransition';
+import EmptyState from '../../components/Common/EmptyState';
+import { CardSkeleton } from '../../components/Common/LoadingSkeleton';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const AlumniEvents = () => {
+ const { showSnackbar } = useSnackbar();
+ const [loading, setLoading] = useState(true);
+ const [selectedEvent, setSelectedEvent] = useState(null);
+ const [registrationDialog, setRegistrationDialog] = useState(false);
+ const [registrationData, setRegistrationData] = useState({
+ fullName: '',
+ email: '',
+ phone: '',
+ graduationYear: '',
+ });
+
+ useEffect(() => {
+ const timer = setTimeout(() => setLoading(false), 1000);
+ return () => clearTimeout(timer);
+ }, []);
+
+ const handleRegister = (event) => {
+ setSelectedEvent(event);
+ setRegistrationDialog(true);
+ };
+
+ const handleSubmitRegistration = () => {
+ if (!registrationData.fullName || !registrationData.email) {
+ showSnackbar('Please fill all required fields', 'error');
+ return;
+ }
+
+ const result = registerForEvent(selectedEvent.id);
+ if (result.success) {
+ showSnackbar(result.message, 'success');
+ setRegistrationDialog(false);
+ setRegistrationData({ fullName: '', email: '', phone: '', graduationYear: '' });
+ } else {
+ showSnackbar(result.message, 'error');
+ }
+ };
+
+ const upcomingEvents = alumniEvents.filter((e) => e.status === 'Upcoming');
+ const pastEvents = alumniEvents.filter((e) => e.status === 'Completed');
+
+ const getEventIcon = (type) => {
+ switch (type) {
+ case 'Reunion':
+ return ;
+ case 'Career Fair':
+ return ;
+ case 'Workshop':
+ return ;
+ case 'Webinar':
+ return ;
+ case 'Sports':
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ const getEventColor = (type) => {
+ switch (type) {
+ case 'Reunion':
+ return 'primary';
+ case 'Career Fair':
+ return 'success';
+ case 'Workshop':
+ return 'warning';
+ case 'Webinar':
+ return 'info';
+ case 'Sports':
+ return 'error';
+ default:
+ return 'default';
+ }
+ };
+
+ if (loading) {
+ return (
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ {/* Stats */}
+
+
+
+
+
+ {upcomingEvents.length}
+
+
+ Upcoming Events
+
+
+
+
+
+
+
+
+ {alumniEvents.reduce((sum, e) => sum + e.registered, 0)}
+
+
+ Total Registrations
+
+
+
+
+
+
+
+
+ {pastEvents.length}
+
+
+ Past Events
+
+
+
+
+
+
+ {/* Upcoming Events */}
+
+ Upcoming Events
+
+ {upcomingEvents.length === 0 ? (
+
+ ) : (
+
+ {upcomingEvents.map((event) => {
+ const spotsLeft = event.capacity - event.registered;
+ const fillPercentage = (event.registered / event.capacity) * 100;
+
+ return (
+
+
+
+
+ {/* Header */}
+
+
+ {event.fee > 0 ? (
+ }
+ label={`PKR ${event.fee.toLocaleString()}`}
+ variant="outlined"
+ size="small"
+ />
+ ) : (
+
+ )}
+
+
+ {/* Title and Description */}
+
+ {event.title}
+
+
+ {event.description}
+
+
+
+
+ {/* Event Details */}
+
+
+
+
+ {new Date(event.date).toLocaleDateString('en-US', {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ })}
+
+
+
+
+ {event.time}
+
+
+
+ {event.venue}
+
+
+
+ {/* Speakers */}
+ {event.speakers && event.speakers.length > 0 && (
+
+
+ Featured Speakers:
+
+
+ {event.speakers.map((speaker, index) => (
+
+ ))}
+
+
+ )}
+
+ {/* Registration Progress */}
+
+
+
+ {event.registered} / {event.capacity} Registered
+
+
+ {spotsLeft} spots left
+
+
+ 90 ? 'error' : 'primary'}
+ />
+
+
+ {/* Register Button */}
+ }
+ onClick={() => handleRegister(event)}
+ disabled={spotsLeft === 0}
+ >
+ {spotsLeft === 0 ? 'Event Full' : 'Register Now'}
+
+
+
+
+ );
+ })}
+
+ )}
+
+ {/* Past Events */}
+ {pastEvents.length > 0 && (
+ <>
+
+ Past Events
+
+
+ {pastEvents.map((event) => (
+
+
+
+
+
+ } label="Completed" color="success" size="small" />
+
+
+ {event.title}
+
+
+ {new Date(event.date).toLocaleDateString()} • {event.registered} attendees
+
+
+
+
+ ))}
+
+ >
+ )}
+
+ {/* Registration Dialog */}
+
+
+
+ );
+};
+
+export default AlumniEvents;
diff --git a/src/pages/Alumni/AlumniNetwork.jsx b/src/pages/Alumni/AlumniNetwork.jsx
new file mode 100644
index 0000000..dc256d5
--- /dev/null
+++ b/src/pages/Alumni/AlumniNetwork.jsx
@@ -0,0 +1,361 @@
+import { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Avatar,
+ Button,
+ Chip,
+ TextField,
+ InputAdornment,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ IconButton,
+ Tooltip,
+ Stack,
+ Divider,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Search,
+ LinkedIn,
+ Business,
+ LocationOn,
+ School,
+ EmojiEvents,
+ FilterList,
+ PersonAdd,
+ Language,
+ Email,
+} from '@mui/icons-material';
+import { alumni, connectWithAlumni } from '../../data/dummyData';
+import PageHeader from '../../components/Common/PageHeader';
+import PageTransition from '../../components/Common/PageTransition';
+import EmptyState from '../../components/Common/EmptyState';
+import { CourseCardSkeleton } from '../../components/Common/LoadingSkeleton';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const AlumniNetwork = () => {
+ const { showSnackbar } = useSnackbar();
+ const [loading, setLoading] = useState(true);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [selectedYear, setSelectedYear] = useState('all');
+ const [selectedMajor, setSelectedMajor] = useState('all');
+ const [filteredAlumni, setFilteredAlumni] = useState([]);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setLoading(false), 1000);
+ return () => clearTimeout(timer);
+ }, []);
+
+ useEffect(() => {
+ let filtered = [...alumni];
+
+ // Filter by search query
+ if (searchQuery) {
+ filtered = filtered.filter(
+ (alum) =>
+ alum.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ alum.currentCompany.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ alum.position.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+ }
+
+ // Filter by graduation year
+ if (selectedYear !== 'all') {
+ filtered = filtered.filter((alum) => alum.graduationYear === parseInt(selectedYear));
+ }
+
+ // Filter by major
+ if (selectedMajor !== 'all') {
+ filtered = filtered.filter((alum) => alum.degree.includes(selectedMajor));
+ }
+
+ setFilteredAlumni(filtered);
+ }, [searchQuery, selectedYear, selectedMajor]);
+
+ const handleConnect = (alumnus) => {
+ const result = connectWithAlumni(alumnus.id);
+ if (result.success) {
+ showSnackbar(result.message, 'success');
+ // Open LinkedIn in new tab
+ if (result.linkedIn) {
+ window.open(result.linkedIn, '_blank');
+ }
+ } else {
+ showSnackbar(result.message, 'error');
+ }
+ };
+
+ const graduationYears = [...new Set(alumni.map((a) => a.graduationYear))].sort((a, b) => b - a);
+ const majors = ['Computer Science', 'Software Engineering', 'Information Technology'];
+
+ if (loading) {
+ return (
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ {/* Filters */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Graduation Year
+
+
+
+
+
+ Major
+
+
+
+
+
+
+
+ {/* Stats */}
+
+
+
+
+
+ {alumni.length}
+
+
+ Total Alumni
+
+
+
+
+
+
+
+
+ {new Set(alumni.map((a) => a.currentCompany)).size}
+
+
+ Companies
+
+
+
+
+
+
+
+
+ {new Set(alumni.map((a) => a.location)).size}
+
+
+ Locations
+
+
+
+
+
+
+
+
+ {filteredAlumni.length}
+
+
+ Showing
+
+
+
+
+
+
+ {/* Alumni Grid */}
+ {filteredAlumni.length === 0 ? (
+
+ ) : (
+
+ {filteredAlumni.map((alumnus) => (
+
+
+
+ {/* Header with Avatar and Company Logo */}
+
+
+
+
+
+ {alumnus.currentCompany}
+
+
+
+
+ {/* Name and Position */}
+
+ {alumnus.name}
+
+
+ {alumnus.position}
+
+
+
+
+ {/* Details */}
+
+
+
+
+ {alumnus.degree} • Class of {alumnus.graduationYear}
+
+
+
+
+ {alumnus.location}
+
+
+
+ {/* Expertise Tags */}
+
+ {alumnus.expertise.slice(0, 3).map((skill, index) => (
+
+ ))}
+
+
+ {/* Achievements */}
+ {alumnus.achievements.length > 0 && (
+
+
+
+
+ Achievements
+
+
+ {alumnus.achievements.slice(0, 2).map((achievement, index) => (
+
+ • {achievement}
+
+ ))}
+
+ )}
+
+ {/* Actions */}
+
+ }
+ onClick={() => handleConnect(alumnus)}
+ >
+ Connect
+
+
+ (window.location.href = `mailto:${alumnus.email}`)}
+ >
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ );
+};
+
+export default AlumniNetwork;
diff --git a/src/pages/Alumni/JobBoard.jsx b/src/pages/Alumni/JobBoard.jsx
new file mode 100644
index 0000000..ad6ac22
--- /dev/null
+++ b/src/pages/Alumni/JobBoard.jsx
@@ -0,0 +1,360 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Button,
+ TextField,
+ InputAdornment,
+ Chip,
+ Avatar,
+ Stack,
+ Paper,
+ Divider,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ Work as WorkIcon,
+ LocationOn as LocationIcon,
+ Business as BusinessIcon,
+ Schedule as ScheduleIcon,
+ TrendingUp as TrendingUpIcon,
+ AttachMoney as MoneyIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const JobBoard = () => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterType, setFilterType] = useState('all');
+ const [filterLocation, setFilterLocation] = useState('all');
+
+ // Mock job listings
+ const jobs = [
+ {
+ id: 1,
+ title: 'Senior Software Engineer',
+ company: 'Tech Solutions Inc.',
+ logo: 'https://i.pravatar.cc/150?img=1',
+ location: 'Karachi, Pakistan',
+ type: 'Full-time',
+ salary: '200K - 300K PKR',
+ postedBy: 'Alumni Relations',
+ postedDate: '2 days ago',
+ description: 'Looking for experienced software engineers to join our growing team.',
+ requirements: ['5+ years experience', 'React/Node.js', 'Team leadership'],
+ applicants: 12,
+ },
+ {
+ id: 2,
+ title: 'Data Scientist',
+ company: 'Analytics Pro',
+ logo: 'https://i.pravatar.cc/150?img=2',
+ location: 'Lahore, Pakistan',
+ type: 'Full-time',
+ salary: '180K - 250K PKR',
+ postedBy: 'Alumni Relations',
+ postedDate: '5 days ago',
+ description: 'Join our data team to work on cutting-edge ML projects.',
+ requirements: ['Python/R', 'Machine Learning', 'Statistics'],
+ applicants: 8,
+ },
+ {
+ id: 3,
+ title: 'UI/UX Designer',
+ company: 'Creative Studio',
+ logo: 'https://i.pravatar.cc/150?img=3',
+ location: 'Remote',
+ type: 'Contract',
+ salary: '150K - 200K PKR',
+ postedBy: 'Alumni Relations',
+ postedDate: '1 week ago',
+ description: 'Design beautiful and intuitive user experiences for web and mobile.',
+ requirements: ['Figma/Adobe XD', 'Portfolio', '3+ years experience'],
+ applicants: 15,
+ },
+ {
+ id: 4,
+ title: 'Product Manager',
+ company: 'Innovation Labs',
+ logo: 'https://i.pravatar.cc/150?img=4',
+ location: 'Islamabad, Pakistan',
+ type: 'Full-time',
+ salary: '250K - 350K PKR',
+ postedBy: 'Alumni Relations',
+ postedDate: '3 days ago',
+ description: 'Lead product strategy and development for our flagship products.',
+ requirements: ['Product Management', 'Agile/Scrum', 'Stakeholder Management'],
+ applicants: 20,
+ },
+ {
+ id: 5,
+ title: 'Backend Developer',
+ company: 'Cloud Systems',
+ logo: 'https://i.pravatar.cc/150?img=5',
+ location: 'Karachi, Pakistan',
+ type: 'Full-time',
+ salary: '150K - 220K PKR',
+ postedBy: 'Alumni Relations',
+ postedDate: '4 days ago',
+ description: 'Build scalable backend systems using modern cloud technologies.',
+ requirements: ['Node.js/Python', 'AWS/Azure', 'Microservices'],
+ applicants: 10,
+ },
+ {
+ id: 6,
+ title: 'Marketing Manager',
+ company: 'Brand Builders',
+ logo: 'https://i.pravatar.cc/150?img=6',
+ location: 'Lahore, Pakistan',
+ type: 'Full-time',
+ salary: '120K - 180K PKR',
+ postedBy: 'Alumni Relations',
+ postedDate: '1 week ago',
+ description: 'Drive marketing strategy and brand awareness initiatives.',
+ requirements: ['Digital Marketing', 'SEO/SEM', 'Analytics'],
+ applicants: 18,
+ },
+ ];
+
+ const stats = [
+ { label: 'Total Jobs', value: '142', change: '+12 this week', trend: 'up', color: 'primary', icon: WorkIcon },
+ { label: 'Companies', value: '56', change: '+5 new', trend: 'up', color: 'success', icon: BusinessIcon },
+ { label: 'Applications', value: '83', change: 'This month', trend: 'up', color: 'info', icon: TrendingUpIcon },
+ ];
+
+ const filteredJobs = jobs.filter(job => {
+ const matchesSearch = job.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ job.company.toLowerCase().includes(searchQuery.toLowerCase());
+ const matchesType = filterType === 'all' || job.type === filterType;
+ const matchesLocation = filterLocation === 'all' || job.location.toLowerCase().includes(filterLocation.toLowerCase());
+ return matchesSearch && matchesType && matchesLocation;
+ });
+
+ const getTypeColor = (type) => {
+ switch (type) {
+ case 'Full-time':
+ return 'success';
+ case 'Part-time':
+ return 'warning';
+ case 'Contract':
+ return 'info';
+ case 'Internship':
+ return 'secondary';
+ default:
+ return 'default';
+ }
+ };
+
+ return (
+
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Filters Card */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Job Type
+
+
+
+
+
+ Location
+
+
+
+
+
+
+
+ {/* Job Listings */}
+
+ {filteredJobs.map((job) => (
+
+
+
+
+
+
+
+
+
+ {job.title}
+
+
+ }
+ label={job.company}
+ size="small"
+ variant="outlined"
+ />
+ }
+ label={job.location}
+ size="small"
+ variant="outlined"
+ />
+
+
+
+
+
+
+ {job.salary}
+
+
+
+
+
+ {job.postedDate}
+
+
+
+
+
+
+
+ {job.description}
+
+
+
+
+ Requirements:
+
+
+ {job.requirements.map((req, idx) => (
+
+ ))}
+
+
+
+
+
+
+
+ {job.applicants}
+
+
+ Applicants
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ {filteredJobs.length === 0 && (
+
+
+
+
+ No jobs found matching your criteria
+
+
+ Try adjusting your filters or search terms
+
+
+
+ )}
+
+
+ );
+};
+
+export default JobBoard;
diff --git a/src/pages/Alumni/Mentorship.jsx b/src/pages/Alumni/Mentorship.jsx
new file mode 100644
index 0000000..ca15200
--- /dev/null
+++ b/src/pages/Alumni/Mentorship.jsx
@@ -0,0 +1,374 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Button,
+ TextField,
+ InputAdornment,
+ Chip,
+ Avatar,
+ Stack,
+ Paper,
+ LinearProgress,
+ IconButton,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ School as SchoolIcon,
+ EmojiEvents as TrophyIcon,
+ Groups as GroupsIcon,
+ PersonAdd as PersonAddIcon,
+ LinkedIn as LinkedInIcon,
+ Email as EmailIcon,
+ Star as StarIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const Mentorship = () => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterExpertise, setFilterExpertise] = useState('all');
+
+ // Mock mentors data
+ const mentors = [
+ {
+ id: 1,
+ name: 'Dr. Ahmed Khan',
+ photo: 'https://i.pravatar.cc/150?img=11',
+ expertise: 'Software Engineering',
+ designation: 'Senior Architect',
+ company: 'Google',
+ graduationYear: 2010,
+ rating: 4.9,
+ mentees: 15,
+ sessionsCompleted: 45,
+ specializations: ['System Design', 'Cloud Architecture', 'Leadership'],
+ availability: 'Available',
+ bio: 'Passionate about helping young engineers grow their careers in tech.',
+ },
+ {
+ id: 2,
+ name: 'Sara Ahmed',
+ photo: 'https://i.pravatar.cc/150?img=12',
+ expertise: 'Data Science',
+ designation: 'Lead Data Scientist',
+ company: 'Meta',
+ graduationYear: 2012,
+ rating: 4.8,
+ mentees: 12,
+ sessionsCompleted: 38,
+ specializations: ['Machine Learning', 'Deep Learning', 'Analytics'],
+ availability: 'Available',
+ bio: 'Helping aspiring data scientists navigate their career paths.',
+ },
+ {
+ id: 3,
+ name: 'Hassan Ali',
+ photo: 'https://i.pravatar.cc/150?img=13',
+ expertise: 'Business Management',
+ designation: 'CEO',
+ company: 'StartupHub',
+ graduationYear: 2008,
+ rating: 5.0,
+ mentees: 20,
+ sessionsCompleted: 62,
+ specializations: ['Entrepreneurship', 'Strategy', 'Leadership'],
+ availability: 'Limited',
+ bio: 'Entrepreneur and mentor helping build the next generation of leaders.',
+ },
+ {
+ id: 4,
+ name: 'Fatima Zain',
+ photo: 'https://i.pravatar.cc/150?img=14',
+ expertise: 'Product Management',
+ designation: 'Senior PM',
+ company: 'Microsoft',
+ graduationYear: 2013,
+ rating: 4.7,
+ mentees: 10,
+ sessionsCompleted: 30,
+ specializations: ['Product Strategy', 'User Research', 'Agile'],
+ availability: 'Available',
+ bio: 'Product leader passionate about creating impactful products.',
+ },
+ {
+ id: 5,
+ name: 'Omar Siddiqui',
+ photo: 'https://i.pravatar.cc/150?img=15',
+ expertise: 'Digital Marketing',
+ designation: 'Marketing Director',
+ company: 'Amazon',
+ graduationYear: 2011,
+ rating: 4.6,
+ mentees: 8,
+ sessionsCompleted: 25,
+ specializations: ['SEO', 'Content Strategy', 'Growth Hacking'],
+ availability: 'Available',
+ bio: 'Digital marketing expert with 12+ years of experience.',
+ },
+ {
+ id: 6,
+ name: 'Ayesha Malik',
+ photo: 'https://i.pravatar.cc/150?img=16',
+ expertise: 'UI/UX Design',
+ designation: 'Design Lead',
+ company: 'Adobe',
+ graduationYear: 2014,
+ rating: 4.9,
+ mentees: 14,
+ sessionsCompleted: 42,
+ specializations: ['User Experience', 'Design Systems', 'Prototyping'],
+ availability: 'Available',
+ bio: 'Design thinking advocate helping designers grow their craft.',
+ },
+ ];
+
+ const stats = [
+ { label: 'Active Mentors', value: '48', change: '+6 this month', trend: 'up', color: 'primary', icon: SchoolIcon },
+ { label: 'Mentees', value: '156', change: '+23 new', trend: 'up', color: 'success', icon: GroupsIcon },
+ { label: 'Sessions', value: '324', change: 'This year', trend: 'up', color: 'info', icon: TrophyIcon },
+ ];
+
+ const filteredMentors = mentors.filter(mentor => {
+ const matchesSearch = mentor.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ mentor.expertise.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ mentor.company.toLowerCase().includes(searchQuery.toLowerCase());
+ const matchesExpertise = filterExpertise === 'all' || mentor.expertise === filterExpertise;
+ return matchesSearch && matchesExpertise;
+ });
+
+ const getAvailabilityColor = (availability) => {
+ switch (availability) {
+ case 'Available':
+ return 'success';
+ case 'Limited':
+ return 'warning';
+ case 'Unavailable':
+ return 'error';
+ default:
+ return 'default';
+ }
+ };
+
+ return (
+
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Filters Card */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Expertise Area
+
+
+
+
+
+
+
+ {/* Mentors Grid */}
+
+ {filteredMentors.map((mentor) => (
+
+
+
+ {/* Header with Avatar */}
+
+
+
+ {mentor.name}
+
+
+ {mentor.designation}
+
+
+ {mentor.company} • Class of {mentor.graduationYear}
+
+
+ {/* Rating */}
+
+
+
+ {mentor.rating}
+
+
+ ({mentor.sessionsCompleted} sessions)
+
+
+
+ {/* Availability */}
+
+
+
+ {/* Expertise Badge */}
+
+
+ Expertise
+
+
+ {mentor.expertise}
+
+
+
+ {/* Bio */}
+
+ {mentor.bio}
+
+
+ {/* Specializations */}
+
+
+ Specializations:
+
+
+ {mentor.specializations.map((spec, idx) => (
+
+ ))}
+
+
+
+ {/* Stats */}
+
+
+
+
+ {mentor.mentees}
+
+
+ Mentees
+
+
+
+
+
+
+ {mentor.sessionsCompleted}
+
+
+ Sessions
+
+
+
+
+
+ {/* Actions */}
+
+ }
+ disabled={mentor.availability === 'Unavailable'}
+ >
+ Request Mentorship
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ {filteredMentors.length === 0 && (
+
+
+
+
+ No mentors found matching your criteria
+
+
+ Try adjusting your filters or search terms
+
+
+
+ )}
+
+
+ );
+};
+
+export default Mentorship;
diff --git a/src/pages/Alumni/SuccessStories.jsx b/src/pages/Alumni/SuccessStories.jsx
new file mode 100644
index 0000000..32a44a7
--- /dev/null
+++ b/src/pages/Alumni/SuccessStories.jsx
@@ -0,0 +1,366 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Button,
+ TextField,
+ InputAdornment,
+ Chip,
+ Avatar,
+ Stack,
+ Paper,
+ IconButton,
+ CardMedia,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ EmojiEvents as TrophyIcon,
+ TrendingUp as TrendingUpIcon,
+ Star as StarIcon,
+ Share as ShareIcon,
+ Favorite as FavoriteIcon,
+ Comment as CommentIcon,
+ LinkedIn as LinkedInIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const SuccessStories = () => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterCategory, setFilterCategory] = useState('all');
+
+ // Mock success stories
+ const stories = [
+ {
+ id: 1,
+ name: 'Ali Hassan',
+ photo: 'https://i.pravatar.cc/150?img=21',
+ graduationYear: 2018,
+ program: 'BS Computer Science',
+ achievement: 'Founded Tech Startup Valued at $10M',
+ company: 'InnovateTech',
+ designation: 'CEO & Founder',
+ category: 'Entrepreneurship',
+ image: 'https://images.unsplash.com/photo-1559136555-9303baea8ebd?w=800',
+ story: 'Started InnovateTech from dorm room, now serving 10,000+ clients across Pakistan. The entrepreneurial skills learned at university helped me take the leap.',
+ likes: 245,
+ comments: 32,
+ shares: 18,
+ tags: ['Startup', 'Technology', 'Leadership'],
+ },
+ {
+ id: 2,
+ name: 'Sana Ahmed',
+ photo: 'https://i.pravatar.cc/150?img=22',
+ graduationYear: 2015,
+ program: 'BS Software Engineering',
+ achievement: 'Senior Engineering Manager at Google',
+ company: 'Google',
+ designation: 'Senior Engineering Manager',
+ category: 'Technology',
+ image: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800',
+ story: 'Joined Google as SDE-1 in 2016, promoted to manager in 2019. Now leading a team of 25 engineers working on Google Cloud Platform.',
+ likes: 312,
+ comments: 45,
+ shares: 28,
+ tags: ['Google', 'Leadership', 'Cloud'],
+ },
+ {
+ id: 3,
+ name: 'Omar Khan',
+ photo: 'https://i.pravatar.cc/150?img=23',
+ graduationYear: 2012,
+ program: 'MBA',
+ achievement: 'Youngest VP at Fortune 500 Company',
+ company: 'Unilever',
+ designation: 'Vice President - Marketing',
+ category: 'Business',
+ image: 'https://images.unsplash.com/photo-1553877522-43269d4ea984?w=800',
+ story: 'Became the youngest VP at Unilever Pakistan at age 32. Leading marketing strategies for 15+ brands with $200M annual revenue.',
+ likes: 198,
+ comments: 28,
+ shares: 15,
+ tags: ['Marketing', 'Leadership', 'Corporate'],
+ },
+ {
+ id: 4,
+ name: 'Fatima Zahra',
+ photo: 'https://i.pravatar.cc/150?img=24',
+ graduationYear: 2016,
+ program: 'BS Data Science',
+ achievement: 'Published Research in Nature Journal',
+ company: 'MIT',
+ designation: 'Research Scientist',
+ category: 'Research',
+ image: 'https://images.unsplash.com/photo-1532094349884-543bc11b234d?w=800',
+ story: 'Published groundbreaking research on AI in healthcare. Now pursuing PhD at MIT while collaborating with leading hospitals.',
+ likes: 267,
+ comments: 38,
+ shares: 42,
+ tags: ['Research', 'AI', 'Healthcare'],
+ },
+ {
+ id: 5,
+ name: 'Ahmed Raza',
+ photo: 'https://i.pravatar.cc/150?img=25',
+ graduationYear: 2014,
+ program: 'BS Civil Engineering',
+ achievement: 'Led $500M Infrastructure Project',
+ company: 'FWO',
+ designation: 'Project Director',
+ category: 'Engineering',
+ image: 'https://images.unsplash.com/photo-1541888946425-d81bb19240f5?w=800',
+ story: 'Managed the construction of 100km highway connecting major cities. Project completed ahead of schedule and under budget.',
+ likes: 189,
+ comments: 21,
+ shares: 12,
+ tags: ['Infrastructure', 'Engineering', 'Leadership'],
+ },
+ {
+ id: 6,
+ name: 'Ayesha Malik',
+ photo: 'https://i.pravatar.cc/150?img=26',
+ graduationYear: 2017,
+ program: 'BS Business Administration',
+ achievement: 'Built NGO Impacting 50,000 Lives',
+ company: 'EduCare Foundation',
+ designation: 'Founder & Director',
+ category: 'Social Impact',
+ image: 'https://images.unsplash.com/photo-1488521787991-ed7bbaae773c?w=800',
+ story: 'Founded NGO providing free education to underprivileged children. Now operating 20 schools across rural Pakistan.',
+ likes: 421,
+ comments: 67,
+ shares: 89,
+ tags: ['Education', 'NGO', 'Social Impact'],
+ },
+ ];
+
+ const stats = [
+ { label: 'Success Stories', value: '78', change: '+12 new', trend: 'up', color: 'primary', icon: TrophyIcon },
+ { label: 'Total Views', value: '12.5K', change: '+2.3K this month', trend: 'up', color: 'success', icon: TrendingUpIcon },
+ { label: 'Inspirations', value: '1.2K', change: 'Total likes', trend: 'up', color: 'info', icon: StarIcon },
+ ];
+
+ const filteredStories = stories.filter(story => {
+ const matchesSearch = story.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ story.achievement.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ story.company.toLowerCase().includes(searchQuery.toLowerCase());
+ const matchesCategory = filterCategory === 'all' || story.category === filterCategory;
+ return matchesSearch && matchesCategory;
+ });
+
+ return (
+
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Filters Card */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Category
+
+
+
+
+
+
+
+ {/* Success Stories Grid */}
+
+ {filteredStories.map((story) => (
+
+
+ {/* Story Image */}
+
+
+
+ {/* Header with Avatar */}
+
+
+
+
+ {story.name}
+
+
+ {story.program} • Class of {story.graduationYear}
+
+
+ {story.designation}
+
+
+ {story.company}
+
+
+
+
+ {/* Category Badge */}
+
+
+ {/* Achievement */}
+
+
+ Achievement
+
+
+ {story.achievement}
+
+
+
+ {/* Story */}
+
+ {story.story}
+
+
+ {/* Tags */}
+
+ {story.tags.map((tag, idx) => (
+
+ ))}
+
+
+ {/* Engagement Stats & Actions */}
+
+
+
+
+
+
+
+ {story.likes}
+
+
+
+
+
+
+
+ {story.comments}
+
+
+
+
+
+
+
+ {story.shares}
+
+
+
+
+ }
+ >
+ Connect
+
+
+
+
+
+ ))}
+
+
+ {filteredStories.length === 0 && (
+
+
+
+
+ No success stories found
+
+
+ Try adjusting your filters or search terms
+
+
+
+ )}
+
+
+ );
+};
+
+export default SuccessStories;
diff --git a/src/pages/Attendance/AttendanceSuccess.jsx b/src/pages/Attendance/AttendanceSuccess.jsx
new file mode 100644
index 0000000..37feba7
--- /dev/null
+++ b/src/pages/Attendance/AttendanceSuccess.jsx
@@ -0,0 +1,155 @@
+import React, { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import { Box, Card, CardContent, Typography, Button, Stack, Divider } from '@mui/material';
+import { CheckCircle, Home } from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+
+const AttendanceSuccess = () => {
+ const navigate = useNavigate();
+ const [countdown, setCountdown] = useState(5);
+ const selectedCourse = JSON.parse(sessionStorage.getItem('selectedCourse') || '{}');
+
+ useEffect(() => {
+ if (countdown > 0) {
+ const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
+ return () => clearTimeout(timer);
+ } else {
+ // Clear session storage
+ sessionStorage.removeItem('selectedCourse');
+ sessionStorage.removeItem('capturedFace');
+ navigate('/dashboard');
+ }
+ }, [countdown, navigate]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Success! ✅
+
+
+ Attendance Marked Successfully
+
+
+
+
+ Course Information
+
+
+ {selectedCourse.code} - {selectedCourse.title}
+
+
+
+
+ Date: {new Date().toLocaleDateString('en-US', {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ })}
+
+
+ Time: {new Date().toLocaleTimeString('en-US', {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ })}
+
+
+ Location: {selectedCourse.room}
+
+
+ Faculty: {selectedCourse.faculty}
+
+
+
+
+
+ Your attendance has been recorded and verified
+
+
+
+ Redirecting to dashboard in {countdown} seconds...
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AttendanceSuccess;
diff --git a/src/pages/Attendance/Confirmation.jsx b/src/pages/Attendance/Confirmation.jsx
new file mode 100644
index 0000000..4e450ef
--- /dev/null
+++ b/src/pages/Attendance/Confirmation.jsx
@@ -0,0 +1,215 @@
+import React from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Stack,
+ Paper,
+} from '@mui/material';
+import {
+ CheckCircle,
+ ArrowBack,
+ AccessTime,
+ LocationOn,
+ Person as PersonIcon,
+} from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+
+const Confirmation = () => {
+ const navigate = useNavigate();
+
+ const selectedCourse = JSON.parse(sessionStorage.getItem('selectedCourse') || '{}');
+ const capturedImage = sessionStorage.getItem('capturedFace');
+
+ return (
+
+ {/* Header */}
+
+
+ Review & Confirm
+
+
+ Step 5 of 6: Verify All Details
+
+
+
+ {/* Main Content */}
+
+
+
+
+
+
+
+
+
+
+ Almost Done!
+
+
+ Please verify all details before submitting
+
+
+
+ {/* Course Info */}
+
+
+
+ Selected Course
+
+
+ {selectedCourse.code} - {selectedCourse.title}
+
+
+
+
+ {selectedCourse.time}
+
+
+
+ {selectedCourse.room}
+
+
+
+ {selectedCourse.faculty}
+
+
+
+
+
+ {/* Captured Image */}
+ {capturedImage && (
+
+
+ Captured Photo:
+
+
+
+ {[
+ { top: 0, left: 0, rotate: '0deg' },
+ { top: 0, right: 0, rotate: '90deg' },
+ { bottom: 0, left: 0, rotate: '-90deg' },
+ { bottom: 0, right: 0, rotate: '180deg' }
+ ].map((pos, i) => (
+
+ ))}
+
+
+ ✓ Face Captured Successfully
+
+
+ )}
+
+ {/* Verification Status */}
+
+
+ Verification Status:
+
+
+ {[
+ { title: '✓ Location Verified', desc: 'You are at the correct location' },
+ { title: '✓ Liveness Verified', desc: 'Real person detected' },
+ { title: '✓ Face Recognized', desc: 'Match confidence: 98.5%' }
+ ].map((item, i) => (
+
+
+
+ {item.title}
+ {item.desc}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Confirmation;
diff --git a/src/pages/Attendance/CourseSelection.jsx b/src/pages/Attendance/CourseSelection.jsx
new file mode 100644
index 0000000..ad541eb
--- /dev/null
+++ b/src/pages/Attendance/CourseSelection.jsx
@@ -0,0 +1,197 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Stack,
+ Divider,
+ Chip,
+} from '@mui/material';
+import {
+ School,
+ ArrowForward,
+ LocationOn,
+ AccessTime,
+ Person as PersonIcon,
+} from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+
+const CourseSelection = () => {
+ const navigate = useNavigate();
+ const [selectedCourse, setSelectedCourse] = useState('');
+
+ // Mock today's classes
+ const todaysClasses = [
+ {
+ id: 'CS101',
+ code: 'CS101',
+ title: 'Data Structures',
+ time: '09:00 AM - 10:30 AM',
+ room: 'Lab 301',
+ faculty: 'Dr. Sarah Ahmed',
+ location: { lat: 31.5204, lng: 74.3587 },
+ },
+ {
+ id: 'CS202',
+ code: 'CS202',
+ title: 'Database Management',
+ time: '11:00 AM - 12:30 PM',
+ room: 'Room 205',
+ faculty: 'Prof. Ali Raza',
+ location: { lat: 31.5204, lng: 74.3587 },
+ },
+ {
+ id: 'CS303',
+ code: 'CS303',
+ title: 'Web Engineering',
+ time: '02:00 PM - 03:30 PM',
+ room: 'Lab 102',
+ faculty: 'Dr. Fatima Malik',
+ location: { lat: 31.5204, lng: 74.3587 },
+ },
+ ];
+
+ const selectedCourseDetails = todaysClasses.find((c) => c.id === selectedCourse);
+
+ const handleProceed = () => {
+ if (selectedCourse) {
+ // Store selected course in sessionStorage
+ sessionStorage.setItem('selectedCourse', JSON.stringify(selectedCourseDetails));
+ navigate('/attendance/gps-verification');
+ }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ Smart Attendance System
+
+
+ Step 1 of 6: Select Your Class
+
+
+
+ {/* Main Content */}
+
+
+
+
+
+
+
+
+ Select Today's Class
+
+
+ Choose which class you want to mark attendance for
+
+
+
+
+ Choose a course
+
+
+
+ {selectedCourse && (
+
+
+
+
+
+
+ {selectedCourseDetails.code}
+
+
+
+
+ {selectedCourseDetails.title}
+
+
+
+
+
+ {selectedCourseDetails.time}
+
+
+
+ {selectedCourseDetails.room}
+
+
+
+ {selectedCourseDetails.faculty}
+
+
+
+
+
+
+ )}
+
+ }
+ sx={{
+ py: 1.5,
+ borderRadius: 2,
+ background: selectedCourse
+ ? 'linear-gradient(135deg, #1976D2 0%, #00796B 100%)'
+ : undefined,
+ fontWeight: 'bold',
+ }}
+ >
+ Proceed to GPS Verification
+
+
+
+
+
+
+
+ );
+};
+
+export default CourseSelection;
diff --git a/src/pages/Attendance/FaceCapture.jsx b/src/pages/Attendance/FaceCapture.jsx
new file mode 100644
index 0000000..78857e8
--- /dev/null
+++ b/src/pages/Attendance/FaceCapture.jsx
@@ -0,0 +1,375 @@
+import React, { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Paper,
+ Alert,
+ Stack,
+ LinearProgress,
+ Divider,
+ List,
+ ListItem,
+ ListItemIcon,
+ ListItemText,
+ CircularProgress,
+} from '@mui/material';
+import {
+ CameraAlt,
+ CheckCircle,
+ ArrowForward,
+ ArrowBack,
+ Camera,
+ RemoveRedEye,
+ Lightbulb,
+ Face,
+ CheckCircleOutline,
+ RadioButtonUnchecked,
+} from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+import Grid from '@mui/material/Grid';
+
+const FaceCapture = () => {
+ const navigate = useNavigate();
+ const [cameraActive, setCameraActive] = useState(false);
+ const [confidence, setConfidence] = useState(0);
+ const [faceDetected, setFaceDetected] = useState(false);
+ const [goodLighting, setGoodLighting] = useState(false);
+ const [livenessVerified, setLivenessVerified] = useState(false);
+
+ // Mock camera start with confidence animation like original
+ useEffect(() => {
+ if (cameraActive) {
+ console.log('Mock camera started for frontend demo');
+ // Simulate status checks
+ setTimeout(() => setFaceDetected(true), 800);
+ setTimeout(() => setGoodLighting(true), 1200);
+ setTimeout(() => setLivenessVerified(true), 1600);
+
+ // Animate confidence like original
+ let current = 0;
+ const interval = setInterval(() => {
+ current += 5;
+ setConfidence(current);
+ if (current >= 95) clearInterval(interval);
+ }, 50);
+ return () => clearInterval(interval);
+ }
+ }, [cameraActive]);
+
+ const captureFace = () => {
+ // Create a mock SVG image for demo
+ const mockImage =
+ 'data:image/svg+xml;base64,' +
+ btoa(`
+
+ `);
+ sessionStorage.setItem('capturedFace', mockImage);
+ navigate('/attendance/confirmation');
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+ Face Capture
+
+
+ Position your face in the frame and capture
+
+
+
+
+ {/* Camera Section */}
+
+
+
+ {!cameraActive ? (
+
+
+
+ Ready to Capture Your Face
+
+
+ Make sure you're in a well-lit area
+
+
+
+ ) : (
+
+ {/* Beautiful camera frame like original with detection box */}
+
+
+
+ {/* Face Detection Box with corner circles like original */}
+
+ {/* Animated corner circles */}
+ {[
+ { top: -8, left: -8 },
+ { top: -8, right: -8 },
+ { bottom: -8, left: -8 },
+ { bottom: -8, right: -8 },
+ ].map((pos, i) => (
+
+ ))}
+
+
+ {/* Scanning line */}
+
+
+
+ {/* Confidence Meter like original */}
+
+
+
+ Match Confidence
+
+
+ {confidence}%
+
+
+
+
+
+
+
+ }
+ sx={{ py: 1.5, borderRadius: 2 }}
+ >
+ Capture & Verify
+
+
+
+ )}
+
+
+
+
+ {/* Instructions Sidebar - like original */}
+
+
+
+
+ Instructions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Status Checks
+
+
+
+
+ {faceDetected ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {goodLighting ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {livenessVerified ? (
+
+ ) : faceDetected ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {confidence >= 95 && (
+ <>
+
+
+
+ Ready to Capture! ✓
+
+
+ Confidence: {confidence}%
+
+
+ >
+ )}
+
+
+
+
+
+
+ );
+};
+
+export default FaceCapture;
diff --git a/src/pages/Attendance/GPSVerification.jsx b/src/pages/Attendance/GPSVerification.jsx
new file mode 100644
index 0000000..3a3cdb6
--- /dev/null
+++ b/src/pages/Attendance/GPSVerification.jsx
@@ -0,0 +1,275 @@
+import React, { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Stack,
+ Paper,
+ Alert,
+ CircularProgress,
+ Divider,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ MyLocation,
+ CheckCircle,
+ LocationOn,
+ ArrowForward,
+ ArrowBack,
+ Map as MapIcon,
+} from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+
+const GPSVerification = () => {
+ const navigate = useNavigate();
+ const [locationVerified, setLocationVerified] = useState(false);
+ const [locationLoading, setLocationLoading] = useState(false);
+ const [userLocation, setUserLocation] = useState(null);
+
+ const selectedCourse = JSON.parse(sessionStorage.getItem('selectedCourse') || '{}');
+
+ // Calculate distance between two coordinates (Haversine formula)
+ const calculateDistance = (lat1, lon1, lat2, lon2) => {
+ const R = 6371e3; // Earth's radius in meters
+ const φ1 = (lat1 * Math.PI) / 180;
+ const φ2 = (lat2 * Math.PI) / 180;
+ const Δφ = ((lat2 - lat1) * Math.PI) / 180;
+ const Δλ = ((lon2 - lon1) * Math.PI) / 180;
+
+ const a =
+ Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
+ Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+
+ return R * c; // Distance in meters
+ };
+
+ const verifyLocation = () => {
+ setLocationLoading(true);
+
+ // For frontend demo - simulate GPS verification without actual location check
+ setTimeout(() => {
+ // Mock user location (near the class location)
+ const mockLocation = {
+ lat: selectedCourse.location.lat + 0.0001, // Very close to class location
+ lng: selectedCourse.location.lng + 0.0001,
+ };
+ setUserLocation(mockLocation);
+ setLocationLoading(false);
+ setLocationVerified(true);
+
+ // Auto-proceed to next step
+ setTimeout(() => navigate('/attendance/liveness-detection'), 2000);
+ }, 1500);
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ GPS Location Verification
+
+
+ Step 2 of 6: Verify Your Location
+
+
+
+ {/* Main Content */}
+
+
+
+
+
+
+ Location Verification
+
+
+
+ {/* Beautiful Map Placeholder like original */}
+
+
+
+ {/* Pulsing location pin like original */}
+ {locationVerified && (
+
+
+
+ )}
+
+
+ {/* GPS Info */}
+
+
+
+
+
+ GPS Coordinates
+
+
+ 31.5204° N, 74.3587° E
+
+
+
+
+
+
+
+
+ Distance from Class
+
+
+ 15 meters
+
+
+
+
+
+
+ {/* Status Indicator */}
+ : locationLoading ? : }
+ sx={{ borderRadius: 2 }}
+ >
+ {locationVerified ? (
+ <>
+ Location Verified
+
+ You are in {selectedCourse.room} - Ready to proceed
+ >
+ ) : (
+ <>
+ {locationLoading ? 'Verifying Location...' : 'Ready to Verify'}
+
+ {locationLoading
+ ? 'Please wait while we confirm your location'
+ : 'Click the button below to verify your GPS location'}
+ >
+ )}
+
+
+
+
+ {!locationVerified && (
+
+ ) : (
+
+ )
+ }
+ sx={{ py: 1.5, borderRadius: 2 }}
+ >
+ {locationLoading ? 'Verifying Location...' : 'Verify My Location'}
+
+ )}
+
+
+
+
+
+
+ {/* Info Sidebar like original */}
+
+
+
+
+ Why Location?
+
+
+
+ We verify your location to ensure you're physically present in the classroom.
+
+
+ You must be within 50 meters of the class location to mark attendance.
+
+
+
+
+ Selected Course
+
+
+ {selectedCourse.code} - {selectedCourse.title}
+
+
+ {selectedCourse.time} • {selectedCourse.room}
+
+
+
+ {locationVerified && (
+
+
+ Ready to Continue!
+
+
+ Proceeding to liveness detection...
+
+
+ )}
+
+
+
+
+
+ );
+};
+
+export default GPSVerification;
diff --git a/src/pages/Attendance/LivenessDetection.jsx b/src/pages/Attendance/LivenessDetection.jsx
new file mode 100644
index 0000000..b02b391
--- /dev/null
+++ b/src/pages/Attendance/LivenessDetection.jsx
@@ -0,0 +1,557 @@
+import React, { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Stack,
+ Paper,
+ ToggleButtonGroup,
+ ToggleButton,
+ List,
+ ListItem,
+ ListItemIcon,
+ ListItemText,
+ Alert,
+ Divider,
+ CircularProgress,
+} from '@mui/material';
+import {
+ Visibility,
+ Mic,
+ CheckCircle,
+ ArrowForward,
+ ArrowBack,
+ VisibilityOff,
+ RemoveRedEye,
+ Face,
+ Lightbulb,
+ Camera,
+ CheckCircleOutline,
+ RadioButtonUnchecked,
+} from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+
+const LivenessDetection = () => {
+ const navigate = useNavigate();
+ const [livenessMethod, setLivenessMethod] = useState('eyes');
+ const [livenessStep, setLivenessStep] = useState(0);
+ const [faceDetected, setFaceDetected] = useState(false);
+ const [goodLighting, setGoodLighting] = useState(false);
+ const [livenessVerified, setLivenessVerified] = useState(false);
+
+ // Mock camera simulation - no actual camera access needed
+ useEffect(() => {
+ console.log('Mock camera initialized for frontend demo');
+ }, []);
+
+ // Simulate face detection and lighting checks
+ useEffect(() => {
+ if (livenessStep === 1) {
+ setTimeout(() => setFaceDetected(true), 800);
+ setTimeout(() => setGoodLighting(true), 1200);
+ }
+ }, [livenessStep]);
+
+ const handleMethodChange = (event, newMethod) => {
+ if (newMethod !== null) {
+ setLivenessMethod(newMethod);
+ setLivenessStep(0);
+ setFaceDetected(false);
+ setGoodLighting(false);
+ setLivenessVerified(false);
+ }
+ };
+
+ const handleCapture = () => {
+ setLivenessVerified(true);
+ setTimeout(() => {
+ navigate('/attendance/face-capture');
+ }, 1500);
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+ Liveness Detection
+
+
+ Verify you're a real person, not a photo
+
+
+
+
+
+ {/* Method Selection */}
+ {livenessStep === 0 && (
+
+
+
+ Choose Verification Method
+
+
+
+
+
+ Eye Blink
+
+
+
+
+
+ Voice
+
+
+
+
+
+
+ Instructions:
+
+
+ {livenessMethod === 'eyes' ? (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* Eye Blink Method */}
+ {livenessStep === 1 && livenessMethod === 'eyes' && (
+
+
+
+ Step 1: Close Your Eyes
+
+
+ Close both eyes and hold for 2 seconds
+
+
+
+ {/* Beautiful camera frame like original */}
+
+
+
+ {/* Face Detection Box with corner circles like original */}
+
+ {/* Animated corner circles */}
+ {[
+ { top: -8, left: -8 },
+ { top: -8, right: -8 },
+ { bottom: -8, left: -8 },
+ { bottom: -8, right: -8 },
+ ].map((pos, i) => (
+
+ ))}
+
+
+ {/* Scanning line */}
+
+
+
+ {/* Status Checks */}
+
+
+ Status Checks
+
+
+
+
+ {faceDetected ? (
+
+ ) : (
+
+ )}
+ Face Detected
+
+
+ {goodLighting ? (
+
+ ) : faceDetected ? (
+
+ ) : (
+
+ )}
+ Good Lighting
+
+
+
+
+
+
+ )}
+
+ {livenessStep === 2 && livenessMethod === 'eyes' && (
+
+
+
+ Step 2: Open Your Eyes
+
+
+ Look directly at the camera
+
+
+
+ {/* Beautiful camera frame with green for success */}
+
+
+
+ {/* Face Detection Box - GREEN */}
+
+ {/* Animated corner circles */}
+ {[
+ { top: -8, left: -8 },
+ { top: -8, right: -8 },
+ { bottom: -8, left: -8 },
+ { bottom: -8, right: -8 },
+ ].map((pos, i) => (
+
+ ))}
+
+
+ {/* Scanning line - GREEN */}
+
+
+
+ {livenessVerified && (
+
+
+ Liveness Verified! ✓
+
+
+ Proceeding to face capture...
+
+
+ )}
+
+ }
+ sx={{ py: 1.5, borderRadius: 2 }}
+ >
+ Capture & Continue
+
+
+ )}
+
+ {/* Voice Method */}
+ {livenessStep === 1 && livenessMethod === 'voice' && (
+
+
+
+ Say: "I am marking my attendance"
+
+
+ Speak clearly into your microphone
+
+
+
+ {/* Voice visualization */}
+
+
+
+
+
+ {/* Audio wave bars */}
+
+ {[1, 2, 3, 4, 5].map((bar) => (
+
+ ))}
+
+
+
+ {livenessVerified && (
+
+
+ Voice Verified! ✓
+
+
+ Proceeding to face capture...
+
+
+ )}
+
+
+
+ }
+ sx={{ py: 1.5, borderRadius: 2 }}
+ >
+ Verify & Continue
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default LivenessDetection;
diff --git a/src/pages/Attendance/SmartAttendance.jsx b/src/pages/Attendance/SmartAttendance.jsx
index 15d5053..2f480f1 100644
--- a/src/pages/Attendance/SmartAttendance.jsx
+++ b/src/pages/Attendance/SmartAttendance.jsx
@@ -6,169 +6,82 @@ import {
CardContent,
Typography,
Button,
- Chip,
Alert,
Select,
MenuItem,
FormControl,
- InputLabel,
- Stepper,
- Step,
- StepLabel,
- LinearProgress,
CircularProgress,
+ Divider,
+ Tooltip,
+ Stack,
List,
ListItem,
ListItemIcon,
ListItemText,
- Divider,
- IconButton,
- Tooltip,
- Stack,
+ Paper,
+ Chip,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
- Videocam,
CheckCircle,
LocationOn,
AccessTime,
- Map as MapIcon,
- Camera,
Person,
- LocalFireDepartment,
ArrowForward,
Help,
History,
- CheckCircleOutline,
- RadioButtonUnchecked,
+ Videocam,
+ MyLocation,
Visibility,
- Lightbulb,
- RemoveRedEye,
Face,
- Edit,
+ LocalFireDepartment,
} from '@mui/icons-material';
import { useNavigate } from 'react-router-dom';
-import { courses, markAttendance, currentUser, attendanceStats } from '../../data/dummyData';
import { pageTransition } from '../../utils/animations';
const SmartAttendance = () => {
const navigate = useNavigate();
- const [activeStep, setActiveStep] = useState(0);
const [selectedCourse, setSelectedCourse] = useState('');
- const [locationVerified, setLocationVerified] = useState(false);
- const [faceDetected, setFaceDetected] = useState(false);
- const [goodLighting, setGoodLighting] = useState(false);
- const [livenessVerified, setLivenessVerified] = useState(false);
- const [confidence, setConfidence] = useState(0);
- const [capturedImage, setCapturedImage] = useState(false);
- const [attendanceMarked, setAttendanceMarked] = useState(false);
- const [countdown, setCountdown] = useState(3);
- const [loading, setLoading] = useState(true);
-
- const steps = ['Location Verification', 'Face Detection', 'Confirmation'];
-
- // Loading effect
- useEffect(() => {
- const timer = setTimeout(() => setLoading(false), 1000);
- return () => clearTimeout(timer);
- }, []);
- // Mock today's classes
+ // Mock today's classes with GPS coordinates
const todaysClasses = [
- { id: 'CS101', code: 'CS101', title: 'Data Structures', time: '09:00 AM', room: 'Lab 301', faculty: 'Dr. Sarah Ahmed' },
- { id: 'CS202', code: 'CS202', title: 'Database Management', time: '11:00 AM', room: 'Room 205', faculty: 'Prof. Ali Raza' },
- { id: 'CS303', code: 'CS303', title: 'Web Engineering', time: '02:00 PM', room: 'Lab 102', faculty: 'Dr. Fatima Malik' },
+ {
+ id: 'CS101',
+ code: 'CS101',
+ title: 'Data Structures',
+ time: '09:00 AM',
+ room: 'Lab 301',
+ faculty: 'Dr. Sarah Ahmed',
+ location: { lat: 31.5204, lng: 74.3587 }
+ },
+ {
+ id: 'CS202',
+ code: 'CS202',
+ title: 'Database Management',
+ time: '11:00 AM',
+ room: 'Room 205',
+ faculty: 'Prof. Ali Raza',
+ location: { lat: 31.5210, lng: 74.3590 }
+ },
+ {
+ id: 'CS303',
+ code: 'CS303',
+ title: 'Web Engineering',
+ time: '02:00 PM',
+ room: 'Lab 102',
+ faculty: 'Dr. Fatima Malik',
+ location: { lat: 31.5200, lng: 74.3585 }
+ },
];
- // Simulate location verification
- useEffect(() => {
- if (activeStep === 0) {
- setTimeout(() => {
- setLocationVerified(true);
- }, 1500);
- }
- }, [activeStep]);
-
- // Simulate face detection process
- useEffect(() => {
- if (activeStep === 1) {
- setTimeout(() => setFaceDetected(true), 1000);
- setTimeout(() => setGoodLighting(true), 1500);
- setTimeout(() => {
- setLivenessVerified(true);
- // Animate confidence
- let current = 0;
- const interval = setInterval(() => {
- current += 5;
- setConfidence(current);
- if (current >= 95) clearInterval(interval);
- }, 50);
- }, 2000);
- }
- }, [activeStep]);
-
- // Countdown for redirect
- useEffect(() => {
- if (attendanceMarked && countdown > 0) {
- const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
- return () => clearTimeout(timer);
- } else if (attendanceMarked && countdown === 0) {
- navigate('/dashboard');
- }
- }, [attendanceMarked, countdown, navigate]);
-
- // Show loading skeleton
- if (loading) {
- return (
-
-
-
- Smart Attendance
-
-
- Loading attendance system...
-
-
-
-
-
-
- );
- }
-
- const handleProceedToFace = () => {
- if (locationVerified) {
- setActiveStep(1);
- }
- };
-
- const handleCapture = () => {
- if (confidence >= 95) {
- setCapturedImage(true);
- setActiveStep(2);
- }
- };
-
- const handleConfirmAttendance = () => {
+ const handleProceedToVerification = () => {
if (selectedCourse) {
- markAttendance(selectedCourse, 'Present');
- setAttendanceMarked(true);
+ const course = todaysClasses.find(c => c.id === selectedCourse);
+ sessionStorage.setItem('selectedCourse', JSON.stringify(course));
+ navigate('/attendance/gps-verification');
}
};
- const handleReset = () => {
- setActiveStep(0);
- setSelectedCourse('');
- setLocationVerified(false);
- setFaceDetected(false);
- setGoodLighting(false);
- setLivenessVerified(false);
- setConfidence(0);
- setCapturedImage(false);
- setAttendanceMarked(false);
- setCountdown(3);
- };
-
return (
@@ -183,701 +96,363 @@ const SmartAttendance = () => {
- {/* Success Animation */}
- {attendanceMarked && (
-
-
-
-
-
-
- Attendance Marked Successfully! ✅
-
-
- {todaysClasses.find(c => c.id === selectedCourse)?.code} - {todaysClasses.find(c => c.id === selectedCourse)?.title}
-
-
- Time: {new Date().toLocaleTimeString()} • Status: Present
-
-
- Redirecting to dashboard in {countdown} seconds...
-
-
-
- )}
-
- {!attendanceMarked && (
-
- {/* Main Content */}
-
- {/* Course Selection */}
-
-
-
- Select Today's Class
-
- {todaysClasses.length === 0 ? (
-
- No classes scheduled for today
-
- ) : (
-
-
-
- )}
-
-
-
- {/* Stepper */}
- {selectedCourse && (
-
-
-
- {steps.map((label) => (
-
- {label}
-
+
+
+
+
+
+
+ {course.time}
+
+
+
+ {course.room}
+
+
+
+ {course.faculty}
+
+
+
))}
-
+
- )}
- {/* Step Content */}
- {selectedCourse && (
-
-
- {/* STEP 1: Location Verification */}
- {activeStep === 0 && (
-
-
- Step 1: Location Verification
-
-
- {/* Map Placeholder */}
-
+
+
+ Verification Process
+
+
+ Complete these steps to mark your attendance
+
+
+
+
+
-
- {locationVerified && (
-
-
-
- )}
-
-
- {/* GPS Info */}
-
-
-
-
-
- GPS Coordinates
-
-
- 31.5204° N, 74.3587° E
-
-
-
-
-
-
-
-
- Distance from Class
-
-
- 15 meters
-
-
-
-
-
-
- {/* Status Indicator */}
- : }
- sx={{ mb: 3, borderRadius: 2 }}
- >
- {locationVerified ? (
- <>
- Location Verified
-
- You are in Computer Science Block - Ready to proceed
- >
- ) : (
- <>
- Verifying Location...
-
- Please wait while we confirm your location
- >
- )}
-
-
-
- )}
-
- {/* STEP 2: Face Detection */}
- {activeStep === 1 && (
-
-
- Step 2: Face Detection
-
-
-
- {/* Camera Viewport */}
-
-
-
-
- {/* Face Detection Box */}
- {faceDetected && (
-
- {/* Corner Circles */}
- {[
- { top: -8, left: -8 },
- { top: -8, right: -8 },
- { bottom: -8, left: -8 },
- { bottom: -8, right: -8 },
- ].map((pos, i) => (
-
- ))}
-
- )}
-
- {/* Scanning Line */}
- {faceDetected && (
-
- )}
-
-
- {/* Confidence Meter */}
-
-
-
- Match Confidence
-
-
- {confidence}%
-
-
-
-
-
-
- {/* Instructions Sidebar */}
-
-
-
-
- Instructions
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Status Checks
-
-
-
-
- {faceDetected ? (
-
- ) : (
-
- )}
-
-
-
-
-
- {goodLighting ? (
-
- ) : (
-
- )}
-
-
-
-
-
- {livenessVerified ? (
-
- ) : faceDetected ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
-
-
-
- }
- sx={{ mt: 3, py: 1.5 }}
- >
- Capture & Verify
-
-
- )}
-
- {/* STEP 3: Confirmation */}
- {activeStep === 2 && (
-
-
- Step 3: Confirmation
-
-
- {/* Captured Frame */}
-
+
+
+
+
+
+
+
+
-
-
-
- {/* Match Result */}
-
-
-
- Match Confidence: 94.7%
-
-
+ justifyContent: 'center'
+ }}>
+
-
-
- {/* Student Info */}
-
-
-
-
-
- Student Name
-
-
- {currentUser.name}
-
-
-
-
- Roll Number
-
-
- {currentUser.rollNo}
-
-
-
-
- Program
-
-
- {currentUser.program}
-
-
-
-
- Timestamp
-
-
- {new Date().toLocaleString()}
-
-
-
-
- Location
-
-
- Computer Science Block (31.5204° N, 74.3587° E)
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ready to start?
+
+
+ The entire process takes less than 30 seconds
+
+
- }
- sx={{ py: 2, fontSize: '1.1rem' }}
- >
- Confirm Attendance
-
-
- )}
+ }
+ onClick={handleProceedToVerification}
+ sx={{
+ mt: 3,
+ py: 2,
+ fontSize: '1.1rem',
+ fontWeight: 600,
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ boxShadow: '0 6px 20px rgba(102,126,234,0.4)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #764ba2 0%, #667eea 100%)',
+ boxShadow: '0 8px 25px rgba(102,126,234,0.5)',
+ transform: 'translateY(-2px)',
+ },
+ transition: 'all 0.3s ease'
+ }}
+ >
+ Start Verification Process
+
- )}
-
+ >
+ )}
+
- {/* SIDEBAR INFO PANEL */}
-
-
- {/* Attendance Stats */}
-
-
-
- Your Attendance Stats
-
-
-
-
-
- 42
-
-
- Present
-
-
-
-
-
-
- 3
-
-
- Absent
-
-
-
-
-
-
- 93%
-
-
- Rate
-
-
-
+ {/* SIDEBAR INFO PANEL */}
+
+
+ {/* Attendance Stats */}
+
+
+
+ Your Attendance Stats
+
+
+
+
+
+ 42
+
+
+ Present
+
+
-
-
-
-
+
+
+
+ 3
+
+
+ Absent
+
+
+
+
+
+
93%
+
+ Rate
+
-
-
-
-
- {/* Attendance Streak */}
-
-
-
-
- 🔥
-
-
- 15 Days
-
-
- Attendance Streak
-
-
-
- Keep it up! You're on a roll 🎉
+
+
+
+
+
+
+ 93%
-
-
+
+
+
- {/* Next Class */}
-
-
-
- Next Class
+ {/* Attendance Streak */}
+
+
+
+
+ 🔥
-
- {countdown > 0 ? (
- <>
-
- Database Systems
-
-
- Room 305 • Dr. Ahmed Khan
-
-
-
- {Math.floor(countdown / 60)}:{(countdown % 60).toString().padStart(2, '0')}
-
-
- Time Remaining
-
-
- >
- ) : (
-
-
- Your class has started! Mark attendance now.
-
-
- )}
-
-
-
- {/* Quick Actions */}
-
-
-
- Quick Actions
+
+ 15 Days
-
-
- }
- onClick={() => navigate('/attendance/history')}
- >
- View Attendance History
-
+
+ Attendance Streak
+
+
+
+ Keep it up! You're on a roll 🎉
+
+
+
+
+
+ {/* Quick Actions */}
+
+
+
+ Quick Actions
+
+
+
+ }
+ onClick={() => navigate('/attendance/history')}
+ >
+ View Attendance History
+
+
}
- onClick={() => alert('Faculty override feature - Admin only')}
+ variant="text"
+ startIcon={}
+ size="small"
>
- Mark Manually
+ How It Works
-
- }
- size="small"
- >
- How It Works
-
-
-
-
-
-
-
+
+
+
+
+
- )}
-
+
+
);
};
diff --git a/src/pages/Auth/ForgotPassword.jsx b/src/pages/Auth/ForgotPassword.jsx
index 2fd43ec..85bc143 100644
--- a/src/pages/Auth/ForgotPassword.jsx
+++ b/src/pages/Auth/ForgotPassword.jsx
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { Box, TextField, Button, Typography, Alert } from '@mui/material';
import { Email, ArrowBack } from '@mui/icons-material';
import { Link, useNavigate } from 'react-router-dom';
+import PageTransition from '../../components/Common/PageTransition';
const ForgotPassword = () => {
const navigate = useNavigate();
@@ -20,26 +21,27 @@ const ForgotPassword = () => {
};
return (
-
+
+
+
-
+
);
};
diff --git a/src/pages/Auth/Login.jsx b/src/pages/Auth/Login.jsx
index e82934b..06358c1 100644
--- a/src/pages/Auth/Login.jsx
+++ b/src/pages/Auth/Login.jsx
@@ -4,17 +4,26 @@ import {
TextField,
Button,
Typography,
- Switch,
+ Checkbox,
FormControlLabel,
Link as MuiLink,
Alert,
- ToggleButton,
- ToggleButtonGroup,
Paper,
+ Chip,
+ Stack,
} from '@mui/material';
-import { School, Login as LoginIcon, Person, Group, AdminPanelSettings } from '@mui/icons-material';
+import {
+ School,
+ Login as LoginIcon,
+ Person,
+ Group,
+ AdminPanelSettings,
+ LocalLibrary,
+ People,
+} from '@mui/icons-material';
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
+import PageTransition from '../../components/Common/PageTransition';
const Login = () => {
const navigate = useNavigate();
@@ -22,6 +31,7 @@ const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [userType, setUserType] = useState('student');
+ const [rememberMe, setRememberMe] = useState(false);
const [error, setError] = useState('');
const handleSubmit = (e) => {
@@ -35,7 +45,14 @@ const Login = () => {
const result = login(email, password, userType);
if (result.success) {
- // Navigate based on user type
+ if (rememberMe) {
+ localStorage.setItem('rememberMe', 'true');
+ localStorage.setItem('userEmail', email);
+ } else {
+ localStorage.removeItem('rememberMe');
+ localStorage.removeItem('userEmail');
+ }
+
switch (userType) {
case 'admin':
navigate('/admin/dashboard');
@@ -43,6 +60,12 @@ const Login = () => {
case 'teacher':
navigate('/teacher/dashboard');
break;
+ case 'librarian':
+ navigate('/librarian/dashboard');
+ break;
+ case 'alumni':
+ navigate('/alumni/network');
+ break;
default:
navigate('/dashboard');
}
@@ -51,168 +74,158 @@ const Login = () => {
}
};
+ const userTypes = [
+ { value: 'student', icon: Person, label: 'Student', color: '#1976D2' },
+ { value: 'teacher', icon: Group, label: 'Teacher', color: '#00796B' },
+ { value: 'admin', icon: AdminPanelSettings, label: 'Admin', color: '#D32F2F' },
+ { value: 'librarian', icon: LocalLibrary, label: 'Librarian', color: '#7B1FA2' },
+ { value: 'alumni', icon: People, label: 'Alumni', color: '#F57C00' },
+ ];
+
return (
-
- {/* Left Side - Image with Overlay */}
-
+
+
+ {/* Left Side - Image */}
-
-
- Project Nexus
-
-
- Unified Intelligent Campus Platform
-
-
- Access your academic life in one place. Track attendance, manage courses,
- submit assignments, pay fees, and connect with AI-powered support.
-
-
-
-
- {/* Right Side - Login Form */}
-
-
- {/* Logo for mobile */}
-
-
-
+
+
+
Project Nexus
+
+ Unified Intelligent Campus Platform
+
+
+ Access your academic life in one place. Track attendance, manage courses, submit assignments, pay fees, and connect with AI-powered support.
+
+
-
- Welcome Back!
-
-
- Sign in to continue to your account
-
-
- {error && (
-
- {error}
-
- )}
-
-
-
-
-
- Demo Credentials:
-
- Email: any@email.com | Password: any
+
+ Welcome Back!
+
+
+ Sign in to continue to your account
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+
+
+ Demo Credentials: Email: any@email.com | Password: any
+
+
-
+
);
};
diff --git a/src/pages/Auth/OTP.jsx b/src/pages/Auth/OTP.jsx
index f34003f..931cd50 100644
--- a/src/pages/Auth/OTP.jsx
+++ b/src/pages/Auth/OTP.jsx
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { Box, TextField, Button, Typography, Alert } from '@mui/material';
import { LockReset, ArrowBack } from '@mui/icons-material';
import { Link, useNavigate, useLocation } from 'react-router-dom';
+import PageTransition from '../../components/Common/PageTransition';
const OTP = () => {
const navigate = useNavigate();
@@ -48,27 +49,28 @@ const OTP = () => {
};
return (
-
+
+
+
-
+
);
};
diff --git a/src/pages/Chat/ChatPortal.jsx b/src/pages/Chat/ChatPortal.jsx
index a50a723..e477d49 100644
--- a/src/pages/Chat/ChatPortal.jsx
+++ b/src/pages/Chat/ChatPortal.jsx
@@ -1,16 +1,12 @@
-import React, { useState, useRef, useEffect } from 'react';
-import { motion } from 'framer-motion';
+import React, { useState, useRef, useEffect } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
import {
Box,
- Card,
- CardContent,
Typography,
TextField,
IconButton,
Avatar,
- Chip,
- Switch,
- Divider,
+ Badge,
Paper,
Button,
List,
@@ -19,24 +15,16 @@ import {
ListItemText,
ListItemAvatar,
InputAdornment,
- Badge,
- Drawer,
- useMediaQuery,
- useTheme,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
- Table,
- TableBody,
- TableRow,
- TableCell,
Stack,
+ Tabs,
+ Tab,
Tooltip,
- Menu,
- MenuItem,
- Fade,
- Collapse,
+ useTheme,
+ useMediaQuery,
} from '@mui/material';
import {
Send,
@@ -44,1208 +32,918 @@ import {
Person,
Add,
Search,
- Settings,
Mic,
AttachFile,
MoreVert,
- Circle,
- Delete,
- ThumbUp,
- ThumbDown,
- Download,
- ContentCopy,
- HelpOutline,
EmojiEmotions,
- Stop,
- Menu as MenuIcon,
+ ArrowBack,
+ Group as GroupIcon,
Close,
- Lightbulb,
- School,
- Event,
- AccountBalance,
- TrendingUp,
+ Check,
+ VideoCall,
+ Call,
} from '@mui/icons-material';
import { useAuth } from '../../contexts/AuthContext';
-import { currentUser } from '../../data/dummyData';
-import { ChatSkeleton } from '../../components/Common/LoadingSkeleton';
-import { pageTransition, staggerContainer, fadeInUp } from '../../utils/animations';
-
+import { useNavigate } from 'react-router-dom';
const ChatPortal = () => {
const { user } = useAuth();
const theme = useTheme();
+ const navigate = useNavigate();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
- const [loading, setLoading] = useState(true);
+ const messagesEndRef = useRef(null);
- // State management
- const [conversations, setConversations] = useState([
- { id: 1, title: 'What is my CGPA?', lastMessage: 'Your current CGPA is 3.85', timestamp: '2 hours ago', active: true },
- { id: 2, title: 'Show my timetable', lastMessage: 'Here is your class schedule for this week', timestamp: 'Yesterday', active: false },
- { id: 3, title: 'Check dues', lastMessage: 'You have PKR 15,000 in pending dues', timestamp: '2 days ago', active: false },
- ]);
- const [activeConversation, setActiveConversation] = useState(1);
- const [messages, setMessages] = useState([]);
+ // WhatsApp Color Scheme
+ const whatsappGreen = '#128C7E';
+ const whatsappDarkGreen = '#075E54';
+ const whatsappLightGreen = '#25D366';
+ const userBubbleColor = '#DCF8C6';
+ const otherBubbleColor = theme.palette.mode === 'dark' ? '#2A2A2A' : '#FFFFFF';
+ const chatBgColor = theme.palette.mode === 'dark' ? '#0D1418' : '#E5DDD5';
+
+ // State Management
+ const [mode, setMode] = useState('ai'); // 'ai' or 'human'
+ const [currentTab, setCurrentTab] = useState(0); // 0: contacts, 1: groups
+ const [selectedChat, setSelectedChat] = useState(null);
+ const [searchQuery, setSearchQuery] = useState('');
const [inputMessage, setInputMessage] = useState('');
- const [isAiMode, setIsAiMode] = useState(true);
+ const [messages, setMessages] = useState([]);
const [isTyping, setIsTyping] = useState(false);
- const [streamingText, setStreamingText] = useState('');
- const [isRecording, setIsRecording] = useState(false);
- const [drawerOpen, setDrawerOpen] = useState(false);
- const [searchQuery, setSearchQuery] = useState('');
- const [citationModal, setCitationModal] = useState({ open: false, content: '' });
- const [anchorEl, setAnchorEl] = useState(null);
- const [showEmojiPicker, setShowEmojiPicker] = useState(false);
- const [charCount, setCharCount] = useState(0);
- const [hoveredMessage, setHoveredMessage] = useState(null);
-
- const messagesEndRef = useRef(null);
- const chatContainerRef = useRef(null);
-
- // Loading effect
- useEffect(() => {
- const timer = setTimeout(() => {
- setLoading(false);
- }, 1200);
- return () => clearTimeout(timer);
- }, []);
-
- // Quick action queries
- const quickActions = [
- { label: 'What is my CGPA?', icon: },
- { label: 'View Attendance', icon: },
- { label: 'Fee Status', icon: },
- { label: 'Course Schedule', icon: },
- ];
-
- // Suggested follow-ups
- const [suggestedQuestions, setSuggestedQuestions] = useState([
- "What is my attendance percentage?",
- "Show my upcoming assignments",
- "When is the next exam?"
+ const [createGroupOpen, setCreateGroupOpen] = useState(false);
+ const [newGroupName, setNewGroupName] = useState('');
+ const [newGroupDescription, setNewGroupDescription] = useState('');
+ const [selectedMembers, setSelectedMembers] = useState([]);
+ const [aiChatHistory, setAiChatHistory] = useState([]);
+
+ // Mock Data
+ const [contacts] = useState([
+ {
+ id: 1,
+ name: 'Dr. Sarah Ahmed',
+ role: 'Database Professor',
+ avatar: '/avatars/sarah.jpg',
+ status: 'online',
+ lastMessage: 'Please review the assignment',
+ lastTime: '10:30 AM',
+ },
+ {
+ id: 2,
+ name: 'Ayesha Khan',
+ role: 'Class Representative',
+ avatar: '/avatars/ayesha.jpg',
+ status: 'online',
+ lastMessage: 'Notes shared in group',
+ lastTime: '9:15 AM',
+ },
+ {
+ id: 3,
+ name: 'Ali Ahmed',
+ role: 'Study Partner',
+ avatar: '/avatars/ali.jpg',
+ status: 'away',
+ lastMessage: 'See you tomorrow',
+ lastTime: 'Yesterday',
+ },
+ {
+ id: 4,
+ name: 'Prof. Hassan Khan',
+ role: 'AI Course Instructor',
+ avatar: '/avatars/hassan.jpg',
+ status: 'offline',
+ lastMessage: 'Lab session at 2 PM',
+ lastTime: 'Yesterday',
+ },
]);
- // Common questions
- const commonQuestions = [
- "How do I submit an assignment?",
- "What are my library timings?",
- "How do I pay my fees?",
- "How do I mark attendance?",
- "Where can I see my transcript?"
- ];
-
- const scrollToBottom = () => {
- messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
- };
-
- useEffect(() => {
- scrollToBottom();
- }, [messages, streamingText]);
+ const [groups, setGroups] = useState([
+ {
+ id: 'G1',
+ name: 'BSIT Batch 2024',
+ description: 'Official batch group',
+ avatar: '',
+ members: 48,
+ lastMessage: 'Quiz on Friday',
+ lastTime: '11:45 AM',
+ },
+ {
+ id: 'G2',
+ name: 'Database Project Team',
+ description: 'Semester project collaboration',
+ avatar: '',
+ members: 5,
+ lastMessage: 'Schema finalized',
+ lastTime: 'Yesterday',
+ },
+ {
+ id: 'G3',
+ name: 'Study Group - AI',
+ description: 'AI course study materials',
+ avatar: '',
+ members: 12,
+ lastMessage: 'New resource shared',
+ lastTime: '2 days ago',
+ },
+ ]);
- // Initialize with welcome message
+ // Initialize AI chat
useEffect(() => {
- if (messages.length === 0) {
- setMessages([{
- id: Date.now(),
- text: `Hello ${currentUser?.name || 'Student'}! 👋 I'm Nexus AI, your intelligent campus assistant. I'm here to help you with anything related to your academics, fees, attendance, courses, and more. How can I assist you today?`,
- isAi: true,
- timestamp: new Date().toLocaleTimeString(),
- isWelcome: true,
- }]);
- }
- }, []);
-
- const getAiResponse = (userMessage) => {
- const lowerMessage = userMessage.toLowerCase();
-
- // Context-aware responses with special content
- if (lowerMessage.includes('cgpa') || lowerMessage.includes('gpa') || lowerMessage.includes('grade')) {
- return {
- text: 'Based on your academic record, here are your current grades:',
- type: 'table',
- tableData: [
- { course: 'Data Structures', grade: 'A', gpa: '4.0', credits: '3' },
- { course: 'Database Systems', grade: 'A-', gpa: '3.7', credits: '3' },
- { course: 'Web Development', grade: 'B+', gpa: '3.3', credits: '3' },
- { course: 'Operating Systems', grade: 'A', gpa: '4.0', credits: '4' },
- ],
- summary: 'Your current CGPA is **3.85** which is excellent! Keep up the great work.',
- citations: ['Source: Academic Transcript', 'Updated: Jan 2026'],
- followUps: ['How can I improve my grades?', 'Show my semester-wise performance', 'What is the highest CGPA in my class?']
- };
- } else if (lowerMessage.includes('fee') || lowerMessage.includes('payment') || lowerMessage.includes('dues')) {
- return {
- text: 'I found the following information about your fee status:',
- type: 'action',
- summary: `• Total Fees: PKR 85,000\n• Paid: PKR 70,000\n• Outstanding: PKR 15,000\n• Due Date: January 15, 2026`,
- actionButton: { label: 'Pay Now', action: 'navigate-fees' },
- citations: ['Source: Finance Portal', 'Last Updated: Jan 4, 2026'],
- followUps: ['What happens if I pay late?', 'Can I get a payment plan?', 'Show payment history']
- };
- } else if (lowerMessage.includes('attendance')) {
- return {
- text: 'Here is your current attendance summary:',
- type: 'data',
- summary: `• Overall Attendance: **87%**\n• Data Structures: 92%\n• Database Systems: 85%\n• Web Development: 88%\n• Operating Systems: 83%\n\n⚠️ Note: Operating Systems attendance is below the required 85% threshold.`,
- citations: ['Source: Attendance System', 'Real-time Data'],
- followUps: ['How many more classes can I miss?', 'Mark my attendance now', 'Request attendance condonation']
- };
- } else if (lowerMessage.includes('assignment') || lowerMessage.includes('homework')) {
- return {
- text: 'You have 3 upcoming assignments:',
- type: 'list',
- items: [
- '📝 **Database Design Project** - Due: Jan 10, 2026 (6 days left)',
- '💻 **Web Dev Portfolio** - Due: Jan 15, 2026 (11 days left)',
- '🧮 **Algorithm Analysis** - Due: Jan 20, 2026 (16 days left)'
- ],
- summary: 'To submit an assignment, go to LMS > My Courses, select your course, and click on the assignment.',
- citations: ['Source: LMS Portal'],
- followUps: ['Show assignment details', 'How do I submit?', 'Can I get an extension?']
- };
- } else if (lowerMessage.includes('timetable') || lowerMessage.includes('schedule') || lowerMessage.includes('class')) {
- return {
- text: 'Here is your class schedule for today (Monday):',
- type: 'schedule',
- summary: `🕐 **09:00 - 10:30** - Data Structures (Room 301)\n🕐 **11:00 - 12:30** - Database Systems (Lab 2)\n🕐 **02:00 - 03:30** - Web Development (Room 205)\n🕐 **04:00 - 05:30** - Operating Systems (Room 401)`,
- citations: ['Source: Academic Schedule'],
- followUps: ['Show full week schedule', 'Any class cancellations?', 'Download timetable PDF']
- };
- } else if (lowerMessage.includes('library')) {
- return {
- text: 'University Library Information:',
- type: 'info',
- summary: `📚 **Operating Hours:**\n• Monday - Friday: 8:00 AM - 8:00 PM\n• Saturday: 9:00 AM - 5:00 PM\n• Sunday: Closed\n\n📖 **Services Available:**\n• Book borrowing & returns\n• Study rooms (bookable)\n• Digital resources access\n• Printing & scanning`,
- citations: ['Source: Library Portal', 'Student Handbook 2026'],
- followUps: ['Search for a book', 'Book a study room', 'Check my borrowed books']
- };
- } else if (lowerMessage.includes('help') || lowerMessage.includes('how') || lowerMessage.includes('?')) {
- return {
- text: 'I can help you with various queries related to:',
- type: 'list',
- items: [
- '🎓 Academics (grades, CGPA, transcripts)',
- '📚 Courses (schedule, assignments, exams)',
- '💰 Finance (fees, payments, vouchers)',
- '✅ Attendance (status, marking, history)',
- '📖 Library (timings, book search, reservations)',
- '🎯 And much more!'
- ],
- summary: 'Just ask me anything in natural language, and I\'ll do my best to help!',
- citations: ['Source: Nexus AI Knowledge Base'],
- followUps: ['Show common questions', 'Connect with human support', 'Take a tour']
- };
- } else {
- return {
- text: 'I\'m not quite sure I understood that correctly. Could you please rephrase your question or try one of these common queries?',
- type: 'error',
- items: commonQuestions.slice(0, 3),
- actionButton: { label: 'Connect with Human Support', action: 'switch-human' },
- citations: ['Source: Nexus AI'],
- followUps: ['View all common questions', 'Start a new chat', 'Report an issue']
- };
+ if (mode === 'ai') {
+ // Restore history if available, otherwise show welcome message
+ if (aiChatHistory.length > 0) {
+ setMessages(aiChatHistory);
+ } else if (messages.length === 0) {
+ setMessages([
+ {
+ id: Date.now(),
+ text: `Hello ${user?.name || 'Student'}! I'm Nexus AI, your intelligent campus assistant. How can I help you today?`,
+ sender: 'ai',
+ timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ },
+ ]);
+ }
+ setSelectedChat({ name: 'Nexus AI Assistant', avatar: '🤖', status: 'online' });
}
- };
-
- const streamResponse = (responseText, callback) => {
- setIsTyping(false);
- setStreamingText('');
- const words = responseText.split(' ');
- let currentIndex = 0;
+ }, [mode]);
- const interval = setInterval(() => {
- if (currentIndex < words.length) {
- setStreamingText(prev => prev + (currentIndex > 0 ? ' ' : '') + words[currentIndex]);
- currentIndex++;
- } else {
- clearInterval(interval);
- callback();
- }
- }, 50);
- };
+ useEffect(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [messages]);
+ // Handlers
const handleSendMessage = () => {
if (!inputMessage.trim()) return;
- // Add user message with slide-up animation
- const userMsg = {
+ const newMessage = {
id: Date.now(),
text: inputMessage,
- isAi: false,
- timestamp: new Date().toLocaleTimeString(),
+ sender: 'user',
+ timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
};
- setMessages(prev => [...prev, userMsg]);
+
+ setMessages((prev) => [...prev, newMessage]);
setInputMessage('');
- setCharCount(0);
- setSuggestedQuestions([]);
- // Show typing indicator
- if (isAiMode) {
+ // Simulate AI response
+ if (mode === 'ai') {
+ setIsTyping(true);
setTimeout(() => {
- setIsTyping(true);
-
- // Simulate API delay
- setTimeout(() => {
- const response = getAiResponse(inputMessage);
-
- // Stream the response
- streamResponse(response.text, () => {
- const aiMsg = {
- id: Date.now() + 1,
- text: response.text,
- isAi: true,
- timestamp: new Date().toLocaleTimeString(),
- type: response.type,
- tableData: response.tableData,
- items: response.items,
- summary: response.summary,
- actionButton: response.actionButton,
- citations: response.citations,
- feedbacks: { thumbsUp: 0, thumbsDown: 0 },
- };
- setMessages(prev => [...prev, aiMsg]);
- setStreamingText('');
-
- // Set follow-up suggestions
- if (response.followUps) {
- setSuggestedQuestions(response.followUps);
- }
- });
- }, 1500);
- }, 100);
- }
- };
-
- const handleKeyPress = (e) => {
- if (e.key === 'Enter' && !e.shiftKey) {
- e.preventDefault();
- handleSendMessage();
+ const aiResponse = {
+ id: Date.now() + 1,
+ text: generateAIResponse(inputMessage),
+ sender: 'ai',
+ timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
+ };
+ setMessages((prev) => [...prev, aiResponse]);
+ setIsTyping(false);
+ }, 1500);
}
};
- const handleVoiceInput = () => {
- setIsRecording(!isRecording);
- if (!isRecording) {
- // Simulate voice recording
- setTimeout(() => {
- setInputMessage('What is my current CGPA?');
- setIsRecording(false);
- }, 3000);
+ const generateAIResponse = (userMessage) => {
+ const lowerMessage = userMessage.toLowerCase();
+ if (lowerMessage.includes('cgpa') || lowerMessage.includes('gpa')) {
+ return 'Your current CGPA is 3.85. You\'re doing great! Would you like to see a detailed breakdown?';
+ } else if (lowerMessage.includes('fee') || lowerMessage.includes('payment')) {
+ return 'You have PKR 15,000 in pending fees. The deadline is January 15, 2026. Would you like to make a payment?';
+ } else if (lowerMessage.includes('attendance')) {
+ return 'Your overall attendance is 88%. You need to maintain 75% minimum. Keep it up!';
+ } else if (lowerMessage.includes('assignment')) {
+ return 'You have 3 pending assignments:\n1. Database Normalization - Due Jan 20\n2. AI Project Proposal - Due Jan 22\n3. Web Dev Lab Task - Due Jan 25';
}
+ return 'I can help you with academics, fees, attendance, courses, and more. What would you like to know?';
};
- const handleNewChat = () => {
- const newConvId = Date.now();
- setConversations(prev => [
- { id: newConvId, title: 'New Conversation', lastMessage: '', timestamp: 'Just now', active: true },
- ...prev.map(c => ({ ...c, active: false }))
+ const handleContactClick = (contact) => {
+ setSelectedChat(contact);
+ setMessages([
+ {
+ id: 1,
+ text: `Hi! This is a conversation with ${contact.name}`,
+ sender: 'other',
+ timestamp: '10:00 AM',
+ },
+ {
+ id: 2,
+ text: 'Hello! How can I help you?',
+ sender: 'user',
+ timestamp: '10:05 AM',
+ },
]);
- setActiveConversation(newConvId);
- setMessages([{
- id: Date.now(),
- text: `Hello ${currentUser?.name || 'Student'}! 👋 I'm Nexus AI, your intelligent campus assistant. I'm here to help you with anything related to your academics, fees, attendance, courses, and more. How can I assist you today?`,
- isAi: true,
- timestamp: new Date().toLocaleTimeString(),
- isWelcome: true,
- }]);
- setSuggestedQuestions([]);
- if (isMobile) setDrawerOpen(false);
- };
-
- const handleDeleteConversation = (convId) => {
- setConversations(prev => prev.filter(c => c.id !== convId));
- if (activeConversation === convId && conversations.length > 1) {
- const nextConv = conversations.find(c => c.id !== convId);
- setActiveConversation(nextConv.id);
- }
};
- const handleQuickAction = (query) => {
- setInputMessage(query);
- setTimeout(() => handleSendMessage(), 100);
- };
-
- const handleFeedback = (messageId, type) => {
- setMessages(prev => prev.map(msg => {
- if (msg.id === messageId) {
- return {
- ...msg,
- feedbacks: {
- thumbsUp: type === 'up' ? 1 : 0,
- thumbsDown: type === 'down' ? 1 : 0,
- }
- };
- }
- return msg;
- }));
+ const handleGroupClick = (group) => {
+ setSelectedChat(group);
+ setMessages([
+ {
+ id: 1,
+ text: 'Welcome to the group!',
+ sender: 'other',
+ senderName: 'Admin',
+ timestamp: '9:00 AM',
+ },
+ {
+ id: 2,
+ text: 'Thanks for adding me!',
+ sender: 'user',
+ timestamp: '9:05 AM',
+ },
+ ]);
};
- const handleCitationClick = (citation) => {
- setCitationModal({
- open: true,
- content: citation,
- details: `This information was retrieved from: ${citation}\n\nRelevant excerpt:\n"The student's current CGPA is calculated based on all completed courses with their respective credit hours and grade points. The calculation follows the standard formula: Sum(Grade Points × Credit Hours) / Total Credit Hours."`
- });
- };
+ const handleCreateGroup = () => {
+ if (!newGroupName.trim() || selectedMembers.length < 2) return;
+
+ const newGroup = {
+ id: G,
+ name: newGroupName,
+ description: newGroupDescription,
+ avatar: '',
+ members: selectedMembers.length + 1, // +1 for creator
+ lastMessage: 'Group created',
+ lastTime: 'Just now',
+ };
- const handleExportChat = () => {
- const chatText = messages.map(m =>
- `[${m.timestamp}] ${m.isAi ? 'Nexus AI' : currentUser?.name}: ${m.text}`
- ).join('\n\n');
-
- const blob = new Blob([chatText], { type: 'text/plain' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = `nexus-chat-${Date.now()}.txt`;
- a.click();
+ setGroups((prev) => [...prev, newGroup]);
+ setCreateGroupOpen(false);
+ setNewGroupName('');
+ setNewGroupDescription('');
+ setSelectedMembers([]);
+ setCurrentTab(1); // Switch to groups tab
};
- const handleCopyChat = () => {
- const chatText = messages.map(m =>
- `[${m.timestamp}] ${m.isAi ? 'Nexus AI' : currentUser?.name}: ${m.text}`
- ).join('\n\n');
- navigator.clipboard.writeText(chatText);
+ const toggleMemberSelection = (contactId) => {
+ setSelectedMembers((prev) =>
+ prev.includes(contactId) ? prev.filter((id) => id !== contactId) : [...prev, contactId]
+ );
};
- const filteredConversations = conversations.filter(c =>
- c.title.toLowerCase().includes(searchQuery.toLowerCase())
+ const filteredContacts = contacts.filter((c) =>
+ c.name.toLowerCase().includes(searchQuery.toLowerCase())
);
- // Emojis for picker (simplified)
- const emojis = ['😊', '😂', '❤️', '👍', '🎉', '🔥', '✨', '💯', '🚀', '📚', '✅', '⚡'];
-
- // Show loading skeleton
- if (loading) {
- return (
-
-
-
- Chat Portal
-
-
- AI-powered assistant for all your queries
-
-
-
-
- );
- }
+ const filteredGroups = groups.filter((g) =>
+ g.name.toLowerCase().includes(searchQuery.toLowerCase())
+ );
- // Left Panel Component
- const LeftPanel = () => (
+ // Chat List Component
+ const ChatList = () => (
- {/* Chat Header */}
-
-
-
- Nexus AI Assistant
-
-
-
-
-
-
- {/* Toggle AI/Human */}
+ {/* Header */}
+
+
+
+ navigate('/dashboard')} sx={{ color: 'white' }}>
+
+
+
+ Nexus Chat
+
+
+
+
+ {
+ // Save AI chat history before switching
+ if (mode === 'ai' && messages.length > 0) {
+ setAiChatHistory(messages);
+ }
+ // Restore AI chat history when switching back
+ if (mode === 'human' && aiChatHistory.length > 0) {
+ setMessages(aiChatHistory);
+ } else {
+ setMessages([]);
+ }
+ setMode(mode === 'ai' ? 'human' : 'ai');
+ setSelectedChat(null);
+ }}
+ sx={{
+ color: 'white',
+ backgroundColor: 'rgba(255,255,255,0.15)',
+ '&:hover': { backgroundColor: 'rgba(255,255,255,0.25)' },
+ }}
+ >
+ {mode === 'ai' ? : }
+
+
+
+
+
+ {/* Mode Indicator */}
- setIsAiMode(true)}
+ {mode === 'ai' ? : }
+
+ {mode === 'ai' ? 'AI Assistant Mode' : 'Human Chat Mode'}
+
+
+
+
+ {/* Search */}
+ {mode === 'human' && (
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
sx={{
- flex: 1,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- gap: 0.5,
- py: 0.75,
- borderRadius: '6px',
- backgroundColor: isAiMode ? 'primary.main' : 'transparent',
- color: isAiMode ? 'white' : 'text.secondary',
- cursor: 'pointer',
- transition: 'all 0.3s',
+ backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : 'white',
+ borderRadius: '8px',
+ '& .MuiOutlinedInput-root': {
+ '& fieldset': { border: 'none' },
+ },
}}
- >
-
-
- AI Mode
-
-
- setIsAiMode(false)}
+ />
+
+ )}
+
+ {/* Tabs for Human Mode */}
+ {mode === 'human' && (
+
+ setCurrentTab(val)}
+ variant="fullWidth"
sx={{
- flex: 1,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- gap: 0.5,
- py: 0.75,
- borderRadius: '6px',
- backgroundColor: !isAiMode ? 'secondary.main' : 'transparent',
- color: !isAiMode ? 'white' : 'text.secondary',
- cursor: 'pointer',
- transition: 'all 0.3s',
+ '& .MuiTab-root': {
+ color: whatsappGreen,
+ fontWeight: 600,
+ },
+ '& .Mui-selected': {
+ color: whatsappGreen,
+ },
+ '& .MuiTabs-indicator': {
+ backgroundColor: whatsappGreen,
+ },
}}
>
-
-
- Human
-
-
+
+
+
-
-
- {/* New Chat Button */}
-
- }
- onClick={handleNewChat}
- sx={{
- py: 1.5,
- borderRadius: '8px',
- textTransform: 'none',
- fontWeight: 600,
- }}
- >
- New Chat
-
-
-
- {/* Search Conversations */}
-
- setSearchQuery(e.target.value)}
- InputProps={{
- startAdornment: (
-
-
-
- ),
- }}
- />
-
+ )}
- {/* Conversation List */}
+ {/* Chat List */}
-
- {filteredConversations.map((conv) => (
- handleDeleteConversation(conv.id)}
- sx={{ opacity: 0, '.MuiListItem-root:hover &': { opacity: 1 } }}
- >
-
-
- }
- >
- {
- setActiveConversation(conv.id);
- if (isMobile) setDrawerOpen(false);
- }}
- sx={{
- borderRadius: '8px',
- mx: 1,
- '&.Mui-selected': {
- backgroundColor: 'primary.light',
- '&:hover': {
- backgroundColor: 'primary.light',
- },
+ {mode === 'ai' ? (
+ {
+ if (messages.length === 0) {
+ setMessages([
+ {
+ id: Date.now(),
+ text: `Hello! I'm Nexus AI. How can I assist you?`,
+ sender: 'ai',
+ timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
},
- }}
- >
-
-
-
-
-
-
- {conv.title}
-
- }
- secondary={
-
- {conv.lastMessage}
-
- }
- />
-
-
- ))}
-
-
-
- {/* Quick Actions */}
-
- }
- sx={{ mb: 1, textTransform: 'none', justifyContent: 'flex-start' }}
- onClick={() => setAnchorEl(document.body)}
- >
- Common Questions
-
- }
- sx={{ textTransform: 'none', justifyContent: 'flex-start' }}
- >
- Help & Tutorial
-
+ ]);
+ }
+ setSelectedChat({ name: 'Nexus AI Assistant', avatar: '🤖', status: 'online' });
+ }}
+ sx={{
+ py: 1.5,
+ px: 2,
+ backgroundColor:
+ selectedChat?.name === 'Nexus AI Assistant'
+ ? theme.palette.mode === 'dark'
+ ? '#2A3942'
+ : '#F0F2F5'
+ : 'transparent',
+ '&:hover': {
+ backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : '#F5F6F6',
+ },
+ }}
+ >
+
+
+
+
+
+
+ Nexus AI Assistant
+
+ }
+ secondary={
+
+ Your intelligent campus companion
+
+ }
+ />
+
+ ) : (
+
+ {currentTab === 0
+ ? filteredContacts.map((contact) => (
+ handleContactClick(contact)}
+ selected={selectedChat?.id === contact.id}
+ sx={{
+ py: 1.5,
+ px: 2,
+ backgroundColor:
+ selectedChat?.id === contact.id
+ ? theme.palette.mode === 'dark'
+ ? '#2A3942'
+ : '#F0F2F5'
+ : 'transparent',
+ '&:hover': {
+ backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : '#F5F6F6',
+ },
+ }}
+ >
+
+
+ }
+ >
+ {contact.name[0]}
+
+
+
+
+ {contact.name}
+
+
+ {contact.lastTime}
+
+
+ }
+ secondary={
+
+
+ {contact.role}
+
+
+ {contact.lastMessage}
+
+
+ }
+ />
+
+ ))
+ : filteredGroups.map((group) => (
+ handleGroupClick(group)}
+ selected={selectedChat?.id === group.id}
+ sx={{
+ py: 1.5,
+ px: 2,
+ backgroundColor:
+ selectedChat?.id === group.id
+ ? theme.palette.mode === 'dark'
+ ? '#2A3942'
+ : '#F0F2F5'
+ : 'transparent',
+ '&:hover': {
+ backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : '#F5F6F6',
+ },
+ }}
+ >
+
+
+ {group.avatar}
+
+
+
+
+ {group.name}
+
+
+ {group.lastTime}
+
+
+ }
+ secondary={
+
+
+ {group.members} members
+
+
+ {group.lastMessage}
+
+
+ }
+ />
+
+ ))}
+
+ )}
-
- );
- return (
-
-
- {/* Left Panel - Desktop */}
- {!isMobile && (
-
-
+ {/* Create Group FAB */}
+ {mode === 'human' && currentTab === 1 && (
+
+ }
+ onClick={() => setCreateGroupOpen(true)}
+ sx={{
+ bgcolor: whatsappGreen,
+ color: 'white',
+ fontWeight: 600,
+ borderRadius: '25px',
+ py: 1.5,
+ '&:hover': {
+ bgcolor: whatsappDarkGreen,
+ },
+ }}
+ >
+ Create New Group
+
)}
+
+ );
- {/* Left Panel - Mobile Drawer */}
- setDrawerOpen(false)}
- >
-
-
-
- {/* Main Chat Area */}
-
- {/* Chat Header Bar */}
-
-
- {isMobile && (
- setDrawerOpen(true)}>
-
-
- )}
-
-
-
+ // Chat Window Component
+ const ChatWindow = () => (
+
+ {selectedChat ? (
+ <>
+ {/* Chat Header */}
+
+
+ {isMobile && (
+ setSelectedChat(null)} sx={{ color: 'white' }}>
+
+
+ )}
+
+ {selectedChat.avatar || selectedChat.name[0]}
-
-
-
- {isAiMode ? 'Nexus AI' : 'Support Team'}
-
-
-
-
-
-
-
-
- {isRecording ? : }
-
-
-
-
-
-
-
-
- setAnchorEl(e.currentTarget)}>
+
+
+ {selectedChat.name}
+
+
+ {mode === 'ai'
+ ? 'Online • Always available'
+ : selectedChat.members
+ ? `${selectedChat.members} members`
+ : selectedChat.status === 'online'
+ ? 'Online'
+ : 'Offline'}
+
+
+
+
+ {mode === 'human' && !selectedChat.members && (
+ <>
+
+
+
+
+
+
+ >
+ )}
+
-
+
-
- {/* Messages Container */}
-
- {messages.map((message, index) => (
-
- setHoveredMessage(message.id)}
- onMouseLeave={() => setHoveredMessage(null)}
- sx={{
- display: 'flex',
- justifyContent: message.isAi ? 'flex-start' : 'flex-end',
- mb: 3,
- animation: 'slideUp 0.3s ease',
- '@keyframes slideUp': {
- from: { transform: 'translateY(20px)', opacity: 0 },
- to: { transform: 'translateY(0)', opacity: 1 },
- },
- }}
- >
-
+
+ {messages.map((msg) => (
+
-
- {message.isAi ? : currentUser?.name[0]}
-
-
-
-
+ {msg.senderName}
+
+ )}
+
+ {msg.text}
+
+
-
- {message.text}
-
-
- {/* Welcome Quick Actions */}
- {message.isWelcome && (
-
- {quickActions.map((action, idx) => (
- handleQuickAction(action.label)}
- sx={{
- cursor: 'pointer',
- '&:hover': { backgroundColor: 'primary.light' },
- }}
- />
- ))}
-
- )}
-
- {/* Table Data */}
- {message.type === 'table' && message.tableData && (
-
-
-
-
- Course
- Grade
- GPA
- Credits
-
- {message.tableData.map((row, idx) => (
-
- {row.course}
- {row.grade}
- {row.gpa}
- {row.credits}
-
- ))}
-
-
-
- )}
-
- {/* List Items */}
- {message.items && (
-
- {message.items.map((item, idx) => (
-
- {item}
-
- ))}
-
- )}
-
- {/* Summary Text */}
- {message.summary && (
-
- {message.summary}
-
- )}
-
- {/* Action Button */}
- {message.actionButton && (
-
- )}
-
-
- {/* Citations */}
- {message.isAi && message.citations && (
-
- {message.citations.map((citation, idx) => (
- handleCitationClick(citation)}
- sx={{
- fontSize: '0.7rem',
- height: 24,
- cursor: 'pointer',
- '&:hover': { backgroundColor: 'action.hover' },
- }}
- />
- ))}
-
- )}
-
- {/* Feedback & Timestamp */}
-
-
- {message.timestamp}
-
-
- {message.isAi && hoveredMessage === message.id && (
-
- handleFeedback(message.id, 'up')}
- color={message.feedbacks?.thumbsUp ? 'primary' : 'default'}
- >
-
-
- handleFeedback(message.id, 'down')}
- color={message.feedbacks?.thumbsDown ? 'error' : 'default'}
- >
-
-
-
- )}
-
+ {msg.timestamp}
+
+
+
+ ))}
- {/* Follow-up Questions */}
- {message.isAi && index === messages.length - 1 && suggestedQuestions.length > 0 && (
-
- {suggestedQuestions.map((question, idx) => (
- handleQuickAction(question)}
- sx={{
- cursor: 'pointer',
- '&:hover': { backgroundColor: 'primary.light' },
- }}
- />
- ))}
-
- )}
-
-
-
-
- ))}
+ {/* Typing Indicator */}
+ {isTyping && (
+
+
+ {[0, 0.2, 0.4].map((delay, i) => (
+
+ ))}
+
+
+ )}
+
+
+
+
- {/* Typing Indicator */}
- {isTyping && (
-
-
-
-
-
+
+
+
+
+
+
+
+ setInputMessage(e.target.value)}
+ onKeyPress={(e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ }}
sx={{
- p: 2,
- backgroundColor: 'grey.100',
- borderRadius: '12px 12px 12px 4px',
- display: 'flex',
- alignItems: 'center',
- gap: 0.5,
+ '& .MuiOutlinedInput-root': {
+ borderRadius: '25px',
+ backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : 'white',
+ '& fieldset': { border: 'none' },
+ },
}}
- >
-
-
-
+ {inputMessage.trim() ? (
+
-
- Nexus is typing...
-
-
-
- )}
-
- {/* Streaming Text */}
- {streamingText && (
-
-
-
-
-
-
- {streamingText}
-
-
-
-
- )}
-
-
-
-
- {/* Input Area */}
+ >
+
+
+ ) : (
+
+
+
+ )}
+
+
+ >
+ ) : (
- {/* Suggestions Row */}
- {inputMessage === '' && suggestedQuestions.length === 0 && messages.length > 0 && (
-
- {['What is my CGPA?', 'Show my timetable', 'Check dues'].map((suggestion, idx) => (
- handleQuickAction(suggestion)}
- sx={{
- cursor: 'pointer',
- '&:hover': { backgroundColor: 'primary.light' },
- }}
- />
- ))}
-
- )}
+
+
+
+
+ Nexus Chat Portal
+
+
+ {mode === 'ai'
+ ? 'Start a conversation with Nexus AI to get instant help with your academics'
+ : 'Select a contact or group to start chatting'}
+
+
+ )}
+
+ );
-
- {/* Emoji Picker */}
-
- setShowEmojiPicker(!showEmojiPicker)} size="small">
-
-
-
- (
+
-
-
-
- {/* Text Input */}
- {
- setInputMessage(e.target.value);
- setCharCount(e.target.value.length);
- }}
- onKeyPress={handleKeyPress}
- multiline
- maxRows={5}
- variant="outlined"
- sx={{
- '& .MuiOutlinedInput-root': {
- borderRadius: '12px',
- },
- }}
- />
-
- {/* Voice Recording Animation */}
- {isRecording && (
-
-
-
- Recording... Click to stop
-
-
- )}
-
- {/* Send Button */}
-
-
-
-
-
- {/* Character Count */}
- {charCount > 500 && (
-
- {charCount} characters
+
+ {contact.name[0]}
+
+
+
+ ))}
+
+
+ {selectedMembers.length} member(s) selected
- )}
-
-
-
- {/* Citation Modal */}
-
+
+
+
+
+
+
+
+
+ );
- {/* More Options Menu */}
-
+ return (
+
+ {(!isMobile || !selectedChat) && }
+ {(!isMobile || selectedChat) && }
+
-
);
};
diff --git a/src/pages/Chat/ChatPortal.jsx.bak b/src/pages/Chat/ChatPortal.jsx.bak
new file mode 100644
index 0000000..46a8748
--- /dev/null
+++ b/src/pages/Chat/ChatPortal.jsx.bak
@@ -0,0 +1,1463 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ TextField,
+ IconButton,
+ Avatar,
+ Chip,
+ Switch,
+ Divider,
+ Paper,
+ Button,
+ List,
+ ListItem,
+ ListItemButton,
+ ListItemText,
+ ListItemAvatar,
+ InputAdornment,
+ Badge,
+ Drawer,
+ useMediaQuery,
+ useTheme,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Table,
+ TableBody,
+ TableRow,
+ TableCell,
+ Stack,
+ Tabs,
+ Tab,
+ Tooltip,
+ Menu,
+ MenuItem,
+ Fade,
+ Collapse,
+} from '@mui/material';
+import {
+ Send,
+ SmartToy,
+ Person,
+ Add,
+ Search,
+ Settings,
+ Mic,
+ AttachFile,
+ MoreVert,
+ Circle,
+ Delete,
+ ThumbUp,
+ ThumbDown,
+ Download,
+ ContentCopy,
+ HelpOutline,
+ EmojiEmotions,
+ Stop,
+ Menu as MenuIcon,
+ Close,
+ Lightbulb,
+ School,
+ Event,
+ AccountBalance,
+ TrendingUp,
+ Groups,
+} from '@mui/icons-material';
+import { useAuth } from '../../contexts/AuthContext';
+import { currentUser } from '../../data/dummyData';
+import { ChatSkeleton } from '../../components/Common/LoadingSkeleton';
+import { pageTransition, staggerContainer, fadeInUp } from '../../utils/animations';
+
+
+const ChatPortal = () => {
+ const { user } = useAuth();
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
+ const [loading, setLoading] = useState(true);
+
+ // State management
+ const [conversations, setConversations] = useState([
+ { id: 1, title: 'What is my CGPA?', lastMessage: 'Your current CGPA is 3.85', timestamp: '2 hours ago', active: true },
+ { id: 2, title: 'Show my timetable', lastMessage: 'Here is your class schedule for this week', timestamp: 'Yesterday', active: false },
+ { id: 3, title: 'Check dues', lastMessage: 'You have PKR 15,000 in pending dues', timestamp: '2 days ago', active: false },
+ ]);
+ const [directChats, setDirectChats] = useState([
+ { id: 'D1', title: 'Ayesha Khan', lastMessage: 'Can you share the notes?', timestamp: '5 min ago', status: 'online', role: 'Class Rep' },
+ { id: 'D2', title: 'Dr. Sarah Ahmed', lastMessage: 'Please review the draft', timestamp: 'Yesterday', status: 'online', role: 'Advisor' },
+ { id: 'D3', title: 'Ali Ahmed', lastMessage: 'Group meeting at 6?', timestamp: '2 days ago', status: 'away', role: 'Peer' },
+ ]);
+ const [groupChats, setGroupChats] = useState([
+ { id: 'G1', title: 'BSIT Batch 2024', lastMessage: 'Quiz on Friday', timestamp: 'Yesterday', members: 48 },
+ { id: 'G2', title: 'Database Project Team', lastMessage: 'Schema finalized', timestamp: '2 days ago', members: 5 },
+ ]);
+ const [activeConversation, setActiveConversation] = useState(1);
+ const [messages, setMessages] = useState([]);
+ const [inputMessage, setInputMessage] = useState('');
+ const [isAiMode, setIsAiMode] = useState(true);
+ const [isTyping, setIsTyping] = useState(false);
+ const [streamingText, setStreamingText] = useState('');
+ const [isRecording, setIsRecording] = useState(false);
+ const [drawerOpen, setDrawerOpen] = useState(false);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [citationModal, setCitationModal] = useState({ open: false, content: '' });
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [showEmojiPicker, setShowEmojiPicker] = useState(false);
+ const [charCount, setCharCount] = useState(0);
+ const [hoveredMessage, setHoveredMessage] = useState(null);
+ const [leftTab, setLeftTab] = useState(0); // 0: Chats, 1: AI History
+
+ const aiHistory = [
+ { id: 'AI-001', title: 'Fee deadline inquiry', lastMessage: 'Deadline is Jan 15', timestamp: '2 days ago' },
+ { id: 'AI-002', title: 'Library timings', lastMessage: 'Open 8 AM - 8 PM', timestamp: '5 days ago' },
+ ];
+
+ const messagesEndRef = useRef(null);
+ const chatContainerRef = useRef(null);
+
+ // Loading effect
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setLoading(false);
+ }, 1200);
+ return () => clearTimeout(timer);
+ }, []);
+
+ // Quick action queries
+ const quickActions = [
+ { label: 'What is my CGPA?', icon: },
+ { label: 'View Attendance', icon: },
+ { label: 'Fee Status', icon: },
+ { label: 'Course Schedule', icon: },
+ ];
+
+ // Suggested follow-ups
+ const [suggestedQuestions, setSuggestedQuestions] = useState([
+ "What is my attendance percentage?",
+ "Show my upcoming assignments",
+ "When is the next exam?"
+ ]);
+
+ // Common questions
+ const commonQuestions = [
+ "How do I submit an assignment?",
+ "What are my library timings?",
+ "How do I pay my fees?",
+ "How do I mark attendance?",
+ "Where can I see my transcript?"
+ ];
+
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ };
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages, streamingText]);
+
+ // Initialize with welcome message
+ useEffect(() => {
+ if (messages.length === 0) {
+ setMessages([{
+ id: Date.now(),
+ text: `Hello ${currentUser?.name || 'Student'}! 👋 I'm Nexus AI, your intelligent campus assistant. I'm here to help you with anything related to your academics, fees, attendance, courses, and more. How can I assist you today?`,
+ isAi: true,
+ timestamp: new Date().toLocaleTimeString(),
+ isWelcome: true,
+ }]);
+ }
+ }, []);
+
+ const getAiResponse = (userMessage) => {
+ const lowerMessage = userMessage.toLowerCase();
+
+ // Context-aware responses with special content
+ if (lowerMessage.includes('cgpa') || lowerMessage.includes('gpa') || lowerMessage.includes('grade')) {
+ return {
+ text: 'Based on your academic record, here are your current grades:',
+ type: 'table',
+ tableData: [
+ { course: 'Data Structures', grade: 'A', gpa: '4.0', credits: '3' },
+ { course: 'Database Systems', grade: 'A-', gpa: '3.7', credits: '3' },
+ { course: 'Web Development', grade: 'B+', gpa: '3.3', credits: '3' },
+ { course: 'Operating Systems', grade: 'A', gpa: '4.0', credits: '4' },
+ ],
+ summary: 'Your current CGPA is **3.85** which is excellent! Keep up the great work.',
+ citations: ['Source: Academic Transcript', 'Updated: Jan 2026'],
+ followUps: ['How can I improve my grades?', 'Show my semester-wise performance', 'What is the highest CGPA in my class?']
+ };
+ } else if (lowerMessage.includes('fee') || lowerMessage.includes('payment') || lowerMessage.includes('dues')) {
+ return {
+ text: 'I found the following information about your fee status:',
+ type: 'action',
+ summary: `• Total Fees: PKR 85,000\n• Paid: PKR 70,000\n• Outstanding: PKR 15,000\n• Due Date: January 15, 2026`,
+ actionButton: { label: 'Pay Now', action: 'navigate-fees' },
+ citations: ['Source: Finance Portal', 'Last Updated: Jan 4, 2026'],
+ followUps: ['What happens if I pay late?', 'Can I get a payment plan?', 'Show payment history']
+ };
+ } else if (lowerMessage.includes('attendance')) {
+ return {
+ text: 'Here is your current attendance summary:',
+ type: 'data',
+ summary: `• Overall Attendance: **87%**\n• Data Structures: 92%\n• Database Systems: 85%\n• Web Development: 88%\n• Operating Systems: 83%\n\n⚠️ Note: Operating Systems attendance is below the required 85% threshold.`,
+ citations: ['Source: Attendance System', 'Real-time Data'],
+ followUps: ['How many more classes can I miss?', 'Mark my attendance now', 'Request attendance condonation']
+ };
+ } else if (lowerMessage.includes('assignment') || lowerMessage.includes('homework')) {
+ return {
+ text: 'You have 3 upcoming assignments:',
+ type: 'list',
+ items: [
+ '📝 **Database Design Project** - Due: Jan 10, 2026 (6 days left)',
+ '💻 **Web Dev Portfolio** - Due: Jan 15, 2026 (11 days left)',
+ '🧮 **Algorithm Analysis** - Due: Jan 20, 2026 (16 days left)'
+ ],
+ summary: 'To submit an assignment, go to LMS > My Courses, select your course, and click on the assignment.',
+ citations: ['Source: LMS Portal'],
+ followUps: ['Show assignment details', 'How do I submit?', 'Can I get an extension?']
+ };
+ } else if (lowerMessage.includes('timetable') || lowerMessage.includes('schedule') || lowerMessage.includes('class')) {
+ return {
+ text: 'Here is your class schedule for today (Monday):',
+ type: 'schedule',
+ summary: `🕐 **09:00 - 10:30** - Data Structures (Room 301)\n🕐 **11:00 - 12:30** - Database Systems (Lab 2)\n🕐 **02:00 - 03:30** - Web Development (Room 205)\n🕐 **04:00 - 05:30** - Operating Systems (Room 401)`,
+ citations: ['Source: Academic Schedule'],
+ followUps: ['Show full week schedule', 'Any class cancellations?', 'Download timetable PDF']
+ };
+ } else if (lowerMessage.includes('library')) {
+ return {
+ text: 'University Library Information:',
+ type: 'info',
+ summary: `📚 **Operating Hours:**\n• Monday - Friday: 8:00 AM - 8:00 PM\n• Saturday: 9:00 AM - 5:00 PM\n• Sunday: Closed\n\n📖 **Services Available:**\n• Book borrowing & returns\n• Study rooms (bookable)\n• Digital resources access\n• Printing & scanning`,
+ citations: ['Source: Library Portal', 'Student Handbook 2026'],
+ followUps: ['Search for a book', 'Book a study room', 'Check my borrowed books']
+ };
+ } else if (lowerMessage.includes('help') || lowerMessage.includes('how') || lowerMessage.includes('?')) {
+ return {
+ text: 'I can help you with various queries related to:',
+ type: 'list',
+ items: [
+ '🎓 Academics (grades, CGPA, transcripts)',
+ '📚 Courses (schedule, assignments, exams)',
+ '💰 Finance (fees, payments, vouchers)',
+ '✅ Attendance (status, marking, history)',
+ '📖 Library (timings, book search, reservations)',
+ '🎯 And much more!'
+ ],
+ summary: 'Just ask me anything in natural language, and I\'ll do my best to help!',
+ citations: ['Source: Nexus AI Knowledge Base'],
+ followUps: ['Show common questions', 'Connect with human support', 'Take a tour']
+ };
+ } else {
+ return {
+ text: 'I\'m not quite sure I understood that correctly. Could you please rephrase your question or try one of these common queries?',
+ type: 'error',
+ items: commonQuestions.slice(0, 3),
+ actionButton: { label: 'Connect with Human Support', action: 'switch-human' },
+ citations: ['Source: Nexus AI'],
+ followUps: ['View all common questions', 'Start a new chat', 'Report an issue']
+ };
+ }
+ };
+
+ const streamResponse = (responseText, callback) => {
+ setIsTyping(false);
+ setStreamingText('');
+ const words = responseText.split(' ');
+ let currentIndex = 0;
+
+ const interval = setInterval(() => {
+ if (currentIndex < words.length) {
+ setStreamingText(prev => prev + (currentIndex > 0 ? ' ' : '') + words[currentIndex]);
+ currentIndex++;
+ } else {
+ clearInterval(interval);
+ callback();
+ }
+ }, 50);
+ };
+
+ const handleSendMessage = () => {
+ if (!inputMessage.trim()) return;
+
+ // Add user message with slide-up animation
+ const userMsg = {
+ id: Date.now(),
+ text: inputMessage,
+ isAi: false,
+ timestamp: new Date().toLocaleTimeString(),
+ };
+ setMessages(prev => [...prev, userMsg]);
+ setInputMessage('');
+ setCharCount(0);
+ setSuggestedQuestions([]);
+
+ if (isAiMode) {
+ setConversations(prev => prev.map(c =>
+ c.id === activeConversation
+ ? { ...c, lastMessage: userMsg.text, timestamp: 'Just now' }
+ : c
+ ));
+ } else if (leftTab === 0) {
+ setDirectChats(prev => prev.map(c =>
+ c.id === activeConversation
+ ? { ...c, lastMessage: userMsg.text, timestamp: 'Just now' }
+ : c
+ ));
+ } else {
+ setGroupChats(prev => prev.map(c =>
+ c.id === activeConversation
+ ? { ...c, lastMessage: userMsg.text, timestamp: 'Just now' }
+ : c
+ ));
+ }
+
+ // Show typing indicator
+ if (isAiMode) {
+ setTimeout(() => {
+ setIsTyping(true);
+
+ // Simulate API delay
+ setTimeout(() => {
+ const response = getAiResponse(inputMessage);
+
+ // Stream the response
+ streamResponse(response.text, () => {
+ const aiMsg = {
+ id: Date.now() + 1,
+ text: response.text,
+ isAi: true,
+ timestamp: new Date().toLocaleTimeString(),
+ type: response.type,
+ tableData: response.tableData,
+ items: response.items,
+ summary: response.summary,
+ actionButton: response.actionButton,
+ citations: response.citations,
+ feedbacks: { thumbsUp: 0, thumbsDown: 0 },
+ };
+ setMessages(prev => [...prev, aiMsg]);
+ setStreamingText('');
+
+ // Set follow-up suggestions
+ if (response.followUps) {
+ setSuggestedQuestions(response.followUps);
+ }
+ });
+ }, 1500);
+ }, 100);
+ }
+ };
+
+ const handleKeyPress = (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ const handleVoiceInput = () => {
+ setIsRecording(!isRecording);
+ if (!isRecording) {
+ // Simulate voice recording
+ setTimeout(() => {
+ setInputMessage('What is my current CGPA?');
+ setIsRecording(false);
+ }, 3000);
+ }
+ };
+
+ const handleNewChat = () => {
+ if (isAiMode) {
+ const newConvId = Date.now();
+ setConversations(prev => [
+ { id: newConvId, title: 'New Conversation', lastMessage: '', timestamp: 'Just now', active: true },
+ ...prev.map(c => ({ ...c, active: false }))
+ ]);
+ setActiveConversation(newConvId);
+ setMessages([{
+ id: Date.now(),
+ text: `Hello ${currentUser?.name || 'Student'}! 👋 I'm Nexus AI, your intelligent campus assistant. I'm here to help you with anything related to your academics, fees, attendance, courses, and more. How can I assist you today?`,
+ isAi: true,
+ timestamp: new Date().toLocaleTimeString(),
+ isWelcome: true,
+ }]);
+ setSuggestedQuestions([]);
+ } else {
+ const newDirectId = `D-${Date.now()}`;
+ setDirectChats(prev => [
+ { id: newDirectId, title: 'New Contact', lastMessage: '', timestamp: 'Just now', status: 'online', role: 'Peer' },
+ ...prev,
+ ]);
+ setActiveConversation(newDirectId);
+ setMessages([{
+ id: Date.now(),
+ text: 'You can start a new conversation here. Share your message and stay connected.',
+ isAi: false,
+ timestamp: new Date().toLocaleTimeString(),
+ }]);
+ }
+ if (isMobile) setDrawerOpen(false);
+ };
+
+ const handleDeleteConversation = (convId) => {
+ if (isAiMode) {
+ setConversations(prev => prev.filter(c => c.id !== convId));
+ if (activeConversation === convId && conversations.length > 1) {
+ const nextConv = conversations.find(c => c.id !== convId);
+ if (nextConv) setActiveConversation(nextConv.id);
+ }
+ } else {
+ setDirectChats(prev => prev.filter(c => c.id !== convId));
+ if (activeConversation === convId && directChats.length > 1) {
+ const nextChat = directChats.find(c => c.id !== convId);
+ if (nextChat) setActiveConversation(nextChat.id);
+ }
+ }
+ };
+
+ const handleQuickAction = (query) => {
+ setInputMessage(query);
+ setTimeout(() => handleSendMessage(), 100);
+ };
+
+ const handleFeedback = (messageId, type) => {
+ setMessages(prev => prev.map(msg => {
+ if (msg.id === messageId) {
+ return {
+ ...msg,
+ feedbacks: {
+ thumbsUp: type === 'up' ? 1 : 0,
+ thumbsDown: type === 'down' ? 1 : 0,
+ }
+ };
+ }
+ return msg;
+ }));
+ };
+
+ const handleCitationClick = (citation) => {
+ setCitationModal({
+ open: true,
+ content: citation,
+ details: `This information was retrieved from: ${citation}\n\nRelevant excerpt:\n"The student's current CGPA is calculated based on all completed courses with their respective credit hours and grade points. The calculation follows the standard formula: Sum(Grade Points × Credit Hours) / Total Credit Hours."`
+ });
+ };
+
+ const handleExportChat = () => {
+ const chatText = messages.map(m =>
+ `[${m.timestamp}] ${m.isAi ? 'Nexus AI' : currentUser?.name}: ${m.text}`
+ ).join('\n\n');
+
+ const blob = new Blob([chatText], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `nexus-chat-${Date.now()}.txt`;
+ a.click();
+ };
+
+ const handleCopyChat = () => {
+ const chatText = messages.map(m =>
+ `[${m.timestamp}] ${m.isAi ? 'Nexus AI' : currentUser?.name}: ${m.text}`
+ ).join('\n\n');
+ navigator.clipboard.writeText(chatText);
+ };
+
+ const filteredConversations = conversations.filter(c =>
+ c.title.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ const filteredDirectChats = directChats.filter(c =>
+ c.title.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ const filteredGroupChats = groupChats.filter(c =>
+ c.title.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ const activeUsers = isAiMode
+ ? [
+ { id: 'A1', name: 'Nexus AI', status: 'online' },
+ { id: 'A2', name: 'Finance Bot', status: 'online' },
+ { id: 'A3', name: 'Academic Assistant', status: 'away' },
+ ]
+ : [
+ { id: 'U1', name: 'Ayesha Khan', status: 'online' },
+ { id: 'U2', name: 'Dr. Sarah Ahmed', status: 'online' },
+ { id: 'U3', name: 'Ali Ahmed', status: 'away' },
+ ];
+
+ const leftItems = isAiMode
+ ? (leftTab === 0 ? filteredConversations : aiHistory)
+ : (leftTab === 0 ? filteredDirectChats : filteredGroupChats);
+
+ const activeItem = leftItems.find((item) => item.id === activeConversation) || leftItems[0];
+
+ useEffect(() => {
+ if (leftItems.length > 0 && !leftItems.find(item => item.id === activeConversation)) {
+ setActiveConversation(leftItems[0].id);
+ }
+ }, [isAiMode, leftTab, leftItems, activeConversation]);
+
+ // Emojis for picker (simplified)
+ const emojis = ['😊', '😂', '❤️', '👍', '🎉', '🔥', '✨', '💯', '🚀', '📚', '✅', '⚡'];
+
+ // Show loading skeleton
+ if (loading) {
+ return (
+
+
+
+ Chat Portal
+
+
+ AI-powered assistant for all your queries
+
+
+
+
+ );
+ }
+
+ // Left Panel Component
+ const LeftPanel = () => {
+ return (
+
+ {/* Chat Header */}
+
+
+
+ {isAiMode ? 'Nexus AI Assistant' : 'P2P Messaging'}
+
+
+
+
+
+
+ {/* Toggle AI/Human */}
+
+ { setIsAiMode(true); setLeftTab(0); }}
+ sx={{
+ flex: 1,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ gap: 0.5,
+ py: 0.75,
+ borderRadius: '6px',
+ backgroundColor: isAiMode ? 'primary.main' : 'transparent',
+ color: isAiMode ? 'white' : 'text.secondary',
+ cursor: 'pointer',
+ transition: 'all 0.3s',
+ }}
+ >
+
+
+ AI Mode
+
+
+ { setIsAiMode(false); setLeftTab(0); }}
+ sx={{
+ flex: 1,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ gap: 0.5,
+ py: 0.75,
+ borderRadius: '6px',
+ backgroundColor: !isAiMode ? 'secondary.main' : 'transparent',
+ color: !isAiMode ? 'white' : 'text.secondary',
+ cursor: 'pointer',
+ transition: 'all 0.3s',
+ }}
+ >
+
+
+ Human
+
+
+
+
+
+ {/* New Chat Button */}
+
+ }
+ onClick={handleNewChat}
+ sx={{
+ py: 1.5,
+ borderRadius: '8px',
+ textTransform: 'none',
+ fontWeight: 600,
+ }}
+ >
+ {isAiMode ? 'New Chat' : (leftTab === 1 ? 'New Group' : 'New Message')}
+
+
+
+ {/* Search Conversations */}
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ {/* Tabs */}
+
+ setLeftTab(v)} variant="fullWidth">
+
+
+
+
+
+ {/* Active Users */}
+
+
+ {isAiMode ? 'Active Assistants' : 'Active Users'}
+
+
+ {activeUsers.map((user) => (
+ }
+ sx={{
+ mb: 0.5,
+ '& .MuiChip-icon': {
+ color: user.status === 'online' ? 'success.main' : 'warning.main',
+ },
+ }}
+ />
+ ))}
+
+
+
+ {/* Conversation List */}
+
+
+ {leftItems.map((conv) => (
+ handleDeleteConversation(conv.id)}
+ sx={{ opacity: 0, '.MuiListItem-root:hover &': { opacity: 1 } }}
+ >
+
+
+ ) : null
+ }
+ >
+ {
+ setActiveConversation(conv.id);
+ if (isMobile) setDrawerOpen(false);
+ }}
+ sx={{
+ borderRadius: '8px',
+ mx: 1,
+ '&.Mui-selected': {
+ backgroundColor: 'primary.light',
+ '&:hover': {
+ backgroundColor: 'primary.light',
+ },
+ },
+ }}
+ >
+
+
+ {isAiMode ? : leftTab === 1 ? : }
+
+
+
+ {conv.title}
+
+ }
+ secondary={
+
+ {conv.lastMessage}
+
+ }
+ />
+
+
+ ))}
+
+
+
+ {/* Quick Actions */}
+
+ }
+ sx={{ mb: 1, textTransform: 'none', justifyContent: 'flex-start' }}
+ onClick={() => setAnchorEl(document.body)}
+ >
+ Common Questions
+
+ }
+ sx={{ textTransform: 'none', justifyContent: 'flex-start' }}
+ >
+ Help & Tutorial
+
+
+
+ );
+ };
+
+ return (
+
+
+ {/* Left Panel - Desktop */}
+ {!isMobile && (
+
+
+
+ )}
+
+ {/* Left Panel - Mobile Drawer */}
+ setDrawerOpen(false)}
+ >
+
+
+
+ {/* Main Chat Area */}
+
+ {/* Chat Header Bar */}
+
+
+ {isMobile && (
+ setDrawerOpen(true)}>
+
+
+ )}
+
+
+
+
+
+
+
+ {isAiMode ? 'Nexus AI' : (activeItem?.title || (leftTab === 1 ? 'Group Chat' : 'Direct Message'))}
+
+
+
+
+
+
+
+
+ {isRecording ? : }
+
+
+
+
+
+
+
+
+ setAnchorEl(e.currentTarget)}>
+
+
+
+
+
+
+ {/* Messages Container */}
+
+ {messages.map((message, index) => (
+
+ setHoveredMessage(message.id)}
+ onMouseLeave={() => setHoveredMessage(null)}
+ sx={{
+ display: 'flex',
+ justifyContent: message.isAi ? 'flex-start' : 'flex-end',
+ mb: 2,
+ }}
+ >
+
+
+ {message.isAi ? : currentUser?.name[0]}
+
+
+
+
+
+ {message.text}
+
+
+ {/* Welcome Quick Actions */}
+ {message.isWelcome && (
+
+ {quickActions.map((action, idx) => (
+ handleQuickAction(action.label)}
+ sx={{
+ cursor: 'pointer',
+ '&:hover': { backgroundColor: 'primary.light' },
+ }}
+ />
+ ))}
+
+ )}
+
+ {/* Table Data */}
+ {message.type === 'table' && message.tableData && (
+
+
+
+
+ Course
+ Grade
+ GPA
+ Credits
+
+ {message.tableData.map((row, idx) => (
+
+ {row.course}
+ {row.grade}
+ {row.gpa}
+ {row.credits}
+
+ ))}
+
+
+
+ )}
+
+ {/* List Items */}
+ {message.items && (
+
+ {message.items.map((item, idx) => (
+
+ {item}
+
+ ))}
+
+ )}
+
+ {/* Summary Text */}
+ {message.summary && (
+
+ {message.summary}
+
+ )}
+
+ {/* Action Button */}
+ {message.actionButton && (
+
+ )}
+
+
+ {/* Citations */}
+ {message.isAi && message.citations && (
+
+ {message.citations.map((citation, idx) => (
+
+ handleCitationClick(citation)}
+ sx={{
+ fontSize: '0.7rem',
+ height: 24,
+ cursor: 'pointer',
+ borderColor: 'primary.main',
+ color: 'primary.main',
+ transition: 'all 0.2s',
+ '&:hover': {
+ backgroundColor: 'primary.light',
+ transform: 'scale(1.05)',
+ },
+ }}
+ />
+
+ ))}
+
+ )}
+
+ {/* Feedback & Timestamp */}
+
+
+ {message.timestamp}
+
+
+ {message.isAi && hoveredMessage === message.id && (
+
+ handleFeedback(message.id, 'up')}
+ color={message.feedbacks?.thumbsUp ? 'primary' : 'default'}
+ >
+
+
+ handleFeedback(message.id, 'down')}
+ color={message.feedbacks?.thumbsDown ? 'error' : 'default'}
+ >
+
+
+
+ )}
+
+
+ {/* Follow-up Questions */}
+ {message.isAi && index === messages.length - 1 && suggestedQuestions.length > 0 && (
+
+ {suggestedQuestions.map((question, idx) => (
+ handleQuickAction(question)}
+ sx={{
+ cursor: 'pointer',
+ '&:hover': { backgroundColor: 'primary.light' },
+ }}
+ />
+ ))}
+
+ )}
+
+
+
+
+ ))}
+
+ {/* Typing Indicator */}
+ {isTyping && (
+
+
+
+
+
+
+
+
+
+
+ AI is thinking...
+
+
+
+
+ )}
+
+ {/* Streaming Text */}
+ {streamingText && (
+
+
+
+
+
+
+
+ {streamingText}
+
+
+
+
+
+ )}
+
+
+
+
+ {/* Input Area */}
+
+ {/* Suggestions Row */}
+ {inputMessage === '' && suggestedQuestions.length === 0 && messages.length > 0 && (
+
+ {['What is my CGPA?', 'Show my timetable', 'Check dues'].map((suggestion, idx) => (
+ handleQuickAction(suggestion)}
+ sx={{
+ cursor: 'pointer',
+ '&:hover': { backgroundColor: 'primary.light' },
+ }}
+ />
+ ))}
+
+ )}
+
+
+ {/* Emoji Picker */}
+
+ setShowEmojiPicker(!showEmojiPicker)} size="small" sx={{ color: '#FFD700' }}>
+
+
+
+
+ {emojis.map((emoji, idx) => (
+ {
+ setInputMessage(prev => prev + emoji);
+ setShowEmojiPicker(false);
+ }}
+ sx={{
+ cursor: 'pointer',
+ fontSize: 24,
+ textAlign: 'center',
+ '&:hover': { backgroundColor: 'action.hover', borderRadius: 1 },
+ }}
+ >
+ {emoji}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ {/* Text Input */}
+ {
+ setInputMessage(e.target.value);
+ setCharCount(e.target.value.length);
+ }}
+ onKeyPress={handleKeyPress}
+ multiline
+ maxRows={3}
+ variant="standard"
+ InputProps={{
+ disableUnderline: true,
+ }}
+ sx={{
+ '& .MuiInputBase-input': {
+ fontSize: '0.95rem',
+ py: 0.5,
+ },
+ }}
+ />
+
+ {/* Voice Recording Animation */}
+ {isRecording && (
+
+
+
+ Recording... Click to stop
+
+
+ )}
+
+ {/* Send/Mic Button */}
+ {inputMessage.trim() ? (
+
+
+
+ ) : (
+
+
+ {isRecording ? : }
+
+
+ )}
+
+
+ {/* Character Count */}
+ {charCount > 500 && (
+
+ {charCount} characters
+
+ )}
+
+
+
+ {/* Citation Modal */}
+
+
+ {/* More Options Menu */}
+
+
+
+ );
+};
+
+export default ChatPortal;
diff --git a/src/pages/Grievances/EnhancedGrievances.jsx b/src/pages/Grievances/EnhancedGrievances.jsx
new file mode 100644
index 0000000..c579d81
--- /dev/null
+++ b/src/pages/Grievances/EnhancedGrievances.jsx
@@ -0,0 +1,569 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Typography,
+ Paper,
+ Button,
+ Stack,
+ TextField,
+ Chip,
+ Avatar,
+ Grid,
+ Divider,
+ alpha,
+ useTheme,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+} from '@mui/material';
+import {
+ Report,
+ FileUpload,
+ CheckCircle,
+ Schedule,
+ Warning,
+ Close,
+ Send,
+ AttachFile,
+ Delete,
+ Assignment as AssignmentIcon,
+} from '@mui/icons-material';
+import { pageTransition } from '../../utils/animations';
+
+const EnhancedGrievances = () => {
+ const theme = useTheme();
+ const [openDialog, setOpenDialog] = useState(false);
+ const [selectedGrievance, setSelectedGrievance] = useState(null);
+ const [newGrievance, setNewGrievance] = useState({
+ category: '',
+ priority: '',
+ subject: '',
+ description: '',
+ attachments: [],
+ });
+
+ const grievances = [
+ {
+ id: 'GRV-2026-001',
+ subject: 'Fee Portal Not Working',
+ category: 'Finance',
+ priority: 'high',
+ status: 'in-progress',
+ submittedDate: 'Jan 20, 2026',
+ lastUpdate: 'Jan 23, 2026',
+ department: 'Finance Office',
+ timeline: [
+ { date: 'Jan 20, 2026 10:30 AM', status: 'Submitted', note: 'Grievance submitted' },
+ { date: 'Jan 20, 2026 02:15 PM', status: 'Acknowledged', note: 'Assigned to Finance Office' },
+ { date: 'Jan 23, 2026 11:00 AM', status: 'In Progress', note: 'IT team investigating the issue' },
+ ],
+ attachments: [
+ { name: 'error_screenshot.png', size: '245 KB' },
+ ],
+ },
+ {
+ id: 'GRV-2026-002',
+ subject: 'Library Book Not Available',
+ category: 'Library',
+ priority: 'medium',
+ status: 'resolved',
+ submittedDate: 'Jan 18, 2026',
+ lastUpdate: 'Jan 22, 2026',
+ department: 'Library',
+ resolution: 'Book has been ordered and will be available next week.',
+ timeline: [
+ { date: 'Jan 18, 2026 09:00 AM', status: 'Submitted', note: 'Grievance submitted' },
+ { date: 'Jan 18, 2026 03:30 PM', status: 'Acknowledged', note: 'Assigned to Library' },
+ { date: 'Jan 22, 2026 10:15 AM', status: 'Resolved', note: 'Book ordered' },
+ ],
+ attachments: [],
+ },
+ {
+ id: 'GRV-2026-003',
+ subject: 'Classroom AC Not Working',
+ category: 'Operations',
+ priority: 'urgent',
+ status: 'new',
+ submittedDate: 'Jan 24, 2026',
+ lastUpdate: 'Jan 24, 2026',
+ department: 'Operations',
+ timeline: [
+ { date: 'Jan 24, 2026 08:45 AM', status: 'Submitted', note: 'Grievance submitted' },
+ ],
+ attachments: [],
+ },
+ ];
+
+ const categories = [
+ 'Academic',
+ 'Finance',
+ 'Library',
+ 'Operations',
+ 'IT Support',
+ 'Administration',
+ 'Hostel',
+ 'Transport',
+ 'Other',
+ ];
+
+ const priorities = [
+ { value: 'low', label: 'Low', color: 'default' },
+ { value: 'medium', label: 'Medium', color: 'info' },
+ { value: 'high', label: 'High', color: 'warning' },
+ { value: 'urgent', label: 'Urgent', color: 'error' },
+ ];
+
+ const statuses = [
+ { value: 'new', label: 'New', color: 'primary', icon: },
+ { value: 'acknowledged', label: 'Acknowledged', color: 'info', icon: },
+ { value: 'in-progress', label: 'In Progress', color: 'warning', icon: },
+ { value: 'resolved', label: 'Resolved', color: 'success', icon: },
+ { value: 'closed', label: 'Closed', color: 'default', icon: },
+ ];
+
+ const getStatusInfo = (status) => {
+ return statuses.find((s) => s.value === status) || statuses[0];
+ };
+
+ const getPriorityInfo = (priority) => {
+ return priorities.find((p) => p.value === priority) || priorities[0];
+ };
+
+ const handleFileUpload = (event) => {
+ const files = Array.from(event.target.files);
+ setNewGrievance({
+ ...newGrievance,
+ attachments: [...newGrievance.attachments, ...files],
+ });
+ };
+
+ const handleRemoveFile = (index) => {
+ setNewGrievance({
+ ...newGrievance,
+ attachments: newGrievance.attachments.filter((_, i) => i !== index),
+ });
+ };
+
+ const handleSubmitGrievance = () => {
+ console.log('Submitting grievance:', newGrievance);
+ setOpenDialog(false);
+ setNewGrievance({
+ category: '',
+ priority: '',
+ subject: '',
+ description: '',
+ attachments: [],
+ });
+ };
+
+ const handleViewDetails = (grievance) => {
+ setSelectedGrievance(grievance);
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+ Grievance Management
+
+
+ Submit and track your grievances
+
+
+ }
+ sx={{
+ bgcolor: 'white',
+ color: 'primary.main',
+ '&:hover': { bgcolor: 'rgba(255,255,255,0.9)' },
+ }}
+ onClick={() => setOpenDialog(true)}
+ >
+ File New Grievance
+
+
+
+ {!selectedGrievance ? (
+ /* Grievance List */
+
+ {grievances.map((grievance) => {
+ const statusInfo = getStatusInfo(grievance.status);
+ const priorityInfo = getPriorityInfo(grievance.priority);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {grievance.subject}
+
+
+ Submitted: {grievance.submittedDate} • Last Update: {grievance.lastUpdate}
+
+
+
+
+
+
+
+
+ {grievance.attachments.length > 0 && (
+
+
+
+ {grievance.attachments.length} attachment(s)
+
+
+ )}
+
+
+ );
+ })}
+
+ ) : (
+ /* Grievance Details */
+
+
+
+
+
+
+
+
+
+
+
+ {selectedGrievance.subject}
+
+
+ Department: {selectedGrievance.department}
+
+
+
+
+
+
+
+ {/* Timeline */}
+
+ Timeline
+
+
+ {selectedGrievance.timeline.map((event, index) => (
+
+
+ {index < selectedGrievance.timeline.length - 1 && (
+
+ )}
+
+
+ {event.status}
+
+
+ {event.date}
+
+ {event.note}
+
+
+ ))}
+
+
+ {selectedGrievance.resolution && (
+ <>
+
+
+
+ Resolution
+
+ {selectedGrievance.resolution}
+
+ >
+ )}
+
+ {selectedGrievance.attachments.length > 0 && (
+ <>
+
+
+ Attachments
+
+
+ {selectedGrievance.attachments.map((file, index) => (
+
+
+
+
+ {file.name}
+
+
+ {file.size}
+
+
+
+
+ ))}
+
+ >
+ )}
+
+ )}
+
+ {/* New Grievance Dialog */}
+
+
+
+ );
+};
+
+export default EnhancedGrievances;
diff --git a/src/pages/Grievances/Grievances.jsx b/src/pages/Grievances/Grievances.jsx
index 47b1fd3..afdca83 100644
--- a/src/pages/Grievances/Grievances.jsx
+++ b/src/pages/Grievances/Grievances.jsx
@@ -1,12 +1,30 @@
-import React from 'react';
+import { useMemo, useState } from 'react';
+import { motion } from 'framer-motion';
import {
- Container,
- Typography,
Box,
Card,
CardContent,
+ Typography,
Button,
Chip,
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ TextField,
+ FormControl,
+ FormLabel,
+ RadioGroup,
+ FormControlLabel,
+ Radio,
+ MenuItem,
+ Stack,
+ useTheme,
+ alpha,
+ IconButton,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
@@ -14,70 +32,638 @@ import {
Report,
CheckCircle,
PendingActions,
+ ExpandMore,
+ Add,
+ Warning,
+ AccessTime,
+ ReportProblem,
+ ConfirmationNumber,
+ Close,
+ Autorenew,
} from '@mui/icons-material';
-import PageHeader from '../../components/Common/PageHeader';
-import StatCard from '../../components/Common/StatCard';
+import PageTransition from '../../components/Common/PageTransition';
+import EmptyState from '../../components/Common/EmptyState';
+import FileDropzone from '../../components/Forms/FileDropzone';
+import { grievances, submitGrievance } from '../../data/dummyData';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
const Grievances = () => {
+ const theme = useTheme();
+ const { showSnackbar } = useSnackbar();
+ const [open, setOpen] = useState(false);
+ const [form, setForm] = useState({
+ category: 'Academic',
+ priority: 'Medium',
+ subject: '',
+ description: '',
+ attachments: [],
+ });
+
+ const stats = useMemo(() => {
+ const total = grievances.length;
+ const pending = grievances.filter((g) => g.status === 'Pending' || g.status === 'In Progress').length;
+ const resolved = grievances.filter((g) => g.status === 'Resolved' || g.status === 'Closed').length;
+ return { total, pending, resolved };
+ }, []);
+
+ const handleSubmit = () => {
+ if (!form.subject || !form.description) {
+ showSnackbar('Please fill all required fields', 'error');
+ return;
+ }
+ const result = submitGrievance({
+ category: form.category,
+ priority: form.priority,
+ subject: form.subject,
+ description: form.description,
+ attachments: form.attachments,
+ status: 'Pending',
+ studentId: 'STU001',
+ });
+ showSnackbar(`Ticket #${result.ticketId} created successfully`, 'success');
+ setOpen(false);
+ setForm({ category: 'Academic', priority: 'Medium', subject: '', description: '', attachments: [] });
+ };
+
return (
-
-
+
+
+ {/* HEADER */}
+
+
+
+
+
+
+
+
+ Grievance Portal
+
+
+ Submit and track your complaints and concerns
+
+
+
+ }
+ onClick={() => setOpen(true)}
+ sx={{
+ px: 4,
+ py: 1.5,
+ borderRadius: 2,
+ background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
+ color: 'white',
+ fontWeight: 'bold',
+ fontSize: '1rem',
+ boxShadow: '0 8px 24px rgba(240,147,251,0.4)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #e082ea 0%, #e4465b 100%)',
+ boxShadow: '0 12px 32px rgba(240,147,251,0.6)',
+ transform: 'translateY(-2px)',
+ },
+ transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
+ }}
+ >
+ Submit Grievance
+
+
+
-
-
-
-
-
-
-
-
-
+ {/* STAT CARDS */}
+
+
+
+
+
+
+
+ {stats.total}
+
+
+ Total Submitted
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.pending}
+
+
+ Pending
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.resolved}
+
+
+ Resolved
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.avgResolutionTime}
+
+
+ Avg Resolution
+
+
+
+
+
+
+
+
+
-
- setOpen(true)}
/>
-
-
+ ) : (
+
+
+ My Grievances ({grievances.length})
+
+
+ {grievances.map((g) => (
+
+ }
+ sx={{
+ px: 3,
+ '& .MuiAccordionSummary-content': {
+ my: 2,
+ },
+ }}
+ >
+
+ {/* CATEGORY ICON */}
+
+ {g.status === 'Resolved' || g.status === 'Closed' ? (
+
+ ) : g.status === 'Pending' ? (
+
+ ) : (
+
+ )}
+
+
+ {/* CONTENT */}
+
+
+ {g.subject}
+
+
+
+
+ }
+ label={g.ticketId}
+ size="small"
+ variant="outlined"
+ sx={{ fontWeight: 600 }}
+ />
+
+
+
+ {/* STATUS CHIP */}
+
+
+
+
+ {/* DESCRIPTION */}
+
+
+ Description
+
+
+ {g.description}
+
+
+
+ {/* ADMIN REPLY */}
+ {g.resolution && (
+
+
+
+
+
+
+
+ Admin Response
+
+
+
+ {g.resolution}
+
+
+
+ )}
+
+ {/* RE-OPEN BUTTON */}
+ {g.status === 'Resolved' && g.resolvedAt && (
+ (Date.now() - new Date(g.resolvedAt).getTime()) <= 7 * 24 * 60 * 60 * 1000 && (
+ }
+ size="small"
+ sx={{
+ mt: 2,
+ borderRadius: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ }}
+ >
+ Re-open Ticket
+
+ )
+ )}
+
+
+ ))}
+
+
+ )}
+
+ {/* ADD GRIEVANCE DIALOG */}
+
+ {/* ATTACHMENTS */}
+
+
+ Attachments (Optional)
+
+ setForm({ ...form, attachments: [file.name] })} />
+
+
+
+
+
+ }
+ sx={{
+ borderRadius: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ px: 3,
+ background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
+ boxShadow: '0 4px 12px rgba(240,147,251,0.4)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #e082ea 0%, #e4465b 100%)',
+ boxShadow: '0 6px 16px rgba(240,147,251,0.5)',
+ },
+ }}
+ >
+ Submit Grievance
+
+
+
+
+
);
};
diff --git a/src/pages/LMS/CourseClassroom.jsx b/src/pages/LMS/CourseClassroom.jsx
index 8b563f0..38690ef 100644
--- a/src/pages/LMS/CourseClassroom.jsx
+++ b/src/pages/LMS/CourseClassroom.jsx
@@ -883,15 +883,40 @@ const CourseClassroom = () => {
Grade Distribution
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
- {chartData.map((entry, index) => (
- |
- ))}
+
+
+
+
+ |
+ |
diff --git a/src/pages/Library/BookManagement.jsx b/src/pages/Library/BookManagement.jsx
new file mode 100644
index 0000000..3024731
--- /dev/null
+++ b/src/pages/Library/BookManagement.jsx
@@ -0,0 +1,383 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Button,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ TextField,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Chip,
+ Stack,
+ InputAdornment,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+ Snackbar,
+ Alert,
+ Avatar,
+} from '@mui/material';
+import {
+ Add as AddIcon,
+ Edit as EditIcon,
+ Delete as DeleteIcon,
+ Search as SearchIcon,
+ MenuBook as BookIcon,
+ Category as CategoryIcon,
+ TrendingUp as TrendingUpIcon,
+ Inventory as InventoryIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const BookManagement = () => {
+ const [openDialog, setOpenDialog] = useState(false);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterCategory, setFilterCategory] = useState('all');
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+ const [editingBook, setEditingBook] = useState(null);
+ const [formData, setFormData] = useState({
+ title: '',
+ author: '',
+ isbn: '',
+ category: '',
+ publisher: '',
+ yearPublished: '',
+ totalCopies: '',
+ availableCopies: '',
+ shelfLocation: '',
+ });
+
+ // Mock books data
+ const [books, setBooks] = useState([
+ { id: 1, title: 'Data Structures & Algorithms', author: 'Narasimha Karumanchi', isbn: '978-8192107554', category: 'Computer Science', publisher: 'CareerMonk Publications', yearPublished: 2016, totalCopies: 15, availableCopies: 8, shelfLocation: 'CS-A-101' },
+ { id: 2, title: 'Introduction to Algorithms', author: 'Thomas H. Cormen', isbn: '978-0262033848', category: 'Computer Science', publisher: 'MIT Press', yearPublished: 2009, totalCopies: 12, availableCopies: 5, shelfLocation: 'CS-A-102' },
+ { id: 3, title: 'Clean Code', author: 'Robert C. Martin', isbn: '978-0132350884', category: 'Software Engineering', publisher: 'Prentice Hall', yearPublished: 2008, totalCopies: 10, availableCopies: 7, shelfLocation: 'SE-B-201' },
+ { id: 4, title: 'Marketing Management', author: 'Philip Kotler', isbn: '978-0136009986', category: 'Business', publisher: 'Pearson', yearPublished: 2015, totalCopies: 20, availableCopies: 12, shelfLocation: 'BUS-C-301' },
+ { id: 5, title: 'Database Systems', author: 'Ramez Elmasri', isbn: '978-0133970777', category: 'Computer Science', publisher: 'Pearson', yearPublished: 2015, totalCopies: 18, availableCopies: 10, shelfLocation: 'CS-A-103' },
+ ]);
+
+ const stats = [
+ { label: 'Total Books', value: books.reduce((sum, b) => sum + b.totalCopies, 0).toString(), change: '+45 new this month', trend: 'up', color: 'primary', icon: BookIcon },
+ { label: 'Available', value: books.reduce((sum, b) => sum + b.availableCopies, 0).toString(), change: 'Ready to issue', trend: 'up', color: 'success', icon: InventoryIcon },
+ { label: 'Categories', value: new Set(books.map(b => b.category)).size.toString(), change: 'Across library', trend: 'up', color: 'info', icon: CategoryIcon },
+ ];
+
+ const handleOpenDialog = (book = null) => {
+ if (book) {
+ setEditingBook(book);
+ setFormData(book);
+ } else {
+ setEditingBook(null);
+ setFormData({
+ title: '',
+ author: '',
+ isbn: '',
+ category: '',
+ publisher: '',
+ yearPublished: '',
+ totalCopies: '',
+ availableCopies: '',
+ shelfLocation: '',
+ });
+ }
+ setOpenDialog(true);
+ };
+
+ const handleCloseDialog = () => {
+ setOpenDialog(false);
+ setEditingBook(null);
+ };
+
+ const handleSave = () => {
+ if (editingBook) {
+ setBooks(books.map(b => b.id === editingBook.id ? { ...formData, id: b.id } : b));
+ setSnackbar({ open: true, message: 'Book updated successfully!', severity: 'success' });
+ } else {
+ const newBook = { ...formData, id: Date.now(), totalCopies: parseInt(formData.totalCopies), availableCopies: parseInt(formData.availableCopies) };
+ setBooks([...books, newBook]);
+ setSnackbar({ open: true, message: 'Book added successfully!', severity: 'success' });
+ }
+ handleCloseDialog();
+ };
+
+ const handleDelete = (id) => {
+ setBooks(books.filter(b => b.id !== id));
+ setSnackbar({ open: true, message: 'Book deleted successfully!', severity: 'warning' });
+ };
+
+ const filteredBooks = books.filter(book => {
+ const matchesSearch = book.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ book.author.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ book.isbn.includes(searchQuery);
+ const matchesCategory = filterCategory === 'all' || book.category === filterCategory;
+ return matchesSearch && matchesCategory;
+ });
+
+ return (
+
+
+ } onClick={() => handleOpenDialog()}>
+ Add New Book
+
+ }
+ />
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Filters Card */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Category
+
+
+
+
+
+
+
+ {/* Books Table */}
+
+
+
+
+
+ ISBN
+ Title
+ Author
+ Category
+ Publisher
+ Total
+ Available
+ Shelf
+ Actions
+
+
+
+ {filteredBooks.map((book) => (
+
+
+
+ {book.isbn}
+
+
+
+
+ {book.title}
+
+
+ {book.yearPublished}
+
+
+ {book.author}
+
+
+
+
+ {book.publisher}
+
+
+
+ {book.totalCopies}
+
+
+
+ 0 ? 'success' : 'error'}
+ />
+
+
+
+ {book.shelfLocation}
+
+
+
+
+ handleOpenDialog(book)}>
+
+
+ handleDelete(book.id)}>
+
+
+
+
+
+ ))}
+
+
+
+
+
+ {/* Add/Edit Dialog */}
+
+
+ {/* Snackbar */}
+ setSnackbar({ ...snackbar, open: false })}
+ >
+ setSnackbar({ ...snackbar, open: false })}>
+ {snackbar.message}
+
+
+
+
+ );
+};
+
+export default BookManagement;
diff --git a/src/pages/Library/IssuedBooks.jsx b/src/pages/Library/IssuedBooks.jsx
new file mode 100644
index 0000000..96825ec
--- /dev/null
+++ b/src/pages/Library/IssuedBooks.jsx
@@ -0,0 +1,370 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Button,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Chip,
+ Stack,
+ TextField,
+ InputAdornment,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+ Avatar,
+ Snackbar,
+ Alert,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ AssignmentReturn as ReturnIcon,
+ AssignmentTurnedIn as IssuedIcon,
+ Warning as WarningIcon,
+ CheckCircle as CheckCircleIcon,
+ Email as EmailIcon,
+ MenuBook as BookIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const IssuedBooks = () => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterStatus, setFilterStatus] = useState('all');
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+ const [returnDialog, setReturnDialog] = useState({ open: false, issue: null });
+
+ // Mock issued books data
+ const [issuedBooks, setIssuedBooks] = useState([
+ {
+ id: 1,
+ bookTitle: 'Data Structures & Algorithms',
+ bookIsbn: '978-8192107554',
+ studentName: 'Ali Hassan',
+ studentRollNo: 'F21-BS-001',
+ studentPhoto: 'https://i.pravatar.cc/150?img=31',
+ issueDate: '2026-01-10',
+ dueDate: '2026-01-24',
+ status: 'issued',
+ daysOverdue: 0,
+ },
+ {
+ id: 2,
+ bookTitle: 'Introduction to Algorithms',
+ bookIsbn: '978-0262033848',
+ studentName: 'Sara Ahmed',
+ studentRollNo: 'F21-BS-002',
+ studentPhoto: 'https://i.pravatar.cc/150?img=32',
+ issueDate: '2026-01-08',
+ dueDate: '2026-01-22',
+ status: 'overdue',
+ daysOverdue: 3,
+ },
+ {
+ id: 3,
+ bookTitle: 'Clean Code',
+ bookIsbn: '978-0132350884',
+ studentName: 'Omar Khan',
+ studentRollNo: 'F21-BS-003',
+ studentPhoto: 'https://i.pravatar.cc/150?img=33',
+ issueDate: '2026-01-15',
+ dueDate: '2026-01-29',
+ status: 'issued',
+ daysOverdue: 0,
+ },
+ {
+ id: 4,
+ bookTitle: 'Marketing Management',
+ bookIsbn: '978-0136009986',
+ studentName: 'Fatima Zahra',
+ studentRollNo: 'F21-BS-004',
+ studentPhoto: 'https://i.pravatar.cc/150?img=34',
+ issueDate: '2026-01-05',
+ dueDate: '2026-01-19',
+ status: 'overdue',
+ daysOverdue: 6,
+ },
+ {
+ id: 5,
+ bookTitle: 'Database Systems',
+ bookIsbn: '978-0133970777',
+ studentName: 'Ahmed Raza',
+ studentRollNo: 'F21-BS-005',
+ studentPhoto: 'https://i.pravatar.cc/150?img=35',
+ issueDate: '2026-01-12',
+ dueDate: '2026-01-26',
+ status: 'issued',
+ daysOverdue: 0,
+ },
+ ]);
+
+ const stats = [
+ {
+ label: 'Currently Issued',
+ value: issuedBooks.filter(b => b.status === 'issued').length.toString(),
+ change: 'Active borrows',
+ trend: 'up',
+ color: 'primary',
+ icon: IssuedIcon,
+ },
+ {
+ label: 'Overdue Books',
+ value: issuedBooks.filter(b => b.status === 'overdue').length.toString(),
+ change: 'Need attention',
+ trend: 'up',
+ color: 'error',
+ icon: WarningIcon,
+ },
+ {
+ label: 'Total Issued',
+ value: issuedBooks.length.toString(),
+ change: 'This month',
+ trend: 'up',
+ color: 'success',
+ icon: BookIcon,
+ },
+ ];
+
+ const handleReturn = (issue) => {
+ setReturnDialog({ open: true, issue });
+ };
+
+ const confirmReturn = () => {
+ setIssuedBooks(issuedBooks.filter(b => b.id !== returnDialog.issue.id));
+ setSnackbar({ open: true, message: 'Book returned successfully!', severity: 'success' });
+ setReturnDialog({ open: false, issue: null });
+ };
+
+ const handleSendReminder = (issue) => {
+ setSnackbar({ open: true, message: `Reminder sent to ${issue.studentName}`, severity: 'info' });
+ };
+
+ const filteredBooks = issuedBooks.filter(issue => {
+ const matchesSearch =
+ issue.bookTitle.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ issue.studentName.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ issue.studentRollNo.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ issue.bookIsbn.includes(searchQuery);
+ const matchesStatus = filterStatus === 'all' || issue.status === filterStatus;
+ return matchesSearch && matchesStatus;
+ });
+
+ const getStatusChip = (status, daysOverdue) => {
+ if (status === 'overdue') {
+ return (
+ }
+ />
+ );
+ }
+ return } />;
+ };
+
+ return (
+
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Filters Card */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Status
+
+
+
+
+
+
+
+ {/* Issued Books Table */}
+
+
+
+
+
+ Student
+ Book
+ ISBN
+ Issue Date
+ Due Date
+ Status
+ Actions
+
+
+
+ {filteredBooks.map((issue) => (
+
+
+
+
+
+
+ {issue.studentName}
+
+
+ {issue.studentRollNo}
+
+
+
+
+
+
+ {issue.bookTitle}
+
+
+
+
+ {issue.bookIsbn}
+
+
+
+ {issue.issueDate}
+
+
+
+ {issue.dueDate}
+
+
+ {getStatusChip(issue.status, issue.daysOverdue)}
+
+
+ handleReturn(issue)}
+ >
+
+
+ {issue.status === 'overdue' && (
+ handleSendReminder(issue)}
+ >
+
+
+ )}
+
+
+
+ ))}
+
+
+
+
+
+ {/* Return Confirmation Dialog */}
+
+
+ {/* Snackbar */}
+ setSnackbar({ ...snackbar, open: false })}
+ >
+ setSnackbar({ ...snackbar, open: false })}>
+ {snackbar.message}
+
+
+
+
+ );
+};
+
+export default IssuedBooks;
diff --git a/src/pages/Library/LibrarianDashboard.jsx b/src/pages/Library/LibrarianDashboard.jsx
new file mode 100644
index 0000000..0a5baae
--- /dev/null
+++ b/src/pages/Library/LibrarianDashboard.jsx
@@ -0,0 +1,973 @@
+import { useEffect, useMemo, useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Tabs,
+ Tab,
+ TextField,
+ Button,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Radio,
+ RadioGroup,
+ FormControlLabel,
+ FormControl,
+ FormLabel,
+ Alert,
+ Chip,
+ InputAdornment,
+ Stack,
+ Divider,
+ Tooltip,
+ alpha,
+ useTheme,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ LibraryBooks,
+ Search,
+ Edit,
+ Delete,
+ AssignmentReturn,
+ CheckCircle,
+ Warning,
+ AttachMoney,
+ QrCode,
+ Receipt,
+ Person,
+ MenuBook,
+ TrendingUp,
+ AutoStories,
+} from '@mui/icons-material';
+import PageTransition from '../../components/Common/PageTransition';
+import { TableSkeleton } from '../../components/Common/LoadingSkeleton';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import {
+ libraryBooks,
+ libraryTransactions,
+ issueBookTransaction,
+ returnBookTransaction,
+ students,
+} from '../../data/dummyData';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const LibrarianDashboard = () => {
+ const { showSnackbar } = useSnackbar();
+ const theme = useTheme();
+ const [loading, setLoading] = useState(true);
+ const [activeTab, setActiveTab] = useState(0);
+ const [books, setBooks] = useState([]);
+ const [transactions, setTransactions] = useState([]);
+
+ const [studentId, setStudentId] = useState('');
+ const [studentName, setStudentName] = useState('');
+ const [bookIsbn, setBookIsbn] = useState('');
+ const [bookTitle, setBookTitle] = useState('');
+ const [bookAvailable, setBookAvailable] = useState(null);
+
+ const [returnSearch, setReturnSearch] = useState('');
+ const [activeLoan, setActiveLoan] = useState(null);
+ const [bookCondition, setBookCondition] = useState('Good');
+
+ const [searchQuery, setSearchQuery] = useState('');
+
+ useEffect(() => {
+ setBooks(libraryBooks);
+ setTransactions(libraryTransactions);
+ const timer = setTimeout(() => setLoading(false), 700);
+ return () => clearTimeout(timer);
+ }, []);
+
+ const stats = useMemo(() => {
+ const today = new Date().toISOString().split('T')[0];
+ const issuedToday = transactions.filter((t) => t.issuedOn === today).length;
+ const overdue = transactions.filter((t) => t.status === 'Issued' && new Date(t.dueDate) < new Date()).length;
+ const fines = transactions.reduce((sum, t) => sum + (t.fine || 0), 0);
+ return { issuedToday, overdue, fines };
+ }, [transactions]);
+
+ const calculatedFine = bookCondition === 'Damaged' ? 500 : bookCondition === 'Lost' ? 2000 : 0;
+
+ const handleStudentBlur = () => {
+ const student = students.find((s) => s.id === studentId);
+ setStudentName(student?.name || '');
+ };
+
+ const handleIsbnBlur = () => {
+ const book = books.find((b) => b.isbn === bookIsbn);
+ setBookTitle(book?.title || '');
+ setBookAvailable(book ? book.availableCopies > 0 : null);
+ };
+
+ const handleIssue = () => {
+ if (!studentId || !bookIsbn) {
+ showSnackbar('Please fill all fields', 'error');
+ return;
+ }
+ const student = students.find((s) => s.id === studentId);
+ const result = issueBookTransaction(studentId, student?.name || 'Student', bookIsbn);
+ if (!result.success) {
+ showSnackbar(result.message, 'error');
+ return;
+ }
+ setTransactions([...libraryTransactions]);
+ setBooks([...libraryBooks]);
+ showSnackbar('Book issued successfully', 'success');
+ setStudentId('');
+ setStudentName('');
+ setBookIsbn('');
+ setBookTitle('');
+ setBookAvailable(null);
+ };
+
+ const handleReturnSearch = () => {
+ const loan = transactions.find(
+ (t) => t.status === 'Issued' && (t.isbn === returnSearch || t.studentId === returnSearch)
+ );
+ setActiveLoan(loan || null);
+ };
+
+ const handleReturn = () => {
+ if (!activeLoan) {
+ showSnackbar('No active loan selected', 'error');
+ return;
+ }
+ const result = returnBookTransaction(activeLoan.id, bookCondition);
+ if (!result.success) {
+ showSnackbar(result.message, 'error');
+ return;
+ }
+ setTransactions([...libraryTransactions]);
+ setBooks([...libraryBooks]);
+ showSnackbar(`Book returned. Fine: PKR ${result.transaction.fine}`, result.transaction.fine > 0 ? 'warning' : 'success');
+ setReturnSearch('');
+ setActiveLoan(null);
+ setBookCondition('Good');
+ };
+
+ const filteredBooks = books.filter(
+ (book) =>
+ book.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ book.author.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ book.isbn.includes(searchQuery)
+ );
+
+ if (loading) {
+ return (
+
+ Loading...
+
+
+ );
+ }
+
+ return (
+
+
+ {/* HEADER */}
+
+
+
+
+
+
+
+ Library Management System
+
+
+ Manage book issues, returns, inventory, and transactions
+
+
+
+
+
+ {/* STAT CARDS - 4 LARGE CARDS */}
+
+
+
+
+
+
+
+
+
+
+ {books.length}
+
+
+ Total Books
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.issuedToday}
+
+
+ Issued Today
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.overdue}
+
+
+ Overdue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ₨{stats.fines}
+
+
+ Total Fines
+
+
+
+
+
+
+
+
+ {/* TABBED INTERFACE */}
+
+ setActiveTab(v)}
+ variant="scrollable"
+ scrollButtons="auto"
+ allowScrollButtonsMobile
+ sx={{
+ borderBottom: 1,
+ borderColor: 'divider',
+ '& .MuiTab-root': {
+ fontWeight: 600,
+ textTransform: 'none',
+ fontSize: { xs: '0.875rem', sm: '1rem' },
+ py: { xs: 1.5, sm: 2 },
+ minHeight: { xs: 56, sm: 64 },
+ },
+ }}
+ >
+ Issue Book}
+ icon={}
+ iconPosition="start"
+ sx={{ '& .MuiTab-iconWrapper': { mb: { xs: 0.5, sm: 0 }, mr: { xs: 0, sm: 1 } } }}
+ />
+ Return Book}
+ icon={}
+ iconPosition="start"
+ sx={{ '& .MuiTab-iconWrapper': { mb: { xs: 0.5, sm: 0 }, mr: { xs: 0, sm: 1 } } }}
+ />
+ Inventory}
+ icon={}
+ iconPosition="start"
+ sx={{ '& .MuiTab-iconWrapper': { mb: { xs: 0.5, sm: 0 }, mr: { xs: 0, sm: 1 } } }}
+ />
+ Transactions}
+ icon={}
+ iconPosition="start"
+ sx={{ '& .MuiTab-iconWrapper': { mb: { xs: 0.5, sm: 0 }, mr: { xs: 0, sm: 1 } } }}
+ />
+
+
+ {/* TAB 1: ISSUE BOOK */}
+ {activeTab === 0 && (
+
+
+
+ Issue Book to Student
+
+
+ setStudentId(e.target.value)}
+ onBlur={handleStudentBlur}
+ fullWidth
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ />
+ {studentName && (
+
+
+ Student: {studentName}
+
+
+ )}
+ setBookIsbn(e.target.value)}
+ onBlur={handleIsbnBlur}
+ fullWidth
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ />
+ {bookTitle && (
+
+
+ {bookTitle}
+
+
+ {bookAvailable ? '✓ Available for issue' : '✗ Not available - All copies issued'}
+
+
+ )}
+
+
+ 📅 Due Date
+
+
+ {new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toLocaleDateString('en-GB', {
+ day: 'numeric',
+ month: 'long',
+ year: 'numeric'
+ })}
+
+
+ (Today + 30 days)
+
+
+ }
+ onClick={handleIssue}
+ sx={{
+ borderRadius: 2,
+ py: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ fontSize: '1.1rem',
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ boxShadow: '0 4px 12px rgba(102,126,234,0.4)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #5568d3 0%, #6a3f8f 100%)',
+ boxShadow: '0 6px 16px rgba(102,126,234,0.5)',
+ },
+ }}
+ >
+ Issue Book
+
+
+
+
+ )}
+
+ {/* TAB 2: RETURN BOOK */}
+ {activeTab === 1 && (
+
+
+
+ Return Book
+
+
+ setReturnSearch(e.target.value)}
+ onBlur={handleReturnSearch}
+ fullWidth
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ />
+ {activeLoan ? (
+
+
+ 📚 {activeLoan.bookTitle}
+
+
+ Student: {activeLoan.studentName} • Due: {activeLoan.dueDate}
+
+
+ ) : returnSearch && (
+
+ No active loan found for this search.
+
+ )}
+
+
+
+ Book Condition
+
+ setBookCondition(e.target.value)}
+ >
+ }
+ label={
+
+ Good
+ No damage - ₨0 fine
+
+ }
+ sx={{ mb: 1 }}
+ />
+ }
+ label={
+
+ Damaged
+ Minor damage - ₨500 fine
+
+ }
+ sx={{ mb: 1 }}
+ />
+ }
+ label={
+
+ Lost
+ Book replacement - ₨2000 fine
+
+ }
+ />
+
+
+
+ 0
+ ? alpha(theme.palette.warning.main, 0.1)
+ : alpha(theme.palette.success.main, 0.1),
+ border: '2px solid',
+ borderColor: calculatedFine > 0 ? 'warning.main' : 'success.main',
+ }}
+ >
+ 0 ? 'warning.main' : 'success.main'} fontWeight="bold">
+ TOTAL FINE
+
+ 0 ? 'warning.main' : 'success.main'} fontWeight="bold" sx={{ mt: 1 }}>
+ ₨ {calculatedFine}
+
+
+
+ }
+ onClick={handleReturn}
+ sx={{
+ borderRadius: 2,
+ py: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ fontSize: '1.1rem',
+ background: calculatedFine > 0
+ ? 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)'
+ : 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
+ boxShadow: `0 4px 12px ${calculatedFine > 0 ? 'rgba(250,112,154,0.4)' : 'rgba(17,153,142,0.4)'}`,
+ '&:hover': {
+ background: calculatedFine > 0
+ ? 'linear-gradient(135deg, #e96089 0%, #edd02f 100%)'
+ : 'linear-gradient(135deg, #0e7d72 0%, #2ed665 100%)',
+ boxShadow: `0 6px 16px ${calculatedFine > 0 ? 'rgba(250,112,154,0.5)' : 'rgba(17,153,142,0.5)'}`,
+ },
+ }}
+ >
+ Confirm Return
+
+
+
+
+ )}
+
+ {/* TAB 3: INVENTORY */}
+ {activeTab === 2 && (
+
+
+ setSearchQuery(e.target.value)}
+ fullWidth
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ />
+
+
+
+
+
+ Cover
+ ISBN
+ Title
+ Author
+ Category
+ Shelf Location
+ Status
+ Actions
+
+
+
+ {filteredBooks.map((book) => (
+
+
+
+
+
+
+ {book.isbn}
+
+
+
+
+ {book.title}
+
+
+ {book.author}
+
+
+
+
+
+ {book.shelfLocation}
+
+
+
+ 0 ? 'Available' : 'Out of Stock'}
+ size="small"
+ sx={{
+ fontWeight: 600,
+ ...(book.availableCopies > 0 && {
+ background: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
+ color: 'white',
+ }),
+ ...(book.availableCopies === 0 && {
+ background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
+ color: 'white',
+ }),
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ )}
+
+ {/* TAB 4: TRANSACTIONS */}
+ {activeTab === 3 && (
+
+
+
+
+
+ Transaction ID
+ Student
+ Book
+ Issued On
+ Due Date
+ Status
+ Fine
+
+
+
+ {transactions.map((t) => (
+
+
+
+ {t.id}
+
+
+
+
+ {t.studentName}
+
+
+
+
+ {t.bookTitle}
+
+
+ {t.issuedOn}
+ {t.dueDate}
+
+
+
+
+ 0 ? 'error.main' : 'text.secondary'}>
+ ₨ {t.fine || 0}
+
+
+
+ ))}
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default LibrarianDashboard;
diff --git a/src/pages/Library/LibrarianGrievances.jsx b/src/pages/Library/LibrarianGrievances.jsx
new file mode 100644
index 0000000..d250b82
--- /dev/null
+++ b/src/pages/Library/LibrarianGrievances.jsx
@@ -0,0 +1,507 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Tab,
+ Tabs,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Chip,
+ Stack,
+ TextField,
+ InputAdornment,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Button,
+ Avatar,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+ Paper,
+ Divider,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ Visibility as ViewIcon,
+ CheckCircle as ResolveIcon,
+ Cancel as RejectIcon,
+ PendingActions as PendingIcon,
+ SupportAgent as SupportIcon,
+ MenuBook as LibraryIcon,
+ Warning as WarningIcon,
+ CheckCircle as CheckCircleIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const LibrarianGrievances = () => {
+ const [tabValue, setTabValue] = useState(0);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterStatus, setFilterStatus] = useState('all');
+ const [viewDialog, setViewDialog] = useState({ open: false, grievance: null });
+ const [responseText, setResponseText] = useState('');
+
+ // Mock library-specific grievances
+ const [grievances, setGrievances] = useState([
+ {
+ id: 1,
+ ticketId: 'LIB-001',
+ studentName: 'Ali Hassan',
+ studentRollNo: 'F21-BS-001',
+ studentPhoto: 'https://i.pravatar.cc/150?img=31',
+ category: 'Library',
+ subcategory: 'Book Unavailability',
+ subject: 'Required textbook not available',
+ description: 'The book "Database Systems" by Ramez Elmasri is required for my coursework but has been unavailable for 2 weeks. All copies are issued.',
+ priority: 'High',
+ status: 'Pending',
+ submittedDate: '2026-01-20',
+ bookTitle: 'Database Systems',
+ isbn: '978-0133970777',
+ },
+ {
+ id: 2,
+ ticketId: 'LIB-002',
+ studentName: 'Sara Ahmed',
+ studentRollNo: 'F21-BS-002',
+ studentPhoto: 'https://i.pravatar.cc/150?img=32',
+ category: 'Library',
+ subcategory: 'Reading Room',
+ subject: 'AC not working in reading room',
+ description: 'The air conditioning in Reading Room 2 has not been working for the past 3 days. It is very hot and difficult to study.',
+ priority: 'Medium',
+ status: 'In Progress',
+ submittedDate: '2026-01-22',
+ assignedTo: 'Facilities Team',
+ response: 'AC repair has been scheduled for tomorrow morning.',
+ },
+ {
+ id: 3,
+ ticketId: 'LIB-003',
+ studentName: 'Omar Khan',
+ studentRollNo: 'F21-BS-003',
+ studentPhoto: 'https://i.pravatar.cc/150?img=33',
+ category: 'Library',
+ subcategory: 'Late Fee Issue',
+ subject: 'Incorrect late fee charged',
+ description: 'I returned "Clean Code" on time but was charged a late fee. I have the return receipt showing I returned it before the due date.',
+ priority: 'High',
+ status: 'Resolved',
+ submittedDate: '2026-01-18',
+ resolvedDate: '2026-01-19',
+ resolution: 'Late fee has been waived after verification of return receipt. Amount will be refunded to your account within 3-5 business days.',
+ },
+ {
+ id: 4,
+ ticketId: 'LIB-004',
+ studentName: 'Fatima Zahra',
+ studentRollNo: 'F21-BS-004',
+ studentPhoto: 'https://i.pravatar.cc/150?img=34',
+ category: 'Library',
+ subcategory: 'Computer Lab',
+ subject: 'Computer not starting in Lab B',
+ description: 'Computer #12 in Lab B is not starting. I need to access research databases for my thesis work.',
+ priority: 'Medium',
+ status: 'Pending',
+ submittedDate: '2026-01-23',
+ },
+ {
+ id: 5,
+ ticketId: 'LIB-005',
+ studentName: 'Ahmed Raza',
+ studentRollNo: 'F21-BS-005',
+ studentPhoto: 'https://i.pravatar.cc/150?img=35',
+ category: 'Library',
+ subcategory: 'Reservation System',
+ subject: 'Book reservation not working',
+ description: 'I tried to reserve "Artificial Intelligence" through the online system but keep getting an error message.',
+ priority: 'Low',
+ status: 'Resolved',
+ submittedDate: '2026-01-21',
+ resolvedDate: '2026-01-22',
+ resolution: 'System issue was fixed. Book has been reserved for you. You will be notified when it becomes available.',
+ },
+ ]);
+
+ const stats = [
+ {
+ label: 'Total Library Complaints',
+ value: grievances.length.toString(),
+ change: 'This month',
+ trend: 'up',
+ color: 'primary',
+ icon: LibraryIcon,
+ },
+ {
+ label: 'Pending',
+ value: grievances.filter(g => g.status === 'Pending').length.toString(),
+ change: 'Need attention',
+ trend: 'up',
+ color: 'warning',
+ icon: PendingIcon,
+ },
+ {
+ label: 'In Progress',
+ value: grievances.filter(g => g.status === 'In Progress').length.toString(),
+ change: 'Being handled',
+ trend: 'up',
+ color: 'info',
+ icon: SupportIcon,
+ },
+ {
+ label: 'Resolved',
+ value: grievances.filter(g => g.status === 'Resolved').length.toString(),
+ change: 'This month',
+ trend: 'up',
+ color: 'success',
+ icon: CheckCircleIcon,
+ },
+ ];
+
+ const handleViewGrievance = (grievance) => {
+ setViewDialog({ open: true, grievance });
+ setResponseText(grievance.response || '');
+ };
+
+ const handleResolve = () => {
+ if (!responseText.trim()) {
+ alert('Please provide a resolution response');
+ return;
+ }
+ const updatedGrievances = grievances.map(g =>
+ g.id === viewDialog.grievance.id
+ ? { ...g, status: 'Resolved', resolution: responseText, resolvedDate: new Date().toISOString().split('T')[0] }
+ : g
+ );
+ setGrievances(updatedGrievances);
+ setViewDialog({ open: false, grievance: null });
+ setResponseText('');
+ };
+
+ const handleUpdateStatus = (status) => {
+ const updatedGrievances = grievances.map(g =>
+ g.id === viewDialog.grievance.id
+ ? { ...g, status, response: responseText }
+ : g
+ );
+ setGrievances(updatedGrievances);
+ setViewDialog({ open: false, grievance: null });
+ setResponseText('');
+ };
+
+ const filteredGrievances = grievances.filter(g => {
+ const matchesSearch =
+ g.subject.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ g.studentName.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ g.ticketId.toLowerCase().includes(searchQuery.toLowerCase());
+ const matchesStatus = filterStatus === 'all' || g.status === filterStatus;
+ const matchesTab =
+ tabValue === 0 ||
+ (tabValue === 1 && g.status === 'Pending') ||
+ (tabValue === 2 && g.status === 'In Progress') ||
+ (tabValue === 3 && g.status === 'Resolved');
+ return matchesSearch && matchesStatus && matchesTab;
+ });
+
+ const getStatusChip = (status) => {
+ const config = {
+ Pending: { color: 'warning', icon: },
+ 'In Progress': { color: 'info', icon: },
+ Resolved: { color: 'success', icon: },
+ };
+ return (
+
+ );
+ };
+
+ const getPriorityChip = (priority) => {
+ const colors = { High: 'error', Medium: 'warning', Low: 'info' };
+ return ;
+ };
+
+ return (
+
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Tabs and Filters */}
+
+ setTabValue(newValue)}
+ sx={{ borderBottom: 1, borderColor: 'divider', px: 2 }}
+ >
+
+ g.status === 'Pending').length})`} />
+ g.status === 'In Progress').length})`} />
+ g.status === 'Resolved').length})`} />
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Status
+
+
+
+
+
+
+
+ {/* Grievances Table */}
+
+
+
+
+
+ Ticket ID
+ Student
+ Issue Type
+ Subject
+ Priority
+ Status
+ Date
+ Actions
+
+
+
+ {filteredGrievances.map((grievance) => (
+
+
+
+ {grievance.ticketId}
+
+
+
+
+
+
+
+ {grievance.studentName}
+
+
+ {grievance.studentRollNo}
+
+
+
+
+
+ {grievance.subcategory}
+
+
+
+ {grievance.subject}
+
+
+ {getPriorityChip(grievance.priority)}
+ {getStatusChip(grievance.status)}
+
+ {grievance.submittedDate}
+
+
+ handleViewGrievance(grievance)}
+ >
+
+
+
+
+ ))}
+
+
+
+
+
+ {/* View/Response Dialog */}
+
+
+
+ );
+};
+
+export default LibrarianGrievances;
diff --git a/src/pages/Library/LibrarianReports.jsx b/src/pages/Library/LibrarianReports.jsx
new file mode 100644
index 0000000..e532480
--- /dev/null
+++ b/src/pages/Library/LibrarianReports.jsx
@@ -0,0 +1,392 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Button,
+ Paper,
+ Stack,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+ LinearProgress,
+ Chip,
+} from '@mui/material';
+import {
+ TrendingUp as TrendingUpIcon,
+ MenuBook as BookIcon,
+ People as PeopleIcon,
+ Assessment as AssessmentIcon,
+ Download as DownloadIcon,
+ Print as PrintIcon,
+ Category as CategoryIcon,
+ Inventory as InventoryIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+import { PieChart, Pie, Cell, BarChart, Bar, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, LineChart, Line, CartesianGrid } from 'recharts';
+
+const LibrarianReports = () => {
+ const [reportType, setReportType] = useState('overview');
+ const [timePeriod, setTimePeriod] = useState('month');
+
+ // Mock data
+ const stats = [
+ { label: 'Total Books', value: '2,847', change: '+145 this year', trend: 'up', color: 'primary', icon: BookIcon },
+ { label: 'Active Members', value: '1,234', change: '+56 this month', trend: 'up', color: 'success', icon: PeopleIcon },
+ { label: 'Books Issued', value: '342', change: 'This month', trend: 'up', color: 'info', icon: InventoryIcon },
+ { label: 'Categories', value: '18', change: 'Across library', trend: 'up', color: 'warning', icon: CategoryIcon },
+ ];
+
+ // Category distribution data
+ const categoryData = [
+ { name: 'Computer Science', value: 520, color: '#2196F3' },
+ { name: 'Business', value: 380, color: '#4CAF50' },
+ { name: 'Engineering', value: 340, color: '#FF9800' },
+ { name: 'Mathematics', value: 280, color: '#9C27B0' },
+ { name: 'Science', value: 250, color: '#F44336' },
+ { name: 'Literature', value: 210, color: '#00BCD4' },
+ { name: 'Others', value: 867, color: '#607D8B' },
+ ];
+
+ // Monthly circulation data
+ const circulationData = [
+ { month: 'Aug', issued: 280, returned: 265, reserved: 42 },
+ { month: 'Sep', issued: 310, returned: 298, reserved: 38 },
+ { month: 'Oct', issued: 295, returned: 285, reserved: 45 },
+ { month: 'Nov', issued: 325, returned: 310, reserved: 52 },
+ { month: 'Dec', issued: 298, returned: 290, reserved: 48 },
+ { month: 'Jan', issued: 342, returned: 320, reserved: 55 },
+ ];
+
+ // Top borrowed books
+ const topBooks = [
+ { title: 'Data Structures & Algorithms', author: 'Narasimha Karumanchi', category: 'Computer Science', borrows: 45 },
+ { title: 'Clean Code', author: 'Robert C. Martin', category: 'Software Engineering', borrows: 38 },
+ { title: 'Marketing Management', author: 'Philip Kotler', category: 'Business', borrows: 35 },
+ { title: 'Introduction to Algorithms', author: 'Thomas H. Cormen', category: 'Computer Science', borrows: 32 },
+ { title: 'Database Systems', author: 'Ramez Elmasri', category: 'Computer Science', borrows: 28 },
+ ];
+
+ // Member statistics
+ const memberData = [
+ { type: 'Students', count: 1089, percentage: 88.2 },
+ { type: 'Faculty', count: 98, percentage: 7.9 },
+ { type: 'Staff', count: 32, percentage: 2.6 },
+ { type: 'Alumni', count: 15, percentage: 1.2 },
+ ];
+
+ const COLORS = ['#2196F3', '#4CAF50', '#FF9800', '#9C27B0', '#F44336', '#00BCD4', '#607D8B'];
+
+ return (
+
+
+
+ }>
+ Export PDF
+
+ }>
+ Print Report
+
+
+ }
+ />
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Filters */}
+
+
+
+
+
+ Report Type
+
+
+
+
+
+ Time Period
+
+
+
+
+
+
+
+
+ {/* Monthly Circulation Trends */}
+
+
+
+
+
+
+ Monthly Circulation Trends
+
+
+ Issued, returned, and reserved books over time
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Collection by Category */}
+
+
+
+
+
+ Collection by Category
+
+
+
+
+
+
+
+ {categoryData.map((entry, index) => (
+
+
+
+
+ ))}
+
+ `${name}\n${(percent * 100).toFixed(0)}%`}
+ outerRadius={window.innerWidth < 600 ? 70 : 90}
+ innerRadius={window.innerWidth < 600 ? 40 : 50}
+ fill="#8884d8"
+ dataKey="value"
+ paddingAngle={2}
+ >
+ {categoryData.map((entry, index) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+ {/* Top Borrowed Books */}
+
+
+
+
+ Top Borrowed Books
+
+
+ Most popular books this month
+
+
+ {topBooks.map((book, index) => (
+
+
+
+
+ {book.title}
+
+
+ by {book.author}
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+ {/* Member Statistics */}
+
+
+
+
+ Member Statistics
+
+
+ Distribution of library members by type
+
+
+ {memberData.map((member, index) => (
+
+
+
+ {member.type}
+
+
+
+ {member.count} members
+
+
+
+
+
+
+ ))}
+
+
+ {/* Summary */}
+
+
+ {memberData.reduce((sum, m) => sum + m.count, 0)}
+
+
+ Total Active Library Members
+
+
+
+
+
+
+
+
+ );
+};
+
+export default LibrarianReports;
diff --git a/src/pages/Library/Library.jsx b/src/pages/Library/Library.jsx
index 9e7eea5..68e8e8f 100644
--- a/src/pages/Library/Library.jsx
+++ b/src/pages/Library/Library.jsx
@@ -1,83 +1,710 @@
-import React from 'react';
+import { useEffect, useMemo, useState } from 'react';
+import { motion } from 'framer-motion';
import {
- Container,
- Typography,
Box,
Card,
CardContent,
+ Typography,
+ TextField,
+ InputAdornment,
Button,
Chip,
+ Stack,
+ MenuItem,
+ Divider,
+ useTheme,
+ IconButton,
+ Tooltip,
+ alpha,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
+ Search,
+ Bookmark,
+ LibraryBooks,
+ FilterList,
+ AutoStories,
MenuBook,
- Inventory,
- Schedule,
- CloudDownload,
+ LocalLibrary,
+ CheckCircle as CheckCircleIcon,
} from '@mui/icons-material';
-import PageHeader from '../../components/Common/PageHeader';
-import StatCard from '../../components/Common/StatCard';
+import PageTransition from '../../components/Common/PageTransition';
+import EmptyState from '../../components/Common/EmptyState';
+import { CardSkeleton } from '../../components/Common/LoadingSkeleton';
+import { useSnackbar } from '../../contexts/SnackbarContext';
+import {
+ libraryBooks,
+ myIssuedBooks,
+ myReservedBooks,
+ reserveBook,
+} from '../../data/dummyData';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
const Library = () => {
+ const theme = useTheme();
+ const { showSnackbar } = useSnackbar();
+ const [loading, setLoading] = useState(true);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [categoryFilter, setCategoryFilter] = useState('All');
+ const [availabilityFilter, setAvailabilityFilter] = useState('All');
+ const [reserveDialog, setReserveDialog] = useState(false);
+ const [selectedBook, setSelectedBook] = useState(null);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setLoading(false), 700);
+ return () => clearTimeout(timer);
+ }, []);
+
+ const issuedLimitReached = myIssuedBooks.length >= 3;
+
+ const categories = useMemo(() => {
+ const unique = new Set(libraryBooks.map((b) => b.category));
+ return ['All', ...Array.from(unique)];
+ }, []);
+
+ const filteredBooks = useMemo(() => {
+ let data = [...libraryBooks];
+
+ if (searchQuery) {
+ const q = searchQuery.toLowerCase();
+ data = data.filter(
+ (b) =>
+ b.title.toLowerCase().includes(q) ||
+ b.author.toLowerCase().includes(q) ||
+ b.isbn.toLowerCase().includes(q)
+ );
+ }
+
+ if (categoryFilter !== 'All') {
+ data = data.filter((b) => b.category === categoryFilter);
+ }
+
+ if (availabilityFilter !== 'All') {
+ data = data.filter((b) =>
+ availabilityFilter === 'Available'
+ ? b.availableCopies > 0
+ : b.availableCopies === 0
+ );
+ }
+
+ return data;
+ }, [searchQuery, categoryFilter, availabilityFilter]);
+
+ const handleReserve = (book) => {
+ if (issuedLimitReached) {
+ showSnackbar('You have reached the maximum issued books limit (3).', 'warning');
+ return;
+ }
+ setSelectedBook(book);
+ setReserveDialog(true);
+ };
+
+ const confirmReservation = () => {
+ const result = reserveBook(selectedBook.id);
+ showSnackbar(result.message, result.success ? 'success' : 'error');
+ setReserveDialog(false);
+ setSelectedBook(null);
+ };
+
+ if (loading) {
+ return (
+
+ Loading...
+
+
+ );
+ }
+
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ {/* HEADER */}
+
+
+
+
+
+
+
+ Library Portal
+
+
+ Browse and reserve books from our extensive collection
+
+
+
+
+
+ {/* STATS CARDS */}
+
+
+
+
+
+
+
+ {libraryBooks.length}
+
+
+ Total Books
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {libraryBooks.filter(b => b.availableCopies > 0).length}
+
+
+ Available Now
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {myIssuedBooks.length}
+
+
+ Issued to You
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {myReservedBooks.length}
+
+
+ Reserved
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+ Find Your Books
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ fontSize: '1.1rem',
+ py: 0.5,
+ },
+ }}
+ />
+
+
+ {/* FILTER SECTION */}
+
+
+ setCategoryFilter(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ >
+ {categories.map((cat) => (
+
+ {cat}
+
+ ))}
+
+
+
+ setAvailabilityFilter(e.target.value)}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ >
+ All
+ Available
+ Out of Stock
+
+
+
+
+
+
+
+ {/* BOOK CARDS IN GRID */}
+ {filteredBooks.length === 0 ? (
+
-
-
-
-
-
-
-
- Library System Coming Soon
-
-
- Browse books, reserve materials, and access digital resources.
-
-
-
-
-
+ ) : (
+
+
+ {filteredBooks.length} {filteredBooks.length === 1 ? 'Book' : 'Books'} Found
+
+
+ {filteredBooks.map((book) => {
+ const isAvailable = book.availableCopies > 0;
+ return (
+
+
+ {/* BOOK COVER IMAGE - Fixed Height */}
+
+
+ {/* AVAILABILITY BADGE */}
+
+
+
+
+
+ {/* TITLE - Fixed 2 lines */}
+
+ {book.title}
+
+
+ {/* AUTHOR */}
+
+
+
+ {book.author}
+
+
+
+ {/* CATEGORY & SHELF */}
+
+
+
+
+
+ 📍 {book.shelfLocation}
+
+
+
+ {/* WARNING MESSAGE */}
+ {issuedLimitReached && (
+
+
+ ⚠️ Limit reached (3 books max)
+
+
+ )}
+
+ {/* RESERVE BUTTON */}
+ }
+ disabled={!isAvailable || issuedLimitReached}
+ onClick={() => handleReserve(book)}
+ sx={{
+ mt: 2,
+ py: 1,
+ borderRadius: 2,
+ fontWeight: 'bold',
+ background: isAvailable
+ ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
+ : 'grey.300',
+ '&:hover': {
+ background: isAvailable
+ ? 'linear-gradient(135deg, #5568d3 0%, #654391 100%)'
+ : 'grey.300',
+ },
+ }}
+ >
+ {isAvailable ? 'Reserve Book' : 'Not Available'}
+
+
+
+
+ );
+ })}
+
+
+ )}
+
+ {/* RESERVATION CONFIRMATION DIALOG */}
+
+
+
);
};
diff --git a/src/pages/Library/LibraryCatalog.jsx b/src/pages/Library/LibraryCatalog.jsx
index 778320f..f0b4843 100644
--- a/src/pages/Library/LibraryCatalog.jsx
+++ b/src/pages/Library/LibraryCatalog.jsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
-import { motion } from 'framer-motion';
+import { motion, AnimatePresence } from 'framer-motion';
import {
Box,
Card,
@@ -44,6 +44,8 @@ import {
LinearProgress,
Avatar,
Snackbar,
+ alpha,
+ useTheme,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
@@ -68,6 +70,8 @@ import {
Payment,
TrendingUp,
LibraryBooks,
+ LocalLibrary,
+ AccountBalance,
} from '@mui/icons-material';
import {
libraryBooks,
@@ -83,6 +87,7 @@ import { GridSkeleton } from '../../components/Common/LoadingSkeleton';
import { pageTransition, staggerContainer, fadeInUp } from '../../utils/animations';
const LibraryCatalog = () => {
+ const theme = useTheme();
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState(0);
const [searchQuery, setSearchQuery] = useState('');
@@ -104,28 +109,25 @@ const LibraryCatalog = () => {
return dueDate < new Date() && book.status === 'issued';
});
- // Loading effect
useEffect(() => {
const timer = setTimeout(() => {
setLoading(false);
- }, 1400);
+ }, 1000);
return () => clearTimeout(timer);
}, []);
- // Calculate fines
const calculateFine = (dueDate) => {
const due = new Date(dueDate);
const today = new Date();
if (today > due) {
const daysOverdue = Math.floor((today - due) / (1000 * 60 * 60 * 24));
- return daysOverdue * 50; // PKR 50 per day
+ return daysOverdue * 50;
}
return 0;
};
const totalFines = myIssuedBooks.reduce((sum, book) => sum + calculateFine(book.dueDate), 0);
- // Filter and sort books
const filteredBooks = libraryBooks
.filter(book => {
const matchesSearch =
@@ -134,7 +136,6 @@ const LibraryCatalog = () => {
book.isbn.includes(searchQuery);
const matchesCategory = categoryFilter === 'All' || book.category === categoryFilter;
-
const matchesAvailability =
availabilityFilter === 'All' ||
(availabilityFilter === 'Available' && book.availableCopies > 0) ||
@@ -204,16 +205,15 @@ const LibraryCatalog = () => {
const availabilityOptions = ['All', 'Available', 'Issued'];
const languages = ['All', 'English', 'Urdu'];
- // Show loading skeleton
if (loading) {
return (
- Digital Library
+ Digital Library Catalog
- Browse and manage your books
+ Loading your books...
@@ -222,75 +222,285 @@ const LibraryCatalog = () => {
}
return (
-
- {/* Header */}
-
-
- Digital Library
-
-
- Browse {libraryBooks.length * 100}+ books and resources
-
+
+ {/* Enhanced Header */}
+
+
+
+
+
+
+
+
+ Digital Library Catalog
+
+
+ Browse {libraryBooks.length * 100}+ books and digital resources
+
+
+
+
+
+ {/* Decorative Corner Markers */}
+
{/* Overdue Warning */}
- {overdueBooks.length > 0 && (
- }>
- Pay Fine
-
- }
- >
- Overdue Books Alert! You have {overdueBooks.length} overdue book(s).
- Total Fine: PKR {totalFines}
-
- )}
+
+ {overdueBooks.length > 0 && (
+
+ }
+ sx={{ fontWeight: 600 }}
+ >
+ Pay Fine
+
+ }
+ >
+
+ ⚠️ Overdue Books Alert!
+
+
+ You have {overdueBooks.length} overdue book(s). Total Fine: PKR {totalFines}
+
+
+
+ )}
+
- {/* Quick Stats */}
-
-
- }
- color="primary"
- />
+ {/* Enhanced Stats Cards */}
+
+
+
+
+
+
+
+
+
+
+ Books Issued
+
+
+ {issuedBooksCount}
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+ Available Books
+
+
+ {availableBooks}
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+ Overdue Books
+
+
+ {overdueBooks.length}
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+ Total Fines
+
+
+ ₨{totalFines}
+
+
+
+
+
- {/* Tabs */}
-
+ {/* Enhanced Tabs */}
+
setActiveTab(newValue)}
- sx={{ borderBottom: 1, borderColor: 'divider' }}
+ sx={{
+ borderBottom: 1,
+ borderColor: 'divider',
+ '& .MuiTab-root': {
+ fontWeight: 600,
+ textTransform: 'none',
+ fontSize: '1rem',
+ },
+ }}
>
} label="Browse Books" iconPosition="start" />
} label="My Books" iconPosition="start" />
@@ -300,95 +510,127 @@ const LibraryCatalog = () => {
{/* TAB 1: Browse Books */}
{activeTab === 0 && (
- {/* Search & Filter Bar */}
-
-
-
-
- setSearchQuery(e.target.value)}
- InputProps={{
- startAdornment: (
-
-
-
- ),
- }}
- />
-
-
-
-
-
- Category:
-
- {categories.map((cat) => (
- setCategoryFilter(cat)}
- color={categoryFilter === cat ? 'primary' : 'default'}
- variant={categoryFilter === cat ? 'filled' : 'outlined'}
- size="small"
- />
- ))}
-
-
-
- Availability:
-
- {availabilityOptions.map((opt) => (
- setAvailabilityFilter(opt)}
- color={availabilityFilter === opt ? 'primary' : 'default'}
- variant={availabilityFilter === opt ? 'filled' : 'outlined'}
- size="small"
- />
- ))}
-
- Language:
+ {/* Enhanced Search & Filter Card */}
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ background: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.05)' : '#ffffff',
+ },
+ }}
+ />
+
+
+
+
+
+ Filters
- {languages.map((lang) => (
- setLanguageFilter(lang)}
- color={languageFilter === lang ? 'primary' : 'default'}
- variant={languageFilter === lang ? 'filled' : 'outlined'}
- size="small"
- />
- ))}
-
-
-
- Sort By
-
-
-
-
+
+
+
+ Category:
+
+ {categories.map((cat) => (
+ setCategoryFilter(cat)}
+ sx={{
+ fontWeight: 600,
+ ...(categoryFilter === cat && {
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ color: 'white',
+ }),
+ }}
+ color={categoryFilter === cat ? undefined : 'default'}
+ variant={categoryFilter === cat ? undefined : 'outlined'}
+ size="small"
+ />
+ ))}
+
+
+
+ Availability:
+
+ {availabilityOptions.map((opt) => (
+ setAvailabilityFilter(opt)}
+ sx={{
+ fontWeight: 600,
+ ...(availabilityFilter === opt && {
+ background: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
+ color: 'white',
+ }),
+ }}
+ color={availabilityFilter === opt ? undefined : 'default'}
+ variant={availabilityFilter === opt ? undefined : 'outlined'}
+ size="small"
+ />
+ ))}
+
+
+
+
+
+ Sort By
+
+
+
{/* Book Grid */}
-
- {filteredBooks.length} books found
-
-
+
+
+ {filteredBooks.length} books found
+
+
+
{filteredBooks.map((book) => (
{
height: '100%',
display: 'flex',
flexDirection: 'column',
+ borderRadius: 3,
+ border: '1px solid',
+ borderColor: 'divider',
+ transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'&:hover': {
- boxShadow: 6,
- transform: 'translateY(-4px)',
- transition: 'all 0.3s',
+ transform: 'translateY(-8px)',
+ boxShadow: theme.palette.mode === 'dark'
+ ? '0 12px 32px rgba(0,0,0,0.5)'
+ : '0 12px 32px rgba(25,118,210,0.2)',
+ borderColor: 'primary.main',
},
}}
>
-
-
+
+
+ 0 ? 'Available' : 'Issued'}
+ size="small"
+ sx={{
+ position: 'absolute',
+ top: 12,
+ right: 12,
+ fontWeight: 'bold',
+ background: book.availableCopies > 0
+ ? 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)'
+ : 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
+ color: 'white',
+ border: '2px solid rgba(255,255,255,0.3)',
+ }}
+ />
+
+
{book.title}
@@ -421,12 +686,15 @@ const LibraryCatalog = () => {
ISBN: {book.isbn}
-
-
- 0 ? 'Available' : 'Issued'}
- size="small"
- color={book.availableCopies > 0 ? 'success' : 'warning'}
+
+
@@ -448,6 +716,15 @@ const LibraryCatalog = () => {
size="small"
onClick={() => handleViewDetails(book)}
startIcon={}
+ sx={{
+ borderRadius: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #5568d3 0%, #6a3f8f 100%)',
+ },
+ }}
>
View Details
@@ -458,11 +735,16 @@ const LibraryCatalog = () => {
size="small"
onClick={() => handleReserve(book.id)}
startIcon={}
+ sx={{
+ borderRadius: 2,
+ fontWeight: 600,
+ textTransform: 'none',
+ }}
>
Reserve
) : (
-
@@ -630,12 +995,18 @@ const LibraryCatalog = () => {
)}
- {/* Book Details Modal */}
+ {/* Book Details Modal - Enhanced */}
-
+
@@ -694,7 +1070,15 @@ const LibraryCatalog = () => {
Category
-
+
Language
@@ -717,7 +1101,15 @@ const LibraryCatalog = () => {
{selectedBook.description}
-
+
Availability
@@ -736,7 +1128,16 @@ const LibraryCatalog = () => {
@@ -744,17 +1145,21 @@ const LibraryCatalog = () => {
setDetailsModalOpen(false)}>Close
- }>
+ } sx={{ borderRadius: 2 }}>
Share
- }>
- Add to Wishlist
-
{selectedBook.availableCopies > 0 && (
}
onClick={() => handleReserve(selectedBook.id)}
+ sx={{
+ borderRadius: 2,
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #5568d3 0%, #6a3f8f 100%)',
+ },
+ }}
>
Reserve Book
@@ -764,46 +1169,20 @@ const LibraryCatalog = () => {
)}
- {/* QR Scanner Modal */}
-
-
{/* QR Scanner FAB */}
setQrScannerOpen(true)}
>
@@ -814,8 +1193,13 @@ const LibraryCatalog = () => {
open={snackbar.open}
autoHideDuration={4000}
onClose={() => setSnackbar({ ...snackbar, open: false })}
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
- setSnackbar({ ...snackbar, open: false })}>
+ setSnackbar({ ...snackbar, open: false })}
+ sx={{ borderRadius: 2 }}
+ >
{snackbar.message}
diff --git a/src/pages/Library/Reservations.jsx b/src/pages/Library/Reservations.jsx
new file mode 100644
index 0000000..4570ed1
--- /dev/null
+++ b/src/pages/Library/Reservations.jsx
@@ -0,0 +1,356 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Button,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Chip,
+ Stack,
+ TextField,
+ InputAdornment,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+ Avatar,
+ Snackbar,
+ Alert,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ CheckCircle as ApproveIcon,
+ Cancel as CancelIcon,
+ EventAvailable as EventIcon,
+ PendingActions as PendingIcon,
+ Book as BookIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const Reservations = () => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterStatus, setFilterStatus] = useState('all');
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+
+ // Mock reservations data
+ const [reservations, setReservations] = useState([
+ {
+ id: 1,
+ bookTitle: 'Artificial Intelligence: A Modern Approach',
+ bookIsbn: '978-0136042594',
+ studentName: 'Zain Ali',
+ studentRollNo: 'F22-BS-015',
+ studentEmail: 'zain.ali@university.edu.pk',
+ studentPhoto: 'https://i.pravatar.cc/150?img=41',
+ reservationDate: '2026-01-22',
+ expectedAvailability: '2026-01-28',
+ status: 'pending',
+ priority: 'high',
+ },
+ {
+ id: 2,
+ bookTitle: 'Machine Learning Yearning',
+ bookIsbn: '978-0999579923',
+ studentName: 'Hira Khan',
+ studentRollNo: 'F22-BS-016',
+ studentEmail: 'hira.khan@university.edu.pk',
+ studentPhoto: 'https://i.pravatar.cc/150?img=42',
+ reservationDate: '2026-01-23',
+ expectedAvailability: '2026-01-30',
+ status: 'pending',
+ priority: 'medium',
+ },
+ {
+ id: 3,
+ bookTitle: 'Design Patterns',
+ bookIsbn: '978-0201633610',
+ studentName: 'Usman Ahmed',
+ studentRollNo: 'F22-BS-017',
+ studentEmail: 'usman.ahmed@university.edu.pk',
+ studentPhoto: 'https://i.pravatar.cc/150?img=43',
+ reservationDate: '2026-01-20',
+ expectedAvailability: '2026-01-26',
+ status: 'approved',
+ priority: 'high',
+ },
+ {
+ id: 4,
+ bookTitle: 'The Pragmatic Programmer',
+ bookIsbn: '978-0135957059',
+ studentName: 'Aisha Malik',
+ studentRollNo: 'F22-BS-018',
+ studentEmail: 'aisha.malik@university.edu.pk',
+ studentPhoto: 'https://i.pravatar.cc/150?img=44',
+ reservationDate: '2026-01-24',
+ expectedAvailability: '2026-02-01',
+ status: 'pending',
+ priority: 'low',
+ },
+ {
+ id: 5,
+ bookTitle: 'Code Complete',
+ bookIsbn: '978-0735619678',
+ studentName: 'Hassan Raza',
+ studentRollNo: 'F22-BS-019',
+ studentEmail: 'hassan.raza@university.edu.pk',
+ studentPhoto: 'https://i.pravatar.cc/150?img=45',
+ reservationDate: '2026-01-21',
+ expectedAvailability: '2026-01-27',
+ status: 'cancelled',
+ priority: 'medium',
+ },
+ ]);
+
+ const stats = [
+ {
+ label: 'Pending Reservations',
+ value: reservations.filter((r) => r.status === 'pending').length.toString(),
+ change: 'Need approval',
+ trend: 'up',
+ color: 'warning',
+ icon: PendingIcon,
+ },
+ {
+ label: 'Approved',
+ value: reservations.filter((r) => r.status === 'approved').length.toString(),
+ change: 'Ready to issue',
+ trend: 'up',
+ color: 'success',
+ icon: EventIcon,
+ },
+ {
+ label: 'Total Reservations',
+ value: reservations.length.toString(),
+ change: 'This month',
+ trend: 'up',
+ color: 'primary',
+ icon: BookIcon,
+ },
+ ];
+
+ const handleApprove = (id) => {
+ setReservations(
+ reservations.map((r) => (r.id === id ? { ...r, status: 'approved' } : r))
+ );
+ setSnackbar({ open: true, message: 'Reservation approved!', severity: 'success' });
+ };
+
+ const handleCancel = (id) => {
+ setReservations(
+ reservations.map((r) => (r.id === id ? { ...r, status: 'cancelled' } : r))
+ );
+ setSnackbar({ open: true, message: 'Reservation cancelled!', severity: 'warning' });
+ };
+
+ const filteredReservations = reservations.filter((reservation) => {
+ const matchesSearch =
+ reservation.bookTitle.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ reservation.studentName.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ reservation.studentRollNo.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ reservation.bookIsbn.includes(searchQuery);
+ const matchesStatus = filterStatus === 'all' || reservation.status === filterStatus;
+ return matchesSearch && matchesStatus;
+ });
+
+ const getStatusChip = (status) => {
+ const statusConfig = {
+ pending: { color: 'warning', label: 'Pending' },
+ approved: { color: 'success', label: 'Approved' },
+ cancelled: { color: 'error', label: 'Cancelled' },
+ };
+ const config = statusConfig[status] || statusConfig.pending;
+ return ;
+ };
+
+ const getPriorityChip = (priority) => {
+ const priorityConfig = {
+ high: { color: 'error', label: 'High' },
+ medium: { color: 'warning', label: 'Medium' },
+ low: { color: 'info', label: 'Low' },
+ };
+ const config = priorityConfig[priority] || priorityConfig.medium;
+ return ;
+ };
+
+ return (
+
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Filters Card */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Status
+
+
+
+
+
+
+
+ {/* Reservations Table */}
+
+
+
+
+
+ Student
+ Book
+ ISBN
+ Reservation Date
+ Expected Availability
+ Priority
+ Status
+ Actions
+
+
+
+ {filteredReservations.map((reservation) => (
+
+
+
+
+
+
+ {reservation.studentName}
+
+
+ {reservation.studentRollNo}
+
+
+
+
+
+
+ {reservation.bookTitle}
+
+
+
+
+ {reservation.bookIsbn}
+
+
+
+ {reservation.reservationDate}
+
+
+ {reservation.expectedAvailability}
+
+ {getPriorityChip(reservation.priority)}
+ {getStatusChip(reservation.status)}
+
+ {reservation.status === 'pending' ? (
+
+ handleApprove(reservation.id)}
+ >
+
+
+ handleCancel(reservation.id)}
+ >
+
+
+
+ ) : (
+
+ No actions
+
+ )}
+
+
+ ))}
+
+
+
+
+
+ {filteredReservations.length === 0 && (
+
+
+
+
+ No reservations found
+
+
+ Try adjusting your filters or search terms
+
+
+
+ )}
+
+ {/* Snackbar */}
+ setSnackbar({ ...snackbar, open: false })}
+ >
+ setSnackbar({ ...snackbar, open: false })}
+ >
+ {snackbar.message}
+
+
+
+
+ );
+};
+
+export default Reservations;
diff --git a/src/pages/Student/AlumniDirectory.jsx b/src/pages/Student/AlumniDirectory.jsx
new file mode 100644
index 0000000..d2dd547
--- /dev/null
+++ b/src/pages/Student/AlumniDirectory.jsx
@@ -0,0 +1,377 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ TextField,
+ InputAdornment,
+ Avatar,
+ Chip,
+ Stack,
+ IconButton,
+ Button,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Paper,
+ Divider,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ Work as WorkIcon,
+ LocationOn as LocationIcon,
+ LinkedIn as LinkedInIcon,
+ Email as EmailIcon,
+ Phone as PhoneIcon,
+ School as SchoolIcon,
+ CalendarToday as CalendarIcon,
+ BusinessCenter as IndustryIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const AlumniDirectory = () => {
+ const [searchQuery, setSearchQuery] = useState('');
+ const [selectedAlumni, setSelectedAlumni] = useState(null);
+ const [dialogOpen, setDialogOpen] = useState(false);
+
+ // Mock alumni data
+ const alumni = [
+ {
+ id: 1,
+ name: 'Ahmed Hassan',
+ photo: 'https://i.pravatar.cc/150?img=12',
+ graduationYear: 2020,
+ degree: 'BS Computer Science',
+ currentRole: 'Senior Software Engineer',
+ company: 'Google',
+ location: 'Dubai, UAE',
+ industry: 'Technology',
+ email: 'ahmed.hassan@gmail.com',
+ phone: '+971-50-123-4567',
+ linkedin: 'linkedin.com/in/ahmedhassan',
+ bio: 'Passionate about building scalable systems and mentoring young developers. Specialized in cloud architecture and distributed systems.',
+ expertise: ['Cloud Computing', 'System Design', 'Microservices'],
+ mentoring: true,
+ },
+ {
+ id: 2,
+ name: 'Sara Malik',
+ photo: 'https://i.pravatar.cc/150?img=5',
+ graduationYear: 2019,
+ degree: 'BS Software Engineering',
+ currentRole: 'Product Manager',
+ company: 'Microsoft',
+ location: 'Seattle, USA',
+ industry: 'Technology',
+ email: 'sara.malik@outlook.com',
+ phone: '+1-206-555-0123',
+ linkedin: 'linkedin.com/in/saramalik',
+ bio: 'Leading product strategy for Azure AI services. Love helping students navigate their career paths in tech.',
+ expertise: ['Product Management', 'AI/ML', 'Strategy'],
+ mentoring: true,
+ },
+ {
+ id: 3,
+ name: 'Omar Abdullah',
+ photo: 'https://i.pravatar.cc/150?img=13',
+ graduationYear: 2018,
+ degree: 'BS Computer Science',
+ currentRole: 'Data Scientist',
+ company: 'Amazon',
+ location: 'London, UK',
+ industry: 'E-commerce',
+ email: 'omar.abdullah@amazon.com',
+ phone: '+44-20-7123-4567',
+ linkedin: 'linkedin.com/in/omarabdullah',
+ bio: 'Working on recommendation systems and predictive analytics. Always happy to discuss data science opportunities.',
+ expertise: ['Machine Learning', 'Data Analytics', 'Python'],
+ mentoring: true,
+ },
+ {
+ id: 4,
+ name: 'Fatima Noor',
+ photo: 'https://i.pravatar.cc/150?img=9',
+ graduationYear: 2021,
+ degree: 'BS Computer Science',
+ currentRole: 'Full Stack Developer',
+ company: 'Careem',
+ location: 'Karachi, Pakistan',
+ industry: 'Transportation',
+ email: 'fatima.noor@careem.com',
+ phone: '+92-21-3456-7890',
+ linkedin: 'linkedin.com/in/fatimanoor',
+ bio: 'Building mobile-first web applications. Passionate about creating impact through technology in Pakistan.',
+ expertise: ['React', 'Node.js', 'Mobile Development'],
+ mentoring: false,
+ },
+ {
+ id: 5,
+ name: 'Hassan Ali',
+ photo: 'https://i.pravatar.cc/150?img=14',
+ graduationYear: 2017,
+ degree: 'BS Software Engineering',
+ currentRole: 'Cybersecurity Consultant',
+ company: 'Deloitte',
+ location: 'Abu Dhabi, UAE',
+ industry: 'Consulting',
+ email: 'hassan.ali@deloitte.com',
+ phone: '+971-2-987-6543',
+ linkedin: 'linkedin.com/in/hassanali',
+ bio: 'Helping organizations secure their digital infrastructure. Specialized in penetration testing and security audits.',
+ expertise: ['Cybersecurity', 'Ethical Hacking', 'Risk Assessment'],
+ mentoring: true,
+ },
+ {
+ id: 6,
+ name: 'Ayesha Khan',
+ photo: 'https://i.pravatar.cc/150?img=10',
+ graduationYear: 2020,
+ degree: 'BS Computer Science',
+ currentRole: 'UX Designer',
+ company: 'Spotify',
+ location: 'Stockholm, Sweden',
+ industry: 'Entertainment',
+ email: 'ayesha.khan@spotify.com',
+ phone: '+46-8-123-4567',
+ linkedin: 'linkedin.com/in/ayeshakhan',
+ bio: 'Creating delightful user experiences for millions of users worldwide. Love to discuss design thinking and user research.',
+ expertise: ['UX Design', 'User Research', 'Prototyping'],
+ mentoring: true,
+ },
+ ];
+
+ const filteredAlumni = alumni.filter(a =>
+ a.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ a.company.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ a.currentRole.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ a.industry.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ const handleViewProfile = (alumnus) => {
+ setSelectedAlumni(alumnus);
+ setDialogOpen(true);
+ };
+
+ return (
+
+
+
+
+ {/* Search */}
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ {/* Alumni Grid */}
+
+ {filteredAlumni.map((alumnus) => (
+
+
+
+
+
+
+
+ {alumnus.name}
+
+
+ Class of {alumnus.graduationYear}
+
+
+
+ }
+ label={alumnus.currentRole}
+ color="primary"
+ size="small"
+ />
+
+
+
+
+ {alumnus.company}
+
+
+
+
+
+
+ {alumnus.location}
+
+
+
+ {alumnus.mentoring && (
+
+ )}
+
+ handleViewProfile(alumnus)}
+ >
+ View Profile
+
+
+
+
+
+ ))}
+
+
+ {/* Profile Dialog */}
+
+
+
+ );
+};
+
+export default AlumniDirectory;
diff --git a/src/pages/Student/Dashboard.jsx b/src/pages/Student/Dashboard.jsx
index 614bf7f..2cf6a3e 100644
--- a/src/pages/Student/Dashboard.jsx
+++ b/src/pages/Student/Dashboard.jsx
@@ -18,6 +18,7 @@ import {
CardMedia,
IconButton,
Divider,
+ Stack,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
@@ -60,6 +61,7 @@ import {
AreaChart,
} from 'recharts';
import { useNavigate } from 'react-router-dom';
+import { useTheme } from '@mui/material/styles';
import { useAuth } from '../../contexts/AuthContext';
import {
courses,
@@ -75,6 +77,7 @@ import { pageTransition, staggerContainer, fadeInUp } from '../../utils/animatio
const Dashboard = () => {
const navigate = useNavigate();
+ const theme = useTheme();
const { user } = useAuth();
const [currentTime, setCurrentTime] = useState(new Date());
const [loading, setLoading] = useState(true);
@@ -163,8 +166,10 @@ const Dashboard = () => {
{
{getGreeting()}, {currentUser.name.split(' ')[0]}! 👋
-
+
{formatDateTime()}
@@ -186,8 +191,8 @@ const Dashboard = () => {
startIcon={}
onClick={() => navigate('/attendance')}
sx={{
- bgcolor: 'rgba(255, 255, 255, 0.2)',
- '&:hover': { bgcolor: 'rgba(255, 255, 255, 0.3)' },
+ bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.2)' : 'rgba(255, 255, 255, 0.2)',
+ '&:hover': { bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.2)' : 'rgba(255, 255, 255, 0.2)' },
}}
>
Mark Attendance
@@ -197,8 +202,8 @@ const Dashboard = () => {
startIcon={}
onClick={() => navigate('/lms')}
sx={{
- bgcolor: 'rgba(255, 255, 255, 0.2)',
- '&:hover': { bgcolor: 'rgba(255, 255, 255, 0.3)' },
+ bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.2)' : 'rgba(255, 255, 255, 0.2)',
+ '&:hover': { bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.2)' : 'rgba(255, 255, 255, 0.2)' },
}}
>
Submit Assignment
@@ -208,8 +213,8 @@ const Dashboard = () => {
startIcon={}
onClick={() => navigate('/finance')}
sx={{
- bgcolor: 'rgba(255, 255, 255, 0.2)',
- '&:hover': { bgcolor: 'rgba(255, 255, 255, 0.3)' },
+ bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.2)' : 'rgba(255, 255, 255, 0.2)',
+ '&:hover': { bgcolor: theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.2)' : 'rgba(255, 255, 255, 0.2)' },
}}
>
Pay Fees
@@ -319,9 +324,15 @@ const Dashboard = () => {
>
{/* LEFT: Line Chart - GPA Trend */}
-
+
-
+
Academic Performance
@@ -330,27 +341,42 @@ const Dashboard = () => {
GPA trend over 7 semesters
-
+
+
+
+
{loading ? (
) : (
-
-
+
+
-
-
+
+
-
-
-
+
+
+
{
stroke="#1976D2"
strokeWidth={3}
fill="url(#colorGpa)"
+ dot={{ fill: '#1976D2', r: 5, strokeWidth: 2, stroke: 'white' }}
+ activeDot={{ r: 7, strokeWidth: 2, stroke: 'white' }}
animationDuration={1500}
/>
@@ -370,40 +398,69 @@ const Dashboard = () => {
{/* RIGHT: Bar Chart - Attendance Overview */}
-
+
-
-
- Attendance Overview
-
-
- Course-wise attendance percentage
-
-
+
+
+
+ Attendance Overview
+
+
+ Course-wise attendance percentage
+
+
+
+
{loading ? (
) : (
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
- {attendanceData.map((entry, index) => (
-
- ))}
-
+ />
)}
diff --git a/src/pages/Student/MyAssignments.jsx b/src/pages/Student/MyAssignments.jsx
new file mode 100644
index 0000000..6c719d9
--- /dev/null
+++ b/src/pages/Student/MyAssignments.jsx
@@ -0,0 +1,411 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Chip,
+ Stack,
+ LinearProgress,
+ useTheme,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Assignment,
+ CheckCircle,
+ PendingActions,
+ Warning,
+ CalendarToday,
+ Grade,
+ ArrowForward,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const MyAssignments = () => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+
+ // Mock data - would come from API
+ const [assignments] = useState([
+ {
+ id: 1,
+ title: 'Database Design Assignment',
+ course: 'Database Systems',
+ courseCode: 'CS-401',
+ dueDate: '2026-01-30',
+ submittedDate: null,
+ status: 'pending',
+ totalMarks: 100,
+ obtainedMarks: null,
+ description: 'Design a normalized database schema for an e-commerce system',
+ },
+ {
+ id: 2,
+ title: 'Algorithm Analysis Report',
+ course: 'Data Structures & Algorithms',
+ courseCode: 'CS-302',
+ dueDate: '2026-01-28',
+ submittedDate: null,
+ status: 'overdue',
+ totalMarks: 50,
+ obtainedMarks: null,
+ description: 'Analyze time complexity of sorting algorithms',
+ },
+ {
+ id: 3,
+ title: 'React Component Development',
+ course: 'Web Development',
+ courseCode: 'CS-501',
+ dueDate: '2026-01-25',
+ submittedDate: '2026-01-24',
+ status: 'submitted',
+ totalMarks: 100,
+ obtainedMarks: null,
+ description: 'Create reusable React components with proper props',
+ },
+ {
+ id: 4,
+ title: 'Network Security Analysis',
+ course: 'Computer Networks',
+ courseCode: 'CS-403',
+ dueDate: '2026-01-20',
+ submittedDate: '2026-01-19',
+ status: 'graded',
+ totalMarks: 100,
+ obtainedMarks: 85,
+ description: 'Analyze security vulnerabilities in network protocols',
+ },
+ ]);
+
+ const stats = {
+ total: assignments.length,
+ pending: assignments.filter(a => a.status === 'pending').length,
+ submitted: assignments.filter(a => a.status === 'submitted').length,
+ graded: assignments.filter(a => a.status === 'graded').length,
+ };
+
+ const getStatusColor = (status) => {
+ switch (status) {
+ case 'pending': return 'warning';
+ case 'overdue': return 'error';
+ case 'submitted': return 'info';
+ case 'graded': return 'success';
+ default: return 'default';
+ }
+ };
+
+ const getStatusIcon = (status) => {
+ switch (status) {
+ case 'pending': return ;
+ case 'overdue': return ;
+ case 'submitted': return ;
+ case 'graded': return ;
+ default: return ;
+ }
+ };
+
+ const getDaysRemaining = (dueDate) => {
+ const today = new Date();
+ const due = new Date(dueDate);
+ const diffTime = due - today;
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+ return diffDays;
+ };
+
+ return (
+
+ {/* HEADER */}
+
+
+ {/* STATS CARDS */}
+
+
+
+
+
+
+
+ {stats.total}
+
+
+ Total
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.pending}
+
+
+ Pending
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.submitted}
+
+
+ Submitted
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.graded}
+
+
+ Graded
+
+
+
+
+
+
+
+
+
+
+
+ {/* ASSIGNMENTS LIST */}
+
+
+ All Assignments
+
+
+
+ {assignments.map((assignment) => {
+ const daysRemaining = getDaysRemaining(assignment.dueDate);
+ return (
+
+
+
+ {/* Header */}
+
+
+
+ {assignment.title}
+
+
+
+
+ {assignment.course}
+
+
+
+
+
+
+ {/* Description */}
+
+ {assignment.description}
+
+
+ {/* Due Date */}
+
+
+
+ Due: {new Date(assignment.dueDate).toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric'
+ })}
+
+ {assignment.status === 'pending' && daysRemaining >= 0 && (
+
+ )}
+
+
+ {/* Graded Info */}
+ {assignment.status === 'graded' && (
+
+
+
+ Score: {assignment.obtainedMarks}/{assignment.totalMarks}
+
+
+ {Math.round((assignment.obtainedMarks / assignment.totalMarks) * 100)}%
+
+
+
+
+ )}
+
+ {/* Action Button */}
+ }
+ onClick={() => navigate(`/lms/assignment/${assignment.id}`)}
+ sx={{
+ py: 1.2,
+ borderRadius: 2,
+ fontWeight: 'bold',
+ ...(assignment.status === 'pending' || assignment.status === 'overdue' ? {
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #5568d3 0%, #654391 100%)',
+ },
+ } : {}),
+ }}
+ >
+ {assignment.status === 'pending' || assignment.status === 'overdue'
+ ? 'Submit Now'
+ : assignment.status === 'submitted'
+ ? 'View Submission'
+ : 'View Details'}
+
+
+
+
+ );
+ })}
+
+
+
+ );
+};
+
+export default MyAssignments;
diff --git a/src/pages/Student/MyTickets.jsx b/src/pages/Student/MyTickets.jsx
new file mode 100644
index 0000000..080e082
--- /dev/null
+++ b/src/pages/Student/MyTickets.jsx
@@ -0,0 +1,484 @@
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Chip,
+ Stack,
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Divider,
+ TextField,
+ useTheme,
+ alpha,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ SupportAgent,
+ ExpandMore,
+ School,
+ AttachMoney,
+ Build,
+ Warning,
+ Computer,
+ Report,
+ CheckCircle,
+ HourglassEmpty,
+ Cancel,
+ Send,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const MyTickets = () => {
+ const theme = useTheme();
+ const [expanded, setExpanded] = useState(false);
+ const [replyText, setReplyText] = useState('');
+
+ // Mock tickets data
+ const [tickets] = useState([
+ {
+ id: 'GR-2026-001',
+ subject: 'Incorrect Grade Posted',
+ category: 'Academic',
+ description: 'My final exam grade for Database Systems was posted incorrectly. I scored 85 but it shows 75 in the portal.',
+ date: '2026-01-23',
+ status: 'In Progress',
+ priority: 'Medium',
+ conversation: [
+ {
+ from: 'You',
+ message: 'My final exam grade for Database Systems was posted incorrectly.',
+ timestamp: '2026-01-23 10:30 AM',
+ },
+ {
+ from: 'Admin (Sarah Johnson)',
+ message: 'We are looking into this issue. Please provide your exam roll number.',
+ timestamp: '2026-01-23 2:15 PM',
+ },
+ ],
+ },
+ {
+ id: 'GR-2026-002',
+ subject: 'Fee Challan Not Generated',
+ category: 'Finance',
+ description: 'Unable to generate fee challan for Spring 2026 semester. System shows error message.',
+ date: '2026-01-22',
+ status: 'Resolved',
+ priority: 'High',
+ conversation: [
+ {
+ from: 'You',
+ message: 'Unable to generate fee challan. Getting error code FIN-402.',
+ timestamp: '2026-01-22 9:00 AM',
+ },
+ {
+ from: 'Finance Officer',
+ message: 'Issue resolved. Your challan has been generated. Check your email.',
+ timestamp: '2026-01-22 11:30 AM',
+ },
+ ],
+ },
+ {
+ id: 'GR-2026-003',
+ subject: 'AC Not Working in Lab 3',
+ category: 'Facilities',
+ description: 'The air conditioning system in Computer Lab 3 has been non-functional for 3 days.',
+ date: '2026-01-20',
+ status: 'Resolved',
+ priority: 'Low',
+ conversation: [
+ {
+ from: 'You',
+ message: 'AC not working in Lab 3 for past 3 days. Very hot environment.',
+ timestamp: '2026-01-20 2:00 PM',
+ },
+ {
+ from: 'Facilities Manager',
+ message: 'Maintenance team has fixed the AC unit. Operational now.',
+ timestamp: '2026-01-21 10:00 AM',
+ },
+ ],
+ },
+ ]);
+
+ const stats = {
+ total: tickets.length,
+ inProgress: tickets.filter(t => t.status === 'In Progress').length,
+ resolved: tickets.filter(t => t.status === 'Resolved').length,
+ rejected: tickets.filter(t => t.status === 'Rejected').length,
+ };
+
+ const getCategoryIcon = (category) => {
+ switch (category) {
+ case 'Academic': return ;
+ case 'Finance': return ;
+ case 'Facilities': return ;
+ case 'Harassment': return ;
+ case 'IT Support': return ;
+ default: return ;
+ }
+ };
+
+ const getStatusIcon = (status) => {
+ switch (status) {
+ case 'Submitted': return ;
+ case 'In Progress': return ;
+ case 'Resolved': return ;
+ case 'Rejected': return ;
+ default: return ;
+ }
+ };
+
+ const getStatusColor = (status) => {
+ switch (status) {
+ case 'Submitted': return 'default';
+ case 'In Progress': return 'info';
+ case 'Resolved': return 'success';
+ case 'Rejected': return 'error';
+ default: return 'default';
+ }
+ };
+
+ const handleChange = (panel) => (event, isExpanded) => {
+ setExpanded(isExpanded ? panel : false);
+ };
+
+ const handleReply = (ticketId) => {
+ console.log(`Reply to ${ticketId}:`, replyText);
+ setReplyText('');
+ };
+
+ return (
+
+ {/* HEADER */}
+
+
+ {/* STATS CARDS */}
+
+
+
+
+
+
+
+ {stats.total}
+
+
+ Total Tickets
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.inProgress}
+
+
+ In Progress
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.resolved}
+
+
+ Resolved
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.rejected}
+
+
+ Rejected
+
+
+
+
+
+
+
+
+
+
+
+ {/* TICKETS LIST */}
+
+
+ Your Tickets
+
+
+
+ {tickets.map((ticket) => (
+
+
+ }
+ sx={{
+ px: 3,
+ py: 2,
+ '&:hover': {
+ background: alpha(theme.palette.primary.main, 0.03),
+ },
+ }}
+ >
+
+
+ {getCategoryIcon(ticket.category)}
+
+
+
+
+ {ticket.subject}
+
+
+
+
+
+ {ticket.date}
+
+
+
+
+
+
+
+
+
+
+ {/* Original Issue */}
+
+
+ Issue Description
+
+
+ {ticket.description}
+
+
+
+ {/* Conversation History */}
+
+
+ Conversation History
+
+
+ {ticket.conversation.map((msg, idx) => (
+
+
+
+ {msg.from}
+
+
+ {msg.timestamp}
+
+
+
+ {msg.message}
+
+
+ ))}
+
+
+
+ {/* Reply Section (only for In Progress tickets) */}
+ {ticket.status === 'In Progress' && (
+
+
+ Add Reply
+
+
+ setReplyText(e.target.value)}
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ borderRadius: 2,
+ },
+ }}
+ />
+ }
+ onClick={() => handleReply(ticket.id)}
+ sx={{
+ minWidth: 120,
+ borderRadius: 2,
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #5568d3 0%, #654391 100%)',
+ },
+ }}
+ >
+ Send
+
+
+
+ )}
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default MyTickets;
diff --git a/src/pages/Student/Notifications.jsx b/src/pages/Student/Notifications.jsx
new file mode 100644
index 0000000..d7f670f
--- /dev/null
+++ b/src/pages/Student/Notifications.jsx
@@ -0,0 +1,361 @@
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Chip,
+ Stack,
+ IconButton,
+ Divider,
+ useTheme,
+ alpha,
+ Badge,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Notifications as NotificationsIcon,
+ Assignment as AssignmentIcon,
+ Payment as PaymentIcon,
+ School as SchoolIcon,
+ Info as InfoIcon,
+ CheckCircle,
+ MarkEmailRead,
+ Delete,
+ Circle,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const Notifications = () => {
+ const theme = useTheme();
+
+ const [notifications, setNotifications] = useState([
+ {
+ id: 1,
+ title: 'Assignment Due Tomorrow',
+ subtitle: 'Binary Search Tree Implementation - Data Structures',
+ time: '2 hours ago',
+ read: false,
+ icon: AssignmentIcon,
+ color: 'error',
+ category: 'Assignment',
+ },
+ {
+ id: 2,
+ title: 'Fee Payment Reminder',
+ subtitle: 'Spring 2026 Tuition Fee due on February 1st',
+ time: '5 hours ago',
+ read: false,
+ icon: PaymentIcon,
+ color: 'warning',
+ category: 'Finance',
+ },
+ {
+ id: 3,
+ title: 'New Course Material',
+ subtitle: 'Dr. Sarah Ahmed posted new lecture slides for Week 5',
+ time: '1 day ago',
+ read: true,
+ icon: SchoolIcon,
+ color: 'info',
+ category: 'Academic',
+ },
+ {
+ id: 4,
+ title: 'Mid-term Exam Schedule',
+ subtitle: 'Data Structures exam on January 28th at 9:00 AM',
+ time: '2 days ago',
+ read: true,
+ icon: InfoIcon,
+ color: 'primary',
+ category: 'Announcement',
+ },
+ {
+ id: 5,
+ title: 'Grade Posted',
+ subtitle: 'Your grade for Database Assignment 3 has been posted: A-',
+ time: '3 days ago',
+ read: true,
+ icon: CheckCircle,
+ color: 'success',
+ category: 'Grade',
+ },
+ {
+ id: 6,
+ title: 'Library Book Due',
+ subtitle: 'Introduction to Algorithms is due for return on January 27th',
+ time: '3 days ago',
+ read: false,
+ icon: InfoIcon,
+ color: 'warning',
+ category: 'Library',
+ },
+ ]);
+
+ const stats = {
+ total: notifications.length,
+ unread: notifications.filter(n => !n.read).length,
+ today: notifications.filter(n => n.time.includes('hour')).length,
+ };
+
+ const handleMarkAsRead = (id) => {
+ setNotifications(notifications.map(n =>
+ n.id === id ? { ...n, read: true } : n
+ ));
+ };
+
+ const handleMarkAllAsRead = () => {
+ setNotifications(notifications.map(n => ({ ...n, read: true })));
+ };
+
+ const handleDelete = (id) => {
+ setNotifications(notifications.filter(n => n.id !== id));
+ };
+
+ return (
+
+ {/* HEADER */}
+
+
+ {/* STATS CARDS */}
+
+
+
+
+
+
+
+ {stats.total}
+
+
+ Total Notifications
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.unread}
+
+
+ Unread
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.today}
+
+
+ Today
+
+
+
+
+
+
+
+
+
+
+
+ {/* ACTIONS */}
+
+ }
+ clickable
+ sx={{
+ fontWeight: 600,
+ px: 2,
+ }}
+ />
+
+
+ {/* NOTIFICATIONS LIST */}
+
+
+ {notifications.map((notification) => {
+ const Icon = notification.icon;
+ return (
+
+
+
+
+ {/* Icon */}
+
+
+
+
+ {/* Content */}
+
+
+ {!notification.read && (
+
+ )}
+
+ {notification.title}
+
+
+
+
+ {notification.subtitle}
+
+
+ {notification.time}
+
+
+
+ {/* Actions */}
+
+ {!notification.read && (
+ handleMarkAsRead(notification.id)}
+ sx={{ color: 'success.main' }}
+ >
+
+
+ )}
+ handleDelete(notification.id)}
+ sx={{ color: 'error.main' }}
+ >
+
+
+
+
+
+
+
+ );
+ })}
+
+
+
+ );
+};
+
+export default Notifications;
diff --git a/src/pages/Teacher/Assignments.jsx b/src/pages/Teacher/Assignments.jsx
new file mode 100644
index 0000000..803f4f7
--- /dev/null
+++ b/src/pages/Teacher/Assignments.jsx
@@ -0,0 +1,522 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Chip,
+ Stack,
+ IconButton,
+ Menu,
+ MenuItem,
+ Tabs,
+ Tab,
+ LinearProgress,
+ useTheme,
+ alpha,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Assignment as AssignmentIcon,
+ Add,
+ Edit,
+ Delete,
+ Visibility,
+ MoreVert,
+ Schedule,
+ People,
+ CheckCircle,
+ PendingActions,
+ Grade,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const Assignments = () => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const [activeTab, setActiveTab] = useState(0);
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [selectedAssignment, setSelectedAssignment] = useState(null);
+
+ const stats = [
+ {
+ title: 'Total Assignments',
+ value: '24',
+ icon: AssignmentIcon,
+ color: 'primary.main',
+ },
+ {
+ title: 'Active',
+ value: '8',
+ icon: Schedule,
+ color: 'success.main',
+ },
+ {
+ title: 'Pending Review',
+ value: '45',
+ icon: PendingActions,
+ color: 'warning.main',
+ },
+ {
+ title: 'Graded',
+ value: '186',
+ icon: Grade,
+ color: 'info.main',
+ },
+ ];
+
+ const assignments = [
+ {
+ id: 1,
+ title: 'Binary Search Tree Implementation',
+ course: 'CS-301',
+ courseName: 'Data Structures & Algorithms',
+ type: 'Lab Assignment',
+ dueDate: '2026-01-28',
+ totalMarks: 100,
+ submissions: 67,
+ totalStudents: 85,
+ pending: 18,
+ graded: 50,
+ avgGrade: 82,
+ status: 'active',
+ createdAt: '2026-01-20',
+ },
+ {
+ id: 2,
+ title: 'Sorting Algorithms Analysis',
+ course: 'CS-301',
+ courseName: 'Data Structures & Algorithms',
+ type: 'Theory Assignment',
+ dueDate: '2026-02-05',
+ totalMarks: 50,
+ submissions: 45,
+ totalStudents: 85,
+ pending: 40,
+ graded: 5,
+ avgGrade: 75,
+ status: 'active',
+ createdAt: '2026-01-22',
+ },
+ {
+ id: 3,
+ title: 'OOP Design Patterns',
+ course: 'CS-201',
+ courseName: 'Object Oriented Programming',
+ type: 'Project',
+ dueDate: '2026-02-10',
+ totalMarks: 150,
+ submissions: 32,
+ totalStudents: 92,
+ pending: 32,
+ graded: 0,
+ avgGrade: 0,
+ status: 'active',
+ createdAt: '2026-01-18',
+ },
+ {
+ id: 4,
+ title: 'Graph Traversal Lab',
+ course: 'CS-301',
+ courseName: 'Data Structures & Algorithms',
+ type: 'Lab Assignment',
+ dueDate: '2026-01-15',
+ totalMarks: 100,
+ submissions: 85,
+ totalStudents: 85,
+ pending: 0,
+ graded: 85,
+ avgGrade: 88,
+ status: 'completed',
+ createdAt: '2026-01-08',
+ },
+ {
+ id: 5,
+ title: 'Inheritance & Polymorphism',
+ course: 'CS-201',
+ courseName: 'Object Oriented Programming',
+ type: 'Lab Assignment',
+ dueDate: '2026-01-12',
+ totalMarks: 75,
+ submissions: 89,
+ totalStudents: 92,
+ pending: 0,
+ graded: 89,
+ avgGrade: 79,
+ status: 'completed',
+ createdAt: '2026-01-05',
+ },
+ {
+ id: 6,
+ title: 'Database Normalization',
+ course: 'CS-401',
+ courseName: 'Database Systems',
+ type: 'Theory Assignment',
+ dueDate: '2026-02-15',
+ totalMarks: 50,
+ submissions: 0,
+ totalStudents: 65,
+ pending: 0,
+ graded: 0,
+ avgGrade: 0,
+ status: 'draft',
+ createdAt: '2026-01-23',
+ },
+ ];
+
+ const activeAssignments = assignments.filter(a => a.status === 'active');
+ const completedAssignments = assignments.filter(a => a.status === 'completed');
+ const draftAssignments = assignments.filter(a => a.status === 'draft');
+
+ const handleMenuOpen = (event, assignment) => {
+ setAnchorEl(event.currentTarget);
+ setSelectedAssignment(assignment);
+ };
+
+ const handleMenuClose = () => {
+ setAnchorEl(null);
+ setSelectedAssignment(null);
+ };
+
+ const getStatusColor = (status) => {
+ switch (status) {
+ case 'active':
+ return 'success';
+ case 'completed':
+ return 'default';
+ case 'draft':
+ return 'warning';
+ default:
+ return 'default';
+ }
+ };
+
+ const AssignmentCard = ({ assignment }) => {
+ const submissionRate = (assignment.submissions / assignment.totalStudents) * 100;
+ const gradedRate = assignment.submissions > 0 ? (assignment.graded / assignment.submissions) * 100 : 0;
+
+ return (
+
+
+
+
+
+
+
+ handleMenuOpen(e, assignment)}>
+
+
+
+
+
+ {assignment.title}
+
+
+
+ {assignment.course} - {assignment.courseName}
+
+
+ {assignment.status !== 'draft' && (
+
+ 📅 Due: {new Date(assignment.dueDate).toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric'
+ })}
+
+ )}
+
+
+
+
+
+ Submissions
+
+
+ {assignment.submissions}/{assignment.totalStudents}
+
+
+
+
+ Pending Review
+
+
+ {assignment.pending}
+
+
+
+
+ Graded
+
+
+ {assignment.graded}
+
+
+
+
+ Total Marks
+
+
+ {assignment.totalMarks}
+
+
+
+
+
+ {assignment.status !== 'draft' && assignment.submissions > 0 && (
+ <>
+
+
+
+ Submission Rate
+
+
+ {submissionRate.toFixed(0)}%
+
+
+
+
+
+
+
+
+ Grading Progress
+
+
+ {gradedRate.toFixed(0)}%
+
+
+
+
+
+ {assignment.graded > 0 && (
+
+ 📊 Average Grade: {assignment.avgGrade}%
+
+ )}
+ >
+ )}
+
+
+ }
+ onClick={() => navigate(`/teacher/assignment/${assignment.id}/submissions`)}
+ >
+ View Submissions
+
+ }
+ onClick={() => navigate(`/teacher/assignment/${assignment.id}/edit`)}
+ >
+ Edit
+
+
+
+
+ );
+ };
+
+ const currentAssignments = activeTab === 0 ? activeAssignments : activeTab === 1 ? completedAssignments : draftAssignments;
+
+ return (
+
+ {/* HEADER */}
+ }
+ variant="contained"
+ onClick={() => navigate('/teacher/create-assignment')}
+ sx={{ px: 3 }}
+ >
+ Create Assignment
+
+ }
+ />
+
+ {/* STATS CARDS */}
+
+ {stats.map((stat, index) => (
+
+
+
+
+
+
+ {stat.value}
+
+
+ {stat.title}
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* TABS */}
+
+ setActiveTab(newValue)}
+ variant="fullWidth"
+ sx={{ borderBottom: 1, borderColor: 'divider' }}
+ >
+
+ Active
+
+
+ }
+ />
+
+ Completed
+
+
+ }
+ />
+
+ Drafts
+
+
+ }
+ />
+
+
+
+ {/* ASSIGNMENT CARDS */}
+
+ {currentAssignments.map((assignment) => (
+
+
+
+ ))}
+
+
+ {currentAssignments.length === 0 && (
+
+
+
+ No {activeTab === 0 ? 'active' : activeTab === 1 ? 'completed' : 'draft'} assignments
+
+
+ )}
+
+ {/* MENU */}
+
+
+ );
+};
+
+export default Assignments;
diff --git a/src/pages/Teacher/CourseManagement.jsx b/src/pages/Teacher/CourseManagement.jsx
new file mode 100644
index 0000000..74ed653
--- /dev/null
+++ b/src/pages/Teacher/CourseManagement.jsx
@@ -0,0 +1,906 @@
+import { useState } from 'react';
+import { useParams, useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Tabs,
+ Tab,
+ Chip,
+ Avatar,
+ Stack,
+ Paper,
+ IconButton,
+ Divider,
+ TextField,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ List,
+ ListItem,
+ ListItemAvatar,
+ ListItemText,
+ ListItemButton,
+ Menu,
+ MenuItem,
+ LinearProgress,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ alpha,
+ useTheme,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ ArrowBack,
+ School,
+ People,
+ Assignment as AssignmentIcon,
+ Quiz as QuizIcon,
+ Campaign,
+ Settings,
+ Add,
+ Edit,
+ Delete,
+ MoreVert,
+ FileUpload,
+ Download,
+ CheckCircle,
+ Schedule,
+ Visibility,
+ AttachFile,
+ Send,
+ Person,
+ Email,
+ Phone,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const CourseManagement = () => {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const theme = useTheme();
+ const [activeTab, setActiveTab] = useState(0);
+ const [openDialog, setOpenDialog] = useState(false);
+ const [dialogType, setDialogType] = useState(''); // 'assignment', 'material', 'announcement', 'student'
+ const [anchorEl, setAnchorEl] = useState(null);
+
+ // Mock course data
+ const course = {
+ id: id,
+ code: 'CS-301',
+ name: 'Data Structures & Algorithms',
+ semester: 'Fall 2025',
+ students: 85,
+ schedule: 'Mon, Wed 9:00 AM',
+ room: 'Lab 3',
+ creditHours: 3,
+ description: 'This course covers fundamental data structures and algorithms including arrays, linked lists, stacks, queues, trees, graphs, searching and sorting algorithms.',
+ };
+
+ const students = [
+ {
+ id: 1,
+ name: 'Muhammad Asad',
+ rollNo: 'BSCS-2023-001',
+ email: 'asad@student.edu.pk',
+ phone: '+92-300-1234567',
+ avatar: 'https://i.pravatar.cc/150?img=12',
+ attendance: 88,
+ assignments: 10,
+ quizzes: 4,
+ avgGrade: 'B+',
+ },
+ {
+ id: 2,
+ name: 'Ayesha Khan',
+ rollNo: 'BSCS-2023-002',
+ email: 'ayesha@student.edu.pk',
+ phone: '+92-300-2234567',
+ avatar: 'https://i.pravatar.cc/150?img=5',
+ attendance: 95,
+ assignments: 12,
+ quizzes: 5,
+ avgGrade: 'A',
+ },
+ {
+ id: 3,
+ name: 'Ali Ahmed',
+ rollNo: 'BSCS-2023-003',
+ email: 'ali@student.edu.pk',
+ phone: '+92-300-3234567',
+ avatar: 'https://i.pravatar.cc/150?img=8',
+ attendance: 76,
+ assignments: 8,
+ quizzes: 3,
+ avgGrade: 'C+',
+ },
+ {
+ id: 4,
+ name: 'Fatima Zahra',
+ rollNo: 'BSCS-2023-004',
+ email: 'fatima@student.edu.pk',
+ phone: '+92-300-4234567',
+ avatar: 'https://i.pravatar.cc/150?img=10',
+ attendance: 92,
+ assignments: 11,
+ quizzes: 5,
+ avgGrade: 'A-',
+ },
+ ];
+
+ const assignments = [
+ {
+ id: 1,
+ title: 'Binary Search Tree Implementation',
+ type: 'Lab Assignment',
+ dueDate: '2026-01-28',
+ totalMarks: 100,
+ submissions: 67,
+ pending: 18,
+ graded: 50,
+ status: 'active',
+ },
+ {
+ id: 2,
+ title: 'Sorting Algorithms Analysis',
+ type: 'Theory Assignment',
+ dueDate: '2026-02-05',
+ totalMarks: 50,
+ submissions: 45,
+ pending: 40,
+ graded: 5,
+ status: 'active',
+ },
+ {
+ id: 3,
+ title: 'Graph Traversal Lab',
+ type: 'Lab Assignment',
+ dueDate: '2026-01-15',
+ totalMarks: 100,
+ submissions: 85,
+ pending: 0,
+ graded: 85,
+ status: 'closed',
+ },
+ ];
+
+ const materials = [
+ {
+ id: 1,
+ title: 'Week 5: Binary Search Trees',
+ type: 'Lecture Slides',
+ fileType: 'PDF',
+ size: '2.5 MB',
+ uploadedAt: '2026-01-20',
+ downloads: 78,
+ },
+ {
+ id: 2,
+ title: 'BST Implementation Code',
+ type: 'Code Sample',
+ fileType: 'ZIP',
+ size: '156 KB',
+ uploadedAt: '2026-01-20',
+ downloads: 65,
+ },
+ {
+ id: 3,
+ title: 'Data Structures Tutorial',
+ type: 'Video Lecture',
+ fileType: 'MP4',
+ size: '125 MB',
+ uploadedAt: '2026-01-18',
+ downloads: 82,
+ },
+ ];
+
+ const announcements = [
+ {
+ id: 1,
+ title: 'Mid-term Exam Schedule',
+ content: 'Mid-term exams will be held on January 28th at 9:00 AM in Lab 3. Please arrive 15 minutes early.',
+ postedAt: '2026-01-22 10:30 AM',
+ priority: 'high',
+ },
+ {
+ id: 2,
+ title: 'Office Hours Update',
+ content: 'Office hours this week will be on Wednesday 2-4 PM instead of Tuesday.',
+ postedAt: '2026-01-20 02:15 PM',
+ priority: 'medium',
+ },
+ ];
+
+ const handleOpenDialog = (type) => {
+ setDialogType(type);
+ setOpenDialog(true);
+ };
+
+ const handleCloseDialog = () => {
+ setOpenDialog(false);
+ setDialogType('');
+ };
+
+ const getFileIcon = (type) => {
+ switch (type) {
+ case 'PDF':
+ return '📄';
+ case 'ZIP':
+ return '📦';
+ case 'MP4':
+ return '🎥';
+ default:
+ return '📎';
+ }
+ };
+
+ const getAttendanceColor = (percentage) => {
+ if (percentage >= 90) return 'success';
+ if (percentage >= 75) return 'warning';
+ return 'error';
+ };
+
+ return (
+
+ {/* HEADER */}
+
+ }
+ onClick={() => navigate('/teacher/courses')}
+ sx={{ mb: 2 }}
+ >
+ Back to Courses
+
+
+
+
+ {/* STATS CARDS */}
+
+
+
+
+
+
+
+ {course.students}
+
+
+ Enrolled Students
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {assignments.filter(a => a.status === 'active').length}
+
+
+ Active Assignments
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {materials.length}
+
+
+ Course Materials
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 88%
+
+
+ Avg Attendance
+
+
+
+
+
+
+
+
+
+
+
+ {/* TABS */}
+
+ setActiveTab(newValue)}
+ variant="scrollable"
+ scrollButtons="auto"
+ sx={{ borderBottom: 1, borderColor: 'divider' }}
+ >
+
+
+
+
+
+
+
+
+ {/* OVERVIEW TAB */}
+ {activeTab === 0 && (
+
+
+ Course Description
+
+
+ {course.description}
+
+
+
+
+
+ Quick Actions
+
+
+
+ }
+ onClick={() => navigate('/teacher/create-assignment')}
+ sx={{ py: 1.5 }}
+ >
+ Create Assignment
+
+
+
+ }
+ onClick={() => handleOpenDialog('material')}
+ sx={{ py: 1.5 }}
+ >
+ Upload Material
+
+
+
+ }
+ onClick={() => handleOpenDialog('announcement')}
+ sx={{ py: 1.5 }}
+ >
+ Post Announcement
+
+
+
+ }
+ onClick={() => navigate('/attendance/smart-attendance')}
+ sx={{ py: 1.5 }}
+ >
+ Mark Attendance
+
+
+
+
+ )}
+
+ {/* STUDENTS TAB */}
+ {activeTab === 1 && (
+
+
+
+ Enrolled Students ({students.length})
+
+ }
+ variant="contained"
+ size="small"
+ onClick={() => handleOpenDialog('student')}
+ >
+ Add Student
+
+
+
+
+
+
+
+ Student
+ Roll No
+ Contact
+ Attendance
+ Assignments
+ Avg Grade
+ Actions
+
+
+
+ {students.map((student) => (
+
+
+
+
+
+ {student.name}
+
+
+
+
+
+ {student.rollNo}
+
+
+
+
+
+
+
+ {student.email}
+
+
+
+
+
+ {student.phone}
+
+
+
+
+
+
+
+ {student.attendance}%
+
+
+
+
+
+
+ {student.assignments}/{assignments.length}
+
+
+
+
+
+
+ setAnchorEl(e.currentTarget)}
+ >
+
+
+
+
+ ))}
+
+
+
+
+ )}
+
+ {/* ASSIGNMENTS TAB */}
+ {activeTab === 2 && (
+
+
+
+ Course Assignments ({assignments.length})
+
+ }
+ variant="contained"
+ size="small"
+ onClick={() => navigate('/teacher/create-assignment')}
+ >
+ Create Assignment
+
+
+
+
+ {assignments.map((assignment) => (
+
+
+
+
+
+ {assignment.title}
+
+
+
+
+
+
+
+
+ Due Date
+
+
+ {new Date(assignment.dueDate).toLocaleDateString()}
+
+
+
+
+ Total Marks
+
+
+ {assignment.totalMarks}
+
+
+
+
+
+
+ Submissions
+
+
+ {assignment.submissions}
+
+
+
+
+ Pending
+
+
+ {assignment.pending}
+
+
+
+
+ Graded
+
+
+ {assignment.graded}
+
+
+
+
+
+
+ navigate(`/teacher/submissions/${assignment.id}`)}
+ >
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )}
+
+ {/* MATERIALS TAB */}
+ {activeTab === 3 && (
+
+
+
+ Course Materials ({materials.length})
+
+ }
+ variant="contained"
+ size="small"
+ onClick={() => handleOpenDialog('material')}
+ >
+ Upload Material
+
+
+
+
+ {materials.map((material) => (
+
+
+
+
+
+ {getFileIcon(material.fileType)}
+
+
+ {material.title}
+
+
+
+
+ {material.size}
+
+
+ • {material.downloads} downloads
+
+
+
+
+ }
+ >
+ Download
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )}
+
+ {/* ANNOUNCEMENTS TAB */}
+ {activeTab === 4 && (
+
+
+
+ Course Announcements ({announcements.length})
+
+ }
+ variant="contained"
+ size="small"
+ onClick={() => handleOpenDialog('announcement')}
+ >
+ Post Announcement
+
+
+
+
+ {announcements.map((announcement) => (
+
+
+
+
+
+ {announcement.title}
+
+
+
+
+
+
+
+
+ {announcement.content}
+
+
+ Posted on {announcement.postedAt}
+
+
+
+ ))}
+
+
+ )}
+
+
+
+ {/* DIALOGS */}
+
+
+ {/* MENU */}
+
+
+ );
+};
+
+export default CourseManagement;
diff --git a/src/pages/Teacher/CreateAssignment.jsx b/src/pages/Teacher/CreateAssignment.jsx
new file mode 100644
index 0000000..d2b9643
--- /dev/null
+++ b/src/pages/Teacher/CreateAssignment.jsx
@@ -0,0 +1,289 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ TextField,
+ MenuItem,
+ Stack,
+ Divider,
+ useTheme,
+ FormControl,
+ FormLabel,
+ InputAdornment,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Assignment,
+ Save,
+ CalendarToday,
+ Description,
+ Grade,
+ AttachFile,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import FileDropzone from '../../components/Forms/FileDropzone';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const CreateAssignment = () => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+
+ const [formData, setFormData] = useState({
+ title: '',
+ course: '',
+ description: '',
+ dueDate: '',
+ totalMarks: 100,
+ instructions: '',
+ attachments: [],
+ });
+
+ const courses = [
+ { id: 1, code: 'CS-401', name: 'Database Systems' },
+ { id: 2, code: 'CS-302', name: 'Data Structures & Algorithms' },
+ { id: 3, code: 'CS-501', name: 'Web Development' },
+ { id: 4, code: 'CS-403', name: 'Computer Networks' },
+ ];
+
+ const handleChange = (field) => (event) => {
+ setFormData({ ...formData, [field]: event.target.value });
+ };
+
+ const handleFilesChange = (files) => {
+ setFormData({ ...formData, attachments: files });
+ };
+
+ const handleSubmit = () => {
+ console.log('Creating assignment:', formData);
+ // Here you would call API to create assignment
+ navigate('/teacher/courses');
+ };
+
+ return (
+
+ {/* HEADER */}
+
+
+
+
+
+
+ {/* Assignment Title */}
+
+ Assignment Title *
+
+
+
+ ),
+ }}
+ sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
+ />
+
+
+ {/* Course Selection */}
+
+
+
+ Course *
+
+ {courses.map((course) => (
+
+ {course.code} - {course.name}
+
+ ))}
+
+
+
+
+
+
+ Total Marks *
+
+
+
+ ),
+ }}
+ sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
+ />
+
+
+
+
+
+ Due Date *
+
+
+
+ ),
+ }}
+ sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
+ />
+
+
+
+
+
+
+ {/* Description */}
+
+ Short Description *
+
+
+
+ ),
+ }}
+ sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
+ />
+
+
+ {/* Detailed Instructions */}
+
+ Detailed Instructions
+
+
+
+
+
+ {/* Attachments */}
+
+
+
+
+ Attachments (Optional)
+
+
+
+
+ Upload reference materials, datasets, or any supporting documents
+
+
+
+
+
+ {/* Action Buttons */}
+
+ navigate('/teacher/courses')}
+ sx={{
+ px: 4,
+ py: 1.2,
+ borderRadius: 2,
+ fontWeight: 'bold',
+ }}
+ >
+ Cancel
+
+ }
+ onClick={handleSubmit}
+ disabled={!formData.title || !formData.course || !formData.dueDate || !formData.description}
+ sx={{
+ px: 4,
+ py: 1.2,
+ borderRadius: 2,
+ fontWeight: 'bold',
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ '&:hover': {
+ background: 'linear-gradient(135deg, #5568d3 0%, #654391 100%)',
+ },
+ }}
+ >
+ Publish Assignment
+
+
+
+
+
+
+
+ );
+};
+
+export default CreateAssignment;
diff --git a/src/pages/Teacher/Dashboard.jsx b/src/pages/Teacher/Dashboard.jsx
index 56b5164..6d68db4 100644
--- a/src/pages/Teacher/Dashboard.jsx
+++ b/src/pages/Teacher/Dashboard.jsx
@@ -43,7 +43,7 @@ const TeacherDashboard = () => {
change: '+1 new',
trend: 'up',
icon: School,
- color: theme.palette.primary.main,
+ color: 'primary',
},
{
title: 'Total Students',
@@ -51,7 +51,7 @@ const TeacherDashboard = () => {
change: '+18 this sem',
trend: 'up',
icon: People,
- color: theme.palette.success.main,
+ color: 'success',
},
{
title: 'Pending Assignments',
@@ -59,7 +59,7 @@ const TeacherDashboard = () => {
change: '5 overdue',
trend: 'down',
icon: Assignment,
- color: theme.palette.warning.main,
+ color: 'warning',
},
{
title: 'Attendance Rate',
@@ -67,7 +67,7 @@ const TeacherDashboard = () => {
change: '+2.3%',
trend: 'up',
icon: CheckCircle,
- color: theme.palette.info.main,
+ color: 'info',
},
];
@@ -151,7 +151,7 @@ const TeacherDashboard = () => {
{/* Stats Cards */}
{stats.map((stat, index) => (
-
+
{
Quick Actions
- }>
+ }
+ onClick={() => navigate('/teacher/create-assignment')}
+ >
Create Assignment
- }>
+ }
+ onClick={() => navigate('/attendance/smart-attendance')}
+ >
Mark Attendance
- }>
+ }
+ onClick={() => navigate('/teacher/students')}
+ >
View Students
- }>
- Send Announcement
+ }
+ onClick={() => navigate('/teacher/courses')}
+ >
+ Manage Courses
diff --git a/src/pages/Teacher/GrievanceManagement.jsx b/src/pages/Teacher/GrievanceManagement.jsx
new file mode 100644
index 0000000..17a550f
--- /dev/null
+++ b/src/pages/Teacher/GrievanceManagement.jsx
@@ -0,0 +1,540 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Grid,
+ Tab,
+ Tabs,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Chip,
+ Stack,
+ TextField,
+ InputAdornment,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Button,
+ Avatar,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
+ Paper,
+} from '@mui/material';
+import {
+ Search as SearchIcon,
+ Visibility as ViewIcon,
+ SupportAgent as SupportIcon,
+ School as AcademicIcon,
+ PendingActions as PendingIcon,
+ CheckCircle as CheckCircleIcon,
+ Forward as ForwardIcon,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { motion } from 'framer-motion';
+import { pageTransition } from '../../utils/animations';
+
+const TeacherGrievanceManagement = () => {
+ const [tabValue, setTabValue] = useState(0);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [filterCategory, setFilterCategory] = useState('all');
+ const [viewDialog, setViewDialog] = useState({ open: false, grievance: null });
+ const [teacherNote, setTeacherNote] = useState('');
+
+ // Mock grievances referred to teacher by admin
+ const [grievances, setGrievances] = useState([
+ {
+ id: 1,
+ ticketId: 'GRV-001',
+ studentName: 'Ali Hassan',
+ studentRollNo: 'F21-BS-001',
+ studentPhoto: 'https://i.pravatar.cc/150?img=31',
+ category: 'Academic',
+ subcategory: 'Grading Issue',
+ subject: 'Dispute about midterm marks',
+ description: 'I believe there was an error in grading my Data Structures midterm. My answer to question 3 was marked incorrect but I have reviewed the solution and believe it is correct.',
+ priority: 'Medium',
+ status: 'Pending Review',
+ submittedDate: '2026-01-20',
+ referredBy: 'Admin',
+ courseCode: 'CS-201',
+ courseName: 'Data Structures',
+ },
+ {
+ id: 2,
+ ticketId: 'GRV-002',
+ studentName: 'Sara Ahmed',
+ studentRollNo: 'F21-BS-002',
+ studentPhoto: 'https://i.pravatar.cc/150?img=32',
+ category: 'Academic',
+ subcategory: 'Assignment Extension',
+ subject: 'Medical emergency - assignment extension request',
+ description: 'I was hospitalized for 3 days and could not complete my assignment. I have attached medical certificate. Requesting extension.',
+ priority: 'High',
+ status: 'Under Review',
+ submittedDate: '2026-01-22',
+ referredBy: 'Admin',
+ courseCode: 'CS-301',
+ courseName: 'Software Engineering',
+ teacherNote: 'Reviewing medical certificate. Will grant 3-day extension if verified.',
+ },
+ {
+ id: 3,
+ ticketId: 'GRV-003',
+ studentName: 'Omar Khan',
+ studentRollNo: 'F21-BS-003',
+ studentPhoto: 'https://i.pravatar.cc/150?img=33',
+ category: 'Academic',
+ subcategory: 'Attendance',
+ subject: 'Attendance marked absent despite being present',
+ description: 'My attendance was marked absent on Jan 15th for Database class but I was present. I can provide testimony from classmates.',
+ priority: 'Medium',
+ status: 'Resolved',
+ submittedDate: '2026-01-18',
+ resolvedDate: '2026-01-19',
+ referredBy: 'Admin',
+ courseCode: 'CS-202',
+ courseName: 'Database Systems',
+ resolution: 'Attendance records verified. Discrepancy found in biometric system. Attendance has been marked present.',
+ },
+ {
+ id: 4,
+ ticketId: 'GRV-004',
+ studentName: 'Fatima Zahra',
+ studentRollNo: 'F21-BS-004',
+ studentPhoto: 'https://i.pravatar.cc/150?img=34',
+ category: 'Academic',
+ subcategory: 'Lab Issues',
+ subject: 'Unable to access lab for project work',
+ description: 'The lab is locked during evening hours but I need access to complete my project. Lab timing posted shows it should be open till 8 PM.',
+ priority: 'High',
+ status: 'Pending Review',
+ submittedDate: '2026-01-23',
+ referredBy: 'Admin',
+ courseCode: 'CS-401',
+ courseName: 'Final Year Project',
+ },
+ {
+ id: 5,
+ ticketId: 'GRV-005',
+ studentName: 'Ahmed Raza',
+ studentRollNo: 'F21-BS-005',
+ studentPhoto: 'https://i.pravatar.cc/150?img=35',
+ category: 'Academic',
+ subcategory: 'Course Content',
+ subject: 'Requesting additional study materials',
+ description: 'The recommended textbook is difficult to understand. Could you suggest additional resources or provide supplementary notes?',
+ priority: 'Low',
+ status: 'Under Review',
+ submittedDate: '2026-01-21',
+ referredBy: 'Admin',
+ courseCode: 'CS-303',
+ courseName: 'Theory of Computation',
+ teacherNote: 'Will upload additional lecture notes and video links by end of week.',
+ },
+ ]);
+
+ const stats = [
+ {
+ label: 'Total Referred',
+ value: grievances.length.toString(),
+ change: 'By admin',
+ trend: 'up',
+ color: 'primary',
+ icon: SupportIcon,
+ },
+ {
+ label: 'Pending Review',
+ value: grievances.filter(g => g.status === 'Pending Review').length.toString(),
+ change: 'Need attention',
+ trend: 'up',
+ color: 'warning',
+ icon: PendingIcon,
+ },
+ {
+ label: 'Under Review',
+ value: grievances.filter(g => g.status === 'Under Review').length.toString(),
+ change: 'In progress',
+ trend: 'up',
+ color: 'info',
+ icon: AcademicIcon,
+ },
+ {
+ label: 'Resolved',
+ value: grievances.filter(g => g.status === 'Resolved').length.toString(),
+ change: 'This month',
+ trend: 'up',
+ color: 'success',
+ icon: CheckCircleIcon,
+ },
+ ];
+
+ const handleViewGrievance = (grievance) => {
+ setViewDialog({ open: true, grievance });
+ setTeacherNote(grievance.teacherNote || '');
+ };
+
+ const handleAddNote = () => {
+ const updatedGrievances = grievances.map(g =>
+ g.id === viewDialog.grievance.id
+ ? { ...g, status: 'Under Review', teacherNote }
+ : g
+ );
+ setGrievances(updatedGrievances);
+ setViewDialog({ open: false, grievance: null });
+ setTeacherNote('');
+ };
+
+ const handleResolve = () => {
+ if (!teacherNote.trim()) {
+ alert('Please provide resolution details');
+ return;
+ }
+ const updatedGrievances = grievances.map(g =>
+ g.id === viewDialog.grievance.id
+ ? { ...g, status: 'Resolved', resolution: teacherNote, resolvedDate: new Date().toISOString().split('T')[0] }
+ : g
+ );
+ setGrievances(updatedGrievances);
+ setViewDialog({ open: false, grievance: null });
+ setTeacherNote('');
+ };
+
+ const filteredGrievances = grievances.filter(g => {
+ const matchesSearch =
+ g.subject.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ g.studentName.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ g.ticketId.toLowerCase().includes(searchQuery.toLowerCase());
+ const matchesCategory = filterCategory === 'all' || g.subcategory === filterCategory;
+ const matchesTab =
+ tabValue === 0 ||
+ (tabValue === 1 && g.status === 'Pending Review') ||
+ (tabValue === 2 && g.status === 'Under Review') ||
+ (tabValue === 3 && g.status === 'Resolved');
+ return matchesSearch && matchesCategory && matchesTab;
+ });
+
+ const getStatusChip = (status) => {
+ const config = {
+ 'Pending Review': { color: 'warning', icon: },
+ 'Under Review': { color: 'info', icon: },
+ Resolved: { color: 'success', icon: },
+ };
+ return (
+
+ );
+ };
+
+ const getPriorityChip = (priority) => {
+ const colors = { High: 'error', Medium: 'warning', Low: 'info' };
+ return ;
+ };
+
+ return (
+
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Tabs and Filters */}
+
+ setTabValue(newValue)}
+ sx={{ borderBottom: 1, borderColor: 'divider', px: 2 }}
+ >
+
+ g.status === 'Pending Review').length})`} />
+ g.status === 'Under Review').length})`} />
+ g.status === 'Resolved').length})`} />
+
+
+
+
+ setSearchQuery(e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+ Issue Type
+
+
+
+
+
+
+
+ {/* Grievances Table */}
+
+
+
+
+
+ Ticket ID
+ Student
+ Course
+ Issue
+ Subject
+ Priority
+ Status
+ Date
+ Actions
+
+
+
+ {filteredGrievances.map((grievance) => (
+
+
+
+ {grievance.ticketId}
+
+
+
+
+
+
+
+ {grievance.studentName}
+
+
+ {grievance.studentRollNo}
+
+
+
+
+
+
+ {grievance.courseCode}
+
+
+ {grievance.courseName}
+
+
+
+ {grievance.subcategory}
+
+
+
+ {grievance.subject}
+
+
+ {getPriorityChip(grievance.priority)}
+ {getStatusChip(grievance.status)}
+
+ {grievance.submittedDate}
+
+
+ handleViewGrievance(grievance)}
+ >
+
+
+
+
+ ))}
+
+
+
+
+
+ {/* View/Response Dialog */}
+
+
+
+ );
+};
+
+export default TeacherGrievanceManagement;
diff --git a/src/pages/Teacher/MyCourses.jsx b/src/pages/Teacher/MyCourses.jsx
index 147ff8d..65037cb 100644
--- a/src/pages/Teacher/MyCourses.jsx
+++ b/src/pages/Teacher/MyCourses.jsx
@@ -274,7 +274,13 @@ const TeacherCourses = () => {
>
View Details
- }>
+ }
+ onClick={() => navigate(`/teacher/course/${course.id}/manage`)}
+ >
Manage
diff --git a/src/pages/Teacher/Quizzes.jsx b/src/pages/Teacher/Quizzes.jsx
new file mode 100644
index 0000000..e89eed2
--- /dev/null
+++ b/src/pages/Teacher/Quizzes.jsx
@@ -0,0 +1,458 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Chip,
+ Stack,
+ IconButton,
+ Menu,
+ MenuItem,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ TextField,
+ useTheme,
+ alpha,
+ Tabs,
+ Tab,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Quiz as QuizIcon,
+ Add,
+ Edit,
+ Delete,
+ Visibility,
+ MoreVert,
+ Timer,
+ QuestionAnswer,
+ CheckCircle,
+ Schedule,
+ People,
+ TrendingUp,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const Quizzes = () => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const [activeTab, setActiveTab] = useState(0);
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [selectedQuiz, setSelectedQuiz] = useState(null);
+ const [openDialog, setOpenDialog] = useState(false);
+
+ const stats = [
+ {
+ title: 'Total Quizzes',
+ value: '18',
+ icon: QuizIcon,
+ color: 'primary.main',
+ },
+ {
+ title: 'Active Quizzes',
+ value: '5',
+ icon: Schedule,
+ color: 'success.main',
+ },
+ {
+ title: 'Total Attempts',
+ value: '432',
+ icon: People,
+ color: 'info.main',
+ },
+ {
+ title: 'Avg Score',
+ value: '78%',
+ icon: TrendingUp,
+ color: 'warning.main',
+ },
+ ];
+
+ const quizzes = [
+ {
+ id: 1,
+ title: 'Data Structures Fundamentals',
+ course: 'CS-301',
+ courseName: 'Data Structures & Algorithms',
+ type: 'MCQ',
+ questions: 20,
+ duration: 30,
+ totalMarks: 20,
+ attempts: 78,
+ avgScore: 82,
+ status: 'active',
+ startDate: '2026-01-25 09:00',
+ endDate: '2026-01-25 18:00',
+ createdAt: '2026-01-20',
+ },
+ {
+ id: 2,
+ title: 'Binary Search Trees',
+ course: 'CS-301',
+ courseName: 'Data Structures & Algorithms',
+ type: 'Mixed',
+ questions: 15,
+ duration: 25,
+ totalMarks: 25,
+ attempts: 65,
+ avgScore: 76,
+ status: 'active',
+ startDate: '2026-01-26 10:00',
+ endDate: '2026-01-26 20:00',
+ createdAt: '2026-01-22',
+ },
+ {
+ id: 3,
+ title: 'OOP Concepts Quiz',
+ course: 'CS-201',
+ courseName: 'Object Oriented Programming',
+ type: 'MCQ',
+ questions: 25,
+ duration: 40,
+ totalMarks: 25,
+ attempts: 85,
+ avgScore: 79,
+ status: 'completed',
+ startDate: '2026-01-15 11:00',
+ endDate: '2026-01-15 23:59',
+ createdAt: '2026-01-10',
+ },
+ {
+ id: 4,
+ title: 'Sorting Algorithms',
+ course: 'CS-301',
+ courseName: 'Data Structures & Algorithms',
+ type: 'MCQ',
+ questions: 18,
+ duration: 30,
+ totalMarks: 20,
+ attempts: 82,
+ avgScore: 84,
+ status: 'completed',
+ startDate: '2026-01-12 09:00',
+ endDate: '2026-01-12 18:00',
+ createdAt: '2026-01-08',
+ },
+ {
+ id: 5,
+ title: 'Inheritance & Polymorphism',
+ course: 'CS-201',
+ courseName: 'Object Oriented Programming',
+ type: 'Mixed',
+ questions: 20,
+ duration: 35,
+ totalMarks: 30,
+ attempts: 0,
+ avgScore: 0,
+ status: 'draft',
+ startDate: '2026-02-01 10:00',
+ endDate: '2026-02-01 22:00',
+ createdAt: '2026-01-23',
+ },
+ ];
+
+ const activeQuizzes = quizzes.filter(q => q.status === 'active');
+ const completedQuizzes = quizzes.filter(q => q.status === 'completed');
+ const draftQuizzes = quizzes.filter(q => q.status === 'draft');
+
+ const handleMenuOpen = (event, quiz) => {
+ setAnchorEl(event.currentTarget);
+ setSelectedQuiz(quiz);
+ };
+
+ const handleMenuClose = () => {
+ setAnchorEl(null);
+ setSelectedQuiz(null);
+ };
+
+ const getStatusColor = (status) => {
+ switch (status) {
+ case 'active':
+ return 'success';
+ case 'completed':
+ return 'default';
+ case 'draft':
+ return 'warning';
+ default:
+ return 'default';
+ }
+ };
+
+ const QuizCard = ({ quiz }) => (
+
+
+
+
+ handleMenuOpen(e, quiz)}>
+
+
+
+
+
+ {quiz.title}
+
+
+
+ {quiz.course} - {quiz.courseName}
+
+
+
+ }
+ label={`${quiz.questions} Questions`}
+ size="small"
+ variant="outlined"
+ />
+ }
+ label={`${quiz.duration} mins`}
+ size="small"
+ variant="outlined"
+ />
+
+
+
+
+
+
+ Attempts
+
+
+ {quiz.attempts}
+
+
+
+
+ Avg Score
+
+
+ {quiz.avgScore}%
+
+
+
+
+ Marks
+
+
+ {quiz.totalMarks}
+
+
+
+
+
+ {quiz.status !== 'draft' && (
+
+ 📅 {new Date(quiz.startDate).toLocaleString()}
+
+ )}
+
+
+ }
+ onClick={() => navigate(`/teacher/quiz/${quiz.id}/results`)}
+ >
+ Results
+
+ }
+ onClick={() => navigate(`/teacher/quiz/${quiz.id}/edit`)}
+ >
+ Edit
+
+
+
+
+ );
+
+ const currentQuizzes = activeTab === 0 ? activeQuizzes : activeTab === 1 ? completedQuizzes : draftQuizzes;
+
+ return (
+
+ {/* HEADER */}
+ }
+ variant="contained"
+ onClick={() => navigate('/teacher/quiz/create')}
+ sx={{ px: 3 }}
+ >
+ Create Quiz
+
+ }
+ />
+
+ {/* STATS CARDS */}
+
+ {stats.map((stat, index) => (
+
+
+
+
+
+
+ {stat.value}
+
+
+ {stat.title}
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* TABS */}
+
+ setActiveTab(newValue)}
+ variant="fullWidth"
+ sx={{ borderBottom: 1, borderColor: 'divider' }}
+ >
+
+ Active
+
+
+ }
+ />
+
+ Completed
+
+
+ }
+ />
+
+ Drafts
+
+
+ }
+ />
+
+
+
+ {/* QUIZ CARDS */}
+
+ {currentQuizzes.map((quiz) => (
+
+
+
+ ))}
+
+
+ {currentQuizzes.length === 0 && (
+
+
+
+ No {activeTab === 0 ? 'active' : activeTab === 1 ? 'completed' : 'draft'} quizzes
+
+
+ )}
+
+ {/* MENU */}
+
+
+ );
+};
+
+export default Quizzes;
diff --git a/src/pages/Teacher/Reports.jsx b/src/pages/Teacher/Reports.jsx
new file mode 100644
index 0000000..5423e78
--- /dev/null
+++ b/src/pages/Teacher/Reports.jsx
@@ -0,0 +1,484 @@
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ TextField,
+ MenuItem,
+ Button,
+ Stack,
+ Chip,
+ LinearProgress,
+ Paper,
+ useTheme,
+ alpha,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ BarChart,
+ Bar,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip,
+ ResponsiveContainer,
+ LineChart,
+ Line,
+ PieChart,
+ Pie,
+ Cell,
+ Legend,
+} from 'recharts';
+import {
+ TrendingUp,
+ TrendingDown,
+ People,
+ Assignment,
+ CheckCircle,
+ School,
+ Download,
+ Print,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import PageTransition from '../../components/Common/PageTransition';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const gradeDistribution = [
+ { grade: 'A', count: 12, percentage: 26.7 },
+ { grade: 'B', count: 18, percentage: 40.0 },
+ { grade: 'C', count: 9, percentage: 20.0 },
+ { grade: 'D', count: 4, percentage: 8.9 },
+ { grade: 'F', count: 2, percentage: 4.4 },
+];
+
+const attendanceTrend = [
+ { week: 'Week 1', attendance: 92 },
+ { week: 'Week 2', attendance: 88 },
+ { week: 'Week 3', attendance: 90 },
+ { week: 'Week 4', attendance: 85 },
+ { week: 'Week 5', attendance: 87 },
+ { week: 'Week 6', attendance: 89 },
+];
+
+const assignmentStats = [
+ { name: 'On Time', value: 72, color: '#4caf50' },
+ { name: 'Late', value: 18, color: '#ff9800' },
+ { name: 'Missing', value: 10, color: '#f44336' },
+];
+
+const Reports = () => {
+ const theme = useTheme();
+ const [course, setCourse] = useState('CS-301');
+ const [range, setRange] = useState('last-30');
+
+ const stats = [
+ {
+ title: 'Total Students',
+ value: '85',
+ change: '+5 this month',
+ trend: 'up',
+ icon: People,
+ color: theme.palette.primary.main,
+ },
+ {
+ title: 'Avg Attendance',
+ value: '88.5%',
+ change: '+2.3%',
+ trend: 'up',
+ icon: CheckCircle,
+ color: theme.palette.success.main,
+ },
+ {
+ title: 'Assignment Completion',
+ value: '72%',
+ change: '-5.2%',
+ trend: 'down',
+ icon: Assignment,
+ color: theme.palette.warning.main,
+ },
+ {
+ title: 'Class Average',
+ value: 'B+',
+ change: '+0.2 GPA',
+ trend: 'up',
+ icon: School,
+ color: theme.palette.info.main,
+ },
+ ];
+
+ return (
+
+
+
+
+ {/* STATS CARDS */}
+
+ {stats.map((stat, index) => (
+
+
+
+
+
+
+ {stat.value}
+
+
+ {stat.title}
+
+ : }
+ label={stat.change}
+ size="small"
+ color={stat.trend === 'up' ? 'success' : 'error'}
+ sx={{ mt: 1 }}
+ />
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+ {/* FILTERS */}
+
+
+
+
+
+ setCourse(e.target.value)}
+ sx={{ minWidth: { xs: '100%', sm: 220 } }}
+ size="small"
+ >
+ CS-301 - Data Structures
+ CS-201 - OOP
+ CS-101 - Intro to Computing
+
+ setRange(e.target.value)}
+ sx={{ minWidth: { xs: '100%', sm: 200 } }}
+ size="small"
+ >
+ Last 7 Days
+ Last 30 Days
+ This Semester
+
+
+
+ } size="small">
+ Export
+
+ } size="small">
+ Print
+
+
+
+
+
+
+
+ {/* GRADE DISTRIBUTION */}
+
+
+
+
+
+
+ Grade Distribution
+
+
+ Current semester performance breakdown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Grade breakdown */}
+
+ {gradeDistribution.map((item) => (
+
+
+
+ Grade {item.grade}
+
+
+ {item.count} students ({item.percentage}%)
+
+
+
+
+ ))}
+
+
+
+
+
+ {/* ASSIGNMENT COMPLETION */}
+
+
+
+
+
+
+ Assignment Submission
+
+
+ Submission status breakdown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `${entry.value}%`}
+ labelLine={{ stroke: '#666', strokeWidth: 1 }}
+ >
+ |
+ |
+ |
+
+
+
+
+
+
+ {assignmentStats.map((item) => (
+
+
+
+
+ {item.name}
+
+
+
+ {item.value}%
+
+
+ ))}
+
+
+
+
+
+ {/* ATTENDANCE TREND */}
+
+
+
+
+
+
+ Attendance Trend
+
+
+ Weekly attendance rate over time
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Reports;
diff --git a/src/pages/Teacher/StudentManagement.jsx b/src/pages/Teacher/StudentManagement.jsx
index b1204c7..b889937 100644
--- a/src/pages/Teacher/StudentManagement.jsx
+++ b/src/pages/Teacher/StudentManagement.jsx
@@ -46,10 +46,12 @@ import {
} from '@mui/icons-material';
import { useTheme } from '@mui/material/styles';
import PageHeader from '../../components/Common/PageHeader';
+import { useSnackbar } from '../../contexts/SnackbarContext';
import { pageTransition } from '../../utils/animations';
const StudentManagement = () => {
const theme = useTheme();
+ const { showSnackbar } = useSnackbar();
const [searchQuery, setSearchQuery] = useState('');
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
@@ -110,6 +112,40 @@ const StudentManagement = () => {
status: 'warning',
avatar: 'https://i.pravatar.cc/150?img=8',
},
+ {
+ id: 4,
+ name: 'Fatima Noor',
+ rollNo: 'CS-2023-089',
+ email: 'fatima@nexus.edu',
+ phone: '+92 303 4567890',
+ course: 'CS-301',
+ attendance: 72,
+ assignments: { submitted: 6, total: 12 },
+ quizzes: { completed: 2, total: 5 },
+ midterm: 55,
+ final: null,
+ grade: 'D',
+ cgpa: 1.85,
+ status: 'warning',
+ avatar: 'https://i.pravatar.cc/150?img=44',
+ },
+ {
+ id: 5,
+ name: 'Hassan Raza',
+ rollNo: 'CS-2023-156',
+ email: 'hassan@nexus.edu',
+ phone: '+92 304 7891234',
+ course: 'CS-301',
+ attendance: 58,
+ assignments: { submitted: 5, total: 12 },
+ quizzes: { completed: 2, total: 5 },
+ midterm: 62,
+ final: null,
+ grade: 'D+',
+ cgpa: 2.15,
+ status: 'warning',
+ avatar: 'https://i.pravatar.cc/150?img=51',
+ },
];
const courses = [
@@ -271,8 +307,25 @@ const StudentManagement = () => {
const performanceIndicator = getPerformanceIndicator(student.grade);
const PerformanceIcon = performanceIndicator.icon;
+ // Check if student is at risk
+ const isAtRisk = student.attendance < 75 || student.cgpa < 2.0;
+
return (
-
+
@@ -350,14 +403,32 @@ const StudentManagement = () => {
View Details
-
- Grade Assignments
-
-
+ {
+ if (selectedStudent) {
+ showSnackbar(`Email sent to ${selectedStudent.name}`, 'success');
+ }
+ handleMenuClose();
+ }}
+ >
Send Email
+ {
+ if (selectedStudent) {
+ const isAtRisk = selectedStudent.attendance < 75 || selectedStudent.cgpa < 2.0;
+ const message = isAtRisk
+ ? `Warning notification sent to ${selectedStudent.name} for low ${selectedStudent.attendance < 75 ? 'attendance' : 'GPA'}`
+ : `Notification sent to ${selectedStudent.name}`;
+ showSnackbar(message, isAtRisk ? 'warning' : 'info');
+ }
+ handleMenuClose();
+ }}
+ >
+ Notify Student
+
- Mark Attendance
+ Grade Assignments
diff --git a/src/pages/Teacher/TeacherAttendance.jsx b/src/pages/Teacher/TeacherAttendance.jsx
new file mode 100644
index 0000000..6dce7ab
--- /dev/null
+++ b/src/pages/Teacher/TeacherAttendance.jsx
@@ -0,0 +1,436 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Chip,
+ Stack,
+ TextField,
+ MenuItem,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Avatar,
+ IconButton,
+ Checkbox,
+ useTheme,
+ alpha,
+ LinearProgress,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ HowToReg,
+ Save,
+ Download,
+ FilterList,
+ CheckCircle,
+ Cancel,
+ Schedule,
+ TrendingUp,
+ TrendingDown,
+ People,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const TeacherAttendance = () => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const [selectedCourse, setSelectedCourse] = useState('CS-301');
+ const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
+ const [attendance, setAttendance] = useState({});
+
+ const courses = [
+ { code: 'CS-301', name: 'Data Structures & Algorithms', students: 85 },
+ { code: 'CS-201', name: 'Object Oriented Programming', students: 92 },
+ { code: 'CS-101', name: 'Introduction to Computing', students: 135 },
+ ];
+
+ const stats = [
+ {
+ title: 'Total Students',
+ value: '85',
+ icon: People,
+ color: 'primary.main',
+ },
+ {
+ title: 'Present Today',
+ value: '78',
+ change: '+5',
+ trend: 'up',
+ icon: CheckCircle,
+ color: 'success.main',
+ },
+ {
+ title: 'Absent Today',
+ value: '7',
+ change: '-2',
+ trend: 'down',
+ icon: Cancel,
+ color: 'error.main',
+ },
+ {
+ title: 'Attendance Rate',
+ value: '91.8%',
+ change: '+2.3%',
+ trend: 'up',
+ icon: TrendingUp,
+ color: 'info.main',
+ },
+ ];
+
+ const students = [
+ {
+ id: 1,
+ name: 'Muhammad Asad',
+ rollNo: 'BSCS-2023-001',
+ avatar: 'https://i.pravatar.cc/150?img=12',
+ attendanceRate: 88,
+ present: 22,
+ absent: 3,
+ totalClasses: 25,
+ },
+ {
+ id: 2,
+ name: 'Ayesha Khan',
+ rollNo: 'BSCS-2023-002',
+ avatar: 'https://i.pravatar.cc/150?img=5',
+ attendanceRate: 96,
+ present: 24,
+ absent: 1,
+ totalClasses: 25,
+ },
+ {
+ id: 3,
+ name: 'Ali Ahmed',
+ rollNo: 'BSCS-2023-003',
+ avatar: 'https://i.pravatar.cc/150?img=8',
+ attendanceRate: 72,
+ present: 18,
+ absent: 7,
+ totalClasses: 25,
+ },
+ {
+ id: 4,
+ name: 'Fatima Zahra',
+ rollNo: 'BSCS-2023-004',
+ avatar: 'https://i.pravatar.cc/150?img=10',
+ attendanceRate: 92,
+ present: 23,
+ absent: 2,
+ totalClasses: 25,
+ },
+ {
+ id: 5,
+ name: 'Hassan Ali',
+ rollNo: 'BSCS-2023-005',
+ avatar: 'https://i.pravatar.cc/150?img=13',
+ attendanceRate: 84,
+ present: 21,
+ absent: 4,
+ totalClasses: 25,
+ },
+ ];
+
+ const handleAttendanceChange = (studentId, value) => {
+ setAttendance(prev => ({
+ ...prev,
+ [studentId]: value
+ }));
+ };
+
+ const handleSaveAttendance = () => {
+ console.log('Saving attendance:', attendance);
+ // API call would go here
+ };
+
+ const handleSelectAll = (value) => {
+ const newAttendance = {};
+ students.forEach(student => {
+ newAttendance[student.id] = value;
+ });
+ setAttendance(newAttendance);
+ };
+
+ const getAttendanceColor = (rate) => {
+ if (rate >= 90) return 'success';
+ if (rate >= 75) return 'warning';
+ return 'error';
+ };
+
+ const presentCount = Object.values(attendance).filter(v => v === 'present').length;
+ const absentCount = Object.values(attendance).filter(v => v === 'absent').length;
+ const leaveCount = Object.values(attendance).filter(v => v === 'leave').length;
+
+ return (
+
+ {/* HEADER */}
+
+
+ {/* STATS CARDS */}
+
+ {stats.map((stat, index) => (
+
+
+
+
+
+
+ {stat.value}
+
+
+ {stat.title}
+
+ {stat.change && (
+ : }
+ label={stat.change}
+ size="small"
+ color={stat.trend === 'up' ? 'success' : 'error'}
+ sx={{ mt: 0.5 }}
+ />
+ )}
+
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* FILTERS */}
+
+
+
+
+ setSelectedCourse(e.target.value)}
+ >
+ {courses.map((course) => (
+
+ {course.code} - {course.name}
+
+ ))}
+
+
+
+ setSelectedDate(e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+
+ }
+ onClick={() => handleSelectAll('present')}
+ >
+ Mark All Present
+
+ }
+ onClick={() => navigate('/attendance/history')}
+ >
+ View History
+
+
+
+
+
+
+
+ {/* ATTENDANCE MARKING */}
+
+
+
+
+ Mark Attendance - {new Date(selectedDate).toLocaleDateString('en-US', {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ })}
+
+ {Object.keys(attendance).length > 0 && (
+
+ }
+ label={`Present: ${presentCount}`}
+ color="success"
+ variant="outlined"
+ />
+ }
+ label={`Absent: ${absentCount}`}
+ color="error"
+ variant="outlined"
+ />
+ {leaveCount > 0 && (
+ }
+ label={`Leave: ${leaveCount}`}
+ color="warning"
+ variant="outlined"
+ />
+ )}
+
+ )}
+
+
+
+
+
+
+ Student
+ Roll Number
+ Overall Attendance
+ Present
+ Absent
+ Total Classes
+ Today's Status
+
+
+
+ {students.map((student) => (
+
+
+
+
+
+ {student.name}
+
+
+
+
+
+ {student.rollNo}
+
+
+
+
+
+ {student.attendanceRate}%
+
+
+
+
+
+
+ {student.present}
+
+
+
+
+ {student.absent}
+
+
+
+
+ {student.totalClasses}
+
+
+
+
+ handleAttendanceChange(student.id, 'present')}
+ >
+ P
+
+ handleAttendanceChange(student.id, 'absent')}
+ >
+ A
+
+ handleAttendanceChange(student.id, 'leave')}
+ >
+ L
+
+
+
+
+ ))}
+
+
+
+
+
+ setAttendance({})}>
+ Clear All
+
+ }
+ onClick={handleSaveAttendance}
+ disabled={Object.keys(attendance).length === 0}
+ >
+ Save Attendance
+
+
+
+
+
+ );
+};
+
+export default TeacherAttendance;
diff --git a/src/pages/Teacher/ViewSubmissions.jsx b/src/pages/Teacher/ViewSubmissions.jsx
new file mode 100644
index 0000000..42fc01d
--- /dev/null
+++ b/src/pages/Teacher/ViewSubmissions.jsx
@@ -0,0 +1,460 @@
+import { useState } from 'react';
+import { useParams, useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ Chip,
+ Stack,
+ Tab,
+ Tabs,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ LinearProgress,
+ TextField,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ useTheme,
+ alpha,
+ IconButton,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Assignment,
+ CheckCircle,
+ PendingActions,
+ Visibility,
+ GetApp,
+ Grade,
+ ArrowBack,
+ Send,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp, staggerContainer } from '../../utils/animations';
+
+const ViewSubmissions = () => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const { assignmentId } = useParams();
+ const [tabValue, setTabValue] = useState(0);
+ const [gradeDialog, setGradeDialog] = useState(false);
+ const [selectedSubmission, setSelectedSubmission] = useState(null);
+ const [marks, setMarks] = useState('');
+ const [feedback, setFeedback] = useState('');
+
+ // Mock assignment data
+ const assignment = {
+ id: 1,
+ title: 'Database Design Assignment',
+ course: 'Database Systems',
+ courseCode: 'CS-401',
+ dueDate: '2026-01-30',
+ totalMarks: 100,
+ description: 'Design a normalized database schema for an e-commerce system',
+ };
+
+ // Mock submissions data
+ const [submissions] = useState([
+ {
+ id: 1,
+ studentId: 'CS-2022-001',
+ studentName: 'Ahmed Ali',
+ submittedDate: '2026-01-28',
+ status: 'graded',
+ marksObtained: 85,
+ feedback: 'Excellent work! Good normalization.',
+ files: ['database_design.pdf'],
+ },
+ {
+ id: 2,
+ studentId: 'CS-2022-002',
+ studentName: 'Fatima Khan',
+ submittedDate: '2026-01-29',
+ status: 'graded',
+ marksObtained: 92,
+ feedback: 'Outstanding! Clear ER diagrams.',
+ files: ['schema.pdf', 'er_diagram.png'],
+ },
+ {
+ id: 3,
+ studentId: 'CS-2022-003',
+ studentName: 'Hassan Raza',
+ submittedDate: '2026-01-30',
+ status: 'pending',
+ marksObtained: null,
+ feedback: null,
+ files: ['project.pdf'],
+ },
+ {
+ id: 4,
+ studentId: 'CS-2022-004',
+ studentName: 'Sara Ahmed',
+ submittedDate: null,
+ status: 'not-submitted',
+ marksObtained: null,
+ feedback: null,
+ files: [],
+ },
+ ]);
+
+ const stats = {
+ total: submissions.filter(s => s.status !== 'not-submitted').length,
+ pending: submissions.filter(s => s.status === 'pending').length,
+ graded: submissions.filter(s => s.status === 'graded').length,
+ notSubmitted: submissions.filter(s => s.status === 'not-submitted').length,
+ };
+
+ const handleGradeClick = (submission) => {
+ setSelectedSubmission(submission);
+ setMarks(submission.marksObtained || '');
+ setFeedback(submission.feedback || '');
+ setGradeDialog(true);
+ };
+
+ const handleSubmitGrade = () => {
+ console.log('Submitting grade for:', selectedSubmission.studentId);
+ console.log('Marks:', marks);
+ console.log('Feedback:', feedback);
+ setGradeDialog(false);
+ };
+
+ const getStatusColor = (status) => {
+ switch (status) {
+ case 'graded': return 'success';
+ case 'pending': return 'warning';
+ case 'not-submitted': return 'error';
+ default: return 'default';
+ }
+ };
+
+ const filteredSubmissions = tabValue === 0
+ ? submissions
+ : tabValue === 1
+ ? submissions.filter(s => s.status === 'pending')
+ : tabValue === 2
+ ? submissions.filter(s => s.status === 'graded')
+ : submissions.filter(s => s.status === 'not-submitted');
+
+ return (
+
+ {/* HEADER */}
+
+ }
+ onClick={() => navigate('/teacher/courses')}
+ sx={{ mb: 2 }}
+ >
+ Back to Courses
+
+
+
+
+ {/* STATS CARDS */}
+
+
+
+
+
+
+
+
+ {stats.total}
+ Submissions
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.pending}
+ Pending
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.graded}
+ Graded
+
+
+
+
+
+
+
+
+
+
+
+
+ {stats.notSubmitted}
+ Not Submitted
+
+
+
+
+
+
+ {/* SUBMISSIONS TABLE */}
+
+
+ setTabValue(val)}
+ sx={{ px: 2 }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ Student ID
+ Name
+ Submitted Date
+ Status
+ Marks
+ Actions
+
+
+
+ {filteredSubmissions.map((submission) => (
+
+
+
+ {submission.studentId}
+
+
+ {submission.studentName}
+
+ {submission.submittedDate
+ ? new Date(submission.submittedDate).toLocaleDateString()
+ : '-'}
+
+
+
+
+
+ {submission.status === 'graded' ? (
+
+
+ {submission.marksObtained}/{assignment.totalMarks}
+
+
+
+ ) : '-'}
+
+
+
+ {submission.status !== 'not-submitted' && (
+ <>
+
+
+
+
+
+
+ }
+ onClick={() => handleGradeClick(submission)}
+ sx={{
+ ...(submission.status === 'pending' ? {
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ } : {}),
+ }}
+ >
+ {submission.status === 'graded' ? 'Edit' : 'Grade'}
+
+ >
+ )}
+
+
+
+ ))}
+
+
+
+
+
+ {/* GRADE DIALOG */}
+
+
+ );
+};
+
+export default ViewSubmissions;
diff --git a/src/styles/globalStyles.js b/src/styles/globalStyles.js
index 83c01b5..6ebc495 100644
--- a/src/styles/globalStyles.js
+++ b/src/styles/globalStyles.js
@@ -70,10 +70,10 @@ const globalStyles = (theme) => ({
background: theme.palette.background.default,
},
'::-webkit-scrollbar-thumb': {
- background: theme.palette.grey[400],
+ background: theme.palette.mode === 'dark' ? '#333333' : theme.palette.grey[400],
borderRadius: '4px',
'&:hover': {
- background: theme.palette.grey[500],
+ background: theme.palette.mode === 'dark' ? '#444444' : theme.palette.grey[500],
},
},
// Page container
@@ -112,12 +112,8 @@ const globalStyles = (theme) => ({
},
// Hover lift effect
'.hover-lift': {
- transition: 'transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
- cursor: 'pointer',
- '&:hover': {
- transform: 'scale(1.02) translateY(-2px)',
- boxShadow: theme.shadows[8],
- },
+ transition: 'none',
+ cursor: 'default',
},
// Status dots
'.status-dot': {
@@ -153,20 +149,33 @@ const globalStyles = (theme) => ({
animation: 'scaleIn 0.2s ease-out',
},
'.shimmer': {
- background: `linear-gradient(to right, ${theme.palette.grey[200]} 0%, ${theme.palette.grey[300]} 20%, ${theme.palette.grey[200]} 40%, ${theme.palette.grey[200]} 100%)`,
+ background: theme.palette.mode === 'dark'
+ ? 'linear-gradient(to right, #333333 0%, #444444 20%, #333333 40%, #333333 100%)'
+ : `linear-gradient(to right, ${theme.palette.grey[200]} 0%, ${theme.palette.grey[300]} 20%, ${theme.palette.grey[200]} 40%, ${theme.palette.grey[200]} 100%)`,
backgroundSize: '1000px 100%',
animation: 'shimmer 2s infinite linear',
},
// Loading skeleton
'.skeleton': {
- backgroundColor: theme.palette.grey[200],
+ backgroundColor: theme.palette.mode === 'dark' ? '#333333' : theme.palette.grey[200],
borderRadius: theme.shape.borderRadius,
'&.shimmer': {
- background: `linear-gradient(to right, ${theme.palette.grey[200]} 0%, ${theme.palette.grey[300]} 20%, ${theme.palette.grey[200]} 40%, ${theme.palette.grey[200]} 100%)`,
+ background: theme.palette.mode === 'dark'
+ ? 'linear-gradient(to right, #333333 0%, #444444 20%, #333333 40%, #333333 100%)'
+ : `linear-gradient(to right, ${theme.palette.grey[200]} 0%, ${theme.palette.grey[300]} 20%, ${theme.palette.grey[200]} 40%, ${theme.palette.grey[200]} 100%)`,
backgroundSize: '1000px 100%',
animation: 'shimmer 2s infinite linear',
},
},
+ // Recharts styling
+ '.recharts-cartesian-grid line': {
+ stroke: theme.palette.mode === 'dark' ? '#444444' : '#E0E0E0',
+ },
+ '.recharts-default-tooltip': {
+ backgroundColor: theme.palette.mode === 'dark' ? '#333333' : '#FFFFFF',
+ border: `1px solid ${theme.palette.mode === 'dark' ? '#444444' : '#E0E0E0'}`,
+ color: theme.palette.mode === 'dark' ? '#FFFFFF' : '#212121',
+ },
// Utility classes
'.smooth-transition': {
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
@@ -178,11 +187,7 @@ const globalStyles = (theme) => ({
backgroundClip: 'text',
},
'.card-hover': {
- transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
- '&:hover': {
- transform: 'translateY(-4px)',
- boxShadow: theme.shadows[12],
- },
+ transition: 'none',
},
// Focus visible styles
'.focus-visible': {
diff --git a/src/theme.js b/src/theme.js
index 7575c3c..6fbff30 100644
--- a/src/theme.js
+++ b/src/theme.js
@@ -9,34 +9,34 @@ export const getTheme = (mode) => {
mode,
primary: {
main: isLight ? '#1976D2' : '#90CAF9',
- light: isLight ? '#42A5F5' : '#A5D6F9',
+ light: isLight ? '#42A5F5' : '#BBDEFB',
dark: isLight ? '#1565C0' : '#64B5F6',
- contrastText: isLight ? '#FFFFFF' : '#000000',
+ contrastText: '#FFFFFF',
},
secondary: {
main: isLight ? '#00796B' : '#26A69A',
light: isLight ? '#26A69A' : '#4DB6AC',
- dark: isLight ? '#004D40' : '#00897B',
+ dark: isLight ? '#004D40' : '#00796B',
contrastText: '#FFFFFF',
},
error: {
main: isLight ? '#D32F2F' : '#EF5350',
- light: isLight ? '#EF5350' : '#E57373',
- dark: isLight ? '#C62828' : '#D32F2F',
+ light: isLight ? '#EF5350' : '#EF5350',
+ dark: isLight ? '#C62828' : '#E53935',
},
warning: {
- main: isLight ? '#F57C00' : '#FF9800',
+ main: isLight ? '#F57C00' : '#FFA726',
light: isLight ? '#FF9800' : '#FFB74D',
- dark: isLight ? '#E65100' : '#F57C00',
+ dark: isLight ? '#E65100' : '#FB8C00',
},
success: {
- main: isLight ? '#388E3C' : '#4CAF50',
- light: isLight ? '#4CAF50' : '#66BB6A',
- dark: isLight ? '#2E7D32' : '#388E3C',
+ main: isLight ? '#388E3C' : '#66BB6A',
+ light: isLight ? '#4CAF50' : '#81C784',
+ dark: isLight ? '#2E7D32' : '#4CAF50',
},
info: {
- main: isLight ? '#0288D1' : '#03A9F4',
- light: isLight ? '#03A9F4' : '#29B6F6',
+ main: isLight ? '#0288D1' : '#29B6F6',
+ light: isLight ? '#03A9F4' : '#4FC3F7',
dark: isLight ? '#01579B' : '#0288D1',
},
background: {
@@ -47,6 +47,7 @@ export const getTheme = (mode) => {
primary: isLight ? '#212121' : '#FFFFFF',
secondary: isLight ? '#757575' : '#B0B0B0',
},
+ divider: isLight ? '#E0E0E0' : '#333333',
},
typography: {
fontFamily: "'Inter', 'Roboto', sans-serif",
@@ -123,7 +124,11 @@ export const getTheme = (mode) => {
styleOverrides: {
root: {
borderRadius: '16px',
- boxShadow: isLight ? '0px 4px 8px rgba(0, 0, 0, 0.08)' : '0px 4px 8px rgba(0, 0, 0, 0.4)',
+ boxShadow: isLight ? '0px 4px 20px rgba(0, 0, 0, 0.05)' : '0px 2px 8px rgba(0, 0, 0, 0.4)',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
+ backgroundImage: isLight ? 'none' : 'linear-gradient(rgba(255,255,255,0.05), rgba(255,255,255,0.05))',
+ border: isLight ? '1px solid transparent' : '1px solid #333333',
+ transition: 'box-shadow 0.2s ease',
},
},
},
@@ -135,6 +140,12 @@ export const getTheme = (mode) => {
fontWeight: 500,
padding: '10px 20px',
},
+ contained: {
+ boxShadow: isLight ? '0px 2px 4px rgba(0, 0, 0, 0.1)' : '0px 2px 8px rgba(33, 150, 243, 0.3)',
+ '&:hover': {
+ boxShadow: isLight ? '0px 4px 8px rgba(0, 0, 0, 0.15)' : '0px 4px 12px rgba(33, 150, 243, 0.4)',
+ },
+ },
},
},
MuiChip: {
@@ -149,6 +160,8 @@ export const getTheme = (mode) => {
styleOverrides: {
root: {
borderRadius: '12px',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
+ backgroundImage: 'none',
},
},
},
@@ -157,6 +170,59 @@ export const getTheme = (mode) => {
root: {
'& .MuiOutlinedInput-root': {
borderRadius: '10px',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
+ '& fieldset': {
+ borderColor: isLight ? 'rgba(0, 0, 0, 0.23)' : '#333333',
+ },
+ '&:hover fieldset': {
+ borderColor: isLight ? 'rgba(0, 0, 0, 0.4)' : '#444444',
+ },
+ },
+ },
+ },
+ },
+ MuiDrawer: {
+ styleOverrides: {
+ paper: {
+ backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
+ borderRight: isLight ? '1px solid rgba(0, 0, 0, 0.12)' : '1px solid #333333',
+ },
+ },
+ },
+ MuiAppBar: {
+ styleOverrides: {
+ root: {
+ backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
+ color: isLight ? '#212121' : '#FFFFFF',
+ boxShadow: isLight ? '0px 1px 3px rgba(0, 0, 0, 0.08)' : 'none',
+ },
+ },
+ },
+ MuiListItemButton: {
+ styleOverrides: {
+ root: {
+ borderRadius: '8px',
+ margin: '4px 8px',
+ '&.Mui-selected': {
+ backgroundColor: isLight ? 'rgba(25, 118, 210, 0.08)' : 'rgba(33, 150, 243, 0.16)',
+ '&:hover': {
+ backgroundColor: isLight ? 'rgba(25, 118, 210, 0.12)' : 'rgba(33, 150, 243, 0.24)',
+ },
+ },
+ '&:hover': {
+ backgroundColor: isLight ? 'rgba(0, 0, 0, 0.04)' : 'rgba(255, 255, 255, 0.05)',
+ },
+ },
+ },
+ },
+ MuiSkeleton: {
+ styleOverrides: {
+ root: {
+ backgroundColor: isLight ? undefined : '#333333',
+ '&::after': {
+ background: isLight
+ ? undefined
+ : 'linear-gradient(90deg, rgba(51,51,51,0) 0%, rgba(68,68,68,0.6) 50%, rgba(51,51,51,0) 100%)',
},
},
},
diff --git a/vite.config.js b/vite.config.js
index 2328e17..6f0150a 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,4 +4,5 @@ import react from '@vitejs/plugin-react-swc'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
+ base: '/Project_Nexus/',
})
From 44bca7eed1de2cbbd7ec6f12165027d9619cdd5b Mon Sep 17 00:00:00 2001
From: Muhammad Asad <148639150+ASAD2204@users.noreply.github.com>
Date: Sun, 25 Jan 2026 02:18:51 +0500
Subject: [PATCH 2/7] Update package.json
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index d470a5d..8d886b2 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"private": true,
"version": "0.0.0",
"type": "module",
- "homepage": "https://YOUR_GITHUB_USERNAME.github.io/Project_Nexus",
+ "homepage": "https://ASAD2204.github.io/Project_Nexus",
"scripts": {
"dev": "vite",
"build": "vite build",
From 267b14812cf5572dcbc16003fced8241daf1946b Mon Sep 17 00:00:00 2001
From: Muhammad Asad <148639150+ASAD2204@users.noreply.github.com>
Date: Sun, 25 Jan 2026 02:28:05 +0500
Subject: [PATCH 3/7] ready to deploy
---
.github/workflows/deploy.yml | 2 +-
DEPLOYMENT.md | 42 ++++++++++++++++++++++++++++++------
2 files changed, 37 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 1f8deee..73df7e9 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -3,7 +3,7 @@ name: Deploy to GitHub Pages
on:
push:
branches:
- - main
+ - Asad_node
permissions:
contents: read
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
index a35474a..5760f9e 100644
--- a/DEPLOYMENT.md
+++ b/DEPLOYMENT.md
@@ -9,19 +9,33 @@ In `package.json`, replace the homepage URL:
```
Change `YOUR_GITHUB_USERNAME` to your actual GitHub username.
-### 2. GitHub Repository Settings
-1. Go to your repository on GitHub
-2. Navigate to **Settings** → **Pages**
-3. Under **Source**, select **GitHub Actions**
+### 2. GitHub Repository Settings - IMPORTANT!
+
+#### Step-by-Step GitHub Pages Configuration:
+
+1. **Go to your GitHub repository** (https://github.com/YOUR_USERNAME/Project_Nexus)
+
+2. **Click on "Settings"** tab (top menu)
+
+3. **In the left sidebar, scroll down and click "Pages"**
+
+4. **Under "Build and deployment" section:**
+ - **Source**: Select **"GitHub Actions"** from the dropdown
+ - ❌ NOT "Deploy from a branch"
+ - ✅ SELECT "GitHub Actions"
+
+5. **Save** (if there's a save button, otherwise it auto-saves)
+
+6. **The workflow will trigger on push to `Asad_node` branch**
### 3. Deploy Options
#### Option A: Automatic Deployment (Recommended)
-Push your code to the `main` branch:
+Push your code to the `Asad_node` branch:
```bash
git add .
git commit -m "Configure GitHub Pages deployment"
-git push origin main
+git push origin Asad_node
```
The GitHub Action will automatically build and deploy your site.
@@ -30,6 +44,7 @@ Run the deployment script:
```bash
npm run deploy
```
+**Note:** Manual deployment creates a `gh-pages` branch. If using GitHub Actions, you don't need this.
### 4. Access Your Site
After deployment, your site will be available at:
@@ -37,6 +52,21 @@ After deployment, your site will be available at:
https://YOUR_GITHUB_USERNAME.github.io/Project_Nexus
```
+### 5. Check Deployment Status
+
+1. Go to **Actions** tab in your repository
+2. You'll see "Deploy to GitHub Pages" workflow
+3. Click on the latest run to see progress
+4. Wait for the green checkmark ✅
+5. Your site will be live in 2-5 minutes
+
+## Current Configuration
+
+- **Deployment Branch**: `Asad_node`
+- **Build Command**: `npm run build`
+- **Output Directory**: `dist/`
+- **Base Path**: `/Project_Nexus/`
+
## Important Notes
- **Base Path**: The `base` property in `vite.config.js` is set to `/Project_Nexus/`. This must match your repository name.
From 1135b237d3a2e082b7f3edfb289966b3897686f6 Mon Sep 17 00:00:00 2001
From: Muhammad Asad <148639150+ASAD2204@users.noreply.github.com>
Date: Sun, 25 Jan 2026 02:37:04 +0500
Subject: [PATCH 4/7] Fix base path for GitHub Pages - use correct repository
name case
---
package.json | 2 +-
vite.config.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index 8d886b2..d10229d 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"private": true,
"version": "0.0.0",
"type": "module",
- "homepage": "https://ASAD2204.github.io/Project_Nexus",
+ "homepage": "https://ASAD2204.github.io/PROJECT_NEXUS",
"scripts": {
"dev": "vite",
"build": "vite build",
diff --git a/vite.config.js b/vite.config.js
index 6f0150a..f47b3da 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,5 +4,5 @@ import react from '@vitejs/plugin-react-swc'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
- base: '/Project_Nexus/',
+ base: '/PROJECT_NEXUS/',
})
From f5a5502592ca6a063b4b3839d859fbc27d809f6e Mon Sep 17 00:00:00 2001
From: Muhammad Asad <148639150+ASAD2204@users.noreply.github.com>
Date: Mon, 26 Jan 2026 00:49:05 +0500
Subject: [PATCH 5/7] fixed cards and splash screen
---
src/App.jsx | 43 +++-
src/components/Common/SplashScreen.jsx | 294 ++++++++++++++++++++++
src/components/Common/StatCard.jsx | 236 +++++++++++------
src/pages/Admin/AlumniManagement.jsx | 11 +-
src/pages/Admin/CourseManagement.jsx | 34 +--
src/pages/Admin/Dashboard.jsx | 20 +-
src/pages/Admin/DepartmentManagement.jsx | 4 +
src/pages/Admin/FinanceManagement.jsx | 4 +
src/pages/Admin/GrievanceManagement.jsx | 142 ++---------
src/pages/Admin/Reports.jsx | 61 +++--
src/pages/Alumni/AlumniEvents.jsx | 53 ++--
src/pages/Alumni/AlumniNetwork.jsx | 70 +++---
src/pages/Alumni/JobBoard.jsx | 27 +-
src/pages/Alumni/Mentorship.jsx | 27 +-
src/pages/Alumni/SuccessStories.jsx | 27 +-
src/pages/Auth/Login.jsx | 194 ++++++++++----
src/pages/Finance/FeeVouchers.jsx | 4 +
src/pages/Grievances/Grievances.jsx | 141 +++--------
src/pages/Library/BookManagement.jsx | 27 +-
src/pages/Library/IssuedBooks.jsx | 18 +-
src/pages/Library/LibrarianDashboard.jsx | 219 +++-------------
src/pages/Library/LibrarianGrievances.jsx | 24 +-
src/pages/Library/LibrarianReports.jsx | 36 ++-
src/pages/Library/Library.jsx | 142 +++--------
src/pages/Library/Reservations.jsx | 18 +-
src/pages/Student/Dashboard.jsx | 55 +---
src/pages/Student/MyAssignments.jsx | 141 +++--------
src/pages/Student/MyTickets.jsx | 143 +++--------
src/pages/Student/Profile.jsx | 4 +
src/pages/Student/Transcript.jsx | 47 +++-
src/pages/Teacher/Assignments.jsx | 40 +--
src/pages/Teacher/Dashboard.jsx | 20 +-
src/pages/Teacher/GrievanceManagement.jsx | 24 +-
src/pages/Teacher/Quizzes.jsx | 40 +--
src/pages/Teacher/Reports.jsx | 60 ++---
src/pages/Teacher/StudentManagement.jsx | 69 +++--
src/pages/Teacher/TeacherAttendance.jsx | 59 ++---
src/pages/Teacher/ViewSubmissions.jsx | 133 +++-------
vite.config.js | 2 +-
39 files changed, 1307 insertions(+), 1406 deletions(-)
create mode 100644 src/components/Common/SplashScreen.jsx
diff --git a/src/App.jsx b/src/App.jsx
index c27a622..81a6767 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,7 +1,10 @@
-import React from 'react';
-import { Routes, Route, Navigate } from 'react-router-dom';
+import React, { useState, useEffect } from 'react';
+import { Routes, Route, Navigate, useNavigate } from 'react-router-dom';
import { useAuth } from './contexts/AuthContext';
+// Components
+import SplashScreen from './components/Common/SplashScreen';
+
// Layouts
import MainLayout from './components/Layout/MainLayout';
@@ -85,7 +88,31 @@ const ProtectedRoute = ({ children }) => {
};
function App() {
- const { userType } = useAuth();
+ const navigate = useNavigate();
+ const { userType, isAuthenticated } = useAuth();
+ const [showSplash, setShowSplash] = useState(true);
+ const [splashComplete, setSplashComplete] = useState(false);
+
+ useEffect(() => {
+ // Check if splash has been shown in this session
+ const splashShown = sessionStorage.getItem('splashShown');
+ if (splashShown) {
+ setShowSplash(false);
+ setSplashComplete(true);
+ }
+ }, []);
+
+ const handleSplashComplete = () => {
+ sessionStorage.setItem('splashShown', 'true');
+ setShowSplash(false);
+ setTimeout(() => {
+ setSplashComplete(true);
+ // Navigate to login if not authenticated
+ if (!isAuthenticated) {
+ navigate('/login');
+ }
+ }, 500);
+ };
// Default dashboard based on user type
const getDefaultDashboard = () => {
@@ -103,6 +130,16 @@ function App() {
}
};
+ // Show splash screen on first load
+ if (showSplash) {
+ return ;
+ }
+
+ // Don't render routes until splash is complete
+ if (!splashComplete) {
+ return null;
+ }
+
return (
{/* Public Routes */}
diff --git a/src/components/Common/SplashScreen.jsx b/src/components/Common/SplashScreen.jsx
new file mode 100644
index 0000000..7427577
--- /dev/null
+++ b/src/components/Common/SplashScreen.jsx
@@ -0,0 +1,294 @@
+import { useState, useEffect } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import { Box, Typography, LinearProgress } from '@mui/material';
+import { useTheme } from '@mui/material/styles';
+
+const SplashScreen = ({ onComplete }) => {
+ const theme = useTheme();
+ const [progress, setProgress] = useState(0);
+
+ useEffect(() => {
+ const timer = setInterval(() => {
+ setProgress((prev) => {
+ if (prev >= 100) {
+ clearInterval(timer);
+ setTimeout(() => onComplete(), 800);
+ return 100;
+ }
+ return prev + 1;
+ });
+ }, 40);
+
+ return () => clearInterval(timer);
+ }, [onComplete]);
+
+ return (
+
+
+ {/* Animated Background Circles */}
+
+ {[...Array(6)].map((_, i) => (
+
+ ))}
+
+
+
+ {/* Animated Logo/Text */}
+
+
+ {/* Glow effect behind text */}
+
+
+ Project Nexus
+
+
+
+
+ {/* Animated Subtitle */}
+
+
+ The Unified Campus Management System
+
+
+
+ {/* Animated Floating Elements */}
+
+ {[...Array(7)].map((_, i) => (
+
+ ))}
+
+
+ {/* Progress Bar */}
+
+
+
+ Loading {progress}%
+
+
+
+ {/* Outer Rings */}
+ {[1, 2, 3].map((ring) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default SplashScreen;
+
diff --git a/src/components/Common/StatCard.jsx b/src/components/Common/StatCard.jsx
index d0b9a1f..2b3cb6c 100644
--- a/src/components/Common/StatCard.jsx
+++ b/src/components/Common/StatCard.jsx
@@ -1,17 +1,21 @@
import React, { useState, useEffect } from 'react';
-import { Card, CardContent, Typography, Box, Skeleton } from '@mui/material';
-import { TrendingUp as TrendingUpIcon, TrendingDown as TrendingDownIcon } from '@mui/icons-material';
+import { Card, CardContent, Typography, Box, Skeleton, Tooltip, IconButton } from '@mui/material';
+import { TrendingUp as TrendingUpIcon, TrendingDown as TrendingDownIcon, InfoOutlined } from '@mui/icons-material';
+import { motion } from 'framer-motion';
+import { useTheme } from '@mui/material/styles';
/**
- * StatCard - Reusable statistics card component
+ * StatCard - Reusable statistics card component with enhanced tooltips
*
* @param {string} title - Card title (e.g., "CGPA")
* @param {string|number} value - Main value to display (e.g., "3.85")
* @param {ReactNode} icon - MUI icon component
- * @param {string} color - 'primary' | 'secondary' | 'success' | 'warning' | 'error'
+ * @param {string} color - 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info'
* @param {object} trend - Trend object { direction: 'up' | 'down', value: '5%' } (optional)
* @param {string} subtitle - Additional subtitle text (optional)
+ * @param {string} tooltip - Helpful tooltip text explaining the stat (optional)
* @param {boolean} loading - Shows skeleton loader if true (optional)
+ * @param {function} onClick - Click handler for the card (optional)
*/
const StatCard = ({
title,
@@ -20,8 +24,11 @@ const StatCard = ({
color = 'primary',
trend,
subtitle,
+ tooltip,
loading = false,
+ onClick,
}) => {
+ const theme = useTheme();
const [animatedValue, setAnimatedValue] = useState(0);
const [mounted, setMounted] = useState(false);
@@ -93,119 +100,204 @@ const StatCard = ({
}
return (
-
-
-
- {/* Left side: Content */}
-
- {/* Value */}
+
+
+
+ {/* Title with Tooltip */}
+
+
+ {title}
+
+ {tooltip && (
+
+
+
+ )}
+
+
+ {/* Animated Icon */}
+ {Icon && (
+
+ theme.palette[color]?.main || theme.palette.primary.main,
+ color: '#ffffff',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexShrink: 0,
+ boxShadow: (theme) => `0 4px 12px ${theme.palette[color]?.main}40`,
+ position: 'relative',
+ overflow: 'hidden',
+ }}
+ >
+
+
+
+ )}
+
+
+ {/* Value with Count-up Animation */}
+
{displayValue()}
+
- {/* Title */}
-
- {title}
-
-
- {/* Trend */}
- {trend && (
{trend.value}
- )}
+
+ )}
- {/* Subtitle */}
- {subtitle && (
+ {/* Subtitle */}
+ {subtitle && (
+
{subtitle}
- )}
-
-
- {/* Right side: Icon */}
- {Icon && (
-
-
-
+
)}
-
-
-
+
+
+
);
};
diff --git a/src/pages/Admin/AlumniManagement.jsx b/src/pages/Admin/AlumniManagement.jsx
index 1892276..e488191 100644
--- a/src/pages/Admin/AlumniManagement.jsx
+++ b/src/pages/Admin/AlumniManagement.jsx
@@ -230,7 +230,13 @@ const AlumniManagement = () => {
{/* Stats */}
-
+
{
icon={CheckCircle}
color="success"
subtitle={`${((stats.verified / stats.total) * 100).toFixed(0)}% verified`}
+ tooltip="Number of alumni with verified profiles and credentials"
/>
@@ -248,6 +255,7 @@ const AlumniManagement = () => {
icon={School}
color="info"
subtitle={`Class of ${new Date().getFullYear()}`}
+ tooltip="Number of alumni who graduated this year"
/>
@@ -257,6 +265,7 @@ const AlumniManagement = () => {
icon={Business}
color="warning"
subtitle={`${((stats.employed / stats.total) * 100).toFixed(0)}% employment rate`}
+ tooltip="Number of alumni currently employed with registered companies"
/>
diff --git a/src/pages/Admin/CourseManagement.jsx b/src/pages/Admin/CourseManagement.jsx
index 15165b4..dfa77cb 100644
--- a/src/pages/Admin/CourseManagement.jsx
+++ b/src/pages/Admin/CourseManagement.jsx
@@ -42,6 +42,7 @@ import {
} from '@mui/icons-material';
import { useTheme } from '@mui/material/styles';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { pageTransition } from '../../utils/animations';
const CourseManagement = () => {
@@ -141,31 +142,14 @@ const CourseManagement = () => {
{departments.map((dept, index) => (
-
-
-
- {dept.name}
-
-
-
-
- Courses
-
-
- {dept.courses}
-
-
-
-
- Students
-
-
- {dept.activeStudents}
-
-
-
-
-
+
))}
diff --git a/src/pages/Admin/Dashboard.jsx b/src/pages/Admin/Dashboard.jsx
index 9d3d588..cb7bcf6 100644
--- a/src/pages/Admin/Dashboard.jsx
+++ b/src/pages/Admin/Dashboard.jsx
@@ -52,34 +52,34 @@ const AdminDashboard = () => {
{
title: 'Total Students',
value: '2,847',
- change: '+12.5%',
- trend: 'up',
+ subtitle: '+12.5% from last month',
icon: People,
color: 'primary',
+ tooltip: 'Total number of enrolled students across all programs and departments',
},
{
title: 'Faculty Members',
value: '186',
- change: '+3.2%',
- trend: 'up',
+ subtitle: '+3.2% new hires',
icon: School,
color: 'success',
+ tooltip: 'Total number of teaching and research faculty members',
},
{
title: 'Active Courses',
value: '342',
- change: '+8.1%',
- trend: 'up',
+ subtitle: '+8.1% this semester',
icon: Assignment,
color: 'info',
+ tooltip: 'Number of courses currently being offered this semester',
},
{
title: 'Revenue (This Month)',
value: '₨ 8.5M',
- change: '+15.3%',
- trend: 'up',
+ subtitle: '+15.3% increase',
icon: AccountBalance,
color: 'warning',
+ tooltip: 'Total revenue collected from fees and other sources this month',
},
];
@@ -148,10 +148,10 @@ const AdminDashboard = () => {
))}
diff --git a/src/pages/Admin/DepartmentManagement.jsx b/src/pages/Admin/DepartmentManagement.jsx
index 9618096..e92b94f 100644
--- a/src/pages/Admin/DepartmentManagement.jsx
+++ b/src/pages/Admin/DepartmentManagement.jsx
@@ -285,6 +285,7 @@ const DepartmentManagement = () => {
icon={School}
color="primary"
subtitle={`${stats.activePrograms} active`}
+ tooltip="Total number of degree programs offered across all departments"
/>
@@ -294,6 +295,7 @@ const DepartmentManagement = () => {
icon={People}
color="success"
subtitle="Across all programs"
+ tooltip="Total number of students enrolled in all programs combined"
/>
@@ -303,6 +305,7 @@ const DepartmentManagement = () => {
icon={People}
color="info"
subtitle="Teaching staff"
+ tooltip="Total number of faculty members teaching across all programs"
/>
@@ -312,6 +315,7 @@ const DepartmentManagement = () => {
icon={MenuBook}
color="warning"
subtitle="Academic departments"
+ tooltip="Number of academic departments offering various programs"
/>
diff --git a/src/pages/Admin/FinanceManagement.jsx b/src/pages/Admin/FinanceManagement.jsx
index da23842..7ea94ad 100644
--- a/src/pages/Admin/FinanceManagement.jsx
+++ b/src/pages/Admin/FinanceManagement.jsx
@@ -143,6 +143,7 @@ const FinanceManagement = () => {
color="success"
trend={{ direction: 'up', value: '+15.3%' }}
subtitle="This Month"
+ tooltip="Total revenue collected from student fee payments this month"
/>
@@ -152,6 +153,7 @@ const FinanceManagement = () => {
icon={AttachMoney}
color="warning"
subtitle={`${stats.unpaidCount + stats.overdueCount} Students`}
+ tooltip="Total outstanding fees from unpaid and overdue students"
/>
@@ -161,6 +163,7 @@ const FinanceManagement = () => {
icon={Warning}
color="error"
subtitle="Requires Action"
+ tooltip="Number of students with overdue payment status requiring immediate attention"
/>
@@ -170,6 +173,7 @@ const FinanceManagement = () => {
icon={Receipt}
color="info"
subtitle="This Semester"
+ tooltip="Total number of financial transactions recorded this semester"
/>
diff --git a/src/pages/Admin/GrievanceManagement.jsx b/src/pages/Admin/GrievanceManagement.jsx
index 8f6281e..74edd92 100644
--- a/src/pages/Admin/GrievanceManagement.jsx
+++ b/src/pages/Admin/GrievanceManagement.jsx
@@ -37,6 +37,7 @@ import {
import { grievances } from '../../data/dummyData';
import PageTransition from '../../components/Common/PageTransition';
import EmptyState from '../../components/Common/EmptyState';
+import StatCard from '../../components/Common/StatCard';
import { useSnackbar } from '../../contexts/SnackbarContext';
import { fadeInUp, staggerContainer } from '../../utils/animations';
@@ -143,129 +144,36 @@ const GrievanceManagement = () => {
animate="animate"
>
-
-
-
-
-
-
-
-
- Total Pending
-
-
- {pendingCount}
-
-
-
-
-
+
-
-
-
-
-
-
-
-
- High Priority Open
-
-
- {highPriorityCount}
-
-
-
-
-
+
-
-
-
-
-
-
-
-
- Avg Resolution Time
-
-
- 2.4d
-
-
-
-
-
+
diff --git a/src/pages/Admin/Reports.jsx b/src/pages/Admin/Reports.jsx
index 0e3ea06..6f83e7c 100644
--- a/src/pages/Admin/Reports.jsx
+++ b/src/pages/Admin/Reports.jsx
@@ -40,6 +40,7 @@ import {
ResponsiveContainer,
} from 'recharts';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { pageTransition } from '../../utils/animations';
const AdminReports = () => {
@@ -57,10 +58,38 @@ const AdminReports = () => {
];
const summaryStats = [
- { label: 'Total Students', value: '2,847', change: '+12.5%', color: theme.palette.primary.main },
- { label: 'Total Revenue', value: '₨ 48.5M', change: '+15.3%', color: theme.palette.success.main },
- { label: 'Avg Attendance', value: '87%', change: '+2.1%', color: theme.palette.info.main },
- { label: 'Course Completion', value: '94%', change: '+3.2%', color: theme.palette.warning.main },
+ {
+ title: 'Total Students',
+ value: '2,847',
+ subtitle: '+12.5% from last year',
+ color: 'primary',
+ icon: People,
+ tooltip: 'Total number of students enrolled across all departments and programs'
+ },
+ {
+ title: 'Total Revenue',
+ value: '₨ 48.5M',
+ subtitle: '+15.3% increase',
+ color: 'success',
+ icon: Payment,
+ tooltip: 'Total revenue generated from tuition fees, lab fees, and other charges'
+ },
+ {
+ title: 'Avg Attendance',
+ value: '87%',
+ subtitle: '+2.1% improvement',
+ color: 'info',
+ icon: School,
+ tooltip: 'Average attendance rate across all classes and programs'
+ },
+ {
+ title: 'Course Completion',
+ value: '94%',
+ subtitle: '+3.2% this year',
+ color: 'warning',
+ icon: Assessment,
+ tooltip: 'Percentage of students successfully completing their enrolled courses'
+ },
];
const enrollmentData = [
@@ -152,22 +181,14 @@ const AdminReports = () => {
{summaryStats.map((stat, index) => (
-
-
-
- {stat.label}
-
-
- {stat.value}
-
-
-
-
+
))}
diff --git a/src/pages/Alumni/AlumniEvents.jsx b/src/pages/Alumni/AlumniEvents.jsx
index ae613bb..e976ea8 100644
--- a/src/pages/Alumni/AlumniEvents.jsx
+++ b/src/pages/Alumni/AlumniEvents.jsx
@@ -36,9 +36,11 @@ import {
VideoCall,
BusinessCenter,
School,
+ History,
} from '@mui/icons-material';
import { alumniEvents, registerForEvent } from '../../data/dummyData';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import PageTransition from '../../components/Common/PageTransition';
import EmptyState from '../../components/Common/EmptyState';
import { CardSkeleton } from '../../components/Common/LoadingSkeleton';
@@ -140,40 +142,31 @@ const AlumniEvents = () => {
{/* Stats */}
-
-
-
- {upcomingEvents.length}
-
-
- Upcoming Events
-
-
-
+
-
-
-
- {alumniEvents.reduce((sum, e) => sum + e.registered, 0)}
-
-
- Total Registrations
-
-
-
+ sum + e.registered, 0)}
+ icon={People}
+ color="success"
+ tooltip="Total participants registered across all events. Shows engagement level of alumni and students in university activities"
+ />
-
-
-
- {pastEvents.length}
-
-
- Past Events
-
-
-
+
diff --git a/src/pages/Alumni/AlumniNetwork.jsx b/src/pages/Alumni/AlumniNetwork.jsx
index dc256d5..b7f7479 100644
--- a/src/pages/Alumni/AlumniNetwork.jsx
+++ b/src/pages/Alumni/AlumniNetwork.jsx
@@ -31,9 +31,11 @@ import {
PersonAdd,
Language,
Email,
+ People,
} from '@mui/icons-material';
import { alumni, connectWithAlumni } from '../../data/dummyData';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import PageTransition from '../../components/Common/PageTransition';
import EmptyState from '../../components/Common/EmptyState';
import { CourseCardSkeleton } from '../../components/Common/LoadingSkeleton';
@@ -172,52 +174,40 @@ const AlumniNetwork = () => {
{/* Stats */}
-
-
-
- {alumni.length}
-
-
- Total Alumni
-
-
-
+
-
-
-
- {new Set(alumni.map((a) => a.currentCompany)).size}
-
-
- Companies
-
-
-
+ a.currentCompany)).size}
+ icon={Business}
+ color="success"
+ tooltip="Number of companies where our alumni work. Includes top tech giants, startups, and Fortune 500 companies"
+ />
-
-
-
- {new Set(alumni.map((a) => a.location)).size}
-
-
- Locations
-
-
-
+ a.location)).size}
+ icon={LocationOn}
+ color="info"
+ tooltip="Cities and countries where alumni are located. Global network spanning across Pakistan and international locations"
+ />
-
-
-
- {filteredAlumni.length}
-
-
- Showing
-
-
-
+
diff --git a/src/pages/Alumni/JobBoard.jsx b/src/pages/Alumni/JobBoard.jsx
index ad6ac22..94bacd3 100644
--- a/src/pages/Alumni/JobBoard.jsx
+++ b/src/pages/Alumni/JobBoard.jsx
@@ -126,9 +126,30 @@ const JobBoard = () => {
];
const stats = [
- { label: 'Total Jobs', value: '142', change: '+12 this week', trend: 'up', color: 'primary', icon: WorkIcon },
- { label: 'Companies', value: '56', change: '+5 new', trend: 'up', color: 'success', icon: BusinessIcon },
- { label: 'Applications', value: '83', change: 'This month', trend: 'up', color: 'info', icon: TrendingUpIcon },
+ {
+ title: 'Total Jobs',
+ value: '142',
+ subtitle: '+12 this week',
+ color: 'primary',
+ icon: WorkIcon,
+ tooltip: 'Total job opportunities posted by alumni and partner companies. Updated regularly with new positions'
+ },
+ {
+ title: 'Companies',
+ value: '56',
+ subtitle: '+5 new',
+ color: 'success',
+ icon: BusinessIcon,
+ tooltip: 'Number of companies hiring through our alumni network. Includes startups, MNCs, and local organizations'
+ },
+ {
+ title: 'Applications',
+ value: '83',
+ subtitle: 'This month',
+ color: 'info',
+ icon: TrendingUpIcon,
+ tooltip: 'Total job applications submitted by students and alumni this month. Track your application status in real-time'
+ },
];
const filteredJobs = jobs.filter(job => {
diff --git a/src/pages/Alumni/Mentorship.jsx b/src/pages/Alumni/Mentorship.jsx
index ca15200..10b321d 100644
--- a/src/pages/Alumni/Mentorship.jsx
+++ b/src/pages/Alumni/Mentorship.jsx
@@ -133,9 +133,30 @@ const Mentorship = () => {
];
const stats = [
- { label: 'Active Mentors', value: '48', change: '+6 this month', trend: 'up', color: 'primary', icon: SchoolIcon },
- { label: 'Mentees', value: '156', change: '+23 new', trend: 'up', color: 'success', icon: GroupsIcon },
- { label: 'Sessions', value: '324', change: 'This year', trend: 'up', color: 'info', icon: TrophyIcon },
+ {
+ title: 'Active Mentors',
+ value: '48',
+ subtitle: '+6 this month',
+ color: 'primary',
+ icon: SchoolIcon,
+ tooltip: 'Experienced alumni available for mentorship. Industry experts from Google, Meta, Microsoft, and leading companies'
+ },
+ {
+ title: 'Mentees',
+ value: '156',
+ subtitle: '+23 new',
+ color: 'success',
+ icon: GroupsIcon,
+ tooltip: 'Students currently enrolled in mentorship programs. Get guidance on career planning, skills, and industry insights'
+ },
+ {
+ title: 'Sessions',
+ value: '324',
+ subtitle: 'This year',
+ color: 'info',
+ icon: TrophyIcon,
+ tooltip: 'Total mentorship sessions completed this year. One-on-one guidance covering career, skills, and personal growth'
+ },
];
const filteredMentors = mentors.filter(mentor => {
diff --git a/src/pages/Alumni/SuccessStories.jsx b/src/pages/Alumni/SuccessStories.jsx
index 32a44a7..1c5f766 100644
--- a/src/pages/Alumni/SuccessStories.jsx
+++ b/src/pages/Alumni/SuccessStories.jsx
@@ -145,9 +145,30 @@ const SuccessStories = () => {
];
const stats = [
- { label: 'Success Stories', value: '78', change: '+12 new', trend: 'up', color: 'primary', icon: TrophyIcon },
- { label: 'Total Views', value: '12.5K', change: '+2.3K this month', trend: 'up', color: 'success', icon: TrendingUpIcon },
- { label: 'Inspirations', value: '1.2K', change: 'Total likes', trend: 'up', color: 'info', icon: StarIcon },
+ {
+ title: 'Success Stories',
+ value: '78',
+ subtitle: '+12 new',
+ color: 'primary',
+ icon: TrophyIcon,
+ tooltip: 'Inspiring achievements by our alumni. Stories include entrepreneurship, research breakthroughs, and leadership roles'
+ },
+ {
+ title: 'Total Views',
+ value: '12.5K',
+ subtitle: '+2.3K this month',
+ color: 'success',
+ icon: TrendingUpIcon,
+ tooltip: 'Combined views across all success stories. These stories inspire and guide current students in their career paths'
+ },
+ {
+ title: 'Inspirations',
+ value: '1.2K',
+ subtitle: 'Total likes',
+ color: 'info',
+ icon: StarIcon,
+ tooltip: 'Total appreciation from community. React to stories that resonate with you and share with fellow students'
+ },
];
const filteredStories = stories.filter(story => {
diff --git a/src/pages/Auth/Login.jsx b/src/pages/Auth/Login.jsx
index 06358c1..dfd4447 100644
--- a/src/pages/Auth/Login.jsx
+++ b/src/pages/Auth/Login.jsx
@@ -11,6 +11,12 @@ import {
Paper,
Chip,
Stack,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ ListItemIcon,
+ ListItemText,
} from '@mui/material';
import {
School,
@@ -20,6 +26,7 @@ import {
AdminPanelSettings,
LocalLibrary,
People,
+ KeyboardArrowDown,
} from '@mui/icons-material';
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
@@ -129,10 +136,10 @@ const Login = () => {
-
+
Welcome Back!
-
+
Sign in to continue to your account
@@ -143,76 +150,157 @@ const Login = () => {
)}
diff --git a/src/pages/Finance/FeeVouchers.jsx b/src/pages/Finance/FeeVouchers.jsx
index 65d69fd..644909c 100644
--- a/src/pages/Finance/FeeVouchers.jsx
+++ b/src/pages/Finance/FeeVouchers.jsx
@@ -273,6 +273,7 @@ const FeeVouchers = () => {
value={`PKR ${totalFees.toLocaleString()}`}
icon={AttachMoney}
color="primary"
+ tooltip="Total fee amount for the current academic year including tuition, lab fees, and other charges."
/>
@@ -282,6 +283,7 @@ const FeeVouchers = () => {
icon={CheckCircle}
color="success"
subtitle={`${feeInvoices.filter(i => i.status === 'Paid').length} invoices`}
+ tooltip="Total amount you have successfully paid. Keep all payment receipts for your records."
/>
@@ -291,6 +293,7 @@ const FeeVouchers = () => {
icon={Warning}
color="error"
subtitle={`${feeInvoices.filter(i => i.status !== 'Paid').length} unpaid`}
+ tooltip="Pending fee amount that must be paid by the due date. Late payment may result in penalties."
/>
@@ -299,6 +302,7 @@ const FeeVouchers = () => {
value={`PKR ${upcomingFees.toLocaleString()}`}
icon={AccessTime}
color="warning"
+ tooltip="Fee invoices that will be due within the next 30 days. Plan your payments in advance."
/>
diff --git a/src/pages/Grievances/Grievances.jsx b/src/pages/Grievances/Grievances.jsx
index afdca83..3c52dc3 100644
--- a/src/pages/Grievances/Grievances.jsx
+++ b/src/pages/Grievances/Grievances.jsx
@@ -43,6 +43,7 @@ import {
} from '@mui/icons-material';
import PageTransition from '../../components/Common/PageTransition';
import EmptyState from '../../components/Common/EmptyState';
+import StatCard from '../../components/Common/StatCard';
import FileDropzone from '../../components/Forms/FileDropzone';
import { grievances, submitGrievance } from '../../data/dummyData';
import { useSnackbar } from '../../contexts/SnackbarContext';
@@ -165,127 +166,43 @@ const Grievances = () => {
animate="animate"
>
-
-
-
-
-
- {stats.total}
-
-
- Total Submitted
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.pending}
-
-
- Pending
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.resolved}
-
-
- Resolved
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.avgResolutionTime}
-
-
- Avg Resolution
-
-
-
-
-
-
-
-
+
diff --git a/src/pages/Library/BookManagement.jsx b/src/pages/Library/BookManagement.jsx
index 3024731..434babb 100644
--- a/src/pages/Library/BookManagement.jsx
+++ b/src/pages/Library/BookManagement.jsx
@@ -72,9 +72,30 @@ const BookManagement = () => {
]);
const stats = [
- { label: 'Total Books', value: books.reduce((sum, b) => sum + b.totalCopies, 0).toString(), change: '+45 new this month', trend: 'up', color: 'primary', icon: BookIcon },
- { label: 'Available', value: books.reduce((sum, b) => sum + b.availableCopies, 0).toString(), change: 'Ready to issue', trend: 'up', color: 'success', icon: InventoryIcon },
- { label: 'Categories', value: new Set(books.map(b => b.category)).size.toString(), change: 'Across library', trend: 'up', color: 'info', icon: CategoryIcon },
+ {
+ title: 'Total Books',
+ value: books.reduce((sum, b) => sum + b.totalCopies, 0).toString(),
+ subtitle: '+45 new this month',
+ color: 'primary',
+ icon: BookIcon,
+ tooltip: 'Total number of physical book copies across all titles and categories in the library catalog'
+ },
+ {
+ title: 'Available',
+ value: books.reduce((sum, b) => sum + b.availableCopies, 0).toString(),
+ subtitle: 'Ready to issue',
+ color: 'success',
+ icon: InventoryIcon,
+ tooltip: 'Books currently available on shelves and ready to be issued to students immediately'
+ },
+ {
+ title: 'Categories',
+ value: new Set(books.map(b => b.category)).size.toString(),
+ subtitle: 'Across library',
+ color: 'info',
+ icon: CategoryIcon,
+ tooltip: 'Number of different book categories including Computer Science, Business, Engineering, and more'
+ },
];
const handleOpenDialog = (book = null) => {
diff --git a/src/pages/Library/IssuedBooks.jsx b/src/pages/Library/IssuedBooks.jsx
index 96825ec..dbe1b14 100644
--- a/src/pages/Library/IssuedBooks.jsx
+++ b/src/pages/Library/IssuedBooks.jsx
@@ -115,28 +115,28 @@ const IssuedBooks = () => {
const stats = [
{
- label: 'Currently Issued',
+ title: 'Currently Issued',
value: issuedBooks.filter(b => b.status === 'issued').length.toString(),
- change: 'Active borrows',
- trend: 'up',
+ subtitle: 'Active borrows',
color: 'primary',
icon: IssuedIcon,
+ tooltip: 'Books currently issued to students with active due dates. Students can keep books for 14 days maximum',
},
{
- label: 'Overdue Books',
+ title: 'Overdue Books',
value: issuedBooks.filter(b => b.status === 'overdue').length.toString(),
- change: 'Need attention',
- trend: 'up',
+ subtitle: 'Need attention',
color: 'error',
icon: WarningIcon,
+ tooltip: 'Books past their due date that need immediate return. Automated reminders are sent to students daily',
},
{
- label: 'Total Issued',
+ title: 'Total Issued',
value: issuedBooks.length.toString(),
- change: 'This month',
- trend: 'up',
+ subtitle: 'This month',
color: 'success',
icon: BookIcon,
+ tooltip: 'Total number of books issued this month. Includes both active and overdue books for circulation tracking',
},
];
diff --git a/src/pages/Library/LibrarianDashboard.jsx b/src/pages/Library/LibrarianDashboard.jsx
index 0a5baae..ac81e7f 100644
--- a/src/pages/Library/LibrarianDashboard.jsx
+++ b/src/pages/Library/LibrarianDashboard.jsx
@@ -49,6 +49,7 @@ import {
} from '@mui/icons-material';
import PageTransition from '../../components/Common/PageTransition';
import { TableSkeleton } from '../../components/Common/LoadingSkeleton';
+import StatCard from '../../components/Common/StatCard';
import { useSnackbar } from '../../contexts/SnackbarContext';
import {
libraryBooks,
@@ -218,7 +219,7 @@ const LibrarianDashboard = () => {
- {/* STAT CARDS - 4 LARGE CARDS */}
+ {/* STAT CARDS */}
{
animate="animate"
>
-
-
-
-
-
-
-
-
- {books.length}
-
-
- Total Books
-
-
-
-
-
+
-
-
-
-
-
-
-
-
- {stats.issuedToday}
-
-
- Issued Today
-
-
-
-
-
+
-
-
-
-
-
-
-
-
- {stats.overdue}
-
-
- Overdue
-
-
-
-
-
+
-
-
-
-
-
-
-
-
- ₨{stats.fines}
-
-
- Total Fines
-
-
-
-
-
+
diff --git a/src/pages/Library/LibrarianGrievances.jsx b/src/pages/Library/LibrarianGrievances.jsx
index d250b82..0e5fd00 100644
--- a/src/pages/Library/LibrarianGrievances.jsx
+++ b/src/pages/Library/LibrarianGrievances.jsx
@@ -138,36 +138,36 @@ const LibrarianGrievances = () => {
const stats = [
{
- label: 'Total Library Complaints',
+ title: 'Total Library Complaints',
value: grievances.length.toString(),
- change: 'This month',
- trend: 'up',
+ subtitle: 'This month',
color: 'primary',
icon: LibraryIcon,
+ tooltip: 'All library-related grievances submitted this month including book issues, facility problems, and system errors',
},
{
- label: 'Pending',
+ title: 'Pending',
value: grievances.filter(g => g.status === 'Pending').length.toString(),
- change: 'Need attention',
- trend: 'up',
+ subtitle: 'Need attention',
color: 'warning',
icon: PendingIcon,
+ tooltip: 'Grievances awaiting initial review and assignment. These require immediate attention from library staff',
},
{
- label: 'In Progress',
+ title: 'In Progress',
value: grievances.filter(g => g.status === 'In Progress').length.toString(),
- change: 'Being handled',
- trend: 'up',
+ subtitle: 'Being handled',
color: 'info',
icon: SupportIcon,
+ tooltip: 'Grievances currently being addressed by the library team. Students will receive updates as progress is made',
},
{
- label: 'Resolved',
+ title: 'Resolved',
value: grievances.filter(g => g.status === 'Resolved').length.toString(),
- change: 'This month',
- trend: 'up',
+ subtitle: 'This month',
color: 'success',
icon: CheckCircleIcon,
+ tooltip: 'Successfully resolved grievances this month. Resolution details and actions taken are documented for each case',
},
];
diff --git a/src/pages/Library/LibrarianReports.jsx b/src/pages/Library/LibrarianReports.jsx
index e532480..4c590ed 100644
--- a/src/pages/Library/LibrarianReports.jsx
+++ b/src/pages/Library/LibrarianReports.jsx
@@ -37,10 +37,38 @@ const LibrarianReports = () => {
// Mock data
const stats = [
- { label: 'Total Books', value: '2,847', change: '+145 this year', trend: 'up', color: 'primary', icon: BookIcon },
- { label: 'Active Members', value: '1,234', change: '+56 this month', trend: 'up', color: 'success', icon: PeopleIcon },
- { label: 'Books Issued', value: '342', change: 'This month', trend: 'up', color: 'info', icon: InventoryIcon },
- { label: 'Categories', value: '18', change: 'Across library', trend: 'up', color: 'warning', icon: CategoryIcon },
+ {
+ title: 'Total Books',
+ value: '2,847',
+ subtitle: '+145 this year',
+ color: 'primary',
+ icon: BookIcon,
+ tooltip: 'Complete library catalog size including all book copies across all categories and subjects'
+ },
+ {
+ title: 'Active Members',
+ value: '1,234',
+ subtitle: '+56 this month',
+ color: 'success',
+ icon: PeopleIcon,
+ tooltip: 'Total registered library members including students, faculty, and staff with active borrowing privileges'
+ },
+ {
+ title: 'Books Issued',
+ value: '342',
+ subtitle: 'This month',
+ color: 'info',
+ icon: InventoryIcon,
+ tooltip: 'Number of books currently issued this month. Helps analyze circulation trends and popular titles'
+ },
+ {
+ title: 'Categories',
+ value: '18',
+ subtitle: 'Across library',
+ color: 'warning',
+ icon: CategoryIcon,
+ tooltip: 'Book categories available including Computer Science, Business, Engineering, Mathematics, and more'
+ },
];
// Category distribution data
diff --git a/src/pages/Library/Library.jsx b/src/pages/Library/Library.jsx
index 68e8e8f..0f08af5 100644
--- a/src/pages/Library/Library.jsx
+++ b/src/pages/Library/Library.jsx
@@ -34,6 +34,7 @@ import {
} from '@mui/icons-material';
import PageTransition from '../../components/Common/PageTransition';
import EmptyState from '../../components/Common/EmptyState';
+import StatCard from '../../components/Common/StatCard';
import { CardSkeleton } from '../../components/Common/LoadingSkeleton';
import { useSnackbar } from '../../contexts/SnackbarContext';
import {
@@ -172,127 +173,44 @@ const Library = () => {
animate="animate"
>
-
-
-
-
-
- {libraryBooks.length}
-
-
- Total Books
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {libraryBooks.filter(b => b.availableCopies > 0).length}
-
-
- Available Now
-
-
-
-
-
-
-
-
+ b.availableCopies > 0).length}
+ icon={CheckCircleIcon}
+ color="success"
+ tooltip="Books that are currently available for issue or reservation."
+ />
-
-
-
-
-
- {myIssuedBooks.length}
-
-
- Issued to You
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {myReservedBooks.length}
-
-
- Reserved
-
-
-
-
-
-
-
-
+
diff --git a/src/pages/Library/Reservations.jsx b/src/pages/Library/Reservations.jsx
index 4570ed1..c308a73 100644
--- a/src/pages/Library/Reservations.jsx
+++ b/src/pages/Library/Reservations.jsx
@@ -114,28 +114,28 @@ const Reservations = () => {
const stats = [
{
- label: 'Pending Reservations',
+ title: 'Pending Reservations',
value: reservations.filter((r) => r.status === 'pending').length.toString(),
- change: 'Need approval',
- trend: 'up',
+ subtitle: 'Need approval',
color: 'warning',
icon: PendingIcon,
+ tooltip: 'Book reservations awaiting librarian approval. Students will be notified once approved',
},
{
- label: 'Approved',
+ title: 'Approved',
value: reservations.filter((r) => r.status === 'approved').length.toString(),
- change: 'Ready to issue',
- trend: 'up',
+ subtitle: 'Ready to issue',
color: 'success',
icon: EventIcon,
+ tooltip: 'Approved reservations ready to be issued when books become available',
},
{
- label: 'Total Reservations',
+ title: 'Total Reservations',
value: reservations.length.toString(),
- change: 'This month',
- trend: 'up',
+ subtitle: 'This month',
color: 'primary',
icon: BookIcon,
+ tooltip: 'Total book reservations this month including pending, approved, and cancelled',
},
];
diff --git a/src/pages/Student/Dashboard.jsx b/src/pages/Student/Dashboard.jsx
index 2cf6a3e..4f54f2e 100644
--- a/src/pages/Student/Dashboard.jsx
+++ b/src/pages/Student/Dashboard.jsx
@@ -243,52 +243,20 @@ const Dashboard = () => {
color="primary"
trend={{ direction: 'up', value: '+0.05' }}
subtitle="Last Semester: 3.92"
+ tooltip="Cumulative Grade Point Average across all completed semesters. Higher CGPA indicates better overall academic performance."
loading={loading}
/>
-
-
-
-
-
- {attendanceStats.percentage}%
-
-
- Attendance
-
-
-
-
- {attendanceStats.attended}/{attendanceStats.totalClasses} classes
-
-
-
-
-
-
-
-
-
+
{
icon={AssignmentIcon}
color="warning"
subtitle={`${assignments.length} total assignments`}
+ tooltip="Number of assignments pending submission. Complete them before the deadline to avoid penalties."
loading={loading}
/>
@@ -307,7 +276,9 @@ const Dashboard = () => {
icon={PaymentIcon}
color={unpaidFees.length > 0 ? 'error' : 'success'}
subtitle={unpaidFees.length > 0 ? `${unpaidFees.length} invoice(s) due` : 'No pending fees'}
+ tooltip={unpaidFees.length > 0 ? 'Outstanding fee amount that needs to be paid. Click to view fee vouchers.' : 'All your fees are paid up to date!'}
loading={loading}
+ onClick={() => navigate('/finance/fee-vouchers')}
/>
diff --git a/src/pages/Student/MyAssignments.jsx b/src/pages/Student/MyAssignments.jsx
index 6c719d9..30c124b 100644
--- a/src/pages/Student/MyAssignments.jsx
+++ b/src/pages/Student/MyAssignments.jsx
@@ -23,6 +23,7 @@ import {
ArrowForward,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { fadeInUp, staggerContainer } from '../../utils/animations';
const MyAssignments = () => {
@@ -137,127 +138,43 @@ const MyAssignments = () => {
animate="animate"
>
-
-
-
-
-
- {stats.total}
-
-
- Total
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.pending}
-
-
- Pending
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.submitted}
-
-
- Submitted
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.graded}
-
-
- Graded
-
-
-
-
-
-
-
-
+
diff --git a/src/pages/Student/MyTickets.jsx b/src/pages/Student/MyTickets.jsx
index 080e082..9686f5f 100644
--- a/src/pages/Student/MyTickets.jsx
+++ b/src/pages/Student/MyTickets.jsx
@@ -32,6 +32,7 @@ import {
Send,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { fadeInUp, staggerContainer } from '../../utils/animations';
const MyTickets = () => {
@@ -174,130 +175,48 @@ const MyTickets = () => {
animate="animate"
>
-
-
-
-
-
- {stats.total}
-
-
- Total Tickets
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.inProgress}
-
-
- In Progress
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.resolved}
-
-
- Resolved
-
-
-
-
-
-
-
-
+
-
-
-
-
-
- {stats.rejected}
-
-
- Rejected
-
-
-
-
-
-
-
-
+
+ {/* TICKETS LIST */}
+
{/* TICKETS LIST */}
{
icon={TrendingUp}
color="primary"
subtitle="Out of 4.0"
+ tooltip="Your Cumulative Grade Point Average across all completed semesters. This reflects your overall academic performance throughout your degree program."
/>
@@ -730,6 +731,7 @@ const Profile = () => {
icon={School}
color="success"
subtitle="Credits earned"
+ tooltip="Total credit hours earned out of required credits for your degree. Each course has credit hours based on its workload and duration."
/>
@@ -739,6 +741,7 @@ const Profile = () => {
icon={CalendarMonth}
color="warning"
subtitle="Completed"
+ tooltip="Number of semesters completed out of total required semesters for your degree program. Most undergraduate programs require 8 semesters (4 years)."
/>
@@ -748,6 +751,7 @@ const Profile = () => {
icon={School}
color="info"
subtitle="Enrolled"
+ tooltip="Number of courses you are currently enrolled in this semester. Each course contributes to your semester GPA and overall CGPA."
/>
diff --git a/src/pages/Student/Transcript.jsx b/src/pages/Student/Transcript.jsx
index 6367601..37d626a 100644
--- a/src/pages/Student/Transcript.jsx
+++ b/src/pages/Student/Transcript.jsx
@@ -15,10 +15,11 @@ import {
Divider,
} from '@mui/material';
import Grid from '@mui/material/Grid';
-import { Download, School } from '@mui/icons-material';
+import { Download, School, TrendingUp, Stars, AssignmentTurnedIn, EmojiEvents } from '@mui/icons-material';
import { useAuth } from '../../contexts/AuthContext';
import { transcript } from '../../data/dummyData';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { Button } from '@mui/material';
import { TableSkeleton } from '../../components/Common/LoadingSkeleton';
import { pageTransition } from '../../utils/animations';
@@ -64,6 +65,50 @@ const Transcript = () => {
}
/>
+ {/* Academic Stats */}
+
+
+
+
+
+
+
+
+
+
+
+ parseFloat(s.semesterGPA))).toFixed(2)}
+ icon={EmojiEvents}
+ color="warning"
+ subtitle="Best semester"
+ tooltip="Your highest semester GPA achieved during your academic journey."
+ />
+
+
+
{/* Student Info Card */}
diff --git a/src/pages/Teacher/Assignments.jsx b/src/pages/Teacher/Assignments.jsx
index 803f4f7..d14e81b 100644
--- a/src/pages/Teacher/Assignments.jsx
+++ b/src/pages/Teacher/Assignments.jsx
@@ -33,6 +33,7 @@ import {
Grade,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { fadeInUp, staggerContainer } from '../../utils/animations';
const Assignments = () => {
@@ -48,24 +49,28 @@ const Assignments = () => {
value: '24',
icon: AssignmentIcon,
color: 'primary.main',
+ tooltip: 'Total assignments created across all your courses. Manage and track assignment submissions',
},
{
title: 'Active',
value: '8',
icon: Schedule,
color: 'success.main',
+ tooltip: 'Assignments currently open for student submissions. Students can submit before due date',
},
{
title: 'Pending Review',
value: '45',
icon: PendingActions,
color: 'warning.main',
+ tooltip: 'Total submissions across all assignments awaiting your grading and feedback',
},
{
title: 'Graded',
value: '186',
icon: Grade,
color: 'info.main',
+ tooltip: 'Submissions that have been graded. Students can view their marks and feedback',
},
];
@@ -404,34 +409,13 @@ const Assignments = () => {
>
{stats.map((stat, index) => (
-
-
-
-
-
- {stat.value}
-
-
- {stat.title}
-
-
-
-
-
-
-
-
+
))}
diff --git a/src/pages/Teacher/Dashboard.jsx b/src/pages/Teacher/Dashboard.jsx
index 6d68db4..35a8242 100644
--- a/src/pages/Teacher/Dashboard.jsx
+++ b/src/pages/Teacher/Dashboard.jsx
@@ -40,34 +40,34 @@ const TeacherDashboard = () => {
{
title: 'My Courses',
value: '5',
- change: '+1 new',
- trend: 'up',
+ subtitle: '+1 new',
icon: School,
color: 'primary',
+ tooltip: 'Total courses you are teaching this semester. Manage course content, assignments, and student performance',
},
{
title: 'Total Students',
value: '312',
- change: '+18 this sem',
- trend: 'up',
+ subtitle: '+18 this sem',
icon: People,
color: 'success',
+ tooltip: 'Total students enrolled across all your courses. View individual student profiles and track their progress',
},
{
title: 'Pending Assignments',
value: '23',
- change: '5 overdue',
- trend: 'down',
+ subtitle: '5 overdue',
icon: Assignment,
color: 'warning',
+ tooltip: 'Assignments awaiting grading. 5 submissions are past the grading deadline and need immediate attention',
},
{
title: 'Attendance Rate',
value: '87%',
- change: '+2.3%',
- trend: 'up',
+ subtitle: '+2.3%',
icon: CheckCircle,
color: 'info',
+ tooltip: 'Average attendance rate across all your courses. Improved by 2.3% compared to last month',
},
];
@@ -155,10 +155,10 @@ const TeacherDashboard = () => {
))}
diff --git a/src/pages/Teacher/GrievanceManagement.jsx b/src/pages/Teacher/GrievanceManagement.jsx
index 17a550f..4481aa1 100644
--- a/src/pages/Teacher/GrievanceManagement.jsx
+++ b/src/pages/Teacher/GrievanceManagement.jsx
@@ -146,36 +146,36 @@ const TeacherGrievanceManagement = () => {
const stats = [
{
- label: 'Total Referred',
+ title: 'Total Referred',
value: grievances.length.toString(),
- change: 'By admin',
- trend: 'up',
+ subtitle: 'By admin',
color: 'primary',
icon: SupportIcon,
+ tooltip: 'Academic grievances referred to you by administration. These require your review and response',
},
{
- label: 'Pending Review',
+ title: 'Pending Review',
value: grievances.filter(g => g.status === 'Pending Review').length.toString(),
- change: 'Need attention',
- trend: 'up',
+ subtitle: 'Need attention',
color: 'warning',
icon: PendingIcon,
+ tooltip: 'Grievances awaiting your initial review. Please review and provide your response or action plan',
},
{
- label: 'Under Review',
+ title: 'Under Review',
value: grievances.filter(g => g.status === 'Under Review').length.toString(),
- change: 'In progress',
- trend: 'up',
+ subtitle: 'In progress',
color: 'info',
icon: AcademicIcon,
+ tooltip: 'Grievances you are currently addressing. Students will receive updates as you work on resolution',
},
{
- label: 'Resolved',
+ title: 'Resolved',
value: grievances.filter(g => g.status === 'Resolved').length.toString(),
- change: 'This month',
- trend: 'up',
+ subtitle: 'This month',
color: 'success',
icon: CheckCircleIcon,
+ tooltip: 'Successfully resolved grievances this month. Resolution details documented for future reference',
},
];
diff --git a/src/pages/Teacher/Quizzes.jsx b/src/pages/Teacher/Quizzes.jsx
index e89eed2..d7862ef 100644
--- a/src/pages/Teacher/Quizzes.jsx
+++ b/src/pages/Teacher/Quizzes.jsx
@@ -38,6 +38,7 @@ import {
TrendingUp,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { fadeInUp, staggerContainer } from '../../utils/animations';
const Quizzes = () => {
@@ -54,24 +55,28 @@ const Quizzes = () => {
value: '18',
icon: QuizIcon,
color: 'primary.main',
+ tooltip: 'Total quizzes created for all your courses. Includes active, draft, and completed quizzes',
},
{
title: 'Active Quizzes',
value: '5',
icon: Schedule,
color: 'success.main',
+ tooltip: 'Quizzes currently open for student attempts. Students can access and submit these quizzes',
},
{
title: 'Total Attempts',
value: '432',
icon: People,
color: 'info.main',
+ tooltip: 'Total quiz attempts by students across all quizzes. View individual student performance and analytics',
},
{
title: 'Avg Score',
value: '78%',
icon: TrendingUp,
color: 'warning.main',
+ tooltip: 'Average score across all quiz attempts. Helps measure overall class understanding and quiz difficulty',
},
];
@@ -340,34 +345,13 @@ const Quizzes = () => {
>
{stats.map((stat, index) => (
-
-
-
-
-
- {stat.value}
-
-
- {stat.title}
-
-
-
-
-
-
-
-
+
))}
diff --git a/src/pages/Teacher/Reports.jsx b/src/pages/Teacher/Reports.jsx
index 5423e78..28975aa 100644
--- a/src/pages/Teacher/Reports.jsx
+++ b/src/pages/Teacher/Reports.jsx
@@ -42,6 +42,7 @@ import {
Print,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import PageTransition from '../../components/Common/PageTransition';
import { fadeInUp, staggerContainer } from '../../utils/animations';
@@ -77,34 +78,34 @@ const Reports = () => {
{
title: 'Total Students',
value: '85',
- change: '+5 this month',
- trend: 'up',
+ subtitle: '+5 this month',
icon: People,
color: theme.palette.primary.main,
+ tooltip: 'Total students enrolled in this course. Track individual performance and generate detailed reports',
},
{
title: 'Avg Attendance',
value: '88.5%',
- change: '+2.3%',
- trend: 'up',
+ subtitle: '+2.3%',
icon: CheckCircle,
color: theme.palette.success.main,
+ tooltip: 'Average attendance rate for this course. Shows improvement of 2.3% compared to previous period',
},
{
title: 'Assignment Completion',
value: '72%',
- change: '-5.2%',
- trend: 'down',
+ subtitle: '-5.2%',
icon: Assignment,
color: theme.palette.warning.main,
+ tooltip: 'Percentage of students who submitted assignments on time. Decreased by 5.2%, may need follow-up',
},
{
title: 'Class Average',
value: 'B+',
- change: '+0.2 GPA',
- trend: 'up',
+ subtitle: '+0.2 GPA',
icon: School,
color: theme.palette.info.main,
+ tooltip: 'Average grade for the class. Shows improvement of 0.2 GPA points compared to previous assessment',
},
];
@@ -130,41 +131,14 @@ const Reports = () => {
>
{stats.map((stat, index) => (
-
-
-
-
-
- {stat.value}
-
-
- {stat.title}
-
- : }
- label={stat.change}
- size="small"
- color={stat.trend === 'up' ? 'success' : 'error'}
- sx={{ mt: 1 }}
- />
-
-
-
-
-
-
-
+
))}
diff --git a/src/pages/Teacher/StudentManagement.jsx b/src/pages/Teacher/StudentManagement.jsx
index b889937..ba0a155 100644
--- a/src/pages/Teacher/StudentManagement.jsx
+++ b/src/pages/Teacher/StudentManagement.jsx
@@ -46,6 +46,7 @@ import {
} from '@mui/icons-material';
import { useTheme } from '@mui/material/styles';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { useSnackbar } from '../../contexts/SnackbarContext';
import { pageTransition } from '../../utils/animations';
@@ -194,52 +195,40 @@ const StudentManagement = () => {
{/* Stats */}
-
-
-
- Total Students
-
-
- {students.length}
-
-
-
+
-
-
-
- Avg Attendance
-
-
- {Math.round(students.reduce((sum, s) => sum + s.attendance, 0) / students.length)}%
-
-
-
+ sum + s.attendance, 0) / students.length)}%`}
+ icon={CheckCircle}
+ color="success"
+ tooltip="Average attendance rate across all students. Monitor attendance patterns and identify issues"
+ />
-
-
-
- Assignments Pending
-
-
- {students.reduce((sum, s) => sum + (s.assignments.total - s.assignments.submitted), 0)}
-
-
-
+ sum + (s.assignments.total - s.assignments.submitted), 0)}
+ icon={Assignment}
+ color="warning"
+ tooltip="Total pending assignments across all students. Follow up with students for timely submissions"
+ />
-
-
-
- At Risk Students
-
-
- {students.filter((s) => s.status === 'warning').length}
-
-
-
+ s.status === 'warning').length}
+ icon={TrendingDown}
+ color="error"
+ tooltip="Students with low attendance or poor performance. These students need immediate attention and support"
+ />
diff --git a/src/pages/Teacher/TeacherAttendance.jsx b/src/pages/Teacher/TeacherAttendance.jsx
index 6dce7ab..fd8c2b5 100644
--- a/src/pages/Teacher/TeacherAttendance.jsx
+++ b/src/pages/Teacher/TeacherAttendance.jsx
@@ -38,6 +38,7 @@ import {
People,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { fadeInUp, staggerContainer } from '../../utils/animations';
const TeacherAttendance = () => {
@@ -59,30 +60,31 @@ const TeacherAttendance = () => {
value: '85',
icon: People,
color: 'primary.main',
+ tooltip: 'Total students enrolled in the selected course. View individual attendance records and trends',
},
{
title: 'Present Today',
value: '78',
- change: '+5',
- trend: 'up',
+ subtitle: '+5',
icon: CheckCircle,
color: 'success.main',
+ tooltip: 'Number of students marked present today. 5 more students attended compared to last class',
},
{
title: 'Absent Today',
value: '7',
- change: '-2',
- trend: 'down',
+ subtitle: '-2',
icon: Cancel,
color: 'error.main',
+ tooltip: 'Number of students marked absent today. 2 fewer absences compared to last class',
},
{
title: 'Attendance Rate',
value: '91.8%',
- change: '+2.3%',
- trend: 'up',
+ subtitle: '+2.3%',
icon: TrendingUp,
color: 'info.main',
+ tooltip: 'Overall attendance rate for this course. Improved by 2.3% compared to previous tracking period',
},
];
@@ -191,43 +193,14 @@ const TeacherAttendance = () => {
>
{stats.map((stat, index) => (
-
-
-
-
-
- {stat.value}
-
-
- {stat.title}
-
- {stat.change && (
- : }
- label={stat.change}
- size="small"
- color={stat.trend === 'up' ? 'success' : 'error'}
- sx={{ mt: 0.5 }}
- />
- )}
-
-
-
-
-
-
-
+
))}
diff --git a/src/pages/Teacher/ViewSubmissions.jsx b/src/pages/Teacher/ViewSubmissions.jsx
index 42fc01d..ed9b9eb 100644
--- a/src/pages/Teacher/ViewSubmissions.jsx
+++ b/src/pages/Teacher/ViewSubmissions.jsx
@@ -39,6 +39,7 @@ import {
Send,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
import { fadeInUp, staggerContainer } from '../../utils/animations';
const ViewSubmissions = () => {
@@ -174,119 +175,43 @@ const ViewSubmissions = () => {
animate="animate"
>
-
-
-
-
-
-
- {stats.total}
- Submissions
-
-
-
+
-
-
-
-
-
-
- {stats.pending}
- Pending
-
-
-
+
-
-
-
-
-
-
- {stats.graded}
- Graded
-
-
-
+
-
-
-
-
-
-
- {stats.notSubmitted}
- Not Submitted
-
-
-
+
diff --git a/vite.config.js b/vite.config.js
index f47b3da..ef9ea04 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,5 +4,5 @@ import react from '@vitejs/plugin-react-swc'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
- base: '/PROJECT_NEXUS/',
+ base: process.env.NODE_ENV === 'production' ? '/PROJECT_NEXUS/' : '/',
})
From a07602c08d7b19fa7ed76dac1c9e64d9437c5d0e Mon Sep 17 00:00:00 2001
From: Muhammad Asad <148639150+ASAD2204@users.noreply.github.com>
Date: Mon, 26 Jan 2026 14:37:34 +0500
Subject: [PATCH 6/7] cards,graphs,charts,sidebar,form,profiles updated
---
src/App.jsx | 13 +-
src/components/Common/SplashScreen.jsx | 375 +++++++-------
src/components/Common/StatCard.jsx | 33 +-
src/components/Layout/Sidebar.jsx | 213 ++++++--
src/components/Layout/TopBar.jsx | 276 +++++++++--
src/pages/Admin/CourseManagement.jsx | 128 ++++-
src/pages/Admin/Profile.jsx | 627 +++++++++++++++++++++++
src/pages/Admin/Reports.jsx | 265 +++++++++-
src/pages/Admin/UserManagement.jsx | 373 +++++++++++++-
src/pages/Alumni/Profile.jsx | 657 +++++++++++++++++++++++++
src/pages/Chat/ChatPortal.jsx | 27 +-
src/pages/LMS/CourseClassroom.jsx | 11 +-
src/pages/Library/LibrarianReports.jsx | 4 +-
src/pages/Library/Profile.jsx | 573 +++++++++++++++++++++
src/pages/Student/Dashboard.jsx | 30 +-
src/pages/Teacher/CreateAssignment.jsx | 55 ++-
src/pages/Teacher/Profile.jsx | 639 ++++++++++++++++++++++++
src/pages/Teacher/Reports.jsx | 353 ++++++++++++-
18 files changed, 4212 insertions(+), 440 deletions(-)
create mode 100644 src/pages/Admin/Profile.jsx
create mode 100644 src/pages/Alumni/Profile.jsx
create mode 100644 src/pages/Library/Profile.jsx
create mode 100644 src/pages/Teacher/Profile.jsx
diff --git a/src/App.jsx b/src/App.jsx
index 81a6767..5343821 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -15,7 +15,7 @@ import OTP from './pages/Auth/OTP';
// Student Pages
import Dashboard from './pages/Student/Dashboard';
-import Profile from './pages/Student/Profile';
+import StudentProfile from './pages/Student/Profile';
import Transcript from './pages/Student/Transcript';
import MyAssignments from './pages/Student/MyAssignments';
import MyTickets from './pages/Student/MyTickets';
@@ -24,6 +24,7 @@ import AlumniDirectory from './pages/Student/AlumniDirectory';
// Admin Pages
import AdminDashboard from './pages/Admin/Dashboard';
+import AdminProfile from './pages/Admin/Profile';
import UserManagement from './pages/Admin/UserManagement';
import AdminCourseManagement from './pages/Admin/CourseManagement';
import AdminReports from './pages/Admin/Reports';
@@ -35,6 +36,7 @@ import DepartmentManagement from './pages/Admin/DepartmentManagement';
// Teacher Pages
import TeacherDashboard from './pages/Teacher/Dashboard';
+import TeacherProfile from './pages/Teacher/Profile';
import TeacherCourses from './pages/Teacher/MyCourses';
import StudentManagement from './pages/Teacher/StudentManagement';
import TeacherReports from './pages/Teacher/Reports';
@@ -68,6 +70,7 @@ import ChatPortal from './pages/Chat/ChatPortal';
// Other Pages
import Library from './pages/Library/Library';
import LibrarianDashboard from './pages/Library/LibrarianDashboard';
+import LibrarianProfile from './pages/Library/Profile';
import BookManagement from './pages/Library/BookManagement';
import IssuedBooks from './pages/Library/IssuedBooks';
import Reservations from './pages/Library/Reservations';
@@ -75,6 +78,7 @@ import LibrarianReports from './pages/Library/LibrarianReports';
import LibrarianGrievances from './pages/Library/LibrarianGrievances';
import Grievances from './pages/Grievances/Grievances';
import AlumniNetwork from './pages/Alumni/AlumniNetwork';
+import AlumniProfile from './pages/Alumni/Profile';
import AlumniEvents from './pages/Alumni/AlumniEvents';
import JobBoard from './pages/Alumni/JobBoard';
import Mentorship from './pages/Alumni/Mentorship';
@@ -160,13 +164,15 @@ function App() {
{/* Student Routes */}
} />
- } />
+ } />
+ } />
} />
} />
} />
{/* Admin Routes */}
} />
+ } />
} />
} />
} />
@@ -178,6 +184,7 @@ function App() {
{/* Teacher Routes */}
} />
+ } />
} />
} />
} />
@@ -224,6 +231,7 @@ function App() {
{/* Library Routes */}
} />
} />
+ } />
} />
} />
} />
@@ -232,6 +240,7 @@ function App() {
{/* Alumni Routes */}
} />
+ } />
} />
} />
} />
diff --git a/src/components/Common/SplashScreen.jsx b/src/components/Common/SplashScreen.jsx
index 7427577..106e485 100644
--- a/src/components/Common/SplashScreen.jsx
+++ b/src/components/Common/SplashScreen.jsx
@@ -1,7 +1,8 @@
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
-import { Box, Typography, LinearProgress } from '@mui/material';
+import { Box, Typography, LinearProgress, alpha } from '@mui/material';
import { useTheme } from '@mui/material/styles';
+import { School, MenuBook, People, TrendingUp } from '@mui/icons-material';
const SplashScreen = ({ onComplete }) => {
const theme = useTheme();
@@ -12,23 +13,30 @@ const SplashScreen = ({ onComplete }) => {
setProgress((prev) => {
if (prev >= 100) {
clearInterval(timer);
- setTimeout(() => onComplete(), 800);
+ setTimeout(() => onComplete(), 500);
return 100;
}
- return prev + 1;
+ return prev + 2;
});
- }, 40);
+ }, 30);
return () => clearInterval(timer);
}, [onComplete]);
+ const bgGradient = theme.palette.mode === 'dark'
+ ? 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #334155 100%)'
+ : 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 50%, #cbd5e1 100%)';
+
+ const primaryColor = theme.palette.mode === 'dark' ? '#60a5fa' : '#3b82f6';
+ const secondaryColor = theme.palette.mode === 'dark' ? '#a78bfa' : '#8b5cf6';
+
return (
{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- background: theme.palette.mode === 'dark'
- ? 'linear-gradient(135deg, #1e3c72 0%, #2a5298 25%, #7e22ce 50%, #2a5298 75%, #1e3c72 100%)'
- : 'linear-gradient(135deg, #4facfe 0%, #00f2fe 25%, #667eea 50%, #00f2fe 75%, #4facfe 100%)',
+ background: bgGradient,
overflow: 'hidden',
}}
>
- {/* Animated Background Circles */}
+ {/* Animated Background Grid */}
- {[...Array(6)].map((_, i) => (
-
+
+ {/* Floating Icons */}
+ {[School, MenuBook, People, TrendingUp].map((Icon, i) => (
+
+
- ))}
-
+
+ ))}
{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
- gap: 6,
+ gap: 4,
position: 'relative',
zIndex: 1,
+ maxWidth: '90%',
}}
>
- {/* Animated Logo/Text */}
+ {/* Company Logo/Icon */}
-
- {/* Glow effect behind text */}
-
-
- Project Nexus
-
+ },
+ }}
+ >
+
- {/* Animated Subtitle */}
+ {/* Brand Name */}
+ Project Nexus
+
+
- The Unified Campus Management System
+ Campus Management System
- {/* Animated Floating Elements */}
-
- {[...Array(7)].map((_, i) => (
-
- ))}
-
-
- {/* Progress Bar */}
+ {/* Progress Section */}
-
+
+
+
- Loading {progress}%
+ Loading... {progress}%
- {/* Outer Rings */}
- {[1, 2, 3].map((ring) => (
-
- ))}
+ {/* Feature Pills */}
+
+
+ {['Smart', 'Secure', 'Efficient'].map((text, i) => (
+
+
+
+ {text}
+
+
+
+ ))}
+
+
diff --git a/src/components/Common/StatCard.jsx b/src/components/Common/StatCard.jsx
index 2b3cb6c..9f45be7 100644
--- a/src/components/Common/StatCard.jsx
+++ b/src/components/Common/StatCard.jsx
@@ -29,46 +29,21 @@ const StatCard = ({
onClick,
}) => {
const theme = useTheme();
- const [animatedValue, setAnimatedValue] = useState(0);
const [mounted, setMounted] = useState(false);
- // Animate value on mount
+ // Simple mount effect without animation
useEffect(() => {
setMounted(true);
-
- // Check if value is a number for animation
- const numericValue = typeof value === 'number' ? value : parseFloat(value);
-
- if (!isNaN(numericValue) && !loading) {
- const duration = 1000; // 1 second
- const steps = 60;
- const increment = numericValue / steps;
- let currentStep = 0;
-
- const timer = setInterval(() => {
- currentStep++;
- setAnimatedValue(Math.min(increment * currentStep, numericValue));
-
- if (currentStep >= steps) {
- clearInterval(timer);
- setAnimatedValue(numericValue);
- }
- }, duration / steps);
-
- return () => clearInterval(timer);
- } else if (!loading) {
- setAnimatedValue(value);
- }
- }, [value, loading]);
+ }, []);
// Format the displayed value
const displayValue = () => {
if (typeof value === 'number') {
// Preserve decimal places from original value
const decimalPlaces = value.toString().split('.')[1]?.length || 0;
- return animatedValue.toFixed(decimalPlaces);
+ return value.toFixed(decimalPlaces);
}
- return animatedValue;
+ return value;
};
// Determine trend color and icon
diff --git a/src/components/Layout/Sidebar.jsx b/src/components/Layout/Sidebar.jsx
index 235b7fc..9473f52 100644
--- a/src/components/Layout/Sidebar.jsx
+++ b/src/components/Layout/Sidebar.jsx
@@ -99,7 +99,7 @@ const studentMenuItems = [
path: '/chat',
badgeColor: 'primary'
},
- { text: 'Profile', icon: PersonIcon, path: '/profile', divider: true },
+ { text: 'Profile', icon: PersonIcon, path: '/student/profile', divider: true },
{ text: 'Transcript', icon: DescriptionIcon, path: '/transcript' },
{ text: 'Library', icon: MenuBookIcon, path: '/library' },
{ text: 'Alumni Directory', icon: PeopleIcon, path: '/student/alumni-directory' },
@@ -119,6 +119,7 @@ const adminMenuItems = [
{ text: 'Grievances', icon: SupportAgentIcon, path: '/admin/grievances' },
{ text: 'Reports', icon: AssessmentIcon, path: '/admin/reports' },
{ text: 'Settings', icon: SettingsIcon, path: '/admin/settings', divider: true },
+ { text: 'Profile', icon: PersonIcon, path: '/admin/profile' },
{ text: 'Library', icon: MenuBookIcon, path: '/library' },
{ text: 'Nexus Chat', icon: ChatIcon, path: '/chat' },
];
@@ -136,7 +137,7 @@ const teacherMenuItems = [
path: '/teacher/attendance',
},
{ text: 'Reports', icon: AssessmentIcon, path: '/teacher/reports', divider: true },
- { text: 'Profile', icon: PersonIcon, path: '/profile' },
+ { text: 'Profile', icon: PersonIcon, path: '/teacher/profile' },
{ text: 'Library', icon: MenuBookIcon, path: '/library' },
{
text: 'Nexus Chat',
@@ -154,6 +155,7 @@ const librarianMenuItems = [
{ text: 'Issued Books', icon: AssignmentIcon, path: '/librarian/issued' },
{ text: 'Reservations', icon: EventIcon, path: '/librarian/reservations' },
{ text: 'Reports', icon: AssessmentIcon, path: '/librarian/reports', divider: true },
+ { text: 'Profile', icon: PersonIcon, path: '/librarian/profile' },
{ text: 'Nexus Chat', icon: ChatIcon, path: '/chat' },
{ text: 'Grievances', icon: SupportAgentIcon, path: '/librarian/grievances' },
{ text: 'Settings', icon: SettingsIcon, path: '/admin/settings' },
@@ -166,7 +168,7 @@ const alumniMenuItems = [
{ text: 'Job Board', icon: AssignmentIcon, path: '/alumni/jobs' },
{ text: 'Mentorship', icon: GroupIcon, path: '/alumni/mentorship' },
{ text: 'Success Stories', icon: SchoolIcon, path: '/alumni/stories', divider: true },
- { text: 'Profile', icon: PersonIcon, path: '/profile' },
+ { text: 'Profile', icon: PersonIcon, path: '/alumni/profile' },
{ text: 'Library', icon: MenuBookIcon, path: '/library' },
{ text: 'Nexus Chat', icon: ChatIcon, path: '/chat' },
{ text: 'Grievances', icon: SupportAgentIcon, path: '/alumni/grievances' },
@@ -227,8 +229,21 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
flexDirection: 'column',
minHeight: 0,
background: theme.palette.mode === 'dark'
- ? 'linear-gradient(180deg, #1A1A1A 0%, #0A0A0A 100%)'
- : 'linear-gradient(180deg, #1976D2 0%, #00796B 100%)',
+ ? 'linear-gradient(180deg, #0F2027 0%, #203A43 50%, #2C5364 100%)'
+ : 'linear-gradient(180deg, #1565C0 0%, #0277BD 35%, #00838F 70%, #00695C 100%)',
+ position: 'relative',
+ '&::before': {
+ content: '""',
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ background: theme.palette.mode === 'dark'
+ ? 'radial-gradient(circle at top right, rgba(76,175,80,0.15) 0%, transparent 50%)'
+ : 'radial-gradient(circle at top right, rgba(255,255,255,0.2) 0%, transparent 50%)',
+ pointerEvents: 'none',
+ },
}}
>
{/* Logo Area */}
@@ -240,10 +255,13 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
alignItems: 'center',
justifyContent: 'center',
minHeight: 80,
- backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(255,255,255,0.15)',
- backdropFilter: 'blur(10px)',
+ backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.18)',
+ backdropFilter: 'blur(12px)',
borderBottom: '1px solid',
- borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(255,255,255,0.2)',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.12)' : 'rgba(255,255,255,0.25)',
+ boxShadow: '0 2px 12px rgba(0,0,0,0.1)',
+ position: 'relative',
+ zIndex: 1,
}}
>
{!collapsed ? (
@@ -253,33 +271,65 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
width: 150,
height: 50,
background: theme.palette.mode === 'dark'
- ? 'linear-gradient(135deg, rgba(76,175,80,0.2) 0%, rgba(33,150,243,0.2) 100%)'
- : 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.15) 100%)',
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.25) 0%, rgba(33,150,243,0.25) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.2) 100%)',
borderRadius: 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mx: 'auto',
mb: 1,
- boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
- border: '1px solid',
- borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.15)' : 'rgba(255,255,255,0.3)',
+ boxShadow: theme.palette.mode === 'dark'
+ ? '0 4px 16px rgba(0,0,0,0.3)'
+ : '0 4px 16px rgba(0,0,0,0.15)',
+ border: '2px solid',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.4)',
+ backdropFilter: 'blur(10px)',
+ transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
+ '&:hover': {
+ transform: 'translateY(-2px)',
+ boxShadow: theme.palette.mode === 'dark'
+ ? '0 6px 20px rgba(76,175,80,0.2)'
+ : '0 6px 20px rgba(255,255,255,0.3)',
+ },
}}
>
-
+
NEXUS
-
+
Intelligent Campus Platform
) : (
-
+
)}
-
+
{/* Navigation Items */}
{
overflowX: 'hidden',
py: 2,
px: collapsed ? 0.5 : 1,
- // Keep comfortable spacing above the footer card.
pb: 2,
+ transition: 'padding 0.35s cubic-bezier(0.4, 0, 0.2, 1)',
+ '&::-webkit-scrollbar': {
+ width: '6px',
+ },
+ '&::-webkit-scrollbar-track': {
+ background: 'transparent',
+ },
+ '&::-webkit-scrollbar-thumb': {
+ background: 'rgba(255,255,255,0.2)',
+ borderRadius: '10px',
+ '&:hover': {
+ background: 'rgba(255,255,255,0.3)',
+ },
+ },
}}
>
@@ -308,31 +371,41 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
borderRadius: collapsed ? '12px' : '16px',
background: isActive
? theme.palette.mode === 'dark'
- ? 'linear-gradient(135deg, rgba(76,175,80,0.25) 0%, rgba(33,150,243,0.25) 100%)'
- : 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.15) 100%)'
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.28) 0%, rgba(33,150,243,0.28) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.18) 100%)'
: 'transparent',
color: 'white',
+ backdropFilter: isActive ? 'blur(10px)' : 'none',
'&:hover': {
background: isActive
? theme.palette.mode === 'dark'
- ? 'linear-gradient(135deg, rgba(76,175,80,0.3) 0%, rgba(33,150,243,0.3) 100%)'
- : 'linear-gradient(135deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.2) 100%)'
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.35) 0%, rgba(33,150,243,0.35) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.35) 0%, rgba(255,255,255,0.25) 100%)'
: theme.palette.mode === 'dark'
- ? 'rgba(255,255,255,0.08)'
- : 'rgba(255,255,255,0.12)',
+ ? 'rgba(255,255,255,0.1)'
+ : 'rgba(255,255,255,0.15)',
+ backdropFilter: 'blur(10px)',
+ transform: 'translateX(4px)',
+ },
+ '&:active': {
+ transform: 'translateX(2px)',
},
border: '1px solid',
borderColor: isActive
? theme.palette.mode === 'dark'
- ? 'rgba(76,175,80,0.3)'
- : 'rgba(255,255,255,0.25)'
+ ? 'rgba(76,175,80,0.4)'
+ : 'rgba(255,255,255,0.35)'
: 'transparent',
py: collapsed ? 1.5 : 1.2,
px: collapsed ? 1.5 : 2,
mx: collapsed ? 0 : 0.5,
justifyContent: collapsed ? 'center' : 'flex-start',
- transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
- boxShadow: isActive ? '0 4px 12px rgba(0,0,0,0.15)' : 'none',
+ transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',
+ boxShadow: isActive
+ ? theme.palette.mode === 'dark'
+ ? '0 4px 16px rgba(76,175,80,0.2)'
+ : '0 4px 16px rgba(0,0,0,0.15)'
+ : 'none',
}}
>
{
sx={{
p: collapsed ? 1 : 1.5,
background: theme.palette.mode === 'dark'
- ? 'linear-gradient(135deg, rgba(76,175,80,0.15) 0%, rgba(33,150,243,0.15) 100%)'
- : 'linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.08) 100%)',
- backdropFilter: 'blur(10px)',
+ ? 'linear-gradient(135deg, rgba(76,175,80,0.18) 0%, rgba(33,150,243,0.18) 100%)'
+ : 'linear-gradient(135deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.12) 100%)',
+ backdropFilter: 'blur(12px)',
borderTop: '1px solid',
- borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(255,255,255,0.2)',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.12)' : 'rgba(255,255,255,0.25)',
+ boxShadow: '0 -2px 12px rgba(0,0,0,0.1)',
+ position: 'relative',
+ zIndex: 1,
}}
>
{!collapsed ? (
@@ -447,7 +523,14 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
{
color="white"
fontWeight={600}
noWrap
+ sx={{
+ textShadow: '0 1px 4px rgba(0,0,0,0.2)',
+ fontSize: '0.9rem',
+ }}
>
{currentUser.name}
@@ -487,6 +576,14 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
mx: 'auto',
mb: 1,
cursor: 'pointer',
+ border: '2px solid',
+ borderColor: 'rgba(255,255,255,0.3)',
+ boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
+ transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
+ '&:hover': {
+ transform: 'scale(1.05)',
+ boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
+ },
}}
/>
@@ -501,15 +598,23 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
sx={{
color: 'white',
fontWeight: 700,
- borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.35)',
+ fontSize: collapsed ? '0.85rem' : '0.9rem',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(244,67,54,0.4)' : 'rgba(255,255,255,0.4)',
background: theme.palette.mode === 'dark'
- ? 'rgba(244,67,54,0.15)'
- : 'rgba(211,47,47,0.08)',
+ ? 'rgba(244,67,54,0.18)'
+ : 'rgba(211,47,47,0.12)',
+ backdropFilter: 'blur(8px)',
+ transition: 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)',
'&:hover': {
- borderColor: theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.4)' : 'rgba(255,255,255,0.6)',
+ borderColor: theme.palette.mode === 'dark' ? 'rgba(244,67,54,0.6)' : 'rgba(255,255,255,0.6)',
background: theme.palette.mode === 'dark'
- ? 'rgba(244,67,54,0.25)'
- : 'rgba(211,47,47,0.15)',
+ ? 'rgba(244,67,54,0.28)'
+ : 'rgba(211,47,47,0.2)',
+ transform: 'translateY(-1px)',
+ boxShadow: '0 4px 12px rgba(244,67,54,0.2)',
+ },
+ '&:active': {
+ transform: 'translateY(0)',
},
py: collapsed ? 1.5 : 0.75,
justifyContent: 'center',
@@ -595,9 +700,23 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
display: 'flex',
flexDirection: 'column',
borderRight: 'none',
- transition: 'width 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
+ transition: 'width 0.35s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease',
overflowX: 'hidden',
- overflowY: 'hidden',
+ overflowY: 'auto',
+ '&::-webkit-scrollbar': {
+ width: collapsed ? '0px' : '6px',
+ transition: 'width 0.3s ease',
+ },
+ '&::-webkit-scrollbar-track': {
+ background: 'rgba(255,255,255,0.05)',
+ },
+ '&::-webkit-scrollbar-thumb': {
+ background: 'rgba(255,255,255,0.2)',
+ borderRadius: '10px',
+ '&:hover': {
+ background: 'rgba(255,255,255,0.3)',
+ },
+ },
},
}}
open
diff --git a/src/components/Layout/TopBar.jsx b/src/components/Layout/TopBar.jsx
index cc4a2fd..56ef71f 100644
--- a/src/components/Layout/TopBar.jsx
+++ b/src/components/Layout/TopBar.jsx
@@ -114,9 +114,13 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
// Generate breadcrumbs based on current route
const generateBreadcrumbs = () => {
const pathnames = location.pathname.split('/').filter(x => x);
- const breadcrumbs = [
- { label: 'Home', path: '/dashboard', icon: }
- ];
+
+ // Don't show breadcrumbs if on root or login
+ if (pathnames.length === 0 || location.pathname === '/login') {
+ return [];
+ }
+
+ const breadcrumbs = [];
const routeNames = {
dashboard: 'Dashboard',
@@ -126,11 +130,15 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
history: 'History',
lms: 'My Courses',
course: 'Course Details',
+ classroom: 'Course Classroom',
assignment: 'Assignment',
assignments: 'Assignments',
+ submit: 'Submit Assignment',
finance: 'Fee Management',
+ vouchers: 'Fee Vouchers',
chat: 'Nexus Chat',
library: 'Library',
+ catalog: 'Library Catalog',
grievances: 'Grievances',
admin: 'Admin',
users: 'User Management',
@@ -155,11 +163,21 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
notifications: 'Notifications',
'alumni-directory': 'Alumni Directory',
'smart-attendance': 'Smart Attendance',
+ 'biometric-enrollment': 'Biometric Enrollment',
+ student: 'Student',
+ operations: 'Operations',
+ 'my-courses': 'My Courses',
};
pathnames.forEach((value, index) => {
const path = `/${pathnames.slice(0, index + 1).join('/')}`;
const label = routeNames[value] || value.toUpperCase();
+
+ // Skip IDs and numeric values in breadcrumbs
+ if (!isNaN(value) || value.length > 20) {
+ return;
+ }
+
breadcrumbs.push({ label, path });
});
@@ -278,6 +296,8 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
color: 'text.primary',
borderBottom: '1px solid',
borderColor: 'divider',
+ backdropFilter: 'blur(12px)',
+ boxShadow: '0 2px 12px rgba(0,0,0,0.05)',
}}
elevation={0}
>
@@ -299,16 +319,20 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
}
+ separator={}
aria-label="breadcrumb"
sx={{
display: { xs: 'none', sm: 'flex' },
'& .MuiBreadcrumbs-ol': {
flexWrap: 'nowrap',
},
+ '& .MuiBreadcrumbs-li': {
+ display: 'flex',
+ alignItems: 'center',
+ },
}}
>
- {breadcrumbs.map((crumb, index) => {
+ {breadcrumbs.length > 0 && breadcrumbs.map((crumb, index) => {
const isLast = index === breadcrumbs.length - 1;
return isLast ? (
{
alignItems: 'center',
fontWeight: 600,
fontSize: '0.95rem',
+ px: 0.75,
+ py: 0.5,
+ borderRadius: 1,
+ backgroundColor: 'action.selected',
}}
>
- {crumb.icon}
{crumb.label}
) : (
@@ -339,12 +366,18 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
alignItems: 'center',
cursor: 'pointer',
fontSize: '0.9rem',
+ fontWeight: 500,
+ transition: 'all 0.2s ease',
+ borderRadius: 1,
+ px: 0.75,
+ py: 0.5,
'&:hover': {
color: 'primary.main',
+ backgroundColor: 'action.hover',
+ textDecoration: 'none',
},
}}
>
- {crumb.icon}
{crumb.label}
);
@@ -366,11 +399,15 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
transition: 'width 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'& .MuiOutlinedInput-root': {
backgroundColor: 'background.default',
+ borderRadius: 2,
+ transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'&:hover': {
backgroundColor: 'background.paper',
+ boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
},
'&.Mui-focused': {
backgroundColor: 'background.paper',
+ boxShadow: '0 4px 12px rgba(0,0,0,0.12)',
},
},
}}
@@ -402,6 +439,8 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
mt: 1,
maxHeight: 400,
overflow: 'auto',
+ borderRadius: 2,
+ boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
},
}}
disableAutoFocus
@@ -412,7 +451,17 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
handleSearchResultClick(result.path)}
- sx={{ py: 1.5 }}
+ sx={{
+ py: 1.5,
+ mx: 1,
+ mb: 0.5,
+ borderRadius: 1.5,
+ transition: 'all 0.2s ease',
+ '&:hover': {
+ backgroundColor: 'action.hover',
+ transform: 'translateX(4px)',
+ },
+ }}
>
{result.type === 'course' ? (
@@ -452,12 +501,24 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
onClick={handleNotificationsOpen}
sx={{
color: 'text.secondary',
+ transition: 'all 0.2s ease',
'&:hover': {
color: 'primary.main',
+ backgroundColor: 'action.hover',
+ transform: 'scale(1.05)',
},
}}
>
-
+
@@ -468,8 +529,11 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
onClick={handleSettings}
sx={{
color: 'text.secondary',
+ transition: 'all 0.2s ease',
'&:hover': {
color: 'primary.main',
+ backgroundColor: 'action.hover',
+ transform: 'rotate(30deg)',
},
display: { xs: 'none', sm: 'inline-flex' },
}}
@@ -483,8 +547,11 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
onClick={toggleTheme}
sx={{
color: 'text.secondary',
+ transition: 'all 0.3s ease',
'&:hover': {
color: 'primary.main',
+ backgroundColor: 'action.hover',
+ transform: 'rotate(180deg)',
},
}}
title={mode === 'light' ? 'Switch to dark mode' : 'Switch to light mode'}
@@ -493,11 +560,26 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
{/* User Avatar */}
-
+
@@ -520,15 +602,22 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
width: 380,
mt: 1.5,
maxHeight: 500,
+ borderRadius: 2,
+ boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
},
}}
>
-
+
Notifications
-
- You have {unreadCount} unread notifications
+
+ You have {unreadCount} unread {unreadCount === 1 ? 'notification' : 'notifications'}
@@ -541,17 +630,35 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
sx={{
backgroundColor: notification.read ? 'transparent' : 'action.hover',
py: 2,
+ px: 2.5,
+ cursor: 'pointer',
+ transition: 'all 0.2s ease',
'&:hover': {
- backgroundColor: 'action.hover',
+ backgroundColor: 'action.selected',
+ transform: 'translateX(4px)',
},
}}
>
-
-
+
+
+
+
+
{notification.title}
@@ -565,7 +672,7 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
{notification.subtitle}
-
+
{notification.time}
>
@@ -622,54 +729,145 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
PaperProps={{
sx: {
mt: 1.5,
- minWidth: 240,
+ minWidth: 260,
borderRadius: 2,
+ boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
},
}}
>
-
-
- {currentUser.name}
-
-
- {currentUser.email}
-
+
+
+
+
+
+ {currentUser.name}
+
+
+ {currentUser.email}
+
+
+
-
+
-
+
- Profile
+
-
+
-
+
- Settings
+
-
+
-
+
- Help & Support
+
-
+
- Logout
+
diff --git a/src/pages/Admin/CourseManagement.jsx b/src/pages/Admin/CourseManagement.jsx
index dfa77cb..457e559 100644
--- a/src/pages/Admin/CourseManagement.jsx
+++ b/src/pages/Admin/CourseManagement.jsx
@@ -50,6 +50,20 @@ const CourseManagement = () => {
const [activeTab, setActiveTab] = useState(0);
const [searchQuery, setSearchQuery] = useState('');
const [openDialog, setOpenDialog] = useState(false);
+ const [formData, setFormData] = useState({
+ courseCode: '',
+ courseTitle: '',
+ creditHours: 3,
+ department: '',
+ instructor: '',
+ semester: '',
+ capacity: 50,
+ description: '',
+ });
+
+ const handleChange = (field) => (event) => {
+ setFormData({ ...formData, [field]: event.target.value });
+ };
const courses = [
{
@@ -321,42 +335,116 @@ const CourseManagement = () => {
-
+
-
+
-
-
- Department
-
-
+
setOpenDialog(false)}>Cancel
setOpenDialog(false)}>
Create Course
diff --git a/src/pages/Admin/Profile.jsx b/src/pages/Admin/Profile.jsx
new file mode 100644
index 0000000..8e852d9
--- /dev/null
+++ b/src/pages/Admin/Profile.jsx
@@ -0,0 +1,627 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Avatar,
+ Button,
+ TextField,
+ Divider,
+ Tabs,
+ Tab,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Chip,
+ Alert,
+ Stack,
+ InputAdornment,
+ Snackbar,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemIcon,
+ Checkbox,
+ FormGroup,
+ FormControlLabel,
+ Switch,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Edit,
+ Save,
+ Cancel,
+ PhotoCamera,
+ Email,
+ Phone,
+ AdminPanelSettings,
+ Security,
+ Person,
+ Settings,
+ Badge as BadgeIcon,
+ VerifiedUser,
+ Dashboard,
+ People,
+ School,
+ Description,
+ Assessment,
+ LocalLibrary,
+ EventNote,
+ Lock,
+ Notifications,
+ Language,
+ Palette,
+} from '@mui/icons-material';
+import { useAuth } from '../../contexts/AuthContext';
+import StatusBadge from '../../components/Common/StatusBadge';
+import StatCard from '../../components/Common/StatCard';
+import { pageTransition } from '../../utils/animations';
+
+const AdminProfile = () => {
+ const { user } = useAuth();
+ const [activeTab, setActiveTab] = useState(0);
+ const [isEditing, setIsEditing] = useState(false);
+ const [showAvatarDialog, setShowAvatarDialog] = useState(false);
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+
+ // Form data
+ const [formData, setFormData] = useState({
+ name: 'Muhammad Raza',
+ email: 'raza.admin@nexus.edu.pk',
+ role: 'Super Admin',
+ department: 'IT Administration',
+ phone: '+92 333 9876543',
+ employeeId: 'ADM-2024-001',
+ joiningDate: '2024-01-15',
+ permissions: [
+ 'User Management',
+ 'Course Management',
+ 'Finance Management',
+ 'Grievance Management',
+ 'System Settings',
+ 'Reports & Analytics',
+ 'Alumni Management',
+ 'Library Management',
+ ],
+ });
+
+ // Settings
+ const [settings, setSettings] = useState({
+ emailNotifications: true,
+ systemAlerts: true,
+ securityAlerts: true,
+ weeklyReports: false,
+ twoFactorAuth: true,
+ autoLogout: true,
+ sessionTimeout: '30',
+ });
+
+ // Stats
+ const stats = [
+ { title: 'Total Users', value: '1,234', icon: People, color: 'primary', tooltip: 'Active users in system' },
+ { title: 'Active Sessions', value: '89', icon: Dashboard, color: 'success', tooltip: 'Current active sessions' },
+ { title: 'Pending Requests', value: '15', icon: EventNote, color: 'warning', tooltip: 'Pending approval requests' },
+ { title: 'System Health', value: '98%', icon: Assessment, color: 'info', tooltip: 'Overall system health' },
+ ];
+
+ // Available permissions for different roles
+ const allPermissions = [
+ { id: 'users', label: 'User Management', icon: People },
+ { id: 'courses', label: 'Course Management', icon: School },
+ { id: 'finance', label: 'Finance Management', icon: Assessment },
+ { id: 'grievance', label: 'Grievance Management', icon: Description },
+ { id: 'settings', label: 'System Settings', icon: Settings },
+ { id: 'reports', label: 'Reports & Analytics', icon: Assessment },
+ { id: 'alumni', label: 'Alumni Management', icon: People },
+ { id: 'library', label: 'Library Management', icon: LocalLibrary },
+ { id: 'attendance', label: 'Attendance Management', icon: EventNote },
+ { id: 'exams', label: 'Exam Management', icon: Description },
+ ];
+
+ // Recent activities
+ const recentActivities = [
+ { action: 'Created new user', user: 'Ali Ahmed', time: '10 mins ago', type: 'create' },
+ { action: 'Updated course CS-301', user: 'System', time: '1 hour ago', type: 'update' },
+ { action: 'Approved grievance #145', user: 'HR Dept', time: '2 hours ago', type: 'approve' },
+ { action: 'Generated monthly report', user: 'System', time: '3 hours ago', type: 'report' },
+ { action: 'Modified system settings', user: 'Muhammad Raza', time: '5 hours ago', type: 'settings' },
+ ];
+
+ const handleFieldChange = (field, value) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handlePermissionToggle = (permission) => {
+ setFormData(prev => ({
+ ...prev,
+ permissions: prev.permissions.includes(permission)
+ ? prev.permissions.filter(p => p !== permission)
+ : [...prev.permissions, permission],
+ }));
+ };
+
+ const handleSettingChange = (setting, value) => {
+ setSettings(prev => ({ ...prev, [setting]: value }));
+ };
+
+ const handleSave = () => {
+ setIsEditing(false);
+ setSnackbar({ open: true, message: 'Profile updated successfully!', severity: 'success' });
+ };
+
+ const handleCancel = () => {
+ setIsEditing(false);
+ };
+
+ const handleAvatarUpload = (e) => {
+ const file = e.target.files[0];
+ if (file) {
+ setSnackbar({ open: true, message: 'Profile picture updated!', severity: 'success' });
+ setShowAvatarDialog(false);
+ }
+ };
+
+ return (
+
+
+ {/* Page Header */}
+
+
+ Administrator Profile
+
+
+ Manage your admin profile, access permissions, and system preferences
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Tabs */}
+
+ setActiveTab(newValue)}
+ sx={{ borderBottom: 1, borderColor: 'divider' }}
+ >
+ } label="Personal Information" iconPosition="start" />
+ } label="Access Control" iconPosition="start" />
+ } label="Activity Log" iconPosition="start" />
+ } label="System Preferences" iconPosition="start" />
+
+
+
+ {/* TAB 1: Personal Information */}
+ {activeTab === 0 && (
+
+ {/* Profile Header Card */}
+
+
+
+
+
+
+ {formData.name[0]}
+
+ setShowAvatarDialog(true)}
+ sx={{
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ width: 120,
+ height: 120,
+ borderRadius: '50%',
+ backgroundColor: 'rgba(0,0,0,0.6)',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ opacity: 0,
+ transition: 'opacity 0.3s',
+ cursor: 'pointer',
+ }}
+ >
+
+
+
+
+
+
+ {formData.name}
+
+
+ } label={formData.role} color="error" />
+ } label={formData.employeeId} />
+ } label="Verified" color="success" />
+
+
+
+
+ {formData.email}
+
+
+
+ {formData.phone}
+
+
+
+ Department: {formData.department}
+
+
+
+ {!isEditing ? (
+ }
+ onClick={() => setIsEditing(true)}
+ fullWidth
+ >
+ Edit Profile
+
+ ) : (
+
+ }
+ onClick={handleSave}
+ fullWidth
+ >
+ Save Changes
+
+ }
+ onClick={handleCancel}
+ fullWidth
+ >
+ Cancel
+
+
+ )}
+
+
+
+
+
+ {/* Basic Information */}
+
+
+
+ Basic Information
+
+
+
+
+ handleFieldChange('name', e.target.value)}
+ disabled={!isEditing}
+ />
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('phone', e.target.value)}
+ disabled={!isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('role', e.target.value)}
+ disabled={!isEditing}
+ select={isEditing}
+ >
+ Super Admin
+ Moderator
+ Staff
+
+
+
+ handleFieldChange('department', e.target.value)}
+ disabled={!isEditing}
+ select={isEditing}
+ >
+ IT Administration
+ Academic Affairs
+ Finance
+ Human Resources
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* TAB 2: Access Control */}
+ {activeTab === 1 && (
+
+
+
+
+ Access Permissions
+
+
+ Manage module access and permissions for this administrator
+
+
+
+
+
+ {formData.role} - Current role with {formData.permissions.length} active permissions
+
+
+
+ {allPermissions.map((perm) => (
+
+ isEditing && handlePermissionToggle(perm.label)}
+ >
+
+
+
+ {perm.label}
+
+
+
+ ))}
+
+
+ {isEditing && (
+
+ Changes to permissions require approval from Super Admin
+
+ )}
+
+
+ )}
+
+ {/* TAB 3: Activity Log */}
+ {activeTab === 2 && (
+
+
+
+ Recent Activities
+
+
+
+ {recentActivities.map((activity, index) => (
+
+
+ {activity.type === 'create' && }
+ {activity.type === 'update' && }
+ {activity.type === 'approve' && }
+ {activity.type === 'report' && }
+ {activity.type === 'settings' && }
+
+
+
+ ))}
+
+
+
+ )}
+
+ {/* TAB 4: System Preferences */}
+ {activeTab === 3 && (
+
+
+
+
+ Notification Settings
+
+
+
+ handleSettingChange('emailNotifications', e.target.checked)}
+ />
+ }
+ label="Email Notifications"
+ />
+ handleSettingChange('systemAlerts', e.target.checked)}
+ />
+ }
+ label="System Alerts"
+ />
+ handleSettingChange('securityAlerts', e.target.checked)}
+ />
+ }
+ label="Security Alerts"
+ />
+ handleSettingChange('weeklyReports', e.target.checked)}
+ />
+ }
+ label="Weekly Reports"
+ />
+
+
+
+
+
+
+
+ Security Settings
+
+
+
+ handleSettingChange('twoFactorAuth', e.target.checked)}
+ />
+ }
+ label="Two-Factor Authentication"
+ />
+ handleSettingChange('autoLogout', e.target.checked)}
+ />
+ }
+ label="Auto Logout on Inactivity"
+ />
+
+ handleSettingChange('sessionTimeout', e.target.value)}
+ sx={{ mt: 2 }}
+ />
+
+
+
+ )}
+
+ {/* Avatar Upload Dialog */}
+
+
+ {/* Snackbar */}
+ setSnackbar({ ...snackbar, open: false })}
+ message={snackbar.message}
+ />
+
+
+ );
+};
+
+export default AdminProfile;
diff --git a/src/pages/Admin/Reports.jsx b/src/pages/Admin/Reports.jsx
index 6f83e7c..e811449 100644
--- a/src/pages/Admin/Reports.jsx
+++ b/src/pages/Admin/Reports.jsx
@@ -37,6 +37,7 @@ import {
YAxis,
CartesianGrid,
Tooltip,
+ Legend,
ResponsiveContainer,
} from 'recharts';
import PageHeader from '../../components/Common/PageHeader';
@@ -109,6 +110,30 @@ const AdminReports = () => {
{ month: 'Jun', revenue: 8.5 },
];
+ const attendanceByDepartment = [
+ { dept: 'CS', attendance: 89 },
+ { dept: 'Business', attendance: 85 },
+ { dept: 'Engineering', attendance: 87 },
+ { dept: 'Medical', attendance: 92 },
+ { dept: 'Arts', attendance: 83 },
+ ];
+
+ const studentGrowth = [
+ { year: '2021', students: 2145 },
+ { year: '2022', students: 2387 },
+ { year: '2023', students: 2543 },
+ { year: '2024', students: 2689 },
+ { year: '2025', students: 2847 },
+ ];
+
+ const facultyDistribution = [
+ { department: 'CS', faculty: 45, students: 852 },
+ { department: 'Business', faculty: 38, students: 743 },
+ { department: 'Engineering', faculty: 42, students: 621 },
+ { department: 'Medical', faculty: 35, students: 431 },
+ { department: 'Arts', faculty: 22, students: 200 },
+ ];
+
return (
@@ -195,7 +220,7 @@ const AdminReports = () => {
{/* Charts */}
-
+
@@ -211,9 +236,9 @@ const AdminReports = () => {
-
+
-
+
@@ -224,14 +249,17 @@ const AdminReports = () => {
{
-
+
Department Enrollment
-
+
-
+
@@ -274,14 +302,17 @@ const AdminReports = () => {
{
+
+ {/* STUDENT GROWTH TREND */}
+
+
+
+
+
+
+ Student Enrollment Growth
+
+
+ Year-over-year student enrollment trends
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* ATTENDANCE BY DEPARTMENT */}
+
+
+
+
+
+
+ Attendance Rate by Department
+
+
+ Department-wise attendance performance comparison
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* FACULTY TO STUDENT RATIO */}
+
+
+
+
+
+
+ Faculty-Student Ratio Analysis
+
+
+ Faculty count vs student enrollment by department
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{/* Export Options */}
diff --git a/src/pages/Admin/UserManagement.jsx b/src/pages/Admin/UserManagement.jsx
index 0011ef6..0db31c5 100644
--- a/src/pages/Admin/UserManagement.jsx
+++ b/src/pages/Admin/UserManagement.jsx
@@ -66,6 +66,29 @@ const UserManagement = () => {
const [openDialog, setOpenDialog] = useState(false);
const [filterDepartment, setFilterDepartment] = useState('all');
const [filterStatus, setFilterStatus] = useState('all');
+ const [userType, setUserType] = useState('student');
+ const [formData, setFormData] = useState({
+ // Student fields
+ fullName: '',
+ email: '',
+ rollNumber: '',
+ department: '',
+ program: '',
+ semester: '',
+ session: '',
+ password: '',
+ // Teacher fields
+ employeeId: '',
+ designation: '',
+ specialization: '',
+ type: '',
+ // Alumni fields
+ graduationYear: '',
+ degree: '',
+ personalEmail: '',
+ currentCompany: '',
+ linkedInProfile: '',
+ });
// Mock data
const students = [
@@ -198,6 +221,53 @@ const UserManagement = () => {
const handleCloseDialog = () => {
setOpenDialog(false);
+ setFormData({
+ fullName: '',
+ email: '',
+ rollNumber: '',
+ department: '',
+ program: '',
+ semester: '',
+ session: '',
+ password: '',
+ employeeId: '',
+ designation: '',
+ specialization: '',
+ type: '',
+ graduationYear: '',
+ degree: '',
+ personalEmail: '',
+ currentCompany: '',
+ linkedInProfile: '',
+ });
+ };
+
+ const handleChange = (field) => (event) => {
+ setFormData({ ...formData, [field]: event.target.value });
+ };
+
+ const handleUserTypeChange = (event) => {
+ setUserType(event.target.value);
+ // Reset form when changing user type
+ setFormData({
+ fullName: '',
+ email: '',
+ rollNumber: '',
+ department: '',
+ program: '',
+ semester: '',
+ session: '',
+ password: '',
+ employeeId: '',
+ designation: '',
+ specialization: '',
+ type: '',
+ graduationYear: '',
+ degree: '',
+ personalEmail: '',
+ currentCompany: '',
+ linkedInProfile: '',
+ });
};
const getStatusColor = (status) => {
@@ -467,35 +537,298 @@ const UserManagement = () => {
{/* Add User Dialog */}
-
+ }
+ secondary={
+
+
+
+ } label={item.company} sx={{ mr: 1 }} />
+ } label={item.location} />
+
+
+ }
+ secondaryTypographyProps={{ component: 'div' }}
+ />
+
+ ))}
+
+
+
+
+ )}
+
+ {/* TAB 3: Achievements & Awards */}
+ {activeTab === 2 && (
+
+
+
+ Achievements & Awards
+
+
+
+ {isEditing && (
+
+ setNewAchievement(e.target.value)}
+ multiline
+ rows={2}
+ InputProps={{
+ endAdornment: (
+
+
+
+
+
+ ),
+ }}
+ />
+
+ )}
+
+
+ {formData.achievements.map((achievement, index) => (
+ handleDeleteAchievement(index)}>
+
+
+ )
+ }
+ >
+
+
+ {achievement}
+
+ }
+ />
+
+ ))}
+
+
+
+ )}
+
+ {/* TAB 4: Settings */}
+ {activeTab === 3 && (
+
+
+
+ Account Settings
+
+
+
+ Manage your profile visibility and notification preferences
+
+
+
+ Change Password
+
+
+ Privacy Settings
+
+
+ Notification Preferences
+
+
+
+
+ )}
+
+ {/* Avatar Upload Dialog */}
+ setShowAvatarDialog(false)}>
+ Update Profile Picture
+
+
+
+
+ setShowAvatarDialog(false)}>Cancel
+
+
+
+ {/* Snackbar */}
+ setSnackbar({ ...snackbar, open: false })}
+ message={snackbar.message}
+ />
+
+
+ );
+};
+
+export default AlumniProfile;
diff --git a/src/pages/Chat/ChatPortal.jsx b/src/pages/Chat/ChatPortal.jsx
index e477d49..4b6ea27 100644
--- a/src/pages/Chat/ChatPortal.jsx
+++ b/src/pages/Chat/ChatPortal.jsx
@@ -40,8 +40,6 @@ import {
Group as GroupIcon,
Close,
Check,
- VideoCall,
- Call,
} from '@mui/icons-material';
import { useAuth } from '../../contexts/AuthContext';
import { useNavigate } from 'react-router-dom';
@@ -289,9 +287,9 @@ const ChatPortal = () => {
const ChatList = () => (
{
-
- {mode === 'human' && !selectedChat.members && (
- <>
-
-
-
-
-
-
- >
- )}
-
-
-
-
+
+
+
{/* Messages Area */}
@@ -935,9 +921,10 @@ const ChatPortal = () => {
return (
{(!isMobile || !selectedChat) && }
diff --git a/src/pages/LMS/CourseClassroom.jsx b/src/pages/LMS/CourseClassroom.jsx
index 38690ef..99b2ead 100644
--- a/src/pages/LMS/CourseClassroom.jsx
+++ b/src/pages/LMS/CourseClassroom.jsx
@@ -882,8 +882,8 @@ const CourseClassroom = () => {
Grade Distribution
-
-
+
+
@@ -898,14 +898,17 @@ const CourseClassroom = () => {
{
{/* Monthly Circulation Trends */}
-
+
{
{/* Collection by Category */}
-
+
{
+ const { user } = useAuth();
+ const [activeTab, setActiveTab] = useState(0);
+ const [isEditing, setIsEditing] = useState(false);
+ const [showAvatarDialog, setShowAvatarDialog] = useState(false);
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+
+ // Form data
+ const [formData, setFormData] = useState({
+ name: 'Fatima Khan',
+ employeeId: 'LIB-2024-015',
+ email: 'fatima.khan@nexus.edu.pk',
+ phone: '+92 300 5556789',
+ shift: 'Morning',
+ assignedSection: 'Computer Science & IT',
+ joiningDate: '2024-03-01',
+ experience: '5 years',
+ qualification: 'Masters in Library Science',
+ workingHours: '8:00 AM - 4:00 PM',
+ emergencyContact: '+92 301 1234567',
+ });
+
+ // Stats
+ const stats = [
+ { title: 'Books Issued Today', value: '28', icon: MenuBook, color: 'primary', tooltip: 'Books issued today' },
+ { title: 'Pending Returns', value: '45', icon: Assignment, color: 'warning', tooltip: 'Books due for return' },
+ { title: 'New Arrivals', value: '12', icon: AutoStories, color: 'success', tooltip: 'New books this week' },
+ { title: 'Total Collection', value: '5,234', icon: LocalLibrary, color: 'info', tooltip: 'Books in assigned section' },
+ ];
+
+ // Library sections
+ const librarySections = [
+ 'Computer Science & IT',
+ 'Business & Management',
+ 'Engineering',
+ 'Social Sciences',
+ 'General Collection',
+ 'Reference Section',
+ 'Periodicals',
+ 'Digital Resources',
+ ];
+
+ // Today's activity
+ const todayActivity = [
+ { action: 'Issued', bookTitle: 'Data Structures & Algorithms', student: 'Ali Ahmed', time: '9:30 AM' },
+ { action: 'Returned', bookTitle: 'Database Systems', student: 'Sara Khan', time: '10:15 AM' },
+ { action: 'Issued', bookTitle: 'Machine Learning Basics', student: 'Hassan Raza', time: '11:00 AM' },
+ { action: 'Renewed', bookTitle: 'Software Engineering', student: 'Ayesha Malik', time: '12:30 PM' },
+ { action: 'Issued', bookTitle: 'Web Development', student: 'Usman Ali', time: '2:00 PM' },
+ ];
+
+ const handleFieldChange = (field, value) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handleSave = () => {
+ setIsEditing(false);
+ setSnackbar({ open: true, message: 'Profile updated successfully!', severity: 'success' });
+ };
+
+ const handleCancel = () => {
+ setIsEditing(false);
+ };
+
+ const handleAvatarUpload = (e) => {
+ const file = e.target.files[0];
+ if (file) {
+ setSnackbar({ open: true, message: 'Profile picture updated!', severity: 'success' });
+ setShowAvatarDialog(false);
+ }
+ };
+
+ return (
+
+
+ {/* Page Header */}
+
+
+ Librarian Profile
+
+
+ Manage your profile and library work details
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Tabs */}
+
+ setActiveTab(newValue)}
+ sx={{ borderBottom: 1, borderColor: 'divider' }}
+ >
+ } label="Personal Information" iconPosition="start" />
+ } label="Work Details" iconPosition="start" />
+ } label="Today's Activity" iconPosition="start" />
+ } label="Settings" iconPosition="start" />
+
+
+
+ {/* TAB 1: Personal Information */}
+ {activeTab === 0 && (
+
+ {/* Profile Header Card */}
+
+
+
+
+
+
+ {formData.name[0]}
+
+ setShowAvatarDialog(true)}
+ sx={{
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ width: 120,
+ height: 120,
+ borderRadius: '50%',
+ backgroundColor: 'rgba(0,0,0,0.6)',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ opacity: 0,
+ transition: 'opacity 0.3s',
+ cursor: 'pointer',
+ }}
+ >
+
+
+
+
+
+
+ {formData.name}
+
+
+ } label="Librarian" color="info" />
+ } label={formData.employeeId} />
+ : }
+ label={`${formData.shift} Shift`}
+ color={formData.shift === 'Morning' ? 'warning' : 'primary'}
+ />
+
+
+
+
+ {formData.email}
+
+
+
+ {formData.phone}
+
+
+
+
+ {formData.assignedSection}
+
+
+
+ {!isEditing ? (
+ }
+ onClick={() => setIsEditing(true)}
+ fullWidth
+ >
+ Edit Profile
+
+ ) : (
+
+ }
+ onClick={handleSave}
+ fullWidth
+ >
+ Save Changes
+
+ }
+ onClick={handleCancel}
+ fullWidth
+ >
+ Cancel
+
+
+ )}
+
+
+
+
+
+ {/* Basic Information */}
+
+
+
+ Basic Information
+
+
+
+
+ handleFieldChange('name', e.target.value)}
+ disabled={!isEditing}
+ />
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('phone', e.target.value)}
+ disabled={!isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('emergencyContact', e.target.value)}
+ disabled={!isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('qualification', e.target.value)}
+ disabled={!isEditing}
+ />
+
+
+
+
+
+ )}
+
+ {/* TAB 2: Work Details */}
+ {activeTab === 1 && (
+
+
+
+ Work Details
+
+
+
+
+ handleFieldChange('shift', e.target.value)}
+ disabled={!isEditing}
+ select={isEditing}
+ InputProps={{
+ startAdornment: (
+
+ {formData.shift === 'Morning' ? : }
+
+ ),
+ }}
+ >
+ Morning Shift
+ Evening Shift
+
+
+
+ handleFieldChange('assignedSection', e.target.value)}
+ disabled={!isEditing}
+ select={isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ >
+ {librarySections.map((section) => (
+
+ {section}
+
+ ))}
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+
+
+ )}
+
+ {/* TAB 3: Today's Activity */}
+ {activeTab === 2 && (
+
+
+
+ Today's Activity Log
+
+
+
+ {todayActivity.map((activity, index) => (
+
+
+
+
+
+ {activity.action}: {activity.bookTitle}
+
+ }
+ secondary={
+
+
+
+ } label={activity.time} />
+
+
+ }
+ secondaryTypographyProps={{ component: 'div' }}
+ />
+
+ ))}
+
+
+
+ )}
+
+ {/* TAB 4: Settings */}
+ {activeTab === 3 && (
+
+
+
+ Account Settings
+
+
+
+ For password changes and system preferences, contact the IT department
+
+
+
+ Change Password
+
+
+ Notification Preferences
+
+
+
+
+ )}
+
+ {/* Avatar Upload Dialog */}
+ setShowAvatarDialog(false)}>
+ Update Profile Picture
+
+
+
+
+ setShowAvatarDialog(false)}>Cancel
+
+
+
+ {/* Snackbar */}
+ setSnackbar({ ...snackbar, open: false })}
+ message={snackbar.message}
+ />
+
+
+ );
+};
+
+export default LibrarianProfile;
diff --git a/src/pages/Student/Dashboard.jsx b/src/pages/Student/Dashboard.jsx
index 4f54f2e..772ab29 100644
--- a/src/pages/Student/Dashboard.jsx
+++ b/src/pages/Student/Dashboard.jsx
@@ -293,8 +293,8 @@ const Dashboard = () => {
animation: isLoaded('charts') ? 'fadeIn 0.3s ease-in-out' : 'none',
}}
>
- {/* LEFT: Line Chart - GPA Trend */}
-
+ {/* GPA Trend Chart - Full Width */}
+
{
{loading ? (
) : (
-
-
+
+
@@ -332,15 +332,18 @@ const Dashboard = () => {
{
- {/* RIGHT: Bar Chart - Attendance Overview */}
-
+ {/* Attendance Overview Chart - Full Width */}
+
{
{loading ? (
) : (
-
-
+
+
@@ -403,18 +406,21 @@ const Dashboard = () => {
{
course: '',
description: '',
dueDate: '',
- totalMarks: 100,
+ dueTime: '',
+ totalMarks: 10,
instructions: '',
attachments: [],
});
@@ -119,7 +120,7 @@ const CreateAssignment = () => {
{/* Course Selection */}
-
+
Course *
{
-
+
Total Marks *
@@ -178,18 +181,33 @@ const CreateAssignment = () => {
/>
+
+
+
+ Due Time *
+
+
+
- {/* Description */}
+ {/* Description (Rich Text) */}
- Short Description *
+
+ Assignment Description (Instructions) *
+
{
),
}}
- sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
- />
-
-
- {/* Detailed Instructions */}
-
- Detailed Instructions
-
+
+ 📝 Provide clear instructions for students including objectives, requirements, and submission format
+
@@ -229,7 +236,7 @@ const CreateAssignment = () => {
- Attachments (Optional)
+ File Attachment (Resource Material)
{
maxFiles={5}
/>
- Upload reference materials, datasets, or any supporting documents
+ 📎 Upload PDF/Doc files with reference materials, datasets, or supporting documents (Optional)
diff --git a/src/pages/Teacher/Profile.jsx b/src/pages/Teacher/Profile.jsx
new file mode 100644
index 0000000..10ea4ec
--- /dev/null
+++ b/src/pages/Teacher/Profile.jsx
@@ -0,0 +1,639 @@
+import React, { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Avatar,
+ Button,
+ TextField,
+ Divider,
+ Tabs,
+ Tab,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ IconButton,
+ Chip,
+ Alert,
+ Stack,
+ InputAdornment,
+ Snackbar,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemIcon,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Edit,
+ Save,
+ Cancel,
+ PhotoCamera,
+ Download,
+ Email,
+ Phone,
+ LocationOn,
+ School,
+ CalendarMonth,
+ Work,
+ Assignment,
+ Psychology,
+ Badge as BadgeIcon,
+ Person,
+ Settings,
+ Business,
+ MenuBook,
+ Groups,
+ Star,
+ TrendingUp,
+} from '@mui/icons-material';
+import { useAuth } from '../../contexts/AuthContext';
+import StatusBadge from '../../components/Common/StatusBadge';
+import StatCard from '../../components/Common/StatCard';
+import { pageTransition } from '../../utils/animations';
+
+const TeacherProfile = () => {
+ const { user } = useAuth();
+ const [activeTab, setActiveTab] = useState(0);
+ const [loading, setLoading] = useState(false);
+ const [isEditing, setIsEditing] = useState(false);
+ const [showAvatarDialog, setShowAvatarDialog] = useState(false);
+ const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+
+ // Form data
+ const [formData, setFormData] = useState({
+ name: 'Dr. Ahmed Hassan',
+ email: 'ahmed.hassan@nexus.edu.pk',
+ designation: 'Assistant Professor',
+ department: 'Computer Science',
+ specialization: 'Artificial Intelligence & Machine Learning',
+ officeLocation: 'Room 304, CS Block',
+ phone: '+92 300 1234567',
+ employmentStatus: 'Permanent',
+ joiningDate: '2020-08-15',
+ qualification: 'PhD in Computer Science',
+ experience: '8 years',
+ researchInterests: 'AI, ML, Deep Learning, NLP',
+ publications: '15 Research Papers',
+ personalEmail: 'ahmed.personal@gmail.com',
+ linkedIn: 'linkedin.com/in/ahmedhassan',
+ officeHours: 'Mon-Fri, 2:00 PM - 4:00 PM',
+ });
+
+ // Stats
+ const stats = [
+ { title: 'Active Courses', value: '5', icon: MenuBook, color: 'primary', tooltip: 'Courses being taught this semester' },
+ { title: 'Total Students', value: '186', icon: Groups, color: 'success', tooltip: 'Students enrolled in your courses' },
+ { title: 'Publications', value: '15', icon: Assignment, color: 'info', tooltip: 'Research papers published' },
+ { title: 'Experience', value: '8 Years', icon: TrendingUp, color: 'warning', tooltip: 'Years of teaching experience' },
+ ];
+
+ // Teaching assignments
+ const courses = [
+ { code: 'CS-301', name: 'Data Structures & Algorithms', students: 45, semester: 'Fall 2025' },
+ { code: 'CS-401', name: 'Machine Learning', students: 38, semester: 'Fall 2025' },
+ { code: 'CS-501', name: 'Artificial Intelligence', students: 32, semester: 'Fall 2025' },
+ { code: 'CS-302', name: 'Database Systems', students: 41, semester: 'Fall 2025' },
+ { code: 'CS-499', name: 'Research Methodology', students: 30, semester: 'Fall 2025' },
+ ];
+
+ // Research & Publications
+ const publications = [
+ {
+ title: 'Deep Learning Approaches for Natural Language Processing',
+ journal: 'IEEE Transactions on Neural Networks',
+ year: 2024,
+ citations: 45,
+ type: 'Journal',
+ },
+ {
+ title: 'AI-Driven Healthcare Solutions: A Comprehensive Survey',
+ conference: 'International Conference on AI and Medicine',
+ year: 2023,
+ citations: 28,
+ type: 'Conference',
+ },
+ {
+ title: 'Machine Learning Models for Educational Data Analysis',
+ journal: 'Journal of Educational Technology',
+ year: 2023,
+ citations: 32,
+ type: 'Journal',
+ },
+ ];
+
+ const handleFieldChange = (field, value) => {
+ setFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handleSave = () => {
+ setIsEditing(false);
+ setSnackbar({ open: true, message: 'Profile updated successfully!', severity: 'success' });
+ };
+
+ const handleCancel = () => {
+ setIsEditing(false);
+ };
+
+ const handleAvatarUpload = (e) => {
+ const file = e.target.files[0];
+ if (file) {
+ setSnackbar({ open: true, message: 'Profile picture updated!', severity: 'success' });
+ setShowAvatarDialog(false);
+ }
+ };
+
+ return (
+
+
+ {/* Page Header */}
+
+
+ Faculty Profile
+
+
+ Manage your academic profile, teaching assignments, and research information
+
+
+
+ {/* Stats Cards */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Tabs */}
+
+ setActiveTab(newValue)}
+ sx={{ borderBottom: 1, borderColor: 'divider' }}
+ >
+ } label="Personal Information" iconPosition="start" />
+ } label="Teaching Assignments" iconPosition="start" />
+ } label="Research & Publications" iconPosition="start" />
+ } label="Account Settings" iconPosition="start" />
+
+
+
+ {/* TAB 1: Personal Information */}
+ {activeTab === 0 && (
+
+ {/* Profile Header Card */}
+
+
+
+
+
+
+ {formData.name[0]}
+
+ setShowAvatarDialog(true)}
+ sx={{
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ width: 120,
+ height: 120,
+ borderRadius: '50%',
+ backgroundColor: 'rgba(0,0,0,0.6)',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ opacity: 0,
+ transition: 'opacity 0.3s',
+ cursor: 'pointer',
+ }}
+ >
+
+
+
+
+
+
+ {formData.name}
+
+
+ } label={formData.designation} color="primary" />
+ } label={formData.department} />
+ } label={formData.employmentStatus} color="success" />
+
+
+
+
+ {formData.email}
+
+
+
+ {formData.phone}
+
+
+
+
+ {formData.officeLocation}
+
+
+
+ {!isEditing ? (
+ }
+ onClick={() => setIsEditing(true)}
+ fullWidth
+ >
+ Edit Profile
+
+ ) : (
+
+ }
+ onClick={handleSave}
+ fullWidth
+ >
+ Save Changes
+
+ }
+ onClick={handleCancel}
+ fullWidth
+ >
+ Cancel
+
+
+ )}
+
+
+
+
+
+ {/* Professional Information */}
+
+
+
+ Professional Information
+
+
+
+
+ handleFieldChange('designation', e.target.value)}
+ disabled={!isEditing}
+ select={isEditing}
+ >
+ Lecturer
+ Assistant Professor
+ Associate Professor
+ Professor
+
+
+
+ handleFieldChange('department', e.target.value)}
+ disabled={!isEditing}
+ select={isEditing}
+ >
+ Computer Science
+ Business Administration
+ Engineering
+ Social Sciences
+
+
+
+ handleFieldChange('specialization', e.target.value)}
+ disabled={!isEditing}
+ />
+
+
+ handleFieldChange('officeLocation', e.target.value)}
+ disabled={!isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('employmentStatus', e.target.value)}
+ disabled={!isEditing}
+ select={isEditing}
+ >
+ Permanent
+ Visiting
+ Contract
+
+
+
+ handleFieldChange('joiningDate', e.target.value)}
+ disabled={!isEditing}
+ InputLabelProps={{ shrink: true }}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('qualification', e.target.value)}
+ disabled={!isEditing}
+ />
+
+
+ handleFieldChange('officeHours', e.target.value)}
+ disabled={!isEditing}
+ />
+
+
+
+
+
+ {/* Contact Information */}
+
+
+
+ Contact Information
+
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('personalEmail', e.target.value)}
+ disabled={!isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('phone', e.target.value)}
+ disabled={!isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+ handleFieldChange('linkedIn', e.target.value)}
+ disabled={!isEditing}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+ )}
+
+ {/* TAB 2: Teaching Assignments */}
+ {activeTab === 1 && (
+
+
+
+ Current Teaching Assignments - Fall 2025
+
+
+
+ {courses.map((course, index) => (
+
+
+
+
+
+
+ {course.code} - {course.name}
+
+ }
+ secondary={
+
+
+ }
+ label={`${course.students} Students`}
+ sx={{ mr: 1 }}
+ />
+
+
+
+ }
+ secondaryTypographyProps={{ component: 'div' }}
+ />
+
+
+ ))}
+
+
+
+ )}
+
+ {/* TAB 3: Research & Publications */}
+ {activeTab === 2 && (
+
+
+
+
+ Research Interests
+
+
+
+ {formData.researchInterests}
+
+
+
+
+
+
+
+
+
+
+
+ Recent Publications
+
+
+ {publications.map((pub, index) => (
+
+
+
+ {pub.title}
+
+
+ {pub.journal || pub.conference} • {pub.year}
+
+
+
+
+
+
+
+ ))}
+
+
+
+ )}
+
+ {/* TAB 4: Settings */}
+ {activeTab === 3 && (
+
+
+
+ Account Settings
+
+
+
+ For security reasons, password changes and critical account settings must be done through the IT Admin.
+
+
+ Request Account Changes
+
+
+
+ )}
+
+ {/* Avatar Upload Dialog */}
+ setShowAvatarDialog(false)}>
+ Update Profile Picture
+
+
+
+
+ setShowAvatarDialog(false)}>Cancel
+
+
+
+ {/* Snackbar */}
+ setSnackbar({ ...snackbar, open: false })}
+ message={snackbar.message}
+ />
+
+
+ );
+};
+
+export default TeacherProfile;
diff --git a/src/pages/Teacher/Reports.jsx b/src/pages/Teacher/Reports.jsx
index 28975aa..fe70504 100644
--- a/src/pages/Teacher/Reports.jsx
+++ b/src/pages/Teacher/Reports.jsx
@@ -69,6 +69,31 @@ const assignmentStats = [
{ name: 'Missing', value: 10, color: '#f44336' },
];
+const studentPerformance = [
+ { name: 'Excellent (A/A+)', value: 15, percentage: 17.6, color: '#2e7d32' },
+ { name: 'Good (B/B+)', value: 28, percentage: 32.9, color: '#4caf50' },
+ { name: 'Average (C/C+)', value: 24, percentage: 28.2, color: '#ff9800' },
+ { name: 'Below Average (D)', value: 12, percentage: 14.1, color: '#f57c00' },
+ { name: 'Failing (F)', value: 6, percentage: 7.1, color: '#d32f2f' },
+];
+
+const weeklyProgress = [
+ { week: 'Week 1', avgScore: 72, attendance: 92 },
+ { week: 'Week 2', avgScore: 75, attendance: 88 },
+ { week: 'Week 3', avgScore: 78, attendance: 90 },
+ { week: 'Week 4', avgScore: 76, attendance: 85 },
+ { week: 'Week 5', avgScore: 80, attendance: 87 },
+ { week: 'Week 6', avgScore: 82, attendance: 89 },
+ { week: 'Week 7', avgScore: 84, attendance: 91 },
+ { week: 'Week 8', avgScore: 85, attendance: 93 },
+];
+
+const courseComparison = [
+ { course: 'CS-301', avgGrade: 3.2, passRate: 88, students: 85 },
+ { course: 'CS-201', avgGrade: 3.5, passRate: 92, students: 72 },
+ { course: 'CS-101', avgGrade: 3.8, passRate: 95, students: 68 },
+];
+
const Reports = () => {
const theme = useTheme();
const [course, setCourse] = useState('CS-301');
@@ -202,7 +227,7 @@ const Reports = () => {
{/* GRADE DISTRIBUTION */}
-
+
{
-
+
-
+
@@ -236,14 +261,17 @@ const Reports = () => {
{
{/* ASSIGNMENT COMPLETION */}
-
+
{
-
+
@@ -330,8 +358,8 @@ const Reports = () => {
data={assignmentStats}
cx="50%"
cy="50%"
- innerRadius={window.innerWidth < 600 ? 45 : 60}
- outerRadius={window.innerWidth < 600 ? 75 : 90}
+ innerRadius="35%"
+ outerRadius="55%"
dataKey="value"
paddingAngle={2}
stroke="white"
@@ -404,9 +432,9 @@ const Reports = () => {
-
+
-
+
@@ -417,15 +445,18 @@ const Reports = () => {
{
+
+ {/* STUDENT PERFORMANCE DISTRIBUTION */}
+
+
+
+
+
+
+ Student Performance Distribution
+
+
+ Overall class performance breakdown by category
+
+
+
+
+
+
+
+
+ {studentPerformance.map((item, idx) => (
+
+
+
+
+ ))}
+
+
+
+
+
+
+ {studentPerformance.map((entry, index) => (
+ |
+ ))}
+
+
+
+
+
+
+ {studentPerformance.map((item, idx) => (
+
+
+
+ {item.name}
+
+
+ {item.value}
+
+
+ {item.percentage}% of class
+
+
+
+ ))}
+
+
+
+
+
+
+ {/* WEEKLY PROGRESS TRACKING */}
+
+
+
+
+
+
+ Weekly Progress Tracking
+
+
+ Average test scores vs attendance correlation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* COURSE COMPARISON */}
+
+
+
+
+
+
+ Course Comparison Analysis
+
+
+ Compare average grades and pass rates across your courses
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 5ac0e47fe11763301b4a9f6b6fef2e04db5e3cef Mon Sep 17 00:00:00 2001
From: Muhammad Asad <148639150+ASAD2204@users.noreply.github.com>
Date: Mon, 26 Jan 2026 20:16:14 +0500
Subject: [PATCH 7/7] Errors and improvemts done
---
src/App.jsx | 9 +-
src/components/Chat/ChatWidget.jsx | 39 +-
src/components/Common/SplashScreen.jsx | 2 +-
src/components/Layout/Sidebar.jsx | 13 +-
src/components/Layout/TopBar.jsx | 113 ++--
src/pages/Admin/AnnouncementManagement.jsx | 463 ++++++++++++++
src/pages/Admin/CourseManagement.jsx | 24 +-
src/pages/Admin/Profile.jsx | 28 +-
src/pages/Admin/Reports.jsx | 2 +-
src/pages/Admin/UserManagement.jsx | 85 ++-
src/pages/Alumni/AlumniEvents.jsx | 204 ++++++-
src/pages/Alumni/AlumniNetwork.jsx | 16 +-
src/pages/Alumni/JobBoard.jsx | 169 ++++-
src/pages/Alumni/Profile.jsx | 26 +-
src/pages/Alumni/SuccessStories.jsx | 162 ++++-
src/pages/Attendance/AttendanceSuccess.jsx | 8 +-
src/pages/Attendance/FaceCapture.jsx | 8 +-
src/pages/Attendance/LivenessDetection.jsx | 8 +-
src/pages/Chat/ChatPortal.jsx | 122 ++--
src/pages/LMS/AssignmentSubmit.jsx | 10 +-
src/pages/LMS/CourseClassroom.jsx | 63 +-
src/pages/Library/Profile.jsx | 26 +-
src/pages/Student/Dashboard.jsx | 25 +-
src/pages/Student/Profile.jsx | 71 ++-
src/pages/Support/HelpSupport.jsx | 382 ++++++++++++
src/pages/Teacher/CreateQuiz.jsx | 678 +++++++++++++++++++++
src/pages/Teacher/GrievanceManagement.jsx | 24 +-
src/pages/Teacher/Profile.jsx | 55 +-
src/pages/Teacher/Reports.jsx | 6 +-
src/theme.js | 152 ++---
30 files changed, 2644 insertions(+), 349 deletions(-)
create mode 100644 src/pages/Admin/AnnouncementManagement.jsx
create mode 100644 src/pages/Support/HelpSupport.jsx
create mode 100644 src/pages/Teacher/CreateQuiz.jsx
diff --git a/src/App.jsx b/src/App.jsx
index 5343821..e4e7a4e 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -33,6 +33,7 @@ import AdminGrievanceManagement from './pages/Admin/GrievanceManagement';
import AdminSettings from './pages/Admin/Settings';
import AlumniManagement from './pages/Admin/AlumniManagement';
import DepartmentManagement from './pages/Admin/DepartmentManagement';
+import AnnouncementManagement from './pages/Admin/AnnouncementManagement';
// Teacher Pages
import TeacherDashboard from './pages/Teacher/Dashboard';
@@ -42,6 +43,7 @@ import StudentManagement from './pages/Teacher/StudentManagement';
import TeacherReports from './pages/Teacher/Reports';
import TeacherCourseManagement from './pages/Teacher/CourseManagement';
import CreateAssignment from './pages/Teacher/CreateAssignment';
+import CreateQuiz from './pages/Teacher/CreateQuiz';
import ViewSubmissions from './pages/Teacher/ViewSubmissions';
import Assignments from './pages/Teacher/Assignments';
import Quizzes from './pages/Teacher/Quizzes';
@@ -84,6 +86,7 @@ import JobBoard from './pages/Alumni/JobBoard';
import Mentorship from './pages/Alumni/Mentorship';
import SuccessStories from './pages/Alumni/SuccessStories';
import TeacherGrievanceManagement from './pages/Teacher/GrievanceManagement';
+import HelpSupport from './pages/Support/HelpSupport';
// Protected Route Component
const ProtectedRoute = ({ children }) => {
@@ -180,6 +183,7 @@ function App() {
} />
} />
} />
+ } />
} />
{/* Teacher Routes */}
@@ -194,8 +198,8 @@ function App() {
} />
} />
} />
- } />
- } />
+ } />
+ } />
} />
} />
} />
@@ -220,6 +224,7 @@ function App() {
{/* Support Routes */}
} />
} />
+ } />
{/* Finance Routes */}
} />
diff --git a/src/components/Chat/ChatWidget.jsx b/src/components/Chat/ChatWidget.jsx
index cc1930b..3d51e3e 100644
--- a/src/components/Chat/ChatWidget.jsx
+++ b/src/components/Chat/ChatWidget.jsx
@@ -168,13 +168,16 @@ const ChatWidget = ({ open, onClose, greetingMessage }) => {
elevation={12}
sx={{
position: 'fixed',
- bottom: 24,
- right: 24,
- width: 380,
- height: 600,
+ bottom: { xs: 0, sm: 24 },
+ right: { xs: 0, sm: 24 },
+ left: { xs: 0, sm: 'auto' },
+ top: { xs: 0, sm: 'auto' },
+ width: { xs: '100%', sm: 400, md: 420 },
+ height: { xs: '100%', sm: 600 },
+ maxHeight: { xs: '100vh', sm: '80vh' },
display: 'flex',
flexDirection: 'column',
- borderRadius: '16px',
+ borderRadius: { xs: 0, sm: '16px' },
overflow: 'hidden',
transformOrigin: 'bottom right',
boxShadow: theme.palette.mode === 'dark'
@@ -188,14 +191,14 @@ const ChatWidget = ({ open, onClose, greetingMessage }) => {
sx={{
background: 'linear-gradient(135deg, #128C7E 0%, #075E54 100%)',
color: 'white',
- p: 2,
+ p: { xs: 1.5, sm: 2 },
display: 'flex',
flexDirection: 'column',
gap: 1,
}}
>
-
+
Nexus Chat
@@ -225,17 +228,17 @@ const ChatWidget = ({ open, onClose, greetingMessage }) => {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- gap: 0.5,
- py: 0.75,
- px: 1.5,
+ gap: { xs: 0.3, sm: 0.5 },
+ py: { xs: 0.6, sm: 0.75 },
+ px: { xs: 1, sm: 1.5 },
borderRadius: '10px',
backgroundColor: mode === 'ai' ? 'rgba(255,255,255,0.25)' : 'transparent',
cursor: 'pointer',
transition: 'all 0.2s',
}}
>
-
- AI Assistant
+
+ AI Assistant
{ setMode('human'); setView('contacts'); }}
@@ -244,17 +247,17 @@ const ChatWidget = ({ open, onClose, greetingMessage }) => {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- gap: 0.5,
- py: 0.75,
- px: 1.5,
+ gap: { xs: 0.3, sm: 0.5 },
+ py: { xs: 0.6, sm: 0.75 },
+ px: { xs: 1, sm: 1.5 },
borderRadius: '10px',
backgroundColor: mode === 'human' ? 'rgba(255,255,255,0.25)' : 'transparent',
cursor: 'pointer',
transition: 'all 0.2s',
}}
>
-
- Contacts
+
+ Contacts
@@ -267,7 +270,7 @@ const ChatWidget = ({ open, onClose, greetingMessage }) => {
sx={{
flex: 1,
overflowY: 'auto',
- p: 2,
+ p: { xs: 1.5, sm: 2 },
backgroundColor: theme.palette.mode === 'dark' ? '#0A0A0A' : '#E5DDD5',
backgroundImage: theme.palette.mode === 'dark'
? 'radial-gradient(rgba(255,255,255,0.03) 1px, transparent 1px)'
diff --git a/src/components/Common/SplashScreen.jsx b/src/components/Common/SplashScreen.jsx
index 106e485..7cadb08 100644
--- a/src/components/Common/SplashScreen.jsx
+++ b/src/components/Common/SplashScreen.jsx
@@ -180,7 +180,7 @@ const SplashScreen = ({ onComplete }) => {
fontWeight: 500,
}}
>
- Campus Management System
+ The Unified Campus Management System
diff --git a/src/components/Layout/Sidebar.jsx b/src/components/Layout/Sidebar.jsx
index 9473f52..ee260b0 100644
--- a/src/components/Layout/Sidebar.jsx
+++ b/src/components/Layout/Sidebar.jsx
@@ -46,6 +46,7 @@ import {
Quiz as QuizIcon,
AccountBalance as AccountBalanceIcon,
CardMembership as CardMembershipIcon,
+ Campaign as CampaignIcon,
} from '@mui/icons-material';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
@@ -117,6 +118,7 @@ const adminMenuItems = [
{ text: 'Alumni Management', icon: CardMembershipIcon, path: '/admin/alumni' },
{ text: 'Finance Management', icon: PaymentIcon, path: '/admin/finance' },
{ text: 'Grievances', icon: SupportAgentIcon, path: '/admin/grievances' },
+ { text: 'Announcements', icon: CampaignIcon, path: '/admin/announcements' },
{ text: 'Reports', icon: AssessmentIcon, path: '/admin/reports' },
{ text: 'Settings', icon: SettingsIcon, path: '/admin/settings', divider: true },
{ text: 'Profile', icon: PersonIcon, path: '/admin/profile' },
@@ -158,7 +160,6 @@ const librarianMenuItems = [
{ text: 'Profile', icon: PersonIcon, path: '/librarian/profile' },
{ text: 'Nexus Chat', icon: ChatIcon, path: '/chat' },
{ text: 'Grievances', icon: SupportAgentIcon, path: '/librarian/grievances' },
- { text: 'Settings', icon: SettingsIcon, path: '/admin/settings' },
];
// Alumni menu items
@@ -250,7 +251,8 @@ const Sidebar = ({ drawerWidth = 240, mobileOpen, onDrawerToggle }) => {
{
const listItemContent = (
navigate(item.path)}
+ onClick={() => {
+ navigate(item.path);
+ if (isMobile && onDrawerToggle) {
+ onDrawerToggle();
+ }
+ }}
sx={{
borderRadius: collapsed ? '12px' : '16px',
background: isActive
diff --git a/src/components/Layout/TopBar.jsx b/src/components/Layout/TopBar.jsx
index 56ef71f..38e9a6a 100644
--- a/src/components/Layout/TopBar.jsx
+++ b/src/components/Layout/TopBar.jsx
@@ -40,6 +40,7 @@ import {
Info as InfoIcon,
DarkMode as DarkModeIcon,
LightMode as LightModeIcon,
+ ArrowBack as ArrowBackIcon,
} from '@mui/icons-material';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
@@ -276,7 +277,7 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
};
const handleHelp = () => {
- // Can navigate to help page or open documentation
+ navigate('/help-support');
handleUserMenuClose();
};
@@ -302,15 +303,15 @@ const TopBar = ({ onMenuClick, drawerWidth }) => {
elevation={0}
>
- {/* Left: Hamburger Menu + Breadcrumbs */}
-
+ {/* Left: Hamburger Menu + Back Button & Breadcrumbs */}
+
{
- }
- aria-label="breadcrumb"
- sx={{
- display: { xs: 'none', sm: 'flex' },
- '& .MuiBreadcrumbs-ol': {
- flexWrap: 'nowrap',
- },
- '& .MuiBreadcrumbs-li': {
+ {/* Current Page Title */}
+ {breadcrumbs.length > 0 && (
+
- {breadcrumbs.length > 0 && breadcrumbs.map((crumb, index) => {
- const isLast = index === breadcrumbs.length - 1;
- return isLast ? (
-
- {crumb.label}
-
- ) : (
- {
- e.preventDefault();
- navigate(crumb.path);
- }}
- sx={{
- display: 'flex',
- alignItems: 'center',
- cursor: 'pointer',
- fontSize: '0.9rem',
- fontWeight: 500,
- transition: 'all 0.2s ease',
- borderRadius: 1,
- px: 0.75,
- py: 0.5,
- '&:hover': {
- color: 'primary.main',
- backgroundColor: 'action.hover',
- textDecoration: 'none',
- },
- }}
- >
- {crumb.label}
-
- );
- })}
-
+ gap: 1.5,
+ px: 2,
+ py: 1,
+ borderRadius: 2,
+ background: (theme) => theme.palette.mode === 'dark'
+ ? 'linear-gradient(135deg, rgba(37, 99, 235, 0.1) 0%, rgba(5, 150, 105, 0.05) 100%)'
+ : 'linear-gradient(135deg, rgba(37, 99, 235, 0.06) 0%, rgba(5, 150, 105, 0.04) 100%)',
+ border: (theme) => `1px solid ${theme.palette.mode === 'dark' ? 'rgba(96, 165, 250, 0.2)' : 'rgba(37, 99, 235, 0.15)'}`,
+ boxShadow: '0 2px 8px rgba(37, 99, 235, 0.08)',
+ }}
+ >
+ theme.palette.mode === 'dark'
+ ? 'linear-gradient(180deg, #60A5FA 0%, #059669 100%)'
+ : 'linear-gradient(180deg, #2563EB 0%, #059669 100%)',
+ }}
+ />
+
+ {breadcrumbs[breadcrumbs.length - 1]?.label}
+
+
+ )}
{/* Center: Search Bar */}
-
+
{
onBlur={handleSearchBlur}
size="small"
sx={{
- width: searchFocused ? 500 : 400,
+ width: searchFocused ? { md: 500, lg: 500 } : { md: 300, lg: 400 },
transition: 'width 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'& .MuiOutlinedInput-root': {
backgroundColor: 'background.default',
diff --git a/src/pages/Admin/AnnouncementManagement.jsx b/src/pages/Admin/AnnouncementManagement.jsx
new file mode 100644
index 0000000..5e7b736
--- /dev/null
+++ b/src/pages/Admin/AnnouncementManagement.jsx
@@ -0,0 +1,463 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ TextField,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Chip,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ IconButton,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Stack,
+ Alert,
+ Avatar,
+ Divider,
+ Switch,
+ FormControlLabel,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Add,
+ Edit,
+ Delete,
+ Visibility,
+ Campaign,
+ People,
+ Schedule,
+ Check,
+} from '@mui/icons-material';
+import { useTheme } from '@mui/material/styles';
+import PageHeader from '../../components/Common/PageHeader';
+import StatCard from '../../components/Common/StatCard';
+import { pageTransition } from '../../utils/animations';
+
+const AnnouncementManagement = () => {
+ const theme = useTheme();
+ const [openDialog, setOpenDialog] = useState(false);
+ const [editMode, setEditMode] = useState(false);
+ const [formData, setFormData] = useState({
+ title: '',
+ content: '',
+ targetAudience: 'all',
+ priority: 'normal',
+ publishNow: true,
+ scheduledDate: '',
+ scheduledTime: '',
+ expiryDate: '',
+ });
+
+ const [announcements, setAnnouncements] = useState([
+ {
+ id: 1,
+ title: 'Mid-Term Examination Schedule Released',
+ content: 'The mid-term examination schedule for Fall 2025 has been released. Please check your student portal for detailed timings and venues.',
+ targetAudience: 'students',
+ priority: 'high',
+ status: 'published',
+ publishedDate: '2026-01-20',
+ publishedBy: 'Admin User',
+ views: 1250,
+ expiryDate: '2026-02-15',
+ },
+ {
+ id: 2,
+ title: 'Faculty Development Workshop',
+ content: 'A workshop on "Modern Teaching Methodologies" will be conducted on January 30th, 2026. All faculty members are requested to attend.',
+ targetAudience: 'faculty',
+ priority: 'normal',
+ status: 'published',
+ publishedDate: '2026-01-18',
+ publishedBy: 'Admin User',
+ views: 85,
+ expiryDate: '2026-01-30',
+ },
+ {
+ id: 3,
+ title: 'Library Timings Extended',
+ content: 'Library will remain open until 10 PM during examination week to facilitate students.',
+ targetAudience: 'all',
+ priority: 'normal',
+ status: 'scheduled',
+ scheduledDate: '2026-01-28',
+ publishedBy: 'Admin User',
+ views: 0,
+ expiryDate: '2026-02-10',
+ },
+ {
+ id: 4,
+ title: 'Alumni Networking Event',
+ content: 'Join us for our annual alumni networking event on February 5th. Register on the alumni portal.',
+ targetAudience: 'alumni',
+ priority: 'high',
+ status: 'draft',
+ publishedBy: 'Admin User',
+ views: 0,
+ expiryDate: '2026-02-05',
+ },
+ ]);
+
+ const handleChange = (field, value) => {
+ setFormData({ ...formData, [field]: value });
+ };
+
+ const handleAddAnnouncement = () => {
+ setEditMode(false);
+ setFormData({
+ title: '',
+ content: '',
+ targetAudience: 'all',
+ priority: 'normal',
+ publishNow: true,
+ scheduledDate: '',
+ scheduledTime: '',
+ expiryDate: '',
+ });
+ setOpenDialog(true);
+ };
+
+ const handleSaveAnnouncement = () => {
+ const newAnnouncement = {
+ id: announcements.length + 1,
+ ...formData,
+ status: formData.publishNow ? 'published' : 'scheduled',
+ publishedDate: formData.publishNow ? new Date().toISOString().split('T')[0] : formData.scheduledDate,
+ publishedBy: 'Admin User',
+ views: 0,
+ };
+ setAnnouncements([...announcements, newAnnouncement]);
+ setOpenDialog(false);
+ };
+
+ const handleDeleteAnnouncement = (id) => {
+ setAnnouncements(announcements.filter(a => a.id !== id));
+ };
+
+ const getStatusColor = (status) => {
+ switch (status) {
+ case 'published': return 'success';
+ case 'scheduled': return 'warning';
+ case 'draft': return 'default';
+ case 'expired': return 'error';
+ default: return 'default';
+ }
+ };
+
+ const getPriorityColor = (priority) => {
+ switch (priority) {
+ case 'high': return 'error';
+ case 'normal': return 'primary';
+ case 'low': return 'default';
+ default: return 'default';
+ }
+ };
+
+ const getAudienceLabel = (audience) => {
+ switch (audience) {
+ case 'all': return 'Everyone';
+ case 'students': return 'Students';
+ case 'faculty': return 'Faculty';
+ case 'alumni': return 'Alumni';
+ case 'staff': return 'Staff';
+ default: return audience;
+ }
+ };
+
+ const stats = [
+ {
+ title: 'Total Announcements',
+ value: announcements.length,
+ icon: Campaign,
+ color: 'primary',
+ subtitle: `${announcements.filter(a => a.status === 'published').length} published`,
+ },
+ {
+ title: 'Total Views',
+ value: announcements.reduce((sum, a) => sum + a.views, 0).toLocaleString(),
+ icon: Visibility,
+ color: 'success',
+ subtitle: 'Across all announcements',
+ },
+ {
+ title: 'Scheduled',
+ value: announcements.filter(a => a.status === 'scheduled').length,
+ icon: Schedule,
+ color: 'warning',
+ subtitle: 'Awaiting publication',
+ },
+ {
+ title: 'Drafts',
+ value: announcements.filter(a => a.status === 'draft').length,
+ icon: Edit,
+ color: 'info',
+ subtitle: 'In progress',
+ },
+ ];
+
+ return (
+
+
+
+
+ {/* Stats */}
+
+ {stats.map((stat, index) => (
+
+
+
+ ))}
+
+
+ {/* Action Bar */}
+
+
+
+
+ All Announcements
+
+ }
+ onClick={handleAddAnnouncement}
+ >
+ Create Announcement
+
+
+
+
+
+ {/* Announcements Table */}
+
+
+
+
+
+ Title
+ Audience
+ Priority
+ Status
+ Published Date
+ Views
+ Actions
+
+
+
+ {announcements.map((announcement) => (
+
+
+
+ {announcement.title}
+
+
+ {announcement.content.substring(0, 80)}...
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+ {announcement.publishedDate || announcement.scheduledDate}
+
+
+
+
+ {announcement.views.toLocaleString()}
+
+
+
+
+
+
+
+
+
+
+ handleDeleteAnnouncement(announcement.id)}
+ >
+
+
+
+
+
+ ))}
+
+
+
+
+
+ {/* Create/Edit Announcement Dialog */}
+ setOpenDialog(false)} maxWidth="md" fullWidth>
+
+
+
+
+
+
+ {editMode ? 'Edit Announcement' : 'Create New Announcement'}
+
+
+
+
+
+ handleChange('title', e.target.value)}
+ placeholder="Enter announcement title"
+ />
+
+ handleChange('content', e.target.value)}
+ placeholder="Enter announcement content..."
+ />
+
+
+
+
+ Target Audience
+ handleChange('targetAudience', e.target.value)}
+ >
+ Everyone
+ Students Only
+ Faculty Only
+ Alumni Only
+ Staff Only
+
+
+
+
+
+
+ Priority
+ handleChange('priority', e.target.value)}
+ >
+ Low
+ Normal
+ High
+
+
+
+
+
+
+
+ handleChange('publishNow', e.target.checked)}
+ />
+ }
+ label="Publish immediately"
+ />
+
+ {!formData.publishNow && (
+
+
+ handleChange('scheduledDate', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+ handleChange('scheduledTime', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+ )}
+
+ handleChange('expiryDate', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ helperText="Announcement will be automatically archived after this date"
+ />
+
+ }>
+ This announcement will be visible to {getAudienceLabel(formData.targetAudience).toLowerCase()} on their dashboard and notifications page.
+
+
+
+
+ setOpenDialog(false)}>Cancel
+ : }
+ onClick={handleSaveAnnouncement}
+ disabled={!formData.title || !formData.content}
+ >
+ {formData.publishNow ? 'Publish Now' : 'Schedule'}
+
+
+
+
+
+ );
+};
+
+export default AnnouncementManagement;
diff --git a/src/pages/Admin/CourseManagement.jsx b/src/pages/Admin/CourseManagement.jsx
index 457e559..ea03130 100644
--- a/src/pages/Admin/CourseManagement.jsx
+++ b/src/pages/Admin/CourseManagement.jsx
@@ -311,12 +311,32 @@ const CourseManagement = () => {
-
+ window.open(`/admin/courses/${course.id}`, '_self')}
+ >
-
+ {
+ setFormData({
+ courseCode: course.code,
+ courseTitle: course.name,
+ creditHours: course.creditHours,
+ department: course.department,
+ instructor: course.instructor.name,
+ semester: course.semester,
+ capacity: course.capacity,
+ description: course.description || '',
+ });
+ setOpenDialog(true);
+ }}
+ >
diff --git a/src/pages/Admin/Profile.jsx b/src/pages/Admin/Profile.jsx
index 8e852d9..4771a7b 100644
--- a/src/pages/Admin/Profile.jsx
+++ b/src/pages/Admin/Profile.jsx
@@ -197,16 +197,32 @@ const AdminProfile = () => {
{/* Tabs */}
-
+
setActiveTab(newValue)}
- sx={{ borderBottom: 1, borderColor: 'divider' }}
+ variant="fullWidth"
+ sx={{
+ borderBottom: 1,
+ borderColor: 'divider',
+ '& .MuiTab-root': {
+ minHeight: { xs: 64, md: 64 },
+ minWidth: { xs: 0, md: 120 },
+ fontSize: { xs: '0.7rem', md: '0.875rem' },
+ px: { xs: 0.5, md: 2 },
+ flexDirection: { xs: 'column', md: 'row' },
+ },
+ '& .MuiTab-iconWrapper': {
+ fontSize: { xs: '1.5rem', md: '1.25rem' },
+ marginBottom: { xs: '4px', md: 0 },
+ marginRight: { xs: 0, md: '8px' },
+ },
+ }}
>
- } label="Personal Information" iconPosition="start" />
- } label="Access Control" iconPosition="start" />
- } label="Activity Log" iconPosition="start" />
- } label="System Preferences" iconPosition="start" />
+ } label="Personal" iconPosition="start" />
+ } label="Access" iconPosition="start" />
+ } label="Activity" iconPosition="start" />
+ } label="Settings" iconPosition="start" />
diff --git a/src/pages/Admin/Reports.jsx b/src/pages/Admin/Reports.jsx
index e811449..d8a4a12 100644
--- a/src/pages/Admin/Reports.jsx
+++ b/src/pages/Admin/Reports.jsx
@@ -193,7 +193,7 @@ const AdminReports = () => {
- }>
+ } sx={{ minWidth: { xs: '100%', md: 'auto' } }}>
Generate
diff --git a/src/pages/Admin/UserManagement.jsx b/src/pages/Admin/UserManagement.jsx
index 0db31c5..5aaedca 100644
--- a/src/pages/Admin/UserManagement.jsx
+++ b/src/pages/Admin/UserManagement.jsx
@@ -32,6 +32,7 @@ import {
Stack,
alpha,
Tooltip,
+ Divider,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
@@ -205,6 +206,35 @@ const UserManagement = () => {
},
];
+ const librarians = [
+ {
+ id: 1,
+ name: 'Ayesha Malik',
+ empId: 'LIB-001',
+ email: 'ayesha.malik@nexus.edu',
+ phone: '+92 303 5555555',
+ department: 'Library Services',
+ qualification: 'MLIS',
+ experience: '5 years',
+ status: 'active',
+ joinDate: '2019-07-01',
+ avatar: 'https://i.pravatar.cc/150?img=45',
+ },
+ {
+ id: 2,
+ name: 'Hassan Raza',
+ empId: 'LIB-002',
+ email: 'hassan.raza@nexus.edu',
+ phone: '+92 304 6666666',
+ department: 'Library Services',
+ qualification: 'MLS',
+ experience: '3 years',
+ status: 'active',
+ joinDate: '2021-08-15',
+ avatar: 'https://i.pravatar.cc/150?img=12',
+ },
+ ];
+
const handleMenuOpen = (event, user) => {
setAnchorEl(event.currentTarget);
setSelectedUser(user);
@@ -444,6 +474,55 @@ const UserManagement = () => {
);
+ const renderLibrarianTable = () => (
+
+
+
+
+ Librarian
+ Employee ID
+ Department
+ Qualification
+ Experience
+ Status
+ Actions
+
+
+
+ {librarians.map((librarian) => (
+
+
+
+
+
+
+ {librarian.name}
+
+
+ {librarian.email}
+
+
+
+
+ {librarian.empId}
+ {librarian.department}
+ {librarian.qualification}
+ {librarian.experience}
+
+
+
+
+ handleMenuOpen(e, librarian)}>
+
+
+
+
+ ))}
+
+
+
+ );
+
return (
@@ -459,6 +538,7 @@ const UserManagement = () => {
setActiveTab(newValue)}>
+
@@ -506,12 +586,13 @@ const UserManagement = () => {
{/* Table */}
{activeTab === 0 && renderStudentTable()}
{activeTab === 1 && renderFacultyTable()}
- {activeTab === 2 && renderAdminTable()}
+ {activeTab === 2 && renderLibrarianTable()}
+ {activeTab === 3 && renderAdminTable()}
{/* Pagination */}
setPage(newPage)}
rowsPerPage={rowsPerPage}
diff --git a/src/pages/Alumni/AlumniEvents.jsx b/src/pages/Alumni/AlumniEvents.jsx
index e976ea8..95b5ddc 100644
--- a/src/pages/Alumni/AlumniEvents.jsx
+++ b/src/pages/Alumni/AlumniEvents.jsx
@@ -20,6 +20,10 @@ import {
Avatar,
AvatarGroup,
Alert,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
@@ -37,6 +41,7 @@ import {
BusinessCenter,
School,
History,
+ Add,
} from '@mui/icons-material';
import { alumniEvents, registerForEvent } from '../../data/dummyData';
import PageHeader from '../../components/Common/PageHeader';
@@ -52,12 +57,24 @@ const AlumniEvents = () => {
const [loading, setLoading] = useState(true);
const [selectedEvent, setSelectedEvent] = useState(null);
const [registrationDialog, setRegistrationDialog] = useState(false);
+ const [organizeDialog, setOrganizeDialog] = useState(false);
const [registrationData, setRegistrationData] = useState({
fullName: '',
email: '',
phone: '',
graduationYear: '',
});
+ const [eventFormData, setEventFormData] = useState({
+ title: '',
+ type: 'Reunion',
+ date: '',
+ time: '',
+ venue: '',
+ capacity: '',
+ fee: '',
+ description: '',
+ speakers: '',
+ });
useEffect(() => {
const timer = setTimeout(() => setLoading(false), 1000);
@@ -85,6 +102,40 @@ const AlumniEvents = () => {
}
};
+ const handleOpenOrganizeDialog = () => {
+ setOrganizeDialog(true);
+ };
+
+ const handleCloseOrganizeDialog = () => {
+ setOrganizeDialog(false);
+ setEventFormData({
+ title: '',
+ type: 'Reunion',
+ date: '',
+ time: '',
+ venue: '',
+ capacity: '',
+ fee: '',
+ description: '',
+ speakers: '',
+ });
+ };
+
+ const handleEventFormChange = (field, value) => {
+ setEventFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handleSubmitEvent = () => {
+ if (!eventFormData.title || !eventFormData.date || !eventFormData.time || !eventFormData.venue) {
+ showSnackbar('Please fill all required fields', 'error');
+ return;
+ }
+ // Here you would typically send the data to your backend
+ console.log('Organizing event:', eventFormData);
+ showSnackbar('Event created successfully! It will be reviewed by admin.', 'success');
+ handleCloseOrganizeDialog();
+ };
+
const upcomingEvents = alumniEvents.filter((e) => e.status === 'Upcoming');
const pastEvents = alumniEvents.filter((e) => e.status === 'Completed');
@@ -134,10 +185,20 @@ const AlumniEvents = () => {
return (
-
+
+
+ }
+ onClick={handleOpenOrganizeDialog}
+ sx={{ mt: 1 }}
+ >
+ Organize Event
+
+
{/* Stats */}
@@ -424,6 +485,141 @@ const AlumniEvents = () => {
+
+ {/* Organize Event Dialog */}
+
+
+
+ Organize an Event
+
+
+
+
+
+
+
+
+ handleEventFormChange('title', e.target.value)}
+ placeholder="e.g., Annual Alumni Reunion 2026"
+ />
+
+
+
+ Event Type
+ handleEventFormChange('type', e.target.value)}
+ label="Event Type"
+ >
+ Reunion
+ Career Fair
+ Workshop
+ Webinar
+ Sports
+ Networking
+
+
+
+
+ handleEventFormChange('venue', e.target.value)}
+ placeholder="e.g., University Main Hall"
+ />
+
+
+ handleEventFormChange('date', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+ handleEventFormChange('time', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+ handleEventFormChange('capacity', e.target.value)}
+ placeholder="Maximum number of attendees"
+ />
+
+
+ handleEventFormChange('fee', e.target.value)}
+ placeholder="0 for free event"
+ helperText="Leave empty or enter 0 for free events"
+ />
+
+
+ handleEventFormChange('description', e.target.value)}
+ placeholder="Describe the event, its purpose, and what attendees can expect..."
+ />
+
+
+ handleEventFormChange('speakers', e.target.value)}
+ placeholder="List speakers/presenters (separate by commas)"
+ helperText="e.g., Dr. Ahmed Khan, Ms. Sarah Ali, Prof. Hassan Raza"
+ />
+
+
+
+ Your event will be submitted for admin review and approval before being published.
+
+
+
+
+
+
+ Cancel
+
+
+ Submit Event
+
+
+
);
diff --git a/src/pages/Alumni/AlumniNetwork.jsx b/src/pages/Alumni/AlumniNetwork.jsx
index b7f7479..15607e6 100644
--- a/src/pages/Alumni/AlumniNetwork.jsx
+++ b/src/pages/Alumni/AlumniNetwork.jsx
@@ -248,11 +248,21 @@ const AlumniNetwork = () => {
sx={{ width: 80, height: 80, border: 3, borderColor: 'primary.main' }}
/>
-
+ variant="rounded"
+ sx={{
+ width: 48,
+ height: 48,
+ bgcolor: 'background.paper',
+ border: 1,
+ borderColor: 'divider',
+ mb: 0.5
+ }}
+ >
+
+
{alumnus.currentCompany}
diff --git a/src/pages/Alumni/JobBoard.jsx b/src/pages/Alumni/JobBoard.jsx
index 94bacd3..1d4a413 100644
--- a/src/pages/Alumni/JobBoard.jsx
+++ b/src/pages/Alumni/JobBoard.jsx
@@ -17,6 +17,10 @@ import {
Select,
FormControl,
InputLabel,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
} from '@mui/material';
import {
Search as SearchIcon,
@@ -26,6 +30,8 @@ import {
Schedule as ScheduleIcon,
TrendingUp as TrendingUpIcon,
AttachMoney as MoneyIcon,
+ Add as AddIcon,
+ Close as CloseIcon,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
import StatCard from '../../components/Common/StatCard';
@@ -36,6 +42,16 @@ const JobBoard = () => {
const [searchQuery, setSearchQuery] = useState('');
const [filterType, setFilterType] = useState('all');
const [filterLocation, setFilterLocation] = useState('all');
+ const [openDialog, setOpenDialog] = useState(false);
+ const [jobFormData, setJobFormData] = useState({
+ title: '',
+ company: '',
+ location: '',
+ type: 'Full-time',
+ salary: '',
+ description: '',
+ requirements: '',
+ });
// Mock job listings
const jobs = [
@@ -175,13 +191,52 @@ const JobBoard = () => {
}
};
+ const handleOpenDialog = () => {
+ setOpenDialog(true);
+ };
+
+ const handleCloseDialog = () => {
+ setOpenDialog(false);
+ setJobFormData({
+ title: '',
+ company: '',
+ location: '',
+ type: 'Full-time',
+ salary: '',
+ description: '',
+ requirements: '',
+ });
+ };
+
+ const handleFormChange = (field, value) => {
+ setJobFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handleSubmitJob = () => {
+ // Here you would typically send the data to your backend
+ console.log('Submitting job:', jobFormData);
+ // Show success message (you can add a snackbar)
+ alert('Job posted successfully!');
+ handleCloseDialog();
+ };
+
return (
-
+
+
+ }
+ onClick={handleOpenDialog}
+ sx={{ mt: 1 }}
+ >
+ Share Job
+
+
{/* Stats Cards */}
@@ -373,6 +428,112 @@ const JobBoard = () => {
)}
+
+ {/* Share Job Dialog */}
+
+
+
+ Share a Job Opportunity
+
+
+
+
+
+
+
+
+ handleFormChange('title', e.target.value)}
+ placeholder="e.g., Senior Software Engineer"
+ />
+
+
+ handleFormChange('company', e.target.value)}
+ placeholder="e.g., Tech Solutions Inc."
+ />
+
+
+ handleFormChange('location', e.target.value)}
+ placeholder="e.g., Karachi, Pakistan"
+ />
+
+
+
+ Job Type
+ handleFormChange('type', e.target.value)}
+ label="Job Type"
+ >
+ Full-time
+ Part-time
+ Contract
+ Internship
+
+
+
+
+ handleFormChange('salary', e.target.value)}
+ placeholder="e.g., 200K - 300K PKR"
+ />
+
+
+ handleFormChange('description', e.target.value)}
+ placeholder="Describe the job responsibilities and expectations..."
+ />
+
+
+ handleFormChange('requirements', e.target.value)}
+ placeholder="List key requirements (separate by commas)"
+ helperText="e.g., 5+ years experience, React/Node.js, Team leadership"
+ />
+
+
+
+
+
+ Cancel
+
+
+ Share Job
+
+
+
);
diff --git a/src/pages/Alumni/Profile.jsx b/src/pages/Alumni/Profile.jsx
index 6dfac09..951c812 100644
--- a/src/pages/Alumni/Profile.jsx
+++ b/src/pages/Alumni/Profile.jsx
@@ -173,15 +173,31 @@ const AlumniProfile = () => {
{/* Tabs */}
-
+
setActiveTab(newValue)}
- sx={{ borderBottom: 1, borderColor: 'divider' }}
+ variant="fullWidth"
+ sx={{
+ borderBottom: 1,
+ borderColor: 'divider',
+ '& .MuiTab-root': {
+ minHeight: { xs: 64, md: 64 },
+ minWidth: { xs: 0, md: 120 },
+ fontSize: { xs: '0.7rem', md: '0.875rem' },
+ px: { xs: 0.5, md: 2 },
+ flexDirection: { xs: 'column', md: 'row' },
+ },
+ '& .MuiTab-iconWrapper': {
+ fontSize: { xs: '1.5rem', md: '1.25rem' },
+ marginBottom: { xs: '4px', md: 0 },
+ marginRight: { xs: 0, md: '8px' },
+ },
+ }}
>
- } label="Personal Information" iconPosition="start" />
- } label="Professional Information" iconPosition="start" />
- } label="Achievements & Awards" iconPosition="start" />
+ } label="Personal" iconPosition="start" />
+ } label="Professional" iconPosition="start" />
+ } label="Achievements" iconPosition="start" />
} label="Settings" iconPosition="start" />
diff --git a/src/pages/Alumni/SuccessStories.jsx b/src/pages/Alumni/SuccessStories.jsx
index 1c5f766..f43a299 100644
--- a/src/pages/Alumni/SuccessStories.jsx
+++ b/src/pages/Alumni/SuccessStories.jsx
@@ -18,6 +18,10 @@ import {
Select,
FormControl,
InputLabel,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
} from '@mui/material';
import {
Search as SearchIcon,
@@ -28,6 +32,8 @@ import {
Favorite as FavoriteIcon,
Comment as CommentIcon,
LinkedIn as LinkedInIcon,
+ Add as AddIcon,
+ Close as CloseIcon,
} from '@mui/icons-material';
import PageHeader from '../../components/Common/PageHeader';
import StatCard from '../../components/Common/StatCard';
@@ -37,6 +43,15 @@ import { pageTransition } from '../../utils/animations';
const SuccessStories = () => {
const [searchQuery, setSearchQuery] = useState('');
const [filterCategory, setFilterCategory] = useState('all');
+ const [openDialog, setOpenDialog] = useState(false);
+ const [storyFormData, setStoryFormData] = useState({
+ achievement: '',
+ company: '',
+ designation: '',
+ category: 'Entrepreneurship',
+ story: '',
+ tags: '',
+ });
// Mock success stories
const stories = [
@@ -171,6 +186,37 @@ const SuccessStories = () => {
},
];
+ const handleOpenDialog = () => {
+ setOpenDialog(true);
+ };
+
+ const handleCloseDialog = () => {
+ setOpenDialog(false);
+ setStoryFormData({
+ achievement: '',
+ company: '',
+ designation: '',
+ category: 'Entrepreneurship',
+ story: '',
+ tags: '',
+ });
+ };
+
+ const handleFormChange = (field, value) => {
+ setStoryFormData(prev => ({ ...prev, [field]: value }));
+ };
+
+ const handleSubmitStory = () => {
+ if (!storyFormData.achievement || !storyFormData.story) {
+ alert('Please fill all required fields');
+ return;
+ }
+ // Here you would typically send the data to your backend
+ console.log('Submitting story:', storyFormData);
+ alert('Success story submitted! It will be reviewed before publishing.');
+ handleCloseDialog();
+ };
+
const filteredStories = stories.filter(story => {
const matchesSearch = story.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
story.achievement.toLowerCase().includes(searchQuery.toLowerCase()) ||
@@ -182,10 +228,20 @@ const SuccessStories = () => {
return (
-
+
+
+ }
+ onClick={handleOpenDialog}
+ sx={{ mt: 1 }}
+ >
+ Share Story
+
+
{/* Stats Cards */}
@@ -379,6 +435,104 @@ const SuccessStories = () => {
)}
+
+ {/* Share Success Story Dialog */}
+
+
+
+ Share Your Success Story
+
+
+
+
+
+
+
+
+ handleFormChange('achievement', e.target.value)}
+ placeholder="e.g., Founded Tech Startup Valued at $10M"
+ />
+
+
+ handleFormChange('company', e.target.value)}
+ placeholder="e.g., InnovateTech"
+ />
+
+
+ handleFormChange('designation', e.target.value)}
+ placeholder="e.g., CEO & Founder"
+ />
+
+
+
+ Category
+ handleFormChange('category', e.target.value)}
+ label="Category"
+ >
+ Entrepreneurship
+ Technology
+ Business
+ Research
+ Engineering
+ Social Impact
+ Healthcare
+ Education
+
+
+
+
+ handleFormChange('story', e.target.value)}
+ placeholder="Share your journey, challenges you overcame, and what you achieved. Inspire fellow students and alumni..."
+ helperText="Be detailed and authentic. Your story will inspire current students and fellow alumni."
+ />
+
+
+ handleFormChange('tags', e.target.value)}
+ placeholder="e.g., Startup, Leadership, Innovation"
+ helperText="Add relevant tags separated by commas"
+ />
+
+
+
+
+
+ Cancel
+
+
+ Share Story
+
+
+
);
diff --git a/src/pages/Attendance/AttendanceSuccess.jsx b/src/pages/Attendance/AttendanceSuccess.jsx
index 37feba7..7b36e49 100644
--- a/src/pages/Attendance/AttendanceSuccess.jsx
+++ b/src/pages/Attendance/AttendanceSuccess.jsx
@@ -31,10 +31,10 @@ const AttendanceSuccess = () => {
>
@@ -57,7 +57,7 @@ const AttendanceSuccess = () => {
boxShadow: '0 10px 40px rgba(0,0,0,0.2)',
}}
>
-
+
@@ -129,7 +129,7 @@ const AttendanceSuccess = () => {
startIcon={}
sx={{
bgcolor: 'white',
- color: '#43e97b',
+ color: '#059669',
fontWeight: 'bold',
px: 5,
py: 1.5,
diff --git a/src/pages/Attendance/FaceCapture.jsx b/src/pages/Attendance/FaceCapture.jsx
index 78857e8..396323c 100644
--- a/src/pages/Attendance/FaceCapture.jsx
+++ b/src/pages/Attendance/FaceCapture.jsx
@@ -155,7 +155,7 @@ const FaceCapture = () => {
position: 'absolute',
width: 250,
height: 330,
- border: '4px solid #4caf50',
+ border: '4px solid #059669',
borderRadius: 3,
animation: 'scanPulse 2s infinite',
'@keyframes scanPulse': {
@@ -179,7 +179,7 @@ const FaceCapture = () => {
width: 16,
height: 16,
borderRadius: '50%',
- bgcolor: '#4caf50',
+ bgcolor: '#059669',
animation: 'cornerPulse 1s infinite',
animationDelay: `${i * 0.2}s`,
'@keyframes cornerPulse': {
@@ -197,8 +197,8 @@ const FaceCapture = () => {
position: 'absolute',
width: 250,
height: 2,
- bgcolor: '#4caf50',
- boxShadow: '0 0 10px #4caf50',
+ bgcolor: '#059669',
+ boxShadow: '0 0 10px #059669',
animation: 'scan 2s linear infinite',
'@keyframes scan': {
'0%': { top: '30%' },
diff --git a/src/pages/Attendance/LivenessDetection.jsx b/src/pages/Attendance/LivenessDetection.jsx
index b02b391..5743746 100644
--- a/src/pages/Attendance/LivenessDetection.jsx
+++ b/src/pages/Attendance/LivenessDetection.jsx
@@ -371,7 +371,7 @@ const LivenessDetection = () => {
position: 'absolute',
width: 250,
height: 330,
- border: '4px solid #4caf50',
+ border: '4px solid #059669',
borderRadius: 3,
animation: 'scanPulse 2s infinite',
}}
@@ -391,7 +391,7 @@ const LivenessDetection = () => {
width: 16,
height: 16,
borderRadius: '50%',
- bgcolor: '#4caf50',
+ bgcolor: '#059669',
animation: 'cornerPulse 1s infinite',
animationDelay: `${i * 0.2}s`,
}}
@@ -405,8 +405,8 @@ const LivenessDetection = () => {
position: 'absolute',
width: 250,
height: 2,
- bgcolor: '#4caf50',
- boxShadow: '0 0 10px #4caf50',
+ bgcolor: '#059669',
+ boxShadow: '0 0 10px #059669',
animation: 'scan 2s linear infinite',
}}
/>
diff --git a/src/pages/Chat/ChatPortal.jsx b/src/pages/Chat/ChatPortal.jsx
index 4b6ea27..58bf500 100644
--- a/src/pages/Chat/ChatPortal.jsx
+++ b/src/pages/Chat/ChatPortal.jsx
@@ -300,15 +300,15 @@ const ChatPortal = () => {
sx={{
background: `linear-gradient(135deg, ${whatsappGreen} 0%, ${whatsappDarkGreen} 100%)`,
color: 'white',
- p: 2,
+ p: { xs: 1.5, md: 2 },
}}
>
-
-
- navigate('/dashboard')} sx={{ color: 'white' }}>
-
+
+
+ navigate('/dashboard')} sx={{ color: 'white', p: { xs: 0.5, md: 1 } }}>
+
-
+
Nexus Chat
@@ -333,9 +333,11 @@ const ChatPortal = () => {
color: 'white',
backgroundColor: 'rgba(255,255,255,0.15)',
'&:hover': { backgroundColor: 'rgba(255,255,255,0.25)' },
+ p: { xs: 0.75, md: 1 },
}}
+ size={isMobile ? 'small' : 'medium'}
>
- {mode === 'ai' ? : }
+ {mode === 'ai' ? : }
@@ -349,12 +351,12 @@ const ChatPortal = () => {
gap: 1,
backgroundColor: 'rgba(255,255,255,0.15)',
borderRadius: '20px',
- px: 2,
- py: 0.5,
+ px: { xs: 1.5, md: 2 },
+ py: { xs: 0.4, md: 0.5 },
}}
>
{mode === 'ai' ? : }
-
+
{mode === 'ai' ? 'AI Assistant Mode' : 'Human Chat Mode'}
@@ -362,7 +364,7 @@ const ChatPortal = () => {
{/* Search */}
{mode === 'human' && (
-
+
{
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
InputProps={{
- startAdornment: (
-
-
-
- ),
- }}
- sx={{
- backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : 'white',
- borderRadius: '8px',
- '& .MuiOutlinedInput-root': {
- '& fieldset': { border: 'none' },
+ startAdornment: ,
+ sx: {
+ backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : '#FFFFFF',
+ borderRadius: '10px',
+ fontSize: { xs: '0.875rem', md: '1rem' },
},
}}
/>
@@ -629,23 +625,23 @@ const ChatPortal = () => {
sx={{
background: `linear-gradient(135deg, ${whatsappGreen} 0%, ${whatsappDarkGreen} 100%)`,
color: 'white',
- p: 2,
+ p: { xs: 1.5, md: 2 },
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
-
+
{isMobile && (
- setSelectedChat(null)} sx={{ color: 'white' }}>
-
+ setSelectedChat(null)} sx={{ color: 'white', p: 0.5 }}>
+
)}
-
+
{selectedChat.avatar || selectedChat.name[0]}
-
+
{selectedChat.name}
@@ -669,7 +665,7 @@ const ChatPortal = () => {
sx={{
flex: 1,
overflowY: 'auto',
- p: 2,
+ p: { xs: 1, md: 2 },
backgroundImage:
theme.palette.mode === 'dark'
? 'none'
@@ -690,9 +686,9 @@ const ChatPortal = () => {
>
{
{msg.senderName}
)}
-
+
{msg.text}
{
textAlign: 'right',
mt: 0.5,
opacity: 0.7,
- fontSize: '0.7rem',
+ fontSize: { xs: '0.65rem', md: '0.7rem' },
}}
>
{msg.timestamp}
@@ -764,13 +767,13 @@ const ChatPortal = () => {
{/* Input Area */}
-
-
-
-
+
+
+
+
-
-
+
+
{
handleSendMessage();
}
}}
+ size={isMobile ? 'small' : 'medium'}
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: '25px',
- backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : 'white',
- '& fieldset': { border: 'none' },
+ backgroundColor: theme.palette.mode === 'dark' ? '#2A3942' : '#FFFFFF',
+ fontSize: { xs: '0.875rem', md: '1rem' },
},
}}
/>
- {inputMessage.trim() ? (
-
-
-
- ) : (
-
-
-
- )}
+
+
+
>
diff --git a/src/pages/LMS/AssignmentSubmit.jsx b/src/pages/LMS/AssignmentSubmit.jsx
index bd6573e..24ec9bb 100644
--- a/src/pages/LMS/AssignmentSubmit.jsx
+++ b/src/pages/LMS/AssignmentSubmit.jsx
@@ -345,11 +345,11 @@ const AssignmentSubmit = () => {
sx={{
mb: 3,
background: `linear-gradient(135deg, ${
- countdown.color === 'error' ? '#f44336' :
- countdown.color === 'warning' ? '#ff9800' : '#4caf50'
+ countdown.color === 'error' ? '#DC2626' :
+ countdown.color === 'warning' ? '#D97706' : '#059669'
} 0%, ${
- countdown.color === 'error' ? '#d32f2f' :
- countdown.color === 'warning' ? '#f57c00' : '#388e3c'
+ countdown.color === 'error' ? '#B91C1C' :
+ countdown.color === 'warning' ? '#B45309' : '#047857'
} 100%)`,
}}
>
@@ -735,7 +735,7 @@ const AssignmentSubmit = () => {
{/* Grade Card */}
{
{
backdropFilter: 'blur(10px)',
}}
/>
-
+
{course.title}
-
+
{course.instructor} • {enrolledStudents} students enrolled
- {/* Progress Indicator - Top Right */}
+ {/* Progress Indicator - Top Right (Hidden on mobile) */}
@@ -398,21 +400,32 @@ const CourseClassroom = () => {
{/* ACTION BAR */}
-
+
setActiveTab(newValue)}
variant="scrollable"
scrollButtons="auto"
+ sx={{ minHeight: { xs: 40, sm: 48 } }}
>
-
-
-
-
-
-
+
+
+
+
+
+
-
+
@@ -421,6 +434,7 @@ const CourseClassroom = () => {
size="small"
startIcon={}
color="error"
+ sx={{ display: { xs: 'none', sm: 'inline-flex' } }}
>
Leave Course
@@ -567,34 +581,38 @@ const CourseClassroom = () => {
{/* Filter Bar */}
-
-
+
+
setAssignmentFilter('all')}
clickable
+ size="small"
/>
setAssignmentFilter('pending')}
clickable
+ size="small"
/>
setAssignmentFilter('submitted')}
clickable
+ size="small"
/>
setAssignmentFilter('graded')}
clickable
+ size="small"
/>
-
+
{
fullWidth
startIcon={}
size="small"
+ onClick={() => window.open(`/resources/syllabus-${course.code}.pdf`, '_blank')}
>
Course Syllabus
@@ -1214,6 +1233,7 @@ const CourseClassroom = () => {
fullWidth
startIcon={}
size="small"
+ onClick={() => navigate('/student/timetable')}
>
Class Schedule
@@ -1222,6 +1242,7 @@ const CourseClassroom = () => {
fullWidth
startIcon={}
size="small"
+ onClick={() => navigate('/help-support')}
>
Help & Support
diff --git a/src/pages/Library/Profile.jsx b/src/pages/Library/Profile.jsx
index 8b255bc..a487bf9 100644
--- a/src/pages/Library/Profile.jsx
+++ b/src/pages/Library/Profile.jsx
@@ -153,15 +153,31 @@ const LibrarianProfile = () => {
{/* Tabs */}
-
+
setActiveTab(newValue)}
- sx={{ borderBottom: 1, borderColor: 'divider' }}
+ variant="fullWidth"
+ sx={{
+ borderBottom: 1,
+ borderColor: 'divider',
+ '& .MuiTab-root': {
+ minHeight: { xs: 64, md: 64 },
+ minWidth: { xs: 0, md: 120 },
+ fontSize: { xs: '0.7rem', md: '0.875rem' },
+ px: { xs: 0.5, md: 2 },
+ flexDirection: { xs: 'column', md: 'row' },
+ },
+ '& .MuiTab-iconWrapper': {
+ fontSize: { xs: '1.5rem', md: '1.25rem' },
+ marginBottom: { xs: '4px', md: 0 },
+ marginRight: { xs: 0, md: '8px' },
+ },
+ }}
>
- } label="Personal Information" iconPosition="start" />
- } label="Work Details" iconPosition="start" />
- } label="Today's Activity" iconPosition="start" />
+ } label="Personal" iconPosition="start" />
+ } label="Work" iconPosition="start" />
+ } label="Activity" iconPosition="start" />
} label="Settings" iconPosition="start" />
diff --git a/src/pages/Student/Dashboard.jsx b/src/pages/Student/Dashboard.jsx
index 772ab29..64eb53b 100644
--- a/src/pages/Student/Dashboard.jsx
+++ b/src/pages/Student/Dashboard.jsx
@@ -19,6 +19,7 @@ import {
IconButton,
Divider,
Stack,
+ alpha,
} from '@mui/material';
import Grid from '@mui/material/Grid';
import {
@@ -276,9 +277,8 @@ const Dashboard = () => {
icon={PaymentIcon}
color={unpaidFees.length > 0 ? 'error' : 'success'}
subtitle={unpaidFees.length > 0 ? `${unpaidFees.length} invoice(s) due` : 'No pending fees'}
- tooltip={unpaidFees.length > 0 ? 'Outstanding fee amount that needs to be paid. Click to view fee vouchers.' : 'All your fees are paid up to date!'}
+ tooltip={unpaidFees.length > 0 ? 'Outstanding fee amount that needs to be paid.' : 'All your fees are paid up to date!'}
loading={loading}
- onClick={() => navigate('/finance/fee-vouchers')}
/>
@@ -297,12 +297,13 @@ const Dashboard = () => {
-
+
@@ -374,12 +375,13 @@ const Dashboard = () => {
-
+
@@ -458,8 +460,8 @@ const Dashboard = () => {
>
{/* TODAY'S SCHEDULE */}
-
-
+
+
Today's Classes
@@ -519,12 +521,17 @@ const Dashboard = () => {
p: 2,
borderRadius: 2,
backgroundColor: classItem.status === 'completed' ? 'action.hover' :
- classItem.status === 'current' ? 'primary.light' :
+ classItem.status === 'current' ? alpha(theme.palette.primary.main, 0.08) :
'background.default',
opacity: classItem.status === 'completed' ? 0.6 : 1,
+ border: classItem.status === 'current' ? `2px solid ${alpha(theme.palette.primary.main, 0.3)}` : 'none',
}}
>
-
+
{classItem.course} - {classItem.title}
@@ -746,7 +753,7 @@ const Dashboard = () => {
fullWidth
variant="outlined"
sx={{ mt: 2 }}
- onClick={() => navigate('/lms')}
+ onClick={() => navigate('/notifications')}
>
View All Announcements
diff --git a/src/pages/Student/Profile.jsx b/src/pages/Student/Profile.jsx
index ae00b63..6328383 100644
--- a/src/pages/Student/Profile.jsx
+++ b/src/pages/Student/Profile.jsx
@@ -323,16 +323,49 @@ const Profile = () => {
{/* Tabs */}
-
+
setActiveTab(newValue)}
- sx={{ borderBottom: 1, borderColor: 'divider' }}
+ variant="fullWidth"
+ sx={{
+ borderBottom: 1,
+ borderColor: 'divider',
+ '& .MuiTab-root': {
+ minHeight: { xs: 64, md: 64 },
+ minWidth: { xs: 0, md: 120 },
+ fontSize: { xs: '0.7rem', md: '0.875rem' },
+ px: { xs: 0.5, md: 2 },
+ flexDirection: { xs: 'column', md: 'row' },
+ },
+ '& .MuiTab-iconWrapper': {
+ fontSize: { xs: '1.5rem', md: '1.25rem' },
+ marginBottom: { xs: '4px', md: 0 },
+ marginRight: { xs: 0, md: '8px' },
+ },
+ }}
>
- } label="Personal Information" iconPosition="start" />
- } label="Academic Information" iconPosition="start" />
- } label="Documents" iconPosition="start" />
- } label="Settings" iconPosition="start" />
+ }
+ label="Personal"
+ iconPosition="start"
+ />
+ }
+ label="Academic"
+ iconPosition="start"
+ />
+ }
+ label="Documents"
+ iconPosition="start"
+ />
+ }
+ label="Settings"
+ iconPosition="start"
+ sx={{ '& .MuiTab-wrapper': { display: 'flex', flexDirection: 'row', gap: 0.5 } }}
+ />
@@ -340,13 +373,15 @@ const Profile = () => {
{activeTab === 0 && (
{/* Profile Header Card */}
-
-
+
+
{
variant="contained"
startIcon={}
onClick={() => setIsEditing(true)}
- size="large"
+ fullWidth
>
Edit Profile
) : (
-
+
}
onClick={handleSave}
disabled={!hasChanges || Object.values(errors).some(e => e)}
+ fullWidth
>
Save Changes
@@ -432,6 +468,7 @@ const Profile = () => {
variant="outlined"
startIcon={}
onClick={handleCancel}
+ fullWidth
>
Cancel
@@ -443,8 +480,8 @@ const Profile = () => {
{/* Personal Details */}
-
-
+
+
Personal Details
@@ -685,8 +722,8 @@ const Profile = () => {
{activeTab === 1 && (
{/* Current Program Card */}
-
-
+
+
Current Program
@@ -1141,8 +1178,8 @@ const Profile = () => {
{/* Danger Zone */}
-
-
+
+
Danger Zone
@@ -1151,7 +1188,7 @@ const Profile = () => {
Deactivating your account will temporarily suspend your access to all university services. You can reactivate your account by contacting the administration.
-
+
Deactivate Account
diff --git a/src/pages/Support/HelpSupport.jsx b/src/pages/Support/HelpSupport.jsx
new file mode 100644
index 0000000..1d04a54
--- /dev/null
+++ b/src/pages/Support/HelpSupport.jsx
@@ -0,0 +1,382 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ TextField,
+ Button,
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ List,
+ ListItem,
+ ListItemIcon,
+ ListItemText,
+ Avatar,
+ Chip,
+ Divider,
+ Stack,
+ Alert,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ ExpandMore,
+ HelpOutline,
+ Email,
+ Phone,
+ LocationOn,
+ Send,
+ CheckCircle,
+ School,
+ Payment,
+ Assignment,
+ LibraryBooks,
+ Group,
+ Security,
+ Language,
+ Feedback,
+} from '@mui/icons-material';
+import { pageTransition } from '../../utils/animations';
+import PageHeader from '../../components/Common/PageHeader';
+
+const HelpSupport = () => {
+ const [ticketForm, setTicketForm] = useState({
+ subject: '',
+ category: '',
+ description: '',
+ });
+ const [showSuccessDialog, setShowSuccessDialog] = useState(false);
+ const [expandedFaq, setExpandedFaq] = useState(false);
+
+ const handleChange = (field, value) => {
+ setTicketForm({ ...ticketForm, [field]: value });
+ };
+
+ const handleSubmitTicket = () => {
+ // Simulate ticket submission
+ setShowSuccessDialog(true);
+ setTicketForm({ subject: '', category: '', description: '' });
+ };
+
+ const faqCategories = [
+ {
+ title: 'Account & Access',
+ icon: Security,
+ color: '#2563EB',
+ faqs: [
+ {
+ question: 'How do I reset my password?',
+ answer: 'Go to the login page and click on "Forgot Password". Enter your email address, and you will receive a password reset link within a few minutes.',
+ },
+ {
+ question: 'How do I update my profile information?',
+ answer: 'Navigate to your Profile page from the sidebar menu. Click the "Edit Profile" button to update your personal information, contact details, and profile picture.',
+ },
+ {
+ question: 'How do I enable biometric authentication?',
+ answer: 'Go to Settings > Security > Biometric Enrollment. Follow the on-screen instructions to enroll your fingerprint or facial recognition.',
+ },
+ ],
+ },
+ {
+ title: 'Attendance & Classes',
+ icon: School,
+ color: '#059669',
+ faqs: [
+ {
+ question: 'How does smart attendance work?',
+ answer: 'Smart Attendance uses GPS verification and face recognition technology. Make sure you are at the class location, then use your device camera for facial verification.',
+ },
+ {
+ question: 'What if I miss marking attendance?',
+ answer: 'Contact your course instructor or submit a grievance through the Grievances portal explaining the reason. Late attendance marking may require approval.',
+ },
+ {
+ question: 'Can I view my attendance history?',
+ answer: 'Yes, go to Attendance > History to view your complete attendance records for all courses, including dates, times, and status.',
+ },
+ ],
+ },
+ {
+ title: 'Assignments & Submissions',
+ icon: Assignment,
+ color: '#D97706',
+ faqs: [
+ {
+ question: 'How do I submit an assignment?',
+ answer: 'Navigate to Assignments, select the assignment, and click "Submit Now". Upload your files (PDF, DOC, or ZIP) and add any comments before final submission.',
+ },
+ {
+ question: 'Can I resubmit an assignment?',
+ answer: 'Resubmission depends on your instructor\'s settings. If allowed, you\'ll see a "Resubmit" button on already submitted assignments.',
+ },
+ {
+ question: 'What file formats are supported?',
+ answer: 'Supported formats include PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX, ZIP, RAR, and common image formats (JPG, PNG). Maximum file size is 50MB.',
+ },
+ ],
+ },
+ {
+ title: 'Fee Management',
+ icon: Payment,
+ color: '#DC2626',
+ faqs: [
+ {
+ question: 'How can I download my fee voucher?',
+ answer: 'Go to Fee Management > Fee Vouchers, select the voucher you need, and click the "Download" button to get a PDF copy.',
+ },
+ {
+ question: 'What payment methods are accepted?',
+ answer: 'You can pay through bank deposit, online transfer, or in-person at the university accounts office. Payment details are on your fee voucher.',
+ },
+ {
+ question: 'How do I check my payment history?',
+ answer: 'Navigate to Fee Management to view all your invoices, payment history, and outstanding balances.',
+ },
+ ],
+ },
+ {
+ title: 'Library Services',
+ icon: LibraryBooks,
+ color: '#0891B2',
+ faqs: [
+ {
+ question: 'How do I borrow a book?',
+ answer: 'Search for books in the Library Catalog, check availability, and click "Reserve". Visit the library with your student card to collect reserved books.',
+ },
+ {
+ question: 'What is the book return policy?',
+ answer: 'Books must be returned within 14 days. Late returns incur a fine of PKR 10 per day. You can renew books online if no other reservations exist.',
+ },
+ {
+ question: 'Can I access e-books?',
+ answer: 'Yes, the Library section provides access to e-books and digital resources. Use your student credentials to access the digital library.',
+ },
+ ],
+ },
+ {
+ title: 'Technical Issues',
+ icon: Language,
+ color: '#7C3AED',
+ faqs: [
+ {
+ question: 'The website is not loading properly',
+ answer: 'Try clearing your browser cache and cookies. Ensure you are using the latest version of Chrome, Firefox, or Edge. If the issue persists, contact IT support.',
+ },
+ {
+ question: 'I cannot upload files',
+ answer: 'Check your internet connection and ensure file size is under 50MB. Try using a different browser. If the problem continues, report it through the feedback form.',
+ },
+ {
+ question: 'The chat feature is not working',
+ answer: 'Make sure you have granted camera/microphone permissions if using video chat. Check your internet connection and try refreshing the page.',
+ },
+ ],
+ },
+ ];
+
+ const contactInfo = [
+ {
+ icon: Email,
+ title: 'Email Support',
+ value: 'support@nexus.edu.pk',
+ description: 'Response within 24 hours',
+ },
+ {
+ icon: Phone,
+ title: 'Phone Support',
+ value: '+92 42 111-222-333',
+ description: 'Mon-Fri, 9:00 AM - 5:00 PM',
+ },
+ {
+ icon: LocationOn,
+ title: 'Campus Office',
+ value: 'Student Services Center, 2nd Floor',
+ description: 'Walk-in support available',
+ },
+ ];
+
+ return (
+
+
+
+
+ {/* Quick Contact Info */}
+
+ {contactInfo.map((contact, index) => (
+
+
+
+
+
+
+
+
+ {contact.title}
+
+
+
+ {contact.value}
+
+
+ {contact.description}
+
+
+
+
+ ))}
+
+
+
+ {/* FAQ Section */}
+
+
+
+
+ Frequently Asked Questions
+
+
+ Find quick answers to common questions
+
+
+ {faqCategories.map((category, categoryIndex) => (
+
+
+
+
+
+
+ {category.title}
+
+
+
+ {category.faqs.map((faq, faqIndex) => (
+
+ }>
+
+ {faq.question}
+
+
+
+
+ {faq.answer}
+
+
+
+ ))}
+
+ ))}
+
+
+
+
+ {/* Submit Ticket Form */}
+
+
+
+
+ Submit a Support Ticket
+
+
+ Can't find what you're looking for? Submit a ticket and we'll get back to you.
+
+
+
+ handleChange('subject', e.target.value)}
+ placeholder="Brief description of your issue"
+ />
+
+ handleChange('category', e.target.value)}
+ SelectProps={{ native: true }}
+ >
+
+
+
+
+
+
+
+
+
+
+ handleChange('description', e.target.value)}
+ placeholder="Provide detailed information about your issue..."
+ />
+
+ }>
+ Support tickets are typically responded to within 24-48 hours during business days.
+
+
+ }
+ onClick={handleSubmitTicket}
+ disabled={!ticketForm.subject || !ticketForm.category || !ticketForm.description}
+ >
+ Submit Ticket
+
+
+
+
+
+
+
+ {/* Success Dialog */}
+ setShowSuccessDialog(false)}>
+
+
+
+
+
+
+ Ticket Submitted Successfully
+
+
+
+
+
+ Your support ticket has been submitted successfully. Our support team will review your request and respond within 24-48 hours.
+
+
+ You can track your ticket status in the "My Tickets" section.
+
+
+
+ setShowSuccessDialog(false)} variant="contained">
+ Close
+
+
+
+
+
+ );
+};
+
+export default HelpSupport;
diff --git a/src/pages/Teacher/CreateQuiz.jsx b/src/pages/Teacher/CreateQuiz.jsx
new file mode 100644
index 0000000..a96e37a
--- /dev/null
+++ b/src/pages/Teacher/CreateQuiz.jsx
@@ -0,0 +1,678 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ Box,
+ Card,
+ CardContent,
+ Typography,
+ Button,
+ TextField,
+ MenuItem,
+ Stack,
+ IconButton,
+ Divider,
+ Chip,
+ Paper,
+ FormControl,
+ InputLabel,
+ Select,
+ FormControlLabel,
+ Radio,
+ RadioGroup,
+ Switch,
+ Alert,
+ Stepper,
+ Step,
+ StepLabel,
+} from '@mui/material';
+import Grid from '@mui/material/Grid';
+import {
+ Quiz as QuizIcon,
+ Add,
+ Delete,
+ ArrowBack,
+ Save,
+ Publish,
+ CheckCircle,
+ RadioButtonChecked,
+ TextFields,
+ HelpOutline,
+} from '@mui/icons-material';
+import PageHeader from '../../components/Common/PageHeader';
+import { fadeInUp } from '../../utils/animations';
+
+const CreateQuiz = () => {
+ const navigate = useNavigate();
+ const [activeStep, setActiveStep] = useState(0);
+
+ // Quiz basic info
+ const [quizInfo, setQuizInfo] = useState({
+ title: '',
+ course: '',
+ description: '',
+ duration: 30,
+ totalMarks: 0,
+ passingMarks: 0,
+ startDate: '',
+ startTime: '',
+ endDate: '',
+ endTime: '',
+ instructions: '',
+ shuffleQuestions: true,
+ showResults: true,
+ });
+
+ // Questions array
+ const [questions, setQuestions] = useState([]);
+
+ const steps = ['Quiz Details', 'Add Questions', 'Review & Publish'];
+
+ const courses = [
+ { value: 'CS-301', label: 'CS-301 - Data Structures & Algorithms' },
+ { value: 'CS-201', label: 'CS-201 - Object Oriented Programming' },
+ { value: 'CS-401', label: 'CS-401 - Database Systems' },
+ { value: 'CS-101', label: 'CS-101 - Programming Fundamentals' },
+ ];
+
+ const questionTypes = [
+ { value: 'mcq', label: 'Multiple Choice (Single Answer)', icon: },
+ { value: 'multiple', label: 'Multiple Choice (Multiple Answers)', icon: },
+ { value: 'truefalse', label: 'True/False', icon: },
+ { value: 'fillblank', label: 'Fill in the Blanks', icon: },
+ { value: 'short', label: 'Short Answer', icon: },
+ ];
+
+ const handleQuizInfoChange = (field, value) => {
+ setQuizInfo(prev => ({ ...prev, [field]: value }));
+ };
+
+ const addQuestion = (type) => {
+ const newQuestion = {
+ id: Date.now(),
+ type,
+ question: '',
+ marks: 1,
+ options: type === 'mcq' || type === 'multiple' ? ['', '', '', ''] : [],
+ correctAnswer: type === 'mcq' ? 0 : type === 'truefalse' ? 'true' : type === 'multiple' ? [] : '',
+ explanation: '',
+ };
+ setQuestions([...questions, newQuestion]);
+ };
+
+ const updateQuestion = (id, field, value) => {
+ setQuestions(questions.map(q =>
+ q.id === id ? { ...q, [field]: value } : q
+ ));
+ };
+
+ const updateOption = (questionId, optionIndex, value) => {
+ setQuestions(questions.map(q =>
+ q.id === questionId
+ ? { ...q, options: q.options.map((opt, idx) => idx === optionIndex ? value : opt) }
+ : q
+ ));
+ };
+
+ const deleteQuestion = (id) => {
+ setQuestions(questions.filter(q => q.id !== id));
+ };
+
+ const calculateTotalMarks = () => {
+ return questions.reduce((sum, q) => sum + parseInt(q.marks || 0), 0);
+ };
+
+ const handleSaveDraft = () => {
+ console.log('Saving draft:', { quizInfo, questions });
+ alert('Quiz saved as draft!');
+ navigate('/teacher/quizzes');
+ };
+
+ const handlePublish = () => {
+ if (!quizInfo.title || !quizInfo.course || questions.length === 0) {
+ alert('Please fill all required fields and add at least one question');
+ return;
+ }
+ console.log('Publishing quiz:', { quizInfo, questions });
+ alert('Quiz published successfully!');
+ navigate('/teacher/quizzes');
+ };
+
+ const handleNext = () => {
+ if (activeStep === 0 && (!quizInfo.title || !quizInfo.course)) {
+ alert('Please fill required fields: Title and Course');
+ return;
+ }
+ if (activeStep === 1 && questions.length === 0) {
+ alert('Please add at least one question');
+ return;
+ }
+ setActiveStep(prev => prev + 1);
+ };
+
+ const handleBack = () => {
+ setActiveStep(prev => prev - 1);
+ };
+
+ const renderQuestionEditor = (question, index) => {
+ return (
+
+
+
+
+
+ t.value === question.type)?.label}
+ size="small"
+ />
+
+ deleteQuestion(question.id)}>
+
+
+
+
+
+
+ updateQuestion(question.id, 'question', e.target.value)}
+ required
+ placeholder="Enter your question here..."
+ />
+
+
+ updateQuestion(question.id, 'marks', e.target.value)}
+ inputProps={{ min: 1 }}
+ />
+
+
+ {/* MCQ Options */}
+ {(question.type === 'mcq' || question.type === 'multiple') && (
+
+
+ Options {question.type === 'multiple' && '(Select all correct answers)'}
+
+ {question.options.map((option, idx) => (
+
+ {question.type === 'mcq' ? (
+ updateQuestion(question.id, 'correctAnswer', idx)}
+ />
+ ) : (
+ {
+ const current = question.correctAnswer || [];
+ const updated = e.target.checked
+ ? [...current, idx]
+ : current.filter(i => i !== idx);
+ updateQuestion(question.id, 'correctAnswer', updated);
+ }}
+ />
+ }
+ label=""
+ />
+ )}
+ updateOption(question.id, idx, e.target.value)}
+ size="small"
+ />
+
+ ))}
+
+ )}
+
+ {/* True/False */}
+ {question.type === 'truefalse' && (
+
+
+
+ Correct Answer
+
+ updateQuestion(question.id, 'correctAnswer', e.target.value)}
+ >
+ } label="True" />
+ } label="False" />
+
+
+
+ )}
+
+ {/* Fill in the Blanks */}
+ {question.type === 'fillblank' && (
+
+ updateQuestion(question.id, 'correctAnswer', e.target.value)}
+ placeholder="Enter the correct answer"
+ helperText="For multiple blanks, separate answers with commas"
+ sx={{ mt: 2 }}
+ />
+
+ )}
+
+ {/* Short Answer */}
+ {question.type === 'short' && (
+
+ updateQuestion(question.id, 'correctAnswer', e.target.value)}
+ placeholder="Enter keywords or sample answer for reference"
+ helperText="This helps in manual grading"
+ sx={{ mt: 2 }}
+ />
+
+ )}
+
+ {/* Explanation */}
+
+ updateQuestion(question.id, 'explanation', e.target.value)}
+ placeholder="Explain the correct answer..."
+ helperText="Students will see this after submitting"
+ />
+
+
+
+
+ );
+ };
+
+ return (
+
+
+
+ }
+ variant="outlined"
+ onClick={() => navigate('/teacher/quizzes')}
+ >
+ Back to Quizzes
+
+
+
+ {/* Stepper */}
+
+
+
+ {steps.map((label) => (
+
+ {label}
+
+ ))}
+
+
+
+
+ {/* Step 1: Quiz Details */}
+ {activeStep === 0 && (
+
+
+
+ Quiz Information
+
+
+
+
+
+ handleQuizInfoChange('title', e.target.value)}
+ placeholder="e.g., Data Structures Fundamentals Quiz"
+ />
+
+
+
+ Course
+ handleQuizInfoChange('course', e.target.value)}
+ label="Course"
+ >
+ {courses.map(course => (
+
+ {course.label}
+
+ ))}
+
+
+
+
+
+ handleQuizInfoChange('description', e.target.value)}
+ placeholder="Brief description of the quiz content and objectives..."
+ />
+
+
+
+ handleQuizInfoChange('duration', e.target.value)}
+ inputProps={{ min: 5 }}
+ />
+
+
+ handleQuizInfoChange('passingMarks', e.target.value)}
+ inputProps={{ min: 0 }}
+ />
+
+
+
+
+
+
+ handleQuizInfoChange('startDate', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+ handleQuizInfoChange('startTime', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+
+ handleQuizInfoChange('endDate', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+ handleQuizInfoChange('endTime', e.target.value)}
+ InputLabelProps={{ shrink: true }}
+ />
+
+
+
+ handleQuizInfoChange('instructions', e.target.value)}
+ placeholder="Important instructions and guidelines for students..."
+ />
+
+
+
+ handleQuizInfoChange('shuffleQuestions', e.target.checked)}
+ />
+ }
+ label="Shuffle Questions"
+ />
+
+
+ handleQuizInfoChange('showResults', e.target.checked)}
+ />
+ }
+ label="Show Results Immediately"
+ />
+
+
+
+
+ )}
+
+ {/* Step 2: Add Questions */}
+ {activeStep === 1 && (
+ <>
+
+
+
+ Add Questions
+
+
+ Select a question type to add to your quiz
+
+
+ {questionTypes.map(type => (
+ addQuestion(type.value)}
+ size="small"
+ >
+ {type.label}
+
+ ))}
+
+
+
+
+ {questions.length === 0 ? (
+
+
+
+
+ No questions added yet
+
+
+ Click on a question type above to start building your quiz
+
+
+
+ ) : (
+ <>
+
+ Total Questions: {questions.length} | Total Marks: {calculateTotalMarks()}
+
+ {questions.map((question, index) => renderQuestionEditor(question, index))}
+ >
+ )}
+ >
+ )}
+
+ {/* Step 3: Review & Publish */}
+ {activeStep === 2 && (
+
+
+
+ Review Quiz
+
+
+
+
+
+
+
+ Quiz Title
+
+
+ {quizInfo.title || 'Untitled Quiz'}
+
+
+
+
+
+
+ Course
+
+
+ {courses.find(c => c.value === quizInfo.course)?.label || 'Not selected'}
+
+
+
+
+
+
+ Total Questions
+
+
+ {questions.length}
+
+
+
+
+
+
+ Total Marks
+
+
+ {calculateTotalMarks()}
+
+
+
+
+
+
+ Duration
+
+
+ {quizInfo.duration}m
+
+
+
+
+
+
+ Passing Marks
+
+
+ {quizInfo.passingMarks}
+
+
+
+
+
+ Your quiz is ready to publish! Review the details above and click Publish to make it available to students.
+
+
+
+
+
+ )}
+
+ {/* Navigation Buttons */}
+
+
+
+
+ Back
+
+
+ }
+ onClick={handleSaveDraft}
+ >
+ Save as Draft
+
+ {activeStep < steps.length - 1 ? (
+
+ Next
+
+ ) : (
+ }
+ onClick={handlePublish}
+ color="success"
+ >
+ Publish Quiz
+
+ )}
+
+
+
+
+
+ );
+};
+
+export default CreateQuiz;
diff --git a/src/pages/Teacher/GrievanceManagement.jsx b/src/pages/Teacher/GrievanceManagement.jsx
index 4481aa1..69e7e60 100644
--- a/src/pages/Teacher/GrievanceManagement.jsx
+++ b/src/pages/Teacher/GrievanceManagement.jsx
@@ -264,17 +264,19 @@ const TeacherGrievanceManagement = () => {
{/* Tabs and Filters */}
- setTabValue(newValue)}
- sx={{ borderBottom: 1, borderColor: 'divider', px: 2 }}
- >
-
- g.status === 'Pending Review').length})`} />
- g.status === 'Under Review').length})`} />
- g.status === 'Resolved').length})`} />
-
-
+
+ setTabValue(newValue)}
+ sx={{ px: 2 }}
+ >
+
+ g.status === 'Pending Review').length})`} />
+ g.status === 'Under Review').length})`} />
+ g.status === 'Resolved').length})`} />
+
+
+
{
{/* Tabs */}
-
+
setActiveTab(newValue)}
- sx={{ borderBottom: 1, borderColor: 'divider' }}
+ variant="fullWidth"
+ sx={{
+ borderBottom: 1,
+ borderColor: 'divider',
+ '& .MuiTab-root': {
+ minHeight: { xs: 64, md: 64 },
+ minWidth: { xs: 0, md: 120 },
+ fontSize: { xs: '0.7rem', md: '0.875rem' },
+ px: { xs: 0.5, md: 2 },
+ flexDirection: { xs: 'column', md: 'row' },
+ },
+ '& .MuiTab-iconWrapper': {
+ fontSize: { xs: '1.5rem', md: '1.25rem' },
+ marginBottom: { xs: '4px', md: 0 },
+ marginRight: { xs: 0, md: '8px' },
+ },
+ }}
>
- } label="Personal Information" iconPosition="start" />
- } label="Teaching Assignments" iconPosition="start" />
- } label="Research & Publications" iconPosition="start" />
- } label="Account Settings" iconPosition="start" />
+ }
+ label="Personal"
+ iconPosition="start"
+ />
+ }
+ label="Teaching"
+ iconPosition="start"
+ />
+ }
+ label="Research"
+ iconPosition="start"
+ />
+ }
+ label="Settings"
+ iconPosition="start"
+ sx={{ '& .MuiTab-wrapper': { display: 'flex', flexDirection: 'row', gap: 0.5 } }}
+ />
@@ -196,13 +229,15 @@ const TeacherProfile = () => {
{activeTab === 0 && (
{/* Profile Header Card */}
-
-
+
+
{
{/* Professional Information */}
-
-
+
+
Professional Information
diff --git a/src/pages/Teacher/Reports.jsx b/src/pages/Teacher/Reports.jsx
index fe70504..0a1696a 100644
--- a/src/pages/Teacher/Reports.jsx
+++ b/src/pages/Teacher/Reports.jsx
@@ -213,11 +213,11 @@ const Reports = () => {
This Semester
-
- } size="small">
+
+ } size="small" sx={{ flex: { xs: '1 1 100%', sm: '0 0 auto' } }}>
Export
- } size="small">
+ } size="small" sx={{ flex: { xs: '1 1 100%', sm: '0 0 auto' } }}>
Print
diff --git a/src/theme.js b/src/theme.js
index 6fbff30..615a207 100644
--- a/src/theme.js
+++ b/src/theme.js
@@ -8,46 +8,46 @@ export const getTheme = (mode) => {
palette: {
mode,
primary: {
- main: isLight ? '#1976D2' : '#90CAF9',
- light: isLight ? '#42A5F5' : '#BBDEFB',
- dark: isLight ? '#1565C0' : '#64B5F6',
+ main: isLight ? '#2563EB' : '#60A5FA',
+ light: isLight ? '#3B82F6' : '#93C5FD',
+ dark: isLight ? '#1E40AF' : '#3B82F6',
contrastText: '#FFFFFF',
},
secondary: {
- main: isLight ? '#00796B' : '#26A69A',
- light: isLight ? '#26A69A' : '#4DB6AC',
- dark: isLight ? '#004D40' : '#00796B',
+ main: isLight ? '#475569' : '#94A3B8',
+ light: isLight ? '#64748B' : '#CBD5E1',
+ dark: isLight ? '#334155' : '#64748B',
contrastText: '#FFFFFF',
},
error: {
- main: isLight ? '#D32F2F' : '#EF5350',
- light: isLight ? '#EF5350' : '#EF5350',
- dark: isLight ? '#C62828' : '#E53935',
+ main: isLight ? '#DC2626' : '#F87171',
+ light: isLight ? '#EF4444' : '#FCA5A5',
+ dark: isLight ? '#B91C1C' : '#EF4444',
},
warning: {
- main: isLight ? '#F57C00' : '#FFA726',
- light: isLight ? '#FF9800' : '#FFB74D',
- dark: isLight ? '#E65100' : '#FB8C00',
+ main: isLight ? '#D97706' : '#FBBF24',
+ light: isLight ? '#F59E0B' : '#FCD34D',
+ dark: isLight ? '#B45309' : '#F59E0B',
},
success: {
- main: isLight ? '#388E3C' : '#66BB6A',
- light: isLight ? '#4CAF50' : '#81C784',
- dark: isLight ? '#2E7D32' : '#4CAF50',
+ main: isLight ? '#059669' : '#34D399',
+ light: isLight ? '#10B981' : '#6EE7B7',
+ dark: isLight ? '#047857' : '#10B981',
},
info: {
- main: isLight ? '#0288D1' : '#29B6F6',
- light: isLight ? '#03A9F4' : '#4FC3F7',
- dark: isLight ? '#01579B' : '#0288D1',
+ main: isLight ? '#0891B2' : '#22D3EE',
+ light: isLight ? '#06B6D4' : '#67E8F9',
+ dark: isLight ? '#0E7490' : '#06B6D4',
},
background: {
- default: isLight ? '#F4F6F8' : '#121212',
- paper: isLight ? '#FFFFFF' : '#1E1E1E',
+ default: isLight ? '#F8FAFC' : '#0F172A',
+ paper: isLight ? '#FFFFFF' : '#1E293B',
},
text: {
- primary: isLight ? '#212121' : '#FFFFFF',
- secondary: isLight ? '#757575' : '#B0B0B0',
+ primary: isLight ? '#0F172A' : '#F1F5F9',
+ secondary: isLight ? '#64748B' : '#94A3B8',
},
- divider: isLight ? '#E0E0E0' : '#333333',
+ divider: isLight ? '#E2E8F0' : '#334155',
},
typography: {
fontFamily: "'Inter', 'Roboto', sans-serif",
@@ -94,56 +94,59 @@ export const getTheme = (mode) => {
},
shadows: [
'none',
- isLight ? '0px 2px 4px rgba(0, 0, 0, 0.05)' : '0px 2px 4px rgba(0, 0, 0, 0.3)',
- isLight ? '0px 4px 8px rgba(0, 0, 0, 0.08)' : '0px 4px 8px rgba(0, 0, 0, 0.4)',
- isLight ? '0px 8px 16px rgba(0, 0, 0, 0.1)' : '0px 8px 16px rgba(0, 0, 0, 0.5)',
- isLight ? '0px 12px 24px rgba(0, 0, 0, 0.12)' : '0px 12px 24px rgba(0, 0, 0, 0.6)',
- isLight ? '0px 16px 32px rgba(0, 0, 0, 0.14)' : '0px 16px 32px rgba(0, 0, 0, 0.7)',
- isLight ? '0px 20px 40px rgba(0, 0, 0, 0.16)' : '0px 20px 40px rgba(0, 0, 0, 0.8)',
- isLight ? '0px 24px 48px rgba(0, 0, 0, 0.18)' : '0px 24px 48px rgba(0, 0, 0, 0.9)',
- isLight ? '0px 2px 4px rgba(0, 0, 0, 0.05)' : '0px 2px 4px rgba(0, 0, 0, 0.3)',
- isLight ? '0px 4px 8px rgba(0, 0, 0, 0.08)' : '0px 4px 8px rgba(0, 0, 0, 0.4)',
- isLight ? '0px 8px 16px rgba(0, 0, 0, 0.1)' : '0px 8px 16px rgba(0, 0, 0, 0.5)',
- isLight ? '0px 12px 24px rgba(0, 0, 0, 0.12)' : '0px 12px 24px rgba(0, 0, 0, 0.6)',
- isLight ? '0px 16px 32px rgba(0, 0, 0, 0.14)' : '0px 16px 32px rgba(0, 0, 0, 0.7)',
- isLight ? '0px 20px 40px rgba(0, 0, 0, 0.16)' : '0px 20px 40px rgba(0, 0, 0, 0.8)',
- isLight ? '0px 24px 48px rgba(0, 0, 0, 0.18)' : '0px 24px 48px rgba(0, 0, 0, 0.9)',
- isLight ? '0px 28px 56px rgba(0, 0, 0, 0.2)' : '0px 28px 56px rgba(0, 0, 0, 1)',
- isLight ? '0px 32px 64px rgba(0, 0, 0, 0.22)' : '0px 32px 64px rgba(0, 0, 0, 1)',
- isLight ? '0px 36px 72px rgba(0, 0, 0, 0.24)' : '0px 36px 72px rgba(0, 0, 0, 1)',
- isLight ? '0px 40px 80px rgba(0, 0, 0, 0.26)' : '0px 40px 80px rgba(0, 0, 0, 1)',
- isLight ? '0px 44px 88px rgba(0, 0, 0, 0.28)' : '0px 44px 88px rgba(0, 0, 0, 1)',
- isLight ? '0px 48px 96px rgba(0, 0, 0, 0.3)' : '0px 48px 96px rgba(0, 0, 0, 1)',
- isLight ? '0px 52px 104px rgba(0, 0, 0, 0.32)' : '0px 52px 104px rgba(0, 0, 0, 1)',
- isLight ? '0px 56px 112px rgba(0, 0, 0, 0.34)' : '0px 56px 112px rgba(0, 0, 0, 1)',
- isLight ? '0px 60px 120px rgba(0, 0, 0, 0.36)' : '0px 60px 120px rgba(0, 0, 0, 1)',
- isLight ? '0px 64px 128px rgba(0, 0, 0, 0.38)' : '0px 64px 128px rgba(0, 0, 0, 1)',
+ isLight ? '0px 1px 2px rgba(0, 0, 0, 0.05)' : '0px 1px 3px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 1px 3px rgba(0, 0, 0, 0.1)' : '0px 2px 4px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 2px 4px rgba(0, 0, 0, 0.1)' : '0px 4px 6px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 4px 6px rgba(0, 0, 0, 0.1)' : '0px 5px 8px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 6px 8px rgba(0, 0, 0, 0.1)' : '0px 6px 10px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 8px 12px rgba(0, 0, 0, 0.1)' : '0px 8px 12px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 10px 16px rgba(0, 0, 0, 0.1)' : '0px 10px 16px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 1px 2px rgba(0, 0, 0, 0.05)' : '0px 1px 3px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 1px 3px rgba(0, 0, 0, 0.1)' : '0px 2px 4px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 2px 4px rgba(0, 0, 0, 0.1)' : '0px 4px 6px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 4px 6px rgba(0, 0, 0, 0.1)' : '0px 5px 8px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 6px 8px rgba(0, 0, 0, 0.1)' : '0px 6px 10px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 8px 12px rgba(0, 0, 0, 0.1)' : '0px 8px 12px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 10px 16px rgba(0, 0, 0, 0.1)' : '0px 10px 16px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 12px 20px rgba(0, 0, 0, 0.1)' : '0px 12px 20px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 14px 24px rgba(0, 0, 0, 0.1)' : '0px 14px 24px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 16px 28px rgba(0, 0, 0, 0.1)' : '0px 16px 28px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 18px 32px rgba(0, 0, 0, 0.1)' : '0px 18px 32px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 20px 36px rgba(0, 0, 0, 0.1)' : '0px 20px 36px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 22px 40px rgba(0, 0, 0, 0.1)' : '0px 22px 40px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 24px 44px rgba(0, 0, 0, 0.1)' : '0px 24px 44px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 26px 48px rgba(0, 0, 0, 0.1)' : '0px 26px 48px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 28px 52px rgba(0, 0, 0, 0.1)' : '0px 28px 52px rgba(0, 0, 0, 0.5)',
+ isLight ? '0px 30px 56px rgba(0, 0, 0, 0.1)' : '0px 30px 56px rgba(0, 0, 0, 0.5)',
],
components: {
MuiCard: {
styleOverrides: {
root: {
- borderRadius: '16px',
- boxShadow: isLight ? '0px 4px 20px rgba(0, 0, 0, 0.05)' : '0px 2px 8px rgba(0, 0, 0, 0.4)',
- backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
- backgroundImage: isLight ? 'none' : 'linear-gradient(rgba(255,255,255,0.05), rgba(255,255,255,0.05))',
- border: isLight ? '1px solid transparent' : '1px solid #333333',
- transition: 'box-shadow 0.2s ease',
+ borderRadius: '12px',
+ boxShadow: isLight ? '0px 1px 3px rgba(0, 0, 0, 0.08)' : '0px 1px 3px rgba(0, 0, 0, 0.5)',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E293B',
+ backgroundImage: 'none',
+ border: isLight ? '1px solid #E2E8F0' : '1px solid #334155',
+ transition: 'box-shadow 0.2s ease, border-color 0.2s ease',
+ '&:hover': {
+ borderColor: isLight ? '#CBD5E1' : '#475569',
+ },
},
},
},
MuiButton: {
styleOverrides: {
root: {
- borderRadius: '12px',
+ borderRadius: '8px',
textTransform: 'none',
fontWeight: 500,
- padding: '10px 20px',
+ padding: '8px 16px',
},
contained: {
- boxShadow: isLight ? '0px 2px 4px rgba(0, 0, 0, 0.1)' : '0px 2px 8px rgba(33, 150, 243, 0.3)',
+ boxShadow: 'none',
'&:hover': {
- boxShadow: isLight ? '0px 4px 8px rgba(0, 0, 0, 0.15)' : '0px 4px 12px rgba(33, 150, 243, 0.4)',
+ boxShadow: 'none',
},
},
},
@@ -159,8 +162,8 @@ export const getTheme = (mode) => {
MuiPaper: {
styleOverrides: {
root: {
- borderRadius: '12px',
- backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
+ borderRadius: '8px',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E293B',
backgroundImage: 'none',
},
},
@@ -169,13 +172,13 @@ export const getTheme = (mode) => {
styleOverrides: {
root: {
'& .MuiOutlinedInput-root': {
- borderRadius: '10px',
- backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
+ borderRadius: '8px',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E293B',
'& fieldset': {
- borderColor: isLight ? 'rgba(0, 0, 0, 0.23)' : '#333333',
+ borderColor: isLight ? '#E2E8F0' : '#334155',
},
'&:hover fieldset': {
- borderColor: isLight ? 'rgba(0, 0, 0, 0.4)' : '#444444',
+ borderColor: isLight ? '#CBD5E1' : '#475569',
},
},
},
@@ -184,17 +187,18 @@ export const getTheme = (mode) => {
MuiDrawer: {
styleOverrides: {
paper: {
- backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
- borderRight: isLight ? '1px solid rgba(0, 0, 0, 0.12)' : '1px solid #333333',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E293B',
+ borderRight: isLight ? '1px solid #E2E8F0' : '1px solid #334155',
},
},
},
MuiAppBar: {
styleOverrides: {
root: {
- backgroundColor: isLight ? '#FFFFFF' : '#1E1E1E',
- color: isLight ? '#212121' : '#FFFFFF',
- boxShadow: isLight ? '0px 1px 3px rgba(0, 0, 0, 0.08)' : 'none',
+ backgroundColor: isLight ? '#FFFFFF' : '#1E293B',
+ color: isLight ? '#0F172A' : '#F1F5F9',
+ boxShadow: isLight ? '0px 1px 2px rgba(0, 0, 0, 0.05)' : 'none',
+ borderBottom: isLight ? '1px solid #E2E8F0' : '1px solid #334155',
},
},
},
@@ -204,13 +208,13 @@ export const getTheme = (mode) => {
borderRadius: '8px',
margin: '4px 8px',
'&.Mui-selected': {
- backgroundColor: isLight ? 'rgba(25, 118, 210, 0.08)' : 'rgba(33, 150, 243, 0.16)',
+ backgroundColor: isLight ? 'rgba(37, 99, 235, 0.08)' : 'rgba(96, 165, 250, 0.12)',
'&:hover': {
- backgroundColor: isLight ? 'rgba(25, 118, 210, 0.12)' : 'rgba(33, 150, 243, 0.24)',
+ backgroundColor: isLight ? 'rgba(37, 99, 235, 0.12)' : 'rgba(96, 165, 250, 0.16)',
},
},
'&:hover': {
- backgroundColor: isLight ? 'rgba(0, 0, 0, 0.04)' : 'rgba(255, 255, 255, 0.05)',
+ backgroundColor: isLight ? 'rgba(15, 23, 42, 0.04)' : 'rgba(241, 245, 249, 0.05)',
},
},
},
@@ -218,18 +222,18 @@ export const getTheme = (mode) => {
MuiSkeleton: {
styleOverrides: {
root: {
- backgroundColor: isLight ? undefined : '#333333',
+ backgroundColor: isLight ? undefined : '#334155',
'&::after': {
background: isLight
? undefined
- : 'linear-gradient(90deg, rgba(51,51,51,0) 0%, rgba(68,68,68,0.6) 50%, rgba(51,51,51,0) 100%)',
+ : 'linear-gradient(90deg, rgba(51,65,85,0) 0%, rgba(71,85,105,0.6) 50%, rgba(51,65,85,0) 100%)',
},
},
},
},
},
shape: {
- borderRadius: 12,
+ borderRadius: 8,
},
});
};