feat: better error message

This commit is contained in:
Thibaut Valentin 2024-07-19 14:52:05 +02:00
parent a58dcdd08e
commit 47daa459e2
35 changed files with 327 additions and 89 deletions

View File

@ -4,6 +4,8 @@ import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm;
import fr.titionfire.ffsaf.utils.SequenceType; import fr.titionfire.ffsaf.utils.SequenceType;
@ -14,7 +16,6 @@ import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked; 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 jakarta.ws.rs.NotFoundException;
import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny;
@ -60,42 +61,42 @@ public class AffiliationService {
return Uni.createFrom().item(affModel) return Uni.createFrom().item(affModel)
.invoke(Unchecked.consumer(model -> { .invoke(Unchecked.consumer(model -> {
if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) { if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) {
throw new IllegalArgumentException("Saison not valid"); throw new DBadRequestException("Saison non valid");
} }
})) }))
.chain(() -> repositoryRequest.count("siret = ?1 and saison = ?2", affModel.getSiret(), .chain(() -> repositoryRequest.count("siret = ?1 and saison = ?2", affModel.getSiret(),
affModel.getSaison())) affModel.getSaison()))
.onItem().invoke(Unchecked.consumer(count -> { .onItem().invoke(Unchecked.consumer(count -> {
if (count != 0 && unique) { if (count != 0 && unique) {
throw new IllegalArgumentException("Affiliation request already exists"); throw new DBadRequestException("Demande d'affiliation déjà existante");
} }
})) }))
.chain(() -> clubRepository.find("SIRET = ?1", affModel.getSiret()).firstResult().chain(club -> .chain(() -> clubRepository.find("SIRET = ?1", affModel.getSiret()).firstResult().chain(club ->
repository.count("club = ?1 and saison = ?2", club, affModel.getSaison()))) repository.count("club = ?1 and saison = ?2", club, affModel.getSaison())))
.onItem().invoke(Unchecked.consumer(count -> { .onItem().invoke(Unchecked.consumer(count -> {
if (count != 0) { if (count != 0) {
throw new IllegalArgumentException("Affiliation already exists"); throw new DBadRequestException("Affiliation déjà existante");
} }
})) }))
.map(o -> affModel) .map(o -> affModel)
.call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence", .call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence",
model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> { model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) { if (count == 0) {
throw new IllegalArgumentException("Licence membre n°1 inconnue"); throw new DBadRequestException("Licence membre n°1 inconnue");
} }
})) : Uni.createFrom().nullItem()) })) : Uni.createFrom().nullItem())
) )
.call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence", .call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence",
model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> { model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) { if (count == 0) {
throw new IllegalArgumentException("Licence membre n°2 inconnue"); throw new DBadRequestException("Licence membre n°2 inconnue");
} }
})) : Uni.createFrom().nullItem()) })) : Uni.createFrom().nullItem())
) )
.call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence", .call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence",
model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> { model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) { if (count == 0) {
throw new IllegalArgumentException("Licence membre n°3 inconnue"); throw new DBadRequestException("Licence membre n°3 inconnue");
} }
})) : Uni.createFrom().nullItem()) })) : Uni.createFrom().nullItem())
); );
@ -104,7 +105,7 @@ public class AffiliationService {
public Uni<?> saveEdit(AffiliationRequestForm form) { public Uni<?> saveEdit(AffiliationRequestForm form) {
return pre_save(form, false) return pre_save(form, false)
.chain(model -> repositoryRequest.findById(form.getId()) .chain(model -> repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
.chain(origine -> { .chain(origine -> {
origine.setName(model.getName()); origine.setName(model.getName());
origine.setRNA(model.getRNA()); origine.setRNA(model.getRNA());
@ -144,7 +145,7 @@ public class AffiliationService {
public Uni<?> saveAdmin(AffiliationRequestSaveForm form) { public Uni<?> saveAdmin(AffiliationRequestSaveForm form) {
return repositoryRequest.findById(form.getId()) return repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
.map(model -> { .map(model -> {
model.setName(form.getName()); model.setName(form.getName());
model.setSiret(form.getSiret()); model.setSiret(form.getSiret());
@ -237,7 +238,7 @@ public class AffiliationService {
public Uni<?> accept(AffiliationRequestSaveForm form) { public Uni<?> accept(AffiliationRequestSaveForm form) {
return repositoryRequest.findById(form.getId()) return repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
.chain(req -> .chain(req ->
clubRepository.find("SIRET = ?1", form.getSiret()).firstResult() clubRepository.find("SIRET = ?1", form.getSiret()).firstResult()
.chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model)) .chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model))
@ -298,7 +299,7 @@ public class AffiliationService {
public Uni<SimpleReqAffiliation> getRequest(long id) { public Uni<SimpleReqAffiliation> getRequest(long id) {
return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel) return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel)
.onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) .onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
.call(out -> clubRepository.find("SIRET = ?1", out.getSiret()).firstResult().invoke(c -> { .call(out -> clubRepository.find("SIRET = ?1", out.getSiret()).firstResult().invoke(c -> {
if (c != null) { if (c != null) {
out.setClub(c.getId()); out.setClub(c.getId());
@ -322,7 +323,7 @@ public class AffiliationService {
public Uni<List<SimpleAffiliation>> getAffiliation(long id) { public Uni<List<SimpleAffiliation>> getAffiliation(long id) {
return clubRepository.findById(id) return clubRepository.findById(id)
.onItem().ifNull().failWith(new NotFoundException("Affiliation request not found")) .onItem().ifNull().failWith(new DNotFoundException("Club non trouvé"))
.call(model -> Mutiny.fetch(model.getAffiliations())) .call(model -> Mutiny.fetch(model.getAffiliations()))
.chain(model -> repositoryRequest.list("siret = ?1", model.getSIRET()) .chain(model -> repositoryRequest.list("siret = ?1", model.getSIRET())
.map(reqs -> reqs.stream().map(req -> .map(reqs -> reqs.stream().map(req ->
@ -334,10 +335,10 @@ public class AffiliationService {
public Uni<SimpleAffiliation> setAffiliation(long id, int saison) { public Uni<SimpleAffiliation> setAffiliation(long id, int saison) {
return clubRepository.findById(id) return clubRepository.findById(id)
.onItem().ifNull().failWith(new NotFoundException("Club non trouver")) .onItem().ifNull().failWith(new DNotFoundException("Club non trouvé"))
.invoke(Unchecked.consumer(club -> { .invoke(Unchecked.consumer(club -> {
if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) { if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) {
throw new IllegalArgumentException("Affiliation deja existante"); throw new DBadRequestException("Affiliation déjà existante");
} }
})) }))
.chain(club -> .chain(club ->

View File

@ -13,6 +13,9 @@ import fr.titionfire.ffsaf.net2.request.SReqClub;
import fr.titionfire.ffsaf.rest.data.DeskMember; import fr.titionfire.ffsaf.rest.data.DeskMember;
import fr.titionfire.ffsaf.rest.data.RenewAffData; import fr.titionfire.ffsaf.rest.data.RenewAffData;
import fr.titionfire.ffsaf.rest.data.SimpleClubList; import fr.titionfire.ffsaf.rest.data.SimpleClubList;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.rest.from.FullClubForm; import fr.titionfire.ffsaf.rest.from.FullClubForm;
import fr.titionfire.ffsaf.rest.from.PartClubForm; import fr.titionfire.ffsaf.rest.from.PartClubForm;
import fr.titionfire.ffsaf.utils.*; import fr.titionfire.ffsaf.utils.*;
@ -26,9 +29,6 @@ import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked; 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 jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.NotFoundException;
import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.jwt.JsonWebToken; import org.eclipse.microprofile.jwt.JsonWebToken;
import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny;
@ -105,7 +105,7 @@ public class ClubService {
.call(result -> query.count().invoke(result::setResult_count)) .call(result -> query.count().invoke(result::setResult_count))
.call(result -> query.pageCount() .call(result -> query.pageCount()
.invoke(Unchecked.consumer(pages -> { .invoke(Unchecked.consumer(pages -> {
if (page > pages) throw new BadRequestException(); if (page > pages) throw new DBadRequestException("Page out of range");
})) }))
.invoke(result::setPage_count)) .invoke(result::setPage_count))
.call(result -> query.page(Page.of(page, limit)).list() .call(result -> query.page(Page.of(page, limit)).list()
@ -124,7 +124,7 @@ public class ClubService {
public Uni<ClubModel> getOfUser(JsonWebToken idToken) { public Uni<ClubModel> getOfUser(JsonWebToken idToken) {
return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> { return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null) if (m == null || m.getClub() == null)
throw new NotFoundException("Club not found"); throw new DNotFoundException("Club non trouvé");
})) }))
.map(MembreModel::getClub) .map(MembreModel::getClub)
.call(club -> Mutiny.fetch(club.getContact())); .call(club -> Mutiny.fetch(club.getContact()));
@ -146,9 +146,9 @@ public class ClubService {
return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> { return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null) if (m == null || m.getClub() == null)
throw new NotFoundException("Club not found"); throw new DNotFoundException("Club non trouvé");
if (!GroupeUtils.isInClubGroup(m.getClub().getId(), idToken)) if (!GroupeUtils.isInClubGroup(m.getClub().getId(), idToken))
throw new ForbiddenException(); throw new DForbiddenException();
})) }))
.map(MembreModel::getClub) .map(MembreModel::getClub)
.call(club -> Mutiny.fetch(club.getContact())) .call(club -> Mutiny.fetch(club.getContact()))
@ -159,7 +159,7 @@ public class ClubService {
try { try {
club.setContact(MAPPER.readValue(form.getContact(), typeRef)); club.setContact(MAPPER.readValue(form.getContact(), typeRef));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new BadRequestException(); throw new DBadRequestException("Erreur de format des contacts");
} }
club.setTraining_location(form.getTraining_location()); club.setTraining_location(form.getTraining_location());
@ -191,7 +191,7 @@ public class ClubService {
try { try {
m.setContact(MAPPER.readValue(input.getContact(), typeRef)); m.setContact(MAPPER.readValue(input.getContact(), typeRef));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new BadRequestException(); throw new DBadRequestException("Erreur de format des contacts");
} }
} }
return Panache.withTransaction(() -> repository.persist(m)); return Panache.withTransaction(() -> repository.persist(m));

View File

@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.data.model.ClubModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.utils.*; import fr.titionfire.ffsaf.utils.*;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
@ -78,7 +79,7 @@ public class KeycloakService {
public Uni<String> getUserFromMember(MembreModel membreModel) { public Uni<String> getUserFromMember(MembreModel membreModel) {
if (membreModel.getUserId() == null) { if (membreModel.getUserId() == null) {
return Uni.createFrom() return Uni.createFrom()
.failure(new NullPointerException("No keycloak user linked to the user id=" + membreModel.getId())); .failure(new DInternalError("No keycloak user linked to the user id=" + membreModel.getId()));
} }
return Uni.createFrom().item(membreModel::getUserId); return Uni.createFrom().item(membreModel::getUserId);
} }

View File

@ -5,6 +5,7 @@ import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.repository.CombRepository; import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.data.repository.LicenceRepository; import fr.titionfire.ffsaf.data.repository.LicenceRepository;
import fr.titionfire.ffsaf.data.repository.SequenceRepository; import fr.titionfire.ffsaf.data.repository.SequenceRepository;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.from.LicenceForm; import fr.titionfire.ffsaf.rest.from.LicenceForm;
import fr.titionfire.ffsaf.utils.SequenceType; import fr.titionfire.ffsaf.utils.SequenceType;
import fr.titionfire.ffsaf.utils.Utils; import fr.titionfire.ffsaf.utils.Utils;
@ -14,7 +15,6 @@ import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked; 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 jakarta.ws.rs.BadRequestException;
import org.eclipse.microprofile.jwt.JsonWebToken; import org.eclipse.microprofile.jwt.JsonWebToken;
import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny;
@ -91,7 +91,7 @@ public class LicenceService {
return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count() return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count()
.invoke(Unchecked.consumer(count -> { .invoke(Unchecked.consumer(count -> {
if (count > 0) if (count > 0)
throw new BadRequestException(); throw new DBadRequestException("Licence déjà demandée");
})).chain(__ -> combRepository.findById(id).chain(combRepository -> { })).chain(__ -> combRepository.findById(id).chain(combRepository -> {
LicenceModel model = new LicenceModel(); LicenceModel model = new LicenceModel();
model.setMembre(combRepository); model.setMembre(combRepository);

View File

@ -11,6 +11,8 @@ import fr.titionfire.ffsaf.net2.request.SReqComb;
import fr.titionfire.ffsaf.rest.data.MeData; import fr.titionfire.ffsaf.rest.data.MeData;
import fr.titionfire.ffsaf.rest.data.SimpleLicence; import fr.titionfire.ffsaf.rest.data.SimpleLicence;
import fr.titionfire.ffsaf.rest.data.SimpleMembre; import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.from.ClubMemberForm; import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
import fr.titionfire.ffsaf.rest.from.FullMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm;
import fr.titionfire.ffsaf.utils.*; import fr.titionfire.ffsaf.utils.*;
@ -25,8 +27,6 @@ import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked; 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 jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.ForbiddenException;
import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.jwt.JsonWebToken; import org.eclipse.microprofile.jwt.JsonWebToken;
import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny;
@ -101,7 +101,7 @@ public class MembreService {
.call(result -> query.count().invoke(result::setResult_count)) .call(result -> query.count().invoke(result::setResult_count))
.call(result -> query.pageCount() .call(result -> query.pageCount()
.invoke(Unchecked.consumer(pages -> { .invoke(Unchecked.consumer(pages -> {
if (page > pages) throw new BadRequestException(); if (page > pages) throw new DBadRequestException("Page out of range");
})) }))
.invoke(result::setPage_count)) .invoke(result::setPage_count))
.call(result -> query.page(Page.of(page, limit)).list() .call(result -> query.page(Page.of(page, limit)).list()
@ -155,7 +155,7 @@ public class MembreService {
return repository.findById(id) return repository.findById(id)
.invoke(Unchecked.consumer(membreModel -> { .invoke(Unchecked.consumer(membreModel -> {
if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken)) if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
throw new ForbiddenException(); throw new DForbiddenException();
})) }))
.invoke(Unchecked.consumer(membreModel -> { .invoke(Unchecked.consumer(membreModel -> {
RoleAsso source = RoleAsso.MEMBRE; RoleAsso source = RoleAsso.MEMBRE;
@ -163,7 +163,7 @@ public class MembreService {
else if (securityIdentity.getRoles().contains("club_secretaire")) source = RoleAsso.SECRETAIRE; else if (securityIdentity.getRoles().contains("club_secretaire")) source = RoleAsso.SECRETAIRE;
else if (securityIdentity.getRoles().contains("club_respo_intra")) source = RoleAsso.MEMBREBUREAU; else if (securityIdentity.getRoles().contains("club_respo_intra")) source = RoleAsso.MEMBREBUREAU;
if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level >= source.level) if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level >= source.level)
throw new ForbiddenException(); throw new DForbiddenException("Permission insuffisante");
})) }))
.onItem().transformToUni(target -> { .onItem().transformToUni(target -> {
target.setFname(membre.getFname()); target.setFname(membre.getFname());
@ -225,12 +225,12 @@ public class MembreService {
return repository.findById(id) return repository.findById(id)
.invoke(Unchecked.consumer(membreModel -> { .invoke(Unchecked.consumer(membreModel -> {
if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken)) if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
throw new ForbiddenException(); throw new DForbiddenException();
})) }))
.call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count() .call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count()
.invoke(Unchecked.consumer(l -> { .invoke(Unchecked.consumer(l -> {
if (l > 0) if (l > 0)
throw new BadRequestException(); throw new DBadRequestException("Impossible de supprimer un membre avec des licences");
}))) })))
.call(membreModel -> (membreModel.getUserId() != null) ? .call(membreModel -> (membreModel.getUserId() != null) ?
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem()) keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())

View File

@ -4,6 +4,7 @@ import fr.titionfire.ffsaf.domain.service.AffiliationService;
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliationResume; import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliationResume;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm; import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm;
import fr.titionfire.ffsaf.utils.GroupeUtils; import fr.titionfire.ffsaf.utils.GroupeUtils;
@ -42,7 +43,7 @@ public class AffiliationEndpoints {
Consumer<Long> checkPerm = Unchecked.consumer(id -> { Consumer<Long> checkPerm = Unchecked.consumer(id -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken)) if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken))
throw new ForbiddenException(); throw new DForbiddenException();
}); });
@GET @GET
@ -68,7 +69,7 @@ public class AffiliationEndpoints {
public Uni<SimpleReqAffiliation> getAffRequest(@PathParam("id") long id) { public Uni<SimpleReqAffiliation> getAffRequest(@PathParam("id") long id) {
return service.getRequest(id).invoke(Unchecked.consumer(o -> { return service.getRequest(id).invoke(Unchecked.consumer(o -> {
if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin")) if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
throw new ForbiddenException(); throw new DForbiddenException();
})).invoke(o -> checkPerm.accept(o.getClub())); })).invoke(o -> checkPerm.accept(o.getClub()));
} }
@ -79,7 +80,7 @@ public class AffiliationEndpoints {
public Uni<?> getDelAffRequest(@PathParam("id") long id) { public Uni<?> getDelAffRequest(@PathParam("id") long id) {
return service.getRequest(id).invoke(Unchecked.consumer(o -> { return service.getRequest(id).invoke(Unchecked.consumer(o -> {
if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin")) if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
throw new ForbiddenException(); throw new DForbiddenException();
})).invoke(o -> checkPerm.accept(o.getClub())) })).invoke(o -> checkPerm.accept(o.getClub()))
.chain(o -> service.deleteReqAffiliation(id)); .chain(o -> service.deleteReqAffiliation(id));
} }

View File

@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.rest.client.SirenService; import fr.titionfire.ffsaf.rest.client.SirenService;
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot; import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.*; import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
@ -20,7 +21,7 @@ public class AssoEndpoints {
return sirenService.get_unite(siren).onFailure().transform(throwable -> { return sirenService.get_unite(siren).onFailure().transform(throwable -> {
if (throwable instanceof WebApplicationException exception) { if (throwable instanceof WebApplicationException exception) {
if (exception.getResponse().getStatus() == 400) if (exception.getResponse().getStatus() == 400)
return new BadRequestException("Not found"); return new DNotFoundException("Siret introuvable");
} }
return throwable; return throwable;
}); });

View File

@ -7,6 +7,8 @@ import fr.titionfire.ffsaf.rest.data.DeskMember;
import fr.titionfire.ffsaf.rest.data.RenewAffData; import fr.titionfire.ffsaf.rest.data.RenewAffData;
import fr.titionfire.ffsaf.rest.data.SimpleClub; import fr.titionfire.ffsaf.rest.data.SimpleClub;
import fr.titionfire.ffsaf.rest.data.SimpleClubList; import fr.titionfire.ffsaf.rest.data.SimpleClubList;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.rest.from.FullClubForm; import fr.titionfire.ffsaf.rest.from.FullClubForm;
import fr.titionfire.ffsaf.rest.from.PartClubForm; import fr.titionfire.ffsaf.rest.from.PartClubForm;
import fr.titionfire.ffsaf.utils.Contact; import fr.titionfire.ffsaf.utils.Contact;
@ -50,11 +52,11 @@ public class ClubEndpoints {
Consumer<ClubModel> checkPerm = Unchecked.consumer(clubModel -> { Consumer<ClubModel> checkPerm = Unchecked.consumer(clubModel -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(clubModel.getId(), if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(clubModel.getId(),
idToken)) idToken))
throw new ForbiddenException(); throw new DForbiddenException();
}); });
Consumer<Long> checkPerm2 = Unchecked.consumer(id -> { Consumer<Long> checkPerm2 = Unchecked.consumer(id -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken)) if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken))
throw new ForbiddenException(); throw new DForbiddenException();
}); });
@GET @GET
@ -112,7 +114,8 @@ public class ClubEndpoints {
if (input.getLogo().length > 0) if (input.getLogo().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub" return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub"
)).invoke(Unchecked.consumer(out -> { )).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})); }));
else else
return Uni.createFrom().nullItem(); return Uni.createFrom().nullItem();
@ -120,7 +123,8 @@ public class ClubEndpoints {
if (input.getStatus().length > 0) if (input.getStatus().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus" return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus"
)).invoke(Unchecked.consumer(out -> { )).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})); }));
else else
return Uni.createFrom().nullItem(); return Uni.createFrom().nullItem();

View File

@ -4,6 +4,8 @@ import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.service.MembreService; import fr.titionfire.ffsaf.domain.service.MembreService;
import fr.titionfire.ffsaf.rest.data.MeData; import fr.titionfire.ffsaf.rest.data.MeData;
import fr.titionfire.ffsaf.rest.data.SimpleMembre; import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.rest.from.ClubMemberForm; import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
import fr.titionfire.ffsaf.rest.from.FullMemberForm; import fr.titionfire.ffsaf.rest.from.FullMemberForm;
import fr.titionfire.ffsaf.utils.GroupeUtils; import fr.titionfire.ffsaf.utils.GroupeUtils;
@ -46,7 +48,7 @@ public class CombEndpoints {
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> { Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup( if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(
membreModel.getClub().getId(), idToken)) membreModel.getClub().getId(), idToken))
throw new ForbiddenException(); throw new DForbiddenException();
}); });
@GET @GET
@ -110,12 +112,14 @@ public class CombEndpoints {
public Uni<String> setAdminMembre(@PathParam("id") long id, FullMemberForm input) { public Uni<String> setAdminMembre(@PathParam("id") long id, FullMemberForm input) {
return membreService.update(id, input) return membreService.update(id, input)
.invoke(Unchecked.consumer(out -> { .invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})).chain(() -> { })).chain(() -> {
if (input.getPhoto_data().length > 0) if (input.getPhoto_data().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre" return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
)).invoke(Unchecked.consumer(out -> { )).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})); }));
else else
return Uni.createFrom().nullItem(); return Uni.createFrom().nullItem();
@ -160,7 +164,8 @@ public class CombEndpoints {
if (input.getPhoto_data().length > 0) if (input.getPhoto_data().length > 0)
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre" return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
)).invoke(Unchecked.consumer(out -> { )).invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); if (!out.equals("OK"))
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
})); }));
else else
return Uni.createFrom().nullItem(); return Uni.createFrom().nullItem();

View File

@ -1,6 +1,7 @@
package fr.titionfire.ffsaf.rest; package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.KeycloakService; import fr.titionfire.ffsaf.domain.service.KeycloakService;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.from.MemberPermForm; import fr.titionfire.ffsaf.rest.from.MemberPermForm;
import fr.titionfire.ffsaf.utils.GroupeUtils; import fr.titionfire.ffsaf.utils.GroupeUtils;
import fr.titionfire.ffsaf.utils.Pair; import fr.titionfire.ffsaf.utils.Pair;
@ -38,7 +39,7 @@ public class CompteEndpoints {
return service.fetchCompte(id).call(pair -> vertx.getOrCreateContext().executeBlocking(() -> { return service.fetchCompte(id).call(pair -> vertx.getOrCreateContext().executeBlocking(() -> {
if (!securityIdentity.getRoles().contains("federation_admin") && pair.getKey().groups().stream().map(GroupRepresentation::getPath) if (!securityIdentity.getRoles().contains("federation_admin") && pair.getKey().groups().stream().map(GroupRepresentation::getPath)
.noneMatch(s -> s.startsWith("/club/") && GroupeUtils.contains(s, accessToken))) .noneMatch(s -> s.startsWith("/club/") && GroupeUtils.contains(s, accessToken)))
throw new ForbiddenException(); throw new DForbiddenException();
return pair; return pair;
})).map(Pair::getValue); })).map(Pair::getValue);
} }

View File

@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.service.LicenceService; import fr.titionfire.ffsaf.domain.service.LicenceService;
import fr.titionfire.ffsaf.rest.data.SimpleLicence; import fr.titionfire.ffsaf.rest.data.SimpleLicence;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.from.LicenceForm; import fr.titionfire.ffsaf.rest.from.LicenceForm;
import fr.titionfire.ffsaf.utils.GroupeUtils; import fr.titionfire.ffsaf.utils.GroupeUtils;
import io.quarkus.oidc.IdToken; import io.quarkus.oidc.IdToken;
@ -33,7 +34,7 @@ public class LicenceEndpoints {
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> { Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken)) if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
throw new ForbiddenException(); throw new DForbiddenException();
}); });
@GET @GET

View File

@ -0,0 +1,14 @@
package fr.titionfire.ffsaf.rest.exception;
import jakarta.ws.rs.core.Response;
import java.io.Serial;
public class DBadRequestException extends DetailException {
@Serial
private static final long serialVersionUID = 7518556311032332135L;
public DBadRequestException(String message) {
super(Response.Status.BAD_REQUEST, message);
}
}

View File

@ -0,0 +1,18 @@
package fr.titionfire.ffsaf.rest.exception;
import jakarta.ws.rs.core.Response;
import java.io.Serial;
public class DForbiddenException extends DetailException {
@Serial
private static final long serialVersionUID = 8408920537659758038L;
public DForbiddenException() {
this("Accès a la ressource interdite");
}
public DForbiddenException(String message) {
super(Response.Status.FORBIDDEN, message);
}
}

View File

@ -0,0 +1,15 @@
package fr.titionfire.ffsaf.rest.exception;
import jakarta.ws.rs.core.Response;
import java.io.Serial;
public class DInternalError extends DetailException {
@Serial
private static final long serialVersionUID = -3635595157694180842L;
public DInternalError(String message) {
super(Response.Status.INTERNAL_SERVER_ERROR, message);
}
}

View File

@ -0,0 +1,15 @@
package fr.titionfire.ffsaf.rest.exception;
import jakarta.ws.rs.core.Response;
import java.io.Serial;
public class DNotFoundException extends DetailException{
@Serial
private static final long serialVersionUID = -5193524134675732376L;
public DNotFoundException(String message) {
super(Response.Status.NOT_FOUND, message);
}
}

View File

@ -0,0 +1,20 @@
package fr.titionfire.ffsaf.rest.exception;
import jakarta.ws.rs.core.Response;
import lombok.Getter;
import java.io.Serial;
@Getter
public class DetailException extends Exception {
@Serial
private static final long serialVersionUID = 5349921926328753676L;
private final Response.Status status;
public DetailException(Response.Status status, String message) {
super(message);
this.status = status;
}
}

View File

@ -0,0 +1,19 @@
package fr.titionfire.ffsaf.rest.exception;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
@Provider
public class DetailExceptionMapper implements ExceptionMapper<DetailException> {
@Override
public Response toResponse(DetailException e) {
return Response.status(e.getStatus())
.entity(e.getMessage())
.type(MediaType.TEXT_PLAIN_TYPE)
.build();
}
}

View File

@ -0,0 +1,20 @@
package fr.titionfire.ffsaf.rest.exception;
import fr.titionfire.ffsaf.utils.KeycloakException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
@Provider
public class KeycloakExceptionMapper implements ExceptionMapper<KeycloakException> {
@Override
public Response toResponse(KeycloakException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur du gestionnaire d'identité: " + e.getMessage())
.type(MediaType.TEXT_PLAIN_TYPE)
.build();
}
}

View File

@ -80,6 +80,14 @@ function Root() {
check_validity(data => dispatch({type: 'init', val: data})) check_validity(data => dispatch({type: 'init', val: data}))
}, []); }, []);
useEffect(() => {
const interval = setInterval(() => {
check_validity(data => dispatch({type: 'update', val: data}))
}, 1000 * 60 * 9)
return () => clearInterval(interval)
}, []);
return <> return <>
<header> <header>
<Nav/> <Nav/>

View File

@ -32,6 +32,8 @@ function authReducer(auth, action) {
case 'update': { case 'update': {
return { return {
...auth, ...auth,
is_authenticated: action.val.state,
userinfo: action.val.userinfo
// data: {realm_access: {roles: ["federation_admin"]}} // data: {realm_access: {roles: ["federation_admin"]}}
// data: JSON.parse(atob(action.token.split('.')[1])) // data: JSON.parse(atob(action.token.split('.')[1]))
} }

View File

@ -8,9 +8,7 @@ export function useCountries(country = 'fr') {
useEffect(() => { useEffect(() => {
if (countries[country] === undefined) { if (countries[country] === undefined) {
console.log('fetch')
apiAxios.get(`/countries/${country}/${country}`).then(data => { apiAxios.get(`/countries/${country}/${country}`).then(data => {
console.log(data.data)
countries[country] = data.data countries[country] = data.data
setOut(data.data) setOut(data.data)
}) })

View File

@ -1,5 +1,5 @@
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {apiAxios, getSaison} from "../utils/Tools.js"; import {apiAxios, errFormater, getSaison} from "../utils/Tools.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {useLocation, useNavigate} from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
@ -89,7 +89,11 @@ export function DemandeAff() {
{ {
pending: "Annulation de la demande d'affiliation en cours", pending: "Annulation de la demande d'affiliation en cours",
success: "Demande d'affiliation annulée avec succès 🎉", success: "Demande d'affiliation annulée avec succès 🎉",
error: "Échec de l'annulation de la demande d'affiliation 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'annulation de la demande d'affiliation")
}
}
} }
).then(_ => { ).then(_ => {
navigate("/club/me") navigate("/club/me")
@ -102,7 +106,11 @@ export function DemandeAff() {
{ {
pending: "Enregistrement des modifications en cours", pending: "Enregistrement des modifications en cours",
success: "Modifications enregistrées avec succès 🎉", success: "Modifications enregistrées avec succès 🎉",
error: "Échec de l'enregistrement des modifications 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'enregistrement des modifications")
}
}
} }
).then(_ => { ).then(_ => {
navigate("/club/me") navigate("/club/me")
@ -115,7 +123,11 @@ export function DemandeAff() {
{ {
pending: "Enregistrement de la demande d'affiliation en cours", pending: "Enregistrement de la demande d'affiliation en cours",
success: "Demande d'affiliation enregistrée avec succès 🎉", success: "Demande d'affiliation enregistrée avec succès 🎉",
error: "Échec de la demande d'affiliation 😕" error: {
render({data}) {
return errFormater(data, "Échec de la demande d'affiliation")
}
}
} }
).then(_ => { ).then(_ => {
navigate("/affiliation/ok") navigate("/affiliation/ok")
@ -208,7 +220,11 @@ function AssoInfo({initData, needFile}) {
{ {
pending: "Recherche de l'association en cours", pending: "Recherche de l'association en cours",
success: "Association trouvée avec succès 🎉", success: "Association trouvée avec succès 🎉",
error: "Échec de la recherche de l'association 😕" error: {
render({data}) {
return errFormater(data, "Échec de la recherche de l'association")
}
}
} }
).then(data => { ).then(data => {
const data2 = data.data.unite_legale const data2 = data.data.unite_legale

View File

@ -7,7 +7,7 @@ import {Input} from "../components/Input.jsx";
import {useLocation, useNavigate} from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
import {Checkbox} from "../components/MemberCustomFiels.jsx"; import {Checkbox} from "../components/MemberCustomFiels.jsx";
import axios from "axios"; import axios from "axios";
import {apiAxios} from "../utils/Tools.js"; import {apiAxios, errFormater} from "../utils/Tools.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {SearchBar} from "../components/SearchBar.jsx"; import {SearchBar} from "../components/SearchBar.jsx";
@ -58,7 +58,11 @@ export function MemberList({source}) {
{ {
pending: "Chargement des licences...", pending: "Chargement des licences...",
success: "Licences chargées", success: "Licences chargées",
error: "Impossible de charger les licences" error: {
render({data}) {
return errFormater(data, "Impossible de charger les licences")
}
}
}) })
.then(data => { .then(data => {
setLicenceData(data.data); setLicenceData(data.data);

View File

@ -3,7 +3,7 @@ import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx
import {useFetch} from "../../../hooks/useFetch.js"; import {useFetch} from "../../../hooks/useFetch.js";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {RoleList, TextField} from "../../../components/MemberCustomFiels.jsx"; 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";
@ -44,7 +44,11 @@ function Content({data, refresh}) {
{ {
pending: "Suppression de la demande d'affiliation en cours", pending: "Suppression de la demande d'affiliation en cours",
success: "Demande d'affiliation supprimée avec succès 🎉", success: "Demande d'affiliation supprimée avec succès 🎉",
error: "Échec de la suppression de la demande d'affiliation 😕" error: {
render({data}) {
return errFormater(data, "Échec de la suppression de la demande d'affiliation")
}
}
} }
).then(_ => { ).then(_ => {
navigate("/admin/affiliation/request") navigate("/admin/affiliation/request")
@ -128,7 +132,11 @@ function Content({data, refresh}) {
{ {
pending: "Enregistrement de la demande d'affiliation en cours", pending: "Enregistrement de la demande d'affiliation en cours",
success: "Demande d'affiliation enregistrée avec succès 🎉", success: "Demande d'affiliation enregistrée avec succès 🎉",
error: "Échec de l'enregistrement de la demande d'affiliation 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'enregistrement de la demande d'affiliation")
}
}
} }
).then(_ => { ).then(_ => {
refresh(`/affiliation/request/${data.id}`) refresh(`/affiliation/request/${data.id}`)
@ -139,7 +147,11 @@ function Content({data, refresh}) {
{ {
pending: "Acceptation de l'affiliation en cours", pending: "Acceptation de l'affiliation en cours",
success: "Affiliation acceptée avec succès 🎉", success: "Affiliation acceptée avec succès 🎉",
error: "Échec de l'acceptation de l'affiliation 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'acceptation de l'affiliation")
}
}
} }
).then(_ => { ).then(_ => {
navigate("/admin/affiliation/request") navigate("/admin/affiliation/request")

View File

@ -4,7 +4,7 @@ import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faEye, faPen} from "@fortawesome/free-solid-svg-icons"; import {faEye, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, getSaison} from "../../../utils/Tools.js"; import {apiAxios, errFormater, getSaison} from "../../../utils/Tools.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {SimpleReducer} from "../../../utils/SimpleReducer.jsx"; import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
import {useNavigate} from "react-router-dom"; import {useNavigate} from "react-router-dom";
@ -71,7 +71,11 @@ function sendAffiliation(event, dispatch) {
{ {
pending: "Enregistrement de l'affiliation en cours", pending: "Enregistrement de l'affiliation en cours",
success: "Affiliation enregistrée avec succès 🎉", success: "Affiliation enregistrée avec succès 🎉",
error: "Échec de l'enregistrement de l'affiliation 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'enregistrement de l'affiliation")
}
}
} }
).then(data => { ).then(data => {
dispatch({type: 'UPDATE_OR_ADD', payload: data.data}) dispatch({type: 'UPDATE_OR_ADD', payload: data.data})
@ -87,7 +91,11 @@ function removeAffiliation(id, dispatch) {
{ {
pending: "Suppression de l'affiliation en cours", pending: "Suppression de l'affiliation en cours",
success: "Affiliation supprimée avec succès 🎉", success: "Affiliation supprimée avec succès 🎉",
error: "Échec de la suppression de l'affiliation 😕" error: {
render({data}) {
return errFormater(data, "Échec de la suppression de l'affiliation")
}
}
} }
).then(_ => { ).then(_ => {
dispatch({type: 'REMOVE', payload: id}) dispatch({type: 'REMOVE', payload: id})

View File

@ -3,7 +3,7 @@ import {useEffect, useState} from "react";
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js"; import {useFetch} from "../../../hooks/useFetch.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {Checkbox} from "../../../components/MemberCustomFiels.jsx"; import {Checkbox} from "../../../components/MemberCustomFiels.jsx";
import {ThreeDots} from "react-loader-spinner"; import {ThreeDots} from "react-loader-spinner";
@ -55,7 +55,11 @@ export function ClubList() {
{ {
pending: "Chargement des affiliation...", pending: "Chargement des affiliation...",
success: "Affiliation chargées", success: "Affiliation chargées",
error: "Impossible de charger les affiliations" error: {
render({data}) {
return errFormater(data, "Impossible de charger les affiliations")
}
}
}) })
.then(data => { .then(data => {
setAffiliationData(data.data); setAffiliationData(data.data);

View File

@ -2,7 +2,7 @@ import {useNavigate, useParams} from "react-router-dom";
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js"; import {useFetch} from "../../../hooks/useFetch.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx"; import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {AffiliationCard} from "./AffiliationCard.jsx"; import {AffiliationCard} from "./AffiliationCard.jsx";
@ -30,7 +30,11 @@ export function ClubPage() {
{ {
pending: "Suppression du club en cours...", pending: "Suppression du club en cours...",
success: "Club supprimé avec succès 🎉", success: "Club supprimé avec succès 🎉",
error: "Échec de la suppression du club 😕" error: {
render({data}) {
return errFormater(data, "Échec de la suppression du club")
}
},
} }
).then(_ => { ).then(_ => {
navigate("/admin/club") navigate("/admin/club")
@ -85,7 +89,11 @@ function InformationForm({data}) {
{ {
pending: "Enregistrement du club en cours", pending: "Enregistrement du club en cours",
success: "Club enregistrée avec succès 🎉", success: "Club enregistrée avec succès 🎉",
error: "Échec de l'enregistrement du club 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'enregistrement du club")
}
},
} }
) )
} }

View File

@ -2,7 +2,7 @@ import {useNavigate} from "react-router-dom";
import {LoadingProvider} from "../../../hooks/useLoading.jsx"; import {LoadingProvider} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js"; import {useFetch} from "../../../hooks/useFetch.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx"; import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
import {useRef, useState} from "react"; import {useRef, useState} from "react";
@ -48,7 +48,11 @@ function InformationForm() {
{ {
pending: "Création du club en cours", pending: "Création du club en cours",
success: "Club créé avec succès 🎉", success: "Club créé avec succès 🎉",
error: "Échec de la création du club 😕" error: {
render({data}) {
return errFormater(data, "Échec de la création du club")
}
}
} }
).then(data => { ).then(data => {
navigate(`/admin/club/${data.data}`); navigate(`/admin/club/${data.data}`);

View File

@ -1,5 +1,5 @@
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js"; import {useFetch} from "../../../hooks/useFetch.js";
import {ColoredCircle} from "../../../components/ColoredCircle.jsx"; import {ColoredCircle} from "../../../components/ColoredCircle.jsx";
@ -8,15 +8,16 @@ import {AxiosError} from "../../../components/AxiosError.jsx";
export function CompteInfo({userData}) { export function CompteInfo({userData}) {
const creatAccount = () => { const creatAccount = () => {
let err = {};
toast.promise( toast.promise(
apiAxios.put(`/compte/${userData.id}/init`).catch(e => { apiAxios.put(`/compte/${userData.id}/init`),
err = e
}),
{ {
pending: 'Création du compte en cours', pending: 'Création du compte en cours',
success: 'Compte créé avec succès 🎉', success: 'Compte créé avec succès 🎉',
error: 'Échec de la création du compte 😕 (code: ' + err.response.status + ')' error: {
render({data}) {
return errFormater(data, "Échec de la création du compte")
}
}
} }
) )
} }
@ -28,7 +29,11 @@ export function CompteInfo({userData}) {
{ {
pending: "Définition de l'identifient en cours", pending: "Définition de l'identifient en cours",
success: "Identifient défini avec succès 🎉", success: "Identifient défini avec succès 🎉",
error: "Échec de la définition de l'identifient 😕 " error: {
render({data}) {
return errFormater(data, "Échec de la définition de l'identifient")
}
}
} }
) )
} }

View File

@ -5,7 +5,7 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faPen} from "@fortawesome/free-solid-svg-icons"; import {faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {CheckField, TextField} from "../../../components/MemberCustomFiels.jsx"; import {CheckField, TextField} from "../../../components/MemberCustomFiels.jsx";
import {apiAxios, getSaison} from "../../../utils/Tools.js"; import {apiAxios, errFormater, getSaison} from "../../../utils/Tools.js";
import {Input} from "../../../components/Input.jsx"; import {Input} from "../../../components/Input.jsx";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
@ -98,7 +98,11 @@ function sendLicence(event, dispatch) {
{ {
pending: "Enregistrement de la licence en cours", pending: "Enregistrement de la licence en cours",
success: "Licence enregistrée avec succès 🎉", success: "Licence enregistrée avec succès 🎉",
error: "Échec de l'enregistrement de la licence 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'enregistrement de la licence")
}
}
} }
).then(data => { ).then(data => {
dispatch({type: 'UPDATE_OR_ADD', payload: data.data}) dispatch({type: 'UPDATE_OR_ADD', payload: data.data})
@ -113,7 +117,11 @@ function removeLicence(id, dispatch) {
{ {
pending: "Suppression de la licence en cours", pending: "Suppression de la licence en cours",
success: "Licence supprimée avec succès 🎉", success: "Licence supprimée avec succès 🎉",
error: "Échec de la suppression de la licence 😕" error: {
render({data}) {
return errFormater(data, "Échec de la suppression de la licence")
}
}
} }
).then(_ => { ).then(_ => {
dispatch({type: 'REMOVE', payload: id}) dispatch({type: 'REMOVE', payload: id})

View File

@ -7,7 +7,7 @@ import {PremForm} from "./PremForm.jsx";
import {InformationForm} from "./InformationForm.jsx"; import {InformationForm} from "./InformationForm.jsx";
import {LicenceCard} from "./LicenceCard.jsx"; import {LicenceCard} from "./LicenceCard.jsx";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx"; import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
const vite_url = import.meta.env.VITE_URL; const vite_url = import.meta.env.VITE_URL;
@ -25,7 +25,11 @@ export function MemberPage() {
{ {
pending: "Suppression du compte en cours...", pending: "Suppression du compte en cours...",
success: "Compte supprimé avec succès 🎉", success: "Compte supprimé avec succès 🎉",
error: "Échec de la suppression du compte 😕" error: {
render({data}) {
return errFormater(data, "Échec de la suppression du compte")
}
}
} }
).then(_ => { ).then(_ => {
navigate("/admin/member") navigate("/admin/member")

View File

@ -2,7 +2,7 @@ import {useNavigate, useParams} from "react-router-dom";
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useFetch} from "../../../hooks/useFetch.js"; import {useFetch} from "../../../hooks/useFetch.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx"; import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {AffiliationCard, BureauCard} from "./AffiliationCard.jsx"; import {AffiliationCard, BureauCard} from "./AffiliationCard.jsx";
@ -58,7 +58,11 @@ function InformationForm({data}) {
{ {
pending: "Enregistrement des modifications en cours", pending: "Enregistrement des modifications en cours",
success: "Modifications enregistrées avec succès 🎉", success: "Modifications enregistrées avec succès 🎉",
error: "Échec de l'enregistrement des modifications 😕" error: {
render({data}) {
return errFormater(data, "Échec de l'enregistrement des modifications")
}
}
} }
) )
} }

View File

@ -4,7 +4,7 @@ import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faInfo, faPen} from "@fortawesome/free-solid-svg-icons"; import {faInfo, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx"; import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, getSaison} from "../../../utils/Tools.js"; import {apiAxios, errFormater, getSaison} from "../../../utils/Tools.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {ColoredText} from "../../../components/ColoredCircle.jsx"; import {ColoredText} from "../../../components/ColoredCircle.jsx";
@ -95,7 +95,11 @@ function sendLicence(event, dispatch) {
{ {
pending: "Enregistrement de la demande de licence en cours", pending: "Enregistrement de la demande de licence en cours",
success: "Demande de licence enregistrée avec succès 🎉", success: "Demande de licence enregistrée avec succès 🎉",
error: "Échec de la demande de licence 😕" error: {
render({data}) {
return errFormater(data, "Échec de la demande de licence")
}
}
} }
).then(data => { ).then(data => {
dispatch({type: 'UPDATE_OR_ADD', payload: data.data}) dispatch({type: 'UPDATE_OR_ADD', payload: data.data})
@ -110,7 +114,11 @@ function removeLicence(id, dispatch) {
{ {
pending: "Suppression de la demande en cours", pending: "Suppression de la demande en cours",
success: "Demande supprimée avec succès 🎉", success: "Demande supprimée avec succès 🎉",
error: "Échec de la suppression de la demande de licence 😕" error: {
render({data}) {
return errFormater(data, "Échec de la suppression de la demande de licence")
}
}
} }
).then(_ => { ).then(_ => {
dispatch({type: 'REMOVE', payload: id}) dispatch({type: 'REMOVE', payload: id})

View File

@ -6,7 +6,7 @@ import {CompteInfo} from "./CompteInfo.jsx";
import {InformationForm} from "./InformationForm.jsx"; import {InformationForm} from "./InformationForm.jsx";
import {LicenceCard} from "./LicenceCard.jsx"; import {LicenceCard} from "./LicenceCard.jsx";
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx"; import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
import {apiAxios} from "../../../utils/Tools.js"; import {apiAxios, errFormater} from "../../../utils/Tools.js";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
const vite_url = import.meta.env.VITE_URL; const vite_url = import.meta.env.VITE_URL;
@ -24,7 +24,11 @@ export function MemberPage() {
{ {
pending: "Suppression du compte en cours...", pending: "Suppression du compte en cours...",
success: "Compte supprimé avec succès 🎉", success: "Compte supprimé avec succès 🎉",
error: "Échec de la suppression du compte 😕" error: {
render({data}) {
return errFormater(data, "Échec de la suppression du compte")
}
}
} }
).then(_ => { ).then(_ => {
navigate("/club/member") navigate("/club/member")

View File

@ -7,6 +7,11 @@ export const apiAxios = axios.create({
}); });
apiAxios.defaults.headers.post['Accept'] = 'application/json; charset=UTF-8'; apiAxios.defaults.headers.post['Accept'] = 'application/json; charset=UTF-8';
export const errFormater = (data, msg) => {
return `${msg} (${data.response.statusText}: ${data.response.data}) 😕`
}
export function getCategoryFormBirthDate(birth_date, currentDate = new Date()) { export function getCategoryFormBirthDate(birth_date, currentDate = new Date()) {
const currentSaison = getSaison(currentDate) const currentSaison = getSaison(currentDate)
const birthYear = birth_date.getFullYear() const birthYear = birth_date.getFullYear()