Merge branch 'refactor/profile-page' into 'dev'
Refactor/profile page See merge request sae-but2/2025-26/gestion-benevoles-association/Frontend!83
This commit is contained in:
@@ -7,7 +7,7 @@ interface ButtonProps {
|
||||
children: ReactNode;
|
||||
variant?: ButtonVariant;
|
||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||
className?: string;
|
||||
className?: string | undefined;
|
||||
}
|
||||
|
||||
const Button = ({
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.profileboard {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.settingImage{
|
||||
width:100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.settingImage {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contentWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
+117
-143
@@ -1,165 +1,139 @@
|
||||
import { useContext, useEffect } from "react";
|
||||
import {useContext, useEffect, useState} from "react";
|
||||
import styles from "./ProfilePage.module.css";
|
||||
|
||||
import { AuthContext } from "../../contexts/auth/AuthContext";
|
||||
import {AuthContext} from "../../contexts/auth/AuthContext";
|
||||
import formatDate from "../../utils/date/formatDate";
|
||||
import Task from "../../components/Task/Task";
|
||||
import SettingsModal from "./components/SettingsModal/SettingsModal";
|
||||
import {useParams} from "react-router";
|
||||
import {useNavigate} from "react-router";
|
||||
import getUserById from "../../utils/users/getUserById.js";
|
||||
import ManageMember from "./components/manageMember/ManageMember.jsx";
|
||||
|
||||
//import Task from "../../components/Task/Task";
|
||||
//import SettingsModal from "./components/SettingsModal/SettingsModal";
|
||||
|
||||
interface TaskType {
|
||||
id: number;
|
||||
title?: string;
|
||||
completed?: boolean;
|
||||
[key: string]: unknown;
|
||||
id: number;
|
||||
title?: string;
|
||||
completed?: boolean;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface User {
|
||||
name: string;
|
||||
lastname: string;
|
||||
role: string;
|
||||
email: string;
|
||||
phone: string | null;
|
||||
created_at: string;
|
||||
tasks: TaskType[];
|
||||
id: number;
|
||||
name: string;
|
||||
lastname: string;
|
||||
role: string;
|
||||
email: string;
|
||||
phone: string | null;
|
||||
created_at: string;
|
||||
isAdmin: boolean;
|
||||
tasks: TaskType[];
|
||||
}
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
update: () => void;
|
||||
user: User;
|
||||
update: () => void;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ProfilePage(){
|
||||
const { user, update } = useContext(AuthContext) as AuthContextType;
|
||||
|
||||
useEffect(() => {
|
||||
update();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
<div className={styles.contentWrapper}>
|
||||
<div className={styles.profilePictureContainer}>
|
||||
<img
|
||||
src="/react.svg"
|
||||
alt="Photo de profil"
|
||||
className={styles.profilePicture}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.description}>
|
||||
<div className={styles.headerProfile}>
|
||||
<h2>
|
||||
{user?.name} {user?.lastname}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className={styles.topDescription}>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p>
|
||||
<strong>Role :</strong> {user?.role}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Membre depuis :</strong>{" "}
|
||||
{user && formatDate(user.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p>
|
||||
<strong>Mail :</strong> {user?.email}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Téléphone :</strong>{" "}
|
||||
{user?.phone ?? "Pas de numéro enregistré"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.settingImage}>
|
||||
{/* <SettingsModal />*/}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Tâches :</h2>
|
||||
|
||||
{/* {user && user.tasks.length > 0 ? (
|
||||
user.tasks.map((task) => (
|
||||
<Task key={task.id} task={task} />
|
||||
))
|
||||
) : (
|
||||
<p>Aucune tâche pour le moment.</p>
|
||||
)}*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProfilePage;
|
||||
|
||||
|
||||
/*import React, { useContext, useEffect } from "react";
|
||||
import styles from "./ProfilePage.module.css";
|
||||
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||
import formatDate from "../../utils/date/formatDate.js";
|
||||
import Task from "../../components/Task/Task";
|
||||
import SettingsModal from "./components/SettingsModal/SettingsModal.jsx";
|
||||
|
||||
function ProfilePage() {
|
||||
|
||||
const { user, update } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
const {id} = useParams();
|
||||
const {user, update} = useContext(AuthContext) as AuthContextType;
|
||||
const [profileUser, setProfileUser] = useState<User | null>(null);
|
||||
|
||||
const isOwnProfile = !id || user.id.toString() === id;
|
||||
|
||||
useEffect(() => {
|
||||
update();
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
<div className={styles.contentWrapper}>
|
||||
<div className={styles.profilePictureContainer}>
|
||||
<img
|
||||
src="/react.svg"
|
||||
alt="Photo de profil"
|
||||
className={styles.profilePicture}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.description}>
|
||||
<div className={styles.headerProfile}>
|
||||
<h2> {user?.name} {user?.lastname} </h2>
|
||||
(async () => {
|
||||
|
||||
if (!isOwnProfile) {
|
||||
console.log(id);
|
||||
const res = await getUserById(id);
|
||||
|
||||
if (res.status === 404) {
|
||||
navigate("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status !== 200) {
|
||||
console.error("Erreur lors du chargement du profil", res);
|
||||
return;
|
||||
}
|
||||
|
||||
setProfileUser(res.data);
|
||||
} else {
|
||||
update();
|
||||
setProfileUser(user)
|
||||
}
|
||||
})()
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
<div className={styles.contentWrapper}>
|
||||
<div className={styles.profilePictureContainer}>
|
||||
<img
|
||||
src="/react.svg"
|
||||
alt="Photo de profil"
|
||||
className={styles.profilePicture}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.description}>
|
||||
<div className={styles.headerProfile}>
|
||||
<h2>
|
||||
{profileUser?.name} {profileUser?.lastname}
|
||||
</h2>
|
||||
{(user?.isAdmin && profileUser && !isOwnProfile) && (
|
||||
<ManageMember userToManage={profileUser}/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.topDescription}>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p>
|
||||
<strong>Role :</strong> {profileUser?.role}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Membre depuis :</strong>{" "}
|
||||
{profileUser && formatDate(profileUser.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p>
|
||||
<strong>Mail :</strong> {profileUser?.email}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Téléphone :</strong>{" "}
|
||||
{profileUser?.phone ?? "Pas de numéro enregistré"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{isOwnProfile && (
|
||||
<div className={styles.settingImage}>
|
||||
<SettingsModal/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h2>Tâches :</h2>
|
||||
|
||||
{profileUser && profileUser.tasks.length > 0 ? (
|
||||
profileUser.tasks.map((task) => (
|
||||
<Task key={task.id} task={task}/>
|
||||
))
|
||||
) : (
|
||||
<p>Aucune tâche pour le moment.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.topDescription}>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p><strong>Role :</strong> {user?.role}</p>
|
||||
<p><strong>Membre depuis :</strong> {user && formatDate(user.created_at)}</p>
|
||||
</div>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p><strong>Mail :</strong> {user?.email} </p>
|
||||
<p><strong>Téléphone :</strong> {user?.phone === null ? "Pas de numéro enregistré" : user?.phone}</p>
|
||||
</div>
|
||||
<div className={styles.settingImage}>
|
||||
<SettingsModal />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Tâches :</h2>
|
||||
{user && 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>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export default ProfilePage;*/
|
||||
export default ProfilePage;
|
||||
@@ -1,86 +0,0 @@
|
||||
import Modal from "../../../../components/ui/modal/modal.tsx";
|
||||
import Button from "../../../../components/ui/button/button.tsx";
|
||||
import styles from "./SettingsModal.module.css"
|
||||
import { useContext, useState } from "react";
|
||||
import ThemeSwitcher from "../../../../components/ui/themeSwitcher/ThemeSwitcher.jsx";
|
||||
import { AuthContext } from "../../../../contexts/auth/AuthContext.js";
|
||||
import TextInput from "../../../../components/ui/input/input.tsx";
|
||||
import updateUser from "../../../../utils/users/updateUser.js";
|
||||
import deleteUser from "../../../../utils/users/deleteUser.js";
|
||||
|
||||
|
||||
export default function SettingsModal() {
|
||||
|
||||
const { user, logout, update } = useContext(AuthContext);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isDelete, setIsDelete] = useState(false);
|
||||
|
||||
const [name, setName] = useState(user?.name);
|
||||
const [lastname, setLastName] = useState(user?.lastname);
|
||||
const [phone, setPhone] = useState(user?.phone);
|
||||
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await updateUser(name, lastname, phone);
|
||||
update();
|
||||
}
|
||||
|
||||
const handleDelete = async () => {
|
||||
await deleteUser();
|
||||
update();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant={"transparent"} onClick={() => setOpen(!open)}>
|
||||
<img src={"/icons/settings-wheel.svg"} alt={"Modify btn"} className={styles.modifyIcon}/>
|
||||
</Button>
|
||||
|
||||
<Modal title={"Paramètres"} open={open} onClose={() => setOpen(false)}>
|
||||
<div className={styles.content}>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h3>Compte</h3>
|
||||
<Button variant={"danger"} onClick={handleLogout}> Déconnexion </Button>
|
||||
<Button variant={"danger"} onClick={() => setIsDelete(true)}> Supprimer le compte </Button>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h4>Changer de Prénom</h4>
|
||||
<TextInput value={name} onChange={e => setName(e.target.value)} type={"text"} />
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h4>Changer de nom</h4>
|
||||
<TextInput value={lastname} onChange={e => setLastName(e.target.value)} type={"text"} />
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h4>Changer de numéro de téléphone</h4>
|
||||
<TextInput value={phone} onChange={e => setPhone(e.target.value)} type={"text"} />
|
||||
</div>
|
||||
|
||||
<Button className={styles.submitBtn} onClick={handleSubmit}>Confirmer</Button>
|
||||
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h3>Apparence</h3>
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
|
||||
<Modal title={"Validation"} open={isDelete} onClose={() => setIsDelete(false)}>
|
||||
<div className={styles.section}>
|
||||
<p className={styles.alignText}> Êtes vous vraiment sûr de vouloir supprimer votre compte ?</p>
|
||||
<Button variant={"danger"} className={styles.submitBtn} onClick={handleDelete}>Confirmer</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,69 +1,94 @@
|
||||
.content {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
.modifyIcon {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.modifyIcon:hover {
|
||||
transform: rotate(15deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-top: 10px;
|
||||
gap: 16px;
|
||||
padding: 18px;
|
||||
border-radius: 12px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.25));
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.section h3 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section h4 {
|
||||
font-size: 0.95rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.submitBtn {
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.section button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.submitBtn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.alignText {
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.section :global(input) {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.content {
|
||||
padding: 24px;
|
||||
padding: 28px;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.section h3 {
|
||||
font-size: 1.2rem;
|
||||
.section {
|
||||
padding: 22px;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.submitBtn {
|
||||
width: auto;
|
||||
align-self: center;
|
||||
min-width: 200px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.section button {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.content {
|
||||
padding: 30px;
|
||||
gap: 30px;
|
||||
padding: 36px;
|
||||
gap: 36px;
|
||||
}
|
||||
|
||||
.section {
|
||||
gap: 14px;
|
||||
padding: 26px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.modifyIcon {
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import {useContext, useState, ChangeEvent} from "react";
|
||||
import Modal from "../../../../components/ui/modal/modal";
|
||||
import Button from "../../../../components/ui/button/button";
|
||||
import styles from "./SettingsModal.module.css";
|
||||
import ThemeSwitcher from "../../../../components/ui/themeSwitcher/ThemeSwitcher.jsx";
|
||||
import {AuthContext} from "../../../../contexts/auth/AuthContext";
|
||||
import TextInput from "../../../../components/ui/input/input";
|
||||
import updateUser from "../../../../utils/users/updateUser.js";
|
||||
import deleteUser from "../../../../utils/users/deleteUser.js";
|
||||
|
||||
interface TaskType {
|
||||
id: number;
|
||||
title?: string;
|
||||
completed?: boolean;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
lastname: string;
|
||||
role: string;
|
||||
email: string;
|
||||
phone: string | null;
|
||||
created_at: string;
|
||||
isAdmin: boolean;
|
||||
tasks: TaskType[];
|
||||
}
|
||||
|
||||
interface AuthContextType {
|
||||
user: User;
|
||||
update: () => void;
|
||||
logout: () => void;
|
||||
}
|
||||
|
||||
export default function SettingsModal() {
|
||||
const {user, logout, update} = useContext(AuthContext) as AuthContextType;
|
||||
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const [isDelete, setIsDelete] = useState<boolean>(false);
|
||||
|
||||
const [name, setName] = useState<string>(user?.name ?? "");
|
||||
const [lastname, setLastName] = useState<string>(user?.lastname ?? "");
|
||||
const [phone, setPhone] = useState<string>(user?.phone ?? "");
|
||||
|
||||
const handleLogout = (): void => {
|
||||
logout();
|
||||
};
|
||||
|
||||
const handleSubmit = async (): Promise<void> => {
|
||||
await updateUser(name, lastname, phone || null);
|
||||
update();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleDelete = async (): Promise<void> => {
|
||||
await deleteUser();
|
||||
update();
|
||||
};
|
||||
|
||||
const handleNameChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setName(e.target.value);
|
||||
};
|
||||
|
||||
const handleLastnameChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setLastName(e.target.value);
|
||||
};
|
||||
|
||||
const handlePhoneChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setPhone(e.target.value);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant="transparent" onClick={() => setOpen((prev) => !prev)}>
|
||||
<img
|
||||
src="/icons/settings-wheel.svg"
|
||||
alt="Modify btn"
|
||||
className={styles.modifyIcon}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Modal
|
||||
title="Paramètres"
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.section}>
|
||||
<h3>Compte</h3>
|
||||
|
||||
<Button variant="danger" onClick={handleLogout}>
|
||||
Déconnexion
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="danger"
|
||||
onClick={() => setIsDelete(true)}
|
||||
>
|
||||
Supprimer le compte
|
||||
</Button>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h4>Changer de Prénom</h4>
|
||||
<TextInput
|
||||
value={name}
|
||||
onChange={handleNameChange}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h4>Changer de nom</h4>
|
||||
<TextInput
|
||||
value={lastname}
|
||||
onChange={handleLastnameChange}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h4>Changer de numéro de téléphone</h4>
|
||||
<TextInput
|
||||
value={phone}
|
||||
onChange={handlePhoneChange}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className={styles.submitBtn}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Confirmer
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className={styles.section}>
|
||||
<h3>Apparence</h3>
|
||||
<ThemeSwitcher/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="Validation"
|
||||
open={isDelete}
|
||||
onClose={() => setIsDelete(false)}
|
||||
>
|
||||
<div className={styles.section}>
|
||||
<p className={styles.alignText}>
|
||||
Êtes vous vraiment sûr de vouloir supprimer votre compte ?
|
||||
</p>
|
||||
<Button
|
||||
variant="danger"
|
||||
className={styles.submitBtn}
|
||||
onClick={handleDelete}
|
||||
>
|
||||
Confirmer
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import styles from "./VolunteerProfilePage.module.css";
|
||||
import { useParams, useNavigate } from "react-router";
|
||||
import { useEffect, useState, useContext } from "react";
|
||||
import getUserById from "../../utils/users/getUserById.js";
|
||||
import formatDate from "../../utils/date/formatDate.js";
|
||||
import Task from "../../components/Task/Task.jsx";
|
||||
import ManageMember from "./components/manageMember/ManageMember.jsx";
|
||||
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||
|
||||
export default function VolunteerProfilePage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { user: currentUser } = useContext(AuthContext);
|
||||
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await getUserById(id);
|
||||
|
||||
if (res.status === 404) {
|
||||
navigate("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status !== 200) {
|
||||
console.error("Erreur lors du chargement du profil", res);
|
||||
return;
|
||||
}
|
||||
|
||||
setUser(res.data);
|
||||
})();
|
||||
}, [id, navigate]);
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.profileboard} glassCard`}>
|
||||
<div className={styles.contentWrapper}>
|
||||
<div className={styles.profilePictureContainer}>
|
||||
<img
|
||||
src="/react.svg"
|
||||
alt="Photo de profil"
|
||||
className={styles.profilePicture}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.description}>
|
||||
<div className={styles.headerProfile}>
|
||||
<h2>{user.name} {user.lastname}</h2>
|
||||
{currentUser?.isAdmin && (
|
||||
<ManageMember userToManage={user} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.topDescription}>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p><strong>Role :</strong> {user.role}</p>
|
||||
<p><strong>Membre depuis :</strong> {formatDate(user.created_at)}</p>
|
||||
</div>
|
||||
<div className={`${styles.infoBlock} glassBorder`}>
|
||||
<p><strong>Mail :</strong> {user.email}</p>
|
||||
<p>
|
||||
<strong>Téléphone :</strong>{" "}
|
||||
{user.phone ?? "Pas de numéro enregistré"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Tâches :</h2>
|
||||
{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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.profileboard {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.contentWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.profilePictureContainer {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.profilePicture {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
margin-left:4%;
|
||||
}
|
||||
|
||||
.topDescription {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.infoBlock {
|
||||
width: 100%;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.eventTasks {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-out, opacity 0.2s ease;
|
||||
opacity: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.eventTasks.expanded {
|
||||
max-height: 500px;
|
||||
opacity: 1;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.description h2{
|
||||
font-size:30px;
|
||||
|
||||
}
|
||||
.profilePicture{
|
||||
width:90%;
|
||||
}
|
||||
|
||||
.settingsBtns {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.contentWrapper {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.profilePictureContainer {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: calc(100% - 170px);
|
||||
margin-left:4%;
|
||||
}
|
||||
|
||||
.topDescription {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.infoBlock {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.event {
|
||||
padding: 15px;
|
||||
}
|
||||
.description h2{
|
||||
font-size:30px;
|
||||
}
|
||||
.profilePicture{
|
||||
width:90%;
|
||||
}
|
||||
}
|
||||
.description button{
|
||||
width:50px;
|
||||
height:50px;
|
||||
|
||||
}
|
||||
|
||||
.headerProfile {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
.container {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.profileboard {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.profilePictureContainer {
|
||||
width: 30%;
|
||||
height:auto;
|
||||
}
|
||||
.profilePicture{
|
||||
width:90%;
|
||||
}
|
||||
.description {
|
||||
width: calc(100% - 200px);
|
||||
margin-left:4%;
|
||||
}
|
||||
|
||||
.event {
|
||||
padding: 18px;
|
||||
}
|
||||
.description h2{
|
||||
font-size:30px;
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -8,7 +8,6 @@ import ProfilePage from "./pages/Profile/ProfilePage";
|
||||
import EventsPage from "./pages/Events/EventsPage.jsx";
|
||||
import AdminPage from "./pages/Admin/AdminPage.jsx";
|
||||
import ValidationErrorPage from "./pages/waitValidationPage/ValidationErrorPage.jsx";
|
||||
import VolunteerProfilePage from "./pages/VolunteerProfile/VolunteerProfilePage.jsx";
|
||||
import PageNotFound from "./pages/PageNotFound/PageNotFound.jsx";
|
||||
import eventDetail from "./pages/eventDetail/eventDetail.jsx";
|
||||
import legalNotices from "./pages/LegalNotices/LegalNotices.jsx";
|
||||
@@ -47,10 +46,6 @@ const router = createBrowserRouter([
|
||||
path: "/admin",
|
||||
Component:AdminPage,
|
||||
},
|
||||
{
|
||||
path:"/profile",
|
||||
Component:ProfilePage,
|
||||
},
|
||||
{
|
||||
path: "/events",
|
||||
Component: EventsPage,
|
||||
@@ -59,9 +54,13 @@ const router = createBrowserRouter([
|
||||
path: "/volunteers",
|
||||
Component: VolunteersPage,
|
||||
},
|
||||
{
|
||||
path:"/profile",
|
||||
Component:ProfilePage,
|
||||
},
|
||||
{
|
||||
path: "/profile/:id",
|
||||
Component: VolunteerProfilePage,
|
||||
Component: ProfilePage,
|
||||
},
|
||||
{
|
||||
path: "/events/:id",
|
||||
|
||||
Reference in New Issue
Block a user