From daccc0cda388e634a84113d4f50a7ed565eeda5e Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 27 Mar 2026 16:27:30 +0100 Subject: [PATCH 01/21] Create update event function --- src/utils/events/updateEvent.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/utils/events/updateEvent.ts diff --git a/src/utils/events/updateEvent.ts b/src/utils/events/updateEvent.ts new file mode 100644 index 0000000..a46b610 --- /dev/null +++ b/src/utils/events/updateEvent.ts @@ -0,0 +1,13 @@ +import fetchWrapper from "../fetchWrapper"; +import getXSRFToken from "../getXSRF"; + +export default async function updateEvent(id: number, name: string, description: string, start: string, end: string) { + const csrfToken = await getXSRFToken(); + + return fetchWrapper( + `/api/events/${id}/update`, + { name, description, start, end }, + "POST", + { "X-XSRF-TOKEN": csrfToken } + ); +} From 49d2a0cc22e1c5517daa8403326e9fa93e6dd668 Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 27 Mar 2026 16:27:39 +0100 Subject: [PATCH 02/21] Create update event component --- src/pages/EventDetail/EventDetailPage.tsx | 2 + .../components/EditEventBtn/EditEventBtn.tsx | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/pages/EventDetail/components/EditEventBtn/EditEventBtn.tsx diff --git a/src/pages/EventDetail/EventDetailPage.tsx b/src/pages/EventDetail/EventDetailPage.tsx index 65e9a64..7c3d37a 100644 --- a/src/pages/EventDetail/EventDetailPage.tsx +++ b/src/pages/EventDetail/EventDetailPage.tsx @@ -7,6 +7,7 @@ import { AuthContext } from "../../contexts/Auth/AuthContext"; import { EventDetailContext } from "../../contexts/EventDetail/EventDetailContext"; import CreateTaskBtn from "./components/CreateTaskBtn/CreateTaskBtn"; import DeleteEventBtn from "./components/DeleteEventBtn/DeleteEventBtn"; +import EditEventBtn from "./components/EditEventBtn/EditEventBtn"; function EventDetailPage() { const params = useParams(); @@ -42,6 +43,7 @@ function EventDetailPage() { {user?.isAdmin && (
+
diff --git a/src/pages/EventDetail/components/EditEventBtn/EditEventBtn.tsx b/src/pages/EventDetail/components/EditEventBtn/EditEventBtn.tsx new file mode 100644 index 0000000..799c9cc --- /dev/null +++ b/src/pages/EventDetail/components/EditEventBtn/EditEventBtn.tsx @@ -0,0 +1,92 @@ +import styles from "../../EventDetailPage.module.css"; +import Button from "../../../../components/ui/Button/Button"; +import Modal from "../../../../components/ui/Modal/Modal"; +import TextInput from "../../../../components/ui/Input/Input"; +import { useContext, useState, useEffect } from "react"; +import { EventDetailContext } from "../../../../contexts/EventDetail/EventDetailContext"; +import updateEvent from "../../../../utils/events/updateEvent"; + +type EditEventBtnProps = { + id: string | number; +}; + +export default function EditEventBtn({ id }: EditEventBtnProps) { + const { eventInfo, updateEventInfo } = useContext(EventDetailContext) as any; + + const [open, setOpen] = useState(false); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [start, setStart] = useState(""); + const [end, setEnd] = useState(""); + + useEffect(() => { + if (open && eventInfo) { + setName(eventInfo.name ?? ""); + setDescription(eventInfo.description ?? ""); + setStart(eventInfo.start ? eventInfo.start.slice(0, 16) : ""); + setEnd(eventInfo.end ? eventInfo.end.slice(0, 16) : ""); + } + }, [open, eventInfo]); + + const handleSubmit = async () => { + const res = await updateEvent(Number(id), name, description, start, end); + await updateEventInfo(id); + setOpen(false); + }; + + return ( + <> + + + setOpen(false)} title={"Modifier l'événement"}> +
+
+

Nom

+ setName(e.target.value)} + placeholder="Nom de l'événement" + /> +
+ +
+

Description

+ setDescription(e.target.value)} + placeholder="Description" + /> +
+ +
+

Début

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

Fin

+ setEnd(e.target.value)} + /> +
+ + +
+
+ + ); +} From f6208cf95df3a3a671fd87eb2edb49696ada0101 Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 27 Mar 2026 16:36:55 +0100 Subject: [PATCH 03/21] Create update task function --- src/utils/tasks/updateTask.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/utils/tasks/updateTask.ts diff --git a/src/utils/tasks/updateTask.ts b/src/utils/tasks/updateTask.ts new file mode 100644 index 0000000..ad18e82 --- /dev/null +++ b/src/utils/tasks/updateTask.ts @@ -0,0 +1,22 @@ +import fetchWrapper from "../fetchWrapper"; +import getXSRFToken from "../getXSRF"; + +interface UpdateTaskParams { + name: string; + description: string; + start: string; + end: string; + location: string; + max_participants: number; +} + +export default async function updateTask(id: number, params: UpdateTaskParams): Promise { + const csrfToken = await getXSRFToken(); + + return fetchWrapper( + `/api/events/tasks/${id}/update`, + params, + "POST", + { "X-XSRF-TOKEN": csrfToken } + ); +} From 2c1da8e93f676923338683ecdde0fee013dfd33b Mon Sep 17 00:00:00 2001 From: T'JAMPENS QUENTIN p2406187 Date: Fri, 27 Mar 2026 16:37:03 +0100 Subject: [PATCH 04/21] Create update task component --- .../components/EditTaskBtn/EditTaskBtn.tsx | 124 ++++++++++++++++++ .../components/EventTask/EventTask.tsx | 3 + 2 files changed, 127 insertions(+) create mode 100644 src/pages/EventDetail/components/EditTaskBtn/EditTaskBtn.tsx diff --git a/src/pages/EventDetail/components/EditTaskBtn/EditTaskBtn.tsx b/src/pages/EventDetail/components/EditTaskBtn/EditTaskBtn.tsx new file mode 100644 index 0000000..3da5cf4 --- /dev/null +++ b/src/pages/EventDetail/components/EditTaskBtn/EditTaskBtn.tsx @@ -0,0 +1,124 @@ +import { useContext, useEffect, 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 TextInput from "../../../../components/ui/Input/Input"; +import styles from "../../EventDetailPage.module.css"; +import updateTask from "../../../../utils/tasks/updateTask"; + +type EditTaskBtnProps = { + taskId: number; + eventId: number | string; +}; + +export default function EditTaskBtn({ taskId, eventId }: EditTaskBtnProps) { + const { user } = useContext(AuthContext) as any; + const { tasks, updateEventInfo } = useContext(EventDetailContext) as any; + const task = tasks?.find((t: any) => t.id === taskId); + + const [open, setOpen] = useState(false); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [start, setStart] = useState(""); + const [end, setEnd] = useState(""); + const [location, setLocation] = useState(""); + const [maxParticipants, setMaxParticipants] = useState(""); + + useEffect(() => { + if (open && task) { + setName(task.name ?? ""); + setDescription(task.description ?? ""); + setStart(task.start ? task.start.slice(0, 16) : ""); + setEnd(task.end ? task.end.slice(0, 16) : ""); + setLocation(task.location ?? ""); + setMaxParticipants(task.max_participants?.toString() ?? ""); + } + }, [open, task]); + + if (!user?.isAdmin) return null; + + const handleSubmit = async () => { + await updateTask(taskId, { + name, + description, + start, + end, + location, + max_participants: Number(maxParticipants), + }); + await updateEventInfo(eventId); + setOpen(false); + }; + + return ( + <> + + + setOpen(false)} title={"Modifier la tâche"}> +
+
+

Nom

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

Description

+ setDescription(e.target.value)} + placeholder="Description" + /> +
+ +
+

Lieu

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

Participants max

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

Début

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

Fin

+ setEnd(e.target.value)} + /> +
+ + +
+
+ + ); +} diff --git a/src/pages/EventDetail/components/EventTask/EventTask.tsx b/src/pages/EventDetail/components/EventTask/EventTask.tsx index 841227e..7166f27 100644 --- a/src/pages/EventDetail/components/EventTask/EventTask.tsx +++ b/src/pages/EventDetail/components/EventTask/EventTask.tsx @@ -10,6 +10,7 @@ import { AuthContext } from "../../../../contexts/Auth/AuthContext"; import { EventDetailContext } from "../../../../contexts/EventDetail/EventDetailContext"; import DeleteTaskBtn from "../DeleteTaskBtn/DeleteTaskBtn"; import AssignUserBtn from "../AssignUserBtn/AssignUserBtn"; +import EditTaskBtn from "../EditTaskBtn/EditTaskBtn"; type EventTaskProps = { taskId: number; @@ -135,6 +136,8 @@ export default function EventTask({ taskId, index }: EventTaskProps) { )} + + Date: Fri, 27 Mar 2026 17:00:23 +0100 Subject: [PATCH 05/21] Improve AssignUserBtn --- .../AssignUserBtn/AssignUserBtn.tsx | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx index b44b5a5..95ae20c 100644 --- a/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx +++ b/src/pages/EventDetail/components/AssignUserBtn/AssignUserBtn.tsx @@ -55,19 +55,24 @@ export default function AssignUserBtn({ taskId, eventId }: AssignUserBtnProps): })(); }, []); - const isUserAssigned = (u: User): boolean => - u.tasks.some(t => t.id === taskId); + const [assignedIds, setAssignedIds] = useState>(new Set()); + + useEffect(() => { + setAssignedIds(new Set( + users.filter(u => u.tasks.some(t => t.id === taskId)).map(u => u.id) + )); + }, [users, taskId]); const handleAssign = async (selectedUser: User) => { + setAssignedIds(prev => new Set(prev).add(selectedUser.id)); await assignOtherUser(taskId as number, selectedUser.id); - updateEventInfo(eventId) - setOpen(false); + updateEventInfo(eventId); }; const handleUnassign = async (selectedUser: User) => { + setAssignedIds(prev => { const next = new Set(prev); next.delete(selectedUser.id); return next; }); await unAssignOtherUser(taskId as number, selectedUser.id); - updateEventInfo(eventId) - setOpen(false); + updateEventInfo(eventId); }; return <> @@ -75,18 +80,15 @@ export default function AssignUserBtn({ taskId, eventId }: AssignUserBtnProps): setOpen(false)} title={"Assigner un utilisateur à cette tâche"}>
- {loading ? : users.map((user: User, index) => ( -
-

- {user.name} {user.lastname} -

- - {isUserAssigned(user) ? ( - ) : ( - )} From 6fcf45d4ff65cbce79bdd57ba7ea831ffa7b61d5 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 15:56:16 +0100 Subject: [PATCH 06/21] delete unecessary admin listener --- .../listeners/adminNotificationsListener.js | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 src/utils/echo/listeners/adminNotificationsListener.js diff --git a/src/utils/echo/listeners/adminNotificationsListener.js b/src/utils/echo/listeners/adminNotificationsListener.js deleted file mode 100644 index 5210122..0000000 --- a/src/utils/echo/listeners/adminNotificationsListener.js +++ /dev/null @@ -1,24 +0,0 @@ -export const adminNotificationsListener = (echo, userData, setNotifications, setUnreadNotification, updatePendingMembers) => { - if (userData.isAdmin) { - const channelName = "users.admin"; - - const handleNotification = (id, content) => { - const newNotification = { - id, - content, - created_at: new Date().toISOString(), - pivot: { unread: 1 } - }; - setNotifications(prev => [newNotification, ...prev]); - setUnreadNotification(true); - }; - - echo.private(channelName) - .listen(".users.registration", (event) => { - handleNotification(event.notificationId, `Nouvelle demande d'inscription : ${event.user.name} ${event.user.lastname}`); - updatePendingMembers(); - }); - return channelName; - } - return null; -}; \ No newline at end of file From 371b38f3e799e606c458946147dd09cb6521e3bf Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 15:56:35 +0100 Subject: [PATCH 07/21] update user listener to use native laravel notifications --- .../listeners/userNotificationsListener.js | 48 +++++++------------ 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/src/utils/echo/listeners/userNotificationsListener.js b/src/utils/echo/listeners/userNotificationsListener.js index f9ec96b..c23876a 100644 --- a/src/utils/echo/listeners/userNotificationsListener.js +++ b/src/utils/echo/listeners/userNotificationsListener.js @@ -1,37 +1,23 @@ -export const userNotificationsListener = (echo, userData, setNotifications, setUnreadNotification) => { +export const userNotificationsListener = (echo, userData, setNotifications, setUnreadNotification, updatePendingMembers) => { - const channelName = `user.${userData.id}`; + const channelName = `App.Models.User.${userData.id}`; - const handleNotification = (id, content) => { + echo.private(channelName) + .notification((notification) => { + const newNotification = { + id: notification.id, + content: notification.message, + created_at: new Date().toISOString(), + pivot: { unread: 1 } + }; - const newNotification = { - id, - content, - created_at: new Date().toISOString(), - pivot: { unread: 1 } - }; + setNotifications(prev => [newNotification, ...prev]); + setUnreadNotification(true); - setNotifications(prev => [newNotification, ...prev]); - setUnreadNotification(true); - }; - - return echo.private(channelName) - .listen('.event.participation.cancelled', (event) => { - handleNotification(event.notificationId,`L'événement ${event.event.name} a été supprimé. Vous n'y participez donc plus.`); - }) - .listen(`.task.participation.cancelled`, (event) => { - handleNotification(event.notificationId,`La tâche ${event.task.name} de l'événement ${event.event.name} a été supprimée. Vous n'y participez donc plus.`); - }) - .listen('.volunteer.assigned.to.task', (event) => { - handleNotification(event.notificationId,`Vous avez été assigné à la tâche ${event.task.name} de l'événement ${event.event.name}.`); - }) - .listen('.volunteer.unassigned.to.task', (event) => { - handleNotification(event.notificationId,`Vous avez été désassigné de la tâche ${event.task.name} de l'événement ${event.event.name}.`); - }) - .listen('.volunteer.role.updated', (event) => { - handleNotification(event.notificationId, `Votre rôle a changé pour : ${event.role} `); - }) - .listen('.task.date.updated', (event) => { - handleNotification(event.notificationId,`La date de la tâche ${event.task.name} de l'événement ${event.event.name} à été mis à jour. Cette tâche aura maintenant lieu du ${event.start} au ${event.end}.`); + if (notification.type === 'App\\Notifications\\UserRegistered' && updatePendingMembers) { + updatePendingMembers(); + } }); + + return channelName; }; \ No newline at end of file From 1836e66429ae81d6aa7f0716399f9f2c219043c3 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 15:56:43 +0100 Subject: [PATCH 08/21] update user listener to use native laravel notifications --- src/components/Header/Header.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 41cba77..a287f8c 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -5,7 +5,6 @@ import getUserNotifications from "../../utils/notifications/getUserNotifications import deleteNotificationUser from "../../utils/notifications/deleteNotificationUser.js"; import initEcho from "../../utils/echo/initEcho.js" import readNotifications from "../../utils/notifications/readNotifications.js"; -import { adminNotificationsListener } from "../../utils/echo/listeners/adminNotificationsListener"; import { userNotificationsListener } from "../../utils/echo/listeners/userNotificationsListener.js"; import NotificationCard from "../NotificationCard/NotificationCard"; import { EventContext } from "../../contexts/Events/EventContext.js"; @@ -35,8 +34,7 @@ function Header() { try { const echo = initEcho(); echoInstance = echo; - adminNotificationsListener(echo, user, setNotifications, setunreadNotification, updatePendingMembers); - userNotificationsListener(echo, user, setNotifications, setunreadNotification); + userNotificationsListener(echo, user, setNotifications, setunreadNotification, updatePendingMembers); const notifData = await getUserNotifications(); From ef30e27ad7c7ff59666eb8c6962878c98dc53d96 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 17:02:08 +0100 Subject: [PATCH 09/21] use uuid instead of id to delete notifications --- src/components/Header/Header.tsx | 2 +- src/utils/notifications/deleteNotificationUser.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index a287f8c..8616430 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -86,7 +86,7 @@ function Header() { } - async function deleteNotification(notificationId: number) { + async function deleteNotification(notificationId: string) { try { const result = await deleteNotificationUser(notificationId); if (result) { diff --git a/src/utils/notifications/deleteNotificationUser.ts b/src/utils/notifications/deleteNotificationUser.ts index 4322722..ce216e9 100644 --- a/src/utils/notifications/deleteNotificationUser.ts +++ b/src/utils/notifications/deleteNotificationUser.ts @@ -1,6 +1,6 @@ import getXSRFToken from "../getXSRF"; -export default async function deleteNotificationUser(notificationId:number) { +export default async function deleteNotificationUser(notificationId:string) { const csrfToken = await getXSRFToken(); try { From d23fd55134e1c77988d502eab1447b08cb0f62f4 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 17:11:27 +0100 Subject: [PATCH 10/21] rename content into message for notifications --- src/components/Header/Header.tsx | 2 +- src/components/NotificationCard/NotificationCard.tsx | 6 +++--- src/interfaces/notification.interface.ts | 12 +++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 8616430..5faeb33 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -40,7 +40,7 @@ function Header() { const data: Notification[] = notifData || []; setNotifications(data); - const hasUnread = data.some(n => n.pivot && n.pivot.unread === 1); + const hasUnread = data.some(n => n.read_at === null); setunreadNotification(hasUnread); } catch (err) { console.error("Erreur initialisation Header:", err); diff --git a/src/components/NotificationCard/NotificationCard.tsx b/src/components/NotificationCard/NotificationCard.tsx index e2086aa..f411215 100644 --- a/src/components/NotificationCard/NotificationCard.tsx +++ b/src/components/NotificationCard/NotificationCard.tsx @@ -5,7 +5,7 @@ import Notification from "../../interfaces/notification.interface"; interface NotificationCardProps { notifications: Notification[]; - deleteNotification: (id: number) => void; + deleteNotification: (id: string) => void; } function NotificationCard({ notifications, deleteNotification }: NotificationCardProps) { @@ -25,9 +25,9 @@ function NotificationCard({ notifications, deleteNotification }: NotificationCar
-
+

- {notification.content} + {notification.data.message}

diff --git a/src/interfaces/notification.interface.ts b/src/interfaces/notification.interface.ts index b666fa7..ca1d2e0 100644 --- a/src/interfaces/notification.interface.ts +++ b/src/interfaces/notification.interface.ts @@ -1,6 +1,8 @@ export default interface Notification { - id: number; - content: string; - created_at: string | number; - pivot: { unread: number }; -} + id: string;s + data: { + message: string; + }; + read_at: string | null; + created_at: string; +} \ No newline at end of file From 60d6f067b47c71348370a0c98de0d2f4c411b5f5 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 17:24:55 +0100 Subject: [PATCH 11/21] rename content into message for notifications --- src/utils/echo/listeners/userNotificationsListener.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/echo/listeners/userNotificationsListener.js b/src/utils/echo/listeners/userNotificationsListener.js index c23876a..f721a7d 100644 --- a/src/utils/echo/listeners/userNotificationsListener.js +++ b/src/utils/echo/listeners/userNotificationsListener.js @@ -6,9 +6,9 @@ export const userNotificationsListener = (echo, userData, setNotifications, setU .notification((notification) => { const newNotification = { id: notification.id, - content: notification.message, + data: { message: notification.message }, + read_at: null, created_at: new Date().toISOString(), - pivot: { unread: 1 } }; setNotifications(prev => [newNotification, ...prev]); From df591c2ca1cd50a5c49fa6ef7087288de80140d5 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 17:25:45 +0100 Subject: [PATCH 12/21] rename content into message for notifications --- src/interfaces/notification.interface.ts | 2 +- src/utils/echo/listeners/userNotificationsListener.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interfaces/notification.interface.ts b/src/interfaces/notification.interface.ts index ca1d2e0..97debd4 100644 --- a/src/interfaces/notification.interface.ts +++ b/src/interfaces/notification.interface.ts @@ -1,5 +1,5 @@ export default interface Notification { - id: string;s + id: string; data: { message: string; }; diff --git a/src/utils/echo/listeners/userNotificationsListener.js b/src/utils/echo/listeners/userNotificationsListener.js index f721a7d..c518d7b 100644 --- a/src/utils/echo/listeners/userNotificationsListener.js +++ b/src/utils/echo/listeners/userNotificationsListener.js @@ -5,9 +5,9 @@ export const userNotificationsListener = (echo, userData, setNotifications, setU echo.private(channelName) .notification((notification) => { const newNotification = { - id: notification.id, - data: { message: notification.message }, - read_at: null, + id: notification.id, + data: { message: notification.message }, + read_at: null, created_at: new Date().toISOString(), }; From b0792d3bdc9db4ca37d7a5848be071faf9fe5ebe Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 17:27:28 +0100 Subject: [PATCH 13/21] add margin to notificationCard --- src/components/NotificationCard/NotificationCard.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/NotificationCard/NotificationCard.module.css b/src/components/NotificationCard/NotificationCard.module.css index 7fadd31..d0aee50 100644 --- a/src/components/NotificationCard/NotificationCard.module.css +++ b/src/components/NotificationCard/NotificationCard.module.css @@ -5,6 +5,7 @@ gap: 0.75rem; border-bottom: 1px solid rgba(0, 0, 0, 0.08); transition: background 0.2s ease; + margin: 0.5rem 0; } .cardHeader { From 9762c915657c354a560db818767cd10a91d20623 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 18:04:13 +0100 Subject: [PATCH 14/21] create updateUserPassword util --- src/utils/users/updateUserPassword.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/utils/users/updateUserPassword.ts diff --git a/src/utils/users/updateUserPassword.ts b/src/utils/users/updateUserPassword.ts new file mode 100644 index 0000000..7d7ceb3 --- /dev/null +++ b/src/utils/users/updateUserPassword.ts @@ -0,0 +1,12 @@ +import fetchWrapper from "../fetchWrapper"; +import getXSRFToken from "../getXSRF"; + +export default async function updateUserPassword(current_password: string, new_password: string, new_password_confirmation: string) { + const csrfToken = await getXSRFToken(); + return fetchWrapper( + "/api/users/update/password", + { current_password, new_password, new_password_confirmation }, + "POST", + { "X-XSRF-TOKEN": csrfToken } + ); +} \ No newline at end of file From d91b7a7d0fbca552ff260f13071342d8cef79a99 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 18:04:33 +0100 Subject: [PATCH 15/21] add section to update user password in parameters modal --- .../SettingsModal/SettingsModal.tsx | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/pages/Profile/components/SettingsModal/SettingsModal.tsx b/src/pages/Profile/components/SettingsModal/SettingsModal.tsx index 1f0df8b..c31a3b5 100644 --- a/src/pages/Profile/components/SettingsModal/SettingsModal.tsx +++ b/src/pages/Profile/components/SettingsModal/SettingsModal.tsx @@ -11,6 +11,7 @@ import ExportCalendarBtn from "../../../../components/ExportCalendarBtn/ExportBt import { useNavigate } from "react-router"; import toggleEmailNotifications from "../../../../utils/users/toggleEmailNotifications"; import toggleWebNotifications from "../../../../utils/users/toggleWebNotifications"; +import updateUserPassword from "../../../../utils/users/updateUserPassword"; interface TaskType { @@ -55,6 +56,10 @@ export default function SettingsModal() { const [emailNotifications, setEmailNotifications] = useState(user?.email_notifications ?? 0); const [webNotifications, setWebNotifications] = useState(user?.web_notifications ?? 0); + const [currentPassword, setCurrentPassword] = useState(''); + const [newPassword, setNewPassword] = useState(''); + const [newPasswordConfirm, setNewPasswordConfirm] = useState(''); + const handleLogout = (): void => { logout(); }; @@ -104,6 +109,17 @@ export default function SettingsModal() { } }; + const handlePasswordUpdate = async () => { + try { + await updateUserPassword(currentPassword, newPassword, newPasswordConfirm); + setCurrentPassword(''); + setNewPassword(''); + setNewPasswordConfirm(''); + } catch (err) { + console.error(err); + } + }; + return ( <> @@ -158,6 +174,26 @@ export default function SettingsModal() {
+ +
+

Changer le mot de passe

+
+

Mot de passe actuel

+ setCurrentPassword(e.target.value)} type="password" /> +
+
+

Nouveau mot de passe

+ setNewPassword(e.target.value)} type="password" /> +
+
+

Confirmer le nouveau mot de passe

+ setNewPasswordConfirm(e.target.value)} type="password" /> +
+ +
+

Apparence

From 5d7adadf942ce05074ddabc398813669ef2dd9c9 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 18:56:24 +0100 Subject: [PATCH 16/21] add section to update user email in parameters modal --- .../SettingsModal/SettingsModal.tsx | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/pages/Profile/components/SettingsModal/SettingsModal.tsx b/src/pages/Profile/components/SettingsModal/SettingsModal.tsx index c31a3b5..6b0672c 100644 --- a/src/pages/Profile/components/SettingsModal/SettingsModal.tsx +++ b/src/pages/Profile/components/SettingsModal/SettingsModal.tsx @@ -12,6 +12,7 @@ import { useNavigate } from "react-router"; import toggleEmailNotifications from "../../../../utils/users/toggleEmailNotifications"; import toggleWebNotifications from "../../../../utils/users/toggleWebNotifications"; import updateUserPassword from "../../../../utils/users/updateUserPassword"; +import updateUserEmail from "../../../../utils/users/updateUserEmail"; interface TaskType { @@ -59,6 +60,7 @@ export default function SettingsModal() { const [currentPassword, setCurrentPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [newPasswordConfirm, setNewPasswordConfirm] = useState(''); + const [email, setEmail] = useState(''); const handleLogout = (): void => { logout(); @@ -120,6 +122,16 @@ export default function SettingsModal() { } }; + const handleEmailUpdate = async () => { + try { + await updateUserEmail(email); + setEmail(''); + update(); + } catch (err) { + console.error(err); + } + }; + return ( <> @@ -139,7 +151,7 @@ export default function SettingsModal() {

Modifier les informations du compte

-
+

Changer de Prénom

-
+

Changer de nom

-
+

Changer de numéro de téléphone

+
+

Changer l'adresse email

+
+

Nouvelle adresse email

+ setEmail(e.target.value)} type="email" /> +
+ +
+

Changer le mot de passe

-
+

Mot de passe actuel

setCurrentPassword(e.target.value)} type="password" />
-
+

Nouveau mot de passe

setNewPassword(e.target.value)} type="password" />
-
+

Confirmer le nouveau mot de passe

setNewPasswordConfirm(e.target.value)} type="password" />
@@ -202,7 +225,7 @@ export default function SettingsModal() {

Notifications

-
+

Notifications par email

-
+

Notifications sur le site

Date: Sat, 28 Mar 2026 18:56:39 +0100 Subject: [PATCH 17/21] create updateUserEmail util --- src/utils/users/updateUserEmail.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/utils/users/updateUserEmail.ts diff --git a/src/utils/users/updateUserEmail.ts b/src/utils/users/updateUserEmail.ts new file mode 100644 index 0000000..e61364e --- /dev/null +++ b/src/utils/users/updateUserEmail.ts @@ -0,0 +1,12 @@ +import fetchWrapper from "../fetchWrapper"; +import getXSRFToken from "../getXSRF"; + +export default async function updateUserEmail(email: string) { + const csrfToken = await getXSRFToken(); + return fetchWrapper( + "/api/users/update/email/request", + { email }, + "POST", + { "X-XSRF-TOKEN": csrfToken } + ); +} \ No newline at end of file From b755e0441e25ad4683ce6bb52698cc59f1e6ae52 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 18:57:10 +0100 Subject: [PATCH 18/21] create updateUserEmail util --- src/components/NotificationCard/NotificationCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NotificationCard/NotificationCard.tsx b/src/components/NotificationCard/NotificationCard.tsx index f411215..1878e33 100644 --- a/src/components/NotificationCard/NotificationCard.tsx +++ b/src/components/NotificationCard/NotificationCard.tsx @@ -11,7 +11,7 @@ interface NotificationCardProps { function NotificationCard({ notifications, deleteNotification }: NotificationCardProps) { return ( notifications.map((notification, index) => ( -
+
Le {formatDate(notification.created_at)} à {formatTime(notification.created_at)} From dd0c139f1658e7d9075a0344a905b363fed4cce5 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 19:17:19 +0100 Subject: [PATCH 19/21] create confirmEmailChange util --- src/utils/users/confirmEmailChange.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/utils/users/confirmEmailChange.ts diff --git a/src/utils/users/confirmEmailChange.ts b/src/utils/users/confirmEmailChange.ts new file mode 100644 index 0000000..285cc72 --- /dev/null +++ b/src/utils/users/confirmEmailChange.ts @@ -0,0 +1,12 @@ +import fetchWrapper from "../fetchWrapper"; +import getXSRFToken from "../getXSRF"; + +export default async function confirmEmailChange(token: string) { + const csrfToken = await getXSRFToken(); + return fetchWrapper( + "/api/users/update/email/confirm", + { token }, + "POST", + { "X-XSRF-TOKEN": csrfToken } + ); +} \ No newline at end of file From 0ef7cfae0248ac2da02f2049097dd54f26b70cf5 Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 19:17:27 +0100 Subject: [PATCH 20/21] create confirmEmailChange Page --- .../ConfirmEmailChangePage.module.css | 26 ++++++++++++ .../ConfirmEmailChangePage.tsx | 41 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/pages/ConfirmEmailChange/ConfirmEmailChangePage.module.css create mode 100644 src/pages/ConfirmEmailChange/ConfirmEmailChangePage.tsx diff --git a/src/pages/ConfirmEmailChange/ConfirmEmailChangePage.module.css b/src/pages/ConfirmEmailChange/ConfirmEmailChangePage.module.css new file mode 100644 index 0000000..c99cebc --- /dev/null +++ b/src/pages/ConfirmEmailChange/ConfirmEmailChangePage.module.css @@ -0,0 +1,26 @@ +.title { + color: white; + font-size: xx-large; + margin-bottom: 20px; +} + +.content { + display: flex; + flex-direction: column; + z-index: 2; + padding: 10px; +} + +.container { + padding: 20px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 15px; + + p { + font-size: large; + white-space: pre-line; + } +} \ No newline at end of file diff --git a/src/pages/ConfirmEmailChange/ConfirmEmailChangePage.tsx b/src/pages/ConfirmEmailChange/ConfirmEmailChangePage.tsx new file mode 100644 index 0000000..aeca838 --- /dev/null +++ b/src/pages/ConfirmEmailChange/ConfirmEmailChangePage.tsx @@ -0,0 +1,41 @@ +import { useEffect, useState } from "react"; +import { useSearchParams, useNavigate } from "react-router"; +import confirmEmailChange from "../../utils/users/confirmEmailChange"; +import styles from "./ConfirmEmailChangePage.module.css"; +import Background from "../../components/Background/Background"; + +export default function ConfirmEmailChange() { + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + const [message, setMessage] = useState('Validation en cours...'); + const [success, setSuccess] = useState(null); + + useEffect(() => { + const token = searchParams.get('token'); + if (!token) { + setMessage('Token manquant.'); + setSuccess(false); + return; + } + + confirmEmailChange(token) + .then(() => { + setMessage('Votre adresse email a bien été mise à jour.\nVous allez être redirigé vers la page de connexion...'); + setSuccess(true); + setTimeout(() => navigate('/login'), 3000); + }) + .catch(() => { + setMessage('Token invalide ou expiré.'); + setSuccess(false); + }); + }, []); + + return +
+

Validation de votre adresse email

+
+

{message}

+
+
+
+} \ No newline at end of file From 57d68b43de98ff70693f1191e80c00c6451bccef Mon Sep 17 00:00:00 2001 From: Giovanni-Josserand Date: Sat, 28 Mar 2026 19:17:34 +0100 Subject: [PATCH 21/21] add confirmEmailChange Page in router --- src/router.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/router.js b/src/router.js index e17bf44..a33dc0b 100644 --- a/src/router.js +++ b/src/router.js @@ -12,6 +12,7 @@ import PageNotFoundPage from "./pages/PageNotFound/PageNotFoundPage.jsx"; import eventDetail from "./pages/EventDetail/EventDetailPage.tsx"; import legalNotices from "./pages/LegalNotices/LegalNoticesPage.tsx"; import RGPDPage from "./pages/RGPD/RGPDPage.jsx"; +import ConfirmEmailChangePage from "./pages/ConfirmEmailChange/ConfirmEmailChangePage.tsx"; const router = createBrowserRouter([ { @@ -34,6 +35,10 @@ const router = createBrowserRouter([ path: "/404", Component: PageNotFoundPage, }, + { + path: "/confirm/email/change", + Component: ConfirmEmailChangePage, + }, { path: "/", Component: Layout,