correction bug admin + lecture de toute les session dans l'edt pour l'admin

This commit is contained in:
trochas
2026-01-11 16:56:10 +01:00
parent ecbddd3a58
commit ddb2b93489
16 changed files with 176 additions and 51 deletions

View File

@@ -1,12 +1,10 @@
package hackathon.FrisbYEE.jpa.dto;
import hackathon.FrisbYEE.jpa.metier.Role;
import lombok.Data;
@Data
public class AdminDTO {
private String id_keycloak;
private Integer id;
private String id_keycloak;
private String name;
private String prenom;
private Role role;
}

View File

@@ -3,17 +3,12 @@ package hackathon.FrisbYEE.rest;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.*;
import hackathon.FrisbYEE.jpa.dto.AdminDTO;
import hackathon.FrisbYEE.jpa.metier.Admin;
import hackathon.FrisbYEE.jpa.service.AdminDAO;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import hackathon.FrisbYEE.jpa.service.UserDAO;
@RestController
@RequestMapping("/admin")
@@ -22,12 +17,24 @@ public class AdminResource {
@Autowired
private AdminDAO adminDAO;
@Autowired
private UserDAO userDAO;
@PostMapping("/create")
@PreAuthorize("hasRole('Admin')") // Only admin can create
@PreAuthorize("hasRole('admin')") // Only admin can create
public ResponseEntity<AdminDTO> create(@RequestBody AdminDTO dto) {
userDAO.findByKeycloakId(dto.getId_keycloak())
.ifPresent(existing -> {
if (!(existing instanceof Admin)) {
userDAO.delete(existing);
userDAO.flush();
}
});
Admin admin = mapToEntity(dto);
if(adminDAO.findByKeycloakId(admin.getKeycloakId()).isPresent()) {
return ResponseEntity.status(200).body(mapToDTO(adminDAO.findByKeycloakId(admin.getKeycloakId()).get()));
}
@@ -55,7 +62,6 @@ public class AdminResource {
dto.setId_keycloak(admin.getKeycloakId());
dto.setName(admin.getName());
dto.setPrenom(admin.getPrenom());
dto.setRole(admin.getRole());
return dto;
}
@@ -65,7 +71,7 @@ public class AdminResource {
admin.setKeycloakId(dto.getId_keycloak());
admin.setName(dto.getName());
admin.setPrenom(dto.getPrenom());
admin.setRole(dto.getRole());
admin.setRole(hackathon.FrisbYEE.jpa.metier.Role.admin);
return admin;
}

View File

@@ -1,8 +1,11 @@
package hackathon.FrisbYEE.rest;
import hackathon.FrisbYEE.jpa.dto.CoachDTO;
import hackathon.FrisbYEE.jpa.metier.Admin;
import hackathon.FrisbYEE.jpa.metier.Coach;
import hackathon.FrisbYEE.jpa.service.CoachDAO;
import hackathon.FrisbYEE.jpa.service.UserDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -17,13 +20,26 @@ import java.util.List;
@RestController
@RequestMapping("/coach")
public class CoachResource {
@Autowired
private CoachDAO coachDAO;
@Autowired
private UserDAO userDAO;
@PostMapping("/create")
@PreAuthorize("hasRole('Admin')") // Only admin can create
@PreAuthorize("hasRole('admin') or hasRole('coach')") // Only admin can create
public ResponseEntity<CoachDTO> create(@RequestBody CoachDTO dto) {
userDAO.findByKeycloakId(dto.getId_keycloak())
.ifPresent(existing -> {
if (!(existing instanceof Coach)) {
userDAO.delete(existing);
userDAO.flush();
}
});
Coach coach = mapToEntity(dto);
if(coachDAO.existsByKeycloakId(coach.getKeycloakId())) {
return ResponseEntity.status(200).body(mapToDTO(coachDAO.findByKeycloakId(coach.getKeycloakId()).get()));
}

View File

@@ -19,6 +19,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@@ -69,6 +70,30 @@ public class SessionResource {
return ResponseEntity.ok(dtos);
}
@GetMapping("/all-between-dates")
@PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('athlete')")
public ResponseEntity<List<SessionDTO>> getAllBetweenDates(
@RequestParam LocalDate startDate,
@RequestParam LocalDate endDate
) {
List<Session> sessions = sessionDAO.findAll();
List<SessionDTO> dtos = new ArrayList<>();
System.out.println("date : " + startDate + " " + endDate);
for (Session session : sessions) {
LocalDate sessionDate = session.getCreneau().toLocalDate();
boolean isBetween =
(!sessionDate.isBefore(startDate) || session.getIsRecurrent()) &&
!sessionDate.isAfter(endDate);
if (isBetween) {
dtos.add(maptoDTO(session));
}
}
return ResponseEntity.ok(dtos);
}
@GetMapping("/{id}")
@PreAuthorize("hasRole('coach') or hasRole('athlete')")
public ResponseEntity<?> getById(@PathVariable Integer id) {

View File

@@ -62,11 +62,16 @@ export const activiteService = {
getByTheme: (theme: string) => api.get(`/activite/theme/${encodeURIComponent(theme)}`),
getDataActivite: (id: number | string) => api.get(`/activite/${id}`),
};
type DateBetween = {
startDate: string;
endDate: string;
}
export const sessionService = {
// controller uses singular /session/* endpoints
create: (data: any) => api.post(`/session/create`, data),
create: (data: SessionDTO) => api.post(`/session/create`, data),
getAll: () => api.get<SessionDTO[]>(`/session/all`),
getAllBetweenDate: (data: any) => api.get<SessionDTO[]>(`/session/all-between-dates`,{params: data,}),
getById: (id: number | null) => api.get(`/session/${id}`),
delete: (id: number | null) => api.delete(`/session/delete/${id}`),
update: (id: number | null, data: any) => api.put(`/session/update/${id}`, data),
@@ -79,7 +84,7 @@ export const sessionService = {
export const coachService = {
// controller doesn't declare a class-level path consistently; support both common patterns
create: (data: any) => api.post<CoachDTO>(`/coach/create`, data),
create: (data: CoachDTO) => api.post<CoachDTO>(`/coach/create`, data),
getAll: () => api.get<CoachDTO[]>(`/coach/all`),
getById: (id: number) => api.get(`/coach/${id}`),
getByKeycloakId: (keycloakId: string) => api.get(`/coach/keycloak/${keycloakId}`),
@@ -97,7 +102,8 @@ export const userService = {
export const adminService = {
getByKeycloakId: (keycloak_id: string) => api.get(`/admin/keycloak/${keycloak_id}`),
getById: (id: number | string) => api.get(`/admin/${id}`),
getById: (id: number | string) => api.get<AdminDTO>(`/admin/${id}`),
create: (data: AdminDTO) => api.post<AdminDTO>("/admin/create", data),
};
export default api;

View File

@@ -55,7 +55,7 @@ export class Athlete extends User{
constructor(dto?:AthleteDTO){
super();
this.id = dto?.id ?? 0;
this.id = dto?.id ?? null;
this.keycloakId = dto?.id_keycloak ?? "";
this.nom = dto?.name ?? "";
this.prenom = dto?.prenom ?? "" ;
@@ -90,7 +90,7 @@ export class Coach extends User{
constructor(dto?:CoachDTO){
super();
this.id = dto?.id ?? 0;
this.id = dto?.id ?? null;
this.keycloakId = dto?.id_keycloak ?? "";
this.nom = dto?.name ?? "";
this.prenom = dto?.prenom ?? "";

View File

@@ -6,7 +6,7 @@ import { createSessionAPI, postSession } from "../requetes";
import './style/createSession.css';
export const CreateSession = () => {
const {user} = useLocalData();
const {userLocal: user} = useLocalData();
const [session,setSession] = useState<Session>(new Session());
const [activities, setActivities] = useState<Activite[]>([]);
const [name,setName] = useState("");

View File

@@ -1,8 +1,8 @@
import { useEffect, useState } from "react"
import { Athlete, Coach, Session} from "../classes"
import { Admin, Athlete, Coach, Session} from "../classes"
import { useLocalData } from "../context/useLocalData"
import './style/edt.css';
import {getSessionsOfUserAPI } from "../requetes";
import {getAllSessionsAPI, getAllSessionsBetweenAPI, getSessionsOfUserAPI } from "../requetes";
import EdtSession from "./edt_session";
import {delay} from "../requetes";
import Loading from "./loading";
@@ -26,7 +26,8 @@ export function hoursToString(date:Date){
export const EDT =() =>{
const {user,setUser} = useLocalData()
const {userLocal} = useLocalData()
const {sessionsLocal,setSessionsLocal} = useLocalData()
const [sessions, setSessions] = useState<Session[]>([])
const [week,setWeek] = useState<Date>(getFirstDay(new Date()));
const [loadedWeek,setLoadedWeek] = useState<Date|null>(null);
@@ -35,12 +36,21 @@ export const EDT =() =>{
const week_days_nums:number[] = [1,2,3,4,5,6,0];
function loadSessions(date:Date){
var maxDate = getNextDay(date,6)
var maxDate = toDateOnly(getNextDay(date,6));
var newWeek: Session[] = []
if(user instanceof Athlete || user instanceof Coach){
user.sessions.forEach(session => {
if((session.creneau >= date && session.creneau <= maxDate && !session.isRecurrent) || (session.isRecurrent && session.creneau<maxDate)){
if(userLocal instanceof Athlete || userLocal instanceof Coach){
userLocal.sessions.forEach(session => {
const creneau = toDateOnly(session.creneau);
if((creneau >= date || session.isRecurrent) && (creneau <= maxDate)){
newWeek.push(session);
}
});
}
else if(userLocal instanceof Admin){
sessionsLocal.forEach(session => {
const creneau = toDateOnly(session.creneau);
if((creneau >= date || session.isRecurrent) && (creneau <= maxDate)){
newWeek.push(session);
}
});
@@ -50,7 +60,7 @@ export const EDT =() =>{
}
function changeWeek(date:Date){
setWeek(date);
setWeek(toDateOnly(date));
}
function isSameDay(date1:Date,date2:Date){
@@ -65,7 +75,7 @@ export const EDT =() =>{
updateWeek(week);
loadSessions(week)
setLoading(true);
},[week,user])
},[week,userLocal])
useEffect(() => {
if(loadedWeek!==null){
@@ -83,9 +93,22 @@ export const EDT =() =>{
//TODO updateSession
//await delay(2000);
//await updateSessionsOfUser(user,null,null);
if(user instanceof Athlete || user instanceof Coach){
const newSessions:Session[] = await getSessionsOfUserAPI(user);
user.sessions = newSessions;
if(userLocal instanceof Athlete || userLocal instanceof Coach){
const newSessions:Session[] = await getSessionsOfUserAPI(userLocal);
userLocal.sessions = newSessions;
}
else if(userLocal instanceof Admin){
const newSessions:Session[] = await getAllSessionsBetweenAPI(week,getNextDay(week,6));
const date = toDateOnly(week);
var maxDate = toDateOnly(getNextDay(date,6));
sessionsLocal.forEach(sessionLocal => { //update seulement la semaine
const creneau = toDateOnly(sessionLocal.creneau);
if(!((creneau >= date || sessionLocal.isRecurrent) && (creneau <= maxDate))){
newSessions.push(sessionLocal);
}
});
setSessionsLocal(newSessions);
}
setLoadedWeek(week);
}
@@ -108,7 +131,15 @@ export const EDT =() =>{
else{
firstDate = getNextDay(date,-numWeek+1);
}
return firstDate;
return toDateOnly(firstDate);
}
function toDateOnly(date: Date): Date {
return new Date(
date.getFullYear(),
date.getMonth(),
date.getDate()
);
}
function getNextDay(date:Date,nb:number):Date{

View File

@@ -9,7 +9,7 @@ import { Modal } from './Modal';
import './style/topBar.css';
export const Login =() =>{
const {user,setUser} = useLocalData()
const {userLocal: user,setUserLocal: setUser} = useLocalData()
const { keycloak } = useKeycloak();
const [open,setOpen] = useState<boolean>(false);

View File

@@ -15,7 +15,7 @@ type Props = {
function DetailSession({session,open,setOpen}:Props){
const {user,setUser} = useLocalData()
const {userLocal: user,setUserLocal: setUser} = useLocalData()
const [activites,setActivites] = useState<Activite[]>([]);
const [open2, setOpen2] = useState<boolean>(false);

View File

@@ -104,6 +104,9 @@ function ObjectUser({user}:Props){
<div className="object_modal">
<div>{user.prenom}</div>
<div>{user.nom}</div>
{user instanceof Athlete && <div>Role : Athlete</div>}
{user instanceof Coach && <div>Role : Coach</div>}
{user instanceof Admin && <div>Role : Admin</div>}
{(user instanceof Athlete || user instanceof Coach) &&
<div className='padding'>
<div className='list_object_modal'>

View File

@@ -15,7 +15,7 @@ import ObjectLigne from "./object/lignes";
export default function RessourcePanel() {
const { keycloak } = useKeycloak();
const { user } = useLocalData();
const { userLocal: user } = useLocalData();
//const user = getUserTest(); //TODO
const [value,setValue] = useState<keyWord>("sessions");

View File

@@ -2,12 +2,10 @@ import { createContext } from 'react'
import { Session, User } from '../classes';
interface LocalDataContextType {
user:User;
setUser: React.Dispatch<React.SetStateAction<User>>
sessions: Session[];
setSessions: React.Dispatch<React.SetStateAction<Session[]>>
users: User[];
setUsers: React.Dispatch<React.SetStateAction<User[]>>
userLocal:User;
setUserLocal: React.Dispatch<React.SetStateAction<User>>
sessionsLocal: Session[];
setSessionsLocal: React.Dispatch<React.SetStateAction<Session[]>>
}

View File

@@ -3,15 +3,14 @@ import { Session, User } from '../classes'
import { LocalDataContext } from '../context/LocalDataContext'
export const LocalDataProvider = ({ children }: { children: React.ReactNode }) => {
const [user, setUser] = useState<User>(new User())
const [sessions, setSessions] = useState<Session[]>([])
const [users, setUsers] = useState<User[]>([])
const [userLocal, setUserLocal] = useState<User>(new User())
const [sessionsLocal, setSessionsLocal] = useState<Session[]>([])
return (
<LocalDataContext.Provider
value={{ user, setUser, sessions, setSessions, users, setUsers }}>
value={{ userLocal, setUserLocal, sessionsLocal,setSessionsLocal}}>
{children}
</LocalDataContext.Provider>
)

View File

@@ -1,4 +1,4 @@
import api, { activiteService, athleteService, coachService, sessionService } from "./api";
import api, { activiteService, adminService, athleteService, coachService, sessionService } from "./api";
import { Activite, Admin, Athlete, Coach, Session, User } from "./classes";
import Keycloak from 'keycloak-js'
import { AdminDTO, AthleteDTO, CoachDTO, SessionDTO } from "./classesDTO";
@@ -27,7 +27,9 @@ export async function loginOrRegister(keycloak:Keycloak): Promise<User|null>{
newAdmin.email = keycloak.tokenParsed.email || "";
newAdmin.nom = keycloak.tokenParsed.family_name || "";
newAdmin.prenom = keycloak.tokenParsed.given_name || "";
const response = await athleteService.create(newAdmin.toDTO());
console.log(newAdmin.keycloakId);
console.log(newAdmin.toDTO().id_keycloak);
const response = await adminService.create(newAdmin.toDTO());
const admin = new Admin(response.data);
return admin;
}
@@ -217,7 +219,7 @@ export async function postAthlete(athlete: Athlete):Promise<Athlete>{
export async function postSession(session: Session){
try {
const data = {
/* const data = {
name: session.name,
creneau: session.creneau, // string ISO OK
duree: session.duree,
@@ -225,9 +227,9 @@ export async function postSession(session: Session){
coachId: session.coach?.id,
groupe: session.groupe ? session.groupe : undefined,
}
}*/
const response = await sessionService.create(data);
const response = await sessionService.create(session.toDTO());
session.id = response.data.id; //TODO ?
session.activites.forEach(activite => {
@@ -331,6 +333,44 @@ export async function getAllSessionsAPI():Promise<Session[]>{
}
}
function formatDateLocal(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
export async function getAllSessionsBetweenAPI(d1:Date,d2:Date):Promise<Session[]>{
try {
const data = {
startDate: formatDateLocal(d1),
endDate: formatDateLocal(d2)
}
console.log(d1 + " " + d2);
console.log(data);
const response = await sessionService.getAllBetweenDate(data);
const sessions = await Promise.all(
response.data.map(async sessionDTO => {
const session = new Session(sessionDTO);
const coach = await getCoachByIdAPI(sessionDTO.coachId);
if (coach != null) {
session.coach = coach;
}
return session;
})
);
return sessions;
} catch (error) {
console.error("Error fetching sessions:", error);
throw error;
}
}
//COACH
export async function getAllCoach(): Promise<Coach[]> {
try {

View File

@@ -30,6 +30,8 @@
background-color: rgba(255, 255, 255, 0.98);
overflow: hidden;
transition: transform 0.3s ease;
width: fit-content;
margin: 0 auto;
}
.card-pf:hover {
@@ -38,6 +40,7 @@
0 8px 20px rgba(16, 185, 129, 0.15);
}
/* Header de la card */
#kc-form-login .card-pf h1,
.login-pf-page h1 {