Refactor: Remove CLI menu loader and finalize admin-based JSON upload flow
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
69a7852f7a
commit
9046deeffa
@ -67,6 +67,7 @@ TEMPLATES = [
|
|||||||
"django.template.context_processors.request",
|
"django.template.context_processors.request",
|
||||||
"django.contrib.auth.context_processors.auth",
|
"django.contrib.auth.context_processors.auth",
|
||||||
"django.contrib.messages.context_processors.messages",
|
"django.contrib.messages.context_processors.messages",
|
||||||
|
"pxy_dashboard.context_processors.sidebar_context",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from .models import SidebarMenuItem
|
||||||
|
|
||||||
# Register your models here.
|
|
||||||
|
@admin.register(SidebarMenuItem)
|
||||||
|
class SidebarMenuAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("label", "type", "url", "order", "parent")
|
||||||
|
list_filter = ("type", "parent")
|
||||||
|
search_fields = ("label", "url")
|
||||||
|
ordering = ("order",)
|
||||||
|
21
pxy_dashboard/context_processors.py
Normal file
21
pxy_dashboard/context_processors.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from .models import SidebarMenuItem
|
||||||
|
|
||||||
|
def build_menu_tree(parent=None):
|
||||||
|
items = SidebarMenuItem.objects.filter(parent=parent).order_by("order")
|
||||||
|
tree = []
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
tree.append({
|
||||||
|
"type": item.type,
|
||||||
|
"label": item.label,
|
||||||
|
"icon": item.icon,
|
||||||
|
"url": item.url,
|
||||||
|
"badge": item.badge,
|
||||||
|
"children": build_menu_tree(parent=item)
|
||||||
|
})
|
||||||
|
|
||||||
|
return tree
|
||||||
|
|
||||||
|
def sidebar_context(request):
|
||||||
|
sidebar_menu = build_menu_tree()
|
||||||
|
return {"sidebar_menu": sidebar_menu}
|
0
pxy_dashboard/management/__init__.py
Normal file
0
pxy_dashboard/management/__init__.py
Normal file
0
pxy_dashboard/management/commands/__init__.py
Normal file
0
pxy_dashboard/management/commands/__init__.py
Normal file
26
pxy_dashboard/management/commands/load_sidebar_menu.py
Normal file
26
pxy_dashboard/management/commands/load_sidebar_menu.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import json
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from pxy_dashboard.models import SidebarMenuItem
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Carga el menú lateral desde un archivo JSON"
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument("json_file", type=str, help="Ruta al archivo JSON")
|
||||||
|
|
||||||
|
def handle(self, *args, **kwargs):
|
||||||
|
json_path = kwargs["json_file"]
|
||||||
|
with open(json_path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
created = self.create_menu_items(data)
|
||||||
|
self.stdout.write(self.style.SUCCESS(f"{created} ítems de menú creados."))
|
||||||
|
|
||||||
|
def create_menu_items(self, items, parent=None):
|
||||||
|
count = 0
|
||||||
|
for item in items:
|
||||||
|
children = item.pop("children", [])
|
||||||
|
menu_item = SidebarMenuItem.objects.create(parent=parent, **item)
|
||||||
|
count += 1
|
||||||
|
if children:
|
||||||
|
count += self.create_menu_items(children, parent=menu_item)
|
||||||
|
return count
|
31
pxy_dashboard/migrations/0001_initial.py
Normal file
31
pxy_dashboard/migrations/0001_initial.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2025-05-15 20:58
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SidebarMenuItem',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('type', models.CharField(choices=[('title', 'Título de sección'), ('link', 'Enlace simple'), ('submenu', 'Submenú (con hijos)')], max_length=10)),
|
||||||
|
('label', models.CharField(max_length=100)),
|
||||||
|
('icon', models.CharField(blank=True, max_length=100, null=True)),
|
||||||
|
('url', models.CharField(blank=True, max_length=200, null=True)),
|
||||||
|
('badge', models.CharField(blank=True, max_length=50, null=True)),
|
||||||
|
('order', models.PositiveIntegerField(default=0)),
|
||||||
|
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='pxy_dashboard.sidebarmenuitem')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['order'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.3 on 2025-05-16 07:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pxy_dashboard', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='sidebarmenuitem',
|
||||||
|
name='open_in_new_tab',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
@ -1,3 +1,24 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
# Create your models here.
|
class SidebarMenuItem(models.Model):
|
||||||
|
MENU_TYPES = [
|
||||||
|
("title", "Título de sección"),
|
||||||
|
("link", "Enlace simple"),
|
||||||
|
("submenu", "Submenú (con hijos)"),
|
||||||
|
]
|
||||||
|
|
||||||
|
type = models.CharField(max_length=10, choices=MENU_TYPES)
|
||||||
|
label = models.CharField(max_length=100)
|
||||||
|
icon = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
url = models.CharField(max_length=200, blank=True, null=True)
|
||||||
|
badge = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE, related_name="children")
|
||||||
|
order = models.PositiveIntegerField(default=0)
|
||||||
|
open_in_new_tab = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["order"]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.label
|
||||||
|
@ -50,6 +50,11 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% load sidebar_menu %}
|
||||||
|
<ul class="side-nav">
|
||||||
|
{% render_sidebar_menu sidebar_menu %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<!--- Sidemenu -->
|
<!--- Sidemenu -->
|
||||||
<ul class="side-nav">
|
<ul class="side-nav">
|
||||||
|
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
{% for item in items %}
|
||||||
|
{% if item.type == "title" %}
|
||||||
|
<li class="side-nav-title mt-1">{{ item.label }}</li>
|
||||||
|
|
||||||
|
{% elif item.children %}
|
||||||
|
<li class="side-nav-item">
|
||||||
|
<a data-bs-toggle="collapse" href="#menu-{{ item.label|slugify }}-{{ level }}" class="side-nav-link">
|
||||||
|
<i class="{{ item.icon }}"></i>
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
<span class="menu-arrow"></span>
|
||||||
|
</a>
|
||||||
|
<div class="collapse" id="menu-{{ item.label|slugify }}-{{ level }}">
|
||||||
|
<ul class="{% if level == 1 %}side-nav-second-level{% elif level == 2 %}side-nav-third-level{% else %}side-nav-forth-level{% endif %}">
|
||||||
|
{% for child in item.children %}
|
||||||
|
{% if child.children %}
|
||||||
|
{% include "pxy_dashboard/partials/sidebar_menu_node.html" with items=child.children level=level|add:"1" %}
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url child.url %}">
|
||||||
|
{{ child.label }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<li class="side-nav-item">
|
||||||
|
<a href="{% url item.url %}" class="side-nav-link" {% if item.open_in_new_tab %}target="_blank"{% endif %}>
|
||||||
|
<i class="{{ item.icon }}"></i>
|
||||||
|
{% if item.badge %}
|
||||||
|
<span class="badge bg-success float-end">{{ item.badge }}</span>
|
||||||
|
{% endif %}
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
0
pxy_dashboard/templatetags/__init__.py
Normal file
0
pxy_dashboard/templatetags/__init__.py
Normal file
11
pxy_dashboard/templatetags/sidebar_menu.py
Normal file
11
pxy_dashboard/templatetags/sidebar_menu.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from django import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.inclusion_tag("pxy_dashboard/partials/sidebar_menu_node.html", takes_context=True)
|
||||||
|
def render_sidebar_menu(context, items, level=1):
|
||||||
|
return {
|
||||||
|
"items": items,
|
||||||
|
"level": level,
|
||||||
|
"request": context["request"],
|
||||||
|
}
|
17
sidebar_menu.json
Normal file
17
sidebar_menu.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "group",
|
||||||
|
"label": "Layouts",
|
||||||
|
"icon": "ri-layout-3-fill",
|
||||||
|
"order": 140,
|
||||||
|
"children": [
|
||||||
|
{ "type": "link", "label": "Horizontal", "url": "layouts:horizontal", "order": 10 },
|
||||||
|
{ "type": "link", "label": "Detached", "url": "layouts:detached", "order": 20 },
|
||||||
|
{ "type": "link", "label": "Full View", "url": "layouts:full", "order": 30 },
|
||||||
|
{ "type": "link", "label": "Fullscreen View", "url": "layouts:fullscreen", "order": 40 },
|
||||||
|
{ "type": "link", "label": "Hover Menu", "url": "layouts:hover", "order": 50 },
|
||||||
|
{ "type": "link", "label": "Compact", "url": "layouts:compact", "order": 60 },
|
||||||
|
{ "type": "link", "label": "Icon View", "url": "layouts:icon-view", "order": 70 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
Loading…
x
Reference in New Issue
Block a user