Merge branch 'features/linkVolunteersPage' into 'dev'
Features/linkvolunteerspage See merge request sae-but2/2025-26/gestion-benevoles-association/Frontend!26
This commit is contained in:
@@ -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`}>
|
||||||
|
|
||||||
|
{/* Header : titre + bouton expand */}
|
||||||
|
<div className={styles.header}>
|
||||||
|
<h2>{user?.name}</h2>
|
||||||
|
<button
|
||||||
|
className={`${styles.expandButton} glassCard`}
|
||||||
|
onClick={toggleExpand}
|
||||||
|
>
|
||||||
|
{isExpanded ? "▼" : "▲"}
|
||||||
|
</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 { useState, useEffect } from "react";
|
||||||
import React, { useState } from "react";
|
|
||||||
import styles from "./VolunteersPage.module.css";
|
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() {
|
function VolunteerPage() {
|
||||||
const volunteers = [
|
|
||||||
{
|
const [users, setUsers] = useState([]);
|
||||||
name: "LEBLOND Malo",
|
|
||||||
role: "Président",
|
useEffect(() => {
|
||||||
since: "2019",
|
(async () => {
|
||||||
email: "malo.leblond@example.com",
|
const data = await getAllUser();
|
||||||
phone: "08 67 87 32 21",
|
setUsers(data);
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{volunteers.map((volunteer, index) => (
|
{users.map((user, index) => <VolunteerCard key={index} id={user} /> )}
|
||||||
<Volunteer
|
|
||||||
key={index}
|
|
||||||
name={volunteer.name}
|
|
||||||
role={volunteer.role}
|
|
||||||
since={volunteer.since}
|
|
||||||
email={volunteer.email}
|
|
||||||
phone={volunteer.phone}
|
|
||||||
photo={volunteer.photo}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +1,113 @@
|
|||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
margin-top: 50px;
|
||||||
margin-top: 50px;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profileboard {
|
.profileboard {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-radius: 10px;
|
margin-top: 3%;
|
||||||
box-sizing: border-box;
|
display: flex;
|
||||||
margin-top: 3%;
|
flex-direction: column;
|
||||||
position: relative;
|
gap: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.mainContent {
|
.mainContent {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
width: 100%;
|
||||||
align-items: flex-start;
|
flex: 1;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicInfo {
|
.basicInfo {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expandedContent {
|
.expandedContent {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: flex-start;
|
width: 100%;
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profilePictureContainer {
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profilePicture {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 50%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.textInfo {
|
.textInfo {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactInfo {
|
.contactInfo {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactInfo p {
|
.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 {
|
.expandButton {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-left: 10px;
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.voirPlus {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 10px;
|
|
||||||
right: 10px;
|
|
||||||
color: #007bff;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.voirPlus:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.profilePictureContainer {
|
.profilePictureContainer {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
.voirPlus{
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
.container {
|
.container {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 50px auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin-top:50px;
|
}
|
||||||
margin-bottom: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profileboard {
|
.profileboard {
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profilePictureContainer {
|
.profilePictureContainer {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export default async function getAllUser() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users`, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
export default async function getUserById (id) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users/${id}`, {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user