From 31315c951a3a63eed31948fd89b63f09bbcf3d77 Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 6 Feb 2026 17:17:44 +0100 Subject: [PATCH] feat: auto poule --- .../titionfire/ffsaf/ws/send/SRegister.java | 9 +- src/main/webapp/public/locales/en/cm.json | 4 + src/main/webapp/public/locales/fr/cm.json | 4 + .../src/components/cm/AutoCatModalContent.jsx | 232 ++++++++++++++++++ .../editor/CategoryAdminContent.jsx | 13 + 5 files changed, 258 insertions(+), 4 deletions(-) create mode 100644 src/main/webapp/src/components/cm/AutoCatModalContent.jsx diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/SRegister.java b/src/main/java/fr/titionfire/ffsaf/ws/send/SRegister.java index 09ec345..9dd8384 100644 --- a/src/main/java/fr/titionfire/ffsaf/ws/send/SRegister.java +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/SRegister.java @@ -33,10 +33,11 @@ public class SRegister { public Uni sendRegister(String uuid, RegisterModel registerModel) { return Mutiny.fetch(registerModel.getCategoriesInscrites()).chain(o -> send(uuid, "sendRegister", CombEntity.fromModel(registerModel).addCategoriesInscrites(o)) - .call(__ -> cardService.addTeamCartToNewComb(registerModel.getMembre().getId(), - registerModel.getClub2().getClubId(), registerModel.getClub2().getName(), - registerModel.getCompetition()) - .chain(cardModels -> send(uuid, "sendCards", cardModels)))); + .call(__ -> registerModel.getClub2() == null ? Uni.createFrom().voidItem() : + cardService.addTeamCartToNewComb(registerModel.getMembre().getId(), + registerModel.getClub2().getClubId(), registerModel.getClub2().getName(), + registerModel.getCompetition()) + .chain(cardModels -> send(uuid, "sendCards", cardModels)))); } public Uni sendRegister(String uuid, CompetitionGuestModel model) { diff --git a/src/main/webapp/public/locales/en/cm.json b/src/main/webapp/public/locales/en/cm.json index 37a904c..5ccf2bc 100644 --- a/src/main/webapp/public/locales/en/cm.json +++ b/src/main/webapp/public/locales/en/cm.json @@ -5,6 +5,7 @@ "actuel": "Current", "administration": "Administration", "adresseDuServeur": "Server address", + "ajoutAutomatique": "Automatic addition", "ajouter": "Add", "ajouterDesCombattants": "Add fighters", "ajouterUn": "Add one", @@ -37,6 +38,7 @@ "chronomètre": "Stopwatch", "classement": "Ranking", "club": "Club", + "combattantsCorrespondentAuxSélectionnés": "fighter(s) match the selections above", "compétition": "Competition", "compétitionManager": "Competition manager", "config.obs.dossierDesResources": "Resources folder", @@ -107,6 +109,7 @@ "poule": "Pool", "poulePour": "Pool for: ", "préparation...": "Preparing...", + "remplacer": "Replace", "rouge": "Red", "réinitialiser": "Reset", "résultat": "Result", @@ -179,6 +182,7 @@ "téléchargementEnCours": "Downloading...", "téléchargementTerminé!": "Download completed!", "uneCatégorie": "a category", + "uneCatégorieNePeutContenirPlusDe10Combattants": "A category cannot contain more than 10 fighters, please create weight categories.", "valider": "Validate", "zone": "Zone", "zoneDeCombat": "Combat zone" diff --git a/src/main/webapp/public/locales/fr/cm.json b/src/main/webapp/public/locales/fr/cm.json index 5bcf129..384acfa 100644 --- a/src/main/webapp/public/locales/fr/cm.json +++ b/src/main/webapp/public/locales/fr/cm.json @@ -5,6 +5,7 @@ "actuel": "Actuel", "administration": "Administration", "adresseDuServeur": "Adresse du serveur", + "ajoutAutomatique": "Ajout automatique", "ajouter": "Ajouter", "ajouterDesCombattants": "Ajouter des combattants", "ajouterUn": "Ajouter un ", @@ -37,6 +38,7 @@ "chronomètre": "Chronomètre", "classement": "Classement", "club": "Club", + "combattantsCorrespondentAuxSélectionnés": "combattant(s) correspondent aux sélectionnés ci-dessus.", "compétition": "Compétition", "compétitionManager": "Compétition manager", "config.obs.dossierDesResources": "Dossier des resources", @@ -107,6 +109,7 @@ "poule": "Poule", "poulePour": "Poule pour: ", "préparation...": "Préparation...", + "remplacer": "Remplacer", "rouge": "Rouge", "réinitialiser": "Réinitialiser", "résultat": "Résultat", @@ -179,6 +182,7 @@ "téléchargementEnCours": "Téléchargement en cours...", "téléchargementTerminé!": "Téléchargement terminé !", "uneCatégorie": "une catégorie", + "uneCatégorieNePeutContenirPlusDe10Combattants": "Une catégorie ne peut contenir plus de 10 combattants, veuillez créer des catégories de poids.", "valider": "Valider", "zone": "Zone", "zoneDeCombat": "Zone de combat" diff --git a/src/main/webapp/src/components/cm/AutoCatModalContent.jsx b/src/main/webapp/src/components/cm/AutoCatModalContent.jsx new file mode 100644 index 0000000..bd9e1ee --- /dev/null +++ b/src/main/webapp/src/components/cm/AutoCatModalContent.jsx @@ -0,0 +1,232 @@ +import React, {useEffect, useState} from "react"; +import {useTranslation} from "react-i18next"; +import {useCountries} from "../../hooks/useCountries.jsx"; +import {ListPresetSelect} from "./ListPresetSelect.jsx"; +import {CatList, getCatName} from "../../utils/Tools.js"; + +export function AutoCatModalContent({data, groups, setGroups, defaultPreset = -1}) { + const country = useCountries('fr') + const {t} = useTranslation("cm"); + + const [country_, setCountry_] = useState("") + const [gender, setGender] = useState({H: true, F: true, NA: true}) + const [cat, setCat] = useState([]) + const [weightMin, setWeightMin] = useState(0) + const [weightMax, setWeightMax] = useState(0) + const [team, setTeam] = useState(false) + const [preset, setPreset] = useState(-1) + + useEffect(() => { + setPreset(defaultPreset) + }, [defaultPreset]) + + const setCat_ = (e, index) => { + if (e.target.checked) { + if (!cat.includes(index)) { + setCat([...cat, index]) + } + } else { + setCat(cat.filter(c => c !== index)) + } + } + + function applyFilter(dataIn, dataOut) { + dataIn.forEach(comb_ => { + const comb = data.find(d => d.id === comb_.id); + if (comb == null) + return; + if ((country_ === "" || comb.country === country_) + && (gender.H && comb.genre === 'H' || gender.F && comb.genre === 'F' || gender.NA && comb.genre === 'NA') + && (cat.includes(Math.min(CatList.length, CatList.indexOf(comb.categorie) + comb.overCategory))) + && (weightMin === 0 || comb.weight !== null && comb.weight >= weightMin) + && (weightMax === 0 || comb.weight !== null && comb.weight <= weightMax) + && ((comb.teamMembers == null || comb.teamMembers.length === 0) !== team) + && (preset === -1 || comb.categoriesInscrites.includes(preset))) { + dataOut.push(comb) + } + } + ) + } + + const dispoFiltered = []; + if (data != null) + applyFilter(data, dispoFiltered); + + const makePoule = (combIn, groups) => { + combIn = combIn.sort(() => Math.random() - 0.5); + const maxInPoule = Math.ceil(combIn.length / 2); + const out = [] + + const pa = []; + const pb = []; + + let nameA; + let nameB; + groups.forEach(g => { + const existsInCombIn = combIn.some(c => c.id === g.id); + if (existsInCombIn) { + if ((pa.length === 0 || g.poule === nameA) && pa.length < maxInPoule) { + nameA = g.poule || "1"; + pa.push(g.id); + } else if ((pb.length === 0 || g.poule === nameB) && pb.length < maxInPoule) { + if (!(nameA === (g.poule || (nameA === "1" ? "2" : "1")))) { + nameB = g.poule || (nameA === "1" ? "2" : "1"); + pb.push(g.id); + } + } + } + }); + nameA = nameA || (nameB === "1" ? "2" : "1"); + nameB = nameB || (nameA === "1" ? "2" : "1"); + + if (combIn.length <= 5) { + combIn.forEach(c => { + if (!pa.includes(c.id)) + pa.push(c.id) + }); + } else { + for (const c of combIn) { + if (pa.includes(c.id) || pb.includes(c.id)) + continue; + + const club = c.club_str || (c.teamMembers && c.teamMembers[0].club_str) || ""; + + const countInPa = pa.filter(p => (p.club_str || (p.teamMembers && p.teamMembers[0].club_str) || "") === club).length; + const countInPb = pb.filter(p => (p.club_str || (p.teamMembers && p.teamMembers[0].club_str) || "") === club).length; + + if (pa.length < maxInPoule && (countInPa <= countInPb || pb.length >= maxInPoule)) { + pa.push(c.id); + } else if (pb.length < maxInPoule) { + pb.push(c.id); + } else { + pa.push(c.id); + } + } + } + + pa.forEach(id => out.push({id: id, poule: nameA})); + pb.forEach(id => out.push({id: id, poule: nameB})); + + return out + } + + const handleSubmit = (e) => { + e.preventDefault(); + + const toReplace = makePoule(dispoFiltered, groups); + setGroups(prev => [...prev.filter(g => !toReplace.some(r => r.id === g.id)), ...toReplace]); + } + + const handleReplace = (e) => { + e.preventDefault(); + + const toReplace = makePoule(dispoFiltered, []); + setGroups(prev => [...prev.map(g => ({id: g.id, poule: "-"})).filter(g => !toReplace.some(r => r.id === g.id)), ...toReplace]); + } + + return <> +
+

{t('ajoutAutomatique')}

+ +
+
+
+
+ + +
+ + +
+ +
+
+ +
+
+ setGender((prev) => { + return {...prev, H: e.target.checked} + })}/> + +
+
+ setGender((prev) => { + return {...prev, F: e.target.checked} + })}/> + +
+
+ setGender((prev) => { + return {...prev, NA: e.target.checked} + })}/> + +
+
+
+
+ +
+
+ setTeam(e.target.checked)}/> + +
+
+
+
+ +
+
setWeightMin(Number(e.target.value))}/>
+
{t('select.à')}
+
setWeightMax(Number(e.target.value))}/>
+
{t('select.msg1')}
+
+
+
+
+
+ + {CatList.map((cat_, index) => { + return
+
+ setCat_(e, index)}/> + +
+
+ })} +
+
+ + {dispoFiltered.length} {t('combattantsCorrespondentAuxSélectionnés')} {dispoFiltered.length > 10 && + {t('uneCatégorieNePeutContenirPlusDe10Combattants')}} + +
+
+ + + +
+ +} diff --git a/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx b/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx index eb878bc..8fe0aff 100644 --- a/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx +++ b/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx @@ -21,6 +21,7 @@ import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard. import {ScorePanel} from "./ScoreAndCardPanel.jsx"; import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx"; +import {AutoCatModalContent} from "../../../components/cm/AutoCatModalContent.jsx"; const vite_url = import.meta.env.VITE_URL; @@ -240,6 +241,10 @@ function AddComb({groups, setGroups, removeGroup, menuActions, cat}) { disabled={data === null} onClick={() => setModalMode(true)}>{t('ajouterUneTeam')} + +