Warn on leaving the editor with unsaved changes
Ergänzt einen beforeunload-Handler im Frontend-Editor: Beim Verlassen der Seite (Link, Zurück, Tab schließen) wird gewarnt, sobald mindestens ein Editor-Formular ungespeicherte Änderungen hat. Der Handler koppelt an die bestehende Dirty-Erkennung (data-survey-dirty-form) und wird nur registriert, wenn solche Formulare existieren – also nur im Editor, nicht in Liste/Reader/Ergebnissen. Ein bewusstes Absenden (Speichern oder Aktion) setzt über einen globalen submit-Listener ein Flag, sodass der normale Speicher-Flow nicht warnt. Bewusst kein Auto-Save (getrennte Formulare mit eigenen CSRF-Tokens, Struktur-Sperre nach Teilnahmen/Veröffentlichung); Dirty-Hinweis plus beforeunload sind der risikoarme Kompromiss. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -170,6 +170,14 @@ Aktueller Aufbau:
|
|||||||
- Fragen im Frontend nicht mehr änderbar
|
- Fragen im Frontend nicht mehr änderbar
|
||||||
- Metadaten im Frontend ebenfalls nicht mehr bearbeitbar
|
- Metadaten im Frontend ebenfalls nicht mehr bearbeitbar
|
||||||
|
|
||||||
|
Ungespeicherte Änderungen (bewusst kein Auto-Save):
|
||||||
|
|
||||||
|
- Jedes Editor-Formular (`data-survey-dirty-form`: Metadaten, jede Frage, neue Frage) trackt clientseitig, ob sich der Inhalt gegenüber dem Ladezustand geändert hat, und blendet den roten „Ungespeicherte Änderungen"-Hinweis ein.
|
||||||
|
- Zusätzlich gibt es einen `beforeunload`-Handler (`_survey_assets.html.twig`), der beim Verlassen der Seite (Link, Zurück, Tab schließen) warnt, sobald mindestens ein Formular ungespeicherte Änderungen hat.
|
||||||
|
- Der Handler wird nur registriert, wenn dirty-getrackte Formulare existieren (also nur im Editor, nicht in Liste/Reader/Ergebnissen).
|
||||||
|
- Ein bewusstes Absenden (Speichern oder eine Aktion) setzt ein `surveyIsSubmitting`-Flag über einen globalen `submit`-Listener, damit der normale Speicher-Flow **nicht** warnt.
|
||||||
|
- Auto-Save wurde bewusst nicht umgesetzt (getrennte Formulare mit eigenen CSRF-Tokens, Struktur-Sperre nach Teilnahmen/Veröffentlichung). Der Dirty-Hinweis plus die `beforeunload`-Warnung sind der gewählte, risikoarme Kompromiss.
|
||||||
|
|
||||||
Wichtige Entscheidung:
|
Wichtige Entscheidung:
|
||||||
|
|
||||||
- Die Header-Badges im Fragen-Akkordeon wurden bewusst entfernt.
|
- Die Header-Badges im Fragen-Akkordeon wurden bewusst entfernt.
|
||||||
|
|||||||
@@ -1166,6 +1166,9 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var surveyDirtyChecks = [];
|
||||||
|
var surveyIsSubmitting = false;
|
||||||
|
|
||||||
document.querySelectorAll('[data-survey-dirty-form]').forEach(function (form) {
|
document.querySelectorAll('[data-survey-dirty-form]').forEach(function (form) {
|
||||||
var formId = form.getAttribute('id') || '';
|
var formId = form.getAttribute('id') || '';
|
||||||
var hint = form.querySelector('[data-survey-dirty-hint]');
|
var hint = form.querySelector('[data-survey-dirty-hint]');
|
||||||
@@ -1174,10 +1177,6 @@
|
|||||||
hint = document.querySelector('[data-survey-dirty-hint-for="' + formId + '"]');
|
hint = document.querySelector('[data-survey-dirty-hint-for="' + formId + '"]');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hint) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var serializeForm = function () {
|
var serializeForm = function () {
|
||||||
var entries = [];
|
var entries = [];
|
||||||
var formData = new FormData(form);
|
var formData = new FormData(form);
|
||||||
@@ -1191,25 +1190,61 @@
|
|||||||
|
|
||||||
var initialState = serializeForm();
|
var initialState = serializeForm();
|
||||||
|
|
||||||
|
var isDirty = function () {
|
||||||
|
return serializeForm() !== initialState;
|
||||||
|
};
|
||||||
|
|
||||||
|
surveyDirtyChecks.push(isDirty);
|
||||||
|
|
||||||
var updateDirtyState = function () {
|
var updateDirtyState = function () {
|
||||||
hint.hidden = serializeForm() === initialState;
|
if (hint) {
|
||||||
};
|
hint.hidden = !isDirty();
|
||||||
|
}
|
||||||
var markDirty = function () {
|
|
||||||
updateDirtyState();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (hint) {
|
||||||
hint.hidden = true;
|
hint.hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
form.addEventListener('input', markDirty);
|
form.addEventListener('input', updateDirtyState);
|
||||||
form.addEventListener('change', markDirty);
|
form.addEventListener('change', updateDirtyState);
|
||||||
form.addEventListener('reset', function () {
|
form.addEventListener('reset', function () {
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
|
if (hint) {
|
||||||
hint.hidden = true;
|
hint.hidden = true;
|
||||||
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (surveyDirtyChecks.length > 0) {
|
||||||
|
// Ein bewusstes Absenden (Speichern oder eine Aktion) darf nicht warnen.
|
||||||
|
document.addEventListener('submit', function () {
|
||||||
|
surveyIsSubmitting = true;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', function (event) {
|
||||||
|
if (surveyIsSubmitting) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasUnsavedChanges = surveyDirtyChecks.some(function (isDirty) {
|
||||||
|
return isDirty();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasUnsavedChanges) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moderne Browser zeigen einen generischen Hinweis; der Text wird
|
||||||
|
// ignoriert, muss aber gesetzt werden, damit der Dialog erscheint.
|
||||||
|
event.preventDefault();
|
||||||
|
event.returnValue = '';
|
||||||
|
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof handorgel === 'function') {
|
if (typeof handorgel === 'function') {
|
||||||
document.querySelectorAll('[data-survey-handorgel]').forEach(function (element) {
|
document.querySelectorAll('[data-survey-handorgel]').forEach(function (element) {
|
||||||
var accordion = new handorgel(element, {
|
var accordion = new handorgel(element, {
|
||||||
|
|||||||
Reference in New Issue
Block a user