599 lines
26 KiB
JavaScript
599 lines
26 KiB
JavaScript
import React, {useEffect, useReducer, useRef, useState} from "react";
|
|
import {CombName, useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
|
|
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
|
|
import {from_sendTree, TreeNode} from "../../../utils/TreeUtils.js";
|
|
import {DrawGraph} from "../../result/DrawGraph.jsx";
|
|
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
|
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
|
|
import {MarchReducer} from "../../../utils/MatchReducer.jsx";
|
|
import {
|
|
CatList,
|
|
getCatName, getShieldSize,
|
|
getShieldTypeName, getSwordSize,
|
|
getSwordTypeName,
|
|
getToastMessage, timePrint,
|
|
virtual_end,
|
|
virtualScore,
|
|
win_end
|
|
} from "../../../utils/Tools.js";
|
|
import "./CMTMatchPanel.css"
|
|
import {useOBS} from "../../../hooks/useOBS.jsx";
|
|
import {useTranslation} from "react-i18next";
|
|
import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
|
import {ScorePanel} from "./ScoreAndCardPanel.jsx";
|
|
import {toast} from "react-toastify";
|
|
import {createPortal} from "react-dom";
|
|
import ProtectionSelector from "../../../components/ProtectionSelector.jsx";
|
|
|
|
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 CategorieSelect({catId, setCatId, menuActions}) {
|
|
const setLoading = useLoadingSwitcher()
|
|
const {data: cats, setData: setCats} = useRequestWS('getAllCategory', {}, setLoading);
|
|
const {dispatch} = useWS();
|
|
const {connected, setText} = useOBS();
|
|
const {t} = useTranslation("cm");
|
|
|
|
useEffect(() => {
|
|
const categoryListener = ({data}) => {
|
|
setCats([...cats.filter(c => c.id !== data.id), data])
|
|
}
|
|
const sendAddCategory = ({data}) => {
|
|
setCats([...cats, data])
|
|
}
|
|
const sendDelCategory = ({data}) => {
|
|
if (catId === data)
|
|
setCatId(-1);
|
|
setCats([...cats.filter(c => c.id !== data)])
|
|
}
|
|
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
|
|
dispatch({type: 'addListener', payload: {callback: sendAddCategory, code: 'sendAddCategory'}})
|
|
dispatch({type: 'addListener', payload: {callback: sendDelCategory, code: 'sendDelCategory'}})
|
|
return () => {
|
|
dispatch({type: 'removeListener', payload: categoryListener})
|
|
dispatch({type: 'removeListener', payload: sendAddCategory})
|
|
dispatch({type: 'removeListener', payload: sendDelCategory})
|
|
}
|
|
}, [cats]);
|
|
|
|
const cat = cats?.find(c => c.id === catId);
|
|
|
|
useEffect(() => {
|
|
setText("poule", cat ? cat.name : "");
|
|
}, [cat, connected]);
|
|
|
|
return <>
|
|
<div className="input-group">
|
|
<h6 style={{margin: "auto 0.5em auto 0"}}>{t('catégorie')}</h6>
|
|
<select className="form-select" onChange={e => setCatId(Number(e.target.value))} value={catId}>
|
|
{cats && <option value={-1}></option>}
|
|
{cats && cats.sort((a, b) => a.name.localeCompare(b.name)).map(c => (
|
|
<option key={c.id} value={c.id}>{c.name}</option>))}
|
|
</select>
|
|
</div>
|
|
{catId !== -1 && <CMTMatchPanel catId={catId} cat={cat} menuActions={menuActions}/>}
|
|
</>
|
|
}
|
|
|
|
function CMTMatchPanel({catId, cat, menuActions}) {
|
|
const setLoading = useLoadingSwitcher()
|
|
const {sendRequest, dispatch} = useWS();
|
|
const [trees, setTrees] = useState({raw: [], formatted: []});
|
|
const [matches, reducer] = useReducer(MarchReducer, []);
|
|
const combDispatch = useCombsDispatch();
|
|
const cardDispatch = useCardsDispatch();
|
|
|
|
function readAndConvertMatch(matches, data, combsToAdd) {
|
|
matches.push({...data, c1: data.c1?.id, c2: data.c2?.id})
|
|
if (data.c1)
|
|
combsToAdd.push(data.c1)
|
|
if (data.c2)
|
|
combsToAdd.push(data.c2)
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!catId)
|
|
return;
|
|
setLoading(1);
|
|
sendRequest('getFullCategory', catId)
|
|
.then((data) => {
|
|
setTrees({
|
|
raw: data.trees.sort((a, b) => a.level - b.level),
|
|
formatted: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
|
|
})
|
|
|
|
cardDispatch({type: 'SET_ALL', payload: data.cards});
|
|
|
|
let matches2 = [];
|
|
let combsToAdd = [];
|
|
data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
|
data.matches.forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
|
|
|
reducer({type: 'REPLACE_ALL', payload: matches2});
|
|
combDispatch({type: 'SET_ALL', payload: {source: "match", data: combsToAdd}});
|
|
}).finally(() => setLoading(0))
|
|
|
|
const treeListener = ({data}) => {
|
|
if (data.length < 1 || data[0].categorie !== catId)
|
|
return
|
|
setTrees({
|
|
raw: data.sort((a, b) => a.level - b.level),
|
|
formatted: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
|
|
})
|
|
|
|
let matches2 = [];
|
|
let combsToAdd = [];
|
|
data.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
|
reducer({type: 'REPLACE_TREE', payload: matches2});
|
|
combDispatch({type: 'SET_ALL', payload: {source: "match", data: combsToAdd}});
|
|
}
|
|
|
|
const matchListener = ({data: datas}) => {
|
|
for (const data of datas) {
|
|
reducer({type: 'UPDATE_OR_ADD', payload: {...data, c1: data.c1?.id, c2: data.c2?.id}})
|
|
combDispatch({type: 'SET_ALL', payload: {source: "match", data: [data.c1, data.c2].filter(d => d != null)}})
|
|
}
|
|
}
|
|
|
|
const matchOrder = ({data}) => {
|
|
reducer({type: 'REORDER', payload: data})
|
|
}
|
|
|
|
const deleteMatch = ({data: datas}) => {
|
|
for (const data of datas)
|
|
reducer({type: 'REMOVE', payload: data})
|
|
}
|
|
|
|
dispatch({type: 'addListener', payload: {callback: treeListener, code: 'sendTreeCategory'}})
|
|
dispatch({type: 'addListener', payload: {callback: matchListener, code: 'sendMatch'}})
|
|
dispatch({type: 'addListener', payload: {callback: matchOrder, code: 'sendMatchOrder'}})
|
|
dispatch({type: 'addListener', payload: {callback: deleteMatch, code: 'sendDeleteMatch'}})
|
|
return () => {
|
|
dispatch({type: 'removeListener', payload: treeListener})
|
|
dispatch({type: 'removeListener', payload: matchListener})
|
|
dispatch({type: 'removeListener', payload: matchOrder})
|
|
dispatch({type: 'removeListener', payload: deleteMatch})
|
|
}
|
|
}, [catId]);
|
|
|
|
return <>
|
|
<ListMatch cat={cat} matches={matches} trees={trees} menuActions={menuActions}/>
|
|
<SetTimeToChrono cat={cat} matches={matches} menuActions={menuActions}/>
|
|
</>
|
|
}
|
|
|
|
function SetTimeToChrono({cat, matches, menuActions}) {
|
|
const [catAverage, setCatAverage] = useState("---");
|
|
const [genreAverage, setGenreAverage] = useState("H");
|
|
const [nbComb, setNbComb] = useState(0);
|
|
const [preset, setPreset] = useState(undefined);
|
|
const [time, setTime] = useState({round: 0, pause: 0});
|
|
const {cards_v} = useCards();
|
|
|
|
const {t} = useTranslation("cm");
|
|
const {getComb} = useCombs();
|
|
|
|
useEffect(() => {
|
|
if (!cat || matches.filter(m => m.categorie === cat.id).length === 0) {
|
|
setCatAverage("---");
|
|
setNbComb(0);
|
|
return;
|
|
}
|
|
setPreset(cat.preset);
|
|
|
|
const genres = [];
|
|
const cats = [];
|
|
const combs = [];
|
|
for (const m of matches.filter(m => m.categorie === cat.id)) {
|
|
if (m.c1 && !combs.includes(m.c1))
|
|
combs.push(m.c1);
|
|
if (m.c2 && !combs.includes(m.c2))
|
|
combs.push(m.c2);
|
|
}
|
|
setNbComb(combs.length);
|
|
|
|
combs.map(cId => getComb(cId, null)).filter(c => c && c.categorie)
|
|
.forEach(c => {
|
|
cats.push(Math.min(CatList.length, CatList.indexOf(c.categorie) + c.overCategory))
|
|
genres.push(c.genre)
|
|
});
|
|
|
|
const catAvg = Math.round(cats.reduce((a, b) => a + b, 0) / cats.length);
|
|
setCatAverage(CatList.at(catAvg) || "---");
|
|
|
|
const genreAvg = Math.round(genres.reduce((a, b) => a + (b === "F" ? 1 : 0), 0) / genres.length);
|
|
setGenreAverage(genreAvg > 0.5 ? "F" : "H");
|
|
|
|
if (!cat.preset || !cat.preset.categories)
|
|
return;
|
|
|
|
const catAvailable = cat.preset.categories.map(c => CatList.indexOf(c.categorie));
|
|
|
|
let p;
|
|
if (catAvailable.includes(catAvg)) {
|
|
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) === catAvg);
|
|
} else {
|
|
const closest = catAvailable.reduce((a, b) => Math.abs(b - catAvg) < Math.abs(a - catAvg) ? b : a);
|
|
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) === closest);
|
|
}
|
|
menuActions.current.setTimerConfig(p.roundDuration, p.pauseDuration)
|
|
setTime({round: p.roundDuration, pause: p.pauseDuration})
|
|
|
|
}, [cat, matches]);
|
|
|
|
const marches2 = matches.filter(m => m.categorie === cat.id)
|
|
.map(m => ({...m, end: m.end || virtual_end(m, cards_v)}))
|
|
|
|
return createPortal(<div className="card mb-3">
|
|
<div className="card-header">{t('informationCatégorie')}</div>
|
|
<div className="card-body">
|
|
<div className="row">
|
|
<div className="col text-start">
|
|
<div>{t('catégorie')} : {getCatName(catAverage)}</div>
|
|
<div>{t('arme', {ns: 'common'})} : {getSwordTypeName(preset?.sword)} - {t('taille')} {getSwordSize(preset?.sword, catAverage, genreAverage)}</div>
|
|
<div>{t('bouclier', {ns: 'common'})} : {getShieldTypeName(preset?.shield)} - {t('taille')} {getShieldSize(preset?.shield, catAverage)}</div>
|
|
<div>{t('duréeRound')} : {timePrint(time.round)}</div>
|
|
<div>{t('duréePause')} : {timePrint(time.pause)}</div>
|
|
<div>{t('matchTerminé')}: {marches2.filter(m => m.end).length} sur {marches2.length}</div>
|
|
<div>{t('nombreDeCombattants')} : {nbComb}</div>
|
|
</div>
|
|
<div className="col text-center">
|
|
<h6>{t('protectionObligatoire', {ns: 'common'})} :</h6>
|
|
<ProtectionSelector shield={preset?.shield !== "NONE"}
|
|
mandatoryProtection={CatList.indexOf(catAverage) <= CatList.indexOf("JUNIOR") ?
|
|
preset?.mandatoryProtection1 : preset?.mandatoryProtection2} setMandatoryProtection={() => {
|
|
}}/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>, document.getElementById("infoCategory"))
|
|
}
|
|
|
|
function ListMatch({cat, matches, trees, menuActions}) {
|
|
const [type, setType] = useState(1);
|
|
const {sendRequest} = useWS();
|
|
const {t} = useTranslation("cm");
|
|
|
|
useEffect(() => {
|
|
if (!cat)
|
|
return;
|
|
if ((cat.type & type) === 0)
|
|
setType(cat.type);
|
|
}, [cat]);
|
|
|
|
const handleCreatClassement = () => {
|
|
toast.promise(sendRequest("createClassementMatchs", cat.id), getToastMessage("toast.matchs.classement.create", "cm"))
|
|
}
|
|
|
|
if (!cat)
|
|
return <></>;
|
|
|
|
return <div style={{marginTop: "1em"}}>
|
|
{cat && cat.type === 3 && <>
|
|
<ul className="nav nav-tabs">
|
|
<li className="nav-item">
|
|
<div className={"nav-link" + (type === 1 ? " active" : "")} aria-current={(type === 1 ? " page" : "false")}
|
|
onClick={_ => setType(1)}>{t('poule')}
|
|
</div>
|
|
</li>
|
|
<li className="nav-item">
|
|
<div className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")}
|
|
onClick={_ => setType(2)}>{(cat.treeAreClassement ? t('classement') : t('tournois'))}
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</>
|
|
}
|
|
|
|
{type === 1 && <>
|
|
<MatchList matches={matches} cat={cat} menuActions={menuActions}/>
|
|
</>}
|
|
|
|
{type === 2 && <>
|
|
{cat.treeAreClassement && !matches.some(m => m.categorie === cat.id && m.categorie_ord === -42 && (m.c1 !== undefined || m.c2 !== undefined)) ? <>
|
|
<button className="btn btn-primary" onClick={handleCreatClassement}>{t('créerLesMatchesDeClassement')}</button>
|
|
</> : <BuildTree treeData={trees} matches={matches} cat={cat} menuActions={menuActions}/>}
|
|
</>}
|
|
</div>
|
|
}
|
|
|
|
function MatchList({matches, cat, menuActions, classement = false, currentMatch = null, setCurrentMatch, getNext}) {
|
|
const [activeMatch, setActiveMatch] = useState(null)
|
|
const [lice, setLice] = useState(localStorage.getItem("cm_lice") || "1")
|
|
const publicAffDispatch = usePubAffDispatch();
|
|
const {t} = useTranslation("cm");
|
|
const {cards_v, getHeightCardForCombInMatch} = useCards();
|
|
const {sendNotify, setState} = useWS();
|
|
|
|
const liceName = (cat.liceName || "N/A").split(";");
|
|
const marches2 = classement
|
|
? matches.filter(m => m.categorie_ord === -42 && m.categorie === cat.id)
|
|
.map(m => ({...m, ...win_end(m, cards_v)}))
|
|
: matches.filter(m => m.categorie_ord !== -42 && m.categorie === cat.id)
|
|
.sort((a, b) => a.categorie_ord - b.categorie_ord)
|
|
.map(m => ({...m, ...win_end(m, cards_v)}))
|
|
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
|
|
|
|
const isActiveMatch = (index) => {
|
|
if (classement)
|
|
return true;
|
|
return liceName.length === 1 || (liceName[(index - firstIndex) % liceName.length] === lice)
|
|
}
|
|
|
|
const match = matches.find(m => m.id === activeMatch)
|
|
useEffect(() => {
|
|
if (!match) {
|
|
publicAffDispatch({type: 'SET_DATA', payload: {c1: undefined, c2: undefined, next: []}});
|
|
} else {
|
|
publicAffDispatch({
|
|
type: 'SET_DATA',
|
|
payload: {
|
|
c1: match.c1,
|
|
c2: match.c2,
|
|
next: marches2.filter((m, index) => !m.end && isActiveMatch(index) && m.id !== activeMatch).map(m => ({
|
|
c1: m.c1,
|
|
c2: m.c2
|
|
}))
|
|
}
|
|
});
|
|
}
|
|
}, [match]);
|
|
|
|
useEffect(() => {
|
|
if (match && match.poule !== lice)
|
|
if (!classement)
|
|
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id)
|
|
}, [lice]);
|
|
|
|
useEffect(() => {
|
|
if (marches2.length === 0)
|
|
return;
|
|
if (marches2.some(m => m.id === (classement ? currentMatch?.matchSelect : activeMatch)))
|
|
return;
|
|
|
|
if (classement) {
|
|
getNext.current = (id) => {
|
|
if (id === null)
|
|
return marches2.findLast((m, index) => !m.end && isActiveMatch(index))?.id;
|
|
const index = marches2.findIndex(m => m.id === id);
|
|
return marches2.slice(0, index).reverse().find((m, index2) => !m.end && isActiveMatch(marches2.length - 1 - index2))?.id;
|
|
}
|
|
} else {
|
|
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id);
|
|
}
|
|
}, [matches])
|
|
|
|
useEffect(() => {
|
|
setState({selectedMatch: activeMatch});
|
|
sendNotify("sendSelectMatch", activeMatch);
|
|
}, [activeMatch]);
|
|
|
|
const handleMatchClick = (matchId) => {
|
|
if (classement) {
|
|
setCurrentMatch({matchSelect: matchId, matchNext: marches2.reverse().find(m => !m.end && m.id !== matchId)?.id});
|
|
} else {
|
|
setActiveMatch(matchId);
|
|
}
|
|
}
|
|
|
|
const GetCard = ({combId, match, cat}) => {
|
|
const c = getHeightCardForCombInMatch(combId, match)
|
|
if (!c)
|
|
return <></>
|
|
let bg = "";
|
|
switch (c.type) {
|
|
case "YELLOW":
|
|
bg = " bg-warning";
|
|
break;
|
|
case "RED":
|
|
bg = " bg-danger";
|
|
break;
|
|
case "BLACK":
|
|
bg = " bg-dark text-white";
|
|
break;
|
|
case "BLUE":
|
|
bg = " bg-primary text-white";
|
|
break;
|
|
}
|
|
return <span
|
|
className={"position-absolute top-0 start-100 translate-middle-y badge border border-light p-2" + bg +
|
|
(c.match === match.id ? " rounded-circle" : (hasEffectCard(c, match.id, cat.id) ? "" : " bg-opacity-50"))}>
|
|
<span className="visually-hidden">card</span></span>
|
|
}
|
|
|
|
return <>
|
|
{liceName.length > 1 &&
|
|
<div className="input-group" style={{maxWidth: "15em", marginTop: "0.5em"}}>
|
|
<label className="input-group-text" htmlFor="selectLice">{t('zoneDeCombat')}</label>
|
|
<select className="form-select" id="selectLice" value={lice} onChange={e => {
|
|
setLice(e.target.value);
|
|
localStorage.setItem("cm_lice", e.target.value);
|
|
}}>
|
|
{liceName.map((l, index) => (
|
|
<option key={index} value={l}>{l}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
}
|
|
|
|
<div className="table-responsive-xxl overflow-y-auto" style={{maxHeight: "50vh"}}>
|
|
<table className="table table-striped table-hover">
|
|
<thead>
|
|
<tr>
|
|
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Z</th>}
|
|
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">P</th>}
|
|
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('no')}</th>
|
|
<th style={{textAlign: "center"}} scope="col"></th>
|
|
<th style={{textAlign: "center"}} scope="col">{t('rouge')}</th>
|
|
<th style={{textAlign: "center"}} scope="col">{t('blue')}</th>
|
|
<th style={{textAlign: "center"}} scope="col"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="table-group-divider">
|
|
{marches2.map((m, index) => (
|
|
<tr key={m.id}
|
|
className={m.id === (classement ? currentMatch?.matchSelect : activeMatch) ? "table-primary" : (m.end ? "table-success" : (isActiveMatch(index) ? "" : "table-warning"))}
|
|
onClick={() => handleMatchClick(m.id)}>
|
|
{!classement && <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
|
|
{liceName[(index - firstIndex) % liceName.length]}</td>}
|
|
{!classement && <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>{m.poule}</td>}
|
|
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
|
|
{index >= firstIndex ? index + 1 - firstIndex : ""}</th>
|
|
<td style={{textAlign: "right", paddingRight: "0"}}>{m.end && ((m.win > 0 && <CupImg/>) || (m.win === 0 && <CupImg2/>))}</td>
|
|
<td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}>
|
|
<small className="position-relative"><CombName combId={m.c1}/>
|
|
<GetCard match={m} combId={m.c1} cat={cat}/></small></td>
|
|
<td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}}>
|
|
<small className="position-relative"><CombName combId={m.c2}/>
|
|
<GetCard match={m} combId={m.c2} cat={cat}/></small></td>
|
|
<td style={{textAlign: "left", paddingLeft: "0"}}>{m.end && ((m.win < 0 && <CupImg/>) || (m.win === 0 && <CupImg2/>))}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{activeMatch && !classement &&
|
|
<LoadingProvider><ScorePanel matchId={activeMatch} matchs={matches} match={match} menuActions={menuActions}/></LoadingProvider>}
|
|
</>
|
|
}
|
|
|
|
function BuildTree({treeData, matches, cat, menuActions}) {
|
|
const scrollRef = useRef(null)
|
|
const [currentMatch, setCurrentMatch] = useState(null)
|
|
const {getComb} = useCombs()
|
|
const publicAffDispatch = usePubAffDispatch();
|
|
const {cards_v} = useCards();
|
|
const {sendNotify, setState} = useWS();
|
|
const getNext = useRef(null);
|
|
const rtrees = useRef(null);
|
|
|
|
const match = matches.find(m => m.id === currentMatch?.matchSelect)
|
|
useEffect(() => {
|
|
if (!match) {
|
|
publicAffDispatch({type: 'SET_DATA', payload: {c1: undefined, c2: undefined}});
|
|
} else {
|
|
publicAffDispatch({type: 'SET_DATA', payload: {c1: match.c1, c2: match.c2}});
|
|
}
|
|
}, [match]);
|
|
const next_match = matches.find(m => m.id === currentMatch?.matchNext)
|
|
useEffect(() => {
|
|
if (!next_match) {
|
|
publicAffDispatch({type: 'SET_DATA', payload: {next: []}});
|
|
} else {
|
|
publicAffDispatch({type: 'SET_DATA', payload: {next: [{c1: next_match.c1, c2: next_match.c2}]}});
|
|
}
|
|
}, [next_match]);
|
|
|
|
function parseTree(data_in, matches_) {
|
|
if (data_in?.data == null)
|
|
return null
|
|
|
|
const matchData = matches_.find(m => m.id === data_in.data)
|
|
const c1 = getComb(matchData?.c1)
|
|
const c2 = getComb(matchData?.c2)
|
|
|
|
const scores2 = []
|
|
for (const score of matchData?.scores) {
|
|
scores2.push({
|
|
...score,
|
|
s1: virtualScore(matchData?.c1, score, matchData, cards_v),
|
|
s2: virtualScore(matchData?.c2, score, matchData, cards_v)
|
|
})
|
|
}
|
|
|
|
|
|
let node = new TreeNode({
|
|
...matchData,
|
|
...win_end(matchData, cards_v),
|
|
scores: scores2,
|
|
c1FullName: c1 !== null ? c1.fname + " " + c1.lname : null,
|
|
c2FullName: c2 !== null ? c2.fname + " " + c2.lname : null
|
|
})
|
|
node.left = parseTree(data_in?.left, matches_)
|
|
node.right = parseTree(data_in?.right, matches_)
|
|
|
|
return node
|
|
}
|
|
|
|
function initTree(data_in, matches_) {
|
|
let out = []
|
|
let out2 = []
|
|
for (let i = 0; i < data_in.raw.length; i++) {
|
|
if (data_in.raw.at(i).level > -10) {
|
|
out.push(parseTree(data_in.formatted.at(i), matches_))
|
|
out2.push(parseTree(data_in.formatted.at(i), matches_))
|
|
}
|
|
}
|
|
return [out, out2.reverse()]
|
|
}
|
|
|
|
const [trees, rTrees] = initTree(treeData, matches);
|
|
rtrees.current = rTrees;
|
|
|
|
useEffect(() => {
|
|
if (matches.length === 0)
|
|
return;
|
|
if (matches.some(m => m.id === currentMatch?.matchSelect))
|
|
return;
|
|
|
|
const timeout = setTimeout(() => {
|
|
const rTrees_ = rtrees.current ? rtrees.current : rTrees;
|
|
const matchId = ((getNext.current) ? getNext.current(null) : null) || new TreeNode(null).nextMatchTree(rTrees_);
|
|
const next = matchId ? ((getNext.current) ? getNext.current(matchId) : null) || new TreeNode(matchId).nextMatchTree(rTrees_) : null;
|
|
setCurrentMatch({matchSelect: matchId, matchNext: next});
|
|
setState({selectedMatch: matchId});
|
|
sendNotify("sendSelectMatch", matchId);
|
|
}, 200);
|
|
return () => clearTimeout(timeout);
|
|
}, [matches])
|
|
|
|
const setCurrentMatch_ = (o) => {
|
|
const rTrees_ = rtrees.current ? rtrees.current : rTrees;
|
|
const matchId = o.matchSelect ? o.matchSelect : new TreeNode(null).nextMatchTree(rTrees_);
|
|
const next = o.matchNext ? o.matchNext : new TreeNode(matchId).nextMatchTree(rTrees_);
|
|
|
|
setCurrentMatch({matchSelect: matchId, matchNext: next});
|
|
setState({selectedMatch: matchId});
|
|
sendNotify("sendSelectMatch", matchId);
|
|
}
|
|
|
|
const onMatchClick = (rect, matchId, __) => {
|
|
const rTrees_ = rtrees.current ? rtrees.current : rTrees;
|
|
setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(rTrees_)});
|
|
setState({selectedMatch: matchId});
|
|
sendNotify("sendSelectMatch", matchId);
|
|
}
|
|
|
|
const onClickVoid = () => {
|
|
}
|
|
|
|
|
|
return <div>
|
|
<div className="overflow-y-auto" style={{maxHeight: "50vh"}}>
|
|
<div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}>
|
|
<DrawGraph root={trees} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid}
|
|
matchSelect={currentMatch?.matchSelect} matchNext={currentMatch?.matchNext} size={23} cards={cards_v}/>
|
|
{cat.fullClassement &&
|
|
<MatchList matches={treeData.raw.filter(n => n.level <= -10).reverse().map(d => matches.find(m => m.id === d.match?.id))}
|
|
cat={cat} menuActions={menuActions} classement={true} currentMatch={currentMatch} setCurrentMatch={setCurrentMatch_}
|
|
getNext={getNext}/>}
|
|
</div>
|
|
</div>
|
|
|
|
{currentMatch?.matchSelect &&
|
|
<LoadingProvider><ScorePanel matchId={currentMatch?.matchSelect} matchs={matches} match={match}
|
|
menuActions={menuActions}/></LoadingProvider>}
|
|
</div>
|
|
}
|