Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
Alexis Leboeuf
2026-01-08 13:40:14 +01:00
19 changed files with 561 additions and 268 deletions

View File

@@ -60,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')")
public ResponseEntity<List<AthleteDTO>> all() {
List<Athlete> athletes = athleteDAO.findAll();
List<AthleteDTO> dtos = new ArrayList<>();

View File

@@ -0,0 +1,48 @@
package hackathon.FrisbYEE.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.jwt.Jwt;
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;
@PostMapping("/sync")
@Transactional
public ResponseEntity<Void> 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);
athlete.setPrenom(firstName);
athlete.setRole(hackathon.FrisbYEE.jpa.metier.Role.athlete);
athleteDAO.save(athlete);
}
return ResponseEntity.ok().build();
}
}

View File

@@ -64,6 +64,7 @@ export function getUserTest():User{
const athlete1 = new Athlete();
athlete1.id = 1;
athlete1.nom = "Alice Dupont";
athlete1.email = "alice@nootnoot.yee";
athlete1.groupe = "Entrainement";
const athlete2 = new Athlete();
@@ -99,6 +100,7 @@ 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;
s1.name = "Entrainement Frisbee"

View File

@@ -39,7 +39,7 @@ export function CreateActivite({ returnActivite }: CreateActiciteProps){
return (
<Modal isOpen={true} onClose={() => cancel()}>
<div className="edt_activite_modal">
<div className="create_activite_modal">
<h2>Nouvelle Activité :</h2>
<div>
Nom de l'activité:

View File

@@ -2,14 +2,16 @@ import { useState, useEffect } from "react";
import { Session, User, Coach, Activite, Groupe } from "../classes";
import { useLocalData } from "../context/useLocalData";
import { activiteService, sessionService } from "../api";
import { postSession } from "../requetes";
export const CreateSession = () => {
const {user} = useLocalData()
const {user} = useLocalData();
const [session,setSession] = useState<Session>(new Session());
const [activities, setActivities] = useState<Activite[]>([]);
const [name,setName] = useState("");
const [groupe, setGroupe] = useState<Groupe>("");
const [creneau, setCreneau] = useState("");
const [duree, setDuree] = useState<number>(0);
const [activities, setActivities] = useState<Activite[]>([]);
const [activiteNom, setActiviteNom] = useState("");
const [activiteTheme, setActiviteTheme] = useState("");
const [activiteDuree, setActiviteDuree] = useState(0);
@@ -23,101 +25,78 @@ export const CreateSession = () => {
newActivite.theme=activiteTheme;
newActivite.duree= activiteDuree;
newActivite.data= new Map<string,string>();
try{
await activiteService.create(newActivite);
console.log("Session créée");
setActivities([...activities, newActivite]);
setActivities([...activities, newActivite]);
session.activites.push(newActivite);
setActiviteNom("");
setActiviteTheme("");
setActiviteDuree(0);
} catch (error) {
console.error("Erreur lors de la création de la session", error);
}
}
async function handleCreateSession() {
const data = {
name: name,
creneau: creneau, // string ISO OK
duree: duree,
isRecurrent: isRecurent,
coachId: user?.id,
groupe: groupe ? groupe : undefined,
postSession(session);
console.log("Session créée");
activites: activities.map(a => ({
nom: a.nom,
theme: a.theme,
duree: a.duree
}))
};
// reset
setName("");
setGroupe("");
setCreneau("");
setDuree(0);
setIsRecurent(false);
setActivities([]);
try {
await sessionService.create(data);
console.log("Session créée");
// reset
setName("");
setGroupe("");
setCreneau("");
setDuree(0);
setIsRecurent(false);
setActivities([]);
} catch (err) {
console.error("Erreur lors de la création de la session", err);
}
}
return (
<div className="ent">
<h2>Activité</h2>
<label>
Name:
<input type="text" value={activiteNom} onChange={e => setActiviteNom(e.target.value)} />
</label>
<label>
Theme:
<input type="text" value={activiteTheme} onChange={e => setActiviteTheme(e.target.value)} />
</label>
<label>
Duree (minutes):
<input type="number" value={activiteDuree} onChange={e => setActiviteDuree(Number(e.target.value))} />
</label>
<button type="button" onClick={addAcitivte}>Add Activite</button>
<h2>Créer une session</h2>
<label>
Nom:
<input type="text" value={name} onChange={e => setName(e.target.value)} />
</label>
<label>
Groupe:
</label>
<label>
Creneau:
<input type="datetime-local" value={creneau} onChange={e => setCreneau(e.target.value)} />
</label>
<label>
Duree (minutes):
<input type="number" value={duree} onChange={e => setDuree(Number(e.target.value))} />
</label>
<label>
Recurrent:
<input type="checkbox" checked={isRecurent} onChange={e => setIsRecurent(e.target.checked)} />
</label>
<h3>Ajouter une activité : </h3>
<label>
Nom de l'activitée:
<input type="text" value={activiteNom} onChange={e => setActiviteNom(e.target.value)} />
</label>
<label>
Theme:
<input type="text" value={activiteTheme} onChange={e => setActiviteTheme(e.target.value)} />
</label>
<label>
Duree (minutes):
<input type="number" value={activiteDuree} onChange={e => setActiviteDuree(Number(e.target.value))} />
</label>
<button type="button" onClick={addAcitivte}>Ajouter</button>
<ul>
{activities.map((act, idx) => (
<li key={idx}>{act.nom} - {act.theme} ({act.duree} min)</li>
))}
</ul>
<h2>Create Session</h2>
<label>
Name:
<input type="text" value={name} onChange={e => setName(e.target.value)} />
</label>
<label>
Groupe:
</label>
<label>
Creneau:
<input type="datetime-local" value={creneau} onChange={e => setCreneau(e.target.value)} />
</label>
<label>
Duree (minutes):
<input type="number" value={duree} onChange={e => setDuree(Number(e.target.value))} />
</label>
<label>
Recurrent:
<input type="checkbox" checked={isRecurent} onChange={e => setIsRecurent(e.target.checked)} />
</label>
<ul>
{activities.map((act, idx) => (
<li key={idx}>{act.nom} - {act.theme} ({act.duree} min)</li>
))}
</ul>
<button type="button" onClick={handleCreateSession}>Create Session</button>
<button type="button" onClick={handleCreateSession}>Create Session</button>
</div>
);
);
};
export default CreateSession;

View File

@@ -42,7 +42,7 @@ export const EDT =() =>{
var newWeek: Session[] = []
user.sessions.forEach(session => {
if((session.creneau >= date && session.creneau <= maxDate && !session.isRecurrent) || session.isRecurrent){
if((session.creneau >= date && session.creneau <= maxDate && !session.isRecurrent) || (session.isRecurrent && session.creneau<maxDate)){
newWeek.push(session);
}
});
@@ -121,7 +121,7 @@ export const EDT =() =>{
<button className="edt_button_week_select" onClick={() => handleNext()}>Next</button>
</div>
<div className="edt_colonnes">
<div className="edt_loading">{loading && <Loading/>}</div>
<div className="top_left_loading">{loading && <Loading/>}</div>
{week_days_nums.map((num,index)=>(
<div className="edt_colonne">
<div className="edt_day_header">

View File

@@ -6,6 +6,7 @@ import { Modal } from './Modal';
import Loading from './loading';
import {delay} from "../requetes";
import CreateActivite from './createActivite';
import DetailSession from './object/detailSession';
type Props = {
@@ -15,91 +16,24 @@ type Props = {
function EdtSession({session}:Props){
const [open, setOpen] = useState<boolean>(false);
const [open2, setOpen2] = useState<boolean>(false);
const [loading,setLoading] = useState<boolean>(false);
const [activites,setActivites] = useState<Activite[]>([]);
function handleOpen(): void {
setOpen(!open);
}
function handleDeleteActivite(activite:Activite): void {
session.activites.splice(session.activites.indexOf(activite), 1);
setActivites([...session.activites])
}
function handleAddActivite(): void {
setOpen2(true)
}
async function updateActivites(){
//TODO
await delay(2000);
//await updateActivitiesOfSessionAPI(session);
setLoading(false);
}
useEffect(() => {
if(open){
setLoading(true);
updateActivites()
}
},[open])
useEffect(() => {
if(!loading){
setActivites([...session.activites])
}
},[loading])
const sDate = session.creneau;
function returnActivite(activite: Activite|null){
if(activite!==null){
session.activites.push(activite);
setActivites([...session.activites])
}
setOpen2(false);
}
return(
<div>
<div className="edt_session" onClick={() => handleOpen()}>
<div className="edt_session_header">
<div className="edt_date">{hoursToString(sDate)}</div>
{session.isRecurrent && <div className="edt_date"> recurrent</div>}
</div>
<div>{session.name}</div>
<div className="edt_session_header">
<div className="edt_date">{hoursToString(sDate)}</div>
{session.isRecurrent && <div className="edt_date"> recurrent</div>}
</div>
<div>{session.name}</div>
</div>
{open &&
<Modal isOpen={open} onClose={() => setOpen(false)}>
<div className="edt_session_modal">
<div>{session.name}</div>
<div>{hoursToString(sDate)}</div>
<div>{dateToString(sDate)}</div>
<div>
Activités :
<div className="ent_activite_list">
{activites.map((activite,index)=>(
<div>
{activite.nom}
<button className="deleteButton" onClick={() => handleDeleteActivite(activite)}>x</button>
</div>
))}
<button className="addButton" onClick={() => handleAddActivite()}>+</button>
{loading && <div className='edt_loading'><Loading/></div>}
</div>
</div>
{open2 &&
<CreateActivite returnActivite={(activite) => returnActivite(activite)}/>
}
</div>
</Modal>
}
<DetailSession session={session} open={open} setOpen={setOpen}/>
</div>
)
}

View File

@@ -8,9 +8,18 @@ export const Login =() =>{
const { keycloak } = useKeycloak();
useEffect(() => {
const syncUser = async () => {
const syncAndLoadUser = async () => {
if (keycloak.authenticated && keycloak.token) {
console.log("Attempting to sync user with backend...");
const tokenParsed = keycloak.tokenParsed;
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",
@@ -26,36 +35,8 @@ export const Login =() =>{
}
};
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]);
useEffect(() => {
if (keycloak.authenticated) {
const tokenParsed = keycloak.tokenParsed;
setUser({
id: 0,
keycloakId: tokenParsed!.sub!,
email: tokenParsed?.email,
nom: tokenParsed?.family_name,
prenom: tokenParsed?.given_name,
role: "athlete",
sessions: []
});
}
}, [keycloak.authenticated]);
syncAndLoadUser();
}, [keycloak.authenticated, keycloak.token, setUser]);
function handleLogin(): void {

View File

@@ -0,0 +1,93 @@
import { useEffect, useState } from "react";
import { Activite, Session } from "../../classes";
import { dateToString, hoursToString } from "../edt";
import { Modal } from "../Modal";
import CreateActivite from "../createActivite";
import Loading from "../loading";
import { delay } from "../../requetes";
type Props = {
session:Session;
open:boolean;
setOpen:(b:boolean)=>void
}
function DetailSession({session,open,setOpen}:Props){
const [activites,setActivites] = useState<Activite[]>([]);
const [open2, setOpen2] = useState<boolean>(false);
const [loading,setLoading] = useState<boolean>(false);
const sDate = session.creneau;
function handleDeleteActivite(activite:Activite): void {
session.activites.splice(session.activites.indexOf(activite), 1);
setActivites([...session.activites])
}
function handleAddActivite(): void {
setOpen2(true)
}
async function updateActivites(){
//TODO
await delay(2000);
//await updateActivitiesOfSessionAPI(session);
setLoading(false);
}
useEffect(() => {
if(open){
setLoading(true);
updateActivites()
}
},[open])
function returnActivite(activite: Activite|null){
if(activite!==null){
session.activites.push(activite);
setActivites([...session.activites])
}
setOpen2(false);
}
useEffect(() => {
if(!loading){
setActivites([...session.activites])
}
},[loading])
return(
<Modal isOpen={open} onClose={() => setOpen(false)}>
<div className="object_modal">
<div>{session.name}</div>
<div>{hoursToString(sDate)}</div>
<div>{dateToString(sDate)}</div>
<div>
Activités :
<div className="session_modal_activite_list">
{activites.map((activite,index)=>(
<div>
{activite.nom}
<button className="deleteButton" onClick={() => handleDeleteActivite(activite)}>x</button>
</div>
))}
<button className="addButton" onClick={() => handleAddActivite()}>+</button>
{loading && <div className='top_left_loading'><Loading/></div>}
</div>
</div>
{open2 &&
<CreateActivite returnActivite={(activite) => returnActivite(activite)}/>
}
</div>
</Modal>
)
}
export default DetailSession;

View File

@@ -0,0 +1,47 @@
import { useEffect, useState } from 'react';
import { Activite, Session } from '../../classes';
import { dateToString, hoursToString } from '../edt';
import '../style/objectList.css';
import { Modal } from '../Modal';
import Loading from '../loading';
import {delay} from "../../requetes";
import CreateActivite from '../createActivite';
import DetailSession from './detailSession';
type Props = {
session:Session;
}
function ObjectSession({session}:Props){
const [open, setOpen] = useState<boolean>(false);
function handleOpen(): void {
setOpen(!open);
}
const sDate = session.creneau;
return(
<div>
<div className="object" onClick={() => handleOpen()}>
<div className="object_header">
{session.isRecurrent ?
<div className="object_small"> Recurrent</div> :
<div className="object_small"> {dateToString(session.creneau)}</div>
}
<div className="object_small">{hoursToString(sDate)}</div>
</div>
<div>{session.name}</div>
<div>{session.groupe}</div>
<div>{session.coach ? session.coach.nom : "Pas de coach sur la séance"}</div>
{session.ligne ? session.ligne.map(ligne => ligne.nom).join(", ") : "Pas de ligne sur la séance"}
</div>
<DetailSession session={session} open={open} setOpen={setOpen}/>
</div>
)
}
export default ObjectSession

View File

@@ -0,0 +1,127 @@
import { useEffect, useState } from 'react';
import { Activite, Admin, Athlete, Coach, Session, User } from '../../classes';
import { dateToString, hoursToString } from '../edt';
import '../style/objectList.css';
import { Modal } from '../Modal';
import Loading from '../loading';
import {delay} from "../../requetes";
import CreateActivite from '../createActivite';
import { useLocalData } from '../../context/useLocalData';
import ObjectSession from './session';
type Props = {
admin?:Admin|null;
athlete?:Athlete|null;
coach?:Coach|null;
}
function ObjectUser({admin=null,athlete=null,coach=null}:Props){
const {user,setUser} = useLocalData()
const[user2,setUser2]= useState<User>(getUser());
const [open, setOpen] = useState<boolean>(false);
const [open2, setOpen2] = useState<boolean>(false);
const [loading,setLoading] = useState<boolean>(false);
const [sessions,setSessions] = useState<Session[]>([]);
function getUser(): User{
if(admin!=null) return admin;
if(athlete!=null) return athlete;
if(coach!=null) return coach;
else return new User();
}
function handleOpen(): void {
setOpen(!open);
}
function handleDeleteSession(session:Session): void {
if(athlete!==null){
athlete.sessions.splice(athlete.sessions.indexOf(session), 1);
setSessions([...athlete.sessions])
}
if(coach!==null){
coach.sessions.splice(coach.sessions.indexOf(session), 1);
setSessions([...coach.sessions])
}
}
function handleAddSession(): void {
if(athlete!==null){
setOpen2(true)
}
}
async function updateSession(){
if(athlete!==null){
//TODO
await delay(2000);
//await update ... (athlete);
setLoading(false);
}
}
useEffect(() => {
if(open){
setLoading(true);
updateSession()
}
},[open])
useEffect(() => {
if(!loading){
if(athlete!==null){
setSessions([...athlete.sessions])
}
if(coach!==null){
setSessions([...coach.sessions])
}
}
},[loading])
function returnSession(session: Session|null){
if(session!==null){
if(athlete!==null){
athlete.sessions.push(session);
setSessions([...athlete.sessions])
}
if(coach!==null){
coach.sessions.push(session);
setSessions([...coach.sessions])
}
}
setOpen2(false);
}
return(
<div>
<div className="object" onClick={() => handleOpen()}>
<div>{user2.nom} ({user2.role})</div>
{/* <div>{user2.role}</div> */}
</div>
{open &&
<Modal isOpen={open} onClose={() => setOpen(false)}>
<div className="object_modal">
<div>{user2.nom} ({user2.role})</div>
<div>{user2.email}</div>
<div className='list_object'>
<div>Sessions :</div>
{user.sessions.map((session,index)=>(
<ObjectSession session={session}/>
))}
</div>
</div>
</Modal>
}
</div>
)
}
export default ObjectUser

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Athlete, Activite, Coach, Session, Ligne } from "../classes";
import ObjectSession from "./object/session";
import {calculStatsAthlete, niveauAlerte} from "../utils/athleteUtils";
import {calculTempsDeJeuParLigne} from "../utils/ligneUtils";
@@ -10,6 +11,9 @@ type CoachListProps = { coachs: Coach[] };
type SessionListProps = { sessions: Session[]};
type LigneListProps = { lignes: Ligne[], tempsDeJeuParLigne: Map<number, number> };
function AthleteList({ athletes, sessions }: AthleteListProps) {
const [dateDebut, setDateDebut] = React.useState(new Date());
const [dateFin, setDateFin] = React.useState(new Date());
@@ -118,28 +122,12 @@ function CoachList({ coachs }: CoachListProps) {
function SessionList({ sessions }: SessionListProps) {
return (
<ul className="SessionList">
{sessions.map((sessions) => (
<li key={sessions.id}>
<div>
<strong>Nom:</strong> {sessions.name}
</div>
<div>
<strong>Groupe:</strong> {sessions.groupe}
</div>
<div>
<strong>Recurrent:</strong> {sessions.isRecurrent ? "Oui" : "Non"}
</div>
<div>
<strong>Coach:</strong> {sessions.coach ? sessions.coach.nom : "Pas de coach sur la séance"}
</div>
<div>
<strong>Ligne:</strong> {sessions.ligne ? sessions.ligne.map(ligne => ligne.nom).join(", ") : "Pas de ligne sur la séance"}
</div>
</li>
<div className="list_object">
{sessions.map((session) => (
<ObjectSession session={session}/>
))}
</ul>
</div>
);
}

View File

@@ -1,33 +1,34 @@
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 { Activite, Athlete, Coach , Session, Ligne, getUserTest } from "../classes";
import {calculTempsDeJeuParLigne} from "../utils/ligneUtils";
import { keyboard } from "@testing-library/user-event/dist/keyboard";
import { unescapeLeadingUnderscores } from "typescript";
import ObjectSession from "./object/session";
import ObjectUser from "./object/user";
export type keyWord = "athletes" | "activites" | "coachs" | "sessions"| "lignes";
export default function RessourcePanel() {
const { user } = useLocalData();
//const { user } = useLocalData();
const user = getUserTest(); //TODO
const [value,setValue] = useState<keyWord>("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.role === "athlete") return null;
const athleteMap: Map<number, Athlete> = new Map();
const athleteMap: Map<number, Athlete> = new Map();//TODO
user.sessions.forEach(session => {
session.athletes?.forEach(a => athleteMap.set(a.id, a));
session.athletes?.forEach(a => athleteMap.set(a.id, a));
});
const allAthletes: Athlete[] = Array.from(athleteMap.values());
const activiteMap: Map<number, Activite> = new Map();
user.sessions.forEach(session => {
session.activites?.forEach(act => activiteMap.set(act.id, act));
session.activites?.forEach(act => activiteMap.set(act.id, act));
});
const allActivites: Activite[] = Array.from(activiteMap.values());
@@ -82,49 +83,42 @@ import { unescapeLeadingUnderscores } from "typescript";
}}>
<option value="athletes">Athlètes</option>
<option value="activites">Activités</option>
{user.role === "Admin" && <option value="coachs"> Coachs</option>}
{user.role === "admin" && <option value="coachs"> Coachs</option>}
<option value="sessions"> Sessions</option>
<option value="lignes"> Lignes</option>
</select>
{value==="athletes" && (
<div className="edt_athletes_panel">
<h3>Liste des athlètes</h3>
<AthleteList athletes={allAthletes} sessions={allSessions} />
</div>
)}
{value==="activites" && (
<div className="edt_activites_panel">
<h3>Liste des activités</h3>
<ActiviteList activites={allActivites} />
</div>
)}
{value==="coachs" && (
<div className="edt_coachs_panel">
<h3>Liste des coachs</h3>
<CoachList coachs={allCoachs} />
</div>
)}
{value==="sessions" && (
<div className="edt_sessions_panel">
<h3>Liste des sessions</h3>
<SessionList sessions={allSessions} />
</div>
)}
{value==="lignes" && (
<div className="edt_lignes_panel">
<h3>Liste des lignes</h3>
<LigneList lignes={allLignes} tempsDeJeuParLigne={tempsDeJeuParLigne}/>
</div>
)}
<div className="edt_sessions_panel">
<h3>Liste des {value}</h3>
<div className="list_object">
{value==="athletes" && (
allAthletes.map((athlete) => (
<ObjectUser athlete={athlete}/>
))
)}
{value==="activites" && (
allSessions.map((session) => ( //TODO
<ObjectSession session={session}/>
))
)}
{value==="coachs" && (
allSessions.map((session) => ( //TODO
<ObjectSession session={session}/>
))
)}
{value==="sessions" && (
allSessions.map((session) => (
<ObjectSession session={session}/>
))
)}
{value==="lignes" && (
allSessions.map((session) => ( //TODO
<ObjectSession session={session}/>
))
)}
</div>
</div>
</div>
);
}

View File

@@ -109,16 +109,6 @@
border-radius: 10px;
}
.deleteButton{
background-color: #FF0000;
border-color: #AA0000;
border-radius: 10px;
}
.addButton{
background-color: var(--tint5);
border-radius: 10px;
}
.edt_activite_modal{
background-color: var(--tint3);

View File

@@ -0,0 +1,52 @@
.list_object{
display: grid;
gap:10px;
background-color: var(--tint1);
padding: 10px;
border-radius: 20px;
}
.object {
font-size: clamp(1px, 8cqi, 18px);
gap: 8px;
background-color: var(--tint3);
border-radius: 12px;
padding: 10px;
min-width: 0;
}
.object:hover {
background-color: var(--tint2);
}
.object:active {
background-color: var(--tint4);
}
.object_header{
display: flex;
gap: 5px;
}
.object_small{
font-size: 0.75em;
}
.object_modal{
background-color: var(--tint2);
padding: 10px;
border-radius: 20px;
position: relative;
}
.session_modal_activite_list{
padding: 10px;
background-color: var(--tint3);
border-radius: 10px;
}
.create_activite_modal{
background-color: var(--tint3);
padding: 10px;
border-radius: 20px;
position: relative;
}

View File

@@ -76,9 +76,35 @@ input{
button{
color: var(--text);
background-color: var(--tint3);
border-radius: 8px;
}
select{
color: var(--text);
background-color: var(--tint3);
border-radius: 8px;
}
.deleteButton{
background-color: #FF0000;
border-color: #AA0000;
border-radius: 10px;
}
.addButton{
background-color: var(--tint5);
border-radius: 10px;
}
.top_left_loading{
position: absolute;
inset: 0;
pointer-events: none;
}
.center_loading{
position: absolute;
inset: 0;
place-items: center;
pointer-events: none;
}

View File

@@ -1,4 +1,4 @@
import api from "./api";
import api, { activiteService, sessionService } from "./api";
import { Activite, Admin, Athlete, Coach, Session, User } from "./classes";
//debug:
@@ -115,6 +115,38 @@ export async function postAthlete(athlete: Athlete):Promise<Athlete>{
}
}
export async function postSession(session: Session){
try {
const data = {
name: session.name,
creneau: session.creneau, // string ISO OK
duree: session.duree,
isRecurrent: session.isRecurrent,
coachId: session.coach?.id,
groupe: session.groupe ? session.groupe : undefined,
}
const response = await sessionService.create(data);
session.id = response.data.id; //TODO ?
session.activites.forEach(activite => {
const data2 = {
name: activite.nom,
duree: activite.duree,
date: activite.data,
theme: activite.theme,
sessionId: session.id, //TODO
}
activiteService.create(data2);
// console.log("Session créée");
});
} catch (error) {
console.error("Error post Session:", error);
throw error;
}
}
export async function postAdmin(athlete: Admin):Promise<Admin>{
try {
const response = await api.post<Admin>("/admin/create/",athlete);