Compare commits

..

No commits in common. "c58dedf80a9eef851053dca2f0935c77f1b76eb0" and "1908de681e6b129f885800f4c801d1f412d1da14" have entirely different histories.

39 changed files with 283 additions and 808 deletions

View File

@ -1,17 +1,20 @@
package fr.titionfire.ffsaf.data.id; package fr.titionfire.ffsaf.data.id;
import jakarta.persistence.Embeddable; import fr.titionfire.ffsaf.data.model.CompetitionModel;
import lombok.*; import fr.titionfire.ffsaf.data.model.MembreModel;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
@Getter @Data
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@Embeddable
public class RegisterId implements Serializable { public class RegisterId implements Serializable {
private Long competitionId; @ManyToOne
private Long membreId; @JoinColumn(name = "id_competition")
private CompetitionModel competition;
@ManyToOne
@JoinColumn(name = "id_membre")
private MembreModel membre;
} }

View File

@ -1,7 +1,6 @@
package fr.titionfire.ffsaf.data.model; package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.RegisterMode;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -22,7 +21,6 @@ import java.util.List;
@Table(name = "compet") @Table(name = "compet")
public class CompetitionModel { public class CompetitionModel {
@Id @Id
@Access(AccessType.PROPERTY)
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
Long id; Long id;
@ -38,26 +36,9 @@ public class CompetitionModel {
String uuid; String uuid;
Date date; Date date;
Date todate;
@Column(columnDefinition = "TEXT")
String description;
String adresse;
Date startRegister;
Date endRegister;
RegisterMode registerMode;
boolean publicVisible;
@OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL) @OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
List<RegisterModel> insc; List<RegisterModel> insc;
String owner; String owner;
String data1;
String data2;
String data3;
String data4;
} }

View File

@ -41,10 +41,10 @@ public class MatchModel {
String c2_str = null; String c2_str = null;
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_category", referencedColumnName = "id") @JoinColumn(name = "id_poule", referencedColumnName = "id")
CategoryModel category = null; PouleModel poule = null;
long category_ord = 0; long poule_ord = 0;
boolean isEnd = true; boolean isEnd = true;
@ -52,5 +52,5 @@ public class MatchModel {
@CollectionTable(name = "score", joinColumns = @JoinColumn(name = "id_match")) @CollectionTable(name = "score", joinColumns = @JoinColumn(name = "id_match"))
List<ScoreEmbeddable> scores = new ArrayList<>(); List<ScoreEmbeddable> scores = new ArrayList<>();
char poule = 'A'; char groupe = 'A';
} }

View File

@ -17,8 +17,8 @@ import java.util.List;
@RegisterForReflection @RegisterForReflection
@Entity @Entity
@Table(name = "category") @Table(name = "poule")
public class CategoryModel { public class PouleModel {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
Long id; Long id;
@ -34,11 +34,11 @@ public class CategoryModel {
CompetitionModel compet; CompetitionModel compet;
@OneToMany(fetch = FetchType.LAZY) @OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "id_category", referencedColumnName = "id") @JoinColumn(name = "id_poule", referencedColumnName = "id")
List<MatchModel> matchs; List<MatchModel> matchs;
@OneToMany(fetch = FetchType.LAZY) @OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "id_category", referencedColumnName = "id") @JoinColumn(name = "id_poule", referencedColumnName = "id")
List<TreeModel> tree; List<TreeModel> tree;
Integer type; Integer type;

View File

@ -17,17 +17,14 @@ import lombok.Setter;
@Entity @Entity
@Table(name = "register") @Table(name = "register")
@IdClass(RegisterId.class)
public class RegisterModel { public class RegisterModel {
@Id
@EmbeddedId
RegisterId id;
@MapsId("competitionId")
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_competition") @JoinColumn(name = "id_competition")
CompetitionModel competition; CompetitionModel competition;
@MapsId("membreId") @Id
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_membre") @JoinColumn(name = "id_membre")
MembreModel membre; MembreModel membre;
@ -40,14 +37,4 @@ public class RegisterModel {
@JoinColumn(name = "club") @JoinColumn(name = "club")
ClubModel club = null; ClubModel club = null;
public RegisterModel(CompetitionModel competition, MembreModel membre, Integer weight, int overCategory,
Categorie categorie, ClubModel club) {
this.id = new RegisterId(competition.getId(), membre.getId());
this.competition = competition;
this.membre = membre;
this.weight = weight;
this.overCategory = overCategory;
this.categorie = categorie;
this.club = club;
}
} }

View File

@ -20,8 +20,8 @@ public class TreeModel {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
Long id; Long id;
@Column(name = "id_category") @Column(name = "id_poule")
Long category; Long poule;
Integer level; Integer level;

View File

@ -1,9 +1,9 @@
package fr.titionfire.ffsaf.data.repository; package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CategoryModel; import fr.titionfire.ffsaf.data.model.PouleModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped @ApplicationScoped
public class CategoryRepository implements PanacheRepositoryBase<CategoryModel, Long> { public class PouleRepository implements PanacheRepositoryBase<PouleModel, Long> {
} }

View File

@ -1,11 +1,9 @@
package fr.titionfire.ffsaf.data.repository; package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.id.RegisterId;
import fr.titionfire.ffsaf.data.model.RegisterModel; import fr.titionfire.ffsaf.data.model.RegisterModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import io.quarkus.hibernate.reactive.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped @ApplicationScoped
public class RegisterRepository implements PanacheRepositoryBase<RegisterModel, RegisterId> { public class RegisterRepository implements PanacheRepository<RegisterModel> {
} }

View File

@ -2,13 +2,11 @@ package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CompetitionModel; import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository; import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.data.repository.RegisterRepository;
import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.ServerCustom;
import fr.titionfire.ffsaf.net2.data.SimpleCompet; import fr.titionfire.ffsaf.net2.data.SimpleCompet;
import fr.titionfire.ffsaf.net2.request.SReqCompet; import fr.titionfire.ffsaf.net2.request.SReqCompet;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.RegisterMode;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.quarkus.cache.Cache; import io.quarkus.cache.Cache;
import io.quarkus.cache.CacheName; import io.quarkus.cache.CacheName;
@ -17,10 +15,7 @@ import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -33,9 +28,6 @@ public class CompetPermService {
@Inject @Inject
ServerCustom serverCustom; ServerCustom serverCustom;
@Inject
CompetitionRepository competitionRepository;
@Inject @Inject
@CacheName("safca-config") @CacheName("safca-config")
Cache cache; Cache cache;
@ -45,16 +37,13 @@ public class CompetPermService {
Cache cacheAccess; Cache cacheAccess;
@Inject @Inject
@CacheName("have-access") CompetitionRepository competitionRepository;
Cache cacheNoneAccess;
@Inject
RegisterRepository registerRepository;
public Uni<SimpleCompet> getSafcaConfig(long id) { public Uni<SimpleCompet> getSafcaConfig(long id) {
return cache.get(id, k -> { return cache.get(id, k -> {
CompletableFuture<SimpleCompet> f = new CompletableFuture<>(); CompletableFuture<SimpleCompet> f = new CompletableFuture<>();
SReqCompet.getConfig(serverCustom.clients, id, f); SReqCompet.getConfig(serverCustom.clients, id, f);
System.out.println("get config");
try { try {
return f.get(1500, TimeUnit.MILLISECONDS); return f.get(1500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
@ -63,172 +52,61 @@ public class CompetPermService {
}); });
} }
public Uni<List<Long>> getAllHaveAdminAccess(SecurityCtx securityCtx) { public Uni<HashMap<Long, String>> getAllHaveAccess(String subject) {
ArrayList<Long> out = new ArrayList<>(); return cacheAccess.get(subject, k -> {
CompletableFuture<HashMap<Long, String>> f = new CompletableFuture<>();
Uni<HashMap<Long, String>> safca = cacheAccess.getAsync(securityCtx.getSubject(), SReqCompet.getAllHaveAccess(serverCustom.clients, subject, f);
k -> competitionRepository.list("system = ?1", CompetitionSystem.SAFCA) System.out.println("get all have access");
.chain(competitionModels -> { try {
CompletableFuture<HashMap<String, String>> f = new CompletableFuture<>(); return f.get(1500, TimeUnit.MILLISECONDS);
SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f); } catch (InterruptedException | ExecutionException | TimeoutException e) {
return Uni.createFrom().future(f, Duration.ofMillis(1500)) throw new RuntimeException(e);
.map(map_ -> { }
HashMap<Long, String> map = new HashMap<>(); });
map_.forEach((key, value) -> map.put(Long.parseLong(key), value));
for (CompetitionModel model : competitionModels) {
if (model.getOwner().equals(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "owner");
else if (securityCtx.roleHas("federation_admin")
|| securityCtx.roleHas("safca_super_admin"))
map.putIfAbsent(model.getId(), "admin");
}
return map;
});
}))
.onFailure().call(throwable -> cacheAccess.invalidate(securityCtx.getSubject()));
Uni<HashMap<Long, String>> none = cacheNoneAccess.getAsync(securityCtx.getSubject(),
k -> competitionRepository.list("system = ?1", CompetitionSystem.NONE)
.map(competitionModels -> {
HashMap<Long, String> map = new HashMap<>();
for (CompetitionModel model : competitionModels) {
if (model.getOwner().equals(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "owner");
else if (securityCtx.roleHas("federation_admin"))
map.putIfAbsent(model.getId(), "admin");
else if (securityCtx.isInClubGroup(model.getClub().getId()) && (securityCtx.roleHas(
"club_president")
|| securityCtx.roleHas("club_respo_intra") || securityCtx.roleHas(
"club_secretaire")
|| securityCtx.roleHas("club_tresorier")))
map.putIfAbsent(model.getId(), "admin");
}
return map;
}));
return safca.invoke(map ->
map.forEach((k, v) -> {
if (v.equals("owner") || v.equals("admin"))
out.add(k);
})
)
.call(__ -> none.invoke(map ->
map.forEach((k, v) -> {
if (v.equals("owner") || v.equals("admin"))
out.add(k);
})
))
.map(__ -> out.stream().distinct().toList());
} }
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
*/
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) { public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
return hasViewPerm(securityCtx, Uni.createFrom().item(competitionModel)); return hasViewPerm(securityCtx, Uni.createFrom().item(competitionModel));
} }
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
*/
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, long id) { public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, long id) {
return hasViewPerm(securityCtx, competitionRepository.findById(id)); return hasViewPerm(securityCtx, competitionRepository.findById(id));
} }
/** private Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm return in.call(o -> (
*/ securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) ?
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) { Uni.createFrom().nullItem()
return in.call(cm -> (cm.isPublicVisible() || cm.getRegisterMode() == RegisterMode.FREE :
|| cm.getRegisterMode() == RegisterMode.HELLOASSO o.getSystem() == CompetitionSystem.SAFCA ?
|| (cm.getRegisterMode() == RegisterMode.CLUB_ADMIN && securityCtx.isClubAdmin())) ? hasSafcaViewPerm(securityCtx, o.getId())
Uni.createFrom().nullItem() : : Uni.createFrom().nullItem().invoke(Unchecked.consumer(__ -> {
hasAdminViewPerm(securityCtx, cm).onFailure() if (!securityCtx.isInClubGroup(o.getClub().getId()))
.recoverWithUni(__ -> throw new DForbiddenException();
registerRepository.count("membre.userId = ?1 AND competition = ?2", })
securityCtx.getSubject(), cm).map(Unchecked.function(c -> { ));
if (c == 0)
throw new DForbiddenException();
return cm;
}))
));
} }
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
*/
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
return hasAdminViewPerm(securityCtx, Uni.createFrom().item(competitionModel));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
*/
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, long id) {
return hasAdminViewPerm(securityCtx, competitionRepository.findById(id));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
*/
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
return in.call(Unchecked.function(o -> {
if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin"))
return Uni.createFrom().nullItem();
if (o.getSystem() == CompetitionSystem.SAFCA)
return hasSafcaViewPerm(securityCtx, o.getId());
if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here
throw new DForbiddenException();
if (o.getSystem() == CompetitionSystem.NONE)
if (securityCtx.roleHas("club_president") || securityCtx.roleHas("club_respo_intra")
|| securityCtx.roleHas("club_secretaire") || securityCtx.roleHas("club_tresorier"))
return Uni.createFrom().nullItem();
throw new DForbiddenException();
})
);
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) { public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
return hasEditPerm(securityCtx, Uni.createFrom().item(competitionModel)); return hasEditPerm(securityCtx, Uni.createFrom().item(competitionModel));
} }
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, long id) { public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, long id) {
return hasEditPerm(securityCtx, competitionRepository.findById(id)); return hasEditPerm(securityCtx, competitionRepository.findById(id));
} }
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) { public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
return in.call(Unchecked.function(o -> { return in.call(o -> (
if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) ?
return Uni.createFrom().nullItem(); Uni.createFrom().nullItem()
:
if (o.getSystem() == CompetitionSystem.SAFCA) o.getSystem() == CompetitionSystem.SAFCA ?
return hasSafcaEditPerm(securityCtx, o.getId()); hasSafcaEditPerm(securityCtx, o.getId())
: Uni.createFrom().nullItem().invoke(Unchecked.consumer(__ -> {
if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here if (!securityCtx.isInClubGroup(o.getClub().getId()))
throw new DForbiddenException(); throw new DForbiddenException();
})
if (o.getSystem() == CompetitionSystem.NONE) ));
if (securityCtx.isClubAdmin())
return Uni.createFrom().nullItem();
throw new DForbiddenException();
})
);
} }
private Uni<?> hasSafcaViewPerm(SecurityCtx securityCtx, long id) { private Uni<?> hasSafcaViewPerm(SecurityCtx securityCtx, long id) {

View File

@ -2,7 +2,6 @@ package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CompetitionModel; import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.ServerCustom;
import fr.titionfire.ffsaf.net2.data.SimpleCompet; import fr.titionfire.ffsaf.net2.data.SimpleCompet;
@ -14,8 +13,9 @@ import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb; import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.RegisterMode; import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.utils.Utils; import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.cache.Cache; import io.quarkus.cache.Cache;
@ -42,14 +42,11 @@ public class CompetitionService {
CompetitionRepository repository; CompetitionRepository repository;
@Inject @Inject
CategoryRepository categoryRepository; PouleRepository pouleRepository;
@Inject @Inject
MatchRepository matchRepository; MatchRepository matchRepository;
@Inject
RegisterRepository registerRepository;
@Inject @Inject
KeycloakService keycloakService; KeycloakService keycloakService;
@ -72,23 +69,16 @@ public class CompetitionService {
@Inject @Inject
@CacheName("safca-have-access") @CacheName("safca-have-access")
Cache cacheAccess; Cache cacheAccess;
@Inject @Inject
@CacheName("have-access") RegisterRepository registerRepository;
Cache cacheNoneAccess;
public Uni<CompetitionData> getById(SecurityCtx securityCtx, Long id) { public Uni<CompetitionData> getById(SecurityCtx securityCtx, Long id) {
return permService.hasViewPerm(securityCtx, id).map(CompetitionData::fromModelLight);
}
public Uni<CompetitionData> getByIdAdmin(SecurityCtx securityCtx, Long id) {
if (id == 0) { if (id == 0) {
return Uni.createFrom() return Uni.createFrom()
.item(new CompetitionData(null, "", "", "", "", new Date(), new Date(), .item(new CompetitionData(null, "", "", new Date(), CompetitionSystem.SAFCA,
CompetitionSystem.NONE, RegisterMode.FREE, new Date(), new Date(), true, null, "", "", null));
null, "", "", null, true, "", "", "", ""));
} }
return permService.hasAdminViewPerm(securityCtx, id) return permService.hasViewPerm(securityCtx, id)
.chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc()) .chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc())
.map(insc -> CompetitionData.fromModel(competitionModel).addInsc(insc))) .map(insc -> CompetitionData.fromModel(competitionModel).addInsc(insc)))
.chain(data -> .chain(data ->
@ -101,53 +91,64 @@ public class CompetitionService {
} }
public Uni<List<CompetitionData>> getAll(SecurityCtx securityCtx) { public Uni<List<CompetitionData>> getAll(SecurityCtx securityCtx) {
List<CompetitionData> out = new ArrayList<>(); return repository.listAll()
return permService.getAllHaveAdminAccess(securityCtx) .chain(o ->
.call(ids -> repository.list("id IN ?1", ids) permService.getAllHaveAccess(securityCtx.getSubject())
.invoke(cm -> { .chain(map -> Uni.createFrom().item(o.stream()
out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList()); .filter(p -> {
out.forEach(competition -> competition.setCanEdit(true)); if (securityCtx.getSubject().equals(p.getOwner()))
})) return true;
.call(ids -> if (p.getSystem() == CompetitionSystem.SAFCA) {
repository.list("id NOT IN ?1 AND (publicVisible = TRUE OR registerMode IN ?2)", ids, if (map.containsKey(p.getId()))
securityCtx.isClubAdmin() ? List.of(RegisterMode.FREE, RegisterMode.HELLOASSO, return map.get(p.getId()).equals("admin");
RegisterMode.CLUB_ADMIN) : List.of(RegisterMode.FREE, RegisterMode.HELLOASSO)) return securityCtx.roleHas("federation_admin")
.invoke(cm -> out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList())) || securityCtx.roleHas("safca_super_admin");
.call(cm -> registerRepository.list(
"membre.userId = ?1 AND competition.id NOT IN ?2 AND competition NOT IN ?3",
securityCtx.getSubject(), ids, cm)
.chain(registerModels -> {
Uni<Void> uni = Uni.createFrom().nullItem();
for (RegisterModel registerModel : registerModels) {
uni = uni.call(__ -> Mutiny.fetch(registerModel.getCompetition())
.invoke(cm2 -> out.add(CompetitionData.fromModelLight(cm2))));
} }
return uni; return securityCtx.roleHas("federation_admin");
}) })
)) .map(CompetitionData::fromModel).toList())
.map(__ -> out); ));
} }
public Uni<List<CompetitionData>> getAllAdmin(SecurityCtx securityCtx) { public Uni<List<CompetitionData>> getAllSystem(SecurityCtx securityCtx,
return permService.getAllHaveAdminAccess(securityCtx) CompetitionSystem system) {
.chain(ids -> repository.list("id IN ?1", ids)) if (system == CompetitionSystem.SAFCA) {
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList()); return permService.getAllHaveAccess(securityCtx.getSubject())
} .chain(map ->
repository.list("system = ?1", system)
.map(data -> data.stream()
.filter(p -> {
if (securityCtx.getSubject().equals(p.getOwner()))
return true;
if (map.containsKey(p.getId()))
return map.get(p.getId()).equals("admin");
return securityCtx.roleHas("federation_admin")
|| securityCtx.roleHas("safca_super_admin");
})
.map(CompetitionData::fromModel).toList())
);
}
public Uni<List<CompetitionData>> getAllSystemAdmin(SecurityCtx securityCtx, return repository.list("system = ?1", system)
CompetitionSystem system) { .map(data -> data.stream()
return permService.getAllHaveAdminAccess(securityCtx) .filter(p -> {
.chain(ids -> repository.list("system = ?1 AND id IN ?2", system, ids)) if (securityCtx.getSubject().equals(p.getOwner()))
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList()); return true;
return securityCtx.roleHas("federation_admin") ||
securityCtx.isInClubGroup(p.getClub().getId());
})
.map(CompetitionData::fromModel).toList());
} }
public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) { public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) {
if (data.getId() == null) { if (data.getId() == null) {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
.invoke(Unchecked.consumer(combModel -> { .invoke(Unchecked.consumer(combModel -> {
if (!securityCtx.getRoles().contains("create_compet") && !securityCtx.getRoles() if (combModel == null)
.contains("federation_admin")) throw new DNotFoundException("Profile non trouvé");
throw new DForbiddenException("Vous ne pouvez pas créer de compétition"); if (data.getSystem() == CompetitionSystem.SAFCA)
if (!securityCtx.getRoles().contains("safca_create_compet"))
throw new DForbiddenException("Vous ne pouvez pas créer de compétition SAFCA");
})) }))
.map(MembreModel::getClub) .map(MembreModel::getClub)
.chain(clubModel -> { .chain(clubModel -> {
@ -156,24 +157,22 @@ public class CompetitionService {
model.setId(null); model.setId(null);
model.setSystem(data.getSystem()); model.setSystem(data.getSystem());
model.setClub(clubModel); model.setClub(clubModel);
model.setDate(data.getDate());
model.setInsc(new ArrayList<>()); model.setInsc(new ArrayList<>());
model.setUuid(UUID.randomUUID().toString()); model.setUuid(UUID.randomUUID().toString());
model.setName(data.getName());
model.setOwner(securityCtx.getSubject()); model.setOwner(securityCtx.getSubject());
copyData(data, model);
return Panache.withTransaction(() -> repository.persist(model)); return Panache.withTransaction(() -> repository.persist(model));
}).map(CompetitionData::fromModel) }).map(CompetitionData::fromModel)
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate( .call(__ -> cacheAccess.invalidate(securityCtx.getSubject()));
securityCtx.getSubject()) : Uni.createFrom().nullItem())
.call(c -> (c.getSystem() == CompetitionSystem.NONE) ? cacheNoneAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem());
} else { } else {
return permService.hasEditPerm(securityCtx, data.getId()) return permService.hasEditPerm(securityCtx, data.getId())
.chain(model -> { .chain(model -> {
copyData(data, model); model.setDate(data.getDate());
model.setName(data.getName());
return vertx.getOrCreateContext().executeBlocking(() -> // Update owner return vertx.getOrCreateContext().executeBlocking(() ->
keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null)) keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null))
.invoke(Unchecked.consumer(newOwner -> { .invoke(Unchecked.consumer(newOwner -> {
if (newOwner == null) if (newOwner == null)
@ -188,29 +187,10 @@ public class CompetitionService {
})) }))
.chain(__ -> Panache.withTransaction(() -> repository.persist(model))); .chain(__ -> Panache.withTransaction(() -> repository.persist(model)));
}).map(CompetitionData::fromModel) }).map(CompetitionData::fromModel)
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate( .call(__ -> cacheAccess.invalidate(securityCtx.getSubject()));
securityCtx.getSubject()) : Uni.createFrom().nullItem())
.call(c -> (c.getSystem() == CompetitionSystem.NONE) ? cacheNoneAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem());
} }
} }
private void copyData(CompetitionData data, CompetitionModel model) {
model.setName(data.getName());
model.setAdresse(data.getAdresse());
model.setDescription(data.getDescription());
model.setDate(data.getDate());
model.setTodate(data.getDate());
model.setPublicVisible(data.isPublicVisible());
model.setStartRegister(data.getStartRegister());
model.setEndRegister(data.getEndRegister());
model.setRegisterMode(data.getRegisterMode());
model.setData1(data.getData1());
model.setData2(data.getData2());
model.setData3(data.getData3());
model.setData4(data.getData4());
}
public Uni<List<SimpleRegisterComb>> getRegister(SecurityCtx securityCtx, Long id) { public Uni<List<SimpleRegisterComb>> getRegister(SecurityCtx securityCtx, Long id) {
return permService.hasEditPerm(securityCtx, id) return permService.hasEditPerm(securityCtx, id)
.chain(c -> Mutiny.fetch(c.getInsc())) .chain(c -> Mutiny.fetch(c.getInsc()))
@ -242,7 +222,7 @@ public class CompetitionService {
r.setClub(combModel.getClub()); r.setClub(combModel.getClub());
} }
} else { } else {
r = new RegisterModel(c, combModel, data.getWeight(), data.getOverCategory(), r = new RegisterModel(c ,combModel, data.getWeight(), data.getOverCategory(),
(combModel.getBirth_date() == null) ? combModel.getCategorie() : (combModel.getBirth_date() == null) ? combModel.getCategorie() :
Utils.getCategoryFormBirthDate(combModel.getBirth_date(), Utils.getCategoryFormBirthDate(combModel.getBirth_date(),
c.getDate()), c.getDate()),
@ -272,8 +252,7 @@ public class CompetitionService {
} else { } else {
if (fname == null || lname == null) if (fname == null || lname == null)
return Uni.createFrom().failure(new DBadRequestException("Nom et prénom requis")); return Uni.createFrom().failure(new DBadRequestException("Nom et prénom requis"));
return combRepository.find("unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2)", return combRepository.find("unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2)", lname,
lname,
fname).firstResult() fname).firstResult()
.invoke(Unchecked.consumer(combModel -> { .invoke(Unchecked.consumer(combModel -> {
if (combModel == null) if (combModel == null)
@ -286,11 +265,11 @@ public class CompetitionService {
return permService.hasEditPerm(securityCtx, id) return permService.hasEditPerm(securityCtx, id)
.chain(c -> registerRepository.delete("competition = ?1 AND membre.id = ?2", c, combId) .chain(c -> registerRepository.delete("competition = ?1 AND membre.id = ?2", c, combId)
.invoke(Unchecked.consumer(l -> { .invoke(Unchecked.consumer(l -> {
if (l != 0) { if (l != 0){
if (c.getSystem() == CompetitionSystem.SAFCA) { if (c.getSystem() == CompetitionSystem.SAFCA) {
SReqRegister.sendRmIfNeed(serverCustom.clients, combId, id); SReqRegister.sendRmIfNeed(serverCustom.clients, combId, id);
} }
} else { }else{
throw new DBadRequestException("Combattant non inscrit"); throw new DBadRequestException("Combattant non inscrit");
} }
})) }))
@ -302,7 +281,7 @@ public class CompetitionService {
if (!(securityCtx.getSubject().equals(c.getOwner()) || securityCtx.roleHas("federation_admin"))) if (!(securityCtx.getSubject().equals(c.getOwner()) || securityCtx.roleHas("federation_admin")))
throw new DForbiddenException(); throw new DForbiddenException();
})) }))
.call(competitionModel -> categoryRepository.list("compet = ?1", competitionModel) .call(competitionModel -> pouleRepository.list("compet = ?1", competitionModel)
.call(pouleModels -> pouleModels.isEmpty() ? Uni.createFrom().nullItem() : .call(pouleModels -> pouleModels.isEmpty() ? Uni.createFrom().nullItem() :
Uni.join().all(pouleModels.stream() Uni.join().all(pouleModels.stream()
.map(pouleModel -> Panache.withTransaction( .map(pouleModel -> Panache.withTransaction(
@ -310,7 +289,7 @@ public class CompetitionService {
.toList()) .toList())
.andCollectFailures())) .andCollectFailures()))
.call(competitionModel -> Panache.withTransaction( .call(competitionModel -> Panache.withTransaction(
() -> categoryRepository.delete("compet = ?1", competitionModel))) () -> pouleRepository.delete("compet = ?1", competitionModel)))
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId()))) .chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())))
.invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id)) .invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id))
.call(__ -> cache.invalidate(id)); .call(__ -> cache.invalidate(id));

View File

@ -253,7 +253,6 @@ public class KeycloakService {
return null; return null;
}) : Uni.createFrom().nullItem()) }) : Uni.createFrom().nullItem())
.invoke(user -> membreModel.setUserId(user.getId())) .invoke(user -> membreModel.setUserId(user.getId()))
.call(user -> updateRole(user.getId(), List.of("safca_user"), List.of()))
.call(user -> enabled_email ? reactiveMailer.send( .call(user -> enabled_email ? reactiveMailer.send(
Mail.withText(user.getEmail(), Mail.withText(user.getEmail(),
"FFSAF - Creation de votre compte sur l'intranet", "FFSAF - Creation de votre compte sur l'intranet",

View File

@ -3,7 +3,7 @@ package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.MatchModel; import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.CombRepository; import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.data.repository.MatchRepository; import fr.titionfire.ffsaf.data.repository.MatchRepository;
import fr.titionfire.ffsaf.data.repository.CategoryRepository; import fr.titionfire.ffsaf.data.repository.PouleRepository;
import fr.titionfire.ffsaf.rest.data.MatchData; import fr.titionfire.ffsaf.rest.data.MatchData;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
@ -25,7 +25,7 @@ public class MatchService {
MatchRepository repository; MatchRepository repository;
@Inject @Inject
CategoryRepository categoryRepository; PouleRepository pouleRepository;
@Inject @Inject
CombRepository combRepository; CombRepository combRepository;
@ -33,17 +33,17 @@ public class MatchService {
@Inject @Inject
CompetPermService permService; CompetPermService permService;
public Uni<MatchData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) { public Uni<MatchData> getById(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult() return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Match not found")) .onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCategory().getCompet())) .call(data -> permService.hasViewPerm(securityCtx, data.getPoule().getCompet()))
.map(MatchData::fromModel); .map(MatchData::fromModel);
} }
public Uni<List<MatchData>> getAllByPouleAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) { public Uni<List<MatchData>> getAllByPoule(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return categoryRepository.find("systemId = ?1 AND system = ?2", id, system).firstResult() return pouleRepository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found")) .onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet())) .call(data -> permService.hasViewPerm(securityCtx, data.getCompet()))
.chain(data -> repository.list("poule = ?1", data.getId()) .chain(data -> repository.list("poule = ?1", data.getId())
.map(o -> o.stream().map(MatchData::fromModel).toList())); .map(o -> o.stream().map(MatchData::fromModel).toList()));
} }
@ -52,21 +52,21 @@ public class MatchService {
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult() return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult()
.chain(o -> { .chain(o -> {
if (o == null) { if (o == null) {
return categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system) return pouleRepository.find("systemId = ?1 AND system = ?2", data.getPoule(), system)
.firstResult() .firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found")) .onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet())) .call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
.map(categoryModel -> { .map(pouleModel -> {
MatchModel model = new MatchModel(); MatchModel model = new MatchModel();
model.setId(null); model.setId(null);
model.setSystem(system); model.setSystem(system);
model.setSystemId(data.getId()); model.setSystemId(data.getId());
model.setCategory(categoryModel); model.setPoule(pouleModel);
return model; return model;
}); });
} else { } else {
return categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system) return pouleRepository.find("systemId = ?1 AND system = ?2", data.getPoule(), system)
.firstResult() .firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found")) .onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet())) .call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
@ -77,7 +77,7 @@ public class MatchService {
.chain(o -> { .chain(o -> {
o.setC1_str(data.getC1_str()); o.setC1_str(data.getC1_str());
o.setC2_str(data.getC2_str()); o.setC2_str(data.getC2_str());
o.setCategory_ord(data.getCategory_ord()); o.setPoule_ord(data.getPoule_ord());
o.getScores().clear(); o.getScores().clear();
o.getScores().addAll(data.getScores()); o.getScores().addAll(data.getScores());
@ -97,7 +97,7 @@ public class MatchService {
List<ScoreEmbeddable> scores) { List<ScoreEmbeddable> scores) {
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult() return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Match not found")) .onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().getCompet())) .call(o2 -> permService.hasEditPerm(securityCtx, o2.getPoule().getCompet()))
.invoke(data -> { .invoke(data -> {
data.getScores().clear(); data.getScores().clear();
data.getScores().addAll(scores); data.getScores().addAll(scores);
@ -109,7 +109,7 @@ public class MatchService {
public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) { public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult() return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Match not found")) .onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().getCompet())) .call(o2 -> permService.hasEditPerm(securityCtx, o2.getPoule().getCompet()))
.chain(data -> Panache.withTransaction(() -> repository.delete(data))); .chain(data -> Panache.withTransaction(() -> repository.delete(data)));
} }
} }

View File

@ -2,11 +2,11 @@ package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.MatchModel; import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.CategoryModel; import fr.titionfire.ffsaf.data.model.PouleModel;
import fr.titionfire.ffsaf.data.model.TreeModel; import fr.titionfire.ffsaf.data.model.TreeModel;
import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.rest.data.CategoryData; import fr.titionfire.ffsaf.rest.data.PouleData;
import fr.titionfire.ffsaf.rest.data.CategoryFullData; import fr.titionfire.ffsaf.rest.data.PouleFullData;
import fr.titionfire.ffsaf.rest.data.TreeData; import fr.titionfire.ffsaf.rest.data.TreeData;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
@ -25,10 +25,10 @@ import java.util.stream.Stream;
@WithSession @WithSession
@ApplicationScoped @ApplicationScoped
public class CategoryService { public class PouleService {
@Inject @Inject
CategoryRepository repository; PouleRepository repository;
@Inject @Inject
CompetitionRepository competRepository; CompetitionRepository competRepository;
@ -45,21 +45,35 @@ public class CategoryService {
@Inject @Inject
CompetPermService permService; CompetPermService permService;
public Uni<CategoryData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) { public Uni<PouleData> getById(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return repository.find("systemId = ?1 AND system = ?2", id, system) return repository.find("systemId = ?1 AND system = ?2", id, system)
.firstResult() .firstResult()
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found")) .onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet())) .call(data -> permService.hasViewPerm(securityCtx, data.getCompet()))
.map(CategoryData::fromModel); .map(PouleData::fromModel);
} }
public Uni<List<CategoryData>> getAllAdmin(SecurityCtx securityCtx, CompetitionSystem system) { public Uni<List<PouleData>> getAll(SecurityCtx securityCtx, CompetitionSystem system) {
return permService.getAllHaveAdminAccess(securityCtx) return repository.list("system = ?1", system)
.chain(ids -> repository.list("system = ?1 AND compet.id IN ?2", system, ids)) .chain(o ->
.map(pouleModels -> pouleModels.stream().map(CategoryData::fromModel).toList()); permService.getAllHaveAccess(securityCtx.getSubject())
.chain(map -> Uni.createFrom().item(o.stream()
.filter(p -> {
if (securityCtx.getSubject().equals(p.getCompet().getOwner()))
return true;
if (p.getSystem() == CompetitionSystem.SAFCA) {
if (map.containsKey(p.getCompet().getId()))
return map.get(p.getId()).equals("admin");
return securityCtx.roleHas("federation_admin")
|| securityCtx.roleHas("safca_super_admin");
}
return securityCtx.roleHas("federation_admin");
})
.map(PouleData::fromModel).toList())
));
} }
public Uni<CategoryData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, CategoryData data) { public Uni<PouleData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, PouleData data) {
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult() return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult()
.chain(o -> { .chain(o -> {
if (o == null) { if (o == null) {
@ -67,7 +81,7 @@ public class CategoryService {
.onItem().ifNull().failWith(() -> new RuntimeException("Competition not found")) .onItem().ifNull().failWith(() -> new RuntimeException("Competition not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2)) .call(o2 -> permService.hasEditPerm(securityCtx, o2))
.chain(competitionModel -> { .chain(competitionModel -> {
CategoryModel model = new CategoryModel(); PouleModel model = new PouleModel();
model.setId(null); model.setId(null);
model.setSystem(system); model.setSystem(system);
@ -85,7 +99,7 @@ public class CategoryService {
o.setType(data.getType()); o.setType(data.getType());
return Panache.withTransaction(() -> repository.persist(o)); return Panache.withTransaction(() -> repository.persist(o));
} }
}).map(CategoryData::fromModel); }).map(PouleData::fromModel);
} }
private MatchModel findMatch(List<MatchModel> matchModelList, Long id) { private MatchModel findMatch(List<MatchModel> matchModelList, Long id) {
@ -114,7 +128,7 @@ public class CategoryService {
} }
} }
private Uni<TreeModel> persisteTree(TreeData data, List<TreeModel> node, CategoryModel poule, private Uni<TreeModel> persisteTree(TreeData data, List<TreeModel> node, PouleModel poule,
List<MatchModel> matchModelList) { List<MatchModel> matchModelList) {
TreeModel mm = findNode(node, data.getMatch()); TreeModel mm = findNode(node, data.getMatch());
if (mm == null) { if (mm == null) {
@ -122,7 +136,7 @@ public class CategoryService {
mm.setId(null); mm.setId(null);
} }
mm.setLevel(data.getLevel()); mm.setLevel(data.getLevel());
mm.setCategory(poule.getId()); mm.setPoule(poule.getId());
mm.setMatch(findMatch(matchModelList, data.getMatch())); mm.setMatch(findMatch(matchModelList, data.getMatch()));
return Uni.createFrom().item(mm) return Uni.createFrom().item(mm)
@ -133,7 +147,7 @@ public class CategoryService {
.chain(o -> Panache.withTransaction(() -> treeRepository.persist(o))); .chain(o -> Panache.withTransaction(() -> treeRepository.persist(o)));
} }
public Uni<?> syncCategory(SecurityCtx securityCtx, CompetitionSystem system, CategoryFullData data) { public Uni<?> syncPoule(SecurityCtx securityCtx, CompetitionSystem system, PouleFullData data) {
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system) return repository.find("systemId = ?1 AND system = ?2", data.getId(), system)
.firstResult() .firstResult()
.onItem().ifNotNull().call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet())) .onItem().ifNotNull().call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
@ -142,7 +156,7 @@ public class CategoryService {
.onItem().ifNull().failWith(() -> new RuntimeException("Compet not found")) .onItem().ifNull().failWith(() -> new RuntimeException("Compet not found"))
.call(o -> permService.hasEditPerm(securityCtx, o)) .call(o -> permService.hasEditPerm(securityCtx, o))
.map(o -> { .map(o -> {
CategoryModel model = new CategoryModel(); PouleModel model = new PouleModel();
model.setId(null); model.setId(null);
model.setSystem(system); model.setSystem(system);
model.setSystemId(data.getId()); model.setSystemId(data.getId());
@ -158,10 +172,10 @@ public class CategoryService {
o.setType(data.getType()); o.setType(data.getType());
WorkData workData = new WorkData(); WorkData workData = new WorkData();
workData.category = o; workData.poule = o;
return workData; return workData;
}) })
.call(o -> Panache.withTransaction(() -> repository.persist(o.category))) .call(o -> Panache.withTransaction(() -> repository.persist(o.poule)))
.call(o -> (data.getMatches() == null || data.getMatches().isEmpty()) ? Uni.createFrom().nullItem() : .call(o -> (data.getMatches() == null || data.getMatches().isEmpty()) ? Uni.createFrom().nullItem() :
Uni.createFrom() Uni.createFrom()
.item(data.getMatches().stream().flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id()) .item(data.getMatches().stream().flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id())
@ -173,7 +187,7 @@ public class CategoryService {
) )
.invoke(in -> { .invoke(in -> {
ArrayList<TreeModel> node = new ArrayList<>(); ArrayList<TreeModel> node = new ArrayList<>();
for (TreeModel treeModel : in.category.getTree()) for (TreeModel treeModel : in.poule.getTree())
flatTreeChild(treeModel, node); flatTreeChild(treeModel, node);
ArrayList<TreeData> new_node = new ArrayList<>(); ArrayList<TreeData> new_node = new ArrayList<>();
@ -190,7 +204,7 @@ public class CategoryService {
n.setLeft(null); n.setLeft(null);
}); });
in.toRmMatch = in.category.getMatchs().stream() in.toRmMatch = in.poule.getMatchs().stream()
.filter(m -> data.getMatches().stream().noneMatch(m2 -> m2.getId().equals(m.getSystemId()))) .filter(m -> data.getMatches().stream().noneMatch(m2 -> m2.getId().equals(m.getSystemId())))
.map(MatchModel::getId).toList(); .map(MatchModel::getId).toList();
}) })
@ -205,21 +219,21 @@ public class CategoryService {
.call(in -> data.getMatches().isEmpty() ? Uni.createFrom().nullItem() : .call(in -> data.getMatches().isEmpty() ? Uni.createFrom().nullItem() :
Uni.join().all( Uni.join().all(
data.getMatches().stream().map(m -> { data.getMatches().stream().map(m -> {
MatchModel mm = findMatch(in.category.getMatchs(), m.getId()); MatchModel mm = findMatch(in.poule.getMatchs(), m.getId());
if (mm == null) { if (mm == null) {
mm = new MatchModel(); mm = new MatchModel();
mm.setId(null); mm.setId(null);
mm.setSystem(system); mm.setSystem(system);
mm.setSystemId(m.getId()); mm.setSystemId(m.getId());
} }
mm.setCategory(in.category); mm.setPoule(in.poule);
mm.setCategory_ord(m.getCategory_ord()); mm.setPoule_ord(m.getPoule_ord());
mm.setC1_str(m.getC1_str()); mm.setC1_str(m.getC1_str());
mm.setC2_str(m.getC2_str()); mm.setC2_str(m.getC2_str());
mm.setC1_id(in.membres.getOrDefault(m.getC1_id(), null)); mm.setC1_id(in.membres.getOrDefault(m.getC1_id(), null));
mm.setC2_id(in.membres.getOrDefault(m.getC2_id(), null)); mm.setC2_id(in.membres.getOrDefault(m.getC2_id(), null));
mm.setEnd(m.isEnd()); mm.setEnd(m.isEnd());
mm.setPoule(m.getPoule()); mm.setGroupe(m.getGroupe());
mm.getScores().clear(); mm.getScores().clear();
mm.getScores().addAll(m.getScores()); mm.getScores().addAll(m.getScores());
@ -230,13 +244,13 @@ public class CategoryService {
.andCollectFailures()) .andCollectFailures())
.call(in -> data.getTrees().isEmpty() ? Uni.createFrom().nullItem() : .call(in -> data.getTrees().isEmpty() ? Uni.createFrom().nullItem() :
Uni.join().all(data.getTrees().stream() Uni.join().all(data.getTrees().stream()
.map(m -> persisteTree(m, in.category.getTree(), in.category, in.match)).toList()) .map(m -> persisteTree(m, in.poule.getTree(), in.poule, in.match)).toList())
.andCollectFailures()) .andCollectFailures())
.map(__ -> "OK"); .map(__ -> "OK");
} }
private static class WorkData { private static class WorkData {
CategoryModel category; PouleModel poule;
HashMap<Long, MembreModel> membres = new HashMap<>(); HashMap<Long, MembreModel> membres = new HashMap<>();
List<MatchModel> match = new ArrayList<>(); List<MatchModel> match = new ArrayList<>();
List<Long> toRmMatch; List<Long> toRmMatch;

View File

@ -0,0 +1,4 @@
package fr.titionfire.ffsaf.domain.service;
public class TreeService {
}

View File

@ -24,7 +24,7 @@ public class SReqCompet {
} }
public static void getAllHaveAccess(ArrayList<Client_Thread> client_Thread, String userId, public static void getAllHaveAccess(ArrayList<Client_Thread> client_Thread, String userId,
CompletableFuture<HashMap<String, String>> future) { CompletableFuture<HashMap<Long, String>> future) {
if (client_Thread.isEmpty()) return; if (client_Thread.isEmpty()) return;
client_Thread.get(0).sendReq(userId, "getAllHaveAccess", client_Thread.get(0).sendReq(userId, "getAllHaveAccess",
new JsonConsumer<>(HashMap.class, future::complete)); new JsonConsumer<>(HashMap.class, future::complete));

View File

@ -1,50 +0,0 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.CompetitionService;
import fr.titionfire.ffsaf.rest.data.CompetitionData;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.quarkus.security.Authenticated;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
@Path("api/competition/admin")
public class CompetitionAdminEndpoints {
@Inject
CompetitionService service;
@Inject
SecurityCtx securityCtx;
@GET
@Path("{id}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<CompetitionData> getByIdAdmin(@PathParam("id") Long id) {
return service.getByIdAdmin(securityCtx, id);
}
@GET
@Path("all")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllAdmin() {
return service.getAllAdmin(securityCtx);
}
@GET
@Path("all/{system}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) {
return service.getAllSystemAdmin(securityCtx, system);
}
}

View File

@ -5,6 +5,7 @@ import fr.titionfire.ffsaf.rest.data.CompetitionData;
import fr.titionfire.ffsaf.rest.data.RegisterRequestData; import fr.titionfire.ffsaf.rest.data.RegisterRequestData;
import fr.titionfire.ffsaf.rest.data.SimpleCompetData; import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb; import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.quarkus.security.Authenticated; import io.quarkus.security.Authenticated;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
@ -28,11 +29,8 @@ public class CompetitionEndpoints {
@Path("{id}") @Path("{id}")
@Authenticated @Authenticated
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Uni<CompetitionData> getById(@PathParam("id") Long id, @QueryParam("light") boolean light) { public Uni<CompetitionData> getById(@PathParam("id") Long id) {
if (light) return service.getById(securityCtx, id);
return service.getById(securityCtx, id);
else
return service.getByIdAdmin(securityCtx, id);
} }
@GET @GET
@ -78,6 +76,14 @@ public class CompetitionEndpoints {
return service.getAll(securityCtx); return service.getAll(securityCtx);
} }
@GET
@Path("all/{system}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllSystem(@PathParam("system") CompetitionSystem system) {
return service.getAllSystem(securityCtx, system);
}
@POST @POST
@Authenticated @Authenticated
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)

View File

@ -115,8 +115,8 @@ public class CompteEndpoints {
else toRemove.add("safca_super_admin"); else toRemove.add("safca_super_admin");
if (form.isSafca_user()) toAdd.add("safca_user"); if (form.isSafca_user()) toAdd.add("safca_user");
else toRemove.add("safca_user"); else toRemove.add("safca_user");
if (form.isCreate_compet()) toAdd.add("create_compet"); if (form.isSafca_create_compet()) toAdd.add("safca_create_compet");
else toRemove.add("create_compet"); else toRemove.add("safca_create_compet");
return service.updateRole(id, toAdd, toRemove); return service.updateRole(id, toAdd, toRemove);
} }

View File

@ -14,8 +14,8 @@ import jakarta.ws.rs.core.MediaType;
import java.util.List; import java.util.List;
@Authenticated @Authenticated
@Path("api/match/{system}/admin") @Path("api/match/{system}/")
public class MatchAdminEndpoints { public class MatchEndpoints {
@PathParam("system") @PathParam("system")
private CompetitionSystem system; private CompetitionSystem system;
@ -30,15 +30,15 @@ public class MatchAdminEndpoints {
@GET @GET
@Path("{id}") @Path("{id}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Uni<MatchData> getByIdAdmin(@PathParam("id") Long id) { public Uni<MatchData> getById(@PathParam("id") Long id) {
return service.getByIdAdmin(securityCtx, system, id); return service.getById(securityCtx, system, id);
} }
@GET @GET
@Path("getAllByPoule/{id}") @Path("getAllByPoule/{id}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Uni<List<MatchData>> getAllByPouleAdmin(@PathParam("id") Long id) { public Uni<List<MatchData>> getAllByPoule(@PathParam("id") Long id) {
return service.getAllByPouleAdmin(securityCtx, system, id); return service.getAllByPoule(securityCtx, system, id);
} }
@POST @POST

View File

@ -1,8 +1,8 @@
package fr.titionfire.ffsaf.rest; package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.CategoryService; import fr.titionfire.ffsaf.domain.service.PouleService;
import fr.titionfire.ffsaf.rest.data.CategoryData; import fr.titionfire.ffsaf.rest.data.PouleData;
import fr.titionfire.ffsaf.rest.data.CategoryFullData; import fr.titionfire.ffsaf.rest.data.PouleFullData;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.quarkus.security.Authenticated; import io.quarkus.security.Authenticated;
@ -14,14 +14,14 @@ import jakarta.ws.rs.core.MediaType;
import java.util.List; import java.util.List;
@Authenticated @Authenticated
@Path("api/poule/{system}/admin/") @Path("api/poule/{system}/")
public class CategoryAdminEndpoints { public class PouleEndpoints {
@PathParam("system") @PathParam("system")
private CompetitionSystem system; private CompetitionSystem system;
@Inject @Inject
CategoryService service; PouleService service;
@Inject @Inject
SecurityCtx securityCtx; SecurityCtx securityCtx;
@ -30,28 +30,28 @@ public class CategoryAdminEndpoints {
@GET @GET
@Path("{id}") @Path("{id}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Uni<CategoryData> getByIdAdmin(@PathParam("id") Long id) { public Uni<PouleData> getById(@PathParam("id") Long id) {
return service.getByIdAdmin(securityCtx, system, id); return service.getById(securityCtx, system, id);
} }
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Uni<List<CategoryData>> getAllAdmin() { public Uni<List<PouleData>> getAll() {
return service.getAllAdmin(securityCtx, system); return service.getAll(securityCtx, system);
} }
@POST @POST
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Uni<CategoryData> addOrUpdate(CategoryData data) { public Uni<PouleData> addOrUpdate(PouleData data) {
return service.addOrUpdate(securityCtx, system, data); return service.addOrUpdate(securityCtx, system, data);
} }
@POST @POST
@Path("sync") @Path("sync")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Uni<?> syncCategory(CategoryFullData data) { public Uni<?> syncPoule(PouleFullData data) {
return service.syncCategory(securityCtx, system, data); return service.syncPoule(securityCtx, system, data);
} }
@DELETE @DELETE

View File

@ -1,10 +1,9 @@
package fr.titionfire.ffsaf.rest.data; package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.CompetitionModel; import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.utils.Categorie; import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.RegisterMode; import fr.titionfire.ffsaf.data.model.RegisterModel;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -18,53 +17,20 @@ import java.util.List;
public class CompetitionData { public class CompetitionData {
private Long id; private Long id;
private String name; private String name;
private String description;
private String adresse;
private String uuid; private String uuid;
private Date date; private Date date;
private Date toDate;
private CompetitionSystem system; private CompetitionSystem system;
private RegisterMode registerMode;
private Date startRegister;
private Date endRegister;
private boolean publicVisible;
private Long club; private Long club;
private String clubName; private String clubName;
private String owner; private String owner;
private List<SimpleRegister> registers; private List<SimpleRegister> registers;
private boolean canEdit;
private String data1;
private String data2;
private String data3;
private String data4;
public static CompetitionData fromModel(CompetitionModel model) { public static CompetitionData fromModel(CompetitionModel model) {
if (model == null) if (model == null)
return null; return null;
return new CompetitionData(model.getId(), model.getName(), model.getDescription(), model.getAdresse(), return new CompetitionData(model.getId(), model.getName(), model.getUuid(), model.getDate(), model.getSystem(),
model.getUuid(), model.getDate(), model.getTodate(), model.getSystem(), model.getClub().getId(), model.getClub().getName(), model.getOwner(), null);
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
model.getClub().getId(), model.getClub().getName(), model.getOwner(), null, false,
model.getData1(), model.getData2(), model.getData3(), model.getData4());
}
public static CompetitionData fromModelLight(CompetitionModel model) {
if (model == null)
return null;
CompetitionData out = new CompetitionData(model.getId(), model.getName(), model.getDescription(),
model.getAdresse(), "", model.getDate(), model.getTodate(), null,
model.getRegisterMode(), model.getStartRegister(), model.getEndRegister(), model.isPublicVisible(),
null, model.getClub().getName(), "", null, false,
"","", "","");
if (model.getRegisterMode() == RegisterMode.HELLOASSO){
out.setData1(model.getData1());
out.setData2(model.getData2());
}
return out;
} }
public CompetitionData addInsc(List<RegisterModel> insc) { public CompetitionData addInsc(List<RegisterModel> insc) {

View File

@ -17,10 +17,10 @@ public class MatchData {
private String c1_str; private String c1_str;
private Long c2_id; private Long c2_id;
private String c2_str; private String c2_str;
private Long category; private Long poule;
private long category_ord; private long poule_ord;
private boolean isEnd = true; private boolean isEnd = true;
private char poule; private char groupe;
private List<ScoreEmbeddable> scores; private List<ScoreEmbeddable> scores;
public static MatchData fromModel(MatchModel model) { public static MatchData fromModel(MatchModel model) {
@ -30,7 +30,7 @@ public class MatchData {
return new MatchData(model.getSystemId(), return new MatchData(model.getSystemId(),
(model.getC1_id() == null) ? null : model.getC1_id().getId(), model.getC1_str(), (model.getC1_id() == null) ? null : model.getC1_id().getId(), model.getC1_str(),
(model.getC2_id() == null) ? null : model.getC2_id().getId(), model.getC2_str(), (model.getC2_id() == null) ? null : model.getC2_id().getId(), model.getC2_str(),
model.getCategory().getId(), model.getCategory_ord(), model.isEnd(), model.getPoule(), model.getPoule().getId(), model.getPoule_ord(), model.isEnd(), model.getGroupe(),
model.getScores()); model.getScores());
} }
} }

View File

@ -1,6 +1,6 @@
package fr.titionfire.ffsaf.rest.data; package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.CategoryModel; import fr.titionfire.ffsaf.data.model.PouleModel;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -8,16 +8,16 @@ import lombok.Data;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@RegisterForReflection @RegisterForReflection
public class CategoryData { public class PouleData {
private Long id; private Long id;
private String name; private String name;
private Long compet; private Long compet;
private Integer type; private Integer type;
public static CategoryData fromModel(CategoryModel model) { public static PouleData fromModel(PouleModel model) {
if (model == null) if (model == null)
return null; return null;
return new CategoryData(model.getSystemId(), model.getName(), model.getCompet().getId(), model.getType()); return new PouleData(model.getSystemId(), model.getName(), model.getCompet().getId(), model.getType());
} }
} }

View File

@ -7,7 +7,7 @@ import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
public class CategoryFullData { public class PouleFullData {
private Long id; private Long id;
private String name; private String name;
private Long compet; private Long compet;

View File

@ -10,7 +10,7 @@ import lombok.Data;
@RegisterForReflection @RegisterForReflection
public class TreeData { public class TreeData {
private Long id; private Long id;
private Long category; private Long poule;
private Integer level; private Integer level;
private Long match; private Long match;
private TreeData left; private TreeData left;
@ -20,7 +20,7 @@ public class TreeData {
if (model == null) if (model == null)
return null; return null;
return new TreeData(model.getId(), model.getCategory(), model.getLevel(), model.getMatch().getId(), return new TreeData(model.getId(), model.getPoule(), model.getLevel(), model.getMatch().getId(),
fromModel(model.getLeft()), fromModel(model.getRight())); fromModel(model.getLeft()), fromModel(model.getRight()));
} }
} }

View File

@ -16,9 +16,9 @@ public class MemberPermForm {
@FormParam("safca_user") @FormParam("safca_user")
private boolean safca_user; private boolean safca_user;
@Schema(description = "Indique si le membre peut créer des compétitions.", example = "false", required = true) @Schema(description = "Indique si le membre peut créer des compétitions sur SAFCA.", example = "false", required = true)
@FormParam("create_compet") @FormParam("safca_create_compet")
private boolean create_compet; private boolean safca_create_compet;
@Schema(description = "Indique si le membre est un super administrateur SAFCA.", example = "false", required = true) @Schema(description = "Indique si le membre est un super administrateur SAFCA.", example = "false", required = true)
@FormParam("safca_super_admin") @FormParam("safca_super_admin")

View File

@ -1,5 +1,5 @@
package fr.titionfire.ffsaf.utils; package fr.titionfire.ffsaf.utils;
public enum CompetitionSystem { public enum CompetitionSystem {
SAFCA, NONE SAFCA,
} }

View File

@ -1,12 +0,0 @@
package fr.titionfire.ffsaf.utils;
public enum RegisterMode {
FREE, CLUB_ADMIN, ADMIN, HELLOASSO
}
/*
HELLOASSO:
-> data1 = organizationSlug
-> data2 = formSlug
-> data3 = tarifs
-> data4 = errorEmail
*/

View File

@ -31,11 +31,6 @@ public class SecurityCtx {
return securityIdentity.getRoles().contains(role); return securityIdentity.getRoles().contains(role);
} }
public boolean isClubAdmin() {
return this.roleHas("club_president") || this.roleHas("club_respo_intra")
|| this.roleHas("club_secretaire") || this.roleHas("club_tresorier");
}
public boolean isInClubGroup(long id) { public boolean isInClubGroup(long id) {
if (idToken == null || idToken.getClaim("user_groups") == null) if (idToken == null || idToken.getClaim("user_groups") == null)
return false; return false;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

View File

@ -4,7 +4,9 @@ import {AxiosError} from "./AxiosError.jsx";
export function ClubSelect({defaultValue, name, na = false, disabled = false}) { export function ClubSelect({defaultValue, name, na = false, disabled = false}) {
return <LoadingProvider> return <LoadingProvider>
<ClubSelect_ defaultValue={defaultValue} name={name} na={na} disabled={disabled}/> <div className="input-group mb-3">
<ClubSelect_ defaultValue={defaultValue} name={name} na={na} disabled={disabled}/>
</div>
</LoadingProvider> </LoadingProvider>
} }
@ -17,7 +19,7 @@ function ClubSelect_({defaultValue, name, na, disabled}) {
? <div className="input-group mb-3"> ? <div className="input-group mb-3">
<label className="input-group-text" id="inputGroupSelect02">Club</label> <label className="input-group-text" id="inputGroupSelect02">Club</label>
<select className="form-select" id="inputGroupSelect02" disabled={disabled} <select className="form-select" id="inputGroupSelect02" disabled={disabled}
defaultValue={defaultValue ? defaultValue : -1} name={name}> defaultValue={defaultValue? defaultValue : -1} name={name}>
<option>Sélectionner...</option> <option>Sélectionner...</option>
{na && <option value={-1}>-- Non licencier --</option>} {na && <option value={-1}>-- Non licencier --</option>}
{data.map(club => (<option key={club.id} value={club.id}>{club.name}</option>))} {data.map(club => (<option key={club.id} value={club.id}>{club.name}</option>))}

View File

@ -14,7 +14,7 @@ export function PremForm({userData}) {
const formData = new FormData(); const formData = new FormData();
formData.append("federation_admin", event.target.federation_admin?.checked); formData.append("federation_admin", event.target.federation_admin?.checked);
formData.append("safca_user", event.target.safca_user?.checked); formData.append("safca_user", event.target.safca_user?.checked);
formData.append("create_compet", event.target.create_compet?.checked); formData.append("safca_create_compet", event.target.safca_create_compet?.checked);
formData.append("safca_super_admin", event.target.safca_super_admin?.checked); formData.append("safca_super_admin", event.target.safca_super_admin?.checked);
apiAxios.put(`/compte/${userData.userId}/roles`, formData, { apiAxios.put(`/compte/${userData.userId}/roles`, formData, {
@ -68,8 +68,6 @@ function PremFormContent({userData}) {
? <> ? <>
<CheckField name="federation_admin" text="Administrateur de la fédération" <CheckField name="federation_admin" text="Administrateur de la fédération"
value={data.includes("federation_admin")}/> value={data.includes("federation_admin")}/>
<CheckField name="create_compet" text="Créer des compétion"
value={data.includes("create_compet")}/>
</> </>
: error && <AxiosError error={error}/>} : error && <AxiosError error={error}/>}
</div> </div>
@ -78,10 +76,12 @@ function PremFormContent({userData}) {
{data {data
? <> ? <>
<CheckField name="safca_user" text="Accès à l'application" value={data.includes("safca_user")}/> <CheckField name="safca_user" text="Accès à l'application" value={data.includes("safca_user")}/>
<CheckField name="safca_create_compet" text="Créer des compétion"
value={data.includes("safca_create_compet")}/>
<CheckField name="safca_super_admin" text="Super administrateur" <CheckField name="safca_super_admin" text="Super administrateur"
value={data.includes("safca_super_admin")}/> value={data.includes("safca_super_admin")}/>
</> </>
: error && <AxiosError error={error}/>} : error && <AxiosError error={error}/>}
</div> </div>
</> </>
} }

View File

@ -2,7 +2,7 @@ import {useNavigate, useParams} from "react-router-dom";
import {useLoadingSwitcher} from "../../hooks/useLoading.jsx"; import {useLoadingSwitcher} from "../../hooks/useLoading.jsx";
import {useFetch} from "../../hooks/useFetch.js"; import {useFetch} from "../../hooks/useFetch.js";
import {AxiosError} from "../../components/AxiosError.jsx"; import {AxiosError} from "../../components/AxiosError.jsx";
import {CheckField, OptionField, TextField} from "../../components/MemberCustomFiels.jsx"; import {Checkbox, CheckField, OptionField, TextField} from "../../components/MemberCustomFiels.jsx";
import {ClubSelect} from "../../components/ClubSelect.jsx"; import {ClubSelect} from "../../components/ClubSelect.jsx";
import {ConfirmDialog} from "../../components/ConfirmDialog.jsx"; import {ConfirmDialog} from "../../components/ConfirmDialog.jsx";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
@ -10,14 +10,14 @@ import {apiAxios, errFormater} from "../../utils/Tools.js";
import {useEffect, useReducer, useState} from "react"; import {useEffect, useReducer, useState} from "react";
import {SimpleReducer} from "../../utils/SimpleReducer.jsx"; import {SimpleReducer} from "../../utils/SimpleReducer.jsx";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faAdd, faTrashCan} from "@fortawesome/free-solid-svg-icons"; import {faAdd, faPen, faTrashCan} from "@fortawesome/free-solid-svg-icons";
export function CompetitionEdit() { export function CompetitionEdit() {
const {id} = useParams() const {id} = useParams()
const navigate = useNavigate(); const navigate = useNavigate();
const setLoading = useLoadingSwitcher() const setLoading = useLoadingSwitcher()
const {data, refresh, error} = useFetch(`/competition/${id}?light=false`, setLoading, 1) const {data, refresh, error} = useFetch(`/competition/${id}`, setLoading, 1)
const handleRm = () => { const handleRm = () => {
toast.promise( toast.promise(
@ -49,7 +49,7 @@ export function CompetitionEdit() {
{data.id !== null && <button style={{marginBottom: "1.5em", width: "100%"}} className="btn btn-primary" {data.id !== null && <button style={{marginBottom: "1.5em", width: "100%"}} className="btn btn-primary"
onClick={_ => navigate(`/competition/${data.id}/register`)}>Voir/Modifier les participants</button>} onClick={_ => navigate(`/competition/${data.id}/register`)}>Voir/Modifier les participants</button>}
{data.id !== null && data.system === "SAFCA" && <ContentSAFCA data2={data}/>} {data.id !== null && <ContentSAFCA data2={data}/>}
{data.id !== null && <> {data.id !== null && <>
<div className="col" style={{textAlign: 'right', marginTop: '1em'}}> <div className="col" style={{textAlign: 'right', marginTop: '1em'}}>
@ -212,74 +212,17 @@ function ContentSAFCA({data2}) {
function Content({data}) { function Content({data}) {
const navigate = useNavigate(); const navigate = useNavigate();
const [registerMode, setRegisterMode] = useState(data.registerMode || "FREE");
const handleSubmit = (event) => { const handleSubmit = (event) => {
event.preventDefault(); event.preventDefault();
let err = false;
const out = {} const out = {}
out['id'] = (data.id === "") ? null : data.id out['id'] = (data.id === "") ? null : data.id
out['name'] = event.target.name?.value out['name'] = event.target.name?.value
out['date'] = event.target.date?.value out['date'] = event.target.date?.value
out['toDate'] = event.target.toDate?.value
out['system'] = event.target.system?.value out['system'] = event.target.system?.value
out['club'] = event.target.club?.value out['club'] = event.target.club?.value
out['owner'] = event.target.owner?.value out['owner'] = event.target.owner?.value
out['description'] = event.target.description?.value
out['adresse'] = event.target.adresse?.value
out['publicVisible'] = event.target.publicVisible?.checked
out['startRegister'] = event.target.startRegister?.value
out['endRegister'] = event.target.endRegister?.value
out['registerMode'] = registerMode
if (out['registerMode'] === "HELLOASSO") {
out['data3'] = event.target.data3?.value
const url = event.target.helloassoUrl?.value
if (!url || !event.target.data3?.value) {
toast.error("Veuillez renseigner l'URL de la billetterie HelloAsso et les tarifs associés.")
err = true;
} else {
const regex = /\/associations\/([^/]+)\/evenements\/([^/]+)/;
const match = url.match(regex);
if (match) {
out['data1'] = match[1]
out['data2'] = match[2]
} else {
toast.error("L'URL de la billetterie HelloAsso n'est pas valide. Veuillez vérifier le format de l'URL.")
err = true;
}
}
out['data4'] = event.target.data4?.value
if (!out['data4']) {
toast.error("Veuillez renseigner l'email de réception des inscriptions échouées.")
err = true;
}
}
if (out['date'] > out['toDate']) {
toast.error("La date de fin doit être postérieure à la date de début.")
err = true;
}
if ((out['registerMode'] === "FREE" || out['registerMode'] === "CLUB_ADMIN") && (!out['startRegister'] || !out['endRegister'])) {
toast.error("Veuillez renseigner les dates de début et de fin d'inscription.")
err = true;
}
if ((out['registerMode'] === "FREE" || out['registerMode'] === "CLUB_ADMIN") && out['startRegister'] > out['endRegister']) {
toast.error("La date de fin d'inscription doit être postérieure à la date de début d'inscription.")
err = true;
}
if (err) {
return;
}
toast.promise( toast.promise(
apiAxios.post(`/competition`, out), apiAxios.post(`/competition`, out),
@ -303,135 +246,24 @@ function Content({data}) {
<input name="id" value={data.id || ""} readOnly hidden/> <input name="id" value={data.id || ""} readOnly hidden/>
<div className="card-header">{data.id ? "Edition competition" : "Création competition"}</div> <div className="card-header">{data.id ? "Edition competition" : "Création competition"}</div>
<div className="card-body text-center"> <div className="card-body text-center">
<TextField name="uuid" text="UUID" value={data.uuid} disabled={true}/>
<TextField name="name" text="Nom" value={data.name}/>
<div className="accordion" id="accordionExample"> <div className="input-group mb-3">
<div className="accordion-item"> <span className="input-group-text" id="birth_date">Date</span>
<h2 className="accordion-header"> <input type="date" className="form-control" placeholder="jj/mm/aaaa" aria-label="date"
<button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" name="date" aria-describedby="date" defaultValue={data.date ? data.date.split('T')[0] : ''} required/>
aria-expanded="false" aria-controls="collapseOne">
Informations techniques
</button>
</h2>
<div id="collapseOne" className="accordion-collapse collapse" data-bs-parent="#accordionExample">
<div className="accordion-body">
<TextField name="uuid" text="UUID" value={data.uuid} disabled={true}/>
<OptionField name="system" text="System" value={data.system} values={{SAFCA: 'SAFCA', NONE: "intranet"}}
disabled={data.id !== null}/>
{data.id !== null &&
<div className="row">
<ClubSelect defaultValue={data.club} name="club" na={false} disabled={true}/>
</div>
}
{data.id !== null && <TextField name="owner" text="Propriétaire" value={data.owner}/>}
</div>
</div>
</div>
<div className="accordion-item">
<h2 className="accordion-header">
<button className="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo"
aria-expanded="true" aria-controls="collapseTwo">
Informations générales sur la competition
</button>
</h2>
<div id="collapseTwo" className="accordion-collapse collapse show" data-bs-parent="#accordionExample">
<div className="accordion-body">
<TextField name="name" text="Nom*" value={data.name}/>
<div className="input-group mb-3">
<span className="input-group-text" id="date">Date*</span>
<span className="input-group-text" id="date">Du</span>
<input type="date" className="form-control" placeholder="jj/mm/aaaa" aria-label="date"
name="date" aria-describedby="date" defaultValue={data.date ? data.date.split('T')[0] : ''} required/>
<span className="input-group-text" id="date">Au</span>
<input type="date" className="form-control" placeholder="jj/mm/aaaa" aria-label="toDate"
name="toDate" aria-describedby="toDate" defaultValue={data.toDate ? data.toDate.split('T')[0] : ''}
required/>
</div>
<TextField name="description" text="Description" value={data.description} required={false}/>
<TextField name="adresse" text="Adresse" value={data.adresse} required={false}/>
<CheckField name="publicVisible" text="Visible par le public (Apparaît dans la liste des compétitions)"
value={data.publicVisible} row={true}/>
<small>Si non coché, la compétition ne sera visible que par les personnes pouvant y inscrire des participants.</small>
</div>
</div>
</div>
<div className="accordion-item">
<h2 className="accordion-header">
<button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree"
aria-expanded="false" aria-controls="collapseThree">
Informations sur le mode d'inscription
</button>
</h2>
<div id="collapseThree" className="accordion-collapse collapse" data-bs-parent="#accordionExample">
<div className="accordion-body">
<div className="row">
<div className="input-group mb-3">
<label className="input-group-text">Qui peut inscrire</label>
<select className="form-select" value={registerMode} onChange={event => setRegisterMode(event.target.value)}>
<option value="FREE">Tous les membres de la FFSAF</option>
<option value="CLUB_ADMIN">Responsables et bureaux des associations</option>
<option value="ADMIN">Uniquement les administrateurs de la compétition</option>
<option value="HELLOASSO">Billetterie HelloAsso</option>
</select>
</div>
</div>
<div className="input-group mb-3"
style={{display: registerMode === "FREE" || registerMode === "CLUB_ADMIN" ? "flex" : "none"}}>
<span className="input-group-text" id="startRegister">Date d'inscription</span>
<span className="input-group-text" id="startRegister">Du</span>
<input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="date"
name="startRegister" aria-describedby="startRegister"
defaultValue={data.startRegister ? data.startRegister.split('+')[0] : ''}/>
<span className="input-group-text" id="endRegister">Au</span>
<input type="datetime-local" className="form-control" placeholder="jj/mm/aaaa" aria-label="endRegister"
name="endRegister" aria-describedby="endRegister"
defaultValue={data.endRegister ? data.endRegister.split('+')[0] : ''}/>
</div>
<div style={{display: registerMode === "HELLOASSO" ? "initial" : "none"}}>
<span style={{textAlign: "left"}}>
<div>Afin de permettre une bonne interconnexion avec HelloAsso, merci de suivre les instructions suivantes :</div>
<ul>
<li><strong>Configurer l'url de notification : </strong>afin que nous puissions recevoir une notification à
chaque inscription, il est nécessaire de configurer l'url de notification de votre compte HelloAsso pour
qu'il redirige vers "https://intra.ffsaf.fr/api/webhook/ha". Pour ce faire, depuis la page d'accueil de
votre association sur HelloAsso, allez dans <strong>Mon compte</strong> &gt; <strong>Paramètres</strong> &gt;
<strong> Intégrations et API</strong> section Notification et copier-coller <strong>https://intra.ffsaf.fr/api/webhook/ha </strong>
dans le champ <strong>Mon URL de callback</strong> et enregister.
<img src="/img/HA-help-4.png" alt="" className="img-fluid" style={{objectFit: "contain"}}/></li>
<li><strong>Copier-coller le nom exacte des tarifs</strong> <em>-sépare par des point-virgules-</em> qui donneront
lieux à une inscription automatique. Tous ces tarifs doivent impérativement demander le numéro de licence
en champs obligatoire. Pour ce faire, lors de la configuration de votre billetterie à l'étape n°3,
cliquer sur <strong>+ Ajouter une information</strong>, saisissez l'intituler exact suivant
<strong> Numéro de licence</strong>, dans <strong>Type de réponse souhaitée</strong> rentrer Nombre,
sélectionner les tarifs entrés plus précédemment et rendre l'information obligatoire.
<img src="/img/HA-help-3.png" className="img-fluid" alt="..." style={{object_fit: 'contain'}}/>
<img src="/img/HA-help-2.png" className="img-fluid" alt="..." style={{object_fit: 'contain'}}/></li>
<li><strong>Copier-coller l'url de votre billetterie</strong> dans le champs si dessous. Il devrais avoir la forme suivante:
<em> https://www.helloasso.com/associations/&lt;nom-asso-sur-helloasso&gt;/evenements/&lt;nom-billetterie&gt;</em></li>
</ul>
</span>
<TextField name="helloassoUrl" text="Url de la billetterie HelloAsso" required={false}
value={data.data1 && data.data2 && `https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}` || ""}
placeholder="https://www.helloasso.com/associations/nom-asso-sur-helloasso/evenements/nom-billetterie"/>
<TextField name="data3" text="Tarifs HelloAsso"
value={data.data3 || ""} required={false}
placeholder="Tarif1;Tarif2;Tarif3"/>
<TextField name="data4" text="Email de réception des inscriptions échoué"
value={data.data4 || ""} required={false}/>
<small>Si pour une raison quelconque l'inscription automatique échoue, un email sera envoyé à cette adresse pour
vous en informer</small>
</div>
</div>
</div>
</div>
</div> </div>
{data.id !== null && <TextField name="owner" text="Propriétaire" value={data.owner}/>}
<OptionField name="system" text="System" value={data.system} values={{SAFCA: 'SAFCA'}} disabled={data.id !== null}/>
{data.id !== null &&
<div className="row">
<ClubSelect defaultValue={data.club} name="club" na={false} disabled={true}/>
</div>
}
</div> </div>
<div className="row mb-3"> <div className="row mb-3">
@ -441,4 +273,4 @@ function Content({data}) {
</div> </div>
</div> </div>
</form> </form>
} }

View File

@ -3,7 +3,6 @@ import {useLoadingSwitcher} from "../../hooks/useLoading.jsx";
import {useFetch} from "../../hooks/useFetch.js"; import {useFetch} from "../../hooks/useFetch.js";
import {AxiosError} from "../../components/AxiosError.jsx"; import {AxiosError} from "../../components/AxiosError.jsx";
import {ThreeDots} from "react-loader-spinner"; import {ThreeDots} from "react-loader-spinner";
import {useAuth} from "../../hooks/useAuth.jsx";
export function CompetitionList() { export function CompetitionList() {
@ -27,13 +26,11 @@ export function CompetitionList() {
} }
function MakeCentralPanel({data, navigate}) { function MakeCentralPanel({data, navigate}) {
const {userinfo} = useAuth()
return <> return <>
{userinfo?.roles?.includes("create_compet") &&
<div className="col mb-2" style={{textAlign: 'right', marginTop: '1em'}}> <div className="col mb-2" style={{textAlign: 'right', marginTop: '1em'}}>
<button type="button" className="btn btn-primary" onClick={() => navigate("/competition/0")}>Nouvelle competition</button> <button type="button" className="btn btn-primary" onClick={() => navigate("/competition/0")}>Nouvelle competition</button>
</div> } </div>
<div className="mb-4"> <div className="mb-4">
<div className="list-group"> <div className="list-group">
{data.map(req => (<MakeRow key={req.id} data={req} navigate={navigate}/>))} {data.map(req => (<MakeRow key={req.id} data={req} navigate={navigate}/>))}
@ -42,32 +39,14 @@ function MakeCentralPanel({data, navigate}) {
</> </>
} }
const inscText = (type) => {
if (type === "FREE") {
return "Inscriptions libres"
} else if (type === "CLUB_ADMIN") {
return "Inscriptions par les responsables de club"
} else if (type === "ADMIN") {
return "Inscriptions par les administrateurs de la compétition"
} else if (type === "HELLOASSO") {
return "Inscriptions sur la billetterie HelloAsso"
}
return ""
}
function MakeRow({data, navigate}) { function MakeRow({data, navigate}) {
return <div className="list-group-item list-group-item-action" return <div className="list-group-item d-flex justify-content-between align-items-start list-group-item-action"
onClick={() => data.canEdit ? navigate("" + data.id) : navigate("view/" + data.id)}> onClick={() => navigate("" + data.id)}>
<div className="row justify-content-between align-items-start "> <div className="ms-2 col-auto">
<div className="ms-2 col-auto"> <div className="fw-bold">{data.name}</div>
<div><strong>{data.name}</strong> <small>par {data.clubName}</small></div> <small>{data.date.split('T')[0]}</small>
<small>Du {new Date(data.date.split('T')[0]).toLocaleDateString()} au {new Date(data.toDate.split('T')[0]).toLocaleDateString()}</small>
</div>
<div className="ms-2 col-auto">
<small style={{textAlign: 'right'}}>{inscText(data.registerMode)}</small>
</div>
</div> </div>
<small style={{textAlign: 'right'}}>{data.clubName}<br/>{data.system}</small>
</div> </div>
} }
@ -79,4 +58,4 @@ function Def() {
<li className="list-group-item"><ThreeDots/></li> <li className="list-group-item"><ThreeDots/></li>
<li className="list-group-item"><ThreeDots/></li> <li className="list-group-item"><ThreeDots/></li>
</div> </div>
} }

View File

@ -3,7 +3,6 @@ import {Outlet} from "react-router-dom";
import {CompetitionList} from "./CompetitionList.jsx"; import {CompetitionList} from "./CompetitionList.jsx";
import {CompetitionEdit} from "./CompetitionEdit.jsx"; import {CompetitionEdit} from "./CompetitionEdit.jsx";
import {CompetitionRegisterAdmin} from "./CompetitionRegisterAdmin.jsx"; import {CompetitionRegisterAdmin} from "./CompetitionRegisterAdmin.jsx";
import {CompetitionView} from "./CompetitionView.jsx";
export function CompetitionRoot() { export function CompetitionRoot() {
return <> return <>
@ -24,13 +23,9 @@ export function getCompetitionChildren() {
path: ':id', path: ':id',
element: <CompetitionEdit/> element: <CompetitionEdit/>
}, },
{
path: 'view/:id',
element: <CompetitionView/>
},
{ {
path: ':id/register', path: ':id/register',
element: <CompetitionRegisterAdmin/> element: <CompetitionRegisterAdmin/>
} }
] ]
} }

View File

@ -1,72 +0,0 @@
import {useNavigate, useParams} from "react-router-dom";
import {useLoadingSwitcher} from "../../hooks/useLoading.jsx";
import {useFetch} from "../../hooks/useFetch.js";
import {AxiosError} from "../../components/AxiosError.jsx";
import {useAuth} from "../../hooks/useAuth.jsx";
import {isClubAdmin} from "../../utils/Tools.js";
export function CompetitionView() {
const {id} = useParams()
const navigate = useNavigate();
const setLoading = useLoadingSwitcher()
const {data, error} = useFetch(`/competition/${id}?light=true`, setLoading, 1)
return <>
<button type="button" className="btn btn-link" onClick={() => navigate("/competition")}>
&laquo; retour
</button>
<div>
{data ? <>
<MakeContent data={data}/>
</>
: error && <AxiosError error={error}/>
}
</div>
</>
}
const inscText = (type) => {
if (type === "FREE") {
return "Libres"
} else if (type === "CLUB_ADMIN") {
return "Par les responsables de club"
} else if (type === "ADMIN") {
return "Par les administrateurs de la compétition"
} else if (type === "HELLOASSO") {
return "Sur la billetterie HelloAsso"
}
return ""
}
function MakeContent({data}) {
const {userinfo} = useAuth()
return <div className="card mb-4">
<div className="card-header">
<h2 className="card-title" style={{textAlign: "center"}}>{data.name}</h2>
</div>
<div className="card-body">
<p>{data.description}</p>
<p><strong>Date
:</strong> Du {new Date(data.date.split('T')[0]).toLocaleDateString()} au {new Date(data.toDate.split('T')[0]).toLocaleDateString()}
</p>
<p><strong>Lieu :</strong> {data.adresse}</p>
<p><strong>Organisateur :</strong> {data.clubName}</p>
<p><strong>Type d'inscription :</strong> {inscText(data.registerMode)}</p>
{(data.registerMode === "FREE" || data.registerMode === "CLUB_ADMIN") &&
<p><strong>Date d'inscription :</strong> Du {new Date(data.startRegister.split('+')[0]).toLocaleString()} au {new Date(data.endRegister.split('+')[0]).toLocaleString()}</p>
}
{(data.registerMode === "CLUB_ADMIN" && isClubAdmin(userinfo)) || data.registerMode === "FREE" &&
<button type="button" className="btn btn-primary" disabled={new Date() < new Date(data.startRegister.split('+')[0]) || new Date() > new Date(data.endRegister.split('+')[0])}>Inscription</button>
}
{data.registerMode === "HELLOASSO" &&
<p><strong>Billetterie :</strong> <a
href={`https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}`} target="_blank"
rel="noopener noreferrer">{`https://www.helloasso.com/associations/${data.data1}/evenements/${data.data2}`}</a></p>
}
</div>
</div>
}

View File

@ -7,15 +7,6 @@ export const apiAxios = axios.create({
}); });
apiAxios.defaults.headers.post['Accept'] = 'application/json; charset=UTF-8'; apiAxios.defaults.headers.post['Accept'] = 'application/json; charset=UTF-8';
export function isInClub(userinfo, clubId) {
return userinfo?.groups.filter((g => g.startsWith("/club/"))).map(g => g.split("/")[2]).map(g => Number(g.split("-")[0])).includes(clubId)
}
export function isClubAdmin(userinfo) {
return userinfo?.roles?.includes("club_president") || userinfo?.roles?.includes("club_respo_intra")
|| userinfo?.roles?.includes("club_secretaire") || userinfo?.roles?.includes("club_tresorier");
}
export const errFormater = (data, msg) => { export const errFormater = (data, msg) => {
if (typeof data.response.data === 'string' || data.response.data instanceof String) if (typeof data.response.data === 'string' || data.response.data instanceof String)