added exam period in frontend and in database
This commit is contained in:
@@ -9,11 +9,8 @@ const CourseForm = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { course } = location.state || {};
|
const { course } = location.state || {};
|
||||||
|
|
||||||
const [options, setOptions] = useState({
|
const [options, setOptions] = useState({ faculties: [] });
|
||||||
faculties: [], // List of all faculties
|
const [suggestions, setSuggestions] = useState({});
|
||||||
});
|
|
||||||
|
|
||||||
const [suggestions, setSuggestions] = useState({}); // To hold suggestions for each field
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
oralsPracticals: "",
|
oralsPracticals: "",
|
||||||
assessment: "",
|
assessment: "",
|
||||||
@@ -32,46 +29,38 @@ const CourseForm = () => {
|
|||||||
pwdPaperSetter: [],
|
pwdPaperSetter: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [examPeriod, setExamPeriod] = useState({
|
||||||
|
year: "",
|
||||||
|
startMonth: "",
|
||||||
|
endMonth: "",
|
||||||
|
});
|
||||||
|
|
||||||
const [errors, setErrors] = useState({});
|
const [errors, setErrors] = useState({});
|
||||||
|
|
||||||
// Fetch faculty list on mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchOptionsAndFaculties = async () => {
|
const fetchOptionsAndFaculties = async () => {
|
||||||
try {
|
try {
|
||||||
const facultiesData = await fetchFaculties(); // Fetch faculty names from backend
|
const facultiesData = await fetchFaculties();
|
||||||
setOptions((prev) => ({ ...prev, faculties: facultiesData }));
|
setOptions((prev) => ({ ...prev, faculties: facultiesData }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch faculties:", error);
|
console.error("Failed to fetch faculties:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchOptionsAndFaculties();
|
fetchOptionsAndFaculties();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle input changes for form fields
|
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = (e) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
|
|
||||||
setFormData({ ...formData, [name]: value });
|
setFormData({ ...formData, [name]: value });
|
||||||
|
|
||||||
if (value.trim() === "") {
|
if (value.trim() === "") {
|
||||||
// Clear suggestions if input is empty
|
setSuggestions((prev) => ({ ...prev, [name]: [] }));
|
||||||
setSuggestions((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[name]: [],
|
|
||||||
}));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter suggestions for the current field
|
|
||||||
if (options.faculties.length > 0) {
|
if (options.faculties.length > 0) {
|
||||||
const filteredSuggestions = options.faculties.filter((faculty) =>
|
const filteredSuggestions = options.faculties.filter((faculty) =>
|
||||||
faculty.name.toLowerCase().includes(value.toLowerCase())
|
faculty.name.toLowerCase().includes(value.toLowerCase())
|
||||||
);
|
);
|
||||||
setSuggestions((prev) => ({
|
setSuggestions((prev) => ({ ...prev, [name]: filteredSuggestions }));
|
||||||
...prev,
|
|
||||||
[name]: filteredSuggestions,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,96 +68,75 @@ const CourseForm = () => {
|
|||||||
const selectedFaculty = options.faculties.find(
|
const selectedFaculty = options.faculties.find(
|
||||||
(faculty) => faculty.name === formData[field]
|
(faculty) => faculty.name === formData[field]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (selectedFaculty) {
|
if (selectedFaculty) {
|
||||||
setTempAssignments((prev) => ({
|
setTempAssignments((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: [...prev[field], selectedFaculty],
|
[field]: [...prev[field], selectedFaculty],
|
||||||
}));
|
}));
|
||||||
setFormData({ ...formData, [field]: "" }); // Clear input field
|
setFormData({ ...formData, [field]: "" });
|
||||||
setSuggestions((prev) => ({ ...prev, [field]: [] })); // Clear suggestions
|
setSuggestions((prev) => ({ ...prev, [field]: [] }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveFaculty = (field, index) => {
|
const handleRemoveFaculty = (field, index) => {
|
||||||
setTempAssignments((prev) => {
|
setTempAssignments((prev) => {
|
||||||
const updatedAssignments = [...prev[field]]; // Create a shallow copy of the current list for this field
|
const updatedAssignments = [...prev[field]];
|
||||||
updatedAssignments.splice(index, 1); // Remove the faculty at the specified index
|
updatedAssignments.splice(index, 1);
|
||||||
return { ...prev, [field]: updatedAssignments }; // Update the tempAssignments state
|
return { ...prev, [field]: updatedAssignments };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors = {};
|
const newErrors = {};
|
||||||
|
|
||||||
// Validate that each field in tempAssignments has at least one assigned faculty
|
|
||||||
Object.keys(tempAssignments).forEach((field) => {
|
Object.keys(tempAssignments).forEach((field) => {
|
||||||
if (!tempAssignments[field] || tempAssignments[field].length === 0) {
|
if (!tempAssignments[field] || tempAssignments[field].length === 0) {
|
||||||
newErrors[field] = "At least one faculty must be assigned.";
|
newErrors[field] = "At least one faculty must be assigned.";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!examPeriod.year || !examPeriod.startMonth || !examPeriod.endMonth) {
|
||||||
|
newErrors.examPeriod = "Exam period is required.";
|
||||||
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors);
|
||||||
return Object.keys(newErrors).length === 0; // Form is valid if no errors
|
return Object.keys(newErrors).length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle form submission
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault(); // Prevent default form submission behavior
|
e.preventDefault();
|
||||||
|
|
||||||
// Validate the form based on tempAssignments
|
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
try {
|
try {
|
||||||
const groupedTasks = {};
|
const groupedTasks = {};
|
||||||
|
|
||||||
// Transform tempAssignments into grouped tasks by facultyId
|
|
||||||
Object.entries(tempAssignments).forEach(([field, facultyList]) => {
|
Object.entries(tempAssignments).forEach(([field, facultyList]) => {
|
||||||
facultyList.forEach((faculty) => {
|
facultyList.forEach((faculty) => {
|
||||||
// Assuming faculty is an object, not just the faculty name
|
|
||||||
const assignedFaculty = options.faculties.find(
|
const assignedFaculty = options.faculties.find(
|
||||||
(optionFaculty) => optionFaculty.facultyId === faculty.facultyId
|
(optionFaculty) => optionFaculty.facultyId === faculty.facultyId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (assignedFaculty) {
|
if (assignedFaculty) {
|
||||||
// Check if the facultyId already exists in groupedTasks
|
|
||||||
if (!groupedTasks[assignedFaculty.facultyId]) {
|
if (!groupedTasks[assignedFaculty.facultyId]) {
|
||||||
groupedTasks[assignedFaculty.facultyId] = {
|
groupedTasks[assignedFaculty.facultyId] = {
|
||||||
facultyId: assignedFaculty.facultyId,
|
facultyId: assignedFaculty.facultyId,
|
||||||
courseId: course?.courseId || id,
|
courseId: course?.courseId || id,
|
||||||
tasks: [],
|
tasks: [],
|
||||||
|
examPeriod: `${examPeriod.year} (${examPeriod.startMonth} - ${examPeriod.endMonth})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Push the task (field) into the tasks array for that faculty
|
|
||||||
groupedTasks[assignedFaculty.facultyId].tasks.push(field);
|
groupedTasks[assignedFaculty.facultyId].tasks.push(field);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(groupedTasks);
|
const payload = Object.values(groupedTasks);
|
||||||
const payload = Object.values(groupedTasks); // Convert grouped tasks into an array
|
|
||||||
console.log("Saving appointment with payload:", payload);
|
|
||||||
|
|
||||||
if (payload.length === 0) {
|
|
||||||
throw new Error("No assignments to submit.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call API to save appointments
|
|
||||||
await saveAppointment(payload);
|
await saveAppointment(payload);
|
||||||
await updateCourseStatus(course?.courseId || id);
|
await updateCourseStatus(course?.courseId || id);
|
||||||
|
|
||||||
console.log("Form submitted successfully:", payload);
|
|
||||||
|
|
||||||
const filteredCourses =
|
const filteredCourses =
|
||||||
JSON.parse(localStorage.getItem("filteredCourses")) || [];
|
JSON.parse(localStorage.getItem("filteredCourses")) || [];
|
||||||
|
|
||||||
// Redirect to courses page after successful submission
|
|
||||||
navigate("/courses", {
|
navigate("/courses", {
|
||||||
state: {
|
state: {
|
||||||
courses: filteredCourses,
|
courses: filteredCourses,
|
||||||
updatedCourse: {
|
updatedCourse: { ...course, status: "Submitted" },
|
||||||
...course,
|
|
||||||
status: "Submitted",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -227,7 +195,7 @@ const CourseForm = () => {
|
|||||||
key={faculty.facultyId}
|
key={faculty.facultyId}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFormData({ ...formData, [name]: faculty.name });
|
setFormData({ ...formData, [name]: faculty.name });
|
||||||
setSuggestions((prev) => ({ ...prev, [name]: [] })); // Clear suggestions for this field
|
setSuggestions((prev) => ({ ...prev, [name]: [] }));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{faculty.name}
|
{faculty.name}
|
||||||
@@ -245,7 +213,7 @@ const CourseForm = () => {
|
|||||||
onClick={() => handleRemoveFaculty(name, index)}
|
onClick={() => handleRemoveFaculty(name, index)}
|
||||||
className="remove-faculty-btn"
|
className="remove-faculty-btn"
|
||||||
>
|
>
|
||||||
✕ {/* This is the "X" symbol */}
|
✕
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -256,6 +224,75 @@ const CourseForm = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
<div className={errors.examPeriod ? "error" : ""}>
|
||||||
|
<label>Exam Period:</label>
|
||||||
|
<select
|
||||||
|
value={examPeriod.year}
|
||||||
|
onChange={(e) => setExamPeriod({ ...examPeriod, year: e.target.value })}
|
||||||
|
>
|
||||||
|
<option value="">Year</option>
|
||||||
|
{[2025, 2026, 2027].map((year) => (
|
||||||
|
<option key={year} value={year}>
|
||||||
|
{year}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
value={examPeriod.startMonth}
|
||||||
|
onChange={(e) =>
|
||||||
|
setExamPeriod({ ...examPeriod, startMonth: e.target.value })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="">Start Month</option>
|
||||||
|
{[
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December",
|
||||||
|
].map((month) => (
|
||||||
|
<option key={month} value={month}>
|
||||||
|
{month}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
value={examPeriod.endMonth}
|
||||||
|
onChange={(e) =>
|
||||||
|
setExamPeriod({ ...examPeriod, endMonth: e.target.value })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="">End Month</option>
|
||||||
|
{[
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December",
|
||||||
|
].map((month) => (
|
||||||
|
<option key={month} value={month}>
|
||||||
|
{month}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
{errors.examPeriod && (
|
||||||
|
<span className="error-message">{errors.examPeriod}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export const fetchOptions = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Save multiple appointments to MongoDB
|
// Save multiple appointments to MongoDB
|
||||||
export const saveAppointment = async (appointmentsData) => {
|
export const saveAppointment = async (appointmentsData) => {
|
||||||
console.log("Saving appointments with payload:", appointmentsData);
|
console.log("Saving appointments with payload:", appointmentsData);
|
||||||
|
|||||||
@@ -2,19 +2,16 @@ const mongoose = require("mongoose");
|
|||||||
const { v4: uuidv4 } = require("uuid");
|
const { v4: uuidv4 } = require("uuid");
|
||||||
|
|
||||||
const AppointmentSchema = new mongoose.Schema({
|
const AppointmentSchema = new mongoose.Schema({
|
||||||
appointmentId: {
|
appointmentId: { type: String, required: true, unique: true, default: uuidv4 },
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true,
|
|
||||||
default: uuidv4
|
|
||||||
},
|
|
||||||
facultyId: { type: String, required: true },
|
facultyId: { type: String, required: true },
|
||||||
facultyName: { type: String, required: true },
|
facultyName: { type: String, required: true },
|
||||||
courseId: { type: String, required: true },
|
courseId: { type: String, required: true },
|
||||||
courseName: { type: String, required: true },
|
courseName: { type: String, required: true },
|
||||||
task: { type: String, required: true },
|
task: { type: String, required: true },
|
||||||
|
examPeriod: { type: String, required: true }, // New field for exam period
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mongoose.model("Appointment", AppointmentSchema);
|
module.exports = mongoose.model("Appointment", AppointmentSchema);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,17 +7,22 @@ const Course = require("../models/Course");
|
|||||||
// Save multiple appointments
|
// Save multiple appointments
|
||||||
router.post("/", async (req, res) => {
|
router.post("/", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { appointments } = req.body; // Expecting an array of appointments
|
const { appointments } = req.body;
|
||||||
if (!appointments || !Array.isArray(appointments)) {
|
if (!appointments || !Array.isArray(appointments)) {
|
||||||
return res.status(400).json({ error: "Invalid or missing data" });
|
return res.status(400).json({ error: "Invalid or missing data" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedAppointments = [];
|
const savedAppointments = [];
|
||||||
for (const appointment of appointments) {
|
for (const appointment of appointments) {
|
||||||
const { facultyId, courseId, tasks } = appointment;
|
const { facultyId, courseId, tasks, examPeriod } = appointment;
|
||||||
|
|
||||||
// Validate input data
|
if (
|
||||||
if (!facultyId || !courseId || !Array.isArray(tasks) || tasks.length === 0) {
|
!facultyId ||
|
||||||
|
!courseId ||
|
||||||
|
!Array.isArray(tasks) ||
|
||||||
|
tasks.length === 0 ||
|
||||||
|
!examPeriod
|
||||||
|
) {
|
||||||
return res.status(400).json({ error: "Invalid appointment data" });
|
return res.status(400).json({ error: "Invalid appointment data" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +35,6 @@ router.post("/", async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save each task as a separate appointment
|
|
||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
const newAppointment = new Appointment({
|
const newAppointment = new Appointment({
|
||||||
facultyId,
|
facultyId,
|
||||||
@@ -38,6 +42,7 @@ router.post("/", async (req, res) => {
|
|||||||
courseId,
|
courseId,
|
||||||
courseName: course.name,
|
courseName: course.name,
|
||||||
task,
|
task,
|
||||||
|
examPeriod,
|
||||||
});
|
});
|
||||||
const savedAppointment = await newAppointment.save();
|
const savedAppointment = await newAppointment.save();
|
||||||
savedAppointments.push(savedAppointment);
|
savedAppointments.push(savedAppointment);
|
||||||
|
|||||||
Reference in New Issue
Block a user