Compare commits
2 Commits
752f03cba5
...
ed5d73c25f
| Author | SHA1 | Date | |
|---|---|---|---|
| ed5d73c25f | |||
| 0a368454c4 |
10
src/main/webapp/package-lock.json
generated
10
src/main/webapp/package-lock.json
generated
@ -22,6 +22,7 @@
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jspdf": "^4.1.0",
|
||||
"jspdf-autotable": "^5.0.7",
|
||||
"jszip": "^3.10.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"obs-websocket-js": "^5.0.7",
|
||||
@ -4060,6 +4061,15 @@
|
||||
"html2canvas": "^1.0.0-rc.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf-autotable": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.7.tgz",
|
||||
"integrity": "sha512-2wr7H6liNDBYNwt25hMQwXkEWFOEopgKIvR1Eukuw6Zmprm/ZcnmLTQEjW7Xx3FCbD3v7pflLcnMAv/h1jFDQw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"jspdf": "^2 || ^3 || ^4"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf/node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jspdf": "^4.1.0",
|
||||
"jspdf-autotable": "^5.0.7",
|
||||
"jszip": "^3.10.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"obs-websocket-js": "^5.0.7",
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"cartonRouge": "Red card",
|
||||
"catégorie": "Category",
|
||||
"catégorieDâgeMoyenne": "Middle-aged category",
|
||||
"catégorieSélectionnée": "Selected category",
|
||||
"catégoriesVontêtreCréées": "weight categories will be created",
|
||||
"ceCartonEstIssuDunCartonDéquipe": "This card comes from a team card, do you really want to delete it?",
|
||||
"certainsCombattantsNontPasDePoidsRenseigné": "Some fighters do not have a weight listed; they will NOT be included in the categories.",
|
||||
@ -86,6 +87,7 @@
|
||||
"etatDesTablesDeMarque": "State of marque tables",
|
||||
"exporter": "Export",
|
||||
"fermer": "Close",
|
||||
"feuilleVierge": "Blank sheet",
|
||||
"finalesUniquement": "Finals only",
|
||||
"genre": "Gender",
|
||||
"genre.f": "F",
|
||||
@ -121,6 +123,7 @@
|
||||
"poule": "Pool",
|
||||
"poulePour": "Pool for: ",
|
||||
"préparation...": "Preparing...",
|
||||
"quoiImprimer?": "What print?",
|
||||
"remplacer": "Replace",
|
||||
"rouge": "Red",
|
||||
"réinitialiser": "Reset",
|
||||
@ -164,6 +167,9 @@
|
||||
"toast.matchs.create.error": "Error while creating matches.",
|
||||
"toast.matchs.create.pending": "Creating matches in progress...",
|
||||
"toast.matchs.create.success": "Matches created successfully.",
|
||||
"toast.print.error": "Error while preparing print",
|
||||
"toast.print.pending": "Preparing print...",
|
||||
"toast.print.success": "Print ready!",
|
||||
"toast.team.update.error": "Error while updating team",
|
||||
"toast.team.update.pending": "Updating team...",
|
||||
"toast.team.update.success": "Team updated!",
|
||||
@ -183,6 +189,8 @@
|
||||
"tournois": "Tournaments",
|
||||
"tousLesMatchs": "All matches",
|
||||
"toutConserver": "Keep all",
|
||||
"touteLaCatégorie": "The entire category",
|
||||
"toutesLesCatégories": "All categories",
|
||||
"ttm.admin.obs": "Short click: Download resources. Long click: Create OBS configuration",
|
||||
"ttm.admin.scripte": "Copy integration script",
|
||||
"ttm.table.inverserLaPosition": "Reverse fighter positions on this screen",
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"cartonRouge": "Carton rouge",
|
||||
"catégorie": "Catégorie",
|
||||
"catégorieDâgeMoyenne": "Catégorie d'âge moyenne",
|
||||
"catégorieSélectionnée": "Catégorie sélectionnée",
|
||||
"catégoriesVontêtreCréées": "catégories de poids vont être créées",
|
||||
"ceCartonEstIssuDunCartonDéquipe": "Ce carton est issu d'un carton d'équipe, voulez-vous vraiment le supprimer ?",
|
||||
"certainsCombattantsNontPasDePoidsRenseigné": "Certains combattants n'ont pas de poids renseigné, ils ne seront PAS insert dans les catégories",
|
||||
@ -86,6 +87,7 @@
|
||||
"etatDesTablesDeMarque": "Etat des tables de marque",
|
||||
"exporter": "Exporter",
|
||||
"fermer": "Fermer",
|
||||
"feuilleVierge": "Feuille vierge",
|
||||
"finalesUniquement": "Finales uniquement",
|
||||
"genre": "Genre",
|
||||
"genre.f": "F",
|
||||
@ -121,6 +123,7 @@
|
||||
"poule": "Poule",
|
||||
"poulePour": "Poule pour: ",
|
||||
"préparation...": "Préparation...",
|
||||
"quoiImprimer?": "Quoi imprimer ?",
|
||||
"remplacer": "Remplacer",
|
||||
"rouge": "Rouge",
|
||||
"réinitialiser": "Réinitialiser",
|
||||
@ -164,6 +167,9 @@
|
||||
"toast.matchs.create.error": "Erreur lors de la création des matchs.",
|
||||
"toast.matchs.create.pending": "Création des matchs en cours...",
|
||||
"toast.matchs.create.success": "Matchs créés avec succès.",
|
||||
"toast.print.error": "Erreur lors de la génération du PDF",
|
||||
"toast.print.pending": "Génération du PDF en cours...",
|
||||
"toast.print.success": "PDF généré !",
|
||||
"toast.team.update.error": "Erreur lors de la mise à jour de l'équipe",
|
||||
"toast.team.update.pending": "Mise à jour de l'équipe...",
|
||||
"toast.team.update.success": "Équipe mise à jour !",
|
||||
@ -183,6 +189,8 @@
|
||||
"tournois": "Tournois",
|
||||
"tousLesMatchs": "Tous les matchs",
|
||||
"toutConserver": "Tout conserver",
|
||||
"touteLaCatégorie": "Toute la catégorie",
|
||||
"toutesLesCatégories": "Toutes les catégories",
|
||||
"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.table.inverserLaPosition": "Inverser la position des combattants sur cette écran",
|
||||
|
||||
@ -663,8 +663,8 @@ export function AutoNewCatModalContent() {
|
||||
</>}
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="liceInput1" className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des ';')</small></Trans></label>
|
||||
<input type="text" className="form-control" id="liceInput1" placeholder="1;2" name="zone de combat" value={lice}
|
||||
<label htmlFor="liceInput2" className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des ';')</small></Trans></label>
|
||||
<input type="text" className="form-control" id="liceInput2" placeholder="1;2" name="zone de combat" value={lice}
|
||||
onChange={e => setLice(e.target.value)}/>
|
||||
</div>
|
||||
|
||||
@ -774,8 +774,8 @@ export function AutoNewCatSModalContent() {
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="liceInput1" className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des ';')</small></Trans></label>
|
||||
<input type="text" className="form-control" id="liceInput1" placeholder="1;2" name="zone de combat" value={lice}
|
||||
<label htmlFor="liceInput3" className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des ';')</small></Trans></label>
|
||||
<input type="text" className="form-control" id="liceInput3" placeholder="1;2" name="zone de combat" value={lice}
|
||||
onChange={e => setLice(e.target.value)}/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ export function ListPresetSelect({disabled, value, onChange, returnId = true}) {
|
||||
value={returnId ? value : (value ? value.id : -1)}
|
||||
onChange={e => {
|
||||
if (returnId) {
|
||||
onChange(e.target.value)
|
||||
onChange(Number(e.target.value))
|
||||
} else {
|
||||
onChange(data.find(c => c.id === Number(e.target.value)))
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React, {useEffect, useRef, useState} from "react";
|
||||
import React, {useEffect, useId, useRef, useState} from "react";
|
||||
import {useRequestWS, useWS} from "../../../hooks/useWS.jsx";
|
||||
import {LoadingProvider, useLoadingSwitcher} from "../../../hooks/useLoading.jsx";
|
||||
import {toast} from "react-toastify";
|
||||
import {build_tree, resize_tree} from "../../../utils/TreeUtils.js"
|
||||
import {build_tree, from_sendTree, resize_tree} from "../../../utils/TreeUtils.js"
|
||||
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
|
||||
import {CategoryContent} from "./CategoryAdminContent.jsx";
|
||||
import {exportOBSConfiguration} from "../../../hooks/useOBS.jsx";
|
||||
@ -14,14 +14,14 @@ import {detectOptimalBackground} from "../../../components/SmartLogoBackground.j
|
||||
import {faFile, faGlobe, faPrint, faTableCellsLarge, faTrash} from "@fortawesome/free-solid-svg-icons";
|
||||
import {Trans, useTranslation} from "react-i18next";
|
||||
import i18n from "i18next";
|
||||
import {getToastMessage} from "../../../utils/Tools.js";
|
||||
import {getToastMessage, toDataURL, win_end} from "../../../utils/Tools.js";
|
||||
import {copyStyles} from "../../../utils/copyStyles.js";
|
||||
import {StateWindow} from "./StateWindow.jsx";
|
||||
import {CombName, useCombs} from "../../../hooks/useComb.jsx";
|
||||
import {useCards, useCardsDispatch} from "../../../hooks/useCard.jsx";
|
||||
import {ListPresetSelect} from "../../../components/cm/ListPresetSelect.jsx";
|
||||
import {AutoNewCatModalContent, AutoNewCatSModalContent} from "../../../components/cm/AutoCatModalContent.jsx";
|
||||
import {makePDF} from "../../../utils/cmPdf.jsx";
|
||||
import {makePDF} from "../../../utils/cmPdf.js";
|
||||
|
||||
const vite_url = import.meta.env.VITE_URL;
|
||||
|
||||
@ -52,7 +52,7 @@ export function CMAdmin({compUuid}) {
|
||||
return <>
|
||||
<div className="card">
|
||||
<div className='card-header'>
|
||||
<CategoryHeader cat={cat} setCatId={setCatId}/>
|
||||
<CategoryHeader cat={cat} setCatId={setCatId} menuActions={menuActions}/>
|
||||
</div>
|
||||
|
||||
<div className="card-body">
|
||||
@ -399,46 +399,102 @@ function Menu({menuActions, compUuid}) {
|
||||
}
|
||||
|
||||
function PrintModal({menuActions}) {
|
||||
const [categorie, setCategorie] = useState(true);
|
||||
const [categorie, setCategorie] = useState(false);
|
||||
const [categorieEmpty, setCategorieEmpty] = useState(false);
|
||||
const [preset, setPreset] = useState(false);
|
||||
const [presetEmpty, setPresetEmpty] = useState(false);
|
||||
const [allCat, setAllCat] = useState(false);
|
||||
const [allCatEmpty, setAllCatEmpty] = useState(false);
|
||||
|
||||
const [presetSelect, setPresetSelect] = useState(-1)
|
||||
|
||||
const {welcomeData} = useWS();
|
||||
const {getComb} = useCombs();
|
||||
const {t} = useTranslation("cm");
|
||||
|
||||
const print = (action) => {
|
||||
const pages = [];
|
||||
const names = [];
|
||||
const pagesPromise = [];
|
||||
|
||||
if (categorie) {
|
||||
if (menuActions.printCategorie) {
|
||||
const [name, page] = menuActions.printCategorie(categorieEmpty);
|
||||
pages.push(...page);
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
if (categorie && menuActions.printCategorie)
|
||||
pagesPromise.push(menuActions.printCategorie(categorieEmpty))
|
||||
|
||||
if (pages.length !== 0) {
|
||||
makePDF(action, pages, names.join(" - "), welcomeData?.name, getComb, t)
|
||||
}
|
||||
if (preset && menuActions.printCategoriePreset)
|
||||
pagesPromise.push(menuActions.printCategoriePreset(presetEmpty, presetSelect))
|
||||
|
||||
if (allCat && menuActions.printAllCategorie)
|
||||
pagesPromise.push(menuActions.printAllCategorie(categorieEmpty, welcomeData?.name + " - " + t('toutesLesCatégories')))
|
||||
|
||||
toast.promise(
|
||||
toDataURL("/Logo-FFSAF-2023.png").then(logo => {
|
||||
return Promise.allSettled(pagesPromise).then(results => {
|
||||
const pages = [];
|
||||
const names = [];
|
||||
let errors = 0;
|
||||
|
||||
for (const result of results) {
|
||||
if (result.status === "fulfilled") {
|
||||
const [name, page, error] = result.value;
|
||||
pages.push(...page);
|
||||
names.push(name);
|
||||
errors += error
|
||||
} else if (result.status === "rejected") {
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors > 0) {
|
||||
toast.error(t('erreurGénérationPages', {count: errors}));
|
||||
}
|
||||
|
||||
if (pages.length !== 0) {
|
||||
makePDF(action, pages, names.join(" - "), welcomeData?.name, getComb, t, logo)
|
||||
}
|
||||
})
|
||||
}), getToastMessage("toast.print", "cm"))
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Quoi imprimer ?</h5>
|
||||
<h5 className="modal-title">{t('quoiImprimer?')}</h5>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" checked={categorie} id="checkPrint"
|
||||
onChange={e => setCategorie(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="checkPrint">Catégorie sélectionner</label>
|
||||
<label className="form-check-label" htmlFor="checkPrint">{t('catégorieSélectionnée')}</label>
|
||||
</div>
|
||||
{categorie &&
|
||||
<div className="form-check" style={{marginLeft: "1em"}}>
|
||||
<input className="form-check-input" type="checkbox" checked={categorieEmpty} id="checkPrint2"
|
||||
onChange={e => setCategorieEmpty(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="checkPrint2">Feuille vierge</label>
|
||||
<label className="form-check-label" htmlFor="checkPrint2">{t('feuilleVierge')}</label>
|
||||
</div>}
|
||||
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" checked={preset} id="checkPrint3"
|
||||
onChange={e => setPreset(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="checkPrint3">{t('touteLaCatégorie')}</label>
|
||||
</div>
|
||||
{preset && <div style={{marginLeft: "1em"}}>
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" checked={presetEmpty} id="checkPrint4"
|
||||
onChange={e => setPresetEmpty(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="checkPrint4">{t('feuilleVierge')}</label>
|
||||
</div>
|
||||
<ListPresetSelect value={presetSelect} onChange={setPresetSelect}/>
|
||||
</div>}
|
||||
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" checked={allCat} id="checkPrint5"
|
||||
onChange={e => setAllCat(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="checkPrint5">{t('toutesLesCatégories')}</label>
|
||||
</div>
|
||||
{allCat &&
|
||||
<div className="form-check" style={{marginLeft: "1em"}}>
|
||||
<input className="form-check-input" type="checkbox" checked={allCatEmpty} id="checkPrint6"
|
||||
onChange={e => setAllCatEmpty(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="checkPrint6">{t('feuilleVierge')}</label>
|
||||
</div>}
|
||||
|
||||
</div>
|
||||
@ -574,9 +630,7 @@ function TeamCardModal() {
|
||||
</>
|
||||
}
|
||||
|
||||
function CategoryHeader({
|
||||
cat, setCatId
|
||||
}) {
|
||||
function CategoryHeader({cat, setCatId, menuActions}) {
|
||||
const setLoading = useLoadingSwitcher()
|
||||
const bthRef = useRef(null);
|
||||
const newBthRef = useRef(null);
|
||||
@ -722,10 +776,103 @@ function CategoryHeader({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PrintCats menuActions={menuActions} cats={cats}/>
|
||||
</div>
|
||||
}
|
||||
|
||||
function PrintCats({menuActions, cats}) {
|
||||
const {cards_v} = useCards();
|
||||
const {sendRequest} = useWS();
|
||||
|
||||
function readAndConvertMatch(matches, data) {
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
const run = (categorieEmpty, cats2, name = "") => {
|
||||
const pagesPromise = cats2.sort((a, b) => a.name.localeCompare(b.name)).map(cat_ => {
|
||||
return sendRequest('getFullCategory', cat_.id)
|
||||
.then((data) => {
|
||||
const cat = {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
liceName: data.liceName,
|
||||
type: data.type,
|
||||
trees: data.trees.sort((a, b) => a.level - b.level).map(d => from_sendTree(d, true)),
|
||||
raw_trees: data.trees.sort((a, b) => a.level - b.level),
|
||||
treeAreClassement: data.treeAreClassement,
|
||||
fullClassement: data.fullClassement,
|
||||
preset: data.preset,
|
||||
}
|
||||
if (name === "") {
|
||||
name = data.preset.name;
|
||||
}
|
||||
|
||||
const newCards = {};
|
||||
for (const o of data.cards)
|
||||
newCards[o.id] = o
|
||||
|
||||
let matches2 = [];
|
||||
data.trees.flatMap(d => from_sendTree(d, false).flat()).forEach((data_) => readAndConvertMatch(matches2, data_));
|
||||
data.matches.forEach((data_) => readAndConvertMatch(matches2, data_));
|
||||
|
||||
const activeMatches = matches2.filter(m => m.poule !== '-')
|
||||
const groups = matches2.flatMap(d => [d.c1, d.c2]).filter((v, i, a) => v != null && a.indexOf(v) === i)
|
||||
.map(d => {
|
||||
let poule = activeMatches.find(m => (m.c1 === d || m.c2 === d) && m.categorie_ord !== -42)?.poule
|
||||
if (!poule)
|
||||
poule = '-'
|
||||
return {id: d, poule: poule}
|
||||
})
|
||||
|
||||
matches2 = matches2.filter(m => m.categorie === cat.id)
|
||||
.map(m => ({...m, ...win_end(m, cards_v)}))
|
||||
matches2.forEach(m => {
|
||||
if (m.end && (!m.scores || m.scores.length === 0))
|
||||
m.scores = [{n_round: 0, s1: 0, s2: 0}];
|
||||
})
|
||||
|
||||
return {
|
||||
type: "categorie",
|
||||
params: ({cat, matches: matches2, groups, cards_v: Object.values({...cards_v, ...newCards}), categorieEmpty})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return Promise.allSettled(pagesPromise)
|
||||
.then((results) => {
|
||||
const pages = [];
|
||||
let error = 0;
|
||||
|
||||
for (const result of results) {
|
||||
if (result.status === "fulfilled") {
|
||||
pages.push(result.value);
|
||||
} else {
|
||||
console.error(result.error);
|
||||
error++;
|
||||
}
|
||||
}
|
||||
|
||||
return [name, pages, error];
|
||||
})
|
||||
}
|
||||
|
||||
menuActions.printCategoriePreset = (categorieEmpty, preset) => {
|
||||
return run(categorieEmpty, cats.filter(cat => cat.preset?.id === preset))
|
||||
}
|
||||
|
||||
menuActions.printAllCategorie = (categorieEmpty, name) => {
|
||||
return run(categorieEmpty, cats, name)
|
||||
}
|
||||
}
|
||||
|
||||
function ModalContent({state, setCatId, setConfirm, confirmRef}) {
|
||||
const id = useId()
|
||||
const [name, setName] = useState("")
|
||||
const [lice, setLice] = useState("1")
|
||||
const [poule, setPoule] = useState(true)
|
||||
@ -908,8 +1055,9 @@ function ModalContent({state, setCatId, setConfirm, confirmRef}) {
|
||||
<ListPresetSelect value={preset} onChange={setPreset}/>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="liceInput1" className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des ';')</small></Trans></label>
|
||||
<input type="text" className="form-control" id="liceInput1" placeholder="1;2" name="zone de combat" value={lice}
|
||||
<label htmlFor={id + "liceInput1"} className="form-label"><Trans i18nKey="nomDesZonesDeCombat" ns="cm">t <small>(séparée par des
|
||||
';')</small></Trans></label>
|
||||
<input type="text" className="form-control" id={id + "liceInput1"} placeholder="1;2" name="zone de combat" value={lice}
|
||||
onChange={e => setLice(e.target.value)}/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -22,7 +22,6 @@ import {hasEffectCard, useCards, useCardsDispatch} from "../../../hooks/useCard.
|
||||
import {ScorePanel} from "./ScoreAndCardPanel.jsx";
|
||||
import {ConfirmDialog} from "../../../components/ConfirmDialog.jsx";
|
||||
import {AutoCatModalContent} from "../../../components/cm/AutoCatModalContent.jsx";
|
||||
import {makePDF} from "../../../utils/cmPdf.jsx";
|
||||
|
||||
const vite_url = import.meta.env.VITE_URL;
|
||||
|
||||
|
||||
@ -418,3 +418,29 @@ export function hex2rgb(hex) {
|
||||
// return {r, g, b}
|
||||
return {r, g, b};
|
||||
}
|
||||
|
||||
export function toDataURL(src, outputFormat) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onload = function() {
|
||||
const canvas = document.createElement('CANVAS');
|
||||
const ctx = canvas.getContext('2d');
|
||||
let dataURL;
|
||||
canvas.height = this.naturalHeight;
|
||||
canvas.width = this.naturalWidth;
|
||||
ctx.drawImage(this, 0, 0);
|
||||
dataURL = canvas.toDataURL(outputFormat);
|
||||
resolve(dataURL);
|
||||
};
|
||||
img.onerror = function() {
|
||||
reject(new Error('Could not load image at ' + src));
|
||||
}
|
||||
|
||||
img.src = src;
|
||||
if (img.complete || img.complete === undefined) {
|
||||
img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
|
||||
img.src = src;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
358
src/main/webapp/src/utils/cmPdf.js
Normal file
358
src/main/webapp/src/utils/cmPdf.js
Normal file
@ -0,0 +1,358 @@
|
||||
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 {getMandatoryProtectionsList} from "../components/ProtectionSelector.jsx";
|
||||
import {scoreToString2} from "./CompetitionTools.js";
|
||||
import {TreeNode} from "./TreeUtils.js";
|
||||
import {drawGraphForPdf} from "../pages/result/DrawGraph.jsx";
|
||||
import autoTable from 'jspdf-autotable'
|
||||
|
||||
function combName(getComb, combId) {
|
||||
if (!combId)
|
||||
return " "
|
||||
const comb = getComb(combId, null);
|
||||
if (comb) {
|
||||
if (comb.lname === "__team")
|
||||
return `${comb.fname}`
|
||||
return `${comb.fname} ${comb.lname}`
|
||||
} else {
|
||||
return `[Comb #${combId}]`
|
||||
}
|
||||
}
|
||||
|
||||
export function makePDF(action, pagesList, name, c_name, getComb, t, logo) {
|
||||
//https://github.com/parallax/jsPDF/blob/ddbfc0f0250ca908f8061a72fa057116b7613e78/jspdf.js#L59
|
||||
const doc = new jsPDF('p', 'pt', 'a4');
|
||||
doc.setProperties({title: name, author: "FFSAF - Intranet", subject: c_name + " - " + name, creator: "FFSAF - Intranet"});
|
||||
|
||||
for (let i = 0; i < pagesList.length; i++) {
|
||||
const context = {pdf_doc: doc, pdf_name: name, c_name, getComb, t, ...pagesList[i].params, logo}
|
||||
switch (pagesList[i].type) {
|
||||
case "categorie":
|
||||
generateCategoriePDF(context);
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
if (i !== pagesList.length - 1)
|
||||
doc.addPage();
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case "show":
|
||||
window.open(doc.output('bloburl', {filename: name + '.pdf'}));
|
||||
break;
|
||||
case "print":
|
||||
const iframe = document.createElement('iframe'); //load content in an iframe to print later
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = doc.output('bloburl', {filename: name + '.pdf'});
|
||||
iframe.onload = function () {
|
||||
setTimeout(function () {
|
||||
iframe.focus();
|
||||
iframe.contentWindow.print();
|
||||
}, 1);
|
||||
};
|
||||
break;
|
||||
case "download":
|
||||
default:
|
||||
doc.save(name + '.pdf');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function matchList(pdf_doc, matches, cat, cards_v, classement, getComb, t) {
|
||||
const {getHeightCardForCombInMatch} = useCardsStatic(cards_v);
|
||||
const liceName = (cat.liceName || "N/A").split(";");
|
||||
|
||||
const getBG = (combId, match) => {
|
||||
const c = getHeightCardForCombInMatch(combId, match)
|
||||
if (!c)
|
||||
return ""
|
||||
let bg = "";
|
||||
let text = "#000";
|
||||
switch (c.type) {
|
||||
case "YELLOW":
|
||||
bg = "#ffc107";
|
||||
break;
|
||||
case "RED":
|
||||
bg = "#dc3545";
|
||||
text = "#FFF";
|
||||
break;
|
||||
case "BLACK":
|
||||
bg = "#000000";
|
||||
text = "#FFF";
|
||||
break;
|
||||
case "BLUE":
|
||||
bg = "#0d6efd";
|
||||
text = "#FFF";
|
||||
break;
|
||||
}
|
||||
return {fillColor: bg, textColor: text}
|
||||
}
|
||||
|
||||
const head = [
|
||||
...(classement ? [
|
||||
{content: t('place', {ns: "result"}), styles: {halign: "center"}},
|
||||
] : [
|
||||
{content: t('no'), styles: {halign: "center"}},
|
||||
{content: t('poule'), styles: {halign: "center"}},
|
||||
{content: t('zone'), styles: {halign: "center"}},
|
||||
]),
|
||||
{content: "", styles: {halign: "center"}},
|
||||
{content: t('rouge'), styles: {halign: "center"}},
|
||||
{content: t('résultat'), styles: {halign: "center"}},
|
||||
{content: t('blue'), styles: {halign: "center"}},
|
||||
{content: "", styles: {halign: "center"}},
|
||||
]
|
||||
|
||||
const body = matches.map((m, index) => ([
|
||||
...(classement ? [
|
||||
{content: `${m.categorie_ord + 1}-${m.categorie_ord + 2}`, styles: {halign: "center", padding: "2pt 0"}}
|
||||
] : [
|
||||
{content: index + 1, styles: {halign: "center", padding: "2pt 0"}},
|
||||
{content: m.poule, styles: {halign: "center"}},
|
||||
{content: liceName[index % liceName.length], styles: {halign: "center"}},
|
||||
]),
|
||||
{content: m.end ? (m.win > 0 ? "X" : (m.win === 0 ? "-" : "")) : " ", styles: {halign: "center", ...getBG(m.c1, m)}},
|
||||
{content: combName(getComb, m.c1), styles: {halign: "center", minWidth: "11em", paddingLeft: "0.2em"}},
|
||||
{content: scoreToString2(m, cards_v), styles: {halign: "center"}},
|
||||
{content: combName(getComb, m.c2), styles: {halign: "center", minWidth: "11em", paddingRight: "0.2em"}},
|
||||
{content: m.end ? (m.win < 0 ? "X" : (m.win === 0 ? "-" : "")) : " ", styles: {halign: "center", ...getBG(m.c2, m)}},
|
||||
]))
|
||||
|
||||
autoTable(pdf_doc, {
|
||||
startY: pdf_doc.lastAutoTable.finalY + 7,
|
||||
styles: {fontSize: 10, cellPadding: 3},
|
||||
columnStyles: classement ? {
|
||||
0: {cellWidth: 35},
|
||||
1: {cellWidth: 15},
|
||||
2: {cellWidth: "auto"},
|
||||
3: {cellWidth: 110},
|
||||
4: {cellWidth: "auto"},
|
||||
5: {cellWidth: 15},
|
||||
} : {
|
||||
0: {cellWidth: 20},
|
||||
1: {cellWidth: 35},
|
||||
2: {cellWidth: 30},
|
||||
3: {cellWidth: 15},
|
||||
4: {cellWidth: "auto"},
|
||||
5: {cellWidth: 110},
|
||||
6: {cellWidth: "auto"},
|
||||
7: {cellWidth: 15},
|
||||
},
|
||||
head: [head],
|
||||
body: body,
|
||||
theme: 'grid',
|
||||
})
|
||||
}
|
||||
|
||||
function buildTree(pdf_doc, treeData, treeRaw, matches, cat, cards_v, getComb, t, categorieEmpty, comb_count = 0) {
|
||||
function parseTree(data_in) {
|
||||
if (data_in?.data == null)
|
||||
return null
|
||||
|
||||
const matchData = matches.find(m => m.id === data_in.data)
|
||||
const c1 = categorieEmpty ? null : getComb(matchData?.c1)
|
||||
const c2 = categorieEmpty ? null : 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({
|
||||
...matchData,
|
||||
...win_end(matchData, cards_v),
|
||||
scores: scores2,
|
||||
c1FullName: c1 !== null ? c1.fname + " " + c1.lname : null,
|
||||
c2FullName: c2 !== null ? c2.fname + " " + c2.lname : null
|
||||
})
|
||||
node.left = parseTree(data_in?.left)
|
||||
node.right = parseTree(data_in?.right)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
|
||||
let out = []
|
||||
for (let i = 0; i < treeRaw.length; i++) {
|
||||
if (treeRaw.at(i).level > -10) {
|
||||
out.push(parseTree(treeData.at(i)))
|
||||
}
|
||||
}
|
||||
|
||||
const canvas = drawGraphForPdf(out, 24, cards_v)
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
const height = canvas.height * (pdf_doc.internal.pageSize.getWidth() - 80) / canvas.width;
|
||||
pdf_doc.addImage(imgData, 'PNG', 40, pdf_doc.lastAutoTable.finalY, pdf_doc.internal.pageSize.getWidth() - 80, height);
|
||||
|
||||
pdf_doc.lastAutoTable.finalY += height;
|
||||
|
||||
if (cat.fullClassement) {
|
||||
let size = 0;
|
||||
if (out.length > 0)
|
||||
size = out[0].getMaxChildrenAtDepth(out[0].death() - 1)
|
||||
|
||||
const matches2 = cat.raw_trees?.filter(n => n.level <= -10).reverse().map(d => categorieEmpty ? ({}) : matches.find(m => m.id === d.match?.id))
|
||||
if (matches2.length === 0) {
|
||||
while (Math.ceil(comb_count / 2) - size > matches2.length)
|
||||
matches2.push({})
|
||||
}
|
||||
|
||||
matches2.forEach((v, i) => {
|
||||
v.categorie_ord = (i + size) * 2
|
||||
})
|
||||
|
||||
matchList(pdf_doc, matches2, cat, cards_v, true, getComb, t)
|
||||
}
|
||||
}
|
||||
|
||||
function pouleList(pdf_doc, groups, getComb, t) {
|
||||
const groups2 = groups.map(g => {
|
||||
const comb = getComb(g.id);
|
||||
return {...g, name: comb ? comb.fname + " " + comb.lname : "", teamMembers: comb ? comb.teamMembers : []};
|
||||
}).sort((a, b) => {
|
||||
if (a.poule !== b.poule) {
|
||||
if (a.poule === '-') return 1;
|
||||
if (b.poule === '-') return -1;
|
||||
return a.poule.localeCompare(b.poule);
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
}).reduce((acc, curr) => {
|
||||
const poule = curr.poule;
|
||||
if (!acc[poule]) {
|
||||
acc[poule] = [];
|
||||
}
|
||||
acc[poule].push(curr);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
autoTable(pdf_doc, {
|
||||
startY: pdf_doc.lastAutoTable.finalY + 7,
|
||||
styles: {fontSize: 10, cellPadding: 3},
|
||||
head: [Object.keys(groups2).map((poule) => ({content: `${t('poule')} ${poule}`, styles: {halign: "center"}}))],
|
||||
body: [Object.keys(groups2).map((poule) => ({
|
||||
content: groups2[poule].map(o => o.name).join(", "),
|
||||
styles: {halign: "center", valign: "middle"}
|
||||
}))],
|
||||
theme: 'grid',
|
||||
})
|
||||
}
|
||||
|
||||
function makeHeader(pdf_doc, c_name, p_name, logo) {
|
||||
autoTable(pdf_doc, {
|
||||
startY: 20,
|
||||
body: [[
|
||||
{content: "", src: `${logo}`, rowSpan: 2, styles: {halign: 'center', valign: 'middle'}},
|
||||
{content: c_name, colSpan: 3, styles: {halign: "center", valign: 'middle', fontSize: 20}},
|
||||
], [
|
||||
{content: p_name, colSpan: 3, styles: {halign: "center", valign: 'middle', fontSize: 20}}
|
||||
]],
|
||||
theme: 'plain',
|
||||
columnStyles: {
|
||||
0: {cellWidth: 60, minCellHeight: 60},
|
||||
},
|
||||
didDrawCell: function (data) {
|
||||
if (data.column.index === 0 && data.row.index === 0) {
|
||||
const dim = data.cell.height - data.cell.padding('vertical');
|
||||
const textPos = data.cell;
|
||||
pdf_doc.addImage(data.cell.raw.src, textPos.x, textPos.y + data.cell.padding('vertical') / 2, dim, dim);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function generateCategoriePDF({pdf_doc, cat, matches, groups, getComb, cards_v, c_name, categorieEmpty, t, logo}) {
|
||||
let catAverage = "---";
|
||||
let genreAverage = "H";
|
||||
let nbComb = 0;
|
||||
let time = 0;
|
||||
|
||||
const marches2 = matches.filter(m => m.categorie === cat.id)
|
||||
.map(m => categorieEmpty ? ({...m, end: false, scores: []}) : m)
|
||||
|
||||
if (marches2.length !== 0) {
|
||||
const genres = [];
|
||||
const cats = [];
|
||||
const combs_ = [];
|
||||
for (const m of marches2) {
|
||||
if (m.c1 && !combs_.includes(m.c1))
|
||||
combs_.push(m.c1);
|
||||
if (m.c2 && !combs_.includes(m.c2))
|
||||
combs_.push(m.c2);
|
||||
}
|
||||
|
||||
combs_.map(cId => getComb(cId, null)).filter(c => c && c.categorie)
|
||||
.forEach(c => {
|
||||
cats.push(Math.min(CatList.length, CatList.indexOf(c.categorie) + c.overCategory))
|
||||
genres.push(c.genre)
|
||||
});
|
||||
|
||||
const catAvg = Math.round(cats.reduce((a, b) => a + b, 0) / cats.length);
|
||||
|
||||
nbComb = combs_.length;
|
||||
catAverage = CatList.at(catAvg) || "---";
|
||||
genreAverage = Math.round(genres.reduce((a, b) => a + (b === "F" ? 1 : 0), 0) / genres.length) > 0.5 ? "F" : "H";
|
||||
|
||||
if (cat.preset && cat.preset.categories) {
|
||||
const catAvailable = cat.preset.categories.map(c => CatList.indexOf(c.categorie));
|
||||
let p;
|
||||
if (catAvailable.includes(catAvg)) {
|
||||
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) === catAvg);
|
||||
} else {
|
||||
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) ===
|
||||
catAvailable.reduce((a, b) => Math.abs(b - catAvg) < Math.abs(a - catAvg) ? b : a));
|
||||
}
|
||||
time = {round: p.roundDuration, pause: p.pauseDuration}
|
||||
}
|
||||
}
|
||||
|
||||
makeHeader(pdf_doc, c_name, cat.name, logo)
|
||||
|
||||
autoTable(pdf_doc, {
|
||||
startY: pdf_doc.lastAutoTable.finalY,
|
||||
styles: {fontSize: 10, cellPadding: 3},
|
||||
body: [[
|
||||
{content: `${t('catégorieDâgeMoyenne')} : ${getCatName(catAverage)}`, styles: {halign: "left"}},
|
||||
{
|
||||
content: `${t('arme', {ns: 'common'})} : ${getSwordTypeName(cat.preset?.sword)} - ${t('taille')} ${getSwordSize(cat.preset?.sword, catAverage, genreAverage)}`,
|
||||
styles: {halign: "left"}
|
||||
},
|
||||
{
|
||||
content: `${t('bouclier', {ns: 'common'})} : ${getShieldTypeName(cat.preset?.shield)} - ${t('taille')} ${getShieldSize(cat.preset?.shield, catAverage)}`,
|
||||
styles: {halign: "left"}
|
||||
},
|
||||
], [
|
||||
{content: `${t('duréeRound')} : ${timePrint(time.round)}`, styles: {halign: "left"}},
|
||||
{content: `${t('duréePause')} : ${timePrint(time.pause)}`, styles: {halign: "left"}},
|
||||
{content: `${t('nombreDeCombattants')} : ${nbComb}`, styles: {halign: "left"}},
|
||||
], [
|
||||
{
|
||||
content: `${t('protectionObligatoire', {ns: 'common'})} ${getMandatoryProtectionsList(CatList.indexOf(catAverage) <= CatList.indexOf("JUNIOR") ?
|
||||
cat.preset?.mandatoryProtection1 : cat.preset?.mandatoryProtection2, cat.preset?.shield, t).join(", ") || "---"}`,
|
||||
colSpan: 3,
|
||||
styles: {halign: "left"}
|
||||
},
|
||||
]],
|
||||
theme: 'grid',
|
||||
})
|
||||
|
||||
pouleList(pdf_doc, groups, getComb, t)
|
||||
|
||||
if ((cat.type & 1) === 1)
|
||||
matchList(pdf_doc, marches2.filter(m => m.categorie_ord !== -42).sort((a, b) => a.categorie_ord - b.categorie_ord),
|
||||
cat, categorieEmpty ? [] : cards_v, false, getComb, t)
|
||||
|
||||
if ((cat.type & 2) === 2) {
|
||||
pdf_doc.setFontSize(12);
|
||||
pdf_doc.text((cat.treeAreClassement ? t('classement') : t('tournois')) + ':', 40, pdf_doc.lastAutoTable.finalY + 16)
|
||||
pdf_doc.lastAutoTable.finalY += 16;
|
||||
buildTree(pdf_doc, cat.trees, cat.raw_trees, marches2, cat, categorieEmpty ? [] : cards_v, getComb, t, categorieEmpty, nbComb)
|
||||
}
|
||||
}
|
||||
@ -1,378 +0,0 @@
|
||||
import {jsPDF} from 'jspdf'
|
||||
import {renderToString} from "react-dom/server";
|
||||
import React from "react";
|
||||
import {hasEffectCard, useCardsStatic} from "../hooks/useCard.jsx";
|
||||
import {CatList, getCatName, getShieldSize, getShieldTypeName, getSwordSize, getSwordTypeName, timePrint, virtualScore, win_end} from "./Tools.js";
|
||||
import {getMandatoryProtectionsList} from "../components/ProtectionSelector.jsx";
|
||||
import {scoreToString2} from "./CompetitionTools.js";
|
||||
import {TreeNode} from "./TreeUtils.js";
|
||||
import {drawGraphForPdf} from "../pages/result/DrawGraph.jsx";
|
||||
|
||||
function CombName({getComb, combId}) {
|
||||
if (!combId)
|
||||
return <> </>
|
||||
const comb = getComb(combId, null);
|
||||
if (comb) {
|
||||
if (comb.lname === "__team")
|
||||
return <>{comb.fname}</>
|
||||
return <>{comb.fname} {comb.lname}</>
|
||||
} else {
|
||||
return <>[Comb #{combId}]</>
|
||||
}
|
||||
}
|
||||
|
||||
function MatchList({matches, cat, cards_v, classement, getComb, t}) {
|
||||
const {getHeightCardForCombInMatch} = useCardsStatic(cards_v);
|
||||
const liceName = (cat.liceName || "N/A").split(";");
|
||||
|
||||
const getBG = (combId, match, cat) => {
|
||||
const c = getHeightCardForCombInMatch(combId, match)
|
||||
if (!c)
|
||||
return ""
|
||||
let bg = "";
|
||||
let text = "#000";
|
||||
switch (c.type) {
|
||||
case "YELLOW":
|
||||
bg = "#ffc107";
|
||||
break;
|
||||
case "RED":
|
||||
bg = "#dc3545";
|
||||
text = "#FFF";
|
||||
break;
|
||||
case "BLACK":
|
||||
bg = "#000000";
|
||||
text = "#FFF";
|
||||
break;
|
||||
case "BLUE":
|
||||
bg = "#0d6efd";
|
||||
text = "#FFF";
|
||||
break;
|
||||
}
|
||||
return {
|
||||
backgroundColor: bg,
|
||||
color: text,
|
||||
borderRadius: c.match === match.id ? "50%" : "0",
|
||||
opacity: hasEffectCard(c, match.id, cat.id) ? 1 : 0.5
|
||||
}
|
||||
}
|
||||
|
||||
return <table style={{width: "100%", borderCollapse: "collapse"}} border={1}>
|
||||
<thead>
|
||||
<tr>
|
||||
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>{t('no')}</th>}
|
||||
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>{t('poule')}</th>}
|
||||
{!classement && <th style={{textAlign: "center", paddingLeft: "0.2em", paddingRight: "0.2em"}}>{t('zone')}</th>}
|
||||
<th></th>
|
||||
<th style={{textAlign: "center"}}>{t('rouge')}</th>
|
||||
<th style={{textAlign: "center"}}>{t('résultat')}</th>
|
||||
<th style={{textAlign: "center"}}>{t('blue')}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{matches.map((m, index) => (
|
||||
<tr key={m.id} id={m.id} style={{
|
||||
background: index % 2 ? "#ededed" : "white",
|
||||
borderLeft: "0.5px solid gray",
|
||||
borderTop: index === 0 ? "2px solid black" : ""
|
||||
}}>
|
||||
{!classement && <th style={{textAlign: "center", padding: "2pt 0"}} scope="row">{index + 1}</th>}
|
||||
{!classement && <td style={{textAlign: "center"}}>{m.poule}</td>}
|
||||
{!classement && <td style={{textAlign: "center"}}>{liceName[index % liceName.length]}</td>}
|
||||
<td style={{textAlign: "center", ...getBG(m.c1, m, cat)}}>{m.end && ((m.win > 0 && "X") || (m.win === 0 && "-"))}</td>
|
||||
<td style={{textAlign: "center", minWidth: "11em", paddingLeft: "0.2em"}}><CombName combId={m.c1} getComb={getComb}/></td>
|
||||
<td style={{textAlign: "center"}}>{scoreToString2(m, cards_v)}</td>
|
||||
<td style={{textAlign: "center", minWidth: "11em", paddingRight: "0.2em"}}><CombName combId={m.c2} getComb={getComb}/></td>
|
||||
<td style={{textAlign: "center", ...getBG(m.c2, m, cat)}}>{m.end && ((m.win < 0 && "X") || (m.win === 0 && "-"))}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
function BuildTree({treeData, treeRaw, matches, cat, cards_v, getComb, t, categorieEmpty}) {
|
||||
function parseTree(data_in) {
|
||||
if (data_in?.data == null)
|
||||
return null
|
||||
|
||||
const matchData = matches.find(m => m.id === data_in.data)
|
||||
const c1 = categorieEmpty ? null : getComb(matchData?.c1)
|
||||
const c2 = categorieEmpty ? null : 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({
|
||||
...matchData,
|
||||
...win_end(matchData, cards_v),
|
||||
scores: scores2,
|
||||
c1FullName: c1 !== null ? c1.fname + " " + c1.lname : null,
|
||||
c2FullName: c2 !== null ? c2.fname + " " + c2.lname : null
|
||||
})
|
||||
node.left = parseTree(data_in?.left)
|
||||
node.right = parseTree(data_in?.right)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
function initTree(data_in, data_raw) {
|
||||
let out = []
|
||||
for (let i = 0; i < data_raw.length; i++) {
|
||||
if (data_raw.at(i).level > -10) {
|
||||
out.push(parseTree(data_in.at(i)))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
const imgData = drawGraphForPdf(initTree(treeData, treeRaw), 24, cards_v).toDataURL('image/png');
|
||||
return <div>
|
||||
<img src={imgData} alt="Arbre" style={{width: "100%", margin: "4pt 0", objectFit: "scale-down"}}/>
|
||||
{cat.fullClassement &&
|
||||
<MatchList
|
||||
matches={cat.raw_trees?.filter(n => n.level <= -10).reverse().map(d => categorieEmpty ? ({}) : matches.find(m => m.id === d.match?.id))}
|
||||
cat={cat}
|
||||
cards_v={cards_v} classement={true} t={t} getComb={getComb}/>}
|
||||
</div>
|
||||
}
|
||||
|
||||
function PouleList({groups, getComb, t}) {
|
||||
const groups2 = groups.map(g => {
|
||||
const comb = getComb(g.id);
|
||||
return {...g, name: comb ? comb.fname + " " + comb.lname : "", teamMembers: comb ? comb.teamMembers : []};
|
||||
}).sort((a, b) => {
|
||||
if (a.poule !== b.poule) {
|
||||
if (a.poule === '-') return 1;
|
||||
if (b.poule === '-') return -1;
|
||||
return a.poule.localeCompare(b.poule);
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
}).reduce((acc, curr) => {
|
||||
const poule = curr.poule;
|
||||
if (!acc[poule]) {
|
||||
acc[poule] = [];
|
||||
}
|
||||
acc[poule].push(curr);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return <table style={{width: "100%"}}>
|
||||
<thead style={{borderCollapse: "collapse"}}>
|
||||
<tr style={{borderCollapse: "collapse"}}>
|
||||
{Object.keys(groups2).map((poule) => <th key={poule} style={{
|
||||
textAlign: "center",
|
||||
borderCollapse: "collapse",
|
||||
border: "1px solid black"
|
||||
}}>{t('poule')} {poule}</th>)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style={{borderCollapse: "collapse"}}>
|
||||
<tr style={{borderCollapse: "collapse"}}>
|
||||
{Object.keys(groups2).map((poule) => <td key={poule} style={{
|
||||
textAlign: "center",
|
||||
borderCollapse: "collapse",
|
||||
border: "1px solid black"
|
||||
}}>{groups2[poule].map(o => o.name).join(", ")}</td>)}
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
}
|
||||
|
||||
export function makePDF(action, pagesList, name, c_name, getComb, t) {
|
||||
//https://github.com/parallax/jsPDF/blob/ddbfc0f0250ca908f8061a72fa057116b7613e78/jspdf.js#L59
|
||||
const doc = new jsPDF('p', 'pt', 'a4');
|
||||
let pageHeight = doc.internal.pageSize.getHeight();
|
||||
|
||||
htmlPage(doc, pagesList);
|
||||
|
||||
function htmlPage(doc2, pages, index = 0) {
|
||||
const hasPages = pages.length > 0;
|
||||
|
||||
if (!hasPages) {
|
||||
doc2.setProperties({title: name, author: "FFSAF - Intranet", subject: c_name + " - " + name, creator: "FFSAF - Intranet"});
|
||||
|
||||
switch (action) {
|
||||
case "show":
|
||||
window.open(doc.output('bloburl', {filename: name + '.pdf'}));
|
||||
break;
|
||||
case "print":
|
||||
const iframe = document.createElement('iframe'); //load content in an iframe to print later
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = doc.output('bloburl', {filename: name + '.pdf'});
|
||||
iframe.onload = function () {
|
||||
setTimeout(function () {
|
||||
iframe.focus();
|
||||
iframe.contentWindow.print();
|
||||
}, 1);
|
||||
};
|
||||
break;
|
||||
case "download":
|
||||
default:
|
||||
doc.save(name + '.pdf');
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let htmlElement = renderToString(
|
||||
<div style={{width: "835.82px", margin: "0 auto", letterSpacing: "0.065em"}}>
|
||||
{processPage(pages[0], ({pdf_name: name, c_name, getComb, t}))}
|
||||
</div>);
|
||||
|
||||
return doc2.html(htmlElement, {
|
||||
y: index * pageHeight,
|
||||
callback: function (pdf) {
|
||||
if (index > 0 && pages.length > 1) pdf.addPage("a4", "p");
|
||||
const restPages = pages.slice();
|
||||
restPages.splice(0, 1);
|
||||
htmlPage(pdf, restPages, index + 1);
|
||||
},
|
||||
html2canvas: {
|
||||
scale: 0.65,
|
||||
logging: false,
|
||||
letterRendering: 1,
|
||||
allowTaint: true,
|
||||
useCORS: true,
|
||||
},
|
||||
margin: [30, 30, 30, 30],
|
||||
width: 595.28,
|
||||
//windowWidth: 300
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function processPage(page, context) {
|
||||
switch (page.type) {
|
||||
case "categorie":
|
||||
return <GenerateCategoriePDF {...context} {...page.params} />
|
||||
default:
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
||||
export function GenerateCategoriePDF({cat, matches, groups, getComb, cards_v, c_name, categorieEmpty, t}) {
|
||||
let catAverage = "---";
|
||||
let genreAverage = "H";
|
||||
let nbComb = 0;
|
||||
let time = 0;
|
||||
|
||||
const marches2 = matches.filter(m => m.categorie === cat.id)
|
||||
.map(m => categorieEmpty ? ({...m, end: false, scores: []}) : m)
|
||||
|
||||
if (marches2.length !== 0) {
|
||||
const genres = [];
|
||||
const cats = [];
|
||||
const combs_ = [];
|
||||
for (const m of marches2) {
|
||||
if (m.c1 && !combs_.includes(m.c1))
|
||||
combs_.push(m.c1);
|
||||
if (m.c2 && !combs_.includes(m.c2))
|
||||
combs_.push(m.c2);
|
||||
}
|
||||
|
||||
combs_.map(cId => getComb(cId, null)).filter(c => c && c.categorie)
|
||||
.forEach(c => {
|
||||
cats.push(Math.min(CatList.length, CatList.indexOf(c.categorie) + c.overCategory))
|
||||
genres.push(c.genre)
|
||||
});
|
||||
|
||||
const catAvg = Math.round(cats.reduce((a, b) => a + b, 0) / cats.length);
|
||||
|
||||
nbComb = combs_.length;
|
||||
catAverage = CatList.at(catAvg) || "---";
|
||||
genreAverage = Math.round(genres.reduce((a, b) => a + (b === "F" ? 1 : 0), 0) / genres.length) > 0.5 ? "F" : "H";
|
||||
|
||||
if (cat.preset && cat.preset.categories) {
|
||||
const catAvailable = cat.preset.categories.map(c => CatList.indexOf(c.categorie));
|
||||
let p;
|
||||
if (catAvailable.includes(catAvg)) {
|
||||
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) === catAvg);
|
||||
} else {
|
||||
p = cat.preset.categories.find(c => CatList.indexOf(c.categorie) ===
|
||||
catAvailable.reduce((a, b) => Math.abs(b - catAvg) < Math.abs(a - catAvg) ? b : a));
|
||||
}
|
||||
time = {round: p.roundDuration, pause: p.pauseDuration}
|
||||
}
|
||||
}
|
||||
|
||||
return <>
|
||||
<table style={{width: "100%", borderCollapse: "collapse", border: "1px solid black"}}>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<table style={{width: "100%"}}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{width: "90pt"}}>
|
||||
<img src="/Logo-FFSAF-2023.png" alt="Logo" style={{height: "90pt", width: "90pt"}}/>
|
||||
</td>
|
||||
<td align={'center'}>
|
||||
<h2 style={{marginLeft: "10pt", textAlign: "center"}}>{c_name}</h2>
|
||||
<h2 style={{marginLeft: "10pt", textAlign: "center"}}>{cat.name}</h2>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="table-group-divider">
|
||||
<tr>
|
||||
<td>
|
||||
<table style={{width: "100%"}}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>{t('catégorieDâgeMoyenne')} :</strong> {getCatName(catAverage)}</td>
|
||||
<td>
|
||||
<strong>{t('arme', {ns: 'common'})} :</strong> {getSwordTypeName(cat.preset?.sword)} - {t('taille')} {getSwordSize(cat.preset?.sword, catAverage, genreAverage)}
|
||||
</td>
|
||||
<td>
|
||||
<strong>{t('bouclier', {ns: 'common'})} :</strong> {getShieldTypeName(cat.preset?.shield)} - {t('taille')} {getShieldSize(cat.preset?.shield, catAverage)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{t('duréeRound')} :</strong> {timePrint(time.round)}</td>
|
||||
<td><strong>{t('duréePause')} :</strong> {timePrint(time.pause)}</td>
|
||||
<td><strong>{t('nombreDeCombattants')} :</strong> {nbComb}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{t('protectionObligatoire', {ns: 'common'})}</strong> {getMandatoryProtectionsList(CatList.indexOf(catAverage) <= CatList.indexOf("JUNIOR") ?
|
||||
cat.preset?.mandatoryProtection1 : cat.preset?.mandatoryProtection2, cat.preset?.shield, t).join(", ") || "---"}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div style={{marginTop: "10pt"}}>
|
||||
<PouleList groups={groups} t={t} getComb={getComb}/>
|
||||
</div>
|
||||
{(cat.type & 1) === 1 &&
|
||||
<div style={{marginTop: "7pt"}}>
|
||||
<MatchList matches={marches2.filter(m => m.categorie_ord !== -42).sort((a, b) => a.categorie_ord - b.categorie_ord)} cat={cat}
|
||||
cards_v={categorieEmpty ? [] : cards_v} classement={false} t={t} getComb={getComb}/>
|
||||
</div>}
|
||||
|
||||
{(cat.type & 2) === 2 &&
|
||||
<div style={{marginTop: "7pt"}}>
|
||||
<strong>{cat.treeAreClassement ? t('classement') : t('tournois')} :</strong>
|
||||
<BuildTree treeData={cat.trees} treeRaw={cat.raw_trees} cat={cat} matches={marches2} cards_v={categorieEmpty ? [] : cards_v}
|
||||
getComb={getComb} t={t} categorieEmpty={categorieEmpty}/>
|
||||
</div>}
|
||||
</>
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user