fix inline code with ProfilePicture
This commit is contained in:
@@ -7,12 +7,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.profileboard {
|
.profileboard {
|
||||||
<<<<<<< HEAD
|
|
||||||
width: 100%;
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
=======
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
@@ -23,7 +17,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
>>>>>>> dev
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.contentWrapper {
|
.contentWrapper {
|
||||||
@@ -91,14 +84,6 @@
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
.headerProfile h2 {
|
|
||||||
font-size: 1.6rem;
|
|
||||||
margin: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
=======
|
|
||||||
.description h2 {
|
.description h2 {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@@ -126,7 +111,6 @@
|
|||||||
height: 22px;
|
height: 22px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
>>>>>>> dev
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Conteneur des informations principales */
|
/* Conteneur des informations principales */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import getXSRFToken from "../../utils/getXSRF.js";
|
import getXSRFToken from "../../utils/getXSRF.js";
|
||||||
import fetchWrapper from "../../utils/fetchWrapper";
|
// import fetchWrapper from "../../utils/fetchWrapper";
|
||||||
import {useContext, useEffect, useState} from "react";
|
import {useContext, useEffect, useState} from "react";
|
||||||
import styles from "./ProfilePage.module.css";
|
import styles from "./ProfilePage.module.css";
|
||||||
import {AuthContext} from "../../contexts/auth/AuthContext";
|
import {AuthContext} from "../../contexts/auth/AuthContext";
|
||||||
@@ -12,6 +12,9 @@ import {useRef} from "react";
|
|||||||
import getUserById from "../../utils/users/getUserById.js";
|
import getUserById from "../../utils/users/getUserById.js";
|
||||||
import ManageMember from "./components/manageMember/ManageMember.jsx";
|
import ManageMember from "./components/manageMember/ManageMember.jsx";
|
||||||
|
|
||||||
|
import uploadProfilePhoto from "../../utils/users/uploadProfilePhoto";
|
||||||
|
import deleteProfilePhoto from "../../utils/users/deleteProfilePhoto.js";
|
||||||
|
|
||||||
|
|
||||||
interface TaskType {
|
interface TaskType {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -61,7 +64,6 @@ function ProfilePage() {
|
|||||||
|
|
||||||
const isOwnProfile = !id || user?.id.toString() === id;
|
const isOwnProfile = !id || user?.id.toString() === id;
|
||||||
|
|
||||||
// Charger le profil de l'utilisateur (soi-même ou un autre)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (!isOwnProfile && id) {
|
if (!isOwnProfile && id) {
|
||||||
@@ -85,38 +87,26 @@ function ProfilePage() {
|
|||||||
})();
|
})();
|
||||||
}, [user, id]);
|
}, [user, id]);
|
||||||
|
|
||||||
// Fonctions pour la photo de profil (uniquement si c'est le profil de l'utilisateur connecté)
|
|
||||||
const handleEditPictureClick = () => {
|
const handleEditPictureClick = () => {
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
fileInputRef.current.click();
|
fileInputRef.current.click();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Remplacé : handleFileChange extrait vers utils/users/uploadProfilePhoto
|
||||||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("photo", file);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const csrfToken = await getXSRFToken();
|
const res = await uploadProfilePhoto(file);
|
||||||
const response = await fetch(`${import.meta.env.VITE_API_URL}/api/profile-photo`, {
|
if (res.ok) {
|
||||||
method: "POST",
|
setProfilePicture(`${import.meta.env.VITE_API_URL}/storage/${res.data.profile_photo_path}`);
|
||||||
headers: { "X-XSRF-TOKEN": decodeURIComponent(csrfToken) },
|
|
||||||
credentials: "include",
|
|
||||||
body: formData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
setProfilePicture(`${import.meta.env.VITE_API_URL}/storage/${data.profile_photo_path}`);
|
|
||||||
setSuccess("Photo mise à jour !");
|
setSuccess("Photo mise à jour !");
|
||||||
setError(null);
|
setError(null);
|
||||||
await update();
|
await update();
|
||||||
} else {
|
} else {
|
||||||
const errorData = await response.json();
|
setError(res.data?.message || res.error || "Erreur lors de l'upload.");
|
||||||
setError(errorData.message || "Erreur lors de l'upload.");
|
|
||||||
setSuccess(null);
|
setSuccess(null);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -125,17 +115,17 @@ function ProfilePage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Remplacé : handleDeletePicture extrait vers utils/users/deleteProfilePhoto
|
||||||
const handleDeletePicture = async () => {
|
const handleDeletePicture = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetchWrapper("/api/profile-photo", null, "DELETE");
|
const res = await deleteProfilePhoto();
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (res.status >= 200 && res.status < 300) {
|
||||||
setProfilePicture(null);
|
setProfilePicture(null);
|
||||||
setSuccess("Photo supprimée !");
|
setSuccess("Photo supprimée !");
|
||||||
setError(null);
|
setError(null);
|
||||||
await update();
|
await update();
|
||||||
} else {
|
} else {
|
||||||
const errorData = response.data;
|
setError(res.data?.message || "Erreur lors de la suppression.");
|
||||||
setError(errorData.message || "Erreur lors de la suppression.");
|
|
||||||
setSuccess(null);
|
setSuccess(null);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,58 +1,3 @@
|
|||||||
<<<<<<< HEAD
|
|
||||||
/*import Modal from "../../../../components/ui/modal/modal.jsx";
|
|
||||||
import Button from "../../../../components/ui/button/button.jsx";
|
|
||||||
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.jsx";
|
|
||||||
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"} />
|
|
||||||
=======
|
|
||||||
import {useContext, useState, ChangeEvent} from "react";
|
import {useContext, useState, ChangeEvent} from "react";
|
||||||
import Modal from "../../../../components/ui/modal/modal";
|
import Modal from "../../../../components/ui/modal/modal";
|
||||||
import Button from "../../../../components/ui/button/button";
|
import Button from "../../../../components/ui/button/button";
|
||||||
@@ -164,31 +109,19 @@ export default function SettingsModal() {
|
|||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
>>>>>>> dev
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<h4>Changer de nom</h4>
|
<h4>Changer de nom</h4>
|
||||||
<<<<<<< HEAD
|
|
||||||
<TextInput value={lastname} onChange={e => setLastName(e.target.value)} type={"text"} />
|
|
||||||
=======
|
|
||||||
<TextInput
|
<TextInput
|
||||||
value={lastname}
|
value={lastname}
|
||||||
onChange={handleLastnameChange}
|
onChange={handleLastnameChange}
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
>>>>>>> dev
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<h4>Changer de numéro de téléphone</h4>
|
<h4>Changer de numéro de téléphone</h4>
|
||||||
<<<<<<< HEAD
|
|
||||||
<TextInput value={phone} onChange={e => setPhone(e.target.value)} type={"text"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button className={styles.submitBtn} onClick={handleSubmit}>Confirmer</Button>
|
|
||||||
|
|
||||||
=======
|
|
||||||
<TextInput
|
<TextInput
|
||||||
value={phone}
|
value={phone}
|
||||||
onChange={handlePhoneChange}
|
onChange={handlePhoneChange}
|
||||||
@@ -202,133 +135,15 @@ export default function SettingsModal() {
|
|||||||
>
|
>
|
||||||
Confirmer
|
Confirmer
|
||||||
</Button>
|
</Button>
|
||||||
>>>>>>> dev
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<h3>Apparence</h3>
|
<h3>Apparence</h3>
|
||||||
<<<<<<< HEAD
|
|
||||||
<ThemeSwitcher />
|
|
||||||
=======
|
|
||||||
<ThemeSwitcher/>
|
<ThemeSwitcher/>
|
||||||
>>>>>>> dev
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
import Modal from "../../../../components/ui/modal/modal";
|
|
||||||
import Button from "../../../../components/ui/button/button";
|
|
||||||
import styles from "./SettingsModal.module.css";
|
|
||||||
import { useContext, useState } from "react";
|
|
||||||
import ThemeSwitcher from "../../../../components/ui/themeSwitcher/ThemeSwitcher";
|
|
||||||
import { AuthContext } from "../../../../contexts/auth/AuthContext";
|
|
||||||
import TextInput from "../../../../components/ui/input/input";
|
|
||||||
import updateUser from "../../../../utils/users/updateUser";
|
|
||||||
import deleteUser from "../../../../utils/users/deleteUser";
|
|
||||||
|
|
||||||
interface User {
|
|
||||||
name: string;
|
|
||||||
lastname: string;
|
|
||||||
phone: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AuthContextType {
|
|
||||||
user: User | null;
|
|
||||||
logout: () => void;
|
|
||||||
update: () => 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 | null>(user?.phone || null);
|
|
||||||
|
|
||||||
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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
<Modal
|
<Modal
|
||||||
title="Validation"
|
title="Validation"
|
||||||
open={isDelete}
|
open={isDelete}
|
||||||
@@ -350,4 +165,3 @@ export default function SettingsModal() {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
>>>>>>> dev
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import fetchWrapper from "../fetchWrapper";
|
||||||
|
|
||||||
|
export default async function deleteProfilePhoto(): Promise<{ status: number; ok: boolean; data?: any; error?: string }> {
|
||||||
|
try {
|
||||||
|
const response = await fetchWrapper("/api/profile-photo", null, "DELETE");
|
||||||
|
return { status: response.status || 0, ok: response.status >= 200 && response.status < 300, data: response.data };
|
||||||
|
} catch (err: any) {
|
||||||
|
return { status: 0, ok: false, error: err?.message || "Network error" };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import uploadProfilePhoto from "./uploadProfilePhoto";
|
||||||
|
|
||||||
|
export default async function replaceProfilePhoto(file: File) {
|
||||||
|
return uploadProfilePhoto(file);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import getXSRFToken from "../getXSRF.js";
|
||||||
|
|
||||||
|
export default async function uploadProfilePhoto(file: File): Promise<{ status: number; ok: boolean; data?: any; error?: string }> {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("photo", file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const csrfToken = await getXSRFToken();
|
||||||
|
const response = await fetch(`${import.meta.env.VITE_API_URL}/api/profile-photo`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "X-XSRF-TOKEN": decodeURIComponent(csrfToken) },
|
||||||
|
credentials: "include",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const status = response.status;
|
||||||
|
let data = null;
|
||||||
|
try {
|
||||||
|
data = await response.json();
|
||||||
|
} catch (e) {
|
||||||
|
// no json
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status, ok: response.ok, data };
|
||||||
|
} catch (err: any) {
|
||||||
|
return { status: 0, ok: false, error: err?.message || "Network error" };
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user