/* ============================================================ * 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
A
${FIRM}
${escHtml(title)}${subtitle ? ' — ' + escHtml(subtitle) : ''}
Exportado: ${escHtml(fechaStr)}
Total registros: ${rows.length}
${pdfHeaders.map(h => ``).join('')}${styledRows}
${escHtml(h)}