Compare commits
32 Commits
d1c7f37a94
...
c7c7b3ba20
| Author | SHA1 | Date | |
|---|---|---|---|
| c7c7b3ba20 | |||
| f8976deb91 | |||
| eb9badb4a1 | |||
| 661fcdb16b | |||
| b2ad633b21 | |||
| 1b5bf8ba6c | |||
| bf75d9d036 | |||
| 9457c5749a | |||
| c7f56881cd | |||
| aebcd62aa9 | |||
| c2eecf4906 | |||
| 87b3bc12e0 | |||
| 3d3d63e58c | |||
| 81c115c655 | |||
| a7ba1d16a4 | |||
| 645949a2f6 | |||
| beb40db1b1 | |||
| 61a4af6ff1 | |||
| d9fc68298c | |||
| fccea5bf6a | |||
| ee476cd0e2 | |||
| b320d7db37 | |||
| 80fef98e07 | |||
| 7e80703c04 | |||
| cc4a3e4e06 | |||
| 9e28356f2c | |||
| 77d66813c7 | |||
| 7625da1d4b | |||
| 4262845074 | |||
| b84e10de44 | |||
| ac6563ac95 | |||
| baf57c3464 |
@ -76,6 +76,7 @@ jobs:
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
script: |
|
||||
cd ${{ secrets.TARGET_DIR }}
|
||||
docker logs ffsaf > "log/ffsaf_logs_$(date +"%Y-%m-%d_%H-%M-%S").log" 2>&1
|
||||
docker stop ffsaf
|
||||
docker rm ffsaf
|
||||
docker compose up --build -d ffsaf
|
||||
|
||||
@ -21,8 +21,7 @@ public class AffiliationRequestModel {
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
long siret;
|
||||
String RNA;
|
||||
String state_id;
|
||||
String address;
|
||||
String contact;
|
||||
|
||||
|
||||
@ -55,11 +55,8 @@ public class ClubModel implements LoggableModel {
|
||||
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris")
|
||||
String address;
|
||||
|
||||
@Schema(description = "RNA du club", example = "W123456789")
|
||||
String RNA;
|
||||
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234")
|
||||
Long SIRET;
|
||||
@Schema(description = "Numéro SIRET ou RNA du club", example = "12345678901234")
|
||||
String StateId;
|
||||
|
||||
@Schema(description = "Numéro d'affiliation du club", example = "12345")
|
||||
Long no_affiliation;
|
||||
|
||||
@ -30,8 +30,10 @@ public class LogModel {
|
||||
|
||||
Long target_id;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
String target_name;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
String message;
|
||||
|
||||
public enum ActionType {
|
||||
|
||||
@ -82,4 +82,22 @@ public class MembreModel implements LoggableModel {
|
||||
public LogModel.ObjectType getObjectType() {
|
||||
return LogModel.ObjectType.Membre;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MembreModel{" +
|
||||
"id=" + id +
|
||||
", userId='" + userId + '\'' +
|
||||
", lname='" + lname + '\'' +
|
||||
", fname='" + fname + '\'' +
|
||||
", categorie=" + categorie +
|
||||
", genre=" + genre +
|
||||
", licence=" + licence +
|
||||
", country='" + country + '\'' +
|
||||
", birth_date=" + birth_date +
|
||||
", email='" + email + '\'' +
|
||||
", role=" + role +
|
||||
", grade_arbitrage=" + grade_arbitrage +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ -38,6 +40,7 @@ public class RegisterModel {
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "club")
|
||||
@OnDelete(action = OnDeleteAction.SET_NULL)
|
||||
ClubModel club = null;
|
||||
|
||||
@Column(nullable = false, columnDefinition = "boolean default false")
|
||||
|
||||
@ -22,8 +22,7 @@ public class ClubEntity {
|
||||
private String training_location;
|
||||
private String training_day_time;
|
||||
private String contact_intern;
|
||||
private String RNA;
|
||||
private Long SIRET;
|
||||
private String StateId;
|
||||
private Long no_affiliation;
|
||||
private boolean international;
|
||||
|
||||
@ -41,8 +40,7 @@ public class ClubEntity {
|
||||
.training_location(model.getTraining_location())
|
||||
.training_day_time(model.getTraining_day_time())
|
||||
.contact_intern(model.getContact_intern())
|
||||
.RNA(model.getRNA())
|
||||
.SIRET(model.getSIRET())
|
||||
.StateId(model.getStateId())
|
||||
.no_affiliation(model.getNo_affiliation())
|
||||
.international(model.isInternational())
|
||||
.build();
|
||||
|
||||
@ -2,6 +2,8 @@ package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.*;
|
||||
import fr.titionfire.ffsaf.data.repository.*;
|
||||
import fr.titionfire.ffsaf.rest.client.SirenService;
|
||||
import fr.titionfire.ffsaf.rest.client.StateIdService;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
|
||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||
@ -21,15 +23,19 @@ import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
public class AffiliationService {
|
||||
private static final Logger LOGGER = Logger.getLogger(AffiliationService.class);
|
||||
|
||||
@Inject
|
||||
CombRepository combRepository;
|
||||
@ -58,6 +64,12 @@ public class AffiliationService {
|
||||
@Inject
|
||||
LoggerService ls;
|
||||
|
||||
@RestClient
|
||||
StateIdService stateIdService;
|
||||
|
||||
@RestClient
|
||||
SirenService sirenService;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
@ -71,6 +83,8 @@ public class AffiliationService {
|
||||
public Uni<AffiliationRequestModel> pre_save(AffiliationRequestForm form, boolean unique) {
|
||||
AffiliationRequestModel affModel = form.toModel();
|
||||
int currentSaison = Utils.getSaison();
|
||||
List<String> out = new ArrayList<>();
|
||||
out.add(affModel.getState_id());
|
||||
|
||||
return Uni.createFrom().item(affModel)
|
||||
.invoke(Unchecked.consumer(model -> {
|
||||
@ -78,14 +92,26 @@ public class AffiliationService {
|
||||
throw new DBadRequestException("Saison non valid");
|
||||
}
|
||||
}))
|
||||
.chain(() -> repositoryRequest.count("siret = ?1 and saison = ?2", affModel.getSiret(),
|
||||
affModel.getSaison()))
|
||||
.onItem().invoke(Unchecked.consumer(count -> {
|
||||
if (count != 0 && unique) {
|
||||
throw new DBadRequestException("Demande d'affiliation déjà existante");
|
||||
}
|
||||
}))
|
||||
.chain(() -> clubRepository.find("SIRET = ?1", affModel.getSiret()).firstResult().chain(club ->
|
||||
.chain(() -> ((affModel.getState_id().charAt(0) == 'W') ? stateIdService.get_rna(
|
||||
affModel.getState_id()) : sirenService.get_unite(affModel.getState_id())
|
||||
.chain(stateIdService::getAssoDataFromUnit)).onItem().transform(o -> {
|
||||
if (o.getRna() != null && !o.getRna().isBlank())
|
||||
out.add(o.getRna());
|
||||
if (o.getSiren() != null && !o.getSiren().isBlank())
|
||||
out.add(o.getSiren());
|
||||
if (o.getIdentite().getSiret_siege() != null && !o.getIdentite().getSiret_siege().isBlank())
|
||||
out.add(o.getIdentite().getSiret_siege());
|
||||
return out;
|
||||
}).onFailure().recoverWithItem(out)
|
||||
.chain(a -> repositoryRequest.count("state_id IN ?1 and saison = ?2",
|
||||
out, affModel.getSaison()))
|
||||
.onItem().invoke(Unchecked.consumer(count -> {
|
||||
if (count != 0 && unique) {
|
||||
throw new DBadRequestException("Demande d'affiliation déjà existante");
|
||||
}
|
||||
}))
|
||||
)
|
||||
.chain(() -> clubRepository.find("StateId IN ?1", out).firstResult().chain(club ->
|
||||
repository.count("club = ?1 and saison = ?2", club, affModel.getSaison())))
|
||||
.onItem().invoke(Unchecked.consumer(count -> {
|
||||
if (count != 0) {
|
||||
@ -122,7 +148,6 @@ public class AffiliationService {
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||
.chain(origine -> {
|
||||
origine.setName(model.getName());
|
||||
origine.setRNA(model.getRNA());
|
||||
origine.setAddress(model.getAddress());
|
||||
origine.setContact(model.getContact());
|
||||
origine.setM1_lname(model.getM1_lname());
|
||||
@ -146,6 +171,9 @@ public class AffiliationService {
|
||||
}
|
||||
|
||||
public Uni<String> save(AffiliationRequestForm form) {
|
||||
LOGGER.debug("Affiliation Request Created");
|
||||
LOGGER.debug(form.toString());
|
||||
|
||||
// noinspection ResultOfMethodCallIgnored,ReactiveStreamsUnusedPublisher
|
||||
return pre_save(form, true)
|
||||
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
|
||||
@ -169,12 +197,14 @@ public class AffiliationService {
|
||||
}
|
||||
|
||||
public Uni<?> saveAdmin(AffiliationRequestSaveForm form) {
|
||||
LOGGER.debug("Affiliation Request Saved");
|
||||
LOGGER.debug(form.toString());
|
||||
|
||||
return repositoryRequest.findById(form.getId())
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||
.map(model -> {
|
||||
model.setName(form.getName());
|
||||
model.setSiret(form.getSiret());
|
||||
model.setRNA(form.getRna());
|
||||
model.setState_id(form.getState_id());
|
||||
model.setAddress(form.getAddress());
|
||||
model.setContact(form.getContact());
|
||||
|
||||
@ -259,7 +289,9 @@ public class AffiliationService {
|
||||
}).call(m -> Panache.withTransaction(() -> combRepository.persist(m)));
|
||||
}
|
||||
})
|
||||
.call(m -> ((m.getUserId() == null) ? keycloakService.initCompte(m.getId()) :
|
||||
.call(m -> ((m.getUserId() == null) ? keycloakService.initCompte(m.getId())
|
||||
.onFailure().invoke(t -> LOGGER.warnf("Failed to init account: %s", t.getMessage())).onFailure()
|
||||
.recoverWithNull() :
|
||||
keycloakService.setClubGroupMembre(m, club).map(__ -> m.getUserId()))
|
||||
.call(userId -> keycloakService.setAutoRoleMembre(userId, m.getRole(), m.getGrade_arbitrage()))
|
||||
.call(userId -> keycloakService.setEmail(userId, m.getEmail())))
|
||||
@ -267,19 +299,24 @@ public class AffiliationService {
|
||||
.call(l1 -> l1 != null && l1.stream().anyMatch(l -> l.getSaison() == saison) ?
|
||||
Uni.createFrom().nullItem() :
|
||||
Panache.withTransaction(() -> licenceRepository.persist(
|
||||
new LicenceModel(null, m, club.getId(), saison, null, true, false)))
|
||||
new LicenceModel(null, m, club.getId(), saison, null, true, false)))
|
||||
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, m.getObjectName(),
|
||||
licenceModel))));
|
||||
}
|
||||
|
||||
public Uni<?> accept(AffiliationRequestSaveForm form) {
|
||||
LOGGER.debug("Affiliation Request Accepted");
|
||||
LOGGER.debug(form.toString());
|
||||
|
||||
return repositoryRequest.findById(form.getId())
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||
.chain(req ->
|
||||
clubRepository.find("SIRET = ?1", form.getSiret()).firstResult()
|
||||
clubRepository.find("StateId = ?1", form.getState_id()).firstResult()
|
||||
.chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model))
|
||||
.call(club -> setMembre(form.new Member(1), club, req.getSaison())
|
||||
.call(__ -> setMembre(form.new Member(2), club, req.getSaison())
|
||||
.call(club -> setMembre(form.new Member(1), club, req.getSaison()).onFailure()
|
||||
.recoverWithNull()
|
||||
.call(__ -> setMembre(form.new Member(2), club, req.getSaison()).onFailure()
|
||||
.recoverWithNull()
|
||||
.call(___ -> setMembre(form.new Member(3), club, req.getSaison()))))
|
||||
.onItem()
|
||||
.invoke(model -> Uni.createFrom()
|
||||
@ -298,13 +335,13 @@ public class AffiliationService {
|
||||
}
|
||||
|
||||
private Uni<ClubModel> acceptNew(AffiliationRequestSaveForm form, AffiliationRequestModel model) {
|
||||
LOGGER.debug("New Club Accepted");
|
||||
return Uni.createFrom().nullItem()
|
||||
.chain(() -> {
|
||||
ClubModel club = new ClubModel();
|
||||
club.setName(form.getName());
|
||||
club.setCountry("FR");
|
||||
club.setSIRET(form.getSiret());
|
||||
club.setRNA(form.getRna());
|
||||
club.setStateId(form.getState_id());
|
||||
club.setAddress(form.getAddress());
|
||||
club.setContact_intern(form.getContact());
|
||||
club.setAffiliations(new ArrayList<>());
|
||||
@ -336,17 +373,24 @@ public class AffiliationService {
|
||||
}
|
||||
|
||||
private Uni<ClubModel> acceptOld(AffiliationRequestSaveForm form, AffiliationRequestModel model, ClubModel club) {
|
||||
AtomicBoolean nameChange = new AtomicBoolean(false);
|
||||
LOGGER.debug("Old Club Accepted");
|
||||
return Uni.createFrom().nullItem()
|
||||
.chain(() -> {
|
||||
club.setName(form.getName());
|
||||
if (!form.getName().equals(club.getName())) {
|
||||
club.setName(form.getName());
|
||||
nameChange.set(true);
|
||||
}
|
||||
club.setCountry("FR");
|
||||
club.setSIRET(form.getSiret());
|
||||
club.setRNA(form.getRna());
|
||||
club.setStateId(form.getState_id());
|
||||
club.setAddress(form.getAddress());
|
||||
club.setContact_intern(form.getContact());
|
||||
return Panache.withTransaction(() -> clubRepository.persist(club)
|
||||
.chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison())))
|
||||
.chain(() -> repositoryRequest.delete(model)));
|
||||
.chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison())))
|
||||
.chain(() -> repositoryRequest.delete(model)))
|
||||
.call(() -> nameChange.get() ? keycloakService.updateGroupFromClub(
|
||||
club) // update group in keycloak
|
||||
: Uni.createFrom().nullItem());
|
||||
})
|
||||
.map(__ -> club);
|
||||
}
|
||||
@ -354,7 +398,7 @@ public class AffiliationService {
|
||||
public Uni<SimpleReqAffiliation> getRequest(long id) {
|
||||
return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel)
|
||||
.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("StateId = ?1", out.getStateId()).firstResult().invoke(c -> {
|
||||
if (c != null) {
|
||||
out.setClub(c.getId());
|
||||
out.setClub_name(c.getName());
|
||||
@ -367,7 +411,7 @@ public class AffiliationService {
|
||||
public Uni<List<SimpleAffiliation>> getCurrentSaisonAffiliation() {
|
||||
return repositoryRequest.list("saison = ?1 or saison = ?1 + 1", Utils.getSaison())
|
||||
.map(models -> models.stream()
|
||||
.map(model -> new SimpleAffiliation(model.getId() * -1, model.getSiret(), model.getSaison(),
|
||||
.map(model -> new SimpleAffiliation(model.getId() * -1, model.getState_id(), model.getSaison(),
|
||||
false)).toList())
|
||||
.chain(aff -> repository.list("saison = ?1", Utils.getSaison())
|
||||
.map(models -> models.stream().map(SimpleAffiliation::fromModel).toList())
|
||||
@ -379,9 +423,9 @@ public class AffiliationService {
|
||||
return clubRepository.findById(id)
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Club non trouvé"))
|
||||
.call(model -> Mutiny.fetch(model.getAffiliations()))
|
||||
.chain(model -> repositoryRequest.list("siret = ?1", model.getSIRET())
|
||||
.chain(model -> repositoryRequest.list("state_id = ?1", model.getStateId())
|
||||
.map(reqs -> reqs.stream().map(req ->
|
||||
new SimpleAffiliation(req.getId() * -1, model.getId(), req.getSaison(), false)))
|
||||
new SimpleAffiliation(req.getId() * -1, model.getStateId(), req.getSaison(), false)))
|
||||
.map(aff2 -> Stream.concat(aff2,
|
||||
model.getAffiliations().stream().map(SimpleAffiliation::fromModel)).toList())
|
||||
);
|
||||
@ -411,9 +455,9 @@ public class AffiliationService {
|
||||
return Panache.withTransaction(() -> repository.deleteById(id));
|
||||
}
|
||||
|
||||
public Uni<?> deleteReqAffiliation(long id, String reason) {
|
||||
public Uni<?> deleteReqAffiliation(long id, String reason, boolean federationAdmin) {
|
||||
return repositoryRequest.findById(id)
|
||||
.call(aff -> reactiveMailer.send(
|
||||
.call(aff -> federationAdmin ? reactiveMailer.send(
|
||||
Mail.withText(aff.getM1_email(),
|
||||
"FFSAF - Votre demande d'affiliation a été rejetée.",
|
||||
String.format(
|
||||
@ -430,7 +474,7 @@ public class AffiliationService {
|
||||
""", aff.getName(), reason)
|
||||
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
|
||||
.addTo(aff.getM2_email(), aff.getM3_email())
|
||||
))
|
||||
) : Uni.createFrom().nullItem())
|
||||
.chain(aff -> Panache.withTransaction(() -> repositoryRequest.delete(aff)))
|
||||
.call(__ -> Utils.deleteMedia(id, media, "aff_request/logo"))
|
||||
.call(__ -> Utils.deleteMedia(id, media, "aff_request/status"));
|
||||
|
||||
@ -32,6 +32,7 @@ import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
|
||||
@ -193,12 +194,17 @@ public class ClubService {
|
||||
}
|
||||
|
||||
public Uni<String> update(long id, FullClubForm input) {
|
||||
AtomicBoolean nameChange = new AtomicBoolean(false);
|
||||
|
||||
return repository.findById(id).call(m -> Mutiny.fetch(m.getContact()))
|
||||
.onItem().transformToUni(Unchecked.function(m -> {
|
||||
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||
};
|
||||
|
||||
m.setName(input.getName());
|
||||
if (!input.getName().equals(m.getName())) {
|
||||
m.setName(input.getName());
|
||||
nameChange.set(true);
|
||||
}
|
||||
m.setCountry(input.getCountry());
|
||||
m.setInternational(input.isInternational());
|
||||
|
||||
@ -211,11 +217,9 @@ public class ClubService {
|
||||
m.setTraining_day_time(input.getTraining_day_time());
|
||||
ls.logChange("Contact interne", m.getContact_intern(), input.getContact_intern(), m);
|
||||
m.setContact_intern(input.getContact_intern());
|
||||
ls.logChange("N° RNA", m.getRNA(), input.getRna(), m);
|
||||
m.setRNA(input.getRna());
|
||||
if (input.getSiret() != null && !input.getSiret().isBlank()) {
|
||||
ls.logChange("N° SIRET", m.getSIRET(), input.getSiret(), m);
|
||||
m.setSIRET(Long.parseLong(input.getSiret()));
|
||||
if (input.getState_id() != null && !input.getState_id().isBlank()) {
|
||||
ls.logChange("N° SIRET", m.getClubId(), input.getState_id(), m);
|
||||
m.setStateId(input.getState_id());
|
||||
}
|
||||
ls.logChange("Adresse administrative", m.getAddress(), input.getAddress(), m);
|
||||
m.setAddress(input.getAddress());
|
||||
@ -230,6 +234,8 @@ public class ClubService {
|
||||
}
|
||||
return Panache.withTransaction(() -> repository.persist(m)).call(() -> ls.append());
|
||||
}))
|
||||
.call(clubModel -> nameChange.get() ? keycloakService.updateGroupFromClub(clubModel) // update group in keycloak
|
||||
: Uni.createFrom().nullItem())
|
||||
.invoke(membreModel -> SReqClub.sendIfNeed(serverCustom.clients,
|
||||
SimpleClubModel.fromModel(membreModel)))
|
||||
.map(__ -> "OK");
|
||||
@ -251,9 +257,8 @@ public class ClubService {
|
||||
clubModel.setTraining_location(input.getTraining_location());
|
||||
clubModel.setTraining_day_time(input.getTraining_day_time());
|
||||
clubModel.setContact_intern(input.getContact_intern());
|
||||
clubModel.setRNA(input.getRna());
|
||||
if (input.getSiret() != null && !input.getSiret().isBlank())
|
||||
clubModel.setSIRET(Long.parseLong(input.getSiret()));
|
||||
if (input.getState_id() != null && !input.getState_id().isBlank())
|
||||
clubModel.setStateId(input.getState_id());
|
||||
clubModel.setAddress(input.getAddress());
|
||||
|
||||
try {
|
||||
@ -300,9 +305,9 @@ public class ClubService {
|
||||
.call(clubModel -> Mutiny.fetch(clubModel.getAffiliations()))
|
||||
.invoke(clubModel -> {
|
||||
data.setName(clubModel.getName());
|
||||
data.setSiret(clubModel.getSIRET());
|
||||
data.setRna(clubModel.getRNA());
|
||||
data.setState_id(clubModel.getStateId());
|
||||
data.setAddress(clubModel.getAddress());
|
||||
data.setContact(clubModel.getContact_intern());
|
||||
data.setSaison(
|
||||
clubModel.getAffiliations().stream().max(Comparator.comparing(AffiliationModel::getSaison))
|
||||
.map(AffiliationModel::getSaison).map(i -> Math.min(i + 1, Utils.getSaison() + 1))
|
||||
|
||||
@ -85,6 +85,31 @@ public class KeycloakService {
|
||||
return Uni.createFrom().item(club::getClubId);
|
||||
}
|
||||
|
||||
public Uni<String> updateGroupFromClub(ClubModel club) {
|
||||
if (club.getClubId() == null) {
|
||||
return getGroupFromClub(club);
|
||||
} else {
|
||||
LOGGER.infof("Updating name of club group %d-%s...", club.getId(), club.getName());
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
GroupRepresentation clubGroup =
|
||||
keycloak.realm(realm).groups().groups().stream().filter(g -> g.getName().equals("club"))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new KeycloakException("Fail to fetch group %s".formatted("club")));
|
||||
|
||||
keycloak.realm(realm).groups().group(clubGroup.getId()).getSubGroups(0, 1000, true).stream()
|
||||
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny()
|
||||
.ifPresent(groupRepresentation -> {
|
||||
groupRepresentation.setName(club.getId() + "-" + club.getName());
|
||||
keycloak.realm(realm).groups().group(groupRepresentation.getId())
|
||||
.update(groupRepresentation);
|
||||
});
|
||||
|
||||
return club.getClubId();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Uni<String> getUserFromMember(MembreModel membreModel) {
|
||||
if (membreModel.getUserId() == null) {
|
||||
return Uni.createFrom()
|
||||
@ -199,16 +224,16 @@ public class KeycloakService {
|
||||
|
||||
public Uni<String> initCompte(long id) {
|
||||
return membreService.getById(id).invoke(Unchecked.consumer(membreModel -> {
|
||||
if (membreModel.getUserId() != null)
|
||||
throw new KeycloakException("User already linked to the user id=" + id);
|
||||
if (membreModel.getEmail() == null)
|
||||
throw new KeycloakException("User email is null");
|
||||
if (membreModel.getFname() == null || membreModel.getLname() == null)
|
||||
throw new KeycloakException("User name is null");
|
||||
})).chain(membreModel -> creatUser(membreModel).chain(user -> {
|
||||
LOGGER.infof("Set user id %s to membre %s", user.getId(), membreModel.getId());
|
||||
return membreService.setUserId(membreModel.getId(), user.getId()).map(__ -> user.getId());
|
||||
}));
|
||||
if (membreModel.getUserId() != null)
|
||||
throw new KeycloakException("User already linked to the user id=" + id);
|
||||
if (membreModel.getEmail() == null)
|
||||
throw new KeycloakException("User email is null");
|
||||
if (membreModel.getFname() == null || membreModel.getLname() == null)
|
||||
throw new KeycloakException("User name is null");
|
||||
})).chain(membreModel -> creatUser(membreModel).chain(user -> {
|
||||
LOGGER.infof("Set user id %s to membre %s", user.getId(), membreModel.getId());
|
||||
return membreService.setUserId(membreModel.getId(), user.getId()).map(__ -> user.getId());
|
||||
}));
|
||||
}
|
||||
|
||||
private Uni<UserRepresentation> creatUser(MembreModel membreModel) {
|
||||
@ -231,9 +256,6 @@ public class KeycloakService {
|
||||
user.setEmail(membreModel.getEmail());
|
||||
user.setEnabled(true);
|
||||
|
||||
user.setRequiredActions(List.of(RequiredAction.VERIFY_EMAIL.name(),
|
||||
RequiredAction.UPDATE_PASSWORD.name()));
|
||||
|
||||
try (Response response = keycloak.realm(realm).users().create(user)) {
|
||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo()
|
||||
.equals(Response.Status.CONFLICT))
|
||||
@ -245,13 +267,6 @@ public class KeycloakService {
|
||||
return getUser(login).orElseThrow(
|
||||
() -> new KeycloakException("Fail to fetch user %s".formatted(finalLogin)));
|
||||
})
|
||||
.call(user -> enabled_email ?
|
||||
vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
keycloak.realm(realm).users().get(user.getId())
|
||||
.executeActionsEmail(List.of(RequiredAction.VERIFY_EMAIL.name(),
|
||||
RequiredAction.UPDATE_PASSWORD.name()));
|
||||
return null;
|
||||
}) : Uni.createFrom().nullItem())
|
||||
.invoke(user -> membreModel.setUserId(user.getId()))
|
||||
.call(user -> updateRole(user.getId(), List.of("safca_user"), List.of()))
|
||||
.call(user -> enabled_email ? reactiveMailer.send(
|
||||
@ -261,14 +276,14 @@ public class KeycloakService {
|
||||
"""
|
||||
Bonjour,
|
||||
|
||||
Suite à votre première inscription %sà la Fédération Française de Soft Armored Fighting (FFSAF), votre compte pour accéder à l'intranet a été créé.
|
||||
Ce compte vous permettra de consulter vos informations, de vous inscrire aux compétitions et de consulter vos résultats.
|
||||
|
||||
Vous allez recevoir dans les prochaines minutes un email vous demandant de vérifier votre email et de définir un mot de passe.
|
||||
Suite à votre première inscription %sà la Fédération Française de Soft Armored Fighting (FFSAF), votre compte intranet a été créé.
|
||||
Ce compte vous permettra de consulter vos informations et, dans un futur proche, de vous inscrire aux compétitions ainsi que d'en consulter les résultats.
|
||||
|
||||
L'intranet est accessible à l'adresse suivante : https://intra.ffsaf.fr
|
||||
Votre nom d'utilisateur est : %s
|
||||
|
||||
Pour définir votre mot de passe, rendez-vous sur l'intranet > "Connexion" > "Mot de passe oublié ?"
|
||||
|
||||
Si vous n'avez pas demandé cette inscription, veuillez contacter le support à l'adresse support@ffsaf.fr.
|
||||
(Pas de panique, nous ne vous enverrons pas de message autre que ce concernant votre compte)
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@ -26,6 +27,7 @@ import java.util.function.Function;
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
public class LicenceService {
|
||||
private static final Logger LOGGER = Logger.getLogger(LicenceService.class);
|
||||
|
||||
@Inject
|
||||
LicenceRepository repository;
|
||||
@ -125,7 +127,9 @@ public class LicenceService {
|
||||
.chain(() -> combRepository.persist(membreModel))
|
||||
: Uni.createFrom().nullItem())
|
||||
.call(__ -> (membreModel.getUserId() == null) ?
|
||||
keycloakService.initCompte(membreModel.getId())
|
||||
keycloakService.initCompte(membreModel.getId()).onFailure()
|
||||
.invoke(t -> LOGGER.infof("Failed to init account: %s", t.getMessage())).onFailure()
|
||||
.recoverWithNull()
|
||||
: Uni.createFrom().nullItem());
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleMembreInOutData;
|
||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||
import fr.titionfire.ffsaf.utils.*;
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
@ -102,15 +103,47 @@ public class MembreService {
|
||||
return baseUni;
|
||||
}
|
||||
|
||||
private Sort getSort(String order) {
|
||||
|
||||
Sort sort;
|
||||
if (order == null || order.isBlank()) {
|
||||
sort = Sort.ascending("fname", "lname");
|
||||
} else {
|
||||
sort = Sort.empty();
|
||||
|
||||
for (String e : order.split(",")) {
|
||||
String[] split = e.split(" ");
|
||||
if (split.length == 2) {
|
||||
sort = sort.and(split[0],
|
||||
split[1].equals("n") ? Sort.Direction.Ascending : Sort.Direction.Descending);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sort;
|
||||
}
|
||||
|
||||
public Uni<PageResult<SimpleMembre>> searchAdmin(int limit, int page, String search, String club,
|
||||
int licenceRequest, int payState) {
|
||||
int licenceRequest, int payState, String order, String categorie) {
|
||||
if (search == null)
|
||||
search = "";
|
||||
search = "%" + search.replaceAll(" ", "% %") + "%";
|
||||
|
||||
String categorieFilter;
|
||||
if (categorie == null || categorie.isBlank())
|
||||
categorieFilter = " True";
|
||||
else
|
||||
categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal();
|
||||
|
||||
String finalSearch = search;
|
||||
Uni<List<LicenceModel>> baseUni = getLicenceListe(licenceRequest, payState);
|
||||
|
||||
Sort sort = getSort(order);
|
||||
if (sort == null)
|
||||
return Uni.createFrom().failure(new DInternalError("Erreur lors calcul du trie"));
|
||||
|
||||
return baseUni
|
||||
.map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList())
|
||||
.chain(ids -> {
|
||||
@ -120,18 +153,18 @@ public class MembreService {
|
||||
|
||||
if (club == null || club.isBlank()) {
|
||||
query = repository.find(
|
||||
"id " + idf + " ?2 AND (" + FIND_NAME_REQUEST + ")",
|
||||
Sort.ascending("fname", "lname"), finalSearch, ids)
|
||||
"id " + idf + " ?2 AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||
sort, finalSearch, ids)
|
||||
.page(Page.ofSize(limit));
|
||||
} else {
|
||||
if (club.equals("null")) {
|
||||
query = repository.find(
|
||||
"id " + idf + " ?2 AND club IS NULL AND (" + FIND_NAME_REQUEST + ")",
|
||||
Sort.ascending("fname", "lname"), finalSearch, ids).page(Page.ofSize(limit));
|
||||
"id " + idf + " ?2 AND club IS NULL AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||
sort, finalSearch, ids).page(Page.ofSize(limit));
|
||||
} else {
|
||||
query = repository.find(
|
||||
"id " + idf + " ?3 AND LOWER(club.name) LIKE LOWER(?2) AND (" + FIND_NAME_REQUEST + ")",
|
||||
Sort.ascending("fname", "lname"), finalSearch, club + "%", ids)
|
||||
"id " + idf + " ?3 AND LOWER(club.name) LIKE LOWER(?2) AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||
sort, finalSearch, club, ids)
|
||||
.page(Page.ofSize(limit));
|
||||
}
|
||||
}
|
||||
@ -140,7 +173,7 @@ public class MembreService {
|
||||
}
|
||||
|
||||
public Uni<PageResult<SimpleMembre>> search(int limit, int page, String search, int licenceRequest, int payState,
|
||||
String subject) {
|
||||
String order, String categorie, String subject) {
|
||||
if (search == null)
|
||||
search = "";
|
||||
search = "%" + search.replaceAll(" ", "% %") + "%";
|
||||
@ -149,6 +182,16 @@ public class MembreService {
|
||||
|
||||
Uni<List<LicenceModel>> baseUni = getLicenceListe(licenceRequest, payState);
|
||||
|
||||
String categorieFilter;
|
||||
if (categorie == null || categorie.isBlank())
|
||||
categorieFilter = " True";
|
||||
else
|
||||
categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal();
|
||||
|
||||
Sort sort = getSort(order);
|
||||
if (sort == null)
|
||||
return Uni.createFrom().failure(new DInternalError("Erreur lors calcul du trie"));
|
||||
|
||||
return baseUni
|
||||
.map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList())
|
||||
.chain(ids -> {
|
||||
@ -157,8 +200,8 @@ public class MembreService {
|
||||
return repository.find("userId = ?1", subject).firstResult()
|
||||
.chain(membreModel -> {
|
||||
PanacheQuery<MembreModel> query = repository.find(
|
||||
"id " + idf + " ?3 AND club = ?2 AND (" + FIND_NAME_REQUEST + ")",
|
||||
Sort.ascending("fname", "lname"), finalSearch, membreModel.getClub(), ids)
|
||||
"id " + idf + " ?3 AND club = ?2 AND (" + FIND_NAME_REQUEST + ") AND " + categorieFilter,
|
||||
sort, finalSearch, membreModel.getClub(), ids)
|
||||
.page(Page.ofSize(limit));
|
||||
return getPageResult(query, limit, page);
|
||||
});
|
||||
@ -197,6 +240,11 @@ public class MembreService {
|
||||
return Uni.createFrom().nullItem();
|
||||
AtomicReference<ClubModel> clubModel = new AtomicReference<>();
|
||||
|
||||
LOGGER.debugf("Membre import (size=%d)", data2.size());
|
||||
for (SimpleMembreInOutData simpleMembreInOutData : data2) {
|
||||
LOGGER.debugf("-> %s", simpleMembreInOutData.toString());
|
||||
}
|
||||
|
||||
return repository.find("userId = ?1", subject).firstResult()
|
||||
.chain(membreModel -> {
|
||||
clubModel.set(membreModel.getClub());
|
||||
@ -205,20 +253,24 @@ public class MembreService {
|
||||
return repository.list("licence IN ?1 OR LOWER(lname || ' ' || fname) IN ?2 OR email IN ?3",
|
||||
data2.stream().map(SimpleMembreInOutData::getLicence).filter(Objects::nonNull).toList(),
|
||||
data2.stream().map(o -> (o.getNom() + " " + o.getPrenom()).toLowerCase()).toList(),
|
||||
data2.stream().map(SimpleMembreInOutData::getEmail).filter(Objects::nonNull).toList());
|
||||
data2.stream().map(SimpleMembreInOutData::getEmail).filter(o -> o != null && !o.isBlank())
|
||||
.toList());
|
||||
})
|
||||
.call(Unchecked.function(membres -> {
|
||||
for (MembreModel membreModel : membres) {
|
||||
if (!Objects.equals(membreModel.getClub(), clubModel.get()))
|
||||
if (!Objects.equals(membreModel.getClub(), clubModel.get())) {
|
||||
LOGGER.info("Similar membres found: " + membreModel);
|
||||
throw new DForbiddenException(
|
||||
"Le membre n°" + membreModel.getLicence() + " n'appartient pas à votre club");
|
||||
}
|
||||
}
|
||||
Uni<Void> uniResult = Uni.createFrom().voidItem();
|
||||
for (SimpleMembreInOutData dataIn : data2) {
|
||||
MembreModel model = membres.stream()
|
||||
.filter(m -> Objects.equals(m.getLicence(), dataIn.getLicence()) || m.getLname()
|
||||
.equals(dataIn.getNom()) && m.getFname().equals(dataIn.getPrenom()) ||
|
||||
Objects.equals(m.getFname(), dataIn.getEmail())).findFirst()
|
||||
.filter(m -> (dataIn.getLicence() != null && Objects.equals(m.getLicence(),
|
||||
dataIn.getLicence())) || m.getLname().equals(dataIn.getNom()) && m.getFname()
|
||||
.equals(dataIn.getPrenom()) || (dataIn.getEmail() != null && !dataIn.getEmail()
|
||||
.isBlank() && Objects.equals(m.getFname(), dataIn.getEmail()))).findFirst()
|
||||
.orElseGet(() -> {
|
||||
MembreModel mm = new MembreModel();
|
||||
mm.setClub(clubModel.get());
|
||||
@ -226,16 +278,23 @@ public class MembreService {
|
||||
mm.setCountry("FR");
|
||||
return mm;
|
||||
});
|
||||
if (model.getId() != null) {
|
||||
LOGGER.debugf("updating -> %s", dataIn.toString());
|
||||
} else {
|
||||
LOGGER.debugf("creating -> %s", dataIn.toString());
|
||||
}
|
||||
|
||||
if (model.getEmail() != null) {
|
||||
if (model.getEmail() != null && !model.getEmail().isBlank()) {
|
||||
if (model.getLicence() != null && !model.getLicence().equals(dataIn.getLicence())) {
|
||||
throw new DBadRequestException("Email déja utiliser");
|
||||
LOGGER.info("Similar membres found: " + model);
|
||||
throw new DBadRequestException("Email '" + model.getEmail() + "' déja utiliser");
|
||||
}
|
||||
|
||||
if (StringSimilarity.similarity(model.getLname().toUpperCase(),
|
||||
dataIn.getNom().toUpperCase()) > 3 || StringSimilarity.similarity(
|
||||
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3) {
|
||||
throw new DBadRequestException("Email déja utiliser");
|
||||
LOGGER.info("Similar membres found: " + model);
|
||||
throw new DBadRequestException("Email '" + model.getEmail() + "' déja utiliser");
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,6 +303,7 @@ public class MembreService {
|
||||
if ((!add && StringSimilarity.similarity(model.getLname().toUpperCase(),
|
||||
dataIn.getNom().toUpperCase()) > 3) || (!add && StringSimilarity.similarity(
|
||||
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) {
|
||||
LOGGER.info("Similar membres found: " + model);
|
||||
throw new DBadRequestException(
|
||||
"Pour enregistrer un nouveau membre, veuillez laisser le champ licence vide.");
|
||||
}
|
||||
@ -319,7 +379,7 @@ public class MembreService {
|
||||
return update(repository.findById(id)
|
||||
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
|
||||
.invoke(Unchecked.consumer(c -> {
|
||||
if (c > 0)
|
||||
if (c > 0 && !membre.getEmail().isBlank())
|
||||
throw new DBadRequestException("Email déjà utiliser");
|
||||
})))
|
||||
.chain(membreModel -> clubRepository.findById(membre.getClub())
|
||||
@ -341,7 +401,7 @@ public class MembreService {
|
||||
return update(repository.findById(id)
|
||||
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
|
||||
.invoke(Unchecked.consumer(c -> {
|
||||
if (c > 0)
|
||||
if (c > 0 && !membre.getEmail().isBlank())
|
||||
throw new DBadRequestException("Email déjà utiliser");
|
||||
})))
|
||||
.invoke(Unchecked.consumer(membreModel -> {
|
||||
|
||||
@ -92,7 +92,7 @@ public class AffiliationRequestEndpoints {
|
||||
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Supprime une demande d'affiliation", description = "Cette méthode supprime une demande " +
|
||||
"d'affiliation pour l'identifiant spécifié.")
|
||||
@ -107,7 +107,7 @@ public class AffiliationRequestEndpoints {
|
||||
if (o.getClub() == null && !securityCtx.roleHas("federation_admin"))
|
||||
throw new DForbiddenException();
|
||||
})).invoke(o -> checkPerm.accept(o.getClub()))
|
||||
.chain(o -> service.deleteReqAffiliation(id, reason));
|
||||
.chain(o -> service.deleteReqAffiliation(id, reason, securityCtx.roleHas("federation_admin")));
|
||||
}
|
||||
|
||||
@PUT
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.rest.client.SirenService;
|
||||
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
||||
import fr.titionfire.ffsaf.rest.client.StateIdService;
|
||||
import fr.titionfire.ffsaf.rest.data.AssoData;
|
||||
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.*;
|
||||
@ -12,18 +13,24 @@ import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
@Path("api/asso")
|
||||
public class AssoEndpoints {
|
||||
|
||||
@RestClient
|
||||
StateIdService stateIdService;
|
||||
|
||||
@RestClient
|
||||
SirenService sirenService;
|
||||
|
||||
@GET
|
||||
@Path("siren/{siren}")
|
||||
@Path("state_id/{stateId}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(hidden = true)
|
||||
public Uni<UniteLegaleRoot> getInfoSiren(@PathParam("siren") String siren) {
|
||||
return sirenService.get_unite(siren).onFailure().transform(throwable -> {
|
||||
public Uni<AssoData> getAssoInfo(@PathParam("stateId") String stateId) {
|
||||
return ((stateId.charAt(0) == 'W') ? stateIdService.get_rna(stateId) : sirenService.get_unite(
|
||||
stateId).chain(stateIdService::getAssoDataFromUnit)).onFailure().transform(throwable -> {
|
||||
if (throwable instanceof WebApplicationException exception) {
|
||||
if (exception.getResponse().getStatus() == 404)
|
||||
return new DNotFoundException("Service momentanément indisponible");
|
||||
if (exception.getResponse().getStatus() == 400)
|
||||
return new DNotFoundException("Siret introuvable");
|
||||
return new DNotFoundException("Asso introuvable");
|
||||
}
|
||||
return throwable;
|
||||
});
|
||||
|
||||
@ -29,6 +29,7 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@ -69,7 +70,8 @@ public class ClubEndpoints {
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<List<SimpleClubModel>> getAll() {
|
||||
return clubService.getAll().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList());
|
||||
return clubService.getAll().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).sorted(
|
||||
Comparator.comparing(SimpleClubModel::getName)).toList());
|
||||
}
|
||||
|
||||
@GET
|
||||
|
||||
@ -58,13 +58,15 @@ public class MembreAdminEndpoints {
|
||||
@Parameter(description = "Page à consulter") @QueryParam("page") Integer page,
|
||||
@Parameter(description = "Text à rechercher") @QueryParam("search") String search,
|
||||
@Parameter(description = "Club à filter") @QueryParam("club") String club,
|
||||
@Parameter(description = "Etat de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest,
|
||||
@Parameter(description = "Etat du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment) {
|
||||
@Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie,
|
||||
@Parameter(description = "État de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest,
|
||||
@Parameter(description = "État du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment,
|
||||
@Parameter(description = "Ordre") @QueryParam("order") String order) {
|
||||
if (limit == null)
|
||||
limit = 50;
|
||||
if (page == null || page < 1)
|
||||
page = 1;
|
||||
return membreService.searchAdmin(limit, page - 1, search, club, licenceRequest, payment);
|
||||
return membreService.searchAdmin(limit, page - 1, search, club, licenceRequest, payment, order, categorie);
|
||||
}
|
||||
|
||||
@GET
|
||||
|
||||
@ -50,13 +50,15 @@ public class MembreClubEndpoints {
|
||||
@Parameter(description = "Nombre max de résulta (max 50)") @QueryParam("limit") Integer limit,
|
||||
@Parameter(description = "Page à consulter") @QueryParam("page") Integer page,
|
||||
@Parameter(description = "Text à rechercher") @QueryParam("search") String search,
|
||||
@Parameter(description = "Catégorie à filter") @QueryParam("categorie") String categorie,
|
||||
@Parameter(description = "Etat de la demande de licence: 0 -> sans demande, 1 -> avec demande ou validée, 2 -> toute les demande non validée, 3 -> validée, 4 -> tout, 5 -> demande complete, 6 -> demande incomplete") @QueryParam("licenceRequest") int licenceRequest,
|
||||
@Parameter(description = "Etat du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment) {
|
||||
@Parameter(description = "Etat du payment: 0 -> non payer, 1 -> payer, 2 -> tout") @QueryParam("payment") int payment,
|
||||
@Parameter(description = "Ordre") @QueryParam("order") String order) {
|
||||
if (limit == null)
|
||||
limit = 50;
|
||||
if (page == null || page < 1)
|
||||
page = 1;
|
||||
return membreService.search(limit, page - 1, search, licenceRequest, payment, securityCtx.getSubject());
|
||||
return membreService.search(limit, page - 1, search, licenceRequest, payment, order, categorie, securityCtx.getSubject());
|
||||
}
|
||||
|
||||
@GET
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package fr.titionfire.ffsaf.rest.client;
|
||||
|
||||
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
||||
import io.quarkus.cache.CacheResult;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
@ -15,5 +16,6 @@ public interface SirenService {
|
||||
|
||||
@GET
|
||||
@Path("/v3/unites_legales/{SIREN}")
|
||||
@CacheResult(cacheName = "AssoData_siren")
|
||||
Uni<UniteLegaleRoot> get_unite(@PathParam("SIREN") String siren);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package fr.titionfire.ffsaf.rest.client;
|
||||
|
||||
import fr.titionfire.ffsaf.rest.data.AssoData;
|
||||
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
||||
import io.quarkus.cache.CacheResult;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@Path("/")
|
||||
@RegisterRestClient
|
||||
public interface StateIdService {
|
||||
|
||||
@GET
|
||||
@Path("/associations/{rna}")
|
||||
@CacheResult(cacheName = "AssoData_rna")
|
||||
Uni<AssoData> get_rna(@PathParam("rna") String rna);
|
||||
|
||||
default Uni<AssoData> getAssoDataFromUnit(UniteLegaleRoot u) {
|
||||
AssoData assoData = new AssoData();
|
||||
assoData.setSiren(u.getUnite_legale().getSiren());
|
||||
assoData.setRna(u.getUnite_legale().getIdentifiant_association());
|
||||
|
||||
AssoData.Identite identite = new AssoData.Identite();
|
||||
identite.setNom(u.getUnite_legale().getDenomination());
|
||||
identite.setSiret_siege(u.getUnite_legale().getEtablissement_siege().getSiret());
|
||||
assoData.setIdentite(identite);
|
||||
|
||||
AssoData.Address address = new AssoData.Address();
|
||||
StringBuilder voie = new StringBuilder();
|
||||
if (u.getUnite_legale().getEtablissement_siege().getNumero_voie() != null)
|
||||
voie.append(u.getUnite_legale().getEtablissement_siege().getNumero_voie()).append(' ');
|
||||
if (u.getUnite_legale().getEtablissement_siege().getType_voie() != null)
|
||||
voie.append(u.getUnite_legale().getEtablissement_siege().getType_voie()).append(' ');
|
||||
if (u.getUnite_legale().getEtablissement_siege().getLibelle_voie() != null)
|
||||
voie.append(u.getUnite_legale().getEtablissement_siege().getLibelle_voie()).append(' ');
|
||||
address.setVoie(voie.toString().trim());
|
||||
address.setComplement(u.getUnite_legale().getEtablissement_siege().getComplement_adresse());
|
||||
address.setCode_postal(u.getUnite_legale().getEtablissement_siege().getCode_postal());
|
||||
address.setCommune(
|
||||
new AssoData.Commune(u.getUnite_legale().getEtablissement_siege().getLibelle_commune()));
|
||||
assoData.setCoordonnees(new AssoData.Coordonnee(address));
|
||||
|
||||
return Uni.createFrom().item(assoData);
|
||||
}
|
||||
}
|
||||
48
src/main/java/fr/titionfire/ffsaf/rest/data/AssoData.java
Normal file
48
src/main/java/fr/titionfire/ffsaf/rest/data/AssoData.java
Normal file
@ -0,0 +1,48 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@RegisterForReflection
|
||||
public class AssoData {
|
||||
String siren;
|
||||
String rna;
|
||||
Identite identite;
|
||||
Coordonnee coordonnees;
|
||||
|
||||
@Data
|
||||
@RegisterForReflection
|
||||
public static class Identite {
|
||||
String nom;
|
||||
String siret_siege;
|
||||
}
|
||||
|
||||
@Data
|
||||
@RegisterForReflection
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Coordonnee {
|
||||
Address adresse_gestion;
|
||||
}
|
||||
|
||||
@Data
|
||||
@RegisterForReflection
|
||||
public static class Address {
|
||||
String voie;
|
||||
String complement;
|
||||
String code_postal;
|
||||
String pays;
|
||||
Commune commune;
|
||||
}
|
||||
|
||||
@Data
|
||||
@RegisterForReflection
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Commune {
|
||||
String nom;
|
||||
}
|
||||
}
|
||||
@ -17,9 +17,9 @@ import java.util.List;
|
||||
@RegisterForReflection
|
||||
public class RenewAffData {
|
||||
String name;
|
||||
Long siret;
|
||||
String rna;
|
||||
String state_id;
|
||||
String address;
|
||||
String contact;
|
||||
int saison;
|
||||
List<RenewMember> members;
|
||||
|
||||
|
||||
@ -14,8 +14,8 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
public class SimpleAffiliation {
|
||||
@Schema(description = "L'identifiant de l'affiliation.", example = "1")
|
||||
private Long id;
|
||||
@Schema(description = "L'identifiant du club associé à l'affiliation.", example = "123")
|
||||
private Long club;
|
||||
@Schema(description = "L'identifiant du club associé à l'affiliation si id > 0 sinon n° SIRET ou RNA du club.", example = "123")
|
||||
private String club;
|
||||
@Schema(description = "La saison de l'affiliation.", example = "2022")
|
||||
private int saison;
|
||||
@Schema(description = "Indique si l'affiliation est validée ou non.", example = "true")
|
||||
@ -27,7 +27,7 @@ public class SimpleAffiliation {
|
||||
|
||||
return new SimpleAffiliationBuilder()
|
||||
.id(model.getId())
|
||||
.club(model.getClub().getId())
|
||||
.club(String.valueOf(model.getClub().getId()))
|
||||
.saison(model.getSaison())
|
||||
.validate(true)
|
||||
.build();
|
||||
|
||||
@ -36,10 +36,8 @@ public class SimpleClub {
|
||||
private String contact_intern;
|
||||
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris")
|
||||
private String address;
|
||||
@Schema(description = "RNA du club", example = "W123456789")
|
||||
private String RNA;
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234")
|
||||
private Long SIRET;
|
||||
@Schema(description = "Numéro SIRET ou RNA du club", example = "12345678901234")
|
||||
private String state_id;
|
||||
@Schema(description = "Numéro d'affiliation du club", example = "12345")
|
||||
private Long no_affiliation;
|
||||
@Schema(description = "Club international", example = "false")
|
||||
@ -60,8 +58,7 @@ public class SimpleClub {
|
||||
.training_location(model.getTraining_location())
|
||||
.training_day_time(model.getTraining_day_time())
|
||||
.contact_intern(model.getContact_intern())
|
||||
.RNA(model.getRNA())
|
||||
.SIRET(model.getSIRET())
|
||||
.state_id(model.getStateId())
|
||||
.no_affiliation(model.getNo_affiliation())
|
||||
.international(model.isInternational())
|
||||
.address(model.getAddress())
|
||||
|
||||
@ -20,8 +20,8 @@ public class SimpleClubList {
|
||||
String name;
|
||||
@Schema(description = "Pays du club", example = "FR")
|
||||
String country;
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234")
|
||||
Long siret;
|
||||
@Schema(description = "Numéro SIRET ou RNA du club", example = "12345678901234")
|
||||
String state_id;
|
||||
@Schema(description = "Numéro d'affiliation du club", example = "12345")
|
||||
Long no_affiliation;
|
||||
|
||||
@ -29,7 +29,7 @@ public class SimpleClubList {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleClubList(model.getId(), model.getName(), model.getCountry(), model.getSIRET(),
|
||||
return new SimpleClubList(model.getId(), model.getName(), model.getCountry(), model.getStateId(),
|
||||
model.getNo_affiliation());
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,10 +25,8 @@ public class SimpleReqAffiliation {
|
||||
Long club_no_aff;
|
||||
@Schema(description = "Nom du club demander", example = "Association sportive")
|
||||
String name;
|
||||
@Schema(description = "Numéro SIRET de l'association", example = "12345678901234")
|
||||
long siret;
|
||||
@Schema(description = "Numéro RNA de l'association", example = "W123456789")
|
||||
String RNA;
|
||||
@Schema(description = "Numéro SIRET ou RNA de l'association", example = "12345678901234")
|
||||
String stateId;
|
||||
@Schema(description = "Adresse de l'association", example = "1 rue de l'exemple, 75000 Paris")
|
||||
String address;
|
||||
@Schema(description = "Email de contact de l'association", example = "test@test.fr")
|
||||
@ -45,8 +43,7 @@ public class SimpleReqAffiliation {
|
||||
return new SimpleReqAffiliation.SimpleReqAffiliationBuilder()
|
||||
.id(model.getId())
|
||||
.name(model.getName())
|
||||
.siret(model.getSiret())
|
||||
.RNA(model.getRNA())
|
||||
.stateId(model.getState_id())
|
||||
.address(model.getAddress())
|
||||
.saison(model.getSaison())
|
||||
.contact(model.getContact())
|
||||
|
||||
@ -16,8 +16,8 @@ public class SimpleReqAffiliationResume {
|
||||
Long id;
|
||||
@Schema(description = "Le nom de l'association.", example = "Association sportive")
|
||||
String name;
|
||||
@Schema(description = "Le numéro SIRET de l'association.", example = "12345678901234")
|
||||
long siret;
|
||||
@Schema(description = "Le numéro SIRET ou RNA de l'association.", example = "12345678901234")
|
||||
String stateId;
|
||||
@Schema(description = "La saison de l'affiliation.", example = "2025")
|
||||
int saison;
|
||||
|
||||
@ -25,10 +25,10 @@ public class SimpleReqAffiliationResume {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleReqAffiliationResume.SimpleReqAffiliationResumeBuilder()
|
||||
return new SimpleReqAffiliationResumeBuilder()
|
||||
.id(model.getId())
|
||||
.name(model.getName())
|
||||
.siret(model.getSiret())
|
||||
.stateId(model.getState_id())
|
||||
.saison(model.getSaison())
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -33,8 +33,8 @@ public class UniteLegaleRoot {
|
||||
public String etat_administratif;
|
||||
public String identifiant_association;
|
||||
public String nic_siege;
|
||||
public Object nom;
|
||||
public Object nom_usage;
|
||||
public String nom;
|
||||
public String nom_usage;
|
||||
public int nombre_periodes;
|
||||
public String nomenclature_activite_principale;
|
||||
public Object prenom_1;
|
||||
@ -67,7 +67,7 @@ public class UniteLegaleRoot {
|
||||
private Object code_pays_etranger_2;
|
||||
private String code_postal;
|
||||
private Object code_postal_2;
|
||||
private Object complement_adresse;
|
||||
private String complement_adresse;
|
||||
private Object complement_adresse2;
|
||||
private String date_creation;
|
||||
private String date_debut;
|
||||
|
||||
@ -11,7 +11,7 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.jboss.resteasy.reactive.PartType;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@ToString(exclude = {"status", "logo"})
|
||||
public class AffiliationRequestForm {
|
||||
@Schema(description = "L'identifiant de l'affiliation. (null si nouvelle demande d'affiliation)")
|
||||
@FormParam("id")
|
||||
@ -21,13 +21,9 @@ public class AffiliationRequestForm {
|
||||
@FormParam("name")
|
||||
private String name = null;
|
||||
|
||||
@Schema(description = "Le numéro SIRET de l'association.", example = "12345678901234", required = true)
|
||||
@FormParam("siret")
|
||||
private Long siret = null;
|
||||
|
||||
@Schema(description = "Le numéro RNA de l'association. (peut être null)", example = "W123456789")
|
||||
@FormParam("rna")
|
||||
private String rna = null;
|
||||
@Schema(description = "Le numéro SIRET/RNA de l'association.", example = "12345678901234", required = true)
|
||||
@FormParam("state_id")
|
||||
private String state_id = null;
|
||||
|
||||
@Schema(description = "L'adresse de l'association.", example = "1 rue de l'exemple, 75000 Paris", required = true)
|
||||
@FormParam("adresse")
|
||||
@ -114,8 +110,7 @@ public class AffiliationRequestForm {
|
||||
public AffiliationRequestModel toModel() {
|
||||
AffiliationRequestModel model = new AffiliationRequestModel();
|
||||
model.setName(this.getName());
|
||||
model.setSiret(this.getSiret());
|
||||
model.setRNA(this.getRna());
|
||||
model.setState_id(this.getState_id());
|
||||
model.setAddress(this.getAdresse());
|
||||
model.setSaison(this.getSaison());
|
||||
model.setContact(this.getContact());
|
||||
|
||||
@ -4,12 +4,10 @@ import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.jboss.resteasy.reactive.PartType;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class AffiliationRequestSaveForm {
|
||||
@Schema(description = "L'identifiant de l'affiliation.", example = "1", required = true)
|
||||
@FormParam("id")
|
||||
@ -19,13 +17,9 @@ public class AffiliationRequestSaveForm {
|
||||
@FormParam("name")
|
||||
private String name = null;
|
||||
|
||||
@Schema(description = "Le numéro SIRET de l'association.", example = "12345678901234", required = true)
|
||||
@FormParam("siret")
|
||||
private Long siret = null;
|
||||
|
||||
@Schema(description = "Le numéro RNA de l'association. (peut être null)", example = "W123456789")
|
||||
@FormParam("rna")
|
||||
private String rna = null;
|
||||
@Schema(description = "Le numéro SIRET ou RNA de l'association.", example = "12345678901234", required = true)
|
||||
@FormParam("state_id")
|
||||
private String state_id = null;
|
||||
|
||||
@Schema(description = "L'adresse de l'association.", example = "1 rue de l'exemple, 75000 Paris", required = true)
|
||||
@FormParam("address")
|
||||
@ -171,4 +165,38 @@ public class AffiliationRequestSaveForm {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AffiliationRequestSaveForm{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", state_id=" + state_id +
|
||||
", address='" + address + '\'' +
|
||||
", contact='" + contact + '\'' +
|
||||
", status_len=" + status.length +
|
||||
", logo_len=" + logo.length +
|
||||
", m1_mode=" + m1_mode +
|
||||
", m1_role=" + m1_role +
|
||||
", m1_lincence='" + m1_lincence + '\'' +
|
||||
", m1_lname='" + m1_lname + '\'' +
|
||||
", m1_fname='" + m1_fname + '\'' +
|
||||
", m1_email='" + m1_email + '\'' +
|
||||
", m1_email_mode=" + m1_email_mode +
|
||||
", m2_mode=" + m2_mode +
|
||||
", m2_role=" + m2_role +
|
||||
", m2_lincence='" + m2_lincence + '\'' +
|
||||
", m2_lname='" + m2_lname + '\'' +
|
||||
", m2_fname='" + m2_fname + '\'' +
|
||||
", m2_email='" + m2_email + '\'' +
|
||||
", m2_email_mode=" + m2_email_mode +
|
||||
", m3_mode=" + m3_mode +
|
||||
", m3_role=" + m3_role +
|
||||
", m3_lincence='" + m3_lincence + '\'' +
|
||||
", m3_lname='" + m3_lname + '\'' +
|
||||
", m3_fname='" + m3_fname + '\'' +
|
||||
", m3_email='" + m3_email + '\'' +
|
||||
", m3_email_mode=" + m3_email_mode +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,13 +43,9 @@ public class FullClubForm {
|
||||
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris", required = true)
|
||||
private String address = null;
|
||||
|
||||
@FormParam("rna")
|
||||
@Schema(description = "RNA du club", example = "W123456789")
|
||||
private String rna = null;
|
||||
|
||||
@FormParam("siret")
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234", required = true)
|
||||
private String siret = null;
|
||||
@FormParam("state_id")
|
||||
@Schema(description = "Numéro SIRET ou RNA du club", example = "12345678901234", required = true)
|
||||
private String state_id = null;
|
||||
|
||||
@FormParam("international")
|
||||
@Schema(description = "Club international", example = "false", required = true)
|
||||
|
||||
@ -43,6 +43,7 @@ notif.affRequest.mail=
|
||||
|
||||
siren-api.key=siren-ap
|
||||
quarkus.rest-client."fr.titionfire.ffsaf.rest.client.SirenService".url=https://data.siren-api.fr/
|
||||
quarkus.rest-client."fr.titionfire.ffsaf.rest.client.StateIdService".url=https://www.data-asso.fr/api/
|
||||
|
||||
#Login
|
||||
quarkus.oidc.token-state-manager.split-tokens=true
|
||||
|
||||
@ -38,18 +38,22 @@ async function getData() {
|
||||
|
||||
let icon = null;
|
||||
if (d.uuid !== null) {
|
||||
const img = await getMeta(`${api_url}/api/club/${d.uuid}/logo`);
|
||||
let ratio = img.naturalHeight / img.naturalWidth;
|
||||
try {
|
||||
const img = await getMeta(`${api_url}/api/club/${d.uuid}/logo`);
|
||||
let ratio = img.naturalHeight / img.naturalWidth;
|
||||
|
||||
icon = L.icon({
|
||||
iconUrl: `${api_url}/api/club/${d.uuid}/logo`,
|
||||
icon = L.icon({
|
||||
iconUrl: `${api_url}/api/club/${d.uuid}/logo`,
|
||||
|
||||
iconSize: [50, 50 * ratio], // size of the icon
|
||||
//shadowSize: [50, 64], // size of the shadow
|
||||
iconAnchor: [25, 50 * ratio], // point of the icon which will correspond to marker's location
|
||||
//shadowAnchor: [4, 62], // the same for the shadow
|
||||
popupAnchor: [0, -50 * ratio] // point from which the popup should open relative to the iconAnchor
|
||||
});
|
||||
iconSize: [50, 50 * ratio], // size of the icon
|
||||
//shadowSize: [50, 64], // size of the shadow
|
||||
iconAnchor: [25, 50 * ratio], // point of the icon which will correspond to marker's location
|
||||
//shadowAnchor: [4, 62], // the same for the shadow
|
||||
popupAnchor: [0, -50 * ratio] // point from which the popup should open relative to the iconAnchor
|
||||
});
|
||||
}catch (e) {
|
||||
console.log("Error loading image for club", d.name, e);
|
||||
}
|
||||
}
|
||||
|
||||
for (const m of d.training_location) {
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import {useEffect, useRef} from 'react'
|
||||
import {Nav} from "./components/Nav.jsx";
|
||||
import {createBrowserRouter, Outlet, RouterProvider, useRouteError} from "react-router-dom";
|
||||
import {createBrowserRouter, Outlet, RouterProvider, useLocation, useRouteError} from "react-router-dom";
|
||||
import {Home} from "./pages/Homepage.jsx";
|
||||
import {AdminRoot, getAdminChildren} from "./pages/admin/AdminRoot.jsx";
|
||||
import {AuthCallback} from "./components/auhCallback.jsx";
|
||||
import {KeycloakContextProvider, useAuthDispatch} from "./hooks/useAuth.jsx";
|
||||
import {check_validity} from "./utils/auth.js";
|
||||
import {KeycloakContextProvider, useAuth, useAuthDispatch} from "./hooks/useAuth.jsx";
|
||||
import {check_validity, login} from "./utils/auth.js";
|
||||
import {ToastContainer} from "react-toastify";
|
||||
|
||||
import './App.css'
|
||||
@ -15,6 +15,8 @@ import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx";
|
||||
import {MePage} from "./pages/MePage.jsx";
|
||||
import {CompetitionRoot, getCompetitionChildren} from "./pages/competition/CompetitionRoot.jsx";
|
||||
import {getResultChildren, ResultRoot} from "./pages/result/ResultRoot.jsx";
|
||||
import {FallingLines} from "react-loader-spinner";
|
||||
import {getResultChildren, ResultRoot} from "./pages/result/ResultRoot.jsx";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -119,6 +121,43 @@ function Root() {
|
||||
theme="light"
|
||||
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>
|
||||
</>
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ export function HoraireEditor({data}) {
|
||||
|
||||
return <div className="row mb-3">
|
||||
<input name="training_day_time" value={JSON.stringify(out_data)} readOnly hidden/>
|
||||
<span className="input-group-text">Horaires d'entrainements</span>
|
||||
<span className="input-group-text">Horaires d'entraînements</span>
|
||||
<ul className="list-group form-control">
|
||||
{state.map((d, index) => {
|
||||
return <div key={index} className="input-group">
|
||||
@ -92,4 +92,4 @@ export function HoraireEditor({data}) {
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ export function LocationEditor({data, setModal, sendData}) {
|
||||
|
||||
return <div className="row mb-3">
|
||||
<input name="training_location" value={JSON.stringify(out_data)} readOnly hidden/>
|
||||
<span className="input-group-text">Lieux d'entrainements</span>
|
||||
<span className="input-group-text">Lieux d'entraînements</span>
|
||||
<ul className="list-group form-control">
|
||||
{state.map((d, index) => {
|
||||
return <div key={index} className="input-group">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {getCategoryFormBirthDate} from "../utils/Tools.js";
|
||||
import {getCategoryFormBirthDate, getCatName} from "../utils/Tools.js";
|
||||
import {useCountries} from "../hooks/useCountries.jsx";
|
||||
|
||||
export function BirthDayField({inti_date, inti_category, required = true}) {
|
||||
@ -27,7 +27,7 @@ export function BirthDayField({inti_date, inti_category, required = true}) {
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="category">Catégorie</span>
|
||||
<input type="text" className="form-control" placeholder="" name="category"
|
||||
aria-label="category" value={category ? category : ""} aria-describedby="category"
|
||||
aria-label="category" value={category ? getCatName(category) : ""} aria-describedby="category"
|
||||
disabled/>
|
||||
{canUpdate && <button className="btn btn-outline-secondary" type="button" id="button-addon1"
|
||||
onClick={updateCat}>Mettre à jours</button>}
|
||||
@ -88,13 +88,14 @@ export function CountryList({name, text, value, values = undefined, disabled = f
|
||||
</div>
|
||||
}
|
||||
|
||||
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true}) {
|
||||
return <div className="row">
|
||||
<div className="input-group mb-3">
|
||||
export function TextField({name, text, value, placeholder, type = "text", disabled = false, required = true, ttip = null}) {
|
||||
return <div className="row mb-3">
|
||||
<div className="input-group">
|
||||
<span className="input-group-text" id={name}>{text}</span>
|
||||
<input type={type} className="form-control" placeholder={placeholder ? placeholder : text} aria-label={name}
|
||||
name={name} aria-describedby={name} defaultValue={value} disabled={disabled} required={required}/>
|
||||
</div>
|
||||
{ttip}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ function ClubMenu() {
|
||||
</div>
|
||||
<ul className="dropdown-menu">
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/club/me">Mon club</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/club/member">Member</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/club/member">Membres</NavLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
@ -88,7 +88,7 @@ function AdminMenu() {
|
||||
Administration
|
||||
</div>
|
||||
<ul className="dropdown-menu">
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/admin/member">Member</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/admin/member">Membres</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/admin/club">Club</NavLink></li>
|
||||
<li className="nav-item"><NavLink className="nav-link" to="/admin/stats">Statistiques</NavLink></li>
|
||||
</ul>
|
||||
|
||||
@ -7,8 +7,8 @@ const removeDiacritics = str => {
|
||||
}
|
||||
|
||||
|
||||
export function SearchBar({search}) {
|
||||
const [searchInput, setSearchInput] = useState("");
|
||||
export function SearchBar({search, defaultValue = ""}) {
|
||||
const [searchInput, setSearchInput] = useState(defaultValue);
|
||||
|
||||
const handelChange = (e) => {
|
||||
setSearchInput(e.target.value);
|
||||
@ -40,4 +40,4 @@ export function SearchBar({search}) {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import {apiAxios, errFormater, getSaison} from "../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
|
||||
const notUpperCase = ["de", "la", "le", "les", "des", "du", "d'", "l'", "sur"];
|
||||
const notUpperCase = ["de", "la", "le", "les", "des", "du", "d'", "l'", "sur", 'lieu', 'dit'];
|
||||
|
||||
function formatAdresse(data) {
|
||||
const words = data.split(" ");
|
||||
@ -16,28 +16,17 @@ function formatAdresse(data) {
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
function reconstruireAdresse(infos) {
|
||||
console.log(infos);
|
||||
function reconstruireAdresse2(infos) {
|
||||
let adresseReconstruite = "";
|
||||
|
||||
if(infos.numero_voie === null){
|
||||
if (infos.complement_adresse) {
|
||||
adresseReconstruite += formatAdresse(infos.complement_adresse) + ', ';
|
||||
}
|
||||
}else{
|
||||
adresseReconstruite += infos.numero_voie + ' ';
|
||||
}
|
||||
if (infos?.complement)
|
||||
adresseReconstruite += formatAdresse(infos.complement) + ', ';
|
||||
|
||||
adresseReconstruite += formatAdresse(infos.type_voie) + ' ';
|
||||
adresseReconstruite += formatAdresse(infos.libelle_voie) + ', ';
|
||||
adresseReconstruite += infos.code_postal + ' ' + infos.libelle_commune + ', ';
|
||||
adresseReconstruite += formatAdresse(infos.voie) + ', ';
|
||||
adresseReconstruite += infos.code_postal + ' ' + infos.commune.nom + ', ';
|
||||
|
||||
if (infos.complement_adresse && infos.numero_voie !== null) {
|
||||
adresseReconstruite += formatAdresse(infos.complement_adresse) + ', ';
|
||||
}
|
||||
if (infos.code_cedex && infos.libelle_cedex) {
|
||||
adresseReconstruite += 'Cedex ' + infos.code_cedex + ' - ' + infos.libelle_cedex;
|
||||
}
|
||||
if (infos?.pays)
|
||||
adresseReconstruite += formatAdresse(infos.pays) + ', ';
|
||||
|
||||
if (adresseReconstruite.endsWith(', ')) {
|
||||
adresseReconstruite = adresseReconstruite.slice(0, -2);
|
||||
@ -46,6 +35,13 @@ function reconstruireAdresse(infos) {
|
||||
return adresseReconstruite;
|
||||
}
|
||||
|
||||
function getSaisonToAff(currentDate = new Date()) {
|
||||
if (currentDate.getMonth() >= 7) { //aout et plus
|
||||
return currentDate.getFullYear()
|
||||
} else {
|
||||
return currentDate.getFullYear() - 1
|
||||
}
|
||||
}
|
||||
|
||||
export function DemandeAff() {
|
||||
const {hash} = useLocation();
|
||||
@ -78,8 +74,7 @@ export function DemandeAff() {
|
||||
event.preventDefault()
|
||||
const formData = new FormData(event.target)
|
||||
formData.append("m1_role", event.target.m1_role?.value)
|
||||
formData.append("rna", event.target.rna?.value)
|
||||
formData.append("siret", event.target.siret?.value)
|
||||
formData.append("state_id", event.target.state_id?.value)
|
||||
|
||||
let error = false;
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
@ -145,7 +140,7 @@ export function DemandeAff() {
|
||||
}
|
||||
|
||||
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
|
||||
suivante.</p>
|
||||
Pour s’affilier, une association sportive doit réunir les conditions suivantes :
|
||||
@ -212,21 +207,23 @@ export function DemandeAff() {
|
||||
|
||||
function AssoInfo({initData, needFile}) {
|
||||
const [denomination, setDenomination] = useState("")
|
||||
const [siret, setSiret] = useState(initData.siret ? String(initData.siret) : "")
|
||||
const [rna, setRna] = useState(initData.rna ? initData.rna : "")
|
||||
const [rnaEnable, setRnaEnable] = useState(false)
|
||||
const [stateId, setStateId] = useState(initData.stateId ? String(initData.stateId) : (initData.state_id ? String(initData.state_id) : ""))
|
||||
const [adresse, setAdresse] = useState(initData.address ? initData.address : "")
|
||||
const [saison, setSaison] = useState(initData.saison ? initData.saison : getSaison())
|
||||
const [contact, setContact] = useState(initData.contact ? initData.contact : "")
|
||||
|
||||
const fetchSiret = () => {
|
||||
if (siret.length < 14) {
|
||||
toast.error("Le SIRET doit contenir 14 chiffres")
|
||||
const fetchStateId = () => {
|
||||
const regex = /^(?:\d{14}|W\d{9})$/;
|
||||
let sid = stateId;
|
||||
if (!regex.test(stateId)) {
|
||||
toast.error("Le format du SIRET/RNA est invalide");
|
||||
return;
|
||||
}else{
|
||||
if (stateId[0] !== 'W')
|
||||
sid = stateId.substring(0, 9); // Pour les SIRET, on ne garde que les 9 premiers chiffres (SIREN)
|
||||
}
|
||||
|
||||
toast.promise(
|
||||
apiAxios.get(`asso/siren/${siret.substring(0, siret.length - 5)}`),
|
||||
apiAxios.get(`asso/state_id/${sid}`),
|
||||
{
|
||||
pending: "Recherche de l'association en cours",
|
||||
success: "Association trouvée avec succès 🎉",
|
||||
@ -237,34 +234,14 @@ function AssoInfo({initData, needFile}) {
|
||||
}
|
||||
}
|
||||
).then(data => {
|
||||
const data2 = data.data.unite_legale
|
||||
setDenomination(data2.denomination)
|
||||
setRnaEnable(data2.identifiant_association === null)
|
||||
setRna(data2.identifiant_association ? data2.identifiant_association : "")
|
||||
const data2 = data.data
|
||||
setDenomination(data2.identite.nom)
|
||||
if (!initData.saison || adresse === "")
|
||||
setAdresse(reconstruireAdresse(data2.etablissement_siege))
|
||||
setAdresse(reconstruireAdresse2(data2.coordonnees.adresse_gestion))
|
||||
})
|
||||
}
|
||||
|
||||
const currentSaison = getSaison();
|
||||
|
||||
return <>
|
||||
<div className="input-group mb-3">
|
||||
<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>
|
||||
<input name="saison" value={getSaisonToAff()} readOnly hidden/>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="basic-addon1">Nom de l'association*</span>
|
||||
@ -274,28 +251,21 @@ function AssoInfo({initData, needFile}) {
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text">N° SIRET*</span>
|
||||
<input type="number" className="form-control" placeholder="siret" name="siret" required value={siret} disabled={!needFile}
|
||||
onChange={e => setSiret(e.target.value)}/>
|
||||
<span className="input-group-text">N° SIRET ou RNA*</span>
|
||||
<input type="text" className="form-control" placeholder="N° SIRET ou RNA*" name="state_id" required value={stateId} disabled={!needFile}
|
||||
onChange={e => setStateId(e.target.value)}/>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon2"
|
||||
onClick={fetchSiret}>Rechercher
|
||||
onClick={fetchStateId} hidden={false}>Rechercher
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<div className="input-group mb-3" hidden={false}>
|
||||
<span className="input-group-text" id="basic-addon1">Dénomination</span>
|
||||
<input type="text" className="form-control" placeholder="Appuyer sur rechercher pour compléter"
|
||||
aria-label="Dénomination"
|
||||
aria-describedby="basic-addon1" disabled value={denomination} readOnly/>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span className="input-group-text" id="basic-addon1">RNA</span>
|
||||
<input type="text" className="form-control" placeholder="RNA" aria-label="RNA"
|
||||
aria-describedby="basic-addon1"
|
||||
disabled={!rnaEnable} name="rna" value={rna} onChange={e => setRna(e.target.value)}/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="input-group">
|
||||
<span className="input-group-text" id="basic-addon1">Adresse administrative*</span>
|
||||
|
||||
@ -5,22 +5,22 @@ export const Home = () => {
|
||||
return <>
|
||||
<div className="container">
|
||||
<div style={{textAlign: "center", margin: "2em"}}>
|
||||
<h1 className="text-green-800 text-4xl">Bienvenu sur l'intranet de Fédération Française de Soft Armored Fighting</h1>
|
||||
<h1 className="text-green-800 text-4xl">Bienvenue sur l’intranet de la Fédération France Soft Armored Fighting</h1>
|
||||
</div>
|
||||
<div className="row" style={{marginTop: "3em"}}>
|
||||
<div className="col" style={{backgroundColor: "#FFFFFF79", padding: "0", borderRadius: "3em 3em 1em 1em", margin: "1em"}}>
|
||||
<div className="align-content-center"
|
||||
style={{textAlign: "center", backgroundColor: "#FFFFFF79", padding: "1em 1em 0em 1em", borderRadius: "3em 3em 0 0"}}>
|
||||
<h2><FontAwesomeIcon icon={faUser} size="2xl"/></h2>
|
||||
<h2>Pour les combatants</h2>
|
||||
<h2>Pour les licenciés</h2>
|
||||
</div>
|
||||
<p style={{padding: "0.5em 1em 0.5em 1em"}}>
|
||||
Vous y retrouverez toutes vos informations ainsi que l'état de votre inscription à la fédération. Vous pouvez également
|
||||
télécharger votre attestation d'inscription, vous inscrire aux compétitions ainsi qu'en consultée vos résultats sous réserve
|
||||
que le club organisateur les ait renseignés. <br/>
|
||||
télécharger votre attestation d'inscription, vous inscrire aux compétitions ainsi que consulter vos résultats sous réserve que
|
||||
le club organisateur les ait renseignés. <br/>
|
||||
<br/>
|
||||
Lors de votre première inscription, vous réservez un email contenant vos
|
||||
informations d'identification sur ce site, ce mail sera envoyé une fois votre inscription validée par nos soins.
|
||||
Lors de votre première inscription, vous recevrez un email contenant vos informations d'identification, ce mail sera envoyé
|
||||
une fois votre licence validée par le secrétariat.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col" style={{backgroundColor: "#FFFFFF79", padding: "0", borderRadius: "3em 3em 1em 1em", margin: "1em"}}>
|
||||
@ -30,12 +30,12 @@ export const Home = () => {
|
||||
<h2>Pour les clubs</h2>
|
||||
</div>
|
||||
<p style={{padding: "0.5em 1em 0.5em 1em"}}>
|
||||
C'est ici que vous pouvez faire l'inscription de vos membres à la fédération, que vous pouvez demander où renouveler votre
|
||||
demande d'affiliation, renseigné vos horaires, lieux d'entraînement et réseaux sociaux qui seront par la suite affichés sur le
|
||||
site ffsaf.fr.<br/>
|
||||
C'est ici que vous pouvez prendre les licences fédérales pour vos adhérents, que vous pouvez demander ou renouveler votre
|
||||
affiliation, renseigner vos horaires, lieux d'entraînement et réseaux sociaux qui seront par la suite affichés sur
|
||||
le site ffsaf.fr.<br/>
|
||||
Vous aurez par ailleurs la possibilité de publier des formulaires d'inscriptions pour vos compétitions ainsi
|
||||
que d'un publié les résultats.<br/><br/>
|
||||
Vous n'étes pas encore affilié à la fédération ? Vous pouvez faire une demande d'affiliation en cliquant <a href="/affiliation">içi</a>.
|
||||
que d'enregistrer les résultats.<br/><br/>
|
||||
Vous n'êtes pas encore affilié à la fédération ? Cliquez <a href="/affiliation">içi</a> pour faire votre première demande.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,4 +55,4 @@ export const Home = () => {
|
||||
}}>
|
||||
</div>
|
||||
</>
|
||||
};
|
||||
};
|
||||
|
||||
@ -6,38 +6,60 @@ import {useEffect, useState} from "react";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {Checkbox} from "../components/MemberCustomFiels.jsx";
|
||||
import * as Tools from "../utils/Tools.js";
|
||||
import {apiAxios, errFormater} from "../utils/Tools.js";
|
||||
import {apiAxios, errFormater, getCatName} from "../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {SearchBar} from "../components/SearchBar.jsx";
|
||||
import * as XLSX from "xlsx-js-style";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faEuroSign} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
|
||||
let lastRefresh = "";
|
||||
|
||||
export function MemberList({source}) {
|
||||
const {hash} = useLocation();
|
||||
const navigate = useNavigate();
|
||||
let page = Number(hash.substring(1));
|
||||
page = (page > 0) ? page : 1;
|
||||
|
||||
const [memberData, setMemberData] = useState([]);
|
||||
const [licenceData, setLicenceData] = useState([]);
|
||||
const [showLicenceState, setShowLicenceState] = useState(false);
|
||||
const [clubFilter, setClubFilter] = useState("");
|
||||
const [stateFilter, setStateFilter] = useState(4)
|
||||
const [lastSearch, setLastSearch] = useState("");
|
||||
const [paymentFilter, setPaymentFilter] = useState(2);
|
||||
|
||||
const setFilter = (filter) => {
|
||||
navigate("#" + encodeURI(JSON.stringify(filter)))
|
||||
}
|
||||
const filter = {
|
||||
page: 1,
|
||||
search: "",
|
||||
club: "",
|
||||
licenceRequest: 4,
|
||||
payment: 2,
|
||||
order: "",
|
||||
categorie: "",
|
||||
...JSON.parse(decodeURI(hash.substring(1)) || "{}"),
|
||||
}
|
||||
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error, refresh} = useFetch(`/member/find/${source}?page=${page}&licenceRequest=${stateFilter}&payment=${paymentFilter}`, setLoading, 1)
|
||||
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
refresh
|
||||
} = useFetch(`/member/find/${source}?page=${filter.page}&search=${filter.search}&club=${filter.club}&licenceRequest=${filter.licenceRequest}&payment=${filter.payment}&order=${filter.order}&categorie=${filter.categorie}`, setLoading, 1)
|
||||
|
||||
useEffect(() => {
|
||||
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}&payment=${paymentFilter}`);
|
||||
}, [hash, clubFilter, stateFilter, lastSearch, paymentFilter]);
|
||||
const tmp = `/member/find/${source}?page=${filter.page}&search=${filter.search}&club=${filter.club}&licenceRequest=${filter.licenceRequest}&payment=${filter.payment}&order=${filter.order}&categorie=${filter.categorie}`;
|
||||
if (tmp === lastRefresh)
|
||||
return;
|
||||
lastRefresh = tmp
|
||||
refresh(lastRefresh);
|
||||
}, [hash]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!data)
|
||||
return;
|
||||
if (data.page_count < filter.page) {
|
||||
setFilter({...filter, page: 1});
|
||||
}
|
||||
|
||||
const data2 = [];
|
||||
for (const e of data.result) {
|
||||
data2.push({
|
||||
@ -74,19 +96,19 @@ export function MemberList({source}) {
|
||||
}, [showLicenceState]);
|
||||
|
||||
const search = (search) => {
|
||||
if (search === lastSearch)
|
||||
if (search === filter.search)
|
||||
return;
|
||||
setLastSearch(search);
|
||||
setFilter({...filter, search: search});
|
||||
}
|
||||
|
||||
return <>
|
||||
<div>
|
||||
<div className="row">
|
||||
<div className="col-lg-9">
|
||||
<SearchBar search={search}/>
|
||||
<SearchBar search={search} defaultValue={filter.search}/>
|
||||
{data
|
||||
? <MakeCentralPanel data={data} visibleMember={memberData} navigate={navigate} showLicenceState={showLicenceState}
|
||||
page={page} source={source}/>
|
||||
page={filter.page} setPage={e => setFilter({...filter, page: e})} source={source}/>
|
||||
: error
|
||||
? <AxiosError error={error}/>
|
||||
: <Def/>
|
||||
@ -102,13 +124,28 @@ export function MemberList({source}) {
|
||||
<button className="btn btn-primary" onClick={() => navigate("pay")} style={{marginTop: "0.5rem"}}>Paiement des
|
||||
licences</button>}
|
||||
</div>
|
||||
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">Trie</div>
|
||||
<div className="card-body">
|
||||
<OrderBar onOrderChange={e => setFilter({...filter, order: e.join(",")})} defaultValues={filter.order} source={source}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card mb-4">
|
||||
<div className="card-header">Filtre</div>
|
||||
<div className="card-body">
|
||||
<FiltreBar showLicenceState={showLicenceState} setShowLicenceState={setShowLicenceState} data={data}
|
||||
clubFilter={clubFilter} setClubFilter={setClubFilter} source={source}
|
||||
stateFilter={stateFilter} setStateFilter={setStateFilter} paymentFilter={paymentFilter}
|
||||
setPaymentFilter={setPaymentFilter}/>
|
||||
<FiltreBar showLicenceState={showLicenceState}
|
||||
setShowLicenceState={setShowLicenceState}
|
||||
clubFilter={filter.club}
|
||||
setClubFilter={e => setFilter({...filter, club: e})}
|
||||
source={source}
|
||||
stateFilter={filter.licenceRequest}
|
||||
setStateFilter={e => setFilter({...filter, licenceRequest: e})}
|
||||
paymentFilter={filter.payment}
|
||||
setPaymentFilter={e => setFilter({...filter, payment: e})}
|
||||
catFilter={filter.categorie}
|
||||
setCatFilter={e => setFilter({...filter, categorie: e})}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -169,6 +206,10 @@ function FileOutput() {
|
||||
certifDate: e.certif ? new Date(e.certif.split("¤")[1]) : '',
|
||||
}
|
||||
|
||||
if (isNaN(tmp.certifDate) || tmp.certifDate === 'NaN') {
|
||||
tmp.certifDate = ''
|
||||
}
|
||||
|
||||
//tmp.birthdate.setMilliseconds(0);
|
||||
//tmp.birthdate.setSeconds(0);
|
||||
//tmp.birthdate.setMinutes(0);
|
||||
@ -332,11 +373,11 @@ function FileInput() {
|
||||
);
|
||||
}
|
||||
|
||||
function MakeCentralPanel({data, visibleMember, navigate, showLicenceState, page, source}) {
|
||||
function MakeCentralPanel({data, visibleMember, navigate, showLicenceState, page, setPage, source}) {
|
||||
const pages = []
|
||||
for (let i = 1; i <= data.page_count; i++) {
|
||||
pages.push(<li key={i} className={"page-item " + ((page === i) ? "active" : "")}>
|
||||
<span className="page-link" onClick={() => navigate("#" + i)}>{i}</span>
|
||||
<span className="page-link" onClick={() => setPage(i)}>{i}</span>
|
||||
</li>);
|
||||
}
|
||||
|
||||
@ -353,10 +394,10 @@ function MakeCentralPanel({data, visibleMember, navigate, showLicenceState, page
|
||||
<nav aria-label="Page navigation">
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={"page-item" + ((page <= 1) ? " disabled" : "")}>
|
||||
<span className="page-link" onClick={() => navigate("#" + (page - 1))}>«</span></li>
|
||||
<span className="page-link" onClick={() => setPage(page - 1)}>«</span></li>
|
||||
{pages}
|
||||
<li className={"page-item" + ((page >= data.page_count) ? " disabled" : "")}>
|
||||
<span className="page-link" onClick={() => navigate("#" + (page + 1))}>»</span></li>
|
||||
<span className="page-link" onClick={() => setPage(page + 1)}>»</span></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
@ -365,45 +406,146 @@ function MakeCentralPanel({data, visibleMember, navigate, showLicenceState, page
|
||||
|
||||
function MakeRow({member, showLicenceState, navigate, source}) {
|
||||
const rowContent = <>
|
||||
<div className="row">
|
||||
<div className="row" style={{padding: "0.6em 0"}}>
|
||||
<span className="col-auto">{(member.licence_number ? String(member.licence_number).padStart(5, '0') : "-------") + " "}
|
||||
{(showLicenceState && member.licence != null && member.licence.pay)? <FontAwesomeIcon icon={faEuroSign}/> : <> </>}</span>
|
||||
{(showLicenceState && member.licence != null && member.licence.pay) ? <FontAwesomeIcon icon={faEuroSign}/> : <> </>}</span>
|
||||
<div className="ms-2 col-auto">
|
||||
<div className="fw-bold">{member.fname} {member.lname}</div>
|
||||
</div>
|
||||
</div>
|
||||
{source === "club" ?
|
||||
<small>{member.categorie}</small>
|
||||
: <small>{member.club?.name || "Sans club"}</small>}
|
||||
<div style={{verticalAlign: "center", margin: "auto 0"}}>
|
||||
{source === "club" ?
|
||||
<small>{getCatName(member.categorie)}</small>
|
||||
: <div style={{
|
||||
textAlign: "right",
|
||||
fontSize: "small"
|
||||
}}>{member.club?.name || "Sans club"}<br/><small>{getCatName(member.categorie)}</small></div>}
|
||||
</div>
|
||||
|
||||
</>
|
||||
|
||||
if (showLicenceState && member.licence != null) {
|
||||
return <div
|
||||
className={"list-group-item d-flex justify-content-between align-items-start list-group-item-action list-group-item-"
|
||||
+ (member.licence.validate ? "success" : (member.licence.certificate.length > 1 ? "warning" : "danger"))}
|
||||
onClick={() => navigate("" + member.id)}>{rowContent}</div>
|
||||
} else {
|
||||
return <div className="list-group-item d-flex justify-content-between align-items-start list-group-item-action"
|
||||
onClick={() => navigate("" + member.id)}>
|
||||
return <a className={"list-group-item d-flex justify-content-between align-items-start list-group-item-action list-group-item-"
|
||||
+ (member.licence.validate ? "success" : (member.licence.certificate.length > 1 ? "warning" : "danger"))}
|
||||
style={{padding: "0 1em"}}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
navigate("" + member.id)
|
||||
}}
|
||||
href={"member/" + member.id}>
|
||||
{rowContent}
|
||||
</div>
|
||||
</a>
|
||||
} else {
|
||||
return <a className="list-group-item d-flex justify-content-between align-items-start list-group-item-action"
|
||||
style={{padding: "0 1em"}}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
navigate("" + member.id)
|
||||
}}
|
||||
href={"member/" + member.id}>
|
||||
{rowContent}
|
||||
</a>
|
||||
}
|
||||
}
|
||||
|
||||
let allClub = []
|
||||
function OrderBar({onOrderChange, defaultValues = "", source}) {
|
||||
const [orderCriteria, setOrderCriteria] = useState([...defaultValues.split(",").filter(c => c !== ''), '']);
|
||||
|
||||
function FiltreBar({showLicenceState, setShowLicenceState, data, clubFilter, setClubFilter, source, stateFilter, setStateFilter, paymentFilter, setPaymentFilter}) {
|
||||
useEffect(() => {
|
||||
if (!data)
|
||||
return;
|
||||
allClub.push(...data.result.map((e) => e.club?.name))
|
||||
allClub = allClub.filter((value, index, self) => self.indexOf(value) === index).filter(value => value != null).sort()
|
||||
}, [data]);
|
||||
const handleChange = (index, value) => {
|
||||
const newCriteria = [...orderCriteria];
|
||||
newCriteria[index] = value;
|
||||
|
||||
// Si le dernier critère est rempli, on en ajoute un nouveau
|
||||
if (index === orderCriteria.length - 1 && value !== '') {
|
||||
newCriteria.push('');
|
||||
}
|
||||
// Si un critère (sauf le premier) est réinitialisé, on le supprime
|
||||
else if (value === '' && (index !== 0 || orderCriteria.length > 1)) {
|
||||
newCriteria.splice(index, 1);
|
||||
}
|
||||
|
||||
setOrderCriteria(newCriteria);
|
||||
onOrderChange(newCriteria.filter(c => c !== ''));
|
||||
};
|
||||
|
||||
// Liste de toutes les options possibles
|
||||
const allOptions = [
|
||||
{value: 'lname n', label: 'Nom ↓', base: 'lname'},
|
||||
{value: 'lname i', label: 'Nom ↑', base: 'lname'},
|
||||
{value: 'fname n', label: 'Prénom ↓', base: 'fname'},
|
||||
{value: 'fname i', label: 'Prénom ↑', base: 'fname'},
|
||||
{value: 'categorie n', label: 'Catégorie ↓', base: 'categorie'},
|
||||
{value: 'categorie i', label: 'Catégorie ↑', base: 'categorie'},
|
||||
{value: 'licence n', label: 'Licence ↓', base: 'licence'},
|
||||
{value: 'licence i', label: 'Licence ↑', base: 'licence'},
|
||||
];
|
||||
|
||||
if (source === "admin") {
|
||||
allOptions.push(
|
||||
{value: 'club.name n', label: 'Club ↓', base: 'club.name'},
|
||||
{value: 'club.name i', label: 'Club ↑', base: 'club.name'},
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mb-3">
|
||||
{orderCriteria.map((criteria, index) => {
|
||||
// Récupère les bases des critères déjà sélectionnés (sauf le courant)
|
||||
const usedBases = orderCriteria
|
||||
.filter((c, i) => c !== '' && i !== index)
|
||||
.map(c => allOptions.find(o => o.value === c)?.base);
|
||||
|
||||
// Filtre les options disponibles
|
||||
const availableOptions = allOptions.filter(option =>
|
||||
!usedBases.includes(option.base) || option.value === criteria
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
key={index}
|
||||
className="form-select mb-2"
|
||||
value={criteria}
|
||||
onChange={(e) => handleChange(index, e.target.value)}
|
||||
>
|
||||
<option value="">----</option>
|
||||
{availableOptions.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FiltreBar({
|
||||
showLicenceState,
|
||||
setShowLicenceState,
|
||||
clubFilter,
|
||||
setClubFilter,
|
||||
source,
|
||||
stateFilter,
|
||||
setStateFilter,
|
||||
paymentFilter,
|
||||
setPaymentFilter,
|
||||
catFilter,
|
||||
setCatFilter,
|
||||
}) {
|
||||
|
||||
return <div>
|
||||
<div className="mb-3">
|
||||
<Checkbox value={showLicenceState} onChange={setShowLicenceState} label="Afficher l'état des licences"/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<select className="form-select" value={catFilter} onChange={event => setCatFilter(event.target.value)}>
|
||||
<option value="">--- toute les catégories ---</option>
|
||||
{Tools.CatList.map(cat => (
|
||||
<option key={cat} value={cat}>{getCatName(cat)}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{source !== "club" && <ClubSelectFilter clubFilter={clubFilter} setClubFilter={setClubFilter}/>}
|
||||
<div className="mb-3">
|
||||
<select className="form-select" value={stateFilter} onChange={event => setStateFilter(Number(event.target.value))}>
|
||||
|
||||
@ -68,7 +68,7 @@ function MakeRow({request, navigate}) {
|
||||
<div className="ms-2 col-auto">
|
||||
<div className="fw-bold">{request.name}</div>
|
||||
</div>
|
||||
<small style={{textAlign: 'right'}}>{request.saison}-{request.saison + 1}<br/>{request.siret}</small>
|
||||
<small style={{textAlign: 'right'}}>{request.saison}-{request.saison + 1}<br/>{request.state_id}</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -104,4 +104,4 @@ function Def() {
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import {RoleList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faFilePdf} from "@fortawesome/free-solid-svg-icons";
|
||||
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
|
||||
|
||||
const vite_url = import.meta.env.VITE_URL;
|
||||
|
||||
@ -67,8 +66,7 @@ function Content({data, refresh}) {
|
||||
|
||||
formData.append('id', data.id);
|
||||
formData.append('name', event.target.name.value);
|
||||
formData.append('siret', event.target.siret.value);
|
||||
formData.append('rna', event.target.rna.value);
|
||||
formData.append('state_id', event.target.state_id.value);
|
||||
formData.append('address', event.target.address.value);
|
||||
formData.append('contact', event.target.contact.value);
|
||||
|
||||
@ -166,7 +164,7 @@ function Content({data, refresh}) {
|
||||
<input name="id" value={data.id} readOnly hidden/>
|
||||
<div className="card-header">Demande d'affiliation</div>
|
||||
<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>
|
||||
|
||||
<div className="row mb-3">
|
||||
@ -178,8 +176,7 @@ function Content({data, refresh}) {
|
||||
{data.club && <div className="form-text" id="name">Ancien nom: {data.club_name}</div>}
|
||||
</div>
|
||||
|
||||
<TextField type="number" name="siret" text="SIRET" value={data.siret} disabled={true}/>
|
||||
<TextField name="rna" text="RNA" value={data.rna} required={false}/>
|
||||
<TextField name="state_id" text="SIRET ou RNA" value={data.stateId} disabled={true}/>
|
||||
<TextField name="address" text="Adresse" value={data.address}/>
|
||||
<TextField name="contact" text="Contact administratif" value={data.contact}/>
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ export function ClubList() {
|
||||
country: e.country,
|
||||
siret: e.siret,
|
||||
no_affiliation: e.no_affiliation,
|
||||
affiliation: showAffiliationState ? affiliationData.find(aff => (aff.id >= 0) ? aff.club === e.id : aff.club === e.siret) : null
|
||||
affiliation: showAffiliationState ? affiliationData.find(aff => (aff.id >= 0) ? Number(aff.club) === e.id : aff.club === e.state_id) : null
|
||||
})
|
||||
}
|
||||
setClubData(data2);
|
||||
@ -197,4 +197,4 @@ function Def() {
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
<li className="list-group-item"><ThreeDots/></li>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,8 +130,7 @@ function InformationForm({data}) {
|
||||
</div>
|
||||
</div>
|
||||
{!switchOn && <>
|
||||
<TextField name="siret" text="SIRET" value={data.siret} required={false} type="number"/>
|
||||
<TextField name="rna" text="RNA" value={data.rna} required={false}/>
|
||||
<TextField name="state_id" text="SIRET ou RNA" value={data.state_id} required={false}/>
|
||||
<TextField name="contact_intern" text="Contact interne" value={data.contact_intern} required={false}
|
||||
placeholder="example@test.com"/>
|
||||
<TextField name="address" text="Adresse administrative" value={data.address} required={false}
|
||||
@ -172,6 +171,7 @@ function InformationForm({data}) {
|
||||
export function BureauCard({clubData}) {
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const {data, error} = useFetch(`/club/desk/${clubData.id}`, setLoading, 1)
|
||||
const navigate = useNavigate();
|
||||
|
||||
return <>
|
||||
<div className="card mb-4">
|
||||
@ -179,7 +179,8 @@ export function BureauCard({clubData}) {
|
||||
<div className="card-body">
|
||||
<ul className="list-group">
|
||||
{data && data.map((d, index) => {
|
||||
return <div key={index} className="list-group-item d-flex justify-content-between align-items-start">
|
||||
return <div key={index} className="list-group-item d-flex justify-content-between align-items-start list-group-item-action"
|
||||
onClick={__ => navigate(`/admin/member/${d.id}`)}>
|
||||
<div className="me-auto"><small>{d.role}</small><br/>{d.lname} {d.fname}</div>
|
||||
</div>
|
||||
})}
|
||||
@ -188,4 +189,4 @@ export function BureauCard({clubData}) {
|
||||
</div>
|
||||
{error && <AxiosError error={error}/>}
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +84,7 @@ function InformationForm() {
|
||||
</div>
|
||||
</div>
|
||||
{!switchOn && <>
|
||||
<TextField name="siret" text="SIRET" required={false} type="number"/>
|
||||
<TextField name="rna" text="RNA" required={false}/>
|
||||
<TextField name="state_id" text="SIRET ou RNA" required={false}/>
|
||||
<TextField name="contact_intern" text="Contact interne" required={false} placeholder="example@test.com"/>
|
||||
<TextField name="address" text="Adresse administrative" required={false} placeholder="Adresse administrative"/>
|
||||
|
||||
|
||||
@ -34,13 +34,13 @@ export function MemberPage() {
|
||||
}
|
||||
}
|
||||
).then(_ => {
|
||||
navigate("/admin/member")
|
||||
navigate(-1)
|
||||
})
|
||||
}
|
||||
|
||||
return <>
|
||||
<h2>Page membre</h2>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate("/admin/member")}>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate(-1)}>
|
||||
« retour
|
||||
</button>
|
||||
{data
|
||||
|
||||
@ -23,7 +23,7 @@ export function ClubRoot() {
|
||||
|
||||
return <>
|
||||
<div style={{display: 'flex', flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap'}}>
|
||||
<h1>Espace club</h1><h3 style={{marginLeft: '0.75em'}}>{club}</h3></div>
|
||||
<h3 style={{marginLeft: '0.75em'}}>Club: {club}</h3></div>
|
||||
<LoadingProvider>
|
||||
<Outlet/>
|
||||
</LoadingProvider>
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../../hooks/useFetch.js";
|
||||
import {useEffect, useReducer, useState} from "react";
|
||||
import {useState} from "react";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faEye, faFilePdf, faPen} from "@fortawesome/free-solid-svg-icons";
|
||||
import {faEye, faFilePdf} from "@fortawesome/free-solid-svg-icons";
|
||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
import {apiAxios, getSaison} from "../../../utils/Tools.js";
|
||||
import {apiAxios} from "../../../utils/Tools.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {SimpleReducer} from "../../../utils/SimpleReducer.jsx";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
const vite_url = import.meta.env.VITE_URL;
|
||||
@ -42,8 +41,8 @@ export function AffiliationCard({clubData}) {
|
||||
|
||||
<a href={`${vite_url}/api/club/me/affiliation`} target='#'>
|
||||
<button className="btn btn-primary" type="button" id="button-addon1" style={{marginTop: '1em'}}
|
||||
onClick={e => null}>
|
||||
Téléchargée l'attestation d'affiliation <FontAwesomeIcon icon={faFilePdf}></FontAwesomeIcon>
|
||||
onClick={_ => null}>
|
||||
Télécharger l’attestation d’affiliation <FontAwesomeIcon icon={faFilePdf}></FontAwesomeIcon>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
@ -140,10 +139,10 @@ function ModalContent2({clubData, data}) {
|
||||
}
|
||||
}
|
||||
|
||||
if (list.length !== 3) {
|
||||
toast.error("Il faut sélectionner 3 membres pour renouveler l'affiliation")
|
||||
return
|
||||
while (list.length < 3) {
|
||||
list.push(-1)
|
||||
}
|
||||
|
||||
apiAxios.get(`/club/renew/${clubData.id}?m1=${list[0]}&m2=${list[1]}&m3=${list[2]}`).then(data => {
|
||||
navigate('/affiliation#d' + encodeURI(JSON.stringify(data.data)))
|
||||
})
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import {useNavigate, useParams} from "react-router-dom";
|
||||
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {useFetch} from "../../../hooks/useFetch.js";
|
||||
import {toast} from "react-toastify";
|
||||
import {apiAxios, errFormater} from "../../../utils/Tools.js";
|
||||
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
|
||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||
import {AffiliationCard, BureauCard} from "./AffiliationCard.jsx";
|
||||
import {CountryList, TextField} from "../../../components/MemberCustomFiels.jsx";
|
||||
@ -22,7 +20,7 @@ export function MyClubPage() {
|
||||
const {data, error} = useFetch(`/club/me`, setLoading, 1)
|
||||
|
||||
return <>
|
||||
<h2>Mon club</h2>
|
||||
<h3>Données administratives</h3>
|
||||
{data
|
||||
? <div>
|
||||
<div className="row">
|
||||
@ -77,8 +75,7 @@ function InformationForm({data}) {
|
||||
<CountryList name="country" text="Pays" value={data.country} disabled={true}/>
|
||||
|
||||
{!data.international && <>
|
||||
<TextField name="siret" text="SIRET" value={data.siret} type="number" disabled={true}/>
|
||||
<TextField name="rna" text="RNA" value={data.rna} required={false} disabled={true}/>
|
||||
<TextField name="state_id" text="SIRET ou RNA" value={data.state_id} disabled={true}/>
|
||||
</>}
|
||||
|
||||
<div className="row mb-3">
|
||||
@ -91,7 +88,7 @@ function InformationForm({data}) {
|
||||
<div className="col-md-6">
|
||||
<a href={`${vite_url}/api/club/${data.id}/status`} target='_blank'>
|
||||
<button className="btn btn-outline-secondary" type="button" id="button-addon1"
|
||||
onClick={e => null}>
|
||||
onClick={_ => null}>
|
||||
<FontAwesomeIcon icon={faFilePdf} size="5x"></FontAwesomeIcon><br/>
|
||||
Voir les statues
|
||||
</button>
|
||||
|
||||
@ -49,7 +49,9 @@ export function InformationForm({data}) {
|
||||
<TextField name="lname" text="Nom" value={data.lname}/>
|
||||
<TextField name="fname" text="Prénom" value={data.fname}/>
|
||||
<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}
|
||||
values={{NA: 'N/A', H: 'H', F: 'F'}}/>
|
||||
<CountryList name="country" text="Pays" value={data.country}/>
|
||||
|
||||
@ -33,13 +33,13 @@ export function MemberPage() {
|
||||
}
|
||||
}
|
||||
).then(_ => {
|
||||
navigate("/club/member")
|
||||
navigate(-1)
|
||||
})
|
||||
}
|
||||
|
||||
return <>
|
||||
<h2>Page membre</h2>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate("/club/member")}>
|
||||
<button type="button" className="btn btn-link" onClick={() => navigate(-1)}>
|
||||
« retour
|
||||
</button>
|
||||
{data
|
||||
|
||||
@ -23,6 +23,20 @@ export const errFormater = (data, msg) => {
|
||||
return `${msg} (${data.response.statusText}: ${JSON.stringify(data.response.data)}) 😕`
|
||||
}
|
||||
|
||||
export const CatList = [
|
||||
"SUPER_MINI",
|
||||
"MINI_POUSSIN",
|
||||
"POUSSIN",
|
||||
"BENJAMIN",
|
||||
"MINIME",
|
||||
"CADET",
|
||||
"JUNIOR",
|
||||
"SENIOR1",
|
||||
"SENIOR2",
|
||||
"VETERAN1",
|
||||
"VETERAN2"
|
||||
];
|
||||
|
||||
export function getCategoryFormBirthDate(birth_date, currentDate = new Date()) {
|
||||
const currentSaison = getSaison(currentDate)
|
||||
const birthYear = birth_date.getFullYear()
|
||||
@ -60,3 +74,32 @@ export function getSaison(currentDate = new Date()) {
|
||||
return currentDate.getFullYear() - 1
|
||||
}
|
||||
}
|
||||
|
||||
export function getCatName(cat) {
|
||||
switch (cat) {
|
||||
case "SUPER_MINI":
|
||||
return "Super Mini";
|
||||
case "MINI_POUSSIN":
|
||||
return "Mini Poussin";
|
||||
case "POUSSIN":
|
||||
return "Poussin";
|
||||
case "BENJAMIN":
|
||||
return "Benjamin";
|
||||
case "MINIME":
|
||||
return "Minime";
|
||||
case "CADET":
|
||||
return "Cadet";
|
||||
case "JUNIOR":
|
||||
return "Junior";
|
||||
case "SENIOR1":
|
||||
return "Senior 1";
|
||||
case "SENIOR2":
|
||||
return "Senior 2";
|
||||
case "VETERAN1":
|
||||
return "Vétéran 1";
|
||||
case "VETERAN2":
|
||||
return "Vétéran 2";
|
||||
default:
|
||||
return cat;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,8 +9,11 @@ export function check_validity(online_callback = () => {
|
||||
axios.get(`${vite_url}/api/auth/userinfo`).then(data => {
|
||||
online_callback({state: true, userinfo: data.data});
|
||||
})
|
||||
}else{
|
||||
online_callback({state: false});
|
||||
}
|
||||
}).catch(() => {
|
||||
console.log("=> Not authenticated");
|
||||
online_callback({state: false});
|
||||
})
|
||||
}
|
||||
@ -32,4 +35,4 @@ export function login_redirect() {
|
||||
|
||||
export function logout() {
|
||||
window.location.href = `${vite_url}/api/logout`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,4 +24,4 @@ export default ({mode}) => {
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user