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

This commit is contained in:
Alexis Leboeuf
2026-01-08 14:57:50 +01:00
2 changed files with 102 additions and 4 deletions

View 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;

View File

@@ -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,8 +115,12 @@ function ObjectUser({admin=null,athlete=null,coach=null}:Props){
{user.sessions.map((session,index)=>( {user.sessions.map((session,index)=>(
<ObjectSession session={session}/> <ObjectSession session={session}/>
))} ))}
</div> </div>
{athlete !== null && (
<div className="stats-container">
<StatAthlete athlete={athlete} sessions={sessions} />
</div>
)}
</div> </div>
</Modal> </Modal>
} }