From 3e8c19534bd4754b0bb275c38c0f0a89664e4807 Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Thu, 18 Dec 2025 19:00:39 +0100 Subject: [PATCH] feat: add score panel --- .../webapp/src/hooks/useExternalWindow.jsx | 2 +- .../competition/editor/CMTMatchPanel.jsx | 40 ++++++++++----- .../src/pages/competition/editor/CMTPoint.jsx | 51 +++++++++++++++++++ .../src/pages/competition/editor/CMTable.jsx | 24 ++++++--- .../pages/competition/editor/PubAffWindow.jsx | 4 +- 5 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 src/main/webapp/src/pages/competition/editor/CMTPoint.jsx diff --git a/src/main/webapp/src/hooks/useExternalWindow.jsx b/src/main/webapp/src/hooks/useExternalWindow.jsx index 1c455a0..233b052 100644 --- a/src/main/webapp/src/hooks/useExternalWindow.jsx +++ b/src/main/webapp/src/hooks/useExternalWindow.jsx @@ -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(() => { }); diff --git a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx index ab7f99b..aca21fc 100644 --- a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx @@ -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}) { ))} - {catId !== -1 && } + {catId !== -1 && } } -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 + return } -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 && <> - + } {type === 2 && <> - + } } -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}) { - {activeMatch && } + {activeMatch && } } -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}/> - {currentMatch?.matchSelect && } + {currentMatch?.matchSelect && + } } -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(); diff --git a/src/main/webapp/src/pages/competition/editor/CMTPoint.jsx b/src/main/webapp/src/pages/competition/editor/CMTPoint.jsx new file mode 100644 index 0000000..810d28c --- /dev/null +++ b/src/main/webapp/src/pages/competition/editor/CMTPoint.jsx @@ -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 = +
+ +

{scoreRouge}

+ +
+ + return
+ {!revers && red} +
+ +

{scoreBleu}

+ +
+ {revers && red} + +
+ + +
+
+} diff --git a/src/main/webapp/src/pages/competition/editor/CMTable.jsx b/src/main/webapp/src/pages/competition/editor/CMTable.jsx index c5e0fde..32b8dfa 100644 --- a/src/main/webapp/src/pages/competition/editor/CMTable.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMTable.jsx @@ -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() { -
- B + +
+
Score
+
+ +
Matches
- +
@@ -48,14 +54,14 @@ export function CMTable() {
- + } 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( <> +
+
-
0
+
{state.scoreRouge}
-
0
+
{state.scoreBleu}
}