Merge branch 'features/ts' into dev
This commit is contained in:
@@ -22,7 +22,9 @@ export interface User {
|
|||||||
export interface AuthContextType {
|
export interface AuthContextType {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
update: () => void;
|
update: () => void;
|
||||||
|
login: (email: string, password: string) => Promise<{ status: number }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const AuthContext = createContext<AuthContextType | null>(null);
|
export const AuthContext = createContext<AuthContextType | null>(null);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { createContext } from "react";
|
|
||||||
|
|
||||||
export const EventContext = createContext(null);
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*import { createContext } from "react";
|
||||||
|
|
||||||
|
export const EventContext = createContext(null);*/
|
||||||
|
|
||||||
|
|
||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
export type Task = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
start: string | Date;
|
||||||
|
end: string | Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventGroup = {
|
||||||
|
date: string;
|
||||||
|
name: string;
|
||||||
|
start: string | Date;
|
||||||
|
tasks: Task[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventContextType = {
|
||||||
|
events: EventGroup[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EventContext = createContext<EventContextType | null>(null);
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import styles from "./eventList.module.css";
|
|
||||||
import { formatDateLetter } from "../../../../utils/date/formatDateLetter.js";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
export default function EventItem({ eventGroup }) {
|
|
||||||
|
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
|
||||||
|
|
||||||
return <div key={eventGroup.date} className={`${styles.eventGroup} glassBorder`} onClick={() => setIsVisible(!isVisible)}>
|
|
||||||
<div className={styles.eventTitle}>
|
|
||||||
<p className={styles.title}>{eventGroup.name}</p>
|
|
||||||
<p>{formatDateLetter(eventGroup.start)}</p>
|
|
||||||
</div>
|
|
||||||
{isVisible &&
|
|
||||||
<ul className={styles.eventDetails}>
|
|
||||||
{eventGroup.tasks.map((task, i) => (
|
|
||||||
<li key={i} className={styles.eventTasks}>
|
|
||||||
<p>- {task.name} - {task.description}</p>
|
|
||||||
<p>{formatDateLetter(task.start)} - {formatDateLetter(task.end)}</p>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import styles from "./eventList.module.css";
|
||||||
|
import { formatDateLetter } from "../../../../utils/date/formatDateLetter";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
type Task = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
start: string | Date;
|
||||||
|
end: string | Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
type EventGroup = {
|
||||||
|
date: string;
|
||||||
|
name: string;
|
||||||
|
start: string | Date;
|
||||||
|
tasks: Task[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type EventItemProps = {
|
||||||
|
eventGroup: EventGroup;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EventItem({ eventGroup }: EventItemProps) {
|
||||||
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={eventGroup.date}
|
||||||
|
className={`${styles.eventGroup} glassBorder`}
|
||||||
|
onClick={() => setIsVisible(!isVisible)}
|
||||||
|
>
|
||||||
|
<div className={styles.eventTitle}>
|
||||||
|
<p className={styles.title}>{eventGroup.name}</p>
|
||||||
|
<p>{formatDateLetter(eventGroup.start)}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isVisible && (
|
||||||
|
<ul className={styles.eventDetails}>
|
||||||
|
{eventGroup.tasks.map((task, i) => (
|
||||||
|
<li key={i} className={styles.eventTasks}>
|
||||||
|
<p>
|
||||||
|
- {task.name} - {task.description}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{formatDateLetter(task.start)} - {formatDateLetter(task.end)}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*import styles from "./eventList.module.css";
|
||||||
|
import { formatDateLetter } from "../../../../utils/date/formatDateLetter.ts";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export default function EventItem({ eventGroup }) {
|
||||||
|
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
|
return <div key={eventGroup.date} className={`${styles.eventGroup} glassBorder`} onClick={() => setIsVisible(!isVisible)}>
|
||||||
|
<div className={styles.eventTitle}>
|
||||||
|
<p className={styles.title}>{eventGroup.name}</p>
|
||||||
|
<p>{formatDateLetter(eventGroup.start)}</p>
|
||||||
|
</div>
|
||||||
|
{isVisible &&
|
||||||
|
<ul className={styles.eventDetails}>
|
||||||
|
{eventGroup.tasks.map((task, i) => (
|
||||||
|
<li key={i} className={styles.eventTasks}>
|
||||||
|
<p>- {task.name} - {task.description}</p>
|
||||||
|
<p>{formatDateLetter(task.start)} - {formatDateLetter(task.end)}</p>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
import styles from "./eventList.module.css";
|
|
||||||
import EventItem from "./eventItem.jsx";
|
|
||||||
import { EventContext } from "../../../../contexts/events/EventContext.js";
|
|
||||||
import { useContext } from "react";
|
|
||||||
|
|
||||||
function EventList() {
|
|
||||||
// Utilise une valeur par défaut pour `events` si elle est `undefined`
|
|
||||||
const { events = [] } = useContext(EventContext);
|
|
||||||
|
|
||||||
const sortedEvents = useMemo(() => {
|
|
||||||
// Vérifie explicitement que `events` est un tableau avant de trier
|
|
||||||
if (!Array.isArray(events)) return [];
|
|
||||||
return [...events].sort(
|
|
||||||
(a, b) => new Date(a.start) - new Date(b.start)
|
|
||||||
);
|
|
||||||
}, [events]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`${styles.glassCard} glassCard`}>
|
|
||||||
<h2>Liste des événements à venir</h2>
|
|
||||||
{sortedEvents.length > 0 ? (
|
|
||||||
<div className={styles.eventList}>
|
|
||||||
{sortedEvents.map((eventGroup, index) => (
|
|
||||||
<EventItem eventGroup={eventGroup} key={index} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p>Aucun événement planifié pour l'instant.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EventList;
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*import { useMemo } from "react";
|
||||||
|
import styles from "./eventList.module.css";
|
||||||
|
import EventItem from "./eventItem.jsx";
|
||||||
|
import { EventContext } from "../../../../contexts/events/EventContext.js";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
function EventList() {
|
||||||
|
// Utilise une valeur par défaut pour `events` si elle est `undefined`
|
||||||
|
const { events = [] } = useContext(EventContext);
|
||||||
|
|
||||||
|
const sortedEvents = useMemo(() => {
|
||||||
|
// Vérifie explicitement que `events` est un tableau avant de trier
|
||||||
|
if (!Array.isArray(events)) return [];
|
||||||
|
return [...events].sort(
|
||||||
|
(a, b) => new Date(a.start) - new Date(b.start)
|
||||||
|
);
|
||||||
|
}, [events]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.glassCard} glassCard`}>
|
||||||
|
<h2>Liste des événements à venir</h2>
|
||||||
|
{sortedEvents.length > 0 ? (
|
||||||
|
<div className={styles.eventList}>
|
||||||
|
{sortedEvents.map((eventGroup, index) => (
|
||||||
|
<EventItem eventGroup={eventGroup} key={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p>Aucun événement planifié pour l'instant.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventList;*/
|
||||||
|
import { useMemo, useContext } from "react";
|
||||||
|
import styles from "./eventList.module.css";
|
||||||
|
import EventItem from "./eventItem";
|
||||||
|
import { EventContext } from "../../../../contexts/events/EventContext";
|
||||||
|
|
||||||
|
type Task = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
start: string | Date;
|
||||||
|
end: string | Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
type EventGroup = {
|
||||||
|
date: string;
|
||||||
|
name: string;
|
||||||
|
start: string | Date;
|
||||||
|
tasks: Task[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type EventContextType = {
|
||||||
|
events?: EventGroup[];
|
||||||
|
};
|
||||||
|
|
||||||
|
function EventList() {
|
||||||
|
const { events = [] } = useContext(EventContext) as EventContextType;
|
||||||
|
|
||||||
|
const sortedEvents = useMemo<EventGroup[]>(() => {
|
||||||
|
if (!Array.isArray(events)) return [];
|
||||||
|
|
||||||
|
return [...events].sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(a.start).getTime() - new Date(b.start).getTime()
|
||||||
|
);
|
||||||
|
}, [events]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.glassCard} glassCard`}>
|
||||||
|
<h2>Liste des événements à venir</h2>
|
||||||
|
|
||||||
|
{sortedEvents.length > 0 ? (
|
||||||
|
<div className={styles.eventList}>
|
||||||
|
{sortedEvents.map((eventGroup, index) => (
|
||||||
|
<EventItem eventGroup={eventGroup} key={index} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p>Aucun événement planifié pour l'instant.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventList;
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import { useState, useContext } from "react";
|
|
||||||
import styles from "./loginForm.module.css";
|
|
||||||
import Button from "../../../../components/ui/button/button.tsx";
|
|
||||||
import Input from "../../../../components/ui/input/input.tsx";
|
|
||||||
import Modal from "../../../../components/ui/modal/modal.tsx";
|
|
||||||
import { useNavigate } from "react-router";
|
|
||||||
import { AuthContext } from "../../../../contexts/auth/AuthContext.js";
|
|
||||||
|
|
||||||
|
|
||||||
function LoginForm({ className }) {
|
|
||||||
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { login } = useContext(AuthContext);
|
|
||||||
const [email, setEmail] = useState('');
|
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
const [open , setOpen] = useState(false);
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
const res = await login(email, password);
|
|
||||||
if (res.status === 401 || res.status === 422) setOpen(true);
|
|
||||||
else navigate("/");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`${className} glassCard ${styles.inputContainer}`}>
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
placeholder="Adresse mail"
|
|
||||||
onChange={e => setEmail(e.target.value)}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<Input password={true} onChange={e => setPassword(e.target.value)} value={password} placeholder={"Mot de passe"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.logButton}>
|
|
||||||
<Button variant={"transparent"} onClick={() => navigate("/register")}>Vous n'avez pas de compte ?</Button>
|
|
||||||
<Button onClick={handleSubmit} variant={"default"}> Se connecter </Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Modal title={"Impossible de se connecter"} open={open} onClose={() => setOpen(false)}>
|
|
||||||
<p>L'Email ou le mot de passe est incorrect</p>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LoginForm;
|
|
||||||
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { useState, useContext, FormEvent } from "react";
|
||||||
|
import styles from "./loginForm.module.css";
|
||||||
|
import Button from "../../../../components/ui/button/button";
|
||||||
|
import Input from "../../../../components/ui/input/input";
|
||||||
|
import Modal from "../../../../components/ui/modal/modal";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { AuthContext, AuthContextType } from "../../../../contexts/auth/AuthContext";
|
||||||
|
|
||||||
|
interface LoginFormProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoginForm({ className }: LoginFormProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const authContext = useContext(AuthContext) as AuthContextType;
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const { login } = authContext;
|
||||||
|
|
||||||
|
async function handleSubmit(e?: FormEvent) {
|
||||||
|
if (e) e.preventDefault();
|
||||||
|
const res = await login(email, password);
|
||||||
|
if (res.status === 401 || res.status === 422) setOpen(true);
|
||||||
|
else navigate("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${className} glassCard ${styles.inputContainer}`} >
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className={styles.formgroup}>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
placeholder="Adresse mail"
|
||||||
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.formgroup}>
|
||||||
|
<Input
|
||||||
|
password={true}
|
||||||
|
value={password}
|
||||||
|
placeholder="Mot de passe"
|
||||||
|
onChange={e => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.logButton}>
|
||||||
|
<Button variant={"transparent"} onClick={() => navigate("/register")}>Vous n'avez pas de compte ?</Button>
|
||||||
|
<Button onClick={handleSubmit} variant={"default"}>Se connecter</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<Modal title={"Impossible de se connecter"} open={open} onClose={() => setOpen(false)}>
|
||||||
|
<p>L'Email ou le mot de passe est incorrect</p>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoginForm;
|
||||||
@@ -66,6 +66,7 @@ function ProfilePage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
console.log(JSON.stringify(import.meta.env.VITE_API_URL));
|
||||||
if (!isOwnProfile && id) {
|
if (!isOwnProfile && id) {
|
||||||
const res = await getUserById(id);
|
const res = await getUserById(id);
|
||||||
if (res.status === 404) {
|
if (res.status === 404) {
|
||||||
@@ -78,10 +79,11 @@ function ProfilePage() {
|
|||||||
}
|
}
|
||||||
setProfileUser(res.data);
|
setProfileUser(res.data);
|
||||||
} else {
|
} else {
|
||||||
update();
|
//update();
|
||||||
setProfileUser(user);
|
setProfileUser(user);
|
||||||
if (user?.profile_photo_path) {
|
if (user?.profile_photo_path) {
|
||||||
setProfilePicture(`${import.meta.env.VITE_API_URL}/storage/${user.profile_photo_path}`);
|
setProfilePicture(`${import.meta.env.VITE_API_URL}/storage/${user.profile_photo_path}`);
|
||||||
|
//setProfilePicture(`${import.meta.env.VITE_API_URL}/${user.profile_photo_path}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -93,7 +95,6 @@ function ProfilePage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -102,6 +103,7 @@ function ProfilePage() {
|
|||||||
const res = await uploadProfilePhoto(file);
|
const res = await uploadProfilePhoto(file);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setProfilePicture(`${import.meta.env.VITE_API_URL}/storage/${res.data.profile_photo_path}`);
|
setProfilePicture(`${import.meta.env.VITE_API_URL}/storage/${res.data.profile_photo_path}`);
|
||||||
|
//setProfilePicture(`${import.meta.env.VITE_API_URL}/${res.data.profile_photo_path}`);
|
||||||
setSuccess("Photo mise à jour !");
|
setSuccess("Photo mise à jour !");
|
||||||
setError(null);
|
setError(null);
|
||||||
await update();
|
await update();
|
||||||
@@ -114,24 +116,10 @@ function ProfilePage() {
|
|||||||
setSuccess(null);
|
setSuccess(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remplacé : handleDeletePicture extrait vers utils/users/deleteProfilePhoto
|
|
||||||
const handleDeletePicture = async () => {
|
const handleDeletePicture = async () => {
|
||||||
try {
|
|
||||||
const res = await deleteProfilePhoto();
|
deleteProfilePhoto();
|
||||||
if (res.status >= 200 && res.status < 300) {
|
setProfilePicture(null);
|
||||||
setProfilePicture(null);
|
|
||||||
setSuccess("Photo supprimée !");
|
|
||||||
setError(null);
|
|
||||||
await update();
|
|
||||||
} else {
|
|
||||||
setError(res.data?.message || "Erreur lors de la suppression.");
|
|
||||||
setSuccess(null);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError("Erreur réseau.");
|
|
||||||
setSuccess(null);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -152,7 +140,7 @@ function ProfilePage() {
|
|||||||
Modifier la photo
|
Modifier la photo
|
||||||
</button>
|
</button>
|
||||||
{user?.profile_photo_path && (
|
{user?.profile_photo_path && (
|
||||||
<button onClick={handleDeletePicture} className={styles.deletePictureButton}>
|
<button onClick={handleDeletePicture} className={styles.editPictureButton}>
|
||||||
Supprimer
|
Supprimer
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,27 +3,28 @@ import { useNavigate } from "react-router";
|
|||||||
import Button from "../../../../components/ui/button/button";
|
import Button from "../../../../components/ui/button/button";
|
||||||
import User from "../../interfaces/user.interface"
|
import User from "../../interfaces/user.interface"
|
||||||
|
|
||||||
|
|
||||||
interface VolunteersCardProps {
|
interface VolunteersCardProps {
|
||||||
user: User;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VolunteerCard({ user }: VolunteersCardProps) {
|
export default function VolunteerCard({ user }: VolunteersCardProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const handleNavigate = (): void => { navigate(`/profile/${user.id}`) }
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const profilePicture = user.profile_photo_path
|
||||||
|
? `${import.meta.env.VITE_API_URL}/storage/${user.profile_photo_path}`
|
||||||
|
: "/react.svg";
|
||||||
|
|
||||||
const handleNavigate = (): void => { navigate(`/profile/${user.id}`) }
|
return (
|
||||||
|
<div className={`glassCard ${styles.volunteerCard}`}>
|
||||||
return (
|
<div className={styles.header}>
|
||||||
<div className={`glassCard ${styles.volunteerCard}`}>
|
<img src={profilePicture} alt="profile" className={styles.profilePicture} />
|
||||||
<div className={styles.header}>
|
<p> {user.name} {user.lastname} </p>
|
||||||
<img src={"/react.svg"} alt="profile" className={styles.profilePicture} />
|
</div>
|
||||||
<p> {user.name} {user.lastname} </p>
|
<div className={styles.content}>
|
||||||
</div>
|
<p> {user.email} </p>
|
||||||
<div className={styles.content}>
|
<Button variant={"primary"} onClick={handleNavigate}>Voir</Button>
|
||||||
<p> {user.email} </p>
|
</div>
|
||||||
<Button variant={"primary"} onClick={handleNavigate}>Voir</Button>
|
</div>
|
||||||
</div>
|
);
|
||||||
</div>
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -12,5 +12,6 @@ export default interface User {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
verified_at: string | null;
|
verified_at: string | null;
|
||||||
validate: number;
|
validate: number;
|
||||||
|
profile_photo_path?: string | null;
|
||||||
tasks: Task[];
|
tasks: Task[];
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,24 @@
|
|||||||
import fetchWrapper from "../fetchWrapper";
|
import getXSRFToken from "../getXSRF.js";
|
||||||
|
|
||||||
export default async function deleteProfilePhoto(): Promise<{ status: number; ok: boolean; data?: any; error?: string }> {
|
export default async function deleteProfilePhoto(): Promise<{ status: number; ok: boolean; data?: any; error?: string }> {
|
||||||
try {
|
try {
|
||||||
const response = await fetchWrapper("/api/profile-photo", null, "DELETE");
|
const csrfToken = await getXSRFToken();
|
||||||
return { status: response.status || 0, ok: response.status >= 200 && response.status < 300, data: response.data };
|
const response = await fetch(`${import.meta.env.VITE_API_URL}/api/profile-photo`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: { "X-XSRF-TOKEN": decodeURIComponent(csrfToken) },
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
|
||||||
|
const status = response.status;
|
||||||
|
let data = null;
|
||||||
|
try {
|
||||||
|
data = await response.json();
|
||||||
|
} catch (e) {
|
||||||
|
// Pas de JSON dans la réponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status, ok: response.ok, data };
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return { status: 0, ok: false, error: err?.message || "Network error" };
|
return { status: 0, ok: false, error: err?.message || "Erreur réseau" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user