Compare commits

..

No commits in common. "89d9e04a6f011cf3c9a61f0e54b70f25a19239ea" and "172dcdaa6737829da4feee1df79273774b259910" have entirely different histories.

25 changed files with 129 additions and 681 deletions

View File

@ -45,12 +45,6 @@ public class CategoryModel {
String liceName = "1"; String liceName = "1";
@Column(nullable = false, columnDefinition = "boolean default false")
boolean treeAreClassement = false;
@Column(nullable = false, columnDefinition = "boolean default false")
boolean fullClassement = false;
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_preset", referencedColumnName = "id") @JoinColumn(name = "id_preset", referencedColumnName = "id")
CatPresetModel preset; CatPresetModel preset;

View File

@ -109,22 +109,6 @@ public class MatchModel {
return sum; return sum;
} }
public CombModel getC1() {
if (this.c1_id != null)
return this.c1_id;
if (this.c1_guest != null)
return this.c1_guest;
return null;
}
public CombModel getC2() {
if (this.c2_id != null)
return this.c2_id;
if (this.c2_guest != null)
return this.c2_guest;
return null;
}
public boolean isC1(Object comb) { public boolean isC1(Object comb) {
if (this.c1_guest != null && this.c1_guest.isInTeam(comb)) if (this.c1_guest != null && this.c1_guest.isInTeam(comb))
return true; return true;

View File

@ -40,14 +40,6 @@ public class TreeModel {
@JoinColumn(referencedColumnName = "id") @JoinColumn(referencedColumnName = "id")
TreeModel right; TreeModel right;
public TreeModel(Long category, Integer level, MatchModel match) {
this.category = category;
this.level = level;
this.match = match;
this.left = null;
this.right = null;
}
public List<TreeModel> flat() { public List<TreeModel> flat() {
List<TreeModel> out = new ArrayList<>(); List<TreeModel> out = new ArrayList<>();
this.flat(out); this.flat(out);
@ -63,44 +55,4 @@ public class TreeModel {
if (this.left != null) if (this.left != null)
this.left.flat(out); this.left.flat(out);
} }
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);
}
public int getMaxChildrenAtDepth(int death, int current) {
if (current == death)
return 1;
int tmp = 0;
if (this.right != null)
tmp += this.right.getMaxChildrenAtDepth(death, current + 1);
if (this.left != null)
tmp += this.left.getMaxChildrenAtDepth(death, current + 1);
return tmp;
}
public void getChildrenAtDepth (int death, int current, List<TreeModel> out) {
if (current == death) {
out.add(this);
return;
}
if (this.right != null)
this.right.getChildrenAtDepth(death, current + 1, out);
if (this.left != null)
this.left.getChildrenAtDepth(death, current + 1, out);
}
} }

View File

@ -20,9 +20,6 @@ public class MatchRepository implements PanacheRepositoryBase<MatchModel, Long>
} }
public Uni<Void> create(List<MatchModel> matchModel) { public Uni<Void> create(List<MatchModel> matchModel) {
if (matchModel.isEmpty())
return Uni.createFrom().voidItem();
matchModel.forEach(model -> model.setSystem(CompetitionSystem.INTERNAL)); matchModel.forEach(model -> model.setSystem(CompetitionSystem.INTERNAL));
return Panache.withTransaction(() -> this.persist(matchModel) return Panache.withTransaction(() -> this.persist(matchModel)
.call(__ -> this.flush()) .call(__ -> this.flush())

View File

@ -173,12 +173,4 @@ public class MatchModelExtend {
public boolean isC2(Object comb) { public boolean isC2(Object comb) {
return match.isC2(comb); return match.isC2(comb);
} }
public CombModel getC1() {
return match.getC1();
}
public CombModel getC2() {
return match.getC2();
}
} }

View File

@ -151,16 +151,14 @@ public class ResultService {
out.setLiceName(categoryModel.getLiceName() == null ? new String[]{} : categoryModel.getLiceName() out.setLiceName(categoryModel.getLiceName() == null ? new String[]{} : categoryModel.getLiceName()
.split(";")); .split(";"));
out.setGenTime(System.currentTimeMillis()); out.setGenTime(System.currentTimeMillis());
out.setTreeIsClassement(categoryModel.isTreeAreClassement());
getArray2(matchModels, membreModel, out); getArray2(matchModels, membreModel, out);
getTree(categoryModel.getTree(), membreModel, cards, out); getTree(categoryModel.getTree(), membreModel, cards, out);
getClassementArray(categoryModel, membreModel, cards, out);
return out; return out;
}); });
} }
public void getArray2(List<MatchModelExtend> matchModels_, MembreModel membreModel, ResultCategoryData out) { private void getArray2(List<MatchModelExtend> matchModels_, MembreModel membreModel, ResultCategoryData out) {
List<MatchModelExtend> matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList(); List<MatchModelExtend> matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList();
HashMap<Character, List<MatchModelExtend>> matchMap = new HashMap<>(); HashMap<Character, List<MatchModelExtend>> matchMap = new HashMap<>();
@ -184,7 +182,7 @@ public class ResultService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(comb -> { .map(comb -> {
CombStat stat = makeStat(matchEntities, comb); CombStat stat = makeStat(matchEntities, comb);
return new ResultCategoryData.RankArray(0, comb, return new ResultCategoryData.RankArray(0,
comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS), stat.score, stat.w, comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS), stat.score, stat.w,
stat.pointMake, stat.pointTake, stat.getPointRate()); stat.pointMake, stat.pointTake, stat.getPointRate());
}) })
@ -212,66 +210,10 @@ public class ResultService {
}); });
} }
private void getClassementArray(CategoryModel categoryModel, MembreModel membreModel, List<CardModel> cards,
ResultCategoryData out) {
if ((categoryModel.getType() & 2) != 0) {
AtomicInteger rank = new AtomicInteger(0);
categoryModel.getTree().stream()
.filter(t -> t.getLevel() > 0)
.sorted(Comparator.comparing(TreeModel::getLevel))
.forEach(treeModel -> makeClassementRow(membreModel,
new MatchModelExtend(treeModel.getMatch(), cards), out, rank));
categoryModel.getTree().stream()
.filter(t -> t.getLevel() <= -10)
.sorted(Comparator.comparing(TreeModel::getLevel).reversed())
.forEach(treeModel -> makeClassementRow(membreModel,
new MatchModelExtend(treeModel.getMatch(), cards), out, rank));
} else {
for (List<ResultCategoryData.RankArray> list : out.getRankArray().values()) {
for (ResultCategoryData.RankArray r : list) {
out.getClassement().add(new ResultCategoryData.ClassementData(r.getRank(), r.getComb(), r.getName()));
}
}
}
}
private static void makeClassementRow(MembreModel membreModel, MatchModelExtend m, ResultCategoryData out,
AtomicInteger rank) {
if (m.isEnd()) {
if (m.getWin() > 0) {
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC1(),
m.getC1Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC2(),
m.getC2Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
} else if (m.getWin() < 0) {
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC2(),
m.getC2Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC1(),
m.getC1Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
} else {
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC1(),
m.getC1Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.getAndIncrement(), m.getC2(),
m.getC2Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
}
} else {
out.getClassement().add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), null, null));
out.getClassement().add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), null, null));
}
}
private static void convertTree(TreeModel src, TreeNode<ResultCategoryData.TreeData> dst, MembreModel membreModel, private static void convertTree(TreeModel src, TreeNode<ResultCategoryData.TreeData> dst, MembreModel membreModel,
ResultPrivacy privacy, List<CardModel> cards) { ResultPrivacy privacy, List<CardModel> cards) {
dst.setData( dst.setData(
ResultCategoryData.TreeData.from(new MatchModelExtend(src.getMatch(), cards), src.getLevel(), ResultCategoryData.TreeData.from(new MatchModelExtend(src.getMatch(), cards), membreModel, privacy));
membreModel, privacy));
if (src.getLeft() != null) { if (src.getLeft() != null) {
dst.setLeft(new TreeNode<>()); dst.setLeft(new TreeNode<>());
convertTree(src.getLeft(), dst.getLeft(), membreModel, privacy, cards); convertTree(src.getLeft(), dst.getLeft(), membreModel, privacy, cards);

View File

@ -1,12 +1,10 @@
package fr.titionfire.ffsaf.domain.service; package fr.titionfire.ffsaf.domain.service;
import io.quarkus.websockets.next.WebSocketConnection;
import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestContext;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.MissingResourceException; import java.util.MissingResourceException;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -23,20 +21,11 @@ public class TradService {
return translate(key); return translate(key);
} }
public String t(String key, WebSocketConnection connection) {
List<String> lang = connection.handshakeRequest().headers().get("Accept-Language");
Locale userLocale = lang != null && !lang.isEmpty() ? Locale.forLanguageTag(lang.get(0)) : fallbackLocale;
return translate(key, userLocale);
}
public String translate(String key) { public String translate(String key) {
ContainerRequestContext requestContext = requestContextInstance.get(); ContainerRequestContext requestContext = requestContextInstance.get();
Locale userLocale = (Locale) requestContext.getProperty("userLocale"); Locale userLocale = (Locale) requestContext.getProperty("userLocale");
return translate(key, userLocale);
}
public String translate(String key, Locale userLocale) {
try { try {
ResourceBundle messages = ResourceBundle.getBundle("lang.messages", userLocale); ResourceBundle messages = ResourceBundle.getBundle("lang.messages", userLocale);
return messages.getString(key); return messages.getString(key);

View File

@ -1,7 +1,5 @@
package fr.titionfire.ffsaf.rest.data; package fr.titionfire.ffsaf.rest.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import fr.titionfire.ffsaf.data.model.CombModel;
import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend; import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
import fr.titionfire.ffsaf.utils.ResultPrivacy; import fr.titionfire.ffsaf.utils.ResultPrivacy;
@ -23,11 +21,9 @@ import java.util.List;
public class ResultCategoryData { public class ResultCategoryData {
int type; int type;
String name; String name;
boolean treeIsClassement;
HashMap<Character, List<PouleArrayData>> matchs = new HashMap<>(); HashMap<Character, List<PouleArrayData>> matchs = new HashMap<>();
HashMap<Character, List<RankArray>> rankArray = new HashMap<>(); HashMap<Character, List<RankArray>> rankArray = new HashMap<>();
ArrayList<TreeNode<TreeData>> trees; ArrayList<TreeNode<TreeData>> trees;
List<ClassementData> classement = new ArrayList<>();
String[] liceName; String[] liceName;
long genTime; long genTime;
@ -36,8 +32,6 @@ public class ResultCategoryData {
@RegisterForReflection @RegisterForReflection
public static class RankArray { public static class RankArray {
int rank; int rank;
@JsonIgnore
CombModel comb;
String name; String name;
int score; int score;
int win; int win;
@ -49,14 +43,12 @@ public class ResultCategoryData {
@RegisterForReflection @RegisterForReflection
public record PouleArrayData(String red, boolean red_w, List<Integer[]> score, boolean blue_w, String blue, public record PouleArrayData(String red, boolean red_w, List<Integer[]> score, boolean blue_w, String blue,
boolean eq, boolean end, Date date) { boolean eq, boolean end, Date date) {
public static PouleArrayData fromModel(MatchModelExtend matchModel, MembreModel membreModel, public static PouleArrayData fromModel(MatchModelExtend matchModel, MembreModel membreModel, ResultPrivacy privacy) {
ResultPrivacy privacy) {
return new PouleArrayData( return new PouleArrayData(
matchModel.getC1Name(membreModel, privacy), matchModel.getC1Name(membreModel, privacy),
matchModel.isEnd() && matchModel.getWin() > 0, matchModel.isEnd() && matchModel.getWin() > 0,
matchModel.isEnd() ? matchModel.isEnd() ?
matchModel.getScoresToPrint().stream().map(s -> new Integer[]{s.getS1(), s.getS2()}) matchModel.getScoresToPrint().stream().map(s -> new Integer[]{s.getS1(), s.getS2()}).toList()
.toList()
: new ArrayList<>(), : new ArrayList<>(),
matchModel.isEnd() && matchModel.getWin() < 0, matchModel.isEnd() && matchModel.getWin() < 0,
matchModel.getC2Name(membreModel, privacy), matchModel.getC2Name(membreModel, privacy),
@ -68,16 +60,10 @@ public class ResultCategoryData {
@RegisterForReflection @RegisterForReflection
public static record TreeData(long id, String c1FullName, String c2FullName, List<ScoreEmbeddable> scores, public static record TreeData(long id, String c1FullName, String c2FullName, List<ScoreEmbeddable> scores,
boolean end, int win, int level, Date date) { boolean end, int win) {
public static TreeData from(MatchModelExtend match, int level, MembreModel membreModel, ResultPrivacy privacy) { public static TreeData from(MatchModelExtend match, MembreModel membreModel, ResultPrivacy privacy) {
return new TreeData(match.getId(), match.getC1Name(membreModel, privacy), return new TreeData(match.getId(), match.getC1Name(membreModel, privacy),
match.getC2Name(membreModel, privacy), match.getScoresToPrint(), match.isEnd(), match.getWin(), match.getC2Name(membreModel, privacy), match.getScoresToPrint(), match.isEnd(), match.getWin());
level, match.getDate());
} }
} }
@RegisterForReflection
public static record ClassementData(int rank, @JsonIgnore CombModel comb, String name) {
}
} }

View File

@ -51,9 +51,9 @@ public class RCard {
return matchRepository.findById(id) return matchRepository.findById(id)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException(trad.t("matche.non.trouver", connection)); throw new DNotFoundException(trad.t("matche.non.trouver"));
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied", connection)); throw new DForbiddenException(trad.t("permission.denied"));
})); }));
} }
@ -103,7 +103,7 @@ public class RCard {
.firstResult() .firstResult()
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException(trad.t("carton.non.trouver", connection)); throw new DNotFoundException(trad.t("carton.non.trouver"));
SSCard.sendRmCards(connection, List.of(o.getId())); SSCard.sendRmCards(connection, List.of(o.getId()));
})) }))
.chain(cardModel -> Panache.withTransaction(() -> cardRepository.delete(cardModel))) .chain(cardModel -> Panache.withTransaction(() -> cardRepository.delete(cardModel)))

View File

@ -1,25 +1,22 @@
package fr.titionfire.ffsaf.ws.recv; package fr.titionfire.ffsaf.ws.recv;
import fr.titionfire.ffsaf.data.model.*; import fr.titionfire.ffsaf.data.model.CardModel;
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.*; import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.domain.entity.MatchEntity; import fr.titionfire.ffsaf.domain.entity.MatchEntity;
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
import fr.titionfire.ffsaf.domain.entity.TreeEntity; import fr.titionfire.ffsaf.domain.entity.TreeEntity;
import fr.titionfire.ffsaf.domain.service.CardService; import fr.titionfire.ffsaf.domain.service.CardService;
import fr.titionfire.ffsaf.domain.service.ResultService;
import fr.titionfire.ffsaf.domain.service.TradService; import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.data.PresetData;
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.TreeNode; import fr.titionfire.ffsaf.utils.TreeNode;
import fr.titionfire.ffsaf.ws.PermLevel; import fr.titionfire.ffsaf.ws.PermLevel;
import fr.titionfire.ffsaf.ws.send.SSCategorie; import fr.titionfire.ffsaf.ws.send.SSCategorie;
import fr.titionfire.ffsaf.ws.send.SSMatch;
import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.websockets.next.UserData;
import io.quarkus.websockets.next.WebSocketConnection; import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked; import io.smallrye.mutiny.unchecked.Unchecked;
@ -28,8 +25,8 @@ import jakarta.inject.Inject;
import lombok.Data; import lombok.Data;
import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny;
import java.util.*; import java.util.ArrayList;
import java.util.stream.Stream; import java.util.List;
@WithSession @WithSession
@ApplicationScoped @ApplicationScoped
@ -52,12 +49,6 @@ public class RCategorie {
@Inject @Inject
CardService cardService; CardService cardService;
@Inject
CardRepository cardRepository;
@Inject
ResultService resultService;
@Inject @Inject
TradService trad; TradService trad;
@ -65,9 +56,9 @@ public class RCategorie {
return categoryRepository.findById(id) return categoryRepository.findById(id)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException(trad.t("categorie.non.trouver", connection)); throw new DNotFoundException(trad.t("categorie.non.trouver"));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied", connection)); throw new DForbiddenException(trad.t("permission.denied"));
})); }));
} }
@ -87,9 +78,6 @@ public class RCategorie {
fullCategory.setName(cat.getName()); fullCategory.setName(cat.getName());
fullCategory.setLiceName(cat.getLiceName()); fullCategory.setLiceName(cat.getLiceName());
fullCategory.setType(cat.getType()); fullCategory.setType(cat.getType());
fullCategory.setTreeAreClassement(cat.isTreeAreClassement());
fullCategory.setFullClassement(cat.isFullClassement());
fullCategory.setPreset(PresetData.fromModel(cat.getPreset()));
}) })
.call(cat -> Mutiny.fetch(cat.getMatchs()) .call(cat -> Mutiny.fetch(cat.getMatchs())
.map(matchModels -> matchModels.stream().filter(o -> o.getCategory_ord() >= 0) .map(matchModels -> matchModels.stream().filter(o -> o.getCategory_ord() >= 0)
@ -128,9 +116,6 @@ public class RCategorie {
cat.setName(categorie.name); cat.setName(categorie.name);
cat.setLiceName(categorie.liceName); cat.setLiceName(categorie.liceName);
cat.setType(categorie.type); cat.setType(categorie.type);
cat.setTreeAreClassement(categorie.treeAreClassement);
cat.setFullClassement(categorie.fullClassement);
// cat.setPreset(cat.getPreset()); //TODO preset update
return Panache.withTransaction(() -> categoryRepository.persist(cat)); return Panache.withTransaction(() -> categoryRepository.persist(cat));
}) })
.call(cat -> { .call(cat -> {
@ -239,149 +224,10 @@ public class RCategorie {
.replaceWithVoid(); .replaceWithVoid();
} }
@WSReceiver(code = "createClassementMatchs", permission = PermLevel.TABLE)
public Uni<Void> createClassementMatchs(WebSocketConnection connection, Long categoryId) {
return getById(categoryId, connection)
.call(cat -> {
PermLevel perm = PermLevel.valueOf(connection.userData().get(UserData.TypedKey.forString("prem")));
if (perm == PermLevel.TABLE) {
return matchRepository.list("category = ?1 AND category_ord = -42", cat.getId())
.chain(l -> l.stream().anyMatch(MatchModel::isEnd) ?
Uni.createFrom().failure(
new DForbiddenException(trad.t("err.match.termine", connection))) :
Uni.createFrom().voidItem());
}
return Uni.createFrom().voidItem();
})
.call(cat -> treeRepository.list("category = ?1 AND level <= -10", cat.getId())
.map(l -> l.stream().map(o -> o.getMatch().getId()).toList())
.call(__ -> treeRepository.delete("category = ?1 AND level <= -10", cat.getId()))
.call(ids -> matchRepository.delete("id IN ?1", ids)))
.call(cat -> Mutiny.fetch(cat.getTree()))
.call(cat -> {
List<MatchModel> toSave = new ArrayList<>();
if (!cat.getTree().isEmpty()) {
for (TreeModel treeModel : cat.getTree())
cleanTree(treeModel, toSave);
}
return Panache.withTransaction(() -> matchRepository.persist(toSave))
.invoke(__ -> SSMatch.sendMatch(connection,
toSave.stream().map(MatchEntity::fromModel).toList()));
})
.chain(cat -> Mutiny.fetch(cat.getMatchs())
.chain(list -> cardRepository.list("competition = ?1", cat.getCompet())
.map(c -> list.stream().map(m -> new MatchModelExtend(m, c)).toList()))
.map(matchModels -> {
ResultCategoryData out = new ResultCategoryData();
resultService.getArray2(matchModels, null, out);
out.getRankArray().remove('-');
return out;
})
.invoke(Unchecked.consumer(result -> {
if (result.getRankArray().size() != 2) {
throw new DForbiddenException(trad.t("configuration.non.supportee", connection));
}
}))
.chain(result -> {
List<MatchModel> toSave = new ArrayList<>();
List<MatchModel> toCreate = new ArrayList<>();
List<TreeModel> toSaveTree = new ArrayList<>();
int treeSize = 0;
List<TreeModel> lastNode = new ArrayList<>();
Optional<TreeModel> tree = cat.getTree().stream().filter(t -> t.getLevel() > 0)
.min(Comparator.comparing(TreeModel::getLevel));
if (tree.isPresent()) {
tree.get().getChildrenAtDepth(tree.get().death() - 1, 0, lastNode);
treeSize = lastNode.size();
}
Iterator<List<ResultCategoryData.RankArray>> iterator = result.getRankArray().values()
.iterator();
List<ResultCategoryData.RankArray> poule1 = iterator.next();
List<ResultCategoryData.RankArray> poule2 = iterator.next();
int maxToMixFill = Math.min(poule1.size(), poule2.size());
int maxToMixTreeFill = Math.min(treeSize, maxToMixFill);
for (int i = 0; i < maxToMixTreeFill; i++) {
CombModel comb1 = poule1.get(i).getComb();
CombModel comb2 = poule2.get(treeSize - i - 1).getComb();
fillMatchComb(lastNode.get(i).getMatch(), comb1, comb2);
toSave.add(lastNode.get(i).getMatch());
}
if (cat.isFullClassement()) {
for (int i = maxToMixTreeFill; i < maxToMixFill; i++) {
MatchModel match = new MatchModel();
match.setCategory(cat);
match.setCategory_ord(-42);
match.setEnd(false);
CombModel comb1 = poule1.get(i).getComb();
CombModel comb2 = poule2.get(i).getComb();
fillMatchComb(match, comb1, comb2);
toCreate.add(match);
toSaveTree.add(new TreeModel(cat.getId(), -10 - i, match));
}
}
return Panache.withTransaction(() -> matchRepository.persist(toSave)
.call(__ -> matchRepository.create(toCreate)))
.call(__ -> toSaveTree.isEmpty() ? Uni.createFrom().voidItem()
: Panache.withTransaction(() -> treeRepository.persist(toSaveTree)))
.map(__ -> Stream.concat(toSave.stream(), toCreate.stream())
.map(MatchEntity::fromModel).toList());
})
)
.onFailure().invoke(t -> System.out.println("error: " + t.getMessage()))
.invoke(matchEntities -> SSMatch.sendMatch(connection, matchEntities))
.call(__ -> treeRepository.list("category = ?1 AND level != 0", categoryId)
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
.invoke(trees -> SSCategorie.sendTreeCategory(connection, trees)))
.replaceWithVoid();
}
private void cleanTree(TreeModel treeModel, List<MatchModel> toSave) {
MatchModel model = treeModel.getMatch();
if (model != null) {
model.setC1_id(null);
model.setC1_guest(null);
model.setC2_id(null);
model.setC2_guest(null);
model.setEnd(false);
model.setDate(null);
model.getScores().clear();
toSave.add(model);
}
if (treeModel.getLeft() != null)
cleanTree(treeModel.getLeft(), toSave);
if (treeModel.getRight() != null)
cleanTree(treeModel.getRight(), toSave);
}
private void fillMatchComb(MatchModel match, CombModel comb1, CombModel comb2) {
if (comb1 instanceof MembreModel m)
match.setC1_id(m);
else if (comb1 instanceof CompetitionGuestModel g)
match.setC1_guest(g);
if (comb2 instanceof MembreModel m)
match.setC2_id(m);
else if (comb2 instanceof CompetitionGuestModel g)
match.setC2_guest(g);
}
@RegisterForReflection @RegisterForReflection
public record JustCategorie(long id, String name, int type, String liceName, boolean treeAreClassement, public record JustCategorie(long id, String name, int type, String liceName) {
boolean fullClassement, PresetData preset) {
public static JustCategorie from(CategoryModel m) { public static JustCategorie from(CategoryModel m) {
return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName(), m.isTreeAreClassement(), return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName());
m.isFullClassement(), PresetData.fromModel(m.getPreset()));
} }
} }
@ -396,9 +242,6 @@ public class RCategorie {
String name; String name;
int type; int type;
String liceName; String liceName;
boolean treeAreClassement = false;
boolean fullClassement = false;
PresetData preset;
List<TreeEntity> trees = null; List<TreeEntity> trees = null;
List<MatchEntity> matches; List<MatchEntity> matches;
List<CardModel> cards; List<CardModel> cards;

View File

@ -55,9 +55,9 @@ public class RMatch {
return matchRepository.findById(id) return matchRepository.findById(id)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException(trad.t("matche.non.trouver", connection)); throw new DNotFoundException(trad.t("matche.non.trouver"));
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied", connection)); throw new DForbiddenException(trad.t("permission.denied"));
})); }));
} }
@ -85,9 +85,9 @@ public class RMatch {
return categoryRepository.findById(m.categorie) return categoryRepository.findById(m.categorie)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException(trad.t("categorie.non.trouver", connection)); throw new DNotFoundException(trad.t("categorie.non.trouver"));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied", connection)); throw new DForbiddenException(trad.t("permission.denied"));
})) }))
.chain(categoryModel -> creatMatch(categoryModel, m)) .chain(categoryModel -> creatMatch(categoryModel, m))
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm))) .chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)))
@ -297,9 +297,9 @@ public class RMatch {
return categoryRepository.findById(data.categorie) return categoryRepository.findById(data.categorie)
.invoke(Unchecked.consumer(o -> { .invoke(Unchecked.consumer(o -> {
if (o == null) if (o == null)
throw new DNotFoundException(trad.t("categorie.non.trouver", connection)); throw new DNotFoundException(trad.t("categorie.non.trouver"));
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid"))) if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
throw new DForbiddenException(trad.t("permission.denied", connection)); throw new DForbiddenException(trad.t("permission.denied"));
})) }))
.call(cm -> data.matchesToRemove.isEmpty() ? Uni.createFrom().voidItem() : .call(cm -> data.matchesToRemove.isEmpty() ? Uni.createFrom().voidItem() :
(Panache.withTransaction( (Panache.withTransaction(

View File

@ -87,5 +87,3 @@ licence.membre.n.3.inconnue=License member no. 3 unknown
demande.d.affiliation.non.trouve=Affiliation request not found demande.d.affiliation.non.trouve=Affiliation request not found
carton.non.trouver=Card not found carton.non.trouver=Card not found
card.cannot.be.added=Unable to add the card card.cannot.be.added=Unable to add the card
configuration.non.supportee=Unsupported configuration
err.match.termine=Error, a placement match has already been played

View File

@ -83,5 +83,3 @@ licence.membre.n.3.inconnue=Licence du membre n
demande.d.affiliation.non.trouve=Demande d'affiliation introuvable demande.d.affiliation.non.trouve=Demande d'affiliation introuvable
carton.non.trouver=Carton introuvable carton.non.trouver=Carton introuvable
card.cannot.be.added=Impossible d'ajouter le carton card.cannot.be.added=Impossible d'ajouter le carton
configuration.non.supportee=Configuration non supportée
err.match.termine=Erreur, un match de classement a déjà été joué

View File

@ -104,13 +104,8 @@ function scorePrint(s1) {
return String(s1); return String(s1);
} }
} }
function scoreToString(score) { function scoreToString(score) {
if (score.length === 0) return score.map(o => scorePrint(o.at(0)) + "-" + scorePrint(o.at(1))).join(" | ");
return "";
if (score.at(0) instanceof Array)
return score.map(s => scorePrint(s.at(0)) + "-" + scorePrint(s.at(1))).join(" | ");
return score.map(o => scorePrint(o.s1) + "-" + scorePrint(o.s2)).join(" | ");
} }
function dateToString(date) { function dateToString(date) {
@ -134,7 +129,7 @@ function dateToString(date) {
return date_.toLocaleDateString(); return date_.toLocaleDateString();
} }
function buildPouleMenu(isPoule, change_view, isClassement = false) { function buildPouleMenu(isPoule, change_view) {
const menuDiv = document.createElement('div'); const menuDiv = document.createElement('div');
menuDiv.id = 'menu'; menuDiv.id = 'menu';
menuDiv.style.borderBottom = '1px solid #9EA0A1'; menuDiv.style.borderBottom = '1px solid #9EA0A1';
@ -193,7 +188,7 @@ function buildPouleMenu(isPoule, change_view, isClassement = false) {
change_view(true); change_view(true);
}); });
ul.appendChild(li1); ul.appendChild(li1);
const li2 = createTab(isClassement ? i18next.t('classement') : i18next.t('tournois'), !isPoule, function () { const li2 = createTab(i18next.t('tournois'), !isPoule, function () {
change_view(false); change_view(false);
}); });
ul.appendChild(li2); ul.appendChild(li2);
@ -267,7 +262,7 @@ function buildRankArray(rankArray) {
} }
function buildTree(treeData) { function buildTree(treeData) {
return drawGraph(initTree(treeData.filter(d => d.data.level >= 0))) return drawGraph(initTree(treeData))
} }
function poulePage(location) { function poulePage(location) {
@ -315,7 +310,7 @@ function poulePage(location) {
dataContainer.append(buildTree(poule['trees'])); dataContainer.append(buildTree(poule['trees']));
} else { } else {
const change_view = (isPoule) => { const change_view = (isPoule) => {
dataContainer.replaceChildren(buildPouleMenu(isPoule, change_view, poule['treeIsClassement'])); dataContainer.replaceChildren(buildPouleMenu(isPoule, change_view));
if (isPoule) { if (isPoule) {
for (const g in poule.matchs) { for (const g in poule.matchs) {
@ -330,19 +325,6 @@ function poulePage(location) {
} }
} else { } else {
dataContainer.append(buildTree(poule['trees'])); dataContainer.append(buildTree(poule['trees']));
if (poule['treeIsClassement'] && poule['trees'].some(d => d.data.level <= -10)) {
dataContainer.append(buildMatchArray(
poule['trees'].filter(d => d.data.level < 0).reverse().map(d => ({
red: d.data.c1FullName,
blue: d.data.c2FullName,
score: d.data.scores,
end: d.data.end,
red_w: d.data.win > 0,
blue_w: d.data.win < 0,
eq: d.data.win === 0,
date: d.data.date,
}))));
}
} }
location[2] = isPoule ? 1 : 2; location[2] = isPoule ? 1 : 2;

View File

@ -35,7 +35,6 @@
"chrono.entrezLeTempsEnS": "Enter time in seconds", "chrono.entrezLeTempsEnS": "Enter time in seconds",
"chrono.recapTemps": "Time: {{temps}}, pause: {{pause}}", "chrono.recapTemps": "Time: {{temps}}, pause: {{pause}}",
"chronomètre": "Stopwatch", "chronomètre": "Stopwatch",
"classement": "Ranking",
"club": "Club", "club": "Club",
"compétition": "Competition", "compétition": "Competition",
"compétitionManager": "Competition manager", "compétitionManager": "Competition manager",
@ -56,8 +55,6 @@
"conserverUniquementLesMatchsTerminés": "Keep only finished matches", "conserverUniquementLesMatchsTerminés": "Keep only finished matches",
"contre": "vs", "contre": "vs",
"couleur": "Color", "couleur": "Color",
"créerLesMatchesDeClassement": "Create the ranking matches",
"créerLesMatchesDeClassement.msg": "Ranking matches have already been set up/played; recreating these matches will delete them all (you will therefore lose any results). Please note down any information you wish to keep.",
"créerLesMatchs": "Create matches", "créerLesMatchs": "Create matches",
"date": "Date", "date": "Date",
"demi-finalesEtFinales": "Semi-finals and finals", "demi-finalesEtFinales": "Semi-finals and finals",
@ -83,8 +80,6 @@
"genre.na": "NA", "genre.na": "NA",
"individuelle": "Individual", "individuelle": "Individual",
"inscrit": "Registered", "inscrit": "Registered",
"leTournoiServiraDePhaseFinaleAuxPoules": "The tournament will serve as the final phase for the group stage.",
"lesCombattantsEnDehors": "Fighters not participating in the tournament will have a ranking match.",
"listeDesCartons": "List of cards", "listeDesCartons": "List of cards",
"manche": "Round", "manche": "Round",
"matchPourLesPerdantsDuTournoi": "Match for tournament losers:", "matchPourLesPerdantsDuTournoi": "Match for tournament losers:",
@ -139,9 +134,6 @@
"toast.deleteCategory.error": "Error while deleting the category", "toast.deleteCategory.error": "Error while deleting the category",
"toast.deleteCategory.pending": "Deleting category...", "toast.deleteCategory.pending": "Deleting category...",
"toast.deleteCategory.success": "Category deleted!", "toast.deleteCategory.success": "Category deleted!",
"toast.matchs.classement.create.error": "Error while creating ranking matches.",
"toast.matchs.classement.create.pending": "Creating ranking matches in progress...",
"toast.matchs.classement.create.success": "Ranking matches created successfully.",
"toast.matchs.create.error": "Error while creating matches.", "toast.matchs.create.error": "Error while creating matches.",
"toast.matchs.create.pending": "Creating matches in progress...", "toast.matchs.create.pending": "Creating matches in progress...",
"toast.matchs.create.success": "Matches created successfully.", "toast.matchs.create.success": "Matches created successfully.",

View File

@ -10,7 +10,6 @@
"bleu": "Blue", "bleu": "Blue",
"catégorie": "Category", "catégorie": "Category",
"chargement": "Loading", "chargement": "Loading",
"classement": "Ranking",
"club": "Club", "club": "Club",
"combattant": "Fighter", "combattant": "Fighter",
"combattants": "Fighters", "combattants": "Fighters",
@ -65,6 +64,5 @@
"tournois": "Tournaments", "tournois": "Tournaments",
"tousLesCombattants": "All fighters", "tousLesCombattants": "All fighters",
"victoire": "Win", "victoire": "Win",
"victoires": "Wins", "victoires": "Wins"
"classementFinal": "Final standings"
} }

View File

@ -35,7 +35,6 @@
"chrono.entrezLeTempsEnS": "Entrez le temps en s", "chrono.entrezLeTempsEnS": "Entrez le temps en s",
"chrono.recapTemps": "Temps: {{temps}}, pause: {{pause}}", "chrono.recapTemps": "Temps: {{temps}}, pause: {{pause}}",
"chronomètre": "Chronomètre", "chronomètre": "Chronomètre",
"classement": "Classement",
"club": "Club", "club": "Club",
"compétition": "Compétition", "compétition": "Compétition",
"compétitionManager": "Compétition manager", "compétitionManager": "Compétition manager",
@ -56,8 +55,6 @@
"conserverUniquementLesMatchsTerminés": "Conserver uniquement les matchs terminés", "conserverUniquementLesMatchsTerminés": "Conserver uniquement les matchs terminés",
"contre": "contre", "contre": "contre",
"couleur": "Couleur", "couleur": "Couleur",
"créerLesMatchesDeClassement": "Créer les matches de classement",
"créerLesMatchesDeClassement.msg": "Des matches de classement ont déjà été configurer/jouer, la recréation de ces matches vont tous les supprimer (vous perdre donc les résultats s'il y en a). Mercie de noter de votre côté les informations que vous voulez conserver.",
"créerLesMatchs": "Créer les matchs", "créerLesMatchs": "Créer les matchs",
"date": "Date", "date": "Date",
"demi-finalesEtFinales": "Demi-finales et finales", "demi-finalesEtFinales": "Demi-finales et finales",
@ -83,8 +80,6 @@
"genre.na": "NA", "genre.na": "NA",
"individuelle": "Individuelle", "individuelle": "Individuelle",
"inscrit": "Inscrit", "inscrit": "Inscrit",
"leTournoiServiraDePhaseFinaleAuxPoules": "Le tournoi servira de phase finale aux poules",
"lesCombattantsEnDehors": "Les combattants en dehors du tournoi auront un match de classement",
"listeDesCartons": "Liste des cartons", "listeDesCartons": "Liste des cartons",
"manche": "Manche", "manche": "Manche",
"matchPourLesPerdantsDuTournoi": "Match pour les perdants du tournoi:", "matchPourLesPerdantsDuTournoi": "Match pour les perdants du tournoi:",
@ -139,9 +134,6 @@
"toast.deleteCategory.error": "Erreur lors de la suppression de la catégorie", "toast.deleteCategory.error": "Erreur lors de la suppression de la catégorie",
"toast.deleteCategory.pending": "Suppression de la catégorie...", "toast.deleteCategory.pending": "Suppression de la catégorie...",
"toast.deleteCategory.success": "Catégorie supprimée !", "toast.deleteCategory.success": "Catégorie supprimée !",
"toast.matchs.classement.create.error": "Erreur lors de la création des matchs de classement.",
"toast.matchs.classement.create.pending": "Création des matchs de classement en cours...",
"toast.matchs.classement.create.success": "Matchs de classement créés avec succès.",
"toast.matchs.create.error": "Erreur lors de la création des matchs.", "toast.matchs.create.error": "Erreur lors de la création des matchs.",
"toast.matchs.create.pending": "Création des matchs en cours...", "toast.matchs.create.pending": "Création des matchs en cours...",
"toast.matchs.create.success": "Matchs créés avec succès.", "toast.matchs.create.success": "Matchs créés avec succès.",

View File

@ -10,7 +10,6 @@
"bleu": "Bleu", "bleu": "Bleu",
"catégorie": "Catégorie", "catégorie": "Catégorie",
"chargement": "Chargement", "chargement": "Chargement",
"classement": "Classement",
"club": "Club", "club": "Club",
"combattant": "Combattant", "combattant": "Combattant",
"combattants": "Combattants", "combattants": "Combattants",
@ -65,6 +64,5 @@
"tournois": "Tournois", "tournois": "Tournois",
"tousLesCombattants": "Tous les combattants", "tousLesCombattants": "Tous les combattants",
"victoire": "Victoire", "victoire": "Victoire",
"victoires": "Victoires", "victoires": "Victoires"
"classementFinal": "Classement final"
} }

View File

@ -18,7 +18,7 @@ import {getToastMessage} from "../../../utils/Tools.js";
import {copyStyles} from "../../../utils/copyStyles.js"; import {copyStyles} from "../../../utils/copyStyles.js";
import {StateWindow} from "./StateWindow.jsx"; import {StateWindow} from "./StateWindow.jsx";
import {CombName, useCombs} from "../../../hooks/useComb.jsx"; import {CombName, useCombs} from "../../../hooks/useComb.jsx";
import {useCards, useCardsDispatch} from "../../../hooks/useCard.jsx"; import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
const vite_url = import.meta.env.VITE_URL; const vite_url = import.meta.env.VITE_URL;
@ -36,10 +36,7 @@ export function CMAdmin({compUuid}) {
...cat_, ...cat_,
name: data.name, name: data.name,
liceName: data.liceName, liceName: data.liceName,
type: data.type, type: data.type
treeAreClassement: data.treeAreClassement,
fullClassement: data.fullClassement,
preset: data.preset,
})) }))
} }
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}}) dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
@ -468,7 +465,7 @@ function TeamCardModal() {
<td><GetCard type={card.type}/></td> <td><GetCard type={card.type}/></td>
<td>{card.teamName}</td> <td>{card.teamName}</td>
</> : <> </> : <>
<td>{card.teamCard ? "|-> " + t('team') : t('individuelle')} </td> <td>{card.teamCard ? "|-> " + t('team'): t('individuelle')} </td>
<td><GetCard type={card.type}/></td> <td><GetCard type={card.type}/></td>
<td><CombName combId={card.comb}/></td> <td><CombName combId={card.comb}/></td>
</>} </>}
@ -572,7 +569,7 @@ function CategoryHeader({
</div> </div>
<div className="col" style={{margin: "auto 0", textAlign: "center"}}> <div className="col" style={{margin: "auto 0", textAlign: "center"}}>
{cat && {cat &&
<div>Type: {(cat.type & 1) !== 0 ? t('poule') : ""}{cat.type === 3 ? " & " : ""}{(cat.type & 2) !== 0 ? (cat.treeAreClassement ? t('classement') : t('tournois')) : ""} | <div>Type: {(cat.type & 1) !== 0 ? t('poule') : ""}{cat.type === 3 ? " & " : ""}{(cat.type & 2) !== 0 ? t('tournois') : ""} |
Zone: {cat.liceName}</div>} Zone: {cat.liceName}</div>}
</div> </div>
<div className="col-auto"> <div className="col-auto">
@ -606,8 +603,6 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
const [lice, setLice] = useState("1") const [lice, setLice] = useState("1")
const [poule, setPoule] = useState(true) const [poule, setPoule] = useState(true)
const [tournoi, setTournoi] = useState(false) const [tournoi, setTournoi] = useState(false)
const [classement, setClassement] = useState(true)
const [fullClassement, setFullClassement] = useState(false)
const [size, setSize] = useState(4) const [size, setSize] = useState(4)
const [loserMatch, setLoserMatch] = useState(1) const [loserMatch, setLoserMatch] = useState(1)
const {t} = useTranslation("cm"); const {t} = useTranslation("cm");
@ -619,17 +614,14 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
setLice(state.liceName || "1"); setLice(state.liceName || "1");
setPoule(((state.type || 1) & 1) !== 0); setPoule(((state.type || 1) & 1) !== 0);
setTournoi((state.type & 2) !== 0); setTournoi((state.type & 2) !== 0);
setClassement(state.treeAreClassement !== undefined && state.treeAreClassement !== false);
setFullClassement(state.fullClassement !== undefined && state.fullClassement !== false);
const trees_ = state?.trees ? state.trees.filter(t => t.level > 0) : null; if (state?.trees && state.trees.length >= 1) {
if (trees_ && trees_.length >= 1) { const tree = state.trees[0];
const tree = trees_[0];
setSize(tree.getMaxChildrenAtDepth(tree.death() - 1) * 2); setSize(tree.getMaxChildrenAtDepth(tree.death() - 1) * 2);
if (trees_.length === 1) { if (state.trees.length === 1) {
setLoserMatch(0); setLoserMatch(0);
} else if (trees_.length === 2) { } else if (state.trees.length === 2) {
setLoserMatch(1); setLoserMatch(1);
} else { } else {
setLoserMatch(-1); setLoserMatch(-1);
@ -655,27 +647,24 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
return; return;
} }
const trees_ = state?.trees ? state.trees.filter(t => t.level > 0) : null;
if (state?.id) { if (state?.id) {
const applyChanges = () => { const applyChanges = () => {
const newData = { const newData = {
id: state.id, id: state.id,
name: name.trim(), name: name.trim(),
liceName: lice.trim(), liceName: lice.trim(),
type: nType, type: nType
treeAreClassement: classement,
fullClassement: fullClassement
} }
let nbMatch = -1; let nbMatch = -1;
let oldSubTree = -1; let oldSubTree = -1;
const oldTrees = trees_ || []; const oldTrees = state?.trees || [];
if (oldTrees.length >= 1) { if (oldTrees.length >= 1) {
const tree = trees_[0]; const tree = state.trees[0];
nbMatch = tree.getMaxChildrenAtDepth(tree.death() - 1); nbMatch = tree.getMaxChildrenAtDepth(tree.death() - 1);
if (trees_.length === 1) if (state.trees.length === 1)
oldSubTree = 0 oldSubTree = 0
else if (trees_.length === 2) else if (state.trees.length === 2)
oldSubTree = 1 oldSubTree = 1
} }
@ -749,6 +738,15 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
} }
}) })
} }
const data = {
name: name.trim(),
liceName: lice.trim(),
type: poule + (tournoi << 1),
size: size,
loserMatch: loserMatch
}
console.log("Submitting category data:", data);
} }
return <form onSubmit={handleSubmit}> return <form onSubmit={handleSubmit}>
@ -784,16 +782,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
onChange={e => setTournoi(e.target.checked)}/> onChange={e => setTournoi(e.target.checked)}/>
<label className="form-check-label" htmlFor="switchCheckDefault2">{t('tournoi')}</label> <label className="form-check-label" htmlFor="switchCheckDefault2">{t('tournoi')}</label>
</div> </div>
<div className="form-check">
<input className="form-check-input" type="checkbox" value="" id="checkDefault" disabled={!tournoi} checked={classement}
onChange={e => setClassement(e.target.checked)}/>
<label className="form-check-label" htmlFor="checkDefault">
{t('leTournoiServiraDePhaseFinaleAuxPoules')}
</label>
</div> </div>
</div>
<div className="mb-3"> <div className="mb-3">
<label htmlFor="sizeInput1" className="form-label">Nombre de combattants</label> <label htmlFor="sizeInput1" className="form-label">Nombre de combattants</label>
@ -832,14 +821,6 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
</div> </div>
</div> </div>
<div className="form-check">
<input className="form-check-input" type="checkbox" value="" id="checkDefault2" disabled={!tournoi || !classement}
checked={fullClassement} onChange={e => setFullClassement(e.target.checked)}/>
<label className="form-check-label" htmlFor="checkDefault2">
{t('lesCombattantsEnDehors')}
</label>
</div>
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button> <button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button>

View File

@ -6,13 +6,12 @@ import {DrawGraph} from "../../result/DrawGraph.jsx";
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx"; import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx"; import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
import {MarchReducer} from "../../../utils/MatchReducer.jsx"; import {MarchReducer} from "../../../utils/MatchReducer.jsx";
import {getToastMessage, virtualScore, win_end} from "../../../utils/Tools.js"; import {virtualScore, win_end} from "../../../utils/Tools.js";
import "./CMTMatchPanel.css" import "./CMTMatchPanel.css"
import {useOBS} from "../../../hooks/useOBS.jsx"; import {useOBS} from "../../../hooks/useOBS.jsx";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx"; import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
import {ScorePanel} from "./ScoreAndCardPanel.jsx"; import {ScorePanel} from "./ScoreAndCardPanel.jsx";
import {toast} from "react-toastify";
function CupImg() { function CupImg() {
return <img decoding="async" loading="lazy" width={"16"} height={"16"} className="wp-image-1635" return <img decoding="async" loading="lazy" width={"16"} height={"16"} className="wp-image-1635"
@ -71,7 +70,7 @@ export function CategorieSelect({catId, setCatId, menuActions}) {
function CMTMatchPanel({catId, cat, menuActions}) { function CMTMatchPanel({catId, cat, menuActions}) {
const setLoading = useLoadingSwitcher() const setLoading = useLoadingSwitcher()
const {sendRequest, dispatch} = useWS(); const {sendRequest, dispatch} = useWS();
const [trees, setTrees] = useState({raw: [], formatted: []}); const [trees, setTrees] = useState([]);
const [matches, reducer] = useReducer(MarchReducer, []); const [matches, reducer] = useReducer(MarchReducer, []);
const combDispatch = useCombsDispatch(); const combDispatch = useCombsDispatch();
const cardDispatch = useCardsDispatch(); const cardDispatch = useCardsDispatch();
@ -90,10 +89,7 @@ function CMTMatchPanel({catId, cat, menuActions}) {
setLoading(1); setLoading(1);
sendRequest('getFullCategory', catId) sendRequest('getFullCategory', catId)
.then((data) => { .then((data) => {
setTrees({ setTrees(data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)))
raw: data.trees.sort((a, b) => a.level - b.level),
formatted: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
})
cardDispatch({type: 'SET_ALL', payload: data.cards}); cardDispatch({type: 'SET_ALL', payload: data.cards});
@ -109,10 +105,7 @@ function CMTMatchPanel({catId, cat, menuActions}) {
const treeListener = ({data}) => { const treeListener = ({data}) => {
if (data.length < 1 || data[0].categorie !== catId) if (data.length < 1 || data[0].categorie !== catId)
return return
setTrees({ setTrees(data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)))
raw: data.sort((a, b) => a.level - b.level),
formatted: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
})
let matches2 = []; let matches2 = [];
let combsToAdd = []; let combsToAdd = [];
@ -154,7 +147,6 @@ function CMTMatchPanel({catId, cat, menuActions}) {
function ListMatch({cat, matches, trees, menuActions}) { function ListMatch({cat, matches, trees, menuActions}) {
const [type, setType] = useState(1); const [type, setType] = useState(1);
const {sendRequest} = useWS();
const {t} = useTranslation("cm"); const {t} = useTranslation("cm");
useEffect(() => { useEffect(() => {
@ -164,10 +156,6 @@ function ListMatch({cat, matches, trees, menuActions}) {
setType(cat.type); setType(cat.type);
}, [cat]); }, [cat]);
const handleCreatClassement = () => {
toast.promise(sendRequest("createClassementMatchs", cat.id), getToastMessage("toast.matchs.classement.create", "cm"))
}
if (!cat) if (!cat)
return <></>; return <></>;
@ -181,7 +169,7 @@ function ListMatch({cat, matches, trees, menuActions}) {
</li> </li>
<li className="nav-item"> <li className="nav-item">
<div className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")} <div className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")}
onClick={_ => setType(2)}>{(cat.treeAreClassement ? t('classement') : t('tournois'))} onClick={_ => setType(2)}>{t('tournois')}
</div> </div>
</li> </li>
</ul> </ul>
@ -193,14 +181,12 @@ function ListMatch({cat, matches, trees, menuActions}) {
</>} </>}
{type === 2 && <> {type === 2 && <>
{cat.treeAreClassement && !matches.some(m => m.categorie === cat.id && m.categorie_ord === -42 && (m.c1 !== undefined || m.c2 !== undefined)) ? <> <BuildTree treeData={trees} matches={matches} menuActions={menuActions}/>
<button className="btn btn-primary" onClick={handleCreatClassement}>{t('créerLesMatchesDeClassement')}</button>
</> : <BuildTree treeData={trees} matches={matches} cat={cat} menuActions={menuActions}/>}
</>} </>}
</div> </div>
} }
function MatchList({matches, cat, menuActions, classement = false, currentMatch = null, setCurrentMatch, getNext}) { function MatchList({matches, cat, menuActions}) {
const [activeMatch, setActiveMatch] = useState(null) const [activeMatch, setActiveMatch] = useState(null)
const [lice, setLice] = useState(localStorage.getItem("cm_lice") || "1") const [lice, setLice] = useState(localStorage.getItem("cm_lice") || "1")
const publicAffDispatch = usePubAffDispatch(); const publicAffDispatch = usePubAffDispatch();
@ -209,17 +195,12 @@ function MatchList({matches, cat, menuActions, classement = false, currentMatch
const {sendNotify, setState} = useWS(); const {sendNotify, setState} = useWS();
const liceName = (cat.liceName || "N/A").split(";"); const liceName = (cat.liceName || "N/A").split(";");
const marches2 = classement const marches2 = matches.filter(m => m.categorie_ord !== -42)
? matches.filter(m => m.categorie_ord === -42)
.map(m => ({...m, ...win_end(m, cards_v)}))
: matches.filter(m => m.categorie_ord !== -42)
.sort((a, b) => a.categorie_ord - b.categorie_ord) .sort((a, b) => a.categorie_ord - b.categorie_ord)
.map(m => ({...m, ...win_end(m, cards_v)})) .map(m => ({...m, ...win_end(m, cards_v)}))
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1; const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
const isActiveMatch = (index) => { const isActiveMatch = (index) => {
if (classement)
return true;
return liceName.length === 1 || (liceName[(index - firstIndex) % liceName.length] === lice) return liceName.length === 1 || (liceName[(index - firstIndex) % liceName.length] === lice)
} }
@ -244,26 +225,16 @@ function MatchList({matches, cat, menuActions, classement = false, currentMatch
useEffect(() => { useEffect(() => {
if (match && match.poule !== lice) if (match && match.poule !== lice)
if (!classement)
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id) setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id)
}, [lice]); }, [lice]);
useEffect(() => { useEffect(() => {
if (marches2.length === 0) if (marches2.length === 0)
return; return;
if (marches2.some(m => m.id === (classement ? currentMatch?.matchSelect : activeMatch))) if (marches2.some(m => m.id === activeMatch))
return; return;
if (classement) {
getNext.current = (id) => {
if (id === null)
return marches2.findLast((m, index) => !m.end && isActiveMatch(index))?.id;
const index = marches2.findIndex(m => m.id === id);
return marches2.slice(0, index).reverse().find((m, index2) => !m.end && isActiveMatch(marches2.length - 1 - index2))?.id;
}
} else {
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id); setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id);
}
}, [matches]) }, [matches])
useEffect(() => { useEffect(() => {
@ -271,13 +242,6 @@ function MatchList({matches, cat, menuActions, classement = false, currentMatch
sendNotify("sendSelectMatch", activeMatch); sendNotify("sendSelectMatch", activeMatch);
}, [activeMatch]); }, [activeMatch]);
const handleMatchClick = (matchId) => {
if (classement) {
setCurrentMatch({matchSelect: matchId, matchNext: marches2.reverse().find(m => !m.end && m.id !== matchId)?.id});
} else {
setActiveMatch(matchId);
}
}
const GetCard = ({combId, match, cat}) => { const GetCard = ({combId, match, cat}) => {
const c = getHeightCardForCombInMatch(combId, match) const c = getHeightCardForCombInMatch(combId, match)
@ -323,8 +287,8 @@ function MatchList({matches, cat, menuActions, classement = false, currentMatch
<table className="table table-striped table-hover"> <table className="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Z</th>} <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">Z</th>
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">P</th>} <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">P</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('no')}</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('no')}</th>
<th style={{textAlign: "center"}} scope="col"></th> <th style={{textAlign: "center"}} scope="col"></th>
<th style={{textAlign: "center"}} scope="col">{t('rouge')}</th> <th style={{textAlign: "center"}} scope="col">{t('rouge')}</th>
@ -335,11 +299,11 @@ function MatchList({matches, cat, menuActions, classement = false, currentMatch
<tbody className="table-group-divider"> <tbody className="table-group-divider">
{marches2.map((m, index) => ( {marches2.map((m, index) => (
<tr key={m.id} <tr key={m.id}
className={m.id === (classement ? currentMatch?.matchSelect : activeMatch) ? "table-primary" : (m.end ? "table-success" : (isActiveMatch(index) ? "" : "table-warning"))} className={m.id === activeMatch ? "table-primary" : (m.end ? "table-success" : (isActiveMatch(index) ? "" : "table-warning"))}
onClick={() => handleMatchClick(m.id)}> onClick={() => setActiveMatch(m.id)}>
{!classement && <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}> <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
{liceName[(index - firstIndex) % liceName.length]}</td>} {liceName[(index - firstIndex) % liceName.length]}</td>
{!classement && <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>{m.poule}</td>} <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>{m.poule}</td>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
{index >= firstIndex ? index + 1 - firstIndex : ""}</th> {index >= firstIndex ? index + 1 - firstIndex : ""}</th>
<td style={{textAlign: "right", paddingRight: "0"}}>{m.end && m.win > 0 && <CupImg/>}</td> <td style={{textAlign: "right", paddingRight: "0"}}>{m.end && m.win > 0 && <CupImg/>}</td>
@ -356,20 +320,18 @@ function MatchList({matches, cat, menuActions, classement = false, currentMatch
</table> </table>
</div> </div>
{activeMatch && !classement && {activeMatch &&
<LoadingProvider><ScorePanel matchId={activeMatch} matchs={matches} match={match} menuActions={menuActions}/></LoadingProvider>} <LoadingProvider><ScorePanel matchId={activeMatch} matchs={matches} match={match} menuActions={menuActions}/></LoadingProvider>}
</> </>
} }
function BuildTree({treeData, matches, cat, menuActions}) { function BuildTree({treeData, matches, menuActions}) {
const scrollRef = useRef(null) const scrollRef = useRef(null)
const [currentMatch, setCurrentMatch] = useState(null) const [currentMatch, setCurrentMatch] = useState(null)
const {getComb} = useCombs() const {getComb} = useCombs()
const publicAffDispatch = usePubAffDispatch(); const publicAffDispatch = usePubAffDispatch();
const {cards_v} = useCards(); const {cards_v} = useCards();
const {sendNotify, setState} = useWS(); const {sendNotify, setState} = useWS();
const getNext = useRef(null);
const rtrees = useRef(null);
const match = matches.find(m => m.id === currentMatch?.matchSelect) const match = matches.find(m => m.id === currentMatch?.matchSelect)
useEffect(() => { useEffect(() => {
@ -388,11 +350,11 @@ function BuildTree({treeData, matches, cat, menuActions}) {
} }
}, [next_match]); }, [next_match]);
function parseTree(data_in, matches_) { function parseTree(data_in) {
if (data_in?.data == null) if (data_in?.data == null)
return null return null
const matchData = matches_.find(m => m.id === data_in.data) const matchData = matches.find(m => m.id === data_in.data)
const c1 = getComb(matchData?.c1) const c1 = getComb(matchData?.c1)
const c2 = getComb(matchData?.c2) const c2 = getComb(matchData?.c2)
@ -413,57 +375,24 @@ function BuildTree({treeData, matches, cat, menuActions}) {
c1FullName: c1 !== null ? c1.fname + " " + c1.lname : null, c1FullName: c1 !== null ? c1.fname + " " + c1.lname : null,
c2FullName: c2 !== null ? c2.fname + " " + c2.lname : null c2FullName: c2 !== null ? c2.fname + " " + c2.lname : null
}) })
node.left = parseTree(data_in?.left, matches_) node.left = parseTree(data_in?.left)
node.right = parseTree(data_in?.right, matches_) node.right = parseTree(data_in?.right)
return node return node
} }
function initTree(data_in, matches_) { function initTree(data_in) {
let out = [] let out = []
let out2 = [] for (const din of data_in) {
for (let i = 0; i < data_in.raw.length; i++) { out.push(parseTree(din))
if (data_in.raw.at(i).level > -10) {
out.push(parseTree(data_in.formatted.at(i), matches_))
out2.push(parseTree(data_in.formatted.at(i), matches_))
} }
} return out
return [out, out2.reverse()]
} }
const [trees, rTrees] = initTree(treeData, matches); const trees = initTree(treeData);
rtrees.current = rTrees;
useEffect(() => {
if (matches.length === 0)
return;
if (matches.some(m => m.id === currentMatch?.matchSelect))
return;
const timeout = setTimeout(() => {
const rTrees_ = rtrees.current ? rtrees.current : rTrees;
const matchId = ((getNext.current) ? getNext.current(null) : null) || new TreeNode(null).nextMatchTree(rTrees_);
const next = matchId ? ((getNext.current) ? getNext.current(matchId) : null) || new TreeNode(matchId).nextMatchTree(rTrees_) : null;
setCurrentMatch({matchSelect: matchId, matchNext: next});
setState({selectedMatch: matchId});
sendNotify("sendSelectMatch", matchId);
}, 200);
return () => clearTimeout(timeout);
}, [matches])
const setCurrentMatch_ = (o) => {
const rTrees_ = rtrees.current ? rtrees.current : rTrees;
const matchId = o.matchSelect ? o.matchSelect : new TreeNode(null).nextMatchTree(rTrees_);
const next = o.matchNext ? o.matchNext : new TreeNode(matchId).nextMatchTree(rTrees_);
setCurrentMatch({matchSelect: matchId, matchNext: next});
setState({selectedMatch: matchId});
sendNotify("sendSelectMatch", matchId);
}
const onMatchClick = (rect, matchId, __) => { const onMatchClick = (rect, matchId, __) => {
const rTrees_ = rtrees.current ? rtrees.current : rTrees; setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(trees.reverse())});
setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(rTrees_)});
setState({selectedMatch: matchId}); setState({selectedMatch: matchId});
sendNotify("sendSelectMatch", matchId); sendNotify("sendSelectMatch", matchId);
} }
@ -477,10 +406,6 @@ function BuildTree({treeData, matches, cat, menuActions}) {
<div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}> <div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}>
<DrawGraph root={trees} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid} <DrawGraph root={trees} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid}
matchSelect={currentMatch?.matchSelect} matchNext={currentMatch?.matchNext} size={23} cards={cards_v}/> matchSelect={currentMatch?.matchSelect} matchNext={currentMatch?.matchNext} size={23} cards={cards_v}/>
{cat.fullClassement &&
<MatchList matches={treeData.raw.filter(n => n.level <= -10).reverse().map(d => matches.find(m => m.id === d.match?.id))}
cat={cat} menuActions={menuActions} classement={true} currentMatch={currentMatch} setCurrentMatch={setCurrentMatch_}
getNext={getNext}/>}
</div> </div>
</div> </div>

View File

@ -251,7 +251,7 @@ function Menu({menuActions}) {
<button ref={teamCardModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#TeamCardModal" <button ref={teamCardModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#TeamCardModal"
style={{display: 'none'}}> style={{display: 'none'}}>
Launch OBS Modal Launch OBS Modal
</button> </button>s
<div className="modal fade" id="TeamCardModal" tabIndex="-1" aria-labelledby="TeamCardModalLabel" aria-hidden="true"> <div className="modal fade" id="TeamCardModal" tabIndex="-1" aria-labelledby="TeamCardModalLabel" aria-hidden="true">
<div className="modal-dialog"> <div className="modal-dialog">
<div className="modal-content"> <div className="modal-content">

View File

@ -20,7 +20,6 @@ import {useTranslation} from "react-i18next";
import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx"; import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
import {ScorePanel} from "./ScoreAndCardPanel.jsx"; import {ScorePanel} from "./ScoreAndCardPanel.jsx";
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
const vite_url = import.meta.env.VITE_URL; const vite_url = import.meta.env.VITE_URL;
@ -63,8 +62,7 @@ export function CategoryContent({cat, catId, setCat, menuActions}) {
return return
setCat(cat_ => ({ setCat(cat_ => ({
...cat_, ...cat_,
trees: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)), trees: data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
raw_trees: data.sort((a, b) => a.level - b.level)
})) }))
let matches2 = []; let matches2 = [];
@ -124,11 +122,7 @@ export function CategoryContent({cat, catId, setCat, menuActions}) {
name: data.name, name: data.name,
liceName: data.liceName, liceName: data.liceName,
type: data.type, type: data.type,
trees: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)), trees: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true))
raw_trees: data.trees.sort((a, b) => a.level - b.level),
treeAreClassement: data.treeAreClassement,
fullClassement: data.fullClassement,
preset: data.preset,
}) })
cardDispatch({type: 'SET_ALL', payload: data.cards}); cardDispatch({type: 'SET_ALL', payload: data.cards});
@ -366,7 +360,6 @@ function ListMatch({cat, matches, groups, reducer}) {
const {sendRequest} = useWS(); const {sendRequest} = useWS();
const [type, setType] = useState(1); const [type, setType] = useState(1);
const bthRef = useRef(null); const bthRef = useRef(null);
const bthRef2 = useRef(null);
const {t} = useTranslation("cm"); const {t} = useTranslation("cm");
useEffect(() => { useEffect(() => {
@ -382,17 +375,6 @@ function ListMatch({cat, matches, groups, reducer}) {
recalculateMatch(0); recalculateMatch(0);
} }
const handleCreatClassement_ = () => {
toast.promise(sendRequest("createClassementMatchs", cat.id), getToastMessage("toast.matchs.classement.create", "cm"))
}
const handleCreatClassement = () => {
if (matches.some(m => m.categorie === cat.id && m.categorie_ord === -42 && m.c1 !== undefined && m.c2 !== undefined)) {
bthRef2.current.click();
return;
}
handleCreatClassement_()
}
const recalculateMatch = (mode) => { const recalculateMatch = (mode) => {
let matchesToKeep = []; let matchesToKeep = [];
let matchesToRemove = []; let matchesToRemove = [];
@ -434,7 +416,7 @@ function ListMatch({cat, matches, groups, reducer}) {
</li> </li>
<li className="nav-item"> <li className="nav-item">
<div className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")} <div className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")}
onClick={_ => setType(2)}>{cat.treeAreClassement ? t('classement') : t('tournois')} onClick={_ => setType(2)}>{t('tournois')}
</div> </div>
</li> </li>
</ul> </ul>
@ -446,9 +428,7 @@ function ListMatch({cat, matches, groups, reducer}) {
</>} </>}
{type === 2 && <> {type === 2 && <>
<BuildTree treeData={cat.trees} treeRaw={cat.raw_trees} cat={cat} matches={matches} groups={groups}/> <BuildTree treeData={cat.trees} matches={matches} groups={groups}/>
{cat.treeAreClassement &&
<button className="btn btn-primary float-end" onClick={handleCreatClassement}>{t('créerLesMatchesDeClassement')}</button>}
</>} </>}
<button ref={bthRef} data-bs-toggle="modal" data-bs-target="#makeMatchMode" style={{display: "none"}}>open</button> <button ref={bthRef} data-bs-toggle="modal" data-bs-target="#makeMatchMode" style={{display: "none"}}>open</button>
@ -477,16 +457,10 @@ function ListMatch({cat, matches, groups, reducer}) {
</div> </div>
</div> </div>
</div> </div>
<button ref={bthRef2} data-bs-toggle="modal" data-bs-target="#confirm-regenerate-classement" style={{display: "none"}}>open</button>
<ConfirmDialog id="confirm-regenerate-classement"
title={t('créerLesMatchesDeClassement')}
message={t('créerLesMatchesDeClassement.msg')}
onConfirm={handleCreatClassement_}/>
</> </>
} }
function MatchList({matches, cat, groups, reducer, classement = false}) { function MatchList({matches, cat, groups, reducer}) {
const {sendRequest} = useWS(); const {sendRequest} = useWS();
const setLoading = useLoadingSwitcher() const setLoading = useLoadingSwitcher()
const selectRef = useRef(null) const selectRef = useRef(null)
@ -501,13 +475,9 @@ function MatchList({matches, cat, groups, reducer, classement = false}) {
const matchModal = useRef(null); const matchModal = useRef(null);
const liceName = (cat.liceName || "N/A").split(";"); const liceName = (cat.liceName || "N/A").split(";");
const marches2 = classement const marches2 = matches.filter(m => m.categorie_ord !== -42)
? matches.filter(m => m.categorie_ord === -42)
.map(m => ({...m, ...win_end(m, cards_v)}))
: matches.filter(m => m.categorie_ord !== -42)
.sort((a, b) => a.categorie_ord - b.categorie_ord) .sort((a, b) => a.categorie_ord - b.categorie_ord)
.map(m => ({...m, ...win_end(m, cards_v)})) .map(m => ({...m, ...win_end(m, cards_v)}))
marches2.forEach(m => { marches2.forEach(m => {
if (m.end && (!m.scores || m.scores.length === 0)) if (m.end && (!m.scores || m.scores.length === 0))
m.scores = [{n_round: 0, s1: 0, s2: 0}]; m.scores = [{n_round: 0, s1: 0, s2: 0}];
@ -521,9 +491,6 @@ function MatchList({matches, cat, groups, reducer, classement = false}) {
); );
const handleDragEnd = async (event) => { const handleDragEnd = async (event) => {
if (classement)
return;
const {active, over} = event; const {active, over} = event;
if (active.id !== over.id) { if (active.id !== over.id) {
const newIndex = marches2.findIndex(m => m.id === over.id); const newIndex = marches2.findIndex(m => m.id === over.id);
@ -603,7 +570,7 @@ function MatchList({matches, cat, groups, reducer, classement = false}) {
const handleDelMatch = (matchId) => { const handleDelMatch = (matchId) => {
const match = matches.find(m => m.id === matchId) const match = matches.find(m => m.id === matchId)
if (!match || classement) if (!match)
return; return;
if (!confirm(t('confirm1'))) if (!confirm(t('confirm1')))
@ -618,7 +585,7 @@ function MatchList({matches, cat, groups, reducer, classement = false}) {
const match = matches.find(m => m.id === matchId) const match = matches.find(m => m.id === matchId)
if (!match) if (!match)
return; return;
setModalMatchId(matchId); setModalMatchId (matchId);
matchModal.current.click(); matchModal.current.click();
} }
@ -678,46 +645,41 @@ function MatchList({matches, cat, groups, reducer, classement = false}) {
<thead> <thead>
<tr> <tr>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('no')}</th> <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('no')}</th>
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('poule')}</th>
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('poule')}</th>} <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('zone')}</th>
{!classement &&
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('zone')}</th>}
<th style={{textAlign: "center"}} scope="col"></th> <th style={{textAlign: "center"}} scope="col"></th>
<th style={{textAlign: "center"}} scope="col">{t('rouge')}</th> <th style={{textAlign: "center"}} scope="col">{t('rouge')}</th>
<th style={{textAlign: "center"}} scope="col">{t('blue')}</th> <th style={{textAlign: "center"}} scope="col">{t('blue')}</th>
<th style={{textAlign: "center"}} scope="col"></th> <th style={{textAlign: "center"}} scope="col"></th>
<th style={{textAlign: "center"}} scope="col">{t('résultat')}</th> <th style={{textAlign: "center"}} scope="col">{t('résultat')}</th>
<th style={{textAlign: "center"}} scope="col"></th> <th style={{textAlign: "center"}} scope="col"></th>
{!classement && <th style={{textAlign: "center"}} scope="col"></th>} <th style={{textAlign: "center"}} scope="col"></th>
{!classement && <th style={{textAlign: "center"}} scope="col"></th>} <th style={{textAlign: "center"}} scope="col"></th>
</tr> </tr>
</thead> </thead>
<tbody className="table-group-divider"> <tbody className="table-group-divider">
{marches2.map((m, index) => ( {marches2.map((m, index) => (
<SortableRow key={m.id} id={m.id}> <SortableRow key={m.id} id={m.id}>
<th style={{textAlign: "center", cursor: "auto"}} scope="row">{index + 1}</th> <th style={{textAlign: "center", cursor: "auto"}} scope="row">{index + 1}</th>
{!classement && <td style={{textAlign: "center", cursor: "auto"}}>{m.poule}</td>} <td style={{textAlign: "center", cursor: "auto"}}>{m.poule}</td>
{!classement && <td style={{textAlign: "center", cursor: "auto"}}>{liceName[index % liceName.length]}</td>} <td style={{textAlign: "center", cursor: "auto"}}>{liceName[index % liceName.length]}</td>
<td style={{textAlign: "right", cursor: "auto", paddingRight: "0"}}>{m.end && m.win > 0 && <CupImg/>}</td> <td style={{textAlign: "right", cursor: "auto", paddingRight: "0"}}>{m.end && m.win > 0 && <CupImg/>}</td>
<td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}} <td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}>
onClick={e => handleCombClick(e, m.id, m.c1)}>
<small className="position-relative"><CombName combId={m.c1}/> <small className="position-relative"><CombName combId={m.c1}/>
<GetCard match={m} combId={m.c1} cat={cat}/></small></td> <GetCard match={m} combId={m.c1} cat={cat}/></small></td>
<td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}} <td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}}>
onClick={e => handleCombClick(e, m.id, m.c2)}>
<small className="position-relative"><CombName combId={m.c2}/> <small className="position-relative"><CombName combId={m.c2}/>
<GetCard match={m} combId={m.c2} cat={cat}/></small></td> <GetCard match={m} combId={m.c2} cat={cat}/></small></td>
<td style={{textAlign: "left", cursor: "auto", paddingLeft: "0"}}>{m.end && m.win < 0 && <CupImg/>}</td> <td style={{textAlign: "left", cursor: "auto", paddingLeft: "0"}}>{m.end && m.win < 0 && <CupImg/>}</td>
<td style={{textAlign: "center", cursor: "auto"}}>{scoreToString2(m, cards_v)}</td> <td style={{textAlign: "center", cursor: "auto"}}>{scoreToString2(m, cards_v)}</td>
<td style={{textAlign: "center", cursor: "pointer", color: "#1381ff"}} onClick={_ => handleEditMatch(m.id)}> <td style={{textAlign: "center", cursor: "pointer", color: "#1381ff"}} onClick={_ => handleEditMatch(m.id)}>
<FontAwesomeIcon icon={faPen}/></td> <FontAwesomeIcon icon={faPen}/></td>
{!classement &&
<td style={{textAlign: "center", cursor: "pointer", color: "#ff1313"}} onClick={_ => handleDelMatch(m.id)}> <td style={{textAlign: "center", cursor: "pointer", color: "#ff1313"}} onClick={_ => handleDelMatch(m.id)}>
<FontAwesomeIcon icon={faTrash}/></td>} <FontAwesomeIcon icon={faTrash}/></td>
<td style={{textAlign: "center", cursor: "grab"}} hidden={classement}></td> <td style={{textAlign: "center", cursor: "grab"}}></td>
</SortableRow> </SortableRow>
))} ))}
{!classement && <tr> <tr>
<td>-</td> <td>-</td>
<td></td> <td></td>
<td></td> <td></td>
@ -733,7 +695,7 @@ function MatchList({matches, cat, groups, reducer, classement = false}) {
<td></td> <td></td>
<td></td> <td></td>
<td></td> <td></td>
</tr>} </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -808,7 +770,7 @@ function SortableRow({id, children}) {
); );
} }
function BuildTree({treeData, treeRaw, matches, cat, groups}) { function BuildTree({treeData, matches, groups}) {
const scrollRef = useRef(null) const scrollRef = useRef(null)
const selectRef = useRef(null) const selectRef = useRef(null)
const lastMatchClick = useRef(null) const lastMatchClick = useRef(null)
@ -849,12 +811,10 @@ function BuildTree({treeData, treeRaw, matches, cat, groups}) {
return node return node
} }
function initTree(data_in, data_raw) { function initTree(data_in) {
let out = [] let out = []
for (let i = 0; i < data_raw.length; i++) { for (const din of data_in) {
if (data_raw.at(i).level > -10) { out.push(parseTree(din))
out.push(parseTree(data_in.at(i)))
}
} }
return out return out
} }
@ -918,10 +878,7 @@ function BuildTree({treeData, treeRaw, matches, cat, groups}) {
const combsIDs = groups.map(m => m.id); const combsIDs = groups.map(m => m.id);
return <div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}> return <div ref={scrollRef} className="overflow-x-auto" style={{position: "relative"}}>
<DrawGraph root={initTree(treeData, treeRaw)} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid} cards={cards_v}/> <DrawGraph root={initTree(treeData)} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid} cards={cards_v}/>
{cat.fullClassement &&
<MatchList matches={treeRaw.filter(n => n.level <= -10).reverse().map(d => matches.find(m => m.id === d.match?.id))}
groups={groups} cat={cat} reducer={undefined} classement={true}/>}
<select ref={selectRef} className="form-select" style={{position: "absolute", top: 0, left: 0, display: "none"}} <select ref={selectRef} className="form-select" style={{position: "absolute", top: 0, left: 0, display: "none"}}
value={combSelect} onChange={e => setCombSelect(Number(e.target.value))}> value={combSelect} onChange={e => setCombSelect(Number(e.target.value))}>
<option value={0}>{t('--SélectionnerUnCombattant--')}</option> <option value={0}>{t('--SélectionnerUnCombattant--')}</option>

View File

@ -74,8 +74,7 @@ function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_, vEnd})
lastScoreClick.current = {matchId: matchId, round: maxRound, comb}; lastScoreClick.current = {matchId: matchId, round: maxRound, comb};
} else { } else {
const score = match.scores.find(s => s.n_round === round); const score = match.scores.find(s => s.n_round === round);
const tmp= (comb === 1 ? score?.s1 : score?.s2) setScoreIn((comb === 1 ? score?.s1 : score?.s2) || "");
setScoreIn((tmp === -1000 ? "" : tmp) || "");
lastScoreClick.current = {matchId: matchId, round, comb}; lastScoreClick.current = {matchId: matchId, round, comb};
setTimeout(() => inputRef.current.select(), 100); setTimeout(() => inputRef.current.select(), 100);
} }
@ -166,7 +165,7 @@ function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_, vEnd})
return () => { return () => {
document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("mousedown", handleClickOutside);
}; };
}, [matchId, match]); }, []);
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]') const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const o = [...tooltipTriggerList] const o = [...tooltipTriggerList]

View File

@ -14,7 +14,6 @@ function CupImg() {
style={{width: "16px"}} src="/img/171891.png" style={{width: "16px"}} src="/img/171891.png"
alt=""/> alt=""/>
} }
function CupImg2() { function CupImg2() {
return <img decoding="async" loading="lazy" width="16" height="16" className="wp-image-1635" return <img decoding="async" loading="lazy" width="16" height="16" className="wp-image-1635"
style={{width: "16px"}} src="/img/171892.png" style={{width: "16px"}} src="/img/171892.png"
@ -147,52 +146,13 @@ function BuildTree({treeData}) {
function initTree(data_in) { function initTree(data_in) {
let out = []; let out = [];
for (const din of data_in.filter(d => d.data.level >= 0)) { for (const din of data_in) {
out.push(parseTree(din)); out.push(parseTree(din));
} }
return out; return out;
} }
return <> return <DrawGraph root={initTree(treeData)}/>
<DrawGraph root={initTree(treeData)}/>
{treeData.some(d => d.data.level <= -10) &&
<BuildMatchArray matchs={treeData.filter(d => d.data.level <= -10).reverse().map(d => ({
red: d.data.c1FullName,
blue: d.data.c2FullName,
score: d.data.scores,
red_w: d.data.win > 0,
blue_w: d.data.win < 0,
eq: d.data.win === 0
}))}/>}
</>
}
function ClassementList({classement}) {
const {t} = useTranslation('result');
const classement2 = classement.sort((a, b) => {
if (a.rank === b.rank)
return a.name.localeCompare(b.name);
return a.rank - b.rank;
})
return <>
<h3 style={{marginTop: "2em"}}>{t('classementFinal')}</h3>
<table className="table table-striped">
<thead>
<tr>
<th scope="col" style={{textAlign: "center"}}>Place</th>
<th scope="col" style={{textAlign: "center"}}>Nom</th>
</tr>
</thead>
<tbody>
{classement2.map((row, idx) => <tr key={idx}>
<td style={{textAlign: "center"}}>{row.rank}</td>
<td style={{textAlign: "left"}}>{row.name}</td>
</tr>)}
</tbody>
</table>
</>
} }
function CategoryList({uuid}) { function CategoryList({uuid}) {
@ -252,7 +212,7 @@ function CategoryResult({uuid, catId}) {
</li> </li>
<li className="nav-item"> <li className="nav-item">
<a className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")} href="#" <a className={"nav-link" + (type === 2 ? " active" : "")} aria-current={(type === 2 ? " page" : "false")} href="#"
onClick={_ => setType(2)}>{data.treeIsClassement ? t('classement') : t('tournois')}</a> onClick={_ => setType(2)}>{t('tournois')}</a>
</li> </li>
</ul> </ul>
</>} </>}
@ -267,7 +227,6 @@ function CategoryResult({uuid, catId}) {
{type === 2 && <> {type === 2 && <>
<BuildTree treeData={data.trees}/> <BuildTree treeData={data.trees}/>
{data.treeIsClassement && <ClassementList classement={data.classement}/>}
</>} </>}
</> </>

View File

@ -22,18 +22,8 @@ export function isClubAdmin(userinfo) {
export const errFormater = (data, msg) => { export const errFormater = (data, msg) => {
if (!data) if (!data)
return msg return msg
if (!data.response) { if (!data.response)
if (data.message) return `${msg} (😕 ${data.message})`
return `${msg} (${data.message} 😕)`
if (typeof data === 'string' || data instanceof String) {
if (/.*D[a-zA-Z]+Exception:.*/.test(data))
return `${msg} (${data.split(":").slice(1).join(":").trim()} 😕)`
else
return `${msg}`
}
return `${msg} (${data} 😕)`
}
if (typeof data.response.data === 'string' || data.response.data instanceof String) if (typeof data.response.data === 'string' || data.response.data instanceof String)
return `${msg} (${data.response.statusText}: ${data.response.data}) 😕` return `${msg} (${data.response.statusText}: ${data.response.data}) 😕`
return `${msg} (${data.response.statusText}: ${JSON.stringify(data.response.data)}) 😕` return `${msg} (${data.response.statusText}: ${JSON.stringify(data.response.data)}) 😕`