diff --git a/backend/workflows/static/workflows/css/form_builder.css b/backend/workflows/static/workflows/css/form_builder.css
index f6db87f..4f7ce6c 100644
--- a/backend/workflows/static/workflows/css/form_builder.css
+++ b/backend/workflows/static/workflows/css/form_builder.css
@@ -977,6 +977,14 @@ body {
gap: 14px;
}
+.builder-card-head-actions {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+}
+
.builder-entity-card-head strong {
color: #142033;
font-size: 15px;
@@ -1003,6 +1011,7 @@ body {
.builder-switch-stack {
display: grid;
gap: 8px;
+ justify-items: end;
}
.builder-switch input[type='checkbox'],
@@ -1031,61 +1040,62 @@ body {
gap: 10px;
}
-.section-rule-grid.drag-over {
- outline: 1px dashed #9db4d2;
- outline-offset: 6px;
-}
-
.section-rule-card {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
gap: 12px;
- padding: 13px 14px;
+ padding: 14px 16px;
border: 1px solid #d6e0ec;
- border-radius: 14px;
- background: linear-gradient(180deg, #f9fbff, #ffffff);
- cursor: move;
+ border-radius: 16px;
+ background: linear-gradient(180deg, #fbfdff, #ffffff);
transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
width: 100%;
}
.section-rule-card:hover {
transform: translateY(-1px);
- box-shadow: 0 10px 20px rgba(15, 23, 42, 0.06);
- border-color: #b8cae0;
+ box-shadow: 0 14px 24px rgba(15, 23, 42, 0.07);
+ border-color: #b2c6df;
}
.section-rule-card.is-locked {
- background: linear-gradient(180deg, #f4f7fb, #fafcff);
+ background: linear-gradient(180deg, #f5f8fc, #fbfdff);
}
-.section-rule-card.dragging {
- opacity: 0.58;
-}
-
-.section-rule-card.manual-dragging {
- opacity: 0.72;
- border-color: #8fb1d8;
- box-shadow: 0 16px 30px rgba(15, 23, 42, 0.10);
-}
-
-.section-rule-drag {
- color: #8aa0be;
- font-size: 18px;
- letter-spacing: -2px;
- user-select: none;
- align-self: stretch;
+.section-rule-actions {
display: inline-flex;
align-items: center;
+ gap: 6px;
padding-right: 2px;
- cursor: grab;
}
-body.builder-dragging,
-body.builder-dragging * {
- cursor: grabbing !important;
- user-select: none !important;
+.section-move-btn {
+ width: 34px;
+ height: 34px;
+ border: 1px solid #cdd9e8;
+ border-radius: 11px;
+ background: linear-gradient(180deg, #ffffff, #f5f9ff);
+ color: #274264;
+ font-size: 15px;
+ font-weight: 700;
+ line-height: 1;
+ cursor: pointer;
+ box-shadow: 0 6px 12px rgba(15, 23, 42, 0.04);
+ transition: transform 0.16s ease, border-color 0.16s ease, background-color 0.16s ease, box-shadow 0.16s ease;
+}
+
+.section-move-btn:hover {
+ transform: translateY(-1px);
+ border-color: #9db4d2;
+ background: linear-gradient(180deg, #ffffff, #eef5ff);
+ box-shadow: 0 10px 16px rgba(15, 23, 42, 0.07);
+}
+
+.section-move-btn:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+ transform: none;
}
.section-rule-copy {
@@ -1096,7 +1106,8 @@ body.builder-dragging * {
.section-rule-copy strong {
color: #0f172a;
- font-size: 14px;
+ font-size: 15px;
+ font-weight: 700;
overflow-wrap: anywhere;
}
diff --git a/backend/workflows/static/workflows/css/onboarding_form.css b/backend/workflows/static/workflows/css/onboarding_form.css
index a787442..9030da4 100644
--- a/backend/workflows/static/workflows/css/onboarding_form.css
+++ b/backend/workflows/static/workflows/css/onboarding_form.css
@@ -192,6 +192,10 @@ h1 {
}
.section-head {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
margin-bottom: 12px;
border-bottom: 1px dashed #dde4f1;
padding-bottom: 8px;
@@ -210,6 +214,9 @@ h1 {
}
.section-itsetup .section-head {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
margin: -14px -14px 16px;
padding: 12px 14px;
border-bottom: 1px solid #d5e2f9;
@@ -348,6 +355,11 @@ h1 {
transform: translateY(1px);
}
+.section-toggle-btn {
+ flex-shrink: 0;
+ margin-left: auto;
+}
+
.itsetup-checklist-body {
padding: 0;
background: #ffffff;
diff --git a/backend/workflows/static/workflows/js/form_builder.js b/backend/workflows/static/workflows/js/form_builder.js
index 1fceee8..393cf0e 100644
--- a/backend/workflows/static/workflows/js/form_builder.js
+++ b/backend/workflows/static/workflows/js/form_builder.js
@@ -148,92 +148,36 @@
const sectionRuleGrid = document.getElementById('section-rule-grid');
if (sectionRuleGrid) {
- let draggingSectionCard = null;
- let manualDraggingSectionCard = null;
-
- function getSectionInsertBeforeNode(mouseY) {
- const cards = Array.from(sectionRuleGrid.querySelectorAll('.section-rule-card:not(.dragging)'));
- return cards.find((card) => {
- const box = card.getBoundingClientRect();
- return mouseY < box.top + box.height / 2;
+ function updateSectionMoveButtons() {
+ const cards = Array.from(sectionRuleGrid.querySelectorAll('.section-rule-card'));
+ cards.forEach((card, index) => {
+ const upBtn = card.querySelector('[data-move-section="up"]');
+ const downBtn = card.querySelector('[data-move-section="down"]');
+ if (upBtn) upBtn.disabled = index === 0;
+ if (downBtn) downBtn.disabled = index === cards.length - 1;
});
}
- function getManualSectionInsertBeforeNode(mouseY) {
- const cards = Array.from(sectionRuleGrid.querySelectorAll('.section-rule-card:not(.manual-dragging)'));
- return cards.find((card) => {
- const box = card.getBoundingClientRect();
- return mouseY < box.top + box.height / 2;
- });
- }
-
- function onManualSectionMove(event) {
- if (!manualDraggingSectionCard) return;
- event.preventDefault();
- sectionRuleGrid.classList.add('drag-over');
- const beforeNode = getManualSectionInsertBeforeNode(event.clientY);
- if (beforeNode) {
- sectionRuleGrid.insertBefore(manualDraggingSectionCard, beforeNode);
- } else {
- sectionRuleGrid.appendChild(manualDraggingSectionCard);
- }
- }
-
- function endManualSectionDrag() {
- if (!manualDraggingSectionCard) return;
- manualDraggingSectionCard.classList.remove('manual-dragging');
- manualDraggingSectionCard = null;
- sectionRuleGrid.classList.remove('drag-over');
- document.body.classList.remove('builder-dragging');
- document.removeEventListener('mousemove', onManualSectionMove);
- document.removeEventListener('mouseup', endManualSectionDrag);
- }
-
- sectionRuleGrid.querySelectorAll('.section-rule-card').forEach((card) => {
- card.addEventListener('dragstart', (event) => {
- draggingSectionCard = card;
- card.classList.add('dragging');
- event.dataTransfer.effectAllowed = 'move';
- event.dataTransfer.setData('text/plain', card.dataset.sectionKey || '');
- });
- card.addEventListener('dragend', () => {
- card.classList.remove('dragging');
- sectionRuleGrid.classList.remove('drag-over');
- draggingSectionCard = null;
- });
- const handle = card.querySelector('.section-rule-drag');
- if (handle) {
- handle.addEventListener('mousedown', (event) => {
- event.preventDefault();
- manualDraggingSectionCard = card;
- card.classList.add('manual-dragging');
- sectionRuleGrid.classList.add('drag-over');
- document.body.classList.add('builder-dragging');
- document.addEventListener('mousemove', onManualSectionMove);
- document.addEventListener('mouseup', endManualSectionDrag);
- });
+ sectionRuleGrid.addEventListener('click', (event) => {
+ const button = event.target.closest('[data-move-section]');
+ if (!button) return;
+ const card = button.closest('.section-rule-card');
+ if (!card) return;
+ const direction = button.dataset.moveSection;
+ if (direction === 'up') {
+ const previousCard = card.previousElementSibling;
+ if (previousCard) {
+ sectionRuleGrid.insertBefore(card, previousCard);
+ }
+ } else if (direction === 'down') {
+ const nextCard = card.nextElementSibling;
+ if (nextCard) {
+ sectionRuleGrid.insertBefore(nextCard, card);
+ }
}
+ updateSectionMoveButtons();
});
- sectionRuleGrid.addEventListener('dragover', (event) => {
- event.preventDefault();
- sectionRuleGrid.classList.add('drag-over');
- if (!draggingSectionCard) return;
- const beforeNode = getSectionInsertBeforeNode(event.clientY);
- if (beforeNode) {
- sectionRuleGrid.insertBefore(draggingSectionCard, beforeNode);
- } else {
- sectionRuleGrid.appendChild(draggingSectionCard);
- }
- });
-
- sectionRuleGrid.addEventListener('dragleave', () => {
- sectionRuleGrid.classList.remove('drag-over');
- });
-
- sectionRuleGrid.addEventListener('drop', (event) => {
- event.preventDefault();
- sectionRuleGrid.classList.remove('drag-over');
- });
+ updateSectionMoveButtons();
}
})();
diff --git a/backend/workflows/static/workflows/js/onboarding_form.js b/backend/workflows/static/workflows/js/onboarding_form.js
index 534355a..e6de8a3 100644
--- a/backend/workflows/static/workflows/js/onboarding_form.js
+++ b/backend/workflows/static/workflows/js/onboarding_form.js
@@ -222,6 +222,45 @@
});
}
+ function setupSectionCheckboxToggles() {
+ document.querySelectorAll('[data-section-checkbox-toggle]').forEach(function (button) {
+ const sectionCard = button.closest('.section-card');
+ if (!sectionCard) return;
+
+ const getCheckboxes = function () {
+ return Array.from(sectionCard.querySelectorAll('.custom-section-checkbox input[type="checkbox"]'));
+ };
+
+ const refreshButtonLabel = function () {
+ const checkboxes = getCheckboxes();
+ if (!checkboxes.length) {
+ button.classList.add('hidden');
+ return;
+ }
+ button.classList.remove('hidden');
+ const allChecked = checkboxes.every(function (box) { return box.checked; });
+ button.textContent = allChecked ? (button.dataset.labelClear || 'Auswahl aufheben') : (button.dataset.labelSelect || 'Alle auswählen');
+ };
+
+ button.addEventListener('click', function () {
+ const checkboxes = getCheckboxes();
+ if (!checkboxes.length) return;
+ const shouldCheck = checkboxes.some(function (box) { return !box.checked; });
+ checkboxes.forEach(function (box) {
+ box.checked = shouldCheck;
+ box.dispatchEvent(new Event('change', { bubbles: true }));
+ });
+ refreshButtonLabel();
+ });
+
+ getCheckboxes().forEach(function (box) {
+ box.addEventListener('change', refreshButtonLabel);
+ });
+
+ refreshButtonLabel();
+ });
+ }
+
function setupChecklistColumns() {
document.querySelectorAll('.itsetup-checklist-body > [id^="id_"]').forEach(function (container) {
const itemCount = container.querySelectorAll(':scope > div').length;
@@ -271,6 +310,7 @@
setupWorkEmailAutofill();
setupBusinessCardAutofill();
setupChecklistToggles();
+ setupSectionCheckboxToggles();
setupChecklistColumns();
jumpToFirstErrorPage();
updateStep();
diff --git a/backend/workflows/templates/workflows/form_builder.html b/backend/workflows/templates/workflows/form_builder.html
index 80fc5ec..1380495 100644
--- a/backend/workflows/templates/workflows/form_builder.html
+++ b/backend/workflows/templates/workflows/form_builder.html
@@ -98,11 +98,17 @@
{% for section in section_rule_items %}
{{ section.subtitle }}
+{{ section.subtitle }}
+