feat: Modularizes dashboard structure
Introduces DashboardSection class to encapsulate dashboard logic, enabling easier extension and customization. Adds separate mood and dreams dashboard templates to streamline content rendering. Refactors main dashboard template to dynamically render content from enabled modules using a new template tag system, improving flexibility and maintainability.
This commit is contained in:
parent
3c3cfc38ba
commit
5c7586bb9a
7 changed files with 254 additions and 185 deletions
|
@ -1,7 +1,9 @@
|
|||
from frontend.classes import NavSection, NavItem
|
||||
from frontend.classes import NavSection, NavItem, DashboardSection
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
# Sidebar navigation items
|
||||
|
||||
dreams_section = NavSection("Dreams")
|
||||
|
||||
dreams_items = {
|
||||
|
@ -14,3 +16,9 @@ for _, item in dreams_items.items():
|
|||
dreams_section.add_item(item)
|
||||
|
||||
NAV_SECTIONS = [dreams_section]
|
||||
|
||||
# Dashboard sections
|
||||
|
||||
dreams_section = DashboardSection("Dreams", "dreams/dashboard_section.html")
|
||||
|
||||
DASHBOARD_SECTIONS = [dreams_section]
|
78
dreams/templates/dreams/dashboard_section.html
Normal file
78
dreams/templates/dreams/dashboard_section.html
Normal file
|
@ -0,0 +1,78 @@
|
|||
{% load dream_stats %}
|
||||
<!-- Dream Stats -->
|
||||
<div class="row">
|
||||
<!-- Total Dreams -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-primary shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Dream count (total)</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">{% total_dreams %}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-book fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Weekly Dreams -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-success shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Dreams (weekly)</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">{% weekly_dreams %}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-calendar fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Special Dreams -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-info shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Special Dreams (lucid / wet)</div>
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-auto">
|
||||
{% special_dreams_weekly as weekly %}
|
||||
<div class="h5 mb-0 mr-3 font-weight-bold text-gray-800">
|
||||
{{ weekly.0 }} / {{ weekly.1 }} <sub><i>(weekly)</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="{{ moodobj.icon }} fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Most common theme -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-warning shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Most Common Theme (weekly)</div>
|
||||
{% most_common_theme_weekly as theme %}
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ theme.0 }} <sub><i>({{ theme.1 }})</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-bed fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,3 +1,6 @@
|
|||
from django.template.loader import render_to_string
|
||||
|
||||
|
||||
class NavSection:
|
||||
def __init__(self, name, order=100):
|
||||
self.name = name
|
||||
|
@ -16,18 +19,23 @@ class NavSection:
|
|||
self.items.sort(key=lambda x: x.order)
|
||||
|
||||
for item in self.items:
|
||||
html += """
|
||||
html += (
|
||||
"""
|
||||
<!-- Nav Item -->
|
||||
<li class="nav-item""" + (" active" if item.name == active else "") + f"""">
|
||||
<li class="nav-item"""
|
||||
+ (" active" if item.name == active else "")
|
||||
+ f"""">
|
||||
<a class="nav-link" href="{item.url}">
|
||||
<i class="{item.icon}"></i>
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
</li>
|
||||
"""
|
||||
)
|
||||
|
||||
return html
|
||||
|
||||
|
||||
class NavItem:
|
||||
def __init__(self, name, url, icon="fas fa-fw fa-smile", title=None, order=100):
|
||||
self.name = name
|
||||
|
@ -35,3 +43,13 @@ class NavItem:
|
|||
self.icon = icon
|
||||
self.title = title or name
|
||||
self.order = order
|
||||
|
||||
|
||||
class DashboardSection:
|
||||
def __init__(self, name, template, context=None):
|
||||
self.name = name
|
||||
self.template = template
|
||||
self.context = context or {}
|
||||
|
||||
def get_html(self, request):
|
||||
return render_to_string(self.template, self.context, request)
|
||||
|
|
|
@ -1,178 +1,6 @@
|
|||
{% extends "frontend/base.html" %}
|
||||
{% load mood_stats %}
|
||||
{% load dream_stats %}
|
||||
{% load dashboard %}
|
||||
{% block "content" %}
|
||||
<h2>Moods</h2>
|
||||
<!-- Mood calendar heatmap -->
|
||||
<div class="row">
|
||||
<div class="col-xl-12 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Mood Calendar</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mood-count-heatmap"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mood cards -->
|
||||
<div class="row">
|
||||
<!-- Status count -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-primary shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Status count (total)</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">{% total_moods %}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-book fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Current Streak Length -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-success shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Current Streak Length</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{% current_streak %} <sub>days</sub>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-calendar fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Average mood -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-info shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Average mood (weekly)</div>
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-auto">
|
||||
{% average_mood_weekly as mood %} {% closest_mood mood as moodobj %}
|
||||
<div class="h5 mb-0 mr-3 font-weight-bold text-gray-800">
|
||||
{{ moodobj }} <sub><i>({{ mood|floatformat:2 }})</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="{{ moodobj.icon }} fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Most common activity -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-warning shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Most Common Activity (weekly)</div>
|
||||
{% most_common_activity_weekly as activity %}
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ activity.0 }}<sub><i>({{ activity.1 }})</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="{{ activity.0.icon }} fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Dreams</h2>
|
||||
<!-- Dream Stats -->
|
||||
<div class="row">
|
||||
<!-- Total Dreams -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-primary shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Dream count (total)</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">{% total_dreams %}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-book fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Weekly Dreams -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-success shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Dreams (weekly)</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">{% weekly_dreams %}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-calendar fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Special Dreams -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-info shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Special Dreams (lucid / wet)</div>
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-auto">
|
||||
{% special_dreams_weekly as weekly %}
|
||||
<div class="h5 mb-0 mr-3 font-weight-bold text-gray-800">
|
||||
{{ weekly.0 }} / {{ weekly.1 }} <sub><i>(weekly)</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="{{ moodobj.icon }} fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Most common theme -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-warning shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Most Common Theme (weekly)</div>
|
||||
{% most_common_theme_weekly as theme %}
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ theme.0 }} <sub><i>({{ theme.1 }})</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-bed fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% dashboard as dashboard %}
|
||||
{{ dashboard | safe }}
|
||||
{% endblock "content" %}
|
||||
|
|
33
frontend/templatetags/dashboard.py
Normal file
33
frontend/templatetags/dashboard.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
from django import template
|
||||
from django.conf import settings
|
||||
|
||||
from importlib import import_module
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def dashboard(context):
|
||||
sections = []
|
||||
|
||||
for module in settings.CORE_MODULES + settings.ENABLED_MODULES:
|
||||
try:
|
||||
features = import_module(f"{module}.features")
|
||||
try:
|
||||
sections += features.DASHBOARD_SECTIONS
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
dashboard_html = ""
|
||||
|
||||
for section in sections:
|
||||
dashboard_html += f"<h2>{section.name}</h2>"
|
||||
|
||||
dashboard_html += section.get_html(context["request"])
|
||||
|
||||
if section != sections[-1]:
|
||||
dashboard_html += '<hr class="dashboard-divider">'
|
||||
|
||||
return dashboard_html
|
|
@ -1,18 +1,28 @@
|
|||
from frontend.classes import NavSection, NavItem
|
||||
from frontend.classes import NavSection, NavItem, DashboardSection
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
# Sidebar navigation items
|
||||
|
||||
mood_section = NavSection("Mood")
|
||||
|
||||
mood_items = {
|
||||
"mood_status_list": NavItem("Status List", reverse_lazy("mood:status_list")),
|
||||
"mood_activity_list": NavItem("Activities", reverse_lazy("mood:activity_list")),
|
||||
"mood_mood_list": NavItem("Moods", reverse_lazy("mood:mood_list")),
|
||||
"mood_notification_list": NavItem("Notifications", reverse_lazy("mood:notification_list")),
|
||||
"mood_statistics": NavItem("Statistics", reverse_lazy("mood:statistics"))
|
||||
"mood_notification_list": NavItem(
|
||||
"Notifications", reverse_lazy("mood:notification_list")
|
||||
),
|
||||
"mood_statistics": NavItem("Statistics", reverse_lazy("mood:statistics")),
|
||||
}
|
||||
|
||||
for _, item in mood_items.items():
|
||||
mood_section.add_item(item)
|
||||
|
||||
NAV_SECTIONS = [mood_section]
|
||||
|
||||
# Dashboard sections
|
||||
|
||||
mood_section = DashboardSection("Moods", "mood/dashboard_section.html")
|
||||
|
||||
DASHBOARD_SECTIONS = [mood_section]
|
||||
|
|
94
mood/templates/mood/dashboard_section.html
Normal file
94
mood/templates/mood/dashboard_section.html
Normal file
|
@ -0,0 +1,94 @@
|
|||
{% load mood_stats %}
|
||||
|
||||
<!-- Mood calendar heatmap -->
|
||||
<div class="row">
|
||||
<div class="col-xl-12 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Mood Calendar</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mood-count-heatmap"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mood cards -->
|
||||
<div class="row">
|
||||
<!-- Status count -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-primary shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Status count (total)</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">{% total_moods %}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-book fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Current Streak Length -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-success shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Current Streak Length</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{% current_streak %} <sub>days</sub>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-calendar fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Average mood -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-info shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Average mood (weekly)</div>
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col-auto">
|
||||
{% average_mood_weekly as mood %} {% closest_mood mood as moodobj %}
|
||||
<div class="h5 mb-0 mr-3 font-weight-bold text-gray-800">
|
||||
{{ moodobj }} <sub><i>({{ mood|floatformat:2 }})</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="{{ moodobj.icon }} fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Most common activity -->
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-warning shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Most Common Activity (weekly)</div>
|
||||
{% most_common_activity_weekly as activity %}
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ activity.0 }}<sub><i>({{ activity.1 }})</i></sub>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="{{ activity.0.icon }} fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue