feat: add score panel

This commit is contained in:
Thibaut Valentin 2025-12-18 19:00:39 +01:00
parent a1b5ca2694
commit 3e8c19534b
5 changed files with 100 additions and 21 deletions

View File

@ -1,6 +1,6 @@
import {createContext, useContext, useReducer} from "react";
const PubAffContext = createContext({next: [], c1: undefined, c2: undefined, showScore: true, timeCb: undefined});
const PubAffContext = createContext({next: [], c1: undefined, c2: undefined, showScore: true, timeCb: undefined, scoreRouge: 0, scoreBleu: 0});
const PubAffDispatchContext = createContext(() => {
});

View File

@ -17,7 +17,7 @@ function CupImg() {
alt=""/>
}
export function CategorieSelect({catId, setCatId}) {
export function CategorieSelect({catId, setCatId, menuActions}) {
const setLoading = useLoadingSwitcher()
const {data: cats, setData: setCats} = useRequestWS('getAllCategory', {}, setLoading);
const {dispatch} = useWS();
@ -41,11 +41,11 @@ export function CategorieSelect({catId, setCatId}) {
<option key={c.id} value={c.id}>{c.name}</option>))}
</select>
</div>
{catId !== -1 && <CMTMatchPanel catId={catId} cat={cat}/>}
{catId !== -1 && <CMTMatchPanel catId={catId} cat={cat} menuActions={menuActions}/>}
</>
}
function CMTMatchPanel({catId, cat}) {
function CMTMatchPanel({catId, cat, menuActions}) {
const setLoading = useLoadingSwitcher()
const {sendRequest, dispatch} = useWS();
const [trees, setTrees] = useState([]);
@ -117,10 +117,10 @@ function CMTMatchPanel({catId, cat}) {
}
}, [catId]);
return <ListMatch cat={cat} matches={matches} trees={trees}/>
return <ListMatch cat={cat} matches={matches} trees={trees} menuActions={menuActions}/>
}
function ListMatch({cat, matches, trees}) {
function ListMatch({cat, matches, trees, menuActions}) {
const [type, setType] = useState(1);
useEffect(() => {
@ -146,16 +146,16 @@ function ListMatch({cat, matches, trees}) {
}
{type === 1 && <>
<MatchList matches={matches} cat={cat}/>
<MatchList matches={matches} cat={cat} menuActions={menuActions}/>
</>}
{type === 2 && <>
<BuildTree treeData={trees} matches={matches}/>
<BuildTree treeData={trees} matches={matches} menuActions={menuActions}/>
</>}
</div>
}
function MatchList({matches, cat}) {
function MatchList({matches, cat, menuActions}) {
const [activeMatch, setActiveMatch] = useState(null)
const [lice, setLice] = useState(localStorage.getItem("cm_lice") || "A")
const publicAffDispatch = usePubAffDispatch();
@ -249,11 +249,11 @@ function MatchList({matches, cat}) {
</table>
</div>
{activeMatch && <LoadingProvider><ScorePanel matchId={activeMatch} match={match}/></LoadingProvider>}
{activeMatch && <LoadingProvider><ScorePanel matchId={activeMatch} match={match} menuActions={menuActions}/></LoadingProvider>}
</>
}
function BuildTree({treeData, matches}) {
function BuildTree({treeData, matches, menuActions}) {
const scrollRef = useRef(null)
const [currentMatch, setCurrentMatch] = useState(null)
const {getComb} = useCombs()
@ -320,11 +320,12 @@ function BuildTree({treeData, matches}) {
matchSelect={currentMatch?.matchSelect} matchNext={currentMatch?.matchNext} size={23}/>
</div>
{currentMatch?.matchSelect && <LoadingProvider><ScorePanel matchId={currentMatch?.matchSelect} match={match}/></LoadingProvider>}
{currentMatch?.matchSelect &&
<LoadingProvider><ScorePanel matchId={currentMatch?.matchSelect} match={match} menuActions={menuActions}/></LoadingProvider>}
</div>
}
function ScorePanel({matchId, match}) {
function ScorePanel({matchId, match, menuActions}) {
const {sendRequest} = useWS()
const setLoading = useLoadingSwitcher()
@ -335,6 +336,21 @@ function ScorePanel({matchId, match}) {
const scoreRef = useRef([])
const lastScoreClick = useRef(null)
useEffect(() => {
menuActions.current.saveScore = (scoreRed, scoreBlue) => {
const maxRound = (Math.max(...match.scores.map(s => s.n_round), -1) + 1) || 0;
const newScore = {n_round: maxRound, s1: scoreRed, s2: scoreBlue};
toast.promise(sendRequest('updateMatchScore', {matchId: matchId, ...newScore}),
{
pending: 'Sauvegarde du score...',
success: 'Score sauvegardé !',
error: 'Erreur lors de la sauvegarde du score'
}
);
}
return () => menuActions.current.saveScore = undefined;
}, [matchId])
const handleScoreClick = (e, round, comb) => {
e.stopPropagation();
const tableRect = tableRef.current.getBoundingClientRect();

View File

@ -0,0 +1,51 @@
import {useEffect, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faChevronDown, faChevronUp} from "@fortawesome/free-solid-svg-icons";
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
export function PointPanel({menuActions}) {
const [revers, setRevers] = useState(false)
const [scoreRouge, setScoreRouge] = useState(0)
const [scoreBleu, setScoreBleu] = useState(0)
const publicAffDispatch = usePubAffDispatch()
menuActions.current.switchSore = () => {
setRevers(!revers)
}
const handleSave = () => {
menuActions.current.saveScore?.(scoreRouge, scoreBleu)
handleReset();
}
const handleReset = () => {
setScoreRouge(0);
setScoreBleu(0);
}
useEffect(() => {
publicAffDispatch({type: 'SET_DATA', payload: {scoreRouge: scoreRouge, scoreBleu: scoreBleu}})
}, [scoreRouge, scoreBleu])
const red =
<div className="col-5 row align-items-center" style={{padding: "0 1em"}}>
<button className="btn btn-secondary" onClick={() => setScoreRouge(scoreRouge + 1)}><FontAwesomeIcon icon={faChevronUp}/></button>
<h1 style={{color: "red", fontSize: "min(15vw, 7em)", textAlign: "center"}}>{scoreRouge}</h1>
<button className="btn btn-secondary" onClick={() => setScoreRouge(scoreRouge - 1)}><FontAwesomeIcon icon={faChevronDown}/></button>
</div>
return <div className="row">
{!revers && red}
<div className="col-5 row align-items-center" style={{padding: "0 1em"}}>
<button className="btn btn-secondary" onClick={() => setScoreBleu(scoreBleu + 1)}><FontAwesomeIcon icon={faChevronUp}/></button>
<h1 style={{color: "blue", fontSize: "min(15vw, 7em)", textAlign: "center"}}>{scoreBleu}</h1>
<button className="btn btn-secondary" onClick={() => setScoreBleu(scoreBleu - 1)}><FontAwesomeIcon icon={faChevronDown}/></button>
</div>
{revers && red}
<div className="col row align-items-center">
<button className="btn btn-danger" onClick={handleReset}>Réinitialiser</button>
<button className="btn btn-success" onClick={handleSave}>Sauvegarder</button>
</div>
</div>
}

View File

@ -5,15 +5,17 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {createPortal} from "react-dom";
import {copyStyles} from "../../../utils/copyStyles.js";
import {PubAffProvider, usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
import {faDisplay} from "@fortawesome/free-solid-svg-icons";
import {faArrowRightArrowLeft, faDisplay} from "@fortawesome/free-solid-svg-icons";
import {PubAffWindow} from "./PubAffWindow.jsx";
import {SimpleIconsScore} from "../../../assets/SimpleIconsScore.ts";
import {ChronoPanel} from "./CMTChronoPanel.jsx";
import {CategorieSelect} from "./CMTMatchPanel.jsx";
import {PointPanel} from "./CMTPoint.jsx";
export function CMTable() {
const combDispatch = useCombsDispatch()
const [catId, setCatId] = useState(-1);
const menuActions = useRef({});
const {data} = useRequestWS("getRegister", null)
useEffect(() => {
@ -32,15 +34,19 @@ export function CMTable() {
<ChronoPanel/>
</div>
</div>
<div style={{backgroundColor: "#0099c7"}}>
B
<div className="card mb-3">
<div className="card-header">Score</div>
<div className="card-body">
<PointPanel menuActions={menuActions}/>
</div>
</div>
</div>
<div className="col-md-12 col-xl-6 col-xxl-5">
<div className="card mb-3">
<div className="card-header">Matches</div>
<div className="card-body">
<CategorieSelect catId={catId} setCatId={setCatId}/>
<CategorieSelect catId={catId} setCatId={setCatId} menuActions={menuActions}/>
</div>
</div>
<div style={{backgroundColor: "#c70000"}}>
@ -48,14 +54,14 @@ export function CMTable() {
</div>
</div>
</div>
<Menu/>
<Menu menuActions={menuActions}/>
</div>
</PubAffProvider>
}
const windowName = "FFSAFScorePublicWindow";
function Menu() {
function Menu({menuActions}) {
const e = document.getElementById("actionMenu")
const publicAffDispatch = usePubAffDispatch()
const [showPubAff, setShowPubAff] = useState(false)
@ -101,11 +107,17 @@ function Menu() {
publicAffDispatch({type: 'SET_DATA', payload: {showScore: !showScore}});
}
const handleSwitchScore = () => {
menuActions.current.switchSore?.();
}
if (!e)
return <></>;
return <>
{createPortal(
<>
<div className="vr" style={{margin: "0 0.5em", height: "100%"}}></div>
<FontAwesomeIcon icon={faArrowRightArrowLeft} size="xl" style={{color: "#6c757d", cursor: "pointer"}} onClick={handleSwitchScore}/>
<div className="vr" style={{margin: "0 0.5em", height: "100%"}}></div>
<FontAwesomeIcon icon={faDisplay} size="xl"
style={{color: showPubAff ? "#00c700" : "#6c757d", cursor: "pointer", marginRight: "0.25em"}}

View File

@ -37,12 +37,12 @@ export function PubAffWindow({document}) {
{showScore &&
<div className="row" style={noMP}>
<div className="col-4" style={noMP}>
<div style={{fontSize: "30vh", lineHeight: "30vh", color: "#ff1414"}}>0</div>
<div style={{fontSize: "30vh", lineHeight: "30vh", color: "#ff1414"}}>{state.scoreRouge}</div>
</div>
<div className="col-4" style={noMP}>
</div>
<div className="col-4" style={noMP}>
<div style={{fontSize: "30vh", lineHeight: "30vh", color: "#14adff"}}>0</div>
<div style={{fontSize: "30vh", lineHeight: "30vh", color: "#14adff"}}>{state.scoreBleu}</div>
</div>
</div>}
</div>