Merge branch 'features/linkEventsPage' into 'dev'
Features/linkeventspage See merge request sae-but2/2025-26/gestion-benevoles-association/Frontend!59
This commit is contained in:
@@ -73,7 +73,7 @@ function Event({event}){
|
||||
<div className={styles.eventContainerBottom}>
|
||||
{(windowSize.width <= 768 || eventMenu) ? (
|
||||
|
||||
<Button onClick={() => navigate(`/event/` + event.id)}>Voir plus</Button>
|
||||
<Button onClick={() => navigate(`/events/` + event.id)}>Voir plus</Button>
|
||||
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 <div style={{"--i": index}} key={index} className={styles.content}>
|
||||
<div className={styles.task}>
|
||||
<div>
|
||||
<span><strong>{task.name}</strong></span>
|
||||
<p className={styles.taskDate}> {formatDateLetter(task.start)} - {formatDateLetter(task.end)} </p>
|
||||
<p>{task.description}</p>
|
||||
</div>
|
||||
{assigned ?
|
||||
<Button variant={"danger"} onClick={() => handleUnassign()}> Se désinscrire </Button>
|
||||
:
|
||||
<Button onClick={() => handleRegisterBtn()}> S’inscrire </Button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Modal open={open} onClose={setOpen} title={title}>
|
||||
<p className={styles.modalContent}>{message}</p>
|
||||
</Modal>
|
||||
</div>
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import SettingsModal from "../../components/SettingsModal/SettingsModal.jsx";
|
||||
function ProfilePage() {
|
||||
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
@@ -21,22 +22,22 @@ function ProfilePage() {
|
||||
</div>
|
||||
<div className={styles.description}>
|
||||
<div className={styles.headerProfile}>
|
||||
<h2> {user.name} {user.lastname} </h2>
|
||||
<h2> {user?.name} {user?.lastname} </h2>
|
||||
</div>
|
||||
<div className={styles.topDescription}>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p><strong>Role :</strong> {user.role}</p>
|
||||
<p><strong>Membre depuis :</strong> {formatDate(user.created_at)}</p>
|
||||
<p><strong>Role :</strong> {user?.role}</p>
|
||||
<p><strong>Membre depuis :</strong> {user && formatDate(user.created_at)}</p>
|
||||
</div>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p><strong>Mail :</strong> {user.email} </p>
|
||||
<p><strong>Téléphone :</strong> {user.phone === null ? "Pas de numéro enregistré" : user.phone}</p>
|
||||
<p><strong>Mail :</strong> {user?.email} </p>
|
||||
<p><strong>Téléphone :</strong> {user?.phone === null ? "Pas de numéro enregistré" : user?.phone}</p>
|
||||
</div>
|
||||
<SettingsModal />
|
||||
</div>
|
||||
|
||||
<h2>Tâches :</h2>
|
||||
{user.tasks && user.tasks.length > 0 ? (
|
||||
{user && user.tasks.length > 0 ? (
|
||||
user.tasks.map((task, index) => (
|
||||
<Task key={index} task={task} />
|
||||
))
|
||||
|
||||
@@ -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 (
|
||||
<div className={styles.allContent}>
|
||||
<div className={`glassCard`}>
|
||||
<div className={styles.textImage}>
|
||||
<img src="/public/icons/theme/dark.svg" alt="Illustration événement" className={styles.eventImage}/>
|
||||
<img src="/icons/theme/dark.svg" alt="Illustration événement" className={styles.eventImage}/>
|
||||
<div className={styles.eventInfo}>
|
||||
<h2>{event.title}</h2>
|
||||
<p>Date: {event.date}</p>
|
||||
<p>Lieu: {event.location}</p>
|
||||
<p>Description: {event.description}</p>
|
||||
<h2>{event?.name}</h2>
|
||||
<p>{event && formatDateLetter(event.start)}</p>
|
||||
<p>{event?.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Tâches</h2>
|
||||
<ul className={styles.tasks}>
|
||||
{tasks.map((task, index) => (
|
||||
<li style={{"--i": index}} key={index}>
|
||||
<div className={styles.tasksButton}>
|
||||
<span><strong>{task.text}</strong></span>
|
||||
<button
|
||||
className={styles.registerButton}
|
||||
onClick={() => toggleRegister(index)}
|
||||
>
|
||||
{task.registered ? "Se désinscrire" : "S’inscrire"}
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<div className={styles.tasks}>
|
||||
{event?.tasks?.map((task, index) => (
|
||||
<EventTask task={task} key={index} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default eventDetail;
|
||||
export default EventDetail;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user