From 46396d035b00ac2ff4fc580df324be12c31367d1 Mon Sep 17 00:00:00 2001 From: tuanvu Date: Fri, 9 Jan 2026 10:46:12 +0100 Subject: [PATCH 1/5] change endpoints --- .../FrisbYEE/rest/UserSyncResource.java | 41 ----------- front_end/src/api.ts | 6 -- front_end/src/components/edt.tsx | 5 +- front_end/src/requetes.tsx | 70 ++++++++----------- 4 files changed, 32 insertions(+), 90 deletions(-) delete mode 100644 back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java deleted file mode 100644 index f4adae2..0000000 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java +++ /dev/null @@ -1,41 +0,0 @@ -package hackathon.FrisbYEE.rest; - - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.core.context.SecurityContextHolder; - -import hackathon.FrisbYEE.jpa.metier.Athlete; -import hackathon.FrisbYEE.jpa.service.AthleteDAO; -import jakarta.transaction.Transactional; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/users") -@CrossOrigin(origins = "http://localhost:3000") -public class UserSyncResource { - @Autowired - private AthleteDAO athleteDAO; - - @PostMapping("/sync") - @Transactional - public ResponseEntity sync() { - Jwt jwt = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - String keycloakId = jwt.getSubject(); - String firstName = jwt.getClaimAsString("given_name"); - String lastName = jwt.getClaimAsString("family_name"); - if (!athleteDAO.existsByKeycloakId(keycloakId)) { - Athlete athlete = new Athlete(); - athlete.setKeycloakId(keycloakId); - athlete.setName(lastName); - athlete.setPrenom(firstName); - athlete.setRole(hackathon.FrisbYEE.jpa.metier.Role.athlete); - athleteDAO.save(athlete); - } - return ResponseEntity.ok().build(); - } -} \ No newline at end of file diff --git a/front_end/src/api.ts b/front_end/src/api.ts index db8c7e7..b6c6e87 100644 --- a/front_end/src/api.ts +++ b/front_end/src/api.ts @@ -70,9 +70,6 @@ export const sessionService = { delete: (id: number | string) => api.delete(`/session/delete/${id}`), update: (id: number | string, data: any) => api.put(`/session/update/${id}`, data), - // plural variants used around the frontend (keep for compatibility) - createPlural: (data: any) => api.post(`/sessions`, data), - getAllPlural: () => api.get(`/sessions`), getActivities: (sessionId: number | string) => api.get(`/sessions/${sessionId}/activities`), addActivity: (sessionId: number | string, activity: any) => api.post(`/sessions/${sessionId}/activities`, activity), subscribe: (sessionId: number | string, userId: number | string) => api.post(`/sessions/${sessionId}/subscribe`, { userId }), @@ -87,9 +84,6 @@ export const coachService = { update: (id: number | string, data: any) => api.put(`/coach/update/${id}`, data), delete: (id: number | string) => api.delete(`/coach/delete/${id}`), getSessionsForCoach: (coachId: number | string) => api.get(`/coach/${coachId}/session`), - // plural convenience - createPlural: (data: any) => api.post(`/coaches`, data), - getAllPlural: () => api.get(`/coaches`), }; export const userService = { diff --git a/front_end/src/components/edt.tsx b/front_end/src/components/edt.tsx index db53199..4ed0d31 100644 --- a/front_end/src/components/edt.tsx +++ b/front_end/src/components/edt.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react" import { Athlete, Coach, Session} from "../classes" import { useLocalData } from "../context/useLocalData" import './style/edt.css'; -import {updateSessionsOfUserAPI } from "../requetes"; +import {getSessionsOfUserAPI } from "../requetes"; import EdtSession from "./edt_session"; import {delay} from "../requetes"; import Loading from "./loading"; @@ -34,9 +34,6 @@ export const EDT =() =>{ const week_days:String[] = ["Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi","Dimanche"]; const week_days_nums:number[] = [1,2,3,4,5,6,0]; - - - function loadSessions(date:Date){ var maxDate = getNextDay(date,6) diff --git a/front_end/src/requetes.tsx b/front_end/src/requetes.tsx index 4f5db60..cbb9879 100644 --- a/front_end/src/requetes.tsx +++ b/front_end/src/requetes.tsx @@ -2,6 +2,7 @@ import api, { activiteService, athleteService, coachService, sessionService } fr import { Activite, Admin, Athlete, Coach, Session, User } from "./classes"; import Keycloak from 'keycloak-js' import { AdminDTO, AthleteDTO, CoachDTO } from "./classesDTO"; +import { useLocalData } from "./context/useLocalData"; //debug: export function delay(ms: number): Promise { @@ -45,24 +46,6 @@ export async function getUser(keycloak:Keycloak): Promise{ } } -/* -retourne toutes les Session dont l'user est inscrit -*/ -export async function updateSessionsOfUserAPI(user:Coach|Athlete, min: Date|null, max: Date|null){ - try { - const response = await api.get(`/users/${user.id}/sessions`, { - params: { - minDate: min ? min.toISOString() : undefined, - maxDate: max ? max.toISOString() : undefined - } - }); - return response.data; - } catch (error) { - console.error("Error fetching sessions for user:", error); - return []; - } -} - export async function updateActivitiesOfSessionAPI(session:Session){ try { const response = await api.get(`/sessions/${session.id}/activities`); @@ -95,10 +78,6 @@ export async function unsubscribeSessionAPI(user:User, session:Session):Promise< // ADMIN : -export async function updateAllSessionAPI(min: Date, max: Date){ - //TODO -} - export async function updateAllUserAPI(){ } @@ -193,26 +172,27 @@ export async function setSessionCreneauAPI(session: Session, date:Date){ } -export async function getSessionsAPI(): Promise { +//GET ///////////////////////////////////////////////////////// + +//SESSION + +export async function getSessionsOfUserAPI(user:Coach|Athlete){ try { - const response = await api.get("/session"); - return response.data; - } catch (error) { - console.error("Error fetching sessions:", error); - throw error; - } -} -export async function getUsersAPI(): Promise { - try { - const response = await api.get("/coach/all"); - return response.data; - } catch (error) { - console.error("Error fetching users:", error); - throw error; + if (user instanceof Coach) { + const response = await coachService.getSessionsForCoach(user.id); //TODO + return response.data; + }else if (user instanceof Athlete) { + const response = await athleteService.getSessionsForAthlete(user.id); //TODO + return response.data; + } + }catch (error) { + console.error("Error fetching sessions for user:", error); + return []; } } -export async function getCoachsAPI(): Promise { +//COACH +export async function getAllCoach(): Promise { try { const response = await api.get("/coach/all"); console.log(response); @@ -221,4 +201,16 @@ export async function getCoachsAPI(): Promise { console.error("Error fetching coachs:", error); throw error; } -} \ No newline at end of file +} + +//ATHLETE +export async function getAllAthlete(): Promise { + try { + const response = await athleteService.getAll(); + return response.data; + } catch (error) { + console.error("Error fetching users:", error); + throw error; + } +} + From b4200cc02956217ca0ec4d0fabb3cb9c2ff9b72e Mon Sep 17 00:00:00 2001 From: trochas Date: Fri, 9 Jan 2026 10:46:41 +0100 Subject: [PATCH 2/5] correction login --- front_end/src/components/login.tsx | 69 ++++++++++++++---------------- front_end/src/requetes.tsx | 56 ++++++++++++++++-------- 2 files changed, 69 insertions(+), 56 deletions(-) diff --git a/front_end/src/components/login.tsx b/front_end/src/components/login.tsx index 625df14..34e3902 100644 --- a/front_end/src/components/login.tsx +++ b/front_end/src/components/login.tsx @@ -2,7 +2,7 @@ import { useKeycloak } from '@react-keycloak/web' import { useEffect } from 'react'; import { Athlete, User } from '../classes'; import { useLocalData } from '../context/useLocalData'; -import { postAthlete } from '../requetes'; +import { loginOrRegister, postAthlete } from '../requetes'; import { clearAuthToken, setAuthToken } from '../api'; import { AthleteDTO } from '../classesDTO'; @@ -10,7 +10,7 @@ export const Login =() =>{ const {user,setUser} = useLocalData() const { keycloak } = useKeycloak(); - useEffect(() => { + /*useEffect(() => { const syncAndLoadUser = async () => { if (keycloak.authenticated && keycloak.token && keycloak.tokenParsed) { try { @@ -24,47 +24,39 @@ export const Login =() =>{ const athlete: Athlete = await postAthlete(newAthlete); setUser(athlete); - } catch (error) { - console.error("Error :", error); - } - } - }; - /*postAthlete - if (keycloak.authenticated && keycloak.token) { - const tokenParsed = keycloak.tokenParsed; - setUser( - - { - id: 0, - keycloakId: tokenParsed!.sub!, - email: tokenParsed?.email || "", - nom: tokenParsed?.family_name || "", - prenom: tokenParsed?.given_name || "", - sessions: [] - } - ); - try { - const response = await fetch("http://localhost:8081/api/users/sync", { - method: "POST", - headers: { - "Authorization": `Bearer ${keycloak.token}`, - "Content-Type": "application/json" - }, - }); - console.log("Sync status:", response.status); } catch (error) { - console.error("Sync fetch failed:", error); + console.error("Error :", error); } - }*/ - + } + }; syncAndLoadUser(); }, [keycloak.authenticated, keycloak.token, setUser]); + */ + async function loginUser(){ + if(keycloak.authenticated){ + setAuthToken(keycloak.token); + //alert("Connexion en cours : " + keycloak.tokenParsed?.sub + " " + keycloak.tokenParsed?.realm_access?.roles); + + const logedUser = await loginOrRegister(keycloak); + console.log(logedUser); + if(logedUser!==null){ + setUser(logedUser); + // alert("Connexion avec succès ! " + keycloak.tokenParsed?.sub); + } + else{ + alert("Erreur de connexion " + keycloak.tokenParsed?.sub + " " + keycloak.tokenParsed?.realm_access?.roles); + } + } + } - function handleLogin(): void { - keycloak.login(); - //TODO setUser + useEffect(() => { + loginUser() + },[keycloak.authenticated]) + + async function handleLogin() { + await keycloak.login(); } function handleLogout(): void { @@ -81,9 +73,12 @@ export const Login =() =>{
Keycloak ID : { keycloak.tokenParsed?.sub} +
+
+ Prenom : { user.prenom}
- User nom : { user.nom} + Nom : { user.nom}
diff --git a/front_end/src/requetes.tsx b/front_end/src/requetes.tsx index 4f5db60..1424def 100644 --- a/front_end/src/requetes.tsx +++ b/front_end/src/requetes.tsx @@ -15,28 +15,46 @@ export function delay(ms: number): Promise { /* retourne l'utilisateur lié à l'identifiant keyloack */ -export async function getUser(keycloak:Keycloak): Promise{ +export async function loginOrRegister(keycloak:Keycloak): Promise{ try { - const roles = keycloak.tokenParsed?.realm_access?.roles - if(roles!=null){ - if(roles.includes("admin")){ - const response = await athleteService.create(keycloak.tokenParsed); - const admin = new Admin(response.data); - return admin; + if(keycloak.tokenParsed!=null){ + const roles = keycloak.tokenParsed?.realm_access?.roles + if(roles!=null){ + if(roles.includes("admin")){ + const id = keycloak.tokenParsed?.sub; + if(id!=null){ + const response = await athleteService.getByKeycloakId(id); + const admin = new Admin(response.data); + return admin; + } + } + else if(roles.includes("coach")){ + const id = keycloak.tokenParsed?.sub; + if(id!=null){ + const response = await coachService.getByKeycloakId(id); + const coach = new Coach(response.data); + return coach; + } + } + else if(roles.includes("athlete")){ + console.error("role = Athlete"); + const newAthlete: Athlete = new Athlete(); + newAthlete.keycloakId = keycloak.tokenParsed.sub || ""; + newAthlete.email = keycloak.tokenParsed.email || ""; + newAthlete.nom = keycloak.tokenParsed.family_name || ""; + newAthlete.prenom = keycloak.tokenParsed.given_name || ""; + const response = await athleteService.create(newAthlete.toDTO()); + console.log(response); + const athlete = new Athlete(response.data); + console.log(athlete); + return athlete; + } + else console.error("Error : role inconnu"); } - else if(roles.includes("coach")){ - const response = await coachService.create(keycloak.tokenParsed); - const coach = new Coach(response.data); - return coach; - } - else if(roles.includes("athletes")){ - const response = await athleteService.create(keycloak.tokenParsed); - const athlete = new Athlete(response.data); - return athlete; - } - console.error("Error roles inconnu"); + else console.error("Error : role null"); } - + else console.error("Error : token Keylcoak null"); + console.error("Error : pendant la récupération de l'User"); return null; } catch (error) { From d23ae3237998f3cbb420667af3dc81d87a9738ed Mon Sep 17 00:00:00 2001 From: tuanvu Date: Fri, 9 Jan 2026 10:49:09 +0100 Subject: [PATCH 3/5] fix --- front_end/src/components/test_api.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front_end/src/components/test_api.tsx b/front_end/src/components/test_api.tsx index bf40bb6..a1c54e2 100644 --- a/front_end/src/components/test_api.tsx +++ b/front_end/src/components/test_api.tsx @@ -1,5 +1,5 @@ import { useKeycloak } from "@react-keycloak/web" -import { getCoachsAPI, getUsersAPI, postAdmin } from "../requetes" +import { getAllCoach, getUsersAPI, postAdmin } from "../requetes" import { Admin } from "../classes"; @@ -7,7 +7,7 @@ function TestAPI(){ const { keycloak } = useKeycloak() function handleGetUsers(): void { - getCoachsAPI(); + getAllCoach(); } function handleSendAdmin(): void { From 4e8587d445529fb9733d4f5f8bc13afb3c05c880 Mon Sep 17 00:00:00 2001 From: tuanvu Date: Fri, 9 Jan 2026 11:23:20 +0100 Subject: [PATCH 4/5] ptet revoir les requetes.tsx --- front_end/src/requetes.tsx | 60 +++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/front_end/src/requetes.tsx b/front_end/src/requetes.tsx index 255f9dc..8b0692f 100644 --- a/front_end/src/requetes.tsx +++ b/front_end/src/requetes.tsx @@ -19,23 +19,23 @@ retourne l'utilisateur lié à l'identifiant keyloack export async function loginOrRegister(keycloak:Keycloak): Promise{ try { if(keycloak.tokenParsed!=null){ - const roles = keycloak.tokenParsed?.realm_access?.roles - if(roles!=null){ - if(roles.includes("admin")){ + const roles = keycloak.tokenParsed?.realm_access?.roles + if(roles!=null){ + if(roles.includes("admin")){ const id = keycloak.tokenParsed?.sub; if(id!=null){ const response = await athleteService.getByKeycloakId(id); - const admin = new Admin(response.data); - return admin; - } + const admin = new Admin(response.data); + return admin; + } } - else if(roles.includes("coach")){ + else if(roles.includes("coach")){ const id = keycloak.tokenParsed?.sub; if(id!=null){ const response = await coachService.getByKeycloakId(id); - const coach = new Coach(response.data); - return coach; - } + const coach = new Coach(response.data); + return coach; + } } else if(roles.includes("athlete")){ console.error("role = Athlete"); @@ -46,10 +46,10 @@ export async function loginOrRegister(keycloak:Keycloak): Promise{ newAthlete.prenom = keycloak.tokenParsed.given_name || ""; const response = await athleteService.create(newAthlete.toDTO()); console.log(response); - const athlete = new Athlete(response.data); + const athlete = new Athlete(response.data); console.log(athlete); - return athlete; - } + return athlete; + } else console.error("Error : role inconnu"); } else console.error("Error : role null"); @@ -64,9 +64,21 @@ export async function loginOrRegister(keycloak:Keycloak): Promise{ } } -export async function updateActivitiesOfSessionAPI(session:Session){ +export async function updateActivitiesOfSessionAPI(session:Session,activities:Activite[]){ try { - const response = await api.get(`/sessions/${session.id}/activities`); + const session_id = session.id + for (const activity of activities) { + const response = await activiteService.update(activity.id!, { + name: activity.nom, + duree: activity.duree, + date: activity.data, + theme: activity.theme, + sessionId: session_id, + }, + ); + } + // To refresh the activities in the session object + const response = await sessionService.getActivities(session_id!); session.activites = response.data; } catch (error) { console.error("Error fetching activities for session:", error); @@ -76,7 +88,9 @@ export async function updateActivitiesOfSessionAPI(session:Session){ export async function subscribeSessionAPI(user:User, session:Session):Promise{ try { - await api.post(`/sessions/${session.id}/subscribe`); + const session_id =session.id + const user_id = user.id + const response = await sessionService.subscribe(session_id!, user_id!); return true; } catch (error) { console.error("Error subscribing to session:", error); @@ -86,7 +100,9 @@ export async function subscribeSessionAPI(user:User, session:Session):Promise{ try { - await api.post(`/session/${session.id}/unsubscribe`); + const session_id =session.id + const user_id = user.id + const response = await sessionService.unsubscribe(session_id!, user_id!); return true; } catch (error) { console.error("Error unsubscribing from session:", error); @@ -209,6 +225,16 @@ export async function getSessionsOfUserAPI(user:Coach|Athlete){ } } +export async function getAllSessionsAPI():Promise{ + try { + const response = await sessionService.getAll(); + return response.data; + } catch (error) { + console.error("Error fetching sessions:", error); + throw error; + } +} + //COACH export async function getAllCoach(): Promise { try { From 7e41266d80141f43350c7b2949bfefe304407919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ama=C3=ABl=20Kesteman?= Date: Fri, 9 Jan 2026 12:16:51 +0100 Subject: [PATCH 5/5] Feat: Ajout de activite.tsx --- front_end/src/components/object/activite.tsx | 34 ++++++++++++++++++++ front_end/src/components/ressourceList.tsx | 18 +++-------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/front_end/src/components/object/activite.tsx b/front_end/src/components/object/activite.tsx index e69de29..07c9079 100644 --- a/front_end/src/components/object/activite.tsx +++ b/front_end/src/components/object/activite.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react'; +import { Activite } from '../../classes'; +import '../style/objectList.css'; + +type Props = { + activite: Activite +} + +function ObjectActivite({activite}: Props) { + const [open, setOpen] = useState(false); + + function handleOpen(): void { + setOpen(!open); + } + + return ( +
+
handleOpen()}> +
{activite.nom}
+
{activite.theme ? activite.theme : "Pas de thème défini pour l'activité"}
+
{activite.duree} min
+
+ {open && activite.data && activite.data.size > 0 && ( +
+ {Array.from(activite.data.entries()).map(([key, value]) => ( +
{key}: {value}
+ ))} +
+ )} +
+ ) +} + +export default ObjectActivite \ No newline at end of file diff --git a/front_end/src/components/ressourceList.tsx b/front_end/src/components/ressourceList.tsx index c674b94..27acfaa 100644 --- a/front_end/src/components/ressourceList.tsx +++ b/front_end/src/components/ressourceList.tsx @@ -3,6 +3,7 @@ import { Athlete, Activite, Coach, Session, Ligne } from "../classes"; import ObjectSession from "./object/session"; import {calculStatsAthlete, niveauAlerte} from "../utils/athleteUtils"; import {calculTempsDeJeuParLigne} from "../utils/ligneUtils"; +import ObjectActivite from "./object/activite"; type AthleteListProps = { athletes: Athlete[], sessions: Session[]}; @@ -87,21 +88,12 @@ function AthleteList({ athletes, sessions }: AthleteListProps) { function ActiviteList({ activites }: ActiviteListProps) { return ( -
    +
    {activites.map((activite) => ( -
  • -
    - Nom: {activite.nom} -
    -
    - Thème: {activite.theme} -
    -
    - Durée: {activite.duree} minutes -
    -
  • + ))} -
+ + ); }