diff --git a/pom.xml b/pom.xml index 5d5ae23..14740ea 100644 --- a/pom.xml +++ b/pom.xml @@ -72,12 +72,28 @@ test + + io.quarkus + quarkus-oidc + + + io.quarkus + quarkus-keycloak-authorization + + org.projectlombok lombok 1.18.22 provided + + + org.apache.tika + tika-core + 3.0.0-BETA + + diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java index 4e5ad6f..e71a738 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/ClubModel.java @@ -1,14 +1,15 @@ package fr.titionfire.ffsaf.data.model; +import fr.titionfire.ffsaf.utils.Contact; import io.quarkus.runtime.annotations.RegisterForReflection; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; + +import java.util.Map; @Getter @Setter +@Builder @AllArgsConstructor @NoArgsConstructor @RegisterForReflection @@ -25,4 +26,25 @@ public class ClubModel { String country; String shieldURL; + + //@Enumerated(EnumType.STRING) + @ElementCollection + @CollectionTable(name = "club_contact_mapping", + joinColumns = {@JoinColumn(name = "club_id", referencedColumnName = "id")}) + @MapKeyColumn(name = "contact_type") + Map contact; + + String training_location; + + String training_day_time; + + String contact_intern; + + String RNA; + + String SIRET; + + String no_affiliation; + + boolean international; } diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/CombModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java similarity index 58% rename from src/main/java/fr/titionfire/ffsaf/data/model/CombModel.java rename to src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java index de9c9fb..a321d16 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/CombModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/MembreModel.java @@ -2,29 +2,31 @@ package fr.titionfire.ffsaf.data.model; import fr.titionfire.ffsaf.utils.Categorie; import fr.titionfire.ffsaf.utils.Genre; +import fr.titionfire.ffsaf.utils.GradeArbitrage; +import fr.titionfire.ffsaf.utils.RoleAsso; import io.quarkus.runtime.annotations.RegisterForReflection; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; + +import java.util.Date; @Getter @Setter +@Builder @AllArgsConstructor @NoArgsConstructor @RegisterForReflection @Entity -@Table(name = "comb") -public class CombModel { +@Table(name = "membre") +public class MembreModel { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; - String lname = ""; - String fname = ""; + String lname; + String fname; Categorie categorie; @@ -34,7 +36,17 @@ public class CombModel { Genre genre; - int licence = 0; + int licence; - String country = "fr"; + String country; + + Date birth_date; + + String email; + + RoleAsso role; + + GradeArbitrage grade_arbitrage; + + String url_photo; } diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/CombRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/CombRepository.java index ccdc089..82ffaf9 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/repository/CombRepository.java +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/CombRepository.java @@ -1,9 +1,9 @@ package fr.titionfire.ffsaf.data.repository; -import fr.titionfire.ffsaf.data.model.CombModel; +import fr.titionfire.ffsaf.data.model.MembreModel; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped -public class CombRepository implements PanacheRepositoryBase { +public class CombRepository implements PanacheRepositoryBase { } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java index 5f04850..45253dd 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/ClubEntity.java @@ -1,11 +1,16 @@ package fr.titionfire.ffsaf.domain.entity; import fr.titionfire.ffsaf.data.model.ClubModel; +import fr.titionfire.ffsaf.utils.Contact; import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import java.util.Map; + @Data +@Builder @AllArgsConstructor @RegisterForReflection public class ClubEntity { @@ -13,12 +18,39 @@ public class ClubEntity { private String name; private String country; private String shieldURL; + private Map contact; + private String training_location; + private String training_day_time; + private String contact_intern; + private String RNA; + private String SIRET; + private String no_affiliation; + private boolean international; public static ClubEntity fromModel (ClubModel model) { - return new ClubEntity(model.getId(), model.getName(), model.getCountry(), model.getShieldURL()); + if (model == null) { + return null; + } + + return ClubEntity.builder() + .id(model.getId()) + .name(model.getName()) + .country(model.getCountry()) + .shieldURL(model.getShieldURL()) + .contact(model.getContact()) + .training_location(model.getTraining_location()) + .training_day_time(model.getTraining_day_time()) + .contact_intern(model.getContact_intern()) + .RNA(model.getRNA()) + .SIRET(model.getSIRET()) + .no_affiliation(model.getNo_affiliation()) + .international(model.isInternational()) + .build(); } public ClubModel toModel () { - return new ClubModel(this.id, this.name, this.country, this.shieldURL); + return new ClubModel(this.id, this.name, this.country, this.shieldURL, this.contact, this.training_location, + this.training_day_time, this.contact_intern, this.RNA, this.SIRET, this.no_affiliation, + this.international); } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java deleted file mode 100644 index 6eb135a..0000000 --- a/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java +++ /dev/null @@ -1,38 +0,0 @@ -package fr.titionfire.ffsaf.domain.entity; - -import fr.titionfire.ffsaf.data.model.CombModel; -import fr.titionfire.ffsaf.utils.Categorie; -import fr.titionfire.ffsaf.utils.Genre; -import io.quarkus.runtime.annotations.RegisterForReflection; -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -@RegisterForReflection -public class CombEntity { - private long id; - private String lname = ""; - private String fname = ""; - private Categorie categorie; - private ClubEntity club; - private Genre genre; - private int licence; - private String country; - - public static CombEntity fromModel(CombModel model) { - if (model == null) - return null; - - return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(), - (model.getClub() == null) ? null : ClubEntity.fromModel(model.getClub()), - model.getGenre(), model.getLicence(), model.getCountry()); - } - - public static String getFullName(CombModel model) { - return model.getFname() + " " + model.getLname(); - } - public String getFullName() { - return this.fname + " " + this.lname; - } -} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/MembreEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/MembreEntity.java new file mode 100644 index 0000000..04805ee --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/MembreEntity.java @@ -0,0 +1,64 @@ +package fr.titionfire.ffsaf.domain.entity; + +import fr.titionfire.ffsaf.data.model.MembreModel; +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; +import fr.titionfire.ffsaf.utils.GradeArbitrage; +import fr.titionfire.ffsaf.utils.RoleAsso; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.Date; + +@Data +@Builder +@AllArgsConstructor +@RegisterForReflection +public class MembreEntity { + private long id; + private String lname = ""; + private String fname = ""; + private Categorie categorie; + private ClubEntity club; + private Genre genre; + private int licence; + private String country; + private Date birth_date; + private String email; + private RoleAsso role; + private GradeArbitrage grade_arbitrage; + private String url_photo; + + + public static MembreEntity fromModel(MembreModel model) { + if (model == null) + return null; + + return new MembreEntityBuilder() + .id(model.getId()) + .lname(model.getLname()) + .fname(model.getFname()) + .categorie(model.getCategorie()) + .club(ClubEntity.fromModel(model.getClub())) + .genre(model.getGenre()) + .licence(model.getLicence()) + .country(model.getCountry()) + .birth_date(model.getBirth_date()) + .email(model.getEmail()) + .role(model.getRole()) + .grade_arbitrage(model.getGrade_arbitrage()) + .url_photo(model.getUrl_photo()) + .build(); + } + + public static String getFullName(MembreModel model) { + return model.getFname() + " " + model.getLname(); + } + public String getFullName() { + return this.fname + " " + this.lname; + } + + +} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java index 9edbc0e..9dab233 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/ClubService.java @@ -2,13 +2,16 @@ package fr.titionfire.ffsaf.domain.service; import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.data.repository.ClubRepository; +import fr.titionfire.ffsaf.net2.data.SimpleClubModel; import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.vertx.VertxContextSupport; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.Collection; +import java.util.List; @WithSession @ApplicationScoped @@ -17,11 +20,16 @@ public class ClubService { @Inject ClubRepository repository; - public ClubModel findByIdOptionalClub (long id) throws Throwable { - return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id))); + public SimpleClubModel findByIdOptionalClub(long id) throws Throwable { + return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel))); } - public Collection findAllClub () throws Throwable { - return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findAll().list())); + public Collection findAllClub() throws Throwable { + return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction( + () -> repository.findAll().list().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList()))); + } + + public Uni> getAll() { + return repository.listAll(); } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/CombService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/CombService.java deleted file mode 100644 index e7cdd9e..0000000 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/CombService.java +++ /dev/null @@ -1,27 +0,0 @@ -package fr.titionfire.ffsaf.domain.service; - -import fr.titionfire.ffsaf.data.model.CombModel; -import fr.titionfire.ffsaf.data.repository.CombRepository; -import io.quarkus.hibernate.reactive.panache.Panache; -import io.quarkus.hibernate.reactive.panache.common.WithSession; -import io.quarkus.vertx.VertxContextSupport; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - - -@WithSession -@ApplicationScoped -public class CombService { - - @Inject - CombRepository repository; - - public CombModel find(int licence, String np) throws Throwable { - return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> - repository.find("licence = ?1 AND (lname = ?2 OR fname = ?2)", licence, np).firstResult())); - } - - public CombModel findByIdOptionalComb (long id) throws Throwable { - return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id))); - } -} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java new file mode 100644 index 0000000..8fb96ae --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java @@ -0,0 +1,64 @@ +package fr.titionfire.ffsaf.domain.service; + +import fr.titionfire.ffsaf.data.model.MembreModel; +import fr.titionfire.ffsaf.data.repository.ClubRepository; +import fr.titionfire.ffsaf.data.repository.CombRepository; +import fr.titionfire.ffsaf.net2.data.SimpleCombModel; +import fr.titionfire.ffsaf.rest.from.FullMemberForm; +import fr.titionfire.ffsaf.utils.Pair; +import io.quarkus.hibernate.reactive.panache.Panache; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.panache.common.Sort; +import io.quarkus.vertx.VertxContextSupport; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.List; + + +@WithSession +@ApplicationScoped +public class MembreService { + + @Inject + CombRepository repository; + @Inject + ClubRepository clubRepository; + + public SimpleCombModel find(int licence, String np) throws Throwable { + return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> + repository.find("licence = ?1 AND (lname = ?2 OR fname = ?2)", licence, np).firstResult().map(SimpleCombModel::fromModel))); + } + + public SimpleCombModel findByIdOptionalComb(long id) throws Throwable { + return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleCombModel::fromModel))); + } + + public Uni> getAll() { + return repository.listAll(Sort.ascending("fname", "lname")); + } + + public Uni getById(long id) { + return repository.findById(id); + } + + public Uni update(long id, FullMemberForm membre) { + return repository.findById(id) + .chain(membreModel -> clubRepository.findById(membre.getClub()).map(club -> new Pair<>(membreModel, club))) + .onItem().transformToUni(pair -> { + MembreModel m = pair.getKey(); + m.setFname(membre.getFname()); + m.setLname(membre.getLname()); + m.setClub(pair.getValue()); + m.setCountry(membre.getCountry()); + m.setBirth_date(membre.getBirth_date()); + m.setGenre(membre.getGenre()); + m.setCategorie(membre.getCategorie()); + m.setRole(membre.getRole()); + m.setGrade_arbitrage(membre.getGrade_arbitrage()); + m.setEmail(membre.getEmail()); + return Panache.withTransaction(() -> repository.persist(m)); + }).map(__ -> "OK"); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/net2/ServerCustom.java b/src/main/java/fr/titionfire/ffsaf/net2/ServerCustom.java index 5072afc..459f504 100644 --- a/src/main/java/fr/titionfire/ffsaf/net2/ServerCustom.java +++ b/src/main/java/fr/titionfire/ffsaf/net2/ServerCustom.java @@ -2,7 +2,7 @@ package fr.titionfire.ffsaf.net2; import com.fasterxml.jackson.databind.JsonNode; import fr.titionfire.ffsaf.domain.service.ClubService; -import fr.titionfire.ffsaf.domain.service.CombService; +import fr.titionfire.ffsaf.domain.service.MembreService; import fr.titionfire.ffsaf.net2.packet.IAction; import fr.titionfire.ffsaf.net2.packet.RegisterAction; import fr.titionfire.ffsaf.utils.Pair; @@ -54,7 +54,7 @@ public class ServerCustom extends Thread { protected Scheduler quartz; @Inject - public CombService combService; + public MembreService membreService; @Inject public ClubService clubService; diff --git a/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleClubModel.java b/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleClubModel.java new file mode 100644 index 0000000..e36daef --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleClubModel.java @@ -0,0 +1,27 @@ +package fr.titionfire.ffsaf.net2.data; + +import fr.titionfire.ffsaf.data.model.ClubModel; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@RegisterForReflection +public class SimpleClubModel { + Long id; + String name; + String country; + String shieldURL; + + public static SimpleClubModel fromModel(ClubModel model) { + if (model == null) + return null; + + return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(), model.getShieldURL()); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleCombModel.java b/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleCombModel.java new file mode 100644 index 0000000..9a062ab --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/net2/data/SimpleCombModel.java @@ -0,0 +1,35 @@ +package fr.titionfire.ffsaf.net2.data; + +import fr.titionfire.ffsaf.data.model.MembreModel; +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@RegisterForReflection +public class SimpleCombModel { + Long id; + String lname = ""; + String fname = ""; + Categorie categorie; + SimpleClubModel club; + Genre genre; + int licence = 0; + String country = "fr"; + +public static SimpleCombModel fromModel(MembreModel model) { + if (model == null) + return null; + + return new SimpleCombModel(model.getId(), model.getLname(), model.getFname(), model.getCategorie(), + (model.getClub() == null) ? null : SimpleClubModel.fromModel(model.getClub()), + model.getGenre(), model.getLicence(), model.getCountry()); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/net2/packet/RClub.java b/src/main/java/fr/titionfire/ffsaf/net2/packet/RClub.java index ba84ba7..b740668 100644 --- a/src/main/java/fr/titionfire/ffsaf/net2/packet/RClub.java +++ b/src/main/java/fr/titionfire/ffsaf/net2/packet/RClub.java @@ -1,7 +1,7 @@ package fr.titionfire.ffsaf.net2.packet; -import fr.titionfire.ffsaf.data.model.ClubModel; import fr.titionfire.ffsaf.net2.ServerCustom; +import fr.titionfire.ffsaf.net2.data.SimpleClubModel; import org.jboss.logging.Logger; import java.util.Collection; @@ -12,7 +12,7 @@ public class RClub { final CIA findByIdOptionalClub = new CIA<>(Long.class, (client_Thread, message) -> { try { - ClubModel clubModel = ServerCustom.getInstance().clubService.findByIdOptionalClub(message.data()); + SimpleClubModel clubModel = ServerCustom.getInstance().clubService.findByIdOptionalClub(message.data()); client_Thread.sendRepTo(clubModel, message); } catch (Throwable e) { LOGGER.error(e.getMessage(), e); @@ -22,7 +22,7 @@ public class RClub { final IAction findAllClub = (client_Thread, message) -> { try { - Collection clubModels = ServerCustom.getInstance().clubService.findAllClub(); + Collection clubModels = ServerCustom.getInstance().clubService.findAllClub(); client_Thread.sendRepTo(clubModels, message); } catch (Throwable e) { LOGGER.error(e.getMessage(), e); diff --git a/src/main/java/fr/titionfire/ffsaf/net2/packet/RComb.java b/src/main/java/fr/titionfire/ffsaf/net2/packet/RComb.java index 26a32a3..850ddc0 100644 --- a/src/main/java/fr/titionfire/ffsaf/net2/packet/RComb.java +++ b/src/main/java/fr/titionfire/ffsaf/net2/packet/RComb.java @@ -1,7 +1,7 @@ package fr.titionfire.ffsaf.net2.packet; -import fr.titionfire.ffsaf.data.model.CombModel; import fr.titionfire.ffsaf.net2.ServerCustom; +import fr.titionfire.ffsaf.net2.data.SimpleCombModel; import jakarta.enterprise.context.ApplicationScoped; import org.jboss.logging.Logger; @@ -13,7 +13,7 @@ public class RComb { final IAction findComb = (client_Thread, message) -> { try { - CombModel combModel = ServerCustom.getInstance().combService.find(message.data().get("licence").asInt(), message.data().get("np").asText()); + SimpleCombModel combModel = ServerCustom.getInstance().membreService.find(message.data().get("licence").asInt(), message.data().get("np").asText()); client_Thread.sendRepTo(combModel, message); } catch (Throwable e) { LOGGER.error(e.getMessage(), e); @@ -23,7 +23,7 @@ public class RComb { final CIA findByIdOptionalComb = new CIA<>(Long.class, (client_Thread, message) -> { try { - CombModel combModel = ServerCustom.getInstance().combService.findByIdOptionalComb(message.data()); + SimpleCombModel combModel = ServerCustom.getInstance().membreService.findByIdOptionalComb(message.data()); client_Thread.sendRepTo(combModel, message); } catch (Throwable e) { LOGGER.error(e.getMessage(), e); diff --git a/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java new file mode 100644 index 0000000..df648a9 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java @@ -0,0 +1,28 @@ +package fr.titionfire.ffsaf.rest; + +import fr.titionfire.ffsaf.domain.service.ClubService; +import fr.titionfire.ffsaf.net2.data.SimpleClubModel; +import io.smallrye.mutiny.Uni; +import jakarta.annotation.security.RolesAllowed; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import java.util.List; + +@Path("/club") +public class ClubEndpoints { + + @Inject + ClubService clubService; + + @GET + @Path("/no_detail") + @RolesAllowed("federation_admin") + @Produces(MediaType.APPLICATION_JSON) + public Uni> getAll() { + return clubService.getAll().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList()); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java new file mode 100644 index 0000000..8844662 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/CombEndpoints.java @@ -0,0 +1,124 @@ +package fr.titionfire.ffsaf.rest; + +import fr.titionfire.ffsaf.domain.service.MembreService; +import fr.titionfire.ffsaf.rest.data.SimpleMembre; +import fr.titionfire.ffsaf.rest.from.FullMemberForm; +import fr.titionfire.ffsaf.utils.Pair; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.unchecked.Unchecked; +import jakarta.annotation.security.RolesAllowed; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.apache.commons.io.FileUtils; +import org.apache.tika.mime.MimeTypeException; +import org.apache.tika.mime.MimeTypes; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLConnection; +import java.nio.file.Files; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +@Path("/member") +public class CombEndpoints { + + @Inject + MembreService membreService; + + @ConfigProperty(name = "upload_dir") + String media; + + @GET + @Path("/all") + @RolesAllowed("federation_admin") + @Produces(MediaType.APPLICATION_JSON) + public Uni> getAll() { + return membreService.getAll().map(membreModels -> membreModels.stream().map(SimpleMembre::fromModel).toList()); + } + + @GET + @Path("{id}") + @RolesAllowed("federation_admin") + @Produces(MediaType.APPLICATION_JSON) + public Uni getById(@PathParam("id") long id) { + return membreService.getById(id).map(SimpleMembre::fromModel); + } + + @POST + @Path("{id}") + @RolesAllowed("federation_admin") + @Produces(MediaType.TEXT_PLAIN) + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Uni setAdminMembre(@PathParam("id") long id, FullMemberForm input) { + Future future = CompletableFuture.supplyAsync(() -> { + try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input.getPhoto_data()))) { + String mimeType = URLConnection.guessContentTypeFromStream(is); + String extension = MimeTypes.getDefaultMimeTypes().forName(mimeType).getExtension(); + FileUtils.writeByteArrayToFile(new File(media, "ppMembre/" + input.getId() + extension), input.getPhoto_data()); + return "OK"; + } catch (IOException | MimeTypeException e) { + return e.getMessage(); + } + }); + + if (input.getPhoto_data().length > 0) { + return membreService.update(id, input) + .invoke(Unchecked.consumer(out -> { + if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); + })) + .chain(() -> Uni.createFrom().future(future)) + .invoke(Unchecked.consumer(out -> { + if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); + })); + } else { + return membreService.update(id, input) + .invoke(Unchecked.consumer(out -> { + if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); + })); + } + } + + @GET + @Path("{id}/photo") + public Uni getPhoto(@PathParam("id") long id) throws URISyntaxException { + Future> future = CompletableFuture.supplyAsync(() -> { + FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id)); + File[] files = new File(media, "ppMembre").listFiles(filter); + if (files != null && files.length > 0) { + File file = files[0]; + try { + byte[] data = Files.readAllBytes(file.toPath()); + return new Pair<>(file, data); + } catch (IOException ignored) { + } + } + return null; + }); + + URI uri = new URI("https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava2.webp"); + + return Uni.createFrom().future(future) + .map(filePair -> { + if (filePair == null) + return Response.temporaryRedirect(uri).build(); + + String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName()); + + Response.ResponseBuilder resp = Response.ok(filePair.getValue()); + resp.type(MediaType.APPLICATION_OCTET_STREAM); + resp.header(HttpHeaders.CONTENT_LENGTH, filePair.getValue().length); + resp.header(HttpHeaders.CONTENT_TYPE, mimeType); + resp.header(HttpHeaders.CONTENT_DISPOSITION, "inline; "); + + return resp.build(); + }); + } + +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleMembre.java b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleMembre.java new file mode 100644 index 0000000..a553c36 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleMembre.java @@ -0,0 +1,55 @@ +package fr.titionfire.ffsaf.rest.data; + +import fr.titionfire.ffsaf.data.model.MembreModel; +import fr.titionfire.ffsaf.net2.data.SimpleClubModel; +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; +import fr.titionfire.ffsaf.utils.GradeArbitrage; +import fr.titionfire.ffsaf.utils.RoleAsso; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.Date; + +@Data +@Builder +@AllArgsConstructor +@RegisterForReflection +public class SimpleMembre { + private long id; + private String lname = ""; + private String fname = ""; + private Categorie categorie; + private SimpleClubModel club; + private Genre genre; + private int licence; + private String country; + private Date birth_date; + private String email; + private RoleAsso role; + private GradeArbitrage grade_arbitrage; + private String url_photo; + + public static SimpleMembre fromModel(MembreModel model) { + if (model == null) + return null; + + return new SimpleMembreBuilder() + .id(model.getId()) + .lname(model.getLname()) + .fname(model.getFname()) + .categorie(model.getCategorie()) + .club(SimpleClubModel.fromModel(model.getClub())) + .genre(model.getGenre()) + .licence(model.getLicence()) + .country(model.getCountry()) + .birth_date(model.getBirth_date()) + .email(model.getEmail()) + .role(model.getRole()) + .grade_arbitrage(model.getGrade_arbitrage()) + .url_photo(model.getUrl_photo()) + .build(); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/from/FullMemberForm.java b/src/main/java/fr/titionfire/ffsaf/rest/from/FullMemberForm.java new file mode 100644 index 0000000..f164c9e --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/from/FullMemberForm.java @@ -0,0 +1,74 @@ +package fr.titionfire.ffsaf.rest.from; + +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; +import fr.titionfire.ffsaf.utils.GradeArbitrage; +import fr.titionfire.ffsaf.utils.RoleAsso; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.core.MediaType; +import lombok.Getter; +import org.jboss.resteasy.reactive.PartType; + +import java.util.Date; + +@Getter +public class FullMemberForm { + @FormParam("id") + private String id = null; + + @FormParam("lname") + private String lname = null; + + @FormParam("fname") + private String fname = null; + + @FormParam("categorie") + private Categorie categorie = null; + + @FormParam("club") + private Long club = null; + + @FormParam("genre") + private Genre genre; + + @FormParam("licence") + private int licence; + + @FormParam("country") + private String country; + + @FormParam("birth_date") + private Date birth_date; + + @FormParam("email") + private String email; + + @FormParam("role") + private RoleAsso role; + + @FormParam("grade_arbitrage") + private GradeArbitrage grade_arbitrage; + + @FormParam("photo_data") + @PartType(MediaType.APPLICATION_OCTET_STREAM) + private byte[] photo_data = new byte[0]; + + @Override + public String toString() { + return "FullMemberForm{" + + "id='" + id + '\'' + + ", lname='" + lname + '\'' + + ", fname='" + fname + '\'' + + ", categorie=" + categorie + + ", club=" + club + + ", genre=" + genre + + ", licence=" + licence + + ", country='" + country + '\'' + + ", birth_date=" + birth_date + + ", email='" + email + '\'' + + ", role=" + role + + ", grade_arbitrage=" + grade_arbitrage + + ", url_photo=" + photo_data.length + + '}'; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/utils/Contact.java b/src/main/java/fr/titionfire/ffsaf/utils/Contact.java new file mode 100644 index 0000000..bf71c20 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/utils/Contact.java @@ -0,0 +1,20 @@ +package fr.titionfire.ffsaf.utils; + +public enum Contact { + COURRIEL("Courriel"), + TELEPHONE("Téléphone"), + SITE("Site web"), + FACEBOOK("Facebook"), + INSTAGRAM("Instagram"); + + public final String name; + + Contact(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/utils/GradeArbitrage.java b/src/main/java/fr/titionfire/ffsaf/utils/GradeArbitrage.java new file mode 100644 index 0000000..800dca5 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/utils/GradeArbitrage.java @@ -0,0 +1,18 @@ +package fr.titionfire.ffsaf.utils; + +public enum GradeArbitrage { + NA("N/A"), + ASSESSEUR("Assesseur"), + ARBITRE("Arbitre"); + + public final String name; + + GradeArbitrage(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/utils/RoleAsso.java b/src/main/java/fr/titionfire/ffsaf/utils/RoleAsso.java new file mode 100644 index 0000000..7d89bc7 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/utils/RoleAsso.java @@ -0,0 +1,20 @@ +package fr.titionfire.ffsaf.utils; + + +public enum RoleAsso { + MEMBRE("Membre"), + PRESIDENT("Président"), + TRESORIER("Trésorier"), + SECRETAIRE("Secrétaire"); + + public final String name; + + RoleAsso(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5734e1b..bd30282 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -17,6 +17,14 @@ quarkus.quartz.start-mode=forced %dev.quarkus.log.min-level=ALL %dev.quarkus.log.category."fr.titionfire.ffsaf".level=ALL +quarkus.oidc.auth-server-url=https://auth.safca.fr/auth/realms/safca +quarkus.oidc.client-id=backend +quarkus.oidc.credentials.secret=secret +quarkus.oidc.tls.verification=required +#quarkus.oidc.tls.verification=none + +quarkus.http.limits.max-body-size=10M + database.prefix = test2_ database.database=ffsaf database.hostname=localhost diff --git a/src/main/webapp/README.md b/src/main/webapp/README.md index f768e33..4db7bd1 100644 --- a/src/main/webapp/README.md +++ b/src/main/webapp/README.md @@ -6,3 +6,6 @@ Currently, two official plugins are available: - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + + +https://mhnpd.github.io/react-loader-spinner/docs/intro diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 4b07f12..97bbfb1 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -6,6 +6,8 @@ Vite + React + + -
+
+
+

Chargement de l'application FFSAF...

+
+
+
- + diff --git a/src/main/webapp/package-lock.json b/src/main/webapp/package-lock.json index cbfec89..d1c274e 100644 --- a/src/main/webapp/package-lock.json +++ b/src/main/webapp/package-lock.json @@ -14,8 +14,10 @@ "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "axios": "^1.6.5", + "browser-image-compression": "^2.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-loader-spinner": "^6.1.6", "react-router-dom": "^6.21.2" }, "devDependencies": { @@ -378,6 +380,24 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.11", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", @@ -1258,6 +1278,11 @@ "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", "dev": true }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1501,6 +1526,14 @@ "concat-map": "0.0.1" } }, + "node_modules/browser-image-compression": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/browser-image-compression/-/browser-image-compression-2.0.2.tgz", + "integrity": "sha512-pBLlQyUf6yB8SmmngrcOw3EoS4RpQ1BcylI3T9Yqn7+4nrQTXJD4sJDe5ODnJdrvNMaio5OicFo75rDyJD2Ucw==", + "dependencies": { + "uzip": "0.20201231.0" + } + }, "node_modules/browserslist": { "version": "4.22.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", @@ -1556,6 +1589,14 @@ "node": ">=6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001576", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", @@ -1642,6 +1683,24 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3140,7 +3199,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -3375,8 +3433,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/postcss": { "version": "8.4.33", @@ -3406,6 +3463,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3487,6 +3549,27 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-loader-spinner": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.1.6.tgz", + "integrity": "sha512-x5h1Jcit7Qn03MuKlrWcMG9o12cp9SNDVHVJTNRi9TgtGPKcjKiXkou4NRfLAtXaFB3+Z8yZsVzONmPzhv2ErA==", + "dependencies": { + "react-is": "^18.2.0", + "styled-components": "^6.1.2" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-loader-spinner/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -3750,6 +3833,11 @@ "node": ">= 0.4" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3789,7 +3877,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3883,6 +3970,70 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -3922,6 +4073,11 @@ "node": ">=4" } }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4065,6 +4221,11 @@ "punycode": "^2.1.0" } }, + "node_modules/uzip": { + "version": "0.20201231.0", + "resolved": "https://registry.npmjs.org/uzip/-/uzip-0.20201231.0.tgz", + "integrity": "sha512-OZeJfZP+R0z9D6TmBgLq2LHzSSptGMGDGigGiEe0pr8UBe/7fdflgHlHBNDASTXB5jnFuxHpNaJywSg8YFeGng==" + }, "node_modules/vite": { "version": "5.0.11", "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz", diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 29b9303..2b2667a 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -16,8 +16,10 @@ "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "axios": "^1.6.5", + "browser-image-compression": "^2.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-loader-spinner": "^6.1.6", "react-router-dom": "^6.21.2" }, "devDependencies": { diff --git a/src/main/webapp/public/index.css b/src/main/webapp/public/index.css new file mode 100644 index 0000000..804723c --- /dev/null +++ b/src/main/webapp/public/index.css @@ -0,0 +1,33 @@ +:root { + +} + +.loader-container { + width: 100%; + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: fixed; + background: rgba(0, 0, 0, 0.834); + z-index: 1; +} + +.spinner { + width: 64px; + height: 64px; + border: 8px solid; + border-color: #3d5af1 transparent #3d5af1 transparent; + border-radius: 50%; + animation: spin-anim 1.2s linear infinite; +} + +@keyframes spin-anim { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/main/webapp/src/App.jsx b/src/main/webapp/src/App.jsx index 12553cf..8903e52 100644 --- a/src/main/webapp/src/App.jsx +++ b/src/main/webapp/src/App.jsx @@ -3,10 +3,11 @@ import './App.css' import {Nav} from "./components/Nav.jsx"; import {createBrowserRouter, Outlet, RouterProvider, useRouteError} from "react-router-dom"; import {Home} from "./pages/Homepage.jsx"; -import {AdminRoot} from "./pages/admin/AdminRoot.jsx"; +import {AdminRoot, getAdminChildren} from "./pages/admin/AdminRoot.jsx"; import {AuthCallback} from "./components/auhCallback.jsx"; -import {useAuthDispatch} from "./hooks/useAuth.jsx"; +import {KeycloakContextProvider, useAuthDispatch} from "./hooks/useAuth.jsx"; import {check_validity} from "./utils/auth.js"; +import {MemberList} from "./pages/admin/MemberList.jsx"; const router = createBrowserRouter([ { @@ -21,16 +22,7 @@ const router = createBrowserRouter([ { path: 'admin', element: , - children: [ - { - path: '', - element:
Admin
- }, - { - path: 'b', - element:
Admin B
- } - ] + children: getAdminChildren() } ] }, @@ -51,6 +43,16 @@ function PageError() { } function Root() { + const dispatch = useAuthDispatch() + const isInit = useRef(false) + + useEffect(() => { + if (isInit.current) + return; + isInit.current = true + check_validity(b => dispatch({type: 'init', val: b})) + }, []); + return <>