Remove expert fields, add form config

This commit is contained in:
Tobias Kunze 2025-11-05 08:57:28 +01:00
parent 1cf1947539
commit 357e39b543
6 changed files with 80 additions and 44 deletions

View file

@ -1,3 +1,6 @@
import json
from pathlib import Path
from django.contrib import admin, messages from django.contrib import admin, messages
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_jsonform.widgets import JSONFormWidget from django_jsonform.widgets import JSONFormWidget
@ -313,9 +316,9 @@ class ServiceDefinitionAdmin(admin.ModelAdmin):
( (
_("Form Configuration"), _("Form Configuration"),
{ {
"fields": ("advanced_fields",), "fields": ("form_config",),
"description": _( "description": _(
"Configure which fields should be hidden behind an 'Advanced' toggle in the form" "Optional custom form configuration. When provided, this will be used instead of auto-generating the form from the OpenAPI spec."
), ),
}, },
), ),
@ -323,19 +326,13 @@ class ServiceDefinitionAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs) form = super().get_form(request, obj, **kwargs)
# JSON schema for advanced_fields field schema_path = Path(__file__).parent / "schemas" / "form_config_schema.json"
advanced_fields_schema = { with open(schema_path) as f:
"type": "array", form_config_schema = json.load(f)
"title": "Advanced Fields",
"items": { if "form_config" in form.base_fields:
"type": "string", form.base_fields["form_config"].widget = JSONFormWidget(
"title": "Field Name", schema=form_config_schema
"description": "Field name in dot notation (e.g., spec.parameters.monitoring.enabled)",
},
}
if "advanced_fields" in form.base_fields:
form.base_fields["advanced_fields"].widget = JSONFormWidget(
schema=advanced_fields_schema
) )
return form return form

View file

@ -0,0 +1,32 @@
# Generated by Django 5.2.7 on 2025-10-31 10:40
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0011_alter_organizationorigin_billing_entity"),
]
operations = [
migrations.RemoveField(
model_name="servicedefinition",
name="advanced_fields",
),
migrations.AlterField(
model_name="organization",
name="name",
field=models.CharField(
max_length=32,
validators=[
django.core.validators.RegexValidator(
message="Organization name can only contain letters, numbers, and spaces.",
regex="^[A-Za-z0-9\\s]+$",
)
],
verbose_name="Name",
),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 5.2.7 on 2025-10-31 10:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0012_remove_advanced_fields"),
]
operations = [
migrations.AddField(
model_name="servicedefinition",
name="form_config",
field=models.JSONField(
blank=True,
help_text='Optional custom form configuration. When provided, this configuration will be used to render the service form instead of auto-generating it from the OpenAPI spec. Format: {"fieldsets": [{"title": "Section", "fields": [{...}]}]}',
null=True,
verbose_name="Form Configuration",
),
),
]

View file

@ -365,7 +365,7 @@ class ServiceDefinition(ServalaModelMixin, models.Model):
help_text=_( help_text=_(
"Optional custom form configuration. When provided, this configuration will be used " "Optional custom form configuration. When provided, this configuration will be used "
"to render the service form instead of auto-generating it from the OpenAPI spec. " "to render the service form instead of auto-generating it from the OpenAPI spec. "
"Format: {\"fieldsets\": [{\"title\": \"Section\", \"fields\": [{...}]}]}" 'Format: {"fieldsets": [{"title": "Section", "fields": [{...}]}]}'
), ),
null=True, null=True,
blank=True, blank=True,

View file

@ -48,9 +48,6 @@
"description": "Whether the field is required", "description": "Whether the field is required",
"default": false "default": false
}, },
"default": {
"description": "Default value for the field"
},
"controlplane_field_mapping": { "controlplane_field_mapping": {
"type": "string", "type": "string",
"description": "Dot-notation path mapping to Kubernetes spec field (e.g., 'spec.parameters.service.fqdn')" "description": "Dot-notation path mapping to Kubernetes spec field (e.g., 'spec.parameters.service.fqdn')"
@ -78,12 +75,9 @@
"description": "Array of [value, label] pairs for choice fields", "description": "Array of [value, label] pairs for choice fields",
"items": { "items": {
"type": "array", "type": "array",
"minItems": 2, "items": {
"maxItems": 2, "type": "string"
"items": [ }
{"type": "string"},
{"type": "string"}
]
} }
}, },
"min_values": { "min_values": {
@ -112,17 +106,7 @@
"enum": ["suggest_fqdn_from_name"] "enum": ["suggest_fqdn_from_name"]
} }
} }
}, }
"allOf": [
{
"if": {
"properties": {"type": {"const": "choice"}}
},
"then": {
"required": ["choices"]
}
}
]
} }
} }
} }

View file

@ -237,42 +237,42 @@ a.btn-keycloak {
flex-grow: 1; flex-grow: 1;
} }
/* CRD Form mandatory field styling */ /* Expert CRD Form mandatory field styling */
.crd-form .form-group.mandatory .form-label { .expert-crd-form .form-group.mandatory .form-label {
font-weight: bold; font-weight: bold;
position: relative; position: relative;
} }
.crd-form .form-group.mandatory .form-label::after { .expert-crd-form .form-group.mandatory .form-label::after {
content: " *"; content: " *";
color: #dc3545; color: #dc3545;
font-weight: bold; font-weight: bold;
} }
.crd-form .form-group.mandatory { .expert-crd-form .form-group.mandatory {
border-left: 3px solid #dc3545; border-left: 3px solid #dc3545;
padding-left: 10px; padding-left: 10px;
background-color: rgba(220, 53, 69, 0.05); background-color: rgba(220, 53, 69, 0.05);
border-radius: 3px; border-radius: 3px;
} }
.crd-form .nav-tabs .nav-link .mandatory-indicator { .expert-crd-form .nav-tabs .nav-link .mandatory-indicator {
color: #dc3545; color: #dc3545;
font-weight: bold; font-weight: bold;
font-size: 1.1em; font-size: 1.1em;
margin-left: 4px; margin-left: 4px;
} }
html[data-bs-theme="dark"] .crd-form .form-group.mandatory { html[data-bs-theme="dark"] .expert-crd-form .form-group.mandatory {
background-color: rgba(220, 53, 69, 0.1); background-color: rgba(220, 53, 69, 0.1);
border-left-color: #ff6b6b; border-left-color: #ff6b6b;
} }
html[data-bs-theme="dark"] .crd-form .form-group.mandatory .form-label::after { html[data-bs-theme="dark"] .expert-crd-form .form-group.mandatory .form-label::after {
color: #ff6b6b; color: #ff6b6b;
} }
html[data-bs-theme="dark"] .crd-form .nav-tabs .nav-link .mandatory-indicator { html[data-bs-theme="dark"] .expert-crd-form .nav-tabs .nav-link .mandatory-indicator {
color: #ff6b6b; color: #ff6b6b;
} }