Compare commits
7 Commits
8a14f58ce5
...
e7deba52e9
| Author | SHA1 | Date | |
|---|---|---|---|
| e7deba52e9 | |||
| d95c173fa8 | |||
| 8a0e4423f2 | |||
| b956236934 | |||
| 7767c98304 | |||
| 7e380ccb69 | |||
| 94d1148eb1 |
@ -267,7 +267,9 @@ public class AffiliationService {
|
|||||||
}).call(m -> Panache.withTransaction(() -> combRepository.persist(m)));
|
}).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()))
|
keycloakService.setClubGroupMembre(m, club).map(__ -> m.getUserId()))
|
||||||
.call(userId -> keycloakService.setAutoRoleMembre(userId, m.getRole(), m.getGrade_arbitrage()))
|
.call(userId -> keycloakService.setAutoRoleMembre(userId, m.getRole(), m.getGrade_arbitrage()))
|
||||||
.call(userId -> keycloakService.setEmail(userId, m.getEmail())))
|
.call(userId -> keycloakService.setEmail(userId, m.getEmail())))
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import io.smallrye.mutiny.unchecked.Unchecked;
|
|||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.hibernate.reactive.mutiny.Mutiny;
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -26,6 +27,7 @@ import java.util.function.Function;
|
|||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class LicenceService {
|
public class LicenceService {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(LicenceService.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
LicenceRepository repository;
|
LicenceRepository repository;
|
||||||
@ -125,7 +127,9 @@ public class LicenceService {
|
|||||||
.chain(() -> combRepository.persist(membreModel))
|
.chain(() -> combRepository.persist(membreModel))
|
||||||
: Uni.createFrom().nullItem())
|
: Uni.createFrom().nullItem())
|
||||||
.call(__ -> (membreModel.getUserId() == null) ?
|
.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());
|
: Uni.createFrom().nullItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -210,7 +210,8 @@ public class MembreService {
|
|||||||
return repository.list("licence IN ?1 OR LOWER(lname || ' ' || fname) IN ?2 OR email IN ?3",
|
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(SimpleMembreInOutData::getLicence).filter(Objects::nonNull).toList(),
|
||||||
data2.stream().map(o -> (o.getNom() + " " + o.getPrenom()).toLowerCase()).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 -> {
|
.call(Unchecked.function(membres -> {
|
||||||
for (MembreModel membreModel : membres) {
|
for (MembreModel membreModel : membres) {
|
||||||
@ -225,7 +226,8 @@ public class MembreService {
|
|||||||
MembreModel model = membres.stream()
|
MembreModel model = membres.stream()
|
||||||
.filter(m -> Objects.equals(m.getLicence(), dataIn.getLicence()) || m.getLname()
|
.filter(m -> Objects.equals(m.getLicence(), dataIn.getLicence()) || m.getLname()
|
||||||
.equals(dataIn.getNom()) && m.getFname().equals(dataIn.getPrenom()) ||
|
.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(() -> {
|
.orElseGet(() -> {
|
||||||
MembreModel mm = new MembreModel();
|
MembreModel mm = new MembreModel();
|
||||||
mm.setClub(clubModel.get());
|
mm.setClub(clubModel.get());
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
|||||||
import org.jboss.resteasy.reactive.PartType;
|
import org.jboss.resteasy.reactive.PartType;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
@ToString(exclude = {"status", "logo"})
|
||||||
public class AffiliationRequestForm {
|
public class AffiliationRequestForm {
|
||||||
@Schema(description = "L'identifiant de l'affiliation. (null si nouvelle demande d'affiliation)")
|
@Schema(description = "L'identifiant de l'affiliation. (null si nouvelle demande d'affiliation)")
|
||||||
@FormParam("id")
|
@FormParam("id")
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import {useEffect, useRef} from 'react'
|
import {useEffect, useRef} from 'react'
|
||||||
import {Nav} from "./components/Nav.jsx";
|
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 {Home} from "./pages/Homepage.jsx";
|
||||||
import {AdminRoot, getAdminChildren} from "./pages/admin/AdminRoot.jsx";
|
import {AdminRoot, getAdminChildren} from "./pages/admin/AdminRoot.jsx";
|
||||||
import {AuthCallback} from "./components/auhCallback.jsx";
|
import {AuthCallback} from "./components/auhCallback.jsx";
|
||||||
import {KeycloakContextProvider, useAuthDispatch} from "./hooks/useAuth.jsx";
|
import {KeycloakContextProvider, useAuth, useAuthDispatch} from "./hooks/useAuth.jsx";
|
||||||
import {check_validity} from "./utils/auth.js";
|
import {check_validity, login} from "./utils/auth.js";
|
||||||
import {ToastContainer} from "react-toastify";
|
import {ToastContainer} from "react-toastify";
|
||||||
|
|
||||||
import './App.css'
|
import './App.css'
|
||||||
@ -14,6 +14,7 @@ import {ClubRoot, getClubChildren} from "./pages/club/ClubRoot.jsx";
|
|||||||
import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx";
|
import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx";
|
||||||
import {MePage} from "./pages/MePage.jsx";
|
import {MePage} from "./pages/MePage.jsx";
|
||||||
import {CompetitionRoot, getCompetitionChildren} from "./pages/competition/CompetitionRoot.jsx";
|
import {CompetitionRoot, getCompetitionChildren} from "./pages/competition/CompetitionRoot.jsx";
|
||||||
|
import {FallingLines} from "react-loader-spinner";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@ -113,6 +114,43 @@ function Root() {
|
|||||||
theme="light"
|
theme="light"
|
||||||
transition: Flip
|
transition: Flip
|
||||||
/>
|
/>
|
||||||
|
<ReAuthMsg/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReAuthMsg() {
|
||||||
|
const {is_authenticated} = useAuth()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
const notAuthPaths = [
|
||||||
|
/^\/$/s,
|
||||||
|
/^\/affiliation(\/)?$/s,
|
||||||
|
/^\/affiliation\/ok(\/)?$/s,
|
||||||
|
/^\/complete\/auth.*$/s
|
||||||
|
]
|
||||||
|
|
||||||
|
if (is_authenticated || notAuthPaths.some(r => r.test(location.pathname)))
|
||||||
|
return <></>
|
||||||
|
return <>
|
||||||
|
<div className="overlayBG" style={{position: 'fixed'}}>
|
||||||
|
<div className="overlayContent" onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}>
|
||||||
|
<div className="card">
|
||||||
|
<div className="card-header">
|
||||||
|
<h5>Session expirée</h5>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<p className="card-text">Votre session a expirée, veuillez vous reconnecter pour continuer à
|
||||||
|
utiliser l'application.</p>
|
||||||
|
</div>
|
||||||
|
<div className="card-footer">
|
||||||
|
<button className="btn btn-primary" onClick={() => login()} style={{marginRight: "0.5em"}}>Se reconnecter</button>
|
||||||
|
<a className="btn btn-secondary" href="/">Accueil</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,13 +88,14 @@ export function CountryList({name, text, value, values = undefined, disabled = f
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) {
|
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true, ttip = null}) {
|
||||||
return <div className="row">
|
return <div className="row mb-3">
|
||||||
<div className="input-group mb-3">
|
<div className="input-group">
|
||||||
<span className="input-group-text" id={name}>{text}</span>
|
<span className="input-group-text" id={name}>{text}</span>
|
||||||
<input type={type} className="form-control" placeholder={placeholder ? placeholder : text} aria-label={name}
|
<input type={type} className="form-control" placeholder={placeholder ? placeholder : text} aria-label={name}
|
||||||
name={name} aria-describedby={name} defaultValue={value} disabled={disabled} required={required}/>
|
name={name} aria-describedby={name} defaultValue={value} disabled={disabled} required={required}/>
|
||||||
</div>
|
</div>
|
||||||
|
{ttip}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,13 @@ function reconstruireAdresse(infos) {
|
|||||||
return adresseReconstruite;
|
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() {
|
export function DemandeAff() {
|
||||||
const {hash} = useLocation();
|
const {hash} = useLocation();
|
||||||
@ -145,7 +152,7 @@ export function DemandeAff() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<h1>Demande d'affiliation</h1>
|
<h1>Demande d'affiliation {getSaisonToAff() + "-" + (getSaisonToAff() + 1)}</h1>
|
||||||
<p>L'affiliation est annuelle et valable pour une saison sportive : du 1er septembre au 31 août de l’année
|
<p>L'affiliation est annuelle et valable pour une saison sportive : du 1er septembre au 31 août de l’année
|
||||||
suivante.</p>
|
suivante.</p>
|
||||||
Pour s’affilier, une association sportive doit réunir les conditions suivantes :
|
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 [rna, setRna] = useState(initData.rna ? initData.rna : "")
|
||||||
const [rnaEnable, setRnaEnable] = useState(false)
|
const [rnaEnable, setRnaEnable] = useState(false)
|
||||||
const [adresse, setAdresse] = useState(initData.address ? initData.address : "")
|
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 [contact, setContact] = useState(initData.contact ? initData.contact : "")
|
||||||
|
|
||||||
const fetchSiret = () => {
|
const fetchSiret = () => {
|
||||||
@ -245,26 +251,8 @@ function AssoInfo({initData, needFile}) {
|
|||||||
setAdresse(reconstruireAdresse(data2.etablissement_siege))
|
setAdresse(reconstruireAdresse(data2.etablissement_siege))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentSaison = getSaison();
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="input-group mb-3">
|
<input name="saison" value={getSaisonToAff()} readOnly hidden/>
|
||||||
<div className="input-group-text">
|
|
||||||
<input className="form-check-input mt-0" type="radio" value={currentSaison} aria-label={currentSaison + "-" + (currentSaison + 1)}
|
|
||||||
name={"saison"} checked={saison === currentSaison}
|
|
||||||
onChange={e => setSaison(Number(e.target.value))}/>
|
|
||||||
{currentSaison + "-" + (currentSaison + 1)}
|
|
||||||
</div>
|
|
||||||
<span className="input-group-text">OU</span>
|
|
||||||
<div className="input-group-text">
|
|
||||||
<input className="form-check-input mt-0" type="radio" value={currentSaison + 1}
|
|
||||||
aria-label={(currentSaison + 1) + "-" + (currentSaison + 2)}
|
|
||||||
name={"saison"} checked={saison === currentSaison + 1}
|
|
||||||
onChange={e => setSaison(Number(e.target.value))}/>
|
|
||||||
{(currentSaison + 1) + "-" + (currentSaison + 2)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="input-group mb-3">
|
<div className="input-group mb-3">
|
||||||
<span className="input-group-text" id="basic-addon1">Nom de l'association*</span>
|
<span className="input-group-text" id="basic-addon1">Nom de l'association*</span>
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import {RoleList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
|||||||
import {useEffect, useRef, useState} from "react";
|
import {useEffect, useRef, useState} from "react";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faFilePdf} from "@fortawesome/free-solid-svg-icons";
|
import {faFilePdf} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
|
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -166,7 +165,7 @@ function Content({data, refresh}) {
|
|||||||
<input name="id" value={data.id} readOnly hidden/>
|
<input name="id" value={data.id} readOnly hidden/>
|
||||||
<div className="card-header">Demande d'affiliation</div>
|
<div className="card-header">Demande d'affiliation</div>
|
||||||
<div className="card-body text-center">
|
<div className="card-body text-center">
|
||||||
{data.club && <h5>Ce club a déjà ete affilier (affiliation n°{data.club_no_aff})</h5>}
|
{data.club && <h5>Ce club a déjà été affilié (affiliation n°{data.club_no_aff})</h5>}
|
||||||
<h4 id="saison">Saison {data.saison}-{data.saison + 1}</h4>
|
<h4 id="saison">Saison {data.saison}-{data.saison + 1}</h4>
|
||||||
|
|
||||||
<div className="row mb-3">
|
<div className="row mb-3">
|
||||||
|
|||||||
@ -139,10 +139,10 @@ function ModalContent2({clubData, data}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.length !== 3) {
|
while (list.length < 3) {
|
||||||
toast.error("Il faut sélectionner 3 membres pour renouveler l'affiliation")
|
list.push(-1)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiAxios.get(`/club/renew/${clubData.id}?m1=${list[0]}&m2=${list[1]}&m3=${list[2]}`).then(data => {
|
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)))
|
navigate('/affiliation#d' + encodeURI(JSON.stringify(data.data)))
|
||||||
})
|
})
|
||||||
|
|||||||
@ -49,7 +49,9 @@ export function InformationForm({data}) {
|
|||||||
<TextField name="lname" text="Nom" value={data.lname}/>
|
<TextField name="lname" text="Nom" value={data.lname}/>
|
||||||
<TextField name="fname" text="Prénom" value={data.fname}/>
|
<TextField name="fname" text="Prénom" value={data.fname}/>
|
||||||
<TextField name="email" text="Email" value={data.email} placeholder="name@example.com"
|
<TextField name="email" text="Email" value={data.email} placeholder="name@example.com"
|
||||||
type="email"/>
|
type="email" ttip={<small className="form-text">L'email sert à la création de compte pour se connecter au site et doit être unique. <br/>
|
||||||
|
Pour les mineurs, l'email des parents peut être utilisé plusieurs fois grâce à la syntaxe suivante : {'email.parent+<caractères alphanumériques>@exemple.com'}.<br/>
|
||||||
|
Exemples : mail.parent+1@exemple.com, mail.parent+titouan@exemple.com, mail.parent+cedrique@exemple.com</small>}/>
|
||||||
<OptionField name="genre" text="Genre" value={data.genre}
|
<OptionField name="genre" text="Genre" value={data.genre}
|
||||||
values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
||||||
<CountryList name="country" text="Pays" value={data.country}/>
|
<CountryList name="country" text="Pays" value={data.country}/>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user