diff --git a/src/main/webapp/package-lock.json b/src/main/webapp/package-lock.json index 820f7b9..dcab915 100644 --- a/src/main/webapp/package-lock.json +++ b/src/main/webapp/package-lock.json @@ -18,7 +18,7 @@ "@fortawesome/react-fontawesome": "^3.1.1", "axios": "^1.13.2", "browser-image-compression": "^2.0.2", - "i18next": "^25.7.4", + "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", "jszip": "^3.10.1", @@ -33,12 +33,12 @@ "react-loader-spinner": "^8.0.2", "react-router-dom": "^7.12.0", "react-toastify": "^11.0.5", - "recharts": "^3.6.0", - "xlsx": "^0.18.5", + "recharts": "^3.7.0", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", "xlsx-js-style": "^1.2.0" }, "devDependencies": { - "@types/react": "^19.2.8", + "@types/react": "^19.2.9", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.2", "eslint": "^9.39.2", @@ -1606,9 +1606,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz", - "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", + "version": "19.2.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", + "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "devOptional": true, "license": "MIT", "peer": true, @@ -2048,14 +2048,6 @@ "node": ">=6" } }, - "node_modules/codepage": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", - "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3357,9 +3349,9 @@ } }, "node_modules/i18next": { - "version": "25.7.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.7.4.tgz", - "integrity": "sha512-hRkpEblXXcXSNbw8mBNq9042OEetgyB/ahc/X17uV/khPwzV+uB8RHceHh3qavyrkPJvmXFKXME2Sy1E0KjAfw==", + "version": "25.8.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.0.tgz", + "integrity": "sha512-urrg4HMFFMQZ2bbKRK7IZ8/CTE7D8H4JRlAwqA2ZwDRFfdd0K/4cdbNNLgfn9mo+I/h9wJu61qJzH7jCFAhUZQ==", "funding": [ { "type": "individual", @@ -3374,6 +3366,7 @@ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" } ], + "license": "MIT", "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" @@ -4674,9 +4667,13 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/recharts": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", - "integrity": "sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.7.0.tgz", + "integrity": "sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew==", + "license": "MIT", + "workspaces": [ + "www" + ], "dependencies": { "@reduxjs/toolkit": "1.x.x || 2.x.x", "clsx": "^2.1.1", @@ -5735,18 +5732,10 @@ } }, "node_modules/xlsx": { - "version": "0.18.5", - "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", - "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", - "dependencies": { - "adler-32": "~1.3.0", - "cfb": "~1.2.1", - "codepage": "~1.15.0", - "crc-32": "~1.2.1", - "ssf": "~0.11.2", - "wmf": "~1.0.1", - "word": "~0.3.0" - }, + "version": "0.20.3", + "resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", + "integrity": "sha512-oLDq3jw7AcLqKWH2AhCpVTZl8mf6X2YReP+Neh0SJUzV/BdZYjth94tG5toiMB1PPrYtxOCfaoUCkvtuH+3AJA==", + "license": "Apache-2.0", "bin": { "xlsx": "bin/xlsx.njs" }, diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index d06dcd3..6575d02 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -20,7 +20,7 @@ "@fortawesome/react-fontawesome": "^3.1.1", "axios": "^1.13.2", "browser-image-compression": "^2.0.2", - "i18next": "^25.7.4", + "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", "jszip": "^3.10.1", @@ -35,12 +35,12 @@ "react-loader-spinner": "^8.0.2", "react-router-dom": "^7.12.0", "react-toastify": "^11.0.5", - "recharts": "^3.6.0", - "xlsx": "^0.18.5", + "recharts": "^3.7.0", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", "xlsx-js-style": "^1.2.0" }, "devDependencies": { - "@types/react": "^19.2.8", + "@types/react": "^19.2.9", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.2", "eslint": "^9.39.2", diff --git a/src/main/webapp/public/locales/fr/common.json b/src/main/webapp/public/locales/fr/common.json index 934cabf..5355b57 100644 --- a/src/main/webapp/public/locales/fr/common.json +++ b/src/main/webapp/public/locales/fr/common.json @@ -173,7 +173,7 @@ "comp.error1": "La date de fin doit être postérieure à la date de début.", "comp.error2": "Veuillez renseigner les dates de début et de fin d'inscription.", "comp.error3": "La date de fin d'inscription doit être postérieure à la date de début d'inscription.", - "comp.exporterLesInscription": "Exporter les inscription", + "comp.exporterLesInscription": "Exporter les inscriptions", "comp.ha.emailDeRéceptionDesInscriptionséchoué": "Email de réception des inscriptions échoué", "comp.ha.error1": "Veuillez renseigner l'URL de la billetterie HelloAsso et les tarifs associés.", "comp.ha.error2": "L'URL de la billetterie HelloAsso n'est pas valide. Veuillez vérifier le format de l'URL.", diff --git a/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx b/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx index c729efa..5a37561 100644 --- a/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx +++ b/src/main/webapp/src/pages/competition/CompetitionRegisterAdmin.jsx @@ -98,7 +98,7 @@ export function CompetitionRegisterAdmin({source}) { filterNotWeight={filterNotWeight} setFilterNotWeight={setFilterNotWeight} source={source}/> - {source === "admin" && } + {source === "admin" && } @@ -721,29 +721,78 @@ function MakeCentralPanel({data, data2, data3, dispatch, id, setModalState, sour } -function FileOutput({data}) { +function FileOutput({data, data2}) { const {t} = useTranslation(); + const handleFileDownload = () => { + const catColumns = {} + for (const cat of data2) { + catColumns[cat.id] = "" + } + + const columnOrder = [ + "licence", "pays", "nom", "prenom", "genre", "weight", + "categorie", "overCategory", "categorie2", "club", + ...Object.keys(catColumns) + ]; + const dataOut = [] for (const e of data) { const tmp = { licence: e.licence, + pays: e.country, nom: e.lname, prenom: e.fname, genre: e.genre, - weight: e.weight, - categorie: e.categorie, + weight: e.weightReal ? e.weightReal : e.weight, + categorie: getCatName(e.categorie), overCategory: e.overCategory, + categorie2: getCatName(applyOverCategory(e.categorie, e.overCategory)), club: e.club ? e.club.name : '', + ...catColumns + } + for (const c of e.categoriesInscrites) { + tmp[c] = "X" } dataOut.push(tmp) } + dataOut.sort((a, b) => a.prenom.localeCompare(b.prenom) || a.nom.localeCompare(b.nom)); + + const secondHeaders = [ + "Licence", "Pays", "Nom", "Prénom", "Genre", "Poids", + "Catégorie normalizer", "Surclassement", "Catégorie d'inscription", "Club", + ...Object.keys(catColumns).map(id => data2.find(p => p.id === Number(id))?.name) + ]; + const headers = [ + "", "", "", "", "", "", "", "", "", "", "Catégories", + ...Object.keys(catColumns).map(() => "") + ]; + + const orderedData = dataOut.map(row => columnOrder.map(col => row[col])); const wb = XLSX.utils.book_new(); - const ws = XLSX.utils.json_to_sheet(dataOut); - XLSX.utils.sheet_add_aoa(ws, [["Licence", "Nom", "Prénom", "Genre", "Poids", "Catégorie normalizer", "Surclassement", "Club"]], {origin: 'A1'}); + const ws = XLSX.utils.json_to_sheet([], {skipHeader: true}); - ws["!cols"] = [{wch: 7}, {wch: 16}, {wch: 16}, {wch: 6}, {wch: 6}, {wch: 10}, {wch: 10}, {wch: 60}] + XLSX.utils.sheet_add_aoa(ws, [headers, secondHeaders, ...orderedData], {origin: "A1"}); + + // Fusionner les cellules pour le titre "Catégories" + const mergeStart = XLSX.utils.encode_cell({r: 0, c: 10}); // Ligne 1, colonne K (index 10) + const mergeEnd = XLSX.utils.encode_cell({r: 0, c: 10 + Object.keys(catColumns).length - 1}); + ws["!merges"] = [{s: mergeStart, e: mergeEnd}]; + + // 10. Appliquer une rotation de 45° aux en-têtes + const headerRow = ws["!rows"] || (ws["!rows"] = {}); + headerRow[1] = {hpt: 70}; // Hauteur de la première ligne + for (let i = 0; i < headers.length; i++) { + const cellRef = XLSX.utils.encode_cell({r: 1, c: i}); + if (!ws[cellRef]) ws[cellRef] = {}; + ws[cellRef].s = { + alignment: {textRotation: 45, vertical: "bottom", wrapText: true} + }; + } + + ws["!cols"] = [{wch: 5}, {wch: 4}, {wch: 16}, {wch: 16}, {wch: 4}, {wch: 4}, {wch: 10}, {wch: 4}, {wch: 10}, {wch: 60}, + ...Object.keys(catColumns).map(() => ({wch: 2}))] XLSX.utils.book_append_sheet(wb, ws, "Feuille 1"); XLSX.writeFile(wb, "output.xlsx");