/* ============================================================
* Donna — Helpers compartidos para exportación a Excel y PDF
* ============================================================
* Requiere XLSX (https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js)
* cargado antes de este script.
*
* Uso:
*
…botón + menú…
* DonnaExport.attach('export-wrap', { onExcel, onPDF });
* DonnaExport.toExcel({ filename, sheetName, headers, rows, columnWidths });
* DonnaExport.toPDF({ filename, title, subtitle, headers, rows,
* pdfCols, orientation, statusColIndex, statusColors, footer });
*/
(function(){
'use strict';
const NAVY = '#1B3A6B';
const GOLD = '#C9A84C';
const FIRM = 'AAL — Arias, Abrego & López';
const ADDR = 'P.H. Midtown, Piso 16, Of. 1605, Calle 74 Este, San Francisco, Ciudad de Panamá';
function escHtml(s){
return (s==null?'':String(s))
.replace(/&/g,'&').replace(/"/g,'"')
.replace(//g,'>');
}
function todayStamp(){
const d = new Date();
const pad = n => String(n).padStart(2,'0');
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
}
function attach(wrapId, handlers){
const wrap = document.getElementById(wrapId);
if (!wrap) return;
const btn = wrap.querySelector('.btn-export');
const menu = wrap.querySelector('.export-menu');
const xl = wrap.querySelector('.export-menu button.xl');
const pdf = wrap.querySelector('.export-menu button.pdf');
if (btn && menu) {
btn.addEventListener('click', e => {
e.stopPropagation();
menu.classList.toggle('open');
});
}
document.addEventListener('click', e => {
if (!wrap.contains(e.target)) menu && menu.classList.remove('open');
});
if (xl && handlers.onExcel) xl.addEventListener('click', () => { menu.classList.remove('open'); handlers.onExcel(); });
if (pdf && handlers.onPDF) pdf.addEventListener('click', () => { menu.classList.remove('open'); handlers.onPDF(); });
}
function toExcel(opts){
if (typeof XLSX === 'undefined') { alert('XLSX no está cargado'); return; }
const headers = opts.headers || [];
const rows = opts.rows || [];
const sheetName = opts.sheetName || 'Datos';
const fileBase = opts.filename || 'Donna_Export';
if (!rows.length) { opts.onEmpty && opts.onEmpty(); return; }
const wb = XLSX.utils.book_new();
const data = [headers, ...rows];
const ws = XLSX.utils.aoa_to_sheet(data);
const widths = opts.columnWidths || headers.map(() => 18);
ws['!cols'] = widths.map(w => ({ wch: w }));
const range = XLSX.utils.decode_range(ws['!ref']);
for (let C = range.s.c; C <= range.e.c; C++) {
const addr = XLSX.utils.encode_cell({r:0, c:C});
if (!ws[addr]) continue;
ws[addr].s = {
fill: { fgColor: { rgb: '1B3A6B' } },
font: { bold: true, color: { rgb: 'FFFFFF' }, sz: 11 },
alignment: { horizontal: 'center', vertical: 'center', wrapText: true }
};
}
XLSX.utils.book_append_sheet(wb, ws, sheetName);
XLSX.writeFile(wb, `${fileBase}_${todayStamp()}.xlsx`);
}
function toPDF(opts){
const headers = opts.headers || [];
const rows = opts.rows || [];
const title = opts.title || 'Reporte';
const subtitle = opts.subtitle || '';
const orientation = opts.orientation || 'landscape';
const fileBase = opts.filename || 'Donna_Export';
const footer = opts.footer != null ? opts.footer : ADDR;
const pdfCols = opts.pdfCols || headers.map((_,i) => i);
const statusCol = opts.statusColIndex;
const statusColors = opts.statusColors || {};
if (!rows.length) { opts.onEmpty && opts.onEmpty(); return; }
const pdfHeaders = pdfCols.map(i => headers[i]);
const styledRows = rows.map((r, ri) => {
const cells = pdfCols.map(ci => {
const val = r[ci] == null ? '' : String(r[ci]);
if (statusCol === ci && statusColors[val]) {
const [bg, fg] = statusColors[val];
return `${escHtml(val)} | `;
}
return `${escHtml(val)} | `;
}).join('');
return `${cells}
`;
}).join('');
const fechaStr = new Date().toLocaleDateString('es-PA');
const html = `
${escHtml(title)} — AAL
${pdfHeaders.map(h => `| ${escHtml(h)} | `).join('')}
${styledRows}