feat: add podium PDF
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m28s

This commit is contained in:
2026-02-19 15:55:46 +01:00
parent ed5d73c25f
commit 2f390b03e2
10 changed files with 238 additions and 5 deletions

View File

@@ -97,6 +97,7 @@
"individuelle": "Individual",
"informationCatégorie": "Category information",
"inscrit": "Registered",
"jusquauRang": "Up to the rank",
"leTournoiServiraDePhaseFinaleAuxPoules": "The tournament will serve as the final phase for the group stage.",
"lesCombattantsEnDehors": "Fighters not participating in the tournament will have a ranking match.",
"lesCombattantsEnDehors2": "Fighters outside the ranking tournament will have a ranking match",
@@ -143,6 +144,7 @@
"select.sélectionnerDesCombatants": "Select fighters",
"select.à": "to",
"serveur": "Server",
"source": "Source",
"suivant": "Next",
"supprimer": "Delete",
"supprimerUn": "Delete one",

View File

@@ -97,6 +97,7 @@
"individuelle": "Individuelle",
"informationCatégorie": "Information catégorie",
"inscrit": "Inscrit",
"jusquauRang": "Jusqu'au rang",
"leTournoiServiraDePhaseFinaleAuxPoules": "Le tournoi servira de phase finale aux poules",
"lesCombattantsEnDehors": "Les combattants en dehors du tournoi auront un match de classement",
"lesCombattantsEnDehors2": "Les combattants en dehors du tournoi de classement auront un match de classement",
@@ -143,6 +144,7 @@
"select.sélectionnerDesCombatants": "Sélectionner des combatants",
"select.à": "à",
"serveur": "Serveur",
"source": "Source",
"suivant": "Suivant",
"supprimer": "Supprimer",
"supprimerUn": "Supprimer un",

View File

@@ -405,13 +405,23 @@ function PrintModal({menuActions}) {
const [presetEmpty, setPresetEmpty] = useState(false);
const [allCat, setAllCat] = useState(false);
const [allCatEmpty, setAllCatEmpty] = useState(false);
const [podium, setPodium] = useState(false);
const [podiumRank, setPodiumRank] = useState(4);
const [presetSelect, setPresetSelect] = useState(-1)
const {welcomeData} = useWS();
const {sendRequest, welcomeData} = useWS();
const {getComb} = useCombs();
const {t} = useTranslation("cm");
const podiumPromise = (podiumRank_) => {
return sendRequest("getPodium", {}).then(data => {
return [welcomeData?.name + " - " + "Podium", [
{type: "podium", params: ({data, maxRank: podiumRank_, minRank: Math.min(4, podiumRank_)})},
]];
});
}
const print = (action) => {
const pagesPromise = [];
@@ -424,6 +434,9 @@ function PrintModal({menuActions}) {
if (allCat && menuActions.printAllCategorie)
pagesPromise.push(menuActions.printAllCategorie(categorieEmpty, welcomeData?.name + " - " + t('toutesLesCatégories')))
if (podium)
pagesPromise.push(podiumPromise(podiumRank));
toast.promise(
toDataURL("/Logo-FFSAF-2023.png").then(logo => {
return Promise.allSettled(pagesPromise).then(results => {
@@ -497,6 +510,17 @@ function PrintModal({menuActions}) {
<label className="form-check-label" htmlFor="checkPrint6">{t('feuilleVierge')}</label>
</div>}
<div className="form-check">
<input className="form-check-input" type="checkbox" checked={podium} id="checkPrint7"
onChange={e => setPodium(e.target.checked)}/>
<label className="form-check-label" htmlFor="checkPrint7">Podium</label>
</div>
{podium &&
<div style={{marginLeft: "1em"}}>
<label htmlFor="range3" className="form-label">{t('jusquauRang')} {podiumRank} </label>
<input type="range" className="form-range" min="1" max="20" step="1" id="range3" value={podiumRank}
onChange={e => setPodiumRank(Number(e.target.value))}/>
</div>}
</div>
<div className="modal-footer">
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal" onClick={() => print("show")}>{t('afficher')}</button>

View File

@@ -1,6 +1,17 @@
import {jsPDF} from 'jspdf'
import {useCardsStatic} from "../hooks/useCard.jsx";
import {CatList, getCatName, getShieldSize, getShieldTypeName, getSwordSize, getSwordTypeName, timePrint, virtualScore, win_end} from "./Tools.js";
import {
CatList,
getCatName,
getShieldSize,
getShieldTypeName,
getSwordSize,
getSwordTypeName,
sortCategories,
timePrint,
virtualScore,
win_end
} from "./Tools.js";
import {getMandatoryProtectionsList} from "../components/ProtectionSelector.jsx";
import {scoreToString2} from "./CompetitionTools.js";
import {TreeNode} from "./TreeUtils.js";
@@ -31,6 +42,9 @@ export function makePDF(action, pagesList, name, c_name, getComb, t, logo) {
case "categorie":
generateCategoriePDF(context);
break;
case "podium":
generatePodium(context);
break;
default:
break
}
@@ -356,3 +370,74 @@ function generateCategoriePDF({pdf_doc, cat, matches, groups, getComb, cards_v,
buildTree(pdf_doc, cat.trees, cat.raw_trees, marches2, cat, categorieEmpty ? [] : cards_v, getComb, t, categorieEmpty, nbComb)
}
}
function generatePodium({pdf_doc, data, t, logo, c_name, minRank = 4, maxRank = 4}) {
makeHeader(pdf_doc, c_name, "Podium", logo)
const data2 = data.sort((a, b) => {
let tmp = sortCategories(a.categorie, b.categorie);
if (tmp !== 0)
return tmp;
return a.poule_name.localeCompare(b.poule_name)
})
let finalY2 = pdf_doc.lastAutoTable.finalY;
let finalY3 = pdf_doc.lastAutoTable.finalY;
for (let i = 0; i < data2.length; i++) {
const p = data2[i];
let pageNumber = pdf_doc.internal.getNumberOfPages()
const body = Array.from({length: minRank}, () => []);
for (const c of p.podium) {
if (c.rank > maxRank)
continue;
if (body[c.rank - 1])
body[c.rank - 1].push(c.name)
else
body[c.rank - 1] = [c.name]
}
for (let j = 0; j < body.length; j++) {
if (body[j].length === 0)
body[j].push(" ")
body[j] = [
{content: j + 1, styles: {halign: "center"}},
{content: body[j].join(", "), styles: {halign: "center"}}
]
}
autoTable(pdf_doc, {
startY: finalY3 + 7,
margin: i % 2 ? {left: pdf_doc.internal.pageSize.getWidth() / 2 + 7} : {right: pdf_doc.internal.pageSize.getWidth() / 2 + 7},
styles: {fontSize: 10, cellPadding: 3},
columnStyles: {
0: {cellWidth: 35},
1: {cellWidth: "auto"},
},
pageBreak: "avoid",
showHead: 'firstPage',
head: [[
{content: p.poule_name, colSpan: 2, styles: {halign: "center"}},
], [
{content: t('place', {ns: "result"}), styles: {halign: "center"}},
{content: t('combattants', {ns: 'result'}), styles: {halign: "center"}},
]],
body: body,
foot: [[
{content: t('source') + " : " + p.source, colSpan: 2, styles: {halign: "left", fontSize: 8}},
]],
rowPageBreak: 'auto',
theme: 'grid',
})
if (i % 2 === 0) {
finalY2 = pdf_doc.lastAutoTable.finalY;
if (pageNumber !== pdf_doc.internal.getNumberOfPages())
finalY3 = 33
} else {
pdf_doc.lastAutoTable.finalY = Math.max(finalY2, pdf_doc.lastAutoTable.finalY);
finalY3 = pdf_doc.lastAutoTable.finalY;
}
}
pdf_doc.lastAutoTable.finalY = Math.max(finalY2, pdf_doc.lastAutoTable.finalY);
}