diff --git a/config/current.ver b/config/current.ver
index 725ffd4..d9e75ec 100644
--- a/config/current.ver
+++ b/config/current.ver
@@ -1 +1 @@
-1.2.27
\ No newline at end of file
+1.2.28
\ No newline at end of file
diff --git a/public/assets/js/ui-editor.js b/public/assets/js/ui-editor.js
index 219d055..d76e79e 100644
--- a/public/assets/js/ui-editor.js
+++ b/public/assets/js/ui-editor.js
@@ -60,8 +60,65 @@ export function initEditor() {
let currentVersionId = 0;
let currentVersionMeta = null;
- const ok = (m) => toast(m, true);
- const err = (m) => toast(m, false);
+ const ok = (m) => toast(m, true);
+ const err = (m) => toast(m, false);
+
+ const showConfirmDialog = (() => {
+ let dialog;
+ let titleEl;
+ let textEl;
+ let btnOk;
+ let btnCancel;
+ let pendingResolve = null;
+
+ const ensure = () => {
+ if (dialog) return;
+ dialog = document.createElement('dialog');
+ dialog.className = 'rounded-2xl p-0 w-[520px]';
+ dialog.innerHTML = `
+
+
+
+
+
+
+
+
+ `;
+ document.body.appendChild(dialog);
+ titleEl = dialog.querySelector('[data-confirm-title]');
+ textEl = dialog.querySelector('[data-confirm-text]');
+ btnOk = dialog.querySelector('[data-confirm-ok]');
+ btnCancel = dialog.querySelector('[data-confirm-cancel]');
+
+ btnOk?.addEventListener('click', () => {
+ if (pendingResolve) pendingResolve(true);
+ pendingResolve = null;
+ dialog.close();
+ });
+ btnCancel?.addEventListener('click', () => {
+ if (pendingResolve) pendingResolve(false);
+ pendingResolve = null;
+ dialog.close();
+ });
+ dialog.addEventListener('close', () => {
+ if (pendingResolve) pendingResolve(false);
+ pendingResolve = null;
+ });
+ };
+
+ return async ({ title, text, confirmLabel = 'Bestätigen', cancelLabel = 'Abbrechen' }) => {
+ ensure();
+ if (titleEl) titleEl.textContent = title || 'Bestätigung';
+ if (textEl) textEl.textContent = text || '';
+ if (btnOk) btnOk.textContent = confirmLabel;
+ if (btnCancel) btnCancel.textContent = cancelLabel;
+ if (!dialog.open) dialog.showModal();
+ return await new Promise(resolve => {
+ pendingResolve = resolve;
+ });
+ };
+ })();
// ---------- Hilfen ----------
function activeMode() {
@@ -1107,13 +1164,21 @@ export function initEditor() {
if (!current?.id || !isTemplateSection()) return true;
const res = await apiAction('templates.references', { method: 'GET', data: { template_id: current.id } }).catch(() => null);
if (!res || res.ok === false) {
- return confirm(`Referenzen konnten nicht geprüft werden. ${actionLabel} trotzdem?`);
+ return await showConfirmDialog({
+ title: 'Referenzen nicht geprüft',
+ text: `Referenzen konnten nicht geprüft werden. ${actionLabel} trotzdem?`,
+ confirmLabel: actionLabel,
+ });
}
const refs = Array.isArray(res?.references) ? res.references : [];
if (!refs.length) return true;
const preview = refs.slice(0, 6).map(r => `${r.name || 'Template'} #${r.id}`).join(', ');
const more = refs.length > 6 ? ` und ${refs.length - 6} weitere` : '';
- return confirm(`Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`);
+ return await showConfirmDialog({
+ title: 'Template wird verwendet',
+ text: `Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`,
+ confirmLabel: actionLabel,
+ });
}
// Buttons
@@ -1146,7 +1211,11 @@ export function initEditor() {
if (!okRefs) return;
let res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: current.id } });
if (res && res.ok === false && Array.isArray(res.references) && res.references.length) {
- const ok = confirm('Dieses Template wird in anderen Templates verwendet. Trotzdem deaktivieren?');
+ const ok = await showConfirmDialog({
+ title: 'Template wird verwendet',
+ text: 'Dieses Template wird in anderen Templates verwendet. Trotzdem deaktivieren?',
+ confirmLabel: 'Deaktivieren',
+ });
if (!ok) return;
res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: current.id, force: 1 } });
}
diff --git a/public/assets/js/ui-list.js b/public/assets/js/ui-list.js
index 35a1dc7..e4045d7 100644
--- a/public/assets/js/ui-list.js
+++ b/public/assets/js/ui-list.js
@@ -9,6 +9,63 @@ function esc(s = '') {
.replace(/'/g, ''');
}
+const showConfirmDialog = (() => {
+ let dialog;
+ let titleEl;
+ let textEl;
+ let btnOk;
+ let btnCancel;
+ let pendingResolve = null;
+
+ const ensure = () => {
+ if (dialog) return;
+ dialog = document.createElement('dialog');
+ dialog.className = 'rounded-2xl p-0 w-[520px]';
+ dialog.innerHTML = `
+
+
+
+
+
+
+
+
+ `;
+ document.body.appendChild(dialog);
+ titleEl = dialog.querySelector('[data-confirm-title]');
+ textEl = dialog.querySelector('[data-confirm-text]');
+ btnOk = dialog.querySelector('[data-confirm-ok]');
+ btnCancel = dialog.querySelector('[data-confirm-cancel]');
+
+ btnOk?.addEventListener('click', () => {
+ if (pendingResolve) pendingResolve(true);
+ pendingResolve = null;
+ dialog.close();
+ });
+ btnCancel?.addEventListener('click', () => {
+ if (pendingResolve) pendingResolve(false);
+ pendingResolve = null;
+ dialog.close();
+ });
+ dialog.addEventListener('close', () => {
+ if (pendingResolve) pendingResolve(false);
+ pendingResolve = null;
+ });
+ };
+
+ return async ({ title, text, confirmLabel = 'Bestätigen', cancelLabel = 'Abbrechen' }) => {
+ ensure();
+ if (titleEl) titleEl.textContent = title || 'Bestätigung';
+ if (textEl) textEl.textContent = text || '';
+ if (btnOk) btnOk.textContent = confirmLabel;
+ if (btnCancel) btnCancel.textContent = cancelLabel;
+ if (!dialog.open) dialog.showModal();
+ return await new Promise(resolve => {
+ pendingResolve = resolve;
+ });
+ };
+})();
+
function formatVersionDate(value) {
if (!value) return '';
try {
@@ -105,12 +162,20 @@ async function openTemplateManager(item, section) {
if (!isTemplateSection()) return true;
const refs = await fetchTemplateReferences();
if (refs === null) {
- return confirm(`Referenzen konnten nicht geprüft werden. ${actionLabel} trotzdem?`);
+ return await showConfirmDialog({
+ title: 'Referenzen nicht geprüft',
+ text: `Referenzen konnten nicht geprüft werden. ${actionLabel} trotzdem?`,
+ confirmLabel: actionLabel,
+ });
}
if (!refs.length) return true;
const preview = refs.slice(0, 6).map(r => `${r.name || 'Template'} #${r.id}`).join(', ');
const more = refs.length > 6 ? ` und ${refs.length - 6} weitere` : '';
- return confirm(`Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`);
+ return await showConfirmDialog({
+ title: 'Template wird verwendet',
+ text: `Dieses Template wird in ${refs.length} anderen Template(s) verwendet (${preview}${more}). ${actionLabel} trotzdem?`,
+ confirmLabel: actionLabel,
+ });
};
const updateDeleteState = () => {
@@ -262,7 +327,11 @@ async function openTemplateManager(item, section) {
}
let res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: item.id } });
if (res && res.ok === false && Array.isArray(res.references) && res.references.length) {
- const ok = confirm('Dieses Template wird in anderen Templates verwendet. Trotzdem deaktivieren?');
+ const ok = await showConfirmDialog({
+ title: 'Template wird verwendet',
+ text: 'Dieses Template wird in anderen Templates verwendet. Trotzdem deaktivieren?',
+ confirmLabel: 'Deaktivieren',
+ });
if (!ok) return;
res = await apiAction('content_versions.deactivate', { method: 'POST', data: { content_id: item.id, force: 1 } });
}
@@ -277,7 +346,11 @@ async function openTemplateManager(item, section) {
if (!confirm('Version wirklich löschen?')) return;
let res = await apiAction('content_versions.delete', { method: 'POST', data: { id: vid, content_id: item.id } });
if (res && res.ok === false && Array.isArray(res.references) && res.references.length) {
- const ok = confirm('Dieses Template wird in anderen Templates verwendet. Trotzdem löschen?');
+ const ok = await showConfirmDialog({
+ title: 'Template wird verwendet',
+ text: 'Dieses Template wird in anderen Templates verwendet. Trotzdem löschen?',
+ confirmLabel: 'Löschen',
+ });
if (!ok) return;
res = await apiAction('content_versions.delete', { method: 'POST', data: { id: vid, content_id: item.id, force: 1 } });
}