diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/AffiliationRequestModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/AffiliationRequestModel.java index 1c6f38e..9e4ce1b 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/AffiliationRequestModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/AffiliationRequestModel.java @@ -21,7 +21,7 @@ public class AffiliationRequestModel { Long id; String name; - long siren; + long siret; String RNA; String address; diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java index 9f26806..91b533f 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java @@ -53,6 +53,6 @@ public class ClubModel { boolean international; - @OneToMany(mappedBy = "club", fetch = FetchType.EAGER) + @OneToMany(mappedBy = "club", fetch = FetchType.LAZY) List affiliations; } diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/AffiliationRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/AffiliationRepository.java new file mode 100644 index 0000000..b09aa38 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/AffiliationRepository.java @@ -0,0 +1,9 @@ +package fr.titionfire.ffsaf.data.repository; + +import fr.titionfire.ffsaf.data.model.AffiliationModel; +import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class AffiliationRepository implements PanacheRepositoryBase { +} 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 0a642b0..0fa74ea 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java @@ -1,11 +1,12 @@ package fr.titionfire.ffsaf.domain.service; +import fr.titionfire.ffsaf.data.model.AffiliationModel; import fr.titionfire.ffsaf.data.model.AffiliationRequestModel; -import fr.titionfire.ffsaf.data.model.ClubModel; +import fr.titionfire.ffsaf.data.repository.AffiliationRepository; import fr.titionfire.ffsaf.data.repository.AffiliationRequestRepository; +import fr.titionfire.ffsaf.data.repository.ClubRepository; import fr.titionfire.ffsaf.data.repository.CombRepository; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; -import fr.titionfire.ffsaf.rest.from.AffiliationForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; import fr.titionfire.ffsaf.utils.Utils; import io.quarkus.hibernate.reactive.panache.Panache; @@ -14,9 +15,10 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.hibernate.reactive.mutiny.Mutiny; import java.util.List; -import java.util.function.Consumer; +import java.util.stream.Stream; @WithSession @ApplicationScoped @@ -26,7 +28,13 @@ public class AffiliationService { CombRepository combRepository; @Inject - AffiliationRequestRepository repository; + ClubRepository clubRepository; + + @Inject + AffiliationRequestRepository repositoryRequest; + + @Inject + AffiliationRepository repository; @ConfigProperty(name = "upload_dir") String media; @@ -57,27 +65,46 @@ public class AffiliationService { throw new IllegalArgumentException("Licence membre n°3 inconnue"); } }) : Uni.createFrom().nullItem()) - ).chain(model -> Panache.withTransaction(() -> repository.persist(model))) - .onItem().invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media, + ).chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model))) + .onItem() + .invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media, "aff_request/logo"))) - .onItem().invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media, + .onItem() + .invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media, "aff_request/status"))) .map(__ -> "Ok"); } public Uni> getCurrentSaisonAffiliation() { - return Uni.createFrom().nullItem(); // TODO + return repository.list("saison = ?1", Utils.getSaison()) + .map(models -> models.stream().map(SimpleAffiliation::fromModel).toList()) + .chain(aff -> repositoryRequest.list("saison = ?1", Utils.getSaison()) + .chain(models -> Uni.join().all(models.stream().map(model -> + clubRepository.find("siret = ?1", model.getSiret()).firstResult() + .map(c -> new SimpleAffiliation(model.getId() * -1, c.getId(), + model.getSaison(), false))) + .toList()).andFailFast() + ).map(aff2 -> Stream.concat(aff2.stream(), aff.stream()).toList()) + ); } - public Uni> getAffiliation(long id, Consumer checkPerm) { - return Uni.createFrom().nullItem(); // TODO + public Uni> getAffiliation(long id) { + return clubRepository.findById(id).call(model -> Mutiny.fetch(model.getAffiliations())) + .chain(model -> repositoryRequest.list("siret = ?1", model.getSIRET()) + .map(reqs -> reqs.stream().map(req -> + new SimpleAffiliation(req.getId() * -1, model.getId(), req.getSaison(), false))) + .map(aff2 -> Stream.concat(aff2, + model.getAffiliations().stream().map(SimpleAffiliation::fromModel)).toList()) + ); } - public Uni setAffiliation(long id, AffiliationForm form) { - return Uni.createFrom().nullItem(); // TODO + public Uni setAffiliation(long id, int saison) { + return clubRepository.findById(id).chain(club -> + Panache.withTransaction(() -> repository.persist(new AffiliationModel(null, club, saison)))) + .map(SimpleAffiliation::fromModel); } public Uni deleteAffiliation(long id) { - return Uni.createFrom().nullItem(); // TODO + return Panache.withTransaction(() -> repository.deleteById(id)); } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java index d44c8c1..8634197 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java @@ -1,9 +1,7 @@ package fr.titionfire.ffsaf.rest; -import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.domain.service.AffiliationService; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; -import fr.titionfire.ffsaf.rest.from.AffiliationForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; import fr.titionfire.ffsaf.utils.GroupeUtils; import io.quarkus.oidc.IdToken; @@ -32,8 +30,8 @@ public class AffiliationEndpoints { @Inject SecurityIdentity securityIdentity; - Consumer checkPerm = Unchecked.consumer(clubModel -> { - if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(clubModel.getId(), idToken)) + Consumer checkPerm = Unchecked.consumer(id -> { + if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken)) throw new ForbiddenException(); }); @@ -57,16 +55,15 @@ public class AffiliationEndpoints { @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"}) @Produces(MediaType.APPLICATION_JSON) public Uni> getLicence(@PathParam("id") long id) { - return service.getAffiliation(id, checkPerm); + return Uni.createFrom().item(id).invoke(checkPerm).chain(__ -> service.getAffiliation(id)); } - @POST + @PUT @Path("{id}") @RolesAllowed("federation_admin") @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.MULTIPART_FORM_DATA) - public Uni setLicence(@PathParam("id") long id, AffiliationForm form) { - return service.setAffiliation(id, form); + public Uni setLicence(@PathParam("id") long id, @QueryParam("saison") int saison) { + return service.setAffiliation(id, saison); } @DELETE diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleAffiliation.java b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleAffiliation.java index e4621c2..90efb04 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleAffiliation.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleAffiliation.java @@ -16,7 +16,7 @@ public class SimpleAffiliation { int saison; boolean validate; - public static SimpleAffiliation fromModel(AffiliationModel model, boolean validate) { + public static SimpleAffiliation fromModel(AffiliationModel model) { if (model == null) return null; @@ -24,7 +24,7 @@ public class SimpleAffiliation { .id(model.getId()) .club(model.getClub().getId()) .saison(model.getSaison()) - .validate(validate) + .validate(true) .build(); } } 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 2657a71..d89b26b 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestForm.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/from/AffiliationRequestForm.java @@ -14,8 +14,8 @@ public class AffiliationRequestForm { @FormParam("name") private String name = null; - @FormParam("siren") - private Long siren = null; + @FormParam("siret") + private Long siret = null; @FormParam("rna") private String rna = null; @@ -67,7 +67,7 @@ public class AffiliationRequestForm { public AffiliationRequestModel toModel() { AffiliationRequestModel model = new AffiliationRequestModel(); model.setName(this.getName()); - model.setSiren(this.getSiren()); + model.setSiret(this.getSiret()); model.setRNA(this.getRna()); model.setAddress(this.getAdresse()); diff --git a/src/main/webapp/src/pages/DemandeAff.jsx b/src/main/webapp/src/pages/DemandeAff.jsx index b07b9d4..8d436d7 100644 --- a/src/main/webapp/src/pages/DemandeAff.jsx +++ b/src/main/webapp/src/pages/DemandeAff.jsx @@ -100,14 +100,19 @@ export function DemandeAff() { function AssoInfo() { const [denomination, setDenomination] = useState("") - const [siren, setSiren] = useState("") + const [siret, setSiret] = useState("") const [rna, setRna] = useState("") const [rnaEnable, setRnaEnable] = useState(false) const [adresse, setAdresse] = useState("") - const fetchSiren = () => { + const fetchSiret = () => { + if (siret.length < 14) { + toast.error("Le SIRET doit contenir 14 chiffres") + return; + } + toast.promise( - apiAxios.get(`asso/siren/${siren}`), + apiAxios.get(`asso/siren/${siret.substring(0, siret.length - 5)}`), { pending: "Recherche de l'association en cours", success: "Association trouvée avec succès 🎉", @@ -131,11 +136,11 @@ function AssoInfo() {
- N° SIREN* - setSiren(e.target.value)} defaultValue={500213731}/> + N° SIRET* + setSiret(e.target.value)} defaultValue={500213731}/>
diff --git a/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx b/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx index a2b82c9..84570c1 100644 --- a/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx +++ b/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx @@ -2,51 +2,25 @@ import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; import {useFetch} from "../../../hooks/useFetch.js"; import {useEffect, useReducer, useState} from "react"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; -import {faPen} from "@fortawesome/free-solid-svg-icons"; +import {faEye, faPen} from "@fortawesome/free-solid-svg-icons"; import {AxiosError} from "../../../components/AxiosError.jsx"; import {apiAxios, getSaison} from "../../../utils/Tools.js"; import {toast} from "react-toastify"; - -function affiliationReducer(affiliation, action) { - switch (action.type) { - case 'ADD': - return [ - ...affiliation, - action.payload - ] - case 'REMOVE': - return affiliation.filter(affiliation => affiliation.id !== action.payload) - case 'UPDATE_OR_ADD': - const index = affiliation.findIndex(affiliation => affiliation.id === action.payload.id) - if (index === -1) { - return [ - ...affiliation, - action.payload - ] - } else { - affiliation[index] = action.payload - return [...affiliation] - } - case 'SORT': - return affiliation.sort((a, b) => b.saison - a.saison) - default: - throw new Error() - } -} +import {SimpleReducer} from "../../../utils/SimpleReducer.jsx"; export function AffiliationCard({clubData}) { const setLoading = useLoadingSwitcher() const {data, error} = useFetch(`/affiliation/${clubData.id}`, setLoading, 1) const [modalAffiliation, setModal] = useState({id: -1, club: clubData.id}) - const [affiliations, dispatch] = useReducer(affiliationReducer, []) + const [affiliations, dispatch] = useReducer(SimpleReducer, []) useEffect(() => { if (!data) return for (const dataKey of data) { dispatch({type: 'UPDATE_OR_ADD', payload: dataKey}) } - dispatch({type: 'SORT'}) + dispatch({type: 'SORT', payload: (a, b) => b.saison - a.saison}) }, [data]); return
@@ -55,7 +29,7 @@ export function AffiliationCard({clubData}) {
Affiliation
@@ -92,7 +66,7 @@ function sendAffiliation(event, dispatch) { const formData = new FormData(event.target); toast.promise( - apiAxios.post(`/affiliation/${formData.get('membre')}`, formData), // TODO + apiAxios.put(`/affiliation/${formData.get('club')}?saison=${formData.get('saison')}`), { pending: "Enregistrement de l'affiliation en cours", success: "Affiliation enregistrée avec succès 🎉", @@ -100,12 +74,13 @@ function sendAffiliation(event, dispatch) { } ).then(data => { dispatch({type: 'UPDATE_OR_ADD', payload: data.data}) - dispatch({type: 'SORT'}) + dispatch({type: 'SORT', payload: (a, b) => b.saison - a.saison}) }) } function removeAffiliation(id, dispatch) { + if (id <= 0) return toast.promise( apiAxios.delete(`/affiliation/${id}`), { @@ -120,30 +95,21 @@ function removeAffiliation(id, dispatch) { function ModalContent({affiliation, dispatch}) { const [saison, setSaison] = useState(0) - const [validate, setValidate] = useState(false) - const [isNew, setNew] = useState(true) const setSeason = (event) => { setSaison(Number(event.target.value)) } - const handleValidateChange = (event) => { - setValidate(event.target.value === 'true'); - } useEffect(() => { - if (affiliation.id !== -1) { - setNew(false) + if (affiliation.id !== 0) { setSaison(affiliation.saison) - setValidate(affiliation.validate) } else { - setNew(true) setSaison(getSaison()) - setValidate(false) } }, [affiliation]); return
sendAffiliation(e, dispatch)}> - +

Edition de l'affiliation

// TODO + } +
- - - {isNew || } + + {affiliation.id <= 0 || }
-} - -function RadioGroupeOnOff({value, onChange, name, text}) { - return
- {text} - - - - -
; } \ No newline at end of file diff --git a/src/main/webapp/src/pages/admin/club/ClubPage.jsx b/src/main/webapp/src/pages/admin/club/ClubPage.jsx index b164fe5..93b7a40 100644 --- a/src/main/webapp/src/pages/admin/club/ClubPage.jsx +++ b/src/main/webapp/src/pages/admin/club/ClubPage.jsx @@ -52,11 +52,11 @@ export function ClubPage() {
-