db7094fd1e
CSRF / actions: - List action forms now send both REQUEST_TOKEN (Contao gate) and _token (Symfony, validated via isCsrfTokenValid) — mirrors the editor; fixes the 400/500 "Ungültiger CSRF-Token" on publish/toggle/duplicate/delete Public reader / URLs: - Reader & results read auto_item via Input::get (marks the route param used) so clean path URLs /<page>/<alias> no longer 404 - List controller generates path URLs via ['parameters' => '/'.$alias] - Allow re-participation in the same session after a 60s cooldown Review fixes: - Results: merge answer snapshots by questionId+type (no more split cards on label edits); seed "Vielleicht" when allowMaybe so screen matches Excel - Reader: replace generic answer hint with per-option "weiter zu Frage X" only when a jump is configured; strip HTML tags from survey description - PDF: render free-text frequency summary; drop duplicate "Minimum" label - Drag-sort: bundle SortableJS locally, drop the duplicated inline sort CSS/JS from _survey_assets, throttle the backend MutationObserver - Backend: move hardcoded tl_survey operation labels to TL_LANG (de+en) - Remove dead code: SurveyEditorVoter, SurveyPermissionService, categoryFilter/categorySummary + helpers, orphaned publishSurvey lang key Reader layout: - max-width 1000px (other views keep 1400px); back/next buttons fill the width as equal, card-sized halves; sort handle icon offset tweak Docs: update AI_HANDOVER (dual-token CSRF, clean URLs) and README Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
110 lines
4.2 KiB
JavaScript
110 lines
4.2 KiB
JavaScript
(function () {
|
|
function ensureSortIconStyles() {
|
|
if (document.getElementById('survey-question-sort-icon-theme')) {
|
|
return;
|
|
}
|
|
|
|
var style = document.createElement('style');
|
|
style.id = 'survey-question-sort-icon-theme';
|
|
style.textContent = [
|
|
'.survey-sort-icon{width:1.14rem !important;height:1.52rem !important;color:#58a6da !important;background:none !important;transform:translateY(1px) !important;}',
|
|
'.survey-sort-icon::before,.survey-sort-icon::after{content:none !important;}',
|
|
'.survey-sort-icon svg{display:block !important;width:100% !important;height:100% !important;transform:translateY(5px);}',
|
|
'.survey-sort-icon svg path:first-child{transform:translateY(1.4px);transform-box:fill-box;transform-origin:center;}',
|
|
'.survey-sort-icon svg path:nth-child(2){transform:translateY(-1.4px);transform-box:fill-box;transform-origin:center;}',
|
|
'.survey-sort-icon svg path[stroke]{stroke-width:2px;}'
|
|
].join('');
|
|
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
function initSurveyQuestionSorting() {
|
|
ensureSortIconStyles();
|
|
|
|
if (typeof Sortable !== 'function') {
|
|
return;
|
|
}
|
|
|
|
var sortableList = document.querySelector('[data-survey-question-sortable]');
|
|
var orderForm = document.querySelector('[data-survey-question-order-shell]');
|
|
var orderInput = document.querySelector('[data-survey-question-order-input]');
|
|
|
|
if (!sortableList || !orderForm || !orderInput || sortableList._surveyQuestionSortable) {
|
|
return;
|
|
}
|
|
|
|
var getOrderedIds = function () {
|
|
return Array.from(sortableList.querySelectorAll('[data-survey-question-item]')).map(function (item) {
|
|
return item.getAttribute('data-question-id') || '';
|
|
}).filter(function (value) {
|
|
return value !== '';
|
|
});
|
|
};
|
|
|
|
var initialOrder = getOrderedIds().join(',');
|
|
|
|
var updateOrderField = function () {
|
|
orderInput.value = getOrderedIds().join(',');
|
|
};
|
|
|
|
var updateQuestionIndices = function () {
|
|
sortableList.querySelectorAll('[data-survey-question-item]').forEach(function (item, index) {
|
|
var indexNode = item.querySelector('.survey-accordion-index');
|
|
|
|
if (indexNode) {
|
|
indexNode.textContent = (index + 1) + '.';
|
|
}
|
|
});
|
|
};
|
|
|
|
var updateSaveVisibility = function () {
|
|
var currentOrder = getOrderedIds().join(',');
|
|
var isDirty = currentOrder !== initialOrder;
|
|
|
|
orderForm.hidden = !isDirty;
|
|
orderForm.classList.toggle('is-visible', isDirty);
|
|
updateOrderField();
|
|
};
|
|
|
|
sortableList.querySelectorAll('[data-survey-question-sort-handle]').forEach(function (handle) {
|
|
handle.addEventListener('click', function (event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
});
|
|
});
|
|
|
|
updateQuestionIndices();
|
|
updateOrderField();
|
|
updateSaveVisibility();
|
|
|
|
sortableList._surveyQuestionSortable = Sortable.create(sortableList, {
|
|
animation: 180,
|
|
draggable: '[data-survey-question-item]',
|
|
handle: '[data-survey-question-sort-handle]',
|
|
ghostClass: 'sortable-ghost',
|
|
forceFallback: true,
|
|
fallbackTolerance: 3,
|
|
onStart: function (event) {
|
|
if (event.item) {
|
|
event.item.classList.add('is-reordering');
|
|
}
|
|
},
|
|
onEnd: function (event) {
|
|
if (event.item) {
|
|
event.item.classList.remove('is-reordering');
|
|
}
|
|
|
|
updateQuestionIndices();
|
|
updateSaveVisibility();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initSurveyQuestionSorting, { once: true });
|
|
} else {
|
|
initSurveyQuestionSorting();
|
|
}
|
|
|
|
window.addEventListener('load', initSurveyQuestionSorting, { once: true });
|
|
})(); |