From c3f71965c3dc86fe6ecd5bae5d1e0f5179b9e9f9 Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 13 Mar 2026 14:40:24 +0100 Subject: [PATCH 1/5] Convert TS --- src/pages/EventDetail/EventDetailPage.jsx | 55 -------- src/pages/EventDetail/EventDetailPage.tsx | 65 +++++++++ .../CreateTaskBtn/CreateTaskBtn.jsx | 116 ---------------- .../CreateTaskBtn/CreateTaskBtn.tsx | 129 ++++++++++++++++++ .../DeleteEventBtn/DeleteEventBtn.jsx | 42 ------ .../DeleteEventBtn/DeleteEventBtn.tsx | 56 ++++++++ .../DeleteTaskBtn/DeleteTaskBtn.jsx | 28 ---- .../DeleteTaskBtn/DeleteTaskBtn.tsx | 48 +++++++ .../components/EventTask/EventTask.jsx | 98 ------------- .../components/EventTask/EventTask.tsx | 119 ++++++++++++++++ src/router.js | 2 +- 11 files changed, 418 insertions(+), 340 deletions(-) delete mode 100644 src/pages/EventDetail/EventDetailPage.jsx create mode 100644 src/pages/EventDetail/EventDetailPage.tsx delete mode 100644 src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.jsx create mode 100644 src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.tsx delete mode 100644 src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.jsx create mode 100644 src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.tsx delete mode 100644 src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.jsx create mode 100644 src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.tsx delete mode 100644 src/pages/EventDetail/components/EventTask/EventTask.jsx create mode 100644 src/pages/EventDetail/components/EventTask/EventTask.tsx diff --git a/src/pages/EventDetail/EventDetailPage.jsx b/src/pages/EventDetail/EventDetailPage.jsx deleted file mode 100644 index ee31c39..0000000 --- a/src/pages/EventDetail/EventDetailPage.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import {useEffect, useContext} from "react"; -import styles from "./EventDetailPage.module.css"; -import {useParams} from "react-router"; -import {formatDateLetter} from "../../utils/date/formatDateLetter.js"; -import EventTask from "./components/EventTask/EventTask.jsx"; -import {AuthContext} from "../../contexts/Auth/AuthContext.js"; -import {EventDetailContext} from "../../contexts/EventDetail/EventDetailContext.js"; -import CreateTaskBtn from "./components/CreateTaskBtn/CreateTaskBtn.jsx"; -import DeleteEventBtn from "./components/DeleteEventBtn/DeleteEventBtn.jsx"; - - -function EventDetailPage() { - - const params = useParams(); - const id = params.id; - const {user} = useContext(AuthContext) - const {tasks, updateEventInfo, eventInfo} = useContext(EventDetailContext) - - useEffect(() => { - updateEventInfo(id); - }, []) - - - return ( -
-
-
- Illustration événement -
-

{eventInfo?.name}

-
-
-

{eventInfo && formatDateLetter(eventInfo.start)}

-

{eventInfo?.description}

-
- {user?.isAdmin &&
- - -
} -
-
-
- -

Tâches

-
- {tasks?.map((task, index) => ( - - ))} -
-
-
- ); -} - -export default EventDetailPage; \ No newline at end of file diff --git a/src/pages/EventDetail/EventDetailPage.tsx b/src/pages/EventDetail/EventDetailPage.tsx new file mode 100644 index 0000000..9c76e8e --- /dev/null +++ b/src/pages/EventDetail/EventDetailPage.tsx @@ -0,0 +1,65 @@ +import { useEffect, useContext } from "react"; +import styles from "./EventDetailPage.module.css"; +import { useParams } from "react-router"; +import { formatDateLetter } from "../../utils/date/formatDateLetter"; +import EventTask from "./components/EventTask/EventTask"; +import { AuthContext } from "../../contexts/Auth/AuthContext"; +import { EventDetailContext } from "../../contexts/EventDetail/EventDetailContext"; +import CreateTaskBtn from "./components/CreateTaskBtn/CreateTaskBtn"; +import DeleteEventBtn from "./components/DeleteEventBtn/DeleteEventBtn"; + +function EventDetailPage() { + const params = useParams(); + const id = params.id as string; + + const { user } = useContext(AuthContext) as any; + const { tasks, updateEventInfo, eventInfo } = useContext(EventDetailContext) as any; + + useEffect(() => { + if (id) { + updateEventInfo(id); + } + }, []); + + return ( +
+
+
+ Illustration événement + +
+

{eventInfo?.name}

+ +
+
+

{eventInfo && formatDateLetter(eventInfo.start)}

+

{eventInfo?.description}

+
+ + {user?.isAdmin && ( +
+ + +
+ )} +
+
+
+ +

Tâches

+ +
+ {tasks?.map((task: any, index: number) => ( + + ))} +
+
+
+ ); +} + +export default EventDetailPage; \ No newline at end of file diff --git a/src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.jsx b/src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.jsx deleted file mode 100644 index 4a0f47b..0000000 --- a/src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import styles from "../../EventDetailPage.module.css"; -import Button from "../../../../components/ui/Button/Button"; -import {useContext, useState} from "react"; -import TextInput from "../../../../components/ui/Input/Input"; -import Modal from "../../../../components/ui/Modal/Modal"; -import createTaskApi from "../../../../utils/tasks/createTask.js"; -import {EventDetailContext} from "../../../../contexts/EventDetail/EventDetailContext.js"; - -export default function CreateTaskBtn({ id }) { - - const [openTaskModal, setOpenTaskModal] = useState(false); - - const [taskName, setTaskName] = useState(""); - const [taskDescription, setTaskDescription] = useState(""); - const [taskLocation, setTaskLocation] = useState(""); - const [maxParticipants, setMaxParticipants] = useState(""); - const [startTime, setStartTime] = useState(""); - const [endTime, setEndTime] = useState(""); - - const { updateEventInfo } = useContext(EventDetailContext) - - const createTask = async () => { - const data = await createTaskApi({ - eventId: id, - name: taskName, - description: taskDescription, - start: startTime, - end: endTime, - location: taskLocation, - maxParticipants, - }); - - if (data?.status === 200) { - setOpenTaskModal(false); - setTaskName(""); - setTaskDescription(""); - setTaskLocation(""); - setMaxParticipants(""); - setStartTime(""); - setEndTime(""); - - updateEventInfo(id) - } else { - console.log("⚠️ Échec de la création de la task"); - } - }; - - return <> - - - - setOpenTaskModal(false)} title={"Créer tâche"}> -
- -
-

Nom de la tâche

- setTaskName(e.target.value)} - placeholder="Nom de la tâche" - /> -
- -
-

Description

- setTaskDescription(e.target.value)} - placeholder="Description de la tâche" - /> -
- -
-

Lieu

- setTaskLocation(e.target.value)} - placeholder="Lieu" - /> -
- -
-

Nombre maximum de participants

- setMaxParticipants(e.target.value)} - placeholder="Participants max" - /> -
- -
-

Heure de début

- setStartTime(e.target.value)} - /> -
- -
-

Heure de fin

- setEndTime(e.target.value)} - /> -
- - - -
-
- - -} \ No newline at end of file diff --git a/src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.tsx b/src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.tsx new file mode 100644 index 0000000..9b228c3 --- /dev/null +++ b/src/pages/EventDetail/components/CreateTaskBtn/CreateTaskBtn.tsx @@ -0,0 +1,129 @@ +import styles from "../../EventDetailPage.module.css"; +import Button from "../../../../components/ui/Button/Button"; +import { useContext, useState } from "react"; +import TextInput from "../../../../components/ui/Input/Input"; +import Modal from "../../../../components/ui/Modal/Modal"; +import createTaskApi from "../../../../utils/tasks/createTask"; +import { EventDetailContext } from "../../../../contexts/EventDetail/EventDetailContext"; + +type CreateTaskBtnProps = { + id: string | number; +}; + +export default function CreateTaskBtn({ id }: CreateTaskBtnProps) { + const [openTaskModal, setOpenTaskModal] = useState(false); + + const [taskName, setTaskName] = useState(""); + const [taskDescription, setTaskDescription] = useState(""); + const [taskLocation, setTaskLocation] = useState(""); + const [maxParticipants, setMaxParticipants] = useState(""); + const [startTime, setStartTime] = useState(""); + const [endTime, setEndTime] = useState(""); + + const { updateEventInfo } = useContext(EventDetailContext) as any; + + const createTask = async () => { + const data = await createTaskApi({ + eventId: id, + name: taskName, + description: taskDescription, + start: startTime, + end: endTime, + location: taskLocation, + maxParticipants, + }); + + if (data?.status === 200) { + setOpenTaskModal(false); + setTaskName(""); + setTaskDescription(""); + setTaskLocation(""); + setMaxParticipants(""); + setStartTime(""); + setEndTime(""); + + updateEventInfo(id); + } else { + console.log("⚠️ Échec de la création de la task"); + } + }; + + return ( + <> + + + setOpenTaskModal(false)} + title={"Créer tâche"} + > +
+
+

Nom de la tâche

+ setTaskName(e.target.value)} + placeholder="Nom de la tâche" + /> +
+ +
+

Description

+ setTaskDescription(e.target.value)} + placeholder="Description de la tâche" + /> +
+ +
+

Lieu

+ setTaskLocation(e.target.value)} + placeholder="Lieu" + /> +
+ +
+

Nombre maximum de participants

+ setMaxParticipants(e.target.value)} + placeholder="Participants max" + /> +
+ +
+

Heure de début

+ setStartTime(e.target.value)} + /> +
+ +
+

Heure de fin

+ setEndTime(e.target.value)} + /> +
+ + +
+
+ + ); +} \ No newline at end of file diff --git a/src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.jsx b/src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.jsx deleted file mode 100644 index 5309450..0000000 --- a/src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import styles from "../../EventDetailPage.module.css"; -import Button from "../../../../components/ui/Button/Button"; -import Modal from "../../../../components/ui/Modal/Modal"; -import { useContext, useState } from "react"; -import { EventDetailContext } from "../../../../contexts/EventDetail/EventDetailContext.js"; -import { EventContext } from "../../../../contexts/Events/EventContext.js"; -import { useNavigate } from "react-router"; - -export default function DeleteEventBtn({ id }) { - - const { eventInfo } = useContext(EventDetailContext) - const { removeEvent } = useContext(EventContext) - const [open, setOpen] = useState(false); - const navigate = useNavigate(); - - const handleDelete = () => { - setOpen(true); - } - - const delEvent = async () => { - const result = await removeEvent(id); - - if (result.status === 200 || result.status === 204) { - setOpen(false); - navigate("/events"); - } - } - - return <> - - - - setOpen(false)} title={"Suppression"}> -
-

Etes-vous sur de vouloir supprimer l'événement {eventInfo?.name} ?

- - -
-
- - -} \ No newline at end of file diff --git a/src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.tsx b/src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.tsx new file mode 100644 index 0000000..9a79467 --- /dev/null +++ b/src/pages/EventDetail/components/DeleteEventBtn/DeleteEventBtn.tsx @@ -0,0 +1,56 @@ +import styles from "../../EventDetailPage.module.css"; +import Button from "../../../../components/ui/Button/Button"; +import Modal from "../../../../components/ui/Modal/Modal"; +import { useContext, useState } from "react"; +import { EventDetailContext } from "../../../../contexts/EventDetail/EventDetailContext"; +import { EventContext } from "../../../../contexts/Events/EventContext"; +import { useNavigate } from "react-router"; + +type DeleteEventBtnProps = { + id: string | number; +}; + +export default function DeleteEventBtn({ id }: DeleteEventBtnProps) { + const { eventInfo } = useContext(EventDetailContext) as any; + const { removeEvent } = useContext(EventContext) as any; + + const [open, setOpen] = useState(false); + const navigate = useNavigate(); + + const handleDelete = () => { + setOpen(true); + }; + + const delEvent = async () => { + const result = await removeEvent(id); + + if (result.status === 200 || result.status === 204) { + setOpen(false); + navigate("/events"); + } + }; + + return ( + <> + + + setOpen(false)} title={"Suppression"}> +
+

+ Etes-vous sur de vouloir supprimer l'événement {eventInfo?.name} ? +

+ + +
+
+ + ); +} \ No newline at end of file diff --git a/src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.jsx b/src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.jsx deleted file mode 100644 index af6e3d0..0000000 --- a/src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useContext } from "react"; -import { AuthContext } from "../../../../contexts/Auth/AuthContext.js"; -import { EventDetailContext } from "../../../../contexts/EventDetail/EventDetailContext.js"; -import Button from "../../../../components/ui/Button/Button"; -import { useState } from "react"; -import Modal from "../../../../components/ui/Modal/Modal"; -import styles from "../../EventDetailPage.module.css"; - -export default function DeleteTaskBtn({ taskId, eventId, taskName }) { - - const { user } = useContext(AuthContext) - const { deleteTask } = useContext(EventDetailContext) - - const [openModal, setOpenModal] = useState(false); - - if(!user?.isAdmin) return null; - - return <> - - - -
-

Etes-vous sur de vouloir supprimer la tâche {taskName} ?

- -
-
- -} \ No newline at end of file diff --git a/src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.tsx b/src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.tsx new file mode 100644 index 0000000..6c16221 --- /dev/null +++ b/src/pages/EventDetail/components/DeleteTaskBtn/DeleteTaskBtn.tsx @@ -0,0 +1,48 @@ +import { useContext, useState } from "react"; +import { AuthContext } from "../../../../contexts/Auth/AuthContext"; +import { EventDetailContext } from "../../../../contexts/EventDetail/EventDetailContext"; +import Button from "../../../../components/ui/Button/Button"; +import Modal from "../../../../components/ui/Modal/Modal"; +import styles from "../../EventDetailPage.module.css"; + +type DeleteTaskBtnProps = { + taskId: string | number; + eventId: string | number; + taskName: string; +}; + +export default function DeleteTaskBtn({ taskId, eventId, taskName }: DeleteTaskBtnProps) { + + const { user } = useContext(AuthContext) as any; + const { deleteTask } = useContext(EventDetailContext) as any; + + const [openModal, setOpenModal] = useState(false); + + if (!user?.isAdmin) return null; + + return ( + <> + + + setOpenModal(false)} title={"Suppression"}> +
+

+ Etes-vous sur de vouloir supprimer la tâche {taskName} ? +

+ + +
+
+ + ); +} \ No newline at end of file diff --git a/src/pages/EventDetail/components/EventTask/EventTask.jsx b/src/pages/EventDetail/components/EventTask/EventTask.jsx deleted file mode 100644 index 41e6429..0000000 --- a/src/pages/EventDetail/components/EventTask/EventTask.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import styles from "./EventTask.module.css"; -import { formatDateLetter } from "../../../../utils/date/formatDateLetter.js"; -import Button from "../../../../components/ui/Button/Button"; -import assign from "../../../../utils/tasks/assign.js"; -import unAssign from "../../../../utils/tasks/unAssign.js"; -import { useContext, useEffect, useState } from "react"; -import isAssigned from "../../../../utils/tasks/isAssigned.js"; -import Modal from "../../../../components/ui/Modal/Modal"; -import { AuthContext } from "../../../../contexts/Auth/AuthContext.js"; -import DeleteTaskBtn from "../DeleteTaskBtn/DeleteTaskBtn.jsx"; - -export default function EventTask({ task, index, eventId }) { - - const { update } = useContext(AuthContext) - - const [assigned, setAssigned] = useState(false); - const [open, setOpen] = useState(false); - const [title, setTitle] = useState(""); - const [message, setMessage] = useState(""); - - async function checkAssign() { - const data = await isAssigned(task.id); - setAssigned(data?.data.assigned); - } - - useEffect(() => { - checkAssign(); - }, []) - - - const handleRegisterBtn = async () => { - const res = await assign(task.id); - - switch (res.status) { - case 200: - update() - setTitle("Inscription réussie") - setMessage(`Vous êtes maintenant inscrit à la tâche ${task.name}`); - setOpen(true); - setAssigned(true); - break; - - case 409: - setTitle("Erreur lors de l'inscription") - setMessage("Vous êtes déjà inscrit à cette tâche") - setOpen(true); - break; - - case 403: - setTitle("Erreur lors de l'inscription") - setMessage("Le nombre maximum d'inscription pour cette tâche à été atteint") - setOpen(true); - break; - } - }; - - const handleUnassign = async () => { - const res = await unAssign(task.id); - - switch (res.status) { - case 200: - update() - setTitle("Désinscription réussie") - setMessage(`Vous n'êtes plus inscrit à la tâche ${task.name}`); - setOpen(true); - setAssigned(false); - break; - - case 409: - setTitle("Erreur lors de la désinscription") - setMessage(`Vous n'êtes pas inscrit à cette tâche`); - setOpen(true); - break; - } - }; - - return
-
-
- {task.name} -

{formatDateLetter(task.start)} - {formatDateLetter(task.end)}

-

{task.description}

-
-
- {assigned ? - - : - - } - -
-
- - -

{message}

-
-
-} \ No newline at end of file diff --git a/src/pages/EventDetail/components/EventTask/EventTask.tsx b/src/pages/EventDetail/components/EventTask/EventTask.tsx new file mode 100644 index 0000000..bebc62d --- /dev/null +++ b/src/pages/EventDetail/components/EventTask/EventTask.tsx @@ -0,0 +1,119 @@ +import styles from "./EventTask.module.css"; +import { formatDateLetter } from "../../../../utils/date/formatDateLetter"; +import Button from "../../../../components/ui/Button/Button"; +import assign from "../../../../utils/tasks/assign"; +import unAssign from "../../../../utils/tasks/unAssign"; +import { useContext, useEffect, useState } from "react"; +import isAssigned from "../../../../utils/tasks/isAssigned"; +import Modal from "../../../../components/ui/Modal/Modal"; +import { AuthContext } from "../../../../contexts/Auth/AuthContext"; +import DeleteTaskBtn from "../DeleteTaskBtn/DeleteTaskBtn"; + +type EventTaskProps = { + task: any; + index: number; + eventId: string | number; +}; + +export default function EventTask({ task, index, eventId }: EventTaskProps) { + + const { update } = useContext(AuthContext) as any; + + const [assigned, setAssigned] = useState(false); + const [open, setOpen] = useState(false); + const [title, setTitle] = useState(""); + const [message, setMessage] = useState(""); + + async function checkAssign() { + const data = await isAssigned(task.id); + setAssigned(data?.data.assigned); + } + + useEffect(() => { + checkAssign(); + }, []); + + const handleRegisterBtn = async () => { + const res = await assign(task.id); + + switch (res.status) { + case 200: + update(); + setTitle("Inscription réussie"); + setMessage(`Vous êtes maintenant inscrit à la tâche ${task.name}`); + setOpen(true); + setAssigned(true); + break; + + case 409: + setTitle("Erreur lors de l'inscription"); + setMessage("Vous êtes déjà inscrit à cette tâche"); + setOpen(true); + break; + + case 403: + setTitle("Erreur lors de l'inscription"); + setMessage("Le nombre maximum d'inscription pour cette tâche à été atteint"); + setOpen(true); + break; + } + }; + + const handleUnassign = async () => { + const res = await unAssign(task.id); + + switch (res.status) { + case 200: + update(); + setTitle("Désinscription réussie"); + setMessage(`Vous n'êtes plus inscrit à la tâche ${task.name}`); + setOpen(true); + setAssigned(false); + break; + + case 409: + setTitle("Erreur lors de la désinscription"); + setMessage(`Vous n'êtes pas inscrit à cette tâche`); + setOpen(true); + break; + } + }; + + return ( +
+
+
+ + {task.name} + +

+ {formatDateLetter(task.start)} - {formatDateLetter(task.end)} +

+

{task.description}

+
+ +
+ {assigned ? ( + + ) : ( + + )} + + +
+
+ + setOpen(false)} title={title}> +

{message}

+
+
+ ); +} \ No newline at end of file diff --git a/src/router.js b/src/router.js index 75d0bd6..11c3bb8 100644 --- a/src/router.js +++ b/src/router.js @@ -9,7 +9,7 @@ import EventsPage from "./pages/Events/EventsPage.jsx"; import AdminPage from "./pages/Admin/AdminPage.jsx"; import WaitValidationPage from "./pages/WaitValidation/WaitValidationPage.jsx"; import PageNotFoundPage from "./pages/PageNotFound/PageNotFoundPage.jsx"; -import eventDetail from "./pages/EventDetail/EventDetailPage.jsx"; +import eventDetail from "./pages/EventDetail/EventDetailPage.tsx"; import legalNotices from "./pages/LegalNotices/LegalNoticesPage.jsx"; import RGPDPage from "./pages/RGPD/RGPDPage.jsx"; From 94ceff82885d9cab6e1819e6a0599f2dd745746e Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 13 Mar 2026 16:54:00 +0100 Subject: [PATCH 2/5] Create AssignUserBtn component --- .../AssignUserBtn/AssignUserBtn.module.css | 23 ++++++ .../AssignUserBtn/AssignUserBtn.tsx | 71 +++++++++++++++++++ .../components/EventTask/EventTask.tsx | 3 + 3 files changed, 97 insertions(+) create mode 100644 src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.module.css create mode 100644 src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx diff --git a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.module.css b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.module.css new file mode 100644 index 0000000..c5798dc --- /dev/null +++ b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.module.css @@ -0,0 +1,23 @@ +.usersList { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 10px; +} + +.userCard { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 14px; + border-radius: 10px; + background: rgba(255,255,255,0.05); + border: 1px solid rgba(255,255,255,0.08); +} + +.userName { + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} \ No newline at end of file diff --git a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx new file mode 100644 index 0000000..ee69cc6 --- /dev/null +++ b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx @@ -0,0 +1,71 @@ +import { ReactNode, useEffect, useState } from "react"; +import Button from "../../../../components/ui/Button/Button"; +import Modal from "../../../../components/ui/Modal/Modal"; +import User from "../../../Volunteers/interfaces/user.interface"; +import getAllUser from "../../../../utils/users/getAllUsers"; +import Loading from "../../../../components/ui/Loading/Loading"; +import getUserById from "../../../../utils/users/getUserById"; +import styles from "./AssignUserBtn.module.css"; + +interface AssignUserBtnProps { + taskId: string | number; +} + +export default function AssignUserBtn({ taskId }: AssignUserBtnProps): ReactNode { + + const [open, setOpen] = useState(false); + const [users, setUsers] = useState>([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + (async () => { + setLoading(true); + + const result = await getAllUser(); + + if (result.status !== 200 || !Array.isArray(result.data)) { + console.error("Erreur lors du chargement des utilisateurs", result); + return; + } + + const usersData = await Promise.all( + result.data.map(async (id: number): Promise => { + const res = await getUserById(id); + + if (res.status === 200) { + return res.data as User; + } + + return null; + }) + ); + + setUsers(usersData.filter((user): user is User => user !== null)); + setLoading(false); + })(); + }, []); + + const handleAssign = async (user: User) => { + + }; + + return <> + + + setOpen(false)} title={"Assigner un utilisateur à cette tâche"}> +
+ {loading ? : users.map((user: User, index) => ( +
+

+ {user.name} {user.lastname} +

+ + +
+ ))} +
+
+ +} \ No newline at end of file diff --git a/src/pages/EventDetail/components/EventTask/EventTask.tsx b/src/pages/EventDetail/components/EventTask/EventTask.tsx index bebc62d..4a1188b 100644 --- a/src/pages/EventDetail/components/EventTask/EventTask.tsx +++ b/src/pages/EventDetail/components/EventTask/EventTask.tsx @@ -8,6 +8,7 @@ import isAssigned from "../../../../utils/tasks/isAssigned"; import Modal from "../../../../components/ui/Modal/Modal"; import { AuthContext } from "../../../../contexts/Auth/AuthContext"; import DeleteTaskBtn from "../DeleteTaskBtn/DeleteTaskBtn"; +import AssignUserBtn from "../AssignUserBtn/AssignUserBtn"; type EventTaskProps = { task: any; @@ -108,6 +109,8 @@ export default function EventTask({ task, index, eventId }: EventTaskProps) { taskId={task.id} taskName={task.name} /> + + From 4dbd1274fde55c22e382aa3aa3acde59acdfa8e4 Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 13 Mar 2026 17:08:36 +0100 Subject: [PATCH 3/5] fix useNavigate import --- src/pages/Login/components/LoginForm/LoginForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Login/components/LoginForm/LoginForm.tsx b/src/pages/Login/components/LoginForm/LoginForm.tsx index 49d576b..a899b16 100644 --- a/src/pages/Login/components/LoginForm/LoginForm.tsx +++ b/src/pages/Login/components/LoginForm/LoginForm.tsx @@ -3,7 +3,7 @@ import styles from "./LoginForm.module.css"; import Button from "../../../../components/ui/Button/Button"; import Input from "../../../../components/ui/Input/Input"; import Modal from "../../../../components/ui/Modal/Modal"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { AuthContext, AuthContextType } from "../../../../contexts/Auth/AuthContext"; interface LoginFormProps { From 6349439435c03856861ee9670b7e223f1519407d Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 13 Mar 2026 17:09:41 +0100 Subject: [PATCH 4/5] Show btn only if user is admin --- .../EventDetail/components/AssignUserBtn/AssignUserBtn.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx index ee69cc6..44bdaeb 100644 --- a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx +++ b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useEffect, useState } from "react"; +import {ReactNode, useContext, useEffect, useState} from "react"; import Button from "../../../../components/ui/Button/Button"; import Modal from "../../../../components/ui/Modal/Modal"; import User from "../../../Volunteers/interfaces/user.interface"; @@ -6,6 +6,7 @@ import getAllUser from "../../../../utils/users/getAllUsers"; import Loading from "../../../../components/ui/Loading/Loading"; import getUserById from "../../../../utils/users/getUserById"; import styles from "./AssignUserBtn.module.css"; +import {AuthContext} from "../../../../contexts/Auth/AuthContext"; interface AssignUserBtnProps { taskId: string | number; @@ -17,6 +18,10 @@ export default function AssignUserBtn({ taskId }: AssignUserBtnProps): ReactNode const [users, setUsers] = useState>([]); const [loading, setLoading] = useState(true); + const { user } = useContext(AuthContext) as any; + + if (!user?.isAdmin) return null; + useEffect(() => { (async () => { setLoading(true); From 539f42d6716e32530fc397226fb270ea14de55e7 Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Sun, 15 Mar 2026 16:12:33 +0100 Subject: [PATCH 5/5] Assign other user by admin account --- .../AssignUserBtn/AssignUserBtn.tsx | 25 ++++++++++++++++--- src/utils/tasks/assignOtherUser.ts | 13 ++++++++++ src/utils/tasks/unAssignOtherUser.ts | 13 ++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/utils/tasks/assignOtherUser.ts create mode 100644 src/utils/tasks/unAssignOtherUser.ts diff --git a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx index 44bdaeb..9a9ad3d 100644 --- a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx +++ b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx @@ -7,6 +7,8 @@ import Loading from "../../../../components/ui/Loading/Loading"; import getUserById from "../../../../utils/users/getUserById"; import styles from "./AssignUserBtn.module.css"; import {AuthContext} from "../../../../contexts/Auth/AuthContext"; +import assignOtherUser from "../../../../utils/tasks/assignOtherUser"; +import unAssignOtherUser from "../../../../utils/tasks/unAssignOtherUser"; interface AssignUserBtnProps { taskId: string | number; @@ -50,8 +52,17 @@ export default function AssignUserBtn({ taskId }: AssignUserBtnProps): ReactNode })(); }, []); - const handleAssign = async (user: User) => { + const isUserAssigned = (u: User): boolean => + u.tasks.some(t => t.id === taskId); + const handleAssign = async (selectedUser: User) => { + await assignOtherUser(taskId as number, selectedUser.id); + setOpen(false); + }; + + const handleUnassign = async (selectedUser: User) => { + await unAssignOtherUser(taskId as number, selectedUser.id); + setOpen(false); }; return <> @@ -65,9 +76,15 @@ export default function AssignUserBtn({ taskId }: AssignUserBtnProps): ReactNode {user.name} {user.lastname}

- + {isUserAssigned(user) ? ( + + ) : ( + + )} ))} diff --git a/src/utils/tasks/assignOtherUser.ts b/src/utils/tasks/assignOtherUser.ts new file mode 100644 index 0000000..189c06b --- /dev/null +++ b/src/utils/tasks/assignOtherUser.ts @@ -0,0 +1,13 @@ +import fetchWrapper from "../fetchWrapper.js"; +import getXSRFToken from "../getXSRF.js"; + +export default async function assignOtherUser(taskId: number, userId: number): Promise { + const csrfToken = await getXSRFToken(); + + return fetchWrapper( + `/api/events/task/assign/${userId}`, + {task_id: taskId}, + "POST", + {"X-XSRF-TOKEN": csrfToken} + ); +} diff --git a/src/utils/tasks/unAssignOtherUser.ts b/src/utils/tasks/unAssignOtherUser.ts new file mode 100644 index 0000000..0781dca --- /dev/null +++ b/src/utils/tasks/unAssignOtherUser.ts @@ -0,0 +1,13 @@ +import fetchWrapper from "../fetchWrapper.js"; +import getXSRFToken from "../getXSRF.js"; + +export default async function unAssignOtherUser(taskId: number, userId: number): Promise { + const csrfToken = await getXSRFToken(); + + return fetchWrapper( + `/api/events/task/unassign/${userId}`, + {task_id: taskId}, + "POST", + {"X-XSRF-TOKEN": csrfToken} + ); +} \ No newline at end of file