diff --git a/client/src/App.js b/client/src/App.js index d4058f6..5fa4542 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -16,6 +16,8 @@ import PrivateRoute from "./components/PrivateRoute"; import TokenRefresher from "./components/TokenRefresher"; import DepartmentConsolidated from "./Pages/DepartmentConsolidated"; import PanelConsolidated from "./Pages/PanelConsolidated"; +import { AdminFacultyPage } from "./Pages/AdminFacultyPage"; + function App() { return ( @@ -34,6 +36,7 @@ function App() { } />} /> } />} /> } />} /> + } />} /> > ); diff --git a/client/src/Pages/AdminFacultyPage.jsx b/client/src/Pages/AdminFacultyPage.jsx new file mode 100644 index 0000000..a87fd65 --- /dev/null +++ b/client/src/Pages/AdminFacultyPage.jsx @@ -0,0 +1,378 @@ +import React, { useState, useEffect } from 'react'; +import axios from 'axios'; +import Navbar from './Navbar'; +import { toast, ToastContainer } from "react-toastify"; +import 'react-toastify/dist/ReactToastify.css'; + + +export const AdminFacultyPage = () => { + const [faculties, setFaculties] = useState([]); + const [showForm, setShowForm] = useState(false); + const [isEditMode, setIsEditMode] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [currentFaculty, setCurrentFaculty] = useState({ + facultyId: '', + name: '', + email: '', + department: '', + program: '', + courses: [''], + }); + + // Fetch all faculties + useEffect(() => { + fetchFaculties(); + }, []); + + const fetchFaculties = async () => { + const res = await axios.get('http://localhost:8080/api/faculty'); + setFaculties(res.data); + }; + + // Add or Update Faculty + const handleAddOrUpdateFaculty = async (e) => { + e.preventDefault(); + + if (isEditMode) { + // Update Faculty (PUT request) + await axios.put(`http://localhost:8080/api/faculty/${currentFaculty._id}`, currentFaculty); + toast.success('Faculty updated successfully!', { position: 'top-center' }); + } else { + // Add Faculty (POST request) + await axios.post('http://localhost:8080/api/faculty', currentFaculty); + toast.success('Faculty added successfully!', { position: 'top-center' }); + } + + // Reset form and refresh data + resetForm(); + fetchFaculties(); + }; + + // Delete Faculty + const handleDeleteFaculty = async (id) => { + const confirm = window.confirm('⚠️ Are you sure you want to delete this faculty?'); + if (confirm) { + await axios.delete(`http://localhost:8080/api/faculty/${id}`); + fetchFaculties(); + toast.success('Faculty deleted successfully!', { position: 'top-center' }); + } + }; + + // Open Form for Edit + const handleOpenEditForm = (faculty) => { + setCurrentFaculty(faculty); + setIsEditMode(true); + setShowForm(true); + }; + + // Open Form for Add + const handleOpenAddForm = () => { + resetForm(); + setIsEditMode(false); + setShowForm(true); + }; + + // Handle Course Input Change + const handleCourseChange = (index, value) => { + const newCourses = [...currentFaculty.courses]; + newCourses[index] = value; + setCurrentFaculty({ ...currentFaculty, courses: newCourses }); + }; + + // Add a new course field + const handleAddCourse = () => { + setCurrentFaculty({ + ...currentFaculty, + courses: [...currentFaculty.courses, ''], + }); + }; + + // Remove a course field + const handleRemoveCourse = (index) => { + const newCourses = currentFaculty.courses.filter((_, i) => i !== index); + setCurrentFaculty({ ...currentFaculty, courses: newCourses }); + }; + + // Reset form + const resetForm = () => { + setCurrentFaculty({ + facultyId: '', + name: '', + email: '', + department: '', + program: '', + courses: [''], + }); + setShowForm(false); + }; + + const filteredFaculties = faculties.filter((faculty) => + faculty.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( + <> + + + Faculty Management + + + setSearchQuery(e.target.value)} + style={{ + padding: '10px', + width: '300px', + borderRadius: '5px', + border: '1px solid #ddd', + outline: 'none', + fontSize: '16px', + }} + /> + (e.target.style.backgroundColor = '#800000')} + onMouseLeave={(e) => (e.target.style.backgroundColor = '#4CAF50')}> + + Add New Faculty + + + {showForm && ( + + {isEditMode ? 'Edit Faculty' : 'Add New Faculty'} + + + setCurrentFaculty({ ...currentFaculty, facultyId: e.target.value })} + style={inputStyle} + /> + setCurrentFaculty({ ...currentFaculty, name: e.target.value })} + style={inputStyle} + /> + + + + setCurrentFaculty({ ...currentFaculty, email: e.target.value })} + style={inputStyle} + /> + setCurrentFaculty({ ...currentFaculty, department: e.target.value })} + style={inputStyle} + /> + + + + setCurrentFaculty({ ...currentFaculty, program: e.target.value })} + style={inputStyle} + /> + + + Courses: + {currentFaculty.courses.map((course, index) => ( + + handleCourseChange(index, e.target.value)} + style={{ ...inputStyle, flex: 1 }} + /> + handleRemoveCourse(index)} style={removeButtonStyle}>❌ + + ))} + + Add Course + + + + {isEditMode ? 'Update Faculty' : 'Add Faculty'} + + setShowForm(false)} style={cancelButtonStyle}>Cancel + + + + )} + + {/* TABLE DATA */} + + + + Faculty ID + Name + Email + Department + Program + Courses + Actions + + + + {filteredFaculties.length > 0 ? ( + filteredFaculties.map((f) => ( + + {f.facultyId} + {f.name} + {f.email} + {f.department} + {f.program} + {f.courses.join(', ')} + + (e.target.style.backgroundColor = '#2e7d32')} + onMouseLeave={(e) => (e.target.style.backgroundColor = '#4CAF50')} + onClick={() => handleOpenEditForm(f)}>Edit + (e.target.style.backgroundColor = '#d32f2f')} + onMouseLeave={(e) => (e.target.style.backgroundColor = '#f44336')} + onClick={() => handleDeleteFaculty(f._id)}>Delete + + + )) + ) : ( + + + No faculty found + + + )} + + + > + ); +}; + + +const tableStyle = { + width: '100%', + borderCollapse: 'collapse', + marginTop: '20px', +}; + +const formStyle = { + backgroundColor: '#f9f9f9', + padding: '20px', + borderRadius: '10px', + boxShadow: '0 5px 20px rgba(0, 0, 0, 0.1)', + maxWidth: '700px', + margin: '0 auto', +}; + +const inputContainer = { + display: 'flex', + gap: '10px', + marginBottom: '10px', +}; + +const inputStyle = { + flex: 1, + padding: '10px 15px', + border: '1px solid #ddd', + borderRadius: '5px', + fontSize: '14px', +}; + +const courseContainerStyle = { + display: 'flex', + alignItems: 'center', + gap: '10px', + marginBottom: '8px', +}; + +const removeButtonStyle = { + backgroundColor: '#ffffff', + color: 'white', + border: 'none', + padding: '5px 10px', + borderRadius: '5px', + cursor: 'pointer', + fontSize: '14px', + transition: '0.3s', +}; + +const addCourseButtonStyle = { + backgroundColor: '#4CAF50', + color: 'white', + border: 'none', + padding: '8px 15px', + borderRadius: '5px', + cursor: 'pointer', + transition: '0.3s', +}; + +const formButtonContainer = { + display: 'flex', + justifyContent: 'space-between', + marginTop: '20px', +}; + +const submitButtonStyle = { + backgroundColor: '#4CAF50', + color: 'white', + border: 'none', + padding: '10px 20px', + borderRadius: '5px', + cursor: 'pointer', + fontSize: '14px', +}; + +const cancelButtonStyle = { + backgroundColor: '#f44336', + color: 'white', + border: 'none', + padding: '10px 20px', + borderRadius: '5px', + cursor: 'pointer', + fontSize: '14px', +}; + +const subHeadingStyle = { + fontSize: '18px', + marginBottom: '10px', +}; diff --git a/server/routes/facultyRoutes.js b/server/routes/facultyRoutes.js index 8ea910c..753a952 100644 --- a/server/routes/facultyRoutes.js +++ b/server/routes/facultyRoutes.js @@ -28,4 +28,61 @@ router.get("/:id", async (req, res) => { } }); +// Add new faculty +router.post("/", async (req, res) => { + const { facultyId, name, email, department, program, courses } = req.body; + try { + const newFaculty = new Faculty({ + facultyId, + name, + email, + department, + program, + courses, + }); + await newFaculty.save(); + res.status(201).json(newFaculty); + } catch (error) { + console.error("Error adding new faculty:", error.message); + res.status(500).json({ error: "Failed to add faculty member" }); + } +}); + +//Update faculty + +router.put("/:id", async (req, res) => { + try { + const updatedFaculty = await Faculty.findByIdAndUpdate( + req.params.id, + req.body, + { new: true } + ); + + if (!updatedFaculty) { + return res.status(404).json({ error: "Faculty member not found" }); + } + + res.status(200).json(updatedFaculty); + } catch (error) { + console.error("Error updating faculty member:", error.message); + res.status(500).json({ error: "Failed to update faculty member" }); + } +}); + +// Delete faculty by ID +router.delete("/:id", async (req, res) => { + try { + const deletedFaculty = await Faculty.findByIdAndDelete(req.params.id); + if (!deletedFaculty) { + return res.status(404).json({ error: "Faculty member not found" }); + } + + res.status(200).json({ message: "Faculty member deleted successfully" }); + } catch (error) { + console.error("Error deleting faculty member:", error.message); + res.status(500).json({ error: "Failed to delete faculty member" }); + } +}); + + module.exports = router;