From b2686522c7ed0c4368854f74acace0d5c1bd7192 Mon Sep 17 00:00:00 2001 From: Md Bayazid Bostame Date: Sat, 28 Mar 2026 00:08:27 +0100 Subject: [PATCH] snapshot: preserve state before onboarding field parity and refactor slice --- .../media/templates/onboarding_template.html | 274 +++++++++++++++--- .../static/workflows/css/admin_tools.css | 40 +++ backend/workflows/tasks.py | 153 ++++++---- .../templates/workflows/intro_builder.html | 248 ++++++++++------ backend/workflows/views.py | 20 ++ 5 files changed, 556 insertions(+), 179 deletions(-) diff --git a/backend/media/templates/onboarding_template.html b/backend/media/templates/onboarding_template.html index 8b9055b..36abe06 100644 --- a/backend/media/templates/onboarding_template.html +++ b/backend/media/templates/onboarding_template.html @@ -114,7 +114,6 @@ .muted-cell { color: #64748b; } - @@ -122,41 +121,250 @@

{{ T.onboarding_title }}

- {% for section in PDF_SECTIONS %} - {% if section.has_content %} -
{{ section.title }}
+
{% if PDF_LANG == 'en' %}Master data{% else %}Stammdaten{% endif %}
+
+
{{ T.onboarding_staff_data }}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ T.name }}{{ DISPLAY_NAME }}
{{ T.department }}{{ ABTEILUNG }}{{ T.job_title }}{{ BERUFSBEZEICHNUNG }}
{{ T.work_email }}{{ EMAIL }}{{ T.employment_type }}{{ BESCHAEFTIGUNG }}
{{ T.contract_start }}{{ VERTRAGSBEGINN }}{{ T.contract_end }}{{ VERTRAGSENDE }}
{{ T.handover_date }}{{ UEBERGABEDATUM }}
+
- {% if section.scalar_rows %} - - {% for row in section.scalar_rows %} - - - {{ row[0].display_value }} - {% if row[1] %} - - - {% endif %} - - {% endfor %} -
{{ row[0].label }}{{ row[1].label }}{{ row[1].display_value }}
- {% endif %} + {% if HAS_DEVICES or HAS_GROUPS or HAS_SOFTWARE or HAS_ACCESSES or HAS_RESOURCES or HAS_GROUP_MAILBOXES or HAS_ADDITIONAL_HARDWARE or HAS_ADDITIONAL_SOFTWARE or HAS_ADDITIONAL_ACCESS %} +
{% if PDF_LANG == 'en' %}IT setup{% else %}IT-Setup{% endif %}
- {% for field in section.list_fields %} -
-
{{ field.label }}
- - {% for row in field.display_value|batch(3, '') %} - - {% for cell in row %} - - {% endfor %} - - {% endfor %} -
{% if cell %}• {{ cell }}{% endif %}
-
+ {% if HAS_DEVICES %} +
+
{{ T.devices }}
+ + {% for row in ARBEITSGERÄTE_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + {% endfor %} - {% endif %} - {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if HAS_GROUPS %} +
+
{{ T.workspace_groups }}
+ + {% for row in ZUGAENGE_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if HAS_SOFTWARE %} +
+
{{ T.software }}
+ + {% for row in SOFTWARE_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if HAS_ACCESSES %} +
+
{{ T.accesses }}
+ + {% for row in ACCOUNT_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if HAS_RESOURCES %} +
+
{{ T.resources }}
+ + {% for row in STANDARD_RESSOURCEN %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if GROUP_MAILBOXES_REQUIRED and HAS_GROUP_MAILBOXES %} +
+
{{ T.group_mailboxes_required }}
+ + {% for row in GROUP_MAILBOXES_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if ADDITIONAL_HARDWARE_NEEDED and HAS_ADDITIONAL_HARDWARE %} +
+
{{ T.additional_hardware_needed }}
+ + {% for row in ADDITIONAL_HARDWARE_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if ADDITIONAL_SOFTWARE_NEEDED and HAS_ADDITIONAL_SOFTWARE %} +
+
{{ T.additional_software_needed }}
+ + {% for row in ADDITIONAL_SOFTWARE_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + + {% if ADDITIONAL_ACCESS_NEEDED and HAS_ADDITIONAL_ACCESS %} +
+
{{ T.additional_access_needed }}
+ + {% for row in ADDITIONAL_ACCESS_LIST %} + + {% for cell in row %}{% endfor %} + {% if row|length < 3 %} + {% for _ in range(3 - row|length) %}{% endfor %} + {% endif %} + + {% endfor %} +
• {{ cell }}
+
+ {% endif %} + {% endif %} + + {% if (VISITENKARTE_BESTELLT and HAS_VISITENKARTE_DATEN) or HAS_ADDITIONAL_HARDWARE_OTHER or HAS_SUCCESSOR_INFO or HAS_ADDITIONAL_NOTES %} +
{{ T.additional_details }}
+ {% endif %} + + {% if VISITENKARTE_BESTELLT and HAS_VISITENKARTE_DATEN %} +
+
{{ T.business_cards }}
+ + + + + + + + + + + + + +
{{ T.name }}{{ VISITENKARTE_NAME }}{{ T.job_title }}{{ VISITENKARTE_TITEL }}
{{ T.email }}{{ VISITENKARTE_EMAIL }}{{ T.phone }}{{ VISITENKARTE_TELEFON }}
+
+ {% endif %} + + {% if HAS_ADDITIONAL_HARDWARE_OTHER %} +
+
{{ T.additional_hardware_other }}
+ + + + +
{{ ADDITIONAL_HARDWARE_OTHER }}
+
+ {% endif %} + + {% if HAS_SUCCESSOR_INFO %} +
+
{{ T.successor_phone }}
+ + + + + + + + + + + +
{{ T.successor_of }}{{ SUCCESSOR_NAME }}{{ T.inherit_phone_number }}{{ INHERIT_PHONE_NUMBER }}
{{ T.direct_extension }}{{ PHONE_NUMBER }}
+
+ {% endif %} + + {% if HAS_ADDITIONAL_NOTES %} +
+
{{ T.notes }}
+ + + + +
{{ ADDITIONAL_NOTES }}
+
+ {% endif %}
{{ T.confirmation }}
diff --git a/backend/workflows/static/workflows/css/admin_tools.css b/backend/workflows/static/workflows/css/admin_tools.css index 639005a..ec0389a 100644 --- a/backend/workflows/static/workflows/css/admin_tools.css +++ b/backend/workflows/static/workflows/css/admin_tools.css @@ -218,3 +218,43 @@ th { background: #f6f9ff; color: #334155; } .app-registry-group-head h2 { margin: 0; color: #17345e; font-size: 18px; } .app-registry-group-body { display: grid; gap: 14px; } .app-registry-group[hidden] { display: none !important; } + +.intro-builder-shell { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 18px; align-items: start; } +.intro-builder-sidebar { position: sticky; top: 18px; display: grid; gap: 14px; } +.intro-side-card { padding: 16px; } +.intro-eyebrow { margin-bottom: 10px; } +.intro-side-stats { display: grid; gap: 12px; } +.intro-side-stat { display: grid; gap: 2px; } +.intro-side-stat strong { font-size: 22px; line-height: 1; color: #163566; } +.intro-side-stat span { color: #60738d; font-size: 12px; font-weight: 700; } +.intro-section-nav { display: grid; gap: 8px; } +.intro-section-link { justify-content: flex-start; } +.intro-builder-main { min-width: 0; display: grid; gap: 14px; } +.intro-builder-form-card, .intro-builder-list-card { padding: 16px; } +.intro-surface-head { margin-bottom: 14px; } +.intro-surface-head h2 { margin: 0; color: #17345e; font-size: 20px; } +.intro-surface-head p { margin: 4px 0 0; color: #60738d; font-size: 13px; } +.intro-builder-actions { display: flex; gap: 10px; align-items: center; justify-content: space-between; flex-wrap: wrap; margin-top: 12px; } +.intro-builder-actions-sticky { position: sticky; bottom: 14px; z-index: 4; padding-top: 12px; border-top: 1px solid #dbe5f2; background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(255,255,255,0.98)); } +.intro-group-stack { display: grid; gap: 16px; } +.intro-group-card { border: 1px solid #d7e3f0; border-radius: 18px; background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(244,248,255,0.95)); padding: 14px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.94); } +.intro-group-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 12px; } +.intro-group-head h3 { margin: 0; color: #17345e; font-size: 18px; } +.intro-item-list { display: grid; gap: 12px; } +.intro-item-card { border: 1px solid #dce6f2; border-radius: 16px; background: rgba(255,255,255,0.88); padding: 14px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.92); } +.intro-item-head { display: flex; justify-content: space-between; gap: 12px; align-items: flex-start; margin-bottom: 12px; } +.intro-item-head strong { color: #17345e; font-size: 15px; line-height: 1.35; } +.intro-item-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } +.intro-inline-check { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; font-weight: 700; color: #334155; } +.intro-inline-check input[type="checkbox"] { width: auto; } +.intro-item-grid { display: grid; grid-template-columns: repeat(2, minmax(220px, 1fr)); gap: 12px; } +.intro-item-field { margin: 0; } +.intro-item-field > span { display: block; margin-bottom: 6px; font-size: 12px; color: #334155; font-weight: 700; } +.intro-item-field-wide { grid-column: 1 / -1; } +@media (max-width: 760px) { + .intro-builder-shell { grid-template-columns: 1fr; } + .intro-builder-sidebar { position: static; } + .intro-item-head { flex-direction: column; } + .intro-item-grid { grid-template-columns: 1fr; } + .intro-builder-actions { align-items: stretch; flex-direction: column; } +} diff --git a/backend/workflows/tasks.py b/backend/workflows/tasks.py index 4ece6b1..36cde91 100644 --- a/backend/workflows/tasks.py +++ b/backend/workflows/tasks.py @@ -963,73 +963,112 @@ def _generate_onboarding_pdf(request_obj: OnboardingRequest) -> Path: phone_number = (request_obj.phone_number or '').strip() display_name = f"{gender} {first_name} {last_name}".strip() if gender and gender != '-' else f"{first_name} {last_name}".strip() + pdf_sections = build_pdf_sections('onboarding', request_obj, lang) + pdf_section_map = {section['key']: section for section in pdf_sections if section.get('has_content')} + pdf_field_map = {} + for section in pdf_sections: + for field in section.get('render_fields', []): + pdf_field_map[field['name']] = field + + def _field_text(name: str, fallback): + field = pdf_field_map.get(name) + if not field: + return fallback + value = field.get('display_value') + if isinstance(value, list): + return ' | '.join(value) if value else fallback + return value or fallback + + def _field_list(name: str) -> list[str]: + field = pdf_field_map.get(name) + if not field: + return [] + value = field.get('display_value') + if isinstance(value, list): + return value + text = str(value or '').strip() + return [text] if text else [] + + devices_visible = _field_list('needed_devices_multi') + groups_visible = _field_list('needed_workspace_groups_multi') + software_visible = _field_list('needed_software_multi') + accesses_visible = _field_list('needed_accesses_multi') + resources_visible = _field_list('needed_resources_multi') + group_mailboxes_visible = _field_list('group_mailboxes') + additional_hardware_visible = _field_list('additional_hardware_multi') + additional_software_visible = _field_list('additional_software_multi') + additional_access_visible = _field_list('additional_access_text') + job_title_visible = 'job_title' in pdf_field_map + itsetup_visible = 'itsetup' in pdf_section_map + context = { 'T': t, 'PDF_LANG': lang, - 'PDF_SECTIONS': build_pdf_sections('onboarding', request_obj, lang), + 'PDF_SECTIONS': pdf_sections, 'VORNAME': first_name, 'NACHNAME': last_name, 'DISPLAY_NAME': display_name or request_obj.full_name, - 'ANREDE': gender, - 'BERUFSBEZEICHNUNG': request_obj.job_title or t['not_available'], - 'ABTEILUNG': request_obj.department or t['not_available'], - 'EMAIL': request_obj.work_email or t['not_available'], - 'VERTRAGSBEGINN': request_obj.contract_start, - 'BESCHAEFTIGUNG': employment_type, - 'VERTRAGSENDE': employment_end, - 'UEBERGABEDATUM': request_obj.handover_date or t['not_available_short'], - 'ARBEITSGERAETE_TEXT': ' | '.join(devices) if devices else t['not_available'], - 'WORKSPACE_GROUPS_TEXT': ' | '.join(groups) if groups else t['not_available'], - 'SOFTWARE_TEXT': ' | '.join(software) if software else t['not_available'], - 'ZUGAENGE_TEXT': ' | '.join(accesses) if accesses else t['not_available'], - 'RESSOURCEN_TEXT': ' | '.join(resources) if resources else t['not_available'], + 'ANREDE': _field_text('gender', gender), + 'JOB_TITLE_VISIBLE': job_title_visible, + 'BERUFSBEZEICHNUNG': _field_text('job_title', t['not_available']), + 'ABTEILUNG': _field_text('department', t['not_available']), + 'EMAIL': _field_text('work_email', t['not_available']), + 'VERTRAGSBEGINN': _field_text('contract_start', request_obj.contract_start), + 'BESCHAEFTIGUNG': _field_text('employment_type', employment_type), + 'VERTRAGSENDE': _field_text('employment_end_date', employment_end), + 'UEBERGABEDATUM': _field_text('handover_date', request_obj.handover_date or t['not_available_short']), + 'ARBEITSGERAETE_TEXT': ' | '.join(devices_visible) if devices_visible else t['not_available'], + 'WORKSPACE_GROUPS_TEXT': ' | '.join(groups_visible) if groups_visible else t['not_available'], + 'SOFTWARE_TEXT': ' | '.join(software_visible) if software_visible else t['not_available'], + 'ZUGAENGE_TEXT': ' | '.join(accesses_visible) if accesses_visible else t['not_available'], + 'RESSOURCEN_TEXT': ' | '.join(resources_visible) if resources_visible else t['not_available'], 'VISITENKARTE_BESTELLT': order_business_cards, - 'HAS_VISITENKARTE_DATEN': order_business_cards and any( + 'HAS_VISITENKARTE_DATEN': order_business_cards and ('business_card_name' in pdf_field_map or 'business_card_title' in pdf_field_map or 'business_card_email' in pdf_field_map or 'business_card_phone' in pdf_field_map) and any( [ - (request_obj.business_card_name or '').strip(), - (request_obj.business_card_title or '').strip(), - (request_obj.business_card_email or '').strip(), - (request_obj.business_card_phone or '').strip(), + _field_text('business_card_name', '').strip(), + _field_text('business_card_title', '').strip(), + _field_text('business_card_email', '').strip(), + _field_text('business_card_phone', '').strip(), ] ), - 'VISITENKARTE_NAME': request_obj.business_card_name or t['not_available_short'], - 'VISITENKARTE_TITEL': request_obj.business_card_title or t['not_available_short'], - 'VISITENKARTE_EMAIL': request_obj.business_card_email or t['not_available_short'], - 'VISITENKARTE_TELEFON': request_obj.business_card_phone or t['not_available_short'], - 'GROUP_MAILBOXES': group_mailboxes or t['not_available'], - 'ADDITIONAL_HARDWARE_OTHER': additional_hardware_other or t['not_available'], - 'ADDITIONAL_HARDWARE': additional_hardware or t['not_available'], - 'ADDITIONAL_SOFTWARE': additional_software or t['not_available'], - 'ADDITIONAL_ACCESS_TEXT': additional_access_text or t['not_available'], - 'SUCCESSOR_NAME': successor_name or t['not_available'], - 'PHONE_NUMBER': phone_number or t['not_available_short'], - 'INHERIT_PHONE_NUMBER': t['yes'] if request_obj.inherit_phone_number else t['no'], - 'ADDITIONAL_NOTES': additional_notes or t['not_available'], - 'GROUP_MAILBOXES_REQUIRED': bool(request_obj.group_mailboxes_required), - 'ADDITIONAL_HARDWARE_NEEDED': bool(request_obj.additional_hardware_needed), - 'ADDITIONAL_SOFTWARE_NEEDED': bool(request_obj.additional_software_needed), - 'ADDITIONAL_ACCESS_NEEDED': bool(request_obj.additional_access_needed), - 'HAS_DEVICES': bool(devices), - 'HAS_GROUPS': bool(groups), - 'HAS_SOFTWARE': bool(software), - 'HAS_ACCESSES': bool(accesses), - 'HAS_RESOURCES': bool(resources), - 'HAS_GROUP_MAILBOXES': bool(group_mailboxes_list), - 'HAS_ADDITIONAL_HARDWARE': bool(additional_hardware_list), - 'HAS_ADDITIONAL_SOFTWARE': bool(additional_software_list), - 'HAS_ADDITIONAL_ACCESS': bool(additional_access_list), - 'HAS_ADDITIONAL_HARDWARE_OTHER': bool(additional_hardware_other), - 'HAS_SUCCESSOR_INFO': bool(successor_name) or bool(request_obj.inherit_phone_number) or bool(phone_number), - 'HAS_ADDITIONAL_NOTES': bool(additional_notes), - 'GROUP_MAILBOXES_LIST': _chunk_list(group_mailboxes_list), - 'ADDITIONAL_HARDWARE_LIST': _chunk_list(additional_hardware_list), - 'ADDITIONAL_SOFTWARE_LIST': _chunk_list(additional_software_list), - 'ADDITIONAL_ACCESS_LIST': _chunk_list(additional_access_list), - 'ZUGAENGE_LIST': _chunk_list(groups), - 'ARBEITSGERÄTE_LIST': _chunk_list(devices), - 'SOFTWARE_LIST': _chunk_list(software), - 'ACCOUNT_LIST': _chunk_list(accesses), - 'STANDARD_RESSOURCEN': _chunk_list(resources), + 'VISITENKARTE_NAME': _field_text('business_card_name', t['not_available_short']), + 'VISITENKARTE_TITEL': _field_text('business_card_title', t['not_available_short']), + 'VISITENKARTE_EMAIL': _field_text('business_card_email', t['not_available_short']), + 'VISITENKARTE_TELEFON': _field_text('business_card_phone', t['not_available_short']), + 'GROUP_MAILBOXES': _field_text('group_mailboxes', group_mailboxes or t['not_available']), + 'ADDITIONAL_HARDWARE_OTHER': _field_text('additional_hardware_other', additional_hardware_other or t['not_available']), + 'ADDITIONAL_HARDWARE': _field_text('additional_hardware_other', additional_hardware or t['not_available']), + 'ADDITIONAL_SOFTWARE': _field_text('additional_software', additional_software or t['not_available']), + 'ADDITIONAL_ACCESS_TEXT': _field_text('additional_access_text', additional_access_text or t['not_available']), + 'SUCCESSOR_NAME': _field_text('successor_name', successor_name or t['not_available']), + 'PHONE_NUMBER': _field_text('phone_number_choice', phone_number or t['not_available_short']), + 'INHERIT_PHONE_NUMBER': _field_text('inherit_phone_number_choice', t['yes'] if request_obj.inherit_phone_number else t['no']), + 'ADDITIONAL_NOTES': _field_text('additional_notes', additional_notes or t['not_available']), + 'GROUP_MAILBOXES_REQUIRED': 'group_mailboxes_required_choice' in pdf_field_map and bool(group_mailboxes_visible), + 'ADDITIONAL_HARDWARE_NEEDED': 'additional_hardware_needed_choice' in pdf_field_map and bool(additional_hardware_visible), + 'ADDITIONAL_SOFTWARE_NEEDED': 'additional_software_needed_choice' in pdf_field_map and bool(additional_software_visible), + 'ADDITIONAL_ACCESS_NEEDED': 'additional_access_needed_choice' in pdf_field_map and bool(additional_access_visible), + 'HAS_DEVICES': itsetup_visible and bool(devices_visible), + 'HAS_GROUPS': itsetup_visible and bool(groups_visible), + 'HAS_SOFTWARE': itsetup_visible and bool(software_visible), + 'HAS_ACCESSES': itsetup_visible and bool(accesses_visible), + 'HAS_RESOURCES': itsetup_visible and bool(resources_visible), + 'HAS_GROUP_MAILBOXES': bool(group_mailboxes_visible), + 'HAS_ADDITIONAL_HARDWARE': bool(additional_hardware_visible), + 'HAS_ADDITIONAL_SOFTWARE': bool(additional_software_visible), + 'HAS_ADDITIONAL_ACCESS': bool(additional_access_visible), + 'HAS_ADDITIONAL_HARDWARE_OTHER': bool(_field_text('additional_hardware_other', '').strip()), + 'HAS_SUCCESSOR_INFO': bool(_field_text('successor_name', '').strip()) or 'inherit_phone_number_choice' in pdf_field_map or bool(_field_text('phone_number_choice', '').strip()), + 'HAS_ADDITIONAL_NOTES': bool(_field_text('additional_notes', '').strip()), + 'GROUP_MAILBOXES_LIST': _chunk_list(group_mailboxes_visible), + 'ADDITIONAL_HARDWARE_LIST': _chunk_list(additional_hardware_visible), + 'ADDITIONAL_SOFTWARE_LIST': _chunk_list(additional_software_visible), + 'ADDITIONAL_ACCESS_LIST': _chunk_list(additional_access_visible), + 'ZUGAENGE_LIST': _chunk_list(groups_visible), + 'ARBEITSGERÄTE_LIST': _chunk_list(devices_visible), + 'SOFTWARE_LIST': _chunk_list(software_visible), + 'ACCOUNT_LIST': _chunk_list(accesses_visible), + 'STANDARD_RESSOURCEN': _chunk_list(resources_visible), 'UNTERSCHRIFT': signature_src, 'UNTERSCHRIFT_HINWEIS': signature_note, 'REQUESTED_BY_NAME': requester_name, diff --git a/backend/workflows/templates/workflows/intro_builder.html b/backend/workflows/templates/workflows/intro_builder.html index 2aaa4e6..acca81b 100644 --- a/backend/workflows/templates/workflows/intro_builder.html +++ b/backend/workflows/templates/workflows/intro_builder.html @@ -25,97 +25,167 @@ {% include 'workflows/includes/messages.html' %} - - {% csrf_token %} - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
{% trans "Bedingungen und Sortierung können anschließend in der Tabelle bearbeitet werden." %}
- +
+
- - - - - - - - - - - - - - - {% for item in items %} - - - - - - - - - - - - {% empty %} - + + + +
+ + {% csrf_token %} + +
+
+

{% trans "Punkt hinzufügen" %}

+

{% trans "Neue Checklistenpunkte direkt in den passenden Abschnitt einfügen." %}

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + {% trans "Bedingungen und Sortierung können anschließend im Editor gepflegt werden." %} +
+ + +
+ {% csrf_token %} + +
+
+

{% trans "Checkliste bearbeiten" %}

+

{% trans "Punkte pro Abschnitt pflegen, Bedingungen setzen und aktive Einträge steuern." %}

+
+
+ +
+ {% for group in grouped_items %} +
+
+
+

{{ group.label }}

+

{% blocktrans trimmed with count=group.count active=group.active_count %}{{ count }} Punkte, {{ active }} aktiv{% endblocktrans %}

+
+
+ + {% if group.items %} +
+ {% for item in group.items %} +
+ +
+ {{ item.label }} +
+ + +
+
+ +
+ + + + + + + +
+
+ {% endfor %} +
+ {% else %} +
{% trans "Noch keine Punkte in diesem Abschnitt." %}
+ {% endif %} +
{% endfor %} -
-
{% trans "Sortierung" %}{% trans "Abschnitt" %}{% trans "Checklistenpunkt (DE)" %}{% trans "Checklistenpunkt (EN)" %}{% trans "Feld-Bedingung" %}{% trans "Operator" %}{% trans "Wert" %}{% trans "Aktiv" %}{% trans "Löschen" %}
- - {{ forloop.counter }} - - - - - - - - -
{% trans "Noch keine benutzerdefinierten Checklistenpunkte angelegt. Solange die Liste leer ist, nutzt das System die integrierten Standardpunkte." %}
+ + +
+ + {% trans "Die Reihenfolge folgt aktuell der gespeicherten Listenreihenfolge innerhalb der Abschnitte." %} +
+ -
{% trans "Reihenfolge folgt derzeit der Tabellenreihenfolge beim Speichern." %}
-
- -
- + {% endblock %} diff --git a/backend/workflows/views.py b/backend/workflows/views.py index 8d72a15..a668113 100644 --- a/backend/workflows/views.py +++ b/backend/workflows/views.py @@ -3167,11 +3167,31 @@ def intro_builder_page(request): ] items = list(IntroChecklistItem.objects.all().order_by('section', 'sort_order', 'label')) + section_label_map = dict(_translate_choice_list(IntroChecklistItem.SECTION_CHOICES)) + grouped_items = [] + for value, _label in IntroChecklistItem.SECTION_CHOICES: + section_items = [item for item in items if item.section == value] + grouped_items.append( + { + 'key': value, + 'label': section_label_map.get(value, value), + 'items': section_items, + 'count': len(section_items), + 'active_count': len([item for item in section_items if item.is_active]), + } + ) return render( request, 'workflows/intro_builder.html', { 'items': items, + 'grouped_items': grouped_items, + 'intro_summary': { + 'total_items': len(items), + 'active_items': len([item for item in items if item.is_active]), + 'conditional_items': len([item for item in items if item.condition_operator != 'always']), + 'section_count': len([group for group in grouped_items if group['count']]), + }, 'section_choices': _translate_choice_list(IntroChecklistItem.SECTION_CHOICES), 'operator_choices': _translate_choice_list(IntroChecklistItem.OPERATOR_CHOICES), 'condition_field_choices': condition_field_choices,