Merge branch 'dev' into 'features/searchBar'
# Conflicts: # src/components/Event/Event.jsx # src/index.css # src/pages/Events/EventsPage.jsx # src/utils/getUser.js # src/utils/getXSRF.js # src/utils/login.js # src/utils/register.js
This commit is contained in:
+10
-139
@@ -1,4 +1,4 @@
|
||||
import {useState} from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Event from "../../components/Event/Event.jsx";
|
||||
import styles from "./EventsPage.module.css";
|
||||
import ToolBar from "../../components/ToolBar/ToolBar.jsx";
|
||||
@@ -36,144 +36,15 @@ function EventsPage(){
|
||||
volunteers_count: 3
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Festival de Musique d’Été",
|
||||
description: "Un week-end de concerts en plein air avec des artistes locaux et internationaux.",
|
||||
image: null,
|
||||
tasks: [
|
||||
{
|
||||
name: "Montage de la scène",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Parc central",
|
||||
description: "Installer la scène, les éclairages et le son.",
|
||||
volunteers_count: 6
|
||||
},
|
||||
{
|
||||
name: "Accueil du public",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Entrée principale",
|
||||
description: "Vérifier les billets et orienter les visiteurs.",
|
||||
volunteers_count: 4
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Conférence Tech Innovante",
|
||||
description: "Journée de conférences et workshops autour des nouvelles technologies.",
|
||||
image: null,
|
||||
tasks: [
|
||||
{
|
||||
name: "Préparation des salles",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Campus Tech",
|
||||
description: "Installer les chaises, projecteurs et matériel pour les conférences.",
|
||||
volunteers_count: 4
|
||||
},
|
||||
{
|
||||
name: "Accueil des intervenants",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Hall principal",
|
||||
description: "Assister les speakers et orienter vers les salles.",
|
||||
volunteers_count: 2
|
||||
},
|
||||
{
|
||||
name: "Gestion du buffet",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Salle de restauration",
|
||||
description: "Distribuer boissons et repas aux participants.",
|
||||
volunteers_count: 3
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Atelier de Jardinage Urbain",
|
||||
description: "Apprenez à cultiver vos plantes en ville et participez à la création de jardins partagés.",
|
||||
image: null,
|
||||
tasks: [
|
||||
{
|
||||
name: "Installation du matériel",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Jardin partagé",
|
||||
description: "Préparer outils et terreaux pour les participants.",
|
||||
volunteers_count: 2
|
||||
},
|
||||
{
|
||||
name: "Encadrement des participants",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Jardin partagé",
|
||||
description: "Aider et conseiller sur la plantation et l’entretien.",
|
||||
volunteers_count: 3
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Collecte de Sang",
|
||||
description: "Aidez à sauver des vies en participant à notre collecte de sang annuelle.",
|
||||
image: null,
|
||||
tasks: [
|
||||
{
|
||||
name: "Accueil et enregistrement",
|
||||
start: "2020-09-24 22:21:20",
|
||||
end: "2020-09-24 22:21:20",
|
||||
place: "Salle municipale",
|
||||
description: "Enregistrer les donneurs et expliquer le processus.",
|
||||
volunteers_count: 4
|
||||
},
|
||||
{
|
||||
name: "Préparation des salles",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Salle municipale",
|
||||
description: "Installer les lits, tables et matériel médical.",
|
||||
volunteers_count: 3
|
||||
},
|
||||
{
|
||||
name: "Assistance post-don",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Salle municipale",
|
||||
description: "Accompagner les donneurs après le don et proposer des rafraîchissements.",
|
||||
volunteers_count: 2
|
||||
},
|
||||
{
|
||||
name: "Accueil et enregistrement",
|
||||
start: "2020-09-24 22:21:20",
|
||||
end: "2020-09-24 22:21:20",
|
||||
place: "Salle municipale",
|
||||
description: "Enregistrer les donneurs et expliquer le processus.",
|
||||
volunteers_count: 4
|
||||
},
|
||||
{
|
||||
name: "Préparation des salles",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Salle municipale",
|
||||
description: "Installer les lits, tables et matériel médical.",
|
||||
volunteers_count: 3
|
||||
},
|
||||
{
|
||||
name: "Assistance post-don",
|
||||
start: "2018-09-24 22:21:20",
|
||||
end: "2018-09-24 22:21:20",
|
||||
place: "Salle municipale",
|
||||
description: "Accompagner les donneurs après le don et proposer des rafraîchissements.",
|
||||
volunteers_count: 2
|
||||
},
|
||||
]
|
||||
},
|
||||
]);
|
||||
}
|
||||
*/
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const data = await getAllEvents();
|
||||
setEvents(data.data);
|
||||
}) ()
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,15 +3,14 @@ import LoginForm from "../../components/loginform/loginForm.jsx";
|
||||
import Background from "../../components/background/background.jsx";
|
||||
|
||||
function LoginPage() {
|
||||
return <Background>
|
||||
|
||||
<div className={styles.container}>
|
||||
<LoginForm />
|
||||
</div>
|
||||
|
||||
</Background>
|
||||
return (
|
||||
<Background>
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>Connexion</h1>
|
||||
<LoginForm />
|
||||
</div>
|
||||
</Background>
|
||||
);
|
||||
}
|
||||
|
||||
export default LoginPage;
|
||||
|
||||
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
.container {
|
||||
position: relative;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
max-height: 80%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-radius:12px;
|
||||
margin-left: 33.3%;
|
||||
border-radius: 12px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.title {
|
||||
font-size: 36px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
text-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
@@ -1,105 +1,63 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useContext } from "react";
|
||||
import styles from "./ProfilePage.module.css";
|
||||
|
||||
function Event({ title,date, tasks }) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const toggleExpand = () => {
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.event} glassCard`}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
||||
<div style={{display:'flex', flexDirection:'column'}}>
|
||||
<h3>{title}</h3>
|
||||
<p style={{ fontSize: '0.8rem', color: '#666', margin: '0 0 0 0' }}>{date}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={toggleExpand}
|
||||
style={{
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
fontSize: '1.2rem',
|
||||
}}
|
||||
>
|
||||
{isExpanded ? '▼' : '▶'}
|
||||
</button>
|
||||
</div>
|
||||
<ul
|
||||
className={`${styles.eventTasks} ${isExpanded ? styles.expanded : ''}`}
|
||||
>
|
||||
{tasks.map((task, index) => (
|
||||
<li key={index}>-- {task}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||
import formatDate from "../../utils/date/formatDate.js";
|
||||
import Task from "../../components/Task/Task";
|
||||
import Button from "../../components/ui/button/button.jsx";
|
||||
import { useNavigate } from "react-router";
|
||||
import ThemeSwitcher from "../../components/ui/themeSwitcher/ThemeSwitcher.jsx";
|
||||
|
||||
function ProfilePage() {
|
||||
|
||||
const { user, logout } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
<div className={styles.contentWrapper}>
|
||||
<div className={styles.profilePictureContainer}>
|
||||
<img
|
||||
src="../src/assets/react.svg"
|
||||
src="/react.svg"
|
||||
alt="Photo de profil"
|
||||
className={styles.profilePicture}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.description}>
|
||||
<h2>LEBLOND Malo</h2>
|
||||
<div className={styles.headerProfile}>
|
||||
<h2> {user.name} {user.lastname} </h2>
|
||||
<div className={styles.settingsBtns}>
|
||||
<ThemeSwitcher />
|
||||
<Button variant={"danger"} onClick={handleLogout}> Déconnexion </Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.topDescription}>
|
||||
<div className={styles.infoBlock}>
|
||||
<p><strong>Role :</strong> Président</p>
|
||||
<p><strong>Membre depuis :</strong> 2019</p>
|
||||
<p><strong>Role :</strong> {user.role}</p>
|
||||
<p><strong>Membre depuis :</strong> {formatDate(user.created_at)}</p>
|
||||
</div>
|
||||
<div className={styles.infoBlock}>
|
||||
<p><strong>Mail :</strong> mailpro@gmail.com</p>
|
||||
<p><strong>Téléphone :</strong> 08 67 87 32 21</p>
|
||||
<p><strong>Mail :</strong> {user.email} </p>
|
||||
<p><strong>Téléphone :</strong> {user.phone}</p>
|
||||
</div>
|
||||
<button href="/../UpdateProfile.jsx">M</button>
|
||||
<Button variant={"transparent"} onClick={() => navigate("/profile/update")}>
|
||||
<img src={"/icons/pen.svg"} alt={"Modify btn"} className={styles.modifyIcon}/>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<h2>Vos Événements :</h2>
|
||||
<Event
|
||||
title="Titre event 1"
|
||||
date="04/11/2025"
|
||||
tasks={["Votre tâche 1", "Votre tâche 2", "Votre tâche 3"]}
|
||||
/>
|
||||
<Event
|
||||
title="Titre event 2"
|
||||
date="04/11/2025"
|
||||
tasks={["Votre tâche 1", "Votre tâche 2", "Votre tâche 3"]}
|
||||
/>
|
||||
<Event
|
||||
title="Titre event 3"
|
||||
date="04/11/2025"
|
||||
tasks={["Votre tâche 1", "Votre tâche 2", "Votre tâche 3"]}
|
||||
/>
|
||||
<Event
|
||||
title="Titre event 4"
|
||||
date="04/11/2025"
|
||||
tasks={["Votre tâche 1", "Votre tâche 2", "Votre tâche 3"]}
|
||||
/>
|
||||
<Event
|
||||
title="Titre event 5"
|
||||
date="04/11/2025"
|
||||
tasks={["Votre tâche 1", "Votre tâche 2", "Votre tâche 3"]}
|
||||
/>
|
||||
<Event
|
||||
title="Titre event 6"
|
||||
date="04/11/2025"
|
||||
tasks={["Votre tâche 1", "Votre tâche 2", "Votre tâche 3"]}
|
||||
/>
|
||||
<Event
|
||||
title="Titre event 7"
|
||||
date="04/11/2025"
|
||||
tasks={["Votre tâche 1", "Votre tâche 2", "Votre tâche 3"]}
|
||||
/>
|
||||
{user.tasks && user.tasks.length > 0 ? (
|
||||
user.tasks.map((task, index) => (
|
||||
<Task key={index} task={task} />
|
||||
))
|
||||
) : (
|
||||
<p>Aucune tâche pour le moment.</p>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
.profileboard {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -51,13 +51,6 @@
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.event {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.eventTasks {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
@@ -73,12 +66,18 @@
|
||||
}
|
||||
.description h2{
|
||||
font-size:30px;
|
||||
margin-bottom: 2%;
|
||||
|
||||
}
|
||||
.profilePicture{
|
||||
width:90%;
|
||||
}
|
||||
/* Tablette (768px et plus) */
|
||||
}
|
||||
|
||||
.settingsBtns {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.contentWrapper {
|
||||
flex-direction: row;
|
||||
@@ -112,7 +111,6 @@
|
||||
}
|
||||
.description h2{
|
||||
font-size:30px;
|
||||
margin-bottom: 2%;
|
||||
}
|
||||
.profilePicture{
|
||||
width:90%;
|
||||
@@ -124,6 +122,19 @@
|
||||
|
||||
}
|
||||
|
||||
.modifyIcon {
|
||||
height:50px;
|
||||
width:50px;
|
||||
}
|
||||
|
||||
.headerProfile {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Desktop (1024px et plus) */
|
||||
@media screen and (min-width: 1024px) {
|
||||
.container {
|
||||
@@ -133,7 +144,7 @@
|
||||
}
|
||||
|
||||
.profileboard {
|
||||
padding: 25px;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.profilePictureContainer {
|
||||
@@ -153,6 +164,5 @@
|
||||
}
|
||||
.description h2{
|
||||
font-size:30px;
|
||||
margin-bottom: 2%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ function RegisterPage() {
|
||||
|
||||
<div className={styles.container}>
|
||||
|
||||
<div className={"glassCard"}>
|
||||
<h1 className={styles.title}>Créer un compte</h1>
|
||||
|
||||
<h1>Créer un compte</h1>
|
||||
<div className={`glassCard ${styles.inputContainer}`}>
|
||||
|
||||
<div className={styles.formGroup}>
|
||||
<Input
|
||||
@@ -62,32 +62,7 @@ function RegisterPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formGroup}>
|
||||
<div style={{ position: 'relative', width: '100%' }}>
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={password}
|
||||
placeholder="Mot de passe"
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '30px',
|
||||
right: '10px',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
{showPassword ? '👁️' : '🔒'}
|
||||
</button>
|
||||
</div>
|
||||
<Input password={true} onChange={e => setPassword(e.target.value)} value={password} />
|
||||
</div>
|
||||
|
||||
<div className={styles.logButton}>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 30%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -9,6 +10,15 @@
|
||||
border-radius:12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
text-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
|
||||
:global(.glassCard) {
|
||||
--glass-bg: #ffffff9f;
|
||||
}
|
||||
@@ -26,4 +36,8 @@
|
||||
h1 {
|
||||
overflow: visible;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inputContainer {
|
||||
padding: 15px;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import React, { useState } from "react";
|
||||
import { Doughnut } from "react-chartjs-2";
|
||||
import { Chart, ArcElement } from "chart.js";
|
||||
import styles from "./StatPage.module.css";
|
||||
import Button from "./../../components/ui/button/button";
|
||||
|
||||
// Enregistrer les éléments nécessaires
|
||||
Chart.register(ArcElement);
|
||||
|
||||
function StatPage() {
|
||||
const [months, setMonths] = useState(3);
|
||||
|
||||
const [participationRate, setParticipationRate] = useState(89);
|
||||
|
||||
const incompleteEvents = [
|
||||
{ title: "Événement A", tasks: "Préparation des flyers" },
|
||||
{ title: "Événement B", tasks: "Réservation de la salle" },
|
||||
{ title: "Événement C", tasks: "Communication sur les réseaux" },
|
||||
];
|
||||
|
||||
const pendingMembers = [
|
||||
{ name: "Antoine Ordonneau" },
|
||||
{ name: "Giovanni Josserand" },
|
||||
{ name: "Quentin T'Jampens" },
|
||||
];
|
||||
|
||||
const handleValidate = (name) => {
|
||||
console.log(`Valider ${name}`);
|
||||
};
|
||||
|
||||
const handleReject = (name) => {
|
||||
console.log(`Refuser ${name}`);
|
||||
};
|
||||
|
||||
const chartData = {
|
||||
labels: ["Progress", "Background"],
|
||||
datasets: [
|
||||
{
|
||||
data: [participationRate, 100 - participationRate],
|
||||
backgroundColor: ["#1e60f9ff", "#ffffffff"],
|
||||
borderWidth: 0,
|
||||
weight:10,
|
||||
cutout: "90%",
|
||||
circumference: 360,
|
||||
rotation: -90,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const chartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
animateScale: true,
|
||||
animateRotate: true,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.leftBlock} glassCard`}>
|
||||
<div className={styles.participationHeader}>
|
||||
<div className={styles.partiText}>
|
||||
<label htmlFor="months">Taux de participation </label>
|
||||
<label htmlFor="months2">depuis : </label>
|
||||
|
||||
<select
|
||||
id="months"
|
||||
value={months}
|
||||
onChange={(e) => setMonths(e.target.value)}
|
||||
className={styles.monthsDropdown}
|
||||
>
|
||||
<option value="1">1 mois</option>
|
||||
<option value="3">3 mois</option>
|
||||
<option value="6">6 mois</option>
|
||||
<option value="12">12 mois</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={styles.chartContainer}>
|
||||
<div style={{ position: "relative", width: "100%", height: "100%" }}>
|
||||
<Doughnut data={chartData} options={chartOptions} />
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
textAlign:"center",
|
||||
transform: "translate(-50%, -50%)",
|
||||
fontSize: "60px",
|
||||
fontWeight: "bold",
|
||||
color: "#333",
|
||||
}}
|
||||
>
|
||||
{participationRate}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.rightSection}>
|
||||
<div className={`${styles.rightBlock} glassCard`}>
|
||||
<h2>Événements incomplets</h2>
|
||||
<div className={styles.eventsContainer}>
|
||||
{incompleteEvents.map((event, index) => (
|
||||
<div key={index} className={`glassCard ${styles.eventCard}`}>
|
||||
<h3>{event.title}</h3>
|
||||
<p>{event.tasks}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${styles.rightBlock} glassCard`}>
|
||||
<h2>Membres en attente de validation</h2>
|
||||
<div className={styles.membersContainer}>
|
||||
{pendingMembers.map((member, index) => (
|
||||
<div key={index} className={`glassCard ${styles.memberCard}`}>
|
||||
<p>{member.name}</p>
|
||||
<div className={styles.buttonGroup}>
|
||||
<Button
|
||||
onClick={() => handleValidate(member.name)}
|
||||
variant="primary"
|
||||
>
|
||||
Valider
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleReject(member.name)}
|
||||
variant="danger"
|
||||
>
|
||||
Refuser
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default StatPage;
|
||||
@@ -0,0 +1,194 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
gap: 20px;
|
||||
}
|
||||
/* style a montrer a giovanni prcq il est + bo */
|
||||
/*.glassCard {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}*/
|
||||
|
||||
.block {
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.leftBlock {
|
||||
width: 40%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height:fit-content;
|
||||
}
|
||||
|
||||
.participationHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
.partiText{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
.participationHeader label{
|
||||
font-size:20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.monthsDropdown {
|
||||
width:25%;
|
||||
border-radius: 5px;
|
||||
opacity: 1;
|
||||
background-color: transparent;
|
||||
border:none;
|
||||
}
|
||||
|
||||
|
||||
.chartContainer {
|
||||
|
||||
max-width: 80%;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.circularProgress {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.progressBackground {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.progressBar {
|
||||
transition: stroke-dasharray 0.5s ease;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.progressText {
|
||||
font-weight: bold;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
|
||||
.rightSection {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.rightBlock {
|
||||
width: 100%;
|
||||
}
|
||||
.rightBlock>h2{
|
||||
font-size:29px;
|
||||
}
|
||||
|
||||
.eventsContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
|
||||
.eventCard {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
padding-bottom:200px;
|
||||
}
|
||||
.eventCard>h3{
|
||||
font-size: 24px;
|
||||
}
|
||||
.membersContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.memberCard {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
.memberCard p{
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
|
||||
.buttonGroup {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: 760px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.leftBlock {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
height:fit-content;
|
||||
}
|
||||
|
||||
.rightSection {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.eventsContainer {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.eventCard {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.donutChart {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.memberCard {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
|
||||
}
|
||||
|
||||
.buttonGroup {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.validateButton,
|
||||
.rejectButton {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import styles from "./VolunteersPage.module.css";
|
||||
import getUserById from "../../utils/users/getUserById.js";
|
||||
import formatDate from "../../utils/date/formatDate.js";
|
||||
import { useNavigate } from "react-router";
|
||||
import Button from "../../components/ui/button/button.jsx";
|
||||
|
||||
export default function VolunteerCard({ id }) {
|
||||
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [user, setUser] = useState({});
|
||||
|
||||
const toggleExpand = () => {
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const data = await getUserById(id);
|
||||
setUser(data);
|
||||
}) ()
|
||||
}, []);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
|
||||
<div className={styles.header}>
|
||||
<h2>{user?.name}</h2>
|
||||
<Button
|
||||
variant={"transparent"}
|
||||
onClick={toggleExpand}
|
||||
className={`${isExpanded ? styles.moreButtonClicked : ""}`}
|
||||
>
|
||||
{">"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className={styles.mainContent}>
|
||||
{!isExpanded ? (
|
||||
<div className={styles.basicInfo}>
|
||||
<p><strong>Rôle :</strong> {user?.role}</p>
|
||||
<p><strong>Membre depuis :</strong> {user?.created_at && formatDate(user.created_at)}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.expandedContent}>
|
||||
<div className={styles.profilePictureContainer}>
|
||||
<img src={"/react.svg"} alt="profile" className={styles.profilePicture} />
|
||||
</div>
|
||||
|
||||
<div className={styles.textInfo}>
|
||||
<div className={styles.basicInfo}>
|
||||
<p><strong>Rôle :</strong> {user?.role}</p>
|
||||
<p><strong>Membre depuis :</strong> {user?.created_at && formatDate(user.created_at)}</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.contactInfo}>
|
||||
<p><strong>Email :</strong> {user?.email}</p>
|
||||
{user?.phone && <p><strong>Téléphone :</strong> {user.phone}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<div className={styles.bottomBar}>
|
||||
<Button
|
||||
onClick={() => navigate(`/profile/${id}`)}
|
||||
variant="transparent"
|
||||
>
|
||||
Voir plus
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,106 +1,23 @@
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import styles from "./VolunteersPage.module.css";
|
||||
import VolunteerCard from "./VolunteerCard.jsx";
|
||||
import getAllUser from "../../utils/users/getAllUsers.js";
|
||||
|
||||
function Volunteer({ name, role, since, email, phone, photo }) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const toggleExpand = () => {
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
<div className={styles.mainContent}>
|
||||
{!isExpanded ? (
|
||||
<div className={styles.basicInfo}>
|
||||
<h2>{name}</h2>
|
||||
<p><strong>Rôle :</strong> {role}</p>
|
||||
<p><strong>Membre depuis :</strong> {since}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.expandedContent}>
|
||||
<div className={styles.profilePictureContainer}>
|
||||
<img src={photo} alt={`${name}'s profile`} className={styles.profilePicture} />
|
||||
</div>
|
||||
<div className={styles.textInfo}>
|
||||
<div className={styles.basicInfo}>
|
||||
<h2>{name}</h2>
|
||||
<p><strong>Rôle :</strong> {role}</p>
|
||||
<p><strong>Membre depuis :</strong> {since}</p>
|
||||
</div>
|
||||
<div className={styles.contactInfo}>
|
||||
<p><strong>Email :</strong> {email}</p>
|
||||
<p><strong>Téléphone :</strong> {phone}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
className={`${styles.expandButton} glassCard`}
|
||||
onClick={toggleExpand}
|
||||
>
|
||||
{isExpanded ? "▲" : "▼"}
|
||||
</button>
|
||||
{isExpanded && (
|
||||
<div className="voirPlus">
|
||||
<a href="#" className={styles.voirPlus}>VOIR PLUS</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function VolunteerPage() {
|
||||
const volunteers = [
|
||||
{
|
||||
name: "LEBLOND Malo",
|
||||
role: "Président",
|
||||
since: "2019",
|
||||
email: "malo.leblond@example.com",
|
||||
phone: "08 67 87 32 21",
|
||||
photo: "../src/assets/react.svg",
|
||||
},
|
||||
{
|
||||
name: "DUPONT Jean",
|
||||
role: "Bénévole",
|
||||
since: "2020",
|
||||
email: "jean.dupont@example.com",
|
||||
phone: "06 12 34 56 78",
|
||||
photo: "../src/assets/react.svg",
|
||||
},
|
||||
{
|
||||
name: "MARTIN Sophie",
|
||||
role: "Bénévole",
|
||||
since: "2021",
|
||||
email: "sophie.martin@example.com",
|
||||
phone: "06 87 65 43 21",
|
||||
photo: "../src/assets/react.svg",
|
||||
},
|
||||
{
|
||||
name: "MARTIN Sophie",
|
||||
role: "Bénévole",
|
||||
since: "2021",
|
||||
email: "sophie.martin@example.com",
|
||||
phone: "06 87 65 43 21",
|
||||
photo: "../src/assets/react.svg",
|
||||
},
|
||||
];
|
||||
|
||||
const [users, setUsers] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const data = await getAllUser();
|
||||
setUsers(data);
|
||||
}) ()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{volunteers.map((volunteer, index) => (
|
||||
<Volunteer
|
||||
key={index}
|
||||
name={volunteer.name}
|
||||
role={volunteer.role}
|
||||
since={volunteer.since}
|
||||
email={volunteer.email}
|
||||
phone={volunteer.phone}
|
||||
photo={volunteer.photo}
|
||||
/>
|
||||
))}
|
||||
{users.map((user, index) => <VolunteerCard key={index} id={user} /> )}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,125 +1,117 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
margin-top: 50px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin-top: 50px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.profileboard {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
margin-top: 3%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
margin-top: 3%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.mainContent {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.basicInfo {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.expandedContent {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.profilePictureContainer {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.profilePicture {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.textInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.contactInfo {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.contactInfo p {
|
||||
margin: 5px 0;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.bottomBar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.profilePictureContainer {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.profilePicture {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.expandButton {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
margin-left: 10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.voirPlus {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.voirPlus:hover {
|
||||
text-decoration: underline;
|
||||
.moreButtonClicked {
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.profilePictureContainer {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
.voirPlus{
|
||||
text-align: right;
|
||||
}
|
||||
.profilePictureContainer {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
margin-top:50px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 50px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.profileboard {
|
||||
padding: 25px;
|
||||
}
|
||||
.profileboard {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.profilePictureContainer {
|
||||
width: 200px;
|
||||
height: auto;
|
||||
}
|
||||
.profilePictureContainer {
|
||||
width: 200px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user