diff --git a/admin/setup.php b/admin/setup.php index 373d94a..eb6ee86 100644 --- a/admin/setup.php +++ b/admin/setup.php @@ -146,7 +146,7 @@ if (empty($templates)) { print ''.dol_print_size(filesize($path)).''; print ''; print ''.$langs->trans("Delete").''; + .'data-dolconfirm="Vorlage löschen?" class="button-small">'.$langs->trans("Delete").''; print ''; print ''; } diff --git a/bericht_card.php b/bericht_card.php index d7a385a..41974a3 100644 --- a/bericht_card.php +++ b/bericht_card.php @@ -243,7 +243,7 @@ if (!$bericht) { } if ($mode !== 'from_order' && $user->hasRight('bericht', 'delete')) { print ''.$langs->trans("Delete").''; } print ''; @@ -548,12 +548,12 @@ if (!$bericht) { print ''; print ''; print '🔀 Neue Version'; - print ''; + print ''; if ($user->hasRight('bericht', 'delete')) { print '🗑️ '.$langs->trans("Delete").''; } print ''; diff --git a/css/bericht.css b/css/bericht.css index c802313..1b32fb0 100644 --- a/css/bericht.css +++ b/css/bericht.css @@ -494,3 +494,48 @@ .qr-url-display { word-break: break-all; } .qr-url-display a { color: var(--colortextlink, #7aa2f7); } .qr-status { padding: 8px; background: var(--colorbackbody, #1a1a1f); border-radius: 4px; margin-top: 12px; } + +/* ---------- dolModal (alert/confirm/prompt Ersatz) ---------- */ +.bericht-dolmodal-overlay { + position: fixed; inset: 0; + background: rgba(0,0,0,0.55); + z-index: 100000; + display: flex; align-items: center; justify-content: center; +} +.bericht-dolmodal { + background: var(--colorbackbody, #22222a); + color: var(--colortext, #eee); + border: 1px solid var(--colorboxbordertitle1, #444); + border-radius: 6px; + min-width: 320px; max-width: 520px; + box-shadow: 0 10px 40px rgba(0,0,0,0.6); + font-size: 14px; +} +.bericht-dolmodal-title { + padding: 10px 14px; + font-weight: bold; + background: var(--colorbacktitle1, #2d2d38); + border-bottom: 1px solid var(--colorboxbordertitle1, #444); + border-radius: 6px 6px 0 0; +} +.bericht-dolmodal-body { + padding: 16px 14px; + line-height: 1.5; + white-space: pre-wrap; +} +.bericht-dolmodal-input { + display: block; + width: 100%; + margin-top: 10px; + padding: 6px 8px; + background: var(--colorbackbody, #1a1a1f); + color: var(--colortext, #eee); + border: 1px solid var(--colorboxbordertitle1, #555); + border-radius: 3px; + box-sizing: border-box; +} +.bericht-dolmodal-foot { + padding: 10px 14px; + display: flex; gap: 8px; justify-content: flex-end; + border-top: 1px solid var(--colorboxbordertitle1, #444); +} diff --git a/js/editor.js b/js/editor.js index 476d111..f595414 100644 --- a/js/editor.js +++ b/js/editor.js @@ -865,7 +865,7 @@ const tplBtn = document.getElementById('btn-save-as-template'); if (tplBtn) { tplBtn.addEventListener('click', async () => { - const label = prompt('Label für die Vorlage:\n(z. B. "PV-Anlage Standard" oder "Wallbox 11kW")'); + const label = await dolPrompt('Label für die Vorlage (z. B. "PV-Anlage Standard" oder "Wallbox 11kW")'); if (!label) return; await savePageAnnotations(false); const fd = new FormData(); @@ -875,12 +875,12 @@ const r = await fetch(cfg.urls.save_as_template, { method: 'POST', body: fd }); const data = await r.json(); if (data.success) toast('✓ Vorlage "' + label + '" gespeichert'); - else alert('Fehler: ' + (data.error || '')); + else dolAlert('Fehler: ' + (data.error || '')); }); } document.getElementById('btn-finalize').addEventListener('click', async () => { - if (!confirm('Bericht jetzt finalisieren und PDF erzeugen?')) return; + if (!(await dolConfirm('Bericht jetzt finalisieren und PDF erzeugen?'))) return; toast('Speichere aktuelle Seite…'); await savePageAnnotations(false); toast('PDF wird erzeugt…'); @@ -918,7 +918,7 @@ btn.addEventListener('click', async () => { const checks = document.querySelectorAll('.att-check:checked'); if (!checks.length) { - alert('Bitte zuerst Bilder ankreuzen'); + dolAlert('Bitte zuerst Bilder ankreuzen'); return; } const layout = layoutSel ? layoutSel.value : 'single'; @@ -942,7 +942,7 @@ const slotCount = { grid_2: 2, grid_2v: 2, grid_4: 4, grid_6: 6, before_after: 2 }[layout] || 4; const imageChecks = Array.from(checks).filter(c => c.dataset.mime.startsWith('image')); if (!imageChecks.length) { - alert('Bitte mindestens ein Bild ankreuzen (PDFs nicht in Grids unterstützt)'); + dolAlert('Bitte mindestens ein Bild ankreuzen (PDFs nicht in Grids unterstützt)'); return; } // In Gruppen à slotCount aufteilen @@ -956,7 +956,7 @@ const r = await fetch(cfg.urls.create_grid_page, { method: 'POST', body: fd }); const data = await r.json().catch(() => ({})); if (!data.success) { - alert('Fehler bei Gruppe '+(Math.floor(i/slotCount)+1)+': '+(data.error || 'unbekannt')); + dolAlert('Fehler bei Gruppe '+(Math.floor(i/slotCount)+1)+': '+(data.error || 'unbekannt')); return; } } @@ -972,7 +972,7 @@ const rel = btn.dataset.relpath; const ref = btn.dataset.sourceRef || ''; const name = btn.parentElement.querySelector('.att-name')?.textContent || rel; - if (!confirm('Datei "' + name + '" aus ' + (ref ? ref : 'dem Anhang') + ' wirklich löschen?\n\nDiese Aktion kann nicht rückgängig gemacht werden.')) return; + if (!(await dolConfirm('Datei "' + name + '" aus ' + (ref ? ref : 'dem Anhang') + ' wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.'))) return; const fd = new FormData(); fd.append('token', cfg.token); fd.append('relpath', rel); @@ -982,7 +982,7 @@ btn.parentElement.remove(); toast('Datei gelöscht'); } else { - alert('Löschen fehlgeschlagen: ' + (data.error || 'unbekannt')); + dolAlert('Löschen fehlgeschlagen: ' + (data.error || 'unbekannt')); } }); }); @@ -1000,7 +1000,7 @@ const r = await fetch(cfg.urls.upload_extra, { method: 'POST', body: fd }); const data = await r.json(); if (data.success) location.reload(); - else alert('Upload fehlgeschlagen: ' + (data.error || '')); + else dolAlert('Upload fehlgeschlagen: ' + (data.error || '')); }); } @@ -1023,7 +1023,7 @@ const r = await fetch(cfg.urls.create_upload_token, { method: 'POST', body: fd }); const data = await r.json(); if (!data.success) { - alert('Token-Erstellung fehlgeschlagen: ' + (data.error || '')); + dolAlert('Token-Erstellung fehlgeschlagen: ' + (data.error || '')); return; } const url = data.url; @@ -1080,7 +1080,7 @@ const del = t.querySelector('.thumb-del'); if (del) del.addEventListener('click', async (e) => { e.stopPropagation(); - if (!confirm(cfg.lang.confirm_del)) return; + if (!(await dolConfirm(cfg.lang.confirm_del))) return; const fd = new FormData(); fd.append('token', cfg.token); fd.append('pageid', t.dataset.pageid); @@ -1111,13 +1111,13 @@ const data = await r.json(); btn.textContent = '🔍'; if (!data.success) { - alert('Fehler: ' + (data.error || 'unbekannt')); + dolAlert('Fehler: ' + (data.error || 'unbekannt')); return; } showSignatureVerifyResult(data); } catch (err) { btn.textContent = '🔍'; - alert('Netzwerkfehler: ' + err.message); + dolAlert('Netzwerkfehler: ' + err.message); } }); }); @@ -1261,5 +1261,97 @@ setTimeout(() => t.remove(), 2000); } + /* ---------- Dolibarr-Style Modal-Dialoge (ersetzen alert/confirm/prompt) ---------- */ + function dolModal(opts) { + // opts: { title, body, buttons:[{label, value, primary}], input:boolean, defaultValue } + return new Promise((resolve) => { + const ov = document.createElement('div'); + ov.className = 'bericht-dolmodal-overlay'; + const box = document.createElement('div'); + box.className = 'bericht-dolmodal'; + const h = document.createElement('div'); + h.className = 'bericht-dolmodal-title'; + h.textContent = opts.title || 'Bericht'; + const b = document.createElement('div'); + b.className = 'bericht-dolmodal-body'; + b.textContent = opts.body || ''; + let inputEl = null; + if (opts.input) { + inputEl = document.createElement('input'); + inputEl.type = 'text'; + inputEl.className = 'bericht-dolmodal-input'; + inputEl.value = opts.defaultValue || ''; + b.appendChild(document.createElement('br')); + b.appendChild(inputEl); + } + const foot = document.createElement('div'); + foot.className = 'bericht-dolmodal-foot'; + (opts.buttons || [{label:'OK', value:true, primary:true}]).forEach(btn => { + const el = document.createElement('button'); + el.type = 'button'; + el.className = btn.primary ? 'butAction butActionConfirm' : 'butAction'; + el.textContent = btn.label; + el.addEventListener('click', () => { + const val = opts.input ? (btn.value ? (inputEl.value || null) : null) : btn.value; + ov.remove(); + resolve(val); + }); + foot.appendChild(el); + }); + box.appendChild(h); box.appendChild(b); box.appendChild(foot); + ov.appendChild(box); + document.body.appendChild(ov); + if (inputEl) { inputEl.focus(); inputEl.select(); } + ov.addEventListener('click', (e) => { + if (e.target === ov) { ov.remove(); resolve(opts.input ? null : false); } + }); + }); + } + async function dolAlert(msg, title) { + return dolModal({ + title: title || 'Hinweis', + body: msg, + buttons: [{label:'OK', value:true, primary:true}], + }); + } + async function dolConfirm(msg, title) { + return dolModal({ + title: title || 'Bestätigen', + body: msg, + buttons: [ + {label:'Abbrechen', value:false}, + {label:'OK', value:true, primary:true}, + ], + }); + } + async function dolPrompt(msg, defaultValue, title) { + return dolModal({ + title: title || 'Eingabe', + body: msg, + input: true, + defaultValue: defaultValue || '', + buttons: [ + {label:'Abbrechen', value:false}, + {label:'OK', value:true, primary:true}, + ], + }); + } + // Globaler Handler: Links/Buttons mit data-dolconfirm abfangen + document.addEventListener('click', async (e) => { + const el = e.target.closest('[data-dolconfirm]'); + if (!el) return; + if (el.dataset._dolconfirmed === '1') return; // schon durchgelaufen + e.preventDefault(); + e.stopPropagation(); + const ok = await dolConfirm(el.getAttribute('data-dolconfirm')); + if (!ok) return; + el.dataset._dolconfirmed = '1'; + if (el.tagName === 'A') { + window.location.href = el.getAttribute('href'); + } else { + el.click(); + } + }, true); + document.addEventListener('DOMContentLoaded', init); })();