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] 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