From 412d344536233af69c079ad2fec0c1e2eb25c47b Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Mon, 31 Mar 2025 13:27:25 +0200 Subject: [PATCH] Show nested fieldsets for form --- src/servala/core/crd.py | 61 +++++++++++++++++++ .../templates/frontend/forms/field.html | 4 +- .../service_offering_detail.html | 2 +- .../includes/tabbed_fieldset_form.html | 50 +++++++++++++++ src/servala/frontend/templatetags/__init__.py | 0 .../frontend/templatetags/get_field.py | 8 +++ 6 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 src/servala/frontend/templates/includes/tabbed_fieldset_form.html create mode 100644 src/servala/frontend/templatetags/__init__.py create mode 100644 src/servala/frontend/templatetags/get_field.py diff --git a/src/servala/core/crd.py b/src/servala/core/crd.py index 42ca351..48df661 100644 --- a/src/servala/core/crd.py +++ b/src/servala/core/crd.py @@ -147,6 +147,67 @@ class CrdModelFormMixin: for field in ("organization", "context"): self.fields[field].widget = forms.HiddenInput() + def get_fieldsets(self): + fieldsets = [] + + # General fieldset for non-spec fields + general_fields = [ + field for field in self.fields if not field.startswith("spec.") + ] + if general_fields: + fieldsets.append( + {"title": "General", "fields": general_fields, "fieldsets": []} + ) + + # Process spec fields + others = [] + nested_fieldsets = {} + + for field_name in self.fields: + if field_name.startswith("spec."): + parts = field_name.split(".") + if len(parts) == 2: # Top-level spec field + others.append(field_name) + else: + parent_key = parts[1] + if not nested_fieldsets.get(parent_key): + nested_fieldsets[parent_key] = { + "fields": [], + "fieldsets": {}, + "title": parent_key.title(), + } + parent = nested_fieldsets[parent_key] + if len(parts) == 3: # Top-level within fieldset + parent["fields"].append(field_name) + else: + sub_key = parts[2] + if not parent["fieldsets"].get(sub_key): + parent["fieldsets"][sub_key] = { + "title": sub_key.title(), + "fields": [], + } + parent["fieldsets"][sub_key]["fields"].append(field_name) + + # Add nested fieldsets to fieldsets + for group in nested_fieldsets.values(): + total_fields = 0 + for fieldset in group["fieldsets"].values(): + if (field_count := len(fieldset["fields"])) == 1: + group["fields"].append(fieldset["fields"][0]) + else: + total_fields += field_count + total_fields += len(group["fields"]) + if total_fields == 1: + others.append(group["fields"][0]) + else: + fieldsets.append(group) + + # Add 'others' tab if there are any fields + if others: + fieldsets.append({"title": "Others", "fields": others, "fieldsets": []}) + + return fieldsets + def get_nested_data(self): """ Builds the original nested JSON structure from flat form data. diff --git a/src/servala/frontend/templates/frontend/forms/field.html b/src/servala/frontend/templates/frontend/forms/field.html index 744732e..3e0a30b 100644 --- a/src/servala/frontend/templates/frontend/forms/field.html +++ b/src/servala/frontend/templates/frontend/forms/field.html @@ -1,6 +1,6 @@ {% load i18n %}
- {% if not hide_label %} + {% if not hide_label and not field.is_hidden %} {% if field.field.widget.input_type != "checkbox" or field.field.widget.allow_multiple_selected %} {% endif %} @@ -9,7 +9,7 @@
{% endif %} {{ field }} - {% if field.field.widget.input_type == "checkbox" and not field.field.widget.allow_multiple_selected %} + {% if field.field.widget.input_type == "checkbox" and not field.field.widget.allow_multiple_selected and not field.is_hidden %} {% endif %} {% if field.use_fieldset %}
{% endif %} diff --git a/src/servala/frontend/templates/frontend/organizations/service_offering_detail.html b/src/servala/frontend/templates/frontend/organizations/service_offering_detail.html index 066c1af..184ff49 100644 --- a/src/servala/frontend/templates/frontend/organizations/service_offering_detail.html +++ b/src/servala/frontend/templates/frontend/organizations/service_offering_detail.html @@ -17,7 +17,7 @@ {% translate "Oops! Something went wrong with the service form generation. Please try again later." %}
{% else %} - {% include "includes/form.html" with form=service_form %} + {% include "includes/tabbed_fieldset_form.html" with form=service_form %} {% endif %} diff --git a/src/servala/frontend/templates/includes/tabbed_fieldset_form.html b/src/servala/frontend/templates/includes/tabbed_fieldset_form.html new file mode 100644 index 0000000..6c69bc3 --- /dev/null +++ b/src/servala/frontend/templates/includes/tabbed_fieldset_form.html @@ -0,0 +1,50 @@ +{% load i18n %} +{% load get_field %} +
+ {% csrf_token %} + +
+ {% for fieldset in form.get_fieldsets %} +
+ {% for field in fieldset.fields %} + {% with field=form|get_field:field %}{{ field.as_field_group }}{% endwith %} + {% endfor %} + {% for subfieldset in fieldset.fieldsets.values %} +

{{ subfieldset.title }}

+ {% for field in subfieldset.fields %} + {% with field=form|get_field:field %}{{ field.as_field_group }}{% endwith %} + {% endfor %} + {% endfor %} +
+ {% endfor %} +
+
+ +
+
diff --git a/src/servala/frontend/templatetags/__init__.py b/src/servala/frontend/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/servala/frontend/templatetags/get_field.py b/src/servala/frontend/templatetags/get_field.py new file mode 100644 index 0000000..3214beb --- /dev/null +++ b/src/servala/frontend/templatetags/get_field.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + + +@register.filter +def get_field(form, field_name): + return form[field_name]