feat: add category registration system to admin
This commit is contained in:
@@ -102,6 +102,7 @@
|
||||
"supprimer": "Delete",
|
||||
"sélectionneLesModesDaffichage": "Select display modes",
|
||||
"sélectionner": "Select",
|
||||
"team": "Team",
|
||||
"terminé": "Finished",
|
||||
"texteCopiéDansLePresse": "Text copied to clipboard! Paste it into an HTML tag on your WordPress.",
|
||||
"toast.createCategory.error": "Error while creating the category",
|
||||
@@ -113,6 +114,9 @@
|
||||
"toast.matchs.create.error": "Error while creating matches.",
|
||||
"toast.matchs.create.pending": "Creating matches in progress...",
|
||||
"toast.matchs.create.success": "Matches created successfully.",
|
||||
"toast.team.update.error": "Error while updating team",
|
||||
"toast.team.update.pending": "Updating team...",
|
||||
"toast.team.update.success": "Team updated!",
|
||||
"toast.updateCategory.error": "Error while updating the category",
|
||||
"toast.updateCategory.pending": "Updating category...",
|
||||
"toast.updateCategory.success": "Category updated!",
|
||||
@@ -125,12 +129,8 @@
|
||||
"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",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"(optionnelle)": "(optional)",
|
||||
"---SansClub---": "--- no club ---",
|
||||
"---TousLesAges---": "--- all ages ---",
|
||||
"---ToutLesClubs---": "--- all clubs ---",
|
||||
"---ToutLesPays---": "--- all countries ---",
|
||||
"---TouteLesCatégories---": "--- all categories ---",
|
||||
@@ -8,6 +9,7 @@
|
||||
"--SélectionnerCatégorie--": "-- Select category --",
|
||||
"1Catégorie": "+1 category",
|
||||
"2Catégorie": "+2 categories",
|
||||
"LesModificationsNontEnregistrer": "/!\\ The changes have not yet been saved, click save /!\\",
|
||||
"activer": "Activate",
|
||||
"admin": "Administration",
|
||||
"administrateur": "Administrator",
|
||||
@@ -81,11 +83,14 @@
|
||||
"ajouterUnClub": "Add a club",
|
||||
"ajouterUnMembre": "Add a member",
|
||||
"all_season": "--- all seasons ---",
|
||||
"arme": "Weapon",
|
||||
"au": "to",
|
||||
"aucun": "None",
|
||||
"aucunMembreSélectionné": "No member selected",
|
||||
"aucuneCatégorieDisponible": "No categories available at this time.",
|
||||
"back": "« back",
|
||||
"blason": "Coat of arms",
|
||||
"bouclier": "Shield",
|
||||
"bureau": "Board",
|
||||
"button.accepter": "Accept",
|
||||
"button.ajouter": "Add",
|
||||
@@ -93,7 +98,6 @@
|
||||
"button.appliquer": "Apply",
|
||||
"button.confirmer": "Confirm",
|
||||
"button.créer": "Create",
|
||||
"button.enregister": "Save",
|
||||
"button.enregistrer": "Save",
|
||||
"button.fermer": "Close",
|
||||
"button.modifier": "Edit",
|
||||
@@ -115,6 +119,7 @@
|
||||
"cat.vétéran2": "Veteran 2",
|
||||
"categorie": "category",
|
||||
"catégorie": "Category",
|
||||
"catégorieàAjouter": "Category to add",
|
||||
"certificatMédical": "Medical certificate",
|
||||
"chargement...": "Loading...",
|
||||
"chargerLexcel": "Load Excel",
|
||||
@@ -249,6 +254,7 @@
|
||||
"compte": "Account",
|
||||
"compétition": "Competition",
|
||||
"configuration": "Configuration",
|
||||
"configurationDeLaCatégorie": "Category configuration",
|
||||
"conserverLancienEmail": "Keep the old email",
|
||||
"contactAdministratif": "Administrative contact",
|
||||
"contactInterne": "Internal contact",
|
||||
@@ -274,6 +280,8 @@
|
||||
"donnéesAdministratives": "Administrative data",
|
||||
"du": "From",
|
||||
"dun": "of a",
|
||||
"duréePause": "Pause duration",
|
||||
"duréeRound": "Round duration",
|
||||
"définirLidDuCompte": "Define account ID",
|
||||
"editionDeL'affiliation": "Editing affiliation",
|
||||
"editionDeLaDemande": "Editing request",
|
||||
@@ -474,6 +482,7 @@
|
||||
"permission": "Permission",
|
||||
"photos": "Photos",
|
||||
"prenom": "First name",
|
||||
"protectionObligatoire": "Mandatory protection",
|
||||
"prénomEtNom": "First and last name",
|
||||
"rechercher": "Search",
|
||||
"rechercher...": "Search...",
|
||||
@@ -494,8 +503,15 @@
|
||||
"role.vise-secrétaire": "Vice-Secretary",
|
||||
"role.vise-trésorier": "Vice-Treasurer",
|
||||
"saison": "Season",
|
||||
"sans": "Without",
|
||||
"secrétariatsDeLice": "Ring secretariats",
|
||||
"selectionner...": "Select...",
|
||||
"shield.buckler": "Buckler",
|
||||
"shield.none": "$t(sans) / $t(nonDéfinie)",
|
||||
"shield.round": "Round",
|
||||
"shield.standard": "Standard",
|
||||
"shield.teardrop": "Teardrop",
|
||||
"siDisponiblePourLaCatégorieDages": "If available for the age category",
|
||||
"siretOuRna": "SIRET or RNA",
|
||||
"stats": "Statistics",
|
||||
"statue": "Statue",
|
||||
@@ -505,6 +521,10 @@
|
||||
"supprimerLeClub.msg": "Are you sure you want to delete this club?",
|
||||
"supprimerLeCompte": "Delete account",
|
||||
"supprimerLeCompte.msg": "Are you sure you want to delete this account?",
|
||||
"sword.none": "$t(sans) / $t(nonDéfinie)",
|
||||
"sword.oneHand": "One hand",
|
||||
"sword.saber": "Saber",
|
||||
"sword.twoHand": "Two hands",
|
||||
"sélectionEnéquipeDeFrance": "Selection in the French team",
|
||||
"sélectionner...": "Select...",
|
||||
"toast.edit.error": "Failed to save changes",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"supprimer": "Supprimer",
|
||||
"sélectionneLesModesDaffichage": "Sélectionne les modes d'affichage",
|
||||
"sélectionner": "Sélectionner",
|
||||
"team": "Équipe",
|
||||
"terminé": "Terminé",
|
||||
"texteCopiéDansLePresse": "Texte copié dans le presse-papier ! Collez-le dans une balise HTML sur votre WordPress.",
|
||||
"toast.createCategory.error": "Erreur lors de la création de la catégorie",
|
||||
@@ -113,6 +114,9 @@
|
||||
"toast.matchs.create.error": "Erreur lors de la création des matchs.",
|
||||
"toast.matchs.create.pending": "Création des matchs en cours...",
|
||||
"toast.matchs.create.success": "Matchs créés avec succès.",
|
||||
"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 !",
|
||||
"toast.updateCategory.error": "Erreur lors de la mise à jour de la catégorie",
|
||||
"toast.updateCategory.pending": "Mise à jour de la catégorie...",
|
||||
"toast.updateCategory.success": "Catégorie mise à jour !",
|
||||
@@ -125,12 +129,8 @@
|
||||
"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",
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"(optionnelle)": "(optionnelle)",
|
||||
"---SansClub---": "--- sans club ---",
|
||||
"---ToutLesClubs---": "--- tout les clubs ---",
|
||||
"---TousLesAges---": "--- tous les ages ---",
|
||||
"---ToutLesClubs---": "--- tous les clubs ---",
|
||||
"---ToutLesPays---": "--- tout les pays ---",
|
||||
"---TouteLesCatégories---": "--- toute les catégories ---",
|
||||
"--NonLicencier--": "-- Non licencier --",
|
||||
"--SélectionnerCatégorie--": "-- Sélectionner catégorie --",
|
||||
"1Catégorie": "+1 catégorie",
|
||||
"2Catégorie": "+2 catégorie",
|
||||
"LesModificationsNontEnregistrer": "/!\\ Les modifications n'ont pas encore été enregistré, cliqué sur enregistrer /!\\",
|
||||
"activer": "Activer",
|
||||
"admin": "Administration",
|
||||
"administrateur": "Administrateur",
|
||||
@@ -81,11 +83,14 @@
|
||||
"ajouterUnClub": "Ajouter un club",
|
||||
"ajouterUnMembre": "Ajouter un membre",
|
||||
"all_season": "--- tout les saisons ---",
|
||||
"arme": "Arme",
|
||||
"au": "au",
|
||||
"aucun": "Aucun",
|
||||
"aucunMembreSélectionné": "Aucun membre sélectionné",
|
||||
"aucuneCatégorieDisponible": "Aucune catégorie disponible pour le moment.",
|
||||
"back": "« retour",
|
||||
"blason": "Blason",
|
||||
"bouclier": "Bouclier",
|
||||
"bureau": "Bureau",
|
||||
"button.accepter": "Accepter",
|
||||
"button.ajouter": "Ajouter",
|
||||
@@ -93,7 +98,6 @@
|
||||
"button.appliquer": "Appliquer",
|
||||
"button.confirmer": "Confirmer",
|
||||
"button.créer": "Créer",
|
||||
"button.enregister": "Enregister",
|
||||
"button.enregistrer": "Enregistrer",
|
||||
"button.fermer": "Fermer",
|
||||
"button.modifier": "Modifier",
|
||||
@@ -115,6 +119,7 @@
|
||||
"cat.vétéran2": "Vétéran 2",
|
||||
"categorie": "categorie",
|
||||
"catégorie": "Catégorie",
|
||||
"catégorieàAjouter": "Catégorie à ajouter",
|
||||
"certificatMédical": "Certificat médical",
|
||||
"chargement...": "Chargement...",
|
||||
"chargerLexcel": "Charger l'Excel",
|
||||
@@ -249,6 +254,7 @@
|
||||
"compte": "Compte",
|
||||
"compétition": "Compétition",
|
||||
"configuration": "Configuration",
|
||||
"configurationDeLaCatégorie": "Configuration de la catégorie",
|
||||
"conserverLancienEmail": "Conserver l'ancien email",
|
||||
"contactAdministratif": "Contact administratif",
|
||||
"contactInterne": "Contact interne",
|
||||
@@ -274,6 +280,8 @@
|
||||
"donnéesAdministratives": "Données administratives",
|
||||
"du": "Du",
|
||||
"dun": "d'un",
|
||||
"duréePause": "Durée pause",
|
||||
"duréeRound": "Durée round",
|
||||
"définirLidDuCompte": "Définir l'id du compte",
|
||||
"editionDeL'affiliation": "Edition de l'affiliation",
|
||||
"editionDeLaDemande": "Edition de la demande ",
|
||||
@@ -474,6 +482,7 @@
|
||||
"permission": "Permission",
|
||||
"photos": "Photos",
|
||||
"prenom": "Prénom",
|
||||
"protectionObligatoire": "Protection obligatoire",
|
||||
"prénomEtNom": "Prénom et nom",
|
||||
"rechercher": "Rechercher",
|
||||
"rechercher...": "Rechercher...",
|
||||
@@ -494,8 +503,15 @@
|
||||
"role.vise-secrétaire": "Vise-Secrétaire",
|
||||
"role.vise-trésorier": "Vise-Trésorier",
|
||||
"saison": "Saison",
|
||||
"sans": "Sans",
|
||||
"secrétariatsDeLice": "Secrétariats de lice",
|
||||
"selectionner...": "Sélectionner...",
|
||||
"shield.buckler": "Bocle",
|
||||
"shield.none": "$t(sans) / $t(nonDéfinie)",
|
||||
"shield.round": "Rond",
|
||||
"shield.standard": "Standard",
|
||||
"shield.teardrop": "Larme",
|
||||
"siDisponiblePourLaCatégorieDages": "Si disponible pour la catégorie d'ages",
|
||||
"siretOuRna": "SIRET ou RNA",
|
||||
"stats": "Statistiques",
|
||||
"statue": "Statue",
|
||||
@@ -505,6 +521,10 @@
|
||||
"supprimerLeClub.msg": "Êtes-vous sûr de vouloir supprimer ce club ?",
|
||||
"supprimerLeCompte": "Supprimer le compte",
|
||||
"supprimerLeCompte.msg": "Êtes-vous sûr de vouloir supprimer ce compte ?",
|
||||
"sword.none": "$t(sans) / $t(nonDéfinie)",
|
||||
"sword.oneHand": "Une main",
|
||||
"sword.saber": "Sabre",
|
||||
"sword.twoHand": "Deux mains",
|
||||
"sélectionEnéquipeDeFrance": "Sélection en équipe de France",
|
||||
"sélectionner...": "Sélectionner...",
|
||||
"toast.edit.error": "Échec de l'enregistrement des modifications",
|
||||
|
||||
99
src/main/webapp/src/components/ProtectionSelector.jsx
Normal file
99
src/main/webapp/src/components/ProtectionSelector.jsx
Normal file
@@ -0,0 +1,99 @@
|
||||
|
||||
const ProtectionSelector = ({mandatoryProtection = 0, setMandatoryProtection = () => {} }) => {
|
||||
const toggle = (bit) => {
|
||||
setMandatoryProtection(v => (v & bit ? v & ~bit : v | bit));
|
||||
};
|
||||
|
||||
const isOn = (bit) => (mandatoryProtection & bit) !== 0;
|
||||
const color = (bit) => (isOn(bit) ? "#4ade80" : "#e5e7eb");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<svg width="200" height="420" viewBox="0 0 160 320">
|
||||
<rect
|
||||
width="160" height="320"
|
||||
fill="#f9fafb00"
|
||||
stroke="#d1d5db"
|
||||
strokeWidth="2"
|
||||
rx="10"
|
||||
/>
|
||||
|
||||
{/* Head */}
|
||||
<ellipse
|
||||
cx="80" cy="35" rx="20" ry="22"
|
||||
fill={color(1)}
|
||||
onClick={() => toggle(1)}
|
||||
/>
|
||||
|
||||
{/* Throat / Neck */}
|
||||
<ellipse
|
||||
cx="80" cy="65" rx="12" ry="8"
|
||||
fill={color(2)}
|
||||
onClick={() => toggle(2)}
|
||||
/>
|
||||
|
||||
{/* Torso */}
|
||||
<ellipse
|
||||
cx="80" cy="118" rx="30" ry="45"
|
||||
fill={color(4)}
|
||||
onClick={() => toggle(4)}
|
||||
/>
|
||||
|
||||
{/* Arms */}
|
||||
<ellipse
|
||||
cx="38" cy="118" rx="12" ry="40"
|
||||
fill={color(8)}
|
||||
onClick={() => toggle(8)}
|
||||
/>
|
||||
<ellipse
|
||||
cx="122" cy="118" rx="12" ry="40"
|
||||
fill={color(8)}
|
||||
onClick={() => toggle(8)}
|
||||
/>
|
||||
|
||||
{/* Hands */}
|
||||
<ellipse
|
||||
cx="38" cy="170" rx="10" ry="12"
|
||||
fill={color(16)}
|
||||
onClick={() => toggle(16)}
|
||||
/>
|
||||
<ellipse
|
||||
cx="122" cy="170" rx="10" ry="12"
|
||||
fill={color(16)}
|
||||
onClick={() => toggle(16)}
|
||||
/>
|
||||
|
||||
{/* Legs */}
|
||||
<ellipse
|
||||
cx="65" cy="230" rx="14" ry="55"
|
||||
fill={color(64)}
|
||||
onClick={() => toggle(64)}
|
||||
/>
|
||||
<ellipse
|
||||
cx="95" cy="230" rx="14" ry="55"
|
||||
fill={color(64)}
|
||||
onClick={() => toggle(64)}
|
||||
/>
|
||||
|
||||
{/* Groin */}
|
||||
<ellipse
|
||||
cx="80" cy="170" rx="20" ry="10"
|
||||
fill={color(32)}
|
||||
onClick={() => toggle(32)}
|
||||
/>
|
||||
|
||||
{/* Feet */}
|
||||
<ellipse
|
||||
cx="65" cy="295" rx="16" ry="8"
|
||||
fill="#f0f0f0"
|
||||
/>
|
||||
<ellipse
|
||||
cx="95" cy="295" rx="16" ry="8"
|
||||
fill="#f0f0f0"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProtectionSelector;
|
||||
@@ -6,12 +6,23 @@ import {CheckField, OptionField, TextField} from "../../components/MemberCustomF
|
||||
import {ClubSelect} from "../../components/ClubSelect.jsx";
|
||||
import {ConfirmDialog} from "../../components/ConfirmDialog.jsx";
|
||||
import {toast} from "react-toastify";
|
||||
import {apiAxios, getToastMessage} from "../../utils/Tools.js";
|
||||
import {useEffect, useReducer, useState} from "react";
|
||||
import {
|
||||
apiAxios,
|
||||
CatList,
|
||||
getCatName, getShieldTypeName,
|
||||
getSwordTypeName,
|
||||
getToastMessage,
|
||||
ShieldList,
|
||||
sortCategories,
|
||||
SwordList,
|
||||
timePrint
|
||||
} from "../../utils/Tools.js";
|
||||
import React, {useEffect, useReducer, useState} from "react";
|
||||
import {SimpleReducer} from "../../utils/SimpleReducer.jsx";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faAdd, faTrashCan} from "@fortawesome/free-solid-svg-icons";
|
||||
import {Trans, useTranslation} from "react-i18next";
|
||||
import ProtectionSelector from "../../components/ProtectionSelector.jsx";
|
||||
|
||||
export function CompetitionEdit() {
|
||||
const {id} = useParams()
|
||||
@@ -190,10 +201,10 @@ function ContentSAFCAAndInternal({data2, type = "SAFCA"}) {
|
||||
}}><FontAwesomeIcon icon={faAdd}/></button>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button type="submit" className="btn btn-primary">{t('button.enregistrer')}</button>
|
||||
<div className="row" style={{marginTop: "1em"}}>
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button type="submit" className="btn btn-primary">{t('button.enregistrer')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -205,6 +216,9 @@ function ContentSAFCAAndInternal({data2, type = "SAFCA"}) {
|
||||
function Content({data}) {
|
||||
const navigate = useNavigate();
|
||||
const [registerMode, setRegisterMode] = useState(data.registerMode || "FREE");
|
||||
const [modaleState, setModaleState] = useState({})
|
||||
const [presets, setPresets] = useState(data.presets || []);
|
||||
const [presetChange, setPresetChange] = useState(false)
|
||||
const {t} = useTranslation();
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
@@ -227,6 +241,7 @@ function Content({data}) {
|
||||
out['startRegister'] = event.target.startRegister?.value
|
||||
out['endRegister'] = event.target.endRegister?.value
|
||||
out['registerMode'] = registerMode
|
||||
out['presets'] = presets
|
||||
|
||||
if (out['registerMode'] === "HELLOASSO") {
|
||||
out['data3'] = event.target.data3?.value
|
||||
@@ -277,14 +292,17 @@ function Content({data}) {
|
||||
toast.promise(
|
||||
apiAxios.post(`/competition`, out), getToastMessage("comp.toast.save")
|
||||
).then(data => {
|
||||
setPresetChange(false)
|
||||
console.log(data.data)
|
||||
if (data.data.id !== undefined)
|
||||
navigate("/competition/" + data.data.id)
|
||||
if (data.data.presets !== undefined)
|
||||
setPresets(data.data.presets)
|
||||
})
|
||||
}
|
||||
|
||||
return <form onSubmit={handleSubmit}>
|
||||
<div className="card mb-4">
|
||||
return <>
|
||||
<form onSubmit={handleSubmit} className="card mb-4">
|
||||
<input name="id" value={data.id || ""} readOnly hidden/>
|
||||
<div className="card-header">{data.id ? t('comp.editionCompétition') : t('comp.créationCompétition')}</div>
|
||||
<div className="card-body text-center">
|
||||
@@ -340,6 +358,40 @@ function Content({data}) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="accordion-item">
|
||||
<h2 className="accordion-header">
|
||||
<button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour"
|
||||
aria-expanded="false" aria-controls="collapseFour">
|
||||
Catégories proposées
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseFour" className="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div className="accordion-body" style={{textAlign: "left"}}>
|
||||
<div className="list-group">
|
||||
{presets.sort((a, b) => a.name.localeCompare(b.name)).map((preset) =>
|
||||
<a key={preset.id} className="list-group-item list-group-item-action" data-bs-toggle="modal"
|
||||
data-bs-target="#catModal" onClick={() => setModaleState(preset)}>
|
||||
<span style={{margin: "0 0.25em 0 0"}}>{preset.name}</span>
|
||||
{preset.categories.sort(sortCategories).map((cat, index) =>
|
||||
<span key={index} className="badge text-bg-secondary"
|
||||
style={{margin: "0 0.25em"}}>{getCatName(cat)}</span>)}
|
||||
</a>)}
|
||||
</div>
|
||||
<div className="row" style={{marginTop: "1em"}}>
|
||||
<div className="col-auto"
|
||||
style={{color: "red"}}>{presetChange && t('LesModificationsNontEnregistrer')}</div>
|
||||
<div className="col" style={{textAlign: "right"}}>
|
||||
<button type="button" className="btn btn-success" data-bs-toggle="modal"
|
||||
data-bs-target="#catModal"
|
||||
onClick={() => setModaleState({id: Math.min(...presets.map(p => p.id), 0) - 1})}>
|
||||
<FontAwesomeIcon icon={faAdd}/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="accordion-item">
|
||||
<h2 className="accordion-header">
|
||||
<button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree"
|
||||
@@ -404,14 +456,162 @@ function Content({data}) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row" style={{marginTop: "1em"}}>
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button type="submit" className="btn btn-primary">{t('button.enregistrer')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="row mb-3">
|
||||
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button type="submit" className="btn btn-primary">{t('button.enregistrer')}</button>
|
||||
<div className="modal fade" id="catModal" tabIndex="-1" aria-labelledby="catModalLabel" aria-hidden="true">
|
||||
<div className="modal-dialog modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
|
||||
<div className="modal-content">
|
||||
<CatModalContent setPresets={setPresets} setPresetChange={setPresetChange} state={modaleState}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
|
||||
function CatModalContent({setPresets, setPresetChange, state}) {
|
||||
const [name, setName] = useState(state.name || "")
|
||||
const [sword, setSword] = useState(state.sword || "NONE")
|
||||
const [shield, setShield] = useState(state.shield || "NONE")
|
||||
const [time, setTime] = useState(timePrint(state.roundDuration || 90000))
|
||||
const [pause, setPause] = useState(timePrint(state.pauseDuration || 60000))
|
||||
const [cats, setCats] = useState(state.categories || [])
|
||||
const [mandatoryProtection, setMandatoryProtection] = useState(state.mandatoryProtection || 33)
|
||||
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setName(state.name || "")
|
||||
setSword(state.sword || "NONE")
|
||||
setShield(state.shield || "NONE")
|
||||
setTime(timePrint(state.roundDuration || 90000))
|
||||
setPause(timePrint(state.pauseDuration || 60000))
|
||||
setCats(state.categories || [])
|
||||
setMandatoryProtection(state.mandatoryProtection || 33)
|
||||
}, [state]);
|
||||
|
||||
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 parseTime = (str) => {
|
||||
const parts = str.split(":").map(part => parseInt(part, 10));
|
||||
if (parts.length === 1) {
|
||||
return parts[0] * 1000;
|
||||
} else if (parts.length === 2) {
|
||||
return (parts[0] * 60 + parts[1]) * 1000;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const out = {
|
||||
id: state.id,
|
||||
name: name,
|
||||
sword: sword,
|
||||
shield: shield,
|
||||
roundDuration: parseTime(time),
|
||||
pauseDuration: parseTime(pause),
|
||||
categories: cats,
|
||||
mandatoryProtection: mandatoryProtection
|
||||
}
|
||||
setPresets(presets => [...presets.filter(p => p.id !== out.id), out])
|
||||
setPresetChange(true)
|
||||
}
|
||||
|
||||
const handleRm = () => {
|
||||
setPresets(presets => presets.filter(p => p.id !== state.id))
|
||||
setPresetChange(true)
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5" id="CategorieModalLabel">{t('configurationDeLaCatégorie')}</h1>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="row">
|
||||
<div className="col-12 col-md-7 mb-3">
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="categorie">{t("nom")}</span>
|
||||
<input type="text" className="form-control" placeholder={t("nom")} name="name"
|
||||
value={name} onChange={e => setName(e.target.value)}/>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text">{t('duréeRound')}</span>
|
||||
<input type="text" className="form-control" placeholder="0" aria-label="Min" value={time}
|
||||
onChange={e => setTime(e.target.value)}/>
|
||||
<span className="input-group-text">(mm:ss)</span>
|
||||
</div>
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text">{t('duréePause')}</span>
|
||||
<input type="text" className="form-control" placeholder="0" aria-label="Min" value={pause}
|
||||
onChange={e => setPause(e.target.value)}/>
|
||||
<span className="input-group-text">(mm:ss)</span>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="sword">{t('arme')}</span>
|
||||
<select className="form-select" aria-label={t('arme')} name="sword" value={sword}
|
||||
onChange={e => setSword(e.target.value)}>
|
||||
{SwordList.map(sword =>
|
||||
<option key={sword} value={sword}>{getSwordTypeName(sword)}</option>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="shield">{t('bouclier')}</span>
|
||||
<select className="form-select" aria-label={t('bouclier')} name="shield" value={shield}
|
||||
onChange={e => setShield(e.target.value)}>
|
||||
{ShieldList.map(shield =>
|
||||
<option key={shield} value={shield}>{getShieldTypeName(shield)}</option>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<label htmlFor="inputState2" className="form-label">
|
||||
{t('catégorie')} :
|
||||
</label>
|
||||
<div className="d-flex flex-wrap">
|
||||
{CatList.map((cat, index) => <div key={index} className="input-group" style={{display: "contents"}}>
|
||||
<div className="input-group-text">
|
||||
<input className="form-check-input mt-0" type="checkbox" id={"catInput" + index} checked={isCatSelected(cat)}
|
||||
aria-label={getCatName(cat)} onChange={e => setCat(e, cat)}/>
|
||||
<label style={{marginLeft: "0.5em"}} htmlFor={"catInput" + index}>{getCatName(cat)}</label>
|
||||
</div>
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-5">
|
||||
<div style={{textAlign: "center"}}>
|
||||
<h6>{t('protectionObligatoire')} :</h6>
|
||||
<ProtectionSelector mandatoryProtection={mandatoryProtection} setMandatoryProtection={setMandatoryProtection}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-danger" data-bs-dismiss="modal" onClick={handleRm}>{t('button.supprimer')}</button>
|
||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('button.fermer')}</button>
|
||||
<button type="button" className="btn btn-primary" data-bs-dismiss="modal" onClick={handleSave}>{t('button.appliquer')}</button>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import {LoadingProvider, useLoadingSwitcher} from "../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../hooks/useFetch.js";
|
||||
import {AxiosError} from "../../components/AxiosError.jsx";
|
||||
import {ThreeDots} from "react-loader-spinner";
|
||||
import {useEffect, useReducer, useRef, useState} from "react";
|
||||
import {apiAxios, CatList, getCatName, getToastMessage} from "../../utils/Tools.js";
|
||||
import React, {useEffect, useReducer, useRef, useState} from "react";
|
||||
import {apiAxios, applyOverCategory, CatList, getCatName, getToastMessage} from "../../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {SimpleReducer} from "../../utils/SimpleReducer.jsx";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
@@ -19,12 +19,14 @@ export function CompetitionRegisterAdmin({source}) {
|
||||
const navigate = useNavigate()
|
||||
const [state, dispatch] = useReducer(SimpleReducer, [])
|
||||
const [clubFilter, setClubFilter] = useState("")
|
||||
const [catFilter, setCatFilter] = useState("")
|
||||
const [catAgeFilter, setCatAgeFilter] = useState("")
|
||||
const [catFilter, setCatFilter] = useState(-1)
|
||||
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 sortName = (a, b) => {
|
||||
if (a.data.fname === b.data.fname) return a.data.lname.localeCompare(b.data.lname);
|
||||
@@ -43,11 +45,11 @@ export function CompetitionRegisterAdmin({source}) {
|
||||
return toast.promise(apiAxios.post(`/competition/${id}/register/${source}`, new_state), getToastMessage("comp.toast.register.add")
|
||||
).then((response) => {
|
||||
if (response.data.error) {
|
||||
return
|
||||
return null;
|
||||
}
|
||||
dispatch({type: 'UPDATE_OR_ADD', payload: {id: response.data.id, data: response.data}})
|
||||
dispatch({type: 'SORT', payload: sortName})
|
||||
document.getElementById("closeModal").click();
|
||||
return response.data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,7 +64,9 @@ export function CompetitionRegisterAdmin({source}) {
|
||||
<div className="col-lg-9">
|
||||
{data ? <div className="">
|
||||
<MakeCentralPanel
|
||||
data={state.filter(s => (clubFilter.length === 0 || s.data.club.name === clubFilter) && (catFilter.length === 0 || s.data.categorie === catFilter))}
|
||||
data={state.filter(s => (clubFilter.length === 0 || s.data.club.name === clubFilter)
|
||||
&& (catAgeFilter.length === 0 || s.data.categorie === catAgeFilter)
|
||||
&& (catFilter === -1 || s.data.categoriesInscrites.includes(catFilter)))}
|
||||
dispatch={dispatch} id={id} setModalState={setModalState} source={source}/>
|
||||
</div> : error ? <AxiosError error={error}/> : <Def/>}
|
||||
</div>
|
||||
@@ -77,36 +81,60 @@ export function CompetitionRegisterAdmin({source}) {
|
||||
onClick={() => setModalState({id: -793548328091516928})}>{t('comp.ajouterUnInvité')}
|
||||
</button>
|
||||
</div>}
|
||||
<QuickAdd sendRegister={sendRegister} source={source}/>
|
||||
<QuickAdd sendRegister={sendRegister} source={source} data2={data2} error2={error2}/>
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">{t('filtre')}</div>
|
||||
<div className="card-body">
|
||||
<FiltreBar data={data} clubFilter={clubFilter} setClubFilter={setClubFilter} catFilter={catFilter}
|
||||
setCatFilter={setCatFilter} source={source}/>
|
||||
<FiltreBar data={data} data2={data2} clubFilter={clubFilter} setClubFilter={setClubFilter} catFilter={catFilter}
|
||||
setCatFilter={setCatFilter} catAgeFilter={catAgeFilter} setCatAgeFilter={setCatAgeFilter} source={source}/>
|
||||
</div>
|
||||
</div>
|
||||
{source === "admin" && <FileOutput data={data}/>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal sendRegister={sendRegister} modalState={modalState} setModalState={setModalState} source={source}/>
|
||||
<Modal data2={data2} error2={error2} sendRegister={sendRegister} modalState={modalState} setModalState={setModalState} source={source}/>
|
||||
</div>
|
||||
}
|
||||
|
||||
function QuickAdd({sendRegister, source}) {
|
||||
function QuickAdd({sendRegister, source, data2, error2}) {
|
||||
const {t} = useTranslation();
|
||||
const [categories, setCategories] = useState([])
|
||||
|
||||
const handleAdd = (licence) => {
|
||||
console.log("Quick add licence: " + licence)
|
||||
|
||||
sendRegister({
|
||||
licence: licence, fname: "", lname: "", weight: "", overCategory: 0, lockEdit: false, id: null
|
||||
licence: licence,
|
||||
fname: "",
|
||||
lname: "",
|
||||
weight: "",
|
||||
overCategory: 0,
|
||||
lockEdit: false,
|
||||
id: null,
|
||||
quick: true,
|
||||
categoriesInscrites: categories
|
||||
})
|
||||
}
|
||||
|
||||
const setCategories_ = (e, catId) => {
|
||||
if (e.target.checked) {
|
||||
if (!categories.includes(catId)) {
|
||||
setCategories([...categories, catId])
|
||||
}
|
||||
} else {
|
||||
setCategories(categories.filter(c => c !== catId))
|
||||
}
|
||||
}
|
||||
|
||||
return <div className="card mb-4">
|
||||
<div className="card-header">{t('comp.ajoutRapide')}</div>
|
||||
<div className="card-body">
|
||||
<div className="d-flex flex-wrap">
|
||||
<label htmlFor="inputState2" className="form-label align-self-center" style={{margin: "0 0.5em 0 0"}}>
|
||||
{t('catégorieàAjouter')}<br/> <small>({t('siDisponiblePourLaCatégorieDages')})</small>
|
||||
</label>
|
||||
<CategoriesList error2={error2} availableCats={data2} categories={categories} setCategories={setCategories_}/>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<span>{t('comp.noDeLicence')}</span>
|
||||
</div>
|
||||
@@ -131,14 +159,14 @@ function QuickAdd({sendRegister, source}) {
|
||||
</button>
|
||||
|
||||
{source === "club" && <LoadingProvider>
|
||||
<SearchMember sendRegister={sendRegister}/>
|
||||
<SearchMember sendRegister={sendRegister} categories={categories}/>
|
||||
</LoadingProvider>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function SearchMember({sendRegister}) {
|
||||
function SearchMember({sendRegister, categories}) {
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/club/members`, setLoading, 1)
|
||||
const [suggestions, setSuggestions] = useState([])
|
||||
@@ -158,7 +186,9 @@ function SearchMember({sendRegister}) {
|
||||
weight: "",
|
||||
overCategory: 0,
|
||||
lockEdit: false,
|
||||
id: null
|
||||
id: null,
|
||||
quick: true,
|
||||
categoriesInscrites: categories
|
||||
})
|
||||
}
|
||||
|
||||
@@ -284,9 +314,33 @@ const AutoCompleteInput = ({suggestions = [], handleAdd}) => {
|
||||
</div>);
|
||||
};
|
||||
|
||||
function Modal({sendRegister, modalState, setModalState, source}) {
|
||||
function CategoriesList({error2, availableCats, fistCatInput, categories, setCategories}) {
|
||||
const {t} = useTranslation();
|
||||
return <>
|
||||
{error2 ? <AxiosError error={error2}/> : <>
|
||||
{availableCats && availableCats.length === 0 && <div>{t('aucuneCatégorieDisponible')}</div>}
|
||||
{availableCats && availableCats.map((cat, index) =>
|
||||
<div key={cat.id} className="input-group"
|
||||
style={{display: "contents"}}>
|
||||
<div className="input-group-text">
|
||||
<input ref={index === 0 ? fistCatInput : undefined} className="form-check-input mt-0" type="checkbox"
|
||||
id={"categoriesInput" + index} checked={categories.includes(cat.id)} aria-label={cat.name}
|
||||
onChange={e => setCategories(e, cat.id)}/>
|
||||
<label style={{marginLeft: "0.5em"}} htmlFor={"categoriesInput" + index}>{cat.name}</label>
|
||||
</div>
|
||||
</div>)}
|
||||
</>}
|
||||
</>
|
||||
}
|
||||
|
||||
function Modal({data2, error2, sendRegister, modalState, setModalState, source}) {
|
||||
const country = useCountries('fr')
|
||||
const {t} = useTranslation();
|
||||
const closeBtn = useRef(null);
|
||||
const licenceInput = useRef(null);
|
||||
const nameInput = useRef(null);
|
||||
const fistCatInput = useRef(null);
|
||||
const submitBtn = useRef(null);
|
||||
|
||||
const [licence, setLicence] = useState("")
|
||||
const [fname, setFname] = useState("")
|
||||
@@ -299,97 +353,125 @@ function Modal({sendRegister, modalState, setModalState, source}) {
|
||||
const [genre, setGenre] = useState("NA")
|
||||
const [editMode, setEditMode] = useState(false)
|
||||
const [lockEdit, setLockEdit] = useState(false)
|
||||
const [categories, setCategories] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(modalState)
|
||||
if (!modalState) {
|
||||
setLicence("")
|
||||
setFname("")
|
||||
setLname("")
|
||||
setWeight("")
|
||||
setCat(0)
|
||||
setEditMode(false)
|
||||
setLockEdit(false)
|
||||
setClub("")
|
||||
setGCat("")
|
||||
setCountry_("FR")
|
||||
setGenre("NA")
|
||||
} else {
|
||||
setLicence(modalState.licence ? modalState.licence : "")
|
||||
setFname(modalState.fname ? modalState.fname : "")
|
||||
setLname(modalState.lname ? modalState.lname : "")
|
||||
setWeight(modalState.weight ? modalState.weight : "")
|
||||
setCat(modalState.overCategory ? modalState.overCategory : 0)
|
||||
setEditMode(modalState.licence || (modalState.fname && modalState.lname))
|
||||
setLockEdit(modalState.lockEdit)
|
||||
setClub(modalState.club ? modalState.club.name : "")
|
||||
setGCat(modalState.categorie ? modalState.categorie : "")
|
||||
setCountry_(modalState.country ? modalState.country : "FR")
|
||||
setGenre(modalState.genre ? modalState.genre : "NA")
|
||||
}
|
||||
setLicence(modalState?.licence ? modalState.licence : "")
|
||||
setFname(modalState?.fname ? modalState.fname : "")
|
||||
setLname(modalState?.lname ? modalState.lname : "")
|
||||
setWeight(modalState?.weight ? modalState.weight : "")
|
||||
setCat(modalState?.overCategory ? modalState.overCategory : 0)
|
||||
setEditMode(modalState?.licence || (modalState.fname && modalState.lname))
|
||||
setLockEdit(modalState?.lockEdit === undefined ? false : modalState.lockEdit)
|
||||
setClub(modalState?.club ? modalState.club.name : "")
|
||||
setGCat(modalState?.categorie ? modalState.categorie : "")
|
||||
setCountry_(modalState?.country ? modalState.country : "FR")
|
||||
setGenre(modalState?.genre ? modalState.genre : "NA")
|
||||
setCategories(modalState?.categoriesInscrites ? modalState.categoriesInscrites : [])
|
||||
|
||||
setTimeout(() => {
|
||||
if (modalState?.id === 0) {
|
||||
licenceInput.current?.focus()
|
||||
} else if (modalState?.id < 0) {
|
||||
nameInput.current?.focus()
|
||||
}
|
||||
}, 450)
|
||||
}, [modalState]);
|
||||
|
||||
return <div className="modal fade" id="registerModal" tabIndex="-1" aria-labelledby="registerLabel"
|
||||
aria-hidden="true">
|
||||
const setCategories_ = (e, catId) => {
|
||||
if (e.target.checked) {
|
||||
if (!categories.includes(catId)) {
|
||||
setCategories([...categories, catId])
|
||||
}
|
||||
} else {
|
||||
setCategories(categories.filter(c => c !== catId))
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
const new_state = {
|
||||
licence: Number.isInteger(licence) ? licence : licence.trim(),
|
||||
fname: fname.trim(),
|
||||
lname: lname.trim(),
|
||||
weight: weight,
|
||||
overCategory: cat,
|
||||
lockEdit: lockEdit,
|
||||
categoriesInscrites: categories,
|
||||
id: modalState.id !== 0 ? modalState.id : null
|
||||
}
|
||||
if (modalState.id < 0) {
|
||||
new_state.licence = -1
|
||||
new_state.categorie = gcat
|
||||
new_state.club = club
|
||||
new_state.country = country_
|
||||
new_state.genre = genre
|
||||
}
|
||||
sendRegister(new_state)
|
||||
.then(data => {
|
||||
if (!data) return;
|
||||
setModalState(data)
|
||||
if (editMode || data.id < 0) {
|
||||
closeBtn.current.click()
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
if (fistCatInput.current) {
|
||||
fistCatInput.current.focus()
|
||||
} else {
|
||||
submitBtn.current.focus()
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const currenCat = gcat !== "" ? applyOverCategory(gcat, cat) : "";
|
||||
const availableCats = data2 ? (currenCat !== "" ? data2.filter(c => c.categories.includes(currenCat)) : data2).sort((a, b) => a.name.localeCompare(b.name)) : []
|
||||
if (availableCats.length === 0) {
|
||||
if (fistCatInput.current) {
|
||||
fistCatInput.current = null
|
||||
}
|
||||
}
|
||||
|
||||
return <div className="modal fade" id="registerModal" tabIndex="-1" aria-labelledby="registerLabel" aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<form onSubmit={e => {
|
||||
e.preventDefault()
|
||||
const new_state = {
|
||||
licence: Number.isInteger(licence) ? licence : licence.trim(),
|
||||
fname: fname.trim(),
|
||||
lname: lname.trim(),
|
||||
weight: weight,
|
||||
overCategory: cat,
|
||||
lockEdit: lockEdit,
|
||||
id: modalState.id !== 0 ? modalState.id : null
|
||||
}
|
||||
if (modalState.id < 0) {
|
||||
new_state.licence = -1
|
||||
new_state.categorie = gcat
|
||||
new_state.club = club
|
||||
new_state.country = country_
|
||||
new_state.genre = genre
|
||||
}
|
||||
sendRegister(new_state)
|
||||
.then(() => {
|
||||
setModalState(new_state)
|
||||
})
|
||||
}}>
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5"
|
||||
id="registerLabel">{editMode ? t('modification') : t('ajout')} {t('dun')} {modalState.id >= 0 ? t('combattant') : t('invité')}</h1>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{modalState.id < 0 &&
|
||||
<div className="mb-2">{t('comp.modal.text1')}</div>}
|
||||
<div className="card" style={{marginBottom: "1em"}}>
|
||||
<div className="card-header">{modalState.id >= 0 ? t('comp.modal.recherche') : t('comp.modal.information')}</div>
|
||||
<div className="card-body">
|
||||
<div className="row" hidden={modalState.id < 0}>
|
||||
<div className="col">
|
||||
<input type="number" min={0} step={1} className="form-control" placeholder={t("comp.noDeLicence")}
|
||||
name="licence"
|
||||
value={licence} onChange={e => setLicence(e.target.value)} disabled={editMode}/>
|
||||
</div>
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5"
|
||||
id="registerLabel">{editMode ? t('modification') : t('ajout')} {t('dun')} {modalState.id >= 0 ? t('combattant') : t('invité')}</h1>
|
||||
<button ref={closeBtn} type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{modalState.id < 0 &&
|
||||
<div className="mb-2">{t('comp.modal.text1')}</div>}
|
||||
<div className="card" style={{marginBottom: "1em"}}>
|
||||
<div className="card-header">{modalState.id >= 0 ? t('comp.modal.recherche') : t('comp.modal.information')}</div>
|
||||
<div className="card-body">
|
||||
<div className="row" hidden={modalState.id < 0}>
|
||||
<div className="col">
|
||||
<input ref={licenceInput} type="number" min={0} step={1} className="form-control"
|
||||
placeholder={t("comp.noDeLicence")} name="licence" value={licence}
|
||||
onChange={e => setLicence(e.target.value)} disabled={editMode}
|
||||
onKeyUp={e => e.key === "Enter" ? handleSubmit(e) : undefined}/>
|
||||
</div>
|
||||
<h5 style={{textAlign: "center", marginTop: "0.25em"}} hidden={modalState.id < 0}>{t('ou')}</h5>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<input type="text" className="form-control" placeholder={t('prenom')} name="fname"
|
||||
disabled={editMode && modalState.id >= 0}
|
||||
value={fname} onChange={e => setFname(e.target.value)}/>
|
||||
</div>
|
||||
<div className="col">
|
||||
<input type="text" className="form-control" placeholder={t('nom')} name="lname"
|
||||
disabled={editMode && modalState.id >= 0}
|
||||
value={lname} onChange={e => setLname(e.target.value)}/>
|
||||
</div>
|
||||
</div>
|
||||
<h5 style={{textAlign: "center", marginTop: "0.25em"}} hidden={modalState.id < 0}>{t('ou')}</h5>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<input ref={nameInput} type="text" className="form-control" placeholder={t('prenom')} name="fname"
|
||||
disabled={editMode && modalState.id >= 0}
|
||||
value={fname} onChange={e => setFname(e.target.value)}/>
|
||||
</div>
|
||||
<div className="col">
|
||||
<input type="text" className="form-control" placeholder={t('nom')} name="lname"
|
||||
disabled={editMode && modalState.id >= 0} value={lname} onChange={e => setLname(e.target.value)}
|
||||
onKeyUp={e => e.key === "Enter" && modalState.id >= 0 ? handleSubmit(e) : undefined}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(editMode || modalState.id < 0) && <>
|
||||
<div className="input-group mb-3" hidden={modalState.id >= 0}>
|
||||
<span className="input-group-text" id="categorie">{t("club", {count: 1})}</span>
|
||||
<input type="text" className="form-control" placeholder={t("club", {count: 1})} name="club"
|
||||
@@ -451,28 +533,34 @@ function Modal({sendRegister, modalState, setModalState, source}) {
|
||||
onChange={e => setLockEdit(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="switchCheckReverse">{t('comp.modal.text2')}</label>
|
||||
</div>}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="submit" className="btn btn-primary">{editMode ? t('button.modifier') : t('button.ajouter')}</button>
|
||||
<button type="reset" className="btn btn-secondary" data-bs-dismiss="modal" id="closeModal">{t('button.annuler')}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="d-flex flex-wrap">
|
||||
<label htmlFor="inputState2" className="form-label align-self-center" style={{margin: "0 0.5em 0 0"}}>
|
||||
{t('catégorie')} :
|
||||
</label>
|
||||
<CategoriesList error2={error2} availableCats={availableCats} fistCatInput={fistCatInput} categories={categories}
|
||||
setCategories={setCategories_}/>
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal" id="closeModal">{t('button.annuler')}</button>
|
||||
<button ref={submitBtn} type="button" className="btn btn-primary"
|
||||
onClick={handleSubmit}>{editMode ? t('button.modifier') : t('button.ajouter')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
let allClub = []
|
||||
let allCat = []
|
||||
|
||||
function FiltreBar({data, clubFilter, setClubFilter, catFilter, setCatFilter, source}) {
|
||||
function FiltreBar({data, data2, clubFilter, setClubFilter, catFilter, setCatFilter, catAgeFilter, setCatAgeFilter, source}) {
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
allClub.push(...data.map((e) => e.club?.name))
|
||||
allClub = allClub.filter((value, index, self) => self.indexOf(value) === index).filter(value => value != null).sort()
|
||||
allCat.push(...data.map((e) => e.categorie))
|
||||
allCat = allCat.filter((value, index, self) => self.indexOf(value) === index).filter(value => value != null).sort()
|
||||
}, [data]);
|
||||
|
||||
return <div>
|
||||
@@ -485,13 +573,19 @@ function FiltreBar({data, clubFilter, setClubFilter, catFilter, setCatFilter, so
|
||||
</select>
|
||||
</div>}
|
||||
<div className="mb-3">
|
||||
<select className="form-select" value={catFilter} onChange={event => setCatFilter(event.target.value)}>
|
||||
<option value="">{t('---TouteLesCatégories---')}</option>
|
||||
{allCat && allCat.map((value, index) => {
|
||||
return <option key={index} value={value}>{value}</option>
|
||||
<select className="form-select" value={catAgeFilter} onChange={event => setCatAgeFilter(event.target.value)}>
|
||||
<option value="">{t('---TousLesAges---')}</option>
|
||||
{CatList && CatList.map((value, index) => {
|
||||
return <option key={index} value={value}>{getCatName(value)}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<select className="form-select" value={catFilter} onChange={event => setCatFilter(Number(event.target.value))}>
|
||||
<option value={-1}>{t('---TouteLesCatégories---')}</option>
|
||||
{data2 && data2.map((cat) => {
|
||||
return <option key={cat.id} value={cat.id}>{cat.name}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ function SelfRegister({data2}) {
|
||||
onClick={handleUnregister}>{t('button.seDésinscrire')}
|
||||
</button>
|
||||
<button type="button" className="btn btn-primary" disabled={disabled}
|
||||
onClick={handleSubmit}>{t('button.enregister')}
|
||||
onClick={handleSubmit}>{t('button.enregistrer')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,6 +42,32 @@ export const CatList = [
|
||||
"VETERAN2"
|
||||
];
|
||||
|
||||
export function sortCategories(catA, catB) {
|
||||
const indexA = CatList.indexOf(catA);
|
||||
const indexB = CatList.indexOf(catB);
|
||||
|
||||
if (indexA === -1 && indexB === -1) {
|
||||
return 0; // Both categories are unknown, maintain their order
|
||||
} else if (indexA === -1) {
|
||||
return 1; // catA is unknown, place it after catB
|
||||
} else if (indexB === -1) {
|
||||
return -1; // catB is unknown, place it after catA
|
||||
} else {
|
||||
return indexA - indexB; // Both categories are known, sort by their indices
|
||||
}
|
||||
}
|
||||
|
||||
export function applyOverCategory(cat, overCat) {
|
||||
const catIndex = CatList.indexOf(cat) + overCat;
|
||||
if (catIndex < 0) {
|
||||
return CatList[0];
|
||||
} else if (catIndex >= CatList.length) {
|
||||
return CatList[CatList.length - 1];
|
||||
} else {
|
||||
return CatList[catIndex];
|
||||
}
|
||||
}
|
||||
|
||||
export function getCategoryFormBirthDate(birth_date, currentDate = new Date()) {
|
||||
const currentSaison = getSaison(currentDate)
|
||||
const birthYear = birth_date.getFullYear()
|
||||
@@ -111,6 +137,53 @@ export function getCatName(cat) {
|
||||
}
|
||||
}
|
||||
|
||||
export const SwordList = [
|
||||
"NONE",
|
||||
"ONE_HAND",
|
||||
"TWO_HAND",
|
||||
"SABER"
|
||||
]
|
||||
|
||||
export function getSwordTypeName(type) {
|
||||
switch (type) {
|
||||
case "NONE":
|
||||
return i18n.t('sword.none');
|
||||
case "ONE_HAND":
|
||||
return i18n.t('sword.oneHand');
|
||||
case "TWO_HAND":
|
||||
return i18n.t('sword.twoHand');
|
||||
case "SABER":
|
||||
return i18n.t('sword.saber');
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
export const ShieldList = [
|
||||
"NONE",
|
||||
"STANDARD",
|
||||
"ROUND",
|
||||
"TEARDROP",
|
||||
"BUCKLER"
|
||||
]
|
||||
|
||||
export function getShieldTypeName(type) {
|
||||
switch (type) {
|
||||
case "NONE":
|
||||
return i18n.t('shield.none');
|
||||
case "STANDARD":
|
||||
return i18n.t('shield.standard');
|
||||
case "ROUND":
|
||||
return i18n.t('shield.round');
|
||||
case "TEARDROP":
|
||||
return i18n.t('shield.teardrop');
|
||||
case "BUCKLER":
|
||||
return i18n.t('shield.buckler');
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
export function getToastMessage(msgKey, ns = 'common') {
|
||||
return {
|
||||
pending: i18n.t(msgKey + '.pending', {ns}),
|
||||
|
||||
Reference in New Issue
Block a user