From 88f94af2b5725b7414dd11159dde2d580857679a Mon Sep 17 00:00:00 2001 From: AbhinavRai01 Date: Fri, 29 Aug 2025 23:24:16 +0530 Subject: [PATCH 1/8] examSlots backend --- client/src/screens/contributions/index.jsx | 29 +++---- client/src/screens/contributions/styles.scss | 26 +++++++ server/index.js | 2 + .../exam-slots/exam-slots.controller.js | 46 +++++++++++ server/modules/exam-slots/exam-slots.model.js | 78 +++++++++++++++++++ .../modules/exam-slots/exam-slots.routes.js | 9 +++ 6 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 server/modules/exam-slots/exam-slots.controller.js create mode 100644 server/modules/exam-slots/exam-slots.model.js create mode 100644 server/modules/exam-slots/exam-slots.routes.js diff --git a/client/src/screens/contributions/index.jsx b/client/src/screens/contributions/index.jsx index ccf808fc..ecfcadab 100644 --- a/client/src/screens/contributions/index.jsx +++ b/client/src/screens/contributions/index.jsx @@ -100,7 +100,7 @@ const Contributions = () => {
Selected Files will get uploaded to the current folder
-
+
{ pond.current = ref; }} /> -
-
-
-
🚫
-
- Do not close this popup until all files are successfully uploaded! -
-
- {!isBR ? ( -
-
⚠️
-
- Please contact your Branch Representative to verify the files you - have uploaded so that it may be visible to everyone + +
+
Selected Files will get uploaded to the current folder
+
🚫 Do not close this popup until all files are uploaded!
+ {!isBR && ( +
+ ⚠️ Please contact your Branch Representative to verify your uploaded files.
-
- ) : ( - <> - )} + )} +
SUBMIT diff --git a/client/src/screens/contributions/styles.scss b/client/src/screens/contributions/styles.scss index 8468a9fc..b52b989f 100644 --- a/client/src/screens/contributions/styles.scss +++ b/client/src/screens/contributions/styles.scss @@ -174,4 +174,30 @@ .filepond--root { max-height: 20vh; +} + +.file_pond { + margin-top: 1rem; + + .large { + width: 100%; + height: 100%; + } + + &.large { + .filepond--root { + height: 00px; // make it taller + max-width: 100%; // stretch width + font-size: 1.1rem; // slightly bigger text + } + + .filepond--drop-label { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; + padding: 2rem; + } + } } \ No newline at end of file diff --git a/server/index.js b/server/index.js index e0d8c9eb..cdf978b4 100644 --- a/server/index.js +++ b/server/index.js @@ -31,6 +31,7 @@ import brRoutes from "./modules/br/br.routes.js"; import fileRoutes from "./modules/file/file.routes.js"; import folderRoutes from "./modules/folder/folder.routes.js"; import yearRoutes from "./modules/year/year.routes.js"; +import examSlotsRoutes from "./modules/exam-slots/exam-slots.routes.js"; import links from "./links.js"; const app = express(); @@ -75,6 +76,7 @@ app.use("/api/br", brRoutes); app.use("/api/files", fileRoutes); app.use("/api/folder", folderRoutes); app.use("/api/year", yearRoutes); +app.use("/api/examslots", examSlotsRoutes); app.use( "/homepage", diff --git a/server/modules/exam-slots/exam-slots.controller.js b/server/modules/exam-slots/exam-slots.controller.js new file mode 100644 index 00000000..3217c90f --- /dev/null +++ b/server/modules/exam-slots/exam-slots.controller.js @@ -0,0 +1,46 @@ +import express from 'express'; +import ExamSlotModel from './exam-slots.model.js'; + +//check if exam slot exists, if it does edit it, if not create it +export const createOrUpdateExamSlot = async (req, res) => { + try { + const {course} = req.body; + const { branch } = req.body; + const {semester} = req.body; + let examSlot = await ExamSlotModel.findOne({course, branch, semester }); + if (examSlot) { + //update + examSlot = await ExamSlotModel.findOneAndUpdate( + {course, branch, semester }, + { $set: req.body }); + return res.status(200).json({ message: 'Exam slot updated successfully', examSlot }); + } else { + //create + examSlot = new ExamSlotModel(req.body); + await examSlot.save(); + return res.status(201).json({ message: 'Exam slot created successfully', examSlot }); + } + } catch (error) { + console.error('Error in createOrUpdateExamSlot:', error); + return res.status(500).json({ message: 'Server error', error: error.message }); + } +}; + +export const getExamSlot = async (req, res) => { + try { + const {course, branch, semester } = req.params; + const examSlot = await ExamSlotModel.findOne({course, branch, semester }); + if (!examSlot) { + return res.status(404).json({ message: 'Exam slot not found' }); + } + return res.status(200).json(examSlot); + } catch (error) { + console.error('Error in getExamSlot:', error); + return res.status(500).json({ message: 'Server error', error: error.message }); + } +}; + +export default { + createOrUpdateExamSlot, + getExamSlot +} \ No newline at end of file diff --git a/server/modules/exam-slots/exam-slots.model.js b/server/modules/exam-slots/exam-slots.model.js new file mode 100644 index 00000000..456a68d9 --- /dev/null +++ b/server/modules/exam-slots/exam-slots.model.js @@ -0,0 +1,78 @@ +import mongoose from 'mongoose'; +const { Schema, model } = mongoose; + +const courseSlotSchema = { + course:{ + type: String, + required: true, + }, + branch: { + type: String, + required: true, + }, + semester: { + type: Number, + required: true, + }, + A: { + type: String, + required: true, + }, + A1: { + type: String, + required: true, + }, + B: { + type: String, + required: true, + }, + B1: { + type: String, + required: true, + }, + C: { + type: String, + required: true, + }, + C1: { + type: String, + required: true, + }, + D: { + type: String, + required: true, + }, + D1: { + type: String, + required: true, + }, + E: { + type: String, + required: true, + }, + E1: { + type: String, + required: true, + }, + F: { + type: String, + required: true, + }, + F1: { + type: String, + required: true, + }, + G: { + type: String, + required: true, + }, + G1: { + type: String, + required: true, + }, +}; + +const courseSlotModel = new Schema(courseSlotSchema, { timestamps: true }); + +const ExamSlotModel = model('ExamSlot', courseSlotModel); +export default ExamSlotModel; diff --git a/server/modules/exam-slots/exam-slots.routes.js b/server/modules/exam-slots/exam-slots.routes.js new file mode 100644 index 00000000..ff3fc3d2 --- /dev/null +++ b/server/modules/exam-slots/exam-slots.routes.js @@ -0,0 +1,9 @@ +import express from 'express'; +import { createOrUpdateExamSlot, getExamSlot } from './exam-slots.controller.js'; + +const router = express.Router(); + +router.post('/update', createOrUpdateExamSlot); +router.get('/:course/:branch/:semester', getExamSlot); + +export default router; From 881c5b231727cd27b7f661d28b2710f92e7a7502 Mon Sep 17 00:00:00 2001 From: Ritika Sahu Date: Fri, 29 Aug 2025 23:28:35 +0530 Subject: [PATCH 2/8] timetable made --- .../dashboard/components/timetable.jsx | 275 ++++++++++++++++++ .../dashboard/components/timetable.scss | 153 ++++++++++ client/src/screens/dashboard/index.jsx | 14 +- client/yarn.lock | 10 + server/yarn.lock | 5 + 5 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 client/src/screens/dashboard/components/timetable.jsx create mode 100644 client/src/screens/dashboard/components/timetable.scss diff --git a/client/src/screens/dashboard/components/timetable.jsx b/client/src/screens/dashboard/components/timetable.jsx new file mode 100644 index 00000000..c14fdaa4 --- /dev/null +++ b/client/src/screens/dashboard/components/timetable.jsx @@ -0,0 +1,275 @@ +import React, { useState } from 'react'; +import './timetable.scss'; + +const Timetable = ({ user }) => { + const [showForm, setShowForm] = useState(false); + const [formData, setFormData] = useState({ + A: '', B: '', C: '', D: '', E: '', F: '', G: '', + A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '' + }); + + const timeSlots = [ + '8:00 AM', + '9:00 AM', + '10:00 AM', + '11:00 AM', + '12:00 PM', + '1:00 PM', + '2:00 PM', + '3:00 PM', + '4:00 PM', + '5:00 PM' + ]; + + const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; + + // Sample timetable data - you can replace this with your actual data structure + const timetableData = { + 'Monday': { + '9:00 AM': { subject: 'Mathematics', room: 'Room 101', type: 'lecture' }, + '11:00 AM': { subject: 'Physics Lab', room: 'Lab 201', type: 'lab' }, + '2:00 PM': { subject: 'Computer Science', room: 'Room 305', type: 'lecture' } + }, + 'Tuesday': { + '10:00 AM': { subject: 'Chemistry', room: 'Room 203', type: 'lecture' }, + '1:00 PM': { subject: 'Engineering Drawing', room: 'Room 401', type: 'practical' }, + '3:00 PM': { subject: 'English', room: 'Room 102', type: 'lecture' } + }, + 'Wednesday': { + '9:00 AM': { subject: 'Mathematics', room: 'Room 101', type: 'lecture' }, + '12:00 PM': { subject: 'Workshop', room: 'Workshop', type: 'practical' }, + '4:00 PM': { subject: 'Tutorial', room: 'Room 205', type: 'tutorial' } + }, + 'Thursday': { + '8:00 AM': { subject: 'Early Morning Lecture', room: 'Room 301', type: 'lecture' }, + '11:00 AM': { subject: 'Physics', room: 'Room 204', type: 'lecture' }, + '2:00 PM': { subject: 'Lab Session', room: 'Lab 301', type: 'lab' } + }, + 'Friday': { + '10:00 AM': { subject: 'Project Work', room: 'Room 501', type: 'project' }, + '1:00 PM': { subject: 'Seminar', room: 'Seminar Hall', type: 'seminar' }, + '3:00 PM': { subject: 'Study Period', room: 'Library', type: 'study' } + } + }; + + const handleInputChange = (slot, value) => { + setFormData(prev => ({ + ...prev, + [slot]: value + })); + }; + + const handleFormSubmit = () => { + console.log('Timetable Form Data:', formData); + + // Log only non-empty slots + const filledSlots = Object.entries(formData).filter(([key, value]) => value.trim() !== ''); + console.log('Filled Slots:', filledSlots); + + // Close form after submission + setShowForm(false); + }; + + const handleFormCancel = () => { + setShowForm(false); + // Reset form data if needed + // setFormData({ + // A: '', B: '', C: '', D: '', E: '', F: '', G: '', + // A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '' + // }); + }; + + const getClassType = (type) => { + const typeClasses = { + lecture: 'lecture', + lab: 'lab', + practical: 'practical', + tutorial: 'tutorial', + project: 'project', + seminar: 'seminar', + study: 'study' + }; + return typeClasses[type] || 'default'; + }; + + // Check if user is BR (you can adjust this condition based on your user object structure) + const isBR = user?.user?.isBR || user?.isBR; + + return ( +
+ {/* BR Form Button and Modal */} + {isBR && ( +
+ +
+ )} + + {/* Form Modal */} + {showForm && ( +
+
+

Update Timetable Slots

+
+
+ {/* Regular slots A-G */} +
+

Regular Slots

+ {['A', 'B', 'C', 'D', 'E', 'F', 'G'].map(slot => ( +
+
+ Slot {slot}: +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for slot ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
+ + {/* Lab slots A1-G1 */} +
+

Lab Slots

+ {['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'].map(slot => ( +
+
+ Slot {slot}: +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for slot ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
+
+ +
+ + +
+
+
+
+ )} + + {/* Original Timetable */} +
+ {/* Header row with time slots */} +
+ {timeSlots.map((time) => ( +
+ {time} +
+ ))} + + {/* Day rows */} + {days.map((day) => ( + +
{day}
+ {timeSlots.map((time) => { + const classData = timetableData[day]?.[time]; + return ( +
+ {classData ? ( +
+
{classData.subject}
+
{classData.room}
+
+ ) : null} +
+ ); + })} +
+ ))} +
+
+ ); +}; + +export default Timetable; \ No newline at end of file diff --git a/client/src/screens/dashboard/components/timetable.scss b/client/src/screens/dashboard/components/timetable.scss new file mode 100644 index 00000000..78e0eb1c --- /dev/null +++ b/client/src/screens/dashboard/components/timetable.scss @@ -0,0 +1,153 @@ + + +// timetable.scss +.timetable-container { + width: 100%; + overflow-x: auto; + padding: 20px 0; + + .timetable-grid { + display: grid; + grid-template-columns: 120px repeat(10, 1fr); + grid-gap: 1px; + background-color: #e0e0e0; + border: 1px solid #ccc; + border-radius: 8px; + overflow: hidden; + min-width: 800px; + + .time-header { + background-color: #f5f5f5; + padding: 12px 8px; + font-weight: bold; + text-align: center; + border-right: 1px solid #ddd; + } + + .time-slot-header { + background-color: #f5f5f5; + padding: 12px 8px; + font-weight: bold; + text-align: center; + font-size: 0.9rem; + color: #333; + } + + .day-header { + background-color: #f0f0f0; + padding: 12px 8px; + font-weight: bold; + text-align: center; + color: #333; + border-right: 1px solid #ddd; + } + + .timetable-cell { + background-color: white; + padding: 8px; + min-height: 60px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + + &.empty { + background-color: #fafafa; + + &:hover { + background-color: #f0f0f0; + } + } + + &.occupied { + cursor: pointer; + + &:hover { + transform: scale(1.02); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + } + + .class-info { + text-align: center; + width: 100%; + + .subject-name { + font-weight: bold; + font-size: 0.85rem; + margin-bottom: 4px; + color: white; + } + + .room-info { + font-size: 0.7rem; + opacity: 0.9; + color: white; + } + } + + // Different colors for different class types + &.lecture { + background: linear-gradient(135deg, #4CAF50, #45a049); + } + + &.lab { + background: linear-gradient(135deg, #2196F3, #1976D2); + } + + &.practical { + background: linear-gradient(135deg, #FF9800, #F57C00); + } + + &.tutorial { + background: linear-gradient(135deg, #9C27B0, #7B1FA2); + } + + &.project { + background: linear-gradient(135deg, #E91E63, #C2185B); + } + + &.seminar { + background: linear-gradient(135deg, #607D8B, #455A64); + } + + &.study { + background: linear-gradient(135deg, #795548, #5D4037); + } + + &.default { + background: linear-gradient(135deg, #757575, #616161); + } + } + } + } +} + +// Mobile responsiveness +@media (max-width: 768px) { + .timetable-container { + .timetable-grid { + grid-template-columns: 80px repeat(10, minmax(80px, 1fr)); + + .time-slot-header, + .day-header { + font-size: 0.8rem; + padding: 8px 4px; + } + + .timetable-cell { + min-height: 50px; + padding: 4px; + + &.occupied .class-info { + .subject-name { + font-size: 0.75rem; + } + + .room-info { + font-size: 0.65rem; + } + } + } + } + } +} \ No newline at end of file diff --git a/client/src/screens/dashboard/index.jsx b/client/src/screens/dashboard/index.jsx index b9b8f431..c3f079e9 100644 --- a/client/src/screens/dashboard/index.jsx +++ b/client/src/screens/dashboard/index.jsx @@ -9,7 +9,7 @@ import CourseCard from "./components/coursecard"; import ContributionBanner from "./components/contributionbanner"; import Footer from "../../components/footer"; import FavouriteCard from "./components/favouritecard"; - +import Timetable from "./components/timetable.jsx"; import { ChangeCurrentCourse, ResetFileBrowserState } from "../../actions/filebrowser_actions"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; @@ -238,12 +238,13 @@ const Dashboard = () => { text={ showPrevious ? "▼ HIDE PREVIOUS COURSES" - : "▶ SHOW PREVIOUS COURSES" + : "▶️ SHOW PREVIOUS COURSES" } color={"light"} type={"bold"} />
+ {showPrevious && ( <> @@ -268,12 +269,19 @@ const Dashboard = () => {
)} + )} + + + + + +
@@ -306,4 +314,4 @@ const Dashboard = () => { ); }; -export default Dashboard; +export default Dashboard; \ No newline at end of file diff --git a/client/yarn.lock b/client/yarn.lock index bbc033e4..a4ff28eb 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -492,6 +492,11 @@ electron-to-chromium@^1.4.251: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +esbuild-darwin-arm64@0.15.16: + version "0.15.16" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.16.tgz" + integrity sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw== + esbuild@^0.15.9: version "0.15.16" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.15.16.tgz" @@ -561,6 +566,11 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" diff --git a/server/yarn.lock b/server/yarn.lock index 586fa22f..4f24dbbb 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -2040,6 +2040,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" From d5e1afea781b2947654213e6ec53e4be07117838 Mon Sep 17 00:00:00 2001 From: Ritika Sahu Date: Fri, 29 Aug 2025 23:59:53 +0530 Subject: [PATCH 3/8] AL1 --- client/src/screens/dashboard/components/timetable.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/screens/dashboard/components/timetable.jsx b/client/src/screens/dashboard/components/timetable.jsx index c14fdaa4..868f7d48 100644 --- a/client/src/screens/dashboard/components/timetable.jsx +++ b/client/src/screens/dashboard/components/timetable.jsx @@ -152,7 +152,7 @@ const Timetable = ({ user }) => { {/* Regular slots A-G */}

Regular Slots

- {['A', 'B', 'C', 'D', 'E', 'F', 'G'].map(slot => ( + {['A', 'B', 'C', 'D', 'E', 'F', 'G','ML1','ML2','ML3','ML4','ML5'].map(slot => (
Slot {slot}: @@ -177,7 +177,7 @@ const Timetable = ({ user }) => { {/* Lab slots A1-G1 */}

Lab Slots

- {['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'].map(slot => ( + {['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1','AL1','AL2','AL3','AL4','AL5'].map(slot => (
Slot {slot}: From 55c03be97777b58a2967321ba2c554443f8d728f Mon Sep 17 00:00:00 2001 From: Ritika Sahu Date: Sat, 30 Aug 2025 00:30:20 +0530 Subject: [PATCH 4/8] AL1 --- client/src/api/Timetable.js | 17 ++++ .../dashboard/components/timetable.jsx | 54 +++++++--- server/modules/exam-slots/exam-slots.model.js | 98 +++++++------------ 3 files changed, 96 insertions(+), 73 deletions(-) create mode 100644 client/src/api/Timetable.js diff --git a/client/src/api/Timetable.js b/client/src/api/Timetable.js new file mode 100644 index 00000000..259b3a4b --- /dev/null +++ b/client/src/api/Timetable.js @@ -0,0 +1,17 @@ +import axios from 'axios'; +import serverRoot from './server'; + +const API = axios.create({ + baseURL: `${serverRoot}/api/examslots`, + withCredentials: true, +}); + +export const fetchExamSlot = async (course, branch, semester) => { + const { data } = await API.get(`/${course}/${branch}/${semester}`); + return data; +} + +export const createOrUpdateExamSlot = async (examSlotData) => { + const { data } = await API.post('/update', examSlotData); + return data; +} \ No newline at end of file diff --git a/client/src/screens/dashboard/components/timetable.jsx b/client/src/screens/dashboard/components/timetable.jsx index 868f7d48..81850053 100644 --- a/client/src/screens/dashboard/components/timetable.jsx +++ b/client/src/screens/dashboard/components/timetable.jsx @@ -1,5 +1,7 @@ import React, { useState } from 'react'; import './timetable.scss'; +import { createOrUpdateExamSlot } from '../../../api/Timetable'; // Adjust path as needed +import { toast } from 'react-toastify'; const Timetable = ({ user }) => { const [showForm, setShowForm] = useState(false); @@ -59,15 +61,45 @@ const Timetable = ({ user }) => { })); }; - const handleFormSubmit = () => { - console.log('Timetable Form Data:', formData); - - // Log only non-empty slots - const filledSlots = Object.entries(formData).filter(([key, value]) => value.trim() !== ''); - console.log('Filled Slots:', filledSlots); - - // Close form after submission - setShowForm(false); + const handleFormSubmit = async () => { + try { + // Prepare the data for API request + const examSlotData = { + branch: user?.user?.department || user?.department, + semester: user?.user?.semester || user?.semester, + course: user?.user?.degree || user?.degree, + // Include all slots (A-G and A1-G1), even if empty + A: formData.A || '', + B: formData.B || '', + C: formData.C || '', + D: formData.D || '', + E: formData.E || '', + F: formData.F || '', + G: formData.G || '', + A1: formData.A1 || '', + B1: formData.B1 || '', + C1: formData.C1 || '', + D1: formData.D1 || '', + E1: formData.E1 || '', + F1: formData.F1 || '', + G1: formData.G1 || '' + }; + + console.log('Submitting timetable data:', examSlotData); + + // Make API request + const response = await createOrUpdateExamSlot(examSlotData); + + console.log('Timetable updated successfully:', response); + toast.success('Timetable updated successfully!'); + + // Close form after successful submission + setShowForm(false); + + } catch (error) { + console.error('Error updating timetable:', error); + toast.error('Failed to update timetable. Please try again.'); + } }; const handleFormCancel = () => { @@ -152,7 +184,7 @@ const Timetable = ({ user }) => { {/* Regular slots A-G */}

Regular Slots

- {['A', 'B', 'C', 'D', 'E', 'F', 'G','ML1','ML2','ML3','ML4','ML5'].map(slot => ( + {['A', 'B', 'C', 'D', 'E', 'F', 'G'].map(slot => (
Slot {slot}: @@ -177,7 +209,7 @@ const Timetable = ({ user }) => { {/* Lab slots A1-G1 */}

Lab Slots

- {['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1','AL1','AL2','AL3','AL4','AL5'].map(slot => ( + {['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'].map(slot => (
Slot {slot}: diff --git a/server/modules/exam-slots/exam-slots.model.js b/server/modules/exam-slots/exam-slots.model.js index 456a68d9..f8654cca 100644 --- a/server/modules/exam-slots/exam-slots.model.js +++ b/server/modules/exam-slots/exam-slots.model.js @@ -2,77 +2,51 @@ import mongoose from 'mongoose'; const { Schema, model } = mongoose; const courseSlotSchema = { - course:{ + course: { type: String, - required: true, - }, + required: true, // still required (probably needed for uniqueness) + }, branch: { type: String, - required: true, + required: true, // still required }, semester: { type: Number, - required: true, - }, - A: { - type: String, - required: true, - }, - A1: { - type: String, - required: true, - }, - B: { - type: String, - required: true, - }, - B1: { - type: String, - required: true, - }, - C: { - type: String, - required: true, - }, - C1: { - type: String, - required: true, - }, - D: { - type: String, - required: true, - }, - D1: { - type: String, - required: true, - }, - E: { - type: String, - required: true, - }, - E1: { - type: String, - required: true, - }, - F: { - type: String, - required: true, - }, - F1: { - type: String, - required: true, - }, - G: { - type: String, - required: true, - }, - G1: { - type: String, - required: true, + required: true, // still required }, + + // Regular slots + A: { type: String, required: false }, + A1: { type: String, required: false }, + B: { type: String, required: false }, + B1: { type: String, required: false }, + C: { type: String, required: false }, + C1: { type: String, required: false }, + D: { type: String, required: false }, + D1: { type: String, required: false }, + E: { type: String, required: false }, + E1: { type: String, required: false }, + F: { type: String, required: false }, + F1: { type: String, required: false }, + G: { type: String, required: false }, + G1: { type: String, required: false }, + + // ML slots + ML1: { type: String, required: false }, + ML2: { type: String, required: false }, + ML3: { type: String, required: false }, + ML4: { type: String, required: false }, + ML5: { type: String, required: false }, + + // AL slots + AL1: { type: String, required: false }, + AL2: { type: String, required: false }, + AL3: { type: String, required: false }, + AL4: { type: String, required: false }, + AL5: { type: String, required: false }, }; const courseSlotModel = new Schema(courseSlotSchema, { timestamps: true }); const ExamSlotModel = model('ExamSlot', courseSlotModel); -export default ExamSlotModel; +export default ExamSlotModel; \ No newline at end of file From 533ab37ad471256c4a54821a7d7b5cebc310e01e Mon Sep 17 00:00:00 2001 From: AbhinavRai01 Date: Sun, 31 Aug 2025 15:04:59 +0530 Subject: [PATCH 5/8] fetching the exam slots now working --- client/src/api/Timetable.js | 17 ++++ .../dashboard/components/timetable.jsx | 22 +++++ .../exam-slots/exam-slots.controller.js | 4 +- server/modules/exam-slots/exam-slots.model.js | 96 +++++++------------ 4 files changed, 77 insertions(+), 62 deletions(-) create mode 100644 client/src/api/Timetable.js diff --git a/client/src/api/Timetable.js b/client/src/api/Timetable.js new file mode 100644 index 00000000..259b3a4b --- /dev/null +++ b/client/src/api/Timetable.js @@ -0,0 +1,17 @@ +import axios from 'axios'; +import serverRoot from './server'; + +const API = axios.create({ + baseURL: `${serverRoot}/api/examslots`, + withCredentials: true, +}); + +export const fetchExamSlot = async (course, branch, semester) => { + const { data } = await API.get(`/${course}/${branch}/${semester}`); + return data; +} + +export const createOrUpdateExamSlot = async (examSlotData) => { + const { data } = await API.post('/update', examSlotData); + return data; +} \ No newline at end of file diff --git a/client/src/screens/dashboard/components/timetable.jsx b/client/src/screens/dashboard/components/timetable.jsx index c14fdaa4..1fd10a81 100644 --- a/client/src/screens/dashboard/components/timetable.jsx +++ b/client/src/screens/dashboard/components/timetable.jsx @@ -1,8 +1,12 @@ import React, { useState } from 'react'; import './timetable.scss'; +import server from '../../../api/server'; +import { useEffect } from 'react'; +import { fetchExamSlot } from '../../../api/Timetable'; const Timetable = ({ user }) => { const [showForm, setShowForm] = useState(false); + const [examSlots, setExamSlots] = useState({}); const [formData, setFormData] = useState({ A: '', B: '', C: '', D: '', E: '', F: '', G: '', A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '' @@ -95,6 +99,24 @@ const Timetable = ({ user }) => { // Check if user is BR (you can adjust this condition based on your user object structure) const isBR = user?.user?.isBR || user?.isBR; +useEffect(() => { + const fetchData = async () => { + console.log(user); + console.log(user?.user?.degree, user?.user?.department,user?.user?.semester); + + var branch = user?.user?.department; + // replace spaces of branch with - + branch = branch.replace(/ /g, '-'); + + console.log(branch); + + const response = await fetchExamSlot(user?.user?.degree, branch,user?.user?.semester); + console.log(response); + }; + + fetchData(); +}, [examSlots, user]); + return (
{/* BR Form Button and Modal */} diff --git a/server/modules/exam-slots/exam-slots.controller.js b/server/modules/exam-slots/exam-slots.controller.js index 3217c90f..44550b8b 100644 --- a/server/modules/exam-slots/exam-slots.controller.js +++ b/server/modules/exam-slots/exam-slots.controller.js @@ -29,7 +29,9 @@ export const createOrUpdateExamSlot = async (req, res) => { export const getExamSlot = async (req, res) => { try { const {course, branch, semester } = req.params; - const examSlot = await ExamSlotModel.findOne({course, branch, semester }); + + const branchFixed = branch.replace(/-/g, ' '); + const examSlot = await ExamSlotModel.findOne({course, branchFixed, semester }); if (!examSlot) { return res.status(404).json({ message: 'Exam slot not found' }); } diff --git a/server/modules/exam-slots/exam-slots.model.js b/server/modules/exam-slots/exam-slots.model.js index 456a68d9..b43907f9 100644 --- a/server/modules/exam-slots/exam-slots.model.js +++ b/server/modules/exam-slots/exam-slots.model.js @@ -2,74 +2,48 @@ import mongoose from 'mongoose'; const { Schema, model } = mongoose; const courseSlotSchema = { - course:{ + course: { type: String, - required: true, - }, + required: true, // still required (probably needed for uniqueness) + }, branch: { type: String, - required: true, + required: true, // still required }, semester: { type: Number, - required: true, - }, - A: { - type: String, - required: true, - }, - A1: { - type: String, - required: true, - }, - B: { - type: String, - required: true, - }, - B1: { - type: String, - required: true, - }, - C: { - type: String, - required: true, - }, - C1: { - type: String, - required: true, - }, - D: { - type: String, - required: true, - }, - D1: { - type: String, - required: true, - }, - E: { - type: String, - required: true, - }, - E1: { - type: String, - required: true, - }, - F: { - type: String, - required: true, - }, - F1: { - type: String, - required: true, - }, - G: { - type: String, - required: true, - }, - G1: { - type: String, - required: true, + required: true, // still required }, + + // Regular slots + A: { type: String, required: false }, + A1: { type: String, required: false }, + B: { type: String, required: false }, + B1: { type: String, required: false }, + C: { type: String, required: false }, + C1: { type: String, required: false }, + D: { type: String, required: false }, + D1: { type: String, required: false }, + E: { type: String, required: false }, + E1: { type: String, required: false }, + F: { type: String, required: false }, + F1: { type: String, required: false }, + G: { type: String, required: false }, + G1: { type: String, required: false }, + + // ML slots + ML1: { type: String, required: false }, + ML2: { type: String, required: false }, + ML3: { type: String, required: false }, + ML4: { type: String, required: false }, + ML5: { type: String, required: false }, + + // AL slots + AL1: { type: String, required: false }, + AL2: { type: String, required: false }, + AL3: { type: String, required: false }, + AL4: { type: String, required: false }, + AL5: { type: String, required: false }, }; const courseSlotModel = new Schema(courseSlotSchema, { timestamps: true }); From a0e469ee6710b7e9eb51276490271a3a0b3c3009 Mon Sep 17 00:00:00 2001 From: sahana-athota Date: Sun, 31 Aug 2025 15:57:06 +0530 Subject: [PATCH 6/8] final timetable hopefully --- .../dashboard/components/timetable.jsx | 210 ++++++--- .../dashboard/components/timetable2.jsx | 434 ++++++++++++++++++ 2 files changed, 592 insertions(+), 52 deletions(-) create mode 100644 client/src/screens/dashboard/components/timetable2.jsx diff --git a/client/src/screens/dashboard/components/timetable.jsx b/client/src/screens/dashboard/components/timetable.jsx index 3c7c4b6c..7412ba7b 100644 --- a/client/src/screens/dashboard/components/timetable.jsx +++ b/client/src/screens/dashboard/components/timetable.jsx @@ -1,8 +1,6 @@ import React, { useState } from 'react'; import './timetable.scss'; -import server from '../../../api/server'; -import { useEffect } from 'react'; -import { fetchExamSlot, createOrUpdateExamSlot } from '../../../api/Timetable'; +import { createOrUpdateExamSlot } from '../../../api/Timetable'; // Adjust path as needed import { toast } from 'react-toastify'; const Timetable = ({ user }) => { @@ -10,9 +8,11 @@ const Timetable = ({ user }) => { const [examSlots, setExamSlots] = useState({}); const [formData, setFormData] = useState({ A: '', B: '', C: '', D: '', E: '', F: '', G: '', - A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '' + A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '', + ML1: '', ML2: '', ML3: '', ML4: '', ML5: '', + AL1: '', AL2: '', AL3: '', AL4: '', AL5: '' }); - + console.log(examSlots); const timeSlots = [ '8:00 AM', '9:00 AM', @@ -28,35 +28,109 @@ const Timetable = ({ user }) => { const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; - // Sample timetable data - you can replace this with your actual data structure - const timetableData = { - 'Monday': { - '9:00 AM': { subject: 'Mathematics', room: 'Room 101', type: 'lecture' }, - '11:00 AM': { subject: 'Physics Lab', room: 'Lab 201', type: 'lab' }, - '2:00 PM': { subject: 'Computer Science', room: 'Room 305', type: 'lecture' } - }, - 'Tuesday': { - '10:00 AM': { subject: 'Chemistry', room: 'Room 203', type: 'lecture' }, - '1:00 PM': { subject: 'Engineering Drawing', room: 'Room 401', type: 'practical' }, - '3:00 PM': { subject: 'English', room: 'Room 102', type: 'lecture' } - }, - 'Wednesday': { - '9:00 AM': { subject: 'Mathematics', room: 'Room 101', type: 'lecture' }, - '12:00 PM': { subject: 'Workshop', room: 'Workshop', type: 'practical' }, - '4:00 PM': { subject: 'Tutorial', room: 'Room 205', type: 'tutorial' } - }, - 'Thursday': { - '8:00 AM': { subject: 'Early Morning Lecture', room: 'Room 301', type: 'lecture' }, - '11:00 AM': { subject: 'Physics', room: 'Room 204', type: 'lecture' }, - '2:00 PM': { subject: 'Lab Session', room: 'Lab 301', type: 'lab' } - }, - 'Friday': { - '10:00 AM': { subject: 'Project Work', room: 'Room 501', type: 'project' }, - '1:00 PM': { subject: 'Seminar', room: 'Seminar Hall', type: 'seminar' }, - '3:00 PM': { subject: 'Study Period', room: 'Library', type: 'study' } - } + // Generate timetable data based on examSlots + const generateTimetableData = () => { + const slotTimes = { + 'A': '8:00 AM', + 'B': '9:00 AM', + 'C': '10:00 AM', + 'D': '11:00 AM', + 'E': '8:00 AM', + 'F': '12:00 PM', + 'G': '12:00 PM', + 'A1': '5:00 PM', + 'B1': '4:00 PM', + 'C1': '3:00 PM', + 'D1': '2:00 PM', + 'E1': '5:00 PM', + 'F1': '1:00 PM', + 'G1': '1:00 PM' + }; + + const daySchedule = { + 'Monday': { + '8:00 AM': 'A', + '9:00 AM': examSlots.ML1 ? 'ML1' : 'B', + '10:00 AM': examSlots.ML1 ? 'ML1' : 'C', + '11:00 AM': examSlots.ML1 ? 'ML1' : 'D', + '12:00 PM': 'F', + '1:00 PM': 'F1', + '2:00 PM': 'D1', + '3:00 PM': examSlots.AL1 ? 'AL1' : 'C1', + '4:00 PM': 'B1', + '5:00 PM': 'A1' + }, + 'Tuesday': { + '8:00 AM': 'E', + '9:00 AM': examSlots.ML2 ? 'ML2' : 'A', + '10:00 AM': examSlots.ML2 ? 'ML2' : 'B', + '11:00 AM': examSlots.ML2 ? 'ML2' : 'C', + '12:00 PM': 'F', + '1:00 PM': 'F1', + '2:00 PM': 'C1', + '3:00 PM': examSlots.AL2 ? 'AL2' : 'B1', + '4:00 PM': 'A1', + '5:00 PM': 'E1' + }, + 'Wednesday': { + '8:00 AM': 'D', + '9:00 AM': examSlots.ML3 ? 'ML3' : 'E', + '10:00 AM': examSlots.ML3 ? 'ML3' : 'A', + '11:00 AM': examSlots.ML3 ? 'ML3' : 'B', + '12:00 PM': 'G', + '1:00 PM': 'G1', + '2:00 PM': 'B1', + '3:00 PM': examSlots.AL3 ? 'AL3' : 'A1', + '4:00 PM': 'E1', + '5:00 PM': 'D1' + }, + 'Thursday': { + '8:00 AM': 'C', + '9:00 AM': examSlots.ML4 ? 'ML4' : 'D', + '10:00 AM': examSlots.ML4 ? 'ML4' : 'E', + '11:00 AM': examSlots.ML4 ? 'ML4' : 'A', + '12:00 PM': 'G', + '1:00 PM': 'G1', + '2:00 PM': 'A1', + '3:00 PM': examSlots.AL4 ? 'AL4' : 'E1', + '4:00 PM': 'D1', + '5:00 PM': 'C1' + }, + 'Friday': { + '8:00 AM': 'B', + '9:00 AM': examSlots.ML5 ? 'ML5' : 'C', + '10:00 AM': examSlots.ML5 ? 'ML5' : 'D', + '11:00 AM': examSlots.ML5 ? 'ML5' : 'F', + '12:00 PM': 'G', + '1:00 PM': 'G1', + '2:00 PM': 'F1', + '3:00 PM': examSlots.AL5 ? 'AL5' : 'D1', + '4:00 PM': 'C1', + '5:00 PM': 'B1' + } + }; + + const timetableData = {}; + + days.forEach(day => { + timetableData[day] = {}; + Object.entries(daySchedule[day]).forEach(([time, slot]) => { + const courseCode = examSlots[slot]; + if (courseCode && courseCode.trim() !== '') { + timetableData[day][time] = { + subject: courseCode, + room: `Slot ${slot}`, + type: slot.includes('L') ? 'lab' : 'lecture' + }; + } + }); + }); + + return timetableData; }; + const timetableData = generateTimetableData(); + const handleInputChange = (slot, value) => { setFormData(prev => ({ ...prev, @@ -68,9 +142,9 @@ const Timetable = ({ user }) => { try { // Prepare the data for API request const examSlotData = { - branch: user?.user?.department || user?.department, + department: user?.user?.department || user?.department, semester: user?.user?.semester || user?.semester, - course: user?.user?.degree || user?.degree, + branch: user?.user?.branch || user?.branch, // Include all slots (A-G and A1-G1), even if empty A: formData.A || '', B: formData.B || '', @@ -130,24 +204,6 @@ const Timetable = ({ user }) => { // Check if user is BR (you can adjust this condition based on your user object structure) const isBR = user?.user?.isBR || user?.isBR; -useEffect(() => { - const fetchData = async () => { - console.log(user); - console.log(user?.user?.degree, user?.user?.department,user?.user?.semester); - - var branch = user?.user?.department; - // replace spaces of branch with - - branch = branch.replace(/ /g, '-'); - - console.log(branch); - - const response = await fetchExamSlot(user?.user?.degree, branch,user?.user?.semester); - console.log(response); - }; - - fetchData(); -}, [examSlots, user]); - return (
{/* BR Form Button and Modal */} @@ -251,6 +307,56 @@ useEffect(() => {
))}
+ + {/* ML slots ML1-ML5 */} +
+

Morning Lab Slots

+ {['ML1', 'ML2', 'ML3', 'ML4', 'ML5'].map(slot => ( +
+
+ {slot} (9-12): +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
+ + {/* AL slots AL1-AL5 */} +
+

Afternoon Lab Slots

+ {['AL1', 'AL2', 'AL3', 'AL4', 'AL5'].map(slot => ( +
+
+ {slot} (3-6): +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
diff --git a/client/src/screens/dashboard/components/timetable2.jsx b/client/src/screens/dashboard/components/timetable2.jsx new file mode 100644 index 00000000..2112fec4 --- /dev/null +++ b/client/src/screens/dashboard/components/timetable2.jsx @@ -0,0 +1,434 @@ +import React, { useState } from 'react'; +import './timetable.scss'; +import { createOrUpdateExamSlot } from '../../../api/Timetable'; // Adjust path as needed +import { toast } from 'react-toastify'; + +const Timetable = ({ user }) => { + const [showForm, setShowForm] = useState(false); + const [examSlots, setExamSlots] = useState({}); + const [formData, setFormData] = useState({ + A: '', B: '', C: '', D: '', E: '', F: '', G: '', + A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '', + ML1: '', ML2: '', ML3: '', ML4: '', ML5: '', + AL1: '', AL2: '', AL3: '', AL4: '', AL5: '' + }); + + const timeSlots = [ + '8:00 AM', + '9:00 AM', + '10:00 AM', + '11:00 AM', + '12:00 PM', + '1:00 PM', + '2:00 PM', + '3:00 PM', + '4:00 PM', + '5:00 PM' + ]; + + const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; + + // Generate timetable data based on examSlots + const generateTimetableData = () => { + const slotTimes = { + 'A': '8:00 AM', + 'B': '9:00 AM', + 'C': '10:00 AM', + 'D': '11:00 AM', + 'E': '8:00 AM', + 'F': '12:00 PM', + 'G': '12:00 PM', + 'A1': '5:00 PM', + 'B1': '4:00 PM', + 'C1': '3:00 PM', + 'D1': '2:00 PM', + 'E1': '5:00 PM', + 'F1': '1:00 PM', + 'G1': '1:00 PM' + }; + + const daySchedule = { + 'Monday': { + '8:00 AM': 'A', + '9:00 AM': examSlots.ML1 ? 'ML1' : 'B', + '10:00 AM': examSlots.ML1 ? 'ML1' : 'C', + '11:00 AM': examSlots.ML1 ? 'ML1' : 'D', + '12:00 PM': 'F', + '1:00 PM': 'F1', + '2:00 PM': 'D1', + '3:00 PM': examSlots.AL1 ? 'AL1' : 'C1', + '4:00 PM': 'B1', + '5:00 PM': 'A1' + }, + 'Tuesday': { + '8:00 AM': 'E', + '9:00 AM': examSlots.ML2 ? 'ML2' : 'A', + '10:00 AM': examSlots.ML2 ? 'ML2' : 'B', + '11:00 AM': examSlots.ML2 ? 'ML2' : 'C', + '12:00 PM': 'F', + '1:00 PM': 'F1', + '2:00 PM': 'C1', + '3:00 PM': examSlots.AL2 ? 'AL2' : 'B1', + '4:00 PM': 'A1', + '5:00 PM': 'E1' + }, + 'Wednesday': { + '8:00 AM': 'D', + '9:00 AM': examSlots.ML3 ? 'ML3' : 'E', + '10:00 AM': examSlots.ML3 ? 'ML3' : 'A', + '11:00 AM': examSlots.ML3 ? 'ML3' : 'B', + '12:00 PM': 'G', + '1:00 PM': 'G1', + '2:00 PM': 'B1', + '3:00 PM': examSlots.AL3 ? 'AL3' : 'A1', + '4:00 PM': 'E1', + '5:00 PM': 'D1' + }, + 'Thursday': { + '8:00 AM': 'C', + '9:00 AM': examSlots.ML4 ? 'ML4' : 'D', + '10:00 AM': examSlots.ML4 ? 'ML4' : 'E', + '11:00 AM': examSlots.ML4 ? 'ML4' : 'A', + '12:00 PM': 'G', + '1:00 PM': 'G1', + '2:00 PM': 'A1', + '3:00 PM': examSlots.AL4 ? 'AL4' : 'E1', + '4:00 PM': 'D1', + '5:00 PM': 'C1' + }, + 'Friday': { + '8:00 AM': 'B', + '9:00 AM': examSlots.ML5 ? 'ML5' : 'C', + '10:00 AM': examSlots.ML5 ? 'ML5' : 'D', + '11:00 AM': examSlots.ML5 ? 'ML5' : 'F', + '12:00 PM': 'G', + '1:00 PM': 'G1', + '2:00 PM': 'F1', + '3:00 PM': examSlots.AL5 ? 'AL5' : 'D1', + '4:00 PM': 'C1', + '5:00 PM': 'B1' + } + }; + + const timetableData = {}; + + days.forEach(day => { + timetableData[day] = {}; + Object.entries(daySchedule[day]).forEach(([time, slot]) => { + const courseCode = examSlots[slot]; + if (courseCode && courseCode.trim() !== '') { + timetableData[day][time] = { + subject: courseCode, + room: `Slot ${slot}`, + type: slot.includes('L') ? 'lab' : 'lecture' + }; + } + }); + }); + + return timetableData; + }; + + const timetableData = generateTimetableData(); + + const handleInputChange = (slot, value) => { + setFormData(prev => ({ + ...prev, + [slot]: value + })); + }; + + const handleFormSubmit = async () => { + try { + // Prepare the data for API request + const examSlotData = { + department: user?.user?.department || user?.department, + semester: user?.user?.semester || user?.semester, + branch: user?.user?.branch || user?.branch, + // Include all slots (A-G and A1-G1), even if empty + A: formData.A || '', + B: formData.B || '', + C: formData.C || '', + D: formData.D || '', + E: formData.E || '', + F: formData.F || '', + G: formData.G || '', + A1: formData.A1 || '', + B1: formData.B1 || '', + C1: formData.C1 || '', + D1: formData.D1 || '', + E1: formData.E1 || '', + F1: formData.F1 || '', + G1: formData.G1 || '' + }; + + console.log('Submitting timetable data:', examSlotData); + + // Make API request + const response = await createOrUpdateExamSlot(examSlotData); + + console.log('Timetable updated successfully:', response); + toast.success('Timetable updated successfully!'); + + // Close form after successful submission + setShowForm(false); + + } catch (error) { + console.error('Error updating timetable:', error); + toast.error('Failed to update timetable. Please try again.'); + } + }; + + const handleFormCancel = () => { + setShowForm(false); + // Reset form data if needed + // setFormData({ + // A: '', B: '', C: '', D: '', E: '', F: '', G: '', + // A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '' + // }); + }; + + const getClassType = (type) => { + const typeClasses = { + lecture: 'lecture', + lab: 'lab', + practical: 'practical', + tutorial: 'tutorial', + project: 'project', + seminar: 'seminar', + study: 'study' + }; + return typeClasses[type] || 'default'; + }; + + // Check if user is BR (you can adjust this condition based on your user object structure) + const isBR = user?.user?.isBR || user?.isBR; + + return ( +
+ {/* BR Form Button and Modal */} + {isBR && ( +
+ +
+ )} + + {/* Form Modal */} + {showForm && ( +
+
+

Update Timetable Slots

+
+
+ {/* Regular slots A-G */} +
+

Regular Slots

+ {['A', 'B', 'C', 'D', 'E', 'F', 'G'].map(slot => ( +
+
+ Slot {slot}: +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for slot ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
+ + {/* Lab slots A1-G1 */} +
+

Lab Slots

+ {['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'].map(slot => ( +
+
+ Slot {slot}: +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for slot ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
+ + {/* ML slots ML1-ML5 */} +
+

Morning Lab Slots

+ {['ML1', 'ML2', 'ML3', 'ML4', 'ML5'].map(slot => ( +
+
+ {slot} (9-12): +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
+ + {/* AL slots AL1-AL5 */} +
+

Afternoon Lab Slots

+ {['AL1', 'AL2', 'AL3', 'AL4', 'AL5'].map(slot => ( +
+
+ {slot} (3-6): +
+ handleInputChange(slot, e.target.value)} + placeholder={`Enter course for ${slot}`} + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ddd', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ ))} +
+
+ +
+ + +
+
+
+
+ )} + + {/* Original Timetable */} +
+ {/* Header row with time slots */} +
+ {timeSlots.map((time) => ( +
+ {time} +
+ ))} + + {/* Day rows */} + {days.map((day) => ( + +
{day}
+ {timeSlots.map((time) => { + const classData = timetableData[day]?.[time]; + return ( +
+ {classData ? ( +
+
{classData.subject}
+
{classData.room}
+
+ ) : null} +
+ ); + })} +
+ ))} +
+
+ ); +}; + +export default Timetable; \ No newline at end of file From bbf924bfaa1718df6df1c51d3d7cba184ce1b543 Mon Sep 17 00:00:00 2001 From: AbhinavRai01 Date: Sun, 31 Aug 2025 16:43:37 +0530 Subject: [PATCH 7/8] final thing --- .../dashboard/components/timetable.jsx | 233 ++++++++++++++---- .../dashboard/components/timetable.scss | 13 +- 2 files changed, 186 insertions(+), 60 deletions(-) diff --git a/client/src/screens/dashboard/components/timetable.jsx b/client/src/screens/dashboard/components/timetable.jsx index 7412ba7b..8067d46f 100644 --- a/client/src/screens/dashboard/components/timetable.jsx +++ b/client/src/screens/dashboard/components/timetable.jsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import './timetable.scss'; -import { createOrUpdateExamSlot } from '../../../api/Timetable'; // Adjust path as needed +import { createOrUpdateExamSlot, fetchExamSlot } from '../../../api/Timetable'; // Adjust path as needed +import { useEffect } from 'react'; import { toast } from 'react-toastify'; const Timetable = ({ user }) => { @@ -55,9 +56,9 @@ const Timetable = ({ user }) => { '11:00 AM': examSlots.ML1 ? 'ML1' : 'D', '12:00 PM': 'F', '1:00 PM': 'F1', - '2:00 PM': 'D1', + '2:00 PM': examSlots.AL1 ? 'AL1' : 'D1', '3:00 PM': examSlots.AL1 ? 'AL1' : 'C1', - '4:00 PM': 'B1', + '4:00 PM': examSlots.AL1 ? 'AL1' : 'B1', '5:00 PM': 'A1' }, 'Tuesday': { @@ -67,9 +68,9 @@ const Timetable = ({ user }) => { '11:00 AM': examSlots.ML2 ? 'ML2' : 'C', '12:00 PM': 'F', '1:00 PM': 'F1', - '2:00 PM': 'C1', + '2:00 PM': examSlots.AL2 ? 'AL2' : 'C1', '3:00 PM': examSlots.AL2 ? 'AL2' : 'B1', - '4:00 PM': 'A1', + '4:00 PM': examSlots.AL2 ? 'AL2' : 'A1', '5:00 PM': 'E1' }, 'Wednesday': { @@ -79,9 +80,9 @@ const Timetable = ({ user }) => { '11:00 AM': examSlots.ML3 ? 'ML3' : 'B', '12:00 PM': 'G', '1:00 PM': 'G1', - '2:00 PM': 'B1', + '2:00 PM': examSlots.AL3 ? 'AL3' : 'B1', '3:00 PM': examSlots.AL3 ? 'AL3' : 'A1', - '4:00 PM': 'E1', + '4:00 PM': examSlots.AL3 ? 'AL3' : 'E1', '5:00 PM': 'D1' }, 'Thursday': { @@ -91,9 +92,9 @@ const Timetable = ({ user }) => { '11:00 AM': examSlots.ML4 ? 'ML4' : 'A', '12:00 PM': 'G', '1:00 PM': 'G1', - '2:00 PM': 'A1', + '2:00 PM': examSlots.AL4 ? 'AL4' : 'A1', '3:00 PM': examSlots.AL4 ? 'AL4' : 'E1', - '4:00 PM': 'D1', + '4:00 PM': examSlots.AL4 ? 'AL4' : 'D1', '5:00 PM': 'C1' }, 'Friday': { @@ -103,9 +104,9 @@ const Timetable = ({ user }) => { '11:00 AM': examSlots.ML5 ? 'ML5' : 'F', '12:00 PM': 'G', '1:00 PM': 'G1', - '2:00 PM': 'F1', + '2:00 PM': examSlots.AL5 ? 'AL5' : 'F1', '3:00 PM': examSlots.AL5 ? 'AL5' : 'D1', - '4:00 PM': 'C1', + '4:00 PM': examSlots.AL5 ? 'AL5' : 'C1', '5:00 PM': 'B1' } }; @@ -141,26 +142,42 @@ const Timetable = ({ user }) => { const handleFormSubmit = async () => { try { // Prepare the data for API request - const examSlotData = { - department: user?.user?.department || user?.department, - semester: user?.user?.semester || user?.semester, - branch: user?.user?.branch || user?.branch, - // Include all slots (A-G and A1-G1), even if empty - A: formData.A || '', - B: formData.B || '', - C: formData.C || '', - D: formData.D || '', - E: formData.E || '', - F: formData.F || '', - G: formData.G || '', - A1: formData.A1 || '', - B1: formData.B1 || '', - C1: formData.C1 || '', - D1: formData.D1 || '', - E1: formData.E1 || '', - F1: formData.F1 || '', - G1: formData.G1 || '' - }; + const examSlotData = { + branch: user?.user?.department || user?.department, + semester: user?.user?.semester || user?.semester, + course: user?.user?.degree || user?.degree, + + // Regular slots + A: formData.A || '', + B: formData.B || '', + C: formData.C || '', + D: formData.D || '', + E: formData.E || '', + F: formData.F || '', + G: formData.G || '', + + A1: formData.A1 || '', + B1: formData.B1 || '', + C1: formData.C1 || '', + D1: formData.D1 || '', + E1: formData.E1 || '', + F1: formData.F1 || '', + G1: formData.G1 || '', + + // AL slots + AL1: formData.AL1 || '', + AL2: formData.AL2 || '', + AL3: formData.AL3 || '', + AL4: formData.AL4 || '', + AL5: formData.AL5 || '', + + // ML slots + ML1: formData.ML1 || '', + ML2: formData.ML2 || '', + ML3: formData.ML3 || '', + ML4: formData.ML4 || '', + ML5: formData.ML5 || '' +}; console.log('Submitting timetable data:', examSlotData); @@ -170,6 +187,9 @@ const Timetable = ({ user }) => { console.log('Timetable updated successfully:', response); toast.success('Timetable updated successfully!'); + // Update examSlots with new data + setExamSlots(formData); + // Close form after successful submission setShowForm(false); @@ -181,29 +201,124 @@ const Timetable = ({ user }) => { const handleFormCancel = () => { setShowForm(false); - // Reset form data if needed - // setFormData({ - // A: '', B: '', C: '', D: '', E: '', F: '', G: '', - // A1: '', B1: '', C1: '', D1: '', E1: '', F1: '', G1: '' - // }); + // Reset form data to current examSlots values + setFormData({ + A: examSlots.A || '', + B: examSlots.B || '', + C: examSlots.C || '', + D: examSlots.D || '', + E: examSlots.E || '', + F: examSlots.F || '', + G: examSlots.G || '', + A1: examSlots.A1 || '', + B1: examSlots.B1 || '', + C1: examSlots.C1 || '', + D1: examSlots.D1 || '', + E1: examSlots.E1 || '', + F1: examSlots.F1 || '', + G1: examSlots.G1 || '', + ML1: examSlots.ML1 || '', + ML2: examSlots.ML2 || '', + ML3: examSlots.ML3 || '', + ML4: examSlots.ML4 || '', + ML5: examSlots.ML5 || '', + AL1: examSlots.AL1 || '', + AL2: examSlots.AL2 || '', + AL3: examSlots.AL3 || '', + AL4: examSlots.AL4 || '', + AL5: examSlots.AL5 || '' + }); }; - const getClassType = (type) => { - const typeClasses = { - lecture: 'lecture', - lab: 'lab', - practical: 'practical', - tutorial: 'tutorial', - project: 'project', - seminar: 'seminar', - study: 'study' + const getSlotColor = (slot) => { + const slotColors = { + // Regular slots - vibrant colors matching the course cards + 'A': '#C8A8E9', // light purple + 'B': '#FFB3D1', // light pink + 'C': '#87CEEB', // light blue + 'D': '#DDA0DD', // plum + 'E': '#F0E68C', // khaki + 'F': '#FFE4B5', // moccasin + 'G': '#98FB98', // pale green + + // Lab slots - slightly darker versions + 'A1': '#B19CD9', // darker purple + 'B1': '#FF9AC1', // darker pink + 'C1': '#6BB6DB', // darker blue + 'D1': '#CD88CD', // darker plum + 'E1': '#E6D87C', // darker khaki + 'F1': '#FFDAB9', // darker moccasin + 'G1': '#90EE90', // darker pale green + + // Morning lab slots - rich colors + 'ML1': '#9370DB', // medium slate blue + 'ML2': '#FF69B4', // hot pink + 'ML3': '#4682B4', // steel blue + 'ML4': '#DA70D6', // orchid + 'ML5': '#FFD700', // gold + + // Afternoon lab slots - warm colors + 'AL1': '#87CEEB', // light blue + 'AL2': '#DDA0DD', // plum + 'AL3': '#F0E68C', // khaki + 'AL4': '#FFE4B5', // moccasin + 'AL5': '#98FB98', // pale green }; - return typeClasses[type] || 'default'; + return slotColors[slot] || '#f8f9fa'; }; // Check if user is BR (you can adjust this condition based on your user object structure) const isBR = user?.user?.isBR || user?.isBR; + useEffect(() => { + const fetchData = async () => { + console.log(user); + console.log(user?.user?.degree, user?.user?.department,user?.user?.semester); + + var branch = user?.user?.department; + // replace spaces of branch with - + branch = branch.replace(/ /g, '-'); + + console.log(branch); + + const response = await fetchExamSlot(user?.user?.degree, branch,user?.user?.semester); + + setExamSlots(response); + + // Update formData with fetched exam slots + setFormData({ + A: response.A || '', + B: response.B || '', + C: response.C || '', + D: response.D || '', + E: response.E || '', + F: response.F || '', + G: response.G || '', + A1: response.A1 || '', + B1: response.B1 || '', + C1: response.C1 || '', + D1: response.D1 || '', + E1: response.E1 || '', + F1: response.F1 || '', + G1: response.G1 || '', + ML1: response.ML1 || '', + ML2: response.ML2 || '', + ML3: response.ML3 || '', + ML4: response.ML4 || '', + ML5: response.ML5 || '', + AL1: response.AL1 || '', + AL2: response.AL2 || '', + AL3: response.AL3 || '', + AL4: response.AL4 || '', + AL5: response.AL5 || '' + }); + + console.log(response); + }; + + fetchData(); + }, [user]); + return (
{/* BR Form Button and Modal */} @@ -274,7 +389,7 @@ const Timetable = ({ user }) => { style={{ width: '100%', padding: '8px', - border: '1px solid #ddd', + border: '1px solid #000000ff', borderRadius: '4px', fontSize: '14px' }} @@ -299,7 +414,7 @@ const Timetable = ({ user }) => { style={{ width: '100%', padding: '8px', - border: '1px solid #ddd', + border: '1px solid #000000ff', borderRadius: '4px', fontSize: '14px' }} @@ -324,7 +439,7 @@ const Timetable = ({ user }) => { style={{ width: '100%', padding: '8px', - border: '1px solid #ddd', + border: '1px solid #000000ff', borderRadius: '4px', fontSize: '14px' }} @@ -339,7 +454,7 @@ const Timetable = ({ user }) => { {['AL1', 'AL2', 'AL3', 'AL4', 'AL5'].map(slot => (
- {slot} (3-6): + {slot} (2-5):
{ style={{ width: '100%', padding: '8px', - border: '1px solid #ddd', + border: '1px solid #000000ff', borderRadius: '4px', fontSize: '14px' }} @@ -413,12 +528,22 @@ const Timetable = ({ user }) => { return (
{classData ? (
-
{classData.subject}
-
{classData.room}
+
+ {classData.subject} +
+
+ {classData.room} +
) : null}
diff --git a/client/src/screens/dashboard/components/timetable.scss b/client/src/screens/dashboard/components/timetable.scss index 78e0eb1c..7b3cdaa2 100644 --- a/client/src/screens/dashboard/components/timetable.scss +++ b/client/src/screens/dashboard/components/timetable.scss @@ -9,9 +9,9 @@ .timetable-grid { display: grid; grid-template-columns: 120px repeat(10, 1fr); - grid-gap: 1px; + // grid-gap: 1px; background-color: #e0e0e0; - border: 1px solid #ccc; + border: 1px solid #000000; border-radius: 8px; overflow: hidden; min-width: 800px; @@ -21,7 +21,7 @@ padding: 12px 8px; font-weight: bold; text-align: center; - border-right: 1px solid #ddd; + // border-right: 1px solid #000000; } .time-slot-header { @@ -39,11 +39,12 @@ font-weight: bold; text-align: center; color: #333; - border-right: 1px solid #ddd; + // border-right: 1px solid #000000; } .timetable-cell { background-color: white; + border: 0.02rem solid #000000 !important; padding: 8px; min-height: 60px; display: flex; @@ -75,13 +76,13 @@ font-weight: bold; font-size: 0.85rem; margin-bottom: 4px; - color: white; + color: rgb(0, 0, 0); } .room-info { font-size: 0.7rem; opacity: 0.9; - color: white; + color: rgb(0, 0, 0); } } From c3cd56bf66ce4f3851418d275a8957dcb969209c Mon Sep 17 00:00:00 2001 From: AbhinavRai01 Date: Sun, 31 Aug 2025 18:11:24 +0530 Subject: [PATCH 8/8] fixed directry and files --- .../components/{ => timetable}/timetable.jsx | 305 ++++++++---- .../components/{ => timetable}/timetable.scss | 0 .../dashboard/components/timetable2.jsx | 434 ------------------ client/src/screens/dashboard/index.jsx | 2 +- 4 files changed, 215 insertions(+), 526 deletions(-) rename client/src/screens/dashboard/components/{ => timetable}/timetable.jsx (70%) rename client/src/screens/dashboard/components/{ => timetable}/timetable.scss (100%) delete mode 100644 client/src/screens/dashboard/components/timetable2.jsx diff --git a/client/src/screens/dashboard/components/timetable.jsx b/client/src/screens/dashboard/components/timetable/timetable.jsx similarity index 70% rename from client/src/screens/dashboard/components/timetable.jsx rename to client/src/screens/dashboard/components/timetable/timetable.jsx index 8067d46f..75b12bfd 100644 --- a/client/src/screens/dashboard/components/timetable.jsx +++ b/client/src/screens/dashboard/components/timetable/timetable.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import './timetable.scss'; -import { createOrUpdateExamSlot, fetchExamSlot } from '../../../api/Timetable'; // Adjust path as needed +import { createOrUpdateExamSlot, fetchExamSlot } from '../../../../api/Timetable'; // Adjust path as needed import { useEffect } from 'react'; import { toast } from 'react-toastify'; @@ -13,7 +13,10 @@ const Timetable = ({ user }) => { ML1: '', ML2: '', ML3: '', ML4: '', ML5: '', AL1: '', AL2: '', AL3: '', AL4: '', AL5: '' }); + const [overlaps, setOverlaps] = useState([]); + console.log(examSlots); + const timeSlots = [ '8:00 AM', '9:00 AM', @@ -29,25 +32,102 @@ const Timetable = ({ user }) => { const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; + // Define slot schedules for overlap detection + const slotSchedules = { + 'Monday': { + '8:00 AM': ['A'], + '9:00 AM': ['B', 'ML1'], + '10:00 AM': ['C', 'ML1'], + '11:00 AM': ['D', 'ML1'], + '12:00 PM': ['F'], + '1:00 PM': ['F1'], + '2:00 PM': ['D1', 'AL1'], + '3:00 PM': ['C1', 'AL1'], + '4:00 PM': ['B1', 'AL1'], + '5:00 PM': ['A1'] + }, + 'Tuesday': { + '8:00 AM': ['E'], + '9:00 AM': ['A', 'ML2'], + '10:00 AM': ['B', 'ML2'], + '11:00 AM': ['C', 'ML2'], + '12:00 PM': ['F'], + '1:00 PM': ['F1'], + '2:00 PM': ['C1', 'AL2'], + '3:00 PM': ['B1', 'AL2'], + '4:00 PM': ['A1', 'AL2'], + '5:00 PM': ['E1'] + }, + 'Wednesday': { + '8:00 AM': ['D'], + '9:00 AM': ['E', 'ML3'], + '10:00 AM': ['A', 'ML3'], + '11:00 AM': ['B', 'ML3'], + '12:00 PM': ['G'], + '1:00 PM': ['G1'], + '2:00 PM': ['B1', 'AL3'], + '3:00 PM': ['A1', 'AL3'], + '4:00 PM': ['E1', 'AL3'], + '5:00 PM': ['D1'] + }, + 'Thursday': { + '8:00 AM': ['C'], + '9:00 AM': ['D', 'ML4'], + '10:00 AM': ['E', 'ML4'], + '11:00 AM': ['A', 'ML4'], + '12:00 PM': ['G'], + '1:00 PM': ['G1'], + '2:00 PM': ['A1', 'AL4'], + '3:00 PM': ['E1', 'AL4'], + '4:00 PM': ['D1', 'AL4'], + '5:00 PM': ['C1'] + }, + 'Friday': { + '8:00 AM': ['B'], + '9:00 AM': ['C', 'ML5'], + '10:00 AM': ['D', 'ML5'], + '11:00 AM': ['F', 'ML5'], + '12:00 PM': ['G'], + '1:00 PM': ['G1'], + '2:00 PM': ['F1', 'AL5'], + '3:00 PM': ['D1', 'AL5'], + '4:00 PM': ['C1', 'AL5'], + '5:00 PM': ['B1'] + } + }; + + // Function to detect overlaps + const detectOverlaps = (currentFormData) => { + const foundOverlaps = []; + + Object.entries(slotSchedules).forEach(([day, daySchedule]) => { + Object.entries(daySchedule).forEach(([time, slots]) => { + // Check if multiple slots have courses assigned for the same time + const activeCourses = slots.filter(slot => { + const course = currentFormData[slot]; + return course && course.trim() !== ''; + }); + + if (activeCourses.length > 1) { + const courseDetails = activeCourses.map(slot => ({ + slot, + course: currentFormData[slot] + })); + + foundOverlaps.push({ + day, + time, + conflicts: courseDetails + }); + } + }); + }); + + return foundOverlaps; + }; + // Generate timetable data based on examSlots const generateTimetableData = () => { - const slotTimes = { - 'A': '8:00 AM', - 'B': '9:00 AM', - 'C': '10:00 AM', - 'D': '11:00 AM', - 'E': '8:00 AM', - 'F': '12:00 PM', - 'G': '12:00 PM', - 'A1': '5:00 PM', - 'B1': '4:00 PM', - 'C1': '3:00 PM', - 'D1': '2:00 PM', - 'E1': '5:00 PM', - 'F1': '1:00 PM', - 'G1': '1:00 PM' - }; - const daySchedule = { 'Monday': { '8:00 AM': 'A', @@ -133,51 +213,65 @@ const Timetable = ({ user }) => { const timetableData = generateTimetableData(); const handleInputChange = (slot, value) => { - setFormData(prev => ({ - ...prev, + const newFormData = { + ...formData, [slot]: value - })); + }; + + setFormData(newFormData); + + // Detect overlaps in real-time + const currentOverlaps = detectOverlaps(newFormData); + setOverlaps(currentOverlaps); }; const handleFormSubmit = async () => { + // Check for overlaps before submitting + const currentOverlaps = detectOverlaps(formData); + + if (currentOverlaps.length > 0) { + toast.error('Please resolve all schedule conflicts before saving!'); + return; + } + try { // Prepare the data for API request - const examSlotData = { - branch: user?.user?.department || user?.department, - semester: user?.user?.semester || user?.semester, - course: user?.user?.degree || user?.degree, - - // Regular slots - A: formData.A || '', - B: formData.B || '', - C: formData.C || '', - D: formData.D || '', - E: formData.E || '', - F: formData.F || '', - G: formData.G || '', - - A1: formData.A1 || '', - B1: formData.B1 || '', - C1: formData.C1 || '', - D1: formData.D1 || '', - E1: formData.E1 || '', - F1: formData.F1 || '', - G1: formData.G1 || '', - - // AL slots - AL1: formData.AL1 || '', - AL2: formData.AL2 || '', - AL3: formData.AL3 || '', - AL4: formData.AL4 || '', - AL5: formData.AL5 || '', - - // ML slots - ML1: formData.ML1 || '', - ML2: formData.ML2 || '', - ML3: formData.ML3 || '', - ML4: formData.ML4 || '', - ML5: formData.ML5 || '' -}; + const examSlotData = { + branch: user?.user?.department || user?.department, + semester: user?.user?.semester || user?.semester, + course: user?.user?.degree || user?.degree, + + // Regular slots + A: formData.A || '', + B: formData.B || '', + C: formData.C || '', + D: formData.D || '', + E: formData.E || '', + F: formData.F || '', + G: formData.G || '', + + A1: formData.A1 || '', + B1: formData.B1 || '', + C1: formData.C1 || '', + D1: formData.D1 || '', + E1: formData.E1 || '', + F1: formData.F1 || '', + G1: formData.G1 || '', + + // AL slots + AL1: formData.AL1 || '', + AL2: formData.AL2 || '', + AL3: formData.AL3 || '', + AL4: formData.AL4 || '', + AL5: formData.AL5 || '', + + // ML slots + ML1: formData.ML1 || '', + ML2: formData.ML2 || '', + ML3: formData.ML3 || '', + ML4: formData.ML4 || '', + ML5: formData.ML5 || '' + }; console.log('Submitting timetable data:', examSlotData); @@ -190,7 +284,8 @@ const Timetable = ({ user }) => { // Update examSlots with new data setExamSlots(formData); - // Close form after successful submission + // Clear overlaps and close form + setOverlaps([]); setShowForm(false); } catch (error) { @@ -201,6 +296,7 @@ const Timetable = ({ user }) => { const handleFormCancel = () => { setShowForm(false); + setOverlaps([]); // Reset form data to current examSlots values setFormData({ A: examSlots.A || '', @@ -286,7 +382,7 @@ const Timetable = ({ user }) => { setExamSlots(response); // Update formData with fetched exam slots - setFormData({ + const newFormData = { A: response.A || '', B: response.B || '', C: response.C || '', @@ -311,7 +407,13 @@ const Timetable = ({ user }) => { AL3: response.AL3 || '', AL4: response.AL4 || '', AL5: response.AL5 || '' - }); + }; + + setFormData(newFormData); + + // Check for existing overlaps + const initialOverlaps = detectOverlaps(newFormData); + setOverlaps(initialOverlaps); console.log(response); }; @@ -319,6 +421,22 @@ const Timetable = ({ user }) => { fetchData(); }, [user]); + const getInputStyle = (slot) => { + // Check if this slot is part of any overlap + const isInOverlap = overlaps.some(overlap => + overlap.conflicts.some(conflict => conflict.slot === slot) + ); + + return { + width: '100%', + padding: '8px', + border: isInOverlap ? '2px solid #dc3545' : '1px solid #000000ff', + borderRadius: '4px', + fontSize: '14px', + backgroundColor: isInOverlap ? '#fff5f5' : 'white' + }; + }; + return (
{/* BR Form Button and Modal */} @@ -360,12 +478,40 @@ const Timetable = ({ user }) => { backgroundColor: 'white', padding: '30px', borderRadius: '10px', - maxWidth: '600px', + maxWidth: '800px', width: '90%', maxHeight: '80vh', overflow: 'auto' }}>

Update Timetable Slots

+ + {/* Overlap Warning */} + {overlaps.length > 0 && ( +
+

⚠️ Schedule Conflicts Detected:

+ {overlaps.map((overlap, index) => ( +
+ {overlap.day} at {overlap.time}: +
    + {overlap.conflicts.map((conflict, i) => ( +
  • Slot {conflict.slot}: {conflict.course}
  • + ))} +
+
+ ))} +

+ Please remove courses from conflicting slots or clear one of the overlapping entries. +

+
+ )} +
{ value={formData[slot]} onChange={(e) => handleInputChange(slot, e.target.value)} placeholder={`Enter course for slot ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #000000ff', - borderRadius: '4px', - fontSize: '14px' - }} + style={getInputStyle(slot)} />
))} @@ -411,13 +551,7 @@ const Timetable = ({ user }) => { value={formData[slot]} onChange={(e) => handleInputChange(slot, e.target.value)} placeholder={`Enter course for slot ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #000000ff', - borderRadius: '4px', - fontSize: '14px' - }} + style={getInputStyle(slot)} />
))} @@ -436,13 +570,7 @@ const Timetable = ({ user }) => { value={formData[slot]} onChange={(e) => handleInputChange(slot, e.target.value)} placeholder={`Enter course for ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #000000ff', - borderRadius: '4px', - fontSize: '14px' - }} + style={getInputStyle(slot)} />
))} @@ -461,13 +589,7 @@ const Timetable = ({ user }) => { value={formData[slot]} onChange={(e) => handleInputChange(slot, e.target.value)} placeholder={`Enter course for ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #000000ff', - borderRadius: '4px', - fontSize: '14px' - }} + style={getInputStyle(slot)} />
))} @@ -477,13 +599,14 @@ const Timetable = ({ user }) => {
-
- )} - - {/* Form Modal */} - {showForm && ( -
-
-

Update Timetable Slots

-
-
- {/* Regular slots A-G */} -
-

Regular Slots

- {['A', 'B', 'C', 'D', 'E', 'F', 'G'].map(slot => ( -
-
- Slot {slot}: -
- handleInputChange(slot, e.target.value)} - placeholder={`Enter course for slot ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #ddd', - borderRadius: '4px', - fontSize: '14px' - }} - /> -
- ))} -
- - {/* Lab slots A1-G1 */} -
-

Lab Slots

- {['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1'].map(slot => ( -
-
- Slot {slot}: -
- handleInputChange(slot, e.target.value)} - placeholder={`Enter course for slot ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #ddd', - borderRadius: '4px', - fontSize: '14px' - }} - /> -
- ))} -
- - {/* ML slots ML1-ML5 */} -
-

Morning Lab Slots

- {['ML1', 'ML2', 'ML3', 'ML4', 'ML5'].map(slot => ( -
-
- {slot} (9-12): -
- handleInputChange(slot, e.target.value)} - placeholder={`Enter course for ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #ddd', - borderRadius: '4px', - fontSize: '14px' - }} - /> -
- ))} -
- - {/* AL slots AL1-AL5 */} -
-

Afternoon Lab Slots

- {['AL1', 'AL2', 'AL3', 'AL4', 'AL5'].map(slot => ( -
-
- {slot} (3-6): -
- handleInputChange(slot, e.target.value)} - placeholder={`Enter course for ${slot}`} - style={{ - width: '100%', - padding: '8px', - border: '1px solid #ddd', - borderRadius: '4px', - fontSize: '14px' - }} - /> -
- ))} -
-
- -
- - -
-
-
-
- )} - - {/* Original Timetable */} -
- {/* Header row with time slots */} -
- {timeSlots.map((time) => ( -
- {time} -
- ))} - - {/* Day rows */} - {days.map((day) => ( - -
{day}
- {timeSlots.map((time) => { - const classData = timetableData[day]?.[time]; - return ( -
- {classData ? ( -
-
{classData.subject}
-
{classData.room}
-
- ) : null} -
- ); - })} -
- ))} -
-
- ); -}; - -export default Timetable; \ No newline at end of file diff --git a/client/src/screens/dashboard/index.jsx b/client/src/screens/dashboard/index.jsx index c3f079e9..ab5ee7a0 100644 --- a/client/src/screens/dashboard/index.jsx +++ b/client/src/screens/dashboard/index.jsx @@ -9,7 +9,7 @@ import CourseCard from "./components/coursecard"; import ContributionBanner from "./components/contributionbanner"; import Footer from "../../components/footer"; import FavouriteCard from "./components/favouritecard"; -import Timetable from "./components/timetable.jsx"; +import Timetable from "./components/timetable/timetable.jsx"; import { ChangeCurrentCourse, ResetFileBrowserState } from "../../actions/filebrowser_actions"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate } from "react-router-dom";