gestion des groupe, todo : rejoindre une session

This commit is contained in:
trochas
2026-01-12 14:06:38 +01:00
parent 4bd0e0a299
commit 3fce71b893
11 changed files with 171 additions and 27 deletions

View File

@@ -77,6 +77,36 @@ public class AthleteResource {
return ResponseEntity.ok(dtos);
}
@Operation(summary = "Récupère tous les athlètes d'un groupe")
@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/group/{groupe}")
@PreAuthorize("hasRole('admin') or hasRole('coach')")
public ResponseEntity<List<AthleteDTO>> allByGroup(@PathVariable String groupe) {
List<Athlete> athletes = athleteDAO.findAll();
List<AthleteDTO> dtos = new ArrayList<>();
for (Athlete athlete : athletes) {
if(groupe.equals("None")){
if(athlete.getGroupe().size()==0){
dtos.add(mapToDTO(athlete));
}
}
else{
boolean containsGroupe = false;
for (String g : athlete.getGroupe()) {
containsGroupe = containsGroupe || g.equals(groupe);
}
if(containsGroupe){
dtos.add(mapToDTO(athlete));
}
}
}
return ResponseEntity.ok(dtos);
}
@Operation(summary = "Récupère l'athlète ayant l'identifiant correspondant")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Récupération effectuée", content = @Content(mediaType = "application/json", schema = @Schema(implementation = AthleteDTO.class)))
@@ -151,6 +181,7 @@ public class AthleteResource {
dto.setPrenom(athlete.getPrenom());
dto.setCategorie(athlete.getCategorie());
dto.setNiveau(athlete.getNiveau());
dto.setGroupes(athlete.getGroupe());
return dto;
}

View File

@@ -76,6 +76,23 @@ public class SessionResource {
return ResponseEntity.ok(dtos);
}
@GetMapping("/all/group/{groupe}")
@PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('athlete')")
public ResponseEntity<List<SessionDTO>> getAllByGroup(@PathVariable String groupe) {
List<Session> sessions = sessionDAO.findAll();
List<SessionDTO> dtos = new ArrayList<>();
String groupeStr = groupe;
if(groupe.equals("None")) groupeStr = "";
for (Session session : sessions) {
if(session.getGroupe().equals(groupeStr)){
dtos.add(maptoDTO(session));
}
}
return ResponseEntity.ok(dtos);
}
@GetMapping("/all-between-dates")
@PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('athlete')")
public ResponseEntity<List<SessionDTO>> getAllBetweenDates(
@@ -100,6 +117,31 @@ public class SessionResource {
return ResponseEntity.ok(dtos);
}
@GetMapping("/all-between-dates/group/{groupe}")
@PreAuthorize("hasRole('admin') or hasRole('coach') or hasRole('athlete')")
public ResponseEntity<List<SessionDTO>> getAllBetweenDates(@RequestParam LocalDate startDate,@RequestParam LocalDate endDate,@PathVariable String groupe) {
List<Session> sessions = sessionDAO.findAll();
List<SessionDTO> dtos = new ArrayList<>();
System.out.println("date : " + startDate + " " + endDate);
String groupeStr = groupe;
if(groupe.equals("None")) groupeStr = "";
for (Session session : sessions) {
if(session.getGroupe().equals(groupeStr)){
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

@@ -170,7 +170,7 @@ code {
}
/* Cards et containers */
.card {
/* .card {
background: linear-gradient(135deg, var(--tint1) 0%, var(--tint2) 100%);
border-radius: 20px;
padding: 24px;
@@ -196,7 +196,7 @@ code {
.card-body {
color: var(--text);
}
} */
/* Inputs */
input[type="text"],
@@ -385,19 +385,20 @@ button.add:hover,
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
max-height:100%;
z-index: 100;
height:100vh;
width:100vw;
}
.modal-overlay {
/* .modal-overlay {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(4px);
display: grid;
place-items: center;
z-index: 1000;
z-index: 100;
}
.modal-header {
@@ -426,7 +427,7 @@ button.add:hover,
.modal-close:hover {
background-color: #dc2626;
color: white;
}
} */
/* Loading */
.loading {
@@ -649,4 +650,4 @@ input[type="radio"] {
min-width: 280px;
margin: 10px;
}
}
}

View File

@@ -39,6 +39,7 @@ export const athleteService = {
// controller is mounted at /athlete
create: (data: any) => api.post<AthleteDTO>("/athlete/create", data),
getAll: () => api.get<AthleteDTO[]>("/athlete/all"),
getAllByGroup: (group:String) => api.get<AthleteDTO[]>(`/athlete/all/group/${group}`),
getById: (id: number | string) => api.get(`/athlete/${id}`),
getByKeycloakId: (keycloakId: string) => api.get(`/athlete/keycloak/${encodeURIComponent(keycloakId)}`),
update: (id: number | string, data: any) => api.put(`/athlete/${id}`, data),
@@ -72,7 +73,9 @@ export const sessionService = {
// controller uses singular /session/* endpoints
create: (data: SessionDTO) => api.post(`/session/create`, data),
getAll: () => api.get<SessionDTO[]>(`/session/all`),
getAllByGroup: (group: String) => api.get<SessionDTO[]>(`/session/all/group/${group}`),
getAllBetweenDate: (data: any) => api.get<SessionDTO[]>(`/session/all-between-dates`,{params: data,}),
getAllBetweenDateByGroup: (group:String,data: any) => api.get<SessionDTO[]>(`/session/all-between-dates/group/${group}`,{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),

View File

@@ -46,7 +46,7 @@ export class Admin extends User{
}
export class Athlete extends User{
groupes!: Groupe[];
groupes: Groupe[] = [];
sessionsID!: number[];
sessions: Session[] = [];
@@ -59,7 +59,7 @@ export class Athlete extends User{
this.keycloakId = dto?.id_keycloak ?? "";
this.nom = dto?.name ?? "";
this.prenom = dto?.prenom ?? "" ;
this.email = ""; //TODO
this.email = "";
this.groupes = dto?.groupes ?? [];
this.sessionsID = dto?.sessionIds ?? [];
this.sessions = [];

View File

@@ -13,11 +13,13 @@ export const Login =() =>{
const {userLocal: user,setUserLocal: setUser} = useLocalData()
const { keycloak } = useKeycloak();
const [open,setOpen] = useState<boolean>(false);
const [openGroupe,setOpenGroupe] = useState<boolean>(false);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) {
setOpen(false);
setOpen(false);
setOpenGroupe(false);
}
};
@@ -44,6 +46,10 @@ export const Login =() =>{
}
}
function openGestionGroupe(): void {
setOpenGroupe(true);
}
useEffect(() => {
loginUser()
},[keycloak.authenticated])
@@ -57,6 +63,7 @@ export const Login =() =>{
setUser(new User());
clearAuthToken();
setOpen(false);
setOpenGroupe(false);
}
function handleOpen(): void {
@@ -80,12 +87,37 @@ export const Login =() =>{
{user instanceof Athlete && <div>Role : Athlete</div>}
{user instanceof Coach && <div>Role : Coach</div>}
{user instanceof Admin && <div>Role : Admin</div>}
{user instanceof Athlete &&
<div>
<div>Groupes : </div>
{(user.groupes.length > 0) ?
<div>
{user.groupes.map((groupe) => (
groupe
))}
</div>
:
<div>aucun groupe</div>}
<button onClick={() => openGestionGroupe()}>gérer les groupes</button>
{/*user instanceof Athlete &&
<div>
<div>
<button>{user.groupes.includes("Loisir")?"Quitter":"Rejoindre"} groupe Loisir</button>
<button>{user.groupes.includes("Entrainement")?"Quitter":"Rejoindre"}Rejoindre groupe Entrainement</button>
<button>{user.groupes.includes("Competition")?"Quitter":"Rejoindre"}Rejoindre groupe Compétition</button>
</div>
</div>
*/}
</div>
}
</div>
<button onClick={() => handleLogout()}>
Se déconnecter
</button>
</div>
}
</div>

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { useLocalData } from "../context/useLocalData";
import { Activite, Athlete, Coach , Session, Ligne, Admin } from "../classes";
import { Activite, Athlete, Coach , Session, Ligne, Admin, Groupe } from "../classes";
import {calculTempsDeJeuParLigne} from "../utils/ligneUtils";
import { keyboard } from "@testing-library/user-event/dist/keyboard";
import ObjectSession from "./object/session";
@@ -24,10 +24,11 @@ import ObjectLigne from "./object/lignes";
const[allCoachs,setAllCoachs] = useState<Coach[]>([]);
const[allSessions,setAllSessions] = useState<Session[]>([]);
const[allLignes,setAllLignes] = useState<Ligne[]>([]);
const [groupe, setGroupe] = useState<String>("Tous");
async function updateAthletes() {
const athletes:Athlete[] = await getAllAthlete();
const athletes:Athlete[] = await getAllAthlete(groupe);
setAllAthletes(athletes);
}
@@ -43,7 +44,7 @@ import ObjectLigne from "./object/lignes";
}
async function updateSessions() {
const sessions:Session[] = await getAllSessionsAPI();
const sessions:Session[] = await getAllSessionsAPI(groupe);
setAllSessions(sessions);
}
@@ -53,7 +54,7 @@ import ObjectLigne from "./object/lignes";
useEffect(() => {
update();
},[user,value])
},[user,value,groupe])
function update(){
if(keycloak.authenticated){
@@ -89,9 +90,22 @@ import ObjectLigne from "./object/lignes";
{(user instanceof Admin || user instanceof Coach) &&<option value="lignes"> Lignes</option>}
</select>
{(value == "athletes" || value == "sessions") &&
<select
onChange={(e) => {
const v = (e.target as HTMLSelectElement).value;
setGroupe(v as Groupe)
console.log(v);
}}>
<option value="Tous"> Tous</option>
<option value="None"> Sans groupe</option>
<option value="Loisir">Loisir</option>
<option value="Entrainement">Entrainement</option>
<option value="Competition">Competition</option>
</select>
}
</div>
}
<div className="edt_sessions_panel">
<h3>Liste des {value}</h3>
<button onClick={()=>update()}> Actualiser </button>

View File

@@ -29,6 +29,7 @@
gap: 16px;
color: var(--text);
width: 100%;
overflow: hidden;
}
.edt_loading {
@@ -66,13 +67,13 @@
color: white;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
}
.edt_day_header.today {
background: linear-gradient(135deg, var(--green-dark) 0%, var(--cyan-accent) 100%);
color: #FFFFFF;
box-shadow: 0 6px 20px var(--green-A-primary);
transform: scale(1.02);
}
.edt_day_content {
@@ -95,6 +96,7 @@
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
box-shadow: var(--shadow-sm);
overflow: hidden;
}
.edt_session:hover {

View File

@@ -54,10 +54,13 @@
align-items: center;
justify-content: flex-end;
gap: 10px;
z-index: 1;
}
.loginContainer {
position: relative;
z-index: 9999;
isolation: isolate;
}
.login {

View File

@@ -17,7 +17,7 @@ function TopBar(){
</div>
<div className="toBarMidle">
<Link to="/">Home</Link>
<Link to="/sessions">Sessions</Link>
<Link to="/sessions">{(userLocal instanceof Athlete) ? "Sessions" : "List"}</Link>
{userLocal instanceof Coach &&<Link to="/gestion">Gestion</Link>}
{userLocal instanceof Admin &&<Link to="/admin">Admin</Link>}
</div>

View File

@@ -300,9 +300,15 @@ export async function getSessionsOfUserAPI(user:User): Promise<Session[]>{
}
}
export async function getAllSessionsAPI():Promise<Session[]>{
export async function getAllSessionsAPI(groupe?:String):Promise<Session[]>{
try {
const response = await sessionService.getAll();
var response;
if(groupe!=null && groupe!="Tous"){
response = await sessionService.getAllByGroup(groupe);
}
else{
response = await sessionService.getAll();
}
const sessions = await Promise.all(
response.data.map(async sessionDTO => {
const session = new Session(sessionDTO);
@@ -332,15 +338,19 @@ function formatDateLocal(date: Date): string {
}
export async function getAllSessionsBetweenAPI(d1:Date,d2:Date):Promise<Session[]>{
export async function getAllSessionsBetweenAPI(d1:Date,d2:Date,groupe?:String):Promise<Session[]>{
try {
const data = {
startDate: formatDateLocal(d1),
endDate: formatDateLocal(d2)
}
console.log(d1 + " " + d2);
console.log(data);
const response = await sessionService.getAllBetweenDate(data);
var response;
if(groupe!=null && groupe!="Tous"){
response = await sessionService.getAllBetweenDateByGroup(groupe,data);
}
else {
response = await sessionService.getAllBetweenDate(data);
}
const sessions = await Promise.all(
response.data.map(async sessionDTO => {
const session = new Session(sessionDTO);
@@ -387,9 +397,15 @@ export async function getAllCoach(): Promise<Coach[]> {
}
//ATHLETE
export async function getAllAthlete(): Promise<Athlete[]> {
export async function getAllAthlete(groupe?:String): Promise<Athlete[]> {
try {
const response = await athleteService.getAll();
var response;
if(groupe!=null && groupe!="Tous"){
response = await athleteService.getAllByGroup(groupe);
}
else{
response = await athleteService.getAll();
}
const athletes:Athlete[] = []
response.data.forEach(dto => {
athletes.push(new Athlete(dto));