diff --git a/src/components/IncompleteEvent/IncompleteEvent.jsx b/src/components/IncompleteEvent/IncompleteEvent.jsx index 0024070..b5d83c7 100644 --- a/src/components/IncompleteEvent/IncompleteEvent.jsx +++ b/src/components/IncompleteEvent/IncompleteEvent.jsx @@ -5,25 +5,13 @@ import getAllEvents from "../../utils/event/getAllEvents"; function IncompleteEvent() { const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); useEffect(() => { const fetchEvents = async () => { - try { - const data = await getAllEvents(); - if (data) { - setEvents(data); - } else { - setError("Aucun événement trouvé."); - } - } catch (err) { - setError("Erreur lors de la récupération des événements."); - console.error(err); - } finally { - setLoading(false); - } + const data = await getAllEvents(); + setEvents(data?.data || data || []); + setLoading(false); }; - fetchEvents(); }, []); @@ -40,48 +28,29 @@ function IncompleteEvent() { ); } - if (error) { - return ( -
-

Derniers événements

-
-
-

{error}

-
-
-
- ); - } - return (

Derniers événements

- {events.length > 0 ? ( - events.map((event, index) => ( -
-

{event.name}

-

- {new Date(event.start).toLocaleDateString("fr-FR")} - {new Date(event.end).toLocaleDateString("fr-FR")} -

-

{event.description}

-

Tâches associées :

-
    - {event.tasks && event.tasks.length > 0 ? ( - event.tasks.map((task, taskIndex) => ( -
  • {task.name}
  • - )) - ) : ( -
  • Aucune tâche associée.
  • - )} -
-
- )) - ) : ( -
-

Aucun événement disponible.

+ {events.map((event) => ( +
+

{event.name}

+

+ {new Date(event.start).toLocaleDateString("fr-FR")} - {new Date(event.end).toLocaleDateString("fr-FR")} +

+

{event.description}

+

Tâches associées :

+
    + {event.tasks?.length ? ( + event.tasks.map((task, taskIndex) => ( +
  • {task.name}
  • + )) + ) : ( +
  • Aucune tâche associée.
  • + )} +
- )} + ))}
); diff --git a/src/components/PendingMembers/PendingMembers.jsx b/src/components/PendingMembers/PendingMembers.jsx index 828a056..77fabf7 100644 --- a/src/components/PendingMembers/PendingMembers.jsx +++ b/src/components/PendingMembers/PendingMembers.jsx @@ -1,94 +1,8 @@ -/*import React, { useState, useEffect } from "react"; -import Button from "../ui/button/button"; -import styles from "./PendingMembers.module.css"; -import getInvalidUsers from "../../utils/users/getUserToValidate"; -import getUserById from "../../utils/users/getUserById"; -import validateUser from "../../utils/users/validateUser"; -import deleteUser from "../../utils/users/deleteUser"; - -function pendingMembers() { - const [pendingMembers, setPendingMembers] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const fetchPendingMembers = async () => { - const userIds = await getInvalidUsers(); - console.log("IDs reçus :", userIds); - - if (userIds && userIds.length > 0) { - const usersWithDetails = await Promise.all( - userIds.map(async (id) => { - const user = await getUserById(id); - console.log("Utilisateur récupéré :", user); - return user; - }) -); - - console.log("Utilisateurs avec détails :", usersWithDetails); - setPendingMembers(usersWithDetails.filter(user => user !== null)); - } - setLoading(false); - }; - fetchPendingMembers(); - }, []); - -const handleReject = async (id) => { - const res = await deleteUser(id); - - if (!res.ok) { - console.error(res.status, res.data); - alert(res.data?.message || "Action refusée"); - return; - } - - setPendingMembers(prev => prev.filter(u => u.id !== id)); -}; - -const handleValidate = async (id) => { - const res = await validateUser(id); - - if (!res.ok) { - alert(res.data?.message || "Validation refusée"); - return; - } - - setPendingMembers(prev => prev.filter(u => u.id !== id)); -}; - - - - - if (loading) { - return
Chargement des membres en attente...
; - } - - return ( -
-

Membres en attente de validation

-
- {pendingMembers.map((member) => ( -
-

{member.name} {member.lastname}

-
- - -
-
- ))} -
-
- ); -} -export default pendingMembers;*/ import React, { useState, useEffect } from "react"; import Button from "../ui/button/button"; import styles from "./PendingMembers.module.css"; -import getInvalidUsers from "../../utils/users/getUserToValidate"; +import getUserToValidate from "../../utils/users/getUserToValidate"; import getUserById from "../../utils/users/getUserById"; import validateUser from "../../utils/users/validateUser"; import deleteUser from "../../utils/users/deleteUser"; @@ -96,46 +10,62 @@ import deleteUser from "../../utils/users/deleteUser"; function PendingMembers() { const [pendingMembers, setPendingMembers] = useState([]); const [loading, setLoading] = useState(true); + const [actionLoading, setActionLoading] = useState(null); + const [error, setError] = useState(null); useEffect(() => { const fetchPendingMembers = async () => { setLoading(true); - - const userIds = await getInvalidUsers(); - + const userIds = await getUserToValidate(); if (userIds && userIds.length > 0) { - const users = await Promise.all( - userIds.map((id) => getUserById(id)) - ); + const users = await Promise.all(userIds.map((id) => getUserById(id))); setPendingMembers(users.filter(Boolean)); } else { setPendingMembers([]); } - setLoading(false); }; - fetchPendingMembers(); }, []); - const handleValidate = async (id) => { - const ok = await validateUser(id); - - if (!ok) return; - - setPendingMembers((prev) => - prev.filter((member) => member.id !== id) + const removeMember = (id) => { + setPendingMembers(prevMembers => + prevMembers.filter(member => member.id !== id) ); }; + const handleValidate = async (id) => { + setActionLoading(id); + setError(null); + try { + const result = await validateUser(id); + if (result.success) { + removeMember(id); + } else { + setError(result.message || "Échec de la validation."); + } + } catch (err) { + setError("Erreur lors de la validation."); + } finally { + setActionLoading(null); + } + }; + const handleReject = async (id) => { - const ok = await deleteUser(id); - - if (!ok) return; - - setPendingMembers((prev) => - prev.filter((member) => member.id !== id) - ); + setActionLoading(id); + setError(null); + try { + const result = await deleteUser(id); + if (result.success) { + removeMember(id); + } else { + setError(result.message || "Échec de la suppression."); + } + } catch (err) { + setError("Erreur lors de la suppression."); + } finally { + setActionLoading(null); + } }; if (loading) { @@ -145,34 +75,28 @@ function PendingMembers() { return (

Membres en attente de validation

+ {error &&

{error}

}
- {pendingMembers.length === 0 && ( -

Aucun membre en attente

- )} + {pendingMembers.length === 0 &&

Aucun membre en attente

} {pendingMembers.map((member) => ( -
-

- {member.name} {member.lastname} -

- +
+

{member.name} {member.lastname}

-
diff --git a/src/components/chart/ParticipationChart.jsx b/src/components/chart/ParticipationChart.jsx index 1d8dbea..90581b3 100644 --- a/src/components/chart/ParticipationChart.jsx +++ b/src/components/chart/ParticipationChart.jsx @@ -1,14 +1,32 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { Doughnut } from "react-chartjs-2"; import { Chart, ArcElement } from "chart.js"; import { MenuItem, Select, FormControl } from "@mui/material"; import styles from "./ParticipationChart.module.css"; +import getAllEvents from "../../utils/event/getAllEvents"; Chart.register(ArcElement); function ParticipationChart() { const [selected, setSelected] = useState("3"); - const [participationRate, setParticipationRate] = useState(78); + + const [eventsCount, setEventsCount] = useState(0); + const [participationRate, setParticipationRate] = useState(0); + + useEffect(() => { + const fetchEvents = async () => { + const res = await getAllEvents(); + + const eventsArray = res?.data ?? []; + + setEventsCount(eventsArray.length); + + const rate = Math.min(eventsArray.length, 100); + setParticipationRate(rate); + }; + + fetchEvents(); + }, []); const chartData = { labels: ["Progress", "Background"], @@ -17,9 +35,7 @@ function ParticipationChart() { data: [participationRate, 100 - participationRate], backgroundColor: ["#1e60f9ff", "#ffffffff"], borderWidth: 0, - weight: 10, cutout: "93%", - circumference: 360, rotation: -90, }, ], @@ -27,48 +43,39 @@ function ParticipationChart() { const chartOptions = { responsive: true, - maintainAspectRatio: true, plugins: { legend: { display: false }, tooltip: { enabled: false }, }, - animation: { - animateScale: true, - animateRotate: true, - }, }; return (
- -
- - - -
+ + + + +
+
-
{participationRate}%
+
+ {eventsCount} events +
@@ -76,4 +83,3 @@ function ParticipationChart() { } export default ParticipationChart; - diff --git a/src/utils/event/eventCounter.js b/src/utils/event/eventCounter.js new file mode 100644 index 0000000..20ee578 --- /dev/null +++ b/src/utils/event/eventCounter.js @@ -0,0 +1,28 @@ +import { useEffect, useState } from "react"; +import getAllEvents from "../../utils/events/getAllEvents"; + +function EventsCounter() { + const [eventsCount, setEventsCount] = useState(0); + const [loading, setLoading] = useState(true); + useEffect(() => { + const fetchEvents = async () => { + try { + const events = await getAllEvents(); + if (Array.isArray(events)) { + setEventsCount(events.length); + } else { + setEventsCount(0); + } + } catch (err) { + console.error(err); + setEventsCount(0); + } finally { + setLoading(false); + } + }; + fetchEvents(); + }, []); + if (loading) return

Chargement...

; + return

Nombre d’événements : {eventsCount}

; +} +export default EventsCounter; diff --git a/src/utils/users/deleteUser.js b/src/utils/users/deleteUser.js index b8ae5b2..5c4a8d3 100644 --- a/src/utils/users/deleteUser.js +++ b/src/utils/users/deleteUser.js @@ -1,14 +1,35 @@ -export default async function deleteUser(userId) { - const res = await fetch( - `http://${import.meta.env.VITE_API_URL}/api/users/${userId}`, - { - method: "DELETE", - credentials: "include", - headers: { - Accept: "application/json", - }, - } - ); +import getXSRFToken from "../getXSRF"; - return res.ok; +export default async function deleteUser(userId) { + + const csrfToken = await getXSRFToken(); + + try { + + + const res = await fetch( + `http://${import.meta.env.VITE_API_URL}/api/users/${userId}`, + { + method: "DELETE", + credentials: "include", + headers: { + Accept: "application/json", + "X-XSRF-TOKEN": csrfToken, + }, + } + ); + + if (!res.ok) { + let message = "Erreur backend"; + try { + const errorData = await res.json(); + message = errorData.message || message; + } catch {} + return { success: false, message }; + } + return { success: true }; + } catch (err) { + console.error(err); + return { success: false, message: "Erreur réseau" }; + } } diff --git a/src/utils/users/validateUser b/src/utils/users/validateUser deleted file mode 100644 index f5611c8..0000000 --- a/src/utils/users/validateUser +++ /dev/null @@ -1,14 +0,0 @@ -export default async function validateUser(userId) { - const res = await fetch( - `http://${import.meta.env.VITE_API_URL}/api/users/${userId}/validate`, - { - method: "POST", - credentials: "include", - headers: { - Accept: "application/json", - }, - } - ); - - return res.ok; -} diff --git a/src/utils/users/validateUser.js b/src/utils/users/validateUser.js new file mode 100644 index 0000000..21f4d1e --- /dev/null +++ b/src/utils/users/validateUser.js @@ -0,0 +1,27 @@ +import getXSRFToken from "../getXSRF.js"; + +export default async function validateUser(userId) { + const csrfToken = await getXSRFToken(); + try { + const res = await fetch( + `http://${import.meta.env.VITE_API_URL}/api/users/${userId}/validate`, + { + method: "POST", + credentials: "include", + headers: { + Accept: "application/json", + 'X-XSRF-TOKEN': csrfToken, + }, + } + ); + if (!res.ok) { + const errorData = await res.json(); + console.error("Erreur backend:", errorData); + return false; + } + return true; + } catch (err) { + console.error("Erreur réseau:", err); + return false; + } +}