diff --git a/pom.xml b/pom.xml index 52baa01..2c0622e 100644 --- a/pom.xml +++ b/pom.xml @@ -133,6 +133,11 @@ io.quarkus quarkus-mailer + + + io.quarkus + quarkus-websockets-next + diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/CardboardModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/CardboardModel.java new file mode 100644 index 0000000..255f83e --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/model/CardboardModel.java @@ -0,0 +1,42 @@ +package fr.titionfire.ffsaf.data.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@RegisterForReflection + +@Entity +@Table(name = "cardboard") +public class CardboardModel { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "comb", referencedColumnName = "id") + MembreModel comb; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "guest_comb", referencedColumnName = "id") + CompetitionGuestModel guestComb; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "match", referencedColumnName = "id") + MatchModel match; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "compet", referencedColumnName = "id") + CompetitionModel compet; + + int red; + int yellow; +} diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/CategoryModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/CategoryModel.java index d32dac0..5f381cf 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/CategoryModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/CategoryModel.java @@ -42,4 +42,6 @@ public class CategoryModel { List tree; Integer type; + + String liceName = "1"; } diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java new file mode 100644 index 0000000..697de65 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionGuestModel.java @@ -0,0 +1,50 @@ +package fr.titionfire.ffsaf.data.model; + +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; +import io.quarkus.runtime.annotations.RegisterForReflection; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@RegisterForReflection + +@Entity +@Table(name = "competition_guest") +public class CompetitionGuestModel { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "competition", referencedColumnName = "id") + CompetitionModel competition; + + String lname = ""; + String fname = ""; + + Categorie categorie = null; + + String club = null; + + Genre genre = null; + + String country = "fr"; + + Integer weight = null; + + public CompetitionGuestModel(String s) { + this.fname = s.substring(0, s.indexOf(" ")); + this.lname = s.substring(s.indexOf(" ") + 1); + } + + public String getName() { + return fname + " " + lname; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java index 77b4b94..8834b92 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/CompetitionModel.java @@ -55,10 +55,18 @@ public class CompetitionModel { @OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL) List insc; + @OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + List guests = new ArrayList<>(); + + List banMembre = new ArrayList<>(); String owner; + List admin = new ArrayList<>(); + @Column(name = "table_") + List table = new ArrayList<>(); + String data1; String data2; String data3; diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java index a20694e..a6b9d73 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java @@ -7,6 +7,7 @@ import jakarta.persistence.*; import lombok.*; import java.util.ArrayList; +import java.util.Date; import java.util.List; @Getter @@ -32,13 +33,17 @@ public class MatchModel { @JoinColumn(name = "c1", referencedColumnName = "id") MembreModel c1_id = null; - String c1_str = null; + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "c1_guest", referencedColumnName = "id") + CompetitionGuestModel c1_guest = null; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "c2", referencedColumnName = "id") MembreModel c2_id = null; - String c2_str = null; + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "c2_guest", referencedColumnName = "id") + CompetitionGuestModel c2_guest = null; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "id_category", referencedColumnName = "id") @@ -48,9 +53,45 @@ public class MatchModel { boolean isEnd = true; + Date date = null; + @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "score", joinColumns = @JoinColumn(name = "id_match")) List scores = new ArrayList<>(); char poule = 'A'; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "match", referencedColumnName = "id") + List cardboard = new ArrayList<>(); + + public String getC1Name() { + if (c1_id != null) + return c1_id.fname + " " + c1_id.lname; + if (c1_guest != null) + return c1_guest.fname + " " + c1_guest.lname; + return ""; + } + + public String getC2Name() { + if (c2_id != null) + return c2_id.fname + " " + c2_id.lname; + if (c2_guest != null) + return c2_guest.fname + " " + c2_guest.lname; + return ""; + } + + public int win() { + int sum = 0; + for (ScoreEmbeddable score : this.getScores()) { + if (score.getS1() == -1000 || score.getS2() == -1000) + continue; + + if (score.getS1() > score.getS2()) + sum++; + else if (score.getS1() < score.getS2()) + sum--; + } + return sum; + } } diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java index ed567e2..02c0f90 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/RegisterModel.java @@ -56,4 +56,14 @@ public class RegisterModel { this.categorie = categorie; this.club = club; } + + public String getName() { + return membre.fname + " " + membre.lname; + } + + public ClubModel getClub2() { + if (club == null) + return membre.club; + return club; + } } diff --git a/src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java b/src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java index 092b950..2f0e556 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java +++ b/src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java @@ -7,6 +7,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.ArrayList; +import java.util.List; + @Getter @Setter @AllArgsConstructor @@ -36,4 +39,20 @@ public class TreeModel { @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinColumn(referencedColumnName = "id") TreeModel right; + + public List flat() { + List out = new ArrayList<>(); + this.flat(out); + return out; + } + + private void flat(List out) { + out.add(this); + + if (this.right != null) + this.right.flat(out); + + if (this.left != null) + this.left.flat(out); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/CardboardRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/CardboardRepository.java new file mode 100644 index 0000000..11fc320 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/CardboardRepository.java @@ -0,0 +1,9 @@ +package fr.titionfire.ffsaf.data.repository; + +import fr.titionfire.ffsaf.data.model.CardboardModel; +import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class CardboardRepository implements PanacheRepositoryBase { +} diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/CategoryRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/CategoryRepository.java index 8271b3b..eb3d5b9 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/repository/CategoryRepository.java +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/CategoryRepository.java @@ -1,9 +1,19 @@ package fr.titionfire.ffsaf.data.repository; import fr.titionfire.ffsaf.data.model.CategoryModel; +import fr.titionfire.ffsaf.utils.CompetitionSystem; +import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class CategoryRepository implements PanacheRepositoryBase { + + public Uni create(CategoryModel categoryModel) { + categoryModel.setSystem(CompetitionSystem.INTERNAL); + return Panache.withTransaction(() -> this.persist(categoryModel) + .invoke(categoryModel1 -> categoryModel1.setSystemId(categoryModel1.getId()))) + .chain(this::persist); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/CompetitionGuestRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/CompetitionGuestRepository.java new file mode 100644 index 0000000..6e0a959 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/CompetitionGuestRepository.java @@ -0,0 +1,9 @@ +package fr.titionfire.ffsaf.data.repository; + +import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; +import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class CompetitionGuestRepository implements PanacheRepositoryBase { +} diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/MatchRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/MatchRepository.java index ab284c4..46044b7 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/repository/MatchRepository.java +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/MatchRepository.java @@ -1,9 +1,30 @@ package fr.titionfire.ffsaf.data.repository; import fr.titionfire.ffsaf.data.model.MatchModel; +import fr.titionfire.ffsaf.utils.CompetitionSystem; +import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; +import java.util.List; + @ApplicationScoped public class MatchRepository implements PanacheRepositoryBase { + + public Uni create(MatchModel matchModel) { + matchModel.setSystem(CompetitionSystem.INTERNAL); + return Panache.withTransaction(() -> this.persistAndFlush(matchModel) + .invoke(matchModel1 -> matchModel1.setSystemId(matchModel1.getId()))) + .chain(this::persist); + } + + public Uni create(List matchModel) { + matchModel.forEach(model -> model.setSystem(CompetitionSystem.INTERNAL)); + return Panache.withTransaction(() -> this.persist(matchModel) + .call(__ -> this.flush()) + .invoke(__ -> matchModel.forEach(model -> model.setSystemId(model.getId()))) + .map(__ -> matchModel)) + .chain(this::persist); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/data/repository/TreeRepository.java b/src/main/java/fr/titionfire/ffsaf/data/repository/TreeRepository.java index 57bb22b..e38aee0 100644 --- a/src/main/java/fr/titionfire/ffsaf/data/repository/TreeRepository.java +++ b/src/main/java/fr/titionfire/ffsaf/data/repository/TreeRepository.java @@ -2,8 +2,22 @@ package fr.titionfire.ffsaf.data.repository; import fr.titionfire.ffsaf.data.model.TreeModel; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class TreeRepository implements PanacheRepositoryBase { + + @WithTransaction + public Uni deleteTree(TreeModel entity) { + Uni uni = Uni.createFrom().item(false); + if (entity == null) + return uni; + if (entity.getLeft() != null) + uni = uni.chain(__ -> this.deleteTree(entity.getLeft())); + if (entity.getRight() != null) + uni = uni.chain(__ -> this.deleteTree(entity.getRight())); + return uni.chain(__ -> this.deleteById(entity.getId())); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/CardboardEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/CardboardEntity.java new file mode 100644 index 0000000..ca15a38 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/CardboardEntity.java @@ -0,0 +1,26 @@ +package fr.titionfire.ffsaf.domain.entity; + +import fr.titionfire.ffsaf.data.model.CardboardModel; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +@RegisterForReflection +public class CardboardEntity { + long comb_id; + long match_id; + long compet_id; + + int red; + int yellow; + + public static CardboardEntity fromModel(CardboardModel model) { + return new CardboardEntity( + model.getComb() != null ? model.getComb().getId() : model.getGuestComb().getId() * -1, + model.getMatch().getId(), + model.getCompet().getId(), + model.getRed(), model.getYellow()); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java new file mode 100644 index 0000000..4126db2 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/CombEntity.java @@ -0,0 +1,56 @@ +package fr.titionfire.ffsaf.domain.entity; + +import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; +import fr.titionfire.ffsaf.data.model.MembreModel; +import fr.titionfire.ffsaf.data.model.RegisterModel; +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; + Categorie categorie; + String club_uuid; + String club_str; + Genre genre; + String country; + int overCategory; + Integer weight; + + public static CombEntity fromModel(MembreModel model) { + if (model == null) + return null; + + return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(), + model.getClub() == null ? null : model.getClub().getClubId(), + model.getClub() == null ? "Sans club" : model.getClub().getName(), model.getGenre(), model.getCountry(), + 0, null); + } + + + public static CombEntity fromModel(CompetitionGuestModel model) { + if (model == null) + return null; + + return new CombEntity(model.getId() * -1, model.getLname(), model.getFname(), model.getCategorie(), null, + model.getClub(), model.getGenre(), model.getCountry(), 0, model.getWeight()); + } + + public static CombEntity fromModel(RegisterModel registerModel) { + if (registerModel == null || registerModel.getMembre() == null) + return null; + MembreModel model = registerModel.getMembre(); + + return new CombEntity(model.getId(), model.getLname(), model.getFname(), registerModel.getCategorie(), + registerModel.getClub2() == null ? null : registerModel.getClub2().getClubId(), + registerModel.getClub2() == null ? "Sans club" : registerModel.getClub2().getName(), model.getGenre(), + model.getCountry(), registerModel.getOverCategory(), registerModel.getWeight()); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/MatchEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/MatchEntity.java new file mode 100644 index 0000000..49b7e77 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/MatchEntity.java @@ -0,0 +1,56 @@ +package fr.titionfire.ffsaf.domain.entity; + +import fr.titionfire.ffsaf.data.model.MatchModel; +import fr.titionfire.ffsaf.utils.ScoreEmbeddable; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Data +@AllArgsConstructor +@RegisterForReflection +public class MatchEntity { + private long id; + private CombEntity c1; + private CombEntity c2; + private long categorie_ord = 0; + private boolean isEnd; + private long categorie; + private Date date; + private List scores; + private char poule; + private List cardboard; + + public static MatchEntity fromModel(MatchModel model) { + if (model == null) + return null; + return new MatchEntity(model.getId(), + (model.getC1_id() == null) ? CombEntity.fromModel(model.getC1_guest()) : CombEntity.fromModel( + model.getC1_id()), + (model.getC2_id() == null) ? CombEntity.fromModel(model.getC2_guest()) : CombEntity.fromModel( + model.getC2_id()), + model.getCategory_ord(), model.isEnd(), model.getCategory().getId(), model.getDate(), + model.getScores(), + model.getPoule(), + (model.getCardboard() == null) ? new ArrayList<>() : model.getCardboard().stream() + .map(CardboardEntity::fromModel).toList()); + } + + public int win() { + int sum = 0; + for (ScoreEmbeddable score : scores) { + if (score.getS1() == -1000 || score.getS2() == -1000) + continue; + + if (score.getS1() > score.getS2()) + sum++; + else if (score.getS1() < score.getS2()) + sum--; + } + return sum; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/entity/TreeEntity.java b/src/main/java/fr/titionfire/ffsaf/domain/entity/TreeEntity.java new file mode 100644 index 0000000..6f65263 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/entity/TreeEntity.java @@ -0,0 +1,71 @@ +package fr.titionfire.ffsaf.domain.entity; + +import fr.titionfire.ffsaf.data.model.TreeModel; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +@RegisterForReflection +public class TreeEntity { + private Long id; + private Long categorie; + private Integer level; + private MatchEntity match; + private TreeEntity left; + private TreeEntity right; + private TreeEntity associatedNode; + + public static TreeEntity fromModel(TreeModel model) { + if (model == null) + return null; + + return new TreeEntity(model.getId(), model.getCategory(), model.getLevel(), MatchEntity.fromModel(model.getMatch()), fromModel(model.getLeft()), + fromModel(model.getRight()), null); + } + + public TreeEntity getMatchNode(Long matchId) { + if (this.match != null && this.match.getId() == matchId) { + return this; + } else { + if (this.left != null) { + TreeEntity left = this.left.getMatchNode(matchId); + if (left != null) { + return left; + } + } + if (this.right != null) { + TreeEntity right = this.right.getMatchNode(matchId); + if (right != null) { + return right; + } + } + } + return null; + } + + public static TreeEntity getParent(TreeEntity current, TreeEntity target) { + if (current == null) { + return null; + } else if (current.equals(target)) { + return null; + } else if (target.equals(current.left) || target.equals(current.right)) { + return current; + } else { + TreeEntity left = getParent(current.left, target); + if (left != null) + return left; + return getParent(current.right, target); + } + } + + public static void setAssociated(TreeEntity current, TreeEntity next) { + if (current == null || next == null) { + return; + } + current.setAssociatedNode(next); + setAssociated(current.getLeft(), next.getLeft()); + setAssociated(current.getRight(), next.getRight()); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/CategoryService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/CategoryService.java index 19cbee0..0485ede 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/CategoryService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/CategoryService.java @@ -1,9 +1,6 @@ package fr.titionfire.ffsaf.domain.service; -import fr.titionfire.ffsaf.data.model.MatchModel; -import fr.titionfire.ffsaf.data.model.MembreModel; -import fr.titionfire.ffsaf.data.model.CategoryModel; -import fr.titionfire.ffsaf.data.model.TreeModel; +import fr.titionfire.ffsaf.data.model.*; import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.rest.data.CategoryData; import fr.titionfire.ffsaf.rest.data.CategoryFullData; @@ -45,10 +42,13 @@ public class CategoryService { @Inject CompetPermService permService; + @Inject + CompetitionGuestRepository competitionGuestRepository; + public Uni getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) { return repository.find("systemId = ?1 AND system = ?2", id, system) .firstResult() - .onItem().ifNull().failWith(() -> new RuntimeException("Poule not found")) + .onItem().ifNull().failWith(() -> new RuntimeException("Category not found")) .call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet())) .map(CategoryData::fromModel); } @@ -171,6 +171,15 @@ public class CategoryService { .invoke(o2 -> o2.forEach(m -> o.membres.put(m.getId(), m))) ) ) + .call(o -> Mutiny.fetch(o.category.getCompet().getGuests()) + .invoke(o2 -> o2.forEach(m -> o.guest.put(m.getFname() + " " + m.getLname(), m))) + .map(o2 -> data.getMatches().stream().flatMap(m -> Stream.of(m.getC1_str(), m.getC2_str()) + .filter(Objects::nonNull)).distinct().filter(s -> !o.guest.containsKey(s)).map( + CompetitionGuestModel::new).toList()) + .call(o3 -> o3.isEmpty() ? Uni.createFrom().nullItem() : + Uni.join().all(o3.stream().map(o4 -> competitionGuestRepository.persist(o4)).toList()) + .andFailFast()) + .invoke(o2 -> o2.forEach(m -> o.guest.put(m.getFname() + " " + m.getLname(), m)))) .invoke(in -> { ArrayList node = new ArrayList<>(); for (TreeModel treeModel : in.category.getTree()) @@ -214,8 +223,8 @@ public class CategoryService { } mm.setCategory(in.category); mm.setCategory_ord(m.getCategory_ord()); - mm.setC1_str(m.getC1_str()); - mm.setC2_str(m.getC2_str()); + mm.setC1_guest(in.guest.getOrDefault(m.getC1_str(), null)); + mm.setC2_guest(in.guest.getOrDefault(m.getC2_str(), null)); mm.setC1_id(in.membres.getOrDefault(m.getC1_id(), null)); mm.setC2_id(in.membres.getOrDefault(m.getC2_id(), null)); mm.setEnd(m.isEnd()); @@ -238,6 +247,7 @@ public class CategoryService { private static class WorkData { CategoryModel category; HashMap membres = new HashMap<>(); + HashMap guest = new HashMap<>(); List match = new ArrayList<>(); List toRmMatch; List unlinkNode; @@ -246,7 +256,7 @@ public class CategoryService { public Uni delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) { return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult() - .onItem().ifNull().failWith(() -> new RuntimeException("Poule not found")) + .onItem().ifNull().failWith(() -> new RuntimeException("Category not found")) .call(o -> permService.hasEditPerm(securityCtx, o.getCompet())) .call(o -> Mutiny.fetch(o.getTree()) .call(o2 -> o2.isEmpty() ? Uni.createFrom().nullItem() : @@ -260,7 +270,7 @@ public class CategoryService { Panache.withTransaction(() -> treeRepository.delete("id IN ?1", in))) ) ) - .call(o -> matchRepository.delete("poule.id = ?1", o.getId())) + .call(o -> matchRepository.delete("category.id = ?1", o.getId())) .chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId()))); } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetPermService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetPermService.java index 5e3ba1e..448b915 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetPermService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetPermService.java @@ -56,7 +56,7 @@ public class CompetPermService { CompletableFuture f = new CompletableFuture<>(); SReqCompet.getConfig(serverCustom.clients, id, f); try { - return f.get(1500, TimeUnit.MILLISECONDS); + return f.get(500, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { throw new RuntimeException(e); } @@ -71,7 +71,8 @@ public class CompetPermService { .chain(competitionModels -> { CompletableFuture> f = new CompletableFuture<>(); SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f); - return Uni.createFrom().future(f, Duration.ofMillis(1500)) + return Uni.createFrom().future(f, Duration.ofMillis(500)) + .onFailure().recoverWithItem(new HashMap<>()) .map(map_ -> { HashMap map = new HashMap<>(); map_.forEach((key, value) -> map.put(Long.parseLong(key), value)); @@ -89,7 +90,7 @@ public class CompetPermService { .onFailure().call(throwable -> cacheAccess.invalidate(securityCtx.getSubject())); Uni> none = cacheNoneAccess.getAsync(securityCtx.getSubject(), - k -> competitionRepository.list("system = ?1", CompetitionSystem.NONE) + k -> competitionRepository.list("system = ?1", CompetitionSystem.INTERNAL) .map(competitionModels -> { HashMap map = new HashMap<>(); for (CompetitionModel model : competitionModels) { @@ -184,7 +185,7 @@ public class CompetPermService { if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here throw new DForbiddenException(); - if (o.getSystem() == CompetitionSystem.NONE) + if (o.getSystem() == CompetitionSystem.INTERNAL) if (securityCtx.roleHas("club_president") || securityCtx.roleHas("club_respo_intra") || securityCtx.roleHas("club_secretaire") || securityCtx.roleHas("club_tresorier")) return Uni.createFrom().nullItem(); @@ -219,13 +220,58 @@ public class CompetPermService { if (o.getSystem() == CompetitionSystem.SAFCA) return hasSafcaEditPerm(securityCtx, o.getId()); - if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here - throw new DForbiddenException(); - - if (o.getSystem() == CompetitionSystem.NONE) - if (securityCtx.isClubAdmin()) + if (o.getSystem() == CompetitionSystem.INTERNAL) { + if (securityCtx.isInClubGroup(o.getClub().getId()) && securityCtx.isClubAdmin()) return Uni.createFrom().nullItem(); + if (o.getAdmin().contains(securityCtx.getSubject())) + return Uni.createFrom().nullItem(); + + throw new DForbiddenException(); + } + + throw new DForbiddenException(); + }) + ); + } + + /** + * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm + */ + public Uni hasTablePerm(SecurityCtx securityCtx, CompetitionModel competitionModel) { + return hasTablePerm(securityCtx, Uni.createFrom().item(competitionModel)); + } + + /** + * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm + */ + public Uni hasTablePerm(SecurityCtx securityCtx, long id) { + return hasTablePerm(securityCtx, competitionRepository.findById(id)); + } + + /** + * @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm + */ + public Uni hasTablePerm(SecurityCtx securityCtx, Uni 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 hasSafcaTablePerm(securityCtx, o.getId()); + + if (o.getSystem() == CompetitionSystem.INTERNAL) { + if (securityCtx.isInClubGroup(o.getClub().getId()) && securityCtx.isClubAdmin()) + return Uni.createFrom().nullItem(); + + if (o.getAdmin().contains(securityCtx.getSubject())) + return Uni.createFrom().nullItem(); + if (o.getTable().contains(securityCtx.getSubject())) + return Uni.createFrom().nullItem(); + + throw new DForbiddenException(); + } + throw new DForbiddenException(); }) ); @@ -236,8 +282,8 @@ public class CompetPermService { Uni.createFrom().nullItem() : getSafcaConfig(id).chain(Unchecked.function(o -> { - if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())) && !o.table() - .contains(UUID.fromString(securityCtx.getSubject()))) + if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())) + && !o.table().contains(UUID.fromString(securityCtx.getSubject()))) throw new DForbiddenException(); return Uni.createFrom().nullItem(); })); @@ -253,4 +299,16 @@ public class CompetPermService { return Uni.createFrom().nullItem(); })); } + + private Uni hasSafcaTablePerm(SecurityCtx securityCtx, long id) { + return securityCtx.roleHas("safca_super_admin") ? + Uni.createFrom().nullItem() + : + getSafcaConfig(id).chain(Unchecked.function(o -> { + if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())) + && !o.table().contains(UUID.fromString(securityCtx.getSubject()))) + throw new DForbiddenException(); + return Uni.createFrom().nullItem(); + })); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java index 52c0db5..7e4caa8 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/CompetitionService.java @@ -1,9 +1,6 @@ package fr.titionfire.ffsaf.domain.service; -import fr.titionfire.ffsaf.data.model.CompetitionModel; -import fr.titionfire.ffsaf.data.model.HelloAssoRegisterModel; -import fr.titionfire.ffsaf.data.model.MembreModel; -import fr.titionfire.ffsaf.data.model.RegisterModel; +import fr.titionfire.ffsaf.data.model.*; import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.data.SimpleCompet; @@ -16,10 +13,9 @@ import fr.titionfire.ffsaf.rest.data.SimpleCompetData; import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb; import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException; -import fr.titionfire.ffsaf.utils.CompetitionSystem; -import fr.titionfire.ffsaf.utils.RegisterMode; -import fr.titionfire.ffsaf.utils.SecurityCtx; -import fr.titionfire.ffsaf.utils.Utils; +import fr.titionfire.ffsaf.rest.exception.DNotFoundException; +import fr.titionfire.ffsaf.utils.*; +import fr.titionfire.ffsaf.ws.send.SRegister; import io.quarkus.cache.Cache; import io.quarkus.cache.CacheName; import io.quarkus.hibernate.reactive.panache.Panache; @@ -64,6 +60,9 @@ public class CompetitionService { @Inject CombRepository combRepository; + @Inject + CompetitionGuestRepository competitionGuestRepository; + @Inject ServerCustom serverCustom; @@ -83,6 +82,9 @@ public class CompetitionService { @Inject Vertx vertx; + @Inject + SRegister sRegister; + @Inject @CacheName("safca-config") Cache cache; @@ -103,12 +105,13 @@ public class CompetitionService { if (id == 0) { return Uni.createFrom() .item(new CompetitionData(null, "", "", "", "", new Date(), new Date(), - CompetitionSystem.NONE, RegisterMode.FREE, new Date(), new Date(), true, + CompetitionSystem.INTERNAL, RegisterMode.FREE, new Date(), new Date(), true, null, "", "", null, true, "", "", "", "")); } return permService.hasAdminViewPerm(securityCtx, id) .chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc()) - .map(insc -> CompetitionData.fromModel(competitionModel).addInsc(insc))) + .chain(insc -> Mutiny.fetch(competitionModel.getGuests()) + .map(guest -> CompetitionData.fromModel(competitionModel).addInsc(insc, guest)))) .chain(data -> vertx.getOrCreateContext().executeBlocking(() -> { keycloakService.getUser(UUID.fromString(data.getOwner())) @@ -159,6 +162,14 @@ public class CompetitionService { .map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList()); } + public Uni> getAllSystemTable(SecurityCtx securityCtx, + CompetitionSystem system) { + return repository.list("system = ?1", system) + .chain(l -> Uni.join().all(l.stream().map(cm -> permService.hasTablePerm(securityCtx, cm)).toList()) + .andCollectFailures()) + .map(l -> l.stream().filter(Objects::nonNull).map(CompetitionData::fromModel).toList()); + } + public Uni addOrUpdate(SecurityCtx securityCtx, CompetitionData data) { if (data.getId() == null) { return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() @@ -175,6 +186,7 @@ public class CompetitionService { model.setSystem(data.getSystem()); model.setClub(clubModel); model.setInsc(new ArrayList<>()); + model.setGuests(new ArrayList<>()); model.setUuid(UUID.randomUUID().toString()); model.setOwner(securityCtx.getSubject()); @@ -184,7 +196,7 @@ public class CompetitionService { }).map(CompetitionData::fromModel) .call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate( securityCtx.getSubject()) : Uni.createFrom().nullItem()) - .call(c -> (c.getSystem() == CompetitionSystem.NONE) ? cacheNoneAccess.invalidate( + .call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate( securityCtx.getSubject()) : Uni.createFrom().nullItem()); } else { return permService.hasEditPerm(securityCtx, data.getId()) @@ -208,7 +220,7 @@ public class CompetitionService { }).map(CompetitionData::fromModel) .call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate( securityCtx.getSubject()) : Uni.createFrom().nullItem()) - .call(c -> (c.getSystem() == CompetitionSystem.NONE) ? cacheNoneAccess.invalidate( + .call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate( securityCtx.getSubject()) : Uni.createFrom().nullItem()); } } @@ -235,11 +247,18 @@ public class CompetitionService { public Uni> getRegister(SecurityCtx securityCtx, Long id, String source) { if ("admin".equals(source)) return permService.hasEditPerm(securityCtx, id) - .chain(c -> Mutiny.fetch(c.getInsc())) - .onItem().transformToMulti(Multi.createFrom()::iterable) - .onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences())) - .map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences())) - .collect().asList(); + .chain(c -> { + Uni> uni = Mutiny.fetch(c.getInsc()) + .onItem().transformToMulti(Multi.createFrom()::iterable) + .onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences())) + .map(cm -> SimpleRegisterComb.fromModel(cm, cm.getMembre().getLicences())) + .collect().asList(); + return uni + .call(l -> Mutiny.fetch(c.getGuests()) + .map(guest -> guest.stream().map(SimpleRegisterComb::fromModel).toList()) + .invoke(l::addAll)); + }); + if ("club".equals(source)) return Uni.createFrom().nullItem() .invoke(Unchecked.consumer(__ -> { @@ -262,17 +281,44 @@ public class CompetitionService { public Uni addRegisterComb(SecurityCtx securityCtx, Long id, RegisterRequestData data, String source) { if ("admin".equals(source)) - return permService.hasEditPerm(securityCtx, id) - .chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname()) - .call(combModel -> { - if (c.getBanMembre() == null) - c.setBanMembre(new ArrayList<>()); - c.getBanMembre().remove(combModel.getId()); - return Panache.withTransaction(() -> repository.persist(c)); - }) - .chain(combModel -> updateRegister(data, c, combModel, true))) - .chain(r -> Mutiny.fetch(r.getMembre().getLicences()) - .map(licences -> SimpleRegisterComb.fromModel(r, licences))); + if (data.getLicence() != -1) { // not a guest + return permService.hasEditPerm(securityCtx, id) + .chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname()) + .call(combModel -> Mutiny.fetch(combModel.getLicences())) + .call(combModel -> { + if (c.getBanMembre() == null) + c.setBanMembre(new ArrayList<>()); + c.getBanMembre().remove(combModel.getId()); + return Panache.withTransaction(() -> repository.persist(c)); + }) + .chain(combModel -> updateRegister(data, c, combModel, true))) + .map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())); + } else { + return permService.hasEditPerm(securityCtx, id) + .chain(c -> competitionGuestRepository.findById(data.getId() * -1) + .map(g -> { + if (g != null) + return g; + CompetitionGuestModel model = new CompetitionGuestModel(); + model.setCompetition(c); + return model; + })) + .chain(model -> { + model.setFname(data.getFname()); + model.setLname(data.getLname()); + model.setGenre(data.getGenre()); + model.setClub(data.getClub()); + model.setCountry(data.getCountry()); + model.setWeight(data.getWeight()); + model.setCategorie(data.getCategorie()); + + return Panache.withTransaction(() -> competitionGuestRepository.persist(model)) + .call(r -> model.getCompetition().getSystem() == CompetitionSystem.INTERNAL ? + sRegister.sendRegister(model.getCompetition().getUuid(), + r) : Uni.createFrom().voidItem()); + }) + .map(SimpleRegisterComb::fromModel); + } if ("club".equals(source)) return repository.findById(id) .invoke(Unchecked.consumer(cm -> { @@ -283,6 +329,7 @@ public class CompetitionService { throw new DBadRequestException("Inscription fermée"); })) .chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname()) + .call(combModel -> Mutiny.fetch(combModel.getLicences())) .invoke(Unchecked.consumer(model -> { if (!securityCtx.isInClubGroup(model.getClub().getId())) throw new DForbiddenException(); @@ -291,8 +338,7 @@ public class CompetitionService { "Vous n'avez pas le droit d'inscrire ce membre (par décision de l'administrateur de la compétition)"); })) .chain(combModel -> updateRegister(data, c, combModel, false))) - .chain(r -> Mutiny.fetch(r.getMembre().getLicences()) - .map(licences -> SimpleRegisterComb.fromModel(r, licences))); + .map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())); return repository.findById(id) .invoke(Unchecked.consumer(cm -> { @@ -347,15 +393,18 @@ public class CompetitionService { SReqRegister.sendIfNeed(serverCustom.clients, new CompetitionData.SimpleRegister(r.getMembre().getId(), r.getOverCategory(), r.getWeight(), r.getCategorie(), - (r.getClub() == null) ? null : r.getClub().getId()), c.getId()); + (r.getClub() == null) ? null : r.getClub().getId(), + (r.getClub() == null) ? null : r.getClub().getName()), c.getId()); } return r; })) - .chain(r -> Panache.withTransaction(() -> registerRepository.persist(r))); + .chain(r -> Panache.withTransaction(() -> registerRepository.persist(r))) + .call(r -> c.getSystem() == CompetitionSystem.INTERNAL ? + sRegister.sendRegister(c.getUuid(), r) : Uni.createFrom().voidItem()); } private Uni findComb(Long licence, String fname, String lname) { - if (licence != null && licence != 0) { + if (licence != null && licence > 0) { return combRepository.find("licence = ?1", licence).firstResult() .invoke(Unchecked.consumer(combModel -> { if (combModel == null) @@ -398,30 +447,45 @@ public class CompetitionService { if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister())) throw new DBadRequestException("Inscription fermée"); })) - .call(cm -> membreService.getByAccountId(securityCtx.getSubject()) + .call(cm -> membreService.getById(combId) .invoke(Unchecked.consumer(model -> { + if (model == null) + throw new DNotFoundException("Membre " + combId + " n'existe pas"); if (!securityCtx.isInClubGroup(model.getClub().getId())) throw new DForbiddenException(); }))) .chain(c -> deleteRegister(combId, c, false)); return repository.findById(id) - .invoke(Unchecked.consumer(cm -> { - if (cm.getRegisterMode() != RegisterMode.FREE) - throw new DForbiddenException(); - if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister())) - throw new DBadRequestException("Inscription fermée"); - })) + .call(cm -> membreService.getByAccountId(securityCtx.getSubject()) + .invoke(Unchecked.consumer(model -> { + if (cm.getRegisterMode() != RegisterMode.FREE || !Objects.equals(model.getId(), combId)) + throw new DForbiddenException(); + if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister())) + throw new DBadRequestException("Inscription fermée"); + }))) .chain(c -> deleteRegister(combId, c, false)); } private Uni deleteRegister(Long combId, CompetitionModel c, boolean admin) { + if (admin && combId < 0) { + return competitionGuestRepository.find("competition = ?1 AND id = ?2", c, combId * -1).firstResult() + .onFailure().transform(t -> new DBadRequestException("Combattant non inscrit")) + .call(Unchecked.function( + model -> Panache.withTransaction(() -> competitionGuestRepository.delete(model)) + .call(r -> c.getSystem() == CompetitionSystem.INTERNAL ? + sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom() + .voidItem()))) + .replaceWithVoid(); + } return registerRepository.find("competition = ?1 AND membre.id = ?2", c, combId).firstResult() .onFailure().transform(t -> new DBadRequestException("Combattant non inscrit")) .call(Unchecked.function(registerModel -> { if (!admin && registerModel.isLockEdit()) throw new DForbiddenException("Modification bloquée par l'administrateur de la compétition"); - return Panache.withTransaction(() -> registerRepository.delete(registerModel)); + return Panache.withTransaction(() -> registerRepository.delete(registerModel)) + .call(r -> c.getSystem() == CompetitionSystem.INTERNAL ? + sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom().voidItem()); })) .replaceWithVoid(); } @@ -440,6 +504,10 @@ public class CompetitionService { .andCollectFailures())) .call(competitionModel -> Panache.withTransaction( () -> categoryRepository.delete("compet = ?1", competitionModel))) + .call(competitionModel -> Panache.withTransaction( + () -> registerRepository.delete("competition = ?1", competitionModel))) + .call(competitionModel -> Panache.withTransaction( + () -> competitionGuestRepository.delete("competition = ?1", competitionModel))) .chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId()))) .invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id)) .call(__ -> cache.invalidate(id)); @@ -528,8 +596,13 @@ public class CompetitionService { continue; uni = uni.call(__ -> Panache.withTransaction( - () -> registerRepository.delete("competition = ?1 AND membre = ?2", - reg.getCompetition(), reg.getMembre()))); + () -> registerRepository.delete("competition = ?1 AND membre = ?2", + reg.getCompetition(), reg.getMembre()))) + .call(r -> reg.getCompetition().getSystem() == CompetitionSystem.INTERNAL ? + sRegister.sendRegisterRemove(reg.getCompetition().getUuid(), + reg.getMembre().getId()) : Uni.createFrom().voidItem()).onFailure() + .recoverWithNull() + ; } return uni; @@ -541,7 +614,8 @@ public class CompetitionService { public Uni registerHelloAsso(NotificationData data) { String organizationSlug = data.getOrganizationSlug(); String formSlug = data.getFormSlug(); - RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false); + RegisterRequestData req = new RegisterRequestData(null, "", "", null, 0, false, null, Categorie.CADET, Genre.NA, + null, "fr"); return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult() .onFailure().recoverWithNull() diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/MatchService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/MatchService.java index 279d43d..71dda91 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/MatchService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MatchService.java @@ -1,9 +1,11 @@ package fr.titionfire.ffsaf.domain.service; +import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; import fr.titionfire.ffsaf.data.model.MatchModel; -import fr.titionfire.ffsaf.data.repository.CombRepository; -import fr.titionfire.ffsaf.data.repository.MatchRepository; import fr.titionfire.ffsaf.data.repository.CategoryRepository; +import fr.titionfire.ffsaf.data.repository.CombRepository; +import fr.titionfire.ffsaf.data.repository.CompetitionGuestRepository; +import fr.titionfire.ffsaf.data.repository.MatchRepository; import fr.titionfire.ffsaf.rest.data.MatchData; import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.utils.CompetitionSystem; @@ -33,6 +35,9 @@ public class MatchService { @Inject CompetPermService permService; + @Inject + CompetitionGuestRepository competitionGuestRepository; + public Uni getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) { return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult() .onItem().ifNull().failWith(() -> new DNotFoundException("Match not found")) @@ -75,8 +80,6 @@ public class MatchService { } ) .chain(o -> { - o.setC1_str(data.getC1_str()); - o.setC2_str(data.getC2_str()); o.setCategory_ord(data.getCategory_ord()); o.getScores().clear(); o.getScores().addAll(data.getScores()); @@ -88,6 +91,20 @@ public class MatchService { .chain(() -> (data.getC1_id() == null) ? Uni.createFrom().nullItem() : combRepository.findById(data.getC2_id())) .invoke(o::setC2_id) + .chain(() -> (data.getC1_str() == null) ? + Uni.createFrom() + .item((CompetitionGuestModel) null) : competitionGuestRepository.find( + "fname = ?1 AND lname = ?2", + data.getC1_str().substring(0, data.getC1_str().indexOf(" ")), + data.getC1_str().substring(data.getC1_str().indexOf(" ") + 1)).firstResult()) + .invoke(o::setC1_guest) + .chain(() -> (data.getC2_str() == null) ? + Uni.createFrom() + .item((CompetitionGuestModel) null) : competitionGuestRepository.find( + "fname = ?1 AND lname = ?2", + data.getC2_str().substring(0, data.getC2_str().indexOf(" ")), + data.getC2_str().substring(data.getC2_str().indexOf(" ") + 1)).firstResult()) + .invoke(o::setC2_guest) .chain(() -> Panache.withTransaction(() -> repository.persist(o))); }) .map(MatchData::fromModel); diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/ResultService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/ResultService.java new file mode 100644 index 0000000..ecd48a4 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/ResultService.java @@ -0,0 +1,400 @@ +package fr.titionfire.ffsaf.domain.service; + +import fr.titionfire.ffsaf.data.model.*; +import fr.titionfire.ffsaf.data.repository.CategoryRepository; +import fr.titionfire.ffsaf.data.repository.CompetitionRepository; +import fr.titionfire.ffsaf.data.repository.MatchRepository; +import fr.titionfire.ffsaf.data.repository.RegisterRepository; +import fr.titionfire.ffsaf.rest.data.ResultCategoryData; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; +import fr.titionfire.ffsaf.utils.*; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.unchecked.Unchecked; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.Builder; +import org.hibernate.reactive.mutiny.Mutiny; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +@WithSession +@ApplicationScoped +public class ResultService { + + @Inject + CompetitionRepository compRepository; + + @Inject + RegisterRepository registerRepository; + + @Inject + MembreService membreService; + + @Inject + CategoryRepository categoryRepository; + + @Inject + MatchRepository matchRepository; + + private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("lang.String"); + + public Uni> getList(SecurityCtx securityCtx) { + return membreService.getByAccountId(securityCtx.getSubject()) + .chain(m -> registerRepository.list("membre = ?1", m)) + .onItem().transformToMulti(Multi.createFrom()::iterable) + .onItem().call(r -> Mutiny.fetch(r.getCompetition())) + .onItem().transform(r -> new Object[]{r.getCompetition().getUuid(), r.getCompetition().getName(), + r.getCompetition().getDate()}) + .collect().asList(); + } + + public Uni> getCategory(String uuid, SecurityCtx securityCtx) { + return hasAccess(uuid, securityCtx) + .chain(m -> categoryRepository.list("compet.uuid = ?1", uuid) + .chain(cats -> matchRepository.list("(c1_id = ?1 OR c2_id = ?1 OR True) AND category IN ?2", //TODO rm OR True + m.getMembre(), cats))) + .map(matchModels -> { + HashMap> map = new HashMap<>(); + for (MatchModel matchModel : matchModels) { + if (!map.containsKey(matchModel.getCategory().getId())) + map.put(matchModel.getCategory().getId(), new ArrayList<>()); + map.get(matchModel.getCategory().getId()).add(matchModel); + } + + return map.values(); + }) + .onItem() + .transformToMulti(Multi.createFrom()::iterable) + .onItem().call(list -> Mutiny.fetch(list.get(0).getCategory().getTree())) + .onItem().transform(this::getData) + .collect().asList(); + + } + + private ResultCategoryData getData(List matchModels) { + ResultCategoryData out = new ResultCategoryData(); + + CategoryModel categoryModel = matchModels.get(0).getCategory(); + out.setName(categoryModel.getName()); + out.setType(categoryModel.getType()); + + getArray2(matchModels, out); + getTree(categoryModel.getTree(), out); + return out; + } + + private void getArray2(List matchModels_, ResultCategoryData out) { + List matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList(); + + HashMap> matchMap = new HashMap<>(); + for (MatchModel model : matchModels) { + char g = model.getPoule(); + if (!matchMap.containsKey(g)) + matchMap.put(g, new ArrayList<>()); + matchMap.get(g).add(model); + } + + matchMap.forEach((c, matchEntities) -> { + List matchs = matchEntities.stream() + .sorted(Comparator.comparing(MatchModel::getCategory_ord)) + .map(ResultCategoryData.PouleArrayData::fromModel) + .toList(); + + List rankArray = matchEntities.stream() + .flatMap(m -> Stream.of(m.getC1Name(), m.getC2Name())) + .distinct() + .map(combName -> { + AtomicInteger w = new AtomicInteger(0); + AtomicInteger pointMake = new AtomicInteger(0); + AtomicInteger pointTake = new AtomicInteger(0); + + matchEntities.stream() + .filter(m -> m.isEnd() && (m.getC1Name().equals(combName) || m.getC2Name() + .equals(combName))) + .forEach(matchModel -> { + int win = matchModel.win(); + if ((matchModel.getC1Name() + .equals(combName) && win > 0) || matchModel.getC2Name() + .equals(combName) && win < 0) + w.getAndIncrement(); + + for (ScoreEmbeddable score : matchModel.getScores()) { + if (score.getS1() <= -900 || score.getS2() <= -900) + continue; + if (matchModel.getC1Name().equals(combName)) { + pointMake.addAndGet(score.getS1()); + pointTake.addAndGet(score.getS2()); + } else { + pointMake.addAndGet(score.getS2()); + pointTake.addAndGet(score.getS1()); + } + } + }); + float pointRate = (pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get(); + + return new ResultCategoryData.RankArray(0, combName, w.get(), + pointMake.get(), pointTake.get(), pointRate); + }) + .sorted(Comparator + .comparing(ResultCategoryData.RankArray::getWin) + .thenComparing(ResultCategoryData.RankArray::getPointRate).reversed()) + .toList(); + out.getMatchs().put(c, matchs); + + int lastWin = -1; + float pointRate = 0; + int rank = 0; + for (ResultCategoryData.RankArray rankArray1 : rankArray) { + if (rankArray1.getWin() != lastWin || pointRate != rankArray1.getPointRate()) { + lastWin = rankArray1.getWin(); + pointRate = rankArray1.getPointRate(); + rank++; + } + rankArray1.setRank(rank); + } + out.getRankArray().put(c, rankArray); + }); + } + + private static void convertTree(TreeModel src, TreeNode dst) { + dst.setData(ResultCategoryData.TreeData.from(src.getMatch())); + if (src.getLeft() != null) { + dst.setLeft(new TreeNode<>()); + convertTree(src.getLeft(), dst.getLeft()); + } + if (src.getRight() != null) { + dst.setRight(new TreeNode<>()); + convertTree(src.getRight(), dst.getRight()); + } + } + + private void getTree(List treeModels, ResultCategoryData out) { + ArrayList> trees = new ArrayList<>(); + treeModels.stream().filter(t -> t.getLevel() != 0).forEach(treeModel -> { + TreeNode root = new TreeNode<>(); + convertTree(treeModel, root); + trees.add(root); + }); + out.setTrees(trees); + } + + public Uni getAllCombArray(String uuid, SecurityCtx securityCtx) { + return hasAccess(uuid, securityCtx) + .chain(cm_register -> registerRepository.list("competition.uuid = ?1", uuid) + .chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid) + .map(matchModels -> new Pair<>(registers, matchModels))) + .map(pair -> { + List registers = pair.getKey(); + List matchModels = pair.getValue(); + + CombsArrayData.CombsArrayDataBuilder builder = CombsArrayData.builder(); + + List combs = matchModels.stream() + .flatMap(m -> Stream.of(m.getC1Name(), m.getC2Name())) + .filter(Objects::nonNull) + .distinct() + .map(combName -> { + var builder2 = CombsArrayData.CombsData.builder(); + AtomicInteger w = new AtomicInteger(0); + AtomicInteger l = new AtomicInteger(0); + AtomicInteger pointMake = new AtomicInteger(); + AtomicInteger pointTake = new AtomicInteger(); + + matchModels.stream() + .filter(m -> m.isEnd() && (m.getC1Name().equals(combName) + || m.getC2Name().equals(combName))) + .forEach(matchModel -> { + int win = matchModel.win(); + if ((combName.equals(matchModel.getC1Name()) && win > 0) || + combName.equals(matchModel.getC2Name()) && win < 0) { + w.getAndIncrement(); + } else { + l.getAndIncrement(); + } + + matchModel.getScores().stream() + .filter(s -> s.getS1() > -900 && s.getS2() > -900) + .forEach(score -> { + if (combName.equals(matchModel.getC1Name())) { + pointMake.addAndGet(score.getS1()); + pointTake.addAndGet(score.getS2()); + } else { + pointMake.addAndGet(score.getS2()); + pointTake.addAndGet(score.getS1()); + } + }); + }); + + Categorie categorie = null; + ClubModel club = null; + + Optional register = registers.stream() + .filter(r -> r.getName().equals(combName)).findFirst(); + if (register.isPresent()) { + categorie = register.get().getCategorie(); + club = register.get().getClub2(); + } + + builder2.cat((categorie == null) ? "---" : categorie.getName(BUNDLE)); + builder2.name(combName); + builder2.w(w.get()); + builder2.l(l.get()); + builder2.ratioVictoire((l.get() == 0) ? w.get() : (float) w.get() / l.get()); + builder2.club((club == null) ? BUNDLE.getString("no.licence") : club.getName()); + builder2.pointMake(pointMake.get()); + builder2.pointTake(pointTake.get()); + builder2.ratioPoint( + (pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get()); + + return builder2.build(); + }) + .sorted(Comparator.comparing(CombsArrayData.CombsData::name)) + .toList(); + + builder.nb_insc(combs.size()); + builder.tt_match((int) matchModels.stream().filter(MatchModel::isEnd).count()); + builder.point(combs.stream().mapToInt(CombsArrayData.CombsData::pointMake).sum()); + builder.combs(combs); + + return builder.build(); + }) + ); + } + + + @Builder + @RegisterForReflection + public static record CombsArrayData(int nb_insc, int tt_match, long point, List combs) { + @Builder + @RegisterForReflection + public static record CombsData(String cat, String club, String name, int w, int l, float ratioVictoire, + float ratioPoint, int pointMake, int pointTake) { + } + } + + public Uni getClubArray(String uuid, SecurityCtx securityCtx) { + ClubArrayData.ClubArrayDataBuilder builder = ClubArrayData.builder(); + + return hasAccess(uuid, securityCtx) + .invoke(cm_register -> builder.name(cm_register.getClub2().getName())) + .chain(cm_register -> registerRepository.list("competition.uuid = ?1 AND membre.club = ?2", uuid, + cm_register.getClub2()) + .chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid) + .map(matchModels -> new Pair<>(registers, matchModels))) + .map(pair -> { + List registers = pair.getKey(); + List matchModels = pair.getValue(); + + builder.nb_insc(registers.size()); + + AtomicInteger tt_win = new AtomicInteger(0); + AtomicInteger tt_match = new AtomicInteger(0); + + List combData = registers.stream() + .map(register -> { + + var builder2 = ClubArrayData.CombData.builder(); + AtomicInteger w = new AtomicInteger(0); + AtomicInteger l = new AtomicInteger(0); + AtomicInteger pointMake = new AtomicInteger(); + AtomicInteger pointTake = new AtomicInteger(); + + matchModels.stream() + .filter(m -> m.isEnd() && (register.getMembre().equals(m.getC1_id()) + || register.getMembre().equals(m.getC2_id()))) + .forEach(matchModel -> { + int win = matchModel.win(); + if ((register.getMembre() + .equals(matchModel.getC1_id()) && win > 0) || + register.getMembre() + .equals(matchModel.getC2_id()) && win < 0) { + w.getAndIncrement(); + } else { + l.getAndIncrement(); + } + + matchModel.getScores().stream() + .filter(s -> s.getS1() > -900 && s.getS2() > -900) + .forEach(score -> { + if (register.getMembre() + .equals(matchModel.getC1_id())) { + pointMake.addAndGet(score.getS1()); + pointTake.addAndGet(score.getS2()); + } else { + pointMake.addAndGet(score.getS2()); + pointTake.addAndGet(score.getS1()); + } + }); + }); + + Categorie categorie = register.getCategorie(); + if (categorie == null) + categorie = register.getMembre().getCategorie(); + + builder2.cat((categorie == null) ? "---" : categorie.getName(BUNDLE)); + builder2.name(register.getName()); + builder2.w(w.get()); + builder2.l(l.get()); + builder2.ratioVictoire((l.get() == 0) ? w.get() : (float) w.get() / l.get()); + builder2.pointMake(pointMake.get()); + builder2.pointTake(pointTake.get()); + builder2.ratioPoint( + (pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get()); + + tt_win.addAndGet(w.get()); + tt_match.addAndGet(w.get() + l.get()); + + return builder2.build(); + }) + .sorted(Comparator.comparing(ClubArrayData.CombData::name)) + .toList(); + + builder.nb_match(tt_match.get()); + builder.match_w(tt_win.get()); + builder.ratioVictoire((float) combData.stream().filter(c -> c.l + c.w != 0) + .mapToDouble(ClubArrayData.CombData::ratioVictoire).average().orElse(0L)); + builder.pointMake(combData.stream().mapToInt(ClubArrayData.CombData::pointMake).sum()); + builder.pointTake(combData.stream().mapToInt(ClubArrayData.CombData::pointTake).sum()); + builder.ratioPoint((float) combData.stream().filter(c -> c.l + c.w != 0) + .mapToDouble(ClubArrayData.CombData::ratioPoint).average().orElse(0L)); + builder.combs(combData); + + return builder.build(); + }) + ); + } + + @Builder + @RegisterForReflection + public static record ClubArrayData(String name, int nb_insc, int nb_match, int match_w, float ratioVictoire, + float ratioPoint, int pointMake, int pointTake, List combs) { + @Builder + @RegisterForReflection + public static record CombData(String cat, String name, int w, int l, float ratioVictoire, + float ratioPoint, int pointMake, int pointTake) { + } + } + + private Uni hasAccess(String uuid, SecurityCtx securityCtx) { + return registerRepository.find("membre.userId = ?1 AND competition.uuid = ?2", securityCtx.getSubject(), uuid) + .firstResult() + .invoke(Unchecked.consumer(o -> { + if (o == null) + throw new DForbiddenException("Access denied"); + })); + } + + private Uni hasAccess(Long compId, SecurityCtx securityCtx) { + return registerRepository.find("membre.userId = ?1 AND competition.id = ?2", securityCtx.getSubject(), compId) + .firstResult() + .invoke(Unchecked.consumer(o -> { + if (o == null) + throw new DForbiddenException("Access denied"); + })); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationRequestEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationRequestEndpoints.java index 16ddb09..fdaf2ea 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationRequestEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationRequestEndpoints.java @@ -186,6 +186,6 @@ public class AffiliationRequestEndpoints { public Uni getStatus( @Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) throws URISyntaxException { return Utils.getMediaFile(id, media, "aff_request/status", "affiliation_request_" + id, - Uni.createFrom().nullItem()); + Uni.createFrom().nullItem(), false); } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java index 7048e4f..a329192 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java @@ -335,7 +335,7 @@ public class ClubEndpoints { @Parameter(description = "Identifiant long (clubId) de club") @PathParam("clubId") String clubId) { return clubService.getByClubId(clubId).chain(Unchecked.function(clubModel -> { try { - return Utils.getMediaFile((clubModel != null) ? clubModel.getId() : -1, media, "ppClub", + return Utils.getMediaFileNoDefault((clubModel != null) ? clubModel.getId() : -1, media, "ppClub", Uni.createFrom().nullItem()); } catch (URISyntaxException e) { throw new InternalError(); @@ -358,7 +358,7 @@ public class ClubEndpoints { return clubService.getById(id).onItem().invoke(checkPerm).chain(Unchecked.function(clubModel -> { try { return Utils.getMediaFile(clubModel.getId(), media, "clubStatus", - "statue-" + clubModel.getName(), Uni.createFrom().nullItem()); + "statue-" + clubModel.getName(), Uni.createFrom().nullItem(), false); } catch (URISyntaxException e) { throw new InternalError(); } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/CompetitionAdminEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/CompetitionAdminEndpoints.java index 51c0d9f..9b9fabf 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/CompetitionAdminEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/CompetitionAdminEndpoints.java @@ -47,4 +47,12 @@ public class CompetitionAdminEndpoints { public Uni> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) { return service.getAllSystemAdmin(securityCtx, system); } + + @GET + @Path("all/{system}/table") + @Authenticated + @Produces(MediaType.APPLICATION_JSON) + public Uni> getAllSystemTable(@PathParam("system") CompetitionSystem system) { + return service.getAllSystemTable(securityCtx, system); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/ResultEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/ResultEndpoints.java new file mode 100644 index 0000000..4016887 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/ResultEndpoints.java @@ -0,0 +1,48 @@ +package fr.titionfire.ffsaf.rest; + +import fr.titionfire.ffsaf.domain.service.ResultService; +import fr.titionfire.ffsaf.rest.data.ResultCategoryData; +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 java.util.List; + +@Authenticated +@Path("api/result") +public class ResultEndpoints { + + @Inject + ResultService resultService; + + @Inject + SecurityCtx securityCtx; + + @GET + @Path("list") + public Uni> getList() { + return resultService.getList(securityCtx); + } + + @GET + @Path("{uuid}") + public Uni> getCategory(@PathParam("uuid") String uuid) { + return resultService.getCategory(uuid, securityCtx); + } + + @GET + @Path("{uuid}/club") + public Uni getClub(@PathParam("uuid") String uuid) { + return resultService.getClubArray(uuid, securityCtx); + } + + @GET + @Path("{uuid}/comb") + public Uni getComb(@PathParam("uuid") String uuid) { + return resultService.getAllCombArray(uuid, securityCtx); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java index d6b07d1..8b33c2c 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/CompetitionData.java @@ -1,5 +1,6 @@ package fr.titionfire.ffsaf.rest.data; +import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; import fr.titionfire.ffsaf.data.model.CompetitionModel; import fr.titionfire.ffsaf.data.model.RegisterModel; import fr.titionfire.ffsaf.utils.Categorie; @@ -11,6 +12,7 @@ import lombok.Data; import java.util.Date; import java.util.List; +import java.util.stream.Stream; @Data @AllArgsConstructor @@ -57,9 +59,9 @@ public class CompetitionData { 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){ + if (model.getRegisterMode() == RegisterMode.HELLOASSO) { out.setData1(model.getData1()); out.setData2(model.getData2()); } @@ -67,10 +69,15 @@ public class CompetitionData { return out; } - public CompetitionData addInsc(List insc) { - this.registers = insc.stream() - .map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight(), - i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId())).toList(); + public CompetitionData addInsc(List insc, List guests) { + this.registers = Stream.concat( + insc.stream() + .map(i -> new SimpleRegister(i.getMembre().getId(), i.getOverCategory(), i.getWeight(), + i.getCategorie(), (i.getClub() == null) ? null : i.getClub().getId(), + (i.getClub() == null) ? null : i.getClub().getName())), + guests.stream() + .map(i -> new SimpleRegister(i.getId() * -1, 0, i.getWeight(), + i.getCategorie(), null, i.getClub()))).toList(); return this; } @@ -83,5 +90,6 @@ public class CompetitionData { Integer weight; Categorie categorie; Long club; + String club_str; } } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/MatchData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/MatchData.java index 143d0ad..7b13012 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/MatchData.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/MatchData.java @@ -28,8 +28,10 @@ public class MatchData { return null; return new MatchData(model.getSystemId(), - (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.getC1_id() == null) ? null : model.getC1_id().getId(), + (model.getC1_guest() == null) ? null : model.getC1_guest().getName(), + (model.getC2_id() == null) ? null : model.getC2_id().getId(), + (model.getC2_guest() == null) ? null : model.getC2_guest().getName(), model.getCategory().getId(), model.getCategory_ord(), model.isEnd(), model.getPoule(), model.getScores()); } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java index 11fa9aa..3b482f1 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/RegisterRequestData.java @@ -1,5 +1,7 @@ package fr.titionfire.ffsaf.rest.data; +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.AllArgsConstructor; import lombok.Data; @@ -17,4 +19,11 @@ public class RegisterRequestData { private Integer weight; private int overCategory; private boolean lockEdit = false; + + // for guest registration only + private Long id = null; + private Categorie categorie = Categorie.CADET; + private Genre genre = Genre.NA; + private String club = null; + private String country = null; } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/ResultCategoryData.java b/src/main/java/fr/titionfire/ffsaf/rest/data/ResultCategoryData.java new file mode 100644 index 0000000..2054e52 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/ResultCategoryData.java @@ -0,0 +1,59 @@ +package fr.titionfire.ffsaf.rest.data; + +import fr.titionfire.ffsaf.data.model.MatchModel; +import fr.titionfire.ffsaf.utils.ScoreEmbeddable; +import fr.titionfire.ffsaf.utils.TreeNode; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@Data +@NoArgsConstructor +@RegisterForReflection +public class ResultCategoryData { + int type; + String name; + HashMap> matchs = new HashMap<>(); + HashMap> rankArray = new HashMap<>(); + ArrayList> trees; + + @Data + @AllArgsConstructor + @RegisterForReflection + public static class RankArray { + int rank; + String name; + int win; + int pointMake; + int pointTake; + float pointRate; + } + + @RegisterForReflection + public record PouleArrayData(String red, boolean red_w, List score, boolean blue_w, String blue, boolean end) { + public static PouleArrayData fromModel(MatchModel matchModel) { + return new PouleArrayData( + matchModel.getC1Name(), + matchModel.isEnd() && matchModel.win() > 0, + matchModel.isEnd() ? + matchModel.getScores().stream().map(s -> new Integer[]{s.getS1(), s.getS2()}).toList() + : new ArrayList<>(), + matchModel.isEnd() && matchModel.win() < 0, + matchModel.getC2Name(), + matchModel.isEnd()); + } + } + + @RegisterForReflection + public static record TreeData(long id, String c1FullName, String c2FullName, List scores, + boolean end) { + public static TreeData from(MatchModel match) { + return new TreeData(match.getId(), match.getC1Name(), match.getC2Name(), match.getScores(), match.isEnd()); + } + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java index 1125357..148c950 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/data/SimpleRegisterComb.java @@ -1,9 +1,12 @@ package fr.titionfire.ffsaf.rest.data; +import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; import fr.titionfire.ffsaf.data.model.LicenceModel; import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.RegisterModel; import fr.titionfire.ffsaf.net2.data.SimpleClubModel; +import fr.titionfire.ffsaf.utils.Categorie; +import fr.titionfire.ffsaf.utils.Genre; import fr.titionfire.ffsaf.utils.Utils; import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.AllArgsConstructor; @@ -18,8 +21,9 @@ public class SimpleRegisterComb { private long id; private String fname; private String lname; - private String genre; - private String categorie; + private Genre genre; + private String country; + private Categorie categorie; private SimpleClubModel club; private Integer licence; private Integer weight; @@ -30,11 +34,18 @@ public class SimpleRegisterComb { public static SimpleRegisterComb fromModel(RegisterModel register, List licences) { MembreModel membreModel = register.getMembre(); return new SimpleRegisterComb(membreModel.getId(), membreModel.getFname(), membreModel.getLname(), - membreModel.getGenre().name(), - (register.getCategorie() == null) ? "Catégorie inconnue" : register.getCategorie().getName(), + membreModel.getGenre(), membreModel.getCountry(), + (register.getCategorie() == null) ? null : register.getCategorie(), SimpleClubModel.fromModel(register.getClub()), membreModel.getLicence(), register.getWeight(), register.getOverCategory(), licences.stream().anyMatch(l -> l.isValidate() && l.getSaison() == Utils.getSaison()), register.isLockEdit()); } + + public static SimpleRegisterComb fromModel(CompetitionGuestModel guest) { + return new SimpleRegisterComb(guest.getId() * -1, guest.getFname(), guest.getLname(), + guest.getGenre(), guest.getCountry(), guest.getCategorie(), + new SimpleClubModel(null, guest.getClub(), "fr", null), + null, guest.getWeight(), 0, false, false); + } } diff --git a/src/main/java/fr/titionfire/ffsaf/utils/CompetitionSystem.java b/src/main/java/fr/titionfire/ffsaf/utils/CompetitionSystem.java index 32cb79a..a183d81 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/CompetitionSystem.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/CompetitionSystem.java @@ -1,5 +1,5 @@ package fr.titionfire.ffsaf.utils; public enum CompetitionSystem { - SAFCA, NONE + SAFCA, INTERNAL } diff --git a/src/main/java/fr/titionfire/ffsaf/utils/TreeNode.java b/src/main/java/fr/titionfire/ffsaf/utils/TreeNode.java new file mode 100644 index 0000000..c175692 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/utils/TreeNode.java @@ -0,0 +1,35 @@ +package fr.titionfire.ffsaf.utils; + +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 TreeNode { + private T data; + private TreeNode left; + private TreeNode right; + + public TreeNode(T data) { + this(data, null, null); + } + + public int death() { + int dg = 0; + int dd = 0; + + if (this.right != null) + dg = this.right.death(); + + if (this.left != null) + dg = this.left.death(); + + return 1 + Math.max(dg, dd); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/utils/Utils.java b/src/main/java/fr/titionfire/ffsaf/utils/Utils.java index b4a74a9..550c6e8 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/Utils.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/Utils.java @@ -150,11 +150,16 @@ public class Utils { public static Uni getMediaFile(long id, String media, String dirname, Uni uniBase) throws URISyntaxException { - return getMediaFile(id, media, dirname, null, uniBase); + return getMediaFile(id, media, dirname, null, uniBase, true); + } + + public static Uni getMediaFileNoDefault(long id, String media, String dirname, + Uni uniBase) throws URISyntaxException { + return getMediaFile(id, media, dirname, null, uniBase, false); } public static Uni getMediaFile(long id, String media, String dirname, String out_filename, - Uni uniBase) throws URISyntaxException { + Uni uniBase, boolean default_) throws URISyntaxException { Future> future = CompletableFuture.supplyAsync(() -> { FilenameFilter filter = (directory, filename) -> filename.startsWith(id + "."); File[] files = new File(media, dirname).listFiles(filter); @@ -182,19 +187,25 @@ public class Utils { return uniBase.chain(__ -> Uni.createFrom().future(future) .chain(filePair -> { if (filePair == null) { - return Uni.createFrom().future(future2).map(data -> { - if (data == null) - return Response.noContent().build(); + if (default_) { + return Uni.createFrom().future(future2).map(data -> { + if (data == null) + return Response.noContent().build(); - String mimeType = "image/apng"; - Response.ResponseBuilder resp = Response.ok(data); - resp.type(MediaType.APPLICATION_OCTET_STREAM); - resp.header(HttpHeaders.CONTENT_LENGTH, data.length); - resp.header(HttpHeaders.CONTENT_TYPE, mimeType); - resp.header(HttpHeaders.CONTENT_DISPOSITION, - "inline; " + ((out_filename == null) ? "" : "filename=\"" + out_filename + "\"")); - return resp.build(); - }); + String mimeType = "image/apng"; + Response.ResponseBuilder resp = Response.ok(data); + resp.type(MediaType.APPLICATION_OCTET_STREAM); + resp.header(HttpHeaders.CONTENT_LENGTH, data.length); + resp.header(HttpHeaders.CONTENT_TYPE, mimeType); + resp.header(HttpHeaders.CONTENT_DISPOSITION, + "inline; " + ((out_filename == null) ? "" : "filename=\"" + out_filename + "\"")); + return resp.build(); + }); + } else { + Response.ResponseBuilder resp = Response.status(404); + resp.header(HttpHeaders.CACHE_CONTROL, "max-age=600"); + return Uni.createFrom().item(resp.build()); + } } else { return Uni.createFrom().item(() -> { String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName()); diff --git a/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java b/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java new file mode 100644 index 0000000..c2256d2 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/CompetitionWS.java @@ -0,0 +1,207 @@ +package fr.titionfire.ffsaf.ws; + +import com.fasterxml.jackson.core.JsonProcessingException; +import fr.titionfire.ffsaf.data.repository.CompetitionRepository; +import fr.titionfire.ffsaf.domain.service.CompetPermService; +import fr.titionfire.ffsaf.net2.MessageType; +import fr.titionfire.ffsaf.utils.SecurityCtx; +import fr.titionfire.ffsaf.ws.data.WelcomeInfo; +import fr.titionfire.ffsaf.ws.recv.*; +import fr.titionfire.ffsaf.ws.send.JsonUni; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.security.Authenticated; +import io.quarkus.websockets.next.*; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.unchecked.Unchecked; +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import jakarta.ws.rs.ForbiddenException; +import org.jboss.logging.Logger; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER; + +@Authenticated +@WebSocket(path = "api/ws/competition/{uuid}") +public class CompetitionWS { + private static final Logger LOGGER = Logger.getLogger(CompetitionWS.class); + + private static final HashMap>> waitingResponse = new HashMap<>(); + + @Inject + RMatch rMatch; + + @Inject + RCategorie rCategorie; + + @Inject + RRegister rRegister; + + @Inject + RCardboard rCardboard; + + @Inject + SecurityCtx securityCtx; + + @Inject + CompetPermService competPermService; + + @Inject + CompetitionRepository competitionRepository; + + HashMap wsMethods = new HashMap<>(); + + public void getWSReceiverMethods(Class clazz, Object object) { + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(WSReceiver.class)) { + if (method.getParameterCount() <= 1) { + LOGGER.warnf("@WSReceiver has no parameters for method %s", method.getName()); + continue; + } + if (!method.getReturnType().equals(Uni.class)) { + LOGGER.warnf("@WSReceiver has returned unexpected type %s", method.getReturnType()); + continue; + } + // method.setAccessible(true); + wsMethods.put(method, object); + } + } + } + + @PostConstruct + void init() { + getWSReceiverMethods(RMatch.class, rMatch); + getWSReceiverMethods(RCategorie.class, rCategorie); + getWSReceiverMethods(RRegister.class, rRegister); + getWSReceiverMethods(RCardboard.class, rCardboard); + } + + @OnOpen + @WithSession + Uni open(WebSocketConnection connection) { + LOGGER.infof("Opening CompetitionWS for %s", connection.pathParam("uuid")); + LOGGER.debugf("Active connections: %d", connection.getOpenConnections().size()); + + return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult() + .invoke(Unchecked.consumer(cm -> { + if (cm == null) + throw new ForbiddenException(); + })) + .call(cm -> competPermService.hasEditPerm(securityCtx, cm).map(__ -> PermLevel.ADMIN) + .onFailure() + .recoverWithUni(competPermService.hasTablePerm(securityCtx, cm).map(__ -> PermLevel.TABLE)) + .onFailure() + .recoverWithUni(competPermService.hasViewPerm(securityCtx, cm).map(__ -> PermLevel.VIEW)) + .invoke(prem -> connection.userData().put(UserData.TypedKey.forString("prem"), prem.toString())) + .invoke(prem -> LOGGER.infof("Connection permission: %s", prem)) + .onFailure().transform(t -> new ForbiddenException())) + .invoke(__ -> { + connection.userData().put(UserData.TypedKey.forString("subject"), securityCtx.getSubject()); + waitingResponse.put(connection, new HashMap<>()); + }) + .map(cm -> { + WelcomeInfo welcomeInfo = new WelcomeInfo(); + + welcomeInfo.setName(cm.getName()); + welcomeInfo.setPerm(connection.userData().get(UserData.TypedKey.forString("prem"))); + + return new MessageOut(UUID.randomUUID(), "welcomeInfo", MessageType.NOTIFY, welcomeInfo); + }); + } + + @OnClose + void close(WebSocketConnection connection) { + LOGGER.infof("Closing CompetitionWS for %s ", connection.pathParam("uuid")); + LOGGER.debugf("Active connections: %d", connection.getOpenConnections().size()); + + waitingResponse.remove(connection); + } + + private MessageOut makeReply(MessageIn message, Object data) { + return new MessageOut(message.uuid(), message.code(), MessageType.REPLY, data); + } + + private MessageOut makeError(MessageIn message, Object data) { + return new MessageOut(message.uuid(), message.code(), MessageType.ERROR, data); + } + + @OnTextMessage + Multi processAsync(WebSocketConnection connection, MessageIn message) { + System.out.println(message); + if (message.type() == MessageType.REPLY || message.type() == MessageType.ERROR) { + try { + JsonUni jsonUni = waitingResponse.get(connection).get(message.uuid()); + if (jsonUni == null) { + LOGGER.debugf("No JsonUni found for %s", message.uuid()); + if (message.type() == MessageType.ERROR) + LOGGER.errorf("request %s make error %s", message.uuid(), message.data()); + return null; + } + waitingResponse.get(connection).remove(message.uuid()); + + if (message.type() == MessageType.ERROR) + return jsonUni.castAndError(message.data()).onFailure() + .invoke(t -> LOGGER.error(t.getMessage(), t)) + .replaceWith((MessageOut) null).toMulti().filter(__ -> false); + + return jsonUni.castAndChain(message.data()).onFailure() + .invoke(t -> LOGGER.error(t.getMessage(), t)) + .replaceWith((MessageOut) null).toMulti().filter(__ -> false); + } catch (JsonProcessingException e) { + LOGGER.warn(e.getMessage(), e); + } + } + + try { + for (Map.Entry entry : wsMethods.entrySet()) { + Method method = entry.getKey(); + WSReceiver wsReceiver = method.getAnnotation(WSReceiver.class); + PermLevel perm = PermLevel.valueOf(connection.userData().get(UserData.TypedKey.forString("prem"))); + if (wsReceiver.code().equalsIgnoreCase(message.code())) { + if (wsReceiver.permission().ordinal() > perm.ordinal()) + return Uni.createFrom().item(makeError(message, "Permission denied")).toMulti(); + return ((Uni) method.invoke(entry.getValue(), connection, + MAPPER.treeToValue(message.data(), method.getParameterTypes()[1]))) + .map(o -> makeReply(message, o)) + .onFailure() + .recoverWithItem(t -> { + LOGGER.error(t.getMessage(), t); + return makeError(message, t.getMessage()); + }).toMulti() + .filter(__ -> message.type() == MessageType.REQUEST); + } + } + return Uni.createFrom().item(makeError(message, "No receiver method found")).toMulti(); + } catch (IllegalAccessException | InvocationTargetException | JsonProcessingException e) { + LOGGER.warn(e.getMessage(), e); + return Uni.createFrom().item(makeError(message, e.getMessage())).toMulti(); + } + + // return Uni.createFrom().item(new Message<>(message.uuid(), message.code(), MessageType.REPLY, "ko")); + } + + public static Uni sendNotifyToOtherEditor(WebSocketConnection connection, String code, Object data) { + String uuid = connection.pathParam("uuid"); + + List> queue = new ArrayList<>(); + queue.add(Uni.createFrom().voidItem()); // For avoid empty queue + + connection.getOpenConnections().forEach(c -> { + if (uuid.equals(c.pathParam("uuid"))) { + queue.add(c.sendText(new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data))); + } + }); + + return Uni.join().all(queue).andCollectFailures().onFailure().recoverWithNull().replaceWithVoid(); + } + + @OnError + Uni error(WebSocketConnection connection, ForbiddenException t) { + return connection.close(CloseReason.INTERNAL_SERVER_ERROR); + //return "forbidden: " + securityCtx.getSubject(); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/MessageIn.java b/src/main/java/fr/titionfire/ffsaf/ws/MessageIn.java new file mode 100644 index 0000000..99532ec --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/MessageIn.java @@ -0,0 +1,12 @@ +package fr.titionfire.ffsaf.ws; + +import com.fasterxml.jackson.databind.JsonNode; +import fr.titionfire.ffsaf.net2.MessageType; +import io.quarkus.runtime.annotations.RegisterForReflection; + +import java.util.UUID; + +@RegisterForReflection +public record MessageIn (UUID uuid, String code, MessageType type, JsonNode data){ + +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/MessageOut.java b/src/main/java/fr/titionfire/ffsaf/ws/MessageOut.java new file mode 100644 index 0000000..4db5c3e --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/MessageOut.java @@ -0,0 +1,11 @@ +package fr.titionfire.ffsaf.ws; + +import fr.titionfire.ffsaf.net2.MessageType; +import io.quarkus.runtime.annotations.RegisterForReflection; + +import java.util.UUID; + +@RegisterForReflection +public record MessageOut(UUID uuid, String code, MessageType type, Object data){ + +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/PermLevel.java b/src/main/java/fr/titionfire/ffsaf/ws/PermLevel.java new file mode 100644 index 0000000..3e7a2e6 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/PermLevel.java @@ -0,0 +1,8 @@ +package fr.titionfire.ffsaf.ws; + +public enum PermLevel { + NONE, + VIEW, + TABLE, + ADMIN, +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/data/WelcomeInfo.java b/src/main/java/fr/titionfire/ffsaf/ws/data/WelcomeInfo.java new file mode 100644 index 0000000..eebe679 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/data/WelcomeInfo.java @@ -0,0 +1,11 @@ +package fr.titionfire.ffsaf.ws.data; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Data; + +@Data +@RegisterForReflection +public class WelcomeInfo { + private String name; + private String perm; +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RCardboard.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RCardboard.java new file mode 100644 index 0000000..59b6360 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RCardboard.java @@ -0,0 +1,128 @@ +package fr.titionfire.ffsaf.ws.recv; + +import fr.titionfire.ffsaf.data.model.CardboardModel; +import fr.titionfire.ffsaf.data.model.MatchModel; +import fr.titionfire.ffsaf.data.repository.CardboardRepository; +import fr.titionfire.ffsaf.data.repository.MatchRepository; +import fr.titionfire.ffsaf.domain.entity.CardboardEntity; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; +import fr.titionfire.ffsaf.rest.exception.DNotFoundException; +import fr.titionfire.ffsaf.ws.PermLevel; +import fr.titionfire.ffsaf.ws.send.SSCardboard; +import io.quarkus.hibernate.reactive.panache.Panache; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.unchecked.Unchecked; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.Data; + +import java.util.Objects; + +@WithSession +@ApplicationScoped +@RegisterForReflection +public class RCardboard { + + @Inject + MatchRepository matchRepository; + + @Inject + CardboardRepository cardboardRepository; + + private Uni getById(long id, WebSocketConnection connection) { + return matchRepository.findById(id) + .invoke(Unchecked.consumer(o -> { + if (o == null) + throw new DNotFoundException("Matche non trouver"); + if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid"))) + throw new DForbiddenException("Permission denied"); + })); + } + + @WSReceiver(code = "sendCardboardChange", permission = PermLevel.TABLE) + public Uni sendCardboardChange(WebSocketConnection connection, SendCardboard card) { + return getById(card.matchId, connection) + .chain(matchModel -> cardboardRepository.find("(comb.id = ?1 OR guestComb.id = ?2) AND match.id = ?3", + card.combId, card.combId * -1, card.matchId).firstResult() + .chain(model -> { + if (model != null) { + model.setRed(model.getRed() + card.red); + model.setYellow(model.getYellow() + card.yellow); + return Panache.withTransaction(() -> cardboardRepository.persist(model)); + } + CardboardModel cardboardModel = new CardboardModel(); + + cardboardModel.setCompet(matchModel.getCategory().getCompet()); + cardboardModel.setMatch(matchModel); + cardboardModel.setRed(card.red); + cardboardModel.setYellow(card.yellow); + cardboardModel.setComb(null); + cardboardModel.setGuestComb(null); + + if (card.combId >= 0) { + if (matchModel.getC1_id() != null && matchModel.getC1_id().getId() == card.combId) + cardboardModel.setComb(matchModel.getC1_id()); + if (matchModel.getC2_id() != null && matchModel.getC2_id().getId() == card.combId) + cardboardModel.setComb(matchModel.getC2_id()); + } else { + if (matchModel.getC1_guest() != null && matchModel.getC1_guest() + .getId() == card.combId * -1) + cardboardModel.setGuestComb(matchModel.getC1_guest()); + if (matchModel.getC2_guest() != null && matchModel.getC2_guest() + .getId() == card.combId * -1) + cardboardModel.setGuestComb(matchModel.getC2_guest()); + } + + if (cardboardModel.getComb() == null && cardboardModel.getGuestComb() == null) + return Uni.createFrom().nullItem(); + return Panache.withTransaction(() -> cardboardRepository.persist(cardboardModel)); + })) + .call(model -> SSCardboard.sendCardboard(connection, CardboardEntity.fromModel(model))) + .replaceWithVoid(); + } + + @WSReceiver(code = "getCardboardWithoutThis", permission = PermLevel.VIEW) + public Uni getCardboardWithoutThis(WebSocketConnection connection, Long matchId) { + return getById(matchId, connection) + .chain(matchModel -> cardboardRepository.list("compet = ?1 AND match != ?2", matchModel.getCategory().getCompet(), matchModel) + .map(models -> { + CardboardAllMatch out = new CardboardAllMatch(); + + models.stream().filter(c -> (matchModel.getC1_id() != null + && Objects.equals(c.getComb(), matchModel.getC1_id())) + || (matchModel.getC1_guest() != null + && Objects.equals(c.getGuestComb(), matchModel.getC1_guest()))) + .forEach(c -> { + out.c1_yellow += c.getYellow(); + out.c1_red += c.getRed(); + }); + + models.stream().filter(c -> (matchModel.getC2_id() != null + && Objects.equals(c.getComb(), matchModel.getC2_id())) + || (matchModel.getC2_guest() != null + && Objects.equals(c.getGuestComb(), matchModel.getC2_guest()))) + .forEach(c -> { + out.c2_yellow += c.getYellow(); + out.c2_red += c.getRed(); + }); + + return out; + })); + } + + @RegisterForReflection + public record SendCardboard(long matchId, long combId, int yellow, int red) { + } + + @Data + @RegisterForReflection + public static class CardboardAllMatch { + int c1_yellow = 0; + int c1_red = 0; + int c2_yellow = 0; + int c2_red = 0; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RCategorie.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RCategorie.java new file mode 100644 index 0000000..816cbea --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RCategorie.java @@ -0,0 +1,229 @@ +package fr.titionfire.ffsaf.ws.recv; + +import fr.titionfire.ffsaf.data.model.CategoryModel; +import fr.titionfire.ffsaf.data.model.MatchModel; +import fr.titionfire.ffsaf.data.model.TreeModel; +import fr.titionfire.ffsaf.data.repository.CategoryRepository; +import fr.titionfire.ffsaf.data.repository.CompetitionRepository; +import fr.titionfire.ffsaf.data.repository.MatchRepository; +import fr.titionfire.ffsaf.data.repository.TreeRepository; +import fr.titionfire.ffsaf.domain.entity.MatchEntity; +import fr.titionfire.ffsaf.domain.entity.TreeEntity; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; +import fr.titionfire.ffsaf.rest.exception.DNotFoundException; +import fr.titionfire.ffsaf.utils.TreeNode; +import fr.titionfire.ffsaf.ws.PermLevel; +import fr.titionfire.ffsaf.ws.send.SSCategorie; +import io.quarkus.hibernate.reactive.panache.Panache; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.unchecked.Unchecked; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.Data; +import org.hibernate.reactive.mutiny.Mutiny; + +import java.util.ArrayList; +import java.util.List; + +@WithSession +@ApplicationScoped +@RegisterForReflection +public class RCategorie { + //private static final Logger LOGGER = Logger.getLogger(RCategorie.class); + + @Inject + CategoryRepository categoryRepository; + + @Inject + CompetitionRepository competitionRepository; + + @Inject + MatchRepository matchRepository; + + @Inject + TreeRepository treeRepository; + + private Uni getById(long id, WebSocketConnection connection) { + return categoryRepository.findById(id) + .invoke(Unchecked.consumer(o -> { + if (o == null) + throw new DNotFoundException("Catégorie non trouver"); + if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) + throw new DForbiddenException("Permission denied"); + })); + } + + @WSReceiver(code = "getAllCategory", permission = PermLevel.VIEW) + public Uni> getAllCategory(WebSocketConnection connection, Object o) { + return categoryRepository.list("compet.uuid", connection.pathParam("uuid")) + .map(category -> category.stream().map(JustCategorie::from).toList()); + } + + @WSReceiver(code = "getFullCategory", permission = PermLevel.VIEW) + public Uni getFullCategory(WebSocketConnection connection, Long id) { + FullCategory fullCategory = new FullCategory(); + + return getById(id, connection) + .invoke(cat -> { + fullCategory.setId(cat.getId()); + fullCategory.setName(cat.getName()); + fullCategory.setLiceName(cat.getLiceName()); + fullCategory.setType(cat.getType()); + }) + .call(cat -> Mutiny.fetch(cat.getMatchs()) + .map(matchModels -> matchModels.stream().filter(o -> o.getCategory_ord() >= 0) + .map(MatchEntity::fromModel).toList()) + .invoke(fullCategory::setMatches)) + .call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId()) + .map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList()) + .invoke(fullCategory::setTrees)) + .map(__ -> fullCategory); + } + + @WSReceiver(code = "createCategory", permission = PermLevel.ADMIN) + public Uni createCategory(WebSocketConnection connection, JustCategorie categorie) { + return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult() + .chain(cm -> { + CategoryModel categoryModel = new CategoryModel(); + categoryModel.setName(categorie.name); + categoryModel.setCompet(cm); + categoryModel.setMatchs(new ArrayList<>()); + categoryModel.setTree(new ArrayList<>()); + categoryModel.setType(categorie.type); + categoryModel.setLiceName(categorie.liceName); + + return categoryRepository.create(categoryModel); + }) + .call(cat -> SSCategorie.sendAddCategory(connection, cat)) + .map(CategoryModel::getId); + } + + @WSReceiver(code = "updateCategory", permission = PermLevel.ADMIN) + public Uni updateCategory(WebSocketConnection connection, JustCategorie categorie) { + return getById(categorie.id, connection) + .chain(cat -> { + cat.setName(categorie.name); + cat.setLiceName(categorie.liceName); + cat.setType(categorie.type); + return Panache.withTransaction(() -> categoryRepository.persist(cat)); + }) + .call(cat -> { + Uni uni = Uni.createFrom().nullItem(); + if ((categorie.type() & 1) == 0) + uni = uni.chain(__ -> matchRepository.delete("category = ?1 AND category_ord >= 0", cat)); + if ((categorie.type() & 2) == 0) { + uni = uni.chain(__ -> treeRepository.delete("category = ?1", cat.getId())) + .chain(__ -> matchRepository.delete("category = ?1 AND category_ord = -42", cat)); + } + return uni; + }) + .call(cat -> SSCategorie.sendCategory(connection, cat)) + .replaceWithVoid(); + } + + private Uni getOrCreateTreeNode(Long id, Long categorieId, int level) { + if (id == null) { + TreeModel treeModel = new TreeModel(); + treeModel.setCategory(categorieId); + treeModel.setLevel(level); + + return Panache.withTransaction(() -> treeRepository.persistAndFlush(treeModel)); + } else { + return Panache.withTransaction( + () -> treeRepository.find("match.id = ?1", id).firstResult() + .invoke(t -> t.setLevel(level)) + .chain(t -> treeRepository.persist(t))); + } + } + + private Uni updateNode(TreeNode current, TreeModel currentTreeModel, CategoryModel category) { + return Uni.createFrom().item(currentTreeModel) + .chain(t -> { + if (current.getData() != null) + return Uni.createFrom().item(t); + + MatchModel matchModel = new MatchModel(); + matchModel.setCategory(category); + matchModel.setCategory_ord(-42); + matchModel.setEnd(false); + + return matchRepository.create(matchModel).onItem() + .invoke(t::setMatch) + .chain(__ -> treeRepository.persistAndFlush(t)); + }) + .call(t -> { + if (current.getLeft() == null) + return Uni.createFrom().item(t); + + return getOrCreateTreeNode(current.getLeft().getData(), category.getId(), 0) + .invoke(t::setLeft) + .call(treeModel -> updateNode(current.getLeft(), treeModel, category)); + }) + .call(t -> { + if (current.getRight() == null) + return Uni.createFrom().item(t); + + return getOrCreateTreeNode(current.getRight().getData(), category.getId(), 0) + .invoke(t::setRight) + .call(treeModel -> updateNode(current.getRight(), treeModel, category)); + }) + .chain(t -> treeRepository.persist(t)) + .replaceWithVoid(); + } + + + @WSReceiver(code = "updateTrees", permission = PermLevel.ADMIN) + public Uni updateTrees(WebSocketConnection connection, TreeUpdate data) { + return getById(data.categoryId, connection) + .call(cat -> treeRepository.update("level = -1, left = NULL, right = NULL WHERE category = ?1", + cat.getId())) + .call(cat -> { + Uni uni = Uni.createFrom().voidItem(); + + for (int i = 0; i < data.trees().size(); i++) { + TreeNode current = data.trees().get(i); + + int finalI = i; + uni = uni.chain(() -> getOrCreateTreeNode(current.getData(), cat.getId(), finalI + 1) + .call(treeModel -> updateNode(current, treeModel, cat))); + + } + Uni finalUni = uni; + return Panache.withTransaction(() -> finalUni); + }) + .call(cat -> treeRepository.list("category = ?1 AND level = -1", cat.getId()) + .map(l -> l.stream().map(o -> o.getMatch().getId()).toList()) + .call(__ -> treeRepository.delete("category = ?1 AND level = -1", cat.getId())) + .call(ids -> matchRepository.delete("id IN ?1", ids))) + .call(__ -> treeRepository.flush()) + .call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId()) + .map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList()) + .chain(trees -> SSCategorie.sendTreeCategory(connection, trees))) + .replaceWithVoid(); + } + + @RegisterForReflection + public record JustCategorie(long id, String name, int type, String liceName) { + public static JustCategorie from(CategoryModel m) { + return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName()); + } + } + + @RegisterForReflection + public record TreeUpdate(long categoryId, List> trees) { + } + + @Data + @RegisterForReflection + public static class FullCategory { + long id; + String name; + int type; + String liceName; + List trees = null; + List matches; + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RMatch.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RMatch.java new file mode 100644 index 0000000..c184f3d --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RMatch.java @@ -0,0 +1,346 @@ +package fr.titionfire.ffsaf.ws.recv; + +import fr.titionfire.ffsaf.data.model.*; +import fr.titionfire.ffsaf.data.repository.*; +import fr.titionfire.ffsaf.domain.entity.MatchEntity; +import fr.titionfire.ffsaf.domain.entity.TreeEntity; +import fr.titionfire.ffsaf.rest.exception.DForbiddenException; +import fr.titionfire.ffsaf.rest.exception.DNotFoundException; +import fr.titionfire.ffsaf.utils.ScoreEmbeddable; +import fr.titionfire.ffsaf.ws.PermLevel; +import fr.titionfire.ffsaf.ws.send.SSMatch; +import io.quarkus.hibernate.reactive.panache.Panache; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.panache.common.Sort; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.unchecked.Unchecked; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.jboss.logging.Logger; + +import java.util.*; +import java.util.stream.Stream; + +@WithSession +@ApplicationScoped +@RegisterForReflection +public class RMatch { + private static final Logger LOGGER = Logger.getLogger(RMatch.class); + + @Inject + MatchRepository matchRepository; + + @Inject + CombRepository combRepository; + + @Inject + CategoryRepository categoryRepository; + + @Inject + TreeRepository treeRepository; + + @Inject + CompetitionGuestRepository competitionGuestRepository; + + private Uni getById(long id, WebSocketConnection connection) { + return matchRepository.findById(id) + .invoke(Unchecked.consumer(o -> { + if (o == null) + throw new DNotFoundException("Matche non trouver"); + if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid"))) + throw new DForbiddenException("Permission denied"); + })); + } + + private Uni creatMatch(CategoryModel categoryModel, AddMatch m) { + return Uni.createFrom().item(() -> { + MatchModel matchModel = new MatchModel(); + + matchModel.setCategory(categoryModel); + matchModel.setCategory_ord(m.categorie_ord); + matchModel.setEnd(false); + matchModel.setPoule(m.poule); + + return matchModel; + }) + .call(mm -> m.c1() >= 0 ? + combRepository.findById(m.c1()).invoke(mm::setC1_id) : + competitionGuestRepository.findById(m.c1() * -1).invoke(mm::setC1_guest)) + .call(mm -> m.c2() >= 0 ? + combRepository.findById(m.c2()).invoke(mm::setC2_id) : + competitionGuestRepository.findById(m.c2() * -1).invoke(mm::setC2_guest)); + } + + @WSReceiver(code = "addMatch", permission = PermLevel.ADMIN) + public Uni addMatch(WebSocketConnection connection, AddMatch m) { + return categoryRepository.findById(m.categorie) + .invoke(Unchecked.consumer(o -> { + if (o == null) + throw new DNotFoundException("Catégorie non trouver"); + if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) + throw new DForbiddenException("Permission denied"); + })) + .chain(categoryModel -> creatMatch(categoryModel, m)) + .chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm))) + .call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm))) + .replaceWithVoid(); + } + + @WSReceiver(code = "updateMatchComb", permission = PermLevel.ADMIN) + public Uni updateMatchComb(WebSocketConnection connection, MatchComb match) { + return getById(match.id(), connection) + .call(mm -> match.c1() != null ? + match.c1() >= 0 ? + combRepository.findById(match.c1()).invoke(model -> { + mm.setC1_id(model); + mm.setC1_guest(null); + }) : + competitionGuestRepository.findById(match.c1() * -1).invoke(model -> { + mm.setC1_id(null); + mm.setC1_guest(model); + + }) : + Uni.createFrom().nullItem().invoke(__ -> { + mm.setC1_id(null); + mm.setC1_guest(null); + })) + .call(mm -> match.c2() != null ? + match.c2() >= 0 ? + combRepository.findById(match.c2()).invoke(model -> { + mm.setC2_id(model); + mm.setC2_guest(null); + }) : + competitionGuestRepository.findById(match.c2() * -1).invoke(model -> { + mm.setC2_id(null); + mm.setC2_guest(model); + }) : + Uni.createFrom().nullItem().invoke(__ -> { + mm.setC2_id(null); + mm.setC2_guest(null); + })) + .chain(mm -> Panache.withTransaction(() -> matchRepository.persist(mm))) + .call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm))) + .replaceWithVoid(); + } + + @WSReceiver(code = "updateMatchOrder", permission = PermLevel.ADMIN) + public Uni updateMatchComb(WebSocketConnection connection, MatchOrder order) { + return getById(order.id(), connection) + .call(m -> matchRepository.update( + "category_ord = category_ord + 1 WHERE category_ord >= ?1 AND category_ord < ?2", order.pos, + m.getCategory_ord())) + .call(m -> matchRepository.update( + "category_ord = category_ord - 1 WHERE category_ord <= ?1 AND category_ord > ?2", order.pos, + m.getCategory_ord())) + .invoke(m -> m.setCategory_ord(order.pos)) + .call(m -> Panache.withTransaction(() -> matchRepository.persist(m))) + .call(mm -> SSMatch.sendMatchOrder(connection, order)) + .replaceWithVoid(); + } + + @WSReceiver(code = "updateMatchScore", permission = PermLevel.TABLE) + public Uni updateMatchScore(WebSocketConnection connection, MatchScore score) { + return getById(score.matchId(), connection) + .chain(matchModel -> { + int old_win = matchModel.win(); + Optional optional = matchModel.getScores().stream() + .filter(s -> s.getN_round() == score.n_round()).findAny(); + boolean b = score.s1() != -1000 || score.s2() != -1000; + if (optional.isPresent()) { + if (b) { + optional.get().setS1(score.s1()); + optional.get().setS2(score.s2()); + } else { + matchModel.getScores().remove(optional.get()); + } + } else if (b) { + matchModel.getScores().add(new ScoreEmbeddable(score.n_round(), score.s1(), score.s2())); + } + + return Panache.withTransaction(() -> matchRepository.persist(matchModel)) + .call(mm -> { + if (mm.isEnd() && mm.win() != old_win && mm.getCategory_ord() == -42) { + return updateEndAndTree(mm, new ArrayList<>()) + .call(l -> SSMatch.sendMatch(connection, l)); + } + return Uni.createFrom().nullItem(); + }); + }) + .call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm))) + .replaceWithVoid(); + } + + @WSReceiver(code = "updateMatchEnd", permission = PermLevel.TABLE) + public Uni updateMatchEnd(WebSocketConnection connection, MatchEnd matchEnd) { + List toSend = new ArrayList<>(); + + return getById(matchEnd.matchId(), connection) + .chain(mm -> { + if (mm.getCategory_ord() == -42 && mm.win() == 0) { // Tournois + mm.setDate(null); + mm.setEnd(false); + } else { + mm.setDate((matchEnd.end) ? new Date() : null); + mm.setEnd(matchEnd.end); + } + return Panache.withTransaction(() -> matchRepository.persist(mm)); + }) + .invoke(mm -> toSend.add(MatchEntity.fromModel(mm))) + .chain(mm -> updateEndAndTree(mm, toSend)) + .call(__ -> SSMatch.sendMatch(connection, toSend)) + .replaceWithVoid(); + } + + private Uni> updateEndAndTree(MatchModel mm, List toSend) { + return (mm.getCategory_ord() != -42) ? + Uni.createFrom().item(toSend) : + treeRepository.list("category = ?1 AND level != 0", Sort.ascending("level"), mm.getCategory().getId()) + .chain(treeModels -> { + List node = treeModels.stream().flatMap(t -> t.flat().stream()).toList(); + List trees = treeModels.stream().map(TreeEntity::fromModel).toList(); + for (int i = 0; i < trees.size() - 1; i++) { + TreeEntity.setAssociated(trees.get(i), trees.get(i + 1)); + } + + TreeEntity root = trees.stream() + .filter(t -> t.getMatchNode(mm.getId()) != null) + .findFirst() + .orElseThrow(); + + TreeEntity currentNode = root.getMatchNode(mm.getId()); + if (currentNode == null) { + LOGGER.error( + "currentNode is empty for " + mm.getId() + " in " + mm.getCategory().getId()); + return Uni.createFrom().voidItem(); + } + + TreeEntity parent = TreeEntity.getParent(root, currentNode); + if (parent == null) { + LOGGER.error("parent is empty for " + mm.getId() + " in " + mm.getCategory().getId()); + return Uni.createFrom().voidItem(); + } + + int w = mm.win(); + MembreModel toSetWin = null; + MembreModel toSetLose = null; + CompetitionGuestModel toSetWinGuest = null; + CompetitionGuestModel toSetLoseGuest = null; + + if (mm.isEnd() && w != 0) { + toSetWin = (w > 0) ? mm.getC1_id() : mm.getC2_id(); + toSetLose = (w > 0) ? mm.getC2_id() : mm.getC1_id(); + toSetWinGuest = (w > 0) ? mm.getC1_guest() : mm.getC2_guest(); + toSetLoseGuest = (w > 0) ? mm.getC2_guest() : mm.getC1_guest(); + } + + MatchModel modelWin = node.stream() + .filter(n -> Objects.equals(n.getId(), parent.getId())) + .findAny() + .map(TreeModel::getMatch) + .orElseThrow(); + + MatchModel modelLose = (parent.getAssociatedNode() == null) ? null : node.stream() + .filter(n -> Objects.equals(n.getId(), parent.getAssociatedNode().getId())) + .findAny() + .map(TreeModel::getMatch) + .orElseThrow(); + + if (currentNode.equals(parent.getLeft())) { + modelWin.setC1_id(toSetWin); + modelWin.setC1_guest(toSetWinGuest); + if (modelLose != null) { + modelLose.setC1_id(toSetLose); + modelLose.setC1_guest(toSetLoseGuest); + } + } else if (currentNode.equals(parent.getRight())) { + modelWin.setC2_id(toSetWin); + modelWin.setC2_guest(toSetWinGuest); + if (modelLose != null) { + modelLose.setC2_id(toSetLose); + modelLose.setC2_guest(toSetLoseGuest); + } + } + + return Panache.withTransaction(() -> matchRepository.persist(modelWin) + .invoke(mm2 -> toSend.add(MatchEntity.fromModel(mm2))) + .call(__ -> modelLose == null ? Uni.createFrom().nullItem() : + matchRepository.persist(modelLose) + .invoke(mm2 -> toSend.add(MatchEntity.fromModel(mm2))) + ) + .replaceWithVoid()); + }) + .map(__ -> toSend); + } + + @WSReceiver(code = "deleteMatch", permission = PermLevel.ADMIN) + public Uni deleteMatch(WebSocketConnection connection, Long idMatch) { + return getById(idMatch, connection) + .chain(matchModel -> Panache.withTransaction(() -> matchRepository.delete(matchModel))) + .call(__ -> SSMatch.sendDeleteMatch(connection, idMatch)) + .replaceWithVoid(); + } + + @WSReceiver(code = "recalculateMatch", permission = PermLevel.ADMIN) + public Uni recalculateMatch(WebSocketConnection connection, RecalculateMatch data) { + ArrayList matches = new ArrayList<>(); + return categoryRepository.findById(data.categorie) + .invoke(Unchecked.consumer(o -> { + if (o == null) + throw new DNotFoundException("Catégorie non trouver"); + if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) + throw new DForbiddenException("Permission denied"); + })) + .call(cm -> matchRepository.delete("id IN ?1 AND category = ?2", data.matchesToRemove, cm) + .call(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove))) + .call(cm -> matchRepository.list("id IN ?1 AND category = ?2", + Stream.concat(data.matchOrderToUpdate.keySet().stream(), + data.matchPouleToUpdate.keySet().stream()) + .distinct().toList(), cm) + .invoke(matchModels -> matchModels.forEach(model -> { + if (data.matchPouleToUpdate.containsKey(model.getId())) + model.setPoule(data.matchPouleToUpdate.get(model.getId())); + if (data.matchOrderToUpdate.containsKey(model.getId())) + model.setCategory_ord(data.matchOrderToUpdate.get(model.getId())); + })) + .call(mm -> Panache.withTransaction(() -> matchRepository.persist(mm))) + .invoke(mm -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList())) + ) + .chain(categoryModel -> { + Uni> uni = Uni.createFrom().item(new ArrayList<>()); + for (AddMatch match : data.newMatch) + uni = uni.call(l -> creatMatch(categoryModel, match).invoke(l::add)); + return uni; + } + ) + .chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)) + .invoke(__ -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList()))) + .call(__ -> SSMatch.sendMatch(connection, matches)) + .replaceWithVoid(); + } + + @RegisterForReflection + public record MatchComb(long id, Long c1, Long c2) { + } + + @RegisterForReflection + public record MatchScore(long matchId, int n_round, int s1, int s2) { + } + + @RegisterForReflection + public record MatchEnd(long matchId, boolean end) { + } + + @RegisterForReflection + public record MatchOrder(long id, long pos) { + } + + @RegisterForReflection + public record AddMatch(long categorie, long categorie_ord, char poule, long c1, long c2) { + } + + public record RecalculateMatch(long categorie, List newMatch, HashMap matchOrderToUpdate, + HashMap matchPouleToUpdate, List matchesToRemove) { + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/RRegister.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/RRegister.java new file mode 100644 index 0000000..c1f7f7c --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/RRegister.java @@ -0,0 +1,37 @@ +package fr.titionfire.ffsaf.ws.recv; + +import fr.titionfire.ffsaf.data.repository.CompetitionRepository; +import fr.titionfire.ffsaf.domain.entity.CombEntity; +import fr.titionfire.ffsaf.ws.PermLevel; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.hibernate.reactive.mutiny.Mutiny; + +import java.util.ArrayList; +import java.util.List; + +@WithSession +@ApplicationScoped +@RegisterForReflection +public class RRegister { + + @Inject + CompetitionRepository competitionRepository; + + @WSReceiver(code = "getRegister", permission = PermLevel.TABLE) + public Uni> getRegister(WebSocketConnection connection, Object o) { + return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult() + .call(cm -> Mutiny.fetch(cm.getInsc())) + .call(cm -> Mutiny.fetch(cm.getGuests())) + .map(cm -> { + ArrayList combEntities = new ArrayList<>(); + combEntities.addAll(cm.getInsc().stream().map(CombEntity::fromModel).toList()); + combEntities.addAll(cm.getGuests().stream().map(CombEntity::fromModel).toList()); + return combEntities; + }); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/recv/WSReceiver.java b/src/main/java/fr/titionfire/ffsaf/ws/recv/WSReceiver.java new file mode 100644 index 0000000..c901563 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/recv/WSReceiver.java @@ -0,0 +1,17 @@ +package fr.titionfire.ffsaf.ws.recv; + +import fr.titionfire.ffsaf.ws.PermLevel; +import io.quarkus.runtime.annotations.RegisterForReflection; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@RegisterForReflection +@Retention(RetentionPolicy.RUNTIME) +public @interface WSReceiver { + + String code(); + + PermLevel permission() default PermLevel.VIEW; + +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/JsonUni.java b/src/main/java/fr/titionfire/ffsaf/ws/send/JsonUni.java new file mode 100644 index 0000000..abe91ac --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/JsonUni.java @@ -0,0 +1,33 @@ +package fr.titionfire.ffsaf.ws.send; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.unchecked.Unchecked; + +import java.util.function.Function; + +import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER; + +@RegisterForReflection +public class JsonUni { + + private final Class clazz; + private final Function> mapper; + + public JsonUni(Class clazz, Function> mapper) { + this.clazz = clazz; + this.mapper = mapper; + } + + public Uni castAndChain(JsonNode message) throws JsonProcessingException { + return Uni.createFrom().item(MAPPER.treeToValue(message, clazz)).chain(mapper); + } + + public Uni castAndError(JsonNode message) throws JsonProcessingException { + return Uni.createFrom().item((T) null).invoke(Unchecked.consumer(__ -> { + throw new WSClientError(MAPPER.treeToValue(message, String.class)); + })).chain(mapper); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/SRegister.java b/src/main/java/fr/titionfire/ffsaf/ws/send/SRegister.java new file mode 100644 index 0000000..9197d2e --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/SRegister.java @@ -0,0 +1,53 @@ +package fr.titionfire.ffsaf.ws.send; + +import fr.titionfire.ffsaf.data.model.CompetitionGuestModel; +import fr.titionfire.ffsaf.data.model.RegisterModel; +import fr.titionfire.ffsaf.domain.entity.CombEntity; +import fr.titionfire.ffsaf.net2.MessageType; +import fr.titionfire.ffsaf.ws.CompetitionWS; +import fr.titionfire.ffsaf.ws.MessageOut; +import fr.titionfire.ffsaf.ws.PermLevel; +import io.quarkus.runtime.annotations.RegisterForReflection; +import io.quarkus.websockets.next.OpenConnections; +import io.quarkus.websockets.next.UserData; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.List; +import java.util.UUID; + +@ApplicationScoped +@RegisterForReflection +public class SRegister { + + @SuppressWarnings("CdiInjectionPointsInspection") + @Inject + OpenConnections connections; + + public Uni sendRegister(String uuid, RegisterModel registerModel) { + return send(uuid, "sendRegister", CombEntity.fromModel(registerModel)); + } + + public Uni sendRegister(String uuid, CompetitionGuestModel model) { + return send(uuid, "sendRegister", CombEntity.fromModel(model)); + } + + public Uni sendRegisterRemove(String uuid, Long combId) { + return send(uuid, "sendRegisterRemove", combId); + } + + public Uni send(String uuid, String code, Object data) { + List> queue = connections.findByEndpointId(CompetitionWS.class.getCanonicalName()).stream() + .filter(c -> c.pathParam("uuid").equals(uuid) && PermLevel.valueOf( + c.userData().get(UserData.TypedKey.forString("prem"))).ordinal() >= PermLevel.TABLE.ordinal()) + .map(c -> c.sendText( + new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data))) + .toList(); + + if (queue.isEmpty()) + return Uni.createFrom().voidItem(); + return Uni.join().all(queue).andCollectFailures().replaceWithVoid(); + } + +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/SSCardboard.java b/src/main/java/fr/titionfire/ffsaf/ws/send/SSCardboard.java new file mode 100644 index 0000000..31963b1 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/SSCardboard.java @@ -0,0 +1,13 @@ +package fr.titionfire.ffsaf.ws.send; + +import fr.titionfire.ffsaf.domain.entity.CardboardEntity; +import fr.titionfire.ffsaf.ws.CompetitionWS; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; + +public class SSCardboard { + + public static Uni sendCardboard(WebSocketConnection connection, CardboardEntity cardboardEntity) { + return CompetitionWS.sendNotifyToOtherEditor(connection, "sendCardboard", cardboardEntity); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/SSCategorie.java b/src/main/java/fr/titionfire/ffsaf/ws/send/SSCategorie.java new file mode 100644 index 0000000..23162b9 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/SSCategorie.java @@ -0,0 +1,33 @@ +package fr.titionfire.ffsaf.ws.send; + +import fr.titionfire.ffsaf.data.model.CategoryModel; +import fr.titionfire.ffsaf.domain.entity.TreeEntity; +import fr.titionfire.ffsaf.ws.CompetitionWS; +import fr.titionfire.ffsaf.ws.recv.RCategorie; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; + +import java.util.List; + +public class SSCategorie { + + public static Uni sendAddCategory(WebSocketConnection connection, CategoryModel category) { + return SSCategorie.sendAddCategory(connection, RCategorie.JustCategorie.from(category)); + } + + public static Uni sendAddCategory(WebSocketConnection connection, RCategorie.JustCategorie justCategorie) { + return CompetitionWS.sendNotifyToOtherEditor(connection, "sendAddCategory", justCategorie); + } + + public static Uni sendCategory(WebSocketConnection connection, CategoryModel category) { + return SSCategorie.sendCategory(connection, RCategorie.JustCategorie.from(category)); + } + + public static Uni sendCategory(WebSocketConnection connection, RCategorie.JustCategorie justCategorie) { + return CompetitionWS.sendNotifyToOtherEditor(connection, "sendCategory", justCategorie); + } + + public static Uni sendTreeCategory(WebSocketConnection connection, List treeEntities) { + return CompetitionWS.sendNotifyToOtherEditor(connection, "sendTreeCategory", treeEntities); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/SSMatch.java b/src/main/java/fr/titionfire/ffsaf/ws/send/SSMatch.java new file mode 100644 index 0000000..b66911d --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/SSMatch.java @@ -0,0 +1,32 @@ +package fr.titionfire.ffsaf.ws.send; + +import fr.titionfire.ffsaf.domain.entity.MatchEntity; +import fr.titionfire.ffsaf.ws.CompetitionWS; +import fr.titionfire.ffsaf.ws.recv.RMatch; +import io.quarkus.websockets.next.WebSocketConnection; +import io.smallrye.mutiny.Uni; + +import java.util.List; + +public class SSMatch { + + public static Uni sendMatch(WebSocketConnection connection, MatchEntity matchEntity) { + return SSMatch.sendMatch(connection, List.of(matchEntity)); + } + + public static Uni sendMatch(WebSocketConnection connection, List matchEntities) { + return CompetitionWS.sendNotifyToOtherEditor(connection, "sendMatch", matchEntities); + } + + public static Uni sendMatchOrder(WebSocketConnection connection, RMatch.MatchOrder matchOrder) { + return CompetitionWS.sendNotifyToOtherEditor(connection, "sendMatchOrder", matchOrder); + } + + public static Uni sendDeleteMatch(WebSocketConnection connection, Long l) { + return SSMatch.sendDeleteMatch(connection, List.of(l)); + } + + public static Uni sendDeleteMatch(WebSocketConnection connection, List longs) { + return CompetitionWS.sendNotifyToOtherEditor(connection, "sendDeleteMatch", longs); + } +} diff --git a/src/main/java/fr/titionfire/ffsaf/ws/send/WSClientError.java b/src/main/java/fr/titionfire/ffsaf/ws/send/WSClientError.java new file mode 100644 index 0000000..b654569 --- /dev/null +++ b/src/main/java/fr/titionfire/ffsaf/ws/send/WSClientError.java @@ -0,0 +1,13 @@ +package fr.titionfire.ffsaf.ws.send; + +import java.io.IOException; +import java.io.Serial; + +public class WSClientError extends IOException { + @Serial + private static final long serialVersionUID = -3790479241838684450L; + + public WSClientError(String message) { + super(message); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 984ee85..0d7d3e6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -45,6 +45,9 @@ siren-api.key=siren-ap quarkus.rest-client."fr.titionfire.ffsaf.rest.client.SirenService".url=https://data.siren-api.fr/ quarkus.rest-client."fr.titionfire.ffsaf.rest.client.StateIdService".url=https://www.data-asso.fr/api/ +quarkus.websockets-next.server.auto-ping-interval=PT10s +quarkus.websockets-next.client.connection-idle-timeout=PT30s + #Login quarkus.oidc.token-state-manager.split-tokens=true quarkus.oidc.token.refresh-expired=true diff --git a/src/main/resources/lang/String.properties b/src/main/resources/lang/String.properties new file mode 100644 index 0000000..7e87f97 --- /dev/null +++ b/src/main/resources/lang/String.properties @@ -0,0 +1,16 @@ +filtre.all=--tout-- +no.licence=Non licenci\u00E9 + + +# Categories +Cat.SUPER_MINI=Super Mini +Cat.MINI_POUSSIN=Mini Poussin +Cat.POUSSIN= Poussin +Cat.BENJAMIN=Benjamin +Cat.MINIME=Minime +Cat.CADET=Cadet +Cat.JUNIOR=Junior +Cat.SENIOR1=Senior 1 +Cat.SENIOR2=Senior 2 +Cat.VETERAN1=V\u00E9t\u00E9ran 1 +Cat.VETERAN2=V\u00E9t\u00E9ran 2 \ No newline at end of file diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 16586f2..d17ca9d 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -8,12 +8,8 @@ - + - + diff --git a/src/main/webapp/package-lock.json b/src/main/webapp/package-lock.json index 9cf6137..67a1ffd 100644 --- a/src/main/webapp/package-lock.json +++ b/src/main/webapp/package-lock.json @@ -8,6 +8,9 @@ "name": "webapp", "version": "0.0.0", "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.1", "@fortawesome/free-regular-svg-icons": "^6.5.1", @@ -399,6 +402,55 @@ "node": ">=6.9.0" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.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", diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 0cc49fc..0e57209 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -10,6 +10,9 @@ "preview": "vite preview" }, "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.1", "@fortawesome/free-regular-svg-icons": "^6.5.1", diff --git a/src/main/webapp/public/flags/svg/ad.svg b/src/main/webapp/public/flags/svg/ad.svg new file mode 100644 index 0000000..655fc8a --- /dev/null +++ b/src/main/webapp/public/flags/svg/ad.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ae.svg b/src/main/webapp/public/flags/svg/ae.svg new file mode 100644 index 0000000..92be1eb --- /dev/null +++ b/src/main/webapp/public/flags/svg/ae.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/af.svg b/src/main/webapp/public/flags/svg/af.svg new file mode 100644 index 0000000..d9e49f9 --- /dev/null +++ b/src/main/webapp/public/flags/svg/af.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ag.svg b/src/main/webapp/public/flags/svg/ag.svg new file mode 100644 index 0000000..f60599e --- /dev/null +++ b/src/main/webapp/public/flags/svg/ag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ai.svg b/src/main/webapp/public/flags/svg/ai.svg new file mode 100644 index 0000000..429ce3b --- /dev/null +++ b/src/main/webapp/public/flags/svg/ai.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/al.svg b/src/main/webapp/public/flags/svg/al.svg new file mode 100644 index 0000000..f8d5550 --- /dev/null +++ b/src/main/webapp/public/flags/svg/al.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/am.svg b/src/main/webapp/public/flags/svg/am.svg new file mode 100644 index 0000000..1e74af0 --- /dev/null +++ b/src/main/webapp/public/flags/svg/am.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ao.svg b/src/main/webapp/public/flags/svg/ao.svg new file mode 100644 index 0000000..6384b44 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ao.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/aq.svg b/src/main/webapp/public/flags/svg/aq.svg new file mode 100644 index 0000000..cef722d --- /dev/null +++ b/src/main/webapp/public/flags/svg/aq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ar.svg b/src/main/webapp/public/flags/svg/ar.svg new file mode 100644 index 0000000..db54af5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/as.svg b/src/main/webapp/public/flags/svg/as.svg new file mode 100644 index 0000000..4d66615 --- /dev/null +++ b/src/main/webapp/public/flags/svg/as.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/at.svg b/src/main/webapp/public/flags/svg/at.svg new file mode 100644 index 0000000..1229a26 --- /dev/null +++ b/src/main/webapp/public/flags/svg/at.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/au.svg b/src/main/webapp/public/flags/svg/au.svg new file mode 100644 index 0000000..38a47de --- /dev/null +++ b/src/main/webapp/public/flags/svg/au.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/aw.svg b/src/main/webapp/public/flags/svg/aw.svg new file mode 100644 index 0000000..a80f4e7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/aw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ax.svg b/src/main/webapp/public/flags/svg/ax.svg new file mode 100644 index 0000000..3af6b6e --- /dev/null +++ b/src/main/webapp/public/flags/svg/ax.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/az.svg b/src/main/webapp/public/flags/svg/az.svg new file mode 100644 index 0000000..f88d080 --- /dev/null +++ b/src/main/webapp/public/flags/svg/az.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ba.svg b/src/main/webapp/public/flags/svg/ba.svg new file mode 100644 index 0000000..177c78d --- /dev/null +++ b/src/main/webapp/public/flags/svg/ba.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bb.svg b/src/main/webapp/public/flags/svg/bb.svg new file mode 100644 index 0000000..820297a --- /dev/null +++ b/src/main/webapp/public/flags/svg/bb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bd.svg b/src/main/webapp/public/flags/svg/bd.svg new file mode 100644 index 0000000..8cf230b --- /dev/null +++ b/src/main/webapp/public/flags/svg/bd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/be.svg b/src/main/webapp/public/flags/svg/be.svg new file mode 100644 index 0000000..107b639 --- /dev/null +++ b/src/main/webapp/public/flags/svg/be.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bf.svg b/src/main/webapp/public/flags/svg/bf.svg new file mode 100644 index 0000000..3a649ae --- /dev/null +++ b/src/main/webapp/public/flags/svg/bf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bg.svg b/src/main/webapp/public/flags/svg/bg.svg new file mode 100644 index 0000000..541c9e5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bh.svg b/src/main/webapp/public/flags/svg/bh.svg new file mode 100644 index 0000000..5ec7560 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bi.svg b/src/main/webapp/public/flags/svg/bi.svg new file mode 100644 index 0000000..2b796ab --- /dev/null +++ b/src/main/webapp/public/flags/svg/bi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bj.svg b/src/main/webapp/public/flags/svg/bj.svg new file mode 100644 index 0000000..b8d1bd2 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bj.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bl.svg b/src/main/webapp/public/flags/svg/bl.svg new file mode 100644 index 0000000..64cc17f --- /dev/null +++ b/src/main/webapp/public/flags/svg/bl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bm.svg b/src/main/webapp/public/flags/svg/bm.svg new file mode 100644 index 0000000..1059eae --- /dev/null +++ b/src/main/webapp/public/flags/svg/bm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bn.svg b/src/main/webapp/public/flags/svg/bn.svg new file mode 100644 index 0000000..98ce7f8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bo.svg b/src/main/webapp/public/flags/svg/bo.svg new file mode 100644 index 0000000..3bf24b0 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bq.svg b/src/main/webapp/public/flags/svg/bq.svg new file mode 100644 index 0000000..2632a89 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/br.svg b/src/main/webapp/public/flags/svg/br.svg new file mode 100644 index 0000000..203e64d --- /dev/null +++ b/src/main/webapp/public/flags/svg/br.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bs.svg b/src/main/webapp/public/flags/svg/bs.svg new file mode 100644 index 0000000..852306a --- /dev/null +++ b/src/main/webapp/public/flags/svg/bs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bt.svg b/src/main/webapp/public/flags/svg/bt.svg new file mode 100644 index 0000000..16045d0 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bv.svg b/src/main/webapp/public/flags/svg/bv.svg new file mode 100644 index 0000000..ebb8fbd --- /dev/null +++ b/src/main/webapp/public/flags/svg/bv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bw.svg b/src/main/webapp/public/flags/svg/bw.svg new file mode 100644 index 0000000..4c60233 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/by.svg b/src/main/webapp/public/flags/svg/by.svg new file mode 100644 index 0000000..b5afd5b --- /dev/null +++ b/src/main/webapp/public/flags/svg/by.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/bz.svg b/src/main/webapp/public/flags/svg/bz.svg new file mode 100644 index 0000000..0f53853 --- /dev/null +++ b/src/main/webapp/public/flags/svg/bz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ca.svg b/src/main/webapp/public/flags/svg/ca.svg new file mode 100644 index 0000000..154cb19 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ca.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cc.svg b/src/main/webapp/public/flags/svg/cc.svg new file mode 100644 index 0000000..0e35450 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cd.svg b/src/main/webapp/public/flags/svg/cd.svg new file mode 100644 index 0000000..5892af0 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cf.svg b/src/main/webapp/public/flags/svg/cf.svg new file mode 100644 index 0000000..e23b567 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cg.svg b/src/main/webapp/public/flags/svg/cg.svg new file mode 100644 index 0000000..c383ee8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ch.svg b/src/main/webapp/public/flags/svg/ch.svg new file mode 100644 index 0000000..f7833a5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ci.svg b/src/main/webapp/public/flags/svg/ci.svg new file mode 100644 index 0000000..4448319 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ci.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ck.svg b/src/main/webapp/public/flags/svg/ck.svg new file mode 100644 index 0000000..237aa87 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ck.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cl.svg b/src/main/webapp/public/flags/svg/cl.svg new file mode 100644 index 0000000..1cbed43 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cm.svg b/src/main/webapp/public/flags/svg/cm.svg new file mode 100644 index 0000000..04a5c7f --- /dev/null +++ b/src/main/webapp/public/flags/svg/cm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cn.svg b/src/main/webapp/public/flags/svg/cn.svg new file mode 100644 index 0000000..3227c3d --- /dev/null +++ b/src/main/webapp/public/flags/svg/cn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/co.svg b/src/main/webapp/public/flags/svg/co.svg new file mode 100644 index 0000000..2f3901d --- /dev/null +++ b/src/main/webapp/public/flags/svg/co.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cr.svg b/src/main/webapp/public/flags/svg/cr.svg new file mode 100644 index 0000000..d0b9fb1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cu.svg b/src/main/webapp/public/flags/svg/cu.svg new file mode 100644 index 0000000..6777e9c --- /dev/null +++ b/src/main/webapp/public/flags/svg/cu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cv.svg b/src/main/webapp/public/flags/svg/cv.svg new file mode 100644 index 0000000..0fd3001 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cw.svg b/src/main/webapp/public/flags/svg/cw.svg new file mode 100644 index 0000000..e8953e6 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cx.svg b/src/main/webapp/public/flags/svg/cx.svg new file mode 100644 index 0000000..8c79df8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cx.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cy.svg b/src/main/webapp/public/flags/svg/cy.svg new file mode 100644 index 0000000..c81cc8f --- /dev/null +++ b/src/main/webapp/public/flags/svg/cy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/cz.svg b/src/main/webapp/public/flags/svg/cz.svg new file mode 100644 index 0000000..3915399 --- /dev/null +++ b/src/main/webapp/public/flags/svg/cz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/de.svg b/src/main/webapp/public/flags/svg/de.svg new file mode 100644 index 0000000..60e5efc --- /dev/null +++ b/src/main/webapp/public/flags/svg/de.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/dj.svg b/src/main/webapp/public/flags/svg/dj.svg new file mode 100644 index 0000000..6b96f37 --- /dev/null +++ b/src/main/webapp/public/flags/svg/dj.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/dk.svg b/src/main/webapp/public/flags/svg/dk.svg new file mode 100644 index 0000000..1ecf992 --- /dev/null +++ b/src/main/webapp/public/flags/svg/dk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/dm.svg b/src/main/webapp/public/flags/svg/dm.svg new file mode 100644 index 0000000..b11ebe5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/dm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/do.svg b/src/main/webapp/public/flags/svg/do.svg new file mode 100644 index 0000000..3ed50b6 --- /dev/null +++ b/src/main/webapp/public/flags/svg/do.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/dz.svg b/src/main/webapp/public/flags/svg/dz.svg new file mode 100644 index 0000000..2b23ca4 --- /dev/null +++ b/src/main/webapp/public/flags/svg/dz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ec.svg b/src/main/webapp/public/flags/svg/ec.svg new file mode 100644 index 0000000..61979d7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ec.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ee.svg b/src/main/webapp/public/flags/svg/ee.svg new file mode 100644 index 0000000..2b64555 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/eg.svg b/src/main/webapp/public/flags/svg/eg.svg new file mode 100644 index 0000000..95fdd19 --- /dev/null +++ b/src/main/webapp/public/flags/svg/eg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/eh.svg b/src/main/webapp/public/flags/svg/eh.svg new file mode 100644 index 0000000..de9ede4 --- /dev/null +++ b/src/main/webapp/public/flags/svg/eh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/er.svg b/src/main/webapp/public/flags/svg/er.svg new file mode 100644 index 0000000..8cb5783 --- /dev/null +++ b/src/main/webapp/public/flags/svg/er.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/es.svg b/src/main/webapp/public/flags/svg/es.svg new file mode 100644 index 0000000..8e6e4c2 --- /dev/null +++ b/src/main/webapp/public/flags/svg/es.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/et.svg b/src/main/webapp/public/flags/svg/et.svg new file mode 100644 index 0000000..53e79aa --- /dev/null +++ b/src/main/webapp/public/flags/svg/et.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/fi.svg b/src/main/webapp/public/flags/svg/fi.svg new file mode 100644 index 0000000..cc3510e --- /dev/null +++ b/src/main/webapp/public/flags/svg/fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/fj.svg b/src/main/webapp/public/flags/svg/fj.svg new file mode 100644 index 0000000..6eebe60 --- /dev/null +++ b/src/main/webapp/public/flags/svg/fj.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/fk.svg b/src/main/webapp/public/flags/svg/fk.svg new file mode 100644 index 0000000..08209e8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/fk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/fm.svg b/src/main/webapp/public/flags/svg/fm.svg new file mode 100644 index 0000000..09532fa --- /dev/null +++ b/src/main/webapp/public/flags/svg/fm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/fo.svg b/src/main/webapp/public/flags/svg/fo.svg new file mode 100644 index 0000000..1e962c5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/fo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/fr.svg b/src/main/webapp/public/flags/svg/fr.svg new file mode 100644 index 0000000..6eb0e18 --- /dev/null +++ b/src/main/webapp/public/flags/svg/fr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ga.svg b/src/main/webapp/public/flags/svg/ga.svg new file mode 100644 index 0000000..3fd1d25 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ga.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gb-eng.svg b/src/main/webapp/public/flags/svg/gb-eng.svg new file mode 100644 index 0000000..f474d10 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gb-eng.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gb-nir.svg b/src/main/webapp/public/flags/svg/gb-nir.svg new file mode 100644 index 0000000..8918d2c --- /dev/null +++ b/src/main/webapp/public/flags/svg/gb-nir.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gb-sct.svg b/src/main/webapp/public/flags/svg/gb-sct.svg new file mode 100644 index 0000000..be27bb2 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gb-sct.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gb-wls.svg b/src/main/webapp/public/flags/svg/gb-wls.svg new file mode 100644 index 0000000..6b0d025 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gb-wls.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gb.svg b/src/main/webapp/public/flags/svg/gb.svg new file mode 100644 index 0000000..82b1fad --- /dev/null +++ b/src/main/webapp/public/flags/svg/gb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gd.svg b/src/main/webapp/public/flags/svg/gd.svg new file mode 100644 index 0000000..bc51894 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ge.svg b/src/main/webapp/public/flags/svg/ge.svg new file mode 100644 index 0000000..7e0459b --- /dev/null +++ b/src/main/webapp/public/flags/svg/ge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gf.svg b/src/main/webapp/public/flags/svg/gf.svg new file mode 100644 index 0000000..cbe2c8c --- /dev/null +++ b/src/main/webapp/public/flags/svg/gf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gg.svg b/src/main/webapp/public/flags/svg/gg.svg new file mode 100644 index 0000000..9e482a9 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gh.svg b/src/main/webapp/public/flags/svg/gh.svg new file mode 100644 index 0000000..fae8956 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gi.svg b/src/main/webapp/public/flags/svg/gi.svg new file mode 100644 index 0000000..400c8d0 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gl.svg b/src/main/webapp/public/flags/svg/gl.svg new file mode 100644 index 0000000..2e355e8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gm.svg b/src/main/webapp/public/flags/svg/gm.svg new file mode 100644 index 0000000..5949185 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gn.svg b/src/main/webapp/public/flags/svg/gn.svg new file mode 100644 index 0000000..9967a7b --- /dev/null +++ b/src/main/webapp/public/flags/svg/gn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gp.svg b/src/main/webapp/public/flags/svg/gp.svg new file mode 100644 index 0000000..250d60b --- /dev/null +++ b/src/main/webapp/public/flags/svg/gp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gq.svg b/src/main/webapp/public/flags/svg/gq.svg new file mode 100644 index 0000000..459b5a1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gr.svg b/src/main/webapp/public/flags/svg/gr.svg new file mode 100644 index 0000000..e3a582c --- /dev/null +++ b/src/main/webapp/public/flags/svg/gr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gs.svg b/src/main/webapp/public/flags/svg/gs.svg new file mode 100644 index 0000000..56e473d --- /dev/null +++ b/src/main/webapp/public/flags/svg/gs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gt.svg b/src/main/webapp/public/flags/svg/gt.svg new file mode 100644 index 0000000..b784b27 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gu.svg b/src/main/webapp/public/flags/svg/gu.svg new file mode 100644 index 0000000..ca41e91 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gw.svg b/src/main/webapp/public/flags/svg/gw.svg new file mode 100644 index 0000000..808b89c --- /dev/null +++ b/src/main/webapp/public/flags/svg/gw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/gy.svg b/src/main/webapp/public/flags/svg/gy.svg new file mode 100644 index 0000000..d48e314 --- /dev/null +++ b/src/main/webapp/public/flags/svg/gy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/hk.svg b/src/main/webapp/public/flags/svg/hk.svg new file mode 100644 index 0000000..c4287fd --- /dev/null +++ b/src/main/webapp/public/flags/svg/hk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/hm.svg b/src/main/webapp/public/flags/svg/hm.svg new file mode 100644 index 0000000..f6e4097 --- /dev/null +++ b/src/main/webapp/public/flags/svg/hm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/hn.svg b/src/main/webapp/public/flags/svg/hn.svg new file mode 100644 index 0000000..3ddeb2c --- /dev/null +++ b/src/main/webapp/public/flags/svg/hn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/hr.svg b/src/main/webapp/public/flags/svg/hr.svg new file mode 100644 index 0000000..ad24ab9 --- /dev/null +++ b/src/main/webapp/public/flags/svg/hr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ht.svg b/src/main/webapp/public/flags/svg/ht.svg new file mode 100644 index 0000000..b6ca967 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ht.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/hu.svg b/src/main/webapp/public/flags/svg/hu.svg new file mode 100644 index 0000000..af28885 --- /dev/null +++ b/src/main/webapp/public/flags/svg/hu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/id.svg b/src/main/webapp/public/flags/svg/id.svg new file mode 100644 index 0000000..08c1098 --- /dev/null +++ b/src/main/webapp/public/flags/svg/id.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ie.svg b/src/main/webapp/public/flags/svg/ie.svg new file mode 100644 index 0000000..ff76bd8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ie.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/il.svg b/src/main/webapp/public/flags/svg/il.svg new file mode 100644 index 0000000..de884ea --- /dev/null +++ b/src/main/webapp/public/flags/svg/il.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/im.svg b/src/main/webapp/public/flags/svg/im.svg new file mode 100644 index 0000000..d86c844 --- /dev/null +++ b/src/main/webapp/public/flags/svg/im.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/in.svg b/src/main/webapp/public/flags/svg/in.svg new file mode 100644 index 0000000..5217928 --- /dev/null +++ b/src/main/webapp/public/flags/svg/in.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/io.svg b/src/main/webapp/public/flags/svg/io.svg new file mode 100644 index 0000000..1f15f04 --- /dev/null +++ b/src/main/webapp/public/flags/svg/io.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/iq.svg b/src/main/webapp/public/flags/svg/iq.svg new file mode 100644 index 0000000..2b62c58 --- /dev/null +++ b/src/main/webapp/public/flags/svg/iq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ir.svg b/src/main/webapp/public/flags/svg/ir.svg new file mode 100644 index 0000000..272eff0 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ir.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/is.svg b/src/main/webapp/public/flags/svg/is.svg new file mode 100644 index 0000000..c1d7fc3 --- /dev/null +++ b/src/main/webapp/public/flags/svg/is.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/it.svg b/src/main/webapp/public/flags/svg/it.svg new file mode 100644 index 0000000..5541378 --- /dev/null +++ b/src/main/webapp/public/flags/svg/it.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/je.svg b/src/main/webapp/public/flags/svg/je.svg new file mode 100644 index 0000000..90e7368 --- /dev/null +++ b/src/main/webapp/public/flags/svg/je.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/jm.svg b/src/main/webapp/public/flags/svg/jm.svg new file mode 100644 index 0000000..7273742 --- /dev/null +++ b/src/main/webapp/public/flags/svg/jm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/jo.svg b/src/main/webapp/public/flags/svg/jo.svg new file mode 100644 index 0000000..5a4eccf --- /dev/null +++ b/src/main/webapp/public/flags/svg/jo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/jp.svg b/src/main/webapp/public/flags/svg/jp.svg new file mode 100644 index 0000000..0f02949 --- /dev/null +++ b/src/main/webapp/public/flags/svg/jp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ke.svg b/src/main/webapp/public/flags/svg/ke.svg new file mode 100644 index 0000000..370a13f --- /dev/null +++ b/src/main/webapp/public/flags/svg/ke.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/kg.svg b/src/main/webapp/public/flags/svg/kg.svg new file mode 100644 index 0000000..fccd639 --- /dev/null +++ b/src/main/webapp/public/flags/svg/kg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/kh.svg b/src/main/webapp/public/flags/svg/kh.svg new file mode 100644 index 0000000..828e54a --- /dev/null +++ b/src/main/webapp/public/flags/svg/kh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ki.svg b/src/main/webapp/public/flags/svg/ki.svg new file mode 100644 index 0000000..ff58c03 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ki.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/km.svg b/src/main/webapp/public/flags/svg/km.svg new file mode 100644 index 0000000..88f36bf --- /dev/null +++ b/src/main/webapp/public/flags/svg/km.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/kn.svg b/src/main/webapp/public/flags/svg/kn.svg new file mode 100644 index 0000000..8764dd7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/kn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/kp.svg b/src/main/webapp/public/flags/svg/kp.svg new file mode 100644 index 0000000..42affa7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/kp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/kr.svg b/src/main/webapp/public/flags/svg/kr.svg new file mode 100644 index 0000000..a44c6e1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/kr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/kw.svg b/src/main/webapp/public/flags/svg/kw.svg new file mode 100644 index 0000000..b4bffef --- /dev/null +++ b/src/main/webapp/public/flags/svg/kw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ky.svg b/src/main/webapp/public/flags/svg/ky.svg new file mode 100644 index 0000000..ad1e433 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ky.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/kz.svg b/src/main/webapp/public/flags/svg/kz.svg new file mode 100644 index 0000000..96855b0 --- /dev/null +++ b/src/main/webapp/public/flags/svg/kz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/la.svg b/src/main/webapp/public/flags/svg/la.svg new file mode 100644 index 0000000..0f596b2 --- /dev/null +++ b/src/main/webapp/public/flags/svg/la.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/lb.svg b/src/main/webapp/public/flags/svg/lb.svg new file mode 100644 index 0000000..f69dfa6 --- /dev/null +++ b/src/main/webapp/public/flags/svg/lb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/lc.svg b/src/main/webapp/public/flags/svg/lc.svg new file mode 100644 index 0000000..887ad78 --- /dev/null +++ b/src/main/webapp/public/flags/svg/lc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/li.svg b/src/main/webapp/public/flags/svg/li.svg new file mode 100644 index 0000000..9cd6ae1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/li.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/lk.svg b/src/main/webapp/public/flags/svg/lk.svg new file mode 100644 index 0000000..33f6e09 --- /dev/null +++ b/src/main/webapp/public/flags/svg/lk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/lr.svg b/src/main/webapp/public/flags/svg/lr.svg new file mode 100644 index 0000000..660ab03 --- /dev/null +++ b/src/main/webapp/public/flags/svg/lr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ls.svg b/src/main/webapp/public/flags/svg/ls.svg new file mode 100644 index 0000000..0dc59f9 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ls.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/lt.svg b/src/main/webapp/public/flags/svg/lt.svg new file mode 100644 index 0000000..90a3ec1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/lt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/lu.svg b/src/main/webapp/public/flags/svg/lu.svg new file mode 100644 index 0000000..b754681 --- /dev/null +++ b/src/main/webapp/public/flags/svg/lu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/lv.svg b/src/main/webapp/public/flags/svg/lv.svg new file mode 100644 index 0000000..0037f27 --- /dev/null +++ b/src/main/webapp/public/flags/svg/lv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ly.svg b/src/main/webapp/public/flags/svg/ly.svg new file mode 100644 index 0000000..8c9ee58 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ma.svg b/src/main/webapp/public/flags/svg/ma.svg new file mode 100644 index 0000000..d6b54a3 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ma.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mc.svg b/src/main/webapp/public/flags/svg/mc.svg new file mode 100644 index 0000000..395adc5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/md.svg b/src/main/webapp/public/flags/svg/md.svg new file mode 100644 index 0000000..6fc8296 --- /dev/null +++ b/src/main/webapp/public/flags/svg/md.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/me.svg b/src/main/webapp/public/flags/svg/me.svg new file mode 100644 index 0000000..8b3f1ab --- /dev/null +++ b/src/main/webapp/public/flags/svg/me.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mf.svg b/src/main/webapp/public/flags/svg/mf.svg new file mode 100644 index 0000000..6eb0e18 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mg.svg b/src/main/webapp/public/flags/svg/mg.svg new file mode 100644 index 0000000..d83543f --- /dev/null +++ b/src/main/webapp/public/flags/svg/mg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mh.svg b/src/main/webapp/public/flags/svg/mh.svg new file mode 100644 index 0000000..4ca4b66 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mk.svg b/src/main/webapp/public/flags/svg/mk.svg new file mode 100644 index 0000000..e10ed70 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ml.svg b/src/main/webapp/public/flags/svg/ml.svg new file mode 100644 index 0000000..df345c2 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ml.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mm.svg b/src/main/webapp/public/flags/svg/mm.svg new file mode 100644 index 0000000..4509bb3 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mn.svg b/src/main/webapp/public/flags/svg/mn.svg new file mode 100644 index 0000000..f1805f8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mo.svg b/src/main/webapp/public/flags/svg/mo.svg new file mode 100644 index 0000000..739c35b --- /dev/null +++ b/src/main/webapp/public/flags/svg/mo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mp.svg b/src/main/webapp/public/flags/svg/mp.svg new file mode 100644 index 0000000..645fab7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mq.svg b/src/main/webapp/public/flags/svg/mq.svg new file mode 100644 index 0000000..0e5c3ce --- /dev/null +++ b/src/main/webapp/public/flags/svg/mq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mr.svg b/src/main/webapp/public/flags/svg/mr.svg new file mode 100644 index 0000000..8c37859 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ms.svg b/src/main/webapp/public/flags/svg/ms.svg new file mode 100644 index 0000000..0c199ca --- /dev/null +++ b/src/main/webapp/public/flags/svg/ms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mt.svg b/src/main/webapp/public/flags/svg/mt.svg new file mode 100644 index 0000000..a880f1f --- /dev/null +++ b/src/main/webapp/public/flags/svg/mt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mu.svg b/src/main/webapp/public/flags/svg/mu.svg new file mode 100644 index 0000000..7b1af9c --- /dev/null +++ b/src/main/webapp/public/flags/svg/mu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mv.svg b/src/main/webapp/public/flags/svg/mv.svg new file mode 100644 index 0000000..82f77be --- /dev/null +++ b/src/main/webapp/public/flags/svg/mv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mw.svg b/src/main/webapp/public/flags/svg/mw.svg new file mode 100644 index 0000000..2c80884 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mx.svg b/src/main/webapp/public/flags/svg/mx.svg new file mode 100644 index 0000000..b82436b --- /dev/null +++ b/src/main/webapp/public/flags/svg/mx.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/my.svg b/src/main/webapp/public/flags/svg/my.svg new file mode 100644 index 0000000..bb160fd --- /dev/null +++ b/src/main/webapp/public/flags/svg/my.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/mz.svg b/src/main/webapp/public/flags/svg/mz.svg new file mode 100644 index 0000000..29e8856 --- /dev/null +++ b/src/main/webapp/public/flags/svg/mz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/na.svg b/src/main/webapp/public/flags/svg/na.svg new file mode 100644 index 0000000..472a82e --- /dev/null +++ b/src/main/webapp/public/flags/svg/na.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/nc.svg b/src/main/webapp/public/flags/svg/nc.svg new file mode 100644 index 0000000..35a2125 --- /dev/null +++ b/src/main/webapp/public/flags/svg/nc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ne.svg b/src/main/webapp/public/flags/svg/ne.svg new file mode 100644 index 0000000..3da7314 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ne.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/nf.svg b/src/main/webapp/public/flags/svg/nf.svg new file mode 100644 index 0000000..959a85b --- /dev/null +++ b/src/main/webapp/public/flags/svg/nf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ng.svg b/src/main/webapp/public/flags/svg/ng.svg new file mode 100644 index 0000000..ca4a19f --- /dev/null +++ b/src/main/webapp/public/flags/svg/ng.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ni.svg b/src/main/webapp/public/flags/svg/ni.svg new file mode 100644 index 0000000..f1d41a1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ni.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/nl.svg b/src/main/webapp/public/flags/svg/nl.svg new file mode 100644 index 0000000..6598c0b --- /dev/null +++ b/src/main/webapp/public/flags/svg/nl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/no.svg b/src/main/webapp/public/flags/svg/no.svg new file mode 100644 index 0000000..ebb8fbd --- /dev/null +++ b/src/main/webapp/public/flags/svg/no.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/np.svg b/src/main/webapp/public/flags/svg/np.svg new file mode 100644 index 0000000..ae7b651 --- /dev/null +++ b/src/main/webapp/public/flags/svg/np.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/nr.svg b/src/main/webapp/public/flags/svg/nr.svg new file mode 100644 index 0000000..22e5bc4 --- /dev/null +++ b/src/main/webapp/public/flags/svg/nr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/nu.svg b/src/main/webapp/public/flags/svg/nu.svg new file mode 100644 index 0000000..cd34cea --- /dev/null +++ b/src/main/webapp/public/flags/svg/nu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/nz.svg b/src/main/webapp/public/flags/svg/nz.svg new file mode 100644 index 0000000..4f6af3c --- /dev/null +++ b/src/main/webapp/public/flags/svg/nz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/om.svg b/src/main/webapp/public/flags/svg/om.svg new file mode 100644 index 0000000..9b80e4b --- /dev/null +++ b/src/main/webapp/public/flags/svg/om.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pa.svg b/src/main/webapp/public/flags/svg/pa.svg new file mode 100644 index 0000000..c625603 --- /dev/null +++ b/src/main/webapp/public/flags/svg/pa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pe.svg b/src/main/webapp/public/flags/svg/pe.svg new file mode 100644 index 0000000..93618a3 --- /dev/null +++ b/src/main/webapp/public/flags/svg/pe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pf.svg b/src/main/webapp/public/flags/svg/pf.svg new file mode 100644 index 0000000..eb69b3c --- /dev/null +++ b/src/main/webapp/public/flags/svg/pf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pg.svg b/src/main/webapp/public/flags/svg/pg.svg new file mode 100644 index 0000000..aa5a7b7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/pg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ph.svg b/src/main/webapp/public/flags/svg/ph.svg new file mode 100644 index 0000000..be4ac14 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ph.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pk.svg b/src/main/webapp/public/flags/svg/pk.svg new file mode 100644 index 0000000..91cd2f1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/pk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pl.svg b/src/main/webapp/public/flags/svg/pl.svg new file mode 100644 index 0000000..4a39bfa --- /dev/null +++ b/src/main/webapp/public/flags/svg/pl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pm.svg b/src/main/webapp/public/flags/svg/pm.svg new file mode 100644 index 0000000..1780f1e --- /dev/null +++ b/src/main/webapp/public/flags/svg/pm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pn.svg b/src/main/webapp/public/flags/svg/pn.svg new file mode 100644 index 0000000..35f2a3d --- /dev/null +++ b/src/main/webapp/public/flags/svg/pn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pr.svg b/src/main/webapp/public/flags/svg/pr.svg new file mode 100644 index 0000000..5ed5f8a --- /dev/null +++ b/src/main/webapp/public/flags/svg/pr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ps.svg b/src/main/webapp/public/flags/svg/ps.svg new file mode 100644 index 0000000..786c8ca --- /dev/null +++ b/src/main/webapp/public/flags/svg/ps.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pt.svg b/src/main/webapp/public/flags/svg/pt.svg new file mode 100644 index 0000000..0c610be --- /dev/null +++ b/src/main/webapp/public/flags/svg/pt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/pw.svg b/src/main/webapp/public/flags/svg/pw.svg new file mode 100644 index 0000000..7ab6c75 --- /dev/null +++ b/src/main/webapp/public/flags/svg/pw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/py.svg b/src/main/webapp/public/flags/svg/py.svg new file mode 100644 index 0000000..63f38a7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/py.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/qa.svg b/src/main/webapp/public/flags/svg/qa.svg new file mode 100644 index 0000000..65b2184 --- /dev/null +++ b/src/main/webapp/public/flags/svg/qa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/re.svg b/src/main/webapp/public/flags/svg/re.svg new file mode 100644 index 0000000..7df5606 --- /dev/null +++ b/src/main/webapp/public/flags/svg/re.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ro.svg b/src/main/webapp/public/flags/svg/ro.svg new file mode 100644 index 0000000..8dc2848 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ro.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/rs.svg b/src/main/webapp/public/flags/svg/rs.svg new file mode 100644 index 0000000..40c9d6d --- /dev/null +++ b/src/main/webapp/public/flags/svg/rs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ru.svg b/src/main/webapp/public/flags/svg/ru.svg new file mode 100644 index 0000000..9c0cc49 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ru.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/rw.svg b/src/main/webapp/public/flags/svg/rw.svg new file mode 100644 index 0000000..d237941 --- /dev/null +++ b/src/main/webapp/public/flags/svg/rw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sa.svg b/src/main/webapp/public/flags/svg/sa.svg new file mode 100644 index 0000000..5eee1b5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sa.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sb.svg b/src/main/webapp/public/flags/svg/sb.svg new file mode 100644 index 0000000..e4bb3ff --- /dev/null +++ b/src/main/webapp/public/flags/svg/sb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sc.svg b/src/main/webapp/public/flags/svg/sc.svg new file mode 100644 index 0000000..ea40823 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sd.svg b/src/main/webapp/public/flags/svg/sd.svg new file mode 100644 index 0000000..477877f --- /dev/null +++ b/src/main/webapp/public/flags/svg/sd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/se.svg b/src/main/webapp/public/flags/svg/se.svg new file mode 100644 index 0000000..b32a8f9 --- /dev/null +++ b/src/main/webapp/public/flags/svg/se.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sg.svg b/src/main/webapp/public/flags/svg/sg.svg new file mode 100644 index 0000000..4d4a817 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sh.svg b/src/main/webapp/public/flags/svg/sh.svg new file mode 100644 index 0000000..d4431b7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/si.svg b/src/main/webapp/public/flags/svg/si.svg new file mode 100644 index 0000000..bb23bb3 --- /dev/null +++ b/src/main/webapp/public/flags/svg/si.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sj.svg b/src/main/webapp/public/flags/svg/sj.svg new file mode 100644 index 0000000..ebb8fbd --- /dev/null +++ b/src/main/webapp/public/flags/svg/sj.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sk.svg b/src/main/webapp/public/flags/svg/sk.svg new file mode 100644 index 0000000..2abe6f4 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sl.svg b/src/main/webapp/public/flags/svg/sl.svg new file mode 100644 index 0000000..ee46023 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sm.svg b/src/main/webapp/public/flags/svg/sm.svg new file mode 100644 index 0000000..98fdaf4 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sn.svg b/src/main/webapp/public/flags/svg/sn.svg new file mode 100644 index 0000000..3860b33 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/so.svg b/src/main/webapp/public/flags/svg/so.svg new file mode 100644 index 0000000..44ce2fd --- /dev/null +++ b/src/main/webapp/public/flags/svg/so.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sr.svg b/src/main/webapp/public/flags/svg/sr.svg new file mode 100644 index 0000000..117d76d --- /dev/null +++ b/src/main/webapp/public/flags/svg/sr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ss.svg b/src/main/webapp/public/flags/svg/ss.svg new file mode 100644 index 0000000..20cc821 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/st.svg b/src/main/webapp/public/flags/svg/st.svg new file mode 100644 index 0000000..555952c --- /dev/null +++ b/src/main/webapp/public/flags/svg/st.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sv.svg b/src/main/webapp/public/flags/svg/sv.svg new file mode 100644 index 0000000..c81ee73 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sx.svg b/src/main/webapp/public/flags/svg/sx.svg new file mode 100644 index 0000000..dcc7c81 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sx.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sy.svg b/src/main/webapp/public/flags/svg/sy.svg new file mode 100644 index 0000000..ca5d90e --- /dev/null +++ b/src/main/webapp/public/flags/svg/sy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/sz.svg b/src/main/webapp/public/flags/svg/sz.svg new file mode 100644 index 0000000..25117b2 --- /dev/null +++ b/src/main/webapp/public/flags/svg/sz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tc.svg b/src/main/webapp/public/flags/svg/tc.svg new file mode 100644 index 0000000..dea5664 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/td.svg b/src/main/webapp/public/flags/svg/td.svg new file mode 100644 index 0000000..f94d6e5 --- /dev/null +++ b/src/main/webapp/public/flags/svg/td.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tf.svg b/src/main/webapp/public/flags/svg/tf.svg new file mode 100644 index 0000000..ea39af9 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tg.svg b/src/main/webapp/public/flags/svg/tg.svg new file mode 100644 index 0000000..27582f1 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/th.svg b/src/main/webapp/public/flags/svg/th.svg new file mode 100644 index 0000000..3f22b44 --- /dev/null +++ b/src/main/webapp/public/flags/svg/th.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tj.svg b/src/main/webapp/public/flags/svg/tj.svg new file mode 100644 index 0000000..ec4e481 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tj.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tk.svg b/src/main/webapp/public/flags/svg/tk.svg new file mode 100644 index 0000000..16aa66a --- /dev/null +++ b/src/main/webapp/public/flags/svg/tk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tl.svg b/src/main/webapp/public/flags/svg/tl.svg new file mode 100644 index 0000000..1d6bb56 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tm.svg b/src/main/webapp/public/flags/svg/tm.svg new file mode 100644 index 0000000..f49a647 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tn.svg b/src/main/webapp/public/flags/svg/tn.svg new file mode 100644 index 0000000..482b55a --- /dev/null +++ b/src/main/webapp/public/flags/svg/tn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/to.svg b/src/main/webapp/public/flags/svg/to.svg new file mode 100644 index 0000000..62cfb73 --- /dev/null +++ b/src/main/webapp/public/flags/svg/to.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tr.svg b/src/main/webapp/public/flags/svg/tr.svg new file mode 100644 index 0000000..5b046e6 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tt.svg b/src/main/webapp/public/flags/svg/tt.svg new file mode 100644 index 0000000..7479d1b --- /dev/null +++ b/src/main/webapp/public/flags/svg/tt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tv.svg b/src/main/webapp/public/flags/svg/tv.svg new file mode 100644 index 0000000..b3f9308 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tw.svg b/src/main/webapp/public/flags/svg/tw.svg new file mode 100644 index 0000000..500d0f3 --- /dev/null +++ b/src/main/webapp/public/flags/svg/tw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/tz.svg b/src/main/webapp/public/flags/svg/tz.svg new file mode 100644 index 0000000..5cbeb7a --- /dev/null +++ b/src/main/webapp/public/flags/svg/tz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ua.svg b/src/main/webapp/public/flags/svg/ua.svg new file mode 100644 index 0000000..4d09b7c --- /dev/null +++ b/src/main/webapp/public/flags/svg/ua.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ug.svg b/src/main/webapp/public/flags/svg/ug.svg new file mode 100644 index 0000000..463d425 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/um.svg b/src/main/webapp/public/flags/svg/um.svg new file mode 100644 index 0000000..9735dfa --- /dev/null +++ b/src/main/webapp/public/flags/svg/um.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/us.svg b/src/main/webapp/public/flags/svg/us.svg new file mode 100644 index 0000000..9735dfa --- /dev/null +++ b/src/main/webapp/public/flags/svg/us.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/uy.svg b/src/main/webapp/public/flags/svg/uy.svg new file mode 100644 index 0000000..9f5b3d6 --- /dev/null +++ b/src/main/webapp/public/flags/svg/uy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/uz.svg b/src/main/webapp/public/flags/svg/uz.svg new file mode 100644 index 0000000..379ab12 --- /dev/null +++ b/src/main/webapp/public/flags/svg/uz.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/va.svg b/src/main/webapp/public/flags/svg/va.svg new file mode 100644 index 0000000..9101c9f --- /dev/null +++ b/src/main/webapp/public/flags/svg/va.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/vc.svg b/src/main/webapp/public/flags/svg/vc.svg new file mode 100644 index 0000000..f3892d7 --- /dev/null +++ b/src/main/webapp/public/flags/svg/vc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ve.svg b/src/main/webapp/public/flags/svg/ve.svg new file mode 100644 index 0000000..912a069 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/vg.svg b/src/main/webapp/public/flags/svg/vg.svg new file mode 100644 index 0000000..6d51bf8 --- /dev/null +++ b/src/main/webapp/public/flags/svg/vg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/vi.svg b/src/main/webapp/public/flags/svg/vi.svg new file mode 100644 index 0000000..606b364 --- /dev/null +++ b/src/main/webapp/public/flags/svg/vi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/vn.svg b/src/main/webapp/public/flags/svg/vn.svg new file mode 100644 index 0000000..691cd96 --- /dev/null +++ b/src/main/webapp/public/flags/svg/vn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/vu.svg b/src/main/webapp/public/flags/svg/vu.svg new file mode 100644 index 0000000..5e1cb1a --- /dev/null +++ b/src/main/webapp/public/flags/svg/vu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/wf.svg b/src/main/webapp/public/flags/svg/wf.svg new file mode 100644 index 0000000..5b7479d --- /dev/null +++ b/src/main/webapp/public/flags/svg/wf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ws.svg b/src/main/webapp/public/flags/svg/ws.svg new file mode 100644 index 0000000..d7d19c2 --- /dev/null +++ b/src/main/webapp/public/flags/svg/ws.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/xk.svg b/src/main/webapp/public/flags/svg/xk.svg new file mode 100644 index 0000000..e1c6eec --- /dev/null +++ b/src/main/webapp/public/flags/svg/xk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/ye.svg b/src/main/webapp/public/flags/svg/ye.svg new file mode 100644 index 0000000..c32376c --- /dev/null +++ b/src/main/webapp/public/flags/svg/ye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/yt.svg b/src/main/webapp/public/flags/svg/yt.svg new file mode 100644 index 0000000..67c9622 --- /dev/null +++ b/src/main/webapp/public/flags/svg/yt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/za.svg b/src/main/webapp/public/flags/svg/za.svg new file mode 100644 index 0000000..a00b44a --- /dev/null +++ b/src/main/webapp/public/flags/svg/za.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/zm.svg b/src/main/webapp/public/flags/svg/zm.svg new file mode 100644 index 0000000..65cc2dd --- /dev/null +++ b/src/main/webapp/public/flags/svg/zm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/flags/svg/zw.svg b/src/main/webapp/public/flags/svg/zw.svg new file mode 100644 index 0000000..92f8e25 --- /dev/null +++ b/src/main/webapp/public/flags/svg/zw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/public/img/171891.png b/src/main/webapp/public/img/171891.png new file mode 100644 index 0000000..4ffcd10 Binary files /dev/null and b/src/main/webapp/public/img/171891.png differ diff --git a/src/main/webapp/src/App.jsx b/src/main/webapp/src/App.jsx index ef74deb..6d89acc 100644 --- a/src/main/webapp/src/App.jsx +++ b/src/main/webapp/src/App.jsx @@ -1,4 +1,4 @@ -import {useEffect, useRef} from 'react' +import {lazy, Suspense, useEffect, useRef} from 'react' import {Nav} from "./components/Nav.jsx"; import {createBrowserRouter, Outlet, RouterProvider, useLocation, useRouteError} from "react-router-dom"; import {Home} from "./pages/Homepage.jsx"; @@ -15,6 +15,7 @@ import {DemandeAff, DemandeAffOk} from "./pages/DemandeAff.jsx"; import {MePage} from "./pages/MePage.jsx"; import {CompetitionRoot, getCompetitionChildren} from "./pages/competition/CompetitionRoot.jsx"; import {FallingLines} from "react-loader-spinner"; +import {getResultChildren, ResultRoot} from "./pages/result/ResultRoot.jsx"; const router = createBrowserRouter([ { @@ -54,6 +55,16 @@ const router = createBrowserRouter([ element: , children: getCompetitionChildren() }, + { + path: 'result', + element: , + children: getResultChildren() + }, + { + path: 'competition-manager/*', + element: , + //children: getCompetitionManagerChildren() + }, { path: 'me', element: @@ -66,6 +77,17 @@ const router = createBrowserRouter([ } ]) +function GetCompetitionMangerLazy() { + const CMLazy = lazy(() => import('./pages/competition/editor/CompetitionManagerRoot.jsx')) + return +

Compétition manager

+

Chargement...

+ }> + +
+} + function PageError() { const error = useRouteError() return
diff --git a/src/main/webapp/src/assets/SimpleIconsScore.ts b/src/main/webapp/src/assets/SimpleIconsScore.ts new file mode 100644 index 0000000..5be0353 --- /dev/null +++ b/src/main/webapp/src/assets/SimpleIconsScore.ts @@ -0,0 +1,26 @@ +import { + IconDefinition, + IconName, + IconPrefix +} from "@fortawesome/fontawesome-svg-core"; + +export const SimpleIconsScore: IconDefinition = { + icon: [ + // SVG viewbox width (in pixels) + 122.88, + + // SVG viewbox height (in pixels) + 100.08, + + // Aliases (not needed) + [], + + // Unicode as hex value (not needed) + "", + + // SVG path data + "M5.49,0h55.95h55.95c1.51,0,2.89,0.62,3.88,1.61c0.99,0.99,1.61,2.37,1.61,3.88v75.79c0,1.51-0.62,2.89-1.61,3.88 c-0.99,0.99-2.37,1.61-3.88,1.61h-25v12.15c0,0.64-0.52,1.16-1.16,1.16H31.66c-0.65,0-1.17-0.53-1.17-1.17V86.77h-25 c-1.51,0-2.89-0.62-3.88-1.61C0.62,84.17,0,82.8,0,81.28V5.49C0,3.98,0.62,2.6,1.61,1.61C2.6,0.62,3.98,0,5.49,0L5.49,0z M45.45,37.11v13.88c0,3.16-0.18,5.45-0.52,6.89c-0.34,1.45-1.05,2.79-2.13,4.05c-1.08,1.25-2.38,2.15-3.9,2.69 c-1.52,0.55-3.22,0.82-5.11,0.82c-2.48,0-4.54-0.29-6.19-0.86c-1.64-0.58-2.95-1.47-3.93-2.68c-0.97-1.22-1.67-2.5-2.08-3.84 c-0.41-1.35-0.61-3.49-0.61-6.42V37.11c0-3.83,0.33-6.69,0.99-8.59c0.66-1.9,1.97-3.43,3.93-4.58c1.96-1.15,4.33-1.72,7.12-1.72 c2.28,0,4.32,0.39,6.12,1.19c1.8,0.8,3.14,1.77,4.03,2.92c0.89,1.15,1.5,2.44,1.82,3.88C45.29,31.66,45.45,33.95,45.45,37.11 L45.45,37.11z M35.08,33.63c0-2.21-0.11-3.59-0.32-4.15c-0.21-0.55-0.71-0.83-1.51-0.83c-0.77,0-1.28,0.3-1.53,0.89 c-0.25,0.59-0.38,1.96-0.38,4.1v20.29c0,2.41,0.11,3.87,0.35,4.36c0.23,0.5,0.73,0.75,1.51,0.75c0.77,0,1.27-0.29,1.52-0.88 c0.24-0.58,0.36-1.89,0.36-3.92V33.63L35.08,33.63z M98.87,23.01v41.64H88.49V42.3c0-3.23-0.07-5.18-0.23-5.83 c-0.15-0.65-0.58-1.15-1.27-1.48c-0.69-0.33-2.24-0.5-4.63-0.5h-1.03v-4.83c5.02-1.07,8.83-3.29,11.42-6.64H98.87L98.87,23.01z M64.96,7.05v72.68h50.87V7.05H64.96L64.96,7.05z M57.92,79.73V7.05H7.05v72.68H57.92L57.92,79.73z" + ], + iconName: "simple-icons-score" as IconName, + prefix: "simple-icons" as IconPrefix +}; diff --git a/src/main/webapp/src/components/Nav.jsx b/src/main/webapp/src/components/Nav.jsx index c8c53f6..e6fd02b 100644 --- a/src/main/webapp/src/components/Nav.jsx +++ b/src/main/webapp/src/components/Nav.jsx @@ -21,6 +21,7 @@ export function Nav() {