From 94d1148eb16540ce42e556eb28bba535d0dbf584 Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 7 Nov 2025 15:30:56 +0100 Subject: [PATCH 1/6] fix: null email on import --- .../ffsaf/domain/service/AffiliationService.java | 6 ++++-- .../titionfire/ffsaf/domain/service/LicenceService.java | 6 +++++- .../fr/titionfire/ffsaf/domain/service/MembreService.java | 8 +++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java index a2b79a8..3bafd63 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java @@ -267,7 +267,9 @@ public class AffiliationService { }).call(m -> Panache.withTransaction(() -> combRepository.persist(m))); } }) - .call(m -> ((m.getUserId() == null) ? keycloakService.initCompte(m.getId()) : + .call(m -> ((m.getUserId() == null) ? keycloakService.initCompte(m.getId()) + .onFailure().invoke(t -> LOGGER.warnf("Failed to init account: %s", t.getMessage())).onFailure() + .recoverWithNull() : keycloakService.setClubGroupMembre(m, club).map(__ -> m.getUserId())) .call(userId -> keycloakService.setAutoRoleMembre(userId, m.getRole(), m.getGrade_arbitrage())) .call(userId -> keycloakService.setEmail(userId, m.getEmail()))) @@ -275,7 +277,7 @@ public class AffiliationService { .call(l1 -> l1 != null && l1.stream().anyMatch(l -> l.getSaison() == saison) ? Uni.createFrom().nullItem() : Panache.withTransaction(() -> licenceRepository.persist( - new LicenceModel(null, m, club.getId(), saison, null, true, false))) + new LicenceModel(null, m, club.getId(), saison, null, true, false))) .call(licenceModel -> ls.logA(LogModel.ActionType.ADD, m.getObjectName(), licenceModel)))); } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java index 2269249..751df30 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/LicenceService.java @@ -18,6 +18,7 @@ import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.hibernate.reactive.mutiny.Mutiny; +import org.jboss.logging.Logger; import java.util.List; import java.util.function.Consumer; @@ -26,6 +27,7 @@ import java.util.function.Function; @WithSession @ApplicationScoped public class LicenceService { + private static final Logger LOGGER = Logger.getLogger(LicenceService.class); @Inject LicenceRepository repository; @@ -125,7 +127,9 @@ public class LicenceService { .chain(() -> combRepository.persist(membreModel)) : Uni.createFrom().nullItem()) .call(__ -> (membreModel.getUserId() == null) ? - keycloakService.initCompte(membreModel.getId()) + keycloakService.initCompte(membreModel.getId()).onFailure() + .invoke(t -> LOGGER.infof("Failed to init account: %s", t.getMessage())).onFailure() + .recoverWithNull() : Uni.createFrom().nullItem()); } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java index 2dc7e7a..781cf95 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java @@ -210,11 +210,12 @@ public class MembreService { return repository.list("licence IN ?1 OR LOWER(lname || ' ' || fname) IN ?2 OR email IN ?3", data2.stream().map(SimpleMembreInOutData::getLicence).filter(Objects::nonNull).toList(), data2.stream().map(o -> (o.getNom() + " " + o.getPrenom()).toLowerCase()).toList(), - data2.stream().map(SimpleMembreInOutData::getEmail).filter(o -> o != null && !o.isBlank()).toList()); + data2.stream().map(SimpleMembreInOutData::getEmail).filter(o -> o != null && !o.isBlank()) + .toList()); }) .call(Unchecked.function(membres -> { for (MembreModel membreModel : membres) { - if (!Objects.equals(membreModel.getClub(), clubModel.get())){ + if (!Objects.equals(membreModel.getClub(), clubModel.get())) { LOGGER.info("Similar membres found: " + membreModel); throw new DForbiddenException( "Le membre n°" + membreModel.getLicence() + " n'appartient pas à votre club"); @@ -225,7 +226,8 @@ public class MembreService { MembreModel model = membres.stream() .filter(m -> Objects.equals(m.getLicence(), dataIn.getLicence()) || m.getLname() .equals(dataIn.getNom()) && m.getFname().equals(dataIn.getPrenom()) || - Objects.equals(m.getFname(), dataIn.getEmail())).findFirst() + (dataIn.getEmail() != null && !dataIn.getEmail().isBlank() && Objects.equals( + m.getFname(), dataIn.getEmail()))).findFirst() .orElseGet(() -> { MembreModel mm = new MembreModel(); mm.setClub(clubModel.get()); From 7e380ccb69e0ca12a439388ce1fd1f3d51f8dda9 Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 7 Nov 2025 15:31:25 +0100 Subject: [PATCH 2/6] feat: add re-login message --- src/main/webapp/src/App.jsx | 45 ++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/src/App.jsx b/src/main/webapp/src/App.jsx index bc53786..bd10731 100644 --- a/src/main/webapp/src/App.jsx +++ b/src/main/webapp/src/App.jsx @@ -1,11 +1,11 @@ import {useEffect, useRef} from 'react' import {Nav} from "./components/Nav.jsx"; -import {createBrowserRouter, Outlet, RouterProvider, useRouteError} from "react-router-dom"; +import {createBrowserRouter, Outlet, RouterProvider, useLocation, useRouteError} from "react-router-dom"; import {Home} from "./pages/Homepage.jsx"; import {AdminRoot, getAdminChildren} from "./pages/admin/AdminRoot.jsx"; import {AuthCallback} from "./components/auhCallback.jsx"; -import {KeycloakContextProvider, useAuthDispatch} from "./hooks/useAuth.jsx"; -import {check_validity} from "./utils/auth.js"; +import {KeycloakContextProvider, useAuth, useAuthDispatch} from "./hooks/useAuth.jsx"; +import {check_validity, login} from "./utils/auth.js"; import {ToastContainer} from "react-toastify"; import './App.css' @@ -14,6 +14,7 @@ import {ClubRoot, getClubChildren} from "./pages/club/ClubRoot.jsx"; import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx"; import {MePage} from "./pages/MePage.jsx"; import {CompetitionRoot, getCompetitionChildren} from "./pages/competition/CompetitionRoot.jsx"; +import {FallingLines} from "react-loader-spinner"; const router = createBrowserRouter([ { @@ -113,6 +114,44 @@ function Root() { theme="light" transition: Flip /> + + + +} + +function ReAuthMsg() { + const {is_authenticated} = useAuth() + const location = useLocation() + + const notAuthPaths = [ + /^\/$/s, + /^\/affiliation(\/)?$/s, + /^\/complete\/auth.*$/s + ] + + console.log(location.pathname, notAuthPaths.some(r => r.test(location.pathname))) + + if (is_authenticated || notAuthPaths.some(r => r.test(location.pathname))) + return <> + return <> +
+
{ + e.stopPropagation() + }}> +
+
+
Session expirée
+
+
+

Votre session a expirée, veuillez vous reconnecter pour continuer à + utiliser l'application.

+
+
+ + Accueil +
+
+
} From 7767c98304056359e6a2586c203afb178f59248f Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 7 Nov 2025 15:53:39 +0100 Subject: [PATCH 3/6] feat: add email tooltip --- src/main/webapp/src/components/MemberCustomFiels.jsx | 7 ++++--- .../src/pages/admin/affiliation/AffiliationReqPage.jsx | 3 +-- src/main/webapp/src/pages/club/member/InformationForm.jsx | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/src/components/MemberCustomFiels.jsx b/src/main/webapp/src/components/MemberCustomFiels.jsx index 7c0a00c..0fd9602 100644 --- a/src/main/webapp/src/components/MemberCustomFiels.jsx +++ b/src/main/webapp/src/components/MemberCustomFiels.jsx @@ -88,13 +88,14 @@ export function CountryList({name, text, value, values = undefined, disabled = f } -export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) { - return
-
+export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true, ttip = null}) { + return
+
{text}
+ {ttip}
} diff --git a/src/main/webapp/src/pages/admin/affiliation/AffiliationReqPage.jsx b/src/main/webapp/src/pages/admin/affiliation/AffiliationReqPage.jsx index 9c8a98e..cb292bb 100644 --- a/src/main/webapp/src/pages/admin/affiliation/AffiliationReqPage.jsx +++ b/src/main/webapp/src/pages/admin/affiliation/AffiliationReqPage.jsx @@ -8,7 +8,6 @@ import {RoleList, TextField} from "../../../components/MemberCustomFiels.jsx"; import {useEffect, useRef, useState} from "react"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {faFilePdf} from "@fortawesome/free-solid-svg-icons"; -import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx"; const vite_url = import.meta.env.VITE_URL; @@ -166,7 +165,7 @@ function Content({data, refresh}) {
Demande d'affiliation
- {data.club &&
Ce club a déjà ete affilier (affiliation n°{data.club_no_aff})
} + {data.club &&
Ce club a déjà été affilié (affiliation n°{data.club_no_aff})
}

Saison {data.saison}-{data.saison + 1}

diff --git a/src/main/webapp/src/pages/club/member/InformationForm.jsx b/src/main/webapp/src/pages/club/member/InformationForm.jsx index a6231ea..9446f96 100644 --- a/src/main/webapp/src/pages/club/member/InformationForm.jsx +++ b/src/main/webapp/src/pages/club/member/InformationForm.jsx @@ -49,7 +49,9 @@ export function InformationForm({data}) { + type="email" ttip={L'email sert à la création de compte pour se connecter au site et doit être unique.
+ Pour les mineurs, l'email des parents peut être utilisé plusieurs fois grâce à la syntaxe suivante : {'email.parent+@exemple.com'}.
+ Exemples : mail.parent+1@exemple.com, mail.parent+titouan@exemple.com, mail.parent+cedrique@exemple.com
}/> From b956236934ffcf98679c8b3079158dc301d3552a Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 7 Nov 2025 15:57:36 +0100 Subject: [PATCH 4/6] fix: affiliation ok login msg --- src/main/webapp/src/App.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/webapp/src/App.jsx b/src/main/webapp/src/App.jsx index bd10731..7cc63dc 100644 --- a/src/main/webapp/src/App.jsx +++ b/src/main/webapp/src/App.jsx @@ -126,6 +126,7 @@ function ReAuthMsg() { const notAuthPaths = [ /^\/$/s, /^\/affiliation(\/)?$/s, + /^\/affiliation\/ok(\/)?$/s, /^\/complete\/auth.*$/s ] From 8a0e4423f2bcb17cfc7b3f40a91bf423fba25788 Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 7 Nov 2025 16:13:37 +0100 Subject: [PATCH 5/6] feat: remove saison selection on aff req --- .../rest/from/AffiliationRequestForm.java | 2 +- src/main/webapp/src/pages/DemandeAff.jsx | 34 ++++++------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestForm.java b/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestForm.java index 961033c..9dfb985 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestForm.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestForm.java @@ -11,7 +11,7 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.jboss.resteasy.reactive.PartType; @Getter -@ToString +@ToString(exclude = {"status", "logo"}) public class AffiliationRequestForm { @Schema(description = "L'identifiant de l'affiliation. (null si nouvelle demande d'affiliation)") @FormParam("id") diff --git a/src/main/webapp/src/pages/DemandeAff.jsx b/src/main/webapp/src/pages/DemandeAff.jsx index ccfa4fd..d0a2953 100644 --- a/src/main/webapp/src/pages/DemandeAff.jsx +++ b/src/main/webapp/src/pages/DemandeAff.jsx @@ -20,11 +20,11 @@ function reconstruireAdresse(infos) { console.log(infos); let adresseReconstruite = ""; - if(infos.numero_voie === null){ + if (infos.numero_voie === null) { if (infos.complement_adresse) { adresseReconstruite += formatAdresse(infos.complement_adresse) + ', '; } - }else{ + } else { adresseReconstruite += infos.numero_voie + ' '; } @@ -46,6 +46,13 @@ function reconstruireAdresse(infos) { return adresseReconstruite; } +function getSaisonToAff(currentDate = new Date()) { + if (currentDate.getMonth() >= 7) { //aout et plus + return currentDate.getFullYear() + } else { + return currentDate.getFullYear() - 1 + } +} export function DemandeAff() { const {hash} = useLocation(); @@ -145,7 +152,7 @@ export function DemandeAff() { } return
-

Demande d'affiliation

+

Demande d'affiliation {getSaisonToAff() + "-" + (getSaisonToAff() + 1)}

L'affiliation est annuelle et valable pour une saison sportive : du 1er septembre au 31 août de l’année suivante.

Pour s’affilier, une association sportive doit réunir les conditions suivantes : @@ -216,7 +223,6 @@ function AssoInfo({initData, needFile}) { const [rna, setRna] = useState(initData.rna ? initData.rna : "") const [rnaEnable, setRnaEnable] = useState(false) const [adresse, setAdresse] = useState(initData.address ? initData.address : "") - const [saison, setSaison] = useState(initData.saison ? initData.saison : getSaison()) const [contact, setContact] = useState(initData.contact ? initData.contact : "") const fetchSiret = () => { @@ -245,26 +251,8 @@ function AssoInfo({initData, needFile}) { setAdresse(reconstruireAdresse(data2.etablissement_siege)) }) } - - const currentSaison = getSaison(); - return <> -
-
- setSaison(Number(e.target.value))}/> - {currentSaison + "-" + (currentSaison + 1)} -
- OU -
- setSaison(Number(e.target.value))}/> - {(currentSaison + 1) + "-" + (currentSaison + 2)} -
-
+
Nom de l'association* From d95c173fa8b1522ebdc6d5824546a754b5ba4dd0 Mon Sep 17 00:00:00 2001 From: Thibaut Valentin Date: Fri, 7 Nov 2025 16:19:49 +0100 Subject: [PATCH 6/6] fix: aff renew select length --- src/main/webapp/src/App.jsx | 2 -- src/main/webapp/src/pages/club/club/AffiliationCard.jsx | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/src/App.jsx b/src/main/webapp/src/App.jsx index 7cc63dc..ef74deb 100644 --- a/src/main/webapp/src/App.jsx +++ b/src/main/webapp/src/App.jsx @@ -130,8 +130,6 @@ function ReAuthMsg() { /^\/complete\/auth.*$/s ] - console.log(location.pathname, notAuthPaths.some(r => r.test(location.pathname))) - if (is_authenticated || notAuthPaths.some(r => r.test(location.pathname))) return <> return <> diff --git a/src/main/webapp/src/pages/club/club/AffiliationCard.jsx b/src/main/webapp/src/pages/club/club/AffiliationCard.jsx index f6de7e3..33439d6 100644 --- a/src/main/webapp/src/pages/club/club/AffiliationCard.jsx +++ b/src/main/webapp/src/pages/club/club/AffiliationCard.jsx @@ -139,10 +139,10 @@ function ModalContent2({clubData, data}) { } } - if (list.length !== 3) { - toast.error("Il faut sélectionner 3 membres pour renouveler l'affiliation") - return + while (list.length < 3) { + list.push(-1) } + apiAxios.get(`/club/renew/${clubData.id}?m1=${list[0]}&m2=${list[1]}&m3=${list[2]}`).then(data => { navigate('/affiliation#d' + encodeURI(JSON.stringify(data.data))) })