dev #107
@ -45,6 +45,12 @@ 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;
|
||||||
|
|||||||
@ -109,6 +109,22 @@ 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;
|
||||||
|
|||||||
@ -40,6 +40,14 @@ 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);
|
||||||
@ -55,4 +63,44 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,9 @@ 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())
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package fr.titionfire.ffsaf.domain.entity;
|
package fr.titionfire.ffsaf.domain.entity;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CatPresetModel;
|
||||||
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
|
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
|
||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
import fr.titionfire.ffsaf.data.model.RegisterModel;
|
import fr.titionfire.ffsaf.data.model.RegisterModel;
|
||||||
@ -28,6 +29,7 @@ public class CombEntity {
|
|||||||
int overCategory;
|
int overCategory;
|
||||||
Integer weight;
|
Integer weight;
|
||||||
List<CombEntity> teamMembers;
|
List<CombEntity> teamMembers;
|
||||||
|
List<Long> categoriesInscrites;
|
||||||
|
|
||||||
public static CombEntity fromModel(MembreModel model) {
|
public static CombEntity fromModel(MembreModel model) {
|
||||||
if (model == null)
|
if (model == null)
|
||||||
@ -36,7 +38,7 @@ public class CombEntity {
|
|||||||
return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(),
|
return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(),
|
||||||
model.getClub() == null ? null : model.getClub().getClubId(),
|
model.getClub() == null ? null : model.getClub().getClubId(),
|
||||||
model.getClub() == null ? "Sans club" : model.getClub().getName(), model.getGenre(), model.getCountry(),
|
model.getClub() == null ? "Sans club" : model.getClub().getName(), model.getGenre(), model.getCountry(),
|
||||||
0, null, new ArrayList<>());
|
0, null, new ArrayList<>(), new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -47,7 +49,15 @@ public class CombEntity {
|
|||||||
return new CombEntity(model.getId() * -1, model.getLname(), model.getFname(), model.getCategorie(), null,
|
return new CombEntity(model.getId() * -1, model.getLname(), model.getFname(), model.getCategorie(), null,
|
||||||
model.getClub(), model.getGenre(), model.getCountry(), 0, model.getWeight(),
|
model.getClub(), model.getGenre(), model.getCountry(), 0, model.getWeight(),
|
||||||
Stream.concat(model.getComb().stream().map(CombEntity::fromModel),
|
Stream.concat(model.getComb().stream().map(CombEntity::fromModel),
|
||||||
model.getGuest().stream().map(CombEntity::fromModel)).toList());
|
model.getGuest().stream().map(CombEntity::fromModel)).toList(),
|
||||||
|
new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CombEntity addCategoriesInscrites(List<CatPresetModel> categoriesInscrites) {
|
||||||
|
if (categoriesInscrites == null)
|
||||||
|
return this;
|
||||||
|
this.categoriesInscrites = categoriesInscrites.stream().map(CatPresetModel::getId).toList();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CombEntity fromModel(RegisterModel registerModel) {
|
public static CombEntity fromModel(RegisterModel registerModel) {
|
||||||
@ -58,6 +68,7 @@ public class CombEntity {
|
|||||||
return new CombEntity(model.getId(), model.getLname(), model.getFname(), registerModel.getCategorie(),
|
return new CombEntity(model.getId(), model.getLname(), model.getFname(), registerModel.getCategorie(),
|
||||||
registerModel.getClub2() == null ? null : registerModel.getClub2().getClubId(),
|
registerModel.getClub2() == null ? null : registerModel.getClub2().getClubId(),
|
||||||
registerModel.getClub2() == null ? "Sans club" : registerModel.getClub2().getName(), model.getGenre(),
|
registerModel.getClub2() == null ? "Sans club" : registerModel.getClub2().getName(), model.getGenre(),
|
||||||
model.getCountry(), registerModel.getOverCategory(), registerModel.getWeight(), new ArrayList<>());
|
model.getCountry(), registerModel.getOverCategory(), registerModel.getWeight(), new ArrayList<>(),
|
||||||
|
new ArrayList<>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,4 +173,12 @@ 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,6 +94,38 @@ public class CardService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uni<List<CardModel>> addTeamCartToNewComb(Long combId, String teamUuid, String teamName,
|
||||||
|
CompetitionModel competition) {
|
||||||
|
return clubCardRepository.list("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
|
||||||
|
Sort.ascending("type"), competition.getId(), teamUuid, teamName)
|
||||||
|
.chain(clubCards -> {
|
||||||
|
Uni<?> queue = Uni.createFrom().voidItem();
|
||||||
|
List<CardModel> addCards = new ArrayList<>();
|
||||||
|
for (ClubCardModel clubCard : clubCards) {
|
||||||
|
CardModel model = new CardModel();
|
||||||
|
model.setCompetition(competition);
|
||||||
|
model.setCompetitionId(competition.getId());
|
||||||
|
model.setComb(combId);
|
||||||
|
model.setTeamCard(true);
|
||||||
|
model.setType(clubCard.getType());
|
||||||
|
model.setDate(clubCard.getDate());
|
||||||
|
|
||||||
|
queue = queue.call(__ -> Panache.withTransaction(() -> cardRepository.persist(model))
|
||||||
|
.invoke(addCards::add)
|
||||||
|
.call(() -> {
|
||||||
|
clubCard.getCardIds().add(model.getId());
|
||||||
|
return Panache.withTransaction(() -> clubCardRepository.persist(clubCard));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return queue.replaceWith(addCards);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uni<Void> rmTeamCardFromComb(Long combId, String uuid) {
|
||||||
|
return cardRepository.delete("comb = ?1 AND competition.uuid = ?2 AND teamCard = True", combId, uuid)
|
||||||
|
.replaceWithVoid();
|
||||||
|
}
|
||||||
|
|
||||||
public Uni<List<CardModel>> addTeamCard(CompetitionModel competition, String teamUuid, String teamName,
|
public Uni<List<CardModel>> addTeamCard(CompetitionModel competition, String teamUuid, String teamName,
|
||||||
CardModel.CardType type, String reason) {
|
CardModel.CardType type, String reason) {
|
||||||
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
|
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
|
||||||
|
|||||||
@ -151,14 +151,16 @@ 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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getArray2(List<MatchModelExtend> matchModels_, MembreModel membreModel, ResultCategoryData out) {
|
public 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<>();
|
||||||
@ -182,7 +184,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,
|
return new ResultCategoryData.RankArray(0, comb,
|
||||||
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());
|
||||||
})
|
})
|
||||||
@ -210,10 +212,66 @@ 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), membreModel, privacy));
|
ResultCategoryData.TreeData.from(new MatchModelExtend(src.getMatch(), cards), src.getLevel(),
|
||||||
|
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);
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
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;
|
||||||
@ -21,11 +23,20 @@ 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);
|
||||||
|
|||||||
@ -4,10 +4,12 @@ import fr.titionfire.ffsaf.data.model.CatPresetModel;
|
|||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
public class PresetData {
|
public class PresetData {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
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;
|
||||||
@ -21,9 +23,11 @@ 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;
|
||||||
|
|
||||||
@ -32,6 +36,8 @@ 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;
|
||||||
@ -43,12 +49,14 @@ 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, ResultPrivacy privacy) {
|
public static PouleArrayData fromModel(MatchModelExtend matchModel, MembreModel membreModel,
|
||||||
|
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()}).toList()
|
matchModel.getScoresToPrint().stream().map(s -> new Integer[]{s.getS1(), s.getS2()})
|
||||||
|
.toList()
|
||||||
: new ArrayList<>(),
|
: new ArrayList<>(),
|
||||||
matchModel.isEnd() && matchModel.getWin() < 0,
|
matchModel.isEnd() && matchModel.getWin() < 0,
|
||||||
matchModel.getC2Name(membreModel, privacy),
|
matchModel.getC2Name(membreModel, privacy),
|
||||||
@ -60,10 +68,16 @@ 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) {
|
boolean end, int win, int level, Date date) {
|
||||||
public static TreeData from(MatchModelExtend match, MembreModel membreModel, ResultPrivacy privacy) {
|
public static TreeData from(MatchModelExtend match, int level, 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) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"));
|
throw new DNotFoundException(trad.t("matche.non.trouver", connection));
|
||||||
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"));
|
throw new DForbiddenException(trad.t("permission.denied", connection));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,13 +97,14 @@ public class RCard {
|
|||||||
|
|
||||||
@WSReceiver(code = "sendCardRm", permission = PermLevel.ADMIN)
|
@WSReceiver(code = "sendCardRm", permission = PermLevel.ADMIN)
|
||||||
public Uni<Void> sendCardRm(WebSocketConnection connection, SendCardAdd card) {
|
public Uni<Void> sendCardRm(WebSocketConnection connection, SendCardAdd card) {
|
||||||
return getById(card.matchId(), connection)
|
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
|
||||||
.chain(matchModel -> cardRepository.find("match = ?1 AND comb = ?2 AND type = ?3",
|
.chain(competition -> cardRepository.find(
|
||||||
matchModel.getId(), card.combId(), card.type())
|
"match IS NULL AND comb = ?1 AND type = ?2 AND competition = ?3 AND match " + (card.matchId() == null ? "IS NULL" : "= " + card.matchId()),
|
||||||
|
card.combId(), card.type(), competition)
|
||||||
.firstResult()
|
.firstResult()
|
||||||
.invoke(Unchecked.consumer(o -> {
|
.invoke(Unchecked.consumer(o -> {
|
||||||
if (o == null)
|
if (o == null)
|
||||||
throw new DNotFoundException(trad.t("carton.non.trouver"));
|
throw new DNotFoundException(trad.t("carton.non.trouver", connection));
|
||||||
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)))
|
||||||
@ -143,7 +144,7 @@ public class RCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
public record SendCardAdd(long matchId, long combId, CardModel.CardType type, String reason) {
|
public record SendCardAdd(Long matchId, long combId, CardModel.CardType type, String reason) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
|
|||||||
@ -1,22 +1,25 @@
|
|||||||
package fr.titionfire.ffsaf.ws.recv;
|
package fr.titionfire.ffsaf.ws.recv;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.CardModel;
|
import fr.titionfire.ffsaf.data.model.*;
|
||||||
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;
|
||||||
@ -25,8 +28,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.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@WithSession
|
@WithSession
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
@ -49,6 +52,15 @@ public class RCategorie {
|
|||||||
@Inject
|
@Inject
|
||||||
CardService cardService;
|
CardService cardService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CardRepository cardRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CatPresetRepository catPresetRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ResultService resultService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TradService trad;
|
TradService trad;
|
||||||
|
|
||||||
@ -56,9 +68,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"));
|
throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
|
||||||
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
||||||
throw new DForbiddenException(trad.t("permission.denied"));
|
throw new DForbiddenException(trad.t("permission.denied", connection));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +90,9 @@ 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)
|
||||||
@ -112,10 +127,21 @@ public class RCategorie {
|
|||||||
@WSReceiver(code = "updateCategory", permission = PermLevel.ADMIN)
|
@WSReceiver(code = "updateCategory", permission = PermLevel.ADMIN)
|
||||||
public Uni<Void> updateCategory(WebSocketConnection connection, JustCategorie categorie) {
|
public Uni<Void> updateCategory(WebSocketConnection connection, JustCategorie categorie) {
|
||||||
return getById(categorie.id, connection)
|
return getById(categorie.id, connection)
|
||||||
|
.call(cat -> {
|
||||||
|
if (categorie.preset() == null) {
|
||||||
|
cat.setPreset(null);
|
||||||
|
return Uni.createFrom().item(cat);
|
||||||
|
} else {
|
||||||
|
return catPresetRepository.findById(categorie.preset().getId())
|
||||||
|
.invoke(cat::setPreset);
|
||||||
|
}
|
||||||
|
})
|
||||||
.chain(cat -> {
|
.chain(cat -> {
|
||||||
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);
|
||||||
return Panache.withTransaction(() -> categoryRepository.persist(cat));
|
return Panache.withTransaction(() -> categoryRepository.persist(cat));
|
||||||
})
|
})
|
||||||
.call(cat -> {
|
.call(cat -> {
|
||||||
@ -224,10 +250,155 @@ public class RCategorie {
|
|||||||
.replaceWithVoid();
|
.replaceWithVoid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WSReceiver(code = "listPreset", permission = PermLevel.VIEW)
|
||||||
|
public Uni<List<PresetData>> listPreset(WebSocketConnection connection, Object o) {
|
||||||
|
return catPresetRepository.list("competition.uuid", connection.pathParam("uuid"))
|
||||||
|
.map(presets -> presets.stream().map(PresetData::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
public record JustCategorie(long id, String name, int type, String liceName, boolean treeAreClassement,
|
||||||
|
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());
|
return new JustCategorie(m.getId(), m.getName(), m.getType(), m.getLiceName(), m.isTreeAreClassement(),
|
||||||
|
m.isFullClassement(), PresetData.fromModel(m.getPreset()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +413,9 @@ 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;
|
||||||
|
|||||||
@ -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"));
|
throw new DNotFoundException(trad.t("matche.non.trouver", connection));
|
||||||
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"));
|
throw new DForbiddenException(trad.t("permission.denied", connection));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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"));
|
throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
|
||||||
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
||||||
throw new DForbiddenException(trad.t("permission.denied"));
|
throw new DForbiddenException(trad.t("permission.denied", connection));
|
||||||
}))
|
}))
|
||||||
.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"));
|
throw new DNotFoundException(trad.t("categorie.non.trouver", connection));
|
||||||
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
if (!o.getCompet().getUuid().equals(connection.pathParam("uuid")))
|
||||||
throw new DForbiddenException(trad.t("permission.denied"));
|
throw new DForbiddenException(trad.t("permission.denied", connection));
|
||||||
}))
|
}))
|
||||||
.call(cm -> data.matchesToRemove.isEmpty() ? Uni.createFrom().voidItem() :
|
.call(cm -> data.matchesToRemove.isEmpty() ? Uni.createFrom().voidItem() :
|
||||||
(Panache.withTransaction(
|
(Panache.withTransaction(
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import fr.titionfire.ffsaf.ws.PermLevel;
|
|||||||
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.WebSocketConnection;
|
import io.quarkus.websockets.next.WebSocketConnection;
|
||||||
|
import io.smallrye.mutiny.Multi;
|
||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
@ -24,14 +25,20 @@ public class RRegister {
|
|||||||
|
|
||||||
@WSReceiver(code = "getRegister", permission = PermLevel.TABLE)
|
@WSReceiver(code = "getRegister", permission = PermLevel.TABLE)
|
||||||
public Uni<List<CombEntity>> getRegister(WebSocketConnection connection, Object o) {
|
public Uni<List<CombEntity>> 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<CombEntity> combEntities = new ArrayList<>();
|
ArrayList<CombEntity> combEntities = new ArrayList<>();
|
||||||
combEntities.addAll(cm.getInsc().stream().map(CombEntity::fromModel).toList());
|
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
|
||||||
combEntities.addAll(cm.getGuests().stream().map(CombEntity::fromModel).toList());
|
.call(cm -> Mutiny.fetch(cm.getInsc())
|
||||||
return combEntities;
|
.onItem().transformToMulti(Multi.createFrom()::iterable)
|
||||||
});
|
.call(r -> Mutiny.fetch(r.getCategoriesInscrites()))
|
||||||
|
.map(r -> CombEntity.fromModel(r).addCategoriesInscrites(r.getCategoriesInscrites()))
|
||||||
|
.collect().asList()
|
||||||
|
.invoke(combEntities::addAll))
|
||||||
|
.call(cm -> Mutiny.fetch(cm.getGuests())
|
||||||
|
.onItem().transformToMulti(Multi.createFrom()::iterable)
|
||||||
|
.call(r -> Mutiny.fetch(r.getCategoriesInscrites()))
|
||||||
|
.map(r -> CombEntity.fromModel(r).addCategoriesInscrites(r.getCategoriesInscrites()))
|
||||||
|
.collect().asList()
|
||||||
|
.invoke(combEntities::addAll))
|
||||||
|
.replaceWith(combEntities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.ws.send;
|
|||||||
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
|
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
|
||||||
import fr.titionfire.ffsaf.data.model.RegisterModel;
|
import fr.titionfire.ffsaf.data.model.RegisterModel;
|
||||||
import fr.titionfire.ffsaf.domain.entity.CombEntity;
|
import fr.titionfire.ffsaf.domain.entity.CombEntity;
|
||||||
|
import fr.titionfire.ffsaf.domain.service.CardService;
|
||||||
import fr.titionfire.ffsaf.net2.MessageType;
|
import fr.titionfire.ffsaf.net2.MessageType;
|
||||||
import fr.titionfire.ffsaf.ws.CompetitionWS;
|
import fr.titionfire.ffsaf.ws.CompetitionWS;
|
||||||
import fr.titionfire.ffsaf.ws.MessageOut;
|
import fr.titionfire.ffsaf.ws.MessageOut;
|
||||||
@ -13,6 +14,7 @@ import io.quarkus.websockets.next.UserData;
|
|||||||
import io.smallrye.mutiny.Uni;
|
import io.smallrye.mutiny.Uni;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -25,16 +27,30 @@ public class SRegister {
|
|||||||
@Inject
|
@Inject
|
||||||
OpenConnections connections;
|
OpenConnections connections;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CardService cardService;
|
||||||
|
|
||||||
public Uni<Void> sendRegister(String uuid, RegisterModel registerModel) {
|
public Uni<Void> sendRegister(String uuid, RegisterModel registerModel) {
|
||||||
return send(uuid, "sendRegister", CombEntity.fromModel(registerModel));
|
return Mutiny.fetch(registerModel.getCategoriesInscrites()).chain(o ->
|
||||||
|
send(uuid, "sendRegister", CombEntity.fromModel(registerModel).addCategoriesInscrites(o))
|
||||||
|
.call(__ -> registerModel.getClub2() == null ? Uni.createFrom().voidItem() :
|
||||||
|
cardService.addTeamCartToNewComb(registerModel.getMembre().getId(),
|
||||||
|
registerModel.getClub2().getClubId(), registerModel.getClub2().getName(),
|
||||||
|
registerModel.getCompetition())
|
||||||
|
.chain(cardModels -> send(uuid, "sendCards", cardModels))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<Void> sendRegister(String uuid, CompetitionGuestModel model) {
|
public Uni<Void> sendRegister(String uuid, CompetitionGuestModel model) {
|
||||||
return send(uuid, "sendRegister", CombEntity.fromModel(model));
|
return Mutiny.fetch(model.getCategoriesInscrites()).chain(o ->
|
||||||
|
send(uuid, "sendRegister", CombEntity.fromModel(model).addCategoriesInscrites(o))
|
||||||
|
.call(__ -> cardService.addTeamCartToNewComb(model.getId() * -1,
|
||||||
|
null, model.getClub(), model.getCompetition())
|
||||||
|
.chain(cardModels -> send(uuid, "sendCards", cardModels))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<Void> sendRegisterRemove(String uuid, Long combId) {
|
public Uni<Void> sendRegisterRemove(String uuid, Long combId) {
|
||||||
return send(uuid, "sendRegisterRemove", combId);
|
return send(uuid, "sendRegisterRemove", combId)
|
||||||
|
.call(__ -> cardService.rmTeamCardFromComb(combId, uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<Void> send(String uuid, String code, Object data) {
|
public Uni<Void> send(String uuid, String code, Object data) {
|
||||||
|
|||||||
@ -87,3 +87,5 @@ 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
|
||||||
|
|||||||
@ -83,3 +83,5 @@ 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é
|
||||||
@ -104,8 +104,13 @@ function scorePrint(s1) {
|
|||||||
return String(s1);
|
return String(s1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scoreToString(score) {
|
function scoreToString(score) {
|
||||||
return score.map(o => scorePrint(o.at(0)) + "-" + scorePrint(o.at(1))).join(" | ");
|
if (score.length === 0)
|
||||||
|
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) {
|
||||||
@ -129,7 +134,7 @@ function dateToString(date) {
|
|||||||
return date_.toLocaleDateString();
|
return date_.toLocaleDateString();
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPouleMenu(isPoule, change_view) {
|
function buildPouleMenu(isPoule, change_view, isClassement = false) {
|
||||||
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';
|
||||||
@ -188,7 +193,7 @@ function buildPouleMenu(isPoule, change_view) {
|
|||||||
change_view(true);
|
change_view(true);
|
||||||
});
|
});
|
||||||
ul.appendChild(li1);
|
ul.appendChild(li1);
|
||||||
const li2 = createTab(i18next.t('tournois'), !isPoule, function () {
|
const li2 = createTab(isClassement ? i18next.t('classement') : i18next.t('tournois'), !isPoule, function () {
|
||||||
change_view(false);
|
change_view(false);
|
||||||
});
|
});
|
||||||
ul.appendChild(li2);
|
ul.appendChild(li2);
|
||||||
@ -262,7 +267,36 @@ function buildRankArray(rankArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildTree(treeData) {
|
function buildTree(treeData) {
|
||||||
return drawGraph(initTree(treeData))
|
return drawGraph(initTree(treeData.filter(d => d.data.level >= 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildClassementArray(classement) {
|
||||||
|
const classement2 = classement.sort((a, b) => {
|
||||||
|
if (a.rank === b.rank)
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
return a.rank - b.rank;
|
||||||
|
})
|
||||||
|
|
||||||
|
const arrayDiv = document.createElement('div');
|
||||||
|
let arrayContent = `<figure class="wp-block-table is-style-stripes" style="font-size: 16px; margin-top: 2em">
|
||||||
|
<table style="width: 600px;overflow: auto">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="has-text-align-center" data-align="center">${i18next.t('place')}</th>
|
||||||
|
<th class="has-text-align-center" data-align="center">${i18next.t('nom')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead><tbody>`
|
||||||
|
for (const row of classement2) {
|
||||||
|
arrayContent += `
|
||||||
|
<tr>
|
||||||
|
<td class="has-text-align-center" data-align="center">${row.rank}</td>
|
||||||
|
<td class="has-text-align-left" data-align="left">${row.name}</td>
|
||||||
|
</tr>`
|
||||||
|
|
||||||
|
}
|
||||||
|
arrayContent += `</tbody></table></figure>`
|
||||||
|
arrayDiv.innerHTML = arrayContent;
|
||||||
|
return arrayDiv;
|
||||||
}
|
}
|
||||||
|
|
||||||
function poulePage(location) {
|
function poulePage(location) {
|
||||||
@ -310,7 +344,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));
|
dataContainer.replaceChildren(buildPouleMenu(isPoule, change_view, poule['treeIsClassement']));
|
||||||
|
|
||||||
if (isPoule) {
|
if (isPoule) {
|
||||||
for (const g in poule.matchs) {
|
for (const g in poule.matchs) {
|
||||||
@ -325,6 +359,22 @@ 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,
|
||||||
|
}))));
|
||||||
|
}
|
||||||
|
if (poule['treeIsClassement']){
|
||||||
|
dataContainer.append(buildClassementArray(poule['classement']));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
location[2] = isPoule ? 1 : 2;
|
location[2] = isPoule ? 1 : 2;
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
"actuel": "Current",
|
"actuel": "Current",
|
||||||
"administration": "Administration",
|
"administration": "Administration",
|
||||||
"adresseDuServeur": "Server address",
|
"adresseDuServeur": "Server address",
|
||||||
|
"ajoutAutomatique": "Automatic addition",
|
||||||
"ajouter": "Add",
|
"ajouter": "Add",
|
||||||
"ajouterDesCombattants": "Add fighters",
|
"ajouterDesCombattants": "Add fighters",
|
||||||
"ajouterUn": "Add one",
|
"ajouterUn": "Add one",
|
||||||
@ -35,7 +36,9 @@
|
|||||||
"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",
|
||||||
|
"combattantsCorrespondentAuxSélectionnés": "fighter(s) match the selections above",
|
||||||
"compétition": "Competition",
|
"compétition": "Competition",
|
||||||
"compétitionManager": "Competition manager",
|
"compétitionManager": "Competition manager",
|
||||||
"config.obs.dossierDesResources": "Resources folder",
|
"config.obs.dossierDesResources": "Resources folder",
|
||||||
@ -55,6 +58,8 @@
|
|||||||
"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",
|
||||||
@ -79,10 +84,14 @@
|
|||||||
"genre.h": "M",
|
"genre.h": "M",
|
||||||
"genre.na": "NA",
|
"genre.na": "NA",
|
||||||
"individuelle": "Individual",
|
"individuelle": "Individual",
|
||||||
|
"informationCatégorie": "Category information",
|
||||||
"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:",
|
||||||
|
"matchTerminé": "Match over",
|
||||||
"matches": "Matches",
|
"matches": "Matches",
|
||||||
"modifier": "Edit",
|
"modifier": "Edit",
|
||||||
"msg1": "There are already matches in this pool; what do you want to do with them?",
|
"msg1": "There are already matches in this pool; what do you want to do with them?",
|
||||||
@ -92,6 +101,7 @@
|
|||||||
"nomDeLaZone": "Area name",
|
"nomDeLaZone": "Area name",
|
||||||
"nomDeLéquipe": "team name",
|
"nomDeLéquipe": "team name",
|
||||||
"nomDesZonesDeCombat": "Combat zone names <1>(separated by ';')</1>",
|
"nomDesZonesDeCombat": "Combat zone names <1>(separated by ';')</1>",
|
||||||
|
"nombreDeCombattants": "Number of fighters",
|
||||||
"nouvelle...": "New...",
|
"nouvelle...": "New...",
|
||||||
"obs.préfixDesSources": "Source prefix",
|
"obs.préfixDesSources": "Source prefix",
|
||||||
"pays": "Country",
|
"pays": "Country",
|
||||||
@ -99,6 +109,7 @@
|
|||||||
"poule": "Pool",
|
"poule": "Pool",
|
||||||
"poulePour": "Pool for: ",
|
"poulePour": "Pool for: ",
|
||||||
"préparation...": "Preparing...",
|
"préparation...": "Preparing...",
|
||||||
|
"remplacer": "Replace",
|
||||||
"rouge": "Red",
|
"rouge": "Red",
|
||||||
"réinitialiser": "Reset",
|
"réinitialiser": "Reset",
|
||||||
"résultat": "Result",
|
"résultat": "Result",
|
||||||
@ -122,6 +133,7 @@
|
|||||||
"supprimerUn": "Delete one",
|
"supprimerUn": "Delete one",
|
||||||
"sélectionneLesModesDaffichage": "Select display modes",
|
"sélectionneLesModesDaffichage": "Select display modes",
|
||||||
"sélectionner": "Select",
|
"sélectionner": "Select",
|
||||||
|
"taille": "Size",
|
||||||
"team": "Team",
|
"team": "Team",
|
||||||
"terminé": "Finished",
|
"terminé": "Finished",
|
||||||
"texteCopiéDansLePresse": "Text copied to clipboard! Paste it into an HTML tag on your WordPress.",
|
"texteCopiéDansLePresse": "Text copied to clipboard! Paste it into an HTML tag on your WordPress.",
|
||||||
@ -134,6 +146,9 @@
|
|||||||
"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.",
|
||||||
@ -167,6 +182,7 @@
|
|||||||
"téléchargementEnCours": "Downloading...",
|
"téléchargementEnCours": "Downloading...",
|
||||||
"téléchargementTerminé!": "Download completed!",
|
"téléchargementTerminé!": "Download completed!",
|
||||||
"uneCatégorie": "a category",
|
"uneCatégorie": "a category",
|
||||||
|
"uneCatégorieNePeutContenirPlusDe10Combattants": "A category cannot contain more than 10 fighters, please create weight categories.",
|
||||||
"valider": "Validate",
|
"valider": "Validate",
|
||||||
"zone": "Zone",
|
"zone": "Zone",
|
||||||
"zoneDeCombat": "Combat zone"
|
"zoneDeCombat": "Combat zone"
|
||||||
|
|||||||
@ -619,9 +619,9 @@
|
|||||||
"supprimerLeCompte": "Delete account",
|
"supprimerLeCompte": "Delete account",
|
||||||
"supprimerLeCompte.msg": "Are you sure you want to delete this account?",
|
"supprimerLeCompte.msg": "Are you sure you want to delete this account?",
|
||||||
"sword.none": "$t(sans) / $t(nonDéfinie)",
|
"sword.none": "$t(sans) / $t(nonDéfinie)",
|
||||||
"sword.oneHand": "One hand",
|
"sword.oneHand": "One hand sword",
|
||||||
"sword.saber": "Saber",
|
"sword.saber": "Saber",
|
||||||
"sword.twoHand": "Two hands",
|
"sword.twoHand": "Two hands sword",
|
||||||
"sélectionEnéquipeDeFrance": "Selection in the French team",
|
"sélectionEnéquipeDeFrance": "Selection in the French team",
|
||||||
"sélectionner...": "Select...",
|
"sélectionner...": "Select...",
|
||||||
"toast.edit.error": "Failed to save changes",
|
"toast.edit.error": "Failed to save changes",
|
||||||
|
|||||||
@ -10,6 +10,8 @@
|
|||||||
"bleu": "Blue",
|
"bleu": "Blue",
|
||||||
"catégorie": "Category",
|
"catégorie": "Category",
|
||||||
"chargement": "Loading",
|
"chargement": "Loading",
|
||||||
|
"classement": "Ranking",
|
||||||
|
"classementFinal": "Final standings",
|
||||||
"club": "Club",
|
"club": "Club",
|
||||||
"combattant": "Fighter",
|
"combattant": "Fighter",
|
||||||
"combattants": "Fighters",
|
"combattants": "Fighters",
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
"actuel": "Actuel",
|
"actuel": "Actuel",
|
||||||
"administration": "Administration",
|
"administration": "Administration",
|
||||||
"adresseDuServeur": "Adresse du serveur",
|
"adresseDuServeur": "Adresse du serveur",
|
||||||
|
"ajoutAutomatique": "Ajout automatique",
|
||||||
"ajouter": "Ajouter",
|
"ajouter": "Ajouter",
|
||||||
"ajouterDesCombattants": "Ajouter des combattants",
|
"ajouterDesCombattants": "Ajouter des combattants",
|
||||||
"ajouterUn": "Ajouter un ",
|
"ajouterUn": "Ajouter un ",
|
||||||
@ -35,7 +36,9 @@
|
|||||||
"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",
|
||||||
|
"combattantsCorrespondentAuxSélectionnés": "combattant(s) correspondent aux sélectionnés ci-dessus.",
|
||||||
"compétition": "Compétition",
|
"compétition": "Compétition",
|
||||||
"compétitionManager": "Compétition manager",
|
"compétitionManager": "Compétition manager",
|
||||||
"config.obs.dossierDesResources": "Dossier des resources",
|
"config.obs.dossierDesResources": "Dossier des resources",
|
||||||
@ -55,6 +58,8 @@
|
|||||||
"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",
|
||||||
@ -79,10 +84,14 @@
|
|||||||
"genre.h": "H",
|
"genre.h": "H",
|
||||||
"genre.na": "NA",
|
"genre.na": "NA",
|
||||||
"individuelle": "Individuelle",
|
"individuelle": "Individuelle",
|
||||||
|
"informationCatégorie": "Information catégorie",
|
||||||
"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:",
|
||||||
|
"matchTerminé": "Match terminé",
|
||||||
"matches": "Matches",
|
"matches": "Matches",
|
||||||
"modifier": "Modifier",
|
"modifier": "Modifier",
|
||||||
"msg1": "Il y a déjà des matchs dans cette poule, que voulez-vous faire avec ?",
|
"msg1": "Il y a déjà des matchs dans cette poule, que voulez-vous faire avec ?",
|
||||||
@ -92,6 +101,7 @@
|
|||||||
"nomDeLaZone": "Nom de la zone",
|
"nomDeLaZone": "Nom de la zone",
|
||||||
"nomDeLéquipe": "Nom de l'équipe",
|
"nomDeLéquipe": "Nom de l'équipe",
|
||||||
"nomDesZonesDeCombat": "Nom des zones de combat <1>(séparée par des ';')</1>",
|
"nomDesZonesDeCombat": "Nom des zones de combat <1>(séparée par des ';')</1>",
|
||||||
|
"nombreDeCombattants": "Nombre de combattants",
|
||||||
"nouvelle...": "Nouvelle...",
|
"nouvelle...": "Nouvelle...",
|
||||||
"obs.préfixDesSources": "Préfix des sources",
|
"obs.préfixDesSources": "Préfix des sources",
|
||||||
"pays": "Pays",
|
"pays": "Pays",
|
||||||
@ -99,6 +109,7 @@
|
|||||||
"poule": "Poule",
|
"poule": "Poule",
|
||||||
"poulePour": "Poule pour: ",
|
"poulePour": "Poule pour: ",
|
||||||
"préparation...": "Préparation...",
|
"préparation...": "Préparation...",
|
||||||
|
"remplacer": "Remplacer",
|
||||||
"rouge": "Rouge",
|
"rouge": "Rouge",
|
||||||
"réinitialiser": "Réinitialiser",
|
"réinitialiser": "Réinitialiser",
|
||||||
"résultat": "Résultat",
|
"résultat": "Résultat",
|
||||||
@ -122,6 +133,7 @@
|
|||||||
"supprimerUn": "Supprimer un",
|
"supprimerUn": "Supprimer un",
|
||||||
"sélectionneLesModesDaffichage": "Sélectionne les modes d'affichage",
|
"sélectionneLesModesDaffichage": "Sélectionne les modes d'affichage",
|
||||||
"sélectionner": "Sélectionner",
|
"sélectionner": "Sélectionner",
|
||||||
|
"taille": "Taille",
|
||||||
"team": "Équipe",
|
"team": "Équipe",
|
||||||
"terminé": "Terminé",
|
"terminé": "Terminé",
|
||||||
"texteCopiéDansLePresse": "Texte copié dans le presse-papier ! Collez-le dans une balise HTML sur votre WordPress.",
|
"texteCopiéDansLePresse": "Texte copié dans le presse-papier ! Collez-le dans une balise HTML sur votre WordPress.",
|
||||||
@ -134,6 +146,9 @@
|
|||||||
"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.",
|
||||||
@ -167,6 +182,7 @@
|
|||||||
"téléchargementEnCours": "Téléchargement en cours...",
|
"téléchargementEnCours": "Téléchargement en cours...",
|
||||||
"téléchargementTerminé!": "Téléchargement terminé !",
|
"téléchargementTerminé!": "Téléchargement terminé !",
|
||||||
"uneCatégorie": "une catégorie",
|
"uneCatégorie": "une catégorie",
|
||||||
|
"uneCatégorieNePeutContenirPlusDe10Combattants": "Une catégorie ne peut contenir plus de 10 combattants, veuillez créer des catégories de poids.",
|
||||||
"valider": "Valider",
|
"valider": "Valider",
|
||||||
"zone": "Zone",
|
"zone": "Zone",
|
||||||
"zoneDeCombat": "Zone de combat"
|
"zoneDeCombat": "Zone de combat"
|
||||||
|
|||||||
@ -623,9 +623,9 @@
|
|||||||
"supprimerLeCompte": "Supprimer le compte",
|
"supprimerLeCompte": "Supprimer le compte",
|
||||||
"supprimerLeCompte.msg": "Êtes-vous sûr de vouloir supprimer ce compte ?",
|
"supprimerLeCompte.msg": "Êtes-vous sûr de vouloir supprimer ce compte ?",
|
||||||
"sword.none": "$t(sans) / $t(nonDéfinie)",
|
"sword.none": "$t(sans) / $t(nonDéfinie)",
|
||||||
"sword.oneHand": "Une main",
|
"sword.oneHand": "Épée une main",
|
||||||
"sword.saber": "Sabre",
|
"sword.saber": "Sabre",
|
||||||
"sword.twoHand": "Deux mains",
|
"sword.twoHand": "Épée deux mains",
|
||||||
"sélectionEnéquipeDeFrance": "Sélection en équipe de France",
|
"sélectionEnéquipeDeFrance": "Sélection en équipe de France",
|
||||||
"sélectionner...": "Sélectionner...",
|
"sélectionner...": "Sélectionner...",
|
||||||
"toast.edit.error": "Échec de l'enregistrement des modifications",
|
"toast.edit.error": "Échec de l'enregistrement des modifications",
|
||||||
|
|||||||
@ -10,6 +10,8 @@
|
|||||||
"bleu": "Bleu",
|
"bleu": "Bleu",
|
||||||
"catégorie": "Catégorie",
|
"catégorie": "Catégorie",
|
||||||
"chargement": "Chargement",
|
"chargement": "Chargement",
|
||||||
|
"classement": "Classement",
|
||||||
|
"classementFinal": "Classement final",
|
||||||
"club": "Club",
|
"club": "Club",
|
||||||
"combattant": "Combattant",
|
"combattant": "Combattant",
|
||||||
"combattants": "Combattants",
|
"combattants": "Combattants",
|
||||||
|
|||||||
232
src/main/webapp/src/components/cm/AutoCatModalContent.jsx
Normal file
232
src/main/webapp/src/components/cm/AutoCatModalContent.jsx
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
import {useCountries} from "../../hooks/useCountries.jsx";
|
||||||
|
import {ListPresetSelect} from "./ListPresetSelect.jsx";
|
||||||
|
import {CatList, getCatName} from "../../utils/Tools.js";
|
||||||
|
|
||||||
|
export function AutoCatModalContent({data, groups, setGroups, defaultPreset = -1}) {
|
||||||
|
const country = useCountries('fr')
|
||||||
|
const {t} = useTranslation("cm");
|
||||||
|
|
||||||
|
const [country_, setCountry_] = useState("")
|
||||||
|
const [gender, setGender] = useState({H: true, F: true, NA: true})
|
||||||
|
const [cat, setCat] = useState([])
|
||||||
|
const [weightMin, setWeightMin] = useState(0)
|
||||||
|
const [weightMax, setWeightMax] = useState(0)
|
||||||
|
const [team, setTeam] = useState(false)
|
||||||
|
const [preset, setPreset] = useState(-1)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPreset(defaultPreset)
|
||||||
|
}, [defaultPreset])
|
||||||
|
|
||||||
|
const setCat_ = (e, index) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
if (!cat.includes(index)) {
|
||||||
|
setCat([...cat, index])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCat(cat.filter(c => c !== index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyFilter(dataIn, dataOut) {
|
||||||
|
dataIn.forEach(comb_ => {
|
||||||
|
const comb = data.find(d => d.id === comb_.id);
|
||||||
|
if (comb == null)
|
||||||
|
return;
|
||||||
|
if ((country_ === "" || comb.country === country_)
|
||||||
|
&& (gender.H && comb.genre === 'H' || gender.F && comb.genre === 'F' || gender.NA && comb.genre === 'NA')
|
||||||
|
&& (cat.includes(Math.min(CatList.length, CatList.indexOf(comb.categorie) + comb.overCategory)))
|
||||||
|
&& (weightMin === 0 || comb.weight !== null && comb.weight >= weightMin)
|
||||||
|
&& (weightMax === 0 || comb.weight !== null && comb.weight <= weightMax)
|
||||||
|
&& ((comb.teamMembers == null || comb.teamMembers.length === 0) !== team)
|
||||||
|
&& (preset === -1 || comb.categoriesInscrites.includes(preset))) {
|
||||||
|
dataOut.push(comb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispoFiltered = [];
|
||||||
|
if (data != null)
|
||||||
|
applyFilter(data, dispoFiltered);
|
||||||
|
|
||||||
|
const makePoule = (combIn, groups) => {
|
||||||
|
combIn = combIn.sort(() => Math.random() - 0.5);
|
||||||
|
const maxInPoule = Math.ceil(combIn.length / 2);
|
||||||
|
const out = []
|
||||||
|
|
||||||
|
const pa = [];
|
||||||
|
const pb = [];
|
||||||
|
|
||||||
|
let nameA;
|
||||||
|
let nameB;
|
||||||
|
groups.forEach(g => {
|
||||||
|
const existsInCombIn = combIn.some(c => c.id === g.id);
|
||||||
|
if (existsInCombIn) {
|
||||||
|
if ((pa.length === 0 || g.poule === nameA) && pa.length < maxInPoule) {
|
||||||
|
nameA = g.poule || "1";
|
||||||
|
pa.push(g.id);
|
||||||
|
} else if ((pb.length === 0 || g.poule === nameB) && pb.length < maxInPoule) {
|
||||||
|
if (!(nameA === (g.poule || (nameA === "1" ? "2" : "1")))) {
|
||||||
|
nameB = g.poule || (nameA === "1" ? "2" : "1");
|
||||||
|
pb.push(g.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nameA = nameA || (nameB === "1" ? "2" : "1");
|
||||||
|
nameB = nameB || (nameA === "1" ? "2" : "1");
|
||||||
|
|
||||||
|
if (combIn.length <= 5) {
|
||||||
|
combIn.forEach(c => {
|
||||||
|
if (!pa.includes(c.id))
|
||||||
|
pa.push(c.id)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for (const c of combIn) {
|
||||||
|
if (pa.includes(c.id) || pb.includes(c.id))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const club = c.club_str || (c.teamMembers && c.teamMembers[0].club_str) || "";
|
||||||
|
|
||||||
|
const countInPa = pa.filter(p => (p.club_str || (p.teamMembers && p.teamMembers[0].club_str) || "") === club).length;
|
||||||
|
const countInPb = pb.filter(p => (p.club_str || (p.teamMembers && p.teamMembers[0].club_str) || "") === club).length;
|
||||||
|
|
||||||
|
if (pa.length < maxInPoule && (countInPa <= countInPb || pb.length >= maxInPoule)) {
|
||||||
|
pa.push(c.id);
|
||||||
|
} else if (pb.length < maxInPoule) {
|
||||||
|
pb.push(c.id);
|
||||||
|
} else {
|
||||||
|
pa.push(c.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pa.forEach(id => out.push({id: id, poule: nameA}));
|
||||||
|
pb.forEach(id => out.push({id: id, poule: nameB}));
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const toReplace = makePoule(dispoFiltered, groups);
|
||||||
|
setGroups(prev => [...prev.filter(g => !toReplace.some(r => r.id === g.id)), ...toReplace]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReplace = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const toReplace = makePoule(dispoFiltered, []);
|
||||||
|
setGroups(prev => [...prev.map(g => ({id: g.id, poule: "-"})).filter(g => !toReplace.some(r => r.id === g.id)), ...toReplace]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<div className="modal-header">
|
||||||
|
<h1 className="modal-title fs-5" id="autoCatModalLabel">{t('ajoutAutomatique')}</h1>
|
||||||
|
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="d-flex flex-wrap justify-content-around mb-1">
|
||||||
|
<div style={{width: "12em"}}>
|
||||||
|
<label htmlFor="inputState0" className="form-label">{t('pays')}</label>
|
||||||
|
<select id="inputState0" className="form-select" value={country_} onChange={(e) => setCountry_(e.target.value)}>
|
||||||
|
<option value={""}>{t('--Tous--')}</option>
|
||||||
|
{country && Object.keys(country).sort((a, b) => {
|
||||||
|
if (a < b) return -1
|
||||||
|
if (a > b) return 1
|
||||||
|
return 0
|
||||||
|
}).map((key, _) => {
|
||||||
|
return (<option key={key} value={key}>{country[key]}</option>)
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ListPresetSelect value={preset} onChange={setPreset}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex flex-wrap justify-content-around mb-3">
|
||||||
|
<div>
|
||||||
|
<label className="form-label">{t('genre')}</label>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<div className="form-check" style={{marginRight: '10px'}}>
|
||||||
|
<input className="form-check-input" type="checkbox" id="gridCheck" checked={gender.H}
|
||||||
|
onChange={e => setGender((prev) => {
|
||||||
|
return {...prev, H: e.target.checked}
|
||||||
|
})}/>
|
||||||
|
<label className="form-check-label" htmlFor="gridCheck">{t('genre.h')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-check" style={{marginRight: '10px'}}>
|
||||||
|
<input className="form-check-input" type="checkbox" id="gridCheck2" checked={gender.F}
|
||||||
|
onChange={e => setGender((prev) => {
|
||||||
|
return {...prev, F: e.target.checked}
|
||||||
|
})}/>
|
||||||
|
<label className="form-check-label" htmlFor="gridCheck2">{t('genre.f')}</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-check">
|
||||||
|
<input className="form-check-input" type="checkbox" id="gridCheck3" checked={gender.NA}
|
||||||
|
onChange={e => setGender((prev) => {
|
||||||
|
return {...prev, NA: e.target.checked}
|
||||||
|
})}/>
|
||||||
|
<label className="form-check-label" htmlFor="gridCheck3">{t('genre.na')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="form-label">{t('team')}</label>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<div className="form-check" style={{marginRight: '10px'}}>
|
||||||
|
<input className="form-check-input" type="checkbox" id="gridCheck" checked={team}
|
||||||
|
onChange={e => setTeam(e.target.checked)}/>
|
||||||
|
<label className="form-check-label" htmlFor="gridCheck">{t('team')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="input5" className="form-label">{t('poids')}</label>
|
||||||
|
<div className="row-cols-sm-auto d-flex align-items-center">
|
||||||
|
<div style={{width: "4.25em"}}><input type="number" className="form-control" id="input5" value={weightMin} min="0"
|
||||||
|
name="999"
|
||||||
|
onChange={e => setWeightMin(Number(e.target.value))}/></div>
|
||||||
|
<div><span>{t('select.à')}</span></div>
|
||||||
|
<div style={{width: "4.25em"}}><input type="number" className="form-control" value={weightMax} min="0" name="999"
|
||||||
|
onChange={e => setWeightMax(Number(e.target.value))}/></div>
|
||||||
|
<div><small>{t('select.msg1')}</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex flex-wrap justify-content-around mb-1">
|
||||||
|
<div className="d-flex flex-wrap mb-3">
|
||||||
|
<label htmlFor="inputState2" className="form-label align-self-center" style={{margin: "0 0.5em 0 0"}}>
|
||||||
|
{t('catégorie')} :
|
||||||
|
</label>
|
||||||
|
{CatList.map((cat_, index) => {
|
||||||
|
return <div key={index} className="input-group"
|
||||||
|
style={{display: "contents"}}>
|
||||||
|
<div className="input-group-text">
|
||||||
|
<input className="form-check-input mt-0" type="checkbox"
|
||||||
|
id={"categoriesInput" + index} checked={cat.includes(index)} aria-label={getCatName(cat_)}
|
||||||
|
onChange={e => setCat_(e, index)}/>
|
||||||
|
<label style={{marginLeft: "0.5em"}} htmlFor={"categoriesInput" + index}>{getCatName(cat_)}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>{dispoFiltered.length} {t('combattantsCorrespondentAuxSélectionnés')} {dispoFiltered.length > 10 &&
|
||||||
|
<span style={{color: "red"}}>{t('uneCatégorieNePeutContenirPlusDe10Combattants')}</span>}</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="modal-footer">
|
||||||
|
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button>
|
||||||
|
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal" onClick={handleSubmit}
|
||||||
|
disabled={dispoFiltered.length <= 0 || dispoFiltered.length > 10}>{t('ajouter')}</button>
|
||||||
|
<button type="submit" className="btn btn-warning" data-bs-dismiss="modal" onClick={handleReplace}
|
||||||
|
disabled={dispoFiltered.length <= 0 || dispoFiltered.length > 10}>{t('remplacer')}</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
37
src/main/webapp/src/components/cm/ListPresetSelect.jsx
Normal file
37
src/main/webapp/src/components/cm/ListPresetSelect.jsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import {useRequestWS} from "../../hooks/useWS.jsx";
|
||||||
|
import {AxiosError} from "../AxiosError.jsx";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
import React, {useId} from "react";
|
||||||
|
|
||||||
|
export function ListPresetSelect({disabled, value, onChange}) {
|
||||||
|
const id = useId()
|
||||||
|
const {data, error} = useRequestWS("listPreset", {}, null);
|
||||||
|
const {t} = useTranslation();
|
||||||
|
return <>
|
||||||
|
{data
|
||||||
|
? <div className="mb-3">
|
||||||
|
<label className="form-label" htmlFor={id}>{t('catégorie')}</label>
|
||||||
|
<select className="form-select" id={id} disabled={disabled}
|
||||||
|
value={value} onChange={e => onChange(Number(e.target.value))}>
|
||||||
|
<option value={-1}>{t('sélectionner...')}</option>
|
||||||
|
{data.sort((a, b) => a.name.localeCompare(b.name)).map(club => (<option key={club.id} value={club.id}>{club.name}</option>))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
: error
|
||||||
|
? <AxiosError error={error}/>
|
||||||
|
: <Def/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Def() {
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
return <div className="input-group mb-3">
|
||||||
|
<label className="input-group-text" id="inputGroupSelect02">{t('catégorie')}</label>
|
||||||
|
<select className="form-select" id="inputGroupSelect02"
|
||||||
|
defaultValue={t('chargement...')}>
|
||||||
|
<option>{t('chargement...')}</option>
|
||||||
|
</select>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
@ -18,7 +18,8 @@ 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 {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
import {useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
||||||
|
import {ListPresetSelect} from "../../../components/cm/ListPresetSelect.jsx";
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -36,7 +37,10 @@ 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'}})
|
||||||
@ -465,7 +469,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>
|
||||||
</>}
|
</>}
|
||||||
@ -569,7 +573,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 ? t('tournois') : ""} |
|
<div>Type: {(cat.type & 1) !== 0 ? t('poule') : ""}{cat.type === 3 ? " & " : ""}{(cat.type & 2) !== 0 ? (cat.treeAreClassement ? t('classement') : t('tournois')) : ""} |
|
||||||
Zone: {cat.liceName}</div>}
|
Zone: {cat.liceName}</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-auto">
|
<div className="col-auto">
|
||||||
@ -603,8 +607,11 @@ 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 [preset, setPreset] = useState(-1)
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
|
|
||||||
const {sendRequest} = useWS();
|
const {sendRequest} = useWS();
|
||||||
@ -614,14 +621,23 @@ 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);
|
||||||
|
setPreset(state.preset?.id || -1);
|
||||||
|
|
||||||
if (state?.trees && state.trees.length >= 1) {
|
let trees_ = []
|
||||||
const tree = state.trees[0];
|
for (let i = 0; i < state?.raw_trees?.length; i++) {
|
||||||
|
if (state.raw_trees.at(i).level > 0) {
|
||||||
|
trees_.push(state.trees.at(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trees_ && trees_.length >= 1) {
|
||||||
|
const tree = trees_[0];
|
||||||
setSize(tree.getMaxChildrenAtDepth(tree.death() - 1) * 2);
|
setSize(tree.getMaxChildrenAtDepth(tree.death() - 1) * 2);
|
||||||
|
|
||||||
if (state.trees.length === 1) {
|
if (trees_.length === 1) {
|
||||||
setLoserMatch(0);
|
setLoserMatch(0);
|
||||||
} else if (state.trees.length === 2) {
|
} else if (trees_.length === 2) {
|
||||||
setLoserMatch(1);
|
setLoserMatch(1);
|
||||||
} else {
|
} else {
|
||||||
setLoserMatch(-1);
|
setLoserMatch(-1);
|
||||||
@ -647,24 +663,33 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let trees_ = []
|
||||||
|
for (let i = 0; i < state?.raw_trees?.length; i++) {
|
||||||
|
if (state.raw_trees.at(i).level > 0) {
|
||||||
|
trees_.push(state.trees.at(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
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,
|
||||||
|
preset: {id: preset !== -1 ? preset : null}
|
||||||
}
|
}
|
||||||
|
|
||||||
let nbMatch = -1;
|
let nbMatch = -1;
|
||||||
let oldSubTree = -1;
|
let oldSubTree = -1;
|
||||||
const oldTrees = state?.trees || [];
|
const oldTrees = trees_ || [];
|
||||||
if (oldTrees.length >= 1) {
|
if (oldTrees.length >= 1) {
|
||||||
const tree = state.trees[0];
|
const tree = trees_[0];
|
||||||
nbMatch = tree.getMaxChildrenAtDepth(tree.death() - 1);
|
nbMatch = tree.getMaxChildrenAtDepth(tree.death() - 1);
|
||||||
if (state.trees.length === 1)
|
if (trees_.length === 1)
|
||||||
oldSubTree = 0
|
oldSubTree = 0
|
||||||
else if (state.trees.length === 2)
|
else if (trees_.length === 2)
|
||||||
oldSubTree = 1
|
oldSubTree = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,7 +749,10 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
|
|||||||
toast.promise(sendRequest('createCategory', {
|
toast.promise(sendRequest('createCategory', {
|
||||||
name: name.trim(),
|
name: name.trim(),
|
||||||
liceName: lice.trim(),
|
liceName: lice.trim(),
|
||||||
type: nType
|
type: nType,
|
||||||
|
treeAreClassement: classement,
|
||||||
|
fullClassement: fullClassement,
|
||||||
|
preset: {id: preset !== -1 ? preset : null}
|
||||||
}), getToastMessage("toast.createCategory", "cm")
|
}), getToastMessage("toast.createCategory", "cm")
|
||||||
).then(id => {
|
).then(id => {
|
||||||
if (tournoi) {
|
if (tournoi) {
|
||||||
@ -738,15 +766,6 @@ 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}>
|
||||||
@ -762,6 +781,8 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
|
|||||||
onChange={e => setName(e.target.value)}/>
|
onChange={e => setName(e.target.value)}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ListPresetSelect value={preset} onChange={setPreset}/>
|
||||||
|
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label htmlFor="liceInput1" className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des ';')</small></Trans></label>
|
<label htmlFor="liceInput1" className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des ';')</small></Trans></label>
|
||||||
<input type="text" className="form-control" id="liceInput1" placeholder="1;2" name="zone de combat" value={lice}
|
<input type="text" className="form-control" id="liceInput1" placeholder="1;2" name="zone de combat" value={lice}
|
||||||
@ -782,7 +803,16 @@ 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>
|
||||||
@ -821,6 +851,14 @@ 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>
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {timePrint} from "../../../utils/Tools.js";
|
|||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useWS} from "../../../hooks/useWS.jsx";
|
import {useWS} from "../../../hooks/useWS.jsx";
|
||||||
|
|
||||||
export function ChronoPanel() {
|
export function ChronoPanel({menuActions}) {
|
||||||
const [config, setConfig] = useState({
|
const [config, setConfig] = useState({
|
||||||
time: Number(sessionStorage.getItem("chronoTime") || "90999"),
|
time: Number(sessionStorage.getItem("chronoTime") || "90999"),
|
||||||
pause: Number(sessionStorage.getItem("chronoPause") || "60999")
|
pause: Number(sessionStorage.getItem("chronoPause") || "60999")
|
||||||
@ -25,6 +25,10 @@ export function ChronoPanel() {
|
|||||||
return chrono.time + Date.now() - chrono.startTime
|
return chrono.time + Date.now() - chrono.startTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menuActions.current.setTimerConfig = (time, pause) => {
|
||||||
|
setConfig({time: time + 999, pause: pause + 999})
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
publicAffDispatch({type: 'CALL_TIME', payload: {timeStr: state.current.lastTimeStr, timeColor: state.current.color}})
|
publicAffDispatch({type: 'CALL_TIME', payload: {timeStr: state.current.lastTimeStr, timeColor: state.current.color}})
|
||||||
}, [])
|
}, [])
|
||||||
|
|||||||
@ -6,12 +6,24 @@ 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 {virtualScore, win_end} from "../../../utils/Tools.js";
|
import {
|
||||||
|
CatList,
|
||||||
|
getCatName, getShieldSize,
|
||||||
|
getShieldTypeName, getSwordSize,
|
||||||
|
getSwordTypeName,
|
||||||
|
getToastMessage, timePrint,
|
||||||
|
virtual_end,
|
||||||
|
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";
|
||||||
|
import {createPortal} from "react-dom";
|
||||||
|
import ProtectionSelector from "../../../components/ProtectionSelector.jsx";
|
||||||
|
|
||||||
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"
|
||||||
@ -70,7 +82,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([]);
|
const [trees, setTrees] = useState({raw: [], formatted: []});
|
||||||
const [matches, reducer] = useReducer(MarchReducer, []);
|
const [matches, reducer] = useReducer(MarchReducer, []);
|
||||||
const combDispatch = useCombsDispatch();
|
const combDispatch = useCombsDispatch();
|
||||||
const cardDispatch = useCardsDispatch();
|
const cardDispatch = useCardsDispatch();
|
||||||
@ -89,7 +101,10 @@ function CMTMatchPanel({catId, cat, menuActions}) {
|
|||||||
setLoading(1);
|
setLoading(1);
|
||||||
sendRequest('getFullCategory', catId)
|
sendRequest('getFullCategory', catId)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setTrees(data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)))
|
setTrees({
|
||||||
|
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});
|
||||||
|
|
||||||
@ -105,7 +120,10 @@ 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(data.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)))
|
setTrees({
|
||||||
|
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 = [];
|
||||||
@ -142,11 +160,102 @@ function CMTMatchPanel({catId, cat, menuActions}) {
|
|||||||
}
|
}
|
||||||
}, [catId]);
|
}, [catId]);
|
||||||
|
|
||||||
return <ListMatch cat={cat} matches={matches} trees={trees} menuActions={menuActions}/>
|
return <>
|
||||||
|
<ListMatch cat={cat} matches={matches} trees={trees} menuActions={menuActions}/>
|
||||||
|
<SetTimeToChrono cat={cat} matches={matches} menuActions={menuActions}/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetTimeToChrono({cat, matches, menuActions}) {
|
||||||
|
const [catAverage, setCatAverage] = useState("---");
|
||||||
|
const [genreAverage, setGenreAverage] = useState("H");
|
||||||
|
const [nbComb, setNbComb] = useState(0);
|
||||||
|
const [preset, setPreset] = useState(undefined);
|
||||||
|
const [time, setTime] = useState({round: 0, pause: 0});
|
||||||
|
const {cards_v} = useCards();
|
||||||
|
|
||||||
|
const {t} = useTranslation("cm");
|
||||||
|
const {getComb} = useCombs();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!cat || matches.filter(m => m.categorie === cat.id).length === 0) {
|
||||||
|
setCatAverage("---");
|
||||||
|
setNbComb(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPreset(cat.preset);
|
||||||
|
|
||||||
|
const genres = [];
|
||||||
|
const cats = [];
|
||||||
|
const combs = [];
|
||||||
|
for (const m of matches.filter(m => m.categorie === cat.id)) {
|
||||||
|
if (m.c1 && !combs.includes(m.c1))
|
||||||
|
combs.push(m.c1);
|
||||||
|
if (m.c2 && !combs.includes(m.c2))
|
||||||
|
combs.push(m.c2);
|
||||||
|
}
|
||||||
|
setNbComb(combs.length);
|
||||||
|
|
||||||
|
combs.map(cId => getComb(cId, null)).filter(c => c && c.categorie)
|
||||||
|
.forEach(c => {
|
||||||
|
cats.push(Math.min(CatList.length, CatList.indexOf(c.categorie) + c.overCategory))
|
||||||
|
genres.push(c.genre)
|
||||||
|
});
|
||||||
|
|
||||||
|
const catAvg = Math.round(cats.reduce((a, b) => a + b, 0) / cats.length);
|
||||||
|
setCatAverage(CatList.at(catAvg) || "---");
|
||||||
|
|
||||||
|
const genreAvg = Math.round(genres.reduce((a, b) => a + (b === "F" ? 1 : 0), 0) / genres.length);
|
||||||
|
setGenreAverage(genreAvg > 0.5 ? "F" : "H");
|
||||||
|
|
||||||
|
if (!cat.preset || !cat.preset.categories)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const catAvailable = cat.preset.categories.map(c => CatList.indexOf(c.categorie));
|
||||||
|
|
||||||
|
let p;
|
||||||
|
if (catAvailable.includes(catAvg)) {
|
||||||
|
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) === catAvg);
|
||||||
|
} else {
|
||||||
|
const closest = catAvailable.reduce((a, b) => Math.abs(b - catAvg) < Math.abs(a - catAvg) ? b : a);
|
||||||
|
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) === closest);
|
||||||
|
}
|
||||||
|
menuActions.current.setTimerConfig(p.roundDuration, p.pauseDuration)
|
||||||
|
setTime({round: p.roundDuration, pause: p.pauseDuration})
|
||||||
|
|
||||||
|
}, [cat, matches]);
|
||||||
|
|
||||||
|
const marches2 = matches.filter(m => m.categorie === cat.id)
|
||||||
|
.map(m => ({...m, end: m.end || virtual_end(m, cards_v)}))
|
||||||
|
|
||||||
|
return createPortal(<div className="card mb-3">
|
||||||
|
<div className="card-header">{t('informationCatégorie')}</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col text-start">
|
||||||
|
<div>{t('catégorie')} : {getCatName(catAverage)}</div>
|
||||||
|
<div>{t('arme', {ns: 'common'})} : {getSwordTypeName(preset?.sword)} - {t('taille')} {getSwordSize(preset?.sword, catAverage, genreAverage)}</div>
|
||||||
|
<div>{t('bouclier', {ns: 'common'})} : {getShieldTypeName(preset?.shield)} - {t('taille')} {getShieldSize(preset?.shield, catAverage)}</div>
|
||||||
|
<div>{t('duréeRound')} : {timePrint(time.round)}</div>
|
||||||
|
<div>{t('duréePause')} : {timePrint(time.pause)}</div>
|
||||||
|
<div>{t('matchTerminé')}: {marches2.filter(m => m.end).length} sur {marches2.length}</div>
|
||||||
|
<div>{t('nombreDeCombattants')} : {nbComb}</div>
|
||||||
|
</div>
|
||||||
|
<div className="col text-center">
|
||||||
|
<h6>{t('protectionObligatoire', {ns: 'common'})} :</h6>
|
||||||
|
<ProtectionSelector shield={preset?.shield !== "NONE"}
|
||||||
|
mandatoryProtection={CatList.indexOf(catAverage) <= CatList.indexOf("JUNIOR") ?
|
||||||
|
preset?.mandatoryProtection1 : preset?.mandatoryProtection2} setMandatoryProtection={() => {
|
||||||
|
}}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>, document.getElementById("infoCategory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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(() => {
|
||||||
@ -156,6 +265,10 @@ 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 <></>;
|
||||||
|
|
||||||
@ -169,7 +282,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)}>{t('tournois')}
|
onClick={_ => setType(2)}>{(cat.treeAreClassement ? t('classement') : t('tournois'))}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -181,12 +294,14 @@ function ListMatch({cat, matches, trees, menuActions}) {
|
|||||||
</>}
|
</>}
|
||||||
|
|
||||||
{type === 2 && <>
|
{type === 2 && <>
|
||||||
<BuildTree treeData={trees} matches={matches} menuActions={menuActions}/>
|
{cat.treeAreClassement && !matches.some(m => m.categorie === cat.id && m.categorie_ord === -42 && (m.c1 !== undefined || m.c2 !== undefined)) ? <>
|
||||||
|
<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}) {
|
function MatchList({matches, cat, menuActions, classement = false, currentMatch = null, setCurrentMatch, getNext}) {
|
||||||
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();
|
||||||
@ -195,12 +310,17 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
const {sendNotify, setState} = useWS();
|
const {sendNotify, setState} = useWS();
|
||||||
|
|
||||||
const liceName = (cat.liceName || "N/A").split(";");
|
const liceName = (cat.liceName || "N/A").split(";");
|
||||||
const marches2 = matches.filter(m => m.categorie_ord !== -42)
|
const marches2 = classement
|
||||||
|
? 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,16 +345,26 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
|
|
||||||
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 === activeMatch))
|
if (marches2.some(m => m.id === (classement ? currentMatch?.matchSelect : 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(() => {
|
||||||
@ -242,6 +372,13 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
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)
|
||||||
@ -287,8 +424,8 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
<table className="table table-striped table-hover">
|
<table className="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<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">Z</th>}
|
||||||
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">P</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">{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>
|
||||||
@ -299,11 +436,11 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
<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 === activeMatch ? "table-primary" : (m.end ? "table-success" : (isActiveMatch(index) ? "" : "table-warning"))}
|
className={m.id === (classement ? currentMatch?.matchSelect : activeMatch) ? "table-primary" : (m.end ? "table-success" : (isActiveMatch(index) ? "" : "table-warning"))}
|
||||||
onClick={() => setActiveMatch(m.id)}>
|
onClick={() => handleMatchClick(m.id)}>
|
||||||
<td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
|
{!classement && <td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
|
||||||
{liceName[(index - firstIndex) % liceName.length]}</td>
|
{liceName[(index - firstIndex) % liceName.length]}</td>}
|
||||||
<td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>{m.poule}</td>
|
{!classement && <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>
|
||||||
@ -320,18 +457,20 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activeMatch &&
|
{activeMatch && !classement &&
|
||||||
<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, menuActions}) {
|
function BuildTree({treeData, matches, cat, 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(() => {
|
||||||
@ -350,11 +489,11 @@ function BuildTree({treeData, matches, menuActions}) {
|
|||||||
}
|
}
|
||||||
}, [next_match]);
|
}, [next_match]);
|
||||||
|
|
||||||
function parseTree(data_in) {
|
function parseTree(data_in, matches_) {
|
||||||
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)
|
||||||
|
|
||||||
@ -375,24 +514,57 @@ function BuildTree({treeData, matches, 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)
|
node.left = parseTree(data_in?.left, matches_)
|
||||||
node.right = parseTree(data_in?.right)
|
node.right = parseTree(data_in?.right, matches_)
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTree(data_in) {
|
function initTree(data_in, matches_) {
|
||||||
let out = []
|
let out = []
|
||||||
for (const din of data_in) {
|
let out2 = []
|
||||||
out.push(parseTree(din))
|
for (let i = 0; i < data_in.raw.length; i++) {
|
||||||
|
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 = initTree(treeData);
|
const [trees, rTrees] = initTree(treeData, matches);
|
||||||
|
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, __) => {
|
||||||
setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(trees.reverse())});
|
const rTrees_ = rtrees.current ? rtrees.current : rTrees;
|
||||||
|
setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(rTrees_)});
|
||||||
setState({selectedMatch: matchId});
|
setState({selectedMatch: matchId});
|
||||||
sendNotify("sendSelectMatch", matchId);
|
sendNotify("sendSelectMatch", matchId);
|
||||||
}
|
}
|
||||||
@ -406,6 +578,10 @@ function BuildTree({treeData, matches, 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>
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import React, {useEffect, useRef, useState} from "react";
|
import React, {useEffect, useRef, useState} from "react";
|
||||||
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
|
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
|
||||||
import {CombName, useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
|
import {useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {createPortal} from "react-dom";
|
import {createPortal} from "react-dom";
|
||||||
import {copyStyles} from "../../../utils/copyStyles.js";
|
import {copyStyles} from "../../../utils/copyStyles.js";
|
||||||
import {PubAffProvider, usePubAffDispatch, usePubAffState} from "../../../hooks/useExternalWindow.jsx";
|
import {PubAffProvider, usePubAffDispatch, usePubAffState} from "../../../hooks/useExternalWindow.jsx";
|
||||||
import {faArrowRightArrowLeft, faDisplay, faFile, faTrash} from "@fortawesome/free-solid-svg-icons";
|
import {faArrowRightArrowLeft, faDisplay, faFile} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {PubAffWindow} from "./PubAffWindow.jsx";
|
import {PubAffWindow} from "./PubAffWindow.jsx";
|
||||||
import {SimpleIconsScore} from "../../../assets/SimpleIconsScore.ts";
|
import {SimpleIconsScore} from "../../../assets/SimpleIconsScore.ts";
|
||||||
import {ChronoPanel} from "./CMTChronoPanel.jsx";
|
import {ChronoPanel} from "./CMTChronoPanel.jsx";
|
||||||
@ -15,7 +15,6 @@ import {importOBSConfiguration, OBSProvider, useOBS} from "../../../hooks/useOBS
|
|||||||
import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts";
|
import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts";
|
||||||
import {Flip, toast} from "react-toastify";
|
import {Flip, toast} from "react-toastify";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
|
||||||
import {getToastMessage} from "../../../utils/Tools.js";
|
import {getToastMessage} from "../../../utils/Tools.js";
|
||||||
|
|
||||||
export function CMTable() {
|
export function CMTable() {
|
||||||
@ -39,7 +38,7 @@ export function CMTable() {
|
|||||||
<div className="card mb-3">
|
<div className="card mb-3">
|
||||||
<div className="card-header">{t('chronomètre')}</div>
|
<div className="card-header">{t('chronomètre')}</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<ChronoPanel/>
|
<ChronoPanel menuActions={menuActions}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -49,6 +48,8 @@ export function CMTable() {
|
|||||||
<PointPanel menuActions={menuActions}/>
|
<PointPanel menuActions={menuActions}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="infoCategory"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-12 col-xl-6 col-xxl-5">
|
<div className="col-md-12 col-xl-6 col-xxl-5">
|
||||||
<div className="card mb-3">
|
<div className="card mb-3">
|
||||||
@ -251,7 +252,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>s
|
</button>
|
||||||
<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">
|
||||||
|
|||||||
@ -20,6 +20,8 @@ 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";
|
||||||
|
import {AutoCatModalContent} from "../../../components/cm/AutoCatModalContent.jsx";
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -62,7 +64,8 @@ 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 = [];
|
||||||
@ -122,7 +125,11 @@ 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});
|
||||||
@ -163,7 +170,7 @@ export function CategoryContent({cat, catId, setCat, menuActions}) {
|
|||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="col-md-3">
|
<div className="col-md-3">
|
||||||
<AddComb groups={groups} setGroups={setGroups} removeGroup={removeGroup} menuActions={menuActions}/>
|
<AddComb groups={groups} setGroups={setGroups} removeGroup={removeGroup} menuActions={menuActions} cat={cat}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-9">
|
<div className="col-md-9">
|
||||||
{cat && <ListMatch cat={cat} matches={matches} groups={groups} reducer={reducer}/>}
|
{cat && <ListMatch cat={cat} matches={matches} groups={groups} reducer={reducer}/>}
|
||||||
@ -171,7 +178,7 @@ export function CategoryContent({cat, catId, setCat, menuActions}) {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddComb({groups, setGroups, removeGroup, menuActions}) {
|
function AddComb({groups, setGroups, removeGroup, menuActions, cat}) {
|
||||||
const {data, setData} = useRequestWS("getRegister", null)
|
const {data, setData} = useRequestWS("getRegister", null)
|
||||||
const combDispatch = useCombsDispatch()
|
const combDispatch = useCombsDispatch()
|
||||||
const {dispatch} = useWS()
|
const {dispatch} = useWS()
|
||||||
@ -234,10 +241,23 @@ function AddComb({groups, setGroups, removeGroup, menuActions}) {
|
|||||||
disabled={data === null} onClick={() => setModalMode(true)}>{t('ajouterUneTeam')}
|
disabled={data === null} onClick={() => setModalMode(true)}>{t('ajouterUneTeam')}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button type="button" className="btn btn-primary mt-3 w-100" data-bs-toggle="modal" data-bs-target="#autoCatModal"
|
||||||
|
disabled={data === null}>{t('ajoutAutomatique')}
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className="modal fade" id="selectCombModal" tabIndex="-1" aria-labelledby="selectCombModalLabel" aria-hidden="true">
|
<div className="modal fade" id="selectCombModal" tabIndex="-1" aria-labelledby="selectCombModalLabel" aria-hidden="true">
|
||||||
<div className="modal-dialog modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
|
<div className="modal-dialog modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
<SelectCombModalContent data={data} setGroups={setGroups} teamMode={modalMode} groups={groups}/>
|
<SelectCombModalContent data={data} setGroups={setGroups} teamMode={modalMode} groups={groups}
|
||||||
|
defaultPreset={cat?.preset?.id || -1}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal fade" id="autoCatModal" tabIndex="-1" aria-labelledby="autoCatModalLabel" aria-hidden="true">
|
||||||
|
<div className="modal-dialog modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
|
||||||
|
<div className="modal-content">
|
||||||
|
<AutoCatModalContent data={data} groups={groups} setGroups={setGroups} defaultPreset={cat?.preset?.id || -1}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -360,6 +380,7 @@ 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(() => {
|
||||||
@ -375,6 +396,17 @@ 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 = [];
|
||||||
@ -416,7 +448,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)}>{t('tournois')}
|
onClick={_ => setType(2)}>{cat.treeAreClassement ? t('classement') : t('tournois')}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -428,7 +460,9 @@ function ListMatch({cat, matches, groups, reducer}) {
|
|||||||
</>}
|
</>}
|
||||||
|
|
||||||
{type === 2 && <>
|
{type === 2 && <>
|
||||||
<BuildTree treeData={cat.trees} matches={matches} groups={groups}/>
|
<BuildTree treeData={cat.trees} treeRaw={cat.raw_trees} cat={cat} 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>
|
||||||
@ -457,10 +491,16 @@ 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}) {
|
function MatchList({matches, cat, groups, reducer, classement = false}) {
|
||||||
const {sendRequest} = useWS();
|
const {sendRequest} = useWS();
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const selectRef = useRef(null)
|
const selectRef = useRef(null)
|
||||||
@ -475,9 +515,13 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
const matchModal = useRef(null);
|
const matchModal = useRef(null);
|
||||||
|
|
||||||
const liceName = (cat.liceName || "N/A").split(";");
|
const liceName = (cat.liceName || "N/A").split(";");
|
||||||
const marches2 = matches.filter(m => m.categorie_ord !== -42)
|
const marches2 = classement
|
||||||
|
? 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}];
|
||||||
@ -491,6 +535,9 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
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);
|
||||||
@ -570,7 +617,7 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
|
|
||||||
const handleDelMatch = (matchId) => {
|
const handleDelMatch = (matchId) => {
|
||||||
const match = matches.find(m => m.id === matchId)
|
const match = matches.find(m => m.id === matchId)
|
||||||
if (!match)
|
if (!match || classement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!confirm(t('confirm1')))
|
if (!confirm(t('confirm1')))
|
||||||
@ -585,7 +632,7 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,41 +692,46 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
<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>
|
||||||
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('poule')}</th>
|
{!classement &&
|
||||||
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('zone')}</th>
|
<th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}} scope="col">{t('poule')}</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>
|
||||||
<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>}
|
||||||
</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>
|
||||||
<td style={{textAlign: "center", cursor: "auto"}}>{m.poule}</td>
|
{!classement && <td style={{textAlign: "center", cursor: "auto"}}>{m.poule}</td>}
|
||||||
<td style={{textAlign: "center", cursor: "auto"}}>{liceName[index % liceName.length]}</td>
|
{!classement && <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"}}>☰</td>
|
<td style={{textAlign: "center", cursor: "grab"}} hidden={classement}>☰</td>
|
||||||
</SortableRow>
|
</SortableRow>
|
||||||
))}
|
))}
|
||||||
<tr>
|
{!classement && <tr>
|
||||||
<td>-</td>
|
<td>-</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
@ -695,7 +747,7 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -770,7 +822,7 @@ function SortableRow({id, children}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BuildTree({treeData, matches, groups}) {
|
function BuildTree({treeData, treeRaw, matches, cat, 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)
|
||||||
@ -811,10 +863,12 @@ function BuildTree({treeData, matches, groups}) {
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTree(data_in) {
|
function initTree(data_in, data_raw) {
|
||||||
let out = []
|
let out = []
|
||||||
for (const din of data_in) {
|
for (let i = 0; i < data_raw.length; i++) {
|
||||||
out.push(parseTree(din))
|
if (data_raw.at(i).level > -10) {
|
||||||
|
out.push(parseTree(data_in.at(i)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
@ -878,7 +932,10 @@ function BuildTree({treeData, matches, 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)} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid} cards={cards_v}/>
|
<DrawGraph root={initTree(treeData, treeRaw)} 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>
|
||||||
|
|||||||
@ -74,7 +74,8 @@ 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);
|
||||||
setScoreIn((comb === 1 ? score?.s1 : score?.s2) || "");
|
const tmp= (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);
|
||||||
}
|
}
|
||||||
@ -165,7 +166,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]
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import {useCountries} from "../../../hooks/useCountries.jsx";
|
import {useCountries} from "../../../hooks/useCountries.jsx";
|
||||||
import {useEffect, useReducer, useRef, useState} from "react";
|
import React, {useEffect, useReducer, useRef, useState} from "react";
|
||||||
import {CatList, getCatName, getToastMessage} from "../../../utils/Tools.js";
|
import {CatList, getCatName, getToastMessage} from "../../../utils/Tools.js";
|
||||||
import {CombName} from "../../../hooks/useComb.jsx";
|
import {CombName} from "../../../hooks/useComb.jsx";
|
||||||
import {useWS} from "../../../hooks/useWS.jsx";
|
import {useWS} from "../../../hooks/useWS.jsx";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {toast} from "react-toastify";
|
import {toast} from "react-toastify";
|
||||||
|
import {ListPresetSelect} from "../../../components/cm/ListPresetSelect.jsx";
|
||||||
|
|
||||||
function SelectReducer(state, action) {
|
function SelectReducer(state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -59,7 +60,7 @@ function SelectReducer(state, action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SelectCombModalContent({data, groups, setGroups, teamMode = false}) {
|
export function SelectCombModalContent({data, groups, setGroups, teamMode = false, defaultPreset = -1}) {
|
||||||
const country = useCountries('fr')
|
const country = useCountries('fr')
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
const {sendRequest, dispatch} = useWS()
|
const {sendRequest, dispatch} = useWS()
|
||||||
@ -77,6 +78,11 @@ export function SelectCombModalContent({data, groups, setGroups, teamMode = fals
|
|||||||
const [weightMax, setWeightMax] = useState(0)
|
const [weightMax, setWeightMax] = useState(0)
|
||||||
const [team, setTeam] = useState(false)
|
const [team, setTeam] = useState(false)
|
||||||
const [teamName, setTeamName] = useState("");
|
const [teamName, setTeamName] = useState("");
|
||||||
|
const [preset, setPreset] = useState(-1)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPreset(defaultPreset)
|
||||||
|
}, [defaultPreset])
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -149,7 +155,8 @@ export function SelectCombModalContent({data, groups, setGroups, teamMode = fals
|
|||||||
&& (weightMin === 0 || comb.weight !== null && comb.weight >= weightMin)
|
&& (weightMin === 0 || comb.weight !== null && comb.weight >= weightMin)
|
||||||
&& (weightMax === 0 || comb.weight !== null && comb.weight <= weightMax)
|
&& (weightMax === 0 || comb.weight !== null && comb.weight <= weightMax)
|
||||||
&& (teamMode && (comb.teamMembers == null || comb.teamMembers.length === 0) || !teamMode
|
&& (teamMode && (comb.teamMembers == null || comb.teamMembers.length === 0) || !teamMode
|
||||||
&& ((comb.teamMembers == null || comb.teamMembers.length === 0) !== team))) {
|
&& ((comb.teamMembers == null || comb.teamMembers.length === 0) !== team))
|
||||||
|
&& (preset === -1 || comb.categoriesInscrites.includes(preset))) {
|
||||||
dataOut[id] = dataIn[id];
|
dataOut[id] = dataIn[id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,6 +222,8 @@ export function SelectCombModalContent({data, groups, setGroups, teamMode = fals
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ListPresetSelect value={preset} onChange={setPreset}/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="inputState1" className="form-label">{t('club')}</label>
|
<label htmlFor="inputState1" className="form-label">{t('club')}</label>
|
||||||
<select id="inputState1" className="form-select" value={club} onChange={(e) => setClub(e.target.value)}>
|
<select id="inputState1" className="form-select" value={club} onChange={(e) => setClub(e.target.value)}>
|
||||||
|
|||||||
@ -14,6 +14,7 @@ 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"
|
||||||
@ -146,13 +147,52 @@ function BuildTree({treeData}) {
|
|||||||
|
|
||||||
function initTree(data_in) {
|
function initTree(data_in) {
|
||||||
let out = [];
|
let out = [];
|
||||||
for (const din of data_in) {
|
for (const din of data_in.filter(d => d.data.level >= 0)) {
|
||||||
out.push(parseTree(din));
|
out.push(parseTree(din));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <DrawGraph root={initTree(treeData)}/>
|
return <>
|
||||||
|
<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"}}>{t('place')}</th>
|
||||||
|
<th scope="col" style={{textAlign: "center"}}>{t('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}) {
|
||||||
@ -212,7 +252,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)}>{t('tournois')}</a>
|
onClick={_ => setType(2)}>{data.treeIsClassement ? t('classement') : t('tournois')}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</>}
|
</>}
|
||||||
@ -227,6 +267,7 @@ function CategoryResult({uuid, catId}) {
|
|||||||
|
|
||||||
{type === 2 && <>
|
{type === 2 && <>
|
||||||
<BuildTree treeData={data.trees}/>
|
<BuildTree treeData={data.trees}/>
|
||||||
|
{data.treeIsClassement && <ClassementList classement={data.classement}/>}
|
||||||
</>}
|
</>}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -22,8 +22,18 @@ 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) {
|
||||||
return `${msg} (😕 ${data.message})`
|
if (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)}) 😕`
|
||||||
@ -189,6 +199,38 @@ export function getSwordTypeName(type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSwordSize(type, cat, gender) {
|
||||||
|
const catIndex = CatList.indexOf(cat)
|
||||||
|
if (type === "ONE_HAND") {
|
||||||
|
if (catIndex <= 1) {
|
||||||
|
return "XS"
|
||||||
|
} else if (catIndex <= 3) {
|
||||||
|
return "S"
|
||||||
|
} else if (catIndex <= 6 || gender === "F") {
|
||||||
|
return "M"
|
||||||
|
} else {
|
||||||
|
return "L"
|
||||||
|
}
|
||||||
|
} else if (type === "TWO_HAND") {
|
||||||
|
if (catIndex <= 5) {
|
||||||
|
return "--"
|
||||||
|
} else if (catIndex <= 6 || gender === "F") {
|
||||||
|
return "XL"
|
||||||
|
} else {
|
||||||
|
return "XXL"
|
||||||
|
}
|
||||||
|
} else if (type === "SABER") {
|
||||||
|
if (catIndex <= 3) {
|
||||||
|
return "--"
|
||||||
|
} else if (catIndex <= 6) {
|
||||||
|
return "S"
|
||||||
|
} else {
|
||||||
|
return "L"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "--"
|
||||||
|
}
|
||||||
|
|
||||||
export const ShieldList = [
|
export const ShieldList = [
|
||||||
"NONE",
|
"NONE",
|
||||||
"STANDARD",
|
"STANDARD",
|
||||||
@ -214,6 +256,18 @@ export function getShieldTypeName(type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getShieldSize(type, cat) {
|
||||||
|
if (type === "STANDARD") {
|
||||||
|
const catIndex = CatList.indexOf(cat)
|
||||||
|
if (catIndex <= 1) {
|
||||||
|
return "S"
|
||||||
|
} else {
|
||||||
|
return "L"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "--"
|
||||||
|
}
|
||||||
|
|
||||||
export function getToastMessage(msgKey, ns = 'common') {
|
export function getToastMessage(msgKey, ns = 'common') {
|
||||||
return {
|
return {
|
||||||
pending: i18n.t(msgKey + '.pending', {ns}),
|
pending: i18n.t(msgKey + '.pending', {ns}),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user