feat: show PDF export progress modal with per-card progress
This commit is contained in:
@@ -104,6 +104,14 @@ body.modal-open { overflow: hidden; }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pdfProgressModal" style="position:fixed;inset:0;background:rgba(15,23,42,.45);display:none;align-items:center;justify-content:center;z-index:95;padding:20px;">
|
||||
<div style="width:min(420px,92vw);background:#fff;border-radius:12px;border:1px solid #dbe7ff;padding:14px;box-shadow:0 20px 60px rgba(15,23,42,.25)">
|
||||
<div style="font-weight:700;margin-bottom:8px">PDF wird erstellt…</div>
|
||||
<div style="height:10px;background:#e2e8f0;border-radius:99px;overflow:hidden"><div id="pdfProgressBar" style="height:100%;width:0%;background:#2563eb"></div></div>
|
||||
<small id="pdfProgressText" style="display:block;margin-top:8px;color:#475569">Starte…</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalAi" style="position:fixed;inset:0;background:rgba(15,23,42,.45);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:75;padding:20px;">
|
||||
<div class="box" style="width:min(1320px,98vw);max-height:96vh;overflow:auto;background:#fff;border-radius:14px;border:1px solid #dbe7ff;box-shadow:0 20px 60px rgba(15,23,42,.25)">
|
||||
<div class="modal-header" style="align-items:flex-start;gap:14px;">
|
||||
@@ -170,6 +178,16 @@ let aiEtaBaseSec = null;
|
||||
let aiEtaBaseTs = null;
|
||||
let aiLastDone = 0;
|
||||
|
||||
function setPdfProgress(open, pct=0, text=''){
|
||||
const m = document.getElementById('pdfProgressModal');
|
||||
const b = document.getElementById('pdfProgressBar');
|
||||
const t = document.getElementById('pdfProgressText');
|
||||
if (!m || !b || !t) return;
|
||||
m.style.display = open ? 'flex' : 'none';
|
||||
b.style.width = `${Math.max(0, Math.min(100, pct))}%`;
|
||||
t.textContent = text || '';
|
||||
}
|
||||
|
||||
function fmtDuration(seconds){
|
||||
const s = Math.max(0, Math.round(seconds||0));
|
||||
const m = Math.floor(s/60);
|
||||
@@ -501,6 +519,7 @@ async function downloadPdf(){
|
||||
console.log('[PDF] click');
|
||||
const openPrintFallback = (msg='') => {
|
||||
console.warn('[PDF] fallback print', msg || '(no message)');
|
||||
setPdfProgress(false);
|
||||
if (msg) alert(msg);
|
||||
const w = window.open('', '_blank');
|
||||
if (!w) {
|
||||
@@ -522,6 +541,8 @@ async function downloadPdf(){
|
||||
return;
|
||||
}
|
||||
|
||||
setPdfProgress(true, 2, 'Bereite Export vor…');
|
||||
|
||||
const root = document.createElement('div');
|
||||
root.style.position = 'fixed';
|
||||
root.style.left = '-10000px';
|
||||
@@ -547,6 +568,8 @@ async function downloadPdf(){
|
||||
const usableH = pageH - margin * 2;
|
||||
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
const pctStart = 5 + Math.round((i / cards.length) * 90);
|
||||
setPdfProgress(true, pctStart, `Rendere Karte ${i + 1} von ${cards.length}…`);
|
||||
console.log('[PDF] rendering card', i + 1, '/', cards.length);
|
||||
const canvas = await Promise.race([
|
||||
window.html2canvas(cards[i], { scale: 2, useCORS: true, backgroundColor: '#ffffff', logging: false }),
|
||||
@@ -563,14 +586,18 @@ async function downloadPdf(){
|
||||
}
|
||||
|
||||
const filename = `coachingcards-${new Date().toISOString().slice(0,10)}.pdf`;
|
||||
setPdfProgress(true, 98, 'Speichere PDF…');
|
||||
console.log('[PDF] saving', filename);
|
||||
pdf.save(filename);
|
||||
console.log('[PDF] save triggered');
|
||||
setPdfProgress(true, 100, 'Fertig');
|
||||
setTimeout(() => setPdfProgress(false), 500);
|
||||
} catch (e) {
|
||||
console.error('[PDF] export failed', e);
|
||||
openPrintFallback('PDF-Download fehlgeschlagen – nutze Druckdialog als Fallback. Schau in die Browser-Konsole.');
|
||||
} finally {
|
||||
root.remove();
|
||||
setTimeout(() => setPdfProgress(false), 300);
|
||||
console.log('[PDF] cleanup done');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user