Added backend and integration for FilterPage and CourseTable done
This commit is contained in:
@@ -1,63 +1,85 @@
|
||||
/* CourseForm.css */
|
||||
.form-container {
|
||||
max-width: 600px;
|
||||
margin: 30px auto;
|
||||
padding: 20px;
|
||||
background-color: #f4f4f9;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
max-width: 600px;
|
||||
margin: 50px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 10px;
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 10px;
|
||||
font-size: 1rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 15px;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: #003d7a;
|
||||
}
|
||||
|
||||
button[type="submit"] {
|
||||
align-self: center;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.form-container {
|
||||
margin: 20px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 12px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
|
||||
button[type="submit"] {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 5px rgba(0, 192, 0, 0.2);
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 12px;
|
||||
font-size: 16px;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,27 +1,182 @@
|
||||
import React from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
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>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<label>
|
||||
Course ID:
|
||||
<input type="text" value={id} readOnly />
|
||||
<input type="text" value={course?.id || id} readOnly />
|
||||
</label>
|
||||
<label>
|
||||
Course Name:
|
||||
<input type="text" placeholder="Enter course name" />
|
||||
<input type="text" value={course?.name || ""} readOnly />
|
||||
</label>
|
||||
<label>
|
||||
Status:
|
||||
<input type="text" placeholder="Enter course status" />
|
||||
<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>
|
||||
<button type="submit">Submit</button>
|
||||
<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>
|
||||
);
|
||||
|
||||
111
client/src/Pages/CourseTable.css
Normal file
111
client/src/Pages/CourseTable.css
Normal file
@@ -0,0 +1,111 @@
|
||||
/* CourseTable.css */
|
||||
|
||||
.course-table {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.course-table h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.course-table ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.course-table li {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.course-table li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.course-table li .status {
|
||||
font-size: 0.9rem;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.course-table li .status.Pending {
|
||||
background-color: #f39c12;
|
||||
}
|
||||
|
||||
.course-table li .status.Submitted {
|
||||
background-color: #2ecc71;
|
||||
}
|
||||
|
||||
.course-table .form-container {
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.course-table .form-container h2 {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.course-table .form-container form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.course-table .form-container label {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.course-table .form-container input[type="text"] {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.course-table .form-container input[type="text"]:focus {
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.course-table .form-container .error {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.course-table .form-container .error-message {
|
||||
color: red;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.course-table .form-container button {
|
||||
padding: 10px;
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.course-table .form-container button:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
89
client/src/Pages/CourseTable.jsx
Normal file
89
client/src/Pages/CourseTable.jsx
Normal file
@@ -0,0 +1,89 @@
|
||||
// import React from "react";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
// import "./CourseTable.css";
|
||||
|
||||
// const CourseTable = ({courses}) => {
|
||||
// const navigate = useNavigate();
|
||||
|
||||
// const handleRowClick = (course) => {
|
||||
// navigate(`/course-form/${course.id}`, { state: { course } });
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="course-table">
|
||||
// <h1>Courses</h1>
|
||||
// <table border="1">
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Course ID</th>
|
||||
// <th>Course Name</th>
|
||||
// <th>Status</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// {courses.map((course) => (
|
||||
// <tr
|
||||
// key={course.id}
|
||||
// onClick={() => handleRowClick(course)}
|
||||
// style={{ cursor: "pointer" }}
|
||||
// >
|
||||
// <td>{course.id}</td>
|
||||
// <td>{course.name}</td>
|
||||
// <td>
|
||||
// <span className={`status ${course.status.replace(" ", "-")}`}>
|
||||
// {course.status}
|
||||
// </span>
|
||||
// </td>
|
||||
// </tr>
|
||||
// ))}
|
||||
// </tbody>
|
||||
// </table>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default CourseTable;
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import "./CourseTable.css";
|
||||
import { fetchCourses } from '../api';
|
||||
|
||||
const CourseTable = () => {
|
||||
const { state } = useLocation();
|
||||
const courses = state?.courses;
|
||||
console.log(courses);
|
||||
|
||||
useEffect(() => {
|
||||
if (!courses || courses.length === 0) {
|
||||
alert("No courses available");
|
||||
}
|
||||
}, [courses]);
|
||||
|
||||
if (!courses) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<table className="course-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CourseID</th>
|
||||
<th>Course Name</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{courses.map(course => (
|
||||
<tr key={course.id}>
|
||||
<td>{course.courseId}</td>
|
||||
<td>{course.name}</td>
|
||||
<td>{course.status}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
export default CourseTable;
|
||||
@@ -1,67 +1,101 @@
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import "./FilterPage.css";
|
||||
import { fetchCourses } from "../api";
|
||||
|
||||
const FilterPage = ({ onApplyFilter }) => {
|
||||
const [selectedOption, setSelectedOption] = useState(null);
|
||||
const FilterPage = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
scheme: "",
|
||||
year: "",
|
||||
semester: "",
|
||||
department: "",
|
||||
program: "",
|
||||
});
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({ ...formData, [name]: value });
|
||||
|
||||
// Reset semester if program changes
|
||||
if (name === "program") {
|
||||
setFormData((prevData) => ({ ...prevData, semester: "" }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleApplyFilter = () => {
|
||||
if (!formData.scheme || !formData.year) {
|
||||
alert("Please select both Scheme and Year before applying the filter.");
|
||||
const handleApplyFilter = async () => {
|
||||
if (!formData.scheme || !formData.semester || !formData.department || !formData.program) {
|
||||
alert("Please fill all the fields before applying the filter.");
|
||||
return;
|
||||
}
|
||||
if (!selectedOption) {
|
||||
alert("Please select either 'Course' or 'Faculty' before applying the filter.");
|
||||
return;
|
||||
|
||||
try {
|
||||
const filteredCourses = await fetchCourses(formData);
|
||||
console.log(formData);
|
||||
if (filteredCourses.length > 0) {
|
||||
navigate("/courses", { state: { courses: filteredCourses } });
|
||||
} else {
|
||||
alert("No courses found for the selected filters.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching courses:", error);
|
||||
alert("Failed to fetch courses. Please try again later.");
|
||||
}
|
||||
onApplyFilter(selectedOption); // Pass the selected option to the parent
|
||||
};
|
||||
|
||||
const handleSelectOption = (option) => {
|
||||
setSelectedOption(option);
|
||||
const getSemesters = () => {
|
||||
if (!formData.program) return [];
|
||||
if (formData.program === "B.Tech") {
|
||||
return Array.from({ length: 8 }, (_, i) => i + 1); // 1 to 8
|
||||
}
|
||||
if (formData.program === "M.Tech") {
|
||||
return Array.from({ length: 4 }, (_, i) => i + 1); // 1 to 4
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="filter-container">
|
||||
<div className="filter-form">
|
||||
<select
|
||||
name="department"
|
||||
value={formData.department}
|
||||
onChange={handleInputChange}
|
||||
>
|
||||
<option value="">Select Department</option>
|
||||
<option value="COMPS">COMPS</option>
|
||||
<option value="IT">IT</option>
|
||||
</select>
|
||||
<select
|
||||
name="program"
|
||||
value={formData.program}
|
||||
onChange={handleInputChange}
|
||||
>
|
||||
<option value="">Select Program</option>
|
||||
<option value="B.Tech">B.Tech</option>
|
||||
<option value="M.Tech">M.Tech</option>
|
||||
</select>
|
||||
<select
|
||||
name="semester"
|
||||
value={formData.semester}
|
||||
onChange={handleInputChange}
|
||||
>
|
||||
<option value="">Select Semester</option>
|
||||
{getSemesters().map((sem) => (
|
||||
<option key={sem} value={sem}>
|
||||
Semester {sem}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
name="scheme"
|
||||
value={formData.scheme}
|
||||
onChange={handleInputChange}
|
||||
>
|
||||
<option value="">Select Scheme</option>
|
||||
<option value="Scheme1">Scheme 1</option>
|
||||
<option value="Scheme2">Scheme 2</option>
|
||||
<option value="2020">SVU 2020</option>
|
||||
<option value="2023">SVU 2023</option>
|
||||
</select>
|
||||
<select
|
||||
name="year"
|
||||
value={formData.year}
|
||||
onChange={handleInputChange}
|
||||
>
|
||||
<option value="">Select Year</option>
|
||||
<option value="1">1st Year</option>
|
||||
<option value="2">2nd Year</option>
|
||||
</select>
|
||||
<button
|
||||
className={selectedOption === "Faculty" ? "active-button" : ""}
|
||||
onClick={() => handleSelectOption("Faculty")}
|
||||
>
|
||||
Faculty
|
||||
</button>
|
||||
<button
|
||||
className={selectedOption === "Course" ? "active-button" : ""}
|
||||
onClick={() => handleSelectOption("Course")}
|
||||
>
|
||||
Course
|
||||
</button>
|
||||
<button onClick={handleApplyFilter}>Apply Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user