Files
appointment_to_examiner/client/src/Pages/CourseForm.jsx

369 lines
12 KiB
JavaScript

import React, { useState, useEffect } from "react";
import { useLocation, useParams, useNavigate } from "react-router-dom";
import { fetchFaculties, saveAppointment } from "../api";
import "./CourseForm.css";
import "./Navbar.jsx";
import Navbar from "./Navbar.jsx";
import Footer from "./Footer.jsx";
const CourseForm = () => {
const { id } = useParams();
const location = useLocation();
const navigate = useNavigate();
const { course, academicYear } = location.state || {};
const [options, setOptions] = useState({ faculties: [] });
const [suggestions, setSuggestions] = useState({});
const [formData, setFormData] = useState({
oralsPracticals: "",
assessment: "",
reassessment: "",
paperSetting: "",
moderation: "",
pwdPaperSetter: "",
});
const [tempAssignments, setTempAssignments] = useState({
oralsPracticals: [],
assessment: [],
reassessment: [],
paperSetting: [],
moderation: [],
pwdPaperSetter: [],
});
const [examPeriod, setExamPeriod] = useState({
year: academicYear || "",
startMonth: "",
endMonth: "",
});
const [errors, setErrors] = useState({});
// State to check if the form is currently submitting
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchOptionsAndFaculties = async () => {
try {
const facultiesData = await fetchFaculties();
const filteredFaculties = facultiesData.filter(
(faculty) => faculty.courses.includes(course?.courseId || id) // Only faculties with this course
);
setOptions((prev) => ({ ...prev, faculties: filteredFaculties }));
} catch (error) {
console.error("Failed to fetch faculties:", error);
}
};
fetchOptionsAndFaculties();
}, []);
const handleInputChange = (e) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
if (value.trim() === "") {
setSuggestions((prev) => ({ ...prev, [name]: [] }));
return;
}
if (options.faculties.length > 0) {
const filteredSuggestions = options.faculties.filter((faculty) =>
faculty.name.toLowerCase().includes(value.toLowerCase())
);
setSuggestions((prev) => ({ ...prev, [name]: filteredSuggestions }));
}
};
const handleAddFaculty = (field) => {
if (!formData[field]) return;
const selectedFaculty = options.faculties.find(
(faculty) => faculty.name === formData[field]
);
if (selectedFaculty) {
setTempAssignments((prev) => ({
...prev,
[field]: [...prev[field], selectedFaculty],
}));
setFormData({ ...formData, [field]: "" });
}
};
const handleRemoveFaculty = (field, index) => {
setTempAssignments((prev) => {
const updatedAssignments = [...prev[field]];
updatedAssignments.splice(index, 1);
return { ...prev, [field]: updatedAssignments };
});
};
const validateForm = () => {
const newErrors = {};
Object.keys(tempAssignments).forEach((field) => {
if (!tempAssignments[field] || tempAssignments[field].length === 0) {
newErrors[field] = "At least one faculty must be assigned.";
}
});
if (!examPeriod.year || !examPeriod.startMonth || !examPeriod.endMonth) {
newErrors.examPeriod = "Exam period is required.";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (validateForm()) {
try {
const groupedTasks = {};
const academicYear = course?.academicYear || examPeriod.year;
Object.entries(tempAssignments).forEach(([field, facultyList]) => {
facultyList.forEach((faculty) => {
const assignedFaculty = options.faculties.find(
(optionFaculty) => optionFaculty.facultyId === faculty.facultyId
);
if (assignedFaculty) {
if (!groupedTasks[assignedFaculty.facultyId]) {
groupedTasks[assignedFaculty.facultyId] = {
facultyId: assignedFaculty.facultyId,
courseId: course?.courseId || id,
tasks: [],
examPeriod: `${examPeriod.startMonth} - ${examPeriod.endMonth}`,
academicYear,
};
}
groupedTasks[assignedFaculty.facultyId].tasks.push(field);
}
});
});
const payload = Object.values(groupedTasks);
// Start loading so user knows it is saving
setLoading(true);
await saveAppointment(payload);
// await updateCourseStatus(course?.courseId || id);
const filteredCourses =
JSON.parse(localStorage.getItem("filteredCourses")) || [];
navigate("/courses", {
state: {
courses: filteredCourses,
academicYear: academicYear,
},
});
} catch (error) {
console.error("Failed to save appointment:", error);
// If error comes, stop the loading
setLoading(false);
}
}
};
return (
<>
<Navbar />
<div>
<div className="courseFormContainer">
<div className="courseFormFormContainer">
<h2 className="courseFormHeader">Course Info</h2>
<form className="courseForm" onSubmit={handleSubmit}>
<div>
<label className="courseFormLabel">
Course ID:
<input
type="text"
className="courseFormInput courseFormReadOnly"
value={course?.courseId || id}
readOnly
/>
</label>
</div>
<div>
<label className="courseFormLabel">
Course Name:
<input
type="text"
className="courseFormInput courseFormReadOnly"
value={course?.name || ""}
readOnly
/>
</label>
</div>
<div className={errors.examPeriod ? "courseFormErrorSelect" : ""}>
<label className="courseFormLabel">Exam Period:</label>
<input
type="text"
className="courseFormInput courseFormReadOnly"
value={examPeriod.year}
readOnly
/>
<select
className="courseFormSelect"
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
className="courseFormSelect"
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="courseFormErrorMessage">
{errors.examPeriod}
</span>
)}
</div>
{[
{ name: "oralsPracticals", label: "Orals/Practicals" },
{ name: "assessment", label: "Assessment" },
{ name: "reassessment", label: "Reassessment" },
{ name: "paperSetting", label: "Paper Setting" },
{ name: "moderation", label: "Moderation" },
{ name: "pwdPaperSetter", label: "PwD Paper Setter" },
].map(({ name, label }) => (
<div
key={name}
className={errors[name] ? "courseFormErrorInput" : ""}
>
<label className="courseFormLabel">
{label}:
<select
className="courseFormSelect"
name={name}
value={formData[name]}
onChange={(e) =>
setFormData({ ...formData, [name]: e.target.value })
}
>
<option value="">Select Faculty</option>
{options.faculties.map((faculty) => (
<option key={faculty.facultyId} value={faculty.name}>
{faculty.name}
</option>
))}
</select>
<button
type="button"
className="courseFormButton"
onClick={() => handleAddFaculty(name)}
disabled={!formData[name].trim()}
>
Add
</button>
<ul
className="courseFormSuggestions"
id={`suggestions-${name}`}
style={{
display:
(suggestions[name] || []).length > 0
? "block"
: "none",
}}
>
{(suggestions[name] || []).map((faculty) => (
<li
className="courseFormSuggestionsItem"
key={faculty.facultyId}
onClick={() => {
setFormData({ ...formData, [name]: faculty.name });
setSuggestions((prev) => ({ ...prev, [name]: [] }));
}}
>
{faculty.name}
</li>
))}
</ul>
</label>
{tempAssignments[name].length > 0 && (
<ul className="courseFormTempList">
{tempAssignments[name].map((faculty, index) => (
<li className="courseFormTempListItem" key={index}>
{faculty.name}
<button
type="button"
className="courseFormRemoveFacultyBtn"
onClick={() => handleRemoveFaculty(name, index)}
>
&#10005;
</button>
</li>
))}
</ul>
)}
{errors[name] && (
<span className="courseFormErrorMessage">
{errors[name]}
</span>
)}
</div>
))}
<button
type="submit"
className="courseFormButton"
style={{ gridColumn: "1 / -1" }}
disabled={loading} // Disable button when loading
>
{/* Change text based on loading state */}
{loading ? "Saving..." : "Submit"}
</button>
</form>
</div>
</div>
<Footer />
</div>
</>
);
};
export default CourseForm;