diff --git a/public/icons/tools.svg b/public/icons/tools.svg new file mode 100644 index 0000000..2f2d697 --- /dev/null +++ b/public/icons/tools.svg @@ -0,0 +1,2 @@ + +tools \ No newline at end of file diff --git a/src/components/manageMember/ManageMember.jsx b/src/components/manageMember/ManageMember.jsx new file mode 100644 index 0000000..04bef13 --- /dev/null +++ b/src/components/manageMember/ManageMember.jsx @@ -0,0 +1,32 @@ +import Button from "../ui/button/button.jsx"; +import Modal from "../ui/modal/modal.jsx"; +import { useState } from "react"; +import styles from "./manageMember.module.css" +import DeleteMemberBtn from "./deleteMember/deleteMemberBtn.jsx"; +import ModifyRole from "./modifyrole/ModifyRole.jsx"; + +export default function ManageMember({ user }) { + + const [open, setOpen] = useState(false); + + + return <> + + + setOpen(false)} title={`Gestion de l'utilisateur ${user.name}`}> + +
+

Supprimer le compte

+ +
+ +
+

Modifier le rôle

+ +
+
+ +} \ No newline at end of file diff --git a/src/components/manageMember/deleteMember/deleteMemberBtn.jsx b/src/components/manageMember/deleteMember/deleteMemberBtn.jsx new file mode 100644 index 0000000..aa686aa --- /dev/null +++ b/src/components/manageMember/deleteMember/deleteMemberBtn.jsx @@ -0,0 +1,31 @@ +import Button from "../../ui/button/button.jsx"; +import Modal from "../../ui/modal/modal.jsx"; +import { useState } from "react"; +import deleteUser from "../../../utils/users/deleteUser.js" +import { useNavigate } from "react-router"; + +export default function DeleteMemberBtn({ name, id, ...props }) { + + const [open, setOpen] = useState(false); + const navigate = useNavigate(); + + const delMember = async () => { + const result = await deleteUser(id); + + if (result.status === 200 || result.status === 204) { + setOpen(false); + navigate("/volunteers"); + } + } + + return
+ + + setOpen(false)} title={"Supprimer"}> +
+

Etes-vous sur de vouloir supprimer le compte {name} ?

+ +
+
+
+} \ No newline at end of file diff --git a/src/components/manageMember/manageMember.module.css b/src/components/manageMember/manageMember.module.css new file mode 100644 index 0000000..fc250ad --- /dev/null +++ b/src/components/manageMember/manageMember.module.css @@ -0,0 +1,47 @@ +.icon { + width: 24px; +} + +.manageBtn { + display: flex; + flex-direction: row; +} + +.manageSection { + display: flex; + flex-direction: column; + gap: 8px; + margin: 1.5em; +} + +.manageBlockContent { + margin: 0.5em; +} + +.modifyRole { + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: center; +} + +.select { + width: 70%; + padding: 10px 14px; + + font-size: 14px; + font-weight: 500; + color: #1d1d1f; + + background-color: #ffffff; + border: 1px solid #d2d2d7; + border-radius: 10px; + + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; +} + +.modalContent { + text-align: center; +} \ No newline at end of file diff --git a/src/components/manageMember/modifyrole/ModifyRole.jsx b/src/components/manageMember/modifyrole/ModifyRole.jsx new file mode 100644 index 0000000..11ecdf3 --- /dev/null +++ b/src/components/manageMember/modifyrole/ModifyRole.jsx @@ -0,0 +1,59 @@ +import styles from "../manageMember.module.css"; +import Button from "../../ui/button/button.jsx"; +import modifyRole from "../../../utils/users/modifyRole.js"; +import { useState, useEffect } from "react"; +import getRoles from "../../../utils/getRoles.js"; +import Modal from "../../ui/modal/modal.jsx"; + +export default function ModifyRole({ user }) { + + const [roles, setRoles] = useState([]); + const [selectedRole, setSelectedRole] = useState(3); + const [open, setOpen] = useState(false); + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); + + useEffect(() => { + getRoles().then(data => { + if (data) setRoles(data); + }); + }, []) + + + const handleRoleUpdate = async () => { + const res = await modifyRole(selectedRole, user.id); + + if (res.success) { + setDescription("Rôle modifié avec succès"); + setTitle("Modification") + setOpen(true); + } else { + setDescription("Erreur lors de la modification du rôle"); + setTitle("Erreur") + setOpen(true); + } + }; + + + return
+ + + + + setOpen(false)} title={title}> +

{description}

+
+
+} \ No newline at end of file diff --git a/src/pages/VolunteerProfile/VolunteerProfilePage.jsx b/src/pages/VolunteerProfile/VolunteerProfilePage.jsx index 1f18805..4c24a51 100644 --- a/src/pages/VolunteerProfile/VolunteerProfilePage.jsx +++ b/src/pages/VolunteerProfile/VolunteerProfilePage.jsx @@ -1,10 +1,12 @@ import styles from "./VolunteerProfilePage.module.css" import { useParams } from "react-router"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useContext } from "react"; import getUserById from "../../utils/users/getUserById.js"; import formatDate from "../../utils/date/formatDate.js"; import Task from "../../components/Task/Task.jsx"; import { useNavigate } from "react-router"; +import ManageMember from "../../components/manageMember/ManageMember.jsx"; +import { AuthContext } from "../../contexts/auth/AuthContext.js"; export default function VolunteerProfilePage() { @@ -13,6 +15,8 @@ export default function VolunteerProfilePage() { const navigate = useNavigate(); + const { user: currentUser } = useContext(AuthContext); + const [user, setUser] = useState({}); useEffect(() => { @@ -41,13 +45,14 @@ export default function VolunteerProfilePage() {

{user.name} {user.lastname}

+ {currentUser?.isAdmin && }
-
+

Role : {user.role}

Membre depuis : {formatDate(user.created_at)}

-
+

Mail : {user.email}

Téléphone : {user.phone === null ? "Pas de numéro enregistré" : user.phone}

diff --git a/src/utils/getRoles.js b/src/utils/getRoles.js new file mode 100644 index 0000000..5d6d3d6 --- /dev/null +++ b/src/utils/getRoles.js @@ -0,0 +1,21 @@ +export default async function getRoles() { + try { + const res = await fetch(`${import.meta.env.VITE_API_URL}/api/roles`, { + method: 'GET', + credentials: 'include', + headers: { + 'Accept': 'application/json' + } + }); + + if (!res.ok) { + throw new Error(`Erreur serveur : ${res.status}`); + } + + const data = await res.json(); + return data; + } catch (err) { + console.error('Erreur :', err); + return null; + } +} diff --git a/src/utils/users/deleteUser.js b/src/utils/users/deleteUser.js index e6f13fd..4e3f24d 100644 --- a/src/utils/users/deleteUser.js +++ b/src/utils/users/deleteUser.js @@ -1,35 +1,34 @@ -import getXSRFToken from "../getXSRF"; +import getXSRFToken from "../getXSRF.js"; export default async function deleteUser(userId) { - - const csrfToken = await getXSRFToken(); + const csrfToken = await getXSRFToken(); - try { - + try { + const response = await fetch(`${import.meta.env.VITE_API_URL}/api/users/${userId}`, { + method: 'DELETE', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + 'X-XSRF-TOKEN': csrfToken, + 'Accept': 'application/json' + }, + body: JSON.stringify({ + id: userId + }) + }); - const res = await fetch( - `${import.meta.env.VITE_API_URL}/api/users/${userId}`, - { - method: "DELETE", - credentials: "include", - headers: { - Accept: "application/json", - "X-XSRF-TOKEN": csrfToken, - }, - } - ); + const data = await response.json(); - if (!res.ok) { - let message = "Erreur backend"; - try { - const errorData = await res.json(); - message = errorData.message || message; - } catch {} - return { success: false, message }; + if (!response.ok) { + return { + status: response.status, + errors: data.errors || data.message + }; + } + + return { status: response.status, data }; + + } catch (error) { + return { status: 500, error }; } - return { success: true }; - } catch (err) { - console.error(err); - return { success: false, message: "Erreur réseau" }; - } } diff --git a/src/utils/users/modifyRole.js b/src/utils/users/modifyRole.js new file mode 100644 index 0000000..e0c9ae8 --- /dev/null +++ b/src/utils/users/modifyRole.js @@ -0,0 +1,33 @@ +import getXSRFToken from "../getXSRF.js"; + + +export default async function modifyRole(role, userId) { + try { + const csrfToken = await getXSRFToken(); + + const res = await fetch( + `${import.meta.env.VITE_API_URL}/api/users/${userId}/role`, + { + method: 'POST', + credentials: 'include', + headers: { + 'Accept': 'application/json', + 'X-XSRF-TOKEN': csrfToken, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ role }), + } + ); + + const data = await res.json(); + + if (!res.ok) { + return { success: false, error: data.message ?? "Erreur serveur" }; + } + + return { success: true, data }; + } catch (err) { + console.error(err); + return { success: false, error: "Erreur réseau" }; + } +}