diff --git a/src/components/Event/Event.jsx b/src/components/Event/Event.jsx index 6a75fd1..331f16c 100644 --- a/src/components/Event/Event.jsx +++ b/src/components/Event/Event.jsx @@ -73,7 +73,7 @@ function Event({event}){
{(windowSize.width <= 768 || eventMenu) ? ( - + ) : null}
diff --git a/src/components/SettingsModal/SettingsModal.jsx b/src/components/SettingsModal/SettingsModal.jsx index e39c64d..dee82e7 100644 --- a/src/components/SettingsModal/SettingsModal.jsx +++ b/src/components/SettingsModal/SettingsModal.jsx @@ -14,9 +14,9 @@ export default function SettingsModal() { const [open, setOpen] = useState(false); - const [name, setName] = useState(user.name); - const [lastname, setLastName] = useState(user.lastname); - const [phone, setPhone] = useState(user.phone); + const [name, setName] = useState(user?.name); + const [lastname, setLastName] = useState(user?.lastname); + const [phone, setPhone] = useState(user?.phone); const handleLogout = () => { logout(); diff --git a/src/components/eventTask/EventTask.jsx b/src/components/eventTask/EventTask.jsx new file mode 100644 index 0000000..fd75379 --- /dev/null +++ b/src/components/eventTask/EventTask.jsx @@ -0,0 +1,88 @@ +import styles from "./eventTask.module.css"; +import { formatDateLetter } from "../../utils/date/formatDateLetter.js"; +import Button from "../ui/button/button.jsx"; +import assign from "../../utils/tasks/assign.js"; +import unAssign from "../../utils/tasks/unAssign.js"; +import { useEffect, useState } from "react"; +import isAssigned from "../../utils/tasks/isAssigned.js"; +import Modal from "../ui/modal/modal.jsx"; + +export default function EventTask({ task, index }) { + + 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?.assigned); + } + + useEffect(() => { + checkAssign(); + }, []) + + const handleRegisterBtn = async () => { + const res = await assign(task.id); + + switch (res.status) { + case 200: + 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: + 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/components/eventTask/eventTask.module.css b/src/components/eventTask/eventTask.module.css new file mode 100644 index 0000000..10d2fa7 --- /dev/null +++ b/src/components/eventTask/eventTask.module.css @@ -0,0 +1,41 @@ +.task { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.taskDate { + opacity: 0.5; +} + +.content { + margin: 16px 0px 16px 0px; + padding: 10px 20px 10px 20px; + border-radius: 20px; + border: none; + background: transparent; + box-shadow: + inset 1.25px 1.25px 1px rgba(255, 255, 255, 1), + inset -1.25px -1.25px 1px rgba(255, 255, 255, 1), + inset -0.25px 0.25px 1px rgba(0, 0, 0, 0.2), + inset 0.25px -0.25px 1px rgba(0, 0, 0, 0.2); + opacity: 0; + animation: fadeInUp 0.6s ease-out forwards; + animation-delay: calc(var(--i) * 0.1s); +} + +.modalContent { + text-align: center; +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} \ No newline at end of file diff --git a/src/pages/Profile/ProfilePage.jsx b/src/pages/Profile/ProfilePage.jsx index 9f61466..b45f842 100644 --- a/src/pages/Profile/ProfilePage.jsx +++ b/src/pages/Profile/ProfilePage.jsx @@ -8,6 +8,7 @@ import SettingsModal from "../../components/SettingsModal/SettingsModal.jsx"; function ProfilePage() { const { user } = useContext(AuthContext); + return (
@@ -21,22 +22,22 @@ function ProfilePage() {
-

{user.name} {user.lastname}

+

{user?.name} {user?.lastname}

-

Role : {user.role}

-

Membre depuis : {formatDate(user.created_at)}

+

Role : {user?.role}

+

Membre depuis : {user && formatDate(user.created_at)}

-

Mail : {user.email}

-

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

+

Mail : {user?.email}

+

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

Tâches :

- {user.tasks && user.tasks.length > 0 ? ( + {user && user.tasks.length > 0 ? ( user.tasks.map((task, index) => ( )) diff --git a/src/pages/eventDetail/eventDetail.jsx b/src/pages/eventDetail/eventDetail.jsx index 4d4669a..acaddaa 100644 --- a/src/pages/eventDetail/eventDetail.jsx +++ b/src/pages/eventDetail/eventDetail.jsx @@ -1,70 +1,46 @@ -import {useState} from "react"; +import { useState, useEffect } from "react"; import styles from "./eventDetail.module.css"; import { useParams } from "react-router"; +import getEventById from "../../utils/event/getEventById.js"; +import { formatDateLetter } from "../../utils/date/formatDateLetter.js"; +import EventTask from "../../components/eventTask/EventTask.jsx"; -function eventDetail() { +function EventDetail() { const params = useParams(); const id = params.id; - const [tasks, setTasks] = useState([ - { text: "Préparer les slides", registered: false }, - { text: "Réserver la salle", registered: false }, - { text: "Envoyer les invitations", registered: false }, - { text: "Tester la configuration technique", registered: false }, - { text: "Organiser le catering", registered: false }, - ]); + const [event, setEvent] = useState(null); - const event = { - title: "Conférence React 2025", - date: "27 Décembre 2025", - location: "Paris, France", - description: "azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop azertyuiop" - }; - - const toggleRegister = (index) => { - setTasks((prevTasks) => - prevTasks.map((task, i) => - i === index - ? { ...task, registered: !task.registered } - : task - ) - ); - }; + useEffect(() => { + (async () => { + const e = await getEventById(id); + setEvent(e); + }) () + }, []) return (
- Illustration événement + Illustration événement
-

{event.title}

-

Date: {event.date}

-

Lieu: {event.location}

-

Description: {event.description}

+

{event?.name}

+

{event && formatDateLetter(event.start)}

+

{event?.description}

Tâches

-
    - {tasks.map((task, index) => ( -
  • -
    - {task.text} - -
    -
  • +
    + {event?.tasks?.map((task, index) => ( + ))} -
+
); } -export default eventDetail; \ No newline at end of file +export default EventDetail; \ No newline at end of file diff --git a/src/pages/eventDetail/eventDetail.module.css b/src/pages/eventDetail/eventDetail.module.css index 4c81bb5..edc436f 100644 --- a/src/pages/eventDetail/eventDetail.module.css +++ b/src/pages/eventDetail/eventDetail.module.css @@ -38,55 +38,11 @@ flex-shrink: 0; } -.registerButton { - margin-left: auto; - padding: 5px 10px; - border-radius: 12px; - border: none; - cursor: pointer; - background: #019AFF; - color: white; - font-weight: 600; -} - .registerButton:hover { opacity: 0.85; } -.tasksButton { - display: flex; - align-items: center; - gap: 16px; -} - .tasks { list-style: none; padding: 0; -} - -.tasks li { - margin: 16px 0px 16px 0px; - padding: 10px 20px 10px 20px; - border-radius: 20px; - border: none; - background: transparent; - box-shadow: - inset 1.25px 1.25px 1px rgba(255, 255, 255, 1), - inset -1.25px -1.25px 1px rgba(255, 255, 255, 1), - inset -0.25px 0.25px 1px rgba(0, 0, 0, 0.2), - inset 0.25px -0.25px 1px rgba(0, 0, 0, 0.2); - opacity: 0; - animation: fadeInUp 0.6s ease-out forwards; - animation-delay: calc(var(--i) * 0.1s); -} - -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} +} \ No newline at end of file diff --git a/src/utils/tasks/assign.js b/src/utils/tasks/assign.js new file mode 100644 index 0000000..88f1ba8 --- /dev/null +++ b/src/utils/tasks/assign.js @@ -0,0 +1,37 @@ +import getXSRFToken from "../getXSRF.js"; + +export default async function assign(taskId) { + const csrfToken = await getXSRFToken(); + + try { + const res = await fetch( + `http://${import.meta.env.VITE_API_URL}/api/events/task/assign`, + { + method: 'POST', + credentials: 'include', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'X-XSRF-TOKEN': csrfToken, + }, + body: JSON.stringify({ + task_id: taskId + }), + } + ); + + const data = await res.json(); + + return { + status: res.status, + message: data?.message ?? null, + }; + + } catch (err) { + console.error("❌ Erreur réseau :", err); + return { + status: 0, + message: "Network error", + }; + } +} diff --git a/src/utils/tasks/isAssigned.js b/src/utils/tasks/isAssigned.js new file mode 100644 index 0000000..3b938a1 --- /dev/null +++ b/src/utils/tasks/isAssigned.js @@ -0,0 +1,24 @@ +export default async function isAssigned(taskId) { + try { + const res = await fetch( + `http://${import.meta.env.VITE_API_URL}/api/events/tasks/${taskId}/assigned`, + { + method: 'GET', + credentials: 'include', + headers: { + 'Accept': 'application/json', + }, + } + ); + + if (!res.ok) { + throw new Error(`Erreur serveur : ${res.status}`); + } + + return await res.json(); + + } catch (err) { + console.error('Erreur :', err); + return null; + } +} diff --git a/src/utils/tasks/unAssign.js b/src/utils/tasks/unAssign.js new file mode 100644 index 0000000..509ec82 --- /dev/null +++ b/src/utils/tasks/unAssign.js @@ -0,0 +1,37 @@ +import getXSRFToken from "../getXSRF.js"; + +export default async function unAssign(taskId) { + const csrfToken = await getXSRFToken(); + + try { + const res = await fetch( + `http://${import.meta.env.VITE_API_URL}/api/events/task/unassign`, + { + method: 'POST', + credentials: 'include', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'X-XSRF-TOKEN': csrfToken, + }, + body: JSON.stringify({ + task_id: taskId + }), + } + ); + + const data = await res.json(); + + return { + status: res.status, + message: data?.message ?? null, + }; + + } catch (err) { + console.error("❌ Erreur réseau :", err); + return { + status: 0, + message: "Network error", + }; + } +}