From 94cba5e60c1237c920aacf13b0caa97bf2753b62 Mon Sep 17 00:00:00 2001 From: tuanvu Date: Thu, 8 Jan 2026 08:57:58 +0100 Subject: [PATCH 01/13] add registration --- front_end/src/classes.tsx | 1 + front_end/src/components/login.tsx | 30 ++++++++++++++++----- front_end/src/components/ressourcePanel.tsx | 3 +++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/front_end/src/classes.tsx b/front_end/src/classes.tsx index a22ad88..365dda1 100644 --- a/front_end/src/classes.tsx +++ b/front_end/src/classes.tsx @@ -4,6 +4,7 @@ export type Role = "Admin" | "Athlete" | "Coach"; export class User{ id!: number; nom!: String; + prenom!:String; sessions: Session[] = []; //nb: Admin liaison non symétrique /!\ email!: String; role!: Role; diff --git a/front_end/src/components/login.tsx b/front_end/src/components/login.tsx index 97b4465..aef8447 100644 --- a/front_end/src/components/login.tsx +++ b/front_end/src/components/login.tsx @@ -8,11 +8,31 @@ import { useLocalData } from '../context/useLocalData'; export const Login =() =>{ const {user,setUser} = useLocalData() + const { keycloak } = useKeycloak(); + useEffect(() => { + if (keycloak.authenticated) { + fetch("http://localhost:8080/api/athlete/create", { + method: "POST", + headers: { + Authorization: `Bearer ${keycloak.token}`, + }, + }); + } + }, [keycloak.authenticated]); - - useEffect(() => { //TODO à supprimer - setUser(getUserTest()) - },[]); + useEffect(() => { + if (keycloak.authenticated) { + const tokenParsed = keycloak.tokenParsed; + setUser({ + id: tokenParsed?.id, + email: tokenParsed?.email, + nom: tokenParsed?.family_name, + prenom: tokenParsed?.given_name, + role: "Athlete", + sessions: [] + }); + } + }, [keycloak.authenticated]); function handleLogin(): void { @@ -24,8 +44,6 @@ export const Login =() =>{ keycloak.logout() setUser(new User()); } - - const { keycloak } = useKeycloak() return(
diff --git a/front_end/src/components/ressourcePanel.tsx b/front_end/src/components/ressourcePanel.tsx index 57f8661..00aca71 100644 --- a/front_end/src/components/ressourcePanel.tsx +++ b/front_end/src/components/ressourcePanel.tsx @@ -11,6 +11,9 @@ const [showSessions, setShowSessions] = useState(false); const [showLignes, setShowLignes] = useState(false); console.log("Rôle utilisateur:", user.role); + console.log(user.nom); + console.log(user.prenom); + console.log(user.email); if (user.role === "Athlete") return null; From 0a5d7bccd5568b016105cc3a89646e9262408fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ama=C3=ABl=20Kesteman?= Date: Thu, 8 Jan 2026 08:58:26 +0100 Subject: [PATCH 02/13] Feat: Calcul de temps de jeu total pour les lignes --- front_end/src/classes.tsx | 5 ++++- front_end/src/components/ressourceList.tsx | 9 +++++--- front_end/src/components/ressourcePanel.tsx | 24 +++++++++++++++------ front_end/src/utils/ligneUtils.tsx | 16 ++++++++++++++ 4 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 front_end/src/utils/ligneUtils.tsx diff --git a/front_end/src/classes.tsx b/front_end/src/classes.tsx index a22ad88..a7aa095 100644 --- a/front_end/src/classes.tsx +++ b/front_end/src/classes.tsx @@ -106,6 +106,7 @@ export function getUserTest():User{ s1.name = "Entrainement Frisbee" s1.isRecurrent = true; s1.ligne = [ligne1]; + s1.duree= 90; var date2 = new Date(); date2.setDate(date2.getDate() + 2); s2.creneau = date2; @@ -113,11 +114,13 @@ export function getUserTest():User{ s2.isRecurrent = false; s2.name = "entraintement 2" s2.ligne = [ligne2]; + s2.duree= 120; s3.creneau = date2; s3.id = 3; s3.isRecurrent = false; s3.name = "entraintement 3" - s3.ligne = [ligne3]; + s3.ligne = [ligne3, ligne1]; + s3.duree= 120; diff --git a/front_end/src/components/ressourceList.tsx b/front_end/src/components/ressourceList.tsx index b21912a..0eca026 100644 --- a/front_end/src/components/ressourceList.tsx +++ b/front_end/src/components/ressourceList.tsx @@ -1,10 +1,12 @@ import { Athlete, Activite, Coach, Session, Ligne } from "../classes"; +import {calculTempsDeJeuParLigne} from "../utils/ligneUtils"; + type AthleteListProps = { athletes: Athlete[] }; type ActiviteListProps = { activites: Activite[] }; type CoachListProps = { coachs: Coach[] }; type SessionListProps = { sessions: Session[]}; -type LigneListProps = { lignes: Ligne[]}; +type LigneListProps = { lignes: Ligne[], tempsDeJeuParLigne: Map }; function AthleteList({ athletes }: AthleteListProps) { return ( @@ -81,7 +83,7 @@ function SessionList({ sessions }: SessionListProps) { ); } -function LigneList({ lignes }: LigneListProps) { +function LigneList({ lignes, tempsDeJeuParLigne }: LigneListProps) { return (
    {lignes.map((lignes) => ( @@ -102,7 +104,8 @@ function LigneList({ lignes }: LigneListProps) {
- Temps de jeu: {lignes.tempsDeJeu} + Temps de jeu total :{" "} + {tempsDeJeuParLigne.get(lignes.id) ?? 0} min
))} diff --git a/front_end/src/components/ressourcePanel.tsx b/front_end/src/components/ressourcePanel.tsx index 1b9c462..63b1ab7 100644 --- a/front_end/src/components/ressourcePanel.tsx +++ b/front_end/src/components/ressourcePanel.tsx @@ -1,8 +1,10 @@ - import { useState } from "react"; - import { useLocalData } from "../context/useLocalData"; - import { AthleteList, ActiviteList, CoachList, SessionList, LigneList} from "./ressourceList"; - import { Activite, Athlete, Coach , Session, Ligne } from "../classes"; +import { useState } from "react"; +import { useLocalData } from "../context/useLocalData"; +import { AthleteList, ActiviteList, CoachList, SessionList, LigneList} from "./ressourceList"; +import { Activite, Athlete, Coach , Session, Ligne } from "../classes"; +import {calculTempsDeJeuParLigne} from "../utils/ligneUtils"; import { keyboard } from "@testing-library/user-event/dist/keyboard"; +import { unescapeLeadingUnderscores } from "typescript"; export type keyWord = "athletes" | "activites" | "coachs" | "sessions"| "lignes"; @@ -11,6 +13,7 @@ import { keyboard } from "@testing-library/user-event/dist/keyboard"; const { user } = useLocalData(); const [value,setValue] = useState("athletes"); console.log("Rôle utilisateur:", user.role); + console.log("name", user.nom); if (user.role === "Athlete") return null; @@ -52,7 +55,16 @@ import { keyboard } from "@testing-library/user-event/dist/keyboard"; } }); - const allLignes: Ligne[] = Array.from(ligneMap.values()); + + const allLignes: Ligne[] = Array.from(ligneMap.values()); + + // Calculer le temps de jeu pour chaque ligne + const tempsDeJeuParLigne: Map = new Map(); + allLignes.forEach(ligne => { + const tempsTotal = calculTempsDeJeuParLigne(allSessions, ligne); + tempsDeJeuParLigne.set(ligne.id, tempsTotal); + ligne.tempsDeJeu = tempsTotal; + }); @@ -105,7 +117,7 @@ import { keyboard } from "@testing-library/user-event/dist/keyboard"; {value==="lignes" && (

Liste des lignes

- +
)} diff --git a/front_end/src/utils/ligneUtils.tsx b/front_end/src/utils/ligneUtils.tsx new file mode 100644 index 0000000..629c5b4 --- /dev/null +++ b/front_end/src/utils/ligneUtils.tsx @@ -0,0 +1,16 @@ +import {Ligne, Session} from '../classes'; + +//Temps de jeu cumulé par ligne + +export function calculTempsDeJeuParLigne(sessions: Session[], ligne : Ligne): number { + let tempsDeJeuTotal = 0; + + sessions.forEach(session => { + // Vérifier si la ligne est présente dans la session + if (session.ligne && session.ligne.some(l => l.id === ligne.id)) { + tempsDeJeuTotal += session.duree; + } + }); + + return tempsDeJeuTotal; +} \ No newline at end of file From 068eb7f611e01cd3a6ef014f038ed41a14fc6e5d Mon Sep 17 00:00:00 2001 From: tuanvu Date: Thu, 8 Jan 2026 08:59:52 +0100 Subject: [PATCH 03/13] remove admin previlige in creation --- .../src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java index b4372ee..32999dd 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java @@ -32,7 +32,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; - @Controller @RequestMapping("/athlete") public class AthleteResource { @@ -45,7 +44,6 @@ public class AthleteResource { @ApiResponse(responseCode = "200", description = "Renvoie l'athlète créé", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AthleteDTO.class))) }) @PostMapping("/create") - @PreAuthorize("hasRole('Admin')") // Only admin can create?? public ResponseEntity create(@RequestBody AthleteDTO dto) { Athlete athlete = mapToEntity(dto); athleteDAO.save(athlete); From b6793f0a0e5dcb5d8b65f3fa965d231ed7a2c288 Mon Sep 17 00:00:00 2001 From: Alexis Leboeuf Date: Thu, 8 Jan 2026 09:15:42 +0100 Subject: [PATCH 04/13] Added overriden getter/setters because of error --- .../hackathon/FrisbYEE/jpa/metier/Admin.java | 36 +++++++++++++++++++ .../FrisbYEE/jpa/metier/Athlete.java | 36 +++++++++++++++++++ .../hackathon/FrisbYEE/jpa/metier/Coach.java | 36 +++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java index 0c64f03..6928f7c 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java @@ -21,4 +21,40 @@ public class Admin extends User{ public String toString() { return "Admin [id=" + super.getId() + " , name=" + super.getName() + "]"; } + + @Override + public void setName(String name) { + super.setName(name); + } + + @Override + public String getName() { + return super.getName(); + } + + @Override + public String getPrenom() { + return super.getPrenom(); + + } + + @Override + public void setPrenom(String prenom) { + super.setPrenom(prenom); + } + + @Override + public String getId_keycloak() { + return super.getId_keycloak(); + } + + @Override + public void setId_keycloak(String id_keycloak) { + super.setId_keycloak(id_keycloak); + } + + @Override + public Role getRole() { + return super.getRole(); + } } \ No newline at end of file diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java index 36a38f0..a4a4865 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java @@ -35,4 +35,40 @@ public class Athlete extends User{ public String toString() { return "Athlete [id=" + super.getId() + " , name=" + super.getName() + "]"; } + + @Override + public void setName(String name) { + super.setName(name); + } + + @Override + public String getName() { + return super.getName(); + } + + @Override + public String getPrenom() { + return super.getPrenom(); + + } + + @Override + public void setPrenom(String prenom) { + super.setPrenom(prenom); + } + + @Override + public String getId_keycloak() { + return super.getId_keycloak(); + } + + @Override + public void setId_keycloak(String id_keycloak) { + super.setId_keycloak(id_keycloak); + } + + @Override + public Role getRole() { + return super.getRole(); + } } diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java index 9b619dc..77ad6ec 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java @@ -27,4 +27,40 @@ public class Coach extends User{ public String toString() { return "Coach [id=" + super.getId() + " , name=" + super.getName() + "]"; } + + @Override + public void setName(String name) { + super.setName(name); + } + + @Override + public String getName() { + return super.getName(); + } + + @Override + public String getPrenom() { + return super.getPrenom(); + + } + + @Override + public void setPrenom(String prenom) { + super.setPrenom(prenom); + } + + @Override + public String getId_keycloak() { + return super.getId_keycloak(); + } + + @Override + public void setId_keycloak(String id_keycloak) { + super.setId_keycloak(id_keycloak); + } + + @Override + public Role getRole() { + return super.getRole(); + } } \ No newline at end of file From fa70da8f2580651f8fbea9258dd4591b786f60a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ama=C3=ABl=20Kesteman?= Date: Thu, 8 Jan 2026 11:22:04 +0100 Subject: [PATCH 05/13] =?UTF-8?q?Feat:=20Ajout=20stats=20athl=C3=A8tes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front_end/src/classes.tsx | 11 ++- front_end/src/components/ressourceList.tsx | 79 ++++++++++++++++++--- front_end/src/components/ressourcePanel.tsx | 2 +- front_end/src/utils/athleteUtils.tsx | 46 ++++++++++++ 4 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 front_end/src/utils/athleteUtils.tsx diff --git a/front_end/src/classes.tsx b/front_end/src/classes.tsx index a7aa095..977f100 100644 --- a/front_end/src/classes.tsx +++ b/front_end/src/classes.tsx @@ -107,6 +107,7 @@ export function getUserTest():User{ s1.isRecurrent = true; s1.ligne = [ligne1]; s1.duree= 90; + s1.athletes = [athlete1,athlete2]; var date2 = new Date(); date2.setDate(date2.getDate() + 2); s2.creneau = date2; @@ -115,19 +116,15 @@ export function getUserTest():User{ s2.name = "entraintement 2" s2.ligne = [ligne2]; s2.duree= 120; + s2.athletes = [athlete1,athlete2, athlete3]; + s3.creneau = date2; s3.id = 3; s3.isRecurrent = false; s3.name = "entraintement 3" s3.ligne = [ligne3, ligne1]; s3.duree= 120; - - - - s1.athletes = [athlete1, athlete2]; - s2.athletes = [athlete2, athlete3]; - s3.athletes = [athlete1, athlete3]; - + s3.athletes = [athlete2, athlete3]; const act1 = new Activite(); act1.id = 1; diff --git a/front_end/src/components/ressourceList.tsx b/front_end/src/components/ressourceList.tsx index 0eca026..3788295 100644 --- a/front_end/src/components/ressourceList.tsx +++ b/front_end/src/components/ressourceList.tsx @@ -1,23 +1,82 @@ +import React from "react"; import { Athlete, Activite, Coach, Session, Ligne } from "../classes"; +import {calculStatsAthlete, niveauAlerte} from "../utils/athleteUtils"; import {calculTempsDeJeuParLigne} from "../utils/ligneUtils"; -type AthleteListProps = { athletes: Athlete[] }; +type AthleteListProps = { athletes: Athlete[], sessions: Session[]}; type ActiviteListProps = { activites: Activite[] }; type CoachListProps = { coachs: Coach[] }; type SessionListProps = { sessions: Session[]}; type LigneListProps = { lignes: Ligne[], tempsDeJeuParLigne: Map }; -function AthleteList({ athletes }: AthleteListProps) { +function AthleteList({ athletes, sessions }: AthleteListProps) { + const [dateDebut, setDateDebut] = React.useState(new Date()); + const [dateFin, setDateFin] = React.useState(new Date()); + const [seuilCritique, setSeuilCritique] = React.useState(0); + const [seuilMax, setSeuilMax] = React.useState(0); + + const dateToDatetimeLocal = (date: Date) => { + const pad = (n: number) => n.toString().padStart(2, "0"); + return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`; + }; + return ( -
    - {athletes.map((athlete) => ( -
  • -
    Nom: {athlete.nom}
    -
    Groupe: {athlete.groupe}
    -
  • - ))} -
+ <> +
+ + + + + +
+ +
    + {athletes.map(a => { + const stats = calculStatsAthlete(sessions, a, dateDebut, dateFin); + const alerte = niveauAlerte(stats, seuilCritique, seuilMax); + + return ( +
  • +
    Nom: {a.nom}
    +
    Groupe: {a.groupe}
    +
    Nombre de sessions: {stats.nbSessions}
    +
    Sessions/semaine: {stats.nbSessionsPerWeek.toFixed(2)}
    +
    Alerte: {alerte}
    +
  • + ); + })} +
+ ); } diff --git a/front_end/src/components/ressourcePanel.tsx b/front_end/src/components/ressourcePanel.tsx index 63b1ab7..62515dc 100644 --- a/front_end/src/components/ressourcePanel.tsx +++ b/front_end/src/components/ressourcePanel.tsx @@ -89,7 +89,7 @@ import { unescapeLeadingUnderscores } from "typescript"; {value==="athletes" && (

Liste des athlètes

- +
)} diff --git a/front_end/src/utils/athleteUtils.tsx b/front_end/src/utils/athleteUtils.tsx new file mode 100644 index 0000000..1a870cb --- /dev/null +++ b/front_end/src/utils/athleteUtils.tsx @@ -0,0 +1,46 @@ +import { Athlete, Session } from '../classes'; + +export interface StatsAthlete { + nbSessions: number; + nbSessionsPerWeek: number; + isAlerte: boolean; + distributions: Map; //le nom de l'activité et son nombre +} + +export function niveauAlerte(stats: StatsAthlete, seuilCritique = 0, seuilMax = 0) { + if (stats.nbSessionsPerWeek > seuilMax) return "Alerte ! Niveau maximal atteint."; + if (stats.nbSessionsPerWeek > seuilCritique) return "Attention! Niveau critique atteint."; + return "Normal"; +} + +export function calculStatsAthlete(sessions: Session[], athlete: Athlete, debut: Date, fin: Date): StatsAthlete { + let nb_sessions = 0; + let nb_semaine = 1; //forcément une semaine + const distributions: Map = new Map(); + const timeDiff = Math.abs(fin.getTime() - debut.getTime()); + nb_semaine = Math.ceil(timeDiff / (1000 * 3600 * 24 * 7)); + + sessions.forEach(session => { + // verification session dans l'intervalle + if (session.creneau < debut || session.creneau > fin) return; + + // verification athlete dans session + if (!session.athletes.some(a => a.id === athlete.id)) return; + + //incrementation (verifie si recurent ou non) + const increment = session.isRecurrent ? nb_semaine : 1; + nb_sessions += increment; + + } + ); + + const nbSessionsPerWeek = nb_sessions / nb_semaine; + const isAlerte = nbSessionsPerWeek > 8; + + return { + nbSessions: nb_sessions, + nbSessionsPerWeek: nbSessionsPerWeek, + isAlerte: isAlerte, + distributions: distributions + }; +} \ No newline at end of file From 9a2d1ae5e66d6d0bfaff6479be38d41cd697f7c9 Mon Sep 17 00:00:00 2001 From: Alexis Leboeuf Date: Thu, 8 Jan 2026 11:26:16 +0100 Subject: [PATCH 06/13] Lot of things Refactored Role enum to be the same as Keycloak roles Managed CORS errors in backend Edited Keycloak config to avoid CORS error Edited frontend API to avoid CORS errors Changed Activite creation management Added debug print in Login (should be removed); --- back_end/package-lock.json | 6 +++++ .../FrisbYEE/config/WebSecurityConfig.java | 22 ++++++++++++++++++- .../hackathon/FrisbYEE/jpa/metier/Admin.java | 2 +- .../FrisbYEE/jpa/metier/Athlete.java | 2 +- .../hackathon/FrisbYEE/jpa/metier/Coach.java | 2 +- .../hackathon/FrisbYEE/jpa/metier/Role.java | 6 ++--- .../FrisbYEE/jpa/metier/Session.java | 4 ++-- .../FrisbYEE/rest/ActiviteResource.java | 2 ++ front_end/public/keycloak.json | 1 + front_end/src/api.ts | 1 + front_end/src/components/createSession.tsx | 4 ++-- front_end/src/components/login.tsx | 5 ++++- 12 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 back_end/package-lock.json diff --git a/back_end/package-lock.json b/back_end/package-lock.json new file mode 100644 index 0000000..fc3a8b4 --- /dev/null +++ b/back_end/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "back_end", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java b/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java index 2d807d7..c96ab6c 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java @@ -6,11 +6,15 @@ import java.util.Map; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @Configuration @EnableWebSecurity @@ -19,9 +23,10 @@ public class WebSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth - .requestMatchers("/", "/public", "/coach/**").permitAll() // allow coach endpoints + .requestMatchers(HttpMethod.OPTIONS, "/", "/public", "/coach/**").permitAll() // allow coach endpoints .requestMatchers("/admin/**").hasRole("admin") .requestMatchers("/user/**").hasRole("user") .anyRequest().authenticated()) @@ -29,6 +34,7 @@ public class WebSecurityConfig { .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtToken -> { Map> realmAccess = jwtToken.getClaim("realm_access"); Collection roles = realmAccess.get("roles"); + System.out.println("ROLES FROM TOKEN " + roles); List authorities = roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .toList(); @@ -37,4 +43,18 @@ public class WebSecurityConfig { return http.build(); } + + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(List.of("http://localhost:3000")); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + config.setAllowCredentials(true); + config.setAllowedHeaders(List.of("Authorization", "Content-Type")); + UrlBasedCorsConfigurationSource source = + new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return source; + } } diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java index 6928f7c..0dc3a34 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java @@ -14,7 +14,7 @@ import jakarta.persistence.Entity; public class Admin extends User{ public Admin(String id_keycloak, String name, String prenom){ - super(name, id_keycloak, prenom, Role.ADMIN ); + super(name, id_keycloak, prenom, Role.admin ); } @Override diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java index a4a4865..1992780 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java @@ -28,7 +28,7 @@ public class Athlete extends User{ private List sessions = new ArrayList<>(); // plusieurs sessions sont possibles public Athlete(String name, String id_keycloak, String prenom){ - super(name, id_keycloak, prenom, Role.ATHLETE); + super(name, id_keycloak, prenom, Role.athlete); } @Override diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java index 77ad6ec..50b9c39 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java @@ -20,7 +20,7 @@ public class Coach extends User{ private List sessions = new ArrayList<>(); // Un coach peut avoir plusieurs sessions public Coach(String name, String id_keycloak, String prenom){ - super(name, id_keycloak, prenom, Role.COACH ); + super(name, id_keycloak, prenom, Role.coach ); } @Override diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Role.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Role.java index dbdc97e..c80919c 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Role.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Role.java @@ -1,7 +1,7 @@ package hackathon.FrisbYEE.jpa.metier; public enum Role { - ADMIN, - COACH, - ATHLETE + admin, + coach, + athlete } \ No newline at end of file diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Session.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Session.java index 7711c2a..11273a4 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Session.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Session.java @@ -58,7 +58,7 @@ public class Session { } public void setCoach(Coach coach) { - if (coach.getRole() != Role.COACH) { + if (coach.getRole() != Role.coach) { throw new IllegalArgumentException("L'utilisateur n'est pas un coach"); } this.coach = coach; @@ -66,7 +66,7 @@ public class Session { public void setAthletes(List athletes) { for (Athlete athlete : athletes) { - if (athlete.getRole() != Role.ATHLETE) { + if (athlete.getRole() != Role.athlete) { throw new IllegalArgumentException("L'utilisateur n'est pas un athlète"); } } diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java index b758823..27bf285 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java @@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; + @CrossOrigin(origins = "http://localhost:3000") @Controller @RequestMapping("/activite") @@ -49,6 +50,7 @@ public class ActiviteResource { public ResponseEntity create(@RequestBody ActiviteDTO dto) { try { + System.out.println("ROLE TEST " + hackathon.FrisbYEE.jpa.metier.Role.coach); Session session = sessionDAO.findById(dto.getSessionId()).get(); Activite activite = mapToEntity(dto); activite.setSession(session); diff --git a/front_end/public/keycloak.json b/front_end/public/keycloak.json index 119260d..f236670 100644 --- a/front_end/public/keycloak.json +++ b/front_end/public/keycloak.json @@ -1,6 +1,7 @@ { "realm": "Frisbyee_realm", "resource": "Frisbyee_client", + "clientId": "Frisbyee_client", "auth-server-url": "http://localhost:8080", "public-client": true } \ No newline at end of file diff --git a/front_end/src/api.ts b/front_end/src/api.ts index aea29ba..9c8ae21 100644 --- a/front_end/src/api.ts +++ b/front_end/src/api.ts @@ -6,6 +6,7 @@ const api = axios.create({ headers: { "Content-Type": "application/json", }, + withCredentials: true, }); api.interceptors.request.use((config) => { diff --git a/front_end/src/components/createSession.tsx b/front_end/src/components/createSession.tsx index d7a10b9..6f385ba 100644 --- a/front_end/src/components/createSession.tsx +++ b/front_end/src/components/createSession.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; import { Session, User, Coach, Activite, Groupe } from "../classes"; import { useLocalData } from "../context/useLocalData"; -import { sessionService } from "../api"; +import { activiteService, sessionService } from "../api"; export const CreateSession = () => { const {user} = useLocalData() @@ -24,7 +24,7 @@ export const CreateSession = () => { newActivite.duree= activiteDuree; newActivite.data= new Map(); try{ - await sessionService.create(newActivite); + await activiteService.create(newActivite); console.log("Session créée"); setActivities([...activities, newActivite]); diff --git a/front_end/src/components/login.tsx b/front_end/src/components/login.tsx index 97b4465..25b3179 100644 --- a/front_end/src/components/login.tsx +++ b/front_end/src/components/login.tsx @@ -16,7 +16,7 @@ export const Login =() =>{ function handleLogin(): void { - keycloak.login() + keycloak.login(); //TODO setUser } @@ -39,6 +39,9 @@ export const Login =() =>{
User nom : { user.nom}
+
+ User role : { user.role} +
} From e28b12683849de8fa8c17575ad74bc1226860cbb Mon Sep 17 00:00:00 2001 From: tuanvu Date: Thu, 8 Jan 2026 11:27:18 +0100 Subject: [PATCH 07/13] change in athlete and getkeycloak --- .../hackathon/FrisbYEE/jpa/metier/Admin.java | 10 ----- .../FrisbYEE/jpa/metier/Athlete.java | 11 ------ .../hackathon/FrisbYEE/jpa/metier/Coach.java | 10 ----- .../hackathon/FrisbYEE/jpa/metier/User.java | 6 +-- .../FrisbYEE/jpa/service/AthleteDAO.java | 5 ++- .../FrisbYEE/rest/AthleteResource.java | 11 +++++- .../FrisbYEE/rest/CoachResource.java | 4 +- .../src/main/resources/application.properties | 1 - front_end/src/App.tsx | 4 +- front_end/src/classes.tsx | 5 +-- front_end/src/components/login.tsx | 37 +++++++++++++++---- 11 files changed, 50 insertions(+), 54 deletions(-) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java index 6928f7c..f7d35c1 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Admin.java @@ -43,16 +43,6 @@ public class Admin extends User{ super.setPrenom(prenom); } - @Override - public String getId_keycloak() { - return super.getId_keycloak(); - } - - @Override - public void setId_keycloak(String id_keycloak) { - super.setId_keycloak(id_keycloak); - } - @Override public Role getRole() { return super.getRole(); diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java index a4a4865..c43b77f 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Athlete.java @@ -56,17 +56,6 @@ public class Athlete extends User{ public void setPrenom(String prenom) { super.setPrenom(prenom); } - - @Override - public String getId_keycloak() { - return super.getId_keycloak(); - } - - @Override - public void setId_keycloak(String id_keycloak) { - super.setId_keycloak(id_keycloak); - } - @Override public Role getRole() { return super.getRole(); diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java index 77ad6ec..cd2ade7 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/Coach.java @@ -49,16 +49,6 @@ public class Coach extends User{ super.setPrenom(prenom); } - @Override - public String getId_keycloak() { - return super.getId_keycloak(); - } - - @Override - public void setId_keycloak(String id_keycloak) { - super.setId_keycloak(id_keycloak); - } - @Override public Role getRole() { return super.getRole(); diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/User.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/User.java index 188bd09..2bdf5ce 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/User.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/metier/User.java @@ -26,8 +26,8 @@ public class User implements Serializable { @GeneratedValue @Column(unique = true, nullable = false) private Integer id; - @Column(nullable = false, unique = true) - private String id_keycloak; + @Column(name = "id_keycloak", unique = true, nullable = false) + private String keycloakId; private String name; private String prenom; @@ -38,7 +38,7 @@ public class User implements Serializable { public User(String name, String id_keycloak, String prenom, Role role) { this.name = name; - this.id_keycloak = id_keycloak; + this.keycloakId = id_keycloak; this.prenom = prenom; this.role = role; } diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/service/AthleteDAO.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/service/AthleteDAO.java index 476acc8..25da7ef 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/service/AthleteDAO.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/service/AthleteDAO.java @@ -1,11 +1,12 @@ package hackathon.FrisbYEE.jpa.service; import hackathon.FrisbYEE.jpa.metier.Athlete; - +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface AthleteDAO extends JpaRepository { - + boolean existsByKeycloakId(String keycloakId); + Optional findByKeycloakId(String keycloakId); } \ No newline at end of file diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java index 32999dd..a638c7f 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java @@ -32,7 +32,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; -@Controller +@RestController @RequestMapping("/athlete") public class AthleteResource { @Autowired @@ -124,8 +124,9 @@ public class AthleteResource { private AthleteDTO mapToDTO(Athlete athlete) { AthleteDTO dto = new AthleteDTO(); - dto.setId_keycloak(athlete.getId_keycloak()); + dto.setId_keycloak(athlete.getKeycloakId()); dto.setName(athlete.getName()); + dto.setPrenom(athlete.getPrenom()); dto.setCategorie(athlete.getCategorie()); dto.setNiveau(athlete.getNiveau()); return dto; @@ -133,6 +134,12 @@ public class AthleteResource { private Athlete mapToEntity(AthleteDTO dto) { Athlete athlete = new Athlete(); + athlete.setName(dto.getName()); + athlete.setPrenom(dto.getPrenom()); + athlete.setKeycloakId(dto.getId_keycloak()); + athlete.setCategorie(dto.getCategorie()); + athlete.setNiveau(dto.getNiveau()); + athlete.setRole(hackathon.FrisbYEE.jpa.metier.Role.ATHLETE); return athlete; } diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/CoachResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/CoachResource.java index 706a777..acd34f1 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/CoachResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/CoachResource.java @@ -70,14 +70,14 @@ public class CoachResource { private CoachDTO mapToDTO(Coach coach) { CoachDTO dto = new CoachDTO(); - dto.setId_keycloak(coach.getId_keycloak()); + dto.setId_keycloak(coach.getKeycloakId()); dto.setName(coach.getName()); return dto; } private Coach mapToEntity(CoachDTO dto) { Coach coach = new Coach(); - coach.setId_keycloak(dto.getId_keycloak()); + coach.setKeycloakId(dto.getId_keycloak()); coach.setName(dto.getName()); return coach; } diff --git a/back_end/src/main/resources/application.properties b/back_end/src/main/resources/application.properties index a0fc7cf..adbc11b 100644 --- a/back_end/src/main/resources/application.properties +++ b/back_end/src/main/resources/application.properties @@ -8,6 +8,5 @@ spring.jpa.show-sql=true spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect server.port=8081 server.servlet.context-path=/api - spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/Frisbyee_realm spring.security.oauth2.resourceserver.jwt.jwk-set-uri: http://localhost:8080/realms/Frisbyee_realm/protocol/openid-connect/certs \ No newline at end of file diff --git a/front_end/src/App.tsx b/front_end/src/App.tsx index a4b5b7f..733add2 100644 --- a/front_end/src/App.tsx +++ b/front_end/src/App.tsx @@ -11,7 +11,7 @@ import EdtCoach from './components/edt_coach' import { Coach } from "./classes"; import RessourcePanel from './components/ressourcePanel'; import TestAPI from './components/test_api'; - +import EdtAthlete from './components/edt_athlete'; // Test const testCoach = new Coach(); testCoach.id = 1; @@ -36,7 +36,7 @@ function App() { - + diff --git a/front_end/src/classes.tsx b/front_end/src/classes.tsx index cf5c7e6..42d638c 100644 --- a/front_end/src/classes.tsx +++ b/front_end/src/classes.tsx @@ -3,6 +3,7 @@ export type Role = "Admin" | "Athlete" | "Coach"; export class User{ id!: number; + keycloakId!: String; nom!: String; prenom!:String; sessions: Session[] = []; //nb: Admin liaison non symétrique /!\ @@ -19,21 +20,17 @@ export class Ligne{ export class Admin extends User{ role!: Role; - } export class Athlete extends User{ nom!: String; groupe!: Groupe; role!: Role; - - } export class Coach extends User{ nom!: String; role!: Role; - } export class Session{ diff --git a/front_end/src/components/login.tsx b/front_end/src/components/login.tsx index aef8447..c02d1b5 100644 --- a/front_end/src/components/login.tsx +++ b/front_end/src/components/login.tsx @@ -3,20 +3,42 @@ import { useEffect } from 'react'; import { getUserTest, User } from '../classes'; import { useLocalData } from '../context/useLocalData'; - - - export const Login =() =>{ const {user,setUser} = useLocalData() const { keycloak } = useKeycloak(); + useEffect(() => { - if (keycloak.authenticated) { - fetch("http://localhost:8080/api/athlete/create", { + const syncUser = async () => { + if (keycloak.authenticated && keycloak.token) { + console.log("Attempting to sync user with backend..."); + 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); + } + } + }; + + syncUser(); + }, [keycloak.authenticated, keycloak.token]); + + useEffect(() => { + if (keycloak.authenticated && keycloak.token) { + fetch("http://localhost:8081/api/users/sync", { method: "POST", headers: { Authorization: `Bearer ${keycloak.token}`, }, - }); + }) + .then(res => console.log("Sync response status:", res.status)) + .catch(err => console.error("Sync error:", err));; } }, [keycloak.authenticated]); @@ -24,7 +46,8 @@ export const Login =() =>{ if (keycloak.authenticated) { const tokenParsed = keycloak.tokenParsed; setUser({ - id: tokenParsed?.id, + id: 0, + keycloakId: tokenParsed!.sub!, email: tokenParsed?.email, nom: tokenParsed?.family_name, prenom: tokenParsed?.given_name, From f77b4016282db783ade6695421832bf3910b5309 Mon Sep 17 00:00:00 2001 From: tuanvu Date: Thu, 8 Jan 2026 11:34:13 +0100 Subject: [PATCH 08/13] athlete test --- .../main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java | 2 +- .../src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java b/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java index c96ab6c..e054c00 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java @@ -26,7 +26,7 @@ public class WebSecurityConfig { .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth - .requestMatchers(HttpMethod.OPTIONS, "/", "/public", "/coach/**").permitAll() // allow coach endpoints + .requestMatchers(HttpMethod.OPTIONS, "/", "/public", "/coach/**","/athlete/**").permitAll() // allow coach endpoints .requestMatchers("/admin/**").hasRole("admin") .requestMatchers("/user/**").hasRole("user") .anyRequest().authenticated()) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java index a638c7f..56c46d3 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java @@ -34,6 +34,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; @RestController @RequestMapping("/athlete") +@CrossOrigin(origins = "http://localhost:3000") public class AthleteResource { @Autowired private AthleteDAO athleteDAO; @@ -44,6 +45,7 @@ public class AthleteResource { @ApiResponse(responseCode = "200", description = "Renvoie l'athlète créé", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AthleteDTO.class))) }) @PostMapping("/create") + @PreAuthorize("hasRole('Admin') or hasRole('Coach') or hasRole('Athlete')") public ResponseEntity create(@RequestBody AthleteDTO dto) { Athlete athlete = mapToEntity(dto); athleteDAO.save(athlete); From 25bae7652e903b98e0bf69c942d2c1d9089b22d8 Mon Sep 17 00:00:00 2001 From: Alexis Leboeuf Date: Thu, 8 Jan 2026 11:36:54 +0100 Subject: [PATCH 09/13] Changed roles in Activite and Session endpoints --- .../java/hackathon/FrisbYEE/jpa/dto/SessionDTO.java | 2 +- .../hackathon/FrisbYEE/rest/ActiviteResource.java | 12 ++++++------ .../hackathon/FrisbYEE/rest/SessionResource.java | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/jpa/dto/SessionDTO.java b/back_end/src/main/java/hackathon/FrisbYEE/jpa/dto/SessionDTO.java index 2e44668..3993af5 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/jpa/dto/SessionDTO.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/jpa/dto/SessionDTO.java @@ -7,7 +7,7 @@ import java.util.List; @Data public class SessionDTO { - + private Integer id; private String name; private Boolean isRecurrent; private LocalDateTime creneau; diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java index 27bf285..2698ff9 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/ActiviteResource.java @@ -46,7 +46,7 @@ public class ActiviteResource { }) @PostMapping("/create") @ResponseBody - @PreAuthorize("hasRole('Coach')") + @PreAuthorize("hasRole('coach')") public ResponseEntity create(@RequestBody ActiviteDTO dto) { try { @@ -69,7 +69,7 @@ public class ActiviteResource { }) @DeleteMapping("/delete/{id}") @ResponseBody - @PreAuthorize("hasRole('Coach')") + @PreAuthorize("hasRole('coach')") public ResponseEntity delete(@PathVariable("id") int id) { try { Activite activite = activiteDAO.findById(id).get(); @@ -89,7 +89,7 @@ public class ActiviteResource { }) @PostMapping("/update/{id}") @ResponseBody - @PreAuthorize("hasRole('Coach')") + @PreAuthorize("hasRole('coach')") public ResponseEntity modifyById(@PathVariable("id") int id, @RequestBody ActiviteDTO dto) { try { Session session = sessionDAO.findById(dto.getSessionId()).get(); @@ -114,7 +114,7 @@ public class ActiviteResource { schema = @Schema(implementation = ActiviteDTO.class))) }) @GetMapping("/{id}") - @PreAuthorize("hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('coach') or hasRole('athlete')") @ResponseBody public ResponseEntity getActivityById(@PathVariable("id") int id) { try { @@ -133,7 +133,7 @@ public class ActiviteResource { schema = @Schema(implementation = ActiviteDTO.class))) }) @GetMapping("/all") - @PreAuthorize("hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('coach') or hasRole('athlete')") @ResponseBody public ResponseEntity> getAllActivity() { try { @@ -153,7 +153,7 @@ public class ActiviteResource { schema = @Schema(implementation = ActiviteDTO.class))) }) @GetMapping("/theme/{theme}") - @PreAuthorize("hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('coach') or hasRole('athlete')") @ResponseBody public ResponseEntity> getActivityByTheme(@PathVariable("theme") String theme) { try { diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/SessionResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/SessionResource.java index b620d73..8269ec5 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/SessionResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/SessionResource.java @@ -40,7 +40,7 @@ public class SessionResource { @PostMapping("/create") @ResponseBody - @PreAuthorize("hasRole('Coach')") + @PreAuthorize("hasRole('coach')") public ResponseEntity create(@RequestBody SessionDTO dto) { try { Session session = maptoEntity(dto); @@ -53,7 +53,7 @@ public class SessionResource { } @GetMapping("/all") - @PreAuthorize("hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('coach') or hasRole('athlete')") public ResponseEntity> getAll() { List sessions = sessionDAO.findAll(); List dtos = new ArrayList<>(); @@ -64,7 +64,7 @@ public class SessionResource { } @GetMapping("/{id}") - @PreAuthorize("hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('coach') or hasRole('athlete')") public ResponseEntity getById(@PathVariable Integer id) { try { Session session = sessionDAO.findById(id).orElseThrow(); @@ -76,7 +76,7 @@ public class SessionResource { @DeleteMapping("/delete/{id}") @ResponseBody - @PreAuthorize("hasRole('Coach')") + @PreAuthorize("hasRole('coach')") public ResponseEntity delete(@PathVariable("id") int id) { try { Session session = sessionDAO.findById(id).get(); @@ -88,7 +88,7 @@ public class SessionResource { } @PutMapping("/update/{id}") - @PreAuthorize("hasRole('Coach')") + @PreAuthorize("hasRole('coach')") public ResponseEntity updateSession(@PathVariable Integer id, @RequestBody SessionDTO dto) { Session session = sessionDAO.findById(id).orElseThrow(() -> new ResponseStatusException( HttpStatus.NOT_FOUND, "Session not found with id " + id)); From 1eef3c4944cea9b062e826b854ce636cda9c0793 Mon Sep 17 00:00:00 2001 From: tuanvu Date: Thu, 8 Jan 2026 11:42:42 +0100 Subject: [PATCH 10/13] edt_athlete --- front_end/src/components/edt_athlete.tsx | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 front_end/src/components/edt_athlete.tsx diff --git a/front_end/src/components/edt_athlete.tsx b/front_end/src/components/edt_athlete.tsx new file mode 100644 index 0000000..c246705 --- /dev/null +++ b/front_end/src/components/edt_athlete.tsx @@ -0,0 +1,65 @@ +import React, { useState } from 'react'; + +export const EdtAthlete = () => { + const [formData, setFormData] = useState({ + name: '', + prenom: '', + id_keycloak: '', + categorie: '', + niveau: '' + }); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const response = await fetch("http://localhost:8081/api/athlete/create", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + if (response.ok) { + alert("Athlete created successfully in PostgreSQL!"); + setFormData({ name: '', prenom: '', id_keycloak: '', categorie: '', niveau: '' }); + } else { + alert("Failed to create athlete. Status: " + response.status); + } + } catch (error) { + console.error("Error creating athlete:", error); + alert("Error: Check console"); + } + }; + + return ( +
+

Test Create Athlete (PostgreSQL)

+
+
+ + setFormData({...formData, name: e.target.value})} /> +
+
+ + setFormData({...formData, prenom: e.target.value})} /> +
+
+ + setFormData({...formData, id_keycloak: e.target.value})} /> +
+
+ + setFormData({...formData, categorie: e.target.value})} /> +
+
+ + setFormData({...formData, niveau: e.target.value})} /> +
+ +
+
+ ); +}; + +export default EdtAthlete; From 988a7c16b353439b6101597c49c84866dbbf836c Mon Sep 17 00:00:00 2001 From: Alexis Leboeuf Date: Thu, 8 Jan 2026 12:01:56 +0100 Subject: [PATCH 11/13] P U S H --- .../hackathon/FrisbYEE/rest/AthleteResource.java | 15 +++++++++------ front_end/src/classes.tsx | 10 +++++----- front_end/src/components/login.tsx | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java index 56c46d3..cc9112e 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/AthleteResource.java @@ -10,6 +10,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,6 +19,8 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import hackathon.FrisbYEE.jpa.dto.ActiviteDTO; import hackathon.FrisbYEE.jpa.dto.AthleteDTO; import hackathon.FrisbYEE.jpa.dto.SessionDTO; @@ -45,7 +48,7 @@ public class AthleteResource { @ApiResponse(responseCode = "200", description = "Renvoie l'athlète créé", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AthleteDTO.class))) }) @PostMapping("/create") - @PreAuthorize("hasRole('Admin') or hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('Athlete')") public ResponseEntity create(@RequestBody AthleteDTO dto) { Athlete athlete = mapToEntity(dto); athleteDAO.save(athlete); @@ -57,7 +60,7 @@ public class AthleteResource { @ApiResponse(responseCode = "200", description = "Récupère tous les athlètes", content = @Content(mediaType = "application/json", schema = @Schema(implementation = List.class))) }) @GetMapping("/all") - @PreAuthorize("hasRole('Admin') or hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('athlete')") public ResponseEntity> all() { List athletes = athleteDAO.findAll(); List dtos = new ArrayList<>(); @@ -72,7 +75,7 @@ public class AthleteResource { @ApiResponse(responseCode = "200", description = "Récupération effectuée", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AthleteDTO.class))) }) @GetMapping("/{id}") - @PreAuthorize("hasRole('Admin') or hasRole('Coach') or hasRole('Athlete')") + @PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('athlete')") public ResponseEntity getById(@PathVariable Integer id) { return athleteDAO.findById(id) .map(athlete -> ResponseEntity.ok(mapToDTO(athlete))) @@ -84,7 +87,7 @@ public class AthleteResource { @ApiResponse(responseCode = "200", description = "Mise à jour effectuée", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AthleteDTO.class))) }) @PutMapping("/{id}") - @PreAuthorize("hasRole('ADMIN') or #id == principal.id") + @PreAuthorize("hasRole('admin') or #id == principal.id") public ResponseEntity update(@PathVariable Integer id, @RequestBody AthleteDTO dto) { try { Athlete athlete = athleteDAO.findById(id).get(); @@ -115,7 +118,7 @@ public class AthleteResource { @ApiResponse(responseCode = "200", description = "Suppression effectuée", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AthleteDTO.class))) }) @DeleteMapping("/{id}") - @PreAuthorize("hasRole('Admin')") + @PreAuthorize("hasRole('admin')") public ResponseEntity delete(@PathVariable Integer id) { if (!athleteDAO.existsById(id)) { return ResponseEntity.notFound().build(); @@ -141,7 +144,7 @@ public class AthleteResource { athlete.setKeycloakId(dto.getId_keycloak()); athlete.setCategorie(dto.getCategorie()); athlete.setNiveau(dto.getNiveau()); - athlete.setRole(hackathon.FrisbYEE.jpa.metier.Role.ATHLETE); + athlete.setRole(hackathon.FrisbYEE.jpa.metier.Role.athlete); return athlete; } diff --git a/front_end/src/classes.tsx b/front_end/src/classes.tsx index 011a8fc..c20a6fc 100644 --- a/front_end/src/classes.tsx +++ b/front_end/src/classes.tsx @@ -1,5 +1,5 @@ export type Groupe = "Entrainement" | "Competition" | "Loisir"| ""; -export type Role = "Admin" | "Athlete" | "Coach"; +export type Role = "admin" | "athlete" | "coach"; export class User{ id!: number; @@ -98,7 +98,7 @@ export function getUserTest():User{ user.id = 0; user.nom = "Emilien-Yee NootNoot"; - user.role = "Coach" + user.role = "coach" s1.creneau = new Date(); s1.id = 1; s1.name = "Entrainement Frisbee" @@ -185,9 +185,9 @@ export function getUserTest():User{ user.sessions.push(s2); user.sessions.push(s3); - athlete1.role = "Athlete"; - athlete2.role = "Athlete"; - athlete3.role = "Athlete"; + athlete1.role = "athlete"; + athlete2.role = "athlete"; + athlete3.role = "athlete"; return user; } diff --git a/front_end/src/components/login.tsx b/front_end/src/components/login.tsx index 0abca28..a917c32 100644 --- a/front_end/src/components/login.tsx +++ b/front_end/src/components/login.tsx @@ -51,7 +51,7 @@ export const Login =() =>{ email: tokenParsed?.email, nom: tokenParsed?.family_name, prenom: tokenParsed?.given_name, - role: "Athlete", + role: "athlete", sessions: [] }); } From 40c9d091b8f356293c4d43cfa3e8782cdfd8c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ama=C3=ABl=20Kesteman?= Date: Thu, 8 Jan 2026 12:04:05 +0100 Subject: [PATCH 12/13] Feat: Ajout des types de sessions en map --- front_end/src/classes.tsx | 20 ++++++++++---------- front_end/src/components/ressourceList.tsx | 1 + front_end/src/utils/athleteUtils.tsx | 12 ++++++++---- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/front_end/src/classes.tsx b/front_end/src/classes.tsx index 011a8fc..9639d09 100644 --- a/front_end/src/classes.tsx +++ b/front_end/src/classes.tsx @@ -4,16 +4,16 @@ export type Role = "Admin" | "Athlete" | "Coach"; export class User{ id!: number; keycloakId!: String; - nom!: String; - prenom!:String; + nom!: string; + prenom!:string; sessions: Session[] = []; //nb: Admin liaison non symétrique /!\ - email!: String; + email!: string; role!: Role; } export class Ligne{ id!: number; - nom!: String; + nom!: string; composition!: Athlete[] //les joueurs compososant la ligne tempsDeJeu!: number; // en minutes } @@ -23,19 +23,19 @@ export class Admin extends User{ } export class Athlete extends User{ - nom!: String; + nom!: string; groupe!: Groupe; role!: Role; } export class Coach extends User{ - nom!: String; + nom!: string; role!: Role; } export class Session{ id!: number; - name!: String; + name!: string; activites: Activite[] = []; isRecurrent! : Boolean; creneau!: Date; @@ -48,10 +48,10 @@ export class Session{ export class Activite{ id!: number; - nom!: String; + nom!: string; session!: Session; - theme!: String; - data!: Map; + theme!: string; + data!: Map; duree!: number; } diff --git a/front_end/src/components/ressourceList.tsx b/front_end/src/components/ressourceList.tsx index 3788295..44bc5f1 100644 --- a/front_end/src/components/ressourceList.tsx +++ b/front_end/src/components/ressourceList.tsx @@ -72,6 +72,7 @@ function AthleteList({ athletes, sessions }: AthleteListProps) {
Nombre de sessions: {stats.nbSessions}
Sessions/semaine: {stats.nbSessionsPerWeek.toFixed(2)}
Alerte: {alerte}
+
Distribuion des activités: {stats.distributions}
); })} diff --git a/front_end/src/utils/athleteUtils.tsx b/front_end/src/utils/athleteUtils.tsx index 1a870cb..893bd68 100644 --- a/front_end/src/utils/athleteUtils.tsx +++ b/front_end/src/utils/athleteUtils.tsx @@ -1,10 +1,10 @@ -import { Athlete, Session } from '../classes'; +import { Athlete, Session , Activite} from '../classes'; export interface StatsAthlete { nbSessions: number; nbSessionsPerWeek: number; isAlerte: boolean; - distributions: Map; //le nom de l'activité et son nombre + distributions: Map; //le nom de l'activité et son nombre } export function niveauAlerte(stats: StatsAthlete, seuilCritique = 0, seuilMax = 0) { @@ -30,9 +30,13 @@ export function calculStatsAthlete(sessions: Session[], athlete: Athlete, debut: //incrementation (verifie si recurent ou non) const increment = session.isRecurrent ? nb_semaine : 1; nb_sessions += increment; + //distribution des activités + session.activites.forEach(activite => { + const currentCount = distributions.get(activite.nom) || 0; + distributions.set(activite.nom, currentCount + increment); + }); - } - ); + }); const nbSessionsPerWeek = nb_sessions / nb_semaine; const isAlerte = nbSessionsPerWeek > 8; From 919149e012e7d385b3f2ad1ac377f398666eda82 Mon Sep 17 00:00:00 2001 From: tuanvu Date: Thu, 8 Jan 2026 12:34:24 +0100 Subject: [PATCH 13/13] //TODO WebSecurityConfig --- .../java/hackathon/FrisbYEE/config/WebSecurityConfig.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java b/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java index e054c00..5c028d5 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/config/WebSecurityConfig.java @@ -26,7 +26,13 @@ public class WebSecurityConfig { .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth - .requestMatchers(HttpMethod.OPTIONS, "/", "/public", "/coach/**","/athlete/**").permitAll() // allow coach endpoints + // TODO //TODO // T O D O + .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() + // 2. Allow public endpoints BEFORE any authenticated() calls + .requestMatchers("/athlete/create", "/", "/public").permitAll() + .requestMatchers("/coach/**").permitAll() + // 3. Authenticated endpoints + .requestMatchers("/users/sync").authenticated() .requestMatchers("/admin/**").hasRole("admin") .requestMatchers("/user/**").hasRole("user") .anyRequest().authenticated())