Compare commits
No commits in common. "34a6911fa0dc6dc12164cc7ab43942b3bff6e352" and "59ba31ae2df571fbe499386fb4a808c4f97ed0f4" have entirely different histories.
34a6911fa0
...
59ba31ae2d
@ -1,64 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.data.model;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.*;
|
|
||||||
import org.hibernate.annotations.CreationTimestamp;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@ToString
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
@RegisterForReflection
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "card")
|
|
||||||
public class CardModel {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
Long id;
|
|
||||||
|
|
||||||
Long comb;
|
|
||||||
Long match;
|
|
||||||
Long category;
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
|
||||||
@JoinColumn(name = "competition", referencedColumnName = "id")
|
|
||||||
CompetitionModel competition;
|
|
||||||
|
|
||||||
@JsonProperty("competition")
|
|
||||||
Long competitionId;
|
|
||||||
|
|
||||||
CardType type;
|
|
||||||
String reason;
|
|
||||||
@CreationTimestamp
|
|
||||||
Date date;
|
|
||||||
|
|
||||||
@Column(nullable = false, columnDefinition = "boolean default false")
|
|
||||||
boolean teamCard = false;
|
|
||||||
|
|
||||||
public boolean hasEffect(MatchModel match) {
|
|
||||||
return switch (this.type) {
|
|
||||||
case BLUE -> false;
|
|
||||||
case YELLOW -> Objects.equals(this.match, match.getId());
|
|
||||||
case RED -> Objects.equals(this.category, match.getCategory().getId())
|
|
||||||
|| Objects.equals(this.match, match.getId());
|
|
||||||
case BLACK -> true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum CardType {
|
|
||||||
BLUE,
|
|
||||||
YELLOW,
|
|
||||||
RED,
|
|
||||||
BLACK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package fr.titionfire.ffsaf.data.model;
|
||||||
|
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "cardboard")
|
||||||
|
public class CardboardModel {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "comb", referencedColumnName = "id")
|
||||||
|
MembreModel comb;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "guest_comb", referencedColumnName = "id")
|
||||||
|
CompetitionGuestModel guestComb;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "match", referencedColumnName = "id")
|
||||||
|
MatchModel match;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "compet", referencedColumnName = "id")
|
||||||
|
CompetitionModel compet;
|
||||||
|
|
||||||
|
int red;
|
||||||
|
int yellow;
|
||||||
|
}
|
||||||
@ -1,38 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.data.model;
|
|
||||||
|
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.hibernate.annotations.CreationTimestamp;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
@RegisterForReflection
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "card_team")
|
|
||||||
public class ClubCardModel {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
Long id;
|
|
||||||
|
|
||||||
Long competition;
|
|
||||||
String teamUuid;
|
|
||||||
String teamName;
|
|
||||||
|
|
||||||
CardModel.CardType type;
|
|
||||||
String reason;
|
|
||||||
@CreationTimestamp
|
|
||||||
Date date;
|
|
||||||
|
|
||||||
List<Long> cardIds;
|
|
||||||
}
|
|
||||||
@ -63,6 +63,10 @@ public class MatchModel {
|
|||||||
|
|
||||||
char poule = 'A';
|
char poule = 'A';
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "match", referencedColumnName = "id")
|
||||||
|
List<CardboardModel> cardboard = new ArrayList<>();
|
||||||
|
|
||||||
public String getC1Name(MembreModel model, ResultPrivacy privacy) {
|
public String getC1Name(MembreModel model, ResultPrivacy privacy) {
|
||||||
if (c1_id != null)
|
if (c1_id != null)
|
||||||
return c1_id.getName(model, privacy);
|
return c1_id.getName(model, privacy);
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
package fr.titionfire.ffsaf.data.repository;
|
package fr.titionfire.ffsaf.data.repository;
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.CardModel;
|
import fr.titionfire.ffsaf.data.model.CardboardModel;
|
||||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class CardRepository implements PanacheRepositoryBase<CardModel, Long> {
|
public class CardboardRepository implements PanacheRepositoryBase<CardboardModel, Long> {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.data.repository;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.ClubCardModel;
|
|
||||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
|
|
||||||
@ApplicationScoped
|
|
||||||
public class ClubCardRepository implements PanacheRepositoryBase<ClubCardModel, Long> {
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package fr.titionfire.ffsaf.domain.entity;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CardboardModel;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RegisterForReflection
|
||||||
|
public class CardboardEntity {
|
||||||
|
long comb_id;
|
||||||
|
long match_id;
|
||||||
|
long compet_id;
|
||||||
|
|
||||||
|
int red;
|
||||||
|
int yellow;
|
||||||
|
|
||||||
|
public static CardboardEntity fromModel(CardboardModel model) {
|
||||||
|
return new CardboardEntity(
|
||||||
|
model.getComb() != null ? model.getComb().getId() : model.getGuestComb().getId() * -1,
|
||||||
|
model.getMatch().getId(),
|
||||||
|
model.getCompet().getId(),
|
||||||
|
model.getRed(), model.getYellow());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ public class MatchEntity {
|
|||||||
private Date date;
|
private Date date;
|
||||||
private List<ScoreEmbeddable> scores;
|
private List<ScoreEmbeddable> scores;
|
||||||
private char poule;
|
private char poule;
|
||||||
|
private List<CardboardEntity> cardboard;
|
||||||
|
|
||||||
public static MatchEntity fromModel(MatchModel model) {
|
public static MatchEntity fromModel(MatchModel model) {
|
||||||
if (model == null)
|
if (model == null)
|
||||||
@ -33,6 +35,22 @@ public class MatchEntity {
|
|||||||
model.getC2_id()),
|
model.getC2_id()),
|
||||||
model.getCategory_ord(), model.isEnd(), model.getCategory().getId(), model.getDate(),
|
model.getCategory_ord(), model.isEnd(), model.getCategory().getId(), model.getDate(),
|
||||||
model.getScores(),
|
model.getScores(),
|
||||||
model.getPoule());
|
model.getPoule(),
|
||||||
|
(model.getCardboard() == null) ? new ArrayList<>() : model.getCardboard().stream()
|
||||||
|
.map(CardboardEntity::fromModel).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int win() {
|
||||||
|
int sum = 0;
|
||||||
|
for (ScoreEmbeddable score : scores) {
|
||||||
|
if (score.getS1() == -1000 || score.getS2() == -1000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (score.getS1() > score.getS2())
|
||||||
|
sum++;
|
||||||
|
else if (score.getS1() < score.getS2())
|
||||||
|
sum--;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,176 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.domain.entity;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.*;
|
|
||||||
import fr.titionfire.ffsaf.utils.ResultPrivacy;
|
|
||||||
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@RegisterForReflection
|
|
||||||
public class MatchModelExtend {
|
|
||||||
final MatchModel match;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
boolean isEnd = false;
|
|
||||||
@Getter
|
|
||||||
List<ScoreEmbeddable> scoresToPrint = new ArrayList<>();
|
|
||||||
@Getter
|
|
||||||
List<ScoreEmbeddable> scoresToCompute = new ArrayList<>();
|
|
||||||
@Getter
|
|
||||||
int win = 0;
|
|
||||||
|
|
||||||
|
|
||||||
public MatchModelExtend(MatchModel match, List<CardModel> cards) {
|
|
||||||
this.match = match;
|
|
||||||
|
|
||||||
List<Long> combIds = extractCombIds(match);
|
|
||||||
List<CardModel> cards2 = cards.stream().filter(c -> combIds.contains(c.getComb()) && c.hasEffect(match))
|
|
||||||
.sorted(Comparator.comparing(CardModel::getType).reversed()).toList();
|
|
||||||
|
|
||||||
|
|
||||||
for (ScoreEmbeddable score : match.getScores()) {
|
|
||||||
if (score.getS1() == -1000 || score.getS2() == -1000)
|
|
||||||
continue;
|
|
||||||
this.scoresToCompute.add(virtualScore(score, cards2, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
calc_win_end(cards2);
|
|
||||||
|
|
||||||
for (ScoreEmbeddable score : match.getScores()) {
|
|
||||||
if (score.getS1() == -1000 || score.getS2() == -1000)
|
|
||||||
continue;
|
|
||||||
this.scoresToPrint.add(virtualScore(score, cards2, true));
|
|
||||||
}
|
|
||||||
if (this.isEnd && this.scoresToPrint.isEmpty()) {
|
|
||||||
this.scoresToPrint.add(virtualScore(new ScoreEmbeddable(0, 0, 0), cards2, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScoreEmbeddable virtualScore(ScoreEmbeddable score, List<CardModel> cards2, boolean toPrint) {
|
|
||||||
if (cards2.size() > 1) {
|
|
||||||
if (!Objects.equals(cards2.get(0).getComb(), cards2.get(1).getComb()))
|
|
||||||
return new ScoreEmbeddable(score.getN_round(), toPrint ? -997 : 0, toPrint ? -997 : 0);
|
|
||||||
}
|
|
||||||
if (!cards2.isEmpty()) {
|
|
||||||
if (isC1(cards2.get(0).getComb()))
|
|
||||||
return new ScoreEmbeddable(score.getN_round(), toPrint ? -997 : 0, 10);
|
|
||||||
else
|
|
||||||
return new ScoreEmbeddable(score.getN_round(), 10, toPrint ? -997 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (score.getS1() < -900 && score.getS2() < -900)
|
|
||||||
return new ScoreEmbeddable(score.getN_round(), toPrint ? score.getS1() : 0, toPrint ? score.getS2() : 0);
|
|
||||||
else if (score.getS1() < -900)
|
|
||||||
return new ScoreEmbeddable(score.getN_round(), toPrint ? score.getS1() : 0, 10);
|
|
||||||
else if (score.getS2() < -900)
|
|
||||||
return new ScoreEmbeddable(score.getN_round(), 10, toPrint ? score.getS2() : 0);
|
|
||||||
|
|
||||||
return new ScoreEmbeddable(score.getN_round(), score.getS1(), score.getS2());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calc_win_end(List<CardModel> cards2) {
|
|
||||||
if (cards2.size() > 1) {
|
|
||||||
if (!Objects.equals(cards2.get(0).getComb(), cards2.get(1).getComb())) {
|
|
||||||
this.win = 0;
|
|
||||||
this.isEnd = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cards2.isEmpty()) {
|
|
||||||
if (match.isC1(cards2.get(0).getComb())) {
|
|
||||||
this.win = -1;
|
|
||||||
} else if (match.isC2(cards2.get(0).getComb())) {
|
|
||||||
this.win = 1;
|
|
||||||
}
|
|
||||||
this.isEnd = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ScoreEmbeddable score : this.scoresToCompute) {
|
|
||||||
if (score.getS1() > score.getS2())
|
|
||||||
win++;
|
|
||||||
else if (score.getS1() < score.getS2())
|
|
||||||
win--;
|
|
||||||
}
|
|
||||||
this.isEnd = match.isEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private List<Long> extractCombIds(MatchModel match) {
|
|
||||||
List<Long> ids = new ArrayList<>();
|
|
||||||
if (match.getC1_id() != null)
|
|
||||||
ids.add(match.getC1_id().getId());
|
|
||||||
if (match.getC2_id() != null)
|
|
||||||
ids.add(match.getC2_id().getId());
|
|
||||||
if (match.getC1_guest() != null)
|
|
||||||
ids.add(match.getC1_guest().getId() * -1);
|
|
||||||
if (match.getC2_guest() != null)
|
|
||||||
ids.add(match.getC2_guest().getId() * -1);
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------- Delegation methods to MatchModel ---------------
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return match.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MembreModel getC1_id() {
|
|
||||||
return match.getC1_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompetitionGuestModel getC1_guest() {
|
|
||||||
return match.getC1_guest();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MembreModel getC2_id() {
|
|
||||||
return match.getC2_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompetitionGuestModel getC2_guest() {
|
|
||||||
return match.getC2_guest();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CategoryModel getCategory() {
|
|
||||||
return match.getCategory();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCategory_ord() {
|
|
||||||
return match.getCategory_ord();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getDate() {
|
|
||||||
return match.getDate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public char getPoule() {
|
|
||||||
return match.getPoule();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getC1Name(MembreModel model, ResultPrivacy privacy) {
|
|
||||||
return match.getC1Name(model, privacy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getC2Name(MembreModel model, ResultPrivacy privacy) {
|
|
||||||
return match.getC2Name(model, privacy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getC2Name() {
|
|
||||||
return match.getC2Name();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getC1Name() {
|
|
||||||
return match.getC1Name();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isC1(Object comb) {
|
|
||||||
return match.isC1(comb);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isC2(Object comb) {
|
|
||||||
return match.isC2(comb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,195 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.domain.service;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.CardModel;
|
|
||||||
import fr.titionfire.ffsaf.data.model.ClubCardModel;
|
|
||||||
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
|
||||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
|
||||||
import fr.titionfire.ffsaf.data.repository.*;
|
|
||||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
|
||||||
import fr.titionfire.ffsaf.ws.recv.RCard;
|
|
||||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
|
||||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
|
||||||
import io.quarkus.panache.common.Sort;
|
|
||||||
import io.smallrye.mutiny.Uni;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@WithSession
|
|
||||||
@ApplicationScoped
|
|
||||||
public class CardService {
|
|
||||||
@Inject
|
|
||||||
CardRepository cardRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ClubCardRepository clubCardRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
RegisterRepository registerRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CompetitionGuestRepository competitionGuestRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MatchRepository matchRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
TradService trad;
|
|
||||||
|
|
||||||
private static final List<CardModel.CardType> COMPETITION_LEVEL_CARDS = List.of(
|
|
||||||
CardModel.CardType.YELLOW,
|
|
||||||
CardModel.CardType.RED,
|
|
||||||
CardModel.CardType.BLACK
|
|
||||||
);
|
|
||||||
|
|
||||||
private List<Long> extractCombIds(MatchModel match) {
|
|
||||||
List<Long> ids = new ArrayList<>();
|
|
||||||
if (match.getC1_id() != null)
|
|
||||||
ids.add(match.getC1_id().getId());
|
|
||||||
if (match.getC2_id() != null)
|
|
||||||
ids.add(match.getC2_id().getId());
|
|
||||||
if (match.getC1_guest() != null)
|
|
||||||
ids.add(match.getC1_guest().getId() * -1);
|
|
||||||
if (match.getC2_guest() != null)
|
|
||||||
ids.add(match.getC2_guest().getId() * -1);
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<List<CardModel>> getForMatch(MatchModel match) {
|
|
||||||
return cardRepository.list(
|
|
||||||
"competition = ?1 AND (type IN ?2 OR (type = CardType.BLUE AND category = ?4)) AND comb IN ?3",
|
|
||||||
match.getCategory().getCompet(), COMPETITION_LEVEL_CARDS,
|
|
||||||
extractCombIds(match), match.getCategory().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<List<CardModel>> getAll(CompetitionModel competition) {
|
|
||||||
return cardRepository.list("competition = ?1", competition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<RCard.SendCardAdd> checkCanBeAdded(RCard.SendCardAdd card, MatchModel matchModel) {
|
|
||||||
return cardRepository.find("competition = ?1 AND comb = ?2",
|
|
||||||
Sort.descending("type"),
|
|
||||||
matchModel.getCategory().getCompet(), card.combId())
|
|
||||||
.firstResult()
|
|
||||||
.map(card_ -> {
|
|
||||||
if (card.type() == CardModel.CardType.BLUE) {
|
|
||||||
return card_ == null || (card_.getType() == CardModel.CardType.BLUE
|
|
||||||
&& !Objects.equals(card_.getCategory(), matchModel.getCategory().getId()));
|
|
||||||
}
|
|
||||||
if (card.type() == CardModel.CardType.BLACK) {
|
|
||||||
return card_ != null && card_.getType() == CardModel.CardType.RED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return card_ == null || card_.getType().ordinal() < card.type().ordinal();
|
|
||||||
})
|
|
||||||
.chain(b -> {
|
|
||||||
if (b)
|
|
||||||
return Uni.createFrom().item(card);
|
|
||||||
else
|
|
||||||
return Uni.createFrom().failure(new DBadRequestException(trad.t("card.cannot.be.added")));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<List<CardModel>> addTeamCard(CompetitionModel competition, String teamUuid, String teamName,
|
|
||||||
CardModel.CardType type, String reason) {
|
|
||||||
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
|
|
||||||
Sort.descending("type"), competition.getId(), teamUuid, teamName)
|
|
||||||
.firstResult()
|
|
||||||
.map(card_ -> {
|
|
||||||
if (type == CardModel.CardType.BLACK) {
|
|
||||||
return card_ != null && card_.getType() == CardModel.CardType.RED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return card_ == null || card_.getType().ordinal() < type.ordinal();
|
|
||||||
})
|
|
||||||
.chain(b -> {
|
|
||||||
if (!b)
|
|
||||||
return Uni.createFrom().failure(new DBadRequestException(trad.t("card.cannot.be.added")));
|
|
||||||
|
|
||||||
if (teamUuid != null) {
|
|
||||||
return registerRepository.list("competition = ?1 AND club.clubId = ?2", competition, teamUuid)
|
|
||||||
.map(l -> l.stream().map(r -> r.getMembre().getId()).toList());
|
|
||||||
} else {
|
|
||||||
return competitionGuestRepository.list("competition = ?1 AND club = ?2", competition,
|
|
||||||
teamName)
|
|
||||||
.map(l -> l.stream().map(r -> r.getId() * -1).toList());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.chain(combIds -> cardRepository.list("competition = ?1 AND comb IN ?2", competition, combIds)
|
|
||||||
.map(cards -> {
|
|
||||||
List<CardModel> newCards = new ArrayList<>();
|
|
||||||
for (Long id : combIds) {
|
|
||||||
Optional<CardModel> optional = cards.stream()
|
|
||||||
.filter(c -> id.equals(c.getComb()) && c.getType() == type).findAny();
|
|
||||||
|
|
||||||
CardModel model = new CardModel();
|
|
||||||
model.setCompetition(competition);
|
|
||||||
model.setCompetitionId(competition.getId());
|
|
||||||
model.setComb(id);
|
|
||||||
model.setTeamCard(true);
|
|
||||||
|
|
||||||
if (optional.isEmpty()) {
|
|
||||||
model.setType(type);
|
|
||||||
} else {
|
|
||||||
model.setType(
|
|
||||||
CardModel.CardType.values()[Math.min(optional.get().getType().ordinal() + 1,
|
|
||||||
CardModel.CardType.BLACK.ordinal())]);
|
|
||||||
}
|
|
||||||
newCards.add(model);
|
|
||||||
}
|
|
||||||
return newCards;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.call(newCards -> Panache.withTransaction(() -> cardRepository.persist(newCards)))
|
|
||||||
.call(newCards -> {
|
|
||||||
ClubCardModel model = new ClubCardModel();
|
|
||||||
model.setCompetition(competition.getId());
|
|
||||||
model.setTeamUuid(teamUuid);
|
|
||||||
model.setTeamName(teamName);
|
|
||||||
model.setType(type);
|
|
||||||
model.setReason(reason);
|
|
||||||
model.setCardIds(newCards.stream().map(CardModel::getId).toList());
|
|
||||||
|
|
||||||
return Panache.withTransaction(() -> clubCardRepository.persist(model));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<List<CardModel>> recvReturnState(CompetitionModel competition, RCard.SendTeamCardReturnState state) {
|
|
||||||
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3) AND type = ?4",
|
|
||||||
competition.getId(), state.teamUuid(), state.teamName(), state.type())
|
|
||||||
.firstResult()
|
|
||||||
.chain(o -> cardRepository.list("id IN ?1", o.getCardIds()))
|
|
||||||
.call(cards -> matchRepository.list("category.compet = ?1 AND category.id IN ?2", competition,
|
|
||||||
state.selectedCategory())
|
|
||||||
.invoke(matches -> {
|
|
||||||
for (CardModel card : cards) {
|
|
||||||
for (MatchModel m : matches.stream()
|
|
||||||
.filter(m -> extractCombIds(m).contains(card.getComb())).toList()) {
|
|
||||||
|
|
||||||
if (state.state() == 1) {
|
|
||||||
card.setCategory(m.getCategory().getId());
|
|
||||||
} else if (state.state() == 2) {
|
|
||||||
card.setCategory(m.getCategory().getId());
|
|
||||||
if (Objects.equals(m.getId(), state.selectedMatch()))
|
|
||||||
card.setMatch(m.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.chain(() -> Panache.withTransaction(() -> cardRepository.persist(cards))));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<List<Long>> rmTeamCard(CompetitionModel competition, String teamUuid, String teamName,
|
|
||||||
CardModel.CardType type) {
|
|
||||||
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3) AND type = ?4",
|
|
||||||
competition.getId(), teamUuid, teamName, type)
|
|
||||||
.firstResult()
|
|
||||||
.chain(card -> Uni.createFrom().item(card.getCardIds())
|
|
||||||
.call(() -> Panache.withTransaction(() -> cardRepository.delete("id IN ?1", card.getCardIds())))
|
|
||||||
.call(() -> Panache.withTransaction(() -> clubCardRepository.delete(card))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,6 @@ package fr.titionfire.ffsaf.domain.service;
|
|||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.*;
|
import fr.titionfire.ffsaf.data.model.*;
|
||||||
import fr.titionfire.ffsaf.data.repository.*;
|
import fr.titionfire.ffsaf.data.repository.*;
|
||||||
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
|
|
||||||
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
|
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
|
||||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
@ -46,9 +45,6 @@ public class ResultService {
|
|||||||
@Inject
|
@Inject
|
||||||
MatchRepository matchRepository;
|
MatchRepository matchRepository;
|
||||||
|
|
||||||
@Inject
|
|
||||||
CardRepository cardRepository;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TradService trad;
|
TradService trad;
|
||||||
|
|
||||||
@ -127,42 +123,39 @@ public class ResultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Uni<ResultCategoryData> getCategory(String uuid, long poule, SecurityCtx securityCtx) {
|
public Uni<ResultCategoryData> getCategory(String uuid, long poule, SecurityCtx securityCtx) {
|
||||||
return hasAccess(uuid, securityCtx).chain(membreModel -> getData(uuid, poule, membreModel));
|
return hasAccess(uuid, securityCtx).chain(membreModel ->
|
||||||
|
matchRepository.list("category.compet.uuid = ?1 AND category.id = ?2", uuid, poule)
|
||||||
|
.call(list -> list.isEmpty() ? Uni.createFrom().voidItem() :
|
||||||
|
Mutiny.fetch(list.get(0).getCategory().getTree()))
|
||||||
|
.map(list -> getData(list, membreModel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<ResultCategoryData> getCategory(String uuid, long poule) {
|
public Uni<ResultCategoryData> getCategory(String uuid, long poule) {
|
||||||
return getData(uuid, poule, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Uni<ResultCategoryData> getData(String uuid, long poule, MembreModel membreModel) {
|
|
||||||
List<CardModel> cards = new ArrayList<>();
|
|
||||||
|
|
||||||
return matchRepository.list("category.compet.uuid = ?1 AND category.id = ?2", uuid, poule)
|
return matchRepository.list("category.compet.uuid = ?1 AND category.id = ?2", uuid, poule)
|
||||||
.call(list -> list.isEmpty() ? Uni.createFrom().voidItem() :
|
.call(list -> list.isEmpty() ? Uni.createFrom().voidItem() :
|
||||||
Mutiny.fetch(list.get(0).getCategory().getTree()))
|
Mutiny.fetch(list.get(0).getCategory().getTree()))
|
||||||
.chain(list -> cardRepository.list("competition.uuid = ?1", uuid).invoke(cards::addAll)
|
.map(list -> getData(list, null));
|
||||||
.map(c -> list.stream().map(m -> new MatchModelExtend(m, c)).toList()))
|
}
|
||||||
.map(matchModels -> {
|
|
||||||
|
private ResultCategoryData getData(List<MatchModel> matchModels, MembreModel membreModel) {
|
||||||
ResultCategoryData out = new ResultCategoryData();
|
ResultCategoryData out = new ResultCategoryData();
|
||||||
|
|
||||||
CategoryModel categoryModel = matchModels.get(0).getCategory();
|
CategoryModel categoryModel = matchModels.get(0).getCategory();
|
||||||
out.setName(categoryModel.getName());
|
out.setName(categoryModel.getName());
|
||||||
out.setType(categoryModel.getType());
|
out.setType(categoryModel.getType());
|
||||||
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());
|
||||||
|
|
||||||
getArray2(matchModels, membreModel, out);
|
getArray2(matchModels, membreModel, out);
|
||||||
getTree(categoryModel.getTree(), membreModel, cards, out);
|
getTree(categoryModel.getTree(), membreModel, out);
|
||||||
return out;
|
return out;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getArray2(List<MatchModelExtend> matchModels_, MembreModel membreModel, ResultCategoryData out) {
|
private void getArray2(List<MatchModel> matchModels_, MembreModel membreModel, ResultCategoryData out) {
|
||||||
List<MatchModelExtend> matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList();
|
List<MatchModel> matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList();
|
||||||
|
|
||||||
HashMap<Character, List<MatchModelExtend>> matchMap = new HashMap<>();
|
HashMap<Character, List<MatchModel>> matchMap = new HashMap<>();
|
||||||
for (MatchModelExtend model : matchModels) {
|
for (MatchModel model : matchModels) {
|
||||||
char g = model.getPoule();
|
char g = model.getPoule();
|
||||||
if (!matchMap.containsKey(g))
|
if (!matchMap.containsKey(g))
|
||||||
matchMap.put(g, new ArrayList<>());
|
matchMap.put(g, new ArrayList<>());
|
||||||
@ -171,7 +164,7 @@ public class ResultService {
|
|||||||
|
|
||||||
matchMap.forEach((c, matchEntities) -> {
|
matchMap.forEach((c, matchEntities) -> {
|
||||||
List<ResultCategoryData.PouleArrayData> matchs = matchEntities.stream()
|
List<ResultCategoryData.PouleArrayData> matchs = matchEntities.stream()
|
||||||
.sorted(Comparator.comparing(MatchModelExtend::getCategory_ord))
|
.sorted(Comparator.comparing(MatchModel::getCategory_ord))
|
||||||
.map(o -> ResultCategoryData.PouleArrayData.fromModel(o, membreModel,
|
.map(o -> ResultCategoryData.PouleArrayData.fromModel(o, membreModel,
|
||||||
ResultPrivacy.REGISTERED_ONLY_NO_DETAILS))
|
ResultPrivacy.REGISTERED_ONLY_NO_DETAILS))
|
||||||
.toList();
|
.toList();
|
||||||
@ -211,28 +204,26 @@ public class ResultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
dst.setData(
|
dst.setData(ResultCategoryData.TreeData.from(src.getMatch(), membreModel, privacy));
|
||||||
ResultCategoryData.TreeData.from(new MatchModelExtend(src.getMatch(), cards), 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);
|
||||||
}
|
}
|
||||||
if (src.getRight() != null) {
|
if (src.getRight() != null) {
|
||||||
dst.setRight(new TreeNode<>());
|
dst.setRight(new TreeNode<>());
|
||||||
convertTree(src.getRight(), dst.getRight(), membreModel, privacy, cards);
|
convertTree(src.getRight(), dst.getRight(), membreModel, privacy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getTree(List<TreeModel> treeModels, MembreModel membreModel, List<CardModel> cards,
|
private void getTree(List<TreeModel> treeModels, MembreModel membreModel, ResultCategoryData out) {
|
||||||
ResultCategoryData out) {
|
|
||||||
ArrayList<TreeNode<ResultCategoryData.TreeData>> trees = new ArrayList<>();
|
ArrayList<TreeNode<ResultCategoryData.TreeData>> trees = new ArrayList<>();
|
||||||
treeModels.stream()
|
treeModels.stream()
|
||||||
.filter(t -> t.getLevel() != 0)
|
.filter(t -> t.getLevel() != 0)
|
||||||
.sorted(Comparator.comparing(TreeModel::getLevel))
|
.sorted(Comparator.comparing(TreeModel::getLevel))
|
||||||
.forEach(treeModel -> {
|
.forEach(treeModel -> {
|
||||||
TreeNode<ResultCategoryData.TreeData> root = new TreeNode<>();
|
TreeNode<ResultCategoryData.TreeData> root = new TreeNode<>();
|
||||||
convertTree(treeModel, root, membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS, cards);
|
convertTree(treeModel, root, membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS);
|
||||||
trees.add(root);
|
trees.add(root);
|
||||||
});
|
});
|
||||||
out.setTrees(trees);
|
out.setTrees(trees);
|
||||||
@ -250,13 +241,10 @@ public class ResultService {
|
|||||||
private Uni<CombsArrayData> getAllCombArray_(String uuid, MembreModel membreModel) {
|
private Uni<CombsArrayData> getAllCombArray_(String uuid, MembreModel membreModel) {
|
||||||
return registerRepository.list("competition.uuid = ?1", uuid)
|
return registerRepository.list("competition.uuid = ?1", uuid)
|
||||||
.chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid)
|
.chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid)
|
||||||
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
|
.map(matchModels -> new Pair<>(registers, matchModels)))
|
||||||
.map(cards -> new Pair<>(registers,
|
|
||||||
matchModels.stream().map(m -> new MatchModelExtend(m, cards)).toList()))))
|
|
||||||
|
|
||||||
.map(pair -> {
|
.map(pair -> {
|
||||||
List<RegisterModel> registers = pair.getKey();
|
List<RegisterModel> registers = pair.getKey();
|
||||||
List<MatchModelExtend> matchModels = pair.getValue();
|
List<MatchModel> matchModels = pair.getValue();
|
||||||
|
|
||||||
CombsArrayData.CombsArrayDataBuilder builder = CombsArrayData.builder();
|
CombsArrayData.CombsArrayDataBuilder builder = CombsArrayData.builder();
|
||||||
|
|
||||||
@ -302,10 +290,11 @@ public class ResultService {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
builder.nb_insc(combs.size());
|
builder.nb_insc(combs.size());
|
||||||
builder.tt_match((int) matchModels.stream().filter(MatchModelExtend::isEnd).count());
|
builder.tt_match((int) matchModels.stream().filter(MatchModel::isEnd).count());
|
||||||
builder.point(matchModels.stream()
|
builder.point(matchModels.stream()
|
||||||
.filter(MatchModelExtend::isEnd)
|
.filter(MatchModel::isEnd)
|
||||||
.flatMap(m -> m.getScoresToCompute().stream())
|
.flatMap(m -> m.getScores().stream())
|
||||||
|
.filter(s -> s.getS1() > -900 && s.getS2() > -900)
|
||||||
.mapToInt(s -> s.getS1() + s.getS2()).sum());
|
.mapToInt(s -> s.getS1() + s.getS2()).sum());
|
||||||
builder.combs(combs);
|
builder.combs(combs);
|
||||||
|
|
||||||
@ -356,7 +345,7 @@ public class ResultService {
|
|||||||
return Uni.createFrom().failure(new DForbiddenException(trad.t("comb.not.found")));
|
return Uni.createFrom().failure(new DForbiddenException(trad.t("comb.not.found")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Uni<List<MatchModelExtend>> uni;
|
Uni<List<MatchModel>> uni;
|
||||||
if (id >= 0) {
|
if (id >= 0) {
|
||||||
uni = registerRepository.find("membre.id = ?1 AND competition.uuid = ?2 AND membre.resultPrivacy <= ?3", id,
|
uni = registerRepository.find("membre.id = ?1 AND competition.uuid = ?2 AND membre.resultPrivacy <= ?3", id,
|
||||||
uuid, privacy).firstResult()
|
uuid, privacy).firstResult()
|
||||||
@ -373,10 +362,7 @@ public class ResultService {
|
|||||||
return matchRepository.list(
|
return matchRepository.list(
|
||||||
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.comb c1g LEFT JOIN m.c2_guest.comb c2g " +
|
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.comb c1g LEFT JOIN m.c2_guest.comb c2g " +
|
||||||
"WHERE m.category.compet.uuid = ?1 AND (m.c1_id = ?2 OR m.c2_id = ?2 OR c1g = ?2 OR c2g = ?2)",
|
"WHERE m.category.compet.uuid = ?1 AND (m.c1_id = ?2 OR m.c2_id = ?2 OR c1g = ?2 OR c2g = ?2)",
|
||||||
uuid, registerModel.getMembre())
|
uuid, registerModel.getMembre());
|
||||||
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
|
|
||||||
.map(cards -> matchModels.stream().map(m -> new MatchModelExtend(m, cards))
|
|
||||||
.toList()));
|
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
uni = competitionGuestRepository.find("id = ?1 AND competition.uuid = ?2", -id, uuid).firstResult()
|
uni = competitionGuestRepository.find("id = ?1 AND competition.uuid = ?2", -id, uuid).firstResult()
|
||||||
@ -389,15 +375,12 @@ public class ResultService {
|
|||||||
return matchRepository.list(
|
return matchRepository.list(
|
||||||
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.guest c1g LEFT JOIN m.c2_guest.guest c2g " +
|
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.guest c1g LEFT JOIN m.c2_guest.guest c2g " +
|
||||||
"WHERE m.category.compet.uuid = ?1 AND (m.c1_guest = ?2 OR m.c2_guest = ?2 OR c1g = ?2 OR c2g = ?2)",
|
"WHERE m.category.compet.uuid = ?1 AND (m.c1_guest = ?2 OR m.c2_guest = ?2 OR c1g = ?2 OR c2g = ?2)",
|
||||||
uuid, guestModel)
|
uuid, guestModel);
|
||||||
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
|
|
||||||
.map(cards -> matchModels.stream().map(m -> new MatchModelExtend(m, cards))
|
|
||||||
.toList()));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return uni.invoke(matchModels -> {
|
return uni.invoke(matchModels -> {
|
||||||
List<CategoryModel> pouleModels = matchModels.stream().map(MatchModelExtend::getCategory).distinct()
|
List<CategoryModel> pouleModels = matchModels.stream().map(MatchModel::getCategory).distinct()
|
||||||
.toList();
|
.toList();
|
||||||
List<CombArrayData.MatchsData> matchs = new ArrayList<>();
|
List<CombArrayData.MatchsData> matchs = new ArrayList<>();
|
||||||
|
|
||||||
@ -405,7 +388,7 @@ public class ResultService {
|
|||||||
AtomicInteger sumPointMake = new AtomicInteger(0);
|
AtomicInteger sumPointMake = new AtomicInteger(0);
|
||||||
AtomicInteger sumPointTake = new AtomicInteger(0);
|
AtomicInteger sumPointTake = new AtomicInteger(0);
|
||||||
|
|
||||||
for (MatchModelExtend matchModel : matchModels) {
|
for (MatchModel matchModel : matchModels) {
|
||||||
if ((matchModel.getC1_id() == null && matchModel.getC1_guest() == null) ||
|
if ((matchModel.getC1_id() == null && matchModel.getC1_guest() == null) ||
|
||||||
(matchModel.getC2_id() == null && matchModel.getC2_guest() == null))
|
(matchModel.getC2_id() == null && matchModel.getC2_guest() == null))
|
||||||
continue;
|
continue;
|
||||||
@ -422,33 +405,35 @@ public class ResultService {
|
|||||||
if (matchModel.isC1(id)) {
|
if (matchModel.isC1(id)) {
|
||||||
builder2.adv(matchModel.getC2Name());
|
builder2.adv(matchModel.getC2Name());
|
||||||
if (matchModel.isEnd()) {
|
if (matchModel.isEnd()) {
|
||||||
matchModel.getScoresToCompute()
|
matchModel.getScores().stream()
|
||||||
|
.filter(s -> s.getS1() > -900 && s.getS2() > -900)
|
||||||
.forEach(scoreEntity -> {
|
.forEach(scoreEntity -> {
|
||||||
pointMake.addAndGet(scoreEntity.getS1());
|
pointMake.addAndGet(scoreEntity.getS1());
|
||||||
pointTake.addAndGet(scoreEntity.getS2());
|
pointTake.addAndGet(scoreEntity.getS2());
|
||||||
});
|
});
|
||||||
builder2.score(matchModel.getScoresToPrint().stream()
|
builder2.score(matchModel.getScores().stream()
|
||||||
.map(s -> new Integer[]{s.getS1(), s.getS2()}).toList());
|
.map(s -> new Integer[]{s.getS1(), s.getS2()}).toList());
|
||||||
} else {
|
} else {
|
||||||
builder2.score(new ArrayList<>());
|
builder2.score(new ArrayList<>());
|
||||||
}
|
}
|
||||||
builder2.win(matchModel.isEnd() && matchModel.getWin() > 0);
|
builder2.win(matchModel.isEnd() && matchModel.win() > 0);
|
||||||
} else {
|
} else {
|
||||||
builder2.adv(matchModel.getC1Name());
|
builder2.adv(matchModel.getC1Name());
|
||||||
if (matchModel.isEnd()) {
|
if (matchModel.isEnd()) {
|
||||||
matchModel.getScoresToCompute()
|
matchModel.getScores().stream()
|
||||||
|
.filter(s -> s.getS1() > -900 && s.getS2() > -900)
|
||||||
.forEach(scoreEntity -> {
|
.forEach(scoreEntity -> {
|
||||||
pointMake.addAndGet(scoreEntity.getS2());
|
pointMake.addAndGet(scoreEntity.getS2());
|
||||||
pointTake.addAndGet(scoreEntity.getS1());
|
pointTake.addAndGet(scoreEntity.getS1());
|
||||||
});
|
});
|
||||||
builder2.score(matchModel.getScoresToPrint().stream()
|
builder2.score(matchModel.getScores().stream()
|
||||||
.map(s -> new Integer[]{s.getS2(), s.getS1()}).toList());
|
.map(s -> new Integer[]{s.getS2(), s.getS1()}).toList());
|
||||||
} else {
|
} else {
|
||||||
builder2.score(new ArrayList<>());
|
builder2.score(new ArrayList<>());
|
||||||
}
|
}
|
||||||
builder2.win(matchModel.isEnd() && matchModel.getWin() < 0);
|
builder2.win(matchModel.isEnd() && matchModel.win() < 0);
|
||||||
}
|
}
|
||||||
builder2.eq(matchModel.isEnd() && matchModel.getWin() == 0);
|
builder2.eq(matchModel.isEnd() && matchModel.win() == 0);
|
||||||
builder2.ratio(
|
builder2.ratio(
|
||||||
(pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get());
|
(pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get());
|
||||||
|
|
||||||
@ -540,13 +525,9 @@ public class ResultService {
|
|||||||
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.guest c1g LEFT JOIN m.c2_guest.guest c2g " +
|
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.guest c1g LEFT JOIN m.c2_guest.guest c2g " +
|
||||||
"WHERE m.category.compet.uuid = ?1 AND (m.c1_guest IN ?2 OR m.c2_guest IN ?2 OR c1g IN ?2 OR c2g IN ?2)",
|
"WHERE m.category.compet.uuid = ?1 AND (m.c1_guest IN ?2 OR m.c2_guest IN ?2 OR c1g IN ?2 OR c2g IN ?2)",
|
||||||
uuid, guests)
|
uuid, guests)
|
||||||
|
.map(matchModels ->
|
||||||
.chain(mm -> cardRepository.list("competition.uuid = ?1", uuid)
|
|
||||||
.map(cards ->
|
|
||||||
getClubArray2(clubName, guests.stream().map(o -> (CombModel) o).toList(),
|
getClubArray2(clubName, guests.stream().map(o -> (CombModel) o).toList(),
|
||||||
mm.stream().map(m -> new MatchModelExtend(m, cards)).toList(),
|
matchModels, new ArrayList<>(), membreModel)));
|
||||||
new ArrayList<>(), membreModel)
|
|
||||||
)));
|
|
||||||
} else {
|
} else {
|
||||||
return clubRepository.findById(id).chain(clubModel ->
|
return clubRepository.findById(id).chain(clubModel ->
|
||||||
registerRepository.list("competition.uuid = ?1 AND membre.club = ?2", uuid, clubModel)
|
registerRepository.list("competition.uuid = ?1 AND membre.club = ?2", uuid, clubModel)
|
||||||
@ -554,20 +535,14 @@ public class ResultService {
|
|||||||
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.comb c1g LEFT JOIN m.c2_guest.comb c2g " +
|
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.comb c1g LEFT JOIN m.c2_guest.comb c2g " +
|
||||||
"WHERE m.category.compet.uuid = ?1 AND (m.c1_id IN ?2 OR m.c2_id IN ?2 OR c1g IN ?2 OR c2g IN ?2)",
|
"WHERE m.category.compet.uuid = ?1 AND (m.c1_id IN ?2 OR m.c2_id IN ?2 OR c1g IN ?2 OR c2g IN ?2)",
|
||||||
uuid, registers.stream().map(RegisterModel::getMembre).toList())
|
uuid, registers.stream().map(RegisterModel::getMembre).toList())
|
||||||
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
|
.map(matchModels ->
|
||||||
.map(cards ->
|
|
||||||
getClubArray2(clubModel.getName(),
|
getClubArray2(clubModel.getName(),
|
||||||
registers.stream().map(o -> (CombModel) o.getMembre())
|
registers.stream().map(o -> (CombModel) o.getMembre()).toList(),
|
||||||
.toList(),
|
matchModels, registers, membreModel))));
|
||||||
matchModels.stream()
|
|
||||||
.map(m -> new MatchModelExtend(m, cards)).toList(),
|
|
||||||
registers, membreModel)
|
|
||||||
|
|
||||||
))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClubArrayData getClubArray2(String name, List<CombModel> combs, List<MatchModelExtend> matchModels,
|
private ClubArrayData getClubArray2(String name, List<CombModel> combs, List<MatchModel> matchModels,
|
||||||
List<RegisterModel> registers, MembreModel membreModel) {
|
List<RegisterModel> registers, MembreModel membreModel) {
|
||||||
ClubArrayData.ClubArrayDataBuilder builder = ClubArrayData.builder();
|
ClubArrayData.ClubArrayDataBuilder builder = ClubArrayData.builder();
|
||||||
builder.name(name);
|
builder.name(name);
|
||||||
@ -622,14 +597,14 @@ public class ResultService {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CombStat makeStat(List<MatchModelExtend> matchModels, CombModel comb) {
|
private static CombStat makeStat(List<MatchModel> matchModels, CombModel comb) {
|
||||||
CombStat stat = new CombStat();
|
CombStat stat = new CombStat();
|
||||||
matchModels.stream()
|
matchModels.stream()
|
||||||
.filter(m -> m.isEnd() && (m.isC1(comb) || m.isC2(comb)))
|
.filter(m -> m.isEnd() && (m.isC1(comb) || m.isC2(comb)))
|
||||||
.forEach(matchModel -> {
|
.forEach(matchModel -> {
|
||||||
stat.match_ids.add(matchModel.getId());
|
stat.match_ids.add(matchModel.getId());
|
||||||
|
|
||||||
int win = matchModel.getWin();
|
int win = matchModel.win();
|
||||||
if (win == 0) {
|
if (win == 0) {
|
||||||
stat.score += 1;
|
stat.score += 1;
|
||||||
} else if ((matchModel.isC1(comb) && win > 0) || matchModel.isC2(comb) && win < 0) {
|
} else if ((matchModel.isC1(comb) && win > 0) || matchModel.isC2(comb) && win < 0) {
|
||||||
@ -640,7 +615,8 @@ public class ResultService {
|
|||||||
stat.l++;
|
stat.l++;
|
||||||
}
|
}
|
||||||
|
|
||||||
matchModel.getScoresToCompute()
|
matchModel.getScores().stream()
|
||||||
|
.filter(s -> s.getS1() > -900 && s.getS2() > -900)
|
||||||
.forEach(score -> {
|
.forEach(score -> {
|
||||||
if (matchModel.isC1(comb)) {
|
if (matchModel.isC1(comb)) {
|
||||||
stat.pointMake += score.getS1();
|
stat.pointMake += score.getS1();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package fr.titionfire.ffsaf.rest.data;
|
package fr.titionfire.ffsaf.rest.data;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||||
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
|
|
||||||
import fr.titionfire.ffsaf.utils.ResultPrivacy;
|
import fr.titionfire.ffsaf.utils.ResultPrivacy;
|
||||||
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||||
import fr.titionfire.ffsaf.utils.TreeNode;
|
import fr.titionfire.ffsaf.utils.TreeNode;
|
||||||
@ -43,16 +43,16 @@ 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(MatchModel 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.win() > 0,
|
||||||
matchModel.isEnd() ?
|
matchModel.isEnd() ?
|
||||||
matchModel.getScoresToPrint().stream().map(s -> new Integer[]{s.getS1(), s.getS2()}).toList()
|
matchModel.getScores().stream().map(s -> new Integer[]{s.getS1(), s.getS2()}).toList()
|
||||||
: new ArrayList<>(),
|
: new ArrayList<>(),
|
||||||
matchModel.isEnd() && matchModel.getWin() < 0,
|
matchModel.isEnd() && matchModel.win() < 0,
|
||||||
matchModel.getC2Name(membreModel, privacy),
|
matchModel.getC2Name(membreModel, privacy),
|
||||||
matchModel.isEnd() && matchModel.getWin() == 0,
|
matchModel.isEnd() && matchModel.win() == 0,
|
||||||
matchModel.isEnd(),
|
matchModel.isEnd(),
|
||||||
matchModel.getDate());
|
matchModel.getDate());
|
||||||
}
|
}
|
||||||
@ -60,10 +60,10 @@ public class ResultCategoryData {
|
|||||||
|
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
public static record TreeData(long id, String c1FullName, String c2FullName, List<ScoreEmbeddable> scores,
|
public static record TreeData(long id, String c1FullName, String c2FullName, List<ScoreEmbeddable> scores,
|
||||||
boolean end, int win) {
|
boolean end) {
|
||||||
public static TreeData from(MatchModelExtend match, MembreModel membreModel, ResultPrivacy privacy) {
|
public static TreeData from(MatchModel match, MembreModel membreModel, ResultPrivacy privacy) {
|
||||||
return new TreeData(match.getId(), match.getC1Name(membreModel, privacy),
|
return new TreeData(match.getId(), match.getC1Name(membreModel, privacy),
|
||||||
match.getC2Name(membreModel, privacy), match.getScoresToPrint(), match.isEnd(), match.getWin());
|
match.getC2Name(membreModel, privacy), match.getScores(), match.isEnd());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,14 +46,11 @@ public class CompetitionWS {
|
|||||||
RRegister rRegister;
|
RRegister rRegister;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RCard rCard;
|
RCardboard rCardboard;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RTeam rTeam;
|
RTeam rTeam;
|
||||||
|
|
||||||
@Inject
|
|
||||||
RState rState;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SecurityCtx securityCtx;
|
SecurityCtx securityCtx;
|
||||||
|
|
||||||
@ -96,9 +93,8 @@ public class CompetitionWS {
|
|||||||
getWSReceiverMethods(RMatch.class, rMatch);
|
getWSReceiverMethods(RMatch.class, rMatch);
|
||||||
getWSReceiverMethods(RCategorie.class, rCategorie);
|
getWSReceiverMethods(RCategorie.class, rCategorie);
|
||||||
getWSReceiverMethods(RRegister.class, rRegister);
|
getWSReceiverMethods(RRegister.class, rRegister);
|
||||||
getWSReceiverMethods(RCard.class, rCard);
|
getWSReceiverMethods(RCardboard.class, rCardboard);
|
||||||
getWSReceiverMethods(RTeam.class, rTeam);
|
getWSReceiverMethods(RTeam.class, rTeam);
|
||||||
getWSReceiverMethods(RState.class, rState);
|
|
||||||
|
|
||||||
executor = notifyExecutor;
|
executor = notifyExecutor;
|
||||||
}
|
}
|
||||||
@ -145,7 +141,6 @@ public class CompetitionWS {
|
|||||||
LOGGER.debugf("Active connections: %d", connection.getOpenConnections().size());
|
LOGGER.debugf("Active connections: %d", connection.getOpenConnections().size());
|
||||||
|
|
||||||
waitingResponse.remove(connection);
|
waitingResponse.remove(connection);
|
||||||
rState.removeConnection(connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageOut makeReply(MessageIn message, Object data) {
|
private MessageOut makeReply(MessageIn message, Object data) {
|
||||||
@ -235,30 +230,6 @@ public class CompetitionWS {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendNotifyState(WebSocketConnection connection, String code, Object data) {
|
|
||||||
String uuid = connection.pathParam("uuid");
|
|
||||||
|
|
||||||
List<Uni<Void>> queue = new ArrayList<>();
|
|
||||||
queue.add(Uni.createFrom().voidItem()); // For avoid empty queue
|
|
||||||
|
|
||||||
connection.getOpenConnections().forEach(c -> {
|
|
||||||
Boolean s = c.userData().get(UserData.TypedKey.forBoolean("needState"));
|
|
||||||
if (uuid.equals(c.pathParam("uuid")) && s != null && s) {
|
|
||||||
queue.add(c.sendText(new MessageOut(UUID.randomUUID(), code, MessageType.NOTIFY, data)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Uni.join().all(queue)
|
|
||||||
.andCollectFailures()
|
|
||||||
.runSubscriptionOn(executor)
|
|
||||||
.subscribeAsCompletionStage()
|
|
||||||
.whenComplete((v, t) -> {
|
|
||||||
if (t != null) {
|
|
||||||
LOGGER.error("Error sending ws_out message", t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnError
|
@OnError
|
||||||
Uni<Void> error(WebSocketConnection connection, ForbiddenException t) {
|
Uni<Void> error(WebSocketConnection connection, ForbiddenException t) {
|
||||||
return connection.close(CloseReason.INTERNAL_SERVER_ERROR);
|
return connection.close(CloseReason.INTERNAL_SERVER_ERROR);
|
||||||
|
|||||||
@ -1,158 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.ws.recv;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.CardModel;
|
|
||||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
|
||||||
import fr.titionfire.ffsaf.data.repository.CardRepository;
|
|
||||||
import fr.titionfire.ffsaf.data.repository.ClubCardRepository;
|
|
||||||
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
|
|
||||||
import fr.titionfire.ffsaf.data.repository.MatchRepository;
|
|
||||||
import fr.titionfire.ffsaf.domain.service.CardService;
|
|
||||||
import fr.titionfire.ffsaf.domain.service.TradService;
|
|
||||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
|
||||||
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
|
||||||
import fr.titionfire.ffsaf.ws.PermLevel;
|
|
||||||
import fr.titionfire.ffsaf.ws.send.SSCard;
|
|
||||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
|
||||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
|
||||||
import io.quarkus.websockets.next.WebSocketConnection;
|
|
||||||
import io.smallrye.mutiny.Uni;
|
|
||||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@WithSession
|
|
||||||
@ApplicationScoped
|
|
||||||
@RegisterForReflection
|
|
||||||
public class RCard {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MatchRepository matchRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ClubCardRepository clubCardRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CardRepository cardRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CardService cardService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CompetitionRepository competitionRepository;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
TradService trad;
|
|
||||||
|
|
||||||
private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
|
|
||||||
return matchRepository.findById(id)
|
|
||||||
.invoke(Unchecked.consumer(o -> {
|
|
||||||
if (o == null)
|
|
||||||
throw new DNotFoundException(trad.t("matche.non.trouver"));
|
|
||||||
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
|
|
||||||
throw new DForbiddenException(trad.t("permission.denied"));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "getCardForMatch", permission = PermLevel.VIEW)
|
|
||||||
public Uni<List<CardModel>> getCardForMatch(WebSocketConnection connection, Long matchId) {
|
|
||||||
if (matchId == null)
|
|
||||||
return Uni.createFrom().nullItem();
|
|
||||||
return getById(matchId, connection).chain(matchModel -> cardService.getForMatch(matchModel));
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "getAllForTeamNoDetail", permission = PermLevel.VIEW)
|
|
||||||
public Uni<List<SendTeamCards>> getAllForTeamNoDetail(WebSocketConnection connection, Object o) {
|
|
||||||
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
|
|
||||||
.chain(c -> clubCardRepository.list("competition = ?1", c.getId()))
|
|
||||||
.map(cards -> cards.stream()
|
|
||||||
.map(card -> new SendTeamCards(card.getTeamUuid(), card.getTeamName(), List.of(),
|
|
||||||
card.getType(), card.getReason(), card.getDate()))
|
|
||||||
.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendCardAdd", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendCardAdd(WebSocketConnection connection, SendCardAdd card) {
|
|
||||||
return getById(card.matchId(), connection)
|
|
||||||
.chain(matchModel -> cardService.checkCanBeAdded(card, matchModel)
|
|
||||||
.chain(c -> {
|
|
||||||
CardModel model = new CardModel();
|
|
||||||
model.setComb(card.combId());
|
|
||||||
model.setMatch(card.matchId());
|
|
||||||
model.setCategory(matchModel.getCategory().getId());
|
|
||||||
model.setCompetition(matchModel.getCategory().getCompet());
|
|
||||||
model.setCompetitionId(matchModel.getCategory().getCompet().getId());
|
|
||||||
model.setType(card.type());
|
|
||||||
model.setReason(card.reason());
|
|
||||||
|
|
||||||
return Panache.withTransaction(() -> cardRepository.persist(model));
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.invoke(cardModel -> SSCard.sendCards(connection, List.of(cardModel)))
|
|
||||||
.replaceWithVoid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendCardRm", permission = PermLevel.ADMIN)
|
|
||||||
public Uni<Void> sendCardRm(WebSocketConnection connection, SendCardAdd card) {
|
|
||||||
return getById(card.matchId(), connection)
|
|
||||||
.chain(matchModel -> cardRepository.find("match = ?1 AND comb = ?2 AND type = ?3",
|
|
||||||
matchModel.getId(), card.combId(), card.type())
|
|
||||||
.firstResult()
|
|
||||||
.invoke(Unchecked.consumer(o -> {
|
|
||||||
if (o == null)
|
|
||||||
throw new DNotFoundException(trad.t("carton.non.trouver"));
|
|
||||||
SSCard.sendRmCards(connection, List.of(o.getId()));
|
|
||||||
}))
|
|
||||||
.chain(cardModel -> Panache.withTransaction(() -> cardRepository.delete(cardModel)))
|
|
||||||
)
|
|
||||||
.replaceWithVoid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "applyTeamCards", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> applyTeamCards(WebSocketConnection connection, SendTeamCards teamCards) {
|
|
||||||
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
|
|
||||||
.chain(c -> cardService.addTeamCard(c, teamCards.teamUuid(), teamCards.teamName(), teamCards.type,
|
|
||||||
teamCards.reason()))
|
|
||||||
.invoke(cards -> SSCard.sendTeamCard(connection,
|
|
||||||
new SendTeamCards(teamCards.teamUuid(), teamCards.teamName(), cards, teamCards.type(),
|
|
||||||
teamCards.reason(), new Date())))
|
|
||||||
.replaceWithVoid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendTeamCardReturnState", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendTeamCardReturnState(WebSocketConnection connection, SendTeamCardReturnState state) {
|
|
||||||
if (state.state <= 0 || state.state > 2)
|
|
||||||
return Uni.createFrom().voidItem();
|
|
||||||
|
|
||||||
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
|
|
||||||
.chain(c -> cardService.recvReturnState(c, state))
|
|
||||||
.invoke(cards -> SSCard.sendCards(connection, cards))
|
|
||||||
.replaceWithVoid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "removeTeamCards", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> removeTeamCards(WebSocketConnection connection, SendTeamCards teamCards) {
|
|
||||||
return competitionRepository.find("uuid", connection.pathParam("uuid")).firstResult()
|
|
||||||
.chain(c -> cardService.rmTeamCard(c, teamCards.teamUuid(), teamCards.teamName(), teamCards.type))
|
|
||||||
.invoke(cards -> SSCard.sendRmCards(connection, cards))
|
|
||||||
.invoke(__ -> SSCard.rmTeamCard(connection, teamCards))
|
|
||||||
.replaceWithVoid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
|
||||||
public record SendCardAdd(long matchId, long combId, CardModel.CardType type, String reason) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
|
||||||
public record SendTeamCards(String teamUuid, String teamName, List<CardModel> cards, CardModel.CardType type,
|
|
||||||
String reason, Date date) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
|
||||||
public record SendTeamCardReturnState(String teamUuid, String teamName, CardModel.CardType type,
|
|
||||||
int state, Long selectedCategory, Long selectedMatch) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
129
src/main/java/fr/titionfire/ffsaf/ws/recv/RCardboard.java
Normal file
129
src/main/java/fr/titionfire/ffsaf/ws/recv/RCardboard.java
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package fr.titionfire.ffsaf.ws.recv;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.data.model.CardboardModel;
|
||||||
|
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.CardboardRepository;
|
||||||
|
import fr.titionfire.ffsaf.data.repository.MatchRepository;
|
||||||
|
import fr.titionfire.ffsaf.domain.entity.CardboardEntity;
|
||||||
|
import fr.titionfire.ffsaf.domain.service.TradService;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||||
|
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||||
|
import fr.titionfire.ffsaf.ws.PermLevel;
|
||||||
|
import fr.titionfire.ffsaf.ws.send.SSCardboard;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||||
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
import io.quarkus.websockets.next.WebSocketConnection;
|
||||||
|
import io.smallrye.mutiny.Uni;
|
||||||
|
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@WithSession
|
||||||
|
@ApplicationScoped
|
||||||
|
@RegisterForReflection
|
||||||
|
public class RCardboard {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MatchRepository matchRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CardboardRepository cardboardRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
TradService trad;
|
||||||
|
|
||||||
|
private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
|
||||||
|
return matchRepository.findById(id)
|
||||||
|
.invoke(Unchecked.consumer(o -> {
|
||||||
|
if (o == null)
|
||||||
|
throw new DNotFoundException(trad.t("matche.non.trouver"));
|
||||||
|
if (!o.getCategory().getCompet().getUuid().equals(connection.pathParam("uuid")))
|
||||||
|
throw new DForbiddenException(trad.t("permission.denied"));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@WSReceiver(code = "sendCardboardChange", permission = PermLevel.TABLE)
|
||||||
|
public Uni<Void> sendCardboardChange(WebSocketConnection connection, SendCardboard card) {
|
||||||
|
return getById(card.matchId, connection)
|
||||||
|
.chain(matchModel -> cardboardRepository.find("(comb.id = ?1 OR guestComb.id = ?2) AND match.id = ?3",
|
||||||
|
card.combId, card.combId * -1, card.matchId).firstResult()
|
||||||
|
.chain(model -> {
|
||||||
|
if (model != null) {
|
||||||
|
model.setRed(model.getRed() + card.red);
|
||||||
|
model.setYellow(model.getYellow() + card.yellow);
|
||||||
|
return Panache.withTransaction(() -> cardboardRepository.persist(model));
|
||||||
|
}
|
||||||
|
CardboardModel cardboardModel = new CardboardModel();
|
||||||
|
|
||||||
|
cardboardModel.setCompet(matchModel.getCategory().getCompet());
|
||||||
|
cardboardModel.setMatch(matchModel);
|
||||||
|
cardboardModel.setRed(card.red);
|
||||||
|
cardboardModel.setYellow(card.yellow);
|
||||||
|
cardboardModel.setComb(null);
|
||||||
|
cardboardModel.setGuestComb(null);
|
||||||
|
|
||||||
|
if (card.combId >= 0) {
|
||||||
|
if (matchModel.getC1_id() != null && matchModel.getC1_id().getId() == card.combId)
|
||||||
|
cardboardModel.setComb(matchModel.getC1_id());
|
||||||
|
if (matchModel.getC2_id() != null && matchModel.getC2_id().getId() == card.combId)
|
||||||
|
cardboardModel.setComb(matchModel.getC2_id());
|
||||||
|
} else {
|
||||||
|
if (matchModel.getC1_guest() != null && matchModel.getC1_guest()
|
||||||
|
.getId() == card.combId * -1)
|
||||||
|
cardboardModel.setGuestComb(matchModel.getC1_guest());
|
||||||
|
if (matchModel.getC2_guest() != null && matchModel.getC2_guest()
|
||||||
|
.getId() == card.combId * -1)
|
||||||
|
cardboardModel.setGuestComb(matchModel.getC2_guest());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardboardModel.getComb() == null && cardboardModel.getGuestComb() == null)
|
||||||
|
return Uni.createFrom().nullItem();
|
||||||
|
return Panache.withTransaction(() -> cardboardRepository.persist(cardboardModel));
|
||||||
|
}))
|
||||||
|
.invoke(model -> SSCardboard.sendCardboard(connection, CardboardEntity.fromModel(model)))
|
||||||
|
.replaceWithVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WSReceiver(code = "getCardboardWithoutThis", permission = PermLevel.VIEW)
|
||||||
|
public Uni<CardboardAllMatch> getCardboardWithoutThis(WebSocketConnection connection, Long matchId) {
|
||||||
|
return getById(matchId, connection)
|
||||||
|
.chain(matchModel -> cardboardRepository.list("compet = ?1 AND match != ?2",
|
||||||
|
matchModel.getCategory().getCompet(), matchModel)
|
||||||
|
.map(models -> {
|
||||||
|
CardboardAllMatch out = new CardboardAllMatch();
|
||||||
|
|
||||||
|
for (CardboardModel c : models) {
|
||||||
|
if ((matchModel.getC1_id() != null && Objects.equals(c.getComb(),
|
||||||
|
matchModel.getC1_id())) || (matchModel.getC1_guest() != null && Objects.equals(
|
||||||
|
c.getGuestComb(), matchModel.getC1_guest()))) {
|
||||||
|
out.c1_yellow += c.getYellow();
|
||||||
|
out.c1_red += c.getRed();
|
||||||
|
}
|
||||||
|
if ((matchModel.getC2_id() != null && Objects.equals(c.getComb(),
|
||||||
|
matchModel.getC2_id())) || (matchModel.getC2_guest() != null && Objects.equals(
|
||||||
|
c.getGuestComb(), matchModel.getC2_guest()))) {
|
||||||
|
out.c2_yellow += c.getYellow();
|
||||||
|
out.c2_red += c.getRed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegisterForReflection
|
||||||
|
public record SendCardboard(long matchId, long combId, int yellow, int red) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@RegisterForReflection
|
||||||
|
public static class CardboardAllMatch {
|
||||||
|
int c1_yellow = 0;
|
||||||
|
int c1_red = 0;
|
||||||
|
int c2_yellow = 0;
|
||||||
|
int c2_red = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,11 @@
|
|||||||
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.CategoryModel;
|
import fr.titionfire.ffsaf.data.model.CategoryModel;
|
||||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||||
import fr.titionfire.ffsaf.data.model.TreeModel;
|
import fr.titionfire.ffsaf.data.model.TreeModel;
|
||||||
import fr.titionfire.ffsaf.data.repository.*;
|
import fr.titionfire.ffsaf.data.repository.*;
|
||||||
import fr.titionfire.ffsaf.domain.entity.MatchEntity;
|
import fr.titionfire.ffsaf.domain.entity.MatchEntity;
|
||||||
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.TradService;
|
import fr.titionfire.ffsaf.domain.service.TradService;
|
||||||
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;
|
||||||
@ -47,7 +45,7 @@ public class RCategorie {
|
|||||||
TreeRepository treeRepository;
|
TreeRepository treeRepository;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CardService cardService;
|
CardboardRepository cardboardRepository;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TradService trad;
|
TradService trad;
|
||||||
@ -86,8 +84,6 @@ public class RCategorie {
|
|||||||
.call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId())
|
.call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId())
|
||||||
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
|
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
|
||||||
.invoke(fullCategory::setTrees))
|
.invoke(fullCategory::setTrees))
|
||||||
.call(cat -> cardService.getAll(cat.getCompet())
|
|
||||||
.invoke(fullCategory::setCards))
|
|
||||||
.map(__ -> fullCategory);
|
.map(__ -> fullCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +214,7 @@ public class RCategorie {
|
|||||||
public Uni<Void> deleteCategory(WebSocketConnection connection, Long id) {
|
public Uni<Void> deleteCategory(WebSocketConnection connection, Long id) {
|
||||||
return getById(id, connection)
|
return getById(id, connection)
|
||||||
.call(cat -> Panache.withTransaction(() -> treeRepository.delete("category = ?1", cat.getId())
|
.call(cat -> Panache.withTransaction(() -> treeRepository.delete("category = ?1", cat.getId())
|
||||||
|
.call(__ -> cardboardRepository.delete("match.category = ?1", cat))
|
||||||
.call(__ -> matchRepository.delete("category = ?1", cat))))
|
.call(__ -> matchRepository.delete("category = ?1", cat))))
|
||||||
.chain(cat -> Panache.withTransaction(() -> categoryRepository.delete(cat)))
|
.chain(cat -> Panache.withTransaction(() -> categoryRepository.delete(cat)))
|
||||||
.invoke(__ -> SSCategorie.sendDelCategory(connection, id))
|
.invoke(__ -> SSCategorie.sendDelCategory(connection, id))
|
||||||
@ -244,6 +241,5 @@ public class RCategorie {
|
|||||||
String liceName;
|
String liceName;
|
||||||
List<TreeEntity> trees = null;
|
List<TreeEntity> trees = null;
|
||||||
List<MatchEntity> matches;
|
List<MatchEntity> matches;
|
||||||
List<CardModel> cards;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,10 +46,10 @@ public class RMatch {
|
|||||||
CompetitionGuestRepository competitionGuestRepository;
|
CompetitionGuestRepository competitionGuestRepository;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TradService trad;
|
CardboardRepository cardboardRepository;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RState rState;
|
TradService trad;
|
||||||
|
|
||||||
private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
|
private Uni<MatchModel> getById(long id, WebSocketConnection connection) {
|
||||||
return matchRepository.findById(id)
|
return matchRepository.findById(id)
|
||||||
@ -195,7 +195,6 @@ public class RMatch {
|
|||||||
return Panache.withTransaction(() -> matchRepository.persist(mm));
|
return Panache.withTransaction(() -> matchRepository.persist(mm));
|
||||||
})
|
})
|
||||||
.invoke(mm -> toSend.add(MatchEntity.fromModel(mm)))
|
.invoke(mm -> toSend.add(MatchEntity.fromModel(mm)))
|
||||||
.invoke(mm -> rState.setMatchEnd(connection, matchEnd))
|
|
||||||
.chain(mm -> updateEndAndTree(mm, toSend))
|
.chain(mm -> updateEndAndTree(mm, toSend))
|
||||||
.invoke(__ -> SSMatch.sendMatch(connection, toSend))
|
.invoke(__ -> SSMatch.sendMatch(connection, toSend))
|
||||||
.replaceWithVoid();
|
.replaceWithVoid();
|
||||||
@ -286,7 +285,9 @@ public class RMatch {
|
|||||||
public Uni<Void> deleteMatch(WebSocketConnection connection, Long idMatch) {
|
public Uni<Void> deleteMatch(WebSocketConnection connection, Long idMatch) {
|
||||||
return getById(idMatch, connection)
|
return getById(idMatch, connection)
|
||||||
.map(__ -> idMatch)
|
.map(__ -> idMatch)
|
||||||
.chain(l -> Panache.withTransaction(() -> matchRepository.delete("id = ?1", l)))
|
.chain(l -> Panache.withTransaction(() ->
|
||||||
|
cardboardRepository.delete("match.id = ?1", l)
|
||||||
|
.chain(__ -> matchRepository.delete("id = ?1", l))))
|
||||||
.invoke(__ -> SSMatch.sendDeleteMatch(connection, idMatch))
|
.invoke(__ -> SSMatch.sendDeleteMatch(connection, idMatch))
|
||||||
.replaceWithVoid();
|
.replaceWithVoid();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,149 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.ws.recv;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.ws.PermLevel;
|
|
||||||
import fr.titionfire.ffsaf.ws.send.SSState;
|
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
|
||||||
import io.quarkus.websockets.next.UserData;
|
|
||||||
import io.quarkus.websockets.next.WebSocketConnection;
|
|
||||||
import io.smallrye.mutiny.Uni;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@ApplicationScoped
|
|
||||||
@RegisterForReflection
|
|
||||||
public class RState {
|
|
||||||
|
|
||||||
private static final HashMap<WebSocketConnection, TableState> tableStates = new HashMap<>();
|
|
||||||
|
|
||||||
@WSReceiver(code = "subscribeToState", permission = PermLevel.VIEW)
|
|
||||||
public Uni<List<TableState>> sendCurrentScore(WebSocketConnection connection, Boolean subscribe) {
|
|
||||||
connection.userData().put(UserData.TypedKey.forBoolean("needState"), subscribe);
|
|
||||||
|
|
||||||
if (subscribe) {
|
|
||||||
String uuid = connection.pathParam("uuid");
|
|
||||||
return Uni.createFrom().item(() ->
|
|
||||||
tableStates.values().stream().filter(s -> s.getCompetitionUuid().equals(uuid)).toList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Uni.createFrom().nullItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendState", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendState(WebSocketConnection connection, TableState tableState) {
|
|
||||||
tableState.setCompetitionUuid(connection.pathParam("uuid"));
|
|
||||||
|
|
||||||
if (tableStates.containsKey(connection))
|
|
||||||
tableState.setId(tableStates.get(connection).getId());
|
|
||||||
if (tableState.getChronoState().isRunning() && tableState.getChronoState().state == 0)
|
|
||||||
tableState.setState(MatchState.IN_PROGRESS);
|
|
||||||
tableStates.put(connection, tableState);
|
|
||||||
SSState.sendStateFull(connection, tableState);
|
|
||||||
return Uni.createFrom().voidItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendSelectCategory", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendSelectCategory(WebSocketConnection connection, Long catId) {
|
|
||||||
TableState tableState = tableStates.get(connection);
|
|
||||||
if (tableState != null) {
|
|
||||||
tableState.setSelectedCategory(catId);
|
|
||||||
tableState.setState(MatchState.NOT_STARTED);
|
|
||||||
SSState.sendStateFull(connection, tableState);
|
|
||||||
}
|
|
||||||
return Uni.createFrom().voidItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendSelectMatch", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendSelectMatch(WebSocketConnection connection, Long matchId) {
|
|
||||||
TableState tableState = tableStates.get(connection);
|
|
||||||
if (tableState != null) {
|
|
||||||
tableState.setSelectedMatch(matchId);
|
|
||||||
tableState.setState(MatchState.NOT_STARTED);
|
|
||||||
SSState.sendStateFull(connection, tableState);
|
|
||||||
}
|
|
||||||
return Uni.createFrom().voidItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendCurentChrono", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendCurentChrono(WebSocketConnection connection, ChronoState chronoState) {
|
|
||||||
TableState tableState = tableStates.get(connection);
|
|
||||||
if (tableState != null) {
|
|
||||||
tableState.setChronoState(chronoState);
|
|
||||||
if (chronoState.isRunning())
|
|
||||||
tableState.setState(MatchState.IN_PROGRESS);
|
|
||||||
SSState.sendStateFull(connection, tableState);
|
|
||||||
}
|
|
||||||
return Uni.createFrom().voidItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendLicenceName", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendCurrentScore(WebSocketConnection connection, String name) {
|
|
||||||
TableState tableState = tableStates.get(connection);
|
|
||||||
if (tableState != null) {
|
|
||||||
tableState.setLiceName(name);
|
|
||||||
SSState.sendStateFull(connection, tableState);
|
|
||||||
}
|
|
||||||
return Uni.createFrom().voidItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WSReceiver(code = "sendCurrentScore", permission = PermLevel.TABLE)
|
|
||||||
public Uni<Void> sendCurrentScore(WebSocketConnection connection, ScoreState scoreState) {
|
|
||||||
TableState tableState = tableStates.get(connection);
|
|
||||||
if (tableState != null) {
|
|
||||||
tableState.setScoreState(scoreState);
|
|
||||||
SSState.sendStateFull(connection, tableState);
|
|
||||||
}
|
|
||||||
return Uni.createFrom().voidItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeConnection(WebSocketConnection connection) {
|
|
||||||
if (tableStates.containsKey(connection)) {
|
|
||||||
SSState.sendRmStateFull(connection, tableStates.get(connection).getId());
|
|
||||||
tableStates.remove(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMatchEnd(WebSocketConnection connection, RMatch.MatchEnd matchEnd) {
|
|
||||||
if (tableStates.containsKey(connection)) {
|
|
||||||
TableState tableState = tableStates.get(connection);
|
|
||||||
if (matchEnd.end())
|
|
||||||
tableState.setState(MatchState.ENDED);
|
|
||||||
else
|
|
||||||
tableState.setState(MatchState.IN_PROGRESS);
|
|
||||||
SSState.sendStateFull(connection, tableState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
|
||||||
public record ChronoState(long time, long startTime, long configTime, long configPause, int state) {
|
|
||||||
public boolean isRunning() {
|
|
||||||
return startTime != 0 || state != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
|
||||||
public record ScoreState(int scoreRouge, int scoreBleu) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@RegisterForReflection
|
|
||||||
public static class TableState {
|
|
||||||
UUID id = UUID.randomUUID();
|
|
||||||
String competitionUuid;
|
|
||||||
Long selectedCategory;
|
|
||||||
Long selectedMatch;
|
|
||||||
ChronoState chronoState;
|
|
||||||
ScoreState scoreState;
|
|
||||||
String liceName = "???";
|
|
||||||
MatchState state = MatchState.NOT_STARTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum MatchState {
|
|
||||||
NOT_STARTED,
|
|
||||||
IN_PROGRESS,
|
|
||||||
ENDED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.ws.send;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.data.model.CardModel;
|
|
||||||
import fr.titionfire.ffsaf.ws.CompetitionWS;
|
|
||||||
import fr.titionfire.ffsaf.ws.recv.RCard;
|
|
||||||
import io.quarkus.websockets.next.WebSocketConnection;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SSCard {
|
|
||||||
|
|
||||||
public static void sendCards(WebSocketConnection connection, List<CardModel> cardModel) {
|
|
||||||
CompetitionWS.sendNotifyToOtherEditor(connection, "sendCards", cardModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sendRmCards(WebSocketConnection connection, List<Long> ids) {
|
|
||||||
CompetitionWS.sendNotifyToOtherEditor(connection, "rmCards", ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sendTeamCard(WebSocketConnection connection, RCard.SendTeamCards teamCards) {
|
|
||||||
CompetitionWS.sendNotifyToOtherEditor(connection, "sendTeamCard", teamCards);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void rmTeamCard(WebSocketConnection connection, RCard.SendTeamCards teamCards) {
|
|
||||||
CompetitionWS.sendNotifyToOtherEditor(connection, "rmTeamCard", teamCards);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12
src/main/java/fr/titionfire/ffsaf/ws/send/SSCardboard.java
Normal file
12
src/main/java/fr/titionfire/ffsaf/ws/send/SSCardboard.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package fr.titionfire.ffsaf.ws.send;
|
||||||
|
|
||||||
|
import fr.titionfire.ffsaf.domain.entity.CardboardEntity;
|
||||||
|
import fr.titionfire.ffsaf.ws.CompetitionWS;
|
||||||
|
import io.quarkus.websockets.next.WebSocketConnection;
|
||||||
|
|
||||||
|
public class SSCardboard {
|
||||||
|
|
||||||
|
public static void sendCardboard(WebSocketConnection connection, CardboardEntity cardboardEntity) {
|
||||||
|
CompetitionWS.sendNotifyToOtherEditor(connection, "sendCardboard", cardboardEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package fr.titionfire.ffsaf.ws.send;
|
|
||||||
|
|
||||||
import fr.titionfire.ffsaf.ws.CompetitionWS;
|
|
||||||
import fr.titionfire.ffsaf.ws.recv.RState;
|
|
||||||
import io.quarkus.websockets.next.WebSocketConnection;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class SSState {
|
|
||||||
|
|
||||||
public static void sendStateFull(WebSocketConnection connection, RState.TableState state) {
|
|
||||||
CompetitionWS.sendNotifyState(connection, "sendStateFull", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sendRmStateFull(WebSocketConnection connection, UUID id) {
|
|
||||||
CompetitionWS.sendNotifyState(connection, "rmStateFull", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -85,5 +85,3 @@ licence.membre.n.1.inconnue=License member no. 1 unknown
|
|||||||
licence.membre.n.2.inconnue=License member no. 2 unknown
|
licence.membre.n.2.inconnue=License member no. 2 unknown
|
||||||
licence.membre.n.3.inconnue=License member no. 3 unknown
|
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
|
|
||||||
card.cannot.be.added=Unable to add the card
|
|
||||||
|
|||||||
@ -81,5 +81,3 @@ licence.membre.n.1.inconnue=Licence du membre n
|
|||||||
licence.membre.n.2.inconnue=Licence du membre n°2 inconnue
|
licence.membre.n.2.inconnue=Licence du membre n°2 inconnue
|
||||||
licence.membre.n.3.inconnue=Licence du membre n°3 inconnue
|
licence.membre.n.3.inconnue=Licence du membre n°3 inconnue
|
||||||
demande.d.affiliation.non.trouve=Demande d'affiliation introuvable
|
demande.d.affiliation.non.trouve=Demande d'affiliation introuvable
|
||||||
carton.non.trouver=Carton introuvable
|
|
||||||
card.cannot.be.added=Impossible d'ajouter le carton
|
|
||||||
@ -90,7 +90,8 @@ function stopLoading(loading) {
|
|||||||
loading['root'].removeChild(loading['element']);
|
loading['root'].removeChild(loading['element']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function scorePrint(s1) {
|
function scoreToString(score) {
|
||||||
|
const scorePrint = (s1) => {
|
||||||
switch (s1) {
|
switch (s1) {
|
||||||
case -997:
|
case -997:
|
||||||
return i18next.t('disc.');
|
return i18next.t('disc.');
|
||||||
@ -104,7 +105,7 @@ function scorePrint(s1) {
|
|||||||
return String(s1);
|
return String(s1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function scoreToString(score) {
|
|
||||||
return score.map(o => scorePrint(o.at(0)) + "-" + scorePrint(o.at(1))).join(" | ");
|
return score.map(o => scorePrint(o.at(0)) + "-" + scorePrint(o.at(1))).join(" | ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,7 +867,7 @@ function drawGraph(root = []) {
|
|||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
|
|
||||||
for (let i = 0; i < scores.length; i++) {
|
for (let i = 0; i < scores.length; i++) {
|
||||||
const score = scorePrint(scores[i].s1) + "-" + scorePrint(scores[i].s2);
|
const score = scores[i].s1 + "-" + scores[i].s2;
|
||||||
const div = (scores.length <= 2) ? 2 : (scores.length >= 4) ? 4 : 3;
|
const div = (scores.length <= 2) ? 2 : (scores.length >= 4) ? 4 : 3;
|
||||||
const text = ctx.measureText(score);
|
const text = ctx.measureText(score);
|
||||||
let dx = (size * 2 - text.width) / 2;
|
let dx = (size * 2 - text.width) / 2;
|
||||||
@ -944,6 +945,20 @@ function drawGraph(root = []) {
|
|||||||
if (tree.right != null) drawNode(tree.right, px - size * 2 - size * 8, py + size * 2 * death);
|
if (tree.right != null) drawNode(tree.right, px - size * 2 - size * 8, py + size * 2 * death);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function win(scores) {
|
||||||
|
let sum = 0;
|
||||||
|
for (const score of scores) {
|
||||||
|
if (score.s1 === -1000 || score.s2 === -1000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (score.s1 > score.s2)
|
||||||
|
sum++;
|
||||||
|
else if (score.s1 < score.s2)
|
||||||
|
sum--;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
let px = max_x;
|
let px = max_x;
|
||||||
let py;
|
let py;
|
||||||
let max_y
|
let max_y
|
||||||
@ -956,7 +971,7 @@ function drawGraph(root = []) {
|
|||||||
for (const node of root) {
|
for (const node of root) {
|
||||||
let win_name = "";
|
let win_name = "";
|
||||||
if (node.data.end) {
|
if (node.data.end) {
|
||||||
if (node.data.win > 0)
|
if (win(node.data.scores) > 0)
|
||||||
win_name = (node.data.c1FullName === null) ? "???" : node.data.c1FullName;
|
win_name = (node.data.c1FullName === null) ? "???" : node.data.c1FullName;
|
||||||
else
|
else
|
||||||
win_name = (node.data.c2FullName === null) ? "???" : node.data.c2FullName;
|
win_name = (node.data.c2FullName === null) ? "???" : node.data.c2FullName;
|
||||||
|
|||||||
@ -7,22 +7,12 @@
|
|||||||
"adresseDuServeur": "Server address",
|
"adresseDuServeur": "Server address",
|
||||||
"ajouter": "Add",
|
"ajouter": "Add",
|
||||||
"ajouterDesCombattants": "Add fighters",
|
"ajouterDesCombattants": "Add fighters",
|
||||||
"ajouterUn": "Add one",
|
|
||||||
"ajouterUneTeam": "Add team",
|
"ajouterUneTeam": "Add team",
|
||||||
"attention": "Warning",
|
"attention": "Warning",
|
||||||
"aucuneConfigurationObs": "No OBS configuration found, please import one",
|
"aucuneConfigurationObs": "No OBS configuration found, please import one",
|
||||||
"avertissement": "Warning",
|
|
||||||
"bleu": "Blue",
|
"bleu": "Blue",
|
||||||
"blue": "Blue",
|
"blue": "Blue",
|
||||||
"cardAdded": "Card added",
|
|
||||||
"cardRemoved": "Card removed",
|
|
||||||
"carton": "Card",
|
|
||||||
"cartonDéquipe": "Team's card",
|
|
||||||
"cartonJaune": "Yellow card",
|
|
||||||
"cartonNoir": "Black card",
|
|
||||||
"cartonRouge": "Red card",
|
|
||||||
"catégorie": "Category",
|
"catégorie": "Category",
|
||||||
"ceCartonEstIssuDunCartonDéquipe": "This card comes from a team card, do you really want to delete it?",
|
|
||||||
"chrono.+/-...S": "+/- ... s",
|
"chrono.+/-...S": "+/- ... s",
|
||||||
"chrono.+10S": "+10 s",
|
"chrono.+10S": "+10 s",
|
||||||
"chrono.+1S": "+1 s",
|
"chrono.+1S": "+1 s",
|
||||||
@ -42,7 +32,6 @@
|
|||||||
"config.obs.motDePasseDuServeur": "Server password",
|
"config.obs.motDePasseDuServeur": "Server password",
|
||||||
"config.obs.warn1": "/! The password will be stored in plain text; it is recommended to use it only on OBS WebSocket and to change it between each competition",
|
"config.obs.warn1": "/! The password will be stored in plain text; it is recommended to use it only on OBS WebSocket and to change it between each competition",
|
||||||
"config.obs.ws": "ws://",
|
"config.obs.ws": "ws://",
|
||||||
"configurationDuNomDeLaZone": "Zone name configuration",
|
|
||||||
"configurationObs": "OBS Configuration",
|
"configurationObs": "OBS Configuration",
|
||||||
"confirm1": "This match already has results; are you sure you want to delete it?",
|
"confirm1": "This match already has results; are you sure you want to delete it?",
|
||||||
"confirm2.msg": "Do you really want to change the tournament tree size or the loser matches? This will modify existing matches (including possible deletions)!",
|
"confirm2.msg": "Do you really want to change the tournament tree size or the loser matches? This will modify existing matches (including possible deletions)!",
|
||||||
@ -51,17 +40,13 @@
|
|||||||
"confirm3.title": "Change category type",
|
"confirm3.title": "Change category type",
|
||||||
"confirm4.msg": "Do you really want to delete the category {{name}}. This will delete all associated matches!",
|
"confirm4.msg": "Do you really want to delete the category {{name}}. This will delete all associated matches!",
|
||||||
"confirm4.title": "Delete category",
|
"confirm4.title": "Delete category",
|
||||||
"confirmer": "Confirm",
|
|
||||||
"conserverUniquementLesMatchsTerminés": "Keep only finished matches",
|
"conserverUniquementLesMatchsTerminés": "Keep only finished matches",
|
||||||
"contre": "vs",
|
"contre": "vs",
|
||||||
"couleur": "Color",
|
|
||||||
"créerLesMatchs": "Create matches",
|
"créerLesMatchs": "Create matches",
|
||||||
"date": "Date",
|
|
||||||
"demi-finalesEtFinales": "Semi-finals and finals",
|
"demi-finalesEtFinales": "Semi-finals and finals",
|
||||||
"duréePause": "Pause duration",
|
"duréePause": "Pause duration",
|
||||||
"duréeRound": "Round duration",
|
"duréeRound": "Round duration",
|
||||||
"editionDeLaCatégorie": "Edit category",
|
"editionDeLaCatégorie": "Edit category",
|
||||||
"editionDuMatch": "Match edition",
|
|
||||||
"enregister": "Save",
|
"enregister": "Save",
|
||||||
"enregistrer": "Save",
|
"enregistrer": "Save",
|
||||||
"epéeBouclier": "Sword and shield",
|
"epéeBouclier": "Sword and shield",
|
||||||
@ -70,7 +55,6 @@
|
|||||||
"err3": "At least one type (pool or tournament) must be selected.",
|
"err3": "At least one type (pool or tournament) must be selected.",
|
||||||
"erreurLorsDeLaCopieDansLePresse": "Error while copying to clipboard: ",
|
"erreurLorsDeLaCopieDansLePresse": "Error while copying to clipboard: ",
|
||||||
"erreurLorsDeLaCréationDesMatchs": "Error while creating matches: ",
|
"erreurLorsDeLaCréationDesMatchs": "Error while creating matches: ",
|
||||||
"etatDesTablesDeMarque": "State of marque tables",
|
|
||||||
"exporter": "Export",
|
"exporter": "Export",
|
||||||
"fermer": "Close",
|
"fermer": "Close",
|
||||||
"finalesUniquement": "Finals only",
|
"finalesUniquement": "Finals only",
|
||||||
@ -78,9 +62,7 @@
|
|||||||
"genre.f": "F",
|
"genre.f": "F",
|
||||||
"genre.h": "M",
|
"genre.h": "M",
|
||||||
"genre.na": "NA",
|
"genre.na": "NA",
|
||||||
"individuelle": "Individual",
|
|
||||||
"inscrit": "Registered",
|
"inscrit": "Registered",
|
||||||
"listeDesCartons": "List of cards",
|
|
||||||
"manche": "Round",
|
"manche": "Round",
|
||||||
"matchPourLesPerdantsDuTournoi": "Match for tournament losers:",
|
"matchPourLesPerdantsDuTournoi": "Match for tournament losers:",
|
||||||
"matches": "Matches",
|
"matches": "Matches",
|
||||||
@ -89,7 +71,6 @@
|
|||||||
"neRienConserver": "Keep nothing",
|
"neRienConserver": "Keep nothing",
|
||||||
"no": "No.",
|
"no": "No.",
|
||||||
"nom": "Name",
|
"nom": "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>",
|
||||||
"nouvelle...": "New...",
|
"nouvelle...": "New...",
|
||||||
@ -119,15 +100,11 @@
|
|||||||
"serveur": "Server",
|
"serveur": "Server",
|
||||||
"suivant": "Next",
|
"suivant": "Next",
|
||||||
"supprimer": "Delete",
|
"supprimer": "Delete",
|
||||||
"supprimerUn": "Delete one",
|
|
||||||
"sélectionneLesModesDaffichage": "Select display modes",
|
"sélectionneLesModesDaffichage": "Select display modes",
|
||||||
"sélectionner": "Select",
|
"sélectionner": "Select",
|
||||||
"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.",
|
||||||
"toast.card.team.error": "Error while editing team card",
|
|
||||||
"toast.card.team.pending": "Editing team card...",
|
|
||||||
"toast.card.team.success": "Team card edited!",
|
|
||||||
"toast.createCategory.error": "Error while creating the category",
|
"toast.createCategory.error": "Error while creating the category",
|
||||||
"toast.createCategory.pending": "Creating category...",
|
"toast.createCategory.pending": "Creating category...",
|
||||||
"toast.createCategory.success": "Category created!",
|
"toast.createCategory.success": "Category created!",
|
||||||
@ -159,7 +136,7 @@
|
|||||||
"ttm.admin.obs": "Short click: Download resources. Long click: Create OBS configuration",
|
"ttm.admin.obs": "Short click: Download resources. Long click: Create OBS configuration",
|
||||||
"ttm.admin.scripte": "Copy integration script",
|
"ttm.admin.scripte": "Copy integration script",
|
||||||
"ttm.table.inverserLaPosition": "Reverse fighter positions on this screen",
|
"ttm.table.inverserLaPosition": "Reverse fighter positions on this screen",
|
||||||
"ttm.table.obs": "Short click: Load configuration and connect.",
|
"ttm.table.obs": "Short click: Load configuration and connect. Long click: Ring configuration",
|
||||||
"ttm.table.pub_aff": "Open public display",
|
"ttm.table.pub_aff": "Open public display",
|
||||||
"ttm.table.pub_score": "Show scores on public display",
|
"ttm.table.pub_score": "Show scores on public display",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
|
|||||||
@ -247,9 +247,6 @@
|
|||||||
"comp.toast.register.self.del.error": "Error during unregistration",
|
"comp.toast.register.self.del.error": "Error during unregistration",
|
||||||
"comp.toast.register.self.del.pending": "Unregistration in progress",
|
"comp.toast.register.self.del.pending": "Unregistration in progress",
|
||||||
"comp.toast.register.self.del.success": "Unregistration completed",
|
"comp.toast.register.self.del.success": "Unregistration completed",
|
||||||
"comp.toast.registers.addMultiple.error": "Import failed",
|
|
||||||
"comp.toast.registers.addMultiple.pending": "Import in progress",
|
|
||||||
"comp.toast.registers.addMultiple.success": "Import completed successfully 🎉",
|
|
||||||
"comp.toast.save.error": "Failed to save competition",
|
"comp.toast.save.error": "Failed to save competition",
|
||||||
"comp.toast.save.pending": "Saving competition in progress",
|
"comp.toast.save.pending": "Saving competition in progress",
|
||||||
"comp.toast.save.success": "Competition saved successfully 🎉",
|
"comp.toast.save.success": "Competition saved successfully 🎉",
|
||||||
@ -312,66 +309,6 @@
|
|||||||
"f": "F",
|
"f": "F",
|
||||||
"faitPar": "Done by",
|
"faitPar": "Done by",
|
||||||
"femme": "Female",
|
"femme": "Female",
|
||||||
"fileImport.variants": {
|
|
||||||
"categorie": [
|
|
||||||
"category",
|
|
||||||
"catégorie",
|
|
||||||
"weight category",
|
|
||||||
"age category"
|
|
||||||
],
|
|
||||||
"club": [
|
|
||||||
"club",
|
|
||||||
"club name",
|
|
||||||
"association",
|
|
||||||
"association name"
|
|
||||||
],
|
|
||||||
"genre": [
|
|
||||||
"gender",
|
|
||||||
"genre",
|
|
||||||
"sex",
|
|
||||||
"civility"
|
|
||||||
],
|
|
||||||
"licence": [
|
|
||||||
"license",
|
|
||||||
"licence",
|
|
||||||
"license number",
|
|
||||||
"license ID",
|
|
||||||
"ID license",
|
|
||||||
"licence no"
|
|
||||||
],
|
|
||||||
"nom": [
|
|
||||||
"last name",
|
|
||||||
"nom",
|
|
||||||
"family name",
|
|
||||||
"surname",
|
|
||||||
"lastname"
|
|
||||||
],
|
|
||||||
"overCategory": [
|
|
||||||
"over category",
|
|
||||||
"surclassement",
|
|
||||||
"category override",
|
|
||||||
"over classification"
|
|
||||||
],
|
|
||||||
"pays": [
|
|
||||||
"country",
|
|
||||||
"pays",
|
|
||||||
"country of residence",
|
|
||||||
"origin country"
|
|
||||||
],
|
|
||||||
"prenom": [
|
|
||||||
"first name",
|
|
||||||
"prénom",
|
|
||||||
"given name",
|
|
||||||
"first given name"
|
|
||||||
],
|
|
||||||
"weight": [
|
|
||||||
"weight",
|
|
||||||
"poids",
|
|
||||||
"weight (kg)",
|
|
||||||
"actual weight",
|
|
||||||
"mass"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filtre": "Filter",
|
"filtre": "Filter",
|
||||||
"gantMainBouclier": "Shield hand glove",
|
"gantMainBouclier": "Shield hand glove",
|
||||||
"gantMainsArmées": "Armed hand(s) glove(s)",
|
"gantMainsArmées": "Armed hand(s) glove(s)",
|
||||||
@ -660,5 +597,68 @@
|
|||||||
"voirLesStatues": "View statues",
|
"voirLesStatues": "View statues",
|
||||||
"vousNêtesPasEncoreInscrit": "You are not yet registered or your registration has not yet been entered on the intranet",
|
"vousNêtesPasEncoreInscrit": "You are not yet registered or your registration has not yet been entered on the intranet",
|
||||||
"à": "at",
|
"à": "at",
|
||||||
"étatDeLaDemande": "Request status"
|
"étatDeLaDemande": "Request status",
|
||||||
|
"fileImport.variants": {
|
||||||
|
"licence": [
|
||||||
|
"license",
|
||||||
|
"licence",
|
||||||
|
"license number",
|
||||||
|
"license ID",
|
||||||
|
"ID license",
|
||||||
|
"licence no"
|
||||||
|
],
|
||||||
|
"pays": [
|
||||||
|
"country",
|
||||||
|
"pays",
|
||||||
|
"country of residence",
|
||||||
|
"origin country"
|
||||||
|
],
|
||||||
|
"nom": [
|
||||||
|
"last name",
|
||||||
|
"nom",
|
||||||
|
"family name",
|
||||||
|
"surname",
|
||||||
|
"lastname"
|
||||||
|
],
|
||||||
|
"prenom": [
|
||||||
|
"first name",
|
||||||
|
"prénom",
|
||||||
|
"given name",
|
||||||
|
"first given name"
|
||||||
|
],
|
||||||
|
"genre": [
|
||||||
|
"gender",
|
||||||
|
"genre",
|
||||||
|
"sex",
|
||||||
|
"civility"
|
||||||
|
],
|
||||||
|
"weight": [
|
||||||
|
"weight",
|
||||||
|
"poids",
|
||||||
|
"weight (kg)",
|
||||||
|
"actual weight",
|
||||||
|
"mass"
|
||||||
|
],
|
||||||
|
"categorie": [
|
||||||
|
"category",
|
||||||
|
"catégorie",
|
||||||
|
"weight category",
|
||||||
|
"age category"
|
||||||
|
],
|
||||||
|
"overCategory": [
|
||||||
|
"over category",
|
||||||
|
"surclassement",
|
||||||
|
"category override",
|
||||||
|
"over classification"
|
||||||
|
],
|
||||||
|
"club": [
|
||||||
|
"club",
|
||||||
|
"club name",
|
||||||
|
"association",
|
||||||
|
"association name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"comp.toast.registers.addMultiple.error": "Import failed",
|
||||||
|
"comp.toast.registers.addMultiple.pending": "Import in progress",
|
||||||
|
"comp.toast.registers.addMultiple.success": "Import completed successfully 🎉"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,22 +7,12 @@
|
|||||||
"adresseDuServeur": "Adresse du serveur",
|
"adresseDuServeur": "Adresse du serveur",
|
||||||
"ajouter": "Ajouter",
|
"ajouter": "Ajouter",
|
||||||
"ajouterDesCombattants": "Ajouter des combattants",
|
"ajouterDesCombattants": "Ajouter des combattants",
|
||||||
"ajouterUn": "Ajouter un ",
|
|
||||||
"ajouterUneTeam": "Ajouter une équipe",
|
"ajouterUneTeam": "Ajouter une équipe",
|
||||||
"attention": "Attention",
|
"attention": "Attention",
|
||||||
"aucuneConfigurationObs": "Aucune configuration OBS trouvée, veuillez en importer une",
|
"aucuneConfigurationObs": "Aucune configuration OBS trouvée, veuillez en importer une",
|
||||||
"avertissement": "Avertissement",
|
|
||||||
"bleu": "Bleu",
|
"bleu": "Bleu",
|
||||||
"blue": "Blue",
|
"blue": "Blue",
|
||||||
"cardAdded": "Carton ajouté",
|
|
||||||
"cardRemoved": "Carton retiré",
|
|
||||||
"carton": "Carton",
|
|
||||||
"cartonDéquipe": "Carton d'équipe",
|
|
||||||
"cartonJaune": "Carton jaune",
|
|
||||||
"cartonNoir": "Carton noir",
|
|
||||||
"cartonRouge": "Carton rouge",
|
|
||||||
"catégorie": "Catégorie",
|
"catégorie": "Catégorie",
|
||||||
"ceCartonEstIssuDunCartonDéquipe": "Ce carton est issu d'un carton d'équipe, voulez-vous vraiment le supprimer ?",
|
|
||||||
"chrono.+/-...S": "+/- ... s",
|
"chrono.+/-...S": "+/- ... s",
|
||||||
"chrono.+10S": "+10 s",
|
"chrono.+10S": "+10 s",
|
||||||
"chrono.+1S": "+1 s",
|
"chrono.+1S": "+1 s",
|
||||||
@ -42,7 +32,6 @@
|
|||||||
"config.obs.motDePasseDuServeur": "Mot de passe du serveur",
|
"config.obs.motDePasseDuServeur": "Mot de passe du serveur",
|
||||||
"config.obs.warn1": "/! Le mot de passe va être stoker en claire, il est recommandé de ne l'utiliser que sur obs websocket et d'en changer entre chaque compétition",
|
"config.obs.warn1": "/! Le mot de passe va être stoker en claire, il est recommandé de ne l'utiliser que sur obs websocket et d'en changer entre chaque compétition",
|
||||||
"config.obs.ws": "ws://",
|
"config.obs.ws": "ws://",
|
||||||
"configurationDuNomDeLaZone": "Configuration du nom de la zone",
|
|
||||||
"configurationObs": "Configuration OBS",
|
"configurationObs": "Configuration OBS",
|
||||||
"confirm1": "Ce match a déjà des résultats, êtes-vous sûr de vouloir le supprimer ?",
|
"confirm1": "Ce match a déjà des résultats, êtes-vous sûr de vouloir le supprimer ?",
|
||||||
"confirm2.msg": "Voulez-vous vraiment changer la taille de l'arbre du tournoi ou les matchs pour les perdants ? Cela va modifier les matchs existants (incluant des possibles suppressions)!",
|
"confirm2.msg": "Voulez-vous vraiment changer la taille de l'arbre du tournoi ou les matchs pour les perdants ? Cela va modifier les matchs existants (incluant des possibles suppressions)!",
|
||||||
@ -51,17 +40,13 @@
|
|||||||
"confirm3.title": "Changement de type de catégorie",
|
"confirm3.title": "Changement de type de catégorie",
|
||||||
"confirm4.msg": "Voulez-vous vraiment supprimer la catégorie {{name}}. Cela va supprimer tous les matchs associés !",
|
"confirm4.msg": "Voulez-vous vraiment supprimer la catégorie {{name}}. Cela va supprimer tous les matchs associés !",
|
||||||
"confirm4.title": "Suppression de la catégorie",
|
"confirm4.title": "Suppression de la catégorie",
|
||||||
"confirmer": "Confirmer",
|
|
||||||
"conserverUniquementLesMatchsTerminés": "Conserver uniquement les matchs terminés",
|
"conserverUniquementLesMatchsTerminés": "Conserver uniquement les matchs terminés",
|
||||||
"contre": "contre",
|
"contre": "contre",
|
||||||
"couleur": "Couleur",
|
|
||||||
"créerLesMatchs": "Créer les matchs",
|
"créerLesMatchs": "Créer les matchs",
|
||||||
"date": "Date",
|
|
||||||
"demi-finalesEtFinales": "Demi-finales et finales",
|
"demi-finalesEtFinales": "Demi-finales et finales",
|
||||||
"duréePause": "Durée pause",
|
"duréePause": "Durée pause",
|
||||||
"duréeRound": "Durée round",
|
"duréeRound": "Durée round",
|
||||||
"editionDeLaCatégorie": "Edition de la catégorie",
|
"editionDeLaCatégorie": "Edition de la catégorie",
|
||||||
"editionDuMatch": "Edition du match",
|
|
||||||
"enregister": "Enregister",
|
"enregister": "Enregister",
|
||||||
"enregistrer": "Enregistrer",
|
"enregistrer": "Enregistrer",
|
||||||
"epéeBouclier": "Epée bouclier",
|
"epéeBouclier": "Epée bouclier",
|
||||||
@ -70,7 +55,6 @@
|
|||||||
"err3": "Au moins un type (poule ou tournoi) doit être sélectionné.",
|
"err3": "Au moins un type (poule ou tournoi) doit être sélectionné.",
|
||||||
"erreurLorsDeLaCopieDansLePresse": "Erreur lors de la copie dans le presse-papier : ",
|
"erreurLorsDeLaCopieDansLePresse": "Erreur lors de la copie dans le presse-papier : ",
|
||||||
"erreurLorsDeLaCréationDesMatchs": "Erreur lors de la création des matchs: ",
|
"erreurLorsDeLaCréationDesMatchs": "Erreur lors de la création des matchs: ",
|
||||||
"etatDesTablesDeMarque": "Etat des tables de marque",
|
|
||||||
"exporter": "Exporter",
|
"exporter": "Exporter",
|
||||||
"fermer": "Fermer",
|
"fermer": "Fermer",
|
||||||
"finalesUniquement": "Finales uniquement",
|
"finalesUniquement": "Finales uniquement",
|
||||||
@ -78,9 +62,7 @@
|
|||||||
"genre.f": "F",
|
"genre.f": "F",
|
||||||
"genre.h": "H",
|
"genre.h": "H",
|
||||||
"genre.na": "NA",
|
"genre.na": "NA",
|
||||||
"individuelle": "Individuelle",
|
|
||||||
"inscrit": "Inscrit",
|
"inscrit": "Inscrit",
|
||||||
"listeDesCartons": "Liste des cartons",
|
|
||||||
"manche": "Manche",
|
"manche": "Manche",
|
||||||
"matchPourLesPerdantsDuTournoi": "Match pour les perdants du tournoi:",
|
"matchPourLesPerdantsDuTournoi": "Match pour les perdants du tournoi:",
|
||||||
"matches": "Matches",
|
"matches": "Matches",
|
||||||
@ -89,7 +71,6 @@
|
|||||||
"neRienConserver": "Ne rien conserver",
|
"neRienConserver": "Ne rien conserver",
|
||||||
"no": "N°",
|
"no": "N°",
|
||||||
"nom": "Nom",
|
"nom": "Nom",
|
||||||
"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>",
|
||||||
"nouvelle...": "Nouvelle...",
|
"nouvelle...": "Nouvelle...",
|
||||||
@ -119,15 +100,11 @@
|
|||||||
"serveur": "Serveur",
|
"serveur": "Serveur",
|
||||||
"suivant": "Suivant",
|
"suivant": "Suivant",
|
||||||
"supprimer": "Supprimer",
|
"supprimer": "Supprimer",
|
||||||
"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",
|
||||||
"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.",
|
||||||
"toast.card.team.error": "Erreur lors de la modification du carton d'équipe",
|
|
||||||
"toast.card.team.pending": "Modification du carton d'équipe...",
|
|
||||||
"toast.card.team.success": "Carton d'équipe modifié !",
|
|
||||||
"toast.createCategory.error": "Erreur lors de la création de la catégorie",
|
"toast.createCategory.error": "Erreur lors de la création de la catégorie",
|
||||||
"toast.createCategory.pending": "Création de la catégorie...",
|
"toast.createCategory.pending": "Création de la catégorie...",
|
||||||
"toast.createCategory.success": "Catégorie créée !",
|
"toast.createCategory.success": "Catégorie créée !",
|
||||||
@ -159,7 +136,7 @@
|
|||||||
"ttm.admin.obs": "Clique court : Télécharger les ressources. Clique long : Créer la configuration obs",
|
"ttm.admin.obs": "Clique court : Télécharger les ressources. Clique long : Créer la configuration obs",
|
||||||
"ttm.admin.scripte": "Copier le scripte d'intégration",
|
"ttm.admin.scripte": "Copier le scripte d'intégration",
|
||||||
"ttm.table.inverserLaPosition": "Inverser la position des combattants sur cette écran",
|
"ttm.table.inverserLaPosition": "Inverser la position des combattants sur cette écran",
|
||||||
"ttm.table.obs": "Clique court : Charger la configuration et se connecter.",
|
"ttm.table.obs": "Clique court : Charger la configuration et se connecter. Clique long : Configuration de la lice",
|
||||||
"ttm.table.pub_aff": "Ouvrir l'affichage public",
|
"ttm.table.pub_aff": "Ouvrir l'affichage public",
|
||||||
"ttm.table.pub_score": "Afficher les scores sur l'affichage public",
|
"ttm.table.pub_score": "Afficher les scores sur l'affichage public",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
|
|||||||
@ -247,9 +247,6 @@
|
|||||||
"comp.toast.register.self.del.error": "Erreur lors de la désinscription",
|
"comp.toast.register.self.del.error": "Erreur lors de la désinscription",
|
||||||
"comp.toast.register.self.del.pending": "Désinscription en cours",
|
"comp.toast.register.self.del.pending": "Désinscription en cours",
|
||||||
"comp.toast.register.self.del.success": "Désinscription réalisée",
|
"comp.toast.register.self.del.success": "Désinscription réalisée",
|
||||||
"comp.toast.registers.addMultiple.error": "Erreur lors de l'importation des combattants",
|
|
||||||
"comp.toast.registers.addMultiple.pending": "Importation des combattants en cours...",
|
|
||||||
"comp.toast.registers.addMultiple.success": "Importation des combattants réussie 🎉",
|
|
||||||
"comp.toast.save.error": "Échec de l'enregistrement de la compétition",
|
"comp.toast.save.error": "Échec de l'enregistrement de la compétition",
|
||||||
"comp.toast.save.pending": "Enregistrement de la compétition en cours",
|
"comp.toast.save.pending": "Enregistrement de la compétition en cours",
|
||||||
"comp.toast.save.success": "Compétition enregistrée avec succès 🎉",
|
"comp.toast.save.success": "Compétition enregistrée avec succès 🎉",
|
||||||
@ -312,70 +309,6 @@
|
|||||||
"f": "F",
|
"f": "F",
|
||||||
"faitPar": "Fait par",
|
"faitPar": "Fait par",
|
||||||
"femme": "Femme",
|
"femme": "Femme",
|
||||||
"fileImport.variants": {
|
|
||||||
"categorie": [
|
|
||||||
"catégorie",
|
|
||||||
"category",
|
|
||||||
"catégorie de poids",
|
|
||||||
"weight category",
|
|
||||||
"catégorie d'âge"
|
|
||||||
],
|
|
||||||
"club": [
|
|
||||||
"club",
|
|
||||||
"nom du club",
|
|
||||||
"club name",
|
|
||||||
"association",
|
|
||||||
"nom de l'association"
|
|
||||||
],
|
|
||||||
"genre": [
|
|
||||||
"genre",
|
|
||||||
"sexe",
|
|
||||||
"gender",
|
|
||||||
"sex",
|
|
||||||
"civilité"
|
|
||||||
],
|
|
||||||
"licence": [
|
|
||||||
"licence",
|
|
||||||
"n° licence",
|
|
||||||
"num licence",
|
|
||||||
"id licence",
|
|
||||||
"license",
|
|
||||||
"licence id"
|
|
||||||
],
|
|
||||||
"nom": [
|
|
||||||
"nom",
|
|
||||||
"nom de famille",
|
|
||||||
"lastname",
|
|
||||||
"family name",
|
|
||||||
"nom complet"
|
|
||||||
],
|
|
||||||
"overCategory": [
|
|
||||||
"surclassement",
|
|
||||||
"over category",
|
|
||||||
"surcatégorie",
|
|
||||||
"surclassement de catégorie"
|
|
||||||
],
|
|
||||||
"pays": [
|
|
||||||
"pays",
|
|
||||||
"country",
|
|
||||||
"pays de résidence",
|
|
||||||
"pays d'origine"
|
|
||||||
],
|
|
||||||
"prenom": [
|
|
||||||
"prénom",
|
|
||||||
"prenom",
|
|
||||||
"first name",
|
|
||||||
"given name",
|
|
||||||
"prénom usuel"
|
|
||||||
],
|
|
||||||
"weight": [
|
|
||||||
"poids",
|
|
||||||
"weight",
|
|
||||||
"poids (kg)",
|
|
||||||
"poids réel",
|
|
||||||
"masse"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filtre": "Filtre",
|
"filtre": "Filtre",
|
||||||
"gantMainBouclier": "Gant main de bouclier",
|
"gantMainBouclier": "Gant main de bouclier",
|
||||||
"gantMainsArmées": "Gant main(s) armée(s)",
|
"gantMainsArmées": "Gant main(s) armée(s)",
|
||||||
@ -664,5 +597,72 @@
|
|||||||
"voirLesStatues": "Voir les statues",
|
"voirLesStatues": "Voir les statues",
|
||||||
"vousNêtesPasEncoreInscrit": "Vous n'êtes pas encore inscrit ou votre inscription n'a pas encore été rentrée sur l'intranet",
|
"vousNêtesPasEncoreInscrit": "Vous n'êtes pas encore inscrit ou votre inscription n'a pas encore été rentrée sur l'intranet",
|
||||||
"à": "à",
|
"à": "à",
|
||||||
"étatDeLaDemande": "État de la demande"
|
"étatDeLaDemande": "État de la demande",
|
||||||
|
"fileImport.variants": {
|
||||||
|
"licence": [
|
||||||
|
"licence",
|
||||||
|
"n° licence",
|
||||||
|
"num licence",
|
||||||
|
"id licence",
|
||||||
|
"license",
|
||||||
|
"licence id"
|
||||||
|
],
|
||||||
|
"pays": [
|
||||||
|
"pays",
|
||||||
|
"country",
|
||||||
|
"pays de résidence",
|
||||||
|
"pays d'origine"
|
||||||
|
],
|
||||||
|
"nom": [
|
||||||
|
"nom",
|
||||||
|
"nom de famille",
|
||||||
|
"lastname",
|
||||||
|
"family name",
|
||||||
|
"nom complet"
|
||||||
|
],
|
||||||
|
"prenom": [
|
||||||
|
"prénom",
|
||||||
|
"prenom",
|
||||||
|
"first name",
|
||||||
|
"given name",
|
||||||
|
"prénom usuel"
|
||||||
|
],
|
||||||
|
"genre": [
|
||||||
|
"genre",
|
||||||
|
"sexe",
|
||||||
|
"gender",
|
||||||
|
"sex",
|
||||||
|
"civilité"
|
||||||
|
],
|
||||||
|
"weight": [
|
||||||
|
"poids",
|
||||||
|
"weight",
|
||||||
|
"poids (kg)",
|
||||||
|
"poids réel",
|
||||||
|
"masse"
|
||||||
|
],
|
||||||
|
"categorie": [
|
||||||
|
"catégorie",
|
||||||
|
"category",
|
||||||
|
"catégorie de poids",
|
||||||
|
"weight category",
|
||||||
|
"catégorie d'âge"
|
||||||
|
],
|
||||||
|
"overCategory": [
|
||||||
|
"surclassement",
|
||||||
|
"over category",
|
||||||
|
"surcatégorie",
|
||||||
|
"surclassement de catégorie"
|
||||||
|
],
|
||||||
|
"club": [
|
||||||
|
"club",
|
||||||
|
"nom du club",
|
||||||
|
"club name",
|
||||||
|
"association",
|
||||||
|
"nom de l'association"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"comp.toast.registers.addMultiple.error": "Erreur lors de l'importation des combattants",
|
||||||
|
"comp.toast.registers.addMultiple.pending": "Importation des combattants en cours...",
|
||||||
|
"comp.toast.registers.addMultiple.success": "Importation des combattants réussie 🎉"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,167 +0,0 @@
|
|||||||
import {createContext, useContext, useEffect, useReducer} from "react";
|
|
||||||
import {useWS} from "./useWS.jsx";
|
|
||||||
|
|
||||||
const CardContext = createContext({comb: {}, team: []});
|
|
||||||
const CardDispatchContext = createContext(() => {
|
|
||||||
});
|
|
||||||
|
|
||||||
function compareCards(a, b) {
|
|
||||||
for (const keys of Object.keys(a)) {
|
|
||||||
if (a[keys] !== b[keys]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CARD_TYPE_ORDER = [
|
|
||||||
'BLUE',
|
|
||||||
'YELLOW',
|
|
||||||
'RED',
|
|
||||||
'BLACK'
|
|
||||||
]
|
|
||||||
|
|
||||||
export function compareCardOrder(a, b) {
|
|
||||||
if (!a || !b) return 0;
|
|
||||||
return CARD_TYPE_ORDER.indexOf(a.type) - CARD_TYPE_ORDER.indexOf(b.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
function reducer(state, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'SET_CARD':
|
|
||||||
if (state.comb[action.payload.id] === undefined || !compareCards(action.payload, state.comb[action.payload.id])) {
|
|
||||||
return {
|
|
||||||
comb: {
|
|
||||||
...state.comb,
|
|
||||||
[action.payload.id]: action.payload
|
|
||||||
},
|
|
||||||
team: state.team
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
case 'SET_ALL':
|
|
||||||
if (action.payload.some(e => state.comb[e.id] === undefined || !compareCards(e, state.comb[e.id]))) {
|
|
||||||
const newCombs = {};
|
|
||||||
for (const o of action.payload) {
|
|
||||||
newCombs[o.id] = o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
comb: {
|
|
||||||
...state.comb,
|
|
||||||
...newCombs
|
|
||||||
},
|
|
||||||
team: state.team
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
case 'REMOVE_CARDS':
|
|
||||||
const newState = {...state}
|
|
||||||
for (const id of action.payload)
|
|
||||||
delete newState.comb[id]
|
|
||||||
return newState
|
|
||||||
case 'SET_TEAM_CARD':
|
|
||||||
return {
|
|
||||||
comb: state.comb,
|
|
||||||
team: [...state.team.filter(e => e.teamName !== action.payload.teamName || e.teamUuid !== action.payload.teamUuid || e.type !== action.payload.type),
|
|
||||||
action.payload]
|
|
||||||
}
|
|
||||||
case 'REMOVE_TEAM_CARD':
|
|
||||||
return {
|
|
||||||
comb: state.comb,
|
|
||||||
team: [...state.team.filter(e => e.teamName !== action.payload.teamName || e.teamUuid !== action.payload.teamUuid || e.type !== action.payload.type)]
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function WSListener({dispatch}) {
|
|
||||||
const {dispatch: dispatchWS} = useWS()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const sendCards = ({data}) => {
|
|
||||||
dispatch({type: 'SET_ALL', payload: data});
|
|
||||||
}
|
|
||||||
const rmCards = ({data}) => {
|
|
||||||
dispatch({type: 'REMOVE_CARDS', payload: data});
|
|
||||||
}
|
|
||||||
const sendTeamCard = ({data}) => {
|
|
||||||
dispatch({type: 'SET_ALL', payload: data.cards});
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_TEAM_CARD',
|
|
||||||
payload: {teamName: data.teamName, teamUuid: data.teamUuid, type: data.type, reason: data.reason, date: data.date}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const rmTeamCard = ({data}) => {
|
|
||||||
dispatch({
|
|
||||||
type: 'REMOVE_TEAM_CARD',
|
|
||||||
payload: {teamName: data.teamName, teamUuid: data.teamUuid, type: data.type, reason: data.reason, date: data.date}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchWS({type: 'addListener', payload: {callback: sendCards, code: 'sendCards'}})
|
|
||||||
dispatchWS({type: 'addListener', payload: {callback: rmCards, code: 'rmCards'}})
|
|
||||||
dispatchWS({type: 'addListener', payload: {callback: sendTeamCard, code: 'sendTeamCard'}})
|
|
||||||
dispatchWS({type: 'addListener', payload: {callback: rmTeamCard, code: 'rmTeamCard'}})
|
|
||||||
return () => {
|
|
||||||
dispatchWS({type: 'removeListener', payload: sendCards})
|
|
||||||
dispatchWS({type: 'removeListener', payload: rmCards})
|
|
||||||
dispatchWS({type: 'removeListener', payload: sendTeamCard})
|
|
||||||
dispatchWS({type: 'removeListener', payload: rmTeamCard})
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CardsProvider({children}) {
|
|
||||||
const [cards, dispatch] = useReducer(reducer, {comb: {}, team: []})
|
|
||||||
|
|
||||||
return <CardContext.Provider value={cards}>
|
|
||||||
<CardDispatchContext.Provider value={dispatch}>
|
|
||||||
{children}
|
|
||||||
<WSListener dispatch={dispatch}/>
|
|
||||||
</CardDispatchContext.Provider>
|
|
||||||
</CardContext.Provider>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useCards() {
|
|
||||||
const cards = useContext(CardContext);
|
|
||||||
return {
|
|
||||||
cards_t: cards.team,
|
|
||||||
cards_v: Object.values(cards.comb),
|
|
||||||
...useCardsStatic(Object.values(cards.comb))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useCardsStatic(cards_v) {
|
|
||||||
return {
|
|
||||||
getCardInMatch: (match) => {
|
|
||||||
return cards_v.filter(card => (card.comb === match.c1 || card.comb === match.c2) && card.match === match.id);
|
|
||||||
},
|
|
||||||
getHeightCardForCombInMatch: (combId, match) => {
|
|
||||||
return cards_v.filter(card => card.comb === combId && (card.category === match.categorie || (card.match !== match.id && card.type !== "BLUE"))).sort(compareCardOrder).pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useCardsDispatch() {
|
|
||||||
return useContext(CardDispatchContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hasEffectCard(card, matchId, categoryId) {
|
|
||||||
switch (card.type) {
|
|
||||||
case 'BLUE':
|
|
||||||
return false;
|
|
||||||
case 'YELLOW':
|
|
||||||
return card.match === matchId;
|
|
||||||
case 'RED':
|
|
||||||
return card.match === matchId || card.category === categoryId;
|
|
||||||
case 'BLACK':
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -29,7 +29,7 @@ function reducer(state, action) {
|
|||||||
//console.debug("Updating comb", comb);
|
//console.debug("Updating comb", comb);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
[comb.id]: {...state[comb.id], ...comb}
|
[comb.id]: comb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
@ -49,10 +49,7 @@ function reducer(state, action) {
|
|||||||
if (combs.some(e => state[e.id] === undefined || !compareCombs(e, state[e.id]))) {
|
if (combs.some(e => state[e.id] === undefined || !compareCombs(e, state[e.id]))) {
|
||||||
const newCombs = {};
|
const newCombs = {};
|
||||||
for (const o of combs) {
|
for (const o of combs) {
|
||||||
newCombs[o.id] = {
|
newCombs[o.id] = o;
|
||||||
...state[o.id],
|
|
||||||
...o
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
//console.debug("Updating combs", newCombs);
|
//console.debug("Updating combs", newCombs);
|
||||||
|
|
||||||
@ -81,7 +78,7 @@ function WSListener({dispatch}) {
|
|||||||
|
|
||||||
dispatchWS({type: 'addListener', payload: {callback: sendRegister, code: 'sendRegister'}})
|
dispatchWS({type: 'addListener', payload: {callback: sendRegister, code: 'sendRegister'}})
|
||||||
return () => {
|
return () => {
|
||||||
dispatchWS({type: 'removeListener', payload: sendRegister})
|
dispatchWS({type: 'removeListener', payload: {callback: sendRegister, code: 'sendRegister'}})
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,6 @@ export function WSProvider({url, onmessage, children}) {
|
|||||||
const [welcomeData, setWelcomeData] = useState({name: "", perm: "", show_blason: true, show_flag: false})
|
const [welcomeData, setWelcomeData] = useState({name: "", perm: "", show_blason: true, show_flag: false})
|
||||||
const [state, dispatch] = useReducer(reducer, {listener: []})
|
const [state, dispatch] = useReducer(reducer, {listener: []})
|
||||||
const ws = useRef(null)
|
const ws = useRef(null)
|
||||||
const tableState = useRef({})
|
|
||||||
const listenersRef = useRef([])
|
const listenersRef = useRef([])
|
||||||
const callbackRef = useRef({})
|
const callbackRef = useRef({})
|
||||||
const isReadyRef = useRef(isReady)
|
const isReadyRef = useRef(isReady)
|
||||||
@ -217,14 +216,14 @@ export function WSProvider({url, onmessage, children}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const ret = {isReady, dispatch, send, wait_length: callbackRef, welcomeData, tableState}
|
const ret = {isReady, dispatch, send, wait_length: callbackRef, welcomeData}
|
||||||
return <WebsocketContext.Provider value={ret}>
|
return <WebsocketContext.Provider value={ret}>
|
||||||
{children}
|
{children}
|
||||||
</WebsocketContext.Provider>
|
</WebsocketContext.Provider>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWS() {
|
export function useWS() {
|
||||||
const {isReady, dispatch, send, wait_length, welcomeData, tableState} = useContext(WebsocketContext)
|
const {isReady, dispatch, send, wait_length, welcomeData} = useContext(WebsocketContext)
|
||||||
return {
|
return {
|
||||||
dispatch,
|
dispatch,
|
||||||
isReady,
|
isReady,
|
||||||
@ -248,10 +247,6 @@ export function useWS() {
|
|||||||
send(uuidv4(), "error", "ERROR", data)
|
send(uuidv4(), "error", "ERROR", data)
|
||||||
},
|
},
|
||||||
send,
|
send,
|
||||||
setState: (newState) => {
|
|
||||||
tableState.current = {...tableState.current, ...newState}
|
|
||||||
},
|
|
||||||
tableState
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {faCircleInfo, faEuroSign} from "@fortawesome/free-solid-svg-icons";
|
|||||||
import "./PayAndValidateList.css";
|
import "./PayAndValidateList.css";
|
||||||
import * as Tools from "../utils/Tools.js";
|
import * as Tools from "../utils/Tools.js";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
|
import {counter} from "@fortawesome/fontawesome-svg-core";
|
||||||
|
|
||||||
export function PayAndValidateList({source}) {
|
export function PayAndValidateList({source}) {
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
@ -30,7 +31,8 @@ export function PayAndValidateList({source}) {
|
|||||||
const [lastSearch, setLastSearch] = useState("");
|
const [lastSearch, setLastSearch] = useState("");
|
||||||
const [paymentFilter, setPaymentFilter] = useState((source === "club") ? 0 : 2);
|
const [paymentFilter, setPaymentFilter] = useState((source === "club") ? 0 : 2);
|
||||||
|
|
||||||
const [selectedMembers, setSelectedMembers] = useState([]);
|
const storedMembers = sessionStorage.getItem("selectedMembers");
|
||||||
|
const [selectedMembers, setSelectedMembers] = useState(storedMembers ? JSON.parse(storedMembers) : []);
|
||||||
|
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const {
|
const {
|
||||||
@ -39,6 +41,10 @@ export function PayAndValidateList({source}) {
|
|||||||
refresh
|
refresh
|
||||||
} = useFetch(`/member/find/${source}?page=${page}&licenceRequest=${stateFilter}&payment=${paymentFilter}&categorie=${catFilter}`, setLoading, 1)
|
} = useFetch(`/member/find/${source}?page=${page}&licenceRequest=${stateFilter}&payment=${paymentFilter}&categorie=${catFilter}`, setLoading, 1)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sessionStorage.setItem("selectedMembers", JSON.stringify(selectedMembers));
|
||||||
|
}, [selectedMembers]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}&payment=${paymentFilter}&categorie=${catFilter}`);
|
refresh(`/member/find/${source}?page=${page}&search=${lastSearch}&club=${clubFilter}&licenceRequest=${stateFilter}&payment=${paymentFilter}&categorie=${catFilter}`);
|
||||||
}, [hash, clubFilter, stateFilter, lastSearch, paymentFilter, catFilter]);
|
}, [hash, clubFilter, stateFilter, lastSearch, paymentFilter, catFilter]);
|
||||||
|
|||||||
@ -457,7 +457,8 @@ function Content({data}) {
|
|||||||
defaultValue={data.endRegister ? data.endRegister.substring(0, 16) : ''}/>
|
defaultValue={data.endRegister ? data.endRegister.substring(0, 16) : ''}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="input-group mb-3" style={{display: "flex" }}>
|
<div className="input-group mb-3"
|
||||||
|
style={{display: registerMode === "FREE" || registerMode === "CLUB_ADMIN" ? "flex" : "none"}}>
|
||||||
<span className="input-group-text" id="startRegister">{t('poidsDemandéPour')}</span>
|
<span className="input-group-text" id="startRegister">{t('poidsDemandéPour')}</span>
|
||||||
{CatList.map((cat, index) => <div key={index} className="input-group-text">
|
{CatList.map((cat, index) => <div key={index} className="input-group-text">
|
||||||
<input className="form-check-input mt-0" type="checkbox" id={"catInput" + index} checked={isCatSelected(cat)}
|
<input className="form-check-input mt-0" type="checkbox" id={"catInput" + index} checked={isCatSelected(cat)}
|
||||||
|
|||||||
@ -88,7 +88,7 @@ export function CompetitionRegisterAdmin({source}) {
|
|||||||
<div className="col-lg-9">
|
<div className="col-lg-9">
|
||||||
{data ? <div className="">
|
{data ? <div className="">
|
||||||
<MakeCentralPanel
|
<MakeCentralPanel
|
||||||
data={state.filter(s => (clubFilter.length === 0 || s.data.club?.name === clubFilter)
|
data={state.filter(s => (clubFilter.length === 0 || s.data.club.name === clubFilter)
|
||||||
&& (catAgeFilter.length === 0 || s.data.categorie === catAgeFilter)
|
&& (catAgeFilter.length === 0 || s.data.categorie === catAgeFilter)
|
||||||
&& (catFilter === -1 || s.data.categoriesInscrites.includes(catFilter))
|
&& (catFilter === -1 || s.data.categoriesInscrites.includes(catFilter))
|
||||||
&& (!filterNotWeight || (data3?.requiredWeight.includes(s.data.categorie) && (
|
&& (!filterNotWeight || (data3?.requiredWeight.includes(s.data.categorie) && (
|
||||||
|
|||||||
@ -11,14 +11,10 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|||||||
import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts";
|
import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts";
|
||||||
import JSZip from "jszip";
|
import JSZip from "jszip";
|
||||||
import {detectOptimalBackground} from "../../../components/SmartLogoBackground.jsx";
|
import {detectOptimalBackground} from "../../../components/SmartLogoBackground.jsx";
|
||||||
import {faFile, faGlobe, faTableCellsLarge, faTrash} from "@fortawesome/free-solid-svg-icons";
|
import {faGlobe} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {Trans, useTranslation} from "react-i18next";
|
import {Trans, useTranslation} from "react-i18next";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import {getToastMessage} from "../../../utils/Tools.js";
|
import {getToastMessage} from "../../../utils/Tools.js";
|
||||||
import {copyStyles} from "../../../utils/copyStyles.js";
|
|
||||||
import {StateWindow} from "./StateWindow.jsx";
|
|
||||||
import {CombName, useCombs} from "../../../hooks/useComb.jsx";
|
|
||||||
import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -165,19 +161,12 @@ async function downloadResourcesAsZip(resourceList) {
|
|||||||
progressText.textContent = i18n.t('téléchargementTerminé!');
|
progressText.textContent = i18n.t('téléchargementTerminé!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowName = "FFSAFTableStateWindow";
|
|
||||||
|
|
||||||
function Menu({menuActions, compUuid}) {
|
function Menu({menuActions, compUuid}) {
|
||||||
const e = document.getElementById("actionMenu")
|
const e = document.getElementById("actionMenu")
|
||||||
const longPress = useRef({time: null, timer: null, button: null});
|
const longPress = useRef({time: null, timer: null, button: null});
|
||||||
const obsModal = useRef(null);
|
const obsModal = useRef(null);
|
||||||
const teamCardModal = useRef(null);
|
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
|
|
||||||
const [showStateWin, setShowStateWin] = useState(false)
|
|
||||||
const externalWindow = useRef(null)
|
|
||||||
const containerEl = useRef(document.createElement("div"))
|
|
||||||
|
|
||||||
for (const x of tto)
|
for (const x of tto)
|
||||||
x.dispose();
|
x.dispose();
|
||||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip2"]')
|
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip2"]')
|
||||||
@ -189,32 +178,6 @@ function Menu({menuActions, compUuid}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (sessionStorage.getItem(windowName + "_open") === "true") {
|
|
||||||
handleStateWin();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleStateWin = __ => {
|
|
||||||
if (showStateWin === false || !externalWindow.current || externalWindow.current.closed) {
|
|
||||||
externalWindow.current = window.open("", windowName, "width=800,height=600,left=200,top=200")
|
|
||||||
externalWindow.current.document.body.innerHTML = ""
|
|
||||||
externalWindow.current.document.body.appendChild(containerEl.current)
|
|
||||||
copyStyles(document, externalWindow.current.document)
|
|
||||||
|
|
||||||
externalWindow.current.addEventListener("beforeunload", () => {
|
|
||||||
setShowStateWin(false);
|
|
||||||
externalWindow.current.close();
|
|
||||||
externalWindow.current = null;
|
|
||||||
sessionStorage.removeItem(windowName + "_open");
|
|
||||||
});
|
|
||||||
setShowStateWin(true);
|
|
||||||
sessionStorage.setItem(windowName + "_open", "true");
|
|
||||||
} else {
|
|
||||||
externalWindow.current.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const longPressDown = (button) => {
|
const longPressDown = (button) => {
|
||||||
longPress.current.button = button;
|
longPress.current.button = button;
|
||||||
longPress.current.time = new Date();
|
longPress.current.time = new Date();
|
||||||
@ -238,8 +201,6 @@ function Menu({menuActions, compUuid}) {
|
|||||||
if (button === "obs") {
|
if (button === "obs") {
|
||||||
downloadResourcesAsZip(menuActions.current.resourceList || [])
|
downloadResourcesAsZip(menuActions.current.resourceList || [])
|
||||||
.then(__ => console.log("Ressources téléchargées"));
|
.then(__ => console.log("Ressources téléchargées"));
|
||||||
} else if (button === "cards") {
|
|
||||||
teamCardModal.current.click();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,14 +240,8 @@ function Menu({menuActions, compUuid}) {
|
|||||||
{createPortal(
|
{createPortal(
|
||||||
<>
|
<>
|
||||||
<div className="vr" style={{margin: "0 0.5em", height: "100%"}}></div>
|
<div className="vr" style={{margin: "0 0.5em", height: "100%"}}></div>
|
||||||
<FontAwesomeIcon icon={faFile} size="xl"
|
|
||||||
style={{color: "#6c757d", cursor: "pointer"}}
|
|
||||||
onMouseDown={() => longPressDown("cards")}
|
|
||||||
onMouseUp={() => longPressUp("cards")}
|
|
||||||
data-bs-toggle="tooltip2" data-bs-placement="top"
|
|
||||||
data-bs-title={t("carton")}/>
|
|
||||||
<FontAwesomeIcon icon={SimpleIconsOBS} size="xl"
|
<FontAwesomeIcon icon={SimpleIconsOBS} size="xl"
|
||||||
style={{color: "#6c757d", cursor: "pointer"}}
|
style={{color: "#6c757d", cursor: "pointer", marginRight: "0.25em"}}
|
||||||
onMouseDown={() => longPressDown("obs")}
|
onMouseDown={() => longPressDown("obs")}
|
||||||
onMouseUp={() => longPressUp("obs")}
|
onMouseUp={() => longPressUp("obs")}
|
||||||
data-bs-toggle="tooltip2" data-bs-placement="top"
|
data-bs-toggle="tooltip2" data-bs-placement="top"
|
||||||
@ -296,12 +251,7 @@ function Menu({menuActions, compUuid}) {
|
|||||||
onClick={() => copyScriptToClipboard()}
|
onClick={() => copyScriptToClipboard()}
|
||||||
data-bs-toggle="tooltip2" data-bs-placement="top"
|
data-bs-toggle="tooltip2" data-bs-placement="top"
|
||||||
data-bs-title={t('ttm.admin.scripte')}/>
|
data-bs-title={t('ttm.admin.scripte')}/>
|
||||||
<FontAwesomeIcon icon={faTableCellsLarge} size="xl"
|
|
||||||
style={{color: showStateWin ? "#00c700" : "#6c757d", cursor: "pointer", marginRight: "0.25em"}}
|
|
||||||
onClick={handleStateWin}
|
|
||||||
data-bs-toggle="tooltip2" data-bs-placement="top" data-bs-title={t('etatDesTablesDeMarque')}/>
|
|
||||||
</>, document.getElementById("actionMenu"))}
|
</>, document.getElementById("actionMenu"))}
|
||||||
{externalWindow.current && createPortal(<StateWindow document={externalWindow.current.document}/>, containerEl.current)}
|
|
||||||
|
|
||||||
<button ref={obsModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#OBSModal" style={{display: 'none'}}>
|
<button ref={obsModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#OBSModal" style={{display: 'none'}}>
|
||||||
Launch OBS Modal
|
Launch OBS Modal
|
||||||
@ -358,147 +308,10 @@ function Menu({menuActions, compUuid}) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button ref={teamCardModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#TeamCardModal"
|
|
||||||
style={{display: 'none'}}>
|
|
||||||
Launch OBS Modal
|
|
||||||
</button>
|
|
||||||
<div className="modal modal-xl fade" id="TeamCardModal" tabIndex="-1" aria-labelledby="TeamCardModalLabel" aria-hidden="true">
|
|
||||||
<div className="modal-dialog">
|
|
||||||
<div className="modal-content">
|
|
||||||
<TeamCardModal/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
function TeamCardModal() {
|
function CategoryHeader({cat, setCatId}) {
|
||||||
const [club, setClub] = useState("")
|
|
||||||
|
|
||||||
const {t} = useTranslation("cm");
|
|
||||||
const {combs} = useCombs()
|
|
||||||
const {sendRequest} = useWS()
|
|
||||||
const {cards_t, cards_v} = useCards()
|
|
||||||
const cardDispatch = useCardsDispatch();
|
|
||||||
const {data} = useRequestWS("getAllForTeamNoDetail", {}, null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!data)
|
|
||||||
return;
|
|
||||||
for (const card of data) {
|
|
||||||
cardDispatch({type: 'SET_TEAM_CARD', payload: card});
|
|
||||||
}
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
let clubList = [];
|
|
||||||
if (combs != null) {
|
|
||||||
clubList = Object.values(combs).map(d => d.club_str).filter((v, i, a) => v !== "" && v !== undefined && a.indexOf(v) === i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAdd = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
toast.promise(sendRequest("applyTeamCards", {
|
|
||||||
teamUuid: Object.values(combs).find(d => d.club_str === club)?.club_uuid,
|
|
||||||
teamName: club,
|
|
||||||
type: "YELLOW"
|
|
||||||
}),
|
|
||||||
getToastMessage("toast.card.team", "cm"))
|
|
||||||
.then(() => {
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const GetCard = ({type}) => {
|
|
||||||
if (!type)
|
|
||||||
return <></>
|
|
||||||
let bg = "";
|
|
||||||
switch (type) {
|
|
||||||
case "YELLOW":
|
|
||||||
bg = " bg-warning";
|
|
||||||
break;
|
|
||||||
case "RED":
|
|
||||||
bg = " bg-danger";
|
|
||||||
break;
|
|
||||||
case "BLACK":
|
|
||||||
bg = " bg-dark text-white";
|
|
||||||
break;
|
|
||||||
case "BLUE":
|
|
||||||
bg = " bg-primary text-white";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return <span className={"badge border border-light p-2" + bg}><span className="visually-hidden">card</span></span>
|
|
||||||
}
|
|
||||||
|
|
||||||
let cards = [...cards_t, ...cards_v].sort((a, b) => new Date(b.date) - new Date(a.date))
|
|
||||||
return <>
|
|
||||||
<div className="modal-header">
|
|
||||||
<h5 className="modal-title">{t('carton')}</h5>
|
|
||||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div className="modal-body">
|
|
||||||
<h5>{t('cartonDéquipe')}</h5>
|
|
||||||
<div className="input-group mb-3">
|
|
||||||
<label htmlFor="inputGroupSelect09" className="input-group-text">{t('club')}</label>
|
|
||||||
<select id="inputGroupSelect09" className="form-select" value={club} onChange={(e) => setClub(e.target.value)}>
|
|
||||||
{clubList.sort((a, b) => a.localeCompare(b)).map((club, index) => (
|
|
||||||
<option key={index} value={club}>{club}</option>))}
|
|
||||||
</select>
|
|
||||||
<button className="btn btn-outline-primary" type="button" onClick={handleAdd}>{t("ajouter")}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5>{t('listeDesCartons')}</h5>
|
|
||||||
<table className="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">{t('date')}</th>
|
|
||||||
<th scope="col">{t('type')}</th>
|
|
||||||
<th scope="col">{t('couleur')}</th>
|
|
||||||
<th scope="col">{t('nom')}</th>
|
|
||||||
<th scope="col"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{cards.map((card, index) => <tr key={index}>
|
|
||||||
<td scope="row">{new Date(card.date).toLocaleString()}</td>
|
|
||||||
{card.teamName ? <>
|
|
||||||
<td>{t('team')}</td>
|
|
||||||
<td><GetCard type={card.type}/></td>
|
|
||||||
<td>{card.teamName}</td>
|
|
||||||
</> : <>
|
|
||||||
<td>{card.teamCard ? "|-> " + t('team'): t('individuelle')} </td>
|
|
||||||
<td><GetCard type={card.type}/></td>
|
|
||||||
<td><CombName combId={card.comb}/></td>
|
|
||||||
</>}
|
|
||||||
|
|
||||||
<td style={{textAlign: "center", cursor: "pointer", color: "#ff1313"}} onClick={_ => {
|
|
||||||
if (confirm("Êtes-vous sûr de vouloir supprimer ce carton ?")) {
|
|
||||||
if (card.teamName) {
|
|
||||||
toast.promise(sendRequest("removeTeamCards", {
|
|
||||||
teamUuid: card.teamUuid, teamName: card.teamName, type: card.type
|
|
||||||
}),
|
|
||||||
getToastMessage("toast.card.team", "cm"))
|
|
||||||
.then(() => {
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
sendRequest('sendCardRm', {matchId: card.match, combId: card.comb, type: card.type})
|
|
||||||
.then(() => toast.success(t('cardRemoved')))
|
|
||||||
.catch(err => toast.error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}><FontAwesomeIcon icon={faTrash}/></td>
|
|
||||||
</tr>)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div className="modal-footer">
|
|
||||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function CategoryHeader({
|
|
||||||
cat, setCatId
|
|
||||||
}) {
|
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const bthRef = useRef();
|
const bthRef = useRef();
|
||||||
const confirmRef = useRef();
|
const confirmRef = useRef();
|
||||||
@ -576,7 +389,7 @@ function CategoryHeader({
|
|||||||
<button className="btn btn-primary float-end" onClick={() => {
|
<button className="btn btn-primary float-end" onClick={() => {
|
||||||
setModal(cat);
|
setModal(cat);
|
||||||
bthRef.current.click();
|
bthRef.current.click();
|
||||||
}} disabled={cat === null}>{t('modifier')}
|
}} disabled={cat === null}>Modifier
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -687,10 +500,7 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
|
|||||||
newTrees.push(trees2.at(i));
|
newTrees.push(trees2.at(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.promise(sendRequest('updateTrees', {
|
toast.promise(sendRequest('updateTrees', {categoryId: state.id, trees: newTrees}), getToastMessage("toast.updateTrees", "cm")
|
||||||
categoryId: state.id,
|
|
||||||
trees: newTrees
|
|
||||||
}), getToastMessage("toast.updateTrees", "cm")
|
|
||||||
).then(__ => {
|
).then(__ => {
|
||||||
toast.promise(sendRequest('updateCategory', newData), getToastMessage("toast.updateCategory", "cm"))
|
toast.promise(sendRequest('updateCategory', newData), getToastMessage("toast.updateCategory", "cm"))
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import React, {useEffect, useRef, useState} from "react";
|
|||||||
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
|
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
|
||||||
import {timePrint} from "../../../utils/Tools.js";
|
import {timePrint} from "../../../utils/Tools.js";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useWS} from "../../../hooks/useWS.jsx";
|
|
||||||
|
|
||||||
export function ChronoPanel() {
|
export function ChronoPanel() {
|
||||||
const [config, setConfig] = useState({
|
const [config, setConfig] = useState({
|
||||||
@ -11,7 +10,6 @@ export function ChronoPanel() {
|
|||||||
})
|
})
|
||||||
const [chrono, setChrono] = useState({time: 0, startTime: 0})
|
const [chrono, setChrono] = useState({time: 0, startTime: 0})
|
||||||
const chronoText = useRef(null)
|
const chronoText = useRef(null)
|
||||||
const [chronoState, setChronoState] = useState(0)
|
|
||||||
const state = useRef({chronoState: 0, countBlink: 20, lastColor: "#000000", lastTimeStr: "00:00"})
|
const state = useRef({chronoState: 0, countBlink: 20, lastColor: "#000000", lastTimeStr: "00:00"})
|
||||||
const publicAffDispatch = usePubAffDispatch();
|
const publicAffDispatch = usePubAffDispatch();
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
@ -61,15 +59,12 @@ export function ChronoPanel() {
|
|||||||
|
|
||||||
if (state_.chronoState === 0 && isRunning()) {
|
if (state_.chronoState === 0 && isRunning()) {
|
||||||
state_.chronoState = 1
|
state_.chronoState = 1
|
||||||
setChronoState(1)
|
|
||||||
} else if (state_.chronoState === 1 && getTime() >= config.time) {
|
} else if (state_.chronoState === 1 && getTime() >= config.time) {
|
||||||
setChrono(prev => ({...prev, time: 0, startTime: Date.now()}))
|
setChrono(prev => ({...prev, time: 0, startTime: Date.now()}))
|
||||||
state_.chronoState = 2
|
state_.chronoState = 2
|
||||||
setChronoState(2)
|
|
||||||
} else if (state_.chronoState === 2 && getTime() >= config.pause) {
|
} else if (state_.chronoState === 2 && getTime() >= config.pause) {
|
||||||
setChrono(prev => ({...prev, time: 0, startTime: Date.now()}))
|
setChrono(prev => ({...prev, time: 0, startTime: Date.now()}))
|
||||||
state_.chronoState = 1
|
state_.chronoState = 1
|
||||||
setChronoState(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRunning()) {
|
if (isRunning()) {
|
||||||
@ -122,7 +117,6 @@ export function ChronoPanel() {
|
|||||||
<button className="btn btn-danger col" onClick={__ => {
|
<button className="btn btn-danger col" onClick={__ => {
|
||||||
setChrono(prev => ({...prev, time: 0, startTime: 0}))
|
setChrono(prev => ({...prev, time: 0, startTime: 0}))
|
||||||
state.current.chronoState = 0
|
state.current.chronoState = 0
|
||||||
setChronoState(0)
|
|
||||||
}}>{t('réinitialiser')}
|
}}>{t('réinitialiser')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -187,19 +181,5 @@ export function ChronoPanel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SendChrono chrono={chrono} config={config} chronoState={chronoState}/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function SendChrono({chrono, config, chronoState}) {
|
|
||||||
const {sendNotify, setState} = useWS();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setState({chronoState: {...chrono, configTime: config.time, configPause: config.pause, state: chronoState}});
|
|
||||||
sendNotify("sendCurentChrono", {...chrono, configTime: config.time, configPause: config.pause, state: chronoState});
|
|
||||||
}, [chrono]);
|
|
||||||
|
|
||||||
return <>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, {useEffect, useReducer, useRef, useState} from "react";
|
import React, {useEffect, useRef, useState, useReducer} from "react";
|
||||||
import {CombName, useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
|
import {CombName, useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
|
||||||
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
|
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
|
||||||
import {from_sendTree, TreeNode} from "../../../utils/TreeUtils.js";
|
import {from_sendTree, TreeNode} from "../../../utils/TreeUtils.js";
|
||||||
@ -6,12 +6,13 @@ 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 {getToastMessage, scorePrint, win} from "../../../utils/Tools.js";
|
||||||
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
|
import {faCircleQuestion} from "@fortawesome/free-regular-svg-icons";
|
||||||
|
import {toast} from "react-toastify";
|
||||||
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 {ScorePanel} from "./ScoreAndCardPanel.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"
|
||||||
@ -73,7 +74,6 @@ function CMTMatchPanel({catId, cat, menuActions}) {
|
|||||||
const [trees, setTrees] = useState([]);
|
const [trees, setTrees] = useState([]);
|
||||||
const [matches, reducer] = useReducer(MarchReducer, []);
|
const [matches, reducer] = useReducer(MarchReducer, []);
|
||||||
const combDispatch = useCombsDispatch();
|
const combDispatch = useCombsDispatch();
|
||||||
const cardDispatch = useCardsDispatch();
|
|
||||||
|
|
||||||
function readAndConvertMatch(matches, data, combsToAdd) {
|
function readAndConvertMatch(matches, data, combsToAdd) {
|
||||||
matches.push({...data, c1: data.c1?.id, c2: data.c2?.id})
|
matches.push({...data, c1: data.c1?.id, c2: data.c2?.id})
|
||||||
@ -91,8 +91,6 @@ function CMTMatchPanel({catId, cat, menuActions}) {
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
setTrees(data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)))
|
setTrees(data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)))
|
||||||
|
|
||||||
cardDispatch({type: 'SET_ALL', payload: data.cards});
|
|
||||||
|
|
||||||
let matches2 = [];
|
let matches2 = [];
|
||||||
let combsToAdd = [];
|
let combsToAdd = [];
|
||||||
data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
||||||
@ -130,15 +128,21 @@ function CMTMatchPanel({catId, cat, menuActions}) {
|
|||||||
reducer({type: 'REMOVE', payload: data})
|
reducer({type: 'REMOVE', payload: data})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sendCardboard = ({data}) => {
|
||||||
|
reducer({type: 'UPDATE_CARDBOARD', payload: {...data}})
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({type: 'addListener', payload: {callback: treeListener, code: 'sendTreeCategory'}})
|
dispatch({type: 'addListener', payload: {callback: treeListener, code: 'sendTreeCategory'}})
|
||||||
dispatch({type: 'addListener', payload: {callback: matchListener, code: 'sendMatch'}})
|
dispatch({type: 'addListener', payload: {callback: matchListener, code: 'sendMatch'}})
|
||||||
dispatch({type: 'addListener', payload: {callback: matchOrder, code: 'sendMatchOrder'}})
|
dispatch({type: 'addListener', payload: {callback: matchOrder, code: 'sendMatchOrder'}})
|
||||||
dispatch({type: 'addListener', payload: {callback: deleteMatch, code: 'sendDeleteMatch'}})
|
dispatch({type: 'addListener', payload: {callback: deleteMatch, code: 'sendDeleteMatch'}})
|
||||||
|
dispatch({type: 'addListener', payload: {callback: sendCardboard, code: 'sendCardboard'}})
|
||||||
return () => {
|
return () => {
|
||||||
dispatch({type: 'removeListener', payload: treeListener})
|
dispatch({type: 'removeListener', payload: treeListener})
|
||||||
dispatch({type: 'removeListener', payload: matchListener})
|
dispatch({type: 'removeListener', payload: matchListener})
|
||||||
dispatch({type: 'removeListener', payload: matchOrder})
|
dispatch({type: 'removeListener', payload: matchOrder})
|
||||||
dispatch({type: 'removeListener', payload: deleteMatch})
|
dispatch({type: 'removeListener', payload: deleteMatch})
|
||||||
|
dispatch({type: 'removeListener', payload: sendCardboard})
|
||||||
}
|
}
|
||||||
}, [catId]);
|
}, [catId]);
|
||||||
|
|
||||||
@ -191,13 +195,11 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
const [lice, setLice] = useState(localStorage.getItem("cm_lice") || "1")
|
const [lice, setLice] = useState(localStorage.getItem("cm_lice") || "1")
|
||||||
const publicAffDispatch = usePubAffDispatch();
|
const publicAffDispatch = usePubAffDispatch();
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
const {cards_v, getHeightCardForCombInMatch} = useCards();
|
|
||||||
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 = 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: win(m.scores)}))
|
||||||
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
|
const firstIndex = marches2.findLastIndex(m => m.poule === '-') + 1;
|
||||||
|
|
||||||
const isActiveMatch = (index) => {
|
const isActiveMatch = (index) => {
|
||||||
@ -222,6 +224,10 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [match]);
|
}, [match]);
|
||||||
|
//useEffect(() => {
|
||||||
|
// if (activeMatch !== null)
|
||||||
|
// setActiveMatch(null);
|
||||||
|
//}, [cat])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (match && match.poule !== lice)
|
if (match && match.poule !== lice)
|
||||||
@ -236,38 +242,6 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
|
|
||||||
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id);
|
setActiveMatch(marches2.find((m, index) => !m.end && isActiveMatch(index))?.id);
|
||||||
}, [matches])
|
}, [matches])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setState({selectedMatch: activeMatch});
|
|
||||||
sendNotify("sendSelectMatch", activeMatch);
|
|
||||||
}, [activeMatch]);
|
|
||||||
|
|
||||||
|
|
||||||
const GetCard = ({combId, match, cat}) => {
|
|
||||||
const c = getHeightCardForCombInMatch(combId, match)
|
|
||||||
if (!c)
|
|
||||||
return <></>
|
|
||||||
let bg = "";
|
|
||||||
switch (c.type) {
|
|
||||||
case "YELLOW":
|
|
||||||
bg = " bg-warning";
|
|
||||||
break;
|
|
||||||
case "RED":
|
|
||||||
bg = " bg-danger";
|
|
||||||
break;
|
|
||||||
case "BLACK":
|
|
||||||
bg = " bg-dark text-white";
|
|
||||||
break;
|
|
||||||
case "BLUE":
|
|
||||||
bg = " bg-primary text-white";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return <span
|
|
||||||
className={"position-absolute top-0 start-100 translate-middle-y badge border border-light p-2" + bg +
|
|
||||||
(c.match === match.id ? " rounded-circle" : (hasEffectCard(c, match.id, cat.id) ? "" : " bg-opacity-50"))}>
|
|
||||||
<span className="visually-hidden">card</span></span>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
{liceName.length > 1 &&
|
{liceName.length > 1 &&
|
||||||
<div className="input-group" style={{maxWidth: "15em", marginTop: "0.5em"}}>
|
<div className="input-group" style={{maxWidth: "15em", marginTop: "0.5em"}}>
|
||||||
@ -299,7 +273,7 @@ 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 === activeMatch ? "table-info" : (isActiveMatch(index) ? "" : "table-warning")}
|
||||||
onClick={() => setActiveMatch(m.id)}>
|
onClick={() => setActiveMatch(m.id)}>
|
||||||
<td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
|
<td style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>
|
||||||
{liceName[(index - firstIndex) % liceName.length]}</td>
|
{liceName[(index - firstIndex) % liceName.length]}</td>
|
||||||
@ -308,11 +282,9 @@ function MatchList({matches, cat, menuActions}) {
|
|||||||
{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>
|
||||||
<td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}>
|
<td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}>
|
||||||
<small className="position-relative"><CombName combId={m.c1}/>
|
<small><CombName combId={m.c1}/></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"}}>
|
||||||
<small className="position-relative"><CombName combId={m.c2}/>
|
<small><CombName combId={m.c2}/></small></td>
|
||||||
<GetCard match={m} combId={m.c2} cat={cat}/></small></td>
|
|
||||||
<td style={{textAlign: "left", paddingLeft: "0"}}>{m.end && m.win < 0 && <CupImg/>}</td>
|
<td style={{textAlign: "left", paddingLeft: "0"}}>{m.end && m.win < 0 && <CupImg/>}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
@ -330,8 +302,6 @@ function BuildTree({treeData, matches, menuActions}) {
|
|||||||
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 {sendNotify, setState} = useWS();
|
|
||||||
|
|
||||||
const match = matches.find(m => m.id === currentMatch?.matchSelect)
|
const match = matches.find(m => m.id === currentMatch?.matchSelect)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -358,20 +328,9 @@ function BuildTree({treeData, matches, menuActions}) {
|
|||||||
const c1 = getComb(matchData?.c1)
|
const c1 = getComb(matchData?.c1)
|
||||||
const c2 = getComb(matchData?.c2)
|
const c2 = getComb(matchData?.c2)
|
||||||
|
|
||||||
const scores2 = []
|
|
||||||
for (const score of matchData?.scores) {
|
|
||||||
scores2.push({
|
|
||||||
...score,
|
|
||||||
s1: virtualScore(matchData?.c1, score, matchData, cards_v),
|
|
||||||
s2: virtualScore(matchData?.c2, score, matchData, cards_v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let node = new TreeNode({
|
let node = new TreeNode({
|
||||||
...matchData,
|
...matchData,
|
||||||
...win_end(matchData, cards_v),
|
|
||||||
scores: scores2,
|
|
||||||
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
|
||||||
})
|
})
|
||||||
@ -393,8 +352,6 @@ function BuildTree({treeData, matches, menuActions}) {
|
|||||||
|
|
||||||
const onMatchClick = (rect, matchId, __) => {
|
const onMatchClick = (rect, matchId, __) => {
|
||||||
setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(trees.reverse())});
|
setCurrentMatch({matchSelect: matchId, matchNext: new TreeNode(matchId).nextMatchTree(trees.reverse())});
|
||||||
setState({selectedMatch: matchId});
|
|
||||||
sendNotify("sendSelectMatch", matchId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClickVoid = () => {
|
const onClickVoid = () => {
|
||||||
@ -405,7 +362,7 @@ function BuildTree({treeData, matches, menuActions}) {
|
|||||||
<div className="overflow-y-auto" style={{maxHeight: "50vh"}}>
|
<div className="overflow-y-auto" style={{maxHeight: "50vh"}}>
|
||||||
<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}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -414,3 +371,301 @@ function BuildTree({treeData, matches, menuActions}) {
|
|||||||
menuActions={menuActions}/></LoadingProvider>}
|
menuActions={menuActions}/></LoadingProvider>}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ScorePanel({matchId, matchs, match, menuActions}) {
|
||||||
|
const onClickVoid = useRef(() => {
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div className="row" onClick={onClickVoid.current}>
|
||||||
|
<ScorePanel_ matchId={matchId} matchs={matchs} match={match} menuActions={menuActions} onClickVoid_={onClickVoid}/>
|
||||||
|
<CardPanel matchId={matchId} match={match}/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_}) {
|
||||||
|
const {sendRequest} = useWS()
|
||||||
|
const setLoading = useLoadingSwitcher()
|
||||||
|
const {t} = useTranslation("cm");
|
||||||
|
|
||||||
|
const [end, setEnd] = useState(match?.end || false)
|
||||||
|
const [scoreIn, setScoreIn] = useState("")
|
||||||
|
const inputRef = useRef(null)
|
||||||
|
const tableRef = useRef(null)
|
||||||
|
const scoreRef = useRef([])
|
||||||
|
const lastScoreClick = useRef(null)
|
||||||
|
const scoreInRef = useRef(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
scoreInRef.current = scoreIn;
|
||||||
|
}, [scoreIn]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
menuActions.current.saveScore = (scoreRed, scoreBlue) => {
|
||||||
|
const maxRound = (Math.max(...match.scores.map(s => s.n_round), -1) + 1) || 0;
|
||||||
|
const newScore = {n_round: maxRound, s1: scoreRed, s2: scoreBlue};
|
||||||
|
toast.promise(sendRequest('updateMatchScore', {matchId: matchId, ...newScore}), getToastMessage("toast.updateMatchScore", "cm"));
|
||||||
|
}
|
||||||
|
return () => menuActions.current.saveScore = undefined;
|
||||||
|
}, [matchId])
|
||||||
|
|
||||||
|
const handleScoreClick = (e, round, comb) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const tableRect = tableRef.current.getBoundingClientRect();
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
|
|
||||||
|
updateScore();
|
||||||
|
|
||||||
|
const sel = inputRef.current;
|
||||||
|
sel.style.top = (rect.y - tableRect.y) + "px";
|
||||||
|
sel.style.left = (rect.x - tableRect.x) + "px";
|
||||||
|
sel.style.width = rect.width + "px";
|
||||||
|
sel.style.height = rect.height + "px";
|
||||||
|
sel.style.display = "block";
|
||||||
|
|
||||||
|
if (round === -1) {
|
||||||
|
const maxRound = (Math.max(...match.scores.map(s => s.n_round), -1) + 1) || 0;
|
||||||
|
setScoreIn("");
|
||||||
|
console.log("Setting for new round", maxRound);
|
||||||
|
lastScoreClick.current = {matchId: matchId, round: maxRound, comb};
|
||||||
|
} else {
|
||||||
|
const score = match.scores.find(s => s.n_round === round);
|
||||||
|
setScoreIn((comb === 1 ? score?.s1 : score?.s2) || "");
|
||||||
|
lastScoreClick.current = {matchId: matchId, round, comb};
|
||||||
|
setTimeout(() => inputRef.current.select(), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateScore = () => {
|
||||||
|
if (lastScoreClick.current !== null) {
|
||||||
|
const {matchId, round, comb} = lastScoreClick.current;
|
||||||
|
lastScoreClick.current = null;
|
||||||
|
|
||||||
|
const scoreIn_ = String(scoreInRef.current).trim() === "" ? -1000 : Number(scoreInRef.current);
|
||||||
|
|
||||||
|
const score = matchs.find(m => m.id === matchId).scores.find(s => s.n_round === round);
|
||||||
|
|
||||||
|
let newScore;
|
||||||
|
if (score) {
|
||||||
|
if (comb === 1)
|
||||||
|
newScore = {...score, s1: scoreIn_};
|
||||||
|
else
|
||||||
|
newScore = {...score, s2: scoreIn_};
|
||||||
|
|
||||||
|
if (newScore.s1 === score?.s1 && newScore.s2 === score?.s2)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
newScore = {n_round: round, s1: (comb === 1 ? scoreIn_ : -1000), s2: (comb === 2 ? scoreIn_ : -1000)};
|
||||||
|
if (newScore.s1 === -1000 && newScore.s2 === -1000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(1)
|
||||||
|
sendRequest('updateMatchScore', {matchId: matchId, ...newScore})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickVoid = () => {
|
||||||
|
updateScore();
|
||||||
|
|
||||||
|
const sel = inputRef.current;
|
||||||
|
sel.style.display = "none";
|
||||||
|
lastScoreClick.current = null;
|
||||||
|
}
|
||||||
|
onClickVoid_.current = onClickVoid;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!match || match?.end === end)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
if (win(match?.scores) === 0 && match.categorie_ord === -42) {
|
||||||
|
toast.error(t('score.err1'));
|
||||||
|
setEnd(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(1)
|
||||||
|
sendRequest('updateMatchEnd', {matchId: matchId, end})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(0)
|
||||||
|
})
|
||||||
|
}, [end]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onClickVoid()
|
||||||
|
}, [matchId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (match?.scores)
|
||||||
|
scoreRef.current = scoreRef.current.slice(0, match.scores.length);
|
||||||
|
}, [match?.scores]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!match)
|
||||||
|
return;
|
||||||
|
setEnd(match.end);
|
||||||
|
}, [match]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (inputRef.current && !inputRef.current.contains(event.target)) {
|
||||||
|
onClickVoid();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||||
|
const o = [...tooltipTriggerList]
|
||||||
|
o.map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||||
|
|
||||||
|
const tt = t('score.spe')
|
||||||
|
|
||||||
|
const maxRound = (match?.scores) ? (Math.max(...match.scores.map(s => s.n_round), -1) + 1) : 0;
|
||||||
|
return <div ref={tableRef} className="col" style={{position: "relative"}}>
|
||||||
|
<h6>{t('scores')} <FontAwesomeIcon icon={faCircleQuestion} role="button" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-title={tt}
|
||||||
|
data-bs-html="true"/></h6>
|
||||||
|
<table className="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style={{textAlign: "center"}} scope="col">{t('manche')}</th>
|
||||||
|
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">{t('rouge')}</th>
|
||||||
|
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">{t('bleu')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="table-group-divider">
|
||||||
|
{match?.scores && match.scores.sort((a, b) => a.n_round - b.n_round).map(score => (
|
||||||
|
<tr key={score.n_round}>
|
||||||
|
<th style={{textAlign: "center"}}>{score.n_round + 1}</th>
|
||||||
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2] = e}
|
||||||
|
onClick={e => handleScoreClick(e, score.n_round, 1)}>{scorePrint(score.s1)}</td>
|
||||||
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2 + 1] = e}
|
||||||
|
onClick={e => handleScoreClick(e, score.n_round, 2)}>{scorePrint(score.s2)}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
<tr>
|
||||||
|
<th style={{textAlign: "center"}}></th>
|
||||||
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2] = e} onClick={e => handleScoreClick(e, -1, 1)}>-</td>
|
||||||
|
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2 + 1] = e} onClick={e => handleScoreClick(e, -1, 2)}>-
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div style={{textAlign: "right"}}>
|
||||||
|
<div className="form-check" style={{display: "inline-block"}}>
|
||||||
|
<input className="form-check-input" type="checkbox" id="checkboxEnd" name="checkboxEnd" checked={end}
|
||||||
|
onChange={e => setEnd(e.target.checked)}/>
|
||||||
|
<label className="form-check-label" htmlFor="checkboxEnd">{t('terminé')}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input ref={inputRef} type="number" className="form-control" style={{position: "absolute", top: 0, left: 0, display: "none"}} min="-999"
|
||||||
|
max="999"
|
||||||
|
value={scoreIn} onChange={e => setScoreIn(e.target.value)}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
onKeyDown={e => {
|
||||||
|
if (e.key === "Tab") {
|
||||||
|
if (lastScoreClick.current !== null) {
|
||||||
|
const {round, comb} = lastScoreClick.current;
|
||||||
|
const nextIndex = (round * 2 + (comb - 1)) + (e.shiftKey ? -1 : 1);
|
||||||
|
if (nextIndex >= 0 && nextIndex < scoreRef.current.length) {
|
||||||
|
e.preventDefault();
|
||||||
|
scoreRef.current[nextIndex].click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
onClickVoid();
|
||||||
|
}
|
||||||
|
}}/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardPanel({matchId, match}) {
|
||||||
|
const {sendRequest, dispatch} = useWS();
|
||||||
|
const setLoading = useLoadingSwitcher()
|
||||||
|
|
||||||
|
const {data, refresh} = useRequestWS('getCardboardWithoutThis', matchId, setLoading);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refresh('getCardboardWithoutThis', matchId);
|
||||||
|
|
||||||
|
const sendCardboard = ({data}) => {
|
||||||
|
if (data.comb_id === match.c1 || data.comb_id === match.c2) {
|
||||||
|
refresh('getCardboardWithoutThis', matchId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({type: 'addListener', payload: {callback: sendCardboard, code: 'sendCardboard'}})
|
||||||
|
return () => dispatch({type: 'removeListener', payload: sendCardboard})
|
||||||
|
}, [matchId])
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return <div className="col"></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const c1Cards = match.cardboard?.find(c => c.comb_id === match.c1) || {red: 0, yellow: 0};
|
||||||
|
const c2Cards = match.cardboard?.find(c => c.comb_id === match.c2) || {red: 0, yellow: 0};
|
||||||
|
|
||||||
|
const handleCard = (combId, yellow, red) => {
|
||||||
|
if (combId === match.c1) {
|
||||||
|
if (c1Cards.red + red < 0 || c1Cards.yellow + yellow < 0)
|
||||||
|
return;
|
||||||
|
} else if (combId === match.c2) {
|
||||||
|
if (c2Cards.red + red < 0 || c2Cards.yellow + yellow < 0)
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(1)
|
||||||
|
sendRequest('sendCardboardChange', {matchId, combId, yellow, red})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="col">
|
||||||
|
<h6>Carton</h6>
|
||||||
|
<div className="bg-danger-subtle text-danger-emphasis" style={{padding: ".25em", borderRadius: "1em 1em 0 0"}}>
|
||||||
|
<div>Competition: <span className="badge text-bg-danger">{(data?.c1_red || 0) + c1Cards.red}</span> <span
|
||||||
|
className="badge text-bg-warning">{(data?.c1_yellow || 0) + c1Cards.yellow}</span></div>
|
||||||
|
<div className="d-flex justify-content-center align-items-center" style={{margin: ".25em"}}>
|
||||||
|
Match:
|
||||||
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
||||||
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c1, 0, +1)}>+</button>
|
||||||
|
<span className="badge text-bg-danger">{c1Cards.red}</span>
|
||||||
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c1, 0, -1)}>-</button>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
||||||
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c1, +1, 0)}>+</button>
|
||||||
|
<span className="badge text-bg-warning">{c1Cards.yellow}</span>
|
||||||
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c1, -1, 0)}>-</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-info-subtle text-info-emphasis" style={{padding: ".25em", borderRadius: "0 0 1em 1em"}}>
|
||||||
|
<div>Competition: <span className="badge text-bg-danger">{(data?.c2_red || 0) + c2Cards.red}</span> <span
|
||||||
|
className="badge text-bg-warning">{(data?.c2_yellow || 0) + c2Cards.yellow}</span></div>
|
||||||
|
<div className="d-flex justify-content-center align-items-center" style={{margin: ".25em"}}>
|
||||||
|
Match:
|
||||||
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
||||||
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c2, 0, +1)}>+</button>
|
||||||
|
<span className="badge text-bg-danger">{c2Cards.red}</span>
|
||||||
|
<button className="col btn btn-xs btn-danger" onClick={__ => handleCard(match.c2, 0, -1)}>-</button>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex flex-column" style={{marginLeft: ".25em"}}>
|
||||||
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c2, +1, 0)}>+</button>
|
||||||
|
<span className="badge text-bg-warning">{c2Cards.yellow}</span>
|
||||||
|
<button className="col btn btn-xs btn-warning" onClick={__ => handleCard(match.c2, -1, 0)}>-</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faChevronDown, faChevronUp} from "@fortawesome/free-solid-svg-icons";
|
import {faChevronDown, faChevronUp} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
|
import {usePubAffDispatch} from "../../../hooks/useExternalWindow.jsx";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useWS} from "../../../hooks/useWS.jsx";
|
|
||||||
|
|
||||||
export function PointPanel({menuActions}) {
|
export function PointPanel({menuActions}) {
|
||||||
const [revers, setRevers] = useState(false)
|
const [revers, setRevers] = useState(false)
|
||||||
@ -50,18 +49,5 @@ export function PointPanel({menuActions}) {
|
|||||||
<button className="btn btn-danger" onClick={handleReset}>{t('réinitialiser')}</button>
|
<button className="btn btn-danger" onClick={handleReset}>{t('réinitialiser')}</button>
|
||||||
<button className="btn btn-success" onClick={handleSave}>{t('sauvegarder')}</button>
|
<button className="btn btn-success" onClick={handleSave}>{t('sauvegarder')}</button>
|
||||||
</div>
|
</div>
|
||||||
<SendScore scoreRouge={scoreRouge} scoreBleu={scoreBleu}/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function SendScore({scoreRouge, scoreBleu}) {
|
|
||||||
const {sendNotify, setState} = useWS();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setState({scoreState: {scoreRouge, scoreBleu}});
|
|
||||||
sendNotify("sendCurrentScore", {scoreRouge, scoreBleu});
|
|
||||||
}, [scoreRouge, scoreBleu]);
|
|
||||||
|
|
||||||
return <>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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} 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";
|
||||||
@ -13,10 +13,8 @@ import {CategorieSelect} from "./CMTMatchPanel.jsx";
|
|||||||
import {PointPanel} from "./CMTPoint.jsx";
|
import {PointPanel} from "./CMTPoint.jsx";
|
||||||
import {importOBSConfiguration, OBSProvider, useOBS} from "../../../hooks/useOBS.jsx";
|
import {importOBSConfiguration, OBSProvider, useOBS} from "../../../hooks/useOBS.jsx";
|
||||||
import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts";
|
import {SimpleIconsOBS} from "../../../assets/SimpleIconsOBS.ts";
|
||||||
import {Flip, toast} from "react-toastify";
|
import {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";
|
|
||||||
|
|
||||||
export function CMTable() {
|
export function CMTable() {
|
||||||
const combDispatch = useCombsDispatch()
|
const combDispatch = useCombsDispatch()
|
||||||
@ -61,7 +59,6 @@ export function CMTable() {
|
|||||||
</div>
|
</div>
|
||||||
<Menu menuActions={menuActions}/>
|
<Menu menuActions={menuActions}/>
|
||||||
<ObsAutoSyncWhitPubAff/>
|
<ObsAutoSyncWhitPubAff/>
|
||||||
<SendCatId catId={catId}/>
|
|
||||||
</div>
|
</div>
|
||||||
</PubAffProvider>
|
</PubAffProvider>
|
||||||
</OBSProvider>
|
</OBSProvider>
|
||||||
@ -76,11 +73,9 @@ function Menu({menuActions}) {
|
|||||||
const publicAffDispatch = usePubAffDispatch()
|
const publicAffDispatch = usePubAffDispatch()
|
||||||
const [showPubAff, setShowPubAff] = useState(false)
|
const [showPubAff, setShowPubAff] = useState(false)
|
||||||
const [showScore, setShowScore] = useState(true)
|
const [showScore, setShowScore] = useState(true)
|
||||||
const [zone, setZone] = useState(sessionStorage.getItem("liceName") || "???")
|
|
||||||
const {connected, connect, disconnect} = useOBS();
|
const {connected, connect, disconnect} = useOBS();
|
||||||
const longPress = useRef({time: null, timer: null, button: null});
|
const longPress = useRef({time: null, timer: null, button: null});
|
||||||
const obsModal = useRef(null);
|
const obsModal = useRef(null);
|
||||||
const teamCardModal = useRef(null);
|
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
|
|
||||||
const externalWindow = useRef(null)
|
const externalWindow = useRef(null)
|
||||||
@ -129,7 +124,7 @@ function Menu({menuActions}) {
|
|||||||
|
|
||||||
const longTimeAction = (button) => {
|
const longTimeAction = (button) => {
|
||||||
if (button === "obs") {
|
if (button === "obs") {
|
||||||
// obsModal.current.click();
|
obsModal.current.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,19 +169,12 @@ function Menu({menuActions}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLiceSubmit = (e) => {
|
const handleOBSSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = e.target;
|
const form = e.target;
|
||||||
const prefix = form[0].value;
|
const prefix = form[0].value;
|
||||||
|
|
||||||
if (prefix === "") {
|
sessionStorage.setItem("obs_prefix", prefix);
|
||||||
sessionStorage.removeItem("liceName");
|
|
||||||
setZone("???");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionStorage.setItem("liceName", prefix);
|
|
||||||
setZone(prefix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!e)
|
if (!e)
|
||||||
@ -195,11 +183,6 @@ function Menu({menuActions}) {
|
|||||||
{createPortal(
|
{createPortal(
|
||||||
<>
|
<>
|
||||||
<div className="vr" style={{margin: "0 0.5em", height: "100%"}}></div>
|
<div className="vr" style={{margin: "0 0.5em", height: "100%"}}></div>
|
||||||
<span onClick={() => obsModal.current.click()} style={{cursor: "pointer"}}>Zone {zone}</span>
|
|
||||||
<div className="vr" style={{margin: "0 0.5em", height: "100%"}}></div>
|
|
||||||
<FontAwesomeIcon icon={faFile} size="xl" style={{color: "#6c757d", cursor: "pointer", marginRight: "0.25em"}}
|
|
||||||
onClick={() => teamCardModal.current.click()} data-bs-toggle="tooltip2" data-bs-placement="top"
|
|
||||||
data-bs-title={t('cartonDéquipe')}/>
|
|
||||||
<FontAwesomeIcon icon={faArrowRightArrowLeft} size="xl" style={{color: "#6c757d", cursor: "pointer", marginRight: "0.25em"}}
|
<FontAwesomeIcon icon={faArrowRightArrowLeft} size="xl" style={{color: "#6c757d", cursor: "pointer", marginRight: "0.25em"}}
|
||||||
onClick={handleSwitchScore} data-bs-toggle="tooltip2" data-bs-placement="top"
|
onClick={handleSwitchScore} data-bs-toggle="tooltip2" data-bs-placement="top"
|
||||||
data-bs-title={t('ttm.table.inverserLaPosition')}/>
|
data-bs-title={t('ttm.table.inverserLaPosition')}/>
|
||||||
@ -220,23 +203,23 @@ function Menu({menuActions}) {
|
|||||||
</>, document.getElementById("actionMenu"))}
|
</>, document.getElementById("actionMenu"))}
|
||||||
{externalWindow.current && createPortal(<PubAffWindow document={externalWindow.current.document}/>, containerEl.current)}
|
{externalWindow.current && createPortal(<PubAffWindow document={externalWindow.current.document}/>, containerEl.current)}
|
||||||
|
|
||||||
<button ref={obsModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#LiceNameModal"
|
<button ref={obsModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#OBSModal" style={{display: 'none'}}>
|
||||||
style={{display: 'none'}}>
|
Launch OBS Modal
|
||||||
Launch Lice Name Modal
|
|
||||||
</button>
|
</button>
|
||||||
<div className="modal fade" id="LiceNameModal" tabIndex="-1" aria-labelledby="LiceNameModalLabel" aria-hidden="true">
|
<div className="modal fade" id="OBSModal" tabIndex="-1" aria-labelledby="OBSModalLabel" aria-hidden="true">
|
||||||
<div className="modal-dialog">
|
<div className="modal-dialog">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
<div className="modal-header">
|
<div className="modal-header">
|
||||||
<h5 className="modal-title">{t('configurationDuNomDeLaZone')}</h5>
|
<h5 className="modal-title">Configuration OBS</h5>
|
||||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={handleLiceSubmit}>
|
<form onSubmit={handleOBSSubmit}>
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
<div className="input-group mb-3">
|
<div className="input-group mb-3">
|
||||||
<span className="input-group-text">{t('nomDeLaZone')}</span>
|
<span className="input-group-text">{t('obs.préfixDesSources')}</span>
|
||||||
<input type="text" className="form-control" placeholder="1" aria-label="" size={1} minLength={0} maxLength={1}
|
<span className="input-group-text">sub</span>
|
||||||
defaultValue={sessionStorage.getItem("liceName") || "1"}/>
|
<input type="text" className="form-control" placeholder="1" aria-label="" size={1} minLength={1} maxLength={1}
|
||||||
|
defaultValue={localStorage.getItem("obs_prefix") || "1"} required/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-footer">
|
<div className="modal-footer">
|
||||||
@ -247,176 +230,6 @@ function Menu({menuActions}) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button ref={teamCardModal} type="button" className="btn btn-link" data-bs-toggle="modal" data-bs-target="#TeamCardModal"
|
|
||||||
style={{display: 'none'}}>
|
|
||||||
Launch OBS Modal
|
|
||||||
</button>s
|
|
||||||
<div className="modal fade" id="TeamCardModal" tabIndex="-1" aria-labelledby="TeamCardModalLabel" aria-hidden="true">
|
|
||||||
<div className="modal-dialog">
|
|
||||||
<div className="modal-content">
|
|
||||||
<TeamCardModal/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<SendLiceName name={zone}/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function TeamCardModal() {
|
|
||||||
const [club, setClub] = useState("")
|
|
||||||
|
|
||||||
const {t} = useTranslation("cm");
|
|
||||||
const {combs} = useCombs()
|
|
||||||
const {sendRequest} = useWS()
|
|
||||||
|
|
||||||
let clubList = [];
|
|
||||||
if (combs != null) {
|
|
||||||
clubList = Object.values(combs).map(d => d.club_str).filter((v, i, a) => v !== "" && v !== undefined && a.indexOf(v) === i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAdd = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
toast.promise(sendRequest("applyTeamCards", {
|
|
||||||
teamUuid: Object.values(combs).find(d => d.club_str === club)?.club_uuid,
|
|
||||||
teamName: club,
|
|
||||||
type: "YELLOW"
|
|
||||||
}),
|
|
||||||
getToastMessage("toast.card.team", "cm"))
|
|
||||||
.then(() => {
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<div className="modal-header">
|
|
||||||
<h5 className="modal-title">{t('cartonDéquipe')}</h5>
|
|
||||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div className="modal-body">
|
|
||||||
<div className="input-group mb-3">
|
|
||||||
<label htmlFor="inputGroupSelect09" className="input-group-text">{t('club')}</label>
|
|
||||||
<select id="inputGroupSelect09" className="form-select" value={club} onChange={(e) => setClub(e.target.value)}>
|
|
||||||
{clubList.sort((a, b) => a.localeCompare(b)).map((club, index) => (
|
|
||||||
<option key={index} value={club}>{club}</option>))}
|
|
||||||
</select>
|
|
||||||
<button className="btn btn-outline-primary" type="button" onClick={handleAdd}>{t("ajouter")}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="modal-footer">
|
|
||||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function SendLiceName({name}) {
|
|
||||||
const {sendNotify, setState} = useWS();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setState({liceName: name});
|
|
||||||
sendNotify("sendLicenceName", name);
|
|
||||||
}, [name]);
|
|
||||||
|
|
||||||
return <>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function SendCatId({catId}) {
|
|
||||||
const notifState = useRef(undefined);
|
|
||||||
const {sendNotify, setState, dispatch, tableState} = useWS();
|
|
||||||
const {t} = useTranslation("cm");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const welcomeInfo = () => {
|
|
||||||
sendNotify("sendState", tableState.current)
|
|
||||||
}
|
|
||||||
const sendTeamCards = ({data}) => {
|
|
||||||
function content({closeToast, data}) {
|
|
||||||
const sendState = (s) => {
|
|
||||||
sendNotify("sendTeamCardReturnState", {
|
|
||||||
state: s,
|
|
||||||
teamUuid: data.teamUuid,
|
|
||||||
teamName: data.teamName,
|
|
||||||
type: data.type,
|
|
||||||
selectedCategory: tableState.current.selectedCategory,
|
|
||||||
selectedMatch: tableState.current.selectedMatch
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center w-full">
|
|
||||||
<span>{`Un carton jaune d'équipe a été émis à l'encontre du club ${data.teamName}. Dans votre zone de combat :`}</span><br/>
|
|
||||||
<div className="form-check">
|
|
||||||
<input className="form-check-input" type="radio" name="radioState" id="radioState1"
|
|
||||||
onChange={e => e.target.checked ? notifState.current = 0 : null}/>
|
|
||||||
<label className="form-check-label" htmlFor="radioState1">
|
|
||||||
Rien n'est en cours
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="form-check">
|
|
||||||
<input className="form-check-input" type="radio" name="radioState" id="radioState2"
|
|
||||||
onChange={e => e.target.checked ? notifState.current = 1 : null}/>
|
|
||||||
<label className="form-check-label" htmlFor="radioState2">
|
|
||||||
La categorie est en cours
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="form-check">
|
|
||||||
<input className="form-check-input" type="radio" name="radioState" id="radioState3"
|
|
||||||
onChange={e => e.target.checked ? notifState.current = 2 : null}/>
|
|
||||||
<label className="form-check-label" htmlFor="radioState3">
|
|
||||||
Le match est en cours
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<button className="btn btn-sm btn-outline-primary ml-2" onClick={() => {
|
|
||||||
if (notifState.current === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendState(notifState.current)
|
|
||||||
closeToast(true);
|
|
||||||
}}>
|
|
||||||
{t('confirmer')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.warn(content, {
|
|
||||||
position: "top-center",
|
|
||||||
autoClose: false,
|
|
||||||
closeButton: false,
|
|
||||||
hideProgressBar: false,
|
|
||||||
closeOnClick: false,
|
|
||||||
pauseOnHover: true,
|
|
||||||
draggable: false,
|
|
||||||
progress: undefined,
|
|
||||||
theme: "colored",
|
|
||||||
transition: Flip,
|
|
||||||
data: {
|
|
||||||
teamUuid: data.teamUuid,
|
|
||||||
teamName: data.teamName,
|
|
||||||
type: data.type,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
welcomeInfo();
|
|
||||||
|
|
||||||
dispatch({type: 'addListener', payload: {callback: welcomeInfo, code: 'welcomeInfo'}})
|
|
||||||
dispatch({type: 'addListener', payload: {callback: sendTeamCards, code: 'sendTeamCards'}})
|
|
||||||
return () => {
|
|
||||||
dispatch({type: 'removeListener', payload: welcomeInfo});
|
|
||||||
dispatch({type: 'removeListener', payload: sendTeamCards});
|
|
||||||
|
|
||||||
setState({selectedCategory: -1});
|
|
||||||
sendNotify("sendSelectCategory", -1);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setState({selectedCategory: catId});
|
|
||||||
sendNotify("sendSelectCategory", catId);
|
|
||||||
}, [catId]);
|
|
||||||
|
|
||||||
return <>
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {CombName, useCombs, useCombsDispatch} from "../../../hooks/useComb.jsx";
|
|||||||
import {from_sendTree, TreeNode} from "../../../utils/TreeUtils.js";
|
import {from_sendTree, TreeNode} from "../../../utils/TreeUtils.js";
|
||||||
import {DrawGraph} from "../../result/DrawGraph.jsx";
|
import {DrawGraph} from "../../result/DrawGraph.jsx";
|
||||||
import {SelectCombModalContent} from "./SelectCombModalContent.jsx";
|
import {SelectCombModalContent} from "./SelectCombModalContent.jsx";
|
||||||
import {createMatch, scoreToString2} from "../../../utils/CompetitionTools.js";
|
import {createMatch, scoreToString} from "../../../utils/CompetitionTools.js";
|
||||||
|
|
||||||
import {DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors} from '@dnd-kit/core';
|
import {DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors} from '@dnd-kit/core';
|
||||||
import {SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy} from '@dnd-kit/sortable';
|
import {SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy} from '@dnd-kit/sortable';
|
||||||
@ -14,12 +14,9 @@ import {useSortable} from '@dnd-kit/sortable';
|
|||||||
import {CSS} from '@dnd-kit/utilities';
|
import {CSS} from '@dnd-kit/utilities';
|
||||||
import {toast} from "react-toastify";
|
import {toast} from "react-toastify";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faPen, faTrash} from "@fortawesome/free-solid-svg-icons";
|
import {faTrash} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {getToastMessage, virtualScore, win_end} from "../../../utils/Tools.js";
|
import {getToastMessage, win} from "../../../utils/Tools.js";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
|
||||||
|
|
||||||
import {ScorePanel} from "./ScoreAndCardPanel.jsx";
|
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -32,7 +29,6 @@ function CupImg() {
|
|||||||
export function CategoryContent({cat, catId, setCat, menuActions}) {
|
export function CategoryContent({cat, catId, setCat, menuActions}) {
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const {sendRequest, dispatch} = useWS();
|
const {sendRequest, dispatch} = useWS();
|
||||||
const cardDispatch = useCardsDispatch();
|
|
||||||
const [matches, reducer] = useReducer(MarchReducer, []);
|
const [matches, reducer] = useReducer(MarchReducer, []);
|
||||||
const [groups, setGroups] = useState([])
|
const [groups, setGroups] = useState([])
|
||||||
const groupsRef = useRef(groups);
|
const groupsRef = useRef(groups);
|
||||||
@ -125,8 +121,6 @@ export function CategoryContent({cat, catId, setCat, menuActions}) {
|
|||||||
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))
|
||||||
})
|
})
|
||||||
|
|
||||||
cardDispatch({type: 'SET_ALL', payload: data.cards});
|
|
||||||
|
|
||||||
let matches2 = [];
|
let matches2 = [];
|
||||||
let combsToAdd = [];
|
let combsToAdd = [];
|
||||||
data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
||||||
@ -469,19 +463,12 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
const [combSelect, setCombSelect] = useState(0)
|
const [combSelect, setCombSelect] = useState(0)
|
||||||
const [combC1nm, setCombC1nm] = useState(null)
|
const [combC1nm, setCombC1nm] = useState(null)
|
||||||
const [combC2nm, setCombC2nm] = useState(null)
|
const [combC2nm, setCombC2nm] = useState(null)
|
||||||
const [modalMatchId, setModalMatchId] = useState(null)
|
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
const {cards_v, getHeightCardForCombInMatch} = useCards();
|
|
||||||
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 = 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: win(m.scores)}))
|
||||||
marches2.forEach(m => {
|
|
||||||
if (m.end && (!m.scores || m.scores.length === 0))
|
|
||||||
m.scores = [{n_round: 0, s1: 0, s2: 0}];
|
|
||||||
})
|
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor),
|
useSensor(PointerSensor),
|
||||||
@ -581,14 +568,6 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
.finally(() => setLoading(0))
|
.finally(() => setLoading(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleEditMatch = (matchId) => {
|
|
||||||
const match = matches.find(m => m.id === matchId)
|
|
||||||
if (!match)
|
|
||||||
return;
|
|
||||||
setModalMatchId (matchId);
|
|
||||||
matchModal.current.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCombClick = (e, matchId, combId) => {
|
const handleCombClick = (e, matchId, combId) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const tableRect = tableRef.current.getBoundingClientRect();
|
const tableRect = tableRef.current.getBoundingClientRect();
|
||||||
@ -611,31 +590,6 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
lastMatchClick.current = null;
|
lastMatchClick.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetCard = ({combId, match, cat}) => {
|
|
||||||
const c = getHeightCardForCombInMatch(combId, match)
|
|
||||||
if (!c)
|
|
||||||
return <></>
|
|
||||||
let bg = "";
|
|
||||||
switch (c.type) {
|
|
||||||
case "YELLOW":
|
|
||||||
bg = " bg-warning";
|
|
||||||
break;
|
|
||||||
case "RED":
|
|
||||||
bg = " bg-danger";
|
|
||||||
break;
|
|
||||||
case "BLACK":
|
|
||||||
bg = " bg-dark text-white";
|
|
||||||
break;
|
|
||||||
case "BLUE":
|
|
||||||
bg = " bg-primary text-white";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return <span
|
|
||||||
className={"position-absolute top-0 start-100 translate-middle-y badge border border-light p-2" + bg +
|
|
||||||
(c.match === match.id ? " rounded-circle" : (hasEffectCard(c, match.id, cat.id) ? "" : " bg-opacity-50"))}>
|
|
||||||
<span className="visually-hidden">card</span></span>
|
|
||||||
}
|
|
||||||
|
|
||||||
const combsIDs = groups.map(m => m.id);
|
const combsIDs = groups.map(m => m.id);
|
||||||
return <div style={{position: "relative"}}>
|
return <div style={{position: "relative"}}>
|
||||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||||
@ -654,7 +608,6 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
<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>
|
<th style={{textAlign: "center"}} scope="col"></th>
|
||||||
<th style={{textAlign: "center"}} scope="col"></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="table-group-divider">
|
<tbody className="table-group-divider">
|
||||||
@ -664,16 +617,14 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
<td style={{textAlign: "center", cursor: "auto"}}>{m.poule}</td>
|
<td style={{textAlign: "center", cursor: "auto"}}>{m.poule}</td>
|
||||||
<td style={{textAlign: "center", cursor: "auto"}}>{liceName[index % liceName.length]}</td>
|
<td style={{textAlign: "center", cursor: "auto"}}>{liceName[index % liceName.length]}</td>
|
||||||
<td style={{textAlign: "right", cursor: "auto", paddingRight: "0"}}>{m.end && m.win > 0 && <CupImg/>}</td>
|
<td style={{textAlign: "right", cursor: "auto", paddingRight: "0"}}>{m.end && m.win > 0 && <CupImg/>}</td>
|
||||||
<td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}>
|
<td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}
|
||||||
<small className="position-relative"><CombName combId={m.c1}/>
|
onClick={e => handleCombClick(e, m.id, m.c1)}>
|
||||||
<GetCard match={m} combId={m.c1} cat={cat}/></small></td>
|
<small><CombName combId={m.c1}/></small></td>
|
||||||
<td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}}>
|
<td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}}
|
||||||
<small className="position-relative"><CombName combId={m.c2}/>
|
onClick={e => handleCombClick(e, m.id, m.c2)}>
|
||||||
<GetCard match={m} combId={m.c2} cat={cat}/></small></td>
|
<small><CombName combId={m.c2}/></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"}}>{scoreToString(m.scores)}</td>
|
||||||
<td style={{textAlign: "center", cursor: "pointer", color: "#1381ff"}} onClick={_ => handleEditMatch(m.id)}>
|
|
||||||
<FontAwesomeIcon icon={faPen}/></td>
|
|
||||||
<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"}}>☰</td>
|
||||||
@ -694,7 +645,6 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -709,35 +659,7 @@ function MatchList({matches, cat, groups, reducer}) {
|
|||||||
<option key={combId} value={combId}><CombName combId={combId}/></option>
|
<option key={combId} value={combId}><CombName combId={combId}/></option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<button ref={matchModal} type="button" style={{display: "none"}} data-bs-toggle="modal" data-bs-target="#editMatchModal">open</button>
|
|
||||||
<div className="modal fade" id="editMatchModal" tabIndex="-1" aria-labelledby="editMatchModalLabel" aria-hidden="true">
|
|
||||||
<div className="modal-dialog modal-dialog-scrollable modal-lg modal-fullscreen-lg-down">
|
|
||||||
<div className="modal-content">
|
|
||||||
<MatchEditModalContent matchId={modalMatchId} matches={matches}/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function MatchEditModalContent({matchId, matches}) {
|
|
||||||
const menuActionsLocal = useRef({});
|
|
||||||
const match = matches.find(m => m.id === matchId)
|
|
||||||
const {t} = useTranslation("cm");
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<div className="modal-header">
|
|
||||||
<h1 className="modal-title fs-5" id="editMatchModalLabel">{t('editionDuMatch')}</h1>
|
|
||||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div className="modal-body" style={{textAlign: "center"}}>
|
|
||||||
<ScorePanel matchId={matchId} match={match} matchs={matches} menuActions={menuActionsLocal} admin={true}/>
|
|
||||||
</div>
|
|
||||||
<div className="modal-footer">
|
|
||||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">{t('fermer')}</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SortableRow({id, children}) {
|
function SortableRow({id, children}) {
|
||||||
@ -779,7 +701,6 @@ function BuildTree({treeData, matches, groups}) {
|
|||||||
const {sendRequest} = useWS();
|
const {sendRequest} = useWS();
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const {t} = useTranslation("cm");
|
const {t} = useTranslation("cm");
|
||||||
const {cards_v} = useCards();
|
|
||||||
|
|
||||||
function parseTree(data_in) {
|
function parseTree(data_in) {
|
||||||
if (data_in?.data == null)
|
if (data_in?.data == null)
|
||||||
@ -789,19 +710,9 @@ function BuildTree({treeData, matches, groups}) {
|
|||||||
const c1 = getComb(matchData?.c1)
|
const c1 = getComb(matchData?.c1)
|
||||||
const c2 = getComb(matchData?.c2)
|
const c2 = getComb(matchData?.c2)
|
||||||
|
|
||||||
const scores2 = []
|
|
||||||
for (const score of matchData?.scores) {
|
|
||||||
scores2.push({
|
|
||||||
...score,
|
|
||||||
s1: virtualScore(matchData?.c1, score, matchData, cards_v),
|
|
||||||
s2: virtualScore(matchData?.c2, score, matchData, cards_v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = new TreeNode({
|
let node = new TreeNode({
|
||||||
...matchData,
|
...matchData,
|
||||||
...win_end(matchData, cards_v),
|
|
||||||
scores: scores2,
|
|
||||||
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
|
||||||
})
|
})
|
||||||
@ -878,7 +789,7 @@ 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)} scrollRef={scrollRef} onMatchClick={onMatchClick} onClickVoid={onClickVoid}/>
|
||||||
<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>
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {ThreeDots} from "react-loader-spinner";
|
|||||||
import {AxiosError} from "../../../components/AxiosError.jsx";
|
import {AxiosError} from "../../../components/AxiosError.jsx";
|
||||||
import {useFetch} from "../../../hooks/useFetch.js";
|
import {useFetch} from "../../../hooks/useFetch.js";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {CardsProvider} from "../../../hooks/useCard.jsx";
|
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -67,7 +66,6 @@ function HomeComp() {
|
|||||||
return <WSProvider url={`${vite_url.replace('http', 'ws')}/api/ws/competition/${compUuid}`} onmessage={messageHandler}>
|
return <WSProvider url={`${vite_url.replace('http', 'ws')}/api/ws/competition/${compUuid}`} onmessage={messageHandler}>
|
||||||
<WSStatus setPerm={setPerm}/>
|
<WSStatus setPerm={setPerm}/>
|
||||||
<CombsProvider>
|
<CombsProvider>
|
||||||
<CardsProvider>
|
|
||||||
<LoadingProvider>
|
<LoadingProvider>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home2 perm={perm}/>}/>
|
<Route path="/" element={<Home2 perm={perm}/>}/>
|
||||||
@ -75,7 +73,6 @@ function HomeComp() {
|
|||||||
<Route path="/table" element={<CMTable/>}/>
|
<Route path="/table" element={<CMTable/>}/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</LoadingProvider>
|
</LoadingProvider>
|
||||||
</CardsProvider>
|
|
||||||
</CombsProvider>
|
</CombsProvider>
|
||||||
</WSProvider>
|
</WSProvider>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,413 +0,0 @@
|
|||||||
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
|
|
||||||
import {useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
|
||||||
import {compareCardOrder, useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
|
||||||
import React, {useEffect, useRef, useState} from "react";
|
|
||||||
import {toast} from "react-toastify";
|
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
|
||||||
import {faCaretLeft} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import {getToastMessage, scorePrint, virtual_end, virtualScore, win} from "../../../utils/Tools.js";
|
|
||||||
import {useTranslation} from "react-i18next";
|
|
||||||
import {faCircleQuestion} from "@fortawesome/free-regular-svg-icons";
|
|
||||||
|
|
||||||
export function ScorePanel({matchId, matchs, match, menuActions, admin = false}) {
|
|
||||||
const {cards_v} = useCards();
|
|
||||||
|
|
||||||
const onClickVoid = useRef(() => {
|
|
||||||
});
|
|
||||||
|
|
||||||
const vEnd = virtual_end(match, cards_v);
|
|
||||||
return <div className="row" onClick={onClickVoid.current}>
|
|
||||||
<ScorePanel_ matchId={matchId} matchs={matchs} match={match} menuActions={menuActions} onClickVoid_={onClickVoid} vEnd={vEnd}/>
|
|
||||||
<CardPanel matchId={matchId} match={match} vEnd={admin ? false : vEnd} admin={admin}/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function ScorePanel_({matchId, matchs, match, menuActions, onClickVoid_, vEnd}) {
|
|
||||||
const {sendRequest} = useWS()
|
|
||||||
const setLoading = useLoadingSwitcher()
|
|
||||||
const {t} = useTranslation("cm");
|
|
||||||
|
|
||||||
const [end, setEnd] = useState(match?.end || false)
|
|
||||||
const [scoreIn, setScoreIn] = useState("")
|
|
||||||
const inputRef = useRef(null)
|
|
||||||
const tableRef = useRef(null)
|
|
||||||
const scoreRef = useRef([])
|
|
||||||
const lastScoreClick = useRef(null)
|
|
||||||
const scoreInRef = useRef(null)
|
|
||||||
const {cards_v} = useCards();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
scoreInRef.current = scoreIn;
|
|
||||||
}, [scoreIn]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
menuActions.current.saveScore = (scoreRed, scoreBlue) => {
|
|
||||||
const maxRound = (Math.max(...match.scores.map(s => s.n_round), -1) + 1) || 0;
|
|
||||||
const newScore = {n_round: maxRound, s1: scoreRed, s2: scoreBlue};
|
|
||||||
toast.promise(sendRequest('updateMatchScore', {matchId: matchId, ...newScore}), getToastMessage("toast.updateMatchScore", "cm"));
|
|
||||||
}
|
|
||||||
return () => menuActions.current.saveScore = undefined;
|
|
||||||
}, [matchId])
|
|
||||||
|
|
||||||
const handleScoreClick = (e, round, comb) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if (vEnd)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const tableRect = tableRef.current.getBoundingClientRect();
|
|
||||||
const rect = e.currentTarget.getBoundingClientRect();
|
|
||||||
|
|
||||||
updateScore();
|
|
||||||
|
|
||||||
const sel = inputRef.current;
|
|
||||||
sel.style.top = (rect.y - tableRect.y) + "px";
|
|
||||||
sel.style.left = (rect.x - tableRect.x) + "px";
|
|
||||||
sel.style.width = rect.width + "px";
|
|
||||||
sel.style.height = rect.height + "px";
|
|
||||||
sel.style.display = "block";
|
|
||||||
|
|
||||||
if (round === -1) {
|
|
||||||
const maxRound = (Math.max(...match.scores.map(s => s.n_round), -1) + 1) || 0;
|
|
||||||
setScoreIn("");
|
|
||||||
console.log("Setting for new round", maxRound);
|
|
||||||
lastScoreClick.current = {matchId: matchId, round: maxRound, comb};
|
|
||||||
} else {
|
|
||||||
const score = match.scores.find(s => s.n_round === round);
|
|
||||||
setScoreIn((comb === 1 ? score?.s1 : score?.s2) || "");
|
|
||||||
lastScoreClick.current = {matchId: matchId, round, comb};
|
|
||||||
setTimeout(() => inputRef.current.select(), 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateScore = () => {
|
|
||||||
if (lastScoreClick.current !== null) {
|
|
||||||
const {matchId, round, comb} = lastScoreClick.current;
|
|
||||||
lastScoreClick.current = null;
|
|
||||||
|
|
||||||
const scoreIn_ = String(scoreInRef.current).trim() === "" ? -1000 : Number(scoreInRef.current);
|
|
||||||
|
|
||||||
const score = matchs?.find(m => m.id === matchId)?.scores?.find(s => s.n_round === round);
|
|
||||||
|
|
||||||
let newScore;
|
|
||||||
if (score) {
|
|
||||||
if (comb === 1)
|
|
||||||
newScore = {...score, s1: scoreIn_};
|
|
||||||
else
|
|
||||||
newScore = {...score, s2: scoreIn_};
|
|
||||||
|
|
||||||
if (newScore.s1 === score?.s1 && newScore.s2 === score?.s2)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
newScore = {n_round: round, s1: (comb === 1 ? scoreIn_ : -1000), s2: (comb === 2 ? scoreIn_ : -1000)};
|
|
||||||
if (newScore.s1 === -1000 && newScore.s2 === -1000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(1)
|
|
||||||
sendRequest('updateMatchScore', {matchId: matchId, ...newScore})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClickVoid = () => {
|
|
||||||
updateScore();
|
|
||||||
|
|
||||||
const sel = inputRef.current;
|
|
||||||
sel.style.display = "none";
|
|
||||||
lastScoreClick.current = null;
|
|
||||||
}
|
|
||||||
onClickVoid_.current = onClickVoid;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!match || match?.end === end)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (end) {
|
|
||||||
if (win(match) === 0 && match.categorie_ord === -42) {
|
|
||||||
toast.error(t('score.err1'));
|
|
||||||
setEnd(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(1)
|
|
||||||
sendRequest('updateMatchEnd', {matchId: matchId, end})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(0)
|
|
||||||
})
|
|
||||||
}, [end]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onClickVoid()
|
|
||||||
}, [matchId]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (match?.scores)
|
|
||||||
scoreRef.current = scoreRef.current.slice(0, match.scores.length);
|
|
||||||
}, [match?.scores]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!match)
|
|
||||||
return;
|
|
||||||
setEnd(match.end);
|
|
||||||
}, [match]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleClickOutside = (event) => {
|
|
||||||
if (inputRef.current && !inputRef.current.contains(event.target)) {
|
|
||||||
onClickVoid();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
|
||||||
const o = [...tooltipTriggerList]
|
|
||||||
o.map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
|
||||||
|
|
||||||
const tt = t('score.spe')
|
|
||||||
|
|
||||||
const maxRound = (match?.scores) ? (Math.max(...match.scores.map(s => s.n_round), -1) + 1) : 0;
|
|
||||||
return <div ref={tableRef} className="col" style={{position: "relative"}}>
|
|
||||||
<h6>{t('scores')} <FontAwesomeIcon icon={faCircleQuestion} role="button" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-title={tt}
|
|
||||||
data-bs-html="true"/></h6>
|
|
||||||
<table className="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style={{textAlign: "center"}} scope="col">{t('manche')}</th>
|
|
||||||
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">{t('rouge')}</th>
|
|
||||||
<th style={{textAlign: "center", minWidth: "4em"}} scope="col">{t('bleu')}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className={"table-group-divider" + (vEnd ? " table-secondary" : "")}>
|
|
||||||
{match?.scores && match.scores.sort((a, b) => a.n_round - b.n_round).map(score => (
|
|
||||||
<tr key={score.n_round}>
|
|
||||||
<th style={{textAlign: "center"}}>{score.n_round + 1}</th>
|
|
||||||
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2] = e}
|
|
||||||
onClick={e => handleScoreClick(e, score.n_round, 1)}>{scorePrint(virtualScore(match.c1, score, match, cards_v))}</td>
|
|
||||||
<td style={{textAlign: "center"}} ref={e => scoreRef.current[score.n_round * 2 + 1] = e}
|
|
||||||
onClick={e => handleScoreClick(e, score.n_round, 2)}>{scorePrint(virtualScore(match.c2, score, match, cards_v))}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
<tr>
|
|
||||||
<th style={{textAlign: "center"}}></th>
|
|
||||||
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2] = e} onClick={e => handleScoreClick(e, -1, 1)}>-</td>
|
|
||||||
<td style={{textAlign: "center"}} ref={e => scoreRef.current[maxRound * 2 + 1] = e} onClick={e => handleScoreClick(e, -1, 2)}>-
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div style={{textAlign: "right"}}>
|
|
||||||
<div className="form-check" style={{display: "inline-block"}}>
|
|
||||||
<input className="form-check-input" type="checkbox" id="checkboxEnd" name="checkboxEnd" checked={end || vEnd} disabled={vEnd}
|
|
||||||
onChange={e => setEnd(e.target.checked)}/>
|
|
||||||
<label className="form-check-label" htmlFor="checkboxEnd">{t('terminé')}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input ref={inputRef} type="number" className="form-control" style={{position: "absolute", top: 0, left: 0, display: "none"}} min="-999"
|
|
||||||
max="999"
|
|
||||||
value={scoreIn} onChange={e => setScoreIn(e.target.value)}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
onKeyDown={e => {
|
|
||||||
if (e.key === "Tab") {
|
|
||||||
if (lastScoreClick.current !== null) {
|
|
||||||
const {round, comb} = lastScoreClick.current;
|
|
||||||
const nextIndex = (round * 2 + (comb - 1)) + (e.shiftKey ? -1 : 1);
|
|
||||||
if (nextIndex >= 0 && nextIndex < scoreRef.current.length) {
|
|
||||||
e.preventDefault();
|
|
||||||
scoreRef.current[nextIndex].click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (e.key === "Enter") {
|
|
||||||
e.preventDefault();
|
|
||||||
onClickVoid();
|
|
||||||
}
|
|
||||||
}}/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardPanel({matchId, match, vEnd, admin}) {
|
|
||||||
const {sendRequest} = useWS();
|
|
||||||
const setLoading = useLoadingSwitcher()
|
|
||||||
const cardDispatch = useCardsDispatch();
|
|
||||||
const {getCardInMatch, getHeightCardForCombInMatch} = useCards();
|
|
||||||
const {t} = useTranslation("cm");
|
|
||||||
|
|
||||||
const {data, refresh} = useRequestWS('getCardForMatch', matchId, setLoading);
|
|
||||||
useEffect(() => {
|
|
||||||
refresh('getCardForMatch', matchId);
|
|
||||||
}, [matchId])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!data)
|
|
||||||
return;
|
|
||||||
cardDispatch({type: 'SET_ALL', payload: data})
|
|
||||||
}, [data])
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
return <div className="col"></div>
|
|
||||||
}
|
|
||||||
const handleCard = (combId, type) => {
|
|
||||||
setLoading(1)
|
|
||||||
sendRequest('sendCardAdd', {matchId, combId, type})
|
|
||||||
.then(() => {
|
|
||||||
toast.success(t('cardAdded'));
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
toast.error(err);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmRm = (combId, type) => {
|
|
||||||
function content({closeToast}) {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center w-full">
|
|
||||||
<span>{t('ceCartonEstIssuDunCartonDéquipe')}</span>{' '}
|
|
||||||
<button
|
|
||||||
className="btn btn-sm btn-warning ml-2"
|
|
||||||
onClick={() => {
|
|
||||||
closeToast(true);
|
|
||||||
handleCardRm_(combId, type)
|
|
||||||
}}>
|
|
||||||
{t('confirmer')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.warn(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCardRm_ = (combId, type) => {
|
|
||||||
setLoading(1)
|
|
||||||
sendRequest('sendCardRm', {matchId, combId, type})
|
|
||||||
.then(() => {
|
|
||||||
toast.success(t('cardRemoved'));
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
toast.error(err);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const cards = getCardInMatch(match)
|
|
||||||
|
|
||||||
const handleCardRm = (combId, type) => {
|
|
||||||
if (cards.find(c => c.comb === combId && c.type === type)?.teamCard) {
|
|
||||||
confirmRm(combId, type)
|
|
||||||
} else {
|
|
||||||
handleCardRm_(combId, type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MakeList = ({comb}) => {
|
|
||||||
const card = getHeightCardForCombInMatch(comb, match);
|
|
||||||
|
|
||||||
return <div className="btn-group-vertical" role="group">
|
|
||||||
<button type="button" className="btn btn-sm btn-primary position-relative"
|
|
||||||
onClick={() => handleCard(comb, "BLUE")}
|
|
||||||
disabled={card && compareCardOrder(card, {type: "BLUE"}) >= 0 || vEnd}>{t('avertissement')}
|
|
||||||
<span className="position-absolute top-0 start-100 p-2 text-danger-emphasis"
|
|
||||||
hidden={!cards.some(c => c.comb === comb && c.type === "BLUE")}>
|
|
||||||
<FontAwesomeIcon icon={faCaretLeft}/></span>
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-warning position-relative"
|
|
||||||
onClick={() => handleCard(comb, "YELLOW")}
|
|
||||||
disabled={card && compareCardOrder(card, {type: "YELLOW"}) >= 0 || vEnd}>{t('cartonJaune')}
|
|
||||||
<span className="position-absolute top-0 start-100 p-2 text-danger-emphasis"
|
|
||||||
hidden={!cards.some(c => c.comb === comb && c.type === "YELLOW")}>
|
|
||||||
<FontAwesomeIcon icon={faCaretLeft}/></span>
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-danger position-relative"
|
|
||||||
onClick={() => handleCard(comb, "RED")}
|
|
||||||
disabled={card && compareCardOrder(card, {type: "RED"}) >= 0 || vEnd}>{t('cartonRouge')}
|
|
||||||
<span className="position-absolute top-0 start-100 p-2 text-danger-emphasis"
|
|
||||||
hidden={!cards.some(c => c.comb === comb && c.type === "RED")}>
|
|
||||||
<FontAwesomeIcon icon={faCaretLeft}/></span>
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-dark position-relative"
|
|
||||||
onClick={() => handleCard(comb, "BLACK")}
|
|
||||||
disabled={card?.type !== "RED" || vEnd}>{t('cartonNoir')}
|
|
||||||
<span className="position-absolute top-0 start-100 p-2 text-danger-emphasis"
|
|
||||||
hidden={!cards.some(c => c.comb === comb && c.type === "BLACK")}>
|
|
||||||
<FontAwesomeIcon icon={faCaretLeft}/></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const MakeListAdmin = ({comb}) => {
|
|
||||||
const card = getHeightCardForCombInMatch(comb, match);
|
|
||||||
|
|
||||||
return <div className="btn-group-vertical" role="group">
|
|
||||||
<div className="btn-group" role="group">
|
|
||||||
<button type="button" className="btn btn-sm btn-secondary">{t('ajouterUn')}</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-secondary">{t('supprimerUn')}</button>
|
|
||||||
</div>
|
|
||||||
<div className="btn-group" role="group">
|
|
||||||
<button type="button" className="btn btn-sm btn-primary"
|
|
||||||
onClick={() => handleCard(comb, "BLUE")}
|
|
||||||
disabled={card && compareCardOrder(card, {type: "BLUE"}) >= 0 || vEnd}>{t('avertissement')}
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-primary"
|
|
||||||
onClick={() => handleCardRm(comb, "BLUE")}
|
|
||||||
disabled={!cards.some(c => c.comb === comb && c.type === "BLUE")}>{t('avertissement')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="btn-group" role="group">
|
|
||||||
<button type="button" className="btn btn-sm btn-warning"
|
|
||||||
onClick={() => handleCard(comb, "YELLOW")}
|
|
||||||
disabled={card && compareCardOrder(card, {type: "YELLOW"}) >= 0 || vEnd}>{t('cartonJaune')}
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-warning"
|
|
||||||
onClick={() => handleCardRm(comb, "YELLOW")}
|
|
||||||
disabled={!cards.some(c => c.comb === comb && c.type === "YELLOW")}>{t('cartonJaune')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="btn-group" role="group">
|
|
||||||
<button type="button" className="btn btn-sm btn-danger"
|
|
||||||
onClick={() => handleCard(comb, "RED")}
|
|
||||||
disabled={card && compareCardOrder(card, {type: "RED"}) >= 0 || vEnd}>{t('cartonRouge')}
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-danger"
|
|
||||||
onClick={() => handleCardRm(comb, "RED")}
|
|
||||||
disabled={!cards.some(c => c.comb === comb && c.type === "RED")}>{t('cartonRouge')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="btn-group" role="group">
|
|
||||||
<button type="button" className="btn btn-sm btn-dark"
|
|
||||||
onClick={() => handleCard(comb, "BLACK")}
|
|
||||||
disabled={card?.type !== "RED" || vEnd}>{t('cartonNoir')}
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-sm btn-dark"
|
|
||||||
onClick={() => handleCardRm(comb, "BLACK")}
|
|
||||||
disabled={!cards.some(c => c.comb === comb && c.type === "BLACK")}>{t('cartonNoir')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="col">
|
|
||||||
<h6>Carton</h6>
|
|
||||||
<div className="bg-danger-subtle text-danger-emphasis" style={{padding: ".25em", borderRadius: "1em 1em 0 0"}}>
|
|
||||||
<h6>Combattant rouge</h6>
|
|
||||||
{admin ? <MakeListAdmin comb={match.c1}/> :
|
|
||||||
<><span>{t('ajouterUn')}</span><MakeList comb={match.c1}/></>}
|
|
||||||
</div>
|
|
||||||
<div className="bg-info-subtle text-info-emphasis" style={{padding: ".25em", borderRadius: "0 0 1em 1em"}}>
|
|
||||||
<h6>Combattant bleu</h6>
|
|
||||||
{admin ? <MakeListAdmin comb={match.c2}/> :
|
|
||||||
<><span>{t('ajouterUn')}</span><MakeList comb={match.c2}/></>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@ -1,211 +0,0 @@
|
|||||||
import {useWS} from "../../../hooks/useWS.jsx";
|
|
||||||
import {useEffect, useReducer, useRef, useState} from "react";
|
|
||||||
import {useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
|
||||||
import {from_sendTree} from "../../../utils/TreeUtils.js";
|
|
||||||
import {MarchReducer} from "../../../utils/MatchReducer.jsx";
|
|
||||||
import {CombName, useCombsDispatch} from "../../../hooks/useComb.jsx";
|
|
||||||
import {timePrint, win_end} from "../../../utils/Tools.js";
|
|
||||||
|
|
||||||
function reducer(state, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'SET':
|
|
||||||
return [...state.filter(s => s.id !== action.payload.id), action.payload]
|
|
||||||
case 'SET_ALL':
|
|
||||||
return action.payload
|
|
||||||
case 'REMOVE':
|
|
||||||
return state.filter(s => s.id !== action.payload)
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function StateWindow({document}) {
|
|
||||||
const {sendRequest, dispatch} = useWS();
|
|
||||||
const [state, dispatchState] = useReducer(reducer, [])
|
|
||||||
|
|
||||||
const subscribeToState = () => {
|
|
||||||
sendRequest("subscribeToState", true)
|
|
||||||
.then((data) => {
|
|
||||||
dispatchState({type: 'SET_ALL', payload: data});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const sendStateFull = ({data}) => {
|
|
||||||
dispatchState({type: 'SET', payload: data});
|
|
||||||
}
|
|
||||||
|
|
||||||
const rmStateFull = ({data}) => {
|
|
||||||
dispatchState({type: 'REMOVE', payload: data});
|
|
||||||
}
|
|
||||||
|
|
||||||
const welcomeInfo = () => {
|
|
||||||
subscribeToState();
|
|
||||||
}
|
|
||||||
subscribeToState();
|
|
||||||
|
|
||||||
dispatch({type: 'addListener', payload: {callback: welcomeInfo, code: 'welcomeInfo'}})
|
|
||||||
dispatch({type: 'addListener', payload: {callback: sendStateFull, code: 'sendStateFull'}})
|
|
||||||
dispatch({type: 'addListener', payload: {callback: rmStateFull, code: 'rmStateFull'}})
|
|
||||||
return () => {
|
|
||||||
dispatch({type: 'removeListener', payload: welcomeInfo});
|
|
||||||
dispatch({type: 'removeListener', payload: sendStateFull});
|
|
||||||
dispatch({type: 'removeListener', payload: rmStateFull});
|
|
||||||
|
|
||||||
sendRequest("subscribeToState", false)
|
|
||||||
.then(() => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
document.title = "État des tables de marque";
|
|
||||||
document.body.className = "overflow-hidden";
|
|
||||||
|
|
||||||
console.log(state)
|
|
||||||
return <>
|
|
||||||
<div className="d-flex flex-row flex-wrap justify-content-around align-items-center align-content-around h-100 p-2 overflow-auto">
|
|
||||||
{state.sort((a, b) => a.liceName.localeCompare(b.liceName)).map((table, index) =>
|
|
||||||
<div key={index} className="card d-inline-flex flex-grow-1 align-self-stretch" style={{minWidth: "25em", maxWidth: "30em"}}>
|
|
||||||
<ShowState table={table} dispatch={dispatchState}/>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function readAndConvertMatch(matches, data, combsToAdd) {
|
|
||||||
matches.push({
|
|
||||||
...data,
|
|
||||||
c1: data.c1?.id,
|
|
||||||
c2: data.c2?.id,
|
|
||||||
c1_cacheName: data.c1?.fname + " " + data.c1?.lname,
|
|
||||||
c2_cacheName: data.c2?.fname + " " + data.c2?.lname
|
|
||||||
})
|
|
||||||
if (data.c1)
|
|
||||||
combsToAdd.push(data.c1)
|
|
||||||
if (data.c2)
|
|
||||||
combsToAdd.push(data.c2)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ShowState({table}) {
|
|
||||||
const cardDispatch = useCardsDispatch();
|
|
||||||
const {sendRequest, dispatch} = useWS();
|
|
||||||
const [matches, reducer] = useReducer(MarchReducer, []);
|
|
||||||
const combDispatch = useCombsDispatch();
|
|
||||||
const {cards_v} = useCards();
|
|
||||||
|
|
||||||
const [cat, setCat] = useState({id: -1, name: ""});
|
|
||||||
|
|
||||||
const marches2 = matches.filter(m => m.categorie === cat.id).map(m => ({...m, ...win_end(m, cards_v)}))
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const categoryListener = ({data}) => {
|
|
||||||
if (data.id !== cat.id)
|
|
||||||
return;
|
|
||||||
setCat({id: data.id, name: data.name});
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchListener = ({data: datas}) => {
|
|
||||||
for (const data of datas) {
|
|
||||||
reducer({type: 'UPDATE_OR_ADD', payload: {...data, c1: data.c1?.id, c2: data.c2?.id}});
|
|
||||||
combDispatch({type: 'SET_ALL', payload: {source: "match", data: [data.c1, data.c2].filter(d => d != null)}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteMatch = ({data: datas}) => {
|
|
||||||
for (const data of datas)
|
|
||||||
reducer({type: 'REMOVE', payload: data});
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({type: 'addListener', payload: {callback: categoryListener, code: 'sendCategory'}})
|
|
||||||
dispatch({type: 'addListener', payload: {callback: matchListener, code: 'sendMatch'}})
|
|
||||||
dispatch({type: 'addListener', payload: {callback: deleteMatch, code: 'sendDeleteMatch'}})
|
|
||||||
return () => {
|
|
||||||
dispatch({type: 'removeListener', payload: matchListener})
|
|
||||||
dispatch({type: 'removeListener', payload: deleteMatch})
|
|
||||||
dispatch({type: 'removeListener', payload: categoryListener})
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (table.selectedCategory !== cat.id) {
|
|
||||||
if (!table.selectedCategory || table.selectedCategory === -1) {
|
|
||||||
setCat({id: -1, name: ""});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendRequest('getFullCategory', table.selectedCategory)
|
|
||||||
.then((data) => {
|
|
||||||
setCat({id: data.id, name: data.name});
|
|
||||||
cardDispatch({type: 'SET_ALL', payload: data.cards});
|
|
||||||
|
|
||||||
let matches2 = [];
|
|
||||||
let combsToAdd = [];
|
|
||||||
data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
|
||||||
data.matches.forEach((data_) => readAndConvertMatch(matches2, data_, combsToAdd));
|
|
||||||
|
|
||||||
reducer({type: 'REPLACE_ALL', payload: matches2});
|
|
||||||
combDispatch({type: 'SET_ALL', payload: {source: "match", data: combsToAdd}});
|
|
||||||
console.log(matches2);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [table]);
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<div className="card-header">
|
|
||||||
Zone de combat {table.liceName}
|
|
||||||
</div>
|
|
||||||
<div className="card-body">
|
|
||||||
Catégorie : {cat.name}<br/>
|
|
||||||
Match terminés : {marches2.filter(m => m.end).length}/{marches2.length}<br/>
|
|
||||||
Matchs : <PrintMatch match={matches.find(m => m.id === table.selectedMatch)}/><br/>
|
|
||||||
Statue : {table?.state}<br/>
|
|
||||||
Score : {table?.scoreState?.scoreRouge} - {table?.scoreState?.scoreBleu}<br/>
|
|
||||||
Chronomètre : <PrintChrono chrono={table?.chronoState}/><br/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function PrintMatch({match}) {
|
|
||||||
return <>{match?.c1 && <CombName combId={match?.c1}/>} vs {match?.c2 && <CombName combId={match?.c2}/>}</>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function PrintChrono({chrono}) {
|
|
||||||
const chronoText = useRef(null)
|
|
||||||
const state = useRef({chronoState: 0, countBlink: 20, lastColor: "#000000", lastTimeStr: "00:00"})
|
|
||||||
|
|
||||||
const isRunning = () => chrono.startTime !== 0
|
|
||||||
const getTime = () => {
|
|
||||||
if (chrono.startTime === 0)
|
|
||||||
return chrono.time
|
|
||||||
return chrono.time + Date.now() - chrono.startTime
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!chrono || !chronoText.current)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const state_ = state.current
|
|
||||||
const text_ = chronoText.current
|
|
||||||
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
let currentDuration = chrono.configTime
|
|
||||||
if (chrono.state === 2) {
|
|
||||||
currentDuration = (chrono.state === 0) ? 10000 : chrono.configPause
|
|
||||||
}
|
|
||||||
const timeStr = (chrono.state === 1 ? " Match - " : " Pause - ") + timePrint(currentDuration - getTime()) + (isRunning() ? "" : " (arrêté)")
|
|
||||||
|
|
||||||
if (timeStr !== state_.lastTimeStr) {
|
|
||||||
text_.textContent = timeStr
|
|
||||||
state_.lastTimeStr = timeStr
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chrono.chronoState === 0) {
|
|
||||||
clearInterval(timer)
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
return () => clearInterval(timer)
|
|
||||||
}, [chrono])
|
|
||||||
|
|
||||||
return <><span ref={chronoText}>{state.current.lastTimeStr}</span></>
|
|
||||||
}
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import {useEffect, useRef} from "react";
|
import {useEffect, useRef} from "react";
|
||||||
import {scorePrint} from "../../utils/Tools.js";
|
import {scorePrint, win} from "../../utils/Tools.js";
|
||||||
import {compareCardOrder, useCardsStatic} from "../../hooks/useCard.jsx";
|
|
||||||
|
|
||||||
const max_x = 500;
|
const max_x = 500;
|
||||||
|
|
||||||
@ -21,14 +20,12 @@ export function DrawGraph({
|
|||||||
},
|
},
|
||||||
matchSelect = null,
|
matchSelect = null,
|
||||||
matchNext = null,
|
matchNext = null,
|
||||||
size = 24,
|
size = 24
|
||||||
cards = []
|
|
||||||
}) {
|
}) {
|
||||||
const canvasRef = useRef(null);
|
const canvasRef = useRef(null);
|
||||||
const actionCanvasRef = useRef(null);
|
const actionCanvasRef = useRef(null);
|
||||||
const ctxARef = useRef(null);
|
const ctxARef = useRef(null);
|
||||||
const actionMapRef = useRef({});
|
const actionMapRef = useRef({});
|
||||||
const {getHeightCardForCombInMatch} = useCardsStatic(cards);
|
|
||||||
|
|
||||||
const selectColor = "#30cc30";
|
const selectColor = "#30cc30";
|
||||||
|
|
||||||
@ -152,40 +149,6 @@ export function DrawGraph({
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
const printCard = (ctx, pos, combId, match) => {
|
|
||||||
const cards2 = getHeightCardForCombInMatch(combId, match)
|
|
||||||
if (cards2 != null) {
|
|
||||||
let oldColor = ctx.fillStyle;
|
|
||||||
switch (cards2.type) {
|
|
||||||
case "BLUE":
|
|
||||||
ctx.fillStyle = "#2e2efd";
|
|
||||||
break;
|
|
||||||
case "YELLOW":
|
|
||||||
ctx.fillStyle = "#d8d800";
|
|
||||||
break;
|
|
||||||
case "RED":
|
|
||||||
ctx.fillStyle = "#FF0000";
|
|
||||||
break;
|
|
||||||
case "BLACK":
|
|
||||||
ctx.fillStyle = "#000000";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ctx.fillStyle = "#FFFFFF00";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cards2.match === match.id) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.strokeStyle = ctx.fillStyle
|
|
||||||
ctx.arc(pos.x + pos.width - 10, pos.y + 5, 5, 0, 2 * Math.PI);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.strokeStyle = "#000000"
|
|
||||||
} else
|
|
||||||
ctx.fillRect(pos.x + pos.width - 18, pos.y - 5, 12, 12);
|
|
||||||
ctx.fillStyle = oldColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const newColor = () => {
|
const newColor = () => {
|
||||||
const letters = '0123456789ABCDEF'
|
const letters = '0123456789ABCDEF'
|
||||||
let color
|
let color
|
||||||
@ -219,9 +182,7 @@ export function DrawGraph({
|
|||||||
|
|
||||||
printScores(ctx, match.scores, px, py, 1);
|
printScores(ctx, match.scores, px, py, 1);
|
||||||
|
|
||||||
|
|
||||||
const pos = {x: px - size * 2 - size * 8, y: py - size - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
const pos = {x: px - size * 2 - size * 8, y: py - size - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
||||||
printCard(ctx, pos, match.c1, match)
|
|
||||||
ctx.fillStyle = "#FF0000"
|
ctx.fillStyle = "#FF0000"
|
||||||
printText(ctx, (match.c1FullName == null) ? "" : match.c1FullName, pos.x, pos.y, pos.width, pos.height, false, true)
|
printText(ctx, (match.c1FullName == null) ? "" : match.c1FullName, pos.x, pos.y, pos.width, pos.height, false, true)
|
||||||
ctxA.fillStyle = newColor()
|
ctxA.fillStyle = newColor()
|
||||||
@ -229,7 +190,6 @@ export function DrawGraph({
|
|||||||
actionMapRef.current[ctxA.fillStyle] = {type: 'match', rect: pos, match: match.id, comb: 1}
|
actionMapRef.current[ctxA.fillStyle] = {type: 'match', rect: pos, match: match.id, comb: 1}
|
||||||
|
|
||||||
const pos2 = {x: px - size * 2 - size * 8, y: py + size - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
const pos2 = {x: px - size * 2 - size * 8, y: py + size - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
||||||
printCard(ctx, pos2, match.c2, match)
|
|
||||||
ctx.fillStyle = "#0000FF"
|
ctx.fillStyle = "#0000FF"
|
||||||
printText(ctx, (match.c2FullName == null) ? "" : match.c2FullName, pos2.x, pos2.y, pos2.width, pos2.height, false, true)
|
printText(ctx, (match.c2FullName == null) ? "" : match.c2FullName, pos2.x, pos2.y, pos2.width, pos2.height, false, true)
|
||||||
ctxA.fillStyle = newColor()
|
ctxA.fillStyle = newColor()
|
||||||
@ -268,7 +228,6 @@ export function DrawGraph({
|
|||||||
printScores(ctx, match.scores, px, py, 1.5);
|
printScores(ctx, match.scores, px, py, 1.5);
|
||||||
|
|
||||||
const pos = {x: px - size * 2 - size * 8, y: py - size * 2 * death - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
const pos = {x: px - size * 2 - size * 8, y: py - size * 2 * death - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
||||||
printCard(ctx, pos, match.c1, match)
|
|
||||||
ctx.fillStyle = "#FF0000"
|
ctx.fillStyle = "#FF0000"
|
||||||
printText(ctx, (match.c1FullName == null) ? "" : match.c1FullName, pos.x, pos.y, pos.width, pos.height, true, true)
|
printText(ctx, (match.c1FullName == null) ? "" : match.c1FullName, pos.x, pos.y, pos.width, pos.height, true, true)
|
||||||
ctxA.fillStyle = newColor()
|
ctxA.fillStyle = newColor()
|
||||||
@ -276,7 +235,6 @@ export function DrawGraph({
|
|||||||
actionMapRef.current[ctxA.fillStyle] = {type: 'match', rect: pos, match: match.id, comb: 1}
|
actionMapRef.current[ctxA.fillStyle] = {type: 'match', rect: pos, match: match.id, comb: 1}
|
||||||
|
|
||||||
const pos2 = {x: px - size * 2 - size * 8, y: py + size * 2 * death - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
const pos2 = {x: px - size * 2 - size * 8, y: py + size * 2 * death - (size * 1.5 / 2 | 0), width: size * 8, height: (size * 1.5 | 0)}
|
||||||
printCard(ctx, pos2, match.c2, match)
|
|
||||||
ctx.fillStyle = "#0000FF"
|
ctx.fillStyle = "#0000FF"
|
||||||
printText(ctx, (match.c2FullName == null) ? "" : match.c2FullName, pos2.x, pos2.y, pos2.width, pos2.height, true, true)
|
printText(ctx, (match.c2FullName == null) ? "" : match.c2FullName, pos2.x, pos2.y, pos2.width, pos2.height, true, true)
|
||||||
ctxA.fillStyle = newColor()
|
ctxA.fillStyle = newColor()
|
||||||
@ -352,7 +310,7 @@ export function DrawGraph({
|
|||||||
for (const node of root) {
|
for (const node of root) {
|
||||||
let win_name = "";
|
let win_name = "";
|
||||||
if (node.data.end) {
|
if (node.data.end) {
|
||||||
win_name = node.data.win > 0
|
win_name = win(node.data.scores) > 0
|
||||||
? (node.data.c1FullName === null ? "???" : node.data.c1FullName)
|
? (node.data.c1FullName === null ? "???" : node.data.c1FullName)
|
||||||
: (node.data.c2FullName === null ? "???" : node.data.c2FullName);
|
: (node.data.c2FullName === null ? "???" : node.data.c2FullName);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {scorePrint, virtualScore} from "./Tools.js";
|
import {scorePrint} from "./Tools.js";
|
||||||
|
|
||||||
export function scoreToString(score) {
|
export function scoreToString(score) {
|
||||||
if (score === null || score === undefined || score.length === 0)
|
if (score === null || score === undefined || score.length === 0)
|
||||||
@ -10,18 +10,6 @@ export function scoreToString(score) {
|
|||||||
return score.sort((a, b) => a.n_round - b.n_round).map(o => scorePrint(o.s1) + "-" + scorePrint(o.s2)).join(" | ")
|
return score.sort((a, b) => a.n_round - b.n_round).map(o => scorePrint(o.s1) + "-" + scorePrint(o.s2)).join(" | ")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scoreToString2(match, cards = []) {
|
|
||||||
if (!match || match.scores === null || match.scores === undefined || match.scores.length === 0)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
if (Array.isArray(match.scores[0]))
|
|
||||||
return match.scores.map(o => scorePrint(o.at(0)) + "-" + scorePrint(o.at(1))).join(" | ")
|
|
||||||
else {
|
|
||||||
return match.scores.sort((a, b) => a.n_round - b.n_round).map(o =>
|
|
||||||
scorePrint(virtualScore(match.c1, o, match, cards)) + "-" + scorePrint(virtualScore(match.c2, o, match, cards))).join(" | ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createMatch(category, matchs, groups) {
|
export function createMatch(category, matchs, groups) {
|
||||||
const combs_2 = {}
|
const combs_2 = {}
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import i18n from "../config/i18n.js";
|
import i18n from "../config/i18n.js";
|
||||||
import {compareCardOrder, hasEffectCard} from "../hooks/useCard.jsx";
|
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -226,18 +225,9 @@ export function getToastMessage(msgKey, ns = 'common') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function win(match, cards = []) {
|
export function win(scores) {
|
||||||
const cards2 = cards.filter(c => (c.comb === match.c1 || c.comb === match.c2) && hasEffectCard(c, match.id, match.categorie)).sort(compareCardOrder).pop()
|
|
||||||
|
|
||||||
if (cards2) {
|
|
||||||
if (cards2.comb === match.c1)
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
let sum = 0
|
let sum = 0
|
||||||
for (const score of match?.scores) {
|
for (const score of scores) {
|
||||||
if (score.s1 === -1000 || score.s2 === -1000) continue
|
if (score.s1 === -1000 || score.s2 === -1000) continue
|
||||||
if (score.s1 > score.s2) sum++
|
if (score.s1 > score.s2) sum++
|
||||||
else if (score.s1 < score.s2) sum--
|
else if (score.s1 < score.s2) sum--
|
||||||
@ -245,61 +235,7 @@ export function win(match, cards = []) {
|
|||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
export function win_end(match, cards = []) {
|
|
||||||
const cards2 = cards?.filter(c => (c.comb === match.c1 || c.comb === match.c2) && hasEffectCard(c, match.id, match.categorie)).sort(compareCardOrder)
|
|
||||||
|
|
||||||
if (cards2.length > 1) {
|
|
||||||
if (cards2[0].comb !== cards2[1].comb)
|
|
||||||
return {win: 0, end: true}
|
|
||||||
}
|
|
||||||
if (cards2.length > 0) {
|
|
||||||
if (cards2[0].comb === match.c1)
|
|
||||||
return {win: -1, end: true}
|
|
||||||
else
|
|
||||||
return {win: 1, end: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let sum = 0
|
|
||||||
for (const score of match?.scores) {
|
|
||||||
if (score.s1 === -1000 || score.s2 === -1000) continue
|
|
||||||
if (score.s1 > score.s2) sum++
|
|
||||||
else if (score.s1 < score.s2) sum--
|
|
||||||
}
|
|
||||||
return {win: sum, end: match?.end}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function virtual_end(match, cards) {
|
|
||||||
if (!cards)
|
|
||||||
return false
|
|
||||||
return cards.some(c => (c.comb === match?.c1 || c.comb === match?.c2) && hasEffectCard(c, match.id, match.categorie))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function virtualScore(combId, score, match, cards) {
|
|
||||||
const cards2 = cards?.filter(c => (c.comb === match?.c1 || c.comb === match?.c2) && hasEffectCard(c, match.id, match.categorie)).sort(compareCardOrder)
|
|
||||||
|
|
||||||
if (cards2.length > 1) {
|
|
||||||
if (cards2[0].comb !== cards2[1].comb)
|
|
||||||
return -997
|
|
||||||
}
|
|
||||||
if (cards2.length > 0) {
|
|
||||||
if (cards2[0].comb === combId)
|
|
||||||
return -997
|
|
||||||
else
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
if (combId === match?.c1) {
|
|
||||||
return score.s1
|
|
||||||
} else if (combId === match?.c2) {
|
|
||||||
return score.s2
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function scorePrint(s1) {
|
export function scorePrint(s1) {
|
||||||
|
|
||||||
switch (s1) {
|
switch (s1) {
|
||||||
case -997:
|
case -997:
|
||||||
return "disc."
|
return "disc."
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user