Thibaut Valentin 01436ac220
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m42s
feat: add match eq support
2026-01-16 15:37:23 +01:00

473 lines
20 KiB
JavaScript

import {useNavigate, useParams} from "react-router-dom";
import {useLoadingSwitcher} from "../../hooks/useLoading.jsx";
import {useFetch} from "../../hooks/useFetch.js";
import {AxiosError} from "../../components/AxiosError.jsx";
import {ThreeDots} from "react-loader-spinner";
import React, {useEffect, useState} from "react";
import {DrawGraph} from "./DrawGraph.jsx";
import {TreeNode} from "../../utils/TreeUtils.js";
import {scoreToString} from "../../utils/CompetitionTools.js";
import {useTranslation} from "react-i18next";
function CupImg() {
return <img decoding="async" loading="lazy" width="16" height="16" className="wp-image-1635"
style={{width: "16px"}} src="/img/171891.png"
alt=""/>
}
function CupImg2() {
return <img decoding="async" loading="lazy" width="16" height="16" className="wp-image-1635"
style={{width: "16px"}} src="/img/171892.png"
alt=""/>
}
export function ResultView() {
const {uuid} = useParams()
const navigate = useNavigate();
const [resultShow, setResultShow] = useState("cat")
const {t} = useTranslation('result');
return <>
<button type="button" className="btn btn-link" onClick={() => navigate("/result")}>
{t('back')}
</button>
<MenuBar resultShow={resultShow} setResultShow={setResultShow}/>
<div className="row" style={{marginTop: "1em"}}>
<div className="col-auto">
{resultShow && resultShow === "cat" && <CategoryList uuid={uuid}/>
|| resultShow && resultShow === "club" && <ClubList uuid={uuid}/>
|| resultShow && resultShow === "comb" && <CombList uuid={uuid}/>
|| resultShow && resultShow === "combs" && <CombsResult uuid={uuid}/>}
</div>
</div>
</>
}
// || resultShow && resultShow === "club_all" && <ClubAllResult uuid={uuid}/>
function MenuBar({resultShow, setResultShow}) {
const {t} = useTranslation('result');
return <ul className="nav nav-tabs">
<li className="nav-item">
<a className={"nav-link my-1" + (resultShow === "cat" ? " active" : "")} aria-current={(resultShow === "cat" ? " page" : "false")}
href="#" onClick={_ => setResultShow("cat")}>{t('parCatégorie')}</a>
</li>
<li className="nav-item">
<a className={"nav-link my-1" + (resultShow === "club" ? " active" : "")} aria-current={(resultShow === "club" ? " page" : "false")}
href="#" onClick={_ => setResultShow("club")}>{t('parClub')}</a>
</li>
<li className="nav-item">
<a className={"nav-link my-1" + (resultShow === "comb" ? " active" : "")} aria-current={(resultShow === "comb" ? " page" : "false")}
href="#" onClick={_ => setResultShow("comb")}>{t('parCombattant')}</a>
</li>
<li className="nav-item">
<a className={"nav-link my-1" + (resultShow === "combs" ? " active" : "")} aria-current={(resultShow === "combs" ? " page" : "false")}
href="#" onClick={_ => setResultShow("combs")}>{t('combattants')}</a>
</li>
</ul>
/*
<li className="nav-item">
<a className={"nav-link my-1" + (resultShow === "club_all" ? " active" : "")}
aria-current={(resultShow === "club_all" ? " page" : "false")}
href="#" onClick={_ => setResultShow("club_all")}>Clubs</a>
</li>
*/
}
function BuildMatchArray({matchs}) {
const {t} = useTranslation('result');
return <>
<table className="table table-striped">
<thead>
<tr>
<th scope="col" style={{textAlign: "center"}}>{t('rouge')}</th>
<th scope="col" style={{textAlign: "center"}}></th>
<th scope="col" style={{textAlign: "center"}}>{t('scores')}</th>
<th scope="col" style={{textAlign: "center"}}></th>
<th scope="col" style={{textAlign: "center"}}>{t('bleu')}</th>
</tr>
</thead>
<tbody>
{matchs.map((match, idx) => <tr key={idx}>
<td style={{textAlign: "right"}}>{match.red}</td>
<td style={{textAlign: "center"}}>{match.red_w ? <CupImg/> : (match.eq ? <CupImg2/> : "")}</td>
<td style={{textAlign: "center"}}>{scoreToString(match.score)}</td>
<td style={{textAlign: "center"}}>{match.blue_w ? <CupImg/> : (match.eq ? <CupImg2/> : "")}</td>
<td style={{textAlign: "left"}}>{match.blue}</td>
</tr>)}
</tbody>
</table>
</>
}
function BuildRankArray({rankArray}) {
const {t} = useTranslation('result');
return <>
<table className="table table-striped">
<thead>
<tr>
<th scope="col" style={{textAlign: "center"}}>{t('place')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('nom')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('score')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('victoire')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('ratio')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('pointsMarqués')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('pointsReçus')}</th>
</tr>
</thead>
<tbody>
{rankArray.map((row, idx) => <tr key={idx}>
<td style={{textAlign: "center"}}>{row.rank}</td>
<td style={{textAlign: "left"}}>{row.name}</td>
<td style={{textAlign: "center"}}>{row.score}</td>
<td style={{textAlign: "center"}}>{row.win}</td>
<td style={{textAlign: "center"}}>{row.pointRate.toFixed(3)}</td>
<td style={{textAlign: "center"}}>{row.pointMake}</td>
<td style={{textAlign: "center"}}>{row.pointTake}</td>
</tr>)}
</tbody>
</table>
</>
}
function BuildTree({treeData}) {
function parseTree(data_in) {
if (data_in?.data == null)
return null;
let node = new TreeNode(data_in.data);
node.left = parseTree(data_in?.left);
node.right = parseTree(data_in?.right);
return node;
}
function initTree(data_in) {
let out = [];
for (const din of data_in) {
out.push(parseTree(din));
}
return out;
}
return <DrawGraph root={initTree(treeData)}/>
}
function CategoryList({uuid}) {
const [catId, setCatId] = useState(null)
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/result/${uuid}/category/list`, setLoading, 1)
const {t} = useTranslation('result');
useEffect(() => {
if (data && Object.keys(data).length > 0)
setCatId(data[Object.keys(data).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))[0]])
}, [data]);
return <>
{data ? <div className="input-group" style={{marginBottom: "1em"}}>
<h6 style={{margin: "auto 0.5em auto 0"}}>{t('catégorie')}</h6>
<select className="form-select" aria-label="Select Result Type" onChange={e => setCatId(e.target.value)}>
{Object.keys(data).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))
.map(key => <option key={key} value={data[key]}>{key}</option>)}
</select>
</div>
: error
? <AxiosError error={error}/>
: <Def/>}
{catId && <CategoryResult uuid={uuid} catId={catId}/>}
</>
}
function CategoryResult({uuid, catId}) {
const [type, setType] = useState(1)
const {t} = useTranslation('result');
const setLoading = useLoadingSwitcher()
const {data, refresh, error} = useFetch(`/result/${uuid}/category/${catId}`, setLoading, 1)
useEffect(() => {
refresh(`/result/${uuid}/category/${catId}`)
}, [catId]);
useEffect(() => {
if (data)
setType(data.type === 3 ? 1 : data.type)
}, [data]);
if (!data) {
return error
? <AxiosError error={error}/>
: <Def/>
}
return <>
{data.type === 3 && <>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className={"nav-link" + (type === 1 ? " active" : "")} aria-current={(type === 1 ? " page" : "false")} href="#"
onClick={_ => setType(1)}>{t('poule')}</a>
</li>
<li className="nav-item">
<a className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")} href="#"
onClick={_ => setType(2)}>{t('tournois')}</a>
</li>
</ul>
</>}
{type === 1 && <>
{Object.keys(data.matchs).map(p => <div key={p}>
{Object.keys(data.matchs).length > 1 && <h4 style={{marginTop: "2em"}}>{t('poule')} {p}</h4>}
<BuildMatchArray matchs={data.matchs[p]}/>
<BuildRankArray rankArray={data.rankArray[p]}/>
</div>)}
</>}
{type === 2 && <>
<BuildTree treeData={data.trees}/>
</>}
</>
}
function ClubList({uuid}) {
const [clubId, setClubId] = useState(null)
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/result/${uuid}/club/list`, setLoading, 1)
const {t} = useTranslation('result');
useEffect(() => {
if (data && Object.keys(data).length > 0)
setClubId(data[Object.keys(data).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))[0]])
}, [data]);
return <>
{data ? <div className="input-group" style={{marginBottom: "1em"}}>
<h6 style={{margin: "auto 0.5em auto 0"}}>{t('club')}</h6>
<select className="form-select" aria-label="Select Result Type" onChange={e => setClubId(e.target.value)}>
{Object.keys(data).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))
.map(key => <option key={key} value={data[key]}>{key}</option>)}
</select>
</div>
: error
? <AxiosError error={error}/>
: <Def/>}
{clubId && <ClubResult uuid={uuid} clubId={clubId}/>}
</>
}
function ClubResult({uuid, clubId}) {
const setLoading = useLoadingSwitcher()
const {data, refresh, error} = useFetch(`/result/${uuid}/club/${clubId}`, setLoading, 1)
const {t} = useTranslation('result');
useEffect(() => {
refresh(`/result/${uuid}/club/${clubId}`)
}, [clubId]);
return <>
{data ? <>
<h3>{t('info')} :</h3>
<ul>
<li>{t('nom')} : {data.name}</li>
<li>{t('nombreDinscris')} : {data.nb_insc}</li>
</ul>
<h3>{t('statistique')} :</h3>
<ul>
<li>{t('nombreDeMatchDisputé2', {nb: data.nb_match})}</li>
<li>{t('nombreDeVictoires2', {nb: data.match_w})}</li>
<li>{t('ratioDeVictoiresMoyen2', {nb: data.ratioVictoire.toFixed(3)})}</li>
<li>{t('pointsMarqués2', {nb: data.pointMake})}</li>
<li>{t('pointsReçus2', {nb: data.pointTake})}</li>
<li>{t('ratioDePointsMoyen2', {nb: data.ratioPoint.toFixed(3)})}</li>
</ul>
<h3>{t('listeDesMembres')} :</h3>
<table className="table table-striped">
<thead>
<tr>
<th scope="col" style={{textAlign: "center"}}>{t('catégorie')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('nom')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('victoires')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('défaites')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('ratioVictoires')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('pointsMarqués')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('pointsReçus')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('ratioPoints')}</th>
</tr>
</thead>
<tbody>
{data.combs.map((comb, idx) => <tr key={idx}>
<td style={{textAlign: "center"}}>{comb.cat}</td>
<td style={{textAlign: "center"}}>{comb.name}</td>
<td style={{textAlign: "center"}}>{comb.w}</td>
<td style={{textAlign: "center"}}>{comb.l}</td>
<td style={{textAlign: "center"}}>{comb.ratioVictoire.toFixed(3)}</td>
<td style={{textAlign: "center"}}>{comb.pointMake}</td>
<td style={{textAlign: "center"}}>{comb.pointTake}</td>
<td style={{textAlign: "center"}}>{comb.ratioPoint.toFixed(3)}</td>
</tr>)}
</tbody>
</table>
</>
: error
? <AxiosError error={error}/>
: <Def/>
}
</>
}
function CombList({uuid}) {
const [combId, setCombId] = useState(null)
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/result/${uuid}/comb/list`, setLoading, 1)
const {t} = useTranslation('result');
useEffect(() => {
if (data && Object.keys(data).length > 0)
setCombId(data[Object.keys(data).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))[0]])
}, [data]);
return <>
{data ? <div className="input-group" style={{marginBottom: "1em"}}>
<h6 style={{margin: "auto 0.5em auto 0"}}>{t('combattant')}</h6>
<select className="form-select" aria-label="Select Result Type" onChange={e => setCombId(e.target.value)}>
{Object.keys(data).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))
.map(key => <option key={key} value={data[key]}>{key}</option>)}
</select>
</div>
: error
? <AxiosError error={error}/>
: <Def/>}
{combId && <CombResult uuid={uuid} combId={combId}/>}
</>
}
function CombResult({uuid, combId}) {
const setLoading = useLoadingSwitcher()
const {data, refresh, error} = useFetch(`/result/${uuid}/comb/${combId}`, setLoading, 1)
const {t} = useTranslation('result');
useEffect(() => {
refresh(`/result/${uuid}/comb/${combId}`)
}, [combId]);
if (!data) {
return error
? <AxiosError error={error}/>
: <Def/>
}
return <div>
<h3>{t('info')} :</h3>
<ul>
<li>{t('nomPrénom')} : {data.name}</li>
<li>{t('club')} : {data.club}</li>
<li>{t('catégorie')} : {data.cat}</li>
</ul>
<h3>{t('statistique')} :</h3>
<ul>
<li>{t('tauxDeVictoire2', {
nb: data.matchs.length === 0 ? "---" : (data.totalWin / data.matchs.length * 100).toFixed(0),
victoires: data.totalWin,
matchs: data.matchs.length
})}
</li>
<li>{t('pointsMarqués2', {nb: data.pointMake})}</li>
<li>{t('pointsReçus2', {nb: data.pointTake})}</li>
<li>{t('ratioDuScore2', {nb: data.pointRatio.toFixed(3)})}</li>
</ul>
<h3>{t('listeDesMatchs')}:</h3>
<table className="table table-striped">
<thead>
<tr>
<th scope="col" style={{textAlign: "center"}}>{t('catégorie')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('adversaire')}</th>
<th scope="col" style={{textAlign: "center"}}>{t("scores")}</th>
<th scope="col" style={{textAlign: "center"}}>{t('ratio')}</th>
<th scope="col" style={{textAlign: "center"}}></th>
</tr>
</thead>
<tbody>
{data.matchs.map((match, idx) => <tr key={idx}>
<td style={{textAlign: "center"}}>{match.poule}</td>
<td style={{textAlign: "center"}}>{match.adv}</td>
<td style={{textAlign: "center"}}>{scoreToString(match.score)}</td>
<td style={{textAlign: "center"}}>{match.ratio.toFixed(3)}</td>
<td style={{textAlign: "left"}}>{match.win ? <CupImg/> : (match.eq ? <CupImg2/> : "")}</td>
</tr>)}
</tbody>
</table>
</div>
}
function CombsResult({uuid}) {
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/result/${uuid}/comb`, setLoading, 1)
const {t} = useTranslation('result');
return <>
{data ? <>
<h3>{t('statistique')} :</h3>
<ul>
<li>{t('nombreDinscris2', {nb: data.nb_insc})}</li>
<li>{t('nombreDeMatchDisputé2', {nb: data.tt_match})}</li>
<li>{t('pointsMarqués2', {nb: data.point})}</li>
</ul>
<h3>{t('listeDesCombattants')} :</h3>
<table className="table table-striped">
<thead>
<tr>
<th scope="col" style={{textAlign: "center"}}>{t('catégorie')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('club')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('nom')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('victoires')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('défaites')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('ratioVictoires')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('pointsMarqués')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('pointsReçus')}</th>
<th scope="col" style={{textAlign: "center"}}>{t('ratiosPoints')}</th>
</tr>
</thead>
<tbody>
{data.combs.map((comb, idx) => <tr key={idx}>
<td style={{textAlign: "center"}}>{comb.cat}</td>
<td style={{textAlign: "center"}}>{comb.club}</td>
<td style={{textAlign: "center"}}>{comb.name}</td>
<td style={{textAlign: "center"}}>{comb.w}</td>
<td style={{textAlign: "center"}}>{comb.l}</td>
<td style={{textAlign: "center"}}>{comb.ratioVictoire.toFixed(3)}</td>
<td style={{textAlign: "center"}}>{comb.pointMake}</td>
<td style={{textAlign: "center"}}>{comb.pointTake}</td>
<td style={{textAlign: "center"}}>{comb.ratioPoint.toFixed(3)}</td>
</tr>)}
</tbody>
</table>
</>
: error
? <AxiosError error={error}/>
: <Def/>
}
</>
}
function Def() {
return <div className="list-group">
<li className="list-group-item"><ThreeDots/></li>
<li className="list-group-item"><ThreeDots/></li>
<li className="list-group-item"><ThreeDots/></li>
<li className="list-group-item"><ThreeDots/></li>
<li className="list-group-item"><ThreeDots/></li>
</div>
}