From 5b875a20827a1186022fe112188e5b4a7370e7bc Mon Sep 17 00:00:00 2001 From: fatima ezzahra majidi <fatima-ezzahra.majidi.etu@univ-lille.fr> Date: Fri, 7 Mar 2025 04:35:48 +0000 Subject: [PATCH 1/2] ajout de roles --- src/App.js | 5 + src/components/CandidacyForm.jsx | 2 +- src/components/CandidacyList.jsx | 2 +- src/components/EnterpriseForm.jsx | 2 +- src/components/EnterpriseList.jsx | 2 +- src/components/Login.jsx | 73 ++++++++++++ src/components/Logout.jsx | 36 ++++++ src/components/Navbar.jsx | 78 +++++++++---- src/components/StageForm.jsx | 119 ++++++++++++++------ src/components/StudentForm.jsx | 12 +- src/components/UnivSupervisorForm.jsx | 2 +- src/components/UpdateUnivSupervisorForm.jsx | 2 +- 12 files changed, 269 insertions(+), 66 deletions(-) create mode 100644 src/components/Login.jsx create mode 100644 src/components/Logout.jsx diff --git a/src/App.js b/src/App.js index d40c687..e1ae369 100644 --- a/src/App.js +++ b/src/App.js @@ -20,6 +20,8 @@ import UpdateEnterpriseForm from "./components/UpdateEnterpriseForm.jsx"; import UpdateStageForm from "./components/UpdateStageForm.jsx"; import UpdateStudentForm from "./components/UpdateStudentForm.jsx"; import UpdateUnivSupervisorForm from "./components/UpdateUnivSupervisorForm.jsx"; +import Login from "./components/Login.jsx"; +import Logout from "./components/Logout.jsx"; function App() { return ( @@ -32,6 +34,9 @@ function App() { <Route path="/entreprises" element={<EnterpriseList />} /> <Route path="/candidatures" element={<CandidacyList />} /> <Route path="/superviseurs" element={<UnivSupervisorList />} /> + <Route path="/login" element={<Login />} /> + <Route path="/logout" element={<Logout />} /> + {/* Routes for Forms */} <Route path="/ajouter-stage" element={<StageForm />} /> diff --git a/src/components/CandidacyForm.jsx b/src/components/CandidacyForm.jsx index 6a7bb6a..79953db 100644 --- a/src/components/CandidacyForm.jsx +++ b/src/components/CandidacyForm.jsx @@ -33,7 +33,7 @@ const CandidacyForm = () => { const handleSubmit = (e) => { e.preventDefault(); - fetch("http://localhost:8080/api/candidacies", { + fetch("http://localhost:8080/api/candidacies/add", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ diff --git a/src/components/CandidacyList.jsx b/src/components/CandidacyList.jsx index cfc2733..1e39ddb 100644 --- a/src/components/CandidacyList.jsx +++ b/src/components/CandidacyList.jsx @@ -15,7 +15,7 @@ function CandidacyList() { // Function to delete candidacy const handleDelete = (id) => { if (window.confirm("Êtes-vous sûr de vouloir supprimer cette candidature ?")) { - fetch(`http://localhost:8080/api/candidacies/${id}`, { method: "DELETE" }) + fetch(`http://localhost:8080/api/candidacies/delete/${id}`, { method: "DELETE" }) .then(() => { alert("Candidature supprimée avec succès !"); setCandidacies(candidacies.filter((candidacy) => candidacy.id !== id)); diff --git a/src/components/EnterpriseForm.jsx b/src/components/EnterpriseForm.jsx index 08249a2..01a66f4 100644 --- a/src/components/EnterpriseForm.jsx +++ b/src/components/EnterpriseForm.jsx @@ -10,7 +10,7 @@ function EnterpriseForm() { const newEnterprise = { name, address }; - fetch("http://localhost:8080/api/enterprises", { + fetch("http://localhost:8080/api/enterprises/add", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newEnterprise) diff --git a/src/components/EnterpriseList.jsx b/src/components/EnterpriseList.jsx index 3546fb2..6bcdfcb 100644 --- a/src/components/EnterpriseList.jsx +++ b/src/components/EnterpriseList.jsx @@ -20,7 +20,7 @@ function EnterpriseList() { // Handle Delete const handleDelete = (id) => { if (window.confirm("Voulez-vous vraiment supprimer cette entreprise ?")) { - fetch(`http://localhost:8080/api/enterprises/${id}`, { + fetch(`http://localhost:8080/api/enterprises/delete/${id}`, { method: "DELETE", }) .then(() => { diff --git a/src/components/Login.jsx b/src/components/Login.jsx new file mode 100644 index 0000000..d6eb8c5 --- /dev/null +++ b/src/components/Login.jsx @@ -0,0 +1,73 @@ +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; + +const Login = () => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + const navigate = useNavigate(); + + useEffect(() => { + const token = localStorage.getItem("token"); + if (token) { + navigate("/"); // Redirect if already logged in + } + }, [navigate]); + + const handleLogin = async (e) => { + e.preventDefault(); + + try { + const response = await fetch("http://localhost:8080/api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, password }), + }); + + if (!response.ok) { + throw new Error("Invalid credentials"); + } + + const data = await response.text(); // Adjust this if backend returns JSON + console.log("✅ Login successful!", data); + + localStorage.setItem("token", "dummy-token"); // Store token (if applicable) + navigate("/"); // Redirect after login + } catch (error) { + console.error("❌ Login failed:", error.message); + setError("Invalid username or password"); + } + }; + + return ( + <div style={{ textAlign: "center", marginTop: "50px" }}> + <h2>Login</h2> + <form onSubmit={handleLogin}> + <div> + <input + type="text" + placeholder="Username" + value={username} + onChange={(e) => setUsername(e.target.value)} + required + /> + </div> + <div> + <input + type="password" + placeholder="Password" + value={password} + onChange={(e) => setPassword(e.target.value)} + required + /> + </div> + {error && <p style={{ color: "red" }}>{error}</p>} + <button type="submit">Login</button> + </form> + </div> + ); +}; + +export default Login; diff --git a/src/components/Logout.jsx b/src/components/Logout.jsx new file mode 100644 index 0000000..fb00d82 --- /dev/null +++ b/src/components/Logout.jsx @@ -0,0 +1,36 @@ +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; + +const Logout = () => { + const navigate = useNavigate(); + + useEffect(() => { + const logoutUser = async () => { + try { + const response = await fetch("http://localhost:8080/api/auth/logout", { + method: "POST", + credentials: "include", // Ensures cookies are sent and removed + }); + + if (response.ok) { + console.log("✅ Logout successful"); + } else { + console.error("❌ Logout request failed:", response.status); + } + } catch (error) { + console.error("❌ Error during logout:", error); + } + + // Clear stored auth details and redirect + localStorage.removeItem("token"); + localStorage.removeItem("user"); + navigate("/login"); + }; + + logoutUser(); + }, [navigate]); + + return <h2>Logging out... Redirecting</h2>; +}; + +export default Logout; diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 9f0dbc6..4274267 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -19,38 +19,70 @@ function Navbar() { </button> <div className="collapse navbar-collapse" id="navbarNav"> <ul className="navbar-nav ms-auto"> - <li className="nav-item"> - <Link className="nav-link" to="/login">🔑 Login</Link> - </li> - <li className="nav-item"> - <Link className="nav-link" to="/stages">📚 Stages</Link> - </li> - <li className="nav-item"> - <Link className="nav-link" to="/etudiants">👨🎓 Étudiants</Link> + + {/* Stages Dropdown */} + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="stagesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 📚 Stages + </a> + <ul className="dropdown-menu" aria-labelledby="stagesDropdown"> + <li><Link className="dropdown-item" to="/stages">📜 Liste des Stages</Link></li> + <li><Link className="dropdown-item" to="/ajouter-stage">➕ Ajouter un Stage</Link></li> + </ul> </li> - <li className="nav-item"> - <Link className="nav-link" to="/entreprises">🏢 Entreprises</Link> + + {/* Étudiants Dropdown */} + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="etudiantsDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 👨🎓 Étudiants + </a> + <ul className="dropdown-menu" aria-labelledby="etudiantsDropdown"> + <li><Link className="dropdown-item" to="/etudiants">📜 Liste des Étudiants</Link></li> + <li><Link className="dropdown-item" to="/ajouter-etudiant">➕ Ajouter un Étudiant</Link></li> + </ul> </li> - <li className="nav-item"> - <Link className="nav-link" to="/candidatures">📝 Candidatures</Link> + + {/* Entreprises Dropdown */} + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="entreprisesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 🏢 Entreprises + </a> + <ul className="dropdown-menu" aria-labelledby="entreprisesDropdown"> + <li><Link className="dropdown-item" to="/entreprises">📜 Liste des Entreprises</Link></li> + <li><Link className="dropdown-item" to="/ajouter-entreprise">➕ Ajouter une Entreprise</Link></li> + </ul> </li> - <li className="nav-item"> - <Link className="nav-link" to="/superviseurs">👨🏫 Superviseurs</Link> + + {/* Candidatures Dropdown */} + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="candidaturesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 📝 Candidatures + </a> + <ul className="dropdown-menu" aria-labelledby="candidaturesDropdown"> + <li><Link className="dropdown-item" to="/candidatures">📜 Liste des Candidatures</Link></li> + <li><Link className="dropdown-item" to="/ajouter-candidature">➕ Ajouter une Candidature</Link></li> + </ul> </li> - {/* Dropdown for adding new entries */} + {/* Superviseurs Dropdown */} <li className="nav-item dropdown"> - <a className="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> - ➕ Ajouter + <a className="nav-link dropdown-toggle" href="#" id="superviseursDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 👨🏫 Superviseurs </a> - <ul className="dropdown-menu" aria-labelledby="navbarDropdown"> - <li><Link className="dropdown-item" to="/ajouter-stage">Ajouter Stage</Link></li> - <li><Link className="dropdown-item" to="/ajouter-entreprise">Ajouter Entreprise</Link></li> - <li><Link className="dropdown-item" to="/ajouter-etudiant">Ajouter Étudiant</Link></li> - <li><Link className="dropdown-item" to="/ajouter-candidature">Ajouter Candidature</Link></li> - <li><Link className="dropdown-item" to="/ajouter-univsuperviseur">Ajouter Superviseur</Link></li> + <ul className="dropdown-menu" aria-labelledby="superviseursDropdown"> + <li><Link className="dropdown-item" to="/superviseurs">📜 Liste des Superviseurs</Link></li> + <li><Link className="dropdown-item" to="/ajouter-univsuperviseur">➕ Ajouter un Superviseur</Link></li> </ul> </li> + + {/* Authentication Links */} + <li className="nav-item"> + <Link className="nav-link" to="/login">🔑 Login</Link> + </li> + <li className="nav-item"> + <Link className="nav-link" to="/logout">🔓 Logout</Link> + </li> + </ul> </div> </div> diff --git a/src/components/StageForm.jsx b/src/components/StageForm.jsx index 07f669f..ce4841c 100644 --- a/src/components/StageForm.jsx +++ b/src/components/StageForm.jsx @@ -1,45 +1,79 @@ import React, { useState, useEffect } from "react"; -import { Navigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; function StageForm() { - const [name, setName] = useState(""); + const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); - const [enterpriseId, setEnterpriseId] = useState(""); - const [enterprises, setEnterprises] = useState([]); + const [enterpriseId, setEnterpriseId] = useState(""); // Stores selected enterprise ID + const [enterprises, setEnterprises] = useState([]); // List of enterprises for dropdown + const navigate = useNavigate(); + /** Fetch the list of enterprises from the backend **/ useEffect(() => { - fetch("http://localhost:8080/api/enterprises") + fetch("http://localhost:8080/api/enterprises", { + method: "GET", + headers: { "Content-Type": "application/json" }, + credentials: "include", + }) .then((res) => res.json()) - .then((data) => setEnterprises(data)) - .catch((err) => console.error("Error fetching enterprises:", err)); + .then((data) => { + console.log("📌 Enterprises loaded:", data); + setEnterprises(data); + }) + .catch((err) => console.error("❌ Erreur lors du chargement des entreprises :", err)); }, []); + /** Handle form submission **/ const handleSubmit = (e) => { e.preventDefault(); - + + // Ensure enterpriseId is selected + if (!enterpriseId || enterpriseId === "none") { + alert("❌ Veuillez sélectionner une entreprise !"); + return; + } + const newStage = { - name, + title, description, - enterprise: { id: enterpriseId } + enterprise: { id: parseInt(enterpriseId, 10) }, // Send enterprise as an object }; - - fetch("http://localhost:8080/api/stages", { + + console.log("📌 Sending stage data:", newStage); + + fetch("http://localhost:8080/api/stages/add", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(newStage) + credentials: "include", + body: JSON.stringify(newStage), // Ensure correct format }) - .then((res) => res.json()) - .then(() => { - alert("Stage added successfully!"); - setName(""); + .then((res) => { + console.log("📌 Response status:", res.status); + + if (!res.ok) { + return res.text().then((text) => { + console.error("❌ Backend Error (text):", text); + throw new Error(text || "Erreur inconnue"); + }); + } + + return res.json(); + }) + .then((data) => { + console.log("✅ Stage added successfully:", data); + alert("Stage ajouté avec succès !"); + setTitle(""); setDescription(""); - setEnterpriseId(""); + setEnterpriseId(""); // Reset dropdown + navigate("/stages"); }) - .catch((err) => console.error("Error adding stage:", err)); + .catch((err) => console.error("❌ Erreur lors de l'ajout du stage :", err.message)); }; - // Handle cancel button - const handleCancel = () => { - Navigate("/stages"); // Redirect back to candidacy list + + + /** Handle cancel button **/ + const handleCancel = () => { + navigate("/stages"); // Redirect back to stages list }; return ( @@ -47,28 +81,49 @@ function StageForm() { <h2>Ajouter un Stage</h2> <form onSubmit={handleSubmit}> <div className="mb-3"> - <label className="form-label">Nom du Stage</label> - <input type="text" className="form-control" value={name} onChange={(e) => setName(e.target.value)} required /> + <label className="form-label">Titre du Stage</label> + <input + type="text" + className="form-control" + value={title} + onChange={(e) => setTitle(e.target.value)} + required + /> </div> <div className="mb-3"> <label className="form-label">Description</label> - <input type="text" className="form-control" value={description} onChange={(e) => setDescription(e.target.value)} required /> + <textarea + className="form-control" + value={description} + onChange={(e) => setDescription(e.target.value)} + required + ></textarea> </div> <div className="mb-3"> <label className="form-label">Entreprise</label> - <select className="form-select" value={enterpriseId} onChange={(e) => setEnterpriseId(e.target.value)} required> - <option value="">Sélectionner une entreprise</option> + <select + className="form-control" + value={enterpriseId} + onChange={(e) => setEnterpriseId(e.target.value)} + required + > + <option value="none">Sélectionnez une entreprise</option> {enterprises.map((enterprise) => ( - <option key={enterprise.id} value={enterprise.id}>{enterprise.name}</option> + <option key={enterprise.id} value={enterprise.id}> + {enterprise.name} + </option> ))} </select> </div> <div className="d-flex gap-2"> - <button type="submit" className="btn btn-primary"> + <button type="submit" className="btn btn-primary"> Ajouter - </button> - <button type="button" className="btn btn-secondary" onClick={handleCancel}>Annuler</button> - </div> </form> + </button> + <button type="button" className="btn btn-secondary" onClick={handleCancel}> + Annuler + </button> + </div> + </form> </div> ); } diff --git a/src/components/StudentForm.jsx b/src/components/StudentForm.jsx index 1332107..b6977f1 100644 --- a/src/components/StudentForm.jsx +++ b/src/components/StudentForm.jsx @@ -1,16 +1,18 @@ import React, { useState } from "react"; -import { Navigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; function StudentForm() { const [firstname, setFirstname] = useState(""); const [lastname, setLastname] = useState(""); + const navigate = useNavigate(); // Correct use of useNavigate + const handleSubmit = (e) => { e.preventDefault(); const newStudent = { firstname, lastname }; - fetch("http://localhost:8080/api/students", { + fetch("http://localhost:8080/api/students/add", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newStudent) @@ -23,9 +25,9 @@ function StudentForm() { }) .catch((err) => console.error("Erreur lors de l'ajout de l'étudiant :", err)); }; - // Handle cancel button - const handleCancel = () => { - Navigate("/etudiants"); // Redirect back to candidacy list + // Handle cancel button correctly + const handleCancel = () => { + navigate("/etudiants"); // Redirect back to student list }; return ( diff --git a/src/components/UnivSupervisorForm.jsx b/src/components/UnivSupervisorForm.jsx index 03c2fab..eb78757 100644 --- a/src/components/UnivSupervisorForm.jsx +++ b/src/components/UnivSupervisorForm.jsx @@ -9,7 +9,7 @@ function UnivSupervisorForm() { const newSupervisor = { name }; - fetch("http://localhost:8080/api/univsupervisors", { + fetch("http://localhost:8080/api/univsupervisors/add", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(newSupervisor), diff --git a/src/components/UpdateUnivSupervisorForm.jsx b/src/components/UpdateUnivSupervisorForm.jsx index f86fb81..20df9cc 100644 --- a/src/components/UpdateUnivSupervisorForm.jsx +++ b/src/components/UpdateUnivSupervisorForm.jsx @@ -22,7 +22,7 @@ function UpdateUnivSupervisorForm() { const handleSubmit = (e) => { e.preventDefault(); - fetch(`http://localhost:8080/api/univsupervisors/${id}`, { + fetch(`http://localhost:8080/api/univsupervisors/update/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(supervisor), -- GitLab From b843bd7f266a2cfc9b2e8af55545df296f3aa9de Mon Sep 17 00:00:00 2001 From: fatima ezzahra majidi <fatima-ezzahra.majidi.etu@univ-lille.fr> Date: Fri, 7 Mar 2025 06:19:37 +0000 Subject: [PATCH 2/2] ajout des roles a la navbar --- src/App.js | 32 ++-- .../{ => Candidatures}/CandidacyForm.jsx | 0 .../{ => Candidatures}/CandidacyList.jsx | 0 .../UpdateCandidacyForm.jsx | 0 .../{ => Entreprises}/EnterpriseForm.jsx | 0 .../{ => Entreprises}/EnterpriseList.jsx | 0 .../UpdateEnterpriseForm.jsx | 0 src/components/Login.jsx | 41 +++-- src/components/Logout.jsx | 6 +- src/components/Navbar.jsx | 152 ++++++++++++------ src/components/{ => Stages}/StageForm.jsx | 0 src/components/{ => Stages}/StageList.jsx | 0 .../{ => Stages}/UpdateStageForm.jsx | 0 src/components/{ => Students}/StudentForm.jsx | 0 src/components/{ => Students}/StudentList.jsx | 0 .../{ => Students}/UpdateStudentForm.jsx | 0 .../{ => Supervisors}/UnivSupervisorForm.jsx | 0 .../{ => Supervisors}/UnivSupervisorList.jsx | 0 .../UpdateUnivSupervisorForm.jsx | 0 19 files changed, 145 insertions(+), 86 deletions(-) rename src/components/{ => Candidatures}/CandidacyForm.jsx (100%) rename src/components/{ => Candidatures}/CandidacyList.jsx (100%) rename src/components/{ => Candidatures}/UpdateCandidacyForm.jsx (100%) rename src/components/{ => Entreprises}/EnterpriseForm.jsx (100%) rename src/components/{ => Entreprises}/EnterpriseList.jsx (100%) rename src/components/{ => Entreprises}/UpdateEnterpriseForm.jsx (100%) rename src/components/{ => Stages}/StageForm.jsx (100%) rename src/components/{ => Stages}/StageList.jsx (100%) rename src/components/{ => Stages}/UpdateStageForm.jsx (100%) rename src/components/{ => Students}/StudentForm.jsx (100%) rename src/components/{ => Students}/StudentList.jsx (100%) rename src/components/{ => Students}/UpdateStudentForm.jsx (100%) rename src/components/{ => Supervisors}/UnivSupervisorForm.jsx (100%) rename src/components/{ => Supervisors}/UnivSupervisorList.jsx (100%) rename src/components/{ => Supervisors}/UpdateUnivSupervisorForm.jsx (100%) diff --git a/src/App.js b/src/App.js index e1ae369..299b45e 100644 --- a/src/App.js +++ b/src/App.js @@ -1,25 +1,25 @@ import React from "react"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import Navbar from "./components/Navbar"; -import StageList from "./components/StageList"; -import EnterpriseList from "./components/EnterpriseList"; -import StudentList from "./components/StudentList"; -import CandidacyList from "./components/CandidacyList"; -import UnivSupervisorList from "./components/UnivSupervisorList"; +import StageList from "./components/Stages/StageList.jsx"; +import EnterpriseList from "./components/Entreprises/EnterpriseList.jsx"; +import StudentList from "./components/Students/StudentList.jsx"; +import CandidacyList from "./components/Candidatures/CandidacyList.jsx"; +import UnivSupervisorList from "./components/Supervisors/UnivSupervisorList.jsx"; // Import the forms -import StageForm from "./components/StageForm"; -import EnterpriseForm from "./components/EnterpriseForm"; -import StudentForm from "./components/StudentForm"; -import CandidacyForm from "./components/CandidacyForm"; -import UnivSupervisorForm from "./components/UnivSupervisorForm"; +import StageForm from "./components/Stages/StageForm.jsx"; +import EnterpriseForm from "./components/Entreprises/EnterpriseForm.jsx"; +import StudentForm from "./components/Students/StudentForm.jsx"; +import CandidacyForm from "./components/Candidatures/CandidacyForm.jsx"; +import UnivSupervisorForm from "./components/Supervisors/UnivSupervisorForm.jsx"; // Import the update form -import UpdateCandidacyForm from "./components/UpdateCandidacyForm"; -import UpdateEnterpriseForm from "./components/UpdateEnterpriseForm.jsx"; -import UpdateStageForm from "./components/UpdateStageForm.jsx"; -import UpdateStudentForm from "./components/UpdateStudentForm.jsx"; -import UpdateUnivSupervisorForm from "./components/UpdateUnivSupervisorForm.jsx"; +import UpdateCandidacyForm from "./components/Candidatures/UpdateCandidacyForm.jsx"; +import UpdateEnterpriseForm from "./components/Entreprises/UpdateEnterpriseForm.jsx"; +import UpdateStageForm from "./components/Stages/UpdateStageForm.jsx"; +import UpdateStudentForm from "./components/Students/UpdateStudentForm.jsx"; +import UpdateUnivSupervisorForm from "./components/Supervisors/UpdateUnivSupervisorForm.jsx"; import Login from "./components/Login.jsx"; import Logout from "./components/Logout.jsx"; @@ -49,7 +49,7 @@ function App() { <Route path="/modifier-candidature/:id" element={<UpdateCandidacyForm />} /> <Route path="/modifier-entreprise/:id" element={<UpdateEnterpriseForm />} /> <Route path="/modifier-etudiant/:id" element={<UpdateStudentForm/>} /> - <Route path="/modifier-etudiant/:id" element={<UpdateStudentForm/>} /> + <Route path="/modifier-stage/:id" element={<UpdateStageForm/>} /> <Route path="/modifier-univsuperviseur/:id" element={<UpdateUnivSupervisorForm />} /> {/* Default Route */} <Route path="/" element={<h2>Bienvenue sur l'Application de Gestion des Stages</h2>} /> diff --git a/src/components/CandidacyForm.jsx b/src/components/Candidatures/CandidacyForm.jsx similarity index 100% rename from src/components/CandidacyForm.jsx rename to src/components/Candidatures/CandidacyForm.jsx diff --git a/src/components/CandidacyList.jsx b/src/components/Candidatures/CandidacyList.jsx similarity index 100% rename from src/components/CandidacyList.jsx rename to src/components/Candidatures/CandidacyList.jsx diff --git a/src/components/UpdateCandidacyForm.jsx b/src/components/Candidatures/UpdateCandidacyForm.jsx similarity index 100% rename from src/components/UpdateCandidacyForm.jsx rename to src/components/Candidatures/UpdateCandidacyForm.jsx diff --git a/src/components/EnterpriseForm.jsx b/src/components/Entreprises/EnterpriseForm.jsx similarity index 100% rename from src/components/EnterpriseForm.jsx rename to src/components/Entreprises/EnterpriseForm.jsx diff --git a/src/components/EnterpriseList.jsx b/src/components/Entreprises/EnterpriseList.jsx similarity index 100% rename from src/components/EnterpriseList.jsx rename to src/components/Entreprises/EnterpriseList.jsx diff --git a/src/components/UpdateEnterpriseForm.jsx b/src/components/Entreprises/UpdateEnterpriseForm.jsx similarity index 100% rename from src/components/UpdateEnterpriseForm.jsx rename to src/components/Entreprises/UpdateEnterpriseForm.jsx diff --git a/src/components/Login.jsx b/src/components/Login.jsx index d6eb8c5..39b181e 100644 --- a/src/components/Login.jsx +++ b/src/components/Login.jsx @@ -18,28 +18,35 @@ const Login = () => { e.preventDefault(); try { - const response = await fetch("http://localhost:8080/api/auth/login", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ username, password }), - }); + const response = await fetch("http://localhost:8080/api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, password }), + }); - if (!response.ok) { - throw new Error("Invalid credentials"); - } + if (!response.ok) { + throw new Error("Invalid credentials"); + } - const data = await response.text(); // Adjust this if backend returns JSON - console.log("✅ Login successful!", data); + const data = await response.json(); // ✅ Parse JSON properly + console.log("✅ Login successful!", data); - localStorage.setItem("token", "dummy-token"); // Store token (if applicable) - navigate("/"); // Redirect after login + localStorage.setItem("token", "dummy-token"); // Store token (if applicable) + + // ✅ Store roles properly if they exist + if (data.roles) { + localStorage.setItem("roles", JSON.stringify(data.roles)); + } + + navigate("/"); // Redirect after login } catch (error) { - console.error("❌ Login failed:", error.message); - setError("Invalid username or password"); + console.error("❌ Login failed:", error.message); + setError("Invalid username or password"); } - }; + }; + return ( <div style={{ textAlign: "center", marginTop: "50px" }}> diff --git a/src/components/Logout.jsx b/src/components/Logout.jsx index fb00d82..27924f2 100644 --- a/src/components/Logout.jsx +++ b/src/components/Logout.jsx @@ -21,9 +21,13 @@ const Logout = () => { console.error("❌ Error during logout:", error); } - // Clear stored auth details and redirect + // ✅ Clear stored authentication details localStorage.removeItem("token"); localStorage.removeItem("user"); + localStorage.removeItem("roles"); // ✅ This is the missing part that clears the roles + localStorage.clear(); // Optional: Clears all localStorage data + + // ✅ Redirect to login page navigate("/login"); }; diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 4274267..ac4edbd 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -1,7 +1,27 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { Link } from "react-router-dom"; function Navbar() { + const [roles, setRoles] = useState([]); + + useEffect(() => { + // ✅ Ensure roles are properly fetched + const storedRoles = localStorage.getItem("roles"); + if (storedRoles) { + try { + setRoles(JSON.parse(storedRoles)); + } catch (error) { + console.error("❌ Error parsing roles from localStorage", error); + setRoles([]); // Fallback if parsing fails + } + } else { + setRoles([]); // Ensure empty roles if none exist + } + }, []); + + // 🔥 Function to check if user has a specific role + const hasRole = (role) => roles.includes(role); + return ( <nav className="navbar navbar-expand-lg navbar-dark bg-primary"> <div className="container"> @@ -20,62 +40,90 @@ function Navbar() { <div className="collapse navbar-collapse" id="navbarNav"> <ul className="navbar-nav ms-auto"> - {/* Stages Dropdown */} - <li className="nav-item dropdown"> - <a className="nav-link dropdown-toggle" href="#" id="stagesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> - 📚 Stages - </a> - <ul className="dropdown-menu" aria-labelledby="stagesDropdown"> - <li><Link className="dropdown-item" to="/stages">📜 Liste des Stages</Link></li> - <li><Link className="dropdown-item" to="/ajouter-stage">➕ Ajouter un Stage</Link></li> - </ul> - </li> + {/* 📚 Stages */} + {(hasRole("ROLE_ETUDIANT") || hasRole("ROLE_ENTREPRISE") || hasRole("ROLE_SUPERVISEUR") || hasRole("ROLE_ADMIN")) && ( + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="stagesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 📚 Stages + </a> + <ul className="dropdown-menu" aria-labelledby="stagesDropdown"> + <li><Link className="dropdown-item" to="/stages">📜 Liste des Stages</Link></li> + {(hasRole("ROLE_ENTREPRISE") || hasRole("ROLE_SUPERVISEUR") || hasRole("ROLE_ADMIN")) && ( + <li><Link className="dropdown-item" to="/ajouter-stage">➕ Ajouter/Modifier un Stage</Link></li> + )} + </ul> + </li> + )} - {/* Étudiants Dropdown */} - <li className="nav-item dropdown"> - <a className="nav-link dropdown-toggle" href="#" id="etudiantsDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> - 👨🎓 Étudiants - </a> - <ul className="dropdown-menu" aria-labelledby="etudiantsDropdown"> - <li><Link className="dropdown-item" to="/etudiants">📜 Liste des Étudiants</Link></li> - <li><Link className="dropdown-item" to="/ajouter-etudiant">➕ Ajouter un Étudiant</Link></li> - </ul> - </li> + {/* 👨🎓 Étudiants */} + {(hasRole("ROLE_ETUDIANT") || hasRole("ROLE_SUPERVISEUR") || hasRole("ROLE_ADMIN")) && ( + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="etudiantsDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 👨🎓 Étudiants + </a> + <ul className="dropdown-menu" aria-labelledby="etudiantsDropdown"> + {/* Everyone with ETUDIANT, SUPERVISEUR, ADMIN can see the list */} + <li><Link className="dropdown-item" to="/etudiants">📜 Liste des Étudiants</Link></li> - {/* Entreprises Dropdown */} - <li className="nav-item dropdown"> - <a className="nav-link dropdown-toggle" href="#" id="entreprisesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> - 🏢 Entreprises - </a> - <ul className="dropdown-menu" aria-labelledby="entreprisesDropdown"> - <li><Link className="dropdown-item" to="/entreprises">📜 Liste des Entreprises</Link></li> - <li><Link className="dropdown-item" to="/ajouter-entreprise">➕ Ajouter une Entreprise</Link></li> - </ul> - </li> + {/* BUT ONLY ADMIN CAN ADD */} + {hasRole("ROLE_ADMIN") && ( + <> - {/* Candidatures Dropdown */} - <li className="nav-item dropdown"> - <a className="nav-link dropdown-toggle" href="#" id="candidaturesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> - 📝 Candidatures - </a> - <ul className="dropdown-menu" aria-labelledby="candidaturesDropdown"> - <li><Link className="dropdown-item" to="/candidatures">📜 Liste des Candidatures</Link></li> - <li><Link className="dropdown-item" to="/ajouter-candidature">➕ Ajouter une Candidature</Link></li> - </ul> - </li> + <li><Link className="dropdown-item" to="/ajouter-etudiant">➕ Ajouter/Modifier un Étudiant</Link></li> + </> + )} + </ul> + </li> + )} - {/* Superviseurs Dropdown */} - <li className="nav-item dropdown"> - <a className="nav-link dropdown-toggle" href="#" id="superviseursDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> - 👨🏫 Superviseurs - </a> - <ul className="dropdown-menu" aria-labelledby="superviseursDropdown"> - <li><Link className="dropdown-item" to="/superviseurs">📜 Liste des Superviseurs</Link></li> - <li><Link className="dropdown-item" to="/ajouter-univsuperviseur">➕ Ajouter un Superviseur</Link></li> - </ul> - </li> + {/* 🏢 Entreprises */} + {(hasRole("ROLE_ENTREPRISE") || hasRole("ROLE_ADMIN")) && ( + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="entreprisesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 🏢 Entreprises + </a> + <ul className="dropdown-menu" aria-labelledby="entreprisesDropdown"> + <li><Link className="dropdown-item" to="/entreprises">📜 Liste des Entreprises</Link></li> + {(hasRole("ROLE_ENTREPRISE") || hasRole("ROLE_ADMIN")) && ( + <li><Link className="dropdown-item" to="/ajouter-entreprise">➕ Ajouter/Modifier une Entreprise</Link></li> + )} + </ul> + </li> + )} + + {/* 📝 Candidatures */} + {(hasRole("ROLE_ETUDIANT") || hasRole("ROLE_ADMIN") || hasRole("ROLE_SUPERVISEUR")) && ( + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="candidaturesDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 📝 Candidatures + </a> + <ul className="dropdown-menu" aria-labelledby="candidaturesDropdown"> + {/* ADMIN can see the list, but ETUDIANT cannot */} + {(hasRole("ROLE_ADMIN") || hasRole("ROLE_SUPERVISEUR")) && ( + <li><Link className="dropdown-item" to="/candidatures">📜 Liste des Candidatures</Link></li> + )} + {/* ALL Étudiants CAN apply */} + <li><Link className="dropdown-item" to="/ajouter-candidature">➕ Ajouter une Candidature</Link></li> + </ul> + </li> + )} + + + {/* 👨🏫 Superviseurs (Admin Only) */} + {(hasRole("ROLE_ADMIN")||hasRole("ROLE_SUPERVISEUR")) && ( + <li className="nav-item dropdown"> + <a className="nav-link dropdown-toggle" href="#" id="superviseursDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> + 👨🏫 Superviseurs + </a> + <ul className="dropdown-menu" aria-labelledby="superviseursDropdown"> + <li><Link className="dropdown-item" to="/superviseurs">📜 Liste des Superviseurs</Link></li> + {hasRole("ROLE_ADMIN") && ( + <li><Link className="dropdown-item" to="/ajouter-univsuperviseur">➕ Ajouter un Superviseur</Link></li>)} + </ul> + </li> + )} - {/* Authentication Links */} + {/* 🔑 Authentification */} <li className="nav-item"> <Link className="nav-link" to="/login">🔑 Login</Link> </li> diff --git a/src/components/StageForm.jsx b/src/components/Stages/StageForm.jsx similarity index 100% rename from src/components/StageForm.jsx rename to src/components/Stages/StageForm.jsx diff --git a/src/components/StageList.jsx b/src/components/Stages/StageList.jsx similarity index 100% rename from src/components/StageList.jsx rename to src/components/Stages/StageList.jsx diff --git a/src/components/UpdateStageForm.jsx b/src/components/Stages/UpdateStageForm.jsx similarity index 100% rename from src/components/UpdateStageForm.jsx rename to src/components/Stages/UpdateStageForm.jsx diff --git a/src/components/StudentForm.jsx b/src/components/Students/StudentForm.jsx similarity index 100% rename from src/components/StudentForm.jsx rename to src/components/Students/StudentForm.jsx diff --git a/src/components/StudentList.jsx b/src/components/Students/StudentList.jsx similarity index 100% rename from src/components/StudentList.jsx rename to src/components/Students/StudentList.jsx diff --git a/src/components/UpdateStudentForm.jsx b/src/components/Students/UpdateStudentForm.jsx similarity index 100% rename from src/components/UpdateStudentForm.jsx rename to src/components/Students/UpdateStudentForm.jsx diff --git a/src/components/UnivSupervisorForm.jsx b/src/components/Supervisors/UnivSupervisorForm.jsx similarity index 100% rename from src/components/UnivSupervisorForm.jsx rename to src/components/Supervisors/UnivSupervisorForm.jsx diff --git a/src/components/UnivSupervisorList.jsx b/src/components/Supervisors/UnivSupervisorList.jsx similarity index 100% rename from src/components/UnivSupervisorList.jsx rename to src/components/Supervisors/UnivSupervisorList.jsx diff --git a/src/components/UpdateUnivSupervisorForm.jsx b/src/components/Supervisors/UpdateUnivSupervisorForm.jsx similarity index 100% rename from src/components/UpdateUnivSupervisorForm.jsx rename to src/components/Supervisors/UpdateUnivSupervisorForm.jsx -- GitLab