diff --git a/src/contexts/auth/AuthContext.ts b/src/contexts/auth/AuthContext.ts index e4f5d00..ed005d1 100644 --- a/src/contexts/auth/AuthContext.ts +++ b/src/contexts/auth/AuthContext.ts @@ -22,7 +22,9 @@ export interface User { export interface AuthContextType { user: User | null; update: () => void; + login: (email: string, password: string) => Promise<{ status: number }>; } + export const AuthContext = createContext(null); diff --git a/src/contexts/events/EventContext.js b/src/contexts/events/EventContext.js deleted file mode 100644 index 24d62c2..0000000 --- a/src/contexts/events/EventContext.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from "react"; - -export const EventContext = createContext(null); \ No newline at end of file diff --git a/src/contexts/events/EventContext.ts b/src/contexts/events/EventContext.ts new file mode 100644 index 0000000..f3c7792 --- /dev/null +++ b/src/contexts/events/EventContext.ts @@ -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(null); \ No newline at end of file diff --git a/src/pages/Home/components/EventList/eventItem.jsx b/src/pages/Home/components/EventList/eventItem.jsx deleted file mode 100644 index a65f3ee..0000000 --- a/src/pages/Home/components/EventList/eventItem.jsx +++ /dev/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
setIsVisible(!isVisible)}> -
-

{eventGroup.name}

-

{formatDateLetter(eventGroup.start)}

-
- {isVisible && -
    - {eventGroup.tasks.map((task, i) => ( -
  • -

    - {task.name} - {task.description}

    -

    {formatDateLetter(task.start)} - {formatDateLetter(task.end)}

    -
  • - ))} -
- } -
-} \ No newline at end of file diff --git a/src/pages/Home/components/EventList/eventItem.tsx b/src/pages/Home/components/EventList/eventItem.tsx new file mode 100644 index 0000000..9cd3de9 --- /dev/null +++ b/src/pages/Home/components/EventList/eventItem.tsx @@ -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(false); + + return ( +
setIsVisible(!isVisible)} + > +
+

{eventGroup.name}

+

{formatDateLetter(eventGroup.start)}

+
+ + {isVisible && ( +
    + {eventGroup.tasks.map((task, i) => ( +
  • +

    + - {task.name} - {task.description} +

    +

    + {formatDateLetter(task.start)} - {formatDateLetter(task.end)} +

    +
  • + ))} +
+ )} +
+ ); +} + +/*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
setIsVisible(!isVisible)}> +
+

{eventGroup.name}

+

{formatDateLetter(eventGroup.start)}

+
+ {isVisible && +
    + {eventGroup.tasks.map((task, i) => ( +
  • +

    - {task.name} - {task.description}

    +

    {formatDateLetter(task.start)} - {formatDateLetter(task.end)}

    +
  • + ))} +
+ } +
+} +*/ \ No newline at end of file diff --git a/src/pages/Home/components/EventList/eventList.jsx b/src/pages/Home/components/EventList/eventList.jsx deleted file mode 100644 index 9ab2321..0000000 --- a/src/pages/Home/components/EventList/eventList.jsx +++ /dev/null @@ -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 ( -
-

Liste des événements à venir

- {sortedEvents.length > 0 ? ( -
- {sortedEvents.map((eventGroup, index) => ( - - ))} -
- ) : ( -

Aucun événement planifié pour l'instant.

- )} -
- ); -} - -export default EventList; diff --git a/src/pages/Home/components/EventList/eventList.tsx b/src/pages/Home/components/EventList/eventList.tsx new file mode 100644 index 0000000..0fd09ee --- /dev/null +++ b/src/pages/Home/components/EventList/eventList.tsx @@ -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 ( +
+

Liste des événements à venir

+ {sortedEvents.length > 0 ? ( +
+ {sortedEvents.map((eventGroup, index) => ( + + ))} +
+ ) : ( +

Aucun événement planifié pour l'instant.

+ )} +
+ ); +} + +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(() => { + if (!Array.isArray(events)) return []; + + return [...events].sort( + (a, b) => + new Date(a.start).getTime() - new Date(b.start).getTime() + ); + }, [events]); + + return ( +
+

Liste des événements à venir

+ + {sortedEvents.length > 0 ? ( +
+ {sortedEvents.map((eventGroup, index) => ( + + ))} +
+ ) : ( +

Aucun événement planifié pour l'instant.

+ )} +
+ ); +} + +export default EventList; \ No newline at end of file diff --git a/src/pages/Login/components/loginform/loginForm.jsx b/src/pages/Login/components/loginform/loginForm.jsx deleted file mode 100644 index b4b3338..0000000 --- a/src/pages/Login/components/loginform/loginForm.jsx +++ /dev/null @@ -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 ( -
-
- setEmail(e.target.value)} - style={{ width: '100%' }} - /> -
-
- setPassword(e.target.value)} value={password} placeholder={"Mot de passe"} /> -
- -
- - -
- - setOpen(false)}> -

L'Email ou le mot de passe est incorrect

-
-
- ); -} - -export default LoginForm; - diff --git a/src/pages/Login/components/loginform/loginForm.tsx b/src/pages/Login/components/loginform/loginForm.tsx new file mode 100644 index 0000000..32aebb0 --- /dev/null +++ b/src/pages/Login/components/loginform/loginForm.tsx @@ -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 ( +
+
+
+ setEmail(e.target.value)} + style={{ width: '100%' }} + /> +
+
+ setPassword(e.target.value)} + /> +
+ +
+ + +
+
+ + setOpen(false)}> +

L'Email ou le mot de passe est incorrect

+
+
+ ); +} + +export default LoginForm; diff --git a/src/pages/Profile/ProfilePage.tsx b/src/pages/Profile/ProfilePage.tsx index eddae6a..4035569 100644 --- a/src/pages/Profile/ProfilePage.tsx +++ b/src/pages/Profile/ProfilePage.tsx @@ -66,6 +66,7 @@ function ProfilePage() { useEffect(() => { (async () => { + console.log(JSON.stringify(import.meta.env.VITE_API_URL)); if (!isOwnProfile && id) { const res = await getUserById(id); if (res.status === 404) { @@ -78,10 +79,11 @@ function ProfilePage() { } setProfileUser(res.data); } else { - update(); + //update(); setProfileUser(user); if (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) => { const file = e.target.files?.[0]; if (!file) return; @@ -102,6 +103,7 @@ function ProfilePage() { const res = await uploadProfilePhoto(file); if (res.ok) { 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 !"); setError(null); await update(); @@ -114,24 +116,10 @@ function ProfilePage() { setSuccess(null); } }; - - // Remplacé : handleDeletePicture extrait vers utils/users/deleteProfilePhoto const handleDeletePicture = async () => { - try { - const res = await deleteProfilePhoto(); - if (res.status >= 200 && res.status < 300) { - 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); - } + + deleteProfilePhoto(); + setProfilePicture(null); }; return ( @@ -152,7 +140,7 @@ function ProfilePage() { Modifier la photo {user?.profile_photo_path && ( - )} diff --git a/src/pages/Volunteers/components/VolunteersCard/VolunteerCard.tsx b/src/pages/Volunteers/components/VolunteersCard/VolunteerCard.tsx index a3d5f01..5d3c2e8 100644 --- a/src/pages/Volunteers/components/VolunteersCard/VolunteerCard.tsx +++ b/src/pages/Volunteers/components/VolunteersCard/VolunteerCard.tsx @@ -3,27 +3,28 @@ import { useNavigate } from "react-router"; import Button from "../../../../components/ui/button/button"; import User from "../../interfaces/user.interface" - interface VolunteersCardProps { - user: User; + user: User; } 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 ( -
-
- profile -

{user.name} {user.lastname}

-
-
-

{user.email}

- -
-
- ); -} + return ( +
+
+ profile +

{user.name} {user.lastname}

+
+
+

{user.email}

+ +
+
+ ); +} \ No newline at end of file diff --git a/src/pages/Volunteers/interfaces/user.interface.ts b/src/pages/Volunteers/interfaces/user.interface.ts index e22a1cd..ce9065a 100644 --- a/src/pages/Volunteers/interfaces/user.interface.ts +++ b/src/pages/Volunteers/interfaces/user.interface.ts @@ -12,5 +12,6 @@ export default interface User { updated_at: string; verified_at: string | null; validate: number; + profile_photo_path?: string | null; tasks: Task[]; } \ No newline at end of file diff --git a/src/utils/users/deleteProfilePhoto.ts b/src/utils/users/deleteProfilePhoto.ts index d0ca642..a5e830f 100644 --- a/src/utils/users/deleteProfilePhoto.ts +++ b/src/utils/users/deleteProfilePhoto.ts @@ -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 }> { 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 }; + const csrfToken = await getXSRFToken(); + 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) { - return { status: 0, ok: false, error: err?.message || "Network error" }; + return { status: 0, ok: false, error: err?.message || "Erreur réseau" }; } }