snapshot: refine builder field rules and conditional summaries
This commit is contained in:
@@ -133,6 +133,46 @@ CONDITIONAL_RULE_OPERATOR_CHOICES = [
|
||||
]
|
||||
|
||||
|
||||
def _field_rule_summary(*, is_visible: bool, is_required, locked: bool) -> str:
|
||||
if locked:
|
||||
return str(_('Fixes Kernfeld, immer sichtbar.'))
|
||||
if not is_visible:
|
||||
return str(_('Ausgeblendet, erscheint nicht im Formular.'))
|
||||
if is_required is True:
|
||||
return str(_('Sichtbar und als Pflichtfeld markiert.'))
|
||||
if is_required is False:
|
||||
return str(_('Sichtbar und optional.'))
|
||||
return str(_('Sichtbar mit Standardverhalten.'))
|
||||
|
||||
|
||||
def _conditional_clause_sentence(clause: dict, field_label_map: dict[str, str]) -> str:
|
||||
field_name = (clause.get('field') or '').strip()
|
||||
operator = (clause.get('operator') or '').strip()
|
||||
value = clause.get('value')
|
||||
if not field_name or not operator:
|
||||
return ''
|
||||
field_label = field_label_map.get(field_name, field_name)
|
||||
if operator == 'checked':
|
||||
return _('%(field)s ist aktiviert') % {'field': field_label}
|
||||
if operator == 'equals':
|
||||
if value not in (None, ''):
|
||||
return _('%(field)s ist gleich %(value)s') % {'field': field_label, 'value': value}
|
||||
return _('%(field)s ist gleich') % {'field': field_label}
|
||||
if operator == 'not_equals':
|
||||
if value not in (None, ''):
|
||||
return _('%(field)s ist nicht gleich %(value)s') % {'field': field_label, 'value': value}
|
||||
return _('%(field)s ist nicht gleich') % {'field': field_label}
|
||||
return _('%(field)s erfüllt die Bedingung') % {'field': field_label}
|
||||
|
||||
|
||||
def _conditional_rule_summary(clauses: list[dict], field_label_map: dict[str, str]) -> str:
|
||||
active_clauses = [clause for clause in clauses if clause.get('field') and clause.get('operator')]
|
||||
if not active_clauses:
|
||||
return str(_('Immer sichtbar.'))
|
||||
parts = [str(_conditional_clause_sentence(clause, field_label_map)) for clause in active_clauses]
|
||||
return str(_('Sichtbar, wenn %(conditions)s.') % {'conditions': ' und '.join(parts)})
|
||||
|
||||
|
||||
def _normalized_conditional_rule_payload(form_type: str) -> dict[str, dict]:
|
||||
configs = ensure_form_conditional_rule_configs(form_type)
|
||||
payload = {}
|
||||
@@ -2239,30 +2279,30 @@ def form_builder_page(request):
|
||||
if delete_option_id:
|
||||
option = FormOption.objects.filter(id=delete_option_id).first()
|
||||
if not option:
|
||||
messages.error(request, 'Option nicht gefunden.')
|
||||
messages.error(request, _('Option nicht gefunden.'))
|
||||
else:
|
||||
option_category = option.category
|
||||
deleted_label = option.label
|
||||
deleted_id = option.id
|
||||
option.delete()
|
||||
_audit(request, 'form_option_deleted', target_type='form_option', target_id=deleted_id, target_label=deleted_label)
|
||||
messages.success(request, 'Option wurde gelöscht.')
|
||||
messages.success(request, _('Option wurde gelöscht.'))
|
||||
return redirect(f"/admin-tools/form-builder/?form_type={form_type}&option_category={option_category}&module=options")
|
||||
if delete_custom_field_id:
|
||||
custom_field = FormCustomFieldConfig.objects.filter(id=delete_custom_field_id, form_type=form_type).first()
|
||||
if not custom_field:
|
||||
messages.error(request, 'Benutzerdefiniertes Feld nicht gefunden.')
|
||||
messages.error(request, _('Benutzerdefiniertes Feld nicht gefunden.'))
|
||||
else:
|
||||
deleted_label = custom_field.label
|
||||
deleted_id = custom_field.id
|
||||
custom_field.delete()
|
||||
_audit(request, 'form_custom_field_deleted', target_type='form_custom_field', target_id=deleted_id, target_label=deleted_label)
|
||||
messages.success(request, 'Benutzerdefiniertes Feld wurde gelöscht.')
|
||||
messages.success(request, _('Benutzerdefiniertes Feld wurde gelöscht.'))
|
||||
return redirect(f"/admin-tools/form-builder/?form_type={form_type}&option_category={option_category}&module=custom-fields")
|
||||
if delete_custom_section_id:
|
||||
custom_section = FormCustomSectionConfig.objects.filter(id=delete_custom_section_id, form_type=form_type).first()
|
||||
if not custom_section:
|
||||
messages.error(request, 'Benutzerdefinierter Abschnitt nicht gefunden.')
|
||||
messages.error(request, _('Benutzerdefinierter Abschnitt nicht gefunden.'))
|
||||
else:
|
||||
deleted_label = custom_section.title
|
||||
deleted_id = custom_section.id
|
||||
@@ -2285,7 +2325,7 @@ def form_builder_page(request):
|
||||
target_label=deleted_label,
|
||||
details={'section_key': section_key, 'deleted_field_count': deleted_field_count},
|
||||
)
|
||||
messages.success(request, 'Benutzerdefinierter Abschnitt wurde gelöscht.')
|
||||
messages.success(request, _('Benutzerdefinierter Abschnitt wurde gelöscht.'))
|
||||
return redirect(f"/admin-tools/form-builder/?form_type={form_type}&option_category={option_category}&module=custom-sections")
|
||||
|
||||
action = request.POST.get('builder_action', '')
|
||||
@@ -2295,9 +2335,9 @@ def form_builder_page(request):
|
||||
label_en = request.POST.get('label_en', '').strip()
|
||||
value = request.POST.get('value', '').strip()
|
||||
if category not in option_categories:
|
||||
messages.error(request, 'Ungültige Kategorie.')
|
||||
messages.error(request, _('Ungültige Kategorie.'))
|
||||
elif not label:
|
||||
messages.error(request, 'Bitte einen Namen für die Option angeben.')
|
||||
messages.error(request, _('Bitte einen Namen für die Option angeben.'))
|
||||
else:
|
||||
next_sort = (
|
||||
FormOption.objects.filter(category=category).order_by('-sort_order').values_list('sort_order', flat=True).first()
|
||||
@@ -2318,7 +2358,7 @@ def form_builder_page(request):
|
||||
target_label=label,
|
||||
details={'category': category, 'label_en': label_en, 'value': value or label},
|
||||
)
|
||||
messages.success(request, 'Option wurde hinzugefügt.')
|
||||
messages.success(request, _('Option wurde hinzugefügt.'))
|
||||
option_category = category
|
||||
|
||||
elif action == 'save_options':
|
||||
@@ -2336,11 +2376,11 @@ def form_builder_page(request):
|
||||
try:
|
||||
option.save(update_fields=['label', 'label_en', 'value', 'is_active', 'sort_order'])
|
||||
except IntegrityError:
|
||||
messages.error(request, f'Doppelte Bezeichnung in Kategorie: {next_label}')
|
||||
messages.error(request, _('Doppelte Bezeichnung in Kategorie: %(label)s') % {'label': next_label})
|
||||
return redirect(f"/admin-tools/form-builder/?form_type={form_type}&option_category={option.category}&module=options")
|
||||
option_category = option.category
|
||||
_audit(request, 'form_options_saved', target_type='form_option', target_label=option_category, details={'count': len(option_ids)})
|
||||
messages.success(request, 'Optionen wurden gespeichert.')
|
||||
messages.success(request, _('Optionen wurden gespeichert.'))
|
||||
|
||||
elif action == 'save_field_texts':
|
||||
field_ids = request.POST.getlist('field_ids')
|
||||
@@ -2354,14 +2394,14 @@ def form_builder_page(request):
|
||||
cfg.help_text_override_en = (request.POST.get(f'help_text_override_en_{cfg.id}') or '').strip()
|
||||
cfg.save(update_fields=['label_override', 'label_override_en', 'help_text_override', 'help_text_override_en'])
|
||||
_audit(request, 'form_field_texts_saved', target_type='form_config', target_label=form_type, details={'count': len(field_ids)})
|
||||
messages.success(request, 'Feldtexte wurden gespeichert.')
|
||||
messages.success(request, _('Feldtexte wurden gespeichert.'))
|
||||
|
||||
elif action == 'add_custom_section' and form_type == 'onboarding':
|
||||
title = (request.POST.get('custom_section_title') or '').strip()
|
||||
title_en = (request.POST.get('custom_section_title_en') or '').strip()
|
||||
sort_order_raw = (request.POST.get('custom_section_sort_order') or '').strip()
|
||||
if not title:
|
||||
messages.error(request, 'Bitte einen Titel für den benutzerdefinierten Abschnitt angeben.')
|
||||
messages.error(request, _('Bitte einen Titel für den benutzerdefinierten Abschnitt angeben.'))
|
||||
else:
|
||||
section_key_base = build_custom_field_key(title)
|
||||
section_key = section_key_base
|
||||
@@ -2382,7 +2422,7 @@ def form_builder_page(request):
|
||||
is_active=True,
|
||||
)
|
||||
_audit(request, 'form_custom_section_added', target_type='form_custom_section', target_label=title, details={'form_type': form_type, 'section_key': section_key})
|
||||
messages.success(request, 'Benutzerdefinierter Abschnitt wurde hinzugefügt.')
|
||||
messages.success(request, _('Benutzerdefinierter Abschnitt wurde hinzugefügt.'))
|
||||
|
||||
elif action == 'save_custom_sections' and form_type == 'onboarding':
|
||||
section_ids = request.POST.getlist('custom_section_ids')
|
||||
@@ -2402,7 +2442,7 @@ def form_builder_page(request):
|
||||
cfg.save(update_fields=['title', 'title_en', 'is_active', 'sort_order'])
|
||||
updated += 1
|
||||
_audit(request, 'form_custom_sections_saved', target_type='form_custom_section', target_label=form_type, details={'count': updated})
|
||||
messages.success(request, 'Benutzerdefinierte Abschnitte wurden gespeichert.')
|
||||
messages.success(request, _('Benutzerdefinierte Abschnitte wurden gespeichert.'))
|
||||
|
||||
elif action == 'add_custom_field':
|
||||
label = (request.POST.get('custom_label') or '').strip()
|
||||
@@ -2417,13 +2457,13 @@ def form_builder_page(request):
|
||||
section_choices = {key for key in get_section_order(form_type)}
|
||||
field_type_choices = {key for key, _ in FormCustomFieldConfig.FIELD_TYPE_CHOICES}
|
||||
if not label:
|
||||
messages.error(request, 'Bitte eine Bezeichnung für das benutzerdefinierte Feld angeben.')
|
||||
messages.error(request, _('Bitte eine Bezeichnung für das benutzerdefinierte Feld angeben.'))
|
||||
elif section_key not in section_choices:
|
||||
messages.error(request, 'Ungültiger Abschnitt für das benutzerdefinierte Feld.')
|
||||
messages.error(request, _('Ungültiger Abschnitt für das benutzerdefinierte Feld.'))
|
||||
elif field_type not in field_type_choices:
|
||||
messages.error(request, 'Ungültiger Feldtyp.')
|
||||
messages.error(request, _('Ungültiger Feldtyp.'))
|
||||
elif field_type == FormCustomFieldConfig.FIELD_TYPE_SELECT and not select_options:
|
||||
messages.error(request, 'Auswahlfelder benötigen mindestens eine Option.')
|
||||
messages.error(request, _('Auswahlfelder benötigen mindestens eine Option.'))
|
||||
else:
|
||||
field_key_base = build_custom_field_key(label)
|
||||
field_key = field_key_base
|
||||
@@ -2451,7 +2491,7 @@ def form_builder_page(request):
|
||||
select_options_en=select_options_en,
|
||||
)
|
||||
_audit(request, 'form_custom_field_added', target_type='form_custom_field', target_label=label, details={'form_type': form_type, 'field_type': field_type, 'section_key': section_key})
|
||||
messages.success(request, 'Benutzerdefiniertes Feld wurde hinzugefügt.')
|
||||
messages.success(request, _('Benutzerdefiniertes Feld wurde hinzugefügt.'))
|
||||
|
||||
elif action == 'save_custom_fields':
|
||||
custom_ids = request.POST.getlist('custom_field_ids')
|
||||
@@ -2482,12 +2522,12 @@ def form_builder_page(request):
|
||||
cfg.select_options = (request.POST.get(f'custom_select_options_{cfg.id}') or '').strip()
|
||||
cfg.select_options_en = (request.POST.get(f'custom_select_options_en_{cfg.id}') or '').strip()
|
||||
if cfg.field_type == FormCustomFieldConfig.FIELD_TYPE_SELECT and not cfg.select_options:
|
||||
messages.error(request, f'Auswahlfeld "{cfg.label}" benötigt mindestens eine Option.')
|
||||
messages.error(request, _('Auswahlfeld "%(label)s" benötigt mindestens eine Option.') % {'label': cfg.label})
|
||||
return redirect(f"/admin-tools/form-builder/?form_type={form_type}&option_category={option_category}&module=custom-fields")
|
||||
cfg.save()
|
||||
updated += 1
|
||||
_audit(request, 'form_custom_fields_saved', target_type='form_custom_field', target_label=form_type, details={'count': updated})
|
||||
messages.success(request, 'Benutzerdefinierte Felder wurden gespeichert.')
|
||||
messages.success(request, _('Benutzerdefinierte Felder wurden gespeichert.'))
|
||||
|
||||
elif action == 'save_field_rules':
|
||||
field_ids = request.POST.getlist('field_rule_ids')
|
||||
@@ -2507,7 +2547,7 @@ def form_builder_page(request):
|
||||
cfg.save(update_fields=['is_visible', 'is_required'])
|
||||
updated += 1
|
||||
_audit(request, 'form_field_rules_saved', target_type='form_config', target_label=form_type, details={'count': updated})
|
||||
messages.success(request, 'Feldregeln wurden gespeichert.')
|
||||
messages.success(request, _('Feldregeln wurden gespeichert.'))
|
||||
|
||||
elif action == 'save_section_rules' and form_type in {'onboarding', 'offboarding'}:
|
||||
section_configs = ensure_form_section_configs(form_type)
|
||||
@@ -2546,7 +2586,7 @@ def form_builder_page(request):
|
||||
cfg.save(update_fields=['is_active'])
|
||||
updated += 1
|
||||
_audit(request, 'form_section_rules_saved', target_type='form_config', target_label=form_type, details={'count': updated})
|
||||
messages.success(request, 'Abschnittsregeln wurden gespeichert.')
|
||||
messages.success(request, _('Abschnittsregeln wurden gespeichert.'))
|
||||
|
||||
elif action == 'save_conditional_rules' and form_type == 'onboarding':
|
||||
rule_configs = ensure_form_conditional_rule_configs(form_type)
|
||||
@@ -2567,16 +2607,16 @@ def form_builder_page(request):
|
||||
cfg.save(update_fields=['is_active', 'clauses'])
|
||||
updated += 1
|
||||
_audit(request, 'form_conditional_rules_saved', target_type='form_config', target_label=form_type, details={'count': updated})
|
||||
messages.success(request, 'Bedingte Logik wurde gespeichert.')
|
||||
messages.success(request, _('Bedingte Logik wurde gespeichert.'))
|
||||
|
||||
elif action == 'apply_preset':
|
||||
preset_key = (request.POST.get('preset_key') or '').strip()
|
||||
if apply_form_preset(form_type, preset_key):
|
||||
active_module = 'preview'
|
||||
_audit(request, 'form_preset_applied', target_type='form_config', target_label=form_type, details={'preset': preset_key})
|
||||
messages.success(request, 'Preset wurde angewendet.')
|
||||
messages.success(request, _('Preset wurde angewendet.'))
|
||||
else:
|
||||
messages.error(request, 'Preset konnte nicht angewendet werden.')
|
||||
messages.error(request, _('Preset konnte nicht angewendet werden.'))
|
||||
|
||||
if action in {'add_option', 'save_options'}:
|
||||
active_module = 'options'
|
||||
@@ -2778,6 +2818,11 @@ def form_builder_page(request):
|
||||
'is_visible': cfg.is_visible,
|
||||
'is_required': cfg.is_required,
|
||||
'locked': cfg.field_name in locked and not can_override_locked_builder_rules,
|
||||
'summary': _field_rule_summary(
|
||||
is_visible=cfg.is_visible,
|
||||
is_required=cfg.is_required,
|
||||
locked=cfg.field_name in locked and not can_override_locked_builder_rules,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2854,6 +2899,7 @@ def form_builder_page(request):
|
||||
conditional_field_choices.append((field_name, labels.get(field_name, field_name)))
|
||||
for cfg in custom_field_configs:
|
||||
conditional_field_choices.append((f'custom__{cfg.field_key}', cfg.translated_label(language_code)))
|
||||
conditional_field_label_map = {value: label for value, label in conditional_field_choices}
|
||||
conditional_target_titles = {
|
||||
'business-card-box': _('Visitenkarten-Details'),
|
||||
'employment-end-box': _('Vertragsende'),
|
||||
@@ -2893,6 +2939,7 @@ def form_builder_page(request):
|
||||
'description': target_description,
|
||||
'is_active': cfg.is_active,
|
||||
'clauses': clauses[:2],
|
||||
'summary': _conditional_rule_summary(clauses[:2], conditional_field_label_map),
|
||||
'field_choices': conditional_field_choices,
|
||||
'operator_choices': CONDITIONAL_RULE_OPERATOR_CHOICES,
|
||||
'target_fields': target_fields,
|
||||
@@ -2945,6 +2992,41 @@ def form_builder_page(request):
|
||||
'custom_field_count': len([cfg for cfg in custom_field_configs if cfg.is_active]),
|
||||
'custom_section_count': len([cfg for cfg in custom_section_configs if cfg.is_active]),
|
||||
}
|
||||
module_labels = {
|
||||
'structure': _('Struktur & Reihenfolge'),
|
||||
'section-rules': _('Abschnitte'),
|
||||
'field-rules': _('Feldregeln'),
|
||||
'conditional-rules': _('Bedingte Logik'),
|
||||
'options': _('Optionen'),
|
||||
'field-texts': _('Feldtexte'),
|
||||
'custom-sections': _('Eigene Abschnitte'),
|
||||
'custom-fields': _('Eigene Felder'),
|
||||
'preview': _('Vorschau'),
|
||||
}
|
||||
option_category_labels = dict(_translate_choice_list(FormOption.CATEGORY_CHOICES))
|
||||
form_type_labels = {
|
||||
'onboarding': _('Onboarding'),
|
||||
'offboarding': _('Offboarding'),
|
||||
}
|
||||
active_focus_label = ''
|
||||
if active_module == 'structure' and active_structure_section:
|
||||
active_focus_label = section_labels.get(active_structure_section, active_structure_section)
|
||||
elif active_module == 'section-rules' and section_rule_items:
|
||||
active_focus_label = _('Alle Abschnitte')
|
||||
elif active_module == 'field-rules' and active_field_rules_section:
|
||||
active_focus_label = section_labels.get(active_field_rules_section, active_field_rules_section)
|
||||
elif active_module == 'conditional-rules' and active_conditional_target:
|
||||
active_focus_label = next((item['title'] for item in conditional_rule_items if item['target_key'] == active_conditional_target), active_conditional_target)
|
||||
elif active_module == 'options':
|
||||
active_focus_label = option_category_labels.get(option_category, option_category)
|
||||
elif active_module == 'field-texts' and active_field_texts_section:
|
||||
active_focus_label = section_labels.get(active_field_texts_section, active_field_texts_section)
|
||||
elif active_module == 'custom-sections':
|
||||
active_focus_label = _('Onboarding')
|
||||
elif active_module == 'custom-fields' and active_custom_fields_section:
|
||||
active_focus_label = section_labels.get(active_custom_fields_section, active_custom_fields_section)
|
||||
elif active_module == 'preview':
|
||||
active_focus_label = _('Live-Vorschau')
|
||||
|
||||
return render(
|
||||
request,
|
||||
@@ -2952,7 +3034,7 @@ def form_builder_page(request):
|
||||
{
|
||||
'form_type': form_type,
|
||||
'columns': columns,
|
||||
'form_types': [('onboarding', 'Onboarding'), ('offboarding', 'Offboarding')],
|
||||
'form_types': [('onboarding', _('Onboarding')), ('offboarding', _('Offboarding'))],
|
||||
'option_categories': _translate_choice_list(FormOption.CATEGORY_CHOICES),
|
||||
'selected_option_category': option_category,
|
||||
'option_items': FormOption.objects.filter(category=option_category).order_by('sort_order', 'label'),
|
||||
@@ -2971,6 +3053,9 @@ def form_builder_page(request):
|
||||
'active_subpanel': active_subpanel,
|
||||
'active_rules_panel': active_rules_panel,
|
||||
'active_module': active_module,
|
||||
'active_form_type_label': form_type_labels.get(form_type, form_type),
|
||||
'active_module_label': module_labels.get(active_module, active_module),
|
||||
'active_focus_label': active_focus_label,
|
||||
'active_structure_section': active_structure_section,
|
||||
'active_field_rules_section': active_field_rules_section,
|
||||
'active_field_texts_section': active_field_texts_section,
|
||||
|
||||
Reference in New Issue
Block a user