Compare commits
No commits in common. "b8f3621b47038884f8d09e6e99aaa88d1d5e9c2a" and "bca79be02a28f0a0c6bcb024013051bd6599bba8" have entirely different histories.
b8f3621b47
...
bca79be02a
6 changed files with 10 additions and 121 deletions
|
|
@ -5,25 +5,14 @@ from django_jsonform.widgets import JSONFormWidget
|
||||||
from servala.core.models import ControlPlane, ServiceDefinition
|
from servala.core.models import ControlPlane, ServiceDefinition
|
||||||
|
|
||||||
CONTROL_PLANE_USER_INFO_SCHEMA = {
|
CONTROL_PLANE_USER_INFO_SCHEMA = {
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"title": {
|
"CNAME Record": {
|
||||||
|
"title": "CNAME Record",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Title",
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Content",
|
|
||||||
},
|
|
||||||
"help_text": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Help Text (optional)",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"required": ["title", "content"],
|
"additionalProperties": {"type": "string"},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
# Generated by Django 5.2.7 on 2025-10-24 10:04
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def convert_user_info_to_array(apps, schema_editor):
|
|
||||||
"""
|
|
||||||
Convert user_info from object format {"key": "value"} to array format
|
|
||||||
[{"title": "key", "content": "value"}].
|
|
||||||
"""
|
|
||||||
ControlPlane = apps.get_model("core", "ControlPlane")
|
|
||||||
|
|
||||||
for control_plane in ControlPlane.objects.all():
|
|
||||||
if not control_plane.user_info:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If it's already an array (migration already run or new format), skip
|
|
||||||
if isinstance(control_plane.user_info, list):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Convert from dict to array
|
|
||||||
if isinstance(control_plane.user_info, dict):
|
|
||||||
new_user_info = []
|
|
||||||
for key, value in control_plane.user_info.items():
|
|
||||||
new_user_info.append({"title": key, "content": value})
|
|
||||||
|
|
||||||
control_plane.user_info = new_user_info
|
|
||||||
control_plane.save(update_fields=["user_info"])
|
|
||||||
|
|
||||||
|
|
||||||
def reverse_user_info_to_object(apps, schema_editor):
|
|
||||||
"""
|
|
||||||
Reverse the migration by converting array format back to object format.
|
|
||||||
Note: help_text will be lost during reversal.
|
|
||||||
"""
|
|
||||||
ControlPlane = apps.get_model("core", "ControlPlane")
|
|
||||||
|
|
||||||
for control_plane in ControlPlane.objects.all():
|
|
||||||
if not control_plane.user_info:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# If it's already an object, skip
|
|
||||||
if isinstance(control_plane.user_info, dict):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Convert from array to dict
|
|
||||||
if isinstance(control_plane.user_info, list):
|
|
||||||
new_user_info = {}
|
|
||||||
for item in control_plane.user_info:
|
|
||||||
if isinstance(item, dict) and "title" in item and "content" in item:
|
|
||||||
new_user_info[item["title"]] = item["content"]
|
|
||||||
|
|
||||||
control_plane.user_info = new_user_info
|
|
||||||
control_plane.save(update_fields=["user_info"])
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("core", "0011_alter_organizationorigin_billing_entity"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(convert_user_info_to_array, reverse_user_info_to_object),
|
|
||||||
]
|
|
||||||
|
|
@ -156,8 +156,7 @@ class ControlPlane(ServalaModelMixin, models.Model):
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_("User Information"),
|
verbose_name=_("User Information"),
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'Array of info objects: [{"title": "…", "content": "…", "help_text": "…"}]. '
|
"Key-value information displayed to users when selecting this control plane"
|
||||||
"The help_text field is optional and will be shown as a hover popover on an info icon."
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
wildcard_dns = models.CharField(
|
wildcard_dns = models.CharField(
|
||||||
|
|
|
||||||
|
|
@ -248,12 +248,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
{% block extra_js %}
|
|
||||||
<script>
|
|
||||||
// Initialize Bootstrap popovers for help text
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
|
|
||||||
[...popoverTriggerList].map(el => new bootstrap.Popover(el));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock extra_js %}
|
|
||||||
|
|
|
||||||
|
|
@ -108,19 +108,4 @@
|
||||||
</script>
|
</script>
|
||||||
<script defer src="{% static "js/fqdn.js" %}"></script>
|
<script defer src="{% static "js/fqdn.js" %}"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script>
|
|
||||||
// Initialize Bootstrap popovers for help text
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
|
|
||||||
[...popoverTriggerList].map(el => new bootstrap.Popover(el));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Re-initialize popovers after HTMX swaps
|
|
||||||
document.body.addEventListener('htmx:afterSwap', function(event) {
|
|
||||||
if (event.detail.target.id === 'control-plane-info') {
|
|
||||||
const popoverTriggerList = event.detail.target.querySelectorAll('[data-bs-toggle="popover"]');
|
|
||||||
[...popoverTriggerList].map(el => new bootstrap.Popover(el));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock extra_js %}
|
{% endblock extra_js %}
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,10 @@
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table mb-0 table-lg">
|
<table class="table mb-0 table-lg">
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for info in control_plane.user_info %}
|
{% for key, value in control_plane.user_info.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>{{ key }}</th>
|
||||||
{{ info.title }}
|
<td>{{ value }}</td>
|
||||||
{% if info.help_text %}
|
|
||||||
<i class="bi bi-info-circle ms-1"
|
|
||||||
data-bs-toggle="popover"
|
|
||||||
data-bs-trigger="hover focus"
|
|
||||||
data-bs-placement="top"
|
|
||||||
data-bs-content="{{ info.help_text }}"
|
|
||||||
style="cursor: help"></i>
|
|
||||||
{% endif %}
|
|
||||||
</th>
|
|
||||||
<td>{{ info.content }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue