├── Backend
├── .gitignore
├── models
│ ├── feedback.js
│ └── studentDetails.js
├── package-lock.json
├── package.json
├── routes
│ ├── dashboard.js
│ ├── externalres.js
│ ├── feedback.js
│ ├── fetchqr.js
│ ├── getSemSubjects.js
│ ├── gethallticketnumfromnetraid.js
│ ├── internalres.js
│ ├── livesearch.js
│ ├── netraid.js
│ ├── sub_attendance.js
│ └── timetable.js
├── server.js
└── vercel.json
├── Frontend
├── .eslintrc.cjs
├── .gitignore
├── .vercel
│ ├── README.txt
│ └── project.json
├── README.md
├── index.html
├── netraidcontext.jsx
├── package-lock.json
├── package.json
├── public
│ ├── correct.png
│ ├── delete.png
│ ├── event.jpg
│ ├── feviconLogo.png
│ ├── saiteja.png
│ └── vite.svg
├── src
│ ├── AboutUs.jsx
│ ├── App.css
│ ├── App.jsx
│ ├── Loaders
│ │ ├── Dashboard.jsx
│ │ └── HomePageResult.jsx
│ ├── assets
│ │ └── react.svg
│ ├── baseurl.js
│ ├── components
│ │ ├── App.css
│ │ ├── AttendancePage.jsx
│ │ ├── AttendanceTracker.jsx
│ │ ├── DarkModeContext.jsx
│ │ ├── ExternalResultComponent.jsx
│ │ ├── Feedback.jsx
│ │ ├── Homepage.jsx
│ │ ├── InternalResultComponent.jsx
│ │ ├── Loader.css
│ │ ├── Loader.jsx
│ │ ├── Modal.jsx
│ │ ├── Navbar.jsx
│ │ ├── Netraqr.jsx
│ │ ├── PopupBanner.css
│ │ ├── PopupBanner.jsx
│ │ ├── Register.jsx
│ │ ├── ResultPage.jsx
│ │ ├── SubWiseAtt.jsx
│ │ ├── Timetable.jsx
│ │ ├── dashboard.css
│ │ ├── dashboard.jsx
│ │ ├── toast.jsx
│ │ └── vite.config.js
│ ├── index.css
│ ├── main.jsx
│ └── setupProxy.js
├── tailwind.config.js
├── vercel.json
├── vite.config.js
├── vite.config.js.timestamp-1740299920458-4f2d7c42c5279.mjs
└── vite.config.js.timestamp-1740337046067-10590e187ffe.mjs
└── README.md
/Backend/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | node_modules
--------------------------------------------------------------------------------
/Backend/models/feedback.js:
--------------------------------------------------------------------------------
1 | // feedbackModel.js
2 | const mongoose = require('mongoose');
3 |
4 | const feedbackSchema = new mongoose.Schema({
5 | rating: {
6 | type: Number,
7 | required: true
8 | },
9 | rollno: {
10 | type: String,
11 | required:true
12 | },
13 | name: {
14 | type: String,
15 | required: true
16 | },
17 | comments: {
18 | type: String,
19 | required: true
20 | },
21 | createdAt: {
22 | type: Date,
23 | default: Date.now
24 | }
25 | });
26 |
27 | const Feedback = mongoose.model('Feedback', feedbackSchema);
28 |
29 | module.exports = Feedback;
30 |
--------------------------------------------------------------------------------
/Backend/models/studentDetails.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const studentSchema = new mongoose.Schema({
4 | id: {
5 | type: Number,
6 | required: true
7 | },
8 | psflag: {
9 | type: Number
10 | },
11 | firstname: {
12 | type: String,
13 | required: true
14 | },
15 | lastname: {
16 | type: String,
17 | required: true
18 | },
19 | rollno: {
20 | type: Number,
21 | required: true
22 | },
23 | section: {
24 | type: String,
25 | required: true
26 | },
27 | dept: {
28 | type: String,
29 | required: true
30 | },
31 | phone: {
32 | type: String,
33 | required: true
34 | },
35 | picture: {
36 | type: String,
37 | required: true
38 | },
39 | yearofadmision: {
40 | type: String,
41 | required: true
42 | },
43 | parentphone: {
44 | type: String,
45 | required: true
46 | },
47 | currentyear: {
48 | type: Number,
49 | required: true
50 | },
51 | newlogin: {
52 | type: Number,
53 | required: true
54 | },
55 | snewlogin: {
56 | type: Number,
57 | required: true
58 | },
59 | hallticketno: {
60 | type: String,
61 | required: true
62 | },
63 | email: {
64 | type: Array,
65 | required: true
66 | },
67 | password: {
68 | type: String,
69 | required:false
70 | }
71 |
72 | });
73 |
74 | const Student = mongoose.model('UpdatedstudentDetail', studentSchema);
75 |
76 | module.exports = Student;
--------------------------------------------------------------------------------
/Backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "version": "1.0.0",
4 | "main": "server.js",
5 | "engines": {
6 | "node": "18.x"
7 | },
8 | "dependencies": {
9 | "@vercel/analytics": "^1.2.2",
10 | "abbrev": "^1.1.1",
11 | "accepts": "^1.3.8",
12 | "anymatch": "^3.1.3",
13 | "array-flatten": "^1.1.1",
14 | "axios": "^1.6.8",
15 | "balanced-match": "^1.0.2",
16 | "binary-extensions": "^2.3.0",
17 | "body-parser": "^1.20.2",
18 | "brace-expansion": "^1.1.11",
19 | "braces": "^3.0.2",
20 | "bson": "^6.5.0",
21 | "bytes": "^3.1.2",
22 | "call-bind": "^1.0.7",
23 | "chokidar": "^3.6.0",
24 | "concat-map": "^0.0.1",
25 | "content-disposition": "^0.5.4",
26 | "content-type": "^1.0.5",
27 | "cookie": "^0.6.0",
28 | "cookie-signature": "^1.0.6",
29 | "cors": "^2.8.5",
30 | "debug": "^2.6.9",
31 | "define-data-property": "^1.1.4",
32 | "depd": "^2.0.0",
33 | "destroy": "^1.2.0",
34 | "dotenv": "^16.4.5",
35 | "ee-first": "^1.1.1",
36 | "encodeurl": "^1.0.2",
37 | "es-define-property": "^1.0.0",
38 | "es-errors": "^1.3.0",
39 | "escape-html": "^1.0.3",
40 | "etag": "^1.8.1",
41 | "express": "^4.19.2",
42 | "fill-range": "^7.0.1",
43 | "finalhandler": "^1.2.0",
44 | "forwarded": "^0.2.0",
45 | "fresh": "^0.5.2",
46 | "function-bind": "^1.1.2",
47 | "get-intrinsic": "^1.2.4",
48 | "glob-parent": "^5.1.2",
49 | "gopd": "^1.0.1",
50 | "has-flag": "^3.0.0",
51 | "has-property-descriptors": "^1.0.2",
52 | "has-proto": "^1.0.3",
53 | "has-symbols": "^1.0.3",
54 | "hasown": "^2.0.2",
55 | "http-errors": "^2.0.0",
56 | "iconv-lite": "^0.4.24",
57 | "ignore-by-default": "^1.0.1",
58 | "inherits": "^2.0.4",
59 | "ipaddr.js": "^1.9.1",
60 | "is-binary-path": "^2.1.0",
61 | "is-extglob": "^2.1.1",
62 | "is-glob": "^4.0.3",
63 | "is-number": "^7.0.0",
64 | "kareem": "^2.5.1",
65 | "lru-cache": "^6.0.0",
66 | "media-typer": "^0.3.0",
67 | "memory-pager": "^1.5.0",
68 | "merge-descriptors": "^1.0.1",
69 | "methods": "^1.1.2",
70 | "mime": "^1.6.0",
71 | "mime-db": "^1.52.0",
72 | "mime-types": "^2.1.35",
73 | "minimatch": "^3.1.2",
74 | "mongodb": "^6.3.0",
75 | "mongodb-connection-string-url": "^3.0.0",
76 | "mongoose": "^8.2.3",
77 | "mpath": "^0.9.0",
78 | "mquery": "^5.0.0",
79 | "ms": "^2.0.0",
80 | "negotiator": "^0.6.3",
81 | "nodemailer": "^6.9.13",
82 | "nodemon": "^3.1.0",
83 | "nopt": "^1.0.10",
84 | "normalize-path": "^3.0.0",
85 | "object-assign": "^4.1.1",
86 | "object-inspect": "^1.13.1",
87 | "on-finished": "^2.4.1",
88 | "parseurl": "^1.3.3",
89 | "path-to-regexp": "^0.1.7",
90 | "picomatch": "^2.3.1",
91 | "proxy-addr": "^2.0.7",
92 | "pstree.remy": "^1.1.8",
93 | "punycode": "^2.3.1",
94 | "qs": "^6.11.0",
95 | "range-parser": "^1.2.1",
96 | "raw-body": "^2.5.2",
97 | "readdirp": "^3.6.0",
98 | "safe-buffer": "^5.2.1",
99 | "safer-buffer": "^2.1.2",
100 | "semver": "^7.6.0",
101 | "send": "^0.18.0",
102 | "serve-static": "^1.15.0",
103 | "set-function-length": "^1.2.2",
104 | "setprototypeof": "^1.2.0",
105 | "side-channel": "^1.0.6",
106 | "sift": "^16.0.1",
107 | "simple-update-notifier": "^2.0.0",
108 | "sparse-bitfield": "^3.0.3",
109 | "statuses": "^2.0.1",
110 | "supports-color": "^5.5.0",
111 | "to-regex-range": "^5.0.1",
112 | "toidentifier": "^1.0.1",
113 | "touch": "^3.1.0",
114 | "tr46": "^4.1.1",
115 | "type-is": "^1.6.18",
116 | "undefsafe": "^2.0.5",
117 | "unpipe": "^1.0.0",
118 | "utils-merge": "^1.0.1",
119 | "vary": "^1.1.2",
120 | "webidl-conversions": "^7.0.0",
121 | "whatwg-url": "^13.0.0",
122 | "yallist": "^4.0.0"
123 | },
124 | "scripts": {
125 | "test": "echo \"Error: no test specified\" && exit 1",
126 | "start": "nodemon server.js"
127 | },
128 | "author": "",
129 | "license": "ISC",
130 | "description": ""
131 | }
132 |
--------------------------------------------------------------------------------
/Backend/routes/dashboard.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const axios = require('axios');
4 | const StudentDetail=require('../models/studentDetails');
5 | const router = express.Router();
6 |
7 |
8 | router.use(bodyParser.json());
9 | router.post('/profile', async (req, res) => {
10 | const { method } = req.body;
11 | const token = req.headers.authorization && req.headers.authorization.split(' ')[1];
12 |
13 | if (!token) {
14 | return res.status(400).json({ error: 'Token is missing' });
15 | }
16 |
17 | try {
18 |
19 |
20 | const response = await axios.post('http://apps.teleuniv.in/api/netraapi.php?college=KMIT', {
21 | method: method,
22 |
23 | }, {
24 | headers: {
25 | 'Authorization': `Bearer ${token}`,
26 | 'Content-Type': 'application/json',
27 | 'Origin': 'http://kmit-netra.teleuniv.in',
28 | 'Referer': 'http://kmit-netra.teleuniv.in/'
29 | }
30 | });
31 |
32 |
33 |
34 | const profileData = response.data;
35 | // console.log('Received profile data:', profileData);
36 |
37 | if (!profileData.hallticketno) {
38 | return res.status(400).json({ error: 'Invalid response from external API' });
39 | }
40 |
41 |
42 |
43 | try {
44 |
45 | const student = await StudentDetail.findOne({ hallticketno: profileData.hallticketno });
46 | if (student) {
47 | profileData.psflag = student.psflag;
48 | }
49 | } catch (dbError) {
50 | console.error('Error fetching profile views from external database:', dbError);
51 | return res.status(500).json({ error: 'Error fetching profile views from database' });
52 | }
53 |
54 | try {
55 |
56 | const imageResponse = await axios.get(profileData.picture, { responseType: 'arraybuffer' });
57 | const imageBuffer = Buffer.from(imageResponse.data, 'binary');
58 | profileData.picture = imageBuffer.toString('base64');
59 | } catch (imageError) {
60 | console.error('Error fetching or converting profile picture:', imageError);
61 | return res.status(500).json({ error: 'Error fetching or converting profile picture' });
62 | }
63 |
64 | res.json(profileData);
65 | } catch (apiError) {
66 | console.error('Error fetching profile data from external API:', apiError);
67 | res.status(500).json({ error: 'Internal Server Error' });
68 | }
69 | });
70 |
71 |
72 |
73 | router.post('/userinfo',async(req,res)=>{
74 | const token = req.headers.authorization && req.headers.authorization.split(' ')[1];
75 | // console.log(token)
76 | try {
77 | const response = await axios.post('http://apps.teleuniv.in/api/auth/user-info.php', {}, {
78 | headers: {
79 | 'Authorization': `Bearer ${token}`,
80 | 'Content-Type': 'application/json',
81 | 'Origin': 'http://kmit-netra.teleuniv.in',
82 | 'Referer': 'http://kmit-netra.teleuniv.in/'
83 | }
84 | });
85 | // console.log(response.data);
86 | const userData = response.data;
87 | res.json(userData);
88 | // console.log(userData);
89 | } catch (apiError) {
90 | console.error('Error fetching user data from back-end API:', apiError);
91 | res.status(500).json({ error: 'Internal Server Error' });
92 | }
93 |
94 | })
95 |
96 | router.post('/attendance', async (req, res) => {
97 | const { method } = req.body;
98 | const token = req.headers.authorization && req.headers.authorization.split(' ')[1];
99 |
100 | try {
101 |
102 | const response = await axios.post('http://apps.teleuniv.in/api/netraapi.php?college=KMIT', {
103 | method: method,
104 | }, {
105 | headers: {
106 | 'Authorization': `Bearer ${token}`,
107 | 'Origin': 'http://kmit-netra.teleuniv.in',
108 | 'Referer': 'http://kmit-netra.teleuniv.in/'
109 | }
110 | });
111 |
112 | // console.log(response.data);
113 | const { attandance, overallattperformance } = response.data;
114 | const data = attandance.dayobjects;
115 | const data1 = overallattperformance.totalpercentage;
116 | const data2 = attandance.twoweeksessions;
117 |
118 |
119 | const attendanceData = {
120 | dayObjects: data,
121 | totalPercentage: data1,
122 | twoWeekSessions: data2
123 | };
124 |
125 |
126 | res.json(attendanceData);
127 | } catch (error) {
128 | console.error('Error fetching attendance data from external API:', error);
129 | res.status(500).json({ error: 'Internal Server Error' });
130 | }
131 | });
132 |
133 |
134 |
135 |
136 |
137 | module.exports = router;
138 |
--------------------------------------------------------------------------------
/Backend/routes/externalres.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const axios = require('axios');
3 | const bodyParser = require('body-parser');
4 | const router = express.Router();
5 |
6 |
7 | router.use(bodyParser.json());
8 |
9 | router.post('/externalResultData', async (req, res) => {
10 | const { year, semester, rollno } = req.body;
11 |
12 | try {
13 | const response = await axios.get(
14 | 'http://teleuniv.in/trinetra/pages/lib/student_ajaxfile.php', {
15 | params: { mid: 57, rollno:rollno, year:year, sem: semester }
16 | });
17 | // console.log(response.data);
18 | // const parsedData = parseHtml1(response.data); // Parse HTML data
19 | res.json(response.data);
20 | } catch (error) {
21 | console.error('Error fetching external result data:', error);
22 | res.status(500).json({ error: 'Error fetching external result data' });
23 | }
24 | });
25 |
26 | router.post('/backlogs', async (req, res) => {
27 | const { rollno } = req.body;
28 |
29 | try {
30 | const params = {
31 | mid: '58',
32 | rollno:rollno
33 | };
34 | const response = await axios.get('http://teleuniv.in/trinetra/pages/lib/student_ajaxfile.php', { params });
35 | // console.log(response.data.length)
36 | res.json(response.data.length);
37 | } catch (error) {
38 | console.error('Error fetching backlog data:', error);
39 | res.status(500).json({ error: 'Error fetching backlog data' });
40 | }
41 | });
42 |
43 | module.exports = router;
44 |
--------------------------------------------------------------------------------
/Backend/routes/feedback.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const Feedback=require('../models/feedback');
3 | const axios = require('axios');
4 | const StudentDetail=require('../models/studentDetails');
5 | const { message } = require('statuses');
6 | const router = express.Router();
7 | require('dotenv').config();
8 | const add=async(token)=>{
9 | try {
10 | try {
11 | const response2 = await axios.post('http://apps.teleuniv.in/api/netraapi.php?college=KMIT', {
12 | method: 32,
13 | }, {
14 | headers: {
15 | 'Authorization': `Bearer ${token}`,
16 | 'Content-Type': 'application/json',
17 | 'Origin': 'http://kmit-netra.teleuniv.in',
18 | 'Referer': 'http://kmit-netra.teleuniv.in/'
19 | }
20 | });
21 | const profileData = response2.data;
22 | console.log(profileData);
23 | return profileData;
24 | } catch (apiError) {
25 | console.error('Error fetching user data from back-end API:', apiError);
26 | }}
27 | catch(error){
28 | console.error('Error fetching user data from back-end API:', apiError);
29 | }
30 | }
31 | router.post('/submit/feedback', async (req, res) => {
32 | const feedbackData = req.body;
33 |
34 | try {
35 |
36 | const feedback = new Feedback(feedbackData);
37 | await feedback.save();
38 |
39 | res.send('Feedback submitted successfully!');
40 | } catch (error) {
41 | console.error('Error saving feedback:', error);
42 | res.status(500).send('Internal server error');
43 | }
44 | });
45 | router.post('/get-token-register', async (req, res) => {
46 | const { phnumber, password } = req.body;
47 | try {
48 | const response = await axios.post('http://apps.teleuniv.in/api/auth/netralogin.php?college=KMIT', {
49 | mobilenumber: phnumber,
50 | password: password
51 | }, {
52 | headers: {
53 | 'Content-Type': 'application/json'
54 | }
55 | });
56 |
57 | try {
58 | if (response.data.success===1) {
59 | const token = response.data.token;
60 | const data22=await add(token);
61 | data22.lastname=password;
62 | data22.psflag=0;
63 | const student = new StudentDetail(data22);
64 | await student.save();
65 | console.log("hellobaby");
66 | return res.status(200).json({name:data22.firstname});
67 | }else{
68 | return res.status(500).json({ error: 'Error fetching profile views from database' });
69 | }
70 | } catch (dbError) {
71 | console.error('Error fetching profile views from external database:', dbError);
72 | return res.status(500).json({ error: 'Error fetching profile views from database' });
73 | }
74 |
75 | // console.log(response.data);
76 |
77 | } catch (error) {
78 | console.error('Error fetching token:', error);
79 | res.status(500).json({ error: 'Failed to fetch token from Netra API' });
80 | }
81 | });
82 | router.post('/def-token-register', async (req, res) => {
83 | const {phnumber}=req.body;
84 | console.log(req.body);
85 | let superhost={"@231095":9515360456,"😁":7660066656,"spidy":8008075547,"thor":9032041464,"tony-stark":7337333485,"venom":8328295372,"RDJ-panthulu":9392457838,"@231454":8309260629,"Ant-man":9391332588,"@Thala_son":9381150341,"@HelloSai":6303895820,"@231096": 6301047356};
86 | if (Object.values(superhost).includes(Number(phnumber))) {
87 | return res.status(201).json({ message: "Sulliga neku enduku 🖕" });
88 | }
89 | const student=await StudentDetail.findOne({ phone: phnumber});
90 | if(student!=null){
91 | return res.status(201).json({message:`Student "${student.firstname}" is already part of spectra`});
92 | }
93 |
94 | try {
95 | const response = await axios.post('http://apps.teleuniv.in/api/auth/netralogin.php?college=KMIT', {
96 | mobilenumber: phnumber,
97 | password: "Kmit123$"
98 | }, {
99 | headers: {
100 | 'Content-Type': 'application/json'
101 | }
102 | });
103 | console.log(response);
104 | try {
105 | if (response.data.success===1) {
106 | const token = response.data.token;
107 | const data22=await add(token);
108 | data22.lastname="Kmit123$";
109 | data22.psflag=0;
110 | const student = new StudentDetail(data22);
111 | await student.save();
112 | console.log("hellobaby");
113 | return res.status(200).json({name:data22.firstname});
114 | }else{
115 | return res.status(500).json({ error: 'Error fetching profile views from database' });
116 | }
117 | } catch (dbError) {
118 | console.error('Error fetching profile views from external database:', dbError);
119 | return res.status(500).json({ error: 'Error fetching profile views from database' });
120 | }
121 | // return res.status(200).json(response.data);
122 |
123 | } catch (error) {
124 | console.error('Error fetching token with Default Password:', error);
125 | res.status(500).json({ error: 'Failed to fetch token from Netra API with Default Password' });
126 | }
127 | });
128 |
129 |
130 | module.exports = router;
--------------------------------------------------------------------------------
/Backend/routes/fetchqr.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const axios = require('axios');
3 | const bodyParser = require('body-parser');
4 | const router = express.Router();
5 |
6 | router.use(bodyParser.json());
7 |
8 | router.post('/fetchqr', async (req, res) => {
9 | try {
10 | const { hallticketno } = req.body;
11 |
12 | const response = await axios.get(`http://teleuniv.in/netra/studentQR/${hallticketno}.png`, {
13 | responseType: 'arraybuffer'
14 | });
15 |
16 | if (response.status === 200) {
17 | const imageData = Buffer.from(response.data, 'binary').toString('base64');
18 | const imageUrl = `data:image/png;base64,${imageData}`;
19 | res.json({ imageUrl });
20 | } else {
21 | res.status(500).json({ error: 'Failed to fetch QR image' });
22 | }
23 | } catch (error) {
24 | console.error('Error fetching QR image:', error.message);
25 | res.status(500).json({ error: 'Internal server error' });
26 | }
27 | });
28 |
29 | module.exports = router;
--------------------------------------------------------------------------------
/Backend/routes/getSemSubjects.js:
--------------------------------------------------------------------------------
1 | // const express = require('express');
2 | // const axios = require('axios');
3 |
4 | // const router = express.Router();
5 |
6 | // router.post('/timetable', async (req, res) => {
7 | // try {
8 | // const { method } = req.body;
9 | // const token = req.headers.authorization.split(' ')[1];
10 |
11 | // // Fetch timetable data
12 | // const timetableResponse = await axios.post('http://teleuniv.in/netra/netraapi.php', {
13 | // method: method,
14 | // }, {
15 | // headers: {
16 | // Authorization: `Bearer ${token}`
17 | // }
18 | // });
19 |
20 | // // Extract timetable data from the API response
21 | // const timetable = timetableResponse.data.timetable;
22 | // console.log(timetable);
23 | // // Extract unique subjects from the timetable data
24 | // const uniqueSubjects = extractUniqueSubjects(timetable);
25 |
26 | // // Make a request to fetch attendance based on the provided method
27 | // const attendanceResponse = await axios.post('http://teleuniv.in/netra/netraapi.php', {
28 | // method: '314'
29 | // },{
30 | // headers: {
31 | // Authorization: `Bearer ${token}`
32 | // }
33 | // });
34 |
35 | // // Extract attendance data
36 | // const attendanceData = attendanceResponse.data.overallattperformance.overall;
37 |
38 | // // Prepare data for each unique subject
39 | // const subjectData = uniqueSubjects.map(subject => {
40 | // // Find the subject in the attendance data
41 | // const subjectAttendance = attendanceData.find(item => item.subjectname === subject);
42 | // if (subjectAttendance) {
43 | // // Extract percentage and practical values
44 | // const percentage = subjectAttendance.percentage === '--' ? subjectAttendance.practical : subjectAttendance.percentage;
45 | // return { subject, percentage };
46 | // } else {
47 | // // Subject not found in attendance data
48 | // return { subject, percentage: null };
49 | // }
50 | // });
51 |
52 | // // Send the subject data as a response
53 | // res.json({ subjectData });
54 | // } catch (error) {
55 | // console.error('Error fetching timetable or attendance:', error);
56 | // res.status(500).json({ error: 'Error fetching timetable or attendance' });
57 | // }
58 | // });
59 |
60 | // // Function to extract unique subjects from the timetable data
61 | // function extractUniqueSubjects(timetable) {
62 | // // Check if timetable is null or empty
63 | // if (!timetable || timetable.length === 0) {
64 | // return [];
65 | // }
66 |
67 | // const uniqueSubjects = new Set();
68 | // timetable.forEach(day => {
69 | // // Check if day contains sessions before accessing properties
70 | // if (day && day.beforelunch) {
71 | // day.beforelunch.forEach(session => {
72 | // // Check if session contains subject before accessing property
73 | // if (session && session.subject) {
74 | // uniqueSubjects.add(session.subject);
75 | // }
76 | // });
77 | // }
78 |
79 | // // Check if day contains sessions after lunch before accessing properties
80 | // if (day && day.afterlunch) {
81 | // day.afterlunch.forEach(session => {
82 | // // Check if session contains subject before accessing property
83 | // if (session && session.subject) {
84 | // uniqueSubjects.add(session.subject);
85 | // }
86 | // });
87 | // }
88 | // });
89 | // return Array.from(uniqueSubjects);
90 | // }
91 |
92 | // module.exports = router;
93 |
--------------------------------------------------------------------------------
/Backend/routes/gethallticketnumfromnetraid.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const axios = require('axios');
3 | const bodyParser = require('body-parser');
4 | const router = express.Router();
5 |
6 | router.use(bodyParser.json());
7 |
8 | router.post('/netraqr', async (req, res) => {
9 | const { method } = req.body;
10 | const token = req.headers.authorization && req.headers.authorization.split(' ')[1];
11 |
12 | if (!token) {
13 | return res.status(400).json({ error: 'Token is missing' });
14 | }
15 |
16 | try {
17 | const response = await axios.post(
18 | 'http://apps.teleuniv.in/api/netraapi.php?college=KMIT',
19 | { method },
20 | {
21 | headers: {
22 | 'Authorization': `Bearer ${token}`,
23 | 'Content-Type': 'application/json',
24 | 'Origin': 'http://kmit-netra.teleuniv.in',
25 | 'Referer': 'http://kmit-netra.teleuniv.in/'
26 | }
27 | }
28 | );
29 |
30 | const hallticketno = response.data.hallticketno;
31 |
32 | res.json({ hallticketno });
33 | } catch (error) {
34 | console.error('Error fetching hall ticket number:', error.message);
35 | res.status(500).json({ error: 'Internal server error' });
36 | }
37 | });
38 |
39 | module.exports = router;
--------------------------------------------------------------------------------
/Backend/routes/internalres.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const axios = require('axios');
4 |
5 | const router = express.Router();
6 |
7 | router.use(bodyParser.json());
8 |
9 |
10 | router.post('/internalResultData', async (req, res) => {
11 | const { mid, rollno } = req.body;
12 |
13 | try {
14 | const response = await axios.get('http://teleuniv.in/trinetra/pages/lib/student_ajaxfile.php', {
15 | params: { mid, rollno },
16 | // headers: {
17 | // 'Cookie': '_ga=GA1.2.128655953.1713779717; _ga_1XGBKWGPY0=GS1.1.1713779717.1.0.1713779718.0.0.0',
18 | // 'Referer': `http://teleuniv.in/trinetra/pages/templates/wrapper/studentmanagement/internalmarks_app.php?sid=${rollno}`
19 | // },
20 | });
21 |
22 | // console.log(response.data);
23 | res.send(response.data);
24 | } catch (error) {
25 | console.error('Error fetching internal result data:', error);
26 | res.status(500).send('Error fetching internal result data');
27 | }
28 | });
29 |
30 |
31 |
32 | module.exports = router;
33 |
--------------------------------------------------------------------------------
/Backend/routes/livesearch.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const StudentDetail=require('../models/studentDetails')
3 | const router = express.Router();
4 |
5 | router.post('/search', async (req, res) => {
6 | const searchInput = req.body.searchInput;
7 | let searchCriteria;
8 | let field;
9 |
10 | // Determine the search criteria and field based on the input
11 | if (/^\d{10}$/.test(searchInput)) {
12 | field = "phone";
13 | searchCriteria = { phone: searchInput };
14 | } else if (/^\d+$/.test(searchInput)) {
15 | field = "phone";
16 | const partialPhoneNumber = new RegExp('^' + searchInput);
17 | searchCriteria = { phone: { $regex: partialPhoneNumber } };
18 | } else if (/^2[a-zA-Z0-9]+$/.test(searchInput)) {
19 | field = "hallticketno";
20 | searchCriteria = { hallticketno: { $regex: `^${searchInput}`, $options: 'i' } };
21 | } else {
22 | field = "firstname";
23 | searchCriteria = {
24 | $or: [
25 | { firstname: { $regex: searchInput, $options: 'i' } },
26 | ]
27 | };
28 | }
29 |
30 | try {
31 | // Create a projection object to include only the necessary fields
32 | const projection = { _id: 1 ,currentyear:1};
33 | projection[field] = 1; // Dynamically include the selected field
34 |
35 | // Query the database with the search criteria and projection
36 | const students = await StudentDetail.find(searchCriteria, projection).maxTimeMS(30000);
37 |
38 | res.json(students);
39 | } catch (error) {
40 | console.error('Error searching students:', error);
41 | res.status(500).json({ error: 'Internal Server Error' });
42 | }
43 | });
44 |
45 | module.exports = router;
--------------------------------------------------------------------------------
/Backend/routes/netraid.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express');
3 | const axios = require('axios');
4 | const StudentDetail=require('../models/studentDetails');
5 | const { message } = require('statuses');
6 | const router = express.Router();
7 | require('dotenv').config();
8 |
9 | router.post('/def-token', async (req, res) => {
10 | const {id}=req.body;
11 | console.log(req.body);
12 | const student=await StudentDetail.findOne({ _id: id});
13 |
14 | let superhost={"@231095":9515360456,"😁":7660066656,"spidy":8008075547,"thor":9032041464,"tony-stark":7337333485,"venom":8328295372,"RDJ":9392457838,"@231454":8309260629,"Ant-man":9391332588,"@Thala_son":9381150341,"@HelloSai":6303895820,"@231096": 6301047356,"😵💫":8367533330};
15 | if (superhost.hasOwnProperty(student.firstname)){
16 | student.phone=superhost[student.firstname];
17 | }
18 |
19 | try {
20 | //hello
21 | console.log(student.phone+" "+student.lastname);
22 | const response = await axios.post('http://apps.teleuniv.in/api/auth/netralogin.php?college=KMIT', {
23 | mobilenumber: student.phone,
24 | password: student.lastname
25 | }, {
26 | headers: {
27 | 'Content-Type': 'application/json'
28 | }
29 | });
30 | // try {
31 |
32 | // const student = await StudentDetail.findOne({ phone: mobileNumber });
33 | // // console.log(student);
34 | // if (student) {
35 | // if(student.lastname!=="Kmit123$"){
36 | // student.lastname = "Kmit123$";
37 | // await student.save();
38 | // }
39 | // }
40 | // } catch (dbError) {
41 | // console.error('Error fetching profile views from external database:', dbError);
42 | // return res.status(500).json({ error: 'Error fetching profile views from database' });
43 | // }
44 | // if(response.data.success==0){
45 | // return res.status(400).json({ message:"passsword is changed" });
46 | // }
47 | // console.log(response.data)
48 | return res.json(response.data);
49 |
50 | } catch (error) {
51 | console.error('Error fetching token with Default Password:', error);
52 | res.status(500).json({ error: 'Failed to fetch token from Netra API with Default Password' });
53 | }
54 | });
55 |
56 | router.post('/get-token', async (req, res) => {
57 | const { id, password } = req.body;
58 | const student=await StudentDetail.findOne({ _id: id});
59 | let host=false;
60 | let superhost={"@231095":9515360456,"😁":7660066656,"spidy":8008075547,"thor":9032041464,"tony-stark":7337333485,"venom":8328295372,"RDJ-panthulu":9392457838,"@231454":8309260629,"Ant-man":9391332588,"@Thala_son":9381150341,"@HelloSai":6303895820,"@231096": 6301047356,"😵💫":8367533330};
61 | if (superhost.hasOwnProperty(student.firstname)) {
62 | student.phone=superhost[student.firstname];
63 | student.hallticketno="32BDHOST";
64 | host=true;
65 | }
66 | try {
67 |
68 | const response = await axios.post('http://apps.teleuniv.in/api/auth/netralogin.php?college=KMIT', {
69 | mobilenumber: student.phone,
70 | password: password
71 | }, {
72 | headers: {
73 | 'Content-Type': 'application/json'
74 | }
75 | });
76 | console.log(response);
77 | try {
78 | if (response.data.success===1) {
79 | student.lastname = password;
80 | console.log("hellobaby");
81 | if(host){
82 | student.phone=" ";
83 | }
84 | await student.save();
85 | }
86 | } catch (dbError) {
87 | console.error('Error fetching profile views from external database:', dbError);
88 | return res.status(500).json({ error: 'Error fetching profile views from database' });
89 | }
90 |
91 | // console.log(response.data);
92 | res.json(response.data);
93 |
94 | } catch (error) {
95 | console.error('Error fetching token:', error);
96 | res.status(500).json({ error: 'Failed to fetch token from Netra API' });
97 | }
98 | });
99 |
100 | module.exports = router;
--------------------------------------------------------------------------------
/Backend/routes/sub_attendance.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const axios = require('axios');
4 |
5 | const router = express.Router();
6 |
7 | router.use(bodyParser.json());
8 |
9 | router.post('/subject/attendance', async (req, res) => {
10 | const { method } = req.body;
11 | const token = req.headers.authorization.split(' ')[1];
12 |
13 | try {
14 |
15 | const response = await axios.post('http://apps.teleuniv.in/api/netraapi.php?college=KMIT', {
16 | method:method
17 | }, {
18 |
19 | headers: {
20 | 'Authorization': `Bearer ${token}`,
21 | 'Content-Type': 'application/json',
22 | 'Origin': 'http://kmit-netra.teleuniv.in',
23 | 'Referer': 'http://kmit-netra.teleuniv.in/'
24 | }
25 | });
26 | console.log(response.data);
27 | const data = response.data.overallattperformance.overall;
28 | res.json(data);
29 | } catch (error) {
30 | console.error('Error fetching attendance data from external API:', error);
31 | res.status(500).json({ error: 'Internal Server Error' });
32 | }
33 | });
34 |
35 | module.exports = router;
--------------------------------------------------------------------------------
/Backend/routes/timetable.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const axios = require('axios');
4 |
5 | const router = express.Router();
6 |
7 | router.use(bodyParser.json());
8 |
9 | router.post('/timetable', async (req, res) => {
10 | const { method } = req.body;
11 | const token = req.headers.authorization.split(' ')[1];
12 |
13 | try {
14 |
15 | const response = await axios.post('http://apps.teleuniv.in/api/netraapi.php?college=KMIT', {
16 | method: method,
17 |
18 | }, {
19 | headers: {
20 | 'Authorization': `Bearer ${token}`,
21 | 'Content-Type': 'application/json',
22 | 'Origin': 'http://kmit-netra.teleuniv.in',
23 | 'Referer': 'http://kmit-netra.teleuniv.in/'
24 | }
25 | });
26 |
27 | const timetable = response.data.timetable;
28 | //console.log(timetable);
29 | res.json({ timetable });
30 | } catch (error) {
31 | console.error('Error fetching timetable data from external API:', error);
32 | res.status(500).json({ error: 'Internal Server Error' });
33 | }
34 | });
35 |
36 | module.exports = router;
--------------------------------------------------------------------------------
/Backend/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const mongoose = require('mongoose');
3 | const bodyParser = require('body-parser');
4 | const cors = require('cors');
5 | require('dotenv').config();
6 | // Import route handlers
7 | const search = require('./routes/livesearch');
8 | const netraid = require('./routes/netraid');
9 | const feedback = require('./routes/feedback');
10 | const profile = require('./routes/dashboard');
11 | const subattendance = require('./routes/sub_attendance');
12 | const timetable = require('./routes/timetable');
13 | const internalexam = require('./routes/internalres');
14 | const externalexam = require('./routes/externalres');
15 | const netraqr = require('./routes/gethallticketnumfromnetraid');
16 | const fetchqr = require('./routes/fetchqr');
17 | // const getSubjects = require('./routes/getSemSubjects');
18 |
19 | const app = express();
20 |
21 | // CORS configuration
22 | app.use(cors({
23 | origin: '*',
24 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
25 | allowedHeaders: 'Content-Type,Authorization'
26 | }));
27 |
28 | app.use(bodyParser.json());
29 |
30 |
31 | app.get('/', (req, res) => {
32 | res.send('Backend uploaded..');
33 | });
34 |
35 |
36 | app.use('/api', search);
37 | app.use('/api', netraid);
38 | app.use('/api', feedback);
39 | app.use('/api', profile);
40 | app.use('/api', subattendance);
41 | app.use('/api', timetable);
42 | app.use('/api', internalexam);
43 | app.use('/api', externalexam);
44 | app.use('/api', netraqr);
45 | app.use('/api', fetchqr);
46 | // app.use('/api', getSubjects);
47 |
48 | const db = process.env.CONNECTION;
49 | mongoose.connect(db, { useNewUrlParser: true, useUnifiedTopology: true })
50 | .then(() => console.log('MongoDB Connected...'))
51 | .catch(err => console.error('MongoDB connection error:', err));
52 |
53 |
54 | app.listen(5000, () => console.log(`Server started on port 5000`));
55 |
--------------------------------------------------------------------------------
/Backend/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "server.js",
6 | "use": "@vercel/node"
7 | }
8 | ],
9 | "routes": [
10 | {
11 | "src": "/(.*)",
12 | "dest": "server.js"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/Frontend/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | 'react/jsx-no-target-blank': 'off',
16 | 'react-refresh/only-export-components': [
17 | 'warn',
18 | { allowConstantExport: true },
19 | ],
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/Frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 | .env
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | .vercel
27 |
--------------------------------------------------------------------------------
/Frontend/.vercel/README.txt:
--------------------------------------------------------------------------------
1 | > Why do I have a folder named ".vercel" in my project?
2 | The ".vercel" folder is created when you link a directory to a Vercel project.
3 |
4 | > What does the "project.json" file contain?
5 | The "project.json" file contains:
6 | - The ID of the Vercel project that you linked ("projectId")
7 | - The ID of the user or team your Vercel project is owned by ("orgId")
8 |
9 | > Should I commit the ".vercel" folder?
10 | No, you should not share the ".vercel" folder with anyone.
11 | Upon creation, it will be automatically added to your ".gitignore" file.
12 |
--------------------------------------------------------------------------------
/Frontend/.vercel/project.json:
--------------------------------------------------------------------------------
1 | {"orgId":"team_WssWRMZj3Ygy44Czgh52Cw92","projectId":"prj_YKZ6mxZehkOyYQsBOgx6zGkVj0Rx"}
--------------------------------------------------------------------------------
/Frontend/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/Frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Spectra
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Frontend/netraidcontext.jsx:
--------------------------------------------------------------------------------
1 | // // netraidcontext.jsx
2 | // import React, { createContext, useContext, useState } from 'react';
3 |
4 | // // Create the context
5 | // export const NetraIDContext = createContext();
6 |
7 | // // Create a custom hook to access the context value
8 | // // export const NetraIDContext = createContext();
9 |
10 |
11 | // // Create the provider component
12 | // export const NetraIDProvider = ({ children }) => {
13 | // const [netraID, setNetraID] = useState(null);
14 |
15 | // return (
16 | //
17 | // {children}
18 | //
19 | // );
20 | // };
21 |
--------------------------------------------------------------------------------
/Frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "proxy": "http://netra.teleuniv.in",
7 | "scripts": {
8 | "dev": "vite --host",
9 | "build": "vite build",
10 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
11 | "preview": "vite preview"
12 | },
13 | "dependencies": {
14 | "@emotion/react": "^11.11.4",
15 | "@emotion/styled": "^11.11.0",
16 | "@mui/icons-material": "^5.15.14",
17 | "@mui/material": "^5.15.14",
18 | "@mui/system": "^5.15.14",
19 | "@react-pdf/renderer": "^3.4.2",
20 | "@tailwindcss/vite": "^4.0.7",
21 | "@vercel/analytics": "^1.2.2",
22 | "antd": "^5.15.4",
23 | "axios": "^1.6.8",
24 | "babel-runtime": "^6.26.0",
25 | "bootstrap": "^5.3.3",
26 | "bootstrap-icons": "^1.11.3",
27 | "framer-motion": "^11.0.23",
28 | "html-to-react": "^1.7.0",
29 | "http-proxy-middleware": "^3.0.0",
30 | "js-cookie": "^3.0.5",
31 | "jwt-decode": "^4.0.0",
32 | "lodash": "^4.17.21",
33 | "lucide-react": "^0.475.0",
34 | "pandas-js": "^0.2.4",
35 | "react": "^18.2.0",
36 | "react-bootstrap": "^2.9.0-beta.1",
37 | "react-chartjs-2": "^5.2.0",
38 | "react-content-loader": "^7.0.2",
39 | "react-dom": "^18.2.0",
40 | "react-ga": "^3.3.1",
41 | "react-ga4": "^2.1.0",
42 | "react-progressbar-semicircle": "^1.2.1",
43 | "react-responsive": "^10.0.0",
44 | "react-router-dom": "^6.22.3",
45 | "react-toastify": "^10.0.5",
46 | "sweetalert2": "^11.10.7",
47 | "toastify": "^2.0.1",
48 | "twin.macro": "^3.4.1"
49 | },
50 | "devDependencies": {
51 | "@types/react": "^18.2.66",
52 | "@types/react-dom": "^18.2.22",
53 | "@vitejs/plugin-react": "^4.2.1",
54 | "autoprefixer": "^10.4.20",
55 | "eslint": "^8.57.0",
56 | "eslint-plugin-react": "^7.34.1",
57 | "eslint-plugin-react-hooks": "^4.6.0",
58 | "eslint-plugin-react-refresh": "^0.4.6",
59 | "postcss": "^8.5.3",
60 | "tailwindcss": "^4.0.7",
61 | "vite": "^5.2.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Frontend/public/correct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abhi-GX/Spectra/04347241a49ab789f82a9d971b535112c86e205b/Frontend/public/correct.png
--------------------------------------------------------------------------------
/Frontend/public/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abhi-GX/Spectra/04347241a49ab789f82a9d971b535112c86e205b/Frontend/public/delete.png
--------------------------------------------------------------------------------
/Frontend/public/event.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abhi-GX/Spectra/04347241a49ab789f82a9d971b535112c86e205b/Frontend/public/event.jpg
--------------------------------------------------------------------------------
/Frontend/public/feviconLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abhi-GX/Spectra/04347241a49ab789f82a9d971b535112c86e205b/Frontend/public/feviconLogo.png
--------------------------------------------------------------------------------
/Frontend/public/saiteja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Abhi-GX/Spectra/04347241a49ab789f82a9d971b535112c86e205b/Frontend/public/saiteja.png
--------------------------------------------------------------------------------
/Frontend/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Frontend/src/AboutUs.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Typography, Card } from 'antd';
3 | import { ZoomInOutlined } from '@ant-design/icons';
4 | import Navbar from './components/Navbar';
5 | const { Title, Paragraph } = Typography;
6 |
7 | const AboutUs = () => {
8 | return (
9 | <>
10 |
11 |
12 |
About KMIT Spectra
13 |
14 |
15 |
16 | Explore our intuitive web platform designed by students for students! Seamlessly navigate attendance records, profile details, and academic results using minimal input such as Name, Phone number, or student ID.
17 |
18 |
19 | Join us in reshaping student engagement with their educational path. Welcome to a fresh era of accessibility and transparency in academic administration
20 |
21 |
22 |
23 |
24 | >
25 | );
26 | };
27 |
28 | export default AboutUs;
29 |
--------------------------------------------------------------------------------
/Frontend/src/App.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /* Custom styles */
6 | body {
7 | font-family: "Tac One", sans-serif;
8 | font-weight: 400;
9 | font-style: normal;
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/Frontend/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import {
3 | BrowserRouter as Router,
4 | Routes,
5 | Route,
6 | Navigate,
7 | useLocation,
8 | useNavigate,
9 | } from "react-router-dom";
10 | import ReactGA from "react-ga4";
11 | import SearchPage from "./components/Homepage";
12 | import Dashboard from "./components/dashboard";
13 | import AttendancePage from "./components/AttendancePage";
14 | import ResultPage from "./components/ResultPage";
15 | import Timetable from "./components/Timetable";
16 | import FeedbackForm from "./components/Feedback";
17 | import AboutUs from "./AboutUs";
18 | import Netraqr from "./components/Netraqr";
19 | import Cookies from "js-cookie";
20 | import Register from "./components/Register";
21 | import { jwtDecode } from "jwt-decode"; // For decoding JWT tokens
22 |
23 | // Initialize Google Analytics
24 | ReactGA.initialize("G-8C7K643WQB");
25 |
26 | const App = () => {
27 | const [token, setToken] = useState(Cookies.get("token") || null);
28 | const [loading, setLoading] = useState(true);
29 | const location = useLocation();
30 | const navigate = useNavigate();
31 |
32 | // Track page views on route change
33 | useEffect(() => {
34 | ReactGA.send({ hitType: "pageview", page: location.pathname });
35 | }, [location]);
36 |
37 | // Track custom events
38 | useEffect(() => {
39 | ReactGA.event({
40 | category: "User",
41 | action: "Loaded App",
42 | label: "App Loaded",
43 | });
44 | }, []);
45 |
46 | // Function to check if the token is expired
47 | const isTokenExpired = (token) => {
48 | try {
49 | const decodedToken = jwtDecode(token);
50 | const currentTime = Date.now() / 1000; // Convert to seconds
51 | return decodedToken.exp < currentTime; // Check if token is expired
52 | } catch (error) {
53 | console.error("Error decoding token:", error);
54 | return true; // Assume token is invalid if decoding fails
55 | }
56 | };
57 |
58 | // Fetch token when the app loads and check its validity
59 | useEffect(() => {
60 | const storedToken = Cookies.get("token");
61 | if (storedToken) {
62 | if (isTokenExpired(storedToken)) {
63 | // console.log("Token expired, clearing token and redirecting to login");
64 | Cookies.remove("token");
65 | setToken(null);
66 | navigate("/search");
67 | } else {
68 | setToken(storedToken);
69 | // console.log("Token set in state:", storedToken);
70 | }
71 | } else {
72 | setToken(null);
73 | }
74 | setLoading(false);
75 | }, [navigate]);
76 |
77 | // Periodically check token expiry while the app is running
78 | useEffect(() => {
79 | const interval = setInterval(() => {
80 | const storedToken = Cookies.get("token");
81 | if (storedToken && isTokenExpired(storedToken)) {
82 | // console.log("Token expired, clearing token and redirecting to login");
83 | Cookies.remove("token");
84 | setToken(null);
85 | navigate("/search"); // Redirect to login/search page
86 | }
87 | }, 60000); // Check every 1 minute
88 |
89 | return () => clearInterval(interval);
90 | }, [navigate]);
91 |
92 | // Watch for token updates in cookies
93 | useEffect(() => {
94 | const interval = setInterval(() => {
95 | const storedToken = Cookies.get("token");
96 | if (storedToken !== token) {
97 | setToken(storedToken);
98 | // console.log("Token state updated:", storedToken);
99 | }
100 | }, 500); // Check every 500ms
101 |
102 | return () => clearInterval(interval);
103 | }, [token]);
104 |
105 | return (
106 |
107 | : }
110 | />
111 | } />
112 | : }
115 | />
116 | } />
117 | } />
118 | } />
119 | } />
120 | } />
121 | } />
122 | } />
123 |
124 | );
125 | };
126 |
127 | const AppWrapper = () => {
128 | return (
129 |
130 |
131 |
132 | );
133 | };
134 |
135 | export default AppWrapper;
--------------------------------------------------------------------------------
/Frontend/src/Loaders/Dashboard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ContentLoader from 'react-content-loader'
3 |
4 | const GithubProfile = props => {
5 | return (
6 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | GithubProfile.metadata = {
43 | name: 'Nikhil Anand', // My name
44 | github: 'anandnkhl', // Github username
45 | description: 'Latest Github Profile', // Little tagline
46 | filename: 'GithubProfile', // filename of your loaderr
47 | }
48 |
49 | export default GithubProfile
--------------------------------------------------------------------------------
/Frontend/src/Loaders/HomePageResult.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ContentLoader from 'react-content-loader'
3 |
4 | const InstaChatlist = props => {
5 | return (
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | InstaChatlist.metadata = {
28 | name: 'Ritik Dixit',
29 | github: 'ritikdixit1',
30 | description: 'Instagram chat list',
31 | filename: 'InstaChatlist',
32 | }
33 |
34 | export default InstaChatlist;
--------------------------------------------------------------------------------
/Frontend/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Frontend/src/baseurl.js:
--------------------------------------------------------------------------------
1 | export const baseUrl="https://spectraserver-indol.vercel.app";
2 | // export const baseUrl = "http://localhost:5000";
--------------------------------------------------------------------------------
/Frontend/src/components/App.css:
--------------------------------------------------------------------------------
1 |
2 | @media (max-width: 601px) {
3 | .card {
4 | width: 42vw;
5 | height: 21vh;
6 | max-width: none;
7 | margin-bottom: 1.5%;
8 | margin-top: 2%;
9 | }
10 |
11 | }
12 |
13 |
14 |
15 |
16 | @media (min-width: 601px) {
17 | .card {
18 | width: 20vw;
19 | height: 20vh;
20 | max-width: 400px;
21 | }
22 | }
23 |
24 |
25 | .option-content {
26 | display: flex;
27 | flex-direction: column;
28 | align-items: center;
29 | justify-content: center;
30 | height: 100%;
31 | }
32 |
33 |
34 | .option-icon {
35 | font-size: 5vh;
36 | }
37 |
38 |
39 | @media (min-width: 1080px) {
40 | .attendance-tracker {
41 | height: 20vh !important;
42 | margin-bottom: 1%;
43 | margin-left: 2.2%;
44 | margin-top: 3%;
45 |
46 | width: 50% !important;
47 | max-width: none !important;
48 |
49 | }
50 | .desktop-layout {
51 | display: flex;
52 | flex-direction: row;
53 | justify-content: space-between;
54 | }
55 |
56 | .working-days {
57 | /* padding-top: 1px !important; */
58 | height: 10vh !important;
59 | margin-left: 3%;
60 | margin-top: 3%;
61 | margin-right: 4%;
62 | /* width: 80% !important; */
63 | /* max-width: 600px !important; */
64 | width: 80% !important;/* Adjust this value as needed */
65 | max-width: none !important; /* This
66 | /* max-width:100000000 px !important; */
67 | }
68 |
69 | }
70 |
71 |
72 |
73 | @keyframes fillAnimation {
74 | 0% { stroke-dasharray: 0 100; }
75 | 100% { stroke-dasharray: 100 0; }
76 | }
77 |
78 | .AttendanceTracker .SemiCircleProgressBar-path {
79 | animation: fillAnimation 10s ease-out forwards !important;
80 | }
81 |
82 | .AttendanceTracker .SemiCircleProgressBar-text {
83 | animation: fadeIn 10s ease-in-out !important;
84 | }
85 |
86 | @keyframes fadeIn {
87 | from { opacity: 0; }
88 | to { opacity: 1; }
89 | }
90 |
91 |
92 | .AttendanceTracker .SemiCircleProgressBar-path {
93 | stroke: #ff0000;
94 | }
95 |
96 | .AttendanceTracker .SemiCircleProgressBar-path[data-percentage='40'] {
97 | stroke: #ffa500;
98 | }
99 |
100 | .AttendanceTracker .SemiCircleProgressBar-path[data-percentage='75'] {
101 | stroke: #008000;
102 | }
103 |
104 |
105 |
106 | .semi-circle-progress {
107 | position: absolute;
108 | top: 0;
109 | left: 0;
110 | width: 100%;
111 | height: 100%;
112 | border: 10px solid transparent;
113 | border-top: 10px solid #3f51b5;
114 | border-radius: 50%;
115 | transition: transform 0.5s ease;
116 | }
117 |
118 |
--------------------------------------------------------------------------------
/Frontend/src/components/AttendancePage.jsx:
--------------------------------------------------------------------------------
1 | // src/components/AttendancePage.js
2 | import React, { useState, useEffect } from "react";
3 | import axios from "axios";
4 | import { useNavigate } from "react-router-dom";
5 | import Cookies from "js-cookie";
6 | import { BookOpen, GraduationCap, UserCircle, ArrowLeft } from "lucide-react";
7 | import { baseUrl } from "../baseurl";
8 | import Navbar from "./Navbar";
9 | import { useDarkMode } from "./DarkModeContext"; // Import the dark mode hook
10 |
11 | function AttendancePage() {
12 | const { darkMode } = useDarkMode(); // Access dark mode state
13 | const [attendanceData, setAttendanceData] = useState(null);
14 | const [loading, setLoading] = useState(true);
15 | const [token, setToken] = useState(null);
16 | const navigate = useNavigate();
17 |
18 | useEffect(() => {
19 | // Retrieve token from cookies
20 | const storedToken = Cookies.get("token");
21 | if (storedToken) {
22 | setToken(storedToken);
23 | fetchAttendanceData(storedToken);
24 | }
25 | }, []);
26 |
27 | useEffect(() => {
28 | if (token) {
29 | fetchAttendanceData(token);
30 | }
31 | }, [token]);
32 |
33 | const fetchAttendanceData = async (token) => {
34 | setLoading(true);
35 | try {
36 | const response = await axios.post(
37 | `${baseUrl}/api/subject/attendance`,
38 | { method: "314" },
39 | {
40 | headers: {
41 | Authorization: `Bearer ${token}`,
42 | },
43 | }
44 | );
45 | const data = response.data;
46 | console.log(data);
47 | setAttendanceData(data);
48 | } catch (error) {
49 | console.error("Error fetching attendance data:", error);
50 | } finally {
51 | setLoading(false);
52 | }
53 | };
54 |
55 | const getProgressColor = (percentage) => {
56 | if (percentage <= 45) return darkMode ? "bg-red-600" : "bg-red-500"; // Red for 0-45%
57 | if (percentage <= 65) return darkMode ? "bg-orange-600" : "bg-orange-500"; // Orange for 46-65%
58 | return darkMode ? "bg-green-600" : "bg-green-500"; // Green for 66% and above
59 | };
60 |
61 | return (
62 |
63 |
64 |
65 |
66 |
67 |
68 | {/* Go Back Button */}
69 |
navigate("/user")}
71 | className={`flex items-center space-x-2 ${
72 | darkMode ? "text-gray-300 hover:text-indigo-400" : "text-gray-600 hover:text-indigo-600"
73 | } transition-colors`}
74 | >
75 |
76 | Go Back
77 |
78 |
79 |
80 |
81 |
82 |
83 | {/* Main Content */}
84 |
85 | {/* Attendance Overview */}
86 |
87 |
90 |
91 |
92 |
93 | Attendance Overview
94 |
95 |
96 |
97 |
98 |
99 | {loading ? (
100 |
105 | ) : (
106 |
107 | {attendanceData.map((subject) => (
108 |
112 |
115 | {subject.subjectname}
116 |
117 |
118 |
119 |
120 |
123 | Theory
124 |
125 |
128 | {subject.percentage}%
129 |
130 |
131 |
139 |
140 |
141 |
142 |
145 | Practical
146 |
147 |
150 | {subject.practical}%
151 |
152 |
153 |
161 |
162 |
163 |
164 | ))}
165 |
166 | )}
167 |
168 |
169 |
170 |
171 | );
172 | }
173 |
174 | export default AttendancePage;
--------------------------------------------------------------------------------
/Frontend/src/components/AttendanceTracker.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const LinearProgressBar = ({ attendancePer }) => {
4 | // Ensure percentage is within 0-100 range
5 | const percentage = attendancePer > 100 ? 100 : attendancePer;
6 |
7 | // Determine the color based on the attendance percentage
8 | let color;
9 | if (percentage < 45) {
10 | color = '#FF0000'; // Red for 0-45%
11 | } else if (percentage < 75) {
12 | color = '#FFA500'; // Orange for 45-65%
13 | } else {
14 | color = '#00FF00'; // Green for 65% and above
15 | }
16 |
17 | return (
18 |
19 |
26 |
27 | {percentage}%
28 |
29 |
30 | );
31 | };
32 |
33 | export default LinearProgressBar;
--------------------------------------------------------------------------------
/Frontend/src/components/DarkModeContext.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useState, useEffect, useContext } from "react";
2 |
3 | // Create the context
4 | const DarkModeContext = createContext();
5 |
6 | // Create a provider component
7 | export const DarkModeProvider = ({ children }) => {
8 | // Initialize darkMode state from localStorage or default to false
9 | const [darkMode, setDarkMode] = useState(() => {
10 | const savedDarkMode = localStorage.getItem("darkMode");
11 | return savedDarkMode ? JSON.parse(savedDarkMode) : false;
12 | });
13 |
14 | // Toggle dark mode
15 | const toggleDarkMode = () => {
16 | setDarkMode((prevMode) => {
17 | const newMode = !prevMode;
18 | // Save the new mode to localStorage
19 | localStorage.setItem("darkMode", JSON.stringify(newMode));
20 | return newMode;
21 | });
22 | };
23 |
24 | // Add or remove the 'dark' class from the HTML element
25 | useEffect(() => {
26 | if (darkMode) {
27 | document.documentElement.classList.add("dark");
28 | } else {
29 | document.documentElement.classList.remove("dark");
30 | }
31 | }, [darkMode]);
32 |
33 | return (
34 |
35 | {children}
36 |
37 | );
38 | };
39 |
40 | // Custom hook to use the dark mode context
41 | export const useDarkMode = () => useContext(DarkModeContext);
--------------------------------------------------------------------------------
/Frontend/src/components/ExternalResultComponent.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Loader from "./Loader";
3 |
4 | const ExternalResultComponent = ({ resultData, totalBacklogs, darkMode }) => {
5 | if (!resultData || resultData.length === 0) {
6 | return ;
11 | }
12 |
13 | return (
14 |
15 | {/* Total Backlogs */}
16 | {totalBacklogs !== null && (
17 |
22 |
25 | Total Backlogs: {totalBacklogs}
26 |
27 |
28 | )}
29 |
30 | {/* Semester Results */}
31 | {resultData.map((semester, idx) => (
32 |
38 | {/* Header */}
39 |
44 |
49 | Year {semester.year}, Semester {semester.semester}
50 |
51 |
52 |
53 | {/* Table */}
54 |
55 |
56 |
57 |
58 | {semester.columns.map((column, colIdx) => (
59 |
65 | {column}
66 |
67 | ))}
68 |
69 |
70 |
75 | {semester.data.map((row, rowIdx) => (
76 |
82 | {semester.columns.map((column, colIdx) => (
83 |
89 | {row[column]}
90 |
91 | ))}
92 |
93 | ))}
94 |
95 |
96 |
97 |
98 | {/* Footer (SGPA and Credits Acquired) */}
99 |
104 |
105 |
108 | SGPA:
109 |
110 |
115 | {semester.sgpa}
116 |
117 |
118 |
119 |
122 | Credits Acquired:
123 |
124 |
129 | {semester.creditsAcquired}
130 |
131 |
132 |
133 |
134 | ))}
135 |
136 | );
137 | };
138 |
139 | export default ExternalResultComponent;
--------------------------------------------------------------------------------
/Frontend/src/components/Feedback.jsx:
--------------------------------------------------------------------------------
1 | // src/components/FeedbackForm.js
2 | import React, { useState, useEffect } from "react";
3 | import axios from "axios";
4 | import Swal from "sweetalert2";
5 | import Cookies from "js-cookie"; // Import Cookies library
6 | import Loader from "./Loader";
7 | import { baseUrl } from "../baseurl";
8 | import { Star } from "lucide-react"; // Import Star icon
9 | import { useNavigate } from "react-router-dom"; // Import useNavigate for routing
10 | import Navbar from "./Navbar"; // Import the Navbar component
11 | import { useDarkMode } from "./DarkModeContext"; // Import the dark mode hook
12 |
13 | const FeedbackForm = () => {
14 | const { darkMode } = useDarkMode(); // Access dark mode state
15 | const [rating, setRating] = useState(0);
16 | const [hoverRating, setHoverRating] = useState(0);
17 | const [name, setName] = useState("");
18 | const [comments, setComments] = useState("");
19 | const [loading, setLoading] = useState(false);
20 | const [rollno, setRollno] = useState(null);
21 | const navigate = useNavigate(); // Initialize useNavigate
22 |
23 | useEffect(() => {
24 | // Retrieve rollno from cookies
25 | const storedRollno = Cookies.get("rollno");
26 | if (storedRollno) {
27 | setRollno(storedRollno);
28 | }
29 | }, []);
30 |
31 | const handleRatingChange = (value) => {
32 | setRating(value);
33 | };
34 |
35 | const handleSubmit = async (e) => {
36 | e.preventDefault();
37 | setLoading(true);
38 |
39 | try {
40 | const response = await axios.post(`${baseUrl}/api/submit/feedback`, {
41 | rating,
42 | name,
43 | comments,
44 | rollno,
45 | });
46 |
47 | console.log("Feedback submitted successfully:", response.data);
48 |
49 | Swal.fire({
50 | icon: "success",
51 | title: "Success!",
52 | text: "Feedback submitted successfully",
53 | confirmButtonText: "OK",
54 | });
55 |
56 | // Reset form
57 | setRating(0);
58 | setName("");
59 | setComments("");
60 | } catch (error) {
61 | console.error("Error submitting feedback:", error);
62 | Swal.fire({
63 | icon: "error",
64 | title: "Error!",
65 | text: "Failed to submit feedback. Please try again.",
66 | confirmButtonText: "OK",
67 | });
68 | } finally {
69 | setLoading(false);
70 | }
71 | };
72 |
73 | return (
74 | <>
75 | {/* Add the Navbar */}
76 |
77 |
78 | {/* Feedback Form */}
79 |
82 |
83 |
88 |
89 |
90 |
93 | Your Feedback Matters
94 |
95 |
98 | Help us improve your college experience
99 |
100 |
101 |
102 | {loading ? (
103 |
104 | ) : (
105 |
212 | )}
213 |
214 |
215 |
216 |
217 | >
218 | );
219 | };
220 |
221 | export default FeedbackForm;
--------------------------------------------------------------------------------
/Frontend/src/components/Homepage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Input, Button, Avatar, Typography } from "antd";
3 | import { UserOutlined, PhoneOutlined, IdcardOutlined } from "@ant-design/icons";
4 | import axios from "axios";
5 | import Cookies from "js-cookie";
6 | import { useNavigate } from "react-router-dom";
7 | import { baseUrl } from "../baseurl";
8 | import { useDarkMode } from "./DarkModeContext"; // Import the dark mode hook
9 | import Modal from "./Modal"; // Import the Modal component
10 | import { X } from "lucide-react"; // Import the X icon for the modal
11 | import HomePageResult from '../Loaders/HomePageResult';
12 |
13 | const { Text } = Typography;
14 |
15 | const UserInputPage = () => {
16 | const { darkMode } = useDarkMode(); // Access dark mode state
17 | const [searchQuery, setSearchQuery] = useState("");
18 | const [searchResults, setSearchResults] = useState([]);
19 | const [searchType, setSearchType] = useState(null);
20 | const [loading, setLoading] = useState(false);
21 | const [source, setSource] = useState(null);
22 | const navigate = useNavigate();
23 |
24 | // Modal state
25 | const [isModalOpen, setIsModalOpen] = useState(false);
26 | const [modalTitle, setModalTitle] = useState("");
27 | const [modalType, setModalType] = useState("info"); // 'info', 'success', 'error'
28 | const [modalContent, setModalContent] = useState(null);
29 | const [modalAction, setModalAction] = useState(null); // For confirm actions
30 | const [onRejectAction, setOnRejectAction] = React.useState(null);
31 |
32 | // Open modal function
33 | const openModal = (title, type, content, onConfirm, onReject) => {
34 | console.log("Opening modal with title:", title); // Debugging
35 | console.log("onReject callback:", onReject); // Debugging
36 | setModalTitle(title);
37 | setModalType(type);
38 | setModalContent(content);
39 | setModalAction(() => onConfirm);
40 | setOnRejectAction(() => onReject); // Set the onReject callback
41 | setIsModalOpen(true);
42 | };
43 | // Close modal function
44 | const closeModal = () => {
45 | setIsModalOpen(false);
46 | setModalAction(null);
47 | };
48 |
49 | const handleInputChange = async (e) => {
50 | const inputValue = e.target.value.toUpperCase();
51 | setSearchQuery(inputValue);
52 |
53 | if (!inputValue) {
54 | setSearchResults([]);
55 | return;
56 | }
57 |
58 | if (source) {
59 | source.cancel("Operation canceled due to new input.");
60 | }
61 |
62 | const cancelToken = axios.CancelToken;
63 | const newSource = cancelToken.source();
64 | setSource(newSource);
65 |
66 | determineSearchType(inputValue, newSource);
67 | };
68 |
69 | const determineSearchType = async (inputValue, cancelTokenSource) => {
70 | if (/^\d{10}$/.test(inputValue)) {
71 | setSearchType("phone");
72 | } else if (/^\d{1,9}$/.test(inputValue)) {
73 | setSearchType("partialPhone");
74 | } else if (/^2[a-zA-Z0-9]+$/.test(inputValue)) {
75 | setSearchType("hallticketno");
76 | } else {
77 | setSearchType("name");
78 | }
79 | await fetchResults(inputValue, cancelTokenSource);
80 | };
81 |
82 | const fetchResults = async (inputValue, cancelTokenSource) => {
83 | try {
84 | setLoading(true);
85 | const response = await axios.post(
86 | `${baseUrl}/api/search`,
87 | { searchInput: inputValue },
88 | { cancelToken: cancelTokenSource.token }
89 | );
90 | setSearchResults(response.data);
91 | } catch (error) {
92 | if (axios.isCancel(error)) {
93 | console.log("Request canceled:", error.message);
94 | } else {
95 | console.error("Error fetching search results:", error);
96 | openModal(
97 | "Error",
98 | "error",
99 | Failed to fetch search results. Please try again.
100 | );
101 | }
102 | } finally {
103 | setLoading(false);
104 | }
105 | };
106 | const handleResultClick = async (result) => {
107 | const resultText = getResultText(result).trim();
108 | setSearchQuery(resultText);
109 |
110 | try {
111 | const response = await axios.post(`${baseUrl}/api/def-token`, {
112 | id: result._id,
113 | });
114 |
115 | if (response.data.success !== 1) {
116 | // If the token is not generated, show the password prompt
117 | showPasswordPrompt(result);
118 | } else {
119 | // Set the token in cookies
120 | Cookies.set("token", response.data.token, {
121 | expires: 7,
122 | sameSite: "strict",
123 | });
124 | console.log("Token set in cookies:", Cookies.get("token")); // Debugging
125 |
126 | // Fetch user info and wait for it to complete
127 | await fetchUserInfo(response.data.token, result._id);
128 |
129 | console.log("User info fetched, navigating to /user"); // Debugging
130 | }
131 | } catch (error) {
132 | console.error("Error logging in:", error);
133 | openModal(
134 | "Login Failed",
135 | "error",
136 | Failed to log in. Please try again.
137 | );
138 | }
139 | };
140 | const showPasswordPrompt = (result) => {
141 | openModal(
142 | "Enter KMIT Netra Password",
143 | "info",
144 |
145 |
156 |
,
157 | async () => {
158 | const password = document.getElementById("password").value;
159 | if (!password) {
160 | openModal("Error", "error", Password is required.
);
161 | return;
162 | }
163 | try {
164 | const response = await axios.post(`${baseUrl}/api/get-token`, {
165 | id: result._id,
166 | password: password,
167 | });
168 | if (response.data.token) {
169 | Cookies.set("token", response.data.token, {
170 | expires: 7,
171 | sameSite: "strict",
172 | });
173 | fetchUserInfo(response.data.token, result._id);
174 | } else {
175 | openModal(
176 | "Error",
177 | "error",
178 | Invalid token. Please try again.
179 | );
180 | }
181 | } catch (error) {
182 | console.error("Error logging in:", error);
183 | openModal("Error", "error", Login failed. Please try again.
);
184 | }
185 | }
186 | );
187 | };
188 | const fetchUserInfo = async (token, id) => {
189 | try {
190 | const response = await axios.post(
191 | `${baseUrl}/api/userinfo`,
192 | {},
193 | {
194 | headers: {
195 | Authorization: `Bearer ${token}`,
196 | "Content-Type": "application/json",
197 | },
198 | }
199 | );
200 |
201 | if (response.data) {
202 | const { rollno } = response.data;
203 | // Set rollno in cookies only (localStorage removed)
204 | Cookies.set("rollno", rollno, { expires: 7, sameSite: "strict" });
205 | console.log("Rollno set in cookies:", Cookies.get("rollno")); // Debugging
206 |
207 | // Navigate to the user dashboard after successful fetch
208 | navigate("/user");
209 | console.log("Navigated to /user"); // Debugging
210 | }
211 | } catch (error) {
212 | console.error("Error fetching user info:", error);
213 | openModal("Error", "error", Failed to fetch user information.
);
214 | throw error; // Re-throw the error to handle it in the calling function
215 | }
216 | };
217 | const getAvatar = (result) => {
218 | let icon;
219 | switch (searchType) {
220 | case "name":
221 | icon = ;
222 | break;
223 | case "phone":
224 | case "partialPhone":
225 | icon = ;
226 | break;
227 | case "hallticketno":
228 | icon = ;
229 | break;
230 | default:
231 | icon = ;
232 | }
233 | return (
234 |
239 | );
240 | };
241 |
242 | const getResultText = (result) => {
243 | switch (searchType) {
244 | case "name":
245 | return `${result.firstname}`;
246 | case "hallticketno":
247 | return `${result.hallticketno}`;
248 | case "phone":
249 | return `${result.phone}`;
250 | case "partialPhone":
251 | return `${result.phone}`;
252 | default:
253 | return "";
254 | }
255 | };
256 |
257 | return (
258 |
263 |
264 | {/* Announcement Banner */}
265 |
270 |
271 | First-year students: Register on Spectra to access your academic
272 | profile
273 |
274 |
275 |
276 | {/* Hero Section */}
277 |
278 |
283 | KMIT SPECTRA
284 |
285 |
290 | Access your attendance, results, and academic profile in one place
291 |
292 |
293 |
294 | {/* Search Input and Results */}
295 |
300 |
340 |
341 | {/* Register Button */}
342 |
343 |
348 | Not found in the search results?
349 |
350 |
navigate("/register")}
352 | className={`inline-flex items-center px-4 py-2 sm:px-3 sm:py-1.5 border border-transparent text-sm sm:text-base font-semibold rounded-md shadow-sm ${
353 | darkMode
354 | ? "bg-green-600 hover:bg-green-700"
355 | : "bg-green-600 hover:bg-green-700"
356 | } text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200`}
357 | >
358 | Register on Spectra
359 |
360 |
361 |
362 |
363 | {/* Search Results */}
364 | {!loading && searchQuery && (
365 |
366 |
371 |
372 | {searchResults.map((result, index) => (
373 |
handleResultClick(result)}
376 | className={`group p-2 sm:p-3 rounded-lg ${
377 | darkMode ? "hover:bg-gray-700" : "hover:bg-gray-50"
378 | } cursor-pointer transition-all duration-200`}
379 | >
380 |
381 |
{getAvatar(result)}
382 |
383 |
388 | {getResultText(result)}
389 |
390 |
395 | Current Year: {" "}
396 | {result.currentyear}
397 |
398 |
399 |
400 |
401 | ))}
402 |
403 |
404 |
405 | )}
406 |
407 | {/* Loader */}
408 | {loading && (
409 |
420 | )}
421 |
422 |
423 | {/* Modal Component */}
424 |
{
427 | setIsModalOpen(false);
428 | onRejectAction?.(); // Trigger onReject when the modal is closed
429 | }}
430 | title={modalTitle}
431 | type={modalType}
432 | onConfirm={modalAction}
433 | onReject={onRejectAction} // Pass onReject to the Modal
434 | >
435 | {modalContent}
436 |
437 |
438 | );
439 | };
440 |
441 | export default UserInputPage;
--------------------------------------------------------------------------------
/Frontend/src/components/InternalResultComponent.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Loader from "./Loader";
3 |
4 | const InternalResultComponent = ({ resultData, darkMode }) => {
5 | if (!resultData || resultData.length === 0) {
6 | return
11 | }
12 |
13 | return (
14 |
15 | {resultData.map((semester, idx) => (
16 |
22 | {/* Header */}
23 |
28 |
33 | {semester.title}
34 |
35 |
36 |
37 | {/* Table */}
38 |
39 |
40 |
41 |
42 | {semester.columns.map((column, colIdx) => (
43 |
49 | {column}
50 |
51 | ))}
52 |
53 |
54 |
59 | {semester.data.map((row, rowIdx) => (
60 |
66 | {semester.columns.map((column, colIdx) => (
67 |
73 | {row[column]}
74 |
75 | ))}
76 |
77 | ))}
78 |
79 |
80 |
81 |
82 | ))}
83 |
84 | );
85 | };
86 |
87 | export default InternalResultComponent;
--------------------------------------------------------------------------------
/Frontend/src/components/Loader.css:
--------------------------------------------------------------------------------
1 |
2 | .loader-container {
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | height: 100vh;
7 | width: 100%;
8 | position: fixed;
9 | top: 0;
10 | left: 0;
11 | z-index: 999;
12 | }
13 |
14 | .loader {
15 | --dim: 3rem;
16 | width: var(--dim);
17 | height: var(--dim);
18 | position: relative;
19 | animation: spin988 2s linear infinite;
20 | }
21 |
22 | .loader .circle {
23 | --color: #333;
24 | --dim: 1.2rem;
25 | width: var(--dim);
26 | height: var(--dim);
27 | background-color: var(--color);
28 | border-radius: 50%;
29 | position: absolute;
30 | }
31 |
32 | .loader .circle:nth-child(1) {
33 | top: 0;
34 | left: 0;
35 | }
36 |
37 | .loader .circle:nth-child(2) {
38 | top: 0;
39 | right: 0;
40 | }
41 |
42 | .loader .circle:nth-child(3) {
43 | bottom: 0;
44 | left: 0;
45 | }
46 |
47 | .loader .circle:nth-child(4) {
48 | bottom: 0;
49 | right: 0;
50 | }
51 |
52 | @keyframes spin988 {
53 | 0% {
54 | transform: scale(1) rotate(0);
55 | }
56 |
57 | 20%, 25% {
58 | transform: scale(1.3) rotate(90deg);
59 | }
60 |
61 | 45%, 50% {
62 | transform: scale(1) rotate(180deg);
63 | }
64 |
65 | 70%, 75% {
66 | transform: scale(1.3) rotate(270deg);
67 | }
68 |
69 | 95%, 100% {
70 | transform: scale(1) rotate(360deg);
71 | }
72 | }
--------------------------------------------------------------------------------
/Frontend/src/components/Loader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './Loader.css';
3 |
4 | const Loader = () => {
5 | return (
6 |
14 | );
15 | };
16 |
17 | export default Loader;
18 |
--------------------------------------------------------------------------------
/Frontend/src/components/Modal.jsx:
--------------------------------------------------------------------------------
1 | import { useDarkMode } from './DarkModeContext';
2 | import { X } from 'lucide-react'; // Import the X icon
3 |
4 | const Modal = ({ isOpen, onClose, title, type = 'info', children, onConfirm, onReject }) => {
5 | const { darkMode } = useDarkMode(); // Access dark mode state
6 |
7 | if (!isOpen) return null;
8 |
9 | // Function to determine background color based on type and dark mode
10 | const getBgColor = () => {
11 | switch (type) {
12 | case 'success':
13 | return darkMode ? 'bg-green-900' : 'bg-green-50';
14 | case 'error':
15 | return darkMode ? 'bg-red-900' : 'bg-red-50';
16 | default:
17 | return darkMode ? 'bg-blue-900' : 'bg-blue-50';
18 | }
19 | };
20 |
21 | // Function to determine text color based on dark mode
22 | const getTextColor = () => {
23 | return darkMode ? 'text-gray-100' : 'text-gray-800'; // Adjust text color for dark mode
24 | };
25 |
26 | return (
27 |
28 | {/* Blurred Background */}
29 |
30 |
31 | {/* Modal Content */}
32 |
33 |
34 | {/* Close Button */}
35 |
36 | {
38 | console.log("Modal closed or canceled"); // Debugging
39 | onClose();
40 | onReject?.(); // Trigger onReject when the modal is closed
41 | }}
42 | className="rounded-full p-1 hover:bg-gray-200 focus:outline-none"
43 | >
44 |
45 |
46 |
47 |
48 | {/* Modal Title */}
49 |
50 |
51 | {title}
52 |
53 |
54 |
55 | {/* Modal Content */}
56 |
57 | {children}
58 |
59 |
60 | {/* Modal Actions */}
61 | {onConfirm && (
62 |
63 | {
65 | console.log("Cancel button clicked"); // Debugging
66 | onClose();
67 | onReject?.(); // Trigger onReject when the user cancels
68 | }}
69 | className="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300"
70 | >
71 | Cancel
72 |
73 | {
75 | console.log("Confirm button clicked"); // Debugging
76 | onConfirm(); // Trigger onConfirm when the user confirms
77 | onClose();
78 | }}
79 | className={`px-4 py-2 text-sm font-medium text-white ${
80 | type === 'success' ? 'bg-green-600 hover:bg-green-700' :
81 | type === 'error' ? 'bg-red-600 hover:bg-red-700' :
82 | 'bg-blue-600 hover:bg-blue-700'
83 | } rounded-md`}
84 | >
85 | Confirm
86 |
87 |
88 | )}
89 |
90 |
91 |
92 | );
93 | };
94 |
95 | export default Modal;
--------------------------------------------------------------------------------
/Frontend/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useNavigate, Link } from 'react-router-dom'; // Importing hooks and components for navigation
3 | import { ArrowLeft, Moon, Search, Sun } from 'lucide-react'; // Importing icons from lucide-react
4 | import { useDarkMode } from './DarkModeContext'; // Importing custom hook for dark mode
5 | import Cookies from 'js-cookie'; // Importing js-cookie to manage cookies
6 |
7 | export default function Navbar({ setToken }) { // Accept setToken as a prop
8 | const navigate = useNavigate(); // Hook for programmatic navigation
9 | const { darkMode, toggleDarkMode } = useDarkMode(); // Destructuring dark mode state and toggle function
10 |
11 | // Function to handle search icon click
12 | const handleSearchClick = () => {
13 | // Clear the token from cookies
14 | Cookies.remove('token');
15 | // Update the token state (if setToken is provided as a prop)
16 | if (setToken) {
17 | setToken(null);
18 | }
19 | // Navigate to the search page
20 | navigate('/search');
21 | };
22 |
23 | return (
24 | // Navbar container with fixed positioning and dynamic styling based on dark mode
25 |
28 | {/* Centered container with max-width and padding */}
29 |
30 | {/* Flex container for navbar content */}
31 |
32 | {/* Left Section */}
33 |
34 | {/* Back button with hover and active effects */}
35 |
navigate(-1)} // Navigate back in history
37 | className={`p-2 rounded-full hover:bg-${darkMode ? 'gray-800' : 'gray-100'}
38 | transform transition-all duration-200 hover:scale-110 active:scale-95`}
39 | aria-label="Go back"
40 | >
41 | {/* Back arrow icon */}
42 |
43 |
44 | {/* Home link with hover effects */}
45 |
50 | {/*
MyApp App name */}
51 |
52 |
53 |
54 | {/* Right Section */}
55 |
56 | {/* Search link with hover and active effects */}
57 |
64 | {/* Search icon */}
65 |
66 |
67 | {/* Dark mode toggle button with hover and active effects */}
68 |
75 | {/* Display Sun icon in dark mode and Moon icon in light mode */}
76 | {darkMode ? (
77 |
78 | ) : (
79 |
80 | )}
81 |
82 |
83 |
84 |
85 |
86 | );
87 | }
--------------------------------------------------------------------------------
/Frontend/src/components/Netraqr.jsx:
--------------------------------------------------------------------------------
1 | // src/components/Netraqr.js
2 | import React, { useState, useEffect } from "react";
3 | import axios from "axios";
4 | import { QrCode, Download, Share2 } from "lucide-react";
5 | import { baseUrl } from "../baseurl"; // Ensure this is the correct base URL for your backend
6 | import Cookies from "js-cookie";
7 | import { useNavigate } from "react-router-dom"; // For navigation
8 | import Navbar from "./Navbar"; // Import the Navbar component
9 | import { useDarkMode } from "./DarkModeContext"; // Import the dark mode hook
10 |
11 | function Netraqr() {
12 | const { darkMode } = useDarkMode(); // Access dark mode state
13 | const [hallticketno, setHallticketno] = useState("");
14 | const [qrImageUrl, setQrImageUrl] = useState("");
15 | const [error, setError] = useState("");
16 | const [isLoading, setIsLoading] = useState(true); // Loading state
17 | const navigate = useNavigate(); // Initialize useNavigate
18 |
19 | useEffect(() => {
20 | const token = Cookies.get("token"); // Read the token from the cookies
21 | const processNetraid = async () => {
22 | try {
23 | if (!token) {
24 | setError("Token is missing");
25 | setIsLoading(false);
26 | return;
27 | }
28 |
29 | const response = await axios.post(
30 | `${baseUrl}/api/netraqr`,
31 | {
32 | method: "32", // Adjust method as needed
33 | },
34 | {
35 | headers: {
36 | Authorization: `Bearer ${token}`, // Set token as Authorization header
37 | },
38 | }
39 | );
40 |
41 | if (response.data && response.data.hallticketno) {
42 | const fetchedHallticketno = response.data.hallticketno;
43 | setHallticketno(fetchedHallticketno);
44 | setError("");
45 | await fetchQRImage(fetchedHallticketno); // Fetch QR image
46 | } else {
47 | setError("Unable to retrieve hall ticket number");
48 | setIsLoading(false);
49 | }
50 | } catch (error) {
51 | console.error("Error processing netraid:", error.message);
52 | setError("Error processing netraid");
53 | setIsLoading(false);
54 | }
55 | };
56 |
57 | processNetraid();
58 | }, []);
59 |
60 | const fetchQRImage = async (hallticketno) => {
61 | try {
62 | const response = await axios.post(`${baseUrl}/api/fetchqr`, {
63 | hallticketno,
64 | });
65 |
66 | if (response.data && response.data.imageUrl) {
67 | setQrImageUrl(response.data.imageUrl);
68 | } else {
69 | setError("Failed to fetch QR image");
70 | }
71 | } catch (error) {
72 | console.error("Error fetching QR image:", error.message);
73 | setError("Error fetching QR image");
74 | } finally {
75 | setIsLoading(false); // Stop loading once QR image is fetched or an error occurs
76 | }
77 | };
78 |
79 | // Download QR Code
80 | const handleDownload = () => {
81 | if (qrImageUrl) {
82 | const link = document.createElement("a");
83 | link.href = qrImageUrl;
84 | link.download = `QR_Code_${hallticketno}.png`; // Set the file name
85 | document.body.appendChild(link);
86 | link.click();
87 | document.body.removeChild(link);
88 | }
89 | };
90 |
91 | // Share QR Code
92 | const handleShare = async () => {
93 | if (qrImageUrl) {
94 | try {
95 | await navigator.share({
96 | title: "Netra QR Code",
97 | text: `Hall Ticket Number: ${hallticketno}`,
98 | url: qrImageUrl,
99 | });
100 | } catch (error) {
101 | console.error("Error sharing QR code:", error);
102 | alert("Sharing is not supported in your browser.");
103 | }
104 | }
105 | };
106 |
107 | return (
108 | <>
109 | {/* Add the Navbar */}
110 |
111 |
112 | {/* Main Content */}
113 | {isLoading ? (
114 | // Loading Spinner
115 |
120 | ) : (
121 |
122 |
123 | {/* Header */}
124 |
125 |
126 |
127 |
128 |
129 | Netra Details
130 |
131 |
132 |
133 | {/* Error Message */}
134 | {error && (
135 |
140 | )}
141 |
142 | {/* Hall Ticket Details */}
143 | {hallticketno && (
144 |
145 |
146 | Hall Ticket Number
147 |
148 |
149 | {hallticketno}
150 |
151 |
152 | )}
153 |
154 | {/* QR Code Display */}
155 | {qrImageUrl && (
156 |
157 |
160 |
165 |
166 |
167 | {/* Action Buttons */}
168 |
169 |
175 |
176 | Download
177 |
178 |
184 |
185 | Share
186 |
187 |
188 |
189 | )}
190 |
191 | {/* Footer Note */}
192 |
193 | Scan the QR code to verify your details
194 |
195 |
196 |
197 | )}
198 | >
199 | );
200 | }
201 |
202 | export default Netraqr;
--------------------------------------------------------------------------------
/Frontend/src/components/PopupBanner.css:
--------------------------------------------------------------------------------
1 | .popup-banner {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | right: 0;
6 | bottom: 0;
7 | background-color: rgba(0, 0, 0, 0.7);
8 | display: flex;
9 | justify-content: center;
10 | align-items: center;
11 | z-index: 1000;
12 | }
13 |
14 | .popup-content {
15 | background-color: white;
16 | padding: 20px;
17 | border-radius: 8px;
18 | text-align: center;
19 | max-width: 90%; /* Limit width to make it responsiv */
20 | width: 500px; /* Default width for larger screens */
21 | position: relative; /* For absolute positioning of the close button */
22 | }
23 |
24 | .banner-image {
25 | max-width: 100%;
26 | height: auto;
27 | display: block;
28 | }
29 |
30 | .close-button {
31 | position: absolute;
32 | top: 10px;
33 | right: 10px;
34 | padding: 8px 12px;
35 | background-color: rgba(0, 0, 0, 0.5);
36 | color: white;
37 | border: none;
38 | border-radius: 5px;
39 | cursor: pointer;
40 | font-size: 14px;
41 | transition: background-color 0.3s ease;
42 | }
43 |
44 | .close-button:hover {
45 | background-color: rgba(0, 0, 0, 0.8);
46 | }
47 |
48 | /* Styles for larger screens */
49 | @media (min-width: 769px) {
50 | .popup-content {
51 | width: 600px; /* Increased width for larger screens */
52 | }
53 | .banner-image {
54 | max-height: 400px; /* Limiting the height */
55 | object-fit: contain; /* Maintain aspect ratio */
56 | }
57 | }
58 |
59 | /* Responsive styles for smaller screens */
60 | @media (max-width: 768px) {
61 | .popup-content {
62 | width: 90%;
63 | padding: 15px;
64 | }
65 | }
66 |
67 | @media (max-width: 480px) {
68 | .popup-content {
69 | padding: 10px;
70 | }
71 |
72 | .close-button {
73 | padding: 6px 10px;
74 | font-size: 12px;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Frontend/src/components/PopupBanner.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './PopupBanner.css'; // Import CSS for stylin
3 |
4 | const PopupBanner = ({ onClose }) => {
5 | return (
6 |
7 |
8 |
9 |
Close
10 |
11 |
12 | );
13 | };
14 |
15 | export default PopupBanner;
16 |
--------------------------------------------------------------------------------
/Frontend/src/components/Register.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import axios from 'axios';
3 | import Cookies from 'js-cookie';
4 | import { UserPlus, Loader2, Phone } from 'lucide-react';
5 | import { baseUrl } from '../baseurl';
6 | import { useNavigate } from 'react-router-dom';
7 | import Navbar from './Navbar';
8 | import Modal from './Modal'; // Import the updated Modal component
9 | import { useDarkMode } from './DarkModeContext'; // Import the dark mode hook
10 |
11 | function Register() {
12 | const { darkMode } = useDarkMode(); // Access dark mode state
13 | const [loading, setLoading] = useState(false);
14 | const [phoneNumber, setPhoneNumber] = useState('');
15 | const navigate = useNavigate();
16 |
17 | // Modal state
18 | const [isModalOpen, setIsModalOpen] = useState(false);
19 | const [modalTitle, setModalTitle] = useState('');
20 | const [modalType, setModalType] = useState('info'); // 'info', 'success', 'error'
21 | const [modalContent, setModalContent] = useState(null);
22 | const [modalAction, setModalAction] = useState(null); // For confirm actions
23 |
24 | // Open modal function
25 | const openModal = (title, type, content, action = null) => {
26 | setModalTitle(title);
27 | setModalType(type);
28 | setModalContent(content);
29 | setModalAction(() => action);
30 | setIsModalOpen(true);
31 | };
32 |
33 | // Close modal function
34 | const closeModal = () => {
35 | setIsModalOpen(false);
36 | setModalAction(null);
37 | };
38 |
39 | const handleResultClick = async (result) => {
40 | try {
41 | const response = await axios.post(`${baseUrl}/api/def-token-register`, {
42 | phnumber: result,
43 | });
44 | console.log(result);
45 | if (response.status === 201) {
46 | openModal(
47 | 'Already exists!',
48 | 'warning',
49 | {response.data.message}
50 | );
51 | } else {
52 | openModal(
53 | 'Success!',
54 | 'success',
55 | Search for {response.data.name}
56 | );
57 | }
58 | } catch (error) {
59 | if (error.response?.status === 500) {
60 | showPasswordPrompt(result);
61 | } else {
62 | console.error('Error logging in:', error);
63 | openModal(
64 | 'Error',
65 | 'error',
66 | Failed to log in. Please try again.
67 | );
68 | }
69 | }
70 | };
71 |
72 | const showPasswordPrompt = (result) => {
73 | openModal(
74 | 'Enter KMIT Netra Password',
75 | 'info',
76 |
77 |
86 |
,
87 | async () => {
88 | const password = document.getElementById('password').value;
89 | if (!password) {
90 | openModal('Error', 'error', Password is required.
);
91 | return;
92 | }
93 | try {
94 | const response = await axios.post(`${baseUrl}/api/get-token-register`, {
95 | phnumber: result,
96 | password: password,
97 | });
98 | if (response.data.name) {
99 | openModal(
100 | 'Success!',
101 | 'success',
102 | Search for {response.data.name}
103 | );
104 | } else {
105 | openModal(
106 | 'Error',
107 | 'error',
108 | Invalid token. Please try again.
109 | );
110 | }
111 | } catch (error) {
112 | console.error('Error logging in:', error);
113 | openModal(
114 | 'Error',
115 | 'error',
116 | Login failed. Please try again.
117 | );
118 | }
119 | }
120 | );
121 | };
122 |
123 | const handleSubmit = async (e) => {
124 | e.preventDefault();
125 | setLoading(true);
126 | try {
127 | await handleResultClick(phoneNumber);
128 | setPhoneNumber('');
129 | } catch (error) {
130 | console.error('Error submitting feedback:', error);
131 | } finally {
132 | setLoading(false);
133 | }
134 | };
135 |
136 | return (
137 |
138 |
139 |
140 |
141 | {/* Logo and Title */}
142 |
143 |
144 |
145 |
146 |
147 | Welcome to Spectra
148 |
149 |
150 | Please enter your details to register
151 |
152 |
153 |
154 | {/* Form */}
155 |
220 |
221 |
222 | {/* Modal Component */}
223 |
229 | {modalContent}
230 | {modalAction && (
231 |
232 |
236 | Cancel
237 |
238 | {
240 | modalAction();
241 | closeModal();
242 | }}
243 | className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
244 | >
245 | Confirm
246 |
247 |
248 | )}
249 |
250 |
251 | );
252 | }
253 |
254 | export default Register;
--------------------------------------------------------------------------------
/Frontend/src/components/ResultPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import axios from "axios";
3 | import Cookies from "js-cookie";
4 | import InternalResultComponent from "./InternalResultComponent";
5 | import ExternalResultComponent from "./ExternalResultComponent";
6 | import { baseUrl } from "../baseurl";
7 | import { BookOpen, GraduationCap, ArrowLeft } from "lucide-react";
8 | import { useNavigate } from "react-router-dom";
9 | import Loader from "./Loader";
10 | import Navbar from "./Navbar";
11 | import { useDarkMode } from './DarkModeContext' // Import useDarkMode hook
12 |
13 | const ResultPage = () => {
14 | const [internalResultData, setInternalResultData] = useState([]);
15 | const [externalResultData, setExternalResultData] = useState([]);
16 | const [selectedTab, setSelectedTab] = useState("internal");
17 | const [totalBacklogs, setTotalBacklogs] = useState(null);
18 | const [isLoading, setIsLoading] = useState(true); // Loading state
19 | const navigate = useNavigate();
20 | const { darkMode } = useDarkMode(); // Access dark mode state
21 |
22 | useEffect(() => {
23 | const rollno = Cookies.get("rollno");
24 | if (rollno) {
25 | fetchInternalResultData(rollno);
26 | fetchExternalResultData(rollno);
27 | } else {
28 | console.error("Roll number not found in cookies");
29 | setIsLoading(false);
30 | }
31 | }, []);
32 |
33 | const fetchInternalResultData = async (rollno) => {
34 | try {
35 | const response = await axios.post(`${baseUrl}/api/internalResultData`, {
36 | mid: 76,
37 | rollno: rollno,
38 | });
39 | parseHtml(response.data, setInternalResultData);
40 | } catch (error) {
41 | console.error("Error fetching internal result data:", error);
42 | } finally {
43 | setIsLoading(false);
44 | }
45 | };
46 |
47 | const fetchExternalResultData = async (rollno) => {
48 | try {
49 | const yearRange = [1, 2, 3, 4];
50 | const semesterRange = [1, 2];
51 | const allResults = [];
52 |
53 | for (let year of yearRange) {
54 | for (let semester of semesterRange) {
55 | const response = await axios.post(`${baseUrl}/api/externalResultData`, {
56 | year,
57 | semester,
58 | rollno: rollno,
59 | });
60 | const parsedData = parseHtml1(response.data);
61 | if (parsedData) {
62 | allResults.push({
63 | year,
64 | semester,
65 | ...parsedData,
66 | });
67 | }
68 | }
69 | }
70 |
71 | setExternalResultData(allResults);
72 |
73 | const backlogResponse = await axios.post(`${baseUrl}/api/backlogs`, {
74 | rollno: rollno,
75 | });
76 | setTotalBacklogs(backlogResponse.data);
77 | } catch (error) {
78 | console.error("Error fetching external result data:", error);
79 | } finally {
80 | setIsLoading(false);
81 | }
82 | };
83 |
84 | const parseHtml = (htmlData, setData) => {
85 | const parser = new DOMParser();
86 | const doc = parser.parseFromString(htmlData, "text/html");
87 | const tables = doc.querySelectorAll(".box-body");
88 |
89 | const parsedData = Array.from(tables).map((table) => {
90 | const titleElement = table.querySelector("h2");
91 | const title = titleElement ? titleElement.textContent : "";
92 |
93 | const thElements = table.querySelectorAll("th");
94 | const columns = Array.from(thElements).map((th) => th.textContent);
95 |
96 | const dataRows = table.querySelectorAll("tr");
97 | const data = Array.from(dataRows)
98 | .slice(1)
99 | .map((row) => {
100 | const rowData = {};
101 | const tdElements = row.querySelectorAll("td");
102 | tdElements.forEach((td, index) => {
103 | if (columns[index]) {
104 | rowData[columns[index]] = td.textContent;
105 | }
106 | });
107 | return rowData;
108 | });
109 |
110 | return { title, columns, data };
111 | });
112 |
113 | setData(parsedData);
114 | };
115 |
116 | const parseHtml1 = (htmlData) => {
117 | const parser = new DOMParser();
118 | const doc = parser.parseFromString(htmlData, "text/html");
119 | const table = doc.querySelector(".tableofcmm");
120 |
121 | if (!table) {
122 | console.error("Table not found in HTML data");
123 | return null;
124 | }
125 |
126 | const columns = Array.from(table.querySelectorAll("th")).map((th) =>
127 | th.textContent.trim()
128 | );
129 | const rows = Array.from(table.querySelectorAll("tbody tr"));
130 |
131 | if (rows.length === 0) {
132 | console.warn("No rows found in the table");
133 | return { columns, data: [], creditsAcquired: "N/A", sgpa: "N/A" };
134 | }
135 |
136 | const data = rows.map((row) => {
137 | const rowData = {};
138 | Array.from(row.querySelectorAll("td")).forEach((td, index) => {
139 | let textContent = td.textContent.trim();
140 | if (textContent.toUpperCase() === "NA") {
141 | textContent = "N/A";
142 | }
143 | rowData[columns[index]] = textContent;
144 | });
145 | return rowData;
146 | });
147 |
148 | let creditsAcquired = "N/A";
149 | let sgpa = "N/A";
150 | const tfoot = table.querySelector("tfoot");
151 | if (tfoot) {
152 | const creditsAcquiredElement = tfoot.querySelector(".creditsacquired");
153 | const sgpaElement = tfoot.querySelector("td:last-child");
154 | if (creditsAcquiredElement && sgpaElement) {
155 | creditsAcquired = creditsAcquiredElement.textContent.trim();
156 | sgpa = sgpaElement.textContent.trim();
157 | }
158 | }
159 |
160 | return { columns, data, creditsAcquired, sgpa };
161 | };
162 |
163 | const handleTabChange = (key) => {
164 | setSelectedTab(key);
165 | };
166 |
167 | return (
168 |
169 |
170 |
171 | {/* Header */}
172 |
180 |
181 | {/* Main Content */}
182 |
183 | {isLoading ? (
184 | // Show loader while data is being fetched
189 | ) : (
190 |
191 | {/* Tabs */}
192 |
193 | handleTabChange("internal")}
195 | className={`flex-1 py-2 sm:py-3 md:py-4 px-3 sm:px-4 md:px-6 text-center text-xs sm:text-sm md:text-base font-medium ${
196 | selectedTab === "internal"
197 | ? `${darkMode ? 'text-blue-400 border-b-2 border-blue-400' : 'text-blue-600 border-b-2 border-blue-600'}`
198 | : `${darkMode ? 'text-gray-400 hover:text-gray-200' : 'text-gray-500 hover:text-gray-700'}`
199 | }`}
200 | >
201 |
202 | Internal Assessment
203 |
204 | handleTabChange("external")}
206 | className={`flex-1 py-2 sm:py-3 md:py-4 px-3 sm:px-4 md:px-6 text-center text-xs sm:text-sm md:text-base font-medium ${
207 | selectedTab === "external"
208 | ? `${darkMode ? 'text-blue-400 border-b-2 border-blue-400' : 'text-blue-600 border-b-2 border-blue-600'}`
209 | : `${darkMode ? 'text-gray-400 hover:text-gray-200' : 'text-gray-500 hover:text-gray-700'}`
210 | }`}
211 | >
212 |
213 | Semester Results
214 |
215 |
216 |
217 | {/* Tab Content */}
218 |
219 | {selectedTab === "internal" ? (
220 |
221 | ) : (
222 |
227 | )}
228 |
229 |
230 | )}
231 |
232 |
233 | );
234 | };
235 |
236 | export default ResultPage;
--------------------------------------------------------------------------------
/Frontend/src/components/SubWiseAtt.jsx:
--------------------------------------------------------------------------------
1 | // import React from 'react';
2 | // import { Card, Typography, Progress, Row, Col } from 'antd';
3 |
4 | // const { Title, Text } = Typography;
5 |
6 | // const AttendancePage = ({ attendanceData }) => {
7 | // return (
8 | //
9 | // {attendanceData.map((subject, index) => (
10 | //
11 | //
{subject.subjectname}
12 | //
13 | //
14 | // Percentage:
15 | //
16 | //
17 | //
18 | // Practical:
19 | //
20 | //
21 | //
22 | //
23 | // ))}
24 | //
25 | // );
26 | // };
27 |
28 | // export default AttendancePage;
29 |
--------------------------------------------------------------------------------
/Frontend/src/components/Timetable.jsx:
--------------------------------------------------------------------------------
1 | // src/components/Timetable.js
2 | import React, { useState, useEffect } from "react";
3 | import axios from "axios";
4 | import { Parser } from "html-to-react";
5 | import { BookOpen, Clock, Calendar } from "lucide-react";
6 | import Cookies from "js-cookie";
7 | import { baseUrl } from "../baseurl";
8 | import { useNavigate } from "react-router-dom";
9 | import Navbar from "./Navbar"; // Import the Navbar component
10 | import { useDarkMode } from "./DarkModeContext"; // Import the dark mode hook
11 |
12 | function Timetable() {
13 | const { darkMode } = useDarkMode(); // Access dark mode state
14 | const [loading, setLoading] = useState(false);
15 | const [timetableData, setTimetableData] = useState([]);
16 | const [selectedDay, setSelectedDay] = useState("Monday");
17 | const [token, setToken] = useState(null);
18 | const parser = new Parser();
19 | const navigate = useNavigate();
20 |
21 | useEffect(() => {
22 | // Retrieve token from cookies
23 | const storedToken = Cookies.get("token");
24 | if (storedToken) {
25 | setToken(storedToken);
26 | fetchData(storedToken);
27 | }
28 | }, []);
29 |
30 | useEffect(() => {
31 | if (token) {
32 | fetchData(token);
33 | }
34 | }, [token]);
35 |
36 | const fetchData = async (currentToken) => {
37 | setLoading(true);
38 | try {
39 | const response = await axios.post(
40 | `${baseUrl}/api/timetable`,
41 | { method: "317" },
42 | {
43 | headers: {
44 | Authorization: `Bearer ${currentToken}`,
45 | },
46 | }
47 | );
48 | let updatedTimetable;
49 | let timetable = response.data.timetable;
50 | if (timetable !== null) {
51 | const daysOfWeek = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
52 | updatedTimetable = daysOfWeek.map((day) => {
53 | const dayData = timetable.find((item) => item.dayname === day);
54 | return dayData || { dayname: day, beforelunch: [], lunch: "", afterlunch: [] };
55 | });
56 | } else {
57 | updatedTimetable = [];
58 | }
59 | setTimetableData(updatedTimetable);
60 | } catch (error) {
61 | console.error("Error fetching timetable:", error);
62 | } finally {
63 | setLoading(false);
64 | }
65 | };
66 |
67 | const days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
68 |
69 | return (
70 |
71 | {/* Add the Navbar */}
72 |
73 |
74 | {/* Header */}
75 |
76 |
77 |
78 |
79 |
80 |
81 | Class Timetable
82 |
83 |
84 |
85 |
86 |
87 |
88 | {/* Main Content */}
89 |
90 | {/* Day Selection Tabs */}
91 |
94 |
95 | {days.map((day) => (
96 | setSelectedDay(day)}
99 | className={`flex-1 min-w-[100px] px-3 sm:px-4 py-2.5 sm:py-3 text-sm font-medium whitespace-nowrap transition-colors ${
100 | selectedDay === day
101 | ? darkMode
102 | ? "text-indigo-400 border-b-2 border-indigo-400 bg-indigo-900/20"
103 | : "text-indigo-600 border-b-2 border-indigo-600 bg-indigo-50/50"
104 | : darkMode
105 | ? "text-gray-300 hover:text-gray-100 hover:bg-gray-700"
106 | : "text-slate-600 hover:text-slate-900 hover:bg-slate-50"
107 | }`}
108 | >
109 | {day}
110 |
111 | ))}
112 |
113 |
114 |
115 | {loading ? (
116 |
117 |
120 |
Loading timetable...
121 |
122 | ) : (
123 |
124 | {/* Morning Sessions */}
125 |
128 |
129 |
130 |
131 |
132 | Morning Sessions
133 |
134 |
135 |
136 |
137 | {timetableData?.find((d) => d.dayname === selectedDay)?.beforelunch.map((session, index) => (
138 |
142 |
143 |
146 |
147 |
148 |
149 |
150 | {session.subject}
151 |
152 |
153 | {parser.parse(session.hour)} {/* Parse HTML content */}
154 |
155 |
156 |
157 |
158 | ))}
159 |
160 |
161 |
162 | {/* Lunch Break */}
163 |
166 |
169 |
170 |
173 | Lunch Break: {timetableData?.find((d) => d.dayname === selectedDay)?.lunch}
174 |
175 |
176 |
177 |
178 | {/* Afternoon Sessions */}
179 |
182 |
183 |
184 |
185 |
186 | Afternoon Sessions
187 |
188 |
189 |
190 |
191 | {timetableData?.find((d) => d.dayname === selectedDay)?.afterlunch.map((session, index) => (
192 |
196 |
197 |
200 |
201 |
202 |
203 |
204 | {session.subject}
205 |
206 |
207 | {parser.parse(session.hour)} {/* Parse HTML content */}
208 |
209 |
210 |
211 |
212 | ))}
213 |
214 |
215 |
216 | )}
217 |
218 |
219 | );
220 | }
221 |
222 | export default Timetable;
--------------------------------------------------------------------------------
/Frontend/src/components/dashboard.css:
--------------------------------------------------------------------------------
1 |
2 | .card {
3 | border-radius: 10px;
4 | border: none;
5 | box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
6 | margin-bottom: 16px;
7 | }
8 |
9 |
10 | @media screen and (min-width: 768px) {
11 | .content {
12 | padding: 20px 50px;
13 | }
14 |
15 | .col {
16 | padding: 0 16px;
17 | }
18 |
19 | .card-meta {
20 | font-size: 16px;
21 | }
22 | }
23 |
24 |
25 | @media screen and (max-width: 768px) {
26 | .content {
27 | padding: 20px 10px;
28 | }
29 |
30 | .col {
31 | padding: 0 8px;
32 | }
33 | .Atten1 {
34 | margin-right: 0 !important;
35 | text-align: center;
36 | }
37 |
38 |
39 | .card {
40 | padding: 24px;
41 | font-size: 16px;
42 | }
43 |
44 | .card-meta {
45 | font-size: 16px;
46 | }
47 | }
48 |
49 |
50 | @media screen and (max-width: 767px) {
51 | .option-card .ant-card-meta-title {
52 | font-size: 16px;
53 | }
54 | }
55 |
56 | @media only screen and (max-width: 767px) {
57 | .attendance-date {
58 | font-size: 10px;
59 | }
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/Frontend/src/components/dashboard.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import {
4 | UserCircle2,
5 | Calendar,
6 | School2,
7 | Users2,
8 | BarChart3,
9 | MessageSquare,
10 | QrCode,
11 | Bell,
12 | Home,
13 | } from "lucide-react";
14 | import axios from "axios";
15 | import Swal from "sweetalert2";
16 | import Cookies from "js-cookie";
17 | import { baseUrl } from "../baseurl";
18 | import LinearProgressBar from "../components/AttendanceTracker";
19 | import Navbar from "./Navbar";
20 | import { useDarkMode } from "./DarkModeContext";
21 | import Modal from "./Modal";
22 | import { Spin } from "antd"; // Import Spin component from Ant Design
23 |
24 | function ProfilePage() {
25 | const { darkMode } = useDarkMode(); // Access dark mode state
26 | const [profileDetails, setProfileDetails] = useState(null);
27 | const [attendanceData, setAttendanceData] = useState(null);
28 | const [attendancePer, setAttendancePer] = useState(0);
29 | const [twoWeekSessions, setTwoWeekSessions] = useState(0);
30 | const [token, setToken] = useState(null);
31 | const [isLoading, setIsLoading] = useState(true);
32 | const navigate = useNavigate();
33 | const [isClubsModalOpen, setIsClubsModalOpen] = useState(false);
34 | const [clubsModalContent, setClubsModalContent] = useState(null);
35 |
36 | useEffect(() => {
37 | let storedToken = Cookies.get("token");
38 | if (storedToken) {
39 | setToken(storedToken);
40 | fetchProfileData(storedToken);
41 | fetchAttendanceData(storedToken);
42 | } else {
43 | navigate("/search");
44 | }
45 | }, []);
46 |
47 | const fetchProfileData = async (token) => {
48 | try {
49 | const response = await axios.post(
50 | `${baseUrl}/api/profile`,
51 | { method: "32" },
52 | { headers: { Authorization: `Bearer ${token}` } }
53 | );
54 | setProfileDetails(response.data);
55 | setIsLoading(false);
56 | } catch (error) {
57 | console.error("Error fetching profile data:", error);
58 | setIsLoading(false);
59 | }
60 | };
61 |
62 | const fetchAttendanceData = async (token) => {
63 | try {
64 | const response = await axios.post(
65 | `${baseUrl}/api/attendance`,
66 | { method: "314" },
67 | { headers: { Authorization: `Bearer ${token}` } }
68 | );
69 | const { dayObjects, totalPercentage, twoWeekSessions } = response.data;
70 | setAttendanceData(dayObjects);
71 | setAttendancePer(totalPercentage);
72 | setTwoWeekSessions(twoWeekSessions);
73 | setIsLoading(false);
74 | } catch (error) {
75 | console.error("Error fetching attendance data:", error);
76 | setIsLoading(false);
77 | }
78 | };
79 |
80 | const handleClubsClick = () => {
81 | setClubsModalContent(
82 |
83 | Club details will be updated soon.
84 |
85 | );
86 | setIsClubsModalOpen(true);
87 | };
88 |
89 | const handleHomeRedirect = () => {
90 | navigate("/search");
91 | };
92 |
93 | if (isLoading) {
94 | return (
95 |
102 | );
103 | }
104 |
105 | if (!profileDetails) {
106 | return (
107 |
108 | {/*
No profile data available.
*/}
109 |
110 | );
111 | }
112 |
113 | const quickActions = [
114 | {
115 | icon: Users2,
116 | label: "Clubs",
117 | color: "bg-purple-100 text-purple-600",
118 | onClick: handleClubsClick,
119 | },
120 | {
121 | icon: Calendar,
122 | label: "Attendance",
123 | color: "bg-blue-100 text-blue-600",
124 | onClick: () => navigate("/attendance"),
125 | },
126 | {
127 | icon: BarChart3,
128 | label: "Results",
129 | color: "bg-green-100 text-green-600",
130 | onClick: () => navigate("/result"),
131 | },
132 | {
133 | icon: School2,
134 | label: "Timetable",
135 | color: "bg-yellow-100 text-yellow-600",
136 | onClick: () => navigate("/timetable"),
137 | },
138 | {
139 | icon: QrCode,
140 | label: "Netra QR",
141 | color: "bg-indigo-100 text-indigo-600",
142 | onClick: () => navigate("/netraqr"),
143 | },
144 | {
145 | icon: MessageSquare,
146 | label: "Feedback",
147 | color: "bg-pink-100 text-pink-600",
148 | onClick: () => navigate("/feedback"),
149 | },
150 | ];
151 |
152 | return (
153 |
154 |
155 |
156 |
157 |
158 |
159 | {/* Home Button */}
160 |
166 |
167 | Home
168 |
169 |
170 |
171 |
174 |
175 |
176 |
177 |
178 | {profileDetails.firstname[0]}
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | {/* Profile Summary */}
189 |
190 |
191 |
192 |
193 | {/* Student's Photo */}
194 |
199 |
200 |
201 | {profileDetails.firstname}
202 |
203 |
204 | {profileDetails.hallticketno}
205 |
206 |
207 |
208 |
209 | Department
210 |
211 | {profileDetails.dept}
212 |
213 |
214 |
215 | Section
216 |
217 | {profileDetails.section}
218 |
219 |
220 |
221 | Current Year
222 |
223 | {profileDetails.currentyear}
224 |
225 |
226 |
227 |
228 | Year of Admission
229 |
230 |
231 | {profileDetails.yearofadmision}
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 | {/* Attendance Overview */}
240 |
241 |
242 |
243 |
244 | Attendance Overview
245 |
246 |
247 |
248 | {/* Animated Progress Bar */}
249 |
250 |
251 |
252 | Overall Attendance
253 |
254 |
255 |
256 | {attendancePer}%
257 |
258 |
259 |
260 | {/* Progress Bar */}
261 |
262 |
263 |
264 | {/* Recent Sessions */}
265 |
266 |
267 | Recent Sessions
268 |
269 | {attendanceData?.slice(0, 8).map((day, index) => (
270 |
276 |
{day.date}
277 |
278 | {Object.values(day.sessions).map((session, idx) => (
279 |
289 | {parseInt(session) === 1
290 | ? "✓"
291 | : parseInt(session) === 0
292 | ? "×"
293 | : "-"}
294 |
295 | ))}
296 |
297 |
298 | ))}
299 |
300 |
301 | {/* Summary Stats */}
302 |
303 |
304 |
305 | {twoWeekSessions.present}
306 |
307 |
Present
308 |
309 |
310 |
311 | {twoWeekSessions.absent}
312 |
313 |
Absent
314 |
315 |
316 |
317 | {twoWeekSessions.nosessions}
318 |
319 |
No Sessions
320 |
321 |
322 |
323 |
324 |
325 | {/* Quick Actions */}
326 |
327 |
328 |
329 | Quick Actions
330 |
331 |
332 | {quickActions.map((action, index) => {
333 | const Icon = action.icon;
334 | return (
335 |
340 |
343 |
344 |
345 |
346 | {action.label}
347 |
348 |
349 | );
350 | })}
351 |
352 |
353 |
354 |
355 |
356 |
setIsClubsModalOpen(false)}
359 | title="Club Details"
360 | type="info"
361 | >
362 | {clubsModalContent}
363 |
364 |
365 | );
366 | }
367 |
368 | export default ProfilePage;
--------------------------------------------------------------------------------
/Frontend/src/components/toast.jsx:
--------------------------------------------------------------------------------
1 | const customToastStyle = {
2 | fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
3 | width: '320px',
4 | padding: '12px',
5 | display: 'flex',
6 | flexDirection: 'row',
7 | alignItems: 'center',
8 | justifyContent: 'start',
9 | background: '#FCE8DB',
10 | borderRadius: '8px',
11 | boxShadow: '0px 0px 5px -3px #111',
12 | };
13 |
14 | const customErrorIconStyle = {
15 | width: '20px',
16 | height: '20px',
17 | transform: 'translateY(-2px)',
18 | marginRight: '8px',
19 | };
20 |
21 | const customErrorTitleStyle = {
22 | fontWeight: '500',
23 | fontSize: '14px',
24 | color: '#71192F',
25 | };
26 |
27 | const customErrorCloseStyle = {
28 | width: '20px',
29 | height: '20px',
30 | cursor: 'pointer',
31 | marginLeft: 'auto',
32 | };
33 |
34 | export { customErrorCloseStyle, customErrorTitleStyle, customToastStyle, customErrorIconStyle };
35 |
--------------------------------------------------------------------------------
/Frontend/src/components/vite.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | build: {
3 | rollupOptions: {
4 | external: ['axios'],
5 | },
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/Frontend/src/index.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
--------------------------------------------------------------------------------
/Frontend/src/main.jsx:
--------------------------------------------------------------------------------
1 | // src/index.js
2 | import React from 'react';
3 | import ReactDOM from 'react-dom/client';
4 | import App from './App';
5 | import { DarkModeProvider } from './components/DarkModeContext'; // Import the provider
6 | import './index.css';
7 |
8 | const root = ReactDOM.createRoot(document.getElementById('root'));
9 | root.render(
10 |
11 |
12 |
13 |
14 |
15 | );
--------------------------------------------------------------------------------
/Frontend/src/setupProxy.js:
--------------------------------------------------------------------------------
1 | // // File: src/setupProxy.js
2 | // const { createProxyMiddleware } = require('http-proxy-middleware');
3 |
4 | // module.exports = function(app) {
5 | // app.use(
6 | // 'teleuniv.in/trinetra/pages/templates/wrapper/studentmanagement/externalmarks_app.php?sid=2421990' ,
7 | // createProxyMiddleware({
8 | // target: 'http://teleuniv.in', // Specify the target URL
9 | // changeOrigin: true, // Needed for virtual hosted sites
10 | // // You can add more configuration options here if needed
11 | // })
12 | // );
13 | // };
14 |
--------------------------------------------------------------------------------
/Frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | // tailwind.config.js
2 | module.exports = {
3 | darkMode: 'class', // Enable class-based dark mode
4 | content: [
5 | "./src/**/*.{js,jsx,ts,tsx}",
6 | ],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | };
--------------------------------------------------------------------------------
/Frontend/vercel.json:
--------------------------------------------------------------------------------
1 | { "rewrites": [{
2 | "source": "/(.*)",
3 | "destination": "/"
4 | }] }
--------------------------------------------------------------------------------
/Frontend/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 | import tailwindcss from '@tailwindcss/vite'
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react(),
7 | tailwindcss(),
8 | ],
9 | })
10 |
--------------------------------------------------------------------------------
/Frontend/vite.config.js.timestamp-1740299920458-4f2d7c42c5279.mjs:
--------------------------------------------------------------------------------
1 | // vite.config.js
2 | import { defineConfig } from "file:///C:/Users/saite/OneDrive/Pictures/Documents/Desktop%20temp/kmit/Frontend/node_modules/vite/dist/node/index.js";
3 | import react from "file:///C:/Users/saite/OneDrive/Pictures/Documents/Desktop%20temp/kmit/Frontend/node_modules/@vitejs/plugin-react/dist/index.mjs";
4 | import tailwindcss from "file:///C:/Users/saite/OneDrive/Pictures/Documents/Desktop%20temp/kmit/Frontend/node_modules/@tailwindcss/vite/dist/index.mjs";
5 | var vite_config_default = defineConfig({
6 | plugins: [
7 | react(),
8 | tailwindcss()
9 | ]
10 | });
11 | export {
12 | vite_config_default as default
13 | };
14 | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxzYWl0ZVxcXFxPbmVEcml2ZVxcXFxQaWN0dXJlc1xcXFxEb2N1bWVudHNcXFxcRGVza3RvcCB0ZW1wXFxcXGttaXRcXFxcRnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkM6XFxcXFVzZXJzXFxcXHNhaXRlXFxcXE9uZURyaXZlXFxcXFBpY3R1cmVzXFxcXERvY3VtZW50c1xcXFxEZXNrdG9wIHRlbXBcXFxca21pdFxcXFxGcm9udGVuZFxcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vQzovVXNlcnMvc2FpdGUvT25lRHJpdmUvUGljdHVyZXMvRG9jdW1lbnRzL0Rlc2t0b3AlMjB0ZW1wL2ttaXQvRnJvbnRlbmQvdml0ZS5jb25maWcuanNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xuaW1wb3J0IHRhaWx3aW5kY3NzIGZyb20gJ0B0YWlsd2luZGNzcy92aXRlJ1xuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtyZWFjdCgpLFxuICAgIHRhaWx3aW5kY3NzKCksXG4gIF0sXG59KVxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFxWixTQUFTLG9CQUFvQjtBQUNsYixPQUFPLFdBQVc7QUFDbEIsT0FBTyxpQkFBaUI7QUFFeEIsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUztBQUFBLElBQUMsTUFBTTtBQUFBLElBQ2QsWUFBWTtBQUFBLEVBQ2Q7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
15 |
--------------------------------------------------------------------------------
/Frontend/vite.config.js.timestamp-1740337046067-10590e187ffe.mjs:
--------------------------------------------------------------------------------
1 | // vite.config.js
2 | import { defineConfig } from "file:///C:/Users/abhig/OneDrive/Desktop/react_JS/new/project/Frontend/node_modules/vite/dist/node/index.js";
3 | import react from "file:///C:/Users/abhig/OneDrive/Desktop/react_JS/new/project/Frontend/node_modules/@vitejs/plugin-react/dist/index.mjs";
4 | import tailwindcss from "file:///C:/Users/abhig/OneDrive/Desktop/react_JS/new/project/Frontend/node_modules/@tailwindcss/vite/dist/index.mjs";
5 | var vite_config_default = defineConfig({
6 | plugins: [
7 | react(),
8 | tailwindcss()
9 | ]
10 | });
11 | export {
12 | vite_config_default as default
13 | };
14 | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxhYmhpZ1xcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXHJlYWN0X0pTXFxcXG5ld1xcXFxwcm9qZWN0XFxcXEZyb250ZW5kXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxhYmhpZ1xcXFxPbmVEcml2ZVxcXFxEZXNrdG9wXFxcXHJlYWN0X0pTXFxcXG5ld1xcXFxwcm9qZWN0XFxcXEZyb250ZW5kXFxcXHZpdGUuY29uZmlnLmpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9DOi9Vc2Vycy9hYmhpZy9PbmVEcml2ZS9EZXNrdG9wL3JlYWN0X0pTL25ldy9wcm9qZWN0L0Zyb250ZW5kL3ZpdGUuY29uZmlnLmpzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xyXG5pbXBvcnQgdGFpbHdpbmRjc3MgZnJvbSAnQHRhaWx3aW5kY3NzL3ZpdGUnXHJcbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXHJcbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XHJcbiAgcGx1Z2luczogW3JlYWN0KCksXHJcbiAgICB0YWlsd2luZGNzcygpLFxyXG4gIF0sXHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBMlgsU0FBUyxvQkFBb0I7QUFDeFosT0FBTyxXQUFXO0FBQ2xCLE9BQU8saUJBQWlCO0FBRXhCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVM7QUFBQSxJQUFDLE1BQU07QUFBQSxJQUNkLFlBQVk7QUFBQSxFQUNkO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | About KMIT Spectra
2 |
3 | Explore our intuitive web platform designed by students for students! Seamlessly navigate attendance records, profile details, and academic results using minimal input such as Name, Phone number, or student ID.
4 | Join us in reshaping student engagement with their educational path. Welcome to a fresh era of accessibility and transparency in academic administration
5 |
--------------------------------------------------------------------------------