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 4868c3a..0e7d596 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java @@ -10,6 +10,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.ArrayList; +import java.util.List; + @Getter @Setter @AllArgsConstructor @@ -40,6 +43,22 @@ public class CompetitionGuestModel implements CombModel { Integer weight = null; + @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) + @JoinTable( + name = "groupe_membre", + joinColumns = @JoinColumn(name = "groupe_id"), + inverseJoinColumns = @JoinColumn(name = "membre_id") + ) + List comb = new ArrayList<>(); + + @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) + @JoinTable( + name = "groupe_guest", + joinColumns = @JoinColumn(name = "groupe_id"), + inverseJoinColumns = @JoinColumn(name = "guest_id") + ) + List guest = new ArrayList<>(); + public CompetitionGuestModel(String s) { this.fname = s.substring(0, s.indexOf(" ")); this.lname = s.substring(s.indexOf(" ") + 1); diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java index 4126db2..8083b58 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java @@ -9,6 +9,10 @@ import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.AllArgsConstructor; import lombok.Data; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + @Data @AllArgsConstructor @RegisterForReflection @@ -23,6 +27,7 @@ public class CombEntity { String country; int overCategory; Integer weight; + List teamMembers; public static CombEntity fromModel(MembreModel model) { if (model == null) @@ -31,7 +36,7 @@ public class CombEntity { return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(), model.getClub() == null ? null : model.getClub().getClubId(), model.getClub() == null ? "Sans club" : model.getClub().getName(), model.getGenre(), model.getCountry(), - 0, null); + 0, null, new ArrayList<>()); } @@ -40,7 +45,9 @@ public class CombEntity { return null; return new CombEntity(model.getId() * -1, model.getLname(), model.getFname(), model.getCategorie(), null, - model.getClub(), model.getGenre(), model.getCountry(), 0, model.getWeight()); + model.getClub(), model.getGenre(), model.getCountry(), 0, model.getWeight(), + Stream.concat(model.getComb().stream().map(CombEntity::fromModel), + model.getGuest().stream().map(CombEntity::fromModel)).toList()); } public static CombEntity fromModel(RegisterModel registerModel) { @@ -51,6 +58,6 @@ public class CombEntity { return new CombEntity(model.getId(), model.getLname(), model.getFname(), registerModel.getCategorie(), registerModel.getClub2() == null ? null : registerModel.getClub2().getClubId(), registerModel.getClub2() == null ? "Sans club" : registerModel.getClub2().getName(), model.getGenre(), - model.getCountry(), registerModel.getOverCategory(), registerModel.getWeight()); + model.getCountry(), registerModel.getOverCategory(), registerModel.getWeight(), new ArrayList<>()); } } 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 a56026f..03faff3 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java @@ -325,7 +325,10 @@ public class CompetitionService { })) .chain(model -> { model.setFname(data.getFname()); - model.setLname(data.getLname()); + if (data.getLname().equals("__team")) + model.setLname("_team"); + else + model.setLname(data.getLname()); model.setGenre(data.getGenre()); model.setClub(data.getClub()); model.setCountry(data.getCountry()); @@ -467,7 +470,8 @@ public class CompetitionService { .call(cm -> membreService.getById(combId) .invoke(Unchecked.consumer(model -> { if (model == null) - throw new DNotFoundException(String.format(trad.t("le.membre.n.existe.pas"), combId)); + throw new DNotFoundException( + String.format(trad.t("le.membre.n.existe.pas"), combId)); if (!securityCtx.isInClubGroup(model.getClub().getId())) throw new DForbiddenException(); }))) diff --git a/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java b/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java index d5246f3..e346295 100644 --- a/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java +++ b/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java @@ -48,6 +48,9 @@ public class CompetitionWS { @Inject RCardboard rCardboard; + @Inject + RTeam rTeam; + @Inject SecurityCtx securityCtx; @@ -91,6 +94,7 @@ public class CompetitionWS { getWSReceiverMethods(RCategorie.class, rCategorie); getWSReceiverMethods(RRegister.class, rRegister); getWSReceiverMethods(RCardboard.class, rCardboard); + getWSReceiverMethods(RTeam.class, rTeam); executor = notifyExecutor; } diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RTeam.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RTeam.java new file mode 100644 index 0000000..54015fd --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RTeam.java @@ -0,0 +1,137 @@ +package fr.titionfire.ffsaf.ws.recv; + +import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; +import fr.titionfire.ffsaf.data.model.RegisterModel; +import fr.titionfire.ffsaf.data.repository.CompetitionGuestRepository; +import fr.titionfire.ffsaf.data.repository.CompetitionRepository; +import fr.titionfire.ffsaf.data.repository.RegisterRepository; +import fr.titionfire.ffsaf.domain.entity.CombEntity; +import fr.titionfire.ffsaf.domain.service.TradService; +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; +import fr.titionfire.ffsaf.utils.Pair; +import fr.titionfire.ffsaf.ws.PermLevel; +import fr.titionfire.ffsaf.ws.send.SSRegister; +import io.quarkus.hibernate.reactive.panache.Panache; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@WithSession +@ApplicationScoped +@RegisterForReflection +public class RTeam { + + @Inject + TradService trad; + + @Inject + CompetitionRepository competitionRepository; + + @Inject + RegisterRepository registerRepository; + + @Inject + CompetitionGuestRepository competitionGuestRepository; + + @WSReceiver(code = "setTeam", permission = PermLevel.ADMIN) + public Uni setTeam(WebSocketConnection connection, TeamData data) { + return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult() + .chain(cm -> registerRepository.list("membre.id IN ?1 AND competition = ?2", + data.members.stream().filter(id -> id >= 0).toList(), cm) + .chain(l -> competitionGuestRepository.list("id IN ?1", + data.members.stream().filter(id -> id < 0).map(i -> i * -1).toList()) + .map(l2 -> new Pair<>(l, l2))) + .chain(pair -> + competitionGuestRepository.find("fname = ?1 AND lname = ?2 AND competition = ?3", + data.name, "__team", cm).firstResult() + .chain(team -> { + if (pair.getKey().isEmpty() && pair.getValue().isEmpty()) { + if (team != null) { + CompetitionGuestModel finalTeam1 = team; + SSRegister.sendRegisterRemove(connection, finalTeam1.getId() * -1); + return Panache.withTransaction( + () -> competitionGuestRepository.delete(finalTeam1)) + .replaceWith((CombEntity) null); + } else + return Uni.createFrom().item((CombEntity) null); + } + + if (team == null) { + // Create new team + team = new CompetitionGuestModel(); + team.setFname(data.name); + team.setLname("__team"); + team.setCompetition(cm); + team.setClub("Team"); + team.setGenre(Genre.NA); + } else { + team.getComb().clear(); + team.getGuest().clear(); + } + + team.setCategorie(Stream.concat( + pair.getKey().stream().map(RegisterModel::getCategorie2), + pair.getValue().stream().map(CompetitionGuestModel::getCategorie)) + .map(Enum::ordinal) + .max(Integer::compareTo) + .map(i -> Categorie.values()[i]).orElse(Categorie.SENIOR1)); + + List s = Stream.concat( + pair.getKey().stream().map(RegisterModel::getWeight), + pair.getValue().stream().map(CompetitionGuestModel::getWeight)) + .filter(Objects::nonNull).toList(); + if (s.isEmpty()) { + team.setWeight(null); + } else if (s.size() == 1) { + team.setWeight(s.get(0)); + } else { + team.setWeight((int) s.stream().mapToInt(Integer::intValue) + .average() + .orElse(0)); + } + + team.setCountry(Stream.concat( + pair.getKey().stream().map(m -> m.getMembre().getCountry()), + pair.getValue().stream().map(CompetitionGuestModel::getCountry)) + .filter(Objects::nonNull) + .map(String::toUpperCase) + .collect(Collectors.groupingBy( + e -> e, // Classer par élément + Collectors.counting() // Compter les occurrences + )) + .entrySet() + .stream() + .max(Map.Entry.comparingByValue()) + .map(Map.Entry::getKey) + .orElse("FR")); + + team.getComb().addAll( + pair.getKey().stream().map(RegisterModel::getMembre).toList()); + team.getGuest().addAll(pair.getValue()); + + CompetitionGuestModel finalTeam = team; + return Panache.withTransaction( + () -> competitionGuestRepository.persistAndFlush(finalTeam)) + .map(CombEntity::fromModel); + })) + ) + .invoke(combEntity -> { + if (combEntity != null) + SSRegister.sendRegister(connection, combEntity); + }); + } + + @RegisterForReflection + public record TeamData(List members, String name) { + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/SSRegister.java b/src/main/java/fr/titionfire/ffsaf/ws/send/SSRegister.java new file mode 100644 index 0000000..e34eac1 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/SSRegister.java @@ -0,0 +1,16 @@ +package fr.titionfire.ffsaf.ws.send; + +import fr.titionfire.ffsaf.domain.entity.CombEntity; +import fr.titionfire.ffsaf.ws.CompetitionWS; +import io.quarkus.websockets.next.WebSocketConnection; + +public class SSRegister { + + public static void sendRegister(WebSocketConnection connection, CombEntity combEntity) { + CompetitionWS.sendNotifyToOtherEditor(connection, "sendRegister", combEntity); + } + + public static void sendRegisterRemove(WebSocketConnection connection, Long combId) { + CompetitionWS.sendNotifyToOtherEditor(connection, "sendRegisterRemove", combId); + } +} diff --git a/src/main/webapp/public/locales/en/cm.json b/src/main/webapp/public/locales/en/cm.json index b21846e..5e0df67 100644 --- a/src/main/webapp/public/locales/en/cm.json +++ b/src/main/webapp/public/locales/en/cm.json @@ -1,11 +1,13 @@ { "--SélectionnerUnCombattant--": "-- Select a fighter --", "--Tous--": "-- All --", + "PourLéquipe": "for the team", "actuel": "Current", "administration": "Administration", "adresseDuServeur": "Server address", "ajouter": "Add", "ajouterDesCombattants": "Add fighters", + "ajouterUneTeam": "Add team", "attention": "Warning", "aucuneConfigurationObs": "No OBS configuration found, please import one", "bleu": "Blue", @@ -69,6 +71,7 @@ "neRienConserver": "Keep nothing", "no": "No.", "nom": "Name", + "nomDeLéquipe": "team name", "nomDesZonesDeCombat": "Combat zone names <1>(separated by ';')", "nouvelle...": "New...", "obs.préfixDesSources": "Source prefix", @@ -122,8 +125,12 @@ "toast.updateTrees.init.success": "Trees created!", "toast.updateTrees.pending": "Updating tournament trees...", "toast.updateTrees.success": "Trees updated!", + "toast.team.update.error": "Error while updating team", + "toast.team.update.pending": "Updating team...", + "toast.team.update.success": "Team updated!", "tournoi": "Tournament", "tournois": "Tournaments", + "team": "Team", "tousLesMatchs": "All matches", "toutConserver": "Keep all", "ttm.admin.obs": "Short click: Download resources. Long click: Create OBS configuration", diff --git a/src/main/webapp/public/locales/fr/cm.json b/src/main/webapp/public/locales/fr/cm.json index 6f620c6..4cc89af 100644 --- a/src/main/webapp/public/locales/fr/cm.json +++ b/src/main/webapp/public/locales/fr/cm.json @@ -1,11 +1,13 @@ { "--SélectionnerUnCombattant--": "-- Sélectionner un combattant --", "--Tous--": "-- Tous --", + "PourLéquipe": " pour l'équipe", "actuel": "Actuel", "administration": "Administration", "adresseDuServeur": "Adresse du serveur", "ajouter": "Ajouter", "ajouterDesCombattants": "Ajouter des combattants", + "ajouterUneTeam": "Ajouter une équipe", "attention": "Attention", "aucuneConfigurationObs": "Aucune configuration OBS trouvée, veuillez en importer une", "bleu": "Bleu", @@ -69,6 +71,7 @@ "neRienConserver": "Ne rien conserver", "no": "N°", "nom": "Nom", + "nomDeLéquipe": "Nom de l'équipe", "nomDesZonesDeCombat": "Nom des zones de combat <1>(séparée par des ';')", "nouvelle...": "Nouvelle...", "obs.préfixDesSources": "Préfix des sources", @@ -122,8 +125,12 @@ "toast.updateTrees.init.success": "Arbres créés !", "toast.updateTrees.pending": "Mise à jour des arbres du tournoi...", "toast.updateTrees.success": "Arbres mis à jour !", + "toast.team.update.error": "Erreur lors de la mise à jour de l'équipe", + "toast.team.update.pending": "Mise à jour de l'équipe...", + "toast.team.update.success": "Équipe mise à jour !", "tournoi": "Tournoi", "tournois": "Tournois", + "team": "Équipe", "tousLesMatchs": "Tous les matchs", "toutConserver": "Tout conserver", "ttm.admin.obs": "Clique court : Télécharger les ressources. Clique long : Créer la configuration obs", diff --git a/src/main/webapp/src/hooks/useComb.jsx b/src/main/webapp/src/hooks/useComb.jsx index aa62ac0..7ddf0a5 100644 --- a/src/main/webapp/src/hooks/useComb.jsx +++ b/src/main/webapp/src/hooks/useComb.jsx @@ -23,6 +23,7 @@ function reducer(state, action) { lname: action.payload.data.lname, genre: action.payload.data.genre, country: action.payload.data.country, + teamMembers: action.payload.data.teamMembers, }) if (state[comb.id] === undefined || !compareCombs(comb, state[comb.id])) { //console.debug("Updating comb", comb); @@ -41,6 +42,7 @@ function reducer(state, action) { lname: e.lname, genre: e.genre, country: e.country, + teamMembers: e.teamMembers, } }); @@ -71,7 +73,7 @@ function WSListener({dispatch}) { useEffect(() => { const sendRegister = ({data}) => { - dispatch({type: 'SET_ALL', payload: {source: "register", data: data}}); + dispatch({type: 'SET_COMB', payload: {source: "register", data: data}}); } dispatchWS({type: 'addListener', payload: {callback: sendRegister, code: 'sendRegister'}}) @@ -110,6 +112,8 @@ export function CombName({combId}) { const {getComb} = useCombs(); const comb = getComb(combId, null); if (comb) { + if (comb.lname === "__team") + return <>{comb.fname} return <>{comb.fname} {comb.lname} } else { return <>[Comb #{combId}] diff --git a/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx b/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx index 9b5bcc3..a2e301b 100644 --- a/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMAdmin.jsx @@ -500,15 +500,15 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) { newTrees.push(trees2.at(i)); } - toast.promise(sendRequest('updateTrees', {categoryId: state.id, trees: newTrees}), getToastMessage("toast.updateTrees") + toast.promise(sendRequest('updateTrees', {categoryId: state.id, trees: newTrees}), getToastMessage("toast.updateTrees", "cm") ).then(__ => { - toast.promise(sendRequest('updateCategory', newData), getToastMessage("toast.updateCategory")) + toast.promise(sendRequest('updateCategory', newData), getToastMessage("toast.updateCategory", "cm")) }) } }) confirmRef.current.click(); } else { - toast.promise(sendRequest('updateCategory', newData), getToastMessage("toast.updateCategory")) + toast.promise(sendRequest('updateCategory', newData), getToastMessage("toast.updateCategory", "cm")) } } @@ -535,13 +535,13 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) { name: name.trim(), liceName: lice.trim(), type: nType - }), getToastMessage("toast.createCategory") + }), getToastMessage("toast.createCategory", "cm") ).then(id => { if (tournoi) { const trees = build_tree(size, loserMatch) console.log("Creating trees for new category:", trees); - toast.promise(sendRequest('updateTrees', {categoryId: id, trees: trees}), getToastMessage("toast.updateTrees.init") + toast.promise(sendRequest('updateTrees', {categoryId: id, trees: trees}), getToastMessage("toast.updateTrees.init", "cm") ).finally(() => setCatId(id)) } else { setCatId(id); @@ -640,7 +640,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) { title: t('confirm4.title'), message: t('confirm4.msg', {name: state.name}), confirm: () => { - toast.promise(sendRequest('deleteCategory', state.id), getToastMessage("toast.deleteCategory") + toast.promise(sendRequest('deleteCategory', state.id), getToastMessage("toast.deleteCategory", "cm") ).then(() => setCatId(null)); } }) diff --git a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx index db2cc35..d2f4a71 100644 --- a/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx +++ b/src/main/webapp/src/pages/competition/editor/CMTMatchPanel.jsx @@ -403,7 +403,7 @@ function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_}) { 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}), getToastMessage("toast.updateMatchScore")); + toast.promise(sendRequest('updateMatchScore', {matchId: matchId, ...newScore}), getToastMessage("toast.updateMatchScore", "cm")); } return () => menuActions.current.saveScore = undefined; }, [matchId]) diff --git a/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx b/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx index 9840be8..67667a2 100644 --- a/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx +++ b/src/main/webapp/src/pages/competition/editor/CategoryAdminContent.jsx @@ -170,6 +170,7 @@ function AddComb({groups, setGroups, removeGroup, menuActions}) { const combDispatch = useCombsDispatch() const {dispatch} = useWS() const [modalId, setModalId] = useState(null) + const [modalMode, setModalMode] = useState(false) const {t} = useTranslation("cm"); useEffect(() => { @@ -220,13 +221,17 @@ function AddComb({groups, setGroups, removeGroup, menuActions}) { return <> + + @@ -247,10 +252,13 @@ function GroupsList({groups, setModalId}) { const groups2 = groups.map(g => { const comb = getComb(g.id); - return {...g, name: comb ? comb.fname + " " + comb.lname : ""}; + return {...g, name: comb ? comb.fname + " " + comb.lname : "", teamMembers: comb ? comb.teamMembers : []}; }).sort((a, b) => { - if (a.poule !== b.poule) + if (a.poule !== b.poule) { + if (a.poule === '-') return 1; + if (b.poule === '-') return -1; return a.poule.localeCompare(b.poule); + } return a.name.localeCompare(b.name); }).reduce((acc, curr) => { const poule = curr.poule; @@ -264,12 +272,20 @@ function GroupsList({groups, setModalId}) { return <> {Object.keys(groups2).map((poule) => (
-
{poule !== '-' ? (t('poule') +" : " + poule) : t('sansPoule')}
+
{poule !== '-' ? (t('poule') + " : " + poule) : t('sansPoule')}
    {groups2[poule].map((comb) => (
  1. setModalId(comb.id)}> -
    +
    + + {comb.teamMembers.length > 0 && <> + {comb.teamMembers.map((m) => ( +
    + +
    ))} + } +
    {comb.poule}
  2. ) )} @@ -369,12 +385,12 @@ function ListMatch({cat, matches, groups, reducer}) { const {newMatch, matchOrderToUpdate, matchPouleToUpdate} = createMatch(cat, matchesToKeep, groups.filter(g => g.poule !== '-')) toast.promise(sendRequest("recalculateMatch", { - categorie: cat.id, - newMatch, - matchOrderToUpdate: Object.fromEntries(matchOrderToUpdate), - matchPouleToUpdate: Object.fromEntries(matchPouleToUpdate), - matchesToRemove: matchesToRemove.map(m => m.id) - }), getToastMessage("toast.matchs.create")) + categorie: cat.id, + newMatch, + matchOrderToUpdate: Object.fromEntries(matchOrderToUpdate), + matchPouleToUpdate: Object.fromEntries(matchPouleToUpdate), + matchesToRemove: matchesToRemove.map(m => m.id) + }), getToastMessage("toast.matchs.create", "ns")) .finally(() => { console.log("Finished creating matches"); }) diff --git a/src/main/webapp/src/pages/competition/editor/SelectCombModalContent.jsx b/src/main/webapp/src/pages/competition/editor/SelectCombModalContent.jsx index a247106..7bf0115 100644 --- a/src/main/webapp/src/pages/competition/editor/SelectCombModalContent.jsx +++ b/src/main/webapp/src/pages/competition/editor/SelectCombModalContent.jsx @@ -1,9 +1,10 @@ import {useCountries} from "../../../hooks/useCountries.jsx"; import {useEffect, useReducer, useState} from "react"; -import {CatList, getCatName} from "../../../utils/Tools.js"; +import {CatList, getCatName, getToastMessage} from "../../../utils/Tools.js"; import {CombName} from "../../../hooks/useComb.jsx"; import {useWS} from "../../../hooks/useWS.jsx"; import {useTranslation} from "react-i18next"; +import {toast} from "react-toastify"; function SelectReducer(state, action) { switch (action.type) { @@ -53,14 +54,14 @@ function SelectReducer(state, action) { } } -export function SelectCombModalContent({data, setGroups}) { +export function SelectCombModalContent({data, setGroups, teamMode = false}) { const country = useCountries('fr') const {t} = useTranslation("cm"); - const {dispatch} = useWS() + const {sendRequest, dispatch} = useWS() const [dispo, dispoReducer] = useReducer(SelectReducer, {}) const [select, selectReducer] = useReducer(SelectReducer, {}) - const [targetGroupe, setTargetGroupe] = useState("A") + const [targetGroupe, setTargetGroupe] = useState("1") const [search, setSearch] = useState("") const [country_, setCountry_] = useState("") const [club, setClub] = useState("") @@ -68,12 +69,27 @@ export function SelectCombModalContent({data, setGroups}) { const [cat, setCat] = useState(-1) const [weightMin, setWeightMin] = useState(0) const [weightMax, setWeightMax] = useState(0) + const [team, setTeam] = useState(false) + const [teamName, setTeamName] = useState(""); const handleSubmit = (e) => { e.preventDefault(); - setGroups(prev => [...prev.filter(d => select[d.id] === undefined), ...Object.keys(select).map(id => { - return {id: Number(id), poule: targetGroupe} - })]) + if (teamMode) { + toast.promise( + sendRequest('setTeam', { + name: teamName, + members: [...Object.keys(select).map(id => Number(id))] + }), getToastMessage("toast.team.update", "cm")) + .then(res => { + if (res && res.id) { + setGroups(prev => [...prev.filter(d => d.id !== Number(res.id)), {id: Number(res.id), poule: targetGroupe}]) + } + }) + } else { + setGroups(prev => [...prev.filter(d => select[d.id] === undefined), ...Object.keys(select).map(id => { + return {id: Number(id), poule: targetGroupe} + })]) + } dispoReducer({type: 'REMOVE_ALL'}) selectReducer({type: 'REMOVE_ALL'}) @@ -101,6 +117,18 @@ export function SelectCombModalContent({data, setGroups}) { dispoReducer({type: 'ADD_ALL', payload: data.map(d => d.id).filter(id => !selectedIds.includes(id))}) }, [data]) + useEffect(() => { + if (data == null) + return + const teamIds = data.filter(d => d.teamMembers != null && d.teamMembers.length > 0).map(t => String(t.id)); + if (teamMode) { + dispoReducer({type: 'REMOVE_IN', payload: teamIds}); + selectReducer({type: 'REMOVE_IN', payload: teamIds}); + } else { + dispoReducer({type: 'ADD_ALL', payload: teamIds}); + } + }, [teamMode]) + function applyFilter(dataIn, dataOut) { Object.keys(dataIn).forEach((id) => { const comb = data.find(d => d.id === Number(id)); @@ -113,7 +141,9 @@ export function SelectCombModalContent({data, setGroups}) { && (gender.H && comb.genre === 'H' || gender.F && comb.genre === 'F' || gender.NA && comb.genre === 'NA') && (cat === -1 || cat === 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)) { + && (weightMax === 0 || comb.weight !== null && comb.weight <= weightMax) + && (teamMode && (comb.teamMembers == null || comb.teamMembers.length === 0) || !teamMode + && ((comb.teamMembers == null || comb.teamMembers.length === 0) !== team))) { dataOut[id] = dataIn[id]; } } @@ -155,7 +185,7 @@ export function SelectCombModalContent({data, setGroups}) { return <>
    -

    {t('select.sélectionnerDesCombatants')}

    +

    {t('select.sélectionnerDesCombatants')}{teamMode && (t('PourLéquipe'))}

    @@ -225,6 +255,16 @@ export function SelectCombModalContent({data, setGroups}) { })}
    + {!teamMode &&
    + +
    +
    + setTeam(e.target.checked)}/> + +
    +
    +
    }
    @@ -277,13 +317,19 @@ export function SelectCombModalContent({data, setGroups}) {
    - - + + setTeamName(e.target.value)}/> + } + + { - if (/^[a-zA-Z0-9]$/.test(e.target.value)) + if (/^[a-zA-Z0-9]?/.test(e.target.value)) setTargetGroupe(e.target.value) }}/> - +
    } diff --git a/src/main/webapp/src/utils/Tools.js b/src/main/webapp/src/utils/Tools.js index a91cd3a..c9a6d56 100644 --- a/src/main/webapp/src/utils/Tools.js +++ b/src/main/webapp/src/utils/Tools.js @@ -19,6 +19,10 @@ export function isClubAdmin(userinfo) { export const errFormater = (data, msg) => { + if (!data) + return msg + if (!data.response) + return `${msg} (😕 ${data.message})` if (typeof data.response.data === 'string' || data.response.data instanceof String) return `${msg} (${data.response.statusText}: ${data.response.data}) 😕` return `${msg} (${data.response.statusText}: ${JSON.stringify(data.response.data)}) 😕` @@ -107,13 +111,13 @@ export function getCatName(cat) { } } -export function getToastMessage(msgKey) { +export function getToastMessage(msgKey, ns = 'common') { return { - pending: i18n.t(msgKey + '.pending'), - success: i18n.t(msgKey + '.success'), + pending: i18n.t(msgKey + '.pending', {ns}), + success: i18n.t(msgKey + '.success', {ns}), error: { render({data}) { - return errFormater(data, i18n.t(msgKey + '.error')) + return errFormater(data, i18n.t(msgKey + '.error', {ns})) } } } @@ -148,7 +152,7 @@ export function timePrint(time, negSign = false) { if (time === null || time === undefined) return "" const neg = time < 0 - if (neg){ + if (neg) { if (!negSign) return "00:00" time = -time @@ -168,22 +172,22 @@ export function timePrint(time, negSign = false) { } //create full hex -function fullHex (hex) { - let r = hex.slice(1,2); - let g = hex.slice(2,3); - let b = hex.slice(3,4); +function fullHex(hex) { + let r = hex.slice(1, 2); + let g = hex.slice(2, 3); + let b = hex.slice(3, 4); - r = parseInt(r+r, 16); - g = parseInt(g+g, 16); - b = parseInt(b+b, 16); + r = parseInt(r + r, 16); + g = parseInt(g + g, 16); + b = parseInt(b + b, 16); // return {r, g, b} - return { r, g, b }; + return {r, g, b}; } //convert hex to rgb -export function hex2rgb (hex) { - if(hex.length === 4){ +export function hex2rgb(hex) { + if (hex.length === 4) { return fullHex(hex); } @@ -192,5 +196,5 @@ export function hex2rgb (hex) { const b = parseInt(hex.slice(5, 7), 16); // return {r, g, b} - return { r, g, b }; + return {r, g, b}; }