forked from CSI-KJSCE/appointment_to_examiner
Commit
This commit is contained in:
@@ -69,6 +69,38 @@ button[type="submit"] {
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.suggestions {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions li {
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions li:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error input {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.form-container {
|
.form-container {
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
|||||||
@@ -1,194 +1,6 @@
|
|||||||
// import React, { useState, useEffect } from "react";
|
|
||||||
// import { useLocation, useParams, useNavigate } from "react-router-dom";
|
|
||||||
// import "./CourseForm.css";
|
|
||||||
|
|
||||||
// const CourseForm = () => {
|
|
||||||
// const { id } = useParams(); // Get the course ID from the URL params
|
|
||||||
// const location = useLocation();
|
|
||||||
// const navigate = useNavigate(); // Updated for navigation
|
|
||||||
// const { course } = location.state || {};
|
|
||||||
|
|
||||||
// const [options, setOptions] = useState({
|
|
||||||
// assessment: [],
|
|
||||||
// reassessment: [],
|
|
||||||
// paperSetting: [],
|
|
||||||
// moderation: [],
|
|
||||||
// pwdPaperSetter: [],
|
|
||||||
// oralsPracticals: [], // New field for Orals/Practicals
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const [formData, setFormData] = useState({
|
|
||||||
// assessment: "",
|
|
||||||
// reassessment: "",
|
|
||||||
// paperSetting: "",
|
|
||||||
// moderation: "",
|
|
||||||
// pwdPaperSetter: "",
|
|
||||||
// oralsPracticals: "", // New field for Orals/Practicals
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const [errors, setErrors] = useState({}); // To track validation errors
|
|
||||||
|
|
||||||
// // Fetch data for search bars
|
|
||||||
// useEffect(() => {
|
|
||||||
// const fetchOptions = async () => {
|
|
||||||
// try {
|
|
||||||
// const response = await fetch("/api/options"); // Replace with your API endpoint
|
|
||||||
// const data = await response.json();
|
|
||||||
// setOptions(data);
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Failed to fetch options:", error);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// fetchOptions();
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// const handleInputChange = (e) => {
|
|
||||||
// const { name, value } = e.target;
|
|
||||||
// setFormData({ ...formData, [name]: value });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const validateForm = () => {
|
|
||||||
// const newErrors = {};
|
|
||||||
// Object.keys(formData).forEach((field) => {
|
|
||||||
// if (!formData[field]) {
|
|
||||||
// newErrors[field] = "This field is required";
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// setErrors(newErrors);
|
|
||||||
// return Object.keys(newErrors).length === 0;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const handleSubmit = (e) => {
|
|
||||||
// e.preventDefault();
|
|
||||||
// if (validateForm()) {
|
|
||||||
// console.log("Form submitted:", formData);
|
|
||||||
|
|
||||||
// navigate("/courses", { state: { updatedCourse: { ...course, status: "Submitted" } } });
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <div className="form-container">
|
|
||||||
// <h2>Course Info</h2>
|
|
||||||
// <form onSubmit={handleSubmit}>
|
|
||||||
// <label>
|
|
||||||
// Course ID:
|
|
||||||
// <input type="text" value={course?.id || id} readOnly />
|
|
||||||
// </label>
|
|
||||||
// <label>
|
|
||||||
// Course Name:
|
|
||||||
// <input type="text" value={course?.name || ""} readOnly />
|
|
||||||
// </label>
|
|
||||||
// <label className={errors.oralsPracticals ? "error" : ""}>
|
|
||||||
// Orals/Practicals:
|
|
||||||
// <input
|
|
||||||
// type="text"
|
|
||||||
// name="oralsPracticals"
|
|
||||||
// list="oralsPracticals-options"
|
|
||||||
// value={formData.oralsPracticals}
|
|
||||||
// onChange={handleInputChange}
|
|
||||||
// />
|
|
||||||
// <datalist id="oralsPracticals-options">
|
|
||||||
// {options.oralsPracticals.map((option, index) => (
|
|
||||||
// <option key={index} value={option} />
|
|
||||||
// ))}
|
|
||||||
// </datalist>
|
|
||||||
// {errors.oralsPracticals && <span className="error-message">{errors.oralsPracticals}</span>}
|
|
||||||
// </label>
|
|
||||||
// <label className={errors.assessment ? "error" : ""}>
|
|
||||||
// Assessment:
|
|
||||||
// <input
|
|
||||||
// type="text"
|
|
||||||
// name="assessment"
|
|
||||||
// list="assessment-options"
|
|
||||||
// value={formData.assessment}
|
|
||||||
// onChange={handleInputChange}
|
|
||||||
// />
|
|
||||||
// <datalist id="assessment-options">
|
|
||||||
// {options.assessment.map((option, index) => (
|
|
||||||
// <option key={index} value={option} />
|
|
||||||
// ))}
|
|
||||||
// </datalist>
|
|
||||||
// {errors.assessment && <span className="error-message">{errors.assessment}</span>}
|
|
||||||
// </label>
|
|
||||||
// <label className={errors.reassessment ? "error" : ""}>
|
|
||||||
// Reassessment:
|
|
||||||
// <input
|
|
||||||
// type="text"
|
|
||||||
// name="reassessment"
|
|
||||||
// list="reassessment-options"
|
|
||||||
// value={formData.reassessment}
|
|
||||||
// onChange={handleInputChange}
|
|
||||||
// />
|
|
||||||
// <datalist id="reassessment-options">
|
|
||||||
// {options.reassessment.map((option, index) => (
|
|
||||||
// <option key={index} value={option} />
|
|
||||||
// ))}
|
|
||||||
// </datalist>
|
|
||||||
// {errors.reassessment && <span className="error-message">{errors.reassessment}</span>}
|
|
||||||
// </label>
|
|
||||||
// <label className={errors.paperSetting ? "error" : ""}>
|
|
||||||
// Paper Setting:
|
|
||||||
// <input
|
|
||||||
// type="text"
|
|
||||||
// name="paperSetting"
|
|
||||||
// list="paperSetting-options"
|
|
||||||
// value={formData.paperSetting}
|
|
||||||
// onChange={handleInputChange}
|
|
||||||
// />
|
|
||||||
// <datalist id="paperSetting-options">
|
|
||||||
// {options.paperSetting.map((option, index) => (
|
|
||||||
// <option key={index} value={option} />
|
|
||||||
// ))}
|
|
||||||
// </datalist>
|
|
||||||
// {errors.paperSetting && <span className="error-message">{errors.paperSetting}</span>}
|
|
||||||
// </label>
|
|
||||||
// <label className={errors.moderation ? "error" : ""}>
|
|
||||||
// Moderation:
|
|
||||||
// <input
|
|
||||||
// type="text"
|
|
||||||
// name="moderation"
|
|
||||||
// list="moderation-options"
|
|
||||||
// value={formData.moderation}
|
|
||||||
// onChange={handleInputChange}
|
|
||||||
// />
|
|
||||||
// <datalist id="moderation-options">
|
|
||||||
// {options.moderation.map((option, index) => (
|
|
||||||
// <option key={index} value={option} />
|
|
||||||
// ))}
|
|
||||||
// </datalist>
|
|
||||||
// {errors.moderation && <span className="error-message">{errors.moderation}</span>}
|
|
||||||
// </label>
|
|
||||||
// <label className={errors.pwdPaperSetter ? "error" : ""}>
|
|
||||||
// PwD Paper Setter:
|
|
||||||
// <input
|
|
||||||
// type="text"
|
|
||||||
// name="pwdPaperSetter"
|
|
||||||
// list="pwdPaperSetter-options"
|
|
||||||
// value={formData.pwdPaperSetter}
|
|
||||||
// onChange={handleInputChange}
|
|
||||||
// />
|
|
||||||
// <datalist id="pwdPaperSetter-options">
|
|
||||||
// {options.pwdPaperSetter.map((option, index) => (
|
|
||||||
// <option key={index} value={option} />
|
|
||||||
// ))}
|
|
||||||
// </datalist>
|
|
||||||
// {errors.pwdPaperSetter && <span className="error-message">{errors.pwdPaperSetter}</span>}
|
|
||||||
// </label>
|
|
||||||
// <button type="submit" disabled={Object.keys(errors).length > 0}>Submit</button>
|
|
||||||
// </form>
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export default CourseForm;
|
|
||||||
|
|
||||||
// CourseForm.jsx
|
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useLocation, useParams, useNavigate } from "react-router-dom";
|
import { useLocation, useParams, useNavigate } from "react-router-dom";
|
||||||
import { fetchFaculties } from "../api";
|
import { fetchFaculties, saveAppointment } from "../api";
|
||||||
import "./CourseForm.css";
|
import "./CourseForm.css";
|
||||||
|
|
||||||
const CourseForm = () => {
|
const CourseForm = () => {
|
||||||
@@ -198,35 +10,27 @@ const CourseForm = () => {
|
|||||||
const { course } = location.state || {};
|
const { course } = location.state || {};
|
||||||
|
|
||||||
const [options, setOptions] = useState({
|
const [options, setOptions] = useState({
|
||||||
assessment: [],
|
faculties: [], // List of all faculties
|
||||||
reassessment: [],
|
|
||||||
paperSetting: [],
|
|
||||||
moderation: [],
|
|
||||||
pwdPaperSetter: [],
|
|
||||||
oralsPracticals: [],
|
|
||||||
faculties: [], // New field for faculties
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [suggestions, setSuggestions] = useState({}); // To hold suggestions for each field
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
|
oralsPracticals: "",
|
||||||
assessment: "",
|
assessment: "",
|
||||||
reassessment: "",
|
reassessment: "",
|
||||||
paperSetting: "",
|
paperSetting: "",
|
||||||
moderation: "",
|
moderation: "",
|
||||||
pwdPaperSetter: "",
|
pwdPaperSetter: "",
|
||||||
oralsPracticals: "", // New field for Orals/Practicals
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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 the backend
|
const facultiesData = await fetchFaculties(); // Fetch faculty names from backend
|
||||||
console.log(facultiesData);
|
setOptions((prev) => ({ ...prev, faculties: facultiesData }));
|
||||||
setOptions(prevOptions => ({
|
|
||||||
...prevOptions,
|
|
||||||
faculties: facultiesData,
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch faculties:", error);
|
console.error("Failed to fetch faculties:", error);
|
||||||
}
|
}
|
||||||
@@ -235,11 +39,25 @@ const CourseForm = () => {
|
|||||||
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 });
|
||||||
|
|
||||||
|
// Filter suggestions for the current field
|
||||||
|
if (options.faculties.length > 0) {
|
||||||
|
const filteredSuggestions = options.faculties.filter((faculty) =>
|
||||||
|
faculty.name.toLowerCase().includes(value.toLowerCase())
|
||||||
|
);
|
||||||
|
setSuggestions((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: filteredSuggestions,
|
||||||
|
}));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Validate the form
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
const newErrors = {};
|
const newErrors = {};
|
||||||
Object.keys(formData).forEach((field) => {
|
Object.keys(formData).forEach((field) => {
|
||||||
@@ -251,30 +69,50 @@ const CourseForm = () => {
|
|||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleSubmit = (e) => {
|
// Handle form submission
|
||||||
// e.preventDefault();
|
const handleSubmit = async (e) => {
|
||||||
// if (validateForm()) {
|
e.preventDefault(); // Prevent default form submission behavior
|
||||||
// console.log("Form submitted:", formData);
|
|
||||||
// navigate("/courses", { state: { updatedCourse: { ...course, status: "Submitted" } } });
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
const handleSubmit = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
console.log("Form submitted:", formData);
|
try {
|
||||||
navigate("/courses", {
|
const groupedTasks = {};
|
||||||
state: {
|
|
||||||
updatedCourse: {
|
// Group tasks by facultyId
|
||||||
...course,
|
Object.entries(formData).forEach(([field, value]) => {
|
||||||
status: "Submitted", // Update status
|
const assignedFaculty = options.faculties.find(
|
||||||
...formData, // Include form data if required
|
(faculty) => faculty.name === value
|
||||||
|
);
|
||||||
|
if (assignedFaculty) {
|
||||||
|
if (!groupedTasks[assignedFaculty.facultyId]) {
|
||||||
|
groupedTasks[assignedFaculty.facultyId] = {
|
||||||
|
facultyId: assignedFaculty.facultyId,
|
||||||
|
courseId: course?.id || id,
|
||||||
|
tasks: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
groupedTasks[assignedFaculty.facultyId].tasks.push(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const payload = Object.values(groupedTasks); // Convert the grouped tasks into an array
|
||||||
|
console.log("Saving appointment with payload:", payload);
|
||||||
|
await saveAppointment(payload); // Save to backend
|
||||||
|
console.log("Form submitted successfully:", payload);
|
||||||
|
|
||||||
|
// Redirect to courses page after successful submission
|
||||||
|
navigate("/courses", {
|
||||||
|
state: {
|
||||||
|
updatedCourse: {
|
||||||
|
...course,
|
||||||
|
status: "Submitted",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
});
|
} catch (error) {
|
||||||
|
console.error("Failed to save appointment:", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-container">
|
<div className="form-container">
|
||||||
<h2>Course Info</h2>
|
<h2>Course Info</h2>
|
||||||
@@ -287,103 +125,42 @@ const CourseForm = () => {
|
|||||||
Course Name:
|
Course Name:
|
||||||
<input type="text" value={course?.name || ""} readOnly />
|
<input type="text" value={course?.name || ""} readOnly />
|
||||||
</label>
|
</label>
|
||||||
<label className={errors.oralsPracticals ? "error" : ""}>
|
{[
|
||||||
Orals/Practicals:
|
{ name: "oralsPracticals", label: "Orals/Practicals" },
|
||||||
<input
|
{ name: "assessment", label: "Assessment" },
|
||||||
type="text"
|
{ name: "reassessment", label: "Reassessment" },
|
||||||
name="oralsPracticals"
|
{ name: "paperSetting", label: "Paper Setting" },
|
||||||
list="oralsPracticals-options"
|
{ name: "moderation", label: "Moderation" },
|
||||||
value={formData.oralsPracticals}
|
{ name: "pwdPaperSetter", label: "PwD Paper Setter" },
|
||||||
onChange={handleInputChange}
|
].map(({ name, label }) => (
|
||||||
/>
|
<div key={name} className={errors[name] ? "error" : ""}>
|
||||||
<datalist id="oralsPracticals-options">
|
<label>
|
||||||
{options.oralsPracticals.map((option, index) => (
|
{label}:
|
||||||
<option key={index} value={option.name} />
|
<input
|
||||||
))}
|
type="text"
|
||||||
</datalist>
|
name={name}
|
||||||
{errors.oralsPracticals && <span className="error-message">{errors.oralsPracticals}</span>}
|
value={formData[name]}
|
||||||
</label>
|
onChange={handleInputChange}
|
||||||
<label className={errors.assessment ? "error" : ""}>
|
placeholder={`Search faculty for ${label}`}
|
||||||
Assessment:
|
/>
|
||||||
<input
|
<ul className="suggestions">
|
||||||
type="text"
|
{(suggestions[name] || []).map((faculty) => (
|
||||||
name="assessment"
|
<li
|
||||||
list="assessment-options"
|
key={faculty.facultyId}
|
||||||
value={formData.assessment}
|
onClick={() => {
|
||||||
onChange={handleInputChange}
|
setFormData({ ...formData, [name]: faculty.name });
|
||||||
/>
|
setSuggestions((prev) => ({ ...prev, [name]: [] })); // Clear suggestions for this field
|
||||||
<datalist id="assessment-options">
|
}}
|
||||||
{options.assessment.map((option, index) => (
|
>
|
||||||
<option key={index} value={option.name} />
|
{faculty.name}
|
||||||
))}
|
</li>
|
||||||
</datalist>
|
))}
|
||||||
{errors.assessment && <span className="error-message">{errors.assessment}</span>}
|
</ul>
|
||||||
</label>
|
</label>
|
||||||
<label className={errors.reassessment ? "error" : ""}>
|
{errors[name] && <span className="error-message">{errors[name]}</span>}
|
||||||
Reassessment:
|
</div>
|
||||||
<input
|
))}
|
||||||
type="text"
|
<button type="submit">Submit</button>
|
||||||
name="reassessment"
|
|
||||||
list="reassessment-options"
|
|
||||||
value={formData.reassessment}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
/>
|
|
||||||
<datalist id="reassessment-options">
|
|
||||||
{options.reassessment.map((option, index) => (
|
|
||||||
<option key={index} value={option.name} />
|
|
||||||
))}
|
|
||||||
</datalist>
|
|
||||||
{errors.reassessment && <span className="error-message">{errors.reassessment}</span>}
|
|
||||||
</label>
|
|
||||||
<label className={errors.paperSetting ? "error" : ""}>
|
|
||||||
Paper Setting:
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="paperSetting"
|
|
||||||
list="paperSetting-options"
|
|
||||||
value={formData.paperSetting}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
/>
|
|
||||||
<datalist id="paperSetting-options">
|
|
||||||
{options.paperSetting.map((option, index) => (
|
|
||||||
<option key={index} value={option.name} />
|
|
||||||
))}
|
|
||||||
</datalist>
|
|
||||||
{errors.paperSetting && <span className="error-message">{errors.paperSetting}</span>}
|
|
||||||
</label>
|
|
||||||
<label className={errors.moderation ? "error" : ""}>
|
|
||||||
Moderation:
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="moderation"
|
|
||||||
list="moderation-options"
|
|
||||||
value={formData.moderation}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
/>
|
|
||||||
<datalist id="moderation-options">
|
|
||||||
{options.moderation.map((option, index) => (
|
|
||||||
<option key={index} value={option.name} />
|
|
||||||
))}
|
|
||||||
</datalist>
|
|
||||||
{errors.moderation && <span className="error-message">{errors.moderation}</span>}
|
|
||||||
</label>
|
|
||||||
<label className={errors.pwdPaperSetter ? "error" : ""}>
|
|
||||||
PwD Paper Setter:
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="pwdPaperSetter"
|
|
||||||
list="pwdPaperSetter-options"
|
|
||||||
value={formData.pwdPaperSetter}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
/>
|
|
||||||
<datalist id="pwdPaperSetter-options">
|
|
||||||
{options.pwdPaperSetter.map((option, index) => (
|
|
||||||
<option key={index} value={option.name} />
|
|
||||||
))}
|
|
||||||
</datalist>
|
|
||||||
{errors.pwdPaperSetter && <span className="error-message">{errors.pwdPaperSetter}</span>}
|
|
||||||
</label>
|
|
||||||
<button type="submit" disabled={Object.keys(errors).length > 0}>Submit</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
392
client/src/Pages/CourseForm1.jsx
Normal file
392
client/src/Pages/CourseForm1.jsx
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
// import React, { useState, useEffect } from "react";
|
||||||
|
// import { useLocation, useParams, useNavigate } from "react-router-dom";
|
||||||
|
// import "./CourseForm.css";
|
||||||
|
|
||||||
|
// const CourseForm = () => {
|
||||||
|
// const { id } = useParams(); // Get the course ID from the URL params
|
||||||
|
// const location = useLocation();
|
||||||
|
// const navigate = useNavigate(); // Updated for navigation
|
||||||
|
// const { course } = location.state || {};
|
||||||
|
|
||||||
|
// const [options, setOptions] = useState({
|
||||||
|
// assessment: [],
|
||||||
|
// reassessment: [],
|
||||||
|
// paperSetting: [],
|
||||||
|
// moderation: [],
|
||||||
|
// pwdPaperSetter: [],
|
||||||
|
// oralsPracticals: [], // New field for Orals/Practicals
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const [formData, setFormData] = useState({
|
||||||
|
// assessment: "",
|
||||||
|
// reassessment: "",
|
||||||
|
// paperSetting: "",
|
||||||
|
// moderation: "",
|
||||||
|
// pwdPaperSetter: "",
|
||||||
|
// oralsPracticals: "", // New field for Orals/Practicals
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const [errors, setErrors] = useState({}); // To track validation errors
|
||||||
|
|
||||||
|
// // Fetch data for search bars
|
||||||
|
// useEffect(() => {
|
||||||
|
// const fetchOptions = async () => {
|
||||||
|
// try {
|
||||||
|
// const response = await fetch("/api/options"); // Replace with your API endpoint
|
||||||
|
// const data = await response.json();
|
||||||
|
// setOptions(data);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Failed to fetch options:", error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// fetchOptions();
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// const handleInputChange = (e) => {
|
||||||
|
// const { name, value } = e.target;
|
||||||
|
// setFormData({ ...formData, [name]: value });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const validateForm = () => {
|
||||||
|
// const newErrors = {};
|
||||||
|
// Object.keys(formData).forEach((field) => {
|
||||||
|
// if (!formData[field]) {
|
||||||
|
// newErrors[field] = "This field is required";
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// setErrors(newErrors);
|
||||||
|
// return Object.keys(newErrors).length === 0;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const handleSubmit = (e) => {
|
||||||
|
// e.preventDefault();
|
||||||
|
// if (validateForm()) {
|
||||||
|
// console.log("Form submitted:", formData);
|
||||||
|
|
||||||
|
// navigate("/courses", { state: { updatedCourse: { ...course, status: "Submitted" } } });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="form-container">
|
||||||
|
// <h2>Course Info</h2>
|
||||||
|
// <form onSubmit={handleSubmit}>
|
||||||
|
// <label>
|
||||||
|
// Course ID:
|
||||||
|
// <input type="text" value={course?.id || id} readOnly />
|
||||||
|
// </label>
|
||||||
|
// <label>
|
||||||
|
// Course Name:
|
||||||
|
// <input type="text" value={course?.name || ""} readOnly />
|
||||||
|
// </label>
|
||||||
|
// <label className={errors.oralsPracticals ? "error" : ""}>
|
||||||
|
// Orals/Practicals:
|
||||||
|
// <input
|
||||||
|
// type="text"
|
||||||
|
// name="oralsPracticals"
|
||||||
|
// list="oralsPracticals-options"
|
||||||
|
// value={formData.oralsPracticals}
|
||||||
|
// onChange={handleInputChange}
|
||||||
|
// />
|
||||||
|
// <datalist id="oralsPracticals-options">
|
||||||
|
// {options.oralsPracticals.map((option, index) => (
|
||||||
|
// <option key={index} value={option} />
|
||||||
|
// ))}
|
||||||
|
// </datalist>
|
||||||
|
// {errors.oralsPracticals && <span className="error-message">{errors.oralsPracticals}</span>}
|
||||||
|
// </label>
|
||||||
|
// <label className={errors.assessment ? "error" : ""}>
|
||||||
|
// Assessment:
|
||||||
|
// <input
|
||||||
|
// type="text"
|
||||||
|
// name="assessment"
|
||||||
|
// list="assessment-options"
|
||||||
|
// value={formData.assessment}
|
||||||
|
// onChange={handleInputChange}
|
||||||
|
// />
|
||||||
|
// <datalist id="assessment-options">
|
||||||
|
// {options.assessment.map((option, index) => (
|
||||||
|
// <option key={index} value={option} />
|
||||||
|
// ))}
|
||||||
|
// </datalist>
|
||||||
|
// {errors.assessment && <span className="error-message">{errors.assessment}</span>}
|
||||||
|
// </label>
|
||||||
|
// <label className={errors.reassessment ? "error" : ""}>
|
||||||
|
// Reassessment:
|
||||||
|
// <input
|
||||||
|
// type="text"
|
||||||
|
// name="reassessment"
|
||||||
|
// list="reassessment-options"
|
||||||
|
// value={formData.reassessment}
|
||||||
|
// onChange={handleInputChange}
|
||||||
|
// />
|
||||||
|
// <datalist id="reassessment-options">
|
||||||
|
// {options.reassessment.map((option, index) => (
|
||||||
|
// <option key={index} value={option} />
|
||||||
|
// ))}
|
||||||
|
// </datalist>
|
||||||
|
// {errors.reassessment && <span className="error-message">{errors.reassessment}</span>}
|
||||||
|
// </label>
|
||||||
|
// <label className={errors.paperSetting ? "error" : ""}>
|
||||||
|
// Paper Setting:
|
||||||
|
// <input
|
||||||
|
// type="text"
|
||||||
|
// name="paperSetting"
|
||||||
|
// list="paperSetting-options"
|
||||||
|
// value={formData.paperSetting}
|
||||||
|
// onChange={handleInputChange}
|
||||||
|
// />
|
||||||
|
// <datalist id="paperSetting-options">
|
||||||
|
// {options.paperSetting.map((option, index) => (
|
||||||
|
// <option key={index} value={option} />
|
||||||
|
// ))}
|
||||||
|
// </datalist>
|
||||||
|
// {errors.paperSetting && <span className="error-message">{errors.paperSetting}</span>}
|
||||||
|
// </label>
|
||||||
|
// <label className={errors.moderation ? "error" : ""}>
|
||||||
|
// Moderation:
|
||||||
|
// <input
|
||||||
|
// type="text"
|
||||||
|
// name="moderation"
|
||||||
|
// list="moderation-options"
|
||||||
|
// value={formData.moderation}
|
||||||
|
// onChange={handleInputChange}
|
||||||
|
// />
|
||||||
|
// <datalist id="moderation-options">
|
||||||
|
// {options.moderation.map((option, index) => (
|
||||||
|
// <option key={index} value={option} />
|
||||||
|
// ))}
|
||||||
|
// </datalist>
|
||||||
|
// {errors.moderation && <span className="error-message">{errors.moderation}</span>}
|
||||||
|
// </label>
|
||||||
|
// <label className={errors.pwdPaperSetter ? "error" : ""}>
|
||||||
|
// PwD Paper Setter:
|
||||||
|
// <input
|
||||||
|
// type="text"
|
||||||
|
// name="pwdPaperSetter"
|
||||||
|
// list="pwdPaperSetter-options"
|
||||||
|
// value={formData.pwdPaperSetter}
|
||||||
|
// onChange={handleInputChange}
|
||||||
|
// />
|
||||||
|
// <datalist id="pwdPaperSetter-options">
|
||||||
|
// {options.pwdPaperSetter.map((option, index) => (
|
||||||
|
// <option key={index} value={option} />
|
||||||
|
// ))}
|
||||||
|
// </datalist>
|
||||||
|
// {errors.pwdPaperSetter && <span className="error-message">{errors.pwdPaperSetter}</span>}
|
||||||
|
// </label>
|
||||||
|
// <button type="submit" disabled={Object.keys(errors).length > 0}>Submit</button>
|
||||||
|
// </form>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default CourseForm;
|
||||||
|
|
||||||
|
// CourseForm.jsx
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { useLocation, useParams, useNavigate } from "react-router-dom";
|
||||||
|
import { fetchFaculties } from "../api";
|
||||||
|
import "./CourseForm.css";
|
||||||
|
|
||||||
|
const CourseForm = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { course } = location.state || {};
|
||||||
|
|
||||||
|
const [options, setOptions] = useState({
|
||||||
|
assessment: [],
|
||||||
|
reassessment: [],
|
||||||
|
paperSetting: [],
|
||||||
|
moderation: [],
|
||||||
|
pwdPaperSetter: [],
|
||||||
|
oralsPracticals: [],
|
||||||
|
faculties: [], // New field for faculties
|
||||||
|
});
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
assessment: "",
|
||||||
|
reassessment: "",
|
||||||
|
paperSetting: "",
|
||||||
|
moderation: "",
|
||||||
|
pwdPaperSetter: "",
|
||||||
|
oralsPracticals: "", // New field for Orals/Practicals
|
||||||
|
});
|
||||||
|
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchOptionsAndFaculties = async () => {
|
||||||
|
try {
|
||||||
|
const facultiesData = await fetchFaculties(); // Fetch faculty names from the backend
|
||||||
|
console.log(facultiesData);
|
||||||
|
setOptions(prevOptions => ({
|
||||||
|
...prevOptions,
|
||||||
|
faculties: facultiesData,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch faculties:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchOptionsAndFaculties();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData({ ...formData, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateForm = () => {
|
||||||
|
const newErrors = {};
|
||||||
|
Object.keys(formData).forEach((field) => {
|
||||||
|
if (!formData[field]) {
|
||||||
|
newErrors[field] = "This field is required";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setErrors(newErrors);
|
||||||
|
return Object.keys(newErrors).length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// const handleSubmit = (e) => {
|
||||||
|
// e.preventDefault();
|
||||||
|
// if (validateForm()) {
|
||||||
|
// console.log("Form submitted:", formData);
|
||||||
|
// navigate("/courses", { state: { updatedCourse: { ...course, status: "Submitted" } } });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (validateForm()) {
|
||||||
|
console.log("Form submitted:", formData);
|
||||||
|
navigate("/courses", {
|
||||||
|
state: {
|
||||||
|
updatedCourse: {
|
||||||
|
...course,
|
||||||
|
status: "Submitted", // Update status
|
||||||
|
...formData, // Include form data if required
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="form-container">
|
||||||
|
<h2>Course Info</h2>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<label>
|
||||||
|
Course ID:
|
||||||
|
<input type="text" value={course?.id || id} readOnly />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Course Name:
|
||||||
|
<input type="text" value={course?.name || ""} readOnly />
|
||||||
|
</label>
|
||||||
|
<label className={errors.oralsPracticals ? "error" : ""}>
|
||||||
|
Orals/Practicals:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="oralsPracticals"
|
||||||
|
list="oralsPracticals-options"
|
||||||
|
value={formData.oralsPracticals}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<datalist id="oralsPracticals-options">
|
||||||
|
{options.oralsPracticals.map((option, index) => (
|
||||||
|
<option key={index} value={option.name} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
{errors.oralsPracticals && <span className="error-message">{errors.oralsPracticals}</span>}
|
||||||
|
</label>
|
||||||
|
<label className={errors.assessment ? "error" : ""}>
|
||||||
|
Assessment:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="assessment"
|
||||||
|
list="assessment-options"
|
||||||
|
value={formData.assessment}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<datalist id="assessment-options">
|
||||||
|
{options.assessment.map((option, index) => (
|
||||||
|
<option key={index} value={option.name} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
{errors.assessment && <span className="error-message">{errors.assessment}</span>}
|
||||||
|
</label>
|
||||||
|
<label className={errors.reassessment ? "error" : ""}>
|
||||||
|
Reassessment:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="reassessment"
|
||||||
|
list="reassessment-options"
|
||||||
|
value={formData.reassessment}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<datalist id="reassessment-options">
|
||||||
|
{options.reassessment.map((option, index) => (
|
||||||
|
<option key={index} value={option.name} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
{errors.reassessment && <span className="error-message">{errors.reassessment}</span>}
|
||||||
|
</label>
|
||||||
|
<label className={errors.paperSetting ? "error" : ""}>
|
||||||
|
Paper Setting:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="paperSetting"
|
||||||
|
list="paperSetting-options"
|
||||||
|
value={formData.paperSetting}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<datalist id="paperSetting-options">
|
||||||
|
{options.paperSetting.map((option, index) => (
|
||||||
|
<option key={index} value={option.name} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
{errors.paperSetting && <span className="error-message">{errors.paperSetting}</span>}
|
||||||
|
</label>
|
||||||
|
<label className={errors.moderation ? "error" : ""}>
|
||||||
|
Moderation:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="moderation"
|
||||||
|
list="moderation-options"
|
||||||
|
value={formData.moderation}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<datalist id="moderation-options">
|
||||||
|
{options.moderation.map((option, index) => (
|
||||||
|
<option key={index} value={option.name} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
{errors.moderation && <span className="error-message">{errors.moderation}</span>}
|
||||||
|
</label>
|
||||||
|
<label className={errors.pwdPaperSetter ? "error" : ""}>
|
||||||
|
PwD Paper Setter:
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="pwdPaperSetter"
|
||||||
|
list="pwdPaperSetter-options"
|
||||||
|
value={formData.pwdPaperSetter}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<datalist id="pwdPaperSetter-options">
|
||||||
|
{options.pwdPaperSetter.map((option, index) => (
|
||||||
|
<option key={index} value={option.name} />
|
||||||
|
))}
|
||||||
|
</datalist>
|
||||||
|
{errors.pwdPaperSetter && <span className="error-message">{errors.pwdPaperSetter}</span>}
|
||||||
|
</label>
|
||||||
|
<button type="submit" disabled={Object.keys(errors).length > 0}>Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CourseForm;
|
||||||
@@ -1,55 +1,122 @@
|
|||||||
const BASE_URL = "http://localhost:8080/api";
|
const BASE_URL = "http://localhost:8080/api";
|
||||||
|
|
||||||
export const fetchCourses = async (filterData) => {
|
// Helper function for handling fetch requests
|
||||||
|
const fetchData = async (url, options) => {
|
||||||
try {
|
try {
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
|
||||||
// Serialize filterData into query parameters
|
// Check if response is OK (status 200-299)
|
||||||
|
if (!response.ok) {
|
||||||
|
let errorDetails = {};
|
||||||
|
try {
|
||||||
|
errorDetails = await response.json(); // Attempt to parse error response
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Failed to parse error details:", err);
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Error: ${response.statusText} (${response.status}) - ${
|
||||||
|
errorDetails.message || "No details available"
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return JSON response if successful
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Request failed for ${url}:`, error.message);
|
||||||
|
throw error; // Re-throw for the caller to handle
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch courses with optional filters
|
||||||
|
export const fetchCourses = async (filterData = {}) => {
|
||||||
|
try {
|
||||||
const queryString = new URLSearchParams(filterData).toString();
|
const queryString = new URLSearchParams(filterData).toString();
|
||||||
// console.log(queryString);
|
const url = `${BASE_URL}/courses?${queryString}`;
|
||||||
const response = await fetch(`${BASE_URL}/courses?${queryString}`, {
|
return fetchData(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Failed to fetch courses");
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredCourses = await response.json();
|
|
||||||
console.log(filteredCourses);
|
|
||||||
return filteredCourses;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching courses:", error);
|
console.error("Error fetching courses:", error.message);
|
||||||
throw error; // Re-throw error to be handled by the caller
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fetch list of faculties
|
||||||
export const fetchFaculties = async () => {
|
export const fetchFaculties = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${BASE_URL}/faculty`);
|
const url = `${BASE_URL}/faculty`;
|
||||||
if (!response.ok) {
|
return fetchData(url, {
|
||||||
throw new Error(`Failed to fetch faculties: ${response.statusText}`);
|
method: "GET",
|
||||||
}
|
headers: {
|
||||||
const data = await response.json();
|
"Content-Type": "application/json",
|
||||||
return data;
|
},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching faculties:", error.message);
|
console.error("Error fetching faculties:", error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fetch available options for form dropdowns
|
||||||
export const fetchOptions = async () => {
|
export const fetchOptions = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${BASE_URL}/options`);
|
const url = `${BASE_URL}/options`;
|
||||||
if (!response.ok) {
|
return fetchData(url, {
|
||||||
throw new Error(`Failed to fetch options: ${response.statusText}`);
|
method: "GET",
|
||||||
}
|
headers: {
|
||||||
const data = await response.json();
|
"Content-Type": "application/json",
|
||||||
return data;
|
},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching options:", error.message);
|
console.error("Error fetching options:", error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Save multiple appointments to MongoDB
|
||||||
|
export const saveAppointment = async (appointmentsData) => {
|
||||||
|
console.log("Saving appointments with payload:", appointmentsData);
|
||||||
|
|
||||||
|
// Validate input format
|
||||||
|
if (!Array.isArray(appointmentsData) || appointmentsData.length === 0) {
|
||||||
|
const errorMessage =
|
||||||
|
"Invalid or missing appointment data: expected a non-empty array";
|
||||||
|
console.error(errorMessage);
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate each appointment's structure
|
||||||
|
const invalidEntries = appointmentsData.filter(
|
||||||
|
(appointment) =>
|
||||||
|
!appointment.facultyId || !appointment.courseId || !Array.isArray(appointment.tasks)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (invalidEntries.length > 0) {
|
||||||
|
const errorMessage = `Invalid appointments detected: ${JSON.stringify(
|
||||||
|
invalidEntries
|
||||||
|
)}`;
|
||||||
|
console.error(errorMessage);
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = `${BASE_URL}/appointments`;
|
||||||
|
const response = await fetchData(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ appointments: appointmentsData }), // Send appointments as an array
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Appointments saved successfully:", response);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error saving appointments:", error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
const { v4: uuidv4 } = require("uuid"); // For UUID generation
|
|
||||||
|
|
||||||
const AppointmentSchema = new mongoose.Schema({
|
const AppointmentSchema = new mongoose.Schema({
|
||||||
appointmentId: { type: String, default: uuidv4 }, // Auto-generate using UUID
|
facultyId: { type: String, required: true },
|
||||||
courseId: { type: String, required: true, ref: "Course" },
|
facultyName: { type: String, required: true },
|
||||||
facultyId: { type: String, required: true, ref: "Faculty" },
|
courseId: { type: String, required: true },
|
||||||
appointmentType: { type: [String], required: true }, // Array of appointment types
|
courseName: { type: String, required: true },
|
||||||
|
task: { type: String, required: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mongoose.model("Appointment", AppointmentSchema);
|
module.exports = mongoose.model("Appointment", AppointmentSchema);
|
||||||
|
|||||||
@@ -1,51 +1,65 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const Appointment = require("../models/Appointment");
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const Appointment = require("../models/Appointment");
|
||||||
|
const Faculty = require("../models/Faculty");
|
||||||
|
const Course = require("../models/Course");
|
||||||
|
|
||||||
|
// Save multiple appointments
|
||||||
|
router.post("/", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { appointments } = req.body; // Expecting an array of appointments
|
||||||
|
if (!appointments || !Array.isArray(appointments)) {
|
||||||
|
return res.status(400).json({ error: "Invalid or missing data" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedAppointments = [];
|
||||||
|
for (const appointment of appointments) {
|
||||||
|
const { facultyId, courseId, tasks } = appointment;
|
||||||
|
|
||||||
|
// Validate input data
|
||||||
|
if (!facultyId || !courseId || !Array.isArray(tasks) || tasks.length === 0) {
|
||||||
|
return res.status(400).json({ error: "Invalid appointment data" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const faculty = await Faculty.findOne({ facultyId });
|
||||||
|
const course = await Course.findOne({ courseId });
|
||||||
|
|
||||||
|
if (!faculty || !course) {
|
||||||
|
return res.status(404).json({
|
||||||
|
error: `Faculty or Course not found for facultyId: ${facultyId}, courseId: ${courseId}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save each task as a separate appointment
|
||||||
|
for (const task of tasks) {
|
||||||
|
const newAppointment = new Appointment({
|
||||||
|
facultyId,
|
||||||
|
facultyName: faculty.name,
|
||||||
|
courseId,
|
||||||
|
courseName: course.name,
|
||||||
|
task,
|
||||||
|
});
|
||||||
|
const savedAppointment = await newAppointment.save();
|
||||||
|
savedAppointments.push(savedAppointment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(201).json(savedAppointments);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error saving appointments:", error);
|
||||||
|
res.status(500).json({ error: "Failed to save appointments" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get all appointments
|
// Get all appointments
|
||||||
router.get("/", async (req, res) => {
|
router.get("/", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const appointments = await Appointment.find()
|
const appointments = await Appointment.find();
|
||||||
.populate("courseId", "name department")
|
|
||||||
.populate("facultyId", "name email");
|
|
||||||
res.json(appointments);
|
res.json(appointments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("Error fetching appointments:", error);
|
||||||
res.status(500).json({ error: "Failed to fetch appointments" });
|
res.status(500).json({ error: "Failed to fetch appointments" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a new appointment
|
|
||||||
router.post("/", async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { courseId, facultyId, appointmentType } = req.body;
|
|
||||||
|
|
||||||
const newAppointment = new Appointment({
|
|
||||||
courseId,
|
|
||||||
facultyId,
|
|
||||||
appointmentType,
|
|
||||||
});
|
|
||||||
|
|
||||||
await newAppointment.save();
|
|
||||||
res.status(201).json(newAppointment);
|
|
||||||
} catch (error) {
|
|
||||||
res.status(400).json({ error: "Failed to create appointment" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get appointment by ID
|
|
||||||
router.get("/:id", async (req, res) => {
|
|
||||||
try {
|
|
||||||
const appointment = await Appointment.findOne({ appointmentId: req.params.id })
|
|
||||||
.populate("courseId", "name department")
|
|
||||||
.populate("facultyId", "name email");
|
|
||||||
if (!appointment) {
|
|
||||||
return res.status(404).json({ error: "Appointment not found" });
|
|
||||||
}
|
|
||||||
res.json(appointment);
|
|
||||||
} catch (error) {
|
|
||||||
res.status(500).json({ error: "Failed to fetch appointment" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
103
server/server.js
103
server/server.js
@@ -1,101 +1,58 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
const bodyParser = require("body-parser");
|
|
||||||
const cors = require("cors");
|
const cors = require("cors");
|
||||||
require("dotenv").config();
|
|
||||||
const passport = require("passport");
|
|
||||||
const session = require("express-session");
|
const session = require("express-session");
|
||||||
const bcrypt = require("bcryptjs");
|
const passport = require("passport");
|
||||||
const LocalStrategy = require("passport-local").Strategy;
|
const bodyParser = require("body-parser");
|
||||||
const crypto = require("crypto");
|
|
||||||
const jwt = require("jsonwebtoken");
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const bcrypt = require("bcryptjs");
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
const User = require("./models/User");
|
// Import Routes
|
||||||
const PasswordRouter = require("./routes/authRoutes");
|
const authRoutes = require("./routes/authRoutes");
|
||||||
const courseRoutes = require("./routes/courseRoutes");
|
const courseRoutes = require("./routes/courseRoutes");
|
||||||
const facultyRoutes = require("./routes/facultyRoutes");
|
const facultyRoutes = require("./routes/facultyRoutes");
|
||||||
const appointmentRoutes = require("./routes/appointmentRoutes");
|
const appointmentRoutes = require("./routes/appointmentRoutes");
|
||||||
const optionsRoutes = require("./routes/optionsRoutes");
|
const optionsRoutes = require("./routes/optionsRoutes");
|
||||||
|
|
||||||
// Existing Database Connection
|
|
||||||
const { connectdb } = require("./ConnectionDb");
|
|
||||||
connectdb();
|
|
||||||
|
|
||||||
// MongoDB Connection
|
// MongoDB Connection
|
||||||
mongoose
|
mongoose
|
||||||
.connect(process.env.mongoURI)
|
.connect(process.env.mongoURI, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||||
.then(() => console.log("MongoDB connected"))
|
.then(() => console.log("MongoDB connected"))
|
||||||
.catch((err) => console.error("MongoDB connection error:", err));
|
.catch((err) => {
|
||||||
|
console.error("MongoDB connection error:", err);
|
||||||
|
process.exit(1); // Exit the app if the database connection fails
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize App
|
||||||
const app = express();
|
const app = express();
|
||||||
|
const PORT = 8080;
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(cors());
|
app.use(cors({ origin: "http://localhost:3000", credentials: true }));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
secret: "secret",
|
secret: "secret", // This can be replaced with another secret from .env if required
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
app.use(passport.session());
|
app.use(passport.session());
|
||||||
|
|
||||||
// CORS configuration
|
// Passport Config
|
||||||
app.use(
|
|
||||||
cors({
|
|
||||||
origin: "http://localhost:3000",
|
|
||||||
credentials: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Passport Configuration
|
|
||||||
require("./config/passport");
|
require("./config/passport");
|
||||||
|
|
||||||
passport.use(
|
|
||||||
new LocalStrategy(
|
|
||||||
{ usernameField: "email" },
|
|
||||||
async (email, password, done) => {
|
|
||||||
try {
|
|
||||||
const user = await User.findOne({ email });
|
|
||||||
if (!user) {
|
|
||||||
return done(null, false, { message: "Incorrect email" });
|
|
||||||
}
|
|
||||||
const isMatch = await bcrypt.compare(password, user.password);
|
|
||||||
if (isMatch) {
|
|
||||||
return done(null, user);
|
|
||||||
} else {
|
|
||||||
return done(null, false, { message: "Incorrect password" });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return done(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
passport.serializeUser((user, done) => {
|
|
||||||
done(null, user.id); // Store user ID in the session
|
|
||||||
});
|
|
||||||
|
|
||||||
passport.deserializeUser((id, done) => {
|
|
||||||
User.findById(id, (err, user) => {
|
|
||||||
done(err, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
app.use("/password", PasswordRouter);
|
app.use("/password", authRoutes);
|
||||||
app.use("/api/courses", courseRoutes);
|
app.use("/api/courses", courseRoutes);
|
||||||
app.use("/api/faculty", facultyRoutes);
|
app.use("/api/faculty", facultyRoutes);
|
||||||
app.use("/api/appointments", appointmentRoutes);
|
app.use("/api/appointments", appointmentRoutes); // Appointment route handles the updated structure
|
||||||
app.use("/api/options", optionsRoutes);
|
app.use("/api/options", optionsRoutes);
|
||||||
|
|
||||||
// OAuth Routes
|
// Google OAuth Routes
|
||||||
app.get(
|
app.get(
|
||||||
"/auth/google",
|
"/auth/google",
|
||||||
passport.authenticate("google", { scope: ["profile", "email"] })
|
passport.authenticate("google", { scope: ["profile", "email"] })
|
||||||
@@ -104,11 +61,12 @@ app.get(
|
|||||||
app.get(
|
app.get(
|
||||||
"/auth/google/callback",
|
"/auth/google/callback",
|
||||||
passport.authenticate("google", { failureRedirect: "/" }),
|
passport.authenticate("google", { failureRedirect: "/" }),
|
||||||
function (req, res) {
|
(req, res) => {
|
||||||
res.redirect("http://localhost:3000/Welcom");
|
res.redirect("http://localhost:3000/Welcom"); // Redirect to a frontend route after successful login
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Local authentication routes (register and login)
|
||||||
app.post("/api/register", async (req, res) => {
|
app.post("/api/register", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { username, email, password } = req.body;
|
const { username, email, password } = req.body;
|
||||||
@@ -180,6 +138,7 @@ app.get("/auth/logout", function (req, res) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// User Profile Route
|
||||||
app.get("/api/user/profile", async (req, res) => {
|
app.get("/api/user/profile", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
@@ -193,19 +152,19 @@ app.get("/api/user/profile", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Serve Static Files
|
// Serve React Build Files
|
||||||
app.use(express.static(path.join(__dirname, "../client/build")));
|
app.use(express.static(path.join(__dirname, "../client/build")));
|
||||||
|
|
||||||
// Catch-All Route
|
|
||||||
app.get("*", (req, res) =>
|
app.get("*", (req, res) =>
|
||||||
res.sendFile(path.join(__dirname, "../client/build/index.html"))
|
res.sendFile(path.join(__dirname, "../client/build/index.html"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Error Handling Middleware
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
console.error("Error:", err.stack);
|
||||||
|
res.status(err.status || 500).json({ error: err.message || "Internal Server Error" });
|
||||||
|
});
|
||||||
|
|
||||||
// Start Server
|
// Start Server
|
||||||
const Port = process.env.PORT || 8080;
|
app.listen(PORT, () => {
|
||||||
app.listen(Port, () => {
|
console.log(`Server is running at http://localhost:8080`);
|
||||||
console.log(`Server is Running at port ${Port}`);
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user