From 4a07eb4ed92041955062891323ff684219b5c196 Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Thu, 22 Jan 2026 15:13:48 +0100 Subject: [PATCH] feat: comp register optional weight config --- .../data/model/CompetitionGuestModel.java | 5 ++ .../ffsaf/data/model/CompetitionModel.java | 3 + .../ffsaf/data/model/RegisterModel.java | 7 ++ .../domain/service/CompetitionService.java | 21 ++++-- .../ffsaf/rest/data/CompetitionData.java | 16 +++-- .../ffsaf/rest/data/RegisterRequestData.java | 1 + .../ffsaf/rest/data/SimpleRegisterComb.java | 5 +- src/main/webapp/public/locales/en/common.json | 4 ++ src/main/webapp/public/locales/fr/common.json | 4 ++ .../src/pages/competition/CompetitionEdit.jsx | 25 ++++++- .../competition/CompetitionRegisterAdmin.jsx | 66 +++++++++++++++---- .../src/pages/competition/CompetitionView.jsx | 20 ++++-- 12 files changed, 142 insertions(+), 35 deletions(-) diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java index 9c55358..fae3a91 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java @@ -44,6 +44,7 @@ public class CompetitionGuestModel implements CombModel { String country = "fr"; Integer weight = null; + Integer weightReal = null; @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinTable( @@ -107,4 +108,8 @@ public class CompetitionGuestModel implements CombModel { } return Stream.concat(comb.stream(), guest.stream()).anyMatch(c -> Objects.equals(c, comb_)); } + + public Integer getWeight2() { + return (this.weightReal != null) ? this.weightReal : this.weight; + } } diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java index ceac29e..5e136a9 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java @@ -1,5 +1,6 @@ package fr.titionfire.ffsaf.data.model; +import fr.titionfire.ffsaf.utils.Categorie; import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.RegisterMode; import io.quarkus.runtime.annotations.RegisterForReflection; @@ -61,6 +62,8 @@ public class CompetitionModel { @OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) List catPreset = new ArrayList<>(); + List requiredWeight = new ArrayList<>(); + List banMembre = new ArrayList<>(); String owner; diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java index c84602d..75c03bd 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java @@ -38,6 +38,7 @@ public class RegisterModel { MembreModel membre; Integer weight; + Integer weightReal; int overCategory = 0; Categorie categorie; @@ -89,4 +90,10 @@ public class RegisterModel { return null; return Categorie.values()[Math.min(tmp.ordinal() + this.overCategory, Categorie.values().length - 1)]; } + + public Integer getWeight2() { + if (weightReal != null) + return weightReal; + return weight; + } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java index 6ac79b7..7282864 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java @@ -301,6 +301,7 @@ public class CompetitionService { model.setStartRegister(data.getStartRegister()); model.setEndRegister(data.getEndRegister()); model.setRegisterMode(data.getRegisterMode()); + model.setRequiredWeight(data.getRequiredWeight()); model.setData1(data.getData1()); model.setData2(data.getData2()); model.setData3(data.getData3()); @@ -388,7 +389,9 @@ public class CompetitionService { model.setGenre(data.getGenre()); model.setClub(data.getClub()); model.setCountry(data.getCountry()); - model.setWeight(data.getWeight()); + model.setWeightReal(data.getWeightReal()); + if (model.getCompetition().getRequiredWeight().contains(model.getCategorie())) + model.setWeight(data.getWeight()); model.setCategorie(data.getCategorie()); }) .call(g -> Mutiny.fetch(g.getCategoriesInscrites())) @@ -452,7 +455,6 @@ public class CompetitionService { if (r != null) { if (!admin && r.isLockEdit()) throw new DForbiddenException(trad.t("insc.err3")); - r.setWeight(data.getWeight()); r.setOverCategory(data.getOverCategory()); r.setCategorie( (combModel.getBirth_date() == null) ? combModel.getCategorie() : @@ -461,24 +463,29 @@ public class CompetitionService { int days = Utils.getDaysBeforeCompetition(c.getDate()); if (days > -7) r.setClub(combModel.getClub()); - if (admin) + if (c.getRequiredWeight().contains(r.getCategorie2())) + r.setWeight(data.getWeight()); + if (admin) { + r.setWeightReal(data.getWeightReal()); r.setLockEdit(data.isLockEdit()); + } } else { r = new RegisterModel(c, combModel, data.getWeight(), data.getOverCategory(), (combModel.getBirth_date() == null) ? combModel.getCategorie() : Utils.getCategoryFormBirthDate(combModel.getBirth_date(), c.getDate()), (combModel.getClub() == null) ? null : combModel.getClub()); - if (admin) + if (admin) { + r.setWeightReal(data.getWeightReal()); r.setLockEdit(data.isLockEdit()); - else + } else r.setLockEdit(false); } if (c.getSystem() == CompetitionSystem.SAFCA) { SReqRegister.sendIfNeed(serverCustom.clients, new CompetitionData.SimpleRegister(r.getMembre().getId(), - r.getOverCategory(), r.getWeight(), r.getCategorie(), + r.getOverCategory(), r.getWeight2(), r.getCategorie(), (r.getClub() == null) ? null : r.getClub().getId(), (r.getClub() == null) ? null : r.getClub().getName()), c.getId()); } @@ -779,7 +786,7 @@ public class CompetitionService { public Uni registerHelloAsso(NotificationData data) { String organizationSlug = data.getOrganizationSlug(); String formSlug = data.getFormSlug(); - RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false, new ArrayList<>(), null, + RegisterRequestData req = new RegisterRequestData(null, "", "", null, null, 0, false, new ArrayList<>(), null, Categorie.CADET, Genre.NA, null, "fr", false); return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult() diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java index 8b48765..df37ffe 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java @@ -35,7 +35,7 @@ public class CompetitionData { private Long club; private String clubName; private String owner; - private List registers; + private List registers; // for SAFCA private boolean canEdit; private boolean canEditRegisters; private String data1; @@ -44,12 +44,13 @@ public class CompetitionData { private String data4; private String config; private List presets; + private List requiredWeight; - public CompetitionData () { + public CompetitionData() { this(null, "", "", "", "", new Date(), new Date(), CompetitionSystem.INTERNAL, RegisterMode.FREE, new Date(), new Date(), true, null, "", "", null, true, true, - "", "", "", "", "{}", new ArrayList<>()); + "", "", "", "", "{}", new ArrayList<>(), new ArrayList<>()); } public static CompetitionData fromModel(CompetitionModel model) { @@ -60,7 +61,8 @@ public class CompetitionData { model.getUuid(), model.getDate(), model.getTodate(), model.getSystem(), model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(), model.getClub().getId(), model.getClub().getName(), model.getOwner(), null, false, false, - model.getData1(), model.getData2(), model.getData3(), model.getData4(), model.getConfig(), new ArrayList<>()); + model.getData1(), model.getData2(), model.getData3(), model.getData4(), model.getConfig(), + new ArrayList<>(), model.getRequiredWeight()); } public static CompetitionData fromModelLight(CompetitionModel model) { @@ -71,7 +73,7 @@ public class CompetitionData { model.getAdresse(), "", model.getDate(), model.getTodate(), null, model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(), null, model.getClub().getName(), "", null, false, false, - "", "", "", "", "{}", new ArrayList<>()); + "", "", "", "", "{}", new ArrayList<>(), model.getRequiredWeight()); if (model.getRegisterMode() == RegisterMode.HELLOASSO) { out.setData1(model.getData1()); @@ -85,11 +87,11 @@ public class CompetitionData { public CompetitionData addInsc(List insc, List guests) { this.registers = Stream.concat( insc.stream() - .map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight(), + .map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight2(), i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId(), (i.getClub() == null) ? null : i.getClub().getName())), guests.stream() - .map(i -> new SimpleRegister(i.getId() * -1, 0, i.getWeight(), + .map(i -> new SimpleRegister(i.getId() * -1, 0, i.getWeight2(), i.getCategorie(), null, i.getClub()))).toList(); return this; } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java index 6d72eb0..0665be5 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java @@ -19,6 +19,7 @@ public class RegisterRequestData { private String lname; private Integer weight; + private Integer weightReal; private int overCategory; private boolean lockEdit = false; private List categoriesInscrites; diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java index c981763..b4b3b21 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java @@ -25,6 +25,7 @@ public class SimpleRegisterComb { private SimpleClubModel club; private Integer licence; private Integer weight; + private Integer weightReal; private int overCategory; private boolean hasLicenceActive; private boolean lockEdit; @@ -36,7 +37,7 @@ public class SimpleRegisterComb { membreModel.getGenre(), membreModel.getCountry(), (register.getCategorie() == null) ? null : register.getCategorie(), SimpleClubModel.fromModel(register.getClub()), membreModel.getLicence(), register.getWeight(), - register.getOverCategory(), + register.getWeightReal(), register.getOverCategory(), licences.stream().anyMatch(l -> l.isValidate() && l.getSaison() == Utils.getSaison()), register.isLockEdit(), new ArrayList<>()); } @@ -45,7 +46,7 @@ public class SimpleRegisterComb { return new SimpleRegisterComb(guest.getId() * -1, guest.getFname(), guest.getLname(), guest.getGenre(), guest.getCountry(), guest.getCategorie(), new SimpleClubModel(null, guest.getClub(), "fr", null), - null, guest.getWeight(), 0, false, false, + null, guest.getWeight(), guest.getWeightReal(), 0, false, false, new ArrayList<>()); } diff --git a/src/main/webapp/public/locales/en/common.json b/src/main/webapp/public/locales/en/common.json index 5f0747c..7e137bd 100644 --- a/src/main/webapp/public/locales/en/common.json +++ b/src/main/webapp/public/locales/en/common.json @@ -76,6 +76,7 @@ "aff_req.toast.undo.error": "Failed to cancel affiliation request", "aff_req.toast.undo.pending": "Cancelling affiliation request in progress", "aff_req.toast.undo.success": "Affiliation request cancelled successfully 🎉", + "afficherLesCombattantsNonPesés": "Show unweighed fighters", "afficherLétatDesAffiliation": "Display affiliation status", "affiliation": "Affiliation", "affiliationNo": "Affiliation no. {{no}}", @@ -193,7 +194,9 @@ "comp.inscriptionsParLesAdministrateursDeLaCompétition": "Registrations by competition administrators", "comp.inscriptionsParLesResponsablesDeClub": "Registrations by club managers", "comp.inscriptionsSurLaBilletterieHelloasso": "Registrations on the HelloAsso ticketing", + "comp.modal.annoncé": "Announced", "comp.modal.information": "Information", + "comp.modal.pesé": "Weighed", "comp.modal.poids": "Weight (in kg)", "comp.modal.recherche": "Search*", "comp.modal.surclassement": "Overclassification", @@ -490,6 +493,7 @@ "peutSinscrire": "Can register?", "photos": "Photos", "plastron": "Breastplate", + "poidsDemandéPour": "Weight required for", "prenom": "First name", "protectionDeBras": "Arm protection", "protectionDeBrasArmé": "Protection of armed arm(s)", diff --git a/src/main/webapp/public/locales/fr/common.json b/src/main/webapp/public/locales/fr/common.json index 68f6a30..934cabf 100644 --- a/src/main/webapp/public/locales/fr/common.json +++ b/src/main/webapp/public/locales/fr/common.json @@ -76,6 +76,7 @@ "aff_req.toast.undo.error": "Échec de l'annulation de la demande d'affiliation", "aff_req.toast.undo.pending": "Annulation de la demande d'affiliation en cours", "aff_req.toast.undo.success": "Demande d'affiliation annulée avec succès 🎉", + "afficherLesCombattantsNonPesés": "Afficher les combattants non pesés", "afficherLétatDesAffiliation": "Afficher l'état des affiliation", "affiliation": "Affiliation", "affiliationNo": "Affiliation n°{{no}}", @@ -193,7 +194,9 @@ "comp.inscriptionsParLesAdministrateursDeLaCompétition": "Inscriptions par les administrateurs de la compétition", "comp.inscriptionsParLesResponsablesDeClub": "Inscriptions par les responsables de club", "comp.inscriptionsSurLaBilletterieHelloasso": "Inscriptions sur la billetterie HelloAsso", + "comp.modal.annoncé": "Annoncé", "comp.modal.information": "Information", + "comp.modal.pesé": "Pesé", "comp.modal.poids": "Poids (en kg)", "comp.modal.recherche": "Recherche*", "comp.modal.surclassement": "Surclassement", @@ -490,6 +493,7 @@ "peutSinscrire": "Peut s'inscrire?", "photos": "Photos", "plastron": "Plastron", + "poidsDemandéPour": "Poids demandé pour", "prenom": "Prénom", "protectionDeBras": "Protection de bras", "protectionDeBrasArmé": "Protection de bras armé(s)", diff --git a/src/main/webapp/src/pages/competition/CompetitionEdit.jsx b/src/main/webapp/src/pages/competition/CompetitionEdit.jsx index 064dcf8..23fb7db 100644 --- a/src/main/webapp/src/pages/competition/CompetitionEdit.jsx +++ b/src/main/webapp/src/pages/competition/CompetitionEdit.jsx @@ -56,7 +56,7 @@ export function CompetitionEdit() { {data.id !== null && } {data.id !== null && (data.system === "SAFCA" || data.system === "INTERNAL") && @@ -219,9 +219,21 @@ function Content({data}) { const [registerMode, setRegisterMode] = useState(data.registerMode || "FREE"); const [modaleState, setModaleState] = useState({}) const [presets, setPresets] = useState(data.presets || []); + const [cats, setCats] = useState(data.requiredWeight || []) const [presetChange, setPresetChange] = useState(false) const {t} = useTranslation(); + const setCat = (e, cat) => { + if (e.target.checked) { + if (!cats.includes(cat)) { + setCats([...cats, cat]) + } + } else { + setCats(cats.filter(c => c !== cat)) + } + } + const isCatSelected = (cat) => cats.includes(cat) + const handleSubmit = (event) => { event.preventDefault(); @@ -243,6 +255,7 @@ function Content({data}) { out['endRegister'] = event.target.endRegister?.value out['registerMode'] = registerMode out['presets'] = presets + out['requiredWeight'] = cats if (out['registerMode'] === "HELLOASSO") { out['data3'] = event.target.data3?.value @@ -444,6 +457,16 @@ function Content({data}) { defaultValue={data.endRegister ? data.endRegister.substring(0, 16) : ''}/> +
+ {t('poidsDemandéPour')} + {CatList.map((cat, index) =>
+ setCat(e, cat)}/> + +
)} +
+
{t('comp.ha.text1')}
diff --git a/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx b/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx index 04638b7..c729efa 100644 --- a/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx +++ b/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx @@ -1,4 +1,4 @@ -import {useNavigate, useParams, useSearchParams} from "react-router-dom"; +import {useNavigate, useParams} from "react-router-dom"; import {LoadingProvider, useLoadingSwitcher} from "../../hooks/useLoading.jsx"; import {useFetch} from "../../hooks/useFetch.js"; import {AxiosError} from "../../components/AxiosError.jsx"; @@ -13,6 +13,7 @@ import "./CompetitionRegisterAdmin.css" import * as XLSX from "xlsx-js-style"; import {useCountries} from "../../hooks/useCountries.jsx"; import {Trans, useTranslation} from "react-i18next"; +import {Checkbox} from "../../components/MemberCustomFiels.jsx"; export function CompetitionRegisterAdmin({source}) { const {id} = useParams() @@ -21,12 +22,14 @@ export function CompetitionRegisterAdmin({source}) { const [clubFilter, setClubFilter] = useState("") const [catAgeFilter, setCatAgeFilter] = useState("") const [catFilter, setCatFilter] = useState(-1) + const [filterNotWeight, setFilterNotWeight] = useState(false) const [modalState, setModalState] = useState({}) const {t} = useTranslation(); const setLoading = useLoadingSwitcher() const {data, error} = useFetch(`/competition/${id}/register/${source}`, setLoading, 1) const {data: data2, error: error2} = useFetch(`/competition/${id}/categories`, setLoading, 1) + const {data: data3} = useFetch(`/competition/${id}?light=true`, setLoading, 1) const sortName = (a, b) => { if (a.data.fname === b.data.fname) return a.data.lname.localeCompare(b.data.lname); @@ -66,8 +69,13 @@ export function CompetitionRegisterAdmin({source}) { (clubFilter.length === 0 || s.data.club.name === clubFilter) && (catAgeFilter.length === 0 || s.data.categorie === catAgeFilter) - && (catFilter === -1 || s.data.categoriesInscrites.includes(catFilter)))} - data2={data2} dispatch={dispatch} id={id} setModalState={setModalState} source={source}/> + && (catFilter === -1 || s.data.categoriesInscrites.includes(catFilter)) + && (!filterNotWeight || (data3?.requiredWeight.includes(s.data.categorie) && ( + (source === "admin" && (s.data.weightReal === "" || s.data.weightReal === null)) || + (source !== "admin" && (s.data.weight === "" || s.data.weight === null)) + ))) + )} + data2={data2} data3={data3} dispatch={dispatch} id={id} setModalState={setModalState} source={source}/>
: error ? : }
@@ -86,14 +94,16 @@ export function CompetitionRegisterAdmin({source}) {
{t('filtre')}
+ setCatFilter={setCatFilter} catAgeFilter={catAgeFilter} setCatAgeFilter={setCatAgeFilter} + filterNotWeight={filterNotWeight} setFilterNotWeight={setFilterNotWeight} source={source}/>
{source === "admin" && } - + } @@ -336,7 +346,7 @@ function CategoriesList({error2, availableCats, fistCatInput, categories, setCat } -function Modal({data2, error2, sendRegister, modalState, setModalState, source}) { +function Modal({data2, data3, error2, sendRegister, modalState, setModalState, source}) { const country = useCountries('fr') const {t} = useTranslation(); const closeBtn = useRef(null); @@ -349,6 +359,7 @@ function Modal({data2, error2, sendRegister, modalState, setModalState, source}) const [fname, setFname] = useState("") const [lname, setLname] = useState("") const [weight, setWeight] = useState("") + const [weightReal, setWeightReal] = useState("") const [cat, setCat] = useState(0) const [gcat, setGCat] = useState("") const [club, setClub] = useState("") @@ -363,6 +374,7 @@ function Modal({data2, error2, sendRegister, modalState, setModalState, source}) setFname(modalState?.fname ? modalState.fname : "") setLname(modalState?.lname ? modalState.lname : "") setWeight(modalState?.weight ? modalState.weight : "") + setWeightReal(modalState?.weightReal ? modalState.weightReal : "") setCat(modalState?.overCategory ? modalState.overCategory : 0) setEditMode(modalState?.licence || (modalState.fname && modalState.lname)) setLockEdit(modalState?.lockEdit === undefined ? false : modalState.lockEdit) @@ -398,6 +410,7 @@ function Modal({data2, error2, sendRegister, modalState, setModalState, source}) fname: fname.trim(), lname: lname.trim(), weight: weight, + weightReal: weightReal, overCategory: cat, lockEdit: lockEdit, categoriesInscrites: categories, @@ -517,8 +530,14 @@ function Modal({data2, error2, sendRegister, modalState, setModalState, source})
{t('comp.modal.poids')} - setWeight(e.target.value)}/> + {source === "admin" && {t('comp.modal.annoncé')}} + setWeight(e.target.value)}/> + {source === "admin" && <>{t('comp.modal.pesé')} + setWeightReal(e.target.value)}/>}