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:
@@ -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>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
@@ -3,33 +3,18 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
padding: 10px;
|
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 {
|
.usersList {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
gap: 16px;
|
||||||
align-items: center;
|
width: 100%;
|
||||||
gap: 10px;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.usersList > * {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.profileboard {
|
.profileboard {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 380px;
|
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) {
|
@media screen and (min-width: 1024px) {
|
||||||
.container {
|
.container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
.usersList {
|
.usersList {
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
padding-right: 5em;
|
||||||
|
padding-left: 5em;
|
||||||
}
|
}
|
||||||
.profileboard {
|
.profileboard {
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
|
|||||||
@@ -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
@@ -3,7 +3,7 @@ import Layout from "./layout.jsx";
|
|||||||
import HomePage from "./pages/Home/HomePage";
|
import HomePage from "./pages/Home/HomePage";
|
||||||
import LoginPage from "./pages/Login/LoginPage";
|
import LoginPage from "./pages/Login/LoginPage";
|
||||||
import RegisterPage from "./pages/Register/RegisterPage.jsx";
|
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 ProfilePage from "./pages/Profile/ProfilePage";
|
||||||
import EventsPage from "./pages/Events/EventsPage.jsx";
|
import EventsPage from "./pages/Events/EventsPage.jsx";
|
||||||
import AdminPage from "./pages/Admin/AdminPage.jsx";
|
import AdminPage from "./pages/Admin/AdminPage.jsx";
|
||||||
|
|||||||
Reference in New Issue
Block a user