subscribe + unsubscribe + edt

This commit is contained in:
trochas
2026-01-10 16:50:53 +01:00
parent c720bc93ff
commit 9aeef08e65
13 changed files with 142 additions and 84 deletions

View File

@@ -8,9 +8,7 @@
--tint5: #373d48;
--text: #FFFFFF;
--text2: #000000;
--accent1: #10b981;
--accent2: #059669;
--disable: #047857;
--disable: #02291d;
--green-primary: #10b981;
--green-secondary: #059669;
--green-dark: #047857;
@@ -28,9 +26,7 @@
--tint5: #c6bab8;
--text: #000000;
--text2: #FFFFFF;
--accent1: #00ce89;
--accent2: #00a571;
--disable: #00825d;
--disable: #02291d;
--green-primary: #00ce89;
--green-secondary: #00a571;
--green-dark: #00825d;
@@ -147,6 +143,7 @@ input[type="number"],
input[type="date"],
input[type="time"],
input[type="search"],
input[type="datetime-local"],
textarea {
background-color: var(--tint2);
color: var(--text);

View File

@@ -45,7 +45,7 @@ export const athleteService = {
delete: (id: number | string) => api.delete(`/athlete/${id}`),
// session-related endpoints exposed by AthleteResource
getSessionsForAthlete: (athleteId: number | null) => api.get<SessionDTO[]>(`/athletes/athlete/${athleteId}/session`),
getSessionsForAthlete: (athleteId: number | null) => api.get<SessionDTO[]>(`/athlete/athlete/${athleteId}/session`),
getAllSessions: () => api.get(`/athletes/session`),
getActivitiesForSession: (sessionId: number | string) => api.get(`/athletes/session/${sessionId}/activities`),
getSessionsAfterDate: (athleteId: number | string, date: string) => api.get(`/athletes/${athleteId}/session/after/${encodeURIComponent(date)}`),
@@ -73,8 +73,8 @@ export const sessionService = {
getActivities: (sessionId: number | null) => api.get<ActiviteDTO[]>(`/session/${sessionId}/activities`),
addActivity: (sessionId: number | null, activityId: number) => api.post(`/session/${sessionId}/activities/${activityId}`),
subscribe: (sessionId: number | null, userId: number | string) => api.post(`/session/${sessionId}/subscribe`, { userId }),
unsubscribe: (sessionId: number | null, userId: number | string) => api.post(`/session/${sessionId}/unsubscribe`, { userId }),
subscribe: (sessionId: number | null, userId: number) => api.put(`/session/${sessionId}/subscribe/${userId}`),
unsubscribe: (sessionId: number | null, userId: number) => api.put(`/session/${sessionId}/unsubscribe/${userId}`),
};
export const coachService = {

View File

@@ -4,10 +4,11 @@ import { Modal } from "./Modal";
type CreateActiciteProps = {
returnActivite: (activite:Activite|null) => void
session: Session;
}
export function CreateActivite({ returnActivite }: CreateActiciteProps){
export function CreateActivite({ returnActivite,session }: CreateActiciteProps){
const [activities, setActivities] = useState<Activite[]>([]);
const [activiteNom, setActiviteNom] = useState("");
@@ -41,6 +42,7 @@ export function CreateActivite({ returnActivite }: CreateActiciteProps){
<Modal isOpen={true} onClose={() => cancel()}>
<div className="create_activite_modal">
<h2>Nouvelle Activité :</h2>
<div>Session : {session.name}</div>
<div>
Nom de l'activité:
</div>

View File

@@ -45,6 +45,7 @@ export const EDT =() =>{
}
});
}
newWeek.sort((a, b) =>a.creneau.getHours() * 60 + a.creneau.getMinutes() -(b.creneau.getHours() * 60 + b.creneau.getMinutes()));
setSessions(newWeek);
}
@@ -116,17 +117,29 @@ export const EDT =() =>{
return newDate;
}
function sameDay(d1:Date,d2:Date):boolean{
return (
d1.getDate() === d2.getDate() &&
d1.getMonth() === d2.getMonth() &&
d1.getFullYear() === d2.getFullYear()
);
}
async function refresh() {
loadSessions(week)
}
return(
<div className="edt">
<div className="edt_header">
<button className="edt_button_week_select" onClick={() =>handlePrev()}>Prev</button>
<button onClick={()=>refresh()}>Actualiser</button>
<button className="edt_button_week_select" onClick={() => handleNext()}>Next</button>
</div>
<div className="edt_colonnes">
<div className="top_left_loading">{loading && <Loading/>}</div>
{week_days_nums.map((num,index)=>(
<div className="edt_colonne">
<div className={`edt_colonne ${sameDay(getNextDay(week, index), new Date()) ? "today" : ""}`}>
<div className="edt_day_header">
<div> {week_days[index]} </div>
<div className="edt_date"> {dateToString(getNextDay(week,index))} </div>

View File

@@ -1,6 +1,6 @@
import { useKeycloak } from '@react-keycloak/web'
import { useEffect } from 'react';
import { Athlete, User } from '../classes';
import { Admin, Athlete, Coach, User } from '../classes';
import { useLocalData } from '../context/useLocalData';
import { loginOrRegister, postAthlete } from '../requetes';
import { clearAuthToken, setAuthToken } from '../api';
@@ -80,6 +80,9 @@ export const Login =() =>{
<div>
Nom : { user.nom}
</div>
{user instanceof Athlete && <div>Role : Athlete</div>}
{user instanceof Coach && <div>Role : Coach</div>}
{user instanceof Admin && <div>Role : Admin</div>}
</div>
}

View File

@@ -1,10 +1,11 @@
import { useEffect, useState } from "react";
import { Activite, Session } from "../../classes";
import { Activite, Athlete, Session } from "../../classes";
import { dateToString, hoursToString } from "../edt";
import { Modal } from "../Modal";
import CreateActivite from "../createActivite";
import Loading from "../loading";
import { addActiviteToSession, createActivityAPI, delay, deletActiviteFromSession, getSessionOfActivite } from "../../requetes";
import { addActiviteToSession, createActivityAPI, delay, deletActiviteFromSession, getSessionOfActivite, subscribeSessionAPI, unsubscribeSessionAPI } from "../../requetes";
import { useLocalData } from "../../context/useLocalData";
type Props = {
session:Session;
@@ -14,9 +15,12 @@ type Props = {
function DetailSession({session,open,setOpen}:Props){
const {user,setUser} = useLocalData()
const [activites,setActivites] = useState<Activite[]>([]);
const [open2, setOpen2] = useState<boolean>(false);
const [loading,setLoading] = useState<boolean>(false);
const [join,setJoin] = useState<boolean>(user instanceof Athlete && user.sessions.includes(session));
const sDate = session.creneau;
@@ -46,6 +50,22 @@ function DetailSession({session,open,setOpen}:Props){
}
},[open])
async function subscribeSession(){
if(user instanceof Athlete){
await subscribeSessionAPI(user,session);
user.sessions.push(session);
setJoin(true);
}
}
async function unsubscribeSession(){
if(user instanceof Athlete){
await unsubscribeSessionAPI(user,session);
user.sessions = user.sessions.filter(s => s !== session);
setJoin(false);
}
}
async function returnActivite(activite: Activite|null){
if(activite!==null){
@@ -67,35 +87,43 @@ function DetailSession({session,open,setOpen}:Props){
}
},[loading])
if(!open2){
return(
return(
<Modal isOpen={open} onClose={() => setOpen(false)}>
<div className="object_modal">
<h2>{session.name}</h2>
<div>{hoursToString(sDate)}</div>
<div>{dateToString(sDate)}</div>
{user instanceof Athlete &&
<div>
{user.sessions.includes(session) ? <button onClick={()=>unsubscribeSession()}>quitter</button>
:<button onClick={()=>subscribeSession()}>rejoindre</button>}
</div>
}
<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>
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>
</div>
{open2 &&
<CreateActivite returnActivite={(activite) => returnActivite(activite)}/>
}
</div>
</Modal>
)
</Modal>
)
}else{
return(
<CreateActivite returnActivite={(activite) => returnActivite(activite)} session={session}/>
)
}
}
export default DetailSession;

View File

@@ -7,5 +7,5 @@
margin: 0px;
font-size: 20px;
display: inline;
border-color: var(--accent1);
border-color: var(--green-A-primary);
}

View File

@@ -10,9 +10,9 @@
.edt_header{
justify-content: center;
display: grid;
grid-template-columns: repeat(2, 0.5fr);
grid-template-columns: repeat(3, 0.5fr);
padding-bottom: 10px;
gap: 30%;
gap: 5%;
}
.edt_colonnes {
@@ -56,6 +56,10 @@
font-weight: 600;
}
.today{
border: 2px solid var(--green-primary) ;
}
.edt_day_content{
display: flex;
flex-direction: column;

View File

@@ -46,10 +46,3 @@
background-color: var(--tint3);
border-radius: 10px;
}
.create_activite_modal{
background-color: var(--tint3);
padding: 10px;
border-radius: 20px;
position: relative;
}

View File

@@ -1,37 +1,4 @@
/* Variables de thème globales */
[data-theme='dark'] {
--tint0: #000000;
--tint1: #0b0c0e;
--tint2: #16181d;
--tint3: #21252b;
--tint4: #2c313a;
--tint5: #373d48;
--text: #FFFFFF;
--text2: #000000;
--accent1: #44c1ee;
--accent2: #0b235f;
--disable: #030918;
--green-primary: #10b981;
--green-secondary: #059669;
--green-dark: #047857;
}
[data-theme='light'] {
--tint0: #FFFFFF;
--tint1: #f4f1f1;
--tint2: #e8e4e3;
--tint3: #ddd6d5;
--tint4: #d2c8c6;
--tint5: #c6bab8;
--text: #000000;
--text2: #FFFFFF;
--accent1: #44c1ee;
--accent2: #113388;
--disable: #061231;
--green-primary: #10b981;
--green-secondary: #059669;
--green-dark: #047857;
}
/* Reset global */
* {

View File

@@ -135,7 +135,7 @@ export async function subscribeSessionAPI(user:User, session:Session):Promise<bo
}
}
export async function unsubscribeSessionAPI(user:User, session:Session):Promise<boolean>{
export async function unsubscribeSessionAPI(user:Athlete, session:Session):Promise<boolean>{
try {
const session_id =session.id
const user_id = user.id