Compare commits
No commits in common. "378a17ba7517e3fd1755b52bf2564ecbb006e2be" and "3cb12826d0633222c0bc0a7ba61c418454124485" have entirely different histories.
378a17ba75
...
3cb12826d0
@ -7,8 +7,6 @@ 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;
|
||||||
import fr.titionfire.ffsaf.utils.*;
|
import fr.titionfire.ffsaf.utils.*;
|
||||||
import io.quarkus.cache.Cache;
|
|
||||||
import io.quarkus.cache.CacheName;
|
|
||||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import io.smallrye.mutiny.Multi;
|
import io.smallrye.mutiny.Multi;
|
||||||
@ -17,7 +15,6 @@ import io.smallrye.mutiny.unchecked.Unchecked;
|
|||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
|
||||||
import org.hibernate.reactive.mutiny.Mutiny;
|
import org.hibernate.reactive.mutiny.Mutiny;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -55,10 +52,6 @@ public class ResultService {
|
|||||||
@Inject
|
@Inject
|
||||||
TradService trad;
|
TradService trad;
|
||||||
|
|
||||||
@Inject
|
|
||||||
@CacheName("club-classement")
|
|
||||||
Cache cacheClubClassement;
|
|
||||||
|
|
||||||
private static final HashMap<Long, String> combTempIds = new HashMap<>();
|
private static final HashMap<Long, String> combTempIds = new HashMap<>();
|
||||||
|
|
||||||
private static String getCombTempId(Long key) {
|
private static String getCombTempId(Long key) {
|
||||||
@ -221,7 +214,7 @@ public class ResultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void getClassementArray(CategoryModel categoryModel, MembreModel membreModel, List<CardModel> cards,
|
public void getClassementArray(CategoryModel categoryModel, MembreModel membreModel, List<CardModel> cards,
|
||||||
ResultCategoryData out) {
|
ResultCategoryData out) {
|
||||||
if ((categoryModel.getType() & 2) != 0) {
|
if ((categoryModel.getType() & 2) != 0) {
|
||||||
AtomicInteger rank = new AtomicInteger(0);
|
AtomicInteger rank = new AtomicInteger(0);
|
||||||
categoryModel.getTree().stream()
|
categoryModel.getTree().stream()
|
||||||
@ -238,8 +231,7 @@ public class ResultService {
|
|||||||
} else {
|
} else {
|
||||||
for (List<ResultCategoryData.RankArray> list : out.getRankArray().values()) {
|
for (List<ResultCategoryData.RankArray> list : out.getRankArray().values()) {
|
||||||
for (ResultCategoryData.RankArray r : list) {
|
for (ResultCategoryData.RankArray r : list) {
|
||||||
out.getClassement()
|
out.getClassement().add(new ResultCategoryData.ClassementData(r.getRank(), r.getComb(), r.getName()));
|
||||||
.add(new ResultCategoryData.ClassementData(r.getRank(), r.getComb(), r.getName()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -732,100 +724,6 @@ public class ResultService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uni<List<ClubClassement>> getAllClubArray(String uuid) {
|
|
||||||
return getAllClubArray(uuid, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<List<ClubClassement>> getAllClubArray(String uuid, SecurityCtx securityCtx) {
|
|
||||||
return hasAccess(uuid, securityCtx).chain(membreModel -> getAllClubArray(uuid, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uni<List<ClubClassement>> getAllClubArray(String uuid, boolean cache) {
|
|
||||||
List<CardModel> cards = new java.util.ArrayList<>();
|
|
||||||
|
|
||||||
//noinspection unchecked
|
|
||||||
cacheClubClassement.invalidateIf(
|
|
||||||
(p) -> ((Pair<Long, List<ClubClassement>>) p).getKey() > System.currentTimeMillis());
|
|
||||||
|
|
||||||
if (!cache)
|
|
||||||
cacheClubClassement.invalidate(uuid);
|
|
||||||
|
|
||||||
return cacheClubClassement.getAsync(uuid, k -> cardRepository.list("competition.uuid = ?1", uuid)
|
|
||||||
.invoke(__ -> System.out.println("Cache miss for club classement with uuid " + uuid))
|
|
||||||
.invoke(cards::addAll)
|
|
||||||
.chain(__ -> matchRepository.list("category.compet.uuid = ?1", uuid))
|
|
||||||
.chain(matchs -> {
|
|
||||||
HashMap<CategoryModel, List<MatchModel>> map = new HashMap<>();
|
|
||||||
for (MatchModel match : matchs) {
|
|
||||||
if (!map.containsKey(match.getCategory()))
|
|
||||||
map.put(match.getCategory(), new java.util.ArrayList<>());
|
|
||||||
map.get(match.getCategory()).add(match);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Multi.createFrom().iterable(map.entrySet())
|
|
||||||
.onItem().call(entry -> Mutiny.fetch(entry.getKey().getTree()))
|
|
||||||
.map(entry -> {
|
|
||||||
ResultCategoryData tmp = new ResultCategoryData();
|
|
||||||
|
|
||||||
getArray2(entry.getValue().stream().map(m -> new MatchModelExtend(m, cards)).toList(),
|
|
||||||
null, tmp);
|
|
||||||
getClassementArray(entry.getKey(), null, cards, tmp);
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
})
|
|
||||||
.collect().asList();
|
|
||||||
})
|
|
||||||
.map(categoryData -> {
|
|
||||||
HashMap<Long, ClubClassement> clubMap = new HashMap<>();
|
|
||||||
|
|
||||||
categoryData.forEach(c -> c.getClassement().forEach(classementData -> {
|
|
||||||
if (classementData.rank() > 3)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (classementData.comb() != null) {
|
|
||||||
long clubId = 0L;
|
|
||||||
String clubName = "";
|
|
||||||
if (classementData.comb() instanceof MembreModel membreModel2) {
|
|
||||||
clubId = (membreModel2.getClub() != null) ? membreModel2.getClub().getId() : 0;
|
|
||||||
clubName = (membreModel2.getClub() != null) ? membreModel2.getClub().getName() : "";
|
|
||||||
} else if (classementData.comb() instanceof CompetitionGuestModel guestModel) {
|
|
||||||
if (guestModel.getClub() != null && !guestModel.getClub().isBlank())
|
|
||||||
clubId = getClubTempId(guestModel.getClub());
|
|
||||||
clubName = guestModel.getClub();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clubId != 0) {
|
|
||||||
clubMap.putIfAbsent(clubId, new ClubClassement(clubName));
|
|
||||||
ClubClassement entity = clubMap.get(clubId);
|
|
||||||
entity.score[classementData.rank() - 1]++;
|
|
||||||
entity.tt_score += 4 - classementData.rank();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return clubMap.values().stream()
|
|
||||||
.sorted(Comparator.comparingInt((ClubClassement c) -> c.tt_score)
|
|
||||||
.thenComparingInt(c -> c.score[0])
|
|
||||||
.thenComparingInt(c -> c.score[1])
|
|
||||||
.thenComparingInt(c -> c.score[2]).reversed())
|
|
||||||
.toList();
|
|
||||||
})
|
|
||||||
.map(l -> new Pair<>(System.currentTimeMillis() + 60 * 1000L, l))
|
|
||||||
).map(Pair::getValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@RegisterForReflection
|
|
||||||
public static class ClubClassement {
|
|
||||||
String name;
|
|
||||||
Integer[] score = new Integer[]{0, 0, 0};
|
|
||||||
int tt_score = 0;
|
|
||||||
|
|
||||||
public ClubClassement(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
public static class CombStat {
|
public static class CombStat {
|
||||||
public int w;
|
public int w;
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import jakarta.ws.rs.*;
|
|||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Path("api/public/result/{id}")
|
@Path("api/public/result/{id}")
|
||||||
public class ExternalResultEndpoints {
|
public class ExternalResultEndpoints {
|
||||||
@ -74,13 +73,6 @@ public class ExternalResultEndpoints {
|
|||||||
return resultService.getClubList(id);
|
return resultService.getClubList(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/club/classement")
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
public Uni<List<ResultService.ClubClassement>> clubClassement() {
|
|
||||||
return resultService.getAllClubArray(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/club/data")
|
@Path("/club/data")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
|||||||
@ -47,12 +47,6 @@ public class ResultEndpoints {
|
|||||||
return resultService.getClubList(uuid, securityCtx);
|
return resultService.getClubList(uuid, securityCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{uuid}/club/classement")
|
|
||||||
public Uni<List<ResultService.ClubClassement>> getClubClassement(@PathParam("uuid") String uuid) {
|
|
||||||
return resultService.getAllClubArray(uuid, securityCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{uuid}/club/{id}")
|
@Path("{uuid}/club/{id}")
|
||||||
public Uni<ResultService.ClubArrayData> getClub(@PathParam("uuid") String uuid, @PathParam("id") long id) {
|
public Uni<ResultService.ClubArrayData> getClub(@PathParam("uuid") String uuid, @PathParam("id") long id) {
|
||||||
@ -70,7 +64,7 @@ public class ResultEndpoints {
|
|||||||
public Uni<?> getCombList(@PathParam("uuid") String uuid, @PathParam("id") String id) {
|
public Uni<?> getCombList(@PathParam("uuid") String uuid, @PathParam("id") String id) {
|
||||||
return resultService.getCombArrayPublic(uuid, id, securityCtx);
|
return resultService.getCombArrayPublic(uuid, id, securityCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{uuid}/comb")
|
@Path("{uuid}/comb")
|
||||||
public Uni<ResultService.CombsArrayData> getComb(@PathParam("uuid") String uuid) {
|
public Uni<ResultService.CombsArrayData> getComb(@PathParam("uuid") String uuid) {
|
||||||
|
|||||||
@ -99,11 +99,6 @@ public class RPDF {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@WSReceiver(code = "getPodiumClub", permission = PermLevel.VIEW)
|
|
||||||
public Uni<List<ResultService.ClubClassement>> getPodiumClub(WebSocketConnection connection, Object o) {
|
|
||||||
return resultService.getAllClubArray(connection.pathParam("uuid"), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
public static record PodiumEntity(String poule_name, String source, Categorie categorie,
|
public static record PodiumEntity(String poule_name, String source, Categorie categorie,
|
||||||
List<ResultCategoryData.ClassementData> podium) {
|
List<ResultCategoryData.ClassementData> podium) {
|
||||||
|
|||||||
713
src/main/webapp/package-lock.json
generated
713
src/main/webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,42 +13,42 @@
|
|||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@fortawesome/fontawesome-svg-core": "^7.2.0",
|
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "^7.2.0",
|
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "^7.2.0",
|
"@fortawesome/free-regular-svg-icons": "^7.1.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^7.2.0",
|
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
||||||
"@fortawesome/react-fontawesome": "^3.2.0",
|
"@fortawesome/react-fontawesome": "^3.1.1",
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.2",
|
||||||
"browser-image-compression": "^2.0.2",
|
"browser-image-compression": "^2.0.2",
|
||||||
"i18next": "^25.8.14",
|
"i18next": "^25.8.0",
|
||||||
"i18next-browser-languagedetector": "^8.2.1",
|
"i18next-browser-languagedetector": "^8.2.0",
|
||||||
"i18next-http-backend": "^3.0.2",
|
"i18next-http-backend": "^3.0.2",
|
||||||
"jspdf": "^4.2.0",
|
"jspdf": "^4.1.0",
|
||||||
"jspdf-autotable": "^5.0.7",
|
"jspdf-autotable": "^5.0.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"obs-websocket-js": "^5.0.7",
|
"obs-websocket-js": "^5.0.7",
|
||||||
"proj4": "^2.20.3",
|
"proj4": "^2.20.2",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.3",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.3",
|
||||||
"react-i18next": "^16.5.5",
|
"react-i18next": "^16.5.3",
|
||||||
"react-is": "^19.2.4",
|
"react-is": "^19.2.3",
|
||||||
"react-leaflet": "^5.0.0",
|
"react-leaflet": "^5.0.0",
|
||||||
"react-loader-spinner": "^8.0.2",
|
"react-loader-spinner": "^8.0.2",
|
||||||
"react-router-dom": "^7.13.1",
|
"react-router-dom": "^7.12.0",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"recharts": "^3.7.0",
|
"recharts": "^3.7.0",
|
||||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||||
"xlsx-js-style": "^1.2.0"
|
"xlsx-js-style": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.9",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^5.1.4",
|
"@vitejs/plugin-react": "^5.1.2",
|
||||||
"eslint": "^10.0.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
"eslint-plugin-react-refresh": "^0.4.26",
|
||||||
"vite": "^7.3.1"
|
"vite": "^7.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,9 +39,6 @@ function setSubPage(name) {
|
|||||||
case 'club':
|
case 'club':
|
||||||
clubPage(location);
|
clubPage(location);
|
||||||
break;
|
break;
|
||||||
case 'clubRank':
|
|
||||||
clubRankPage();
|
|
||||||
break;
|
|
||||||
case 'all':
|
case 'all':
|
||||||
combsPage();
|
combsPage();
|
||||||
break;
|
break;
|
||||||
@ -57,7 +54,6 @@ function homePage() {
|
|||||||
<li><a id="pouleLink" href="javascript:void(0);">${i18next.t('parCatégorie')}</a></li>
|
<li><a id="pouleLink" href="javascript:void(0);">${i18next.t('parCatégorie')}</a></li>
|
||||||
<li><a id="combLink" href="javascript:void(0);">${i18next.t('parCombattant')}</a></li>
|
<li><a id="combLink" href="javascript:void(0);">${i18next.t('parCombattant')}</a></li>
|
||||||
<li><a id="clubLink" href="javascript:void(0);">${i18next.t('parClub')}</a></li>
|
<li><a id="clubLink" href="javascript:void(0);">${i18next.t('parClub')}</a></li>
|
||||||
<li><a id="clubClassement" href="javascript:void(0);">${i18next.t('classementClub')}</a></li>
|
|
||||||
<li><a id="allLink" href="javascript:void(0);">${i18next.t('tousLesCombattants')}</a></li>
|
<li><a id="allLink" href="javascript:void(0);">${i18next.t('tousLesCombattants')}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
`
|
`
|
||||||
@ -66,7 +62,6 @@ function homePage() {
|
|||||||
document.getElementById('pouleLink').addEventListener('click', () => setSubPage('poule'));
|
document.getElementById('pouleLink').addEventListener('click', () => setSubPage('poule'));
|
||||||
document.getElementById('combLink').addEventListener('click', () => setSubPage('comb'));
|
document.getElementById('combLink').addEventListener('click', () => setSubPage('comb'));
|
||||||
document.getElementById('clubLink').addEventListener('click', () => setSubPage('club'));
|
document.getElementById('clubLink').addEventListener('click', () => setSubPage('club'));
|
||||||
document.getElementById('clubClassement').addEventListener('click', () => setSubPage('clubRank'));
|
|
||||||
document.getElementById('allLink').addEventListener('click', () => setSubPage('all'));
|
document.getElementById('allLink').addEventListener('click', () => setSubPage('all'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,60 +640,6 @@ function clubPage(location) {
|
|||||||
rootDiv.append(content)
|
rootDiv.append(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildClubsView(clubs) {
|
|
||||||
const pouleDiv = document.createElement('div');
|
|
||||||
let arrayContent = `
|
|
||||||
<h3>${i18next.t('classementDesClub')} :</h3>
|
|
||||||
|
|
||||||
<figure class="wp-block-table is-style-stripes" style="font-size: 16px">
|
|
||||||
<table style="width: 1200px;overflow: auto"><thead>
|
|
||||||
<tr>
|
|
||||||
<th class="has-text-align-center" data-align="center">${i18next.t('club')}</th>
|
|
||||||
<th class="has-text-align-center" data-align="center">${i18next.t('1er')}</th>
|
|
||||||
<th class="has-text-align-center" data-align="center">${i18next.t('2eme')}</th>
|
|
||||||
<th class="has-text-align-center" data-align="center">${i18next.t('3eme')}</th>
|
|
||||||
<th class="has-text-align-center" data-align="center">${i18next.t('scores')}</th>
|
|
||||||
</tr>
|
|
||||||
</thead><tbody>`
|
|
||||||
for (const club of clubs) {
|
|
||||||
arrayContent += `
|
|
||||||
<tr>
|
|
||||||
<td class="has-text-align-center" data-align="center">${club.name}</td>
|
|
||||||
<td class="has-text-align-center" data-align="center">${club.score[0]}</td>
|
|
||||||
<td class="has-text-align-center" data-align="center">${club.score[1]}</td>
|
|
||||||
<td class="has-text-align-center" data-align="center">${club.score[2]}</td>
|
|
||||||
<td class="has-text-align-center" data-align="center">${club.tt_score}</td>
|
|
||||||
</tr>`
|
|
||||||
}
|
|
||||||
arrayContent += `</tbody></table></figure>`
|
|
||||||
pouleDiv.innerHTML = arrayContent;
|
|
||||||
return pouleDiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clubRankPage() {
|
|
||||||
rootDiv.innerHTML = `<h4>${i18next.t('résultatDeLaCompétition')} :</h4><a id="homeLink" href="javascript:void(0);">${i18next.t('back')}</a>`;
|
|
||||||
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
|
|
||||||
|
|
||||||
const content = document.createElement('div');
|
|
||||||
content.style.marginTop = '1em';
|
|
||||||
|
|
||||||
const dataContainer = document.createElement('div');
|
|
||||||
dataContainer.id = 'data-container';
|
|
||||||
|
|
||||||
const loading = startLoading(content);
|
|
||||||
fetch(`${apiUrlRoot}/club/classement`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(clubs => {
|
|
||||||
console.log(clubs);
|
|
||||||
dataContainer.replaceChildren(buildClubsView(clubs));
|
|
||||||
})
|
|
||||||
.catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDeLaListe'))))
|
|
||||||
.finally(() => stopLoading(loading));
|
|
||||||
|
|
||||||
content.append(dataContainer);
|
|
||||||
rootDiv.append(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildCombsView(combs) {
|
function buildCombsView(combs) {
|
||||||
const pouleDiv = document.createElement('div');
|
const pouleDiv = document.createElement('div');
|
||||||
let arrayContent = `
|
let arrayContent = `
|
||||||
|
|||||||
@ -120,8 +120,6 @@
|
|||||||
"obs.préfixDesSources": "Source prefix",
|
"obs.préfixDesSources": "Source prefix",
|
||||||
"pays": "Country",
|
"pays": "Country",
|
||||||
"personnaliser": "Personalize",
|
"personnaliser": "Personalize",
|
||||||
"podium": "Podium",
|
|
||||||
"podiumDesClubs": "Club podium",
|
|
||||||
"poids": "Weight",
|
"poids": "Weight",
|
||||||
"poule": "Pool",
|
"poule": "Pool",
|
||||||
"poulePour": "Pool for: ",
|
"poulePour": "Pool for: ",
|
||||||
|
|||||||
@ -2,9 +2,6 @@
|
|||||||
"--sélectionnerUnClub--": "--Select a club--",
|
"--sélectionnerUnClub--": "--Select a club--",
|
||||||
"--sélectionnerUnCombattant--": "--Select a fighter--",
|
"--sélectionnerUnCombattant--": "--Select a fighter--",
|
||||||
"--sélectionnerUneCatégorie--": "--Select a category--",
|
"--sélectionnerUneCatégorie--": "--Select a category--",
|
||||||
"1er": "Gold medal",
|
|
||||||
"2eme": "Silver medal",
|
|
||||||
"3eme": "Bronze medal",
|
|
||||||
"abs.": "abs.",
|
"abs.": "abs.",
|
||||||
"adversaire": "Opponent",
|
"adversaire": "Opponent",
|
||||||
"aujourdhuià": "Today at {{time}}",
|
"aujourdhuià": "Today at {{time}}",
|
||||||
@ -14,9 +11,7 @@
|
|||||||
"catégorie": "Category",
|
"catégorie": "Category",
|
||||||
"chargement": "Loading",
|
"chargement": "Loading",
|
||||||
"classement": "Ranking",
|
"classement": "Ranking",
|
||||||
"classementClub": "Club ranking",
|
"classementFinal": "Final standings",
|
||||||
"classementDesClub": "Clubs ranking",
|
|
||||||
"classementFinal": "Final ranking",
|
|
||||||
"club": "Club",
|
"club": "Club",
|
||||||
"combattant": "Fighter",
|
"combattant": "Fighter",
|
||||||
"combattants": "Fighters",
|
"combattants": "Fighters",
|
||||||
|
|||||||
@ -120,8 +120,6 @@
|
|||||||
"obs.préfixDesSources": "Préfix des sources",
|
"obs.préfixDesSources": "Préfix des sources",
|
||||||
"pays": "Pays",
|
"pays": "Pays",
|
||||||
"personnaliser": "Personnaliser",
|
"personnaliser": "Personnaliser",
|
||||||
"podium": "Podium",
|
|
||||||
"podiumDesClubs": "Podium des clubs",
|
|
||||||
"poids": "Poids",
|
"poids": "Poids",
|
||||||
"poule": "Poule",
|
"poule": "Poule",
|
||||||
"poulePour": "Poule pour: ",
|
"poulePour": "Poule pour: ",
|
||||||
|
|||||||
@ -2,9 +2,6 @@
|
|||||||
"--sélectionnerUnClub--": "--Sélectionner un club--",
|
"--sélectionnerUnClub--": "--Sélectionner un club--",
|
||||||
"--sélectionnerUnCombattant--": "--Sélectionner un combattant--",
|
"--sélectionnerUnCombattant--": "--Sélectionner un combattant--",
|
||||||
"--sélectionnerUneCatégorie--": "--Sélectionner une catégorie--",
|
"--sélectionnerUneCatégorie--": "--Sélectionner une catégorie--",
|
||||||
"1er": "Médaille d'or",
|
|
||||||
"2eme": "Médaille d'argent",
|
|
||||||
"3eme": "Médaille de bronze",
|
|
||||||
"abs.": "abs.",
|
"abs.": "abs.",
|
||||||
"adversaire": "Adversaire",
|
"adversaire": "Adversaire",
|
||||||
"aujourdhuià": "Aujourd'hui à {{time}}",
|
"aujourdhuià": "Aujourd'hui à {{time}}",
|
||||||
@ -14,8 +11,6 @@
|
|||||||
"catégorie": "Catégorie",
|
"catégorie": "Catégorie",
|
||||||
"chargement": "Chargement",
|
"chargement": "Chargement",
|
||||||
"classement": "Classement",
|
"classement": "Classement",
|
||||||
"classementClub": "Classement club",
|
|
||||||
"classementDesClub": "Classement des club",
|
|
||||||
"classementFinal": "Classement final",
|
"classementFinal": "Classement final",
|
||||||
"club": "Club",
|
"club": "Club",
|
||||||
"combattant": "Combattant",
|
"combattant": "Combattant",
|
||||||
|
|||||||
@ -1,84 +0,0 @@
|
|||||||
class ProcessorDTMF extends AudioWorkletProcessor {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.sampleRate = sampleRate;
|
|
||||||
this.symbolDuration = 0.03; // 50 ms par symbole
|
|
||||||
this.samplesPerSymbol = Math.floor(this.sampleRate * this.symbolDuration);
|
|
||||||
this.encodeLowPrio = [];
|
|
||||||
this.symbolSamples = [];
|
|
||||||
this.lastBlackStep = 0;
|
|
||||||
this.port.onmessage = (e) => {
|
|
||||||
if (e.data.type === 'encode') {
|
|
||||||
this.symbolSamples.push(...this.encodeSymbols(e.data.symbols));
|
|
||||||
this.symbolSamples.push(...this.encodeBlack(this.sampleRate * 0.02));
|
|
||||||
}
|
|
||||||
if (e.data.type === 'encodeLowPrio') {
|
|
||||||
this.encodeLowPrio.push(e.data.symbols);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
dtmfFrequencies = [
|
|
||||||
[697, 770, 852, 941], // Fréquences basses
|
|
||||||
[1209, 1336, 1477, 1633] // Fréquences hautes
|
|
||||||
];
|
|
||||||
|
|
||||||
encodeSymbols(symbols) {
|
|
||||||
const samples = [];
|
|
||||||
for (const symbol of symbols) {
|
|
||||||
const lf = this.dtmfFrequencies[0][symbol % 4]; // Fréquence basse
|
|
||||||
const hf = this.dtmfFrequencies[1][Math.floor(symbol / 4)]; // Fréquence haute
|
|
||||||
// console.log(`Symbol: ${symbol}, LF: ${lf} Hz, HF: ${hf} Hz`);
|
|
||||||
for (let i = 0; i < this.samplesPerSymbol; i++) {
|
|
||||||
const t = i / this.sampleRate;
|
|
||||||
const t2 = (this.lastBlackStep + i) / this.sampleRate;
|
|
||||||
samples.push(0.5 * Math.sin(2 * Math.PI * lf * t) + 0.5 * Math.sin(2 * Math.PI * hf * t) // Signal DTMF
|
|
||||||
+ Math.sin(2 * Math.PI * 150 * t2) * (0.0625 * (Math.sin(2 * Math.PI * 0.5 * t2) + 1))); // Ajouter un signal à 150 Hz pour le "black"
|
|
||||||
}
|
|
||||||
this.lastBlackStep += this.samplesPerSymbol;
|
|
||||||
|
|
||||||
// ajouter un silence de 10 ms entre les symboles
|
|
||||||
samples.push(...this.encodeBlack(this.sampleRate * 0.01)); // Silence
|
|
||||||
}
|
|
||||||
return samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
encodeBlack(size) {
|
|
||||||
const samples = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
const t = (this.lastBlackStep + i) / this.sampleRate;
|
|
||||||
samples.push(Math.sin(2 * Math.PI * 150 * t) * (0.0625 * (Math.sin(2 * Math.PI * 0.5 * t) + 1))); // Signal à 350 Hz pour le "black"
|
|
||||||
}
|
|
||||||
this.lastBlackStep += size;
|
|
||||||
this.lastBlackStep %= this.sampleRate * 2; // Réinitialiser tous les 2 secondes pour éviter les débordements
|
|
||||||
|
|
||||||
return samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
process(inputs, outputs, parameters) {
|
|
||||||
const output = outputs[0]; // output est un tableau de canaux (ex: [Float32Array, ...])
|
|
||||||
const channelData = output[0]; // Accéder au premier canal (mono)
|
|
||||||
|
|
||||||
if (this.symbolSamples.length === 0 && this.encodeLowPrio.length > 0) {
|
|
||||||
this.symbolSamples.push(...this.encodeSymbols(this.encodeLowPrio.shift()));
|
|
||||||
this.symbolSamples.push(...this.encodeBlack(this.sampleRate * 0.02));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.symbolSamples.length === 0) {
|
|
||||||
const samples = this.encodeBlack(channelData.length)
|
|
||||||
for (let i = 0; i < channelData.length; i++) {
|
|
||||||
channelData[i] = samples[i] || 0;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < channelData.length; i++) {
|
|
||||||
channelData[i] = this.symbolSamples.shift() || 0; // Prendre le prochain échantillon ou 0 si vide
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerProcessor('dtmf-processor', ProcessorDTMF);
|
|
||||||
@ -1,207 +0,0 @@
|
|||||||
import React, {useEffect, useRef, useState} from 'react';
|
|
||||||
import {useTablesState} from "../../pages/competition/editor/StateWindow.jsx";
|
|
||||||
import {timePrint} from "../../utils/Tools.js";
|
|
||||||
|
|
||||||
let initialized = false;
|
|
||||||
const AudioEncoder = () => {
|
|
||||||
const audioContextRef = useRef(null);
|
|
||||||
const qpskProcessorRef = useRef(null);
|
|
||||||
const [isReady, setIsReady] = useState(false);
|
|
||||||
const [table, setTable] = useState('1');
|
|
||||||
|
|
||||||
const lastSend = useRef({id: 0});
|
|
||||||
const {state} = useTablesState();
|
|
||||||
|
|
||||||
// Initialisation de l'AudioContext et du AudioWorklet
|
|
||||||
useEffect(() => {
|
|
||||||
const initAudio = async () => {
|
|
||||||
if (initialized)
|
|
||||||
return;
|
|
||||||
console.log("Initialisation de l'audio après interaction utilisateur");
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
||||||
console.log("AudioContext state:", audioContext.state);
|
|
||||||
|
|
||||||
if (audioContext.state === 'suspended') {
|
|
||||||
await audioContext.resume(); // Nécessaire pour démarrer le contexte audio
|
|
||||||
console.log("AudioContext resumed");
|
|
||||||
}
|
|
||||||
|
|
||||||
await audioContext.audioWorklet.addModule('/processor-dtmf.js');
|
|
||||||
await new Promise(r => setTimeout(r, 100));
|
|
||||||
const processor = new AudioWorkletNode(audioContext, 'dtmf-processor');
|
|
||||||
processor.connect(audioContext.destination);
|
|
||||||
qpskProcessorRef.current = processor;
|
|
||||||
audioContextRef.current = audioContext;
|
|
||||||
|
|
||||||
await new Promise(r => setTimeout(r, 100));
|
|
||||||
setIsReady(true);
|
|
||||||
} catch (err) {
|
|
||||||
initialized = false;
|
|
||||||
console.error("Erreur d'initialisation AudioWorklet:", err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialiser après un clic utilisateur (pour contourner les restrictions des navigateurs)
|
|
||||||
const handleUserInteraction = () => {
|
|
||||||
document.removeEventListener('click', handleUserInteraction);
|
|
||||||
initAudio();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('click', handleUserInteraction);
|
|
||||||
return () => {
|
|
||||||
if (audioContextRef.current?.state !== 'closed') {
|
|
||||||
audioContextRef.current?.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Fonction pour encoder et envoyer un message
|
|
||||||
const encodeAndSend = (data, lowPrio = false) => {
|
|
||||||
if (!isReady) return;
|
|
||||||
|
|
||||||
const symbols = Array.from(data).flatMap(byte => [byte >> 4, byte & 0x0F]);
|
|
||||||
console.log("Bits :", symbols);
|
|
||||||
|
|
||||||
// 5. Envoyer les symboles au processeur audio
|
|
||||||
if (lowPrio) {
|
|
||||||
qpskProcessorRef.current.port.postMessage({type: 'encodeLowPrio', symbols});
|
|
||||||
} else {
|
|
||||||
qpskProcessorRef.current.port.postMessage({type: 'encode', symbols});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const t = state.find(o => o.liceName === table)
|
|
||||||
if (!t)
|
|
||||||
return
|
|
||||||
|
|
||||||
// console.log("Data for table 1:", t, t.selectedMatch)
|
|
||||||
const last = lastSend.current;
|
|
||||||
if (t.selectedMatch !== last.id) {
|
|
||||||
clearTimeout(last.time_id)
|
|
||||||
last.time_id = setTimeout(() => {
|
|
||||||
last.time_id = null
|
|
||||||
if (t.selectedMatch === null) {
|
|
||||||
encodeAndSend(new Uint8Array([0, 0, 0, 0, 0, 0, 0]));
|
|
||||||
} else {
|
|
||||||
const data = [];
|
|
||||||
for (let i = 0; i < 7; i++) { // MaxSafeInteger est sur 7 bytes (53 bits de précision)
|
|
||||||
data.unshift(Number((BigInt(t.selectedMatch) >> BigInt(i * 8)) & 0xFFn))
|
|
||||||
}
|
|
||||||
data[0] = data[0] & 0x1F // 3 premiers bits à 0 pour différencier des autres types de messages (ex: score, chrono, etc.)
|
|
||||||
// console.log("Data to send (selectedMatch):", data)
|
|
||||||
encodeAndSend(new Uint8Array(data), false);
|
|
||||||
}
|
|
||||||
}, 250)
|
|
||||||
|
|
||||||
last.id = t.selectedMatch
|
|
||||||
}
|
|
||||||
|
|
||||||
const isRunning = (c) => c.startTime !== 0
|
|
||||||
const getTime = (c) => {
|
|
||||||
if (c.startTime === 0)
|
|
||||||
return c.time
|
|
||||||
return c.time + Date.now() - c.startTime
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeStr = last.chronoState ? timePrint((last.chronoState.state === 2) ? last.chronoState.configPause : last.chronoState.configTime - getTime(last.chronoState)) : "-"
|
|
||||||
const timeStr2 = timePrint((t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState))
|
|
||||||
|
|
||||||
if (timeStr !== timeStr2) {
|
|
||||||
clearInterval(lastSend.current.time_chronoInter)
|
|
||||||
clearTimeout(lastSend.current.time_chronoText)
|
|
||||||
lastSend.current.time_chronoText = setTimeout(() => {
|
|
||||||
let time = (t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState)
|
|
||||||
const ms = time % 1000
|
|
||||||
time = (time - ms) / 1000
|
|
||||||
|
|
||||||
const data = [((time >> 8) & 0x1F) + 0x20, time & 0xFF];
|
|
||||||
// console.log("Data to send (time):", data)
|
|
||||||
encodeAndSend(new Uint8Array(data));
|
|
||||||
|
|
||||||
lastSend.current.time_chronoInter = setInterval(() => {
|
|
||||||
let time = (t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState)
|
|
||||||
const ms = time % 1000
|
|
||||||
time = (time - ms) / 1000
|
|
||||||
|
|
||||||
const data = [((time >> 8) & 0x1F) + 0x20, time & 0xFF];
|
|
||||||
// console.log("Data to send (time-auto):", data)
|
|
||||||
encodeAndSend(new Uint8Array(data), true);
|
|
||||||
}, 10000);
|
|
||||||
}, 150)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!last.chronoState || last.chronoState.state !== t.chronoState.state || isRunning(last.chronoState) !== isRunning(t.chronoState)) {
|
|
||||||
let time = (t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState)
|
|
||||||
const ms = Math.round((time % 1000) / 250)
|
|
||||||
|
|
||||||
const data = [0x40 + (t.chronoState.state << 3) + (isRunning(t.chronoState) << 2) + (ms & 0x03)];
|
|
||||||
// console.log("Data to send (chrono state):", data)
|
|
||||||
encodeAndSend(new Uint8Array(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
last.chronoState = {...t.chronoState}
|
|
||||||
|
|
||||||
// console.log(timeStr, timeStr2)
|
|
||||||
// console.log(last.chronoState, t.chronoState)
|
|
||||||
|
|
||||||
if (last.scoreRouge !== t.scoreState.scoreRouge) {
|
|
||||||
clearTimeout(last.time_sr)
|
|
||||||
last.time_sr = setTimeout(() => {
|
|
||||||
if (last.scoreRouge !== t.scoreState.scoreRouge) {
|
|
||||||
const b = t.scoreState.scoreRouge < 0
|
|
||||||
const s = b ? -t.scoreState.scoreRouge : t.scoreState.scoreRouge
|
|
||||||
const data = [0x60 + (b << 3) + ((s >> 8) & 0x07), (s & 0xFF)];
|
|
||||||
console.log("Data to send (score r):", data)
|
|
||||||
encodeAndSend(new Uint8Array(data), true);
|
|
||||||
last.scoreRouge = t.scoreState.scoreRouge
|
|
||||||
}
|
|
||||||
}, 250)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last.scoreBleu !== t.scoreState.scoreBleu) {
|
|
||||||
clearTimeout(last.time_sb)
|
|
||||||
last.time_sb = setTimeout(() => {
|
|
||||||
if (last.scoreBleu !== t.scoreState.scoreBleu) {
|
|
||||||
const b = t.scoreState.scoreBleu < 0
|
|
||||||
const s = b ? -t.scoreState.scoreBleu : t.scoreState.scoreBleu
|
|
||||||
const data = [0x60 + 0x10 + (b << 3) + ((s >> 8) & 0x07), (s & 0xFF)];
|
|
||||||
console.log("Data to send (score b):", data)
|
|
||||||
encodeAndSend(new Uint8Array(data), true);
|
|
||||||
last.scoreBleu = t.scoreState.scoreBleu
|
|
||||||
}
|
|
||||||
}, 250)
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [state])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const last = lastSend.current;
|
|
||||||
clearTimeout(last.scoreBleu)
|
|
||||||
clearTimeout(last.scoreRouge)
|
|
||||||
clearTimeout(last.time_id)
|
|
||||||
clearTimeout(last.time_chronoText)
|
|
||||||
clearInterval(last.time_chronoInter)
|
|
||||||
last.id = 0
|
|
||||||
last.scoreBleu = 0
|
|
||||||
last.scoreRouge = 0
|
|
||||||
last.chronoState = null
|
|
||||||
}, [table])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={table}
|
|
||||||
onChange={(e) => setTable(e.target.value)}
|
|
||||||
placeholder="Nom de la zone"
|
|
||||||
/>
|
|
||||||
<span>{isReady ? 'Actif' : "Zone non configurée"}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AudioEncoder;
|
|
||||||
@ -407,7 +407,6 @@ function PrintModal({menuActions}) {
|
|||||||
const [allCatEmpty, setAllCatEmpty] = useState(false);
|
const [allCatEmpty, setAllCatEmpty] = useState(false);
|
||||||
const [podium, setPodium] = useState(false);
|
const [podium, setPodium] = useState(false);
|
||||||
const [podiumRank, setPodiumRank] = useState(4);
|
const [podiumRank, setPodiumRank] = useState(4);
|
||||||
const [podiumClub, setPodiumClub] = useState(false);
|
|
||||||
|
|
||||||
const [presetSelect, setPresetSelect] = useState(-1)
|
const [presetSelect, setPresetSelect] = useState(-1)
|
||||||
|
|
||||||
@ -417,20 +416,12 @@ function PrintModal({menuActions}) {
|
|||||||
|
|
||||||
const podiumPromise = (podiumRank_) => {
|
const podiumPromise = (podiumRank_) => {
|
||||||
return sendRequest("getPodium", {}).then(data => {
|
return sendRequest("getPodium", {}).then(data => {
|
||||||
return [welcomeData?.name + " - " + t('podium'), [
|
return [welcomeData?.name + " - " + "Podium", [
|
||||||
{type: "podium", params: ({data, maxRank: podiumRank_, minRank: Math.min(4, podiumRank_)})},
|
{type: "podium", params: ({data, maxRank: podiumRank_, minRank: Math.min(4, podiumRank_)})},
|
||||||
]];
|
]];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const podiumClubPromise = () => {
|
|
||||||
return sendRequest("getPodiumClub", {}).then(data => {
|
|
||||||
return [welcomeData?.name + " - " + t('classementDesClub', {ns: "result"}), [
|
|
||||||
{type: "podiumClub", params: ({data})},
|
|
||||||
]];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const print = (action) => {
|
const print = (action) => {
|
||||||
const pagesPromise = [];
|
const pagesPromise = [];
|
||||||
|
|
||||||
@ -446,9 +437,6 @@ function PrintModal({menuActions}) {
|
|||||||
if (podium)
|
if (podium)
|
||||||
pagesPromise.push(podiumPromise(podiumRank));
|
pagesPromise.push(podiumPromise(podiumRank));
|
||||||
|
|
||||||
if (podiumClub)
|
|
||||||
pagesPromise.push(podiumClubPromise());
|
|
||||||
|
|
||||||
toast.promise(
|
toast.promise(
|
||||||
toDataURL("/Logo-FFSAF-2023.png").then(logo => {
|
toDataURL("/Logo-FFSAF-2023.png").then(logo => {
|
||||||
return Promise.allSettled(pagesPromise).then(results => {
|
return Promise.allSettled(pagesPromise).then(results => {
|
||||||
@ -525,7 +513,7 @@ function PrintModal({menuActions}) {
|
|||||||
<div className="form-check">
|
<div className="form-check">
|
||||||
<input className="form-check-input" type="checkbox" checked={podium} id="checkPrint7"
|
<input className="form-check-input" type="checkbox" checked={podium} id="checkPrint7"
|
||||||
onChange={e => setPodium(e.target.checked)}/>
|
onChange={e => setPodium(e.target.checked)}/>
|
||||||
<label className="form-check-label" htmlFor="checkPrint7">{t('podium')}</label>
|
<label className="form-check-label" htmlFor="checkPrint7">Podium</label>
|
||||||
</div>
|
</div>
|
||||||
{podium &&
|
{podium &&
|
||||||
<div style={{marginLeft: "1em"}}>
|
<div style={{marginLeft: "1em"}}>
|
||||||
@ -533,12 +521,6 @@ function PrintModal({menuActions}) {
|
|||||||
<input type="range" className="form-range" min="1" max="20" step="1" id="range3" value={podiumRank}
|
<input type="range" className="form-range" min="1" max="20" step="1" id="range3" value={podiumRank}
|
||||||
onChange={e => setPodiumRank(Number(e.target.value))}/>
|
onChange={e => setPodiumRank(Number(e.target.value))}/>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
<div className="form-check">
|
|
||||||
<input className="form-check-input" type="checkbox" checked={podiumClub} id="checkPrint8"
|
|
||||||
onChange={e => setPodiumClub(e.target.checked)}/>
|
|
||||||
<label className="form-check-label" htmlFor="checkPrint8">{t('classementDesClub', {ns: "result"})}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-footer">
|
<div className="modal-footer">
|
||||||
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal" onClick={() => print("show")}>{t('afficher')}</button>
|
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal" onClick={() => print("show")}>{t('afficher')}</button>
|
||||||
|
|||||||
@ -568,9 +568,7 @@ function MatchList({matches, cat, groups, reducer, classement = false}) {
|
|||||||
|
|
||||||
const {active, over} = event;
|
const {active, over} = event;
|
||||||
if (active.id !== over.id) {
|
if (active.id !== over.id) {
|
||||||
let newIndex = marches2.findIndex(m => m.id === over.id);
|
const newIndex = marches2.findIndex(m => m.id === over.id);
|
||||||
if (newIndex > 0)
|
|
||||||
newIndex = marches2[newIndex].categorie_ord;
|
|
||||||
reducer({type: 'REORDER', payload: {id: active.id, pos: newIndex}});
|
reducer({type: 'REORDER', payload: {id: active.id, pos: newIndex}});
|
||||||
sendRequest('updateMatchOrder', {id: active.id, pos: newIndex}).then(__ => {
|
sendRequest('updateMatchOrder', {id: active.id, pos: newIndex}).then(__ => {
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,7 +11,6 @@ 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";
|
import {CardsProvider} from "../../../hooks/useCard.jsx";
|
||||||
import AudioEncoder from "../../../components/cm/AudioEncoder.jsx";
|
|
||||||
|
|
||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
|
|
||||||
@ -74,7 +73,6 @@ function HomeComp() {
|
|||||||
<Route path="/" element={<Home2 perm={perm}/>}/>
|
<Route path="/" element={<Home2 perm={perm}/>}/>
|
||||||
<Route path="/admin" element={<CMAdmin compUuid={compUuid}/>}/>
|
<Route path="/admin" element={<CMAdmin compUuid={compUuid}/>}/>
|
||||||
<Route path="/table" element={<CMTable/>}/>
|
<Route path="/table" element={<CMTable/>}/>
|
||||||
<Route path="/view/audio" element={<AudioEncoder/>}/>
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</LoadingProvider>
|
</LoadingProvider>
|
||||||
</CardsProvider>
|
</CardsProvider>
|
||||||
|
|||||||
@ -19,7 +19,7 @@ function reducer(state, action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTablesState() {
|
export function StateWindow({document}) {
|
||||||
const {sendRequest, dispatch} = useWS();
|
const {sendRequest, dispatch} = useWS();
|
||||||
const [state, dispatchState] = useReducer(reducer, [])
|
const [state, dispatchState] = useReducer(reducer, [])
|
||||||
|
|
||||||
@ -58,12 +58,6 @@ export function useTablesState() {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return {state};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function StateWindow({document}) {
|
|
||||||
const {state} = useTablesState();
|
|
||||||
|
|
||||||
document.title = "État des tables de marque";
|
document.title = "État des tables de marque";
|
||||||
document.body.className = "overflow-hidden";
|
document.body.className = "overflow-hidden";
|
||||||
|
|
||||||
@ -72,7 +66,7 @@ export function StateWindow({document}) {
|
|||||||
<div className="d-flex flex-row flex-wrap justify-content-around align-items-center align-content-around h-100 p-2 overflow-auto">
|
<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) =>
|
{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"}}>
|
<div key={index} className="card d-inline-flex flex-grow-1 align-self-stretch" style={{minWidth: "25em", maxWidth: "30em"}}>
|
||||||
<ShowState table={table}/>
|
<ShowState table={table} dispatch={dispatchState}/>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -197,7 +191,7 @@ function PrintChrono({chrono}) {
|
|||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
let currentDuration = chrono.configTime
|
let currentDuration = chrono.configTime
|
||||||
if (chrono.state === 2) {
|
if (chrono.state === 2) {
|
||||||
currentDuration = chrono.configPause
|
currentDuration = (chrono.state === 0) ? 10000 : chrono.configPause
|
||||||
}
|
}
|
||||||
const timeStr = (chrono.state === 1 ? " Match - " : " Pause - ") + timePrint(currentDuration - getTime()) + (isRunning() ? "" : " (arrêté)")
|
const timeStr = (chrono.state === 1 ? " Match - " : " Pause - ") + timePrint(currentDuration - getTime()) + (isRunning() ? "" : " (arrêté)")
|
||||||
|
|
||||||
|
|||||||
@ -39,8 +39,7 @@ export function ResultView() {
|
|||||||
{resultShow && resultShow === "cat" && <CategoryList uuid={uuid}/>
|
{resultShow && resultShow === "cat" && <CategoryList uuid={uuid}/>
|
||||||
|| resultShow && resultShow === "club" && <ClubList uuid={uuid}/>
|
|| resultShow && resultShow === "club" && <ClubList uuid={uuid}/>
|
||||||
|| resultShow && resultShow === "comb" && <CombList uuid={uuid}/>
|
|| resultShow && resultShow === "comb" && <CombList uuid={uuid}/>
|
||||||
|| resultShow && resultShow === "combs" && <CombsResult uuid={uuid}/>
|
|| resultShow && resultShow === "combs" && <CombsResult uuid={uuid}/>}
|
||||||
|| resultShow && resultShow === "clubs" && <ClubsResult uuid={uuid}/>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@ -67,10 +66,6 @@ function MenuBar({resultShow, setResultShow}) {
|
|||||||
<a className={"nav-link my-1" + (resultShow === "combs" ? " active" : "")} aria-current={(resultShow === "combs" ? " page" : "false")}
|
<a className={"nav-link my-1" + (resultShow === "combs" ? " active" : "")} aria-current={(resultShow === "combs" ? " page" : "false")}
|
||||||
href="#" onClick={_ => setResultShow("combs")}>{t('combattants')}</a>
|
href="#" onClick={_ => setResultShow("combs")}>{t('combattants')}</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="nav-item">
|
|
||||||
<a className={"nav-link my-1" + (resultShow === "clubs" ? " active" : "")} aria-current={(resultShow === "clubs" ? " page" : "false")}
|
|
||||||
href="#" onClick={_ => setResultShow("clubs")}>{t('classementClub')}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -455,43 +450,6 @@ function CombResult({uuid, combId}) {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function ClubsResult({uuid}) {
|
|
||||||
const setLoading = useLoadingSwitcher()
|
|
||||||
const {data, error} = useFetch(`/result/${uuid}/club/classement`, setLoading, 1)
|
|
||||||
const {t} = useTranslation('result');
|
|
||||||
|
|
||||||
return <>
|
|
||||||
{data ? <>
|
|
||||||
<h3>{t('classementDesClub')} :</h3>
|
|
||||||
|
|
||||||
<table className="table table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col" style={{textAlign: "center"}}>{t('club')}</th>
|
|
||||||
<th scope="col" style={{textAlign: "center"}}>{t('1er')}</th>
|
|
||||||
<th scope="col" style={{textAlign: "center"}}>{t('2eme')}</th>
|
|
||||||
<th scope="col" style={{textAlign: "center"}}>{t('3eme')}</th>
|
|
||||||
<th scope="col" style={{textAlign: "center"}}>{t('scores')}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{data.map((club, idx) => <tr key={idx}>
|
|
||||||
<td style={{textAlign: "center"}}>{club.name}</td>
|
|
||||||
<td style={{textAlign: "center"}}>{club.score[0]}</td>
|
|
||||||
<td style={{textAlign: "center"}}>{club.score[1]}</td>
|
|
||||||
<td style={{textAlign: "center"}}>{club.score[2]}</td>
|
|
||||||
<td style={{textAlign: "center"}}>{club.tt_score}</td>
|
|
||||||
</tr>)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</>
|
|
||||||
: error
|
|
||||||
? <AxiosError error={error}/>
|
|
||||||
: <Def/>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function CombsResult({uuid}) {
|
function CombsResult({uuid}) {
|
||||||
const setLoading = useLoadingSwitcher()
|
const setLoading = useLoadingSwitcher()
|
||||||
const {data, error} = useFetch(`/result/${uuid}/comb`, setLoading, 1)
|
const {data, error} = useFetch(`/result/${uuid}/comb`, setLoading, 1)
|
||||||
|
|||||||
@ -56,10 +56,8 @@ export function MarchReducer(datas, action) {
|
|||||||
return datas.sort(action.payload)
|
return datas.sort(action.payload)
|
||||||
case 'REORDER':
|
case 'REORDER':
|
||||||
const oldIndex = datas.findIndex(data => data.id === action.payload.id)
|
const oldIndex = datas.findIndex(data => data.id === action.payload.id)
|
||||||
if (oldIndex === -1)
|
if (oldIndex === -1 || datas[oldIndex].categorie_ord === action.payload.pos)
|
||||||
return datas // Do nothing
|
return datas // Do nothing
|
||||||
if (datas[oldIndex].categorie_ord === action.payload.pos)
|
|
||||||
return [...datas] // Do nothing
|
|
||||||
|
|
||||||
const oldPos = datas[oldIndex].categorie_ord
|
const oldPos = datas[oldIndex].categorie_ord
|
||||||
const newPos = action.payload.pos
|
const newPos = action.payload.pos
|
||||||
|
|||||||
@ -45,9 +45,6 @@ export function makePDF(action, pagesList, name, c_name, getComb, t, logo) {
|
|||||||
case "podium":
|
case "podium":
|
||||||
generatePodium(context);
|
generatePodium(context);
|
||||||
break;
|
break;
|
||||||
case "podiumClub":
|
|
||||||
generateClubPodium(context);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -375,7 +372,7 @@ function generateCategoriePDF({pdf_doc, cat, matches, groups, getComb, cards_v,
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generatePodium({pdf_doc, data, t, logo, c_name, minRank = 4, maxRank = 4}) {
|
function generatePodium({pdf_doc, data, t, logo, c_name, minRank = 4, maxRank = 4}) {
|
||||||
makeHeader(pdf_doc, c_name, t('podium'), logo)
|
makeHeader(pdf_doc, c_name, "Podium", logo)
|
||||||
|
|
||||||
const data2 = data.sort((a, b) => {
|
const data2 = data.sort((a, b) => {
|
||||||
let tmp = sortCategories(a.categorie, b.categorie);
|
let tmp = sortCategories(a.categorie, b.categorie);
|
||||||
@ -444,59 +441,3 @@ function generatePodium({pdf_doc, data, t, logo, c_name, minRank = 4, maxRank =
|
|||||||
}
|
}
|
||||||
pdf_doc.lastAutoTable.finalY = Math.max(finalY2, pdf_doc.lastAutoTable.finalY);
|
pdf_doc.lastAutoTable.finalY = Math.max(finalY2, pdf_doc.lastAutoTable.finalY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateClubPodium({pdf_doc, data, t, logo, c_name}) {
|
|
||||||
makeHeader(pdf_doc, c_name, t('podiumDesClubs'), logo)
|
|
||||||
|
|
||||||
const body = data.map(c => [
|
|
||||||
{content: [c.name], styles: {halign: "center"}},
|
|
||||||
{content: c.score[0], styles: {halign: "center"}},
|
|
||||||
{content: c.score[1], styles: {halign: "center"}},
|
|
||||||
{content: c.score[2], styles: {halign: "center"}},
|
|
||||||
{content: c.tt_score, styles: {halign: "center"}},
|
|
||||||
]);
|
|
||||||
|
|
||||||
let rank = 0;
|
|
||||||
let lastScores = null;
|
|
||||||
let lastB = null;
|
|
||||||
for (const b of body) {
|
|
||||||
const scores = [b[1].content, b[2].content, b[3].content, b[4].content];
|
|
||||||
if (lastScores !== scores) {
|
|
||||||
rank++;
|
|
||||||
b.unshift({content: rank, styles: {halign: "center"}})
|
|
||||||
lastB = b
|
|
||||||
} else {
|
|
||||||
lastB[1].content.push(b[0].content);
|
|
||||||
delete body.indexOf(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const b of body) {
|
|
||||||
b[1].content = b[1].content.join(", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
autoTable(pdf_doc, {
|
|
||||||
startY: pdf_doc.lastAutoTable.finalY + 7,
|
|
||||||
styles: {fontSize: 10, cellPadding: 3},
|
|
||||||
columnStyles: {
|
|
||||||
0: {cellWidth: 35},
|
|
||||||
1: {cellWidth: "auto"},
|
|
||||||
2: {cellWidth: 45},
|
|
||||||
3: {cellWidth: 45},
|
|
||||||
4: {cellWidth: 45},
|
|
||||||
5: {cellWidth: 40},
|
|
||||||
},
|
|
||||||
pageBreak: "avoid",
|
|
||||||
showHead: 'firstPage',
|
|
||||||
head: [[
|
|
||||||
{content: t('place', {ns: "result"}), styles: {halign: "center"}},
|
|
||||||
{content: t('club', {ns: 'result'}), styles: {halign: "center"}},
|
|
||||||
{content: t('1er', {ns: 'result'}), styles: {halign: "center"}},
|
|
||||||
{content: t('2eme', {ns: 'result'}), styles: {halign: "center"}},
|
|
||||||
{content: t('3eme', {ns: 'result'}), styles: {halign: "center"}},
|
|
||||||
{content: t('scores', {ns: 'result'}), styles: {halign: "center"}},
|
|
||||||
]],
|
|
||||||
body: body,
|
|
||||||
rowPageBreak: 'auto',
|
|
||||||
theme: 'grid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user