Gestion-des-Marches-RLA/services/export-xlsx.js

85 lines
2.4 KiB
JavaScript

/**
* services/export-xlsx.js
* Génération XLSX avec ExcelJS (SuperAdmin uniquement)
*/
const ExcelJS = require('exceljs');
const HEADER_FILL = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF002D62' } };
const HEADER_FONT = { color: { argb: 'FFFFFFFF' }, bold: true, size: 10 };
const ALT_FILL = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF1F5F9' } };
function styleHeader(row) {
row.eachCell(cell => {
cell.fill = HEADER_FILL;
cell.font = HEADER_FONT;
cell.alignment = { vertical: 'middle', horizontal: 'center' };
cell.border = { bottom: { style: 'thin', color: { argb: 'FFE2E8F0' } } };
});
row.height = 22;
}
function styleDataRow(row, alt) {
if (alt) {
row.eachCell(cell => { cell.fill = ALT_FILL; });
}
row.height = 16;
}
async function generateXlsx(view, data) {
const wb = new ExcelJS.Workbook();
wb.creator = 'RLA API';
wb.created = new Date();
const titles = {
synthese: 'Synthèse Globale',
alertes: 'Alertes Délais',
'en-service': 'Marchés en Service',
'en-cours': 'Marchés en Cours',
'par-region': 'Par Région',
clotures: 'Marchés Clôturés',
pilotage: 'Pilotage Proactif',
'matrice-risque': 'Matrice de Risque',
};
const title = titles[view] || view;
const ws = wb.addWorksheet(title.slice(0, 31));
const items = data.items || data.regions || [];
if (!items.length) {
ws.addRow(['Aucune donnée disponible.']);
return wb.xlsx.writeBuffer();
}
const sample = items[0];
const keys = Object.keys(sample).filter(k => !k.endsWith('_raw') && k !== 'id' && typeof sample[k] !== 'object');
// En-tête
ws.columns = keys.map(k => ({ header: k, key: k, width: 20 }));
styleHeader(ws.getRow(1));
// Données
items.forEach((item, i) => {
const row = ws.addRow(keys.map(k => item[k] ?? ''));
styleDataRow(row, i % 2 === 1);
});
// Freeze header
ws.views = [{ state: 'frozen', ySplit: 1 }];
// Onglet résumé si synthèse
if (view === 'synthese' && data.par_statut) {
const ws2 = wb.addWorksheet('Par Statut');
ws2.columns = [{ header: 'Statut', key: 'statut', width: 30 }, { header: 'Nombre', key: 'nb', width: 15 }];
styleHeader(ws2.getRow(1));
Object.entries(data.par_statut).forEach(([s, n], i) => {
const row = ws2.addRow({ statut: s, nb: n });
styleDataRow(row, i % 2 === 1);
});
}
return wb.xlsx.writeBuffer();
}
module.exports = { generateXlsx };