|
|
|
|
@@ -10,6 +10,7 @@ import {scorePrint, win} from "../../../utils/Tools.js";
|
|
|
|
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|
|
|
|
import {faCircleQuestion} from "@fortawesome/free-regular-svg-icons";
|
|
|
|
|
import {toast} from "react-toastify";
|
|
|
|
|
import "./CMTMatchPanel.css"
|
|
|
|
|
|
|
|
|
|
function CupImg() {
|
|
|
|
|
return <img decoding="async" loading="lazy" width={"16"} height={"16"} className="wp-image-1635"
|
|
|
|
|
@@ -105,15 +106,21 @@ function CMTMatchPanel({catId, cat, menuActions}) {
|
|
|
|
|
reducer({type: 'REMOVE', payload: data})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sendCardboard = ({data}) => {
|
|
|
|
|
reducer({type: 'UPDATE_CARDBOARD', 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'}})
|
|
|
|
|
dispatch({type: 'addListener', payload: {callback: sendCardboard, code: 'sendCardboard'}})
|
|
|
|
|
return () => {
|
|
|
|
|
dispatch({type: 'removeListener', payload: treeListener})
|
|
|
|
|
dispatch({type: 'removeListener', payload: matchListener})
|
|
|
|
|
dispatch({type: 'removeListener', payload: matchOrder})
|
|
|
|
|
dispatch({type: 'removeListener', payload: deleteMatch})
|
|
|
|
|
dispatch({type: 'removeListener', payload: sendCardboard})
|
|
|
|
|
}
|
|
|
|
|
}, [catId]);
|
|
|
|
|
|
|
|
|
|
@@ -164,6 +171,7 @@ function MatchList({matches, cat, menuActions}) {
|
|
|
|
|
const marches2 = matches.filter(m => m.categorie_ord !== -42)
|
|
|
|
|
.sort((a, b) => a.categorie_ord - b.categorie_ord)
|
|
|
|
|
.map(m => ({...m, win: win(m.scores)}))
|
|
|
|
|
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
|
|
|
|
|
|
|
|
|
|
const match = matches.find(m => m.id === activeMatch)
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
@@ -175,7 +183,10 @@ function MatchList({matches, cat, menuActions}) {
|
|
|
|
|
payload: {
|
|
|
|
|
c1: match.c1,
|
|
|
|
|
c2: match.c2,
|
|
|
|
|
next: marches2.filter(m => !m.end && m.poule === lice && m.id !== activeMatch).map(m => ({c1: m.c1, c2: m.c2}))
|
|
|
|
|
next: marches2.filter((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice && m.id !== activeMatch).map(m => ({
|
|
|
|
|
c1: m.c1,
|
|
|
|
|
c2: m.c2
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@@ -187,7 +198,7 @@ function MatchList({matches, cat, menuActions}) {
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (match && match.poule !== lice)
|
|
|
|
|
setActiveMatch(marches2.find(m => !m.end && m.poule === lice)?.id)
|
|
|
|
|
setActiveMatch(marches2.find((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice)?.id)
|
|
|
|
|
}, [lice]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
@@ -196,10 +207,8 @@ function MatchList({matches, cat, menuActions}) {
|
|
|
|
|
if (marches2.some(m => m.id === activeMatch))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
setActiveMatch(marches2.find(m => !m.end && m.poule === lice)?.id);
|
|
|
|
|
setActiveMatch(marches2.find((m, index) => !m.end && liceName[(index - firstIndex) % liceName.length] === lice)?.id);
|
|
|
|
|
}, [matches])
|
|
|
|
|
|
|
|
|
|
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
|
|
|
|
|
return <>
|
|
|
|
|
{liceName.length > 1 &&
|
|
|
|
|
<div className="input-group" style={{maxWidth: "10em", marginTop: "0.5em"}}>
|
|
|
|
|
@@ -230,7 +239,8 @@ function MatchList({matches, cat, menuActions}) {
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody className="table-group-divider">
|
|
|
|
|
{marches2.map((m, index) => (
|
|
|
|
|
<tr key={m.id} className={m.id === activeMatch ? "table-info" : (m.poule === lice ? "" : "table-warning")}
|
|
|
|
|
<tr key={m.id}
|
|
|
|
|
className={m.id === activeMatch ? "table-info" : (liceName[(index - firstIndex) % liceName.length] === lice ? "" : "table-warning")}
|
|
|
|
|
onClick={() => setActiveMatch(m.id)}>
|
|
|
|
|
<td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
|
|
|
|
|
{liceName[(index - firstIndex) % liceName.length]}</td>
|
|
|
|
|
@@ -326,6 +336,16 @@ function BuildTree({treeData, matches, menuActions}) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ScorePanel({matchId, match, menuActions}) {
|
|
|
|
|
const onClickVoid = useRef(() => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return <div className="row" onClick={onClickVoid.current}>
|
|
|
|
|
<ScorePanel_ matchId={matchId} match={match} menuActions={menuActions} onClickVoid_={onClickVoid}/>
|
|
|
|
|
<CardPanel matchId={matchId} match={match}/>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ScorePanel_({matchId, match, menuActions, onClickVoid_}) {
|
|
|
|
|
const {sendRequest} = useWS()
|
|
|
|
|
const setLoading = useLoadingSwitcher()
|
|
|
|
|
|
|
|
|
|
@@ -418,6 +438,7 @@ function ScorePanel({matchId, match, menuActions}) {
|
|
|
|
|
sel.style.display = "none";
|
|
|
|
|
lastScoreClick.current = null;
|
|
|
|
|
}
|
|
|
|
|
onClickVoid_.current = onClickVoid;
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!match || match?.end === end)
|
|
|
|
|
@@ -463,64 +484,143 @@ function ScorePanel({matchId, match, menuActions}) {
|
|
|
|
|
"-999 : forfait"
|
|
|
|
|
|
|
|
|
|
const maxRound = (match?.scores) ? (Math.max(...match.scores.map(s => s.n_round), -1) + 1) : 0;
|
|
|
|
|
return <div className="row" onClick={onClickVoid}>
|
|
|
|
|
<div ref={tableRef} className="col" style={{position: "relative"}}>
|
|
|
|
|
<h6>Scores <FontAwesomeIcon icon={faCircleQuestion} role="button" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-title={tt}
|
|
|
|
|
data-bs-html="true"/></h6>
|
|
|
|
|
<table className="table table-striped">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th style={{textAlign: "center"}} scope="col">Manche</th>
|
|
|
|
|
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">Rouge</th>
|
|
|
|
|
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">Bleu</th>
|
|
|
|
|
return <div ref={tableRef} className="col" style={{position: "relative"}}>
|
|
|
|
|
<h6>Scores <FontAwesomeIcon icon={faCircleQuestion} role="button" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-title={tt}
|
|
|
|
|
data-bs-html="true"/></h6>
|
|
|
|
|
<table className="table table-striped">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th style={{textAlign: "center"}} scope="col">Manche</th>
|
|
|
|
|
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">Rouge</th>
|
|
|
|
|
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">Bleu</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody className="table-group-divider">
|
|
|
|
|
{match?.scores && match.scores.sort((a, b) => a.n_round - b.n_round).map(score => (
|
|
|
|
|
<tr key={score.n_round}>
|
|
|
|
|
<th style={{textAlign: "center"}}>{score.n_round + 1}</th>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2] = e}
|
|
|
|
|
onClick={e => handleScoreClick(e, score.n_round, 1)}>{scorePrint(score.s1)}</td>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2 + 1] = e}
|
|
|
|
|
onClick={e => handleScoreClick(e, score.n_round, 2)}>{scorePrint(score.s2)}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody className="table-group-divider">
|
|
|
|
|
{match?.scores && match.scores.sort((a, b) => a.n_round - b.n_round).map(score => (
|
|
|
|
|
<tr key={score.n_round}>
|
|
|
|
|
<th style={{textAlign: "center"}}>{score.n_round + 1}</th>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2] = e}
|
|
|
|
|
onClick={e => handleScoreClick(e, score.n_round, 1)}>{scorePrint(score.s1)}</td>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2 + 1] = e}
|
|
|
|
|
onClick={e => handleScoreClick(e, score.n_round, 2)}>{scorePrint(score.s2)}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
))}
|
|
|
|
|
<tr>
|
|
|
|
|
<th style={{textAlign: "center"}}></th>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2] = e} onClick={e => handleScoreClick(e, -1, 1)}>-</td>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2 + 1] = e} onClick={e => handleScoreClick(e, -1, 2)}>-
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
<div style={{textAlign: "right"}}>
|
|
|
|
|
<div className="form-check" style={{display: "inline-block"}}>
|
|
|
|
|
<input className="form-check-input" type="checkbox" id="checkboxEnd" name="checkboxEnd" checked={end}
|
|
|
|
|
onChange={e => setEnd(e.target.checked)}/>
|
|
|
|
|
<label className="form-check-label" htmlFor="checkboxEnd">Terminé</label>
|
|
|
|
|
))}
|
|
|
|
|
<tr>
|
|
|
|
|
<th style={{textAlign: "center"}}></th>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2] = e} onClick={e => handleScoreClick(e, -1, 1)}>-</td>
|
|
|
|
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2 + 1] = e} onClick={e => handleScoreClick(e, -1, 2)}>-
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
<div style={{textAlign: "right"}}>
|
|
|
|
|
<div className="form-check" style={{display: "inline-block"}}>
|
|
|
|
|
<input className="form-check-input" type="checkbox" id="checkboxEnd" name="checkboxEnd" checked={end}
|
|
|
|
|
onChange={e => setEnd(e.target.checked)}/>
|
|
|
|
|
<label className="form-check-label" htmlFor="checkboxEnd">Terminé</label>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<input ref={inputRef} type="number" className="form-control" style={{position: "absolute", top: 0, left: 0, display: "none"}} min="-999"
|
|
|
|
|
max="999"
|
|
|
|
|
value={scoreIn} onChange={e => setScoreIn(e.target.value)}
|
|
|
|
|
onClick={e => e.stopPropagation()}
|
|
|
|
|
onKeyDown={e => {
|
|
|
|
|
if (e.key === "Tab") {
|
|
|
|
|
if (lastScoreClick.current !== null) {
|
|
|
|
|
const {round, comb} = lastScoreClick.current;
|
|
|
|
|
const nextIndex = (round * 2 + (comb - 1)) + (e.shiftKey ? -1 : 1);
|
|
|
|
|
if (nextIndex >= 0 && nextIndex < scoreRef.current.length) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
scoreRef.current[nextIndex].click();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (e.key === "Enter") {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
onClickVoid();
|
|
|
|
|
}
|
|
|
|
|
}}/>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function CardPanel({matchId, match}) {
|
|
|
|
|
const {sendRequest, dispatch} = useWS();
|
|
|
|
|
const setLoading = useLoadingSwitcher()
|
|
|
|
|
|
|
|
|
|
const {data, refresh} = useRequestWS('getCardboardWithoutThis', matchId, setLoading);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
refresh('getCardboardWithoutThis', matchId);
|
|
|
|
|
|
|
|
|
|
const sendCardboard = ({data}) => {
|
|
|
|
|
if (data.comb_id === match.c1 || data.comb_id === match.c2) {
|
|
|
|
|
refresh('getCardboardWithoutThis', matchId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatch({type: 'addListener', payload: {callback: sendCardboard, code: 'sendCardboard'}})
|
|
|
|
|
return () => dispatch({type: 'removeListener', payload: sendCardboard})
|
|
|
|
|
}, [matchId])
|
|
|
|
|
|
|
|
|
|
if (!match) {
|
|
|
|
|
return <div className="col"></div>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const c1Cards = match.cardboard?.find(c => c.comb_id === match.c1) || {red: 0, yellow: 0};
|
|
|
|
|
const c2Cards = match.cardboard?.find(c => c.comb_id === match.c2) || {red: 0, yellow: 0};
|
|
|
|
|
|
|
|
|
|
const handleCard = (combId, yellow, red) => {
|
|
|
|
|
if (combId === match.c1) {
|
|
|
|
|
if (c1Cards.red + red < 0 || c1Cards.yellow + yellow < 0)
|
|
|
|
|
return;
|
|
|
|
|
} else if (combId === match.c2) {
|
|
|
|
|
if (c2Cards.red + red < 0 || c2Cards.yellow + yellow < 0)
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setLoading(1)
|
|
|
|
|
sendRequest('sendCardboardChange', {matchId, combId, yellow, red})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
setLoading(0)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <div className="col">
|
|
|
|
|
<h6>Carton</h6>
|
|
|
|
|
<div className="bg-danger-subtle text-danger-emphasis" style={{padding: ".25em", borderRadius: "1em 1em 0 0"}}>
|
|
|
|
|
<div>Competition: <span className="badge text-bg-danger">{(data?.c1_red || 0) + c1Cards.red}</span> <span
|
|
|
|
|
className="badge text-bg-warning">{(data?.c1_yellow || 0) + c1Cards.yellow}</span></div>
|
|
|
|
|
<div className="d-flex justify-content-center align-items-center" style={{margin: ".25em"}}>
|
|
|
|
|
Match:
|
|
|
|
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
|
|
|
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c1, 0, +1)}>+</button>
|
|
|
|
|
<span className="badge text-bg-danger">{c1Cards.red}</span>
|
|
|
|
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c1, 0, -1)}>-</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
|
|
|
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c1, +1, 0)}>+</button>
|
|
|
|
|
<span className="badge text-bg-warning">{c1Cards.yellow}</span>
|
|
|
|
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c1, -1, 0)}>-</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<input ref={inputRef} type="number" className="form-control" style={{position: "absolute", top: 0, left: 0, display: "none"}} min="-999"
|
|
|
|
|
max="999"
|
|
|
|
|
value={scoreIn} onChange={e => setScoreIn(e.target.value)}
|
|
|
|
|
onClick={e => e.stopPropagation()}
|
|
|
|
|
onKeyDown={e => {
|
|
|
|
|
if (e.key === "Tab") {
|
|
|
|
|
if (lastScoreClick.current !== null) {
|
|
|
|
|
const {round, comb} = lastScoreClick.current;
|
|
|
|
|
const nextIndex = (round * 2 + (comb - 1)) + (e.shiftKey ? -1 : 1);
|
|
|
|
|
if (nextIndex >= 0 && nextIndex < scoreRef.current.length) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
scoreRef.current[nextIndex].click();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (e.key === "Enter") {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
onClickVoid();
|
|
|
|
|
}
|
|
|
|
|
}}/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="col">
|
|
|
|
|
<div className="bg-info-subtle text-info-emphasis" style={{padding: ".25em", borderRadius: "0 0 1em 1em"}}>
|
|
|
|
|
<div>Competition: <span className="badge text-bg-danger">{(data?.c2_red || 0) + c2Cards.red}</span> <span
|
|
|
|
|
className="badge text-bg-warning">{(data?.c2_yellow || 0) + c2Cards.yellow}</span></div>
|
|
|
|
|
<div className="d-flex justify-content-center align-items-center" style={{margin: ".25em"}}>
|
|
|
|
|
Match:
|
|
|
|
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
|
|
|
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c2, 0, +1)}>+</button>
|
|
|
|
|
<span className="badge text-bg-danger">{c2Cards.red}</span>
|
|
|
|
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c2, 0, -1)}>-</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
|
|
|
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c2, +1, 0)}>+</button>
|
|
|
|
|
<span className="badge text-bg-warning">{c2Cards.yellow}</span>
|
|
|
|
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c2, -1, 0)}>-</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
|