Merge branch 'style/volunteersPage' into 'dev'

Style/volunteerspage

See merge request sae-but2/2025-26/gestion-benevoles-association/Frontend!77
This commit is contained in:
T'JAMPENS QUENTIN p2406187
2026-02-10 14:29:46 +00:00
9 changed files with 212 additions and 156 deletions
-78
View File
@@ -1,78 +0,0 @@
import React, {useState} from "react";
import styles from "./VolunteersPage.module.css";
import formatDate from "../../utils/date/formatDate.js";
import { useNavigate } from "react-router";
import Button from "../../components/ui/button/button.tsx";
export default function VolunteerCard({ user }) {
const [isExpanded, setIsExpanded] = useState(false);
const toggleExpand = () => {
setIsExpanded(!isExpanded);
};
const navigate = useNavigate();
return (
<div className={`${styles.profileboard} glassCard`} onClick={(() => setIsExpanded(!isExpanded))}>
<div className={styles.header}>
<h2 className={styles.userName}>
<span className={styles.firstName}>{user?.name}</span>{" "}
<span className={styles.lastName}>{user?.lastname}</span>
</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/${user.id}`)}
variant="primary"
>
Voir plus
</Button>
</div>
)}
</div>
);
}
-53
View File
@@ -1,53 +0,0 @@
import { useState, useEffect } from "react";
import styles from "./VolunteersPage.module.css";
import VolunteerCard from "./VolunteerCard.jsx";
import getAllUser from "../../utils/users/getAllUsers.js";
import ToolBar from "../../components/ToolBar/ToolBar.jsx";
import filter from "../../utils/filter.js";
import getUserById from "../../utils/users/getUserById.js";
function VolunteerPage() {
const [filters, setFilters] = useState({
alphabetical: "asc",
yearOrder: "desc",
});
const [users, setUsers] = useState([]);
useEffect(() => {
(async () => {
const result = await getAllUser();
if (result.status !== 200 || !Array.isArray(result.data)) {
console.error("Erreur lors du chargement des utilisateurs", result);
return;
}
const usersData = await Promise.all(
result.data.map(async (id) => {
const res = await getUserById(id);
if (res.status === 200) {
return res.data;
}
console.warn(`Utilisateur ${id} non récupéré`, res.status);
return null;
})
);
setUsers(usersData.filter(Boolean));
})();
}, []);
return (
<div className={styles.container}>
<ToolBar setFilters={setFilters} filters={filters} showCreate={false} />
<div className={styles.usersList}>
{filter(users, filters, "user").map((user, index) => (
<VolunteerCard key={user.id ?? index} user={user} />
))}
</div>
</div>
);
}
export default VolunteerPage;
+20 -24
View File
@@ -3,33 +3,18 @@
position: relative;
padding: 10px;
}
.userName {
display: flex;
gap: 5px;
font-weight: bold;
font-size: 1rem;
overflow: hidden;
}
.firstName {
white-space: nowrap;
flex-shrink: 0;
}
.lastName {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
max-width: 100px;
}
.usersList {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
display: grid;
gap: 16px;
width: 100%;
grid-template-columns: 1fr;
}
.usersList > * {
min-width: 0;
}
.profileboard {
width: 100%;
max-width: 380px;
@@ -140,12 +125,23 @@
}
}
.loading {
width: 100%;
text-align: center;
font-size: 1.1rem;
opacity: 1;
color: white;
}
@media screen and (min-width: 1024px) {
.container {
padding: 20px;
}
.usersList {
gap: 20px;
grid-template-columns: repeat(3, 1fr);
padding-right: 5em;
padding-left: 5em;
}
.profileboard {
max-width: 700px;
+83
View File
@@ -0,0 +1,83 @@
import { useEffect, useState } from "react";
import styles from "./VolunteersPage.module.css";
import VolunteerCard from "./components/VolunteersCard/VolunteerCard";
import getAllUser from "../../utils/users/getAllUsers";
import ToolBar from "../../components/ToolBar/ToolBar";
import filter from "../../utils/filter";
import getUserById from "../../utils/users/getUserById";
import User from "./interfaces/user.interface";
interface Filters {
alphabetical: "asc" | "desc";
yearOrder: "asc" | "desc";
}
const VolunteerPage: React.FC = () => {
const [filters, setFilters] = useState<Filters>({
alphabetical: "asc",
yearOrder: "desc",
});
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
const fetchUsers = async (): Promise<void> => {
setLoading(true);
try {
const result = await getAllUser();
if (result.status !== 200 || !Array.isArray(result.data)) {
console.error("Erreur lors du chargement des utilisateurs", result);
return;
}
const usersData = await Promise.all(
result.data.map(async (id: number): Promise<User | null> => {
const res = await getUserById(id);
if (res.status === 200) {
return res.data as User;
}
console.warn(`Utilisateur ${id} non récupéré`, res.status);
return null;
})
);
setUsers(usersData.filter((user): user is User => user !== null));
} catch (error) {
console.error("Erreur réseau", error);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
return (
<div className={styles.container}>
<ToolBar
setFilters={setFilters}
filters={filters}
showCreate={false}
/>
<div className={styles.usersList}>
{loading ? (
<div className={styles.loading}>Chargement des bénévoles</div>
) : (
filter(users, filters, "user").map((user: User) => (
<VolunteerCard key={user.id} user={user} />
))
)}
</div>
</div>
);
};
export default VolunteerPage;
@@ -0,0 +1,29 @@
import styles from "./VolunteersCard.module.css";
import { useNavigate } from "react-router";
import Button from "../../../../components/ui/button/button";
import User from "../../interfaces/user.interface"
interface VolunteersCardProps {
user: User;
}
export default function VolunteerCard({ user }: VolunteersCardProps) {
const navigate = useNavigate();
const handleNavigate = (): void => { navigate(`/profile/${user.id}`) }
return (
<div className={`glassCard ${styles.volunteerCard}`}>
<div className={styles.header}>
<img src={"/react.svg"} alt="profile" className={styles.profilePicture} />
<p> {user.name} {user.lastname} </p>
</div>
<div className={styles.content}>
<p> {user.email} </p>
<Button variant={"primary"} onClick={handleNavigate}>Voir</Button>
</div>
</div>
);
}
@@ -0,0 +1,47 @@
.volunteerCard {
display: flex;
flex-direction: column;
gap: 12px;
}
.header {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.header p {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.content {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.content p {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.content button {
flex-shrink: 0;
}
.profilePicture {
background-color: #1d1d1f;
border-radius: 20px;
width: 40px;
height: 40px;
flex-shrink: 0;
}
@@ -0,0 +1,16 @@
export default interface Task {
id: number;
name: string;
description: string;
location: string;
start: string;
end: string;
max_participants: number;
events_id: number;
created_at: string;
updated_at: string;
pivot: {
user_id: number;
task_id: number;
}
}
@@ -0,0 +1,16 @@
import Task from "./task.interface";
export default interface User {
id: number;
name: string;
lastname: string;
email: string;
role: string;
isAdmin: boolean;
phone: string | null;
created_at: string;
updated_at: string;
verified_at: string | null;
validate: number;
tasks: Task[];
}
+1 -1
View File
@@ -3,7 +3,7 @@ import Layout from "./layout.jsx";
import HomePage from "./pages/Home/HomePage";
import LoginPage from "./pages/Login/LoginPage";
import RegisterPage from "./pages/Register/RegisterPage.jsx";
import VolunteersPage from "./pages/Volunteers/VolunteersPage";
import VolunteersPage from "./pages/Volunteers/VolunteersPage.tsx";
import ProfilePage from "./pages/Profile/ProfilePage";
import EventsPage from "./pages/Events/EventsPage.jsx";
import AdminPage from "./pages/Admin/AdminPage.jsx";