diff --git a/client/package-lock.json b/client/package-lock.json
index 7f3445e..63b5ea0 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -17,6 +17,7 @@
"mongoose": "^8.3.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.2",
+ "react-csv": "^2.2.2",
"react-dom": "^18.2.0",
"react-icons": "^5.4.0",
"react-router-dom": "^6.28.0",
@@ -15339,6 +15340,11 @@
}
}
},
+ "node_modules/react-csv": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/react-csv/-/react-csv-2.2.2.tgz",
+ "integrity": "sha512-RG5hOcZKZFigIGE8LxIEV/OgS1vigFQT4EkaHeKgyuCbUAu9Nbd/1RYq++bJcJJ9VOqO/n9TZRADsXNDR4VEpw=="
+ },
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
diff --git a/client/package.json b/client/package.json
index 67219c7..f466e56 100644
--- a/client/package.json
+++ b/client/package.json
@@ -12,6 +12,7 @@
"mongoose": "^8.3.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.2",
+ "react-csv": "^2.2.2",
"react-dom": "^18.2.0",
"react-icons": "^5.4.0",
"react-router-dom": "^6.28.0",
diff --git a/client/src/Pages/ConsolidatedTable.jsx b/client/src/Pages/ConsolidatedTable.jsx
index 67ab71c..f18505c 100644
--- a/client/src/Pages/ConsolidatedTable.jsx
+++ b/client/src/Pages/ConsolidatedTable.jsx
@@ -1,5 +1,87 @@
+// import React, { useState, useEffect } from "react";
+// import axios from "axios";
+
+// const ConsolidatedTable = () => {
+// const [data, setData] = useState([]);
+// const [loading, setLoading] = useState(true);
+
+// useEffect(() => {
+// const fetchData = async () => {
+// try {
+// const response = await axios.get("http://localhost:8080/api/table/consolidated-table");
+// setData(response.data);
+// setLoading(false);
+// } catch (error) {
+// console.error("Error fetching table data:", error);
+// setLoading(false);
+// }
+// };
+
+// fetchData();
+// }, []);
+
+// if (loading) {
+// return
Loading...
;
+// }
+
+// return (
+//
+//
Consolidated Table
+//
+//
+//
+// | Semester |
+// Course Code |
+// Course Name |
+// Exam Type |
+// Year |
+// Marks |
+// Name |
+// Affiliation/College |
+// Highest Qualification |
+// Career Experience |
+// Oral/Practical |
+// Assessment |
+// Reassessment |
+// Paper Setting |
+// Moderation |
+// PwD Paper Setting |
+//
+//
+//
+// {data.map((row, index) => (
+//
+// | {row.semester} |
+// {row.courseCode} |
+// {row.courseName} |
+// {row.examType} |
+// {row.year} |
+// {row.marks} |
+// {row.Name} |
+// {row.affiliation} |
+// {row.qualification} |
+// {row.experience} |
+// {row.oralPractical} |
+// {row.assessment} |
+// {row.reassessment} |
+// {row.paperSetting} |
+// {row.moderation} |
+// {row.pwdPaperSetting} |
+//
+// ))}
+//
+//
+//
+// );
+// };
+
+// export default ConsolidatedTable;
+
+
+
import React, { useState, useEffect } from "react";
import axios from "axios";
+import { CSVLink } from "react-csv";
const ConsolidatedTable = () => {
const [data, setData] = useState([]);
@@ -24,53 +106,81 @@ const ConsolidatedTable = () => {
return Loading...
;
}
+ // Extract unique faculty names
+ const uniqueTeachers = [...new Set(data.map((row) => row.Name))];
+
return (
-
Consolidated Table
-
-
-
- | Semester |
- Course Code |
- Course Name |
- Exam Type |
- Year |
- Marks |
- Name |
- Affiliation/College |
- Highest Qualification |
- Career Experience |
- Oral/Practical |
- Assessment |
- Reassessment |
- Paper Setting |
- Moderation |
- PwD Paper Setting |
-
-
-
- {data.map((row, index) => (
-
- | {row.semester} |
- {row.courseCode} |
- {row.courseName} |
- {row.examType} |
- {row.year} |
- {row.marks} |
- {row.Name} |
- {row.affiliation} |
- {row.qualification} |
- {row.experience} |
- {row.oralPractical} |
- {row.assessment} |
- {row.reassessment} |
- {row.paperSetting} |
- {row.moderation} |
- {row.pwdPaperSetting} |
-
- ))}
-
-
+
Faculty Tables with Download Option
+ {uniqueTeachers.map((teacher, index) => {
+ // Filter rows for the current teacher
+ const teacherData = data.filter((row) => row.Name === teacher);
+
+ return (
+
+
{teacher}'s Table
+
+
+
+ | Semester |
+ Course Code |
+ Course Name |
+ Exam Type |
+ Year |
+ Marks |
+ Name |
+ Affiliation/College |
+ Highest Qualification |
+ Career Experience |
+ Oral/Practical |
+ Assessment |
+ Reassessment |
+ Paper Setting |
+ Moderation |
+ PwD Paper Setting |
+
+
+
+ {teacherData.map((row, idx) => (
+
+ | {row.semester} |
+ {row.courseCode} |
+ {row.courseName} |
+ {row.examType} |
+ {row.year} |
+ {row.marks} |
+ {row.Name} |
+ {row.affiliation} |
+ {row.qualification} |
+ {row.experience} |
+ {row.oralPractical} |
+ {row.assessment} |
+ {row.reassessment} |
+ {row.paperSetting} |
+ {row.moderation} |
+ {row.pwdPaperSetting} |
+
+ ))}
+
+
+ {/* CSV Download Button */}
+
+ Download CSV
+
+
+ );
+ })}
);
};
diff --git a/client/src/Pages/CourseForm.css b/client/src/Pages/CourseForm.css
index 1099137..e8e4714 100644
--- a/client/src/Pages/CourseForm.css
+++ b/client/src/Pages/CourseForm.css
@@ -115,3 +115,16 @@ button[type="submit"] {
width: 100%;
}
}
+.remove-faculty-btn {
+ padding: 0px 0px;
+ background: none;
+ border: none;
+ color: red;
+ cursor: pointer;
+ font-size: 16px;
+ margin-left: 10px;
+}
+
+.remove-faculty-btn:hover {
+ color: darkred;
+}
\ No newline at end of file
diff --git a/client/src/Pages/CourseForm.jsx b/client/src/Pages/CourseForm.jsx
index 72c0416..bba7082 100644
--- a/client/src/Pages/CourseForm.jsx
+++ b/client/src/Pages/CourseForm.jsx
@@ -23,6 +23,15 @@ const CourseForm = () => {
pwdPaperSetter: "",
});
+ const [tempAssignments, setTempAssignments] = useState({
+ oralsPracticals: [],
+ assessment: [],
+ reassessment: [],
+ paperSetting: [],
+ moderation: [],
+ pwdPaperSetter: [],
+ });
+
const [errors, setErrors] = useState({});
// Fetch faculty list on mount
@@ -45,6 +54,15 @@ const CourseForm = () => {
setFormData({ ...formData, [name]: value });
+ if (value.trim() === "") {
+ // Clear suggestions if input is empty
+ setSuggestions((prev) => ({
+ ...prev,
+ [name]: [],
+ }));
+ return;
+ }
+
// Filter suggestions for the current field
if (options.faculties.length > 0) {
const filteredSuggestions = options.faculties.filter((faculty) =>
@@ -57,49 +75,91 @@ const CourseForm = () => {
}
};
- // Validate the form
+ const handleAddFaculty = (field) => {
+ const selectedFaculty = options.faculties.find(
+ (faculty) => faculty.name === formData[field]
+ );
+
+ if (selectedFaculty) {
+ setTempAssignments((prev) => ({
+ ...prev,
+ [field]: [...prev[field], selectedFaculty],
+ }));
+ setFormData({ ...formData, [field]: "" }); // Clear input field
+ setSuggestions((prev) => ({ ...prev, [field]: [] })); // Clear suggestions
+ }
+ };
+
+ const handleRemoveFaculty = (field, index) => {
+ setTempAssignments((prev) => {
+ const updatedAssignments = [...prev[field]]; // Create a shallow copy of the current list for this field
+ updatedAssignments.splice(index, 1); // Remove the faculty at the specified index
+ return { ...prev, [field]: updatedAssignments }; // Update the tempAssignments state
+ });
+ };
+
const validateForm = () => {
const newErrors = {};
- Object.keys(formData).forEach((field) => {
- if (!formData[field]) {
- newErrors[field] = "This field is required";
+
+ // Validate that each field in tempAssignments has at least one assigned faculty
+ Object.keys(tempAssignments).forEach((field) => {
+ if (!tempAssignments[field] || tempAssignments[field].length === 0) {
+ newErrors[field] = "At least one faculty must be assigned.";
}
});
+
setErrors(newErrors);
- return Object.keys(newErrors).length === 0;
+ return Object.keys(newErrors).length === 0; // Form is valid if no errors
};
// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault(); // Prevent default form submission behavior
+
+ // Validate the form based on tempAssignments
if (validateForm()) {
try {
const groupedTasks = {};
- // Group tasks by facultyId
- Object.entries(formData).forEach(([field, value]) => {
- const assignedFaculty = options.faculties.find(
- (faculty) => faculty.name === value
- );
- if (assignedFaculty) {
- if (!groupedTasks[assignedFaculty.facultyId]) {
- groupedTasks[assignedFaculty.facultyId] = {
- facultyId: assignedFaculty.facultyId,
- courseId: course?.courseId || id,
- tasks: [],
- };
+ // Transform tempAssignments into grouped tasks by facultyId
+ Object.entries(tempAssignments).forEach(([field, facultyList]) => {
+ facultyList.forEach((faculty) => {
+ // Assuming faculty is an object, not just the faculty name
+ const assignedFaculty = options.faculties.find(
+ (optionFaculty) => optionFaculty.facultyId === faculty.facultyId
+ );
+
+ if (assignedFaculty) {
+ // Check if the facultyId already exists in groupedTasks
+ if (!groupedTasks[assignedFaculty.facultyId]) {
+ groupedTasks[assignedFaculty.facultyId] = {
+ facultyId: assignedFaculty.facultyId,
+ courseId: course?.courseId || id,
+ tasks: [],
+ };
+ }
+ // Push the task (field) into the tasks array for that faculty
+ groupedTasks[assignedFaculty.facultyId].tasks.push(field);
}
- groupedTasks[assignedFaculty.facultyId].tasks.push(field);
- }
+ });
});
- const payload = Object.values(groupedTasks); // Convert the grouped tasks into an array
+ console.log(groupedTasks);
+ const payload = Object.values(groupedTasks); // Convert grouped tasks into an array
console.log("Saving appointment with payload:", payload);
- await saveAppointment(payload); // Save to backend
+
+ if (payload.length === 0) {
+ throw new Error("No assignments to submit.");
+ }
+
+ // Call API to save appointments
+ await saveAppointment(payload);
await updateCourseStatus(course?.courseId || id);
+
console.log("Form submitted successfully:", payload);
- const filteredCourses = JSON.parse(localStorage.getItem("filteredCourses")) || [];
+ const filteredCourses =
+ JSON.parse(localStorage.getItem("filteredCourses")) || [];
// Redirect to courses page after successful submission
navigate("/courses", {
@@ -111,8 +171,6 @@ const CourseForm = () => {
},
},
});
-
-
} catch (error) {
console.error("Failed to save appointment:", error);
}
@@ -149,7 +207,21 @@ const CourseForm = () => {
onChange={handleInputChange}
placeholder={`Search faculty for ${label}`}
/>
-
+
+ 0 ? "block" : "none",
+ }}
+ >
{(suggestions[name] || []).map((faculty) => (
- {
))}
- {errors[name] && {errors[name]}}
+ {tempAssignments[name].length > 0 && (
+
+ {tempAssignments[name].map((faculty, index) => (
+ -
+ {faculty.name}
+
+
+ ))}
+
+ )}
+ {errors[name] && (
+ {errors[name]}
+ )}
))}