merge
This commit is contained in:
@@ -7,6 +7,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class AthleteDTO {
|
public class AthleteDTO {
|
||||||
|
private Integer id;
|
||||||
private String id_keycloak;
|
private String id_keycloak;
|
||||||
private String name;
|
private String name;
|
||||||
private String prenom;
|
private String prenom;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import lombok.Data;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class CoachDTO {
|
public class CoachDTO {
|
||||||
|
private Integer id;
|
||||||
private String id_keycloak;
|
private String id_keycloak;
|
||||||
private String name;
|
private String name;
|
||||||
private String prenom;
|
private String prenom;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import lombok.Data;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class UserDTO {
|
public class UserDTO {
|
||||||
|
private Integer id;
|
||||||
private String id_keycloak;
|
private String id_keycloak;
|
||||||
private String name;
|
private String name;
|
||||||
private String prenom;
|
private String prenom;
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package hackathon.FrisbYEE.jpa.service;
|
package hackathon.FrisbYEE.jpa.service;
|
||||||
|
|
||||||
|
import hackathon.FrisbYEE.jpa.metier.Coach;
|
||||||
import hackathon.FrisbYEE.jpa.metier.User;
|
import hackathon.FrisbYEE.jpa.metier.User;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface UserDAO extends JpaRepository<User, Integer> {
|
public interface UserDAO extends JpaRepository<User, Integer> {
|
||||||
|
Optional<Coach> findByKeycloakId(String keycloakId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ public class AthleteResource {
|
|||||||
|
|
||||||
private AthleteDTO mapToDTO(Athlete athlete) {
|
private AthleteDTO mapToDTO(Athlete athlete) {
|
||||||
AthleteDTO dto = new AthleteDTO();
|
AthleteDTO dto = new AthleteDTO();
|
||||||
|
dto.setId(athlete.getId());
|
||||||
dto.setId_keycloak(athlete.getKeycloakId());
|
dto.setId_keycloak(athlete.getKeycloakId());
|
||||||
dto.setName(athlete.getName());
|
dto.setName(athlete.getName());
|
||||||
dto.setPrenom(athlete.getPrenom());
|
dto.setPrenom(athlete.getPrenom());
|
||||||
@@ -138,6 +139,7 @@ public class AthleteResource {
|
|||||||
|
|
||||||
private Athlete mapToEntity(AthleteDTO dto) {
|
private Athlete mapToEntity(AthleteDTO dto) {
|
||||||
Athlete athlete = new Athlete();
|
Athlete athlete = new Athlete();
|
||||||
|
athlete.setId(dto.getId());
|
||||||
athlete.setName(dto.getName());
|
athlete.setName(dto.getName());
|
||||||
athlete.setPrenom(dto.getPrenom());
|
athlete.setPrenom(dto.getPrenom());
|
||||||
athlete.setKeycloakId(dto.getId_keycloak());
|
athlete.setKeycloakId(dto.getId_keycloak());
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public class CoachResource {
|
|||||||
|
|
||||||
private CoachDTO mapToDTO(Coach coach) {
|
private CoachDTO mapToDTO(Coach coach) {
|
||||||
CoachDTO dto = new CoachDTO();
|
CoachDTO dto = new CoachDTO();
|
||||||
|
dto.setId(coach.getId());
|
||||||
dto.setId_keycloak(coach.getKeycloakId());
|
dto.setId_keycloak(coach.getKeycloakId());
|
||||||
dto.setName(coach.getName());
|
dto.setName(coach.getName());
|
||||||
return dto;
|
return dto;
|
||||||
@@ -77,6 +78,7 @@ public class CoachResource {
|
|||||||
|
|
||||||
private Coach mapToEntity(CoachDTO dto) {
|
private Coach mapToEntity(CoachDTO dto) {
|
||||||
Coach coach = new Coach();
|
Coach coach = new Coach();
|
||||||
|
coach.setId(dto.getId());
|
||||||
coach.setKeycloakId(dto.getId_keycloak());
|
coach.setKeycloakId(dto.getId_keycloak());
|
||||||
coach.setName(dto.getName());
|
coach.setName(dto.getName());
|
||||||
return coach;
|
return coach;
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package hackathon.FrisbYEE.rest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import hackathon.FrisbYEE.jpa.dto.AthleteDTO;
|
||||||
|
import hackathon.FrisbYEE.jpa.dto.UserDTO;
|
||||||
|
import hackathon.FrisbYEE.jpa.metier.Athlete;
|
||||||
|
import hackathon.FrisbYEE.jpa.metier.User;
|
||||||
|
import hackathon.FrisbYEE.jpa.service.UserDAO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/users")
|
||||||
|
@CrossOrigin(origins = "http://localhost:3000")
|
||||||
|
public class UserResource {
|
||||||
|
@Autowired
|
||||||
|
private UserDAO userDAO;
|
||||||
|
|
||||||
|
@Operation(summary = "Récupère tous les utilisateurs")
|
||||||
|
@ApiResponses(value = {
|
||||||
|
@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')")
|
||||||
|
public ResponseEntity<List<UserDTO>> all() {
|
||||||
|
List<User> users = userDAO.findAll();
|
||||||
|
List<UserDTO> dtos = new ArrayList<>();
|
||||||
|
for (User user : users) {
|
||||||
|
dtos.add(mapToDTO(user));
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(dtos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Récupère l'utilisateur ayant l'identifiant correspondant")
|
||||||
|
@ApiResponses(value = {
|
||||||
|
@ApiResponse(responseCode = "200", description = "Récupération effectuée", content = @Content(mediaType = "application/json", schema = @Schema(implementation = UserDTO.class)))
|
||||||
|
})
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('athlete')")
|
||||||
|
public ResponseEntity<UserDTO> getById(@PathVariable String id) {
|
||||||
|
User user = userDAO.findByKeycloakId(id).get();
|
||||||
|
return ResponseEntity.ok(mapToDTO(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDTO mapToDTO(User user) {
|
||||||
|
UserDTO dto = new UserDTO();
|
||||||
|
dto.setId(user.getId());
|
||||||
|
dto.setId_keycloak(user.getKeycloakId());
|
||||||
|
dto.setName(user.getName());
|
||||||
|
dto.setPrenom(user.getPrenom());
|
||||||
|
dto.setRole(user.getRole());
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,18 +9,15 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
import hackathon.FrisbYEE.jpa.metier.Athlete;
|
import hackathon.FrisbYEE.jpa.metier.Athlete;
|
||||||
import hackathon.FrisbYEE.jpa.service.AthleteDAO;
|
import hackathon.FrisbYEE.jpa.service.AthleteDAO;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/users")
|
@RequestMapping("/users")
|
||||||
@CrossOrigin(origins = "http://localhost:3000")
|
@CrossOrigin(origins = "http://localhost:3000")
|
||||||
public class UserSyncResource {
|
public class UserSyncResource {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AthleteDAO athleteDAO;
|
private AthleteDAO athleteDAO;
|
||||||
|
|
||||||
@@ -28,13 +25,10 @@ public class UserSyncResource {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity<Void> sync() {
|
public ResponseEntity<Void> sync() {
|
||||||
Jwt jwt = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
Jwt jwt = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
|
||||||
String keycloakId = jwt.getSubject();
|
String keycloakId = jwt.getSubject();
|
||||||
String firstName = jwt.getClaimAsString("given_name");
|
String firstName = jwt.getClaimAsString("given_name");
|
||||||
String lastName = jwt.getClaimAsString("family_name");
|
String lastName = jwt.getClaimAsString("family_name");
|
||||||
|
|
||||||
if (!athleteDAO.existsByKeycloakId(keycloakId)) {
|
if (!athleteDAO.existsByKeycloakId(keycloakId)) {
|
||||||
System.out.println("New user detected from Keycloak. Syncing: " + firstName + " " + lastName);
|
|
||||||
Athlete athlete = new Athlete();
|
Athlete athlete = new Athlete();
|
||||||
athlete.setKeycloakId(keycloakId);
|
athlete.setKeycloakId(keycloakId);
|
||||||
athlete.setName(lastName);
|
athlete.setName(lastName);
|
||||||
@@ -42,7 +36,6 @@ public class UserSyncResource {
|
|||||||
athlete.setRole(hackathon.FrisbYEE.jpa.metier.Role.athlete);
|
athlete.setRole(hackathon.FrisbYEE.jpa.metier.Role.athlete);
|
||||||
athleteDAO.save(athlete);
|
athleteDAO.save(athlete);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,21 +11,13 @@ import EdtCoach from './components/edt_coach'
|
|||||||
import { Coach } from "./classes";
|
import { Coach } from "./classes";
|
||||||
import RessourcePanel from './components/ressourcePanel';
|
import RessourcePanel from './components/ressourcePanel';
|
||||||
import TestAPI from './components/test_api';
|
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 = {
|
const keycloakInitOptions = {
|
||||||
onLoad: 'login-required',
|
onLoad: 'login-required',
|
||||||
checkLoginIframe: false
|
checkLoginIframe: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactKeycloakProvider authClient={keycloak} /*initOptions={keycloakInitOptions}*/>
|
<ReactKeycloakProvider authClient={keycloak} /*initOptions={keycloakInitOptions}*/>
|
||||||
<LocalDataProvider>
|
<LocalDataProvider>
|
||||||
@@ -36,7 +28,6 @@ function App() {
|
|||||||
<RessourcePanel/>
|
<RessourcePanel/>
|
||||||
<EDT/>
|
<EDT/>
|
||||||
<CreateSession/>
|
<CreateSession/>
|
||||||
<EdtAthlete/>
|
|
||||||
<TestAPI/>
|
<TestAPI/>
|
||||||
</div>
|
</div>
|
||||||
</LocalDataProvider>
|
</LocalDataProvider>
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
import keycloak from "./keycloak";
|
import keycloak from "./keycloak";
|
||||||
|
|
||||||
|
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: "http://localhost:8081/api",
|
// backend listens on 8081 and controllers are mounted at root (no /api prefix)
|
||||||
|
baseURL: "http://localhost:8081",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Simple interceptor to ensure headers object exists; actual token should be set via setAuthToken()
|
||||||
api.interceptors.request.use((config) => {
|
api.interceptors.request.use((config) => {
|
||||||
if (keycloak?.token) {
|
if (keycloak?.token) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
@@ -18,10 +22,23 @@ api.interceptors.request.use((config) => {
|
|||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Helpers to set/clear the Authorization header programmatically (call after Keycloak login)
|
||||||
|
export function setAuthToken(token: string | null | undefined) {
|
||||||
|
if (token) {
|
||||||
|
api.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
||||||
|
} else {
|
||||||
|
delete api.defaults.headers.common["Authorization"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearAuthToken() {
|
||||||
|
delete api.defaults.headers.common["Authorization"];
|
||||||
|
}
|
||||||
|
|
||||||
export const athleteService = {
|
export const athleteService = {
|
||||||
create: (data: any) => api.post("/athletes/create", data),
|
create: (data: any) => api.post("/athletes/create", data),
|
||||||
getAll: () => api.get("/athletes/all"),
|
getAll: () => api.get("/athletes/all"),
|
||||||
getById: (id: number | string) => api.get(`/athletes/${id}`),
|
getByKeycloakId: (id: number | string) => api.get(`/athletes/${id}`),
|
||||||
update: (id: number | string, data: any) => api.put(`/athletes/${id}`, data),
|
update: (id: number | string, data: any) => api.put(`/athletes/${id}`, data),
|
||||||
delete: (id: number | string) => api.delete(`/athletes/${id}`),
|
delete: (id: number | string) => api.delete(`/athletes/${id}`),
|
||||||
|
|
||||||
@@ -63,7 +80,7 @@ export const coachService = {
|
|||||||
// controller doesn't declare a class-level path consistently; support both common patterns
|
// controller doesn't declare a class-level path consistently; support both common patterns
|
||||||
create: (data: any) => api.post(`/coach/create`, data),
|
create: (data: any) => api.post(`/coach/create`, data),
|
||||||
getAll: () => api.get(`/coach/all`),
|
getAll: () => api.get(`/coach/all`),
|
||||||
getById: (id: number | string) => api.get(`/coach/${id}`),
|
getByKeycloakId: (id: number | string) => api.get(`/coach/${id}`),
|
||||||
update: (id: number | string, data: any) => api.put(`/coach/update/${id}`, data),
|
update: (id: number | string, data: any) => api.put(`/coach/update/${id}`, data),
|
||||||
delete: (id: number | string) => api.delete(`/coach/delete/${id}`),
|
delete: (id: number | string) => api.delete(`/coach/delete/${id}`),
|
||||||
|
|
||||||
@@ -73,7 +90,7 @@ export const coachService = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const userService = {
|
export const userService = {
|
||||||
getById: (id: number | string) => api.get(`/users/${id}`),
|
getByKeycloakId: (id: number | string) => api.get(`/users/${id}`),
|
||||||
getAll: () => api.get(`/users`),
|
getAll: () => api.get(`/users`),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
94
front_end/src/components/StatsAthlete.tsx
Normal file
94
front_end/src/components/StatsAthlete.tsx
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Athlete, Session } from "../classes";
|
||||||
|
import { calculStatsAthlete, niveauAlerte, StatsAthlete } from "../utils/athleteUtils"
|
||||||
|
|
||||||
|
interface AthleteStatsProps {
|
||||||
|
athlete: Athlete;
|
||||||
|
sessions: Session[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatAthlete({ athlete, sessions }: AthleteStatsProps) {
|
||||||
|
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 [stats, setStats] = React.useState<StatsAthlete | null>(null);
|
||||||
|
|
||||||
|
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())}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCalculerStats = () => {
|
||||||
|
const statsCalculees = calculStatsAthlete(sessions, athlete, dateDebut, dateFin);
|
||||||
|
setStats(statsCalculees);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="creneau-stats">
|
||||||
|
<label>
|
||||||
|
Début :
|
||||||
|
<input
|
||||||
|
type="datetime-local"
|
||||||
|
value={dateToDatetimeLocal(dateDebut)}
|
||||||
|
onChange={e => setDateDebut(new Date(e.target.value))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Fin :
|
||||||
|
<input
|
||||||
|
type="datetime-local"
|
||||||
|
value={dateToDatetimeLocal(dateFin)}
|
||||||
|
onChange={e => setDateFin(new Date(e.target.value))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Seuil critique :
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={seuilCritique}
|
||||||
|
min={1}
|
||||||
|
onChange={e => setSeuilCritique(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Seuil max :
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={seuilMax}
|
||||||
|
min={1}
|
||||||
|
onChange={e => setSeuilMax(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button onClick={handleCalculerStats}>Calculer les statistiques</button>
|
||||||
|
|
||||||
|
{stats && (
|
||||||
|
<div className="stats-display">
|
||||||
|
<h3>Statistiques de {athlete.nom}</h3>
|
||||||
|
<p><strong>Nombre total de sessions :</strong> {stats.nbSessions}</p>
|
||||||
|
<p><strong>Sessions par semaine :</strong> {stats.nbSessionsPerWeek.toFixed(2)}</p>
|
||||||
|
<p><strong>Statut :</strong> {niveauAlerte(stats, seuilCritique, seuilMax)}</p>
|
||||||
|
|
||||||
|
{stats.distributions.size > 0 && (
|
||||||
|
<>
|
||||||
|
<h4>Distribution des activités :</h4>
|
||||||
|
<ul>
|
||||||
|
{Array.from(stats.distributions.entries()).map(([nomActivite, count]) => (
|
||||||
|
<li key={String(nomActivite)}>
|
||||||
|
{nomActivite} : {count} session(s)
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StatAthlete;
|
||||||
@@ -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 (
|
|
||||||
<div style={{ padding: '20px', border: '1px solid #ccc', margin: '10px' }}>
|
|
||||||
<h3>Test Create Athlete (PostgreSQL)</h3>
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div>
|
|
||||||
<label>Nom: </label>
|
|
||||||
<input type="text" value={formData.name} onChange={(e) => setFormData({...formData, name: e.target.value})} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Prénom: </label>
|
|
||||||
<input type="text" value={formData.prenom} onChange={(e) => setFormData({...formData, prenom: e.target.value})} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Keycloak ID: </label>
|
|
||||||
<input type="text" value={formData.id_keycloak} onChange={(e) => setFormData({...formData, id_keycloak: e.target.value})} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Catégorie: </label>
|
|
||||||
<input type="text" value={formData.categorie} onChange={(e) => setFormData({...formData, categorie: e.target.value})} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Niveau: </label>
|
|
||||||
<input type="text" value={formData.niveau} onChange={(e) => setFormData({...formData, niveau: e.target.value})} />
|
|
||||||
</div>
|
|
||||||
<button type="submit" style={{ marginTop: '10px' }}>Créer l'athlète</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EdtAthlete;
|
|
||||||
@@ -3,6 +3,7 @@ import { useEffect } from 'react';
|
|||||||
import { Athlete, User } from '../classes';
|
import { Athlete, User } from '../classes';
|
||||||
import { useLocalData } from '../context/useLocalData';
|
import { useLocalData } from '../context/useLocalData';
|
||||||
import { postAthlete } from '../requetes';
|
import { postAthlete } from '../requetes';
|
||||||
|
import { clearAuthToken, setAuthToken } from '../api';
|
||||||
|
|
||||||
export const Login =() =>{
|
export const Login =() =>{
|
||||||
const {user,setUser} = useLocalData()
|
const {user,setUser} = useLocalData()
|
||||||
@@ -14,6 +15,7 @@ export const Login =() =>{
|
|||||||
|
|
||||||
const athlete:Athlete = await postAthlete(newAthlete)
|
const athlete:Athlete = await postAthlete(newAthlete)
|
||||||
|
|
||||||
|
setAuthToken(keycloak.token);
|
||||||
setUser(athlete);
|
setUser(athlete);
|
||||||
/*postAthlete
|
/*postAthlete
|
||||||
if (keycloak.authenticated && keycloak.token) {
|
if (keycloak.authenticated && keycloak.token) {
|
||||||
@@ -56,6 +58,7 @@ export const Login =() =>{
|
|||||||
function handleLogout(): void {
|
function handleLogout(): void {
|
||||||
keycloak.logout()
|
keycloak.logout()
|
||||||
setUser(new User());
|
setUser(new User());
|
||||||
|
clearAuthToken();
|
||||||
}
|
}
|
||||||
return(
|
return(
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {delay} from "../../requetes";
|
|||||||
import CreateActivite from '../createActivite';
|
import CreateActivite from '../createActivite';
|
||||||
import { useLocalData } from '../../context/useLocalData';
|
import { useLocalData } from '../../context/useLocalData';
|
||||||
import ObjectSession from './session';
|
import ObjectSession from './session';
|
||||||
|
import StatAthlete from '../StatsAthlete';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
admin?:Admin|null;
|
admin?:Admin|null;
|
||||||
@@ -115,6 +115,11 @@ function ObjectUser({admin=null,athlete=null,coach=null}:Props){
|
|||||||
{/* TODO */}
|
{/* TODO */}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{athlete !== null && (
|
||||||
|
<div className="stats-container">
|
||||||
|
<StatAthlete athlete={athlete} sessions={sessions} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user