Admin page - faculty management
This commit is contained in:
@@ -16,6 +16,8 @@ import PrivateRoute from "./components/PrivateRoute";
|
|||||||
import TokenRefresher from "./components/TokenRefresher";
|
import TokenRefresher from "./components/TokenRefresher";
|
||||||
import DepartmentConsolidated from "./Pages/DepartmentConsolidated";
|
import DepartmentConsolidated from "./Pages/DepartmentConsolidated";
|
||||||
import PanelConsolidated from "./Pages/PanelConsolidated";
|
import PanelConsolidated from "./Pages/PanelConsolidated";
|
||||||
|
import { AdminFacultyPage } from "./Pages/AdminFacultyPage";
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -34,6 +36,7 @@ function App() {
|
|||||||
<Route path="/courseConsolidated" element={<PrivateRoute element={<CourseConsolidated />} />} />
|
<Route path="/courseConsolidated" element={<PrivateRoute element={<CourseConsolidated />} />} />
|
||||||
<Route path="/departmentConsolidated" element={<PrivateRoute element={<DepartmentConsolidated />} />} />
|
<Route path="/departmentConsolidated" element={<PrivateRoute element={<DepartmentConsolidated />} />} />
|
||||||
<Route path="/panelConsolidated" element={<PrivateRoute element={<PanelConsolidated/>} />} />
|
<Route path="/panelConsolidated" element={<PrivateRoute element={<PanelConsolidated/>} />} />
|
||||||
|
<Route path="/AdminFacultyPage" element={<PrivateRoute element={<AdminFacultyPage/>} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
378
client/src/Pages/AdminFacultyPage.jsx
Normal file
378
client/src/Pages/AdminFacultyPage.jsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<Navbar />
|
||||||
|
<div style={{ fontFamily: 'Arial, sans-serif', backgroundColor: '#f4f6f9', padding: '20px' }}>
|
||||||
|
<h2 style={{ textAlign: 'center', color: '#333', marginBottom: '20px' }}>Faculty Management</h2>
|
||||||
|
|
||||||
|
<div style={{ textAlign: 'center', marginBottom: '20px' }}></div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search faculty by name..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
style={{
|
||||||
|
padding: '10px',
|
||||||
|
width: '300px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
border: '1px solid #ddd',
|
||||||
|
outline: 'none',
|
||||||
|
fontSize: '16px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button onClick={handleOpenAddForm} style={{
|
||||||
|
backgroundColor: '#4CAF50',
|
||||||
|
color: '#fff',
|
||||||
|
padding: '10px 15px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
marginLeft: '10px',
|
||||||
|
border: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
transition: '0.3s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => (e.target.style.backgroundColor = '#800000')}
|
||||||
|
onMouseLeave={(e) => (e.target.style.backgroundColor = '#4CAF50')}>
|
||||||
|
+ Add New Faculty</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showForm && (
|
||||||
|
<div style={{
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: '10px',
|
||||||
|
padding: '20px',
|
||||||
|
boxShadow: '0 5px 20px rgba(0,0,0,0.1)',
|
||||||
|
marginBottom: '20px',
|
||||||
|
}}>
|
||||||
|
<h3>{isEditMode ? 'Edit Faculty' : 'Add New Faculty'}</h3>
|
||||||
|
<form onSubmit={handleAddOrUpdateFaculty} style={formStyle}>
|
||||||
|
<div style={inputContainer}>
|
||||||
|
<input
|
||||||
|
placeholder="Faculty ID"
|
||||||
|
value={currentFaculty.facultyId}
|
||||||
|
onChange={(e) => setCurrentFaculty({ ...currentFaculty, facultyId: e.target.value })}
|
||||||
|
style={inputStyle}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
placeholder="Name"
|
||||||
|
value={currentFaculty.name}
|
||||||
|
onChange={(e) => setCurrentFaculty({ ...currentFaculty, name: e.target.value })}
|
||||||
|
style={inputStyle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={inputContainer}>
|
||||||
|
<input
|
||||||
|
|
||||||
|
placeholder="Email"
|
||||||
|
value={currentFaculty.email}
|
||||||
|
onChange={(e) => setCurrentFaculty({ ...currentFaculty, email: e.target.value })}
|
||||||
|
style={inputStyle}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
|
||||||
|
placeholder="Department"
|
||||||
|
value={currentFaculty.department}
|
||||||
|
onChange={(e) => setCurrentFaculty({ ...currentFaculty, department: e.target.value })}
|
||||||
|
style={inputStyle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={inputContainer}>
|
||||||
|
<input
|
||||||
|
placeholder="Program"
|
||||||
|
value={currentFaculty.program}
|
||||||
|
onChange={(e) => setCurrentFaculty({ ...currentFaculty, program: e.target.value })}
|
||||||
|
style={inputStyle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 style={subHeadingStyle}>Courses:</h4>
|
||||||
|
{currentFaculty.courses.map((course, index) => (
|
||||||
|
<div key={index} style={courseContainerStyle}>
|
||||||
|
<input
|
||||||
|
|
||||||
|
placeholder="Course Name"
|
||||||
|
value={course}
|
||||||
|
onChange={(e) => handleCourseChange(index, e.target.value)}
|
||||||
|
style={{ ...inputStyle, flex: 1 }}
|
||||||
|
/>
|
||||||
|
<button type="button" onClick={() => handleRemoveCourse(index)} style={removeButtonStyle}>❌</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<button type="button" onClick={handleAddCourse} style={addCourseButtonStyle}>+ Add Course</button>
|
||||||
|
|
||||||
|
<div style={formButtonContainer}>
|
||||||
|
<button type="submit" style={submitButtonStyle}>
|
||||||
|
{isEditMode ? 'Update Faculty' : 'Add Faculty'}
|
||||||
|
</button>
|
||||||
|
<button type="button" onClick={() => setShowForm(false)} style={cancelButtonStyle}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* TABLE DATA */}
|
||||||
|
<table style={tableStyle}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Faculty ID</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Department</th>
|
||||||
|
<th>Program</th>
|
||||||
|
<th>Courses</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{filteredFaculties.length > 0 ? (
|
||||||
|
filteredFaculties.map((f) => (
|
||||||
|
<tr key={f._id}>
|
||||||
|
<td>{f.facultyId}</td>
|
||||||
|
<td>{f.name}</td>
|
||||||
|
<td>{f.email}</td>
|
||||||
|
<td>{f.department}</td>
|
||||||
|
<td>{f.program}</td>
|
||||||
|
<td>{f.courses.join(', ')}</td>
|
||||||
|
<td>
|
||||||
|
<button style={{
|
||||||
|
backgroundColor: '#4CAF50',
|
||||||
|
color: 'white',
|
||||||
|
padding: '8px 12px',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '5px',
|
||||||
|
marginRight: '10px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
transition: '0.3s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => (e.target.style.backgroundColor = '#2e7d32')}
|
||||||
|
onMouseLeave={(e) => (e.target.style.backgroundColor = '#4CAF50')}
|
||||||
|
onClick={() => handleOpenEditForm(f)}>Edit</button>
|
||||||
|
<button style={{
|
||||||
|
backgroundColor: '#f44336',
|
||||||
|
color: 'white',
|
||||||
|
padding: '8px 12px',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '5px',
|
||||||
|
marginLeft: '10px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
transition: '0.3s',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => (e.target.style.backgroundColor = '#d32f2f')}
|
||||||
|
onMouseLeave={(e) => (e.target.style.backgroundColor = '#f44336')}
|
||||||
|
onClick={() => handleDeleteFaculty(f._id)}>Delete</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan="7" style={{ textAlign: 'center', color: 'red' }}>
|
||||||
|
No faculty found
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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',
|
||||||
|
};
|
||||||
@@ -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;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user