This commit is contained in:
Harikrishnan Gopal
2024-12-17 11:21:25 +05:30
parent ce73a591c5
commit b4adca13c7
7 changed files with 702 additions and 461 deletions

View File

@@ -69,6 +69,38 @@ button[type="submit"] {
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) {
.form-container {
margin: 20px;

View File

@@ -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 { useLocation, useParams, useNavigate } from "react-router-dom";
import { fetchFaculties } from "../api";
import { fetchFaculties, saveAppointment } from "../api";
import "./CourseForm.css";
const CourseForm = () => {
@@ -198,35 +10,27 @@ const CourseForm = () => {
const { course } = location.state || {};
const [options, setOptions] = useState({
assessment: [],
reassessment: [],
paperSetting: [],
moderation: [],
pwdPaperSetter: [],
oralsPracticals: [],
faculties: [], // New field for faculties
faculties: [], // List of all faculties
});
const [suggestions, setSuggestions] = useState({}); // To hold suggestions for each field
const [formData, setFormData] = useState({
oralsPracticals: "",
assessment: "",
reassessment: "",
paperSetting: "",
moderation: "",
pwdPaperSetter: "",
oralsPracticals: "", // New field for Orals/Practicals
});
const [errors, setErrors] = useState({});
// Fetch faculty list on mount
useEffect(() => {
const fetchOptionsAndFaculties = async () => {
try {
const facultiesData = await fetchFaculties(); // Fetch faculty names from the backend
console.log(facultiesData);
setOptions(prevOptions => ({
...prevOptions,
faculties: facultiesData,
}));
const facultiesData = await fetchFaculties(); // Fetch faculty names from backend
setOptions((prev) => ({ ...prev, faculties: facultiesData }));
} catch (error) {
console.error("Failed to fetch faculties:", error);
}
@@ -235,11 +39,25 @@ const CourseForm = () => {
fetchOptionsAndFaculties();
}, []);
// Handle input changes for form fields
const handleInputChange = (e) => {
const { name, value } = e.target;
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 newErrors = {};
Object.keys(formData).forEach((field) => {
@@ -251,30 +69,50 @@ const CourseForm = () => {
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();
// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault(); // Prevent default form submission behavior
if (validateForm()) {
console.log("Form submitted:", formData);
navigate("/courses", {
state: {
updatedCourse: {
...course,
status: "Submitted", // Update status
...formData, // Include form data if required
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?.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 (
<div className="form-container">
<h2>Course Info</h2>
@@ -287,103 +125,42 @@ const CourseForm = () => {
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>
{[
{ 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] ? "error" : ""}>
<label>
{label}:
<input
type="text"
name={name}
value={formData[name]}
onChange={handleInputChange}
placeholder={`Search faculty for ${label}`}
/>
<ul className="suggestions">
{(suggestions[name] || []).map((faculty) => (
<li
key={faculty.facultyId}
onClick={() => {
setFormData({ ...formData, [name]: faculty.name });
setSuggestions((prev) => ({ ...prev, [name]: [] })); // Clear suggestions for this field
}}
>
{faculty.name}
</li>
))}
</ul>
</label>
{errors[name] && <span className="error-message">{errors[name]}</span>}
</div>
))}
<button type="submit">Submit</button>
</form>
</div>
);

View 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;

View File

@@ -1,55 +1,122 @@
const BASE_URL = "http://localhost:8080/api";
export const fetchCourses = async (filterData) => {
// Helper function for handling fetch requests
const fetchData = async (url, options) => {
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();
// console.log(queryString);
const response = await fetch(`${BASE_URL}/courses?${queryString}`, {
const url = `${BASE_URL}/courses?${queryString}`;
return fetchData(url, {
method: "GET",
headers: {
"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) {
console.error("Error fetching courses:", error);
throw error; // Re-throw error to be handled by the caller
console.error("Error fetching courses:", error.message);
throw error;
}
};
// Fetch list of faculties
export const fetchFaculties = async () => {
try {
const response = await fetch(`${BASE_URL}/faculty`);
if (!response.ok) {
throw new Error(`Failed to fetch faculties: ${response.statusText}`);
}
const data = await response.json();
return data;
const url = `${BASE_URL}/faculty`;
return fetchData(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
console.error("Error fetching faculties:", error.message);
throw error;
}
};
// Fetch available options for form dropdowns
export const fetchOptions = async () => {
try {
const response = await fetch(`${BASE_URL}/options`);
if (!response.ok) {
throw new Error(`Failed to fetch options: ${response.statusText}`);
}
const data = await response.json();
return data;
const url = `${BASE_URL}/options`;
return fetchData(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
console.error("Error fetching options:", error.message);
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;
}
};

View File

@@ -1,11 +1,11 @@
const mongoose = require("mongoose");
const { v4: uuidv4 } = require("uuid"); // For UUID generation
const AppointmentSchema = new mongoose.Schema({
appointmentId: { type: String, default: uuidv4 }, // Auto-generate using UUID
courseId: { type: String, required: true, ref: "Course" },
facultyId: { type: String, required: true, ref: "Faculty" },
appointmentType: { type: [String], required: true }, // Array of appointment types
facultyId: { type: String, required: true },
facultyName: { type: String, required: true },
courseId: { type: String, required: true },
courseName: { type: String, required: true },
task: { type: String, required: true },
});
module.exports = mongoose.model("Appointment", AppointmentSchema);

View File

@@ -1,51 +1,65 @@
const express = require("express");
const Appointment = require("../models/Appointment");
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
router.get("/", async (req, res) => {
try {
const appointments = await Appointment.find()
.populate("courseId", "name department")
.populate("facultyId", "name email");
const appointments = await Appointment.find();
res.json(appointments);
} catch (error) {
console.error("Error fetching appointments:", error);
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;

View File

@@ -1,101 +1,58 @@
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const cors = require("cors");
require("dotenv").config();
const passport = require("passport");
const session = require("express-session");
const bcrypt = require("bcryptjs");
const LocalStrategy = require("passport-local").Strategy;
const crypto = require("crypto");
const jwt = require("jsonwebtoken");
const passport = require("passport");
const bodyParser = require("body-parser");
const path = require("path");
const bcrypt = require("bcryptjs");
require("dotenv").config();
const User = require("./models/User");
const PasswordRouter = require("./routes/authRoutes");
// Import Routes
const authRoutes = require("./routes/authRoutes");
const courseRoutes = require("./routes/courseRoutes");
const facultyRoutes = require("./routes/facultyRoutes");
const appointmentRoutes = require("./routes/appointmentRoutes");
const optionsRoutes = require("./routes/optionsRoutes");
// Existing Database Connection
const { connectdb } = require("./ConnectionDb");
connectdb();
// MongoDB Connection
mongoose
.connect(process.env.mongoURI)
.connect(process.env.mongoURI, { useNewUrlParser: true, useUnifiedTopology: true })
.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 PORT = 8080;
// Middleware
app.use(cors());
app.use(cors({ origin: "http://localhost:3000", credentials: true }));
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(
session({
secret: "secret",
secret: "secret", // This can be replaced with another secret from .env if required
resave: false,
saveUninitialized: false,
})
);
app.use(passport.initialize());
app.use(passport.session());
// CORS configuration
app.use(
cors({
origin: "http://localhost:3000",
credentials: true,
})
);
// Passport Configuration
// Passport Config
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
app.use("/password", PasswordRouter);
app.use("/password", authRoutes);
app.use("/api/courses", courseRoutes);
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);
// OAuth Routes
// Google OAuth Routes
app.get(
"/auth/google",
passport.authenticate("google", { scope: ["profile", "email"] })
@@ -104,11 +61,12 @@ app.get(
app.get(
"/auth/google/callback",
passport.authenticate("google", { failureRedirect: "/" }),
function (req, res) {
res.redirect("http://localhost:3000/Welcom");
(req, res) => {
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) => {
try {
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) => {
try {
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")));
// Catch-All Route
app.get("*", (req, res) =>
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
const Port = process.env.PORT || 8080;
app.listen(Port, () => {
console.log(`Server is Running at port ${Port}`);
app.listen(PORT, () => {
console.log(`Server is running at http://localhost:8080`);
});