diff --git a/back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java b/back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java index 1f5f302..f4adae2 100644 --- a/back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java +++ b/back_end/src/main/java/hackathon/FrisbYEE/rest/UserSyncResource.java @@ -9,18 +9,15 @@ 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; @@ -28,13 +25,10 @@ public class UserSyncResource { @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)) { - System.out.println("New user detected from Keycloak. Syncing: " + firstName + " " + lastName); Athlete athlete = new Athlete(); athlete.setKeycloakId(keycloakId); athlete.setName(lastName); @@ -42,7 +36,6 @@ public class UserSyncResource { 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/App.tsx b/front_end/src/App.tsx index 733add2..5e78330 100644 --- a/front_end/src/App.tsx +++ b/front_end/src/App.tsx @@ -11,21 +11,13 @@ 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; -testCoach.nom = "Coach Test"; const keycloakInitOptions = { onLoad: 'login-required', checkLoginIframe: false } - function App() { - - return ( @@ -36,7 +28,6 @@ function App() { - diff --git a/front_end/src/api.ts b/front_end/src/api.ts index 6a80c6e..451804f 100644 --- a/front_end/src/api.ts +++ b/front_end/src/api.ts @@ -23,7 +23,7 @@ api.interceptors.request.use((config) => { }); // Helpers to set/clear the Authorization header programmatically (call after Keycloak login) -export function setAuthToken(token: string | null) { +export function setAuthToken(token: string | null | undefined) { if (token) { api.defaults.headers.common["Authorization"] = `Bearer ${token}`; } else { diff --git a/front_end/src/classes.tsx b/front_end/src/classes.tsx index 8eb677a..251e279 100644 --- a/front_end/src/classes.tsx +++ b/front_end/src/classes.tsx @@ -1,15 +1,7 @@ -export type Groupe = "Entrainement" | "Competition" | "Loisir"| ""; -export type Role = "admin" | "athlete" | "coach"; +import { ActiviteDTO, AdminDTO, AthleteDTO, CoachDTO, SessionDTO } from "./classesDTO"; + +export type Groupe = "Entrainement" | "Competition" | "Loisir"| ""; -export class User{ - id!: number; - keycloakId!: String; - nom!: string; - prenom!:string; - sessions: Session[] = []; //nb: Admin liaison non symétrique /!\ - email!: string; - role!: Role; -} export class Ligne{ id!: number; @@ -18,32 +10,103 @@ export class Ligne{ tempsDeJeu!: number; // en minutes } +export class User{ + id!: number; + keycloakId!: String; + nom!: string; + prenom!:string; + email!: string; +} + export class Admin extends User{ - role!: Role; + + constructor(dto:AdminDTO); + constructor(); + + constructor(dto?:AdminDTO){ + super(); + this.id = dto?.id ?? 0; + this.keycloakId = dto?.id_keycloak ?? ""; + this.nom = dto?.name ?? ""; + this.prenom = dto?.prenom ?? ""; + this.email = ""; //TODO + } + } export class Athlete extends User{ - nom!: string; - groupe!: Groupe; - role!: Role; + groupes!: Groupe[]; + sessionsID!: number[]; + sessions: Session[] = []; + + constructor(dto:AthleteDTO); + constructor(); + + constructor(dto?:AthleteDTO){ + super(); + this.id = dto?.id ?? 0; + this.keycloakId = dto?.id_keycloak ?? ""; + this.nom = dto?.name ?? ""; + this.prenom = dto?.prenom ?? "" ; + this.email = ""; //TODO + this.groupes = dto?.groupes ?? []; + this.sessionsID = dto?.sessionIds ?? []; + this.sessions = []; + } } export class Coach extends User{ nom!: string; - role!: Role; + sessionsID!: number[]; + sessions: Session[] = []; + + constructor(dto:CoachDTO); + constructor(); + + constructor(dto?:CoachDTO){ + super(); + this.id = dto?.id ?? 0; + this.keycloakId = dto?.id_keycloak ?? ""; + this.nom = dto?.name ?? ""; + this.prenom = dto?.prenom ?? ""; + this.email = ""; //TODO + this.sessionsID = dto?.sessionIds ?? []; + this.sessions = []; + } } export class Session{ id!: number; name!: string; + activitesID: number[] = []; activites: Activite[] = []; isRecurrent! : Boolean; creneau!: Date; coach!: Coach; + athletesID!: number[] athletes!: Athlete[] duree! : number; groupe! : Groupe; ligne! : Ligne[]; + + constructor(dto:SessionDTO); + constructor(); + + constructor(dto?:SessionDTO){ + this.id = dto?.id?? 0; + this.name = dto?.name?? ""; + this.activitesID = dto?.activiteIds?? []; + this.activites = []; + this.isRecurrent = dto?.isRecurrent?? false; + this.creneau = dto? new Date(dto.creneau) : new Date(); + //this.coach = new Coach(); //dto.coachId; //TODO + this.athletesID = dto?.athleteIds ?? []; + this.athletes = []; + this.duree = dto?.duree ?? 0; + this.groupe = dto?.groupe ?? ""; + this.ligne = []; //TODO + + } } export class Activite{ @@ -54,10 +117,23 @@ export class Activite{ data!: Map; duree!: number; -} + constructor(dto:ActiviteDTO); + constructor(); + constructor(dto?:ActiviteDTO){ + this.id = dto?.id ?? 0; + this.nom = dto?.name ?? ""; + //this.session = dto.sessionId; //TODO + this.theme = dto?.theme ?? ""; + //this.data = //TODO + this.duree = dto?.duree ?? 0; + + } + +} +/* export function getUserTest():User{ - const user = new User(); + const user = new Athlete(); const s1 = new Session(); const s2 = new Session(); const s3 = new Session(); @@ -65,17 +141,17 @@ export function getUserTest():User{ athlete1.id = 1; athlete1.nom = "Alice Dupont"; athlete1.email = "alice@nootnoot.yee"; - athlete1.groupe = "Entrainement"; + athlete1.groupes = ["Entrainement"]; const athlete2 = new Athlete(); athlete2.id = 2; athlete2.nom = "Bob Martin"; - athlete2.groupe = "Competition"; + athlete2.groupes = ["Competition"]; const athlete3 = new Athlete(); athlete3.id = 3; athlete3.nom = "Clara Lopez"; - athlete3.groupe = "Loisir"; + athlete3.groupes = ["Loisir"]; const ligne1 = new Ligne(); ligne1.id = 1; @@ -99,7 +175,6 @@ export function getUserTest():User{ user.id = 0; user.nom = "Emilien-Yee NootNoot"; - user.role = "coach" user.email = "emilien@nootnoot.yee"; s1.creneau = new Date(); s1.id = 1; @@ -187,9 +262,7 @@ export function getUserTest():User{ user.sessions.push(s2); user.sessions.push(s3); - athlete1.role = "athlete"; - athlete2.role = "athlete"; - athlete3.role = "athlete"; return user; } +*/ \ No newline at end of file diff --git a/front_end/src/classesDTO.tsx b/front_end/src/classesDTO.tsx new file mode 100644 index 0000000..5f7d466 --- /dev/null +++ b/front_end/src/classesDTO.tsx @@ -0,0 +1,50 @@ +import { Groupe } from "./classes"; + +export type ActiviteDTO = { + id: number; + name: string; + theme: string; + duree: number; + dataActivite: string[]; + sessionId: number; +} + +export type AdminDTO = { + id: number; + id_keycloak: string; + name: string; + prenom: string; +} + +export type AthleteDTO = { + id:number; + id_keycloak: string; + name: string; + prenom: string; + categorie: string; + niveau: string; + groupes: Groupe[]; + sessionIds: number[]; +}; + +export type CoachDTO = { + id: number; + id_keycloak: string; + name: string; + prenom: string; + sessionIds: number[]; +}; + +export type SessionDTO = { + id: number; + name: string; + isRecurrent: boolean; + creneau: string; // LocalDateTime → ISO string + duree: number; + groupe: Groupe; + + coachId: number; + athleteIds: number[]; + activiteIds: number[]; +}; + diff --git a/front_end/src/components/edt_athlete.tsx b/front_end/src/components/edt_athlete.tsx deleted file mode 100644 index c246705..0000000 --- a/front_end/src/components/edt_athlete.tsx +++ /dev/null @@ -1,65 +0,0 @@ -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; diff --git a/front_end/src/components/login.tsx b/front_end/src/components/login.tsx index cd30877..e01f7f0 100644 --- a/front_end/src/components/login.tsx +++ b/front_end/src/components/login.tsx @@ -1,45 +1,53 @@ import { useKeycloak } from '@react-keycloak/web' import { useEffect } from 'react'; -import { getUserTest, User } from '../classes'; +import { Athlete, User } from '../classes'; import { useLocalData } from '../context/useLocalData'; -import { setAuthToken, clearAuthToken } from '../api'; +import { postAthlete } from '../requetes'; +import { clearAuthToken, setAuthToken } from '../api'; export const Login =() =>{ const {user,setUser} = useLocalData() const { keycloak } = useKeycloak(); useEffect(() => { - const syncAndLoadUser = async () => { - if (keycloak.authenticated && keycloak.token) { - const tokenParsed = keycloak.tokenParsed; - // set axios default auth header for API calls - setAuthToken(keycloak.token); - setUser({ - id: 0, - keycloakId: tokenParsed!.sub!, - email: tokenParsed?.email || "", - nom: tokenParsed?.family_name || "", - prenom: tokenParsed?.given_name || "", - role: "athlete", - 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); - } - } - }; + const syncAndLoadUser = async () => { + const newAthlete:Athlete = new Athlete() - syncAndLoadUser(); - }, [keycloak.authenticated, keycloak.token, setUser]); + const athlete:Athlete = await postAthlete(newAthlete) + + setAuthToken(keycloak.token); + setUser(athlete); + /*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); + } + }*/ + }; + + syncAndLoadUser(); + }, [keycloak.authenticated, keycloak.token, setUser]); function handleLogin(): void { @@ -65,9 +73,6 @@ export const Login =() =>{
User nom : { user.nom}
-
- User role : { user.role} -
} diff --git a/front_end/src/components/object/user.tsx b/front_end/src/components/object/user.tsx index c8efdf4..38dbb0d 100644 --- a/front_end/src/components/object/user.tsx +++ b/front_end/src/components/object/user.tsx @@ -101,20 +101,19 @@ function ObjectUser({admin=null,athlete=null,coach=null}:Props){ return(
handleOpen()}> -
{user2.nom} ({user2.role})
+
{user2.nom}
{/*
{user2.role}
*/}
{open && setOpen(false)}>
-
{user2.nom} ({user2.role})
+
{user2.nom}
{user2.email}
Sessions :
- {user.sessions.map((session,index)=>( - - ))} + {/* TODO */} +
{athlete !== null && (
diff --git a/front_end/src/components/ressourceList.tsx b/front_end/src/components/ressourceList.tsx index 0cd40f2..c674b94 100644 --- a/front_end/src/components/ressourceList.tsx +++ b/front_end/src/components/ressourceList.tsx @@ -72,7 +72,7 @@ function AthleteList({ athletes, sessions }: AthleteListProps) { return (
  • Nom: {a.nom}
    -
    Groupe: {a.groupe}
    +
    Groupe: {a.groupes}
    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 84b2efc..4a3adb7 100644 --- a/front_end/src/components/ressourcePanel.tsx +++ b/front_end/src/components/ressourcePanel.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { useLocalData } from "../context/useLocalData"; -import { Activite, Athlete, Coach , Session, Ligne, getUserTest } from "../classes"; +import { Activite, Athlete, Coach , Session, Ligne, Admin } from "../classes"; import {calculTempsDeJeuParLigne} from "../utils/ligneUtils"; import { keyboard } from "@testing-library/user-event/dist/keyboard"; import ObjectSession from "./object/session"; @@ -10,14 +10,11 @@ import ObjectUser from "./object/user"; export type keyWord = "athletes" | "activites" | "coachs" | "sessions"| "lignes"; export default function RessourcePanel() { - //const { user } = useLocalData(); - const user = getUserTest(); //TODO + const { user } = useLocalData(); + //const user = getUserTest(); //TODO const [value,setValue] = useState("athletes"); - 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; + + if (user instanceof Athlete) return null; const allAthletes: Athlete[] = []; @@ -28,26 +25,8 @@ import ObjectUser from "./object/user"; const allSessions: Session[] = []; - const ligneMap: Map = new Map(); - user.sessions.forEach(session => { - if (session.ligne) { + const allLignes: Ligne[] = []; - session.ligne.forEach(ligne => { - ligneMap.set(ligne.id, ligne); - }); - } - }); - - - 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; - }); @@ -63,7 +42,7 @@ import ObjectUser from "./object/user"; }}> - {user.role === "admin" && } + {user instanceof Admin && } diff --git a/front_end/src/requetes.tsx b/front_end/src/requetes.tsx index 49e31bc..b8362b1 100644 --- a/front_end/src/requetes.tsx +++ b/front_end/src/requetes.tsx @@ -1,6 +1,7 @@ import api, { activiteService, sessionService } from "./api"; import { Activite, Admin, Athlete, Coach, Session, User } from "./classes"; import Keycloak from 'keycloak-js' +import { AdminDTO, AthleteDTO, CoachDTO } from "./classesDTO"; //debug: export function delay(ms: number): Promise { @@ -19,16 +20,19 @@ export async function getUser(keycloak:Keycloak): Promise{ const roles = keycloak.tokenParsed?.realm_access?.roles if(roles!=null){ if(roles.includes("admin")){ - const response = await api.get(`/TODO`); - return response.data; + const response = await api.get(`/TODO`); + const admin = new Admin(response.data); + return admin; } else if(roles.includes("coach")){ - const response = await api.get(`/TODO`); - return response.data; + const response = await api.get(`/TODO`); + const coach = new Coach(response.data); + return coach; } else if(roles.includes("athletes")){ - const response = await api.get(`/TODO`); - return response.data; + const response = await api.get(`/TODO`); + const athlete = new Athlete(response.data); + return athlete; } console.error("Error roles inconnu"); }