diff --git a/backend/core/settings.py b/backend/core/settings.py index 97c840d..77f47bc 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -131,7 +131,7 @@ ROOT_URLCONF = 'core.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [BASE_DIR / "templates"], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -226,6 +226,7 @@ UNFOLD = { "SITE_HEADER": os.getenv("SITE_HEADER"), "SITE_URL": DOMAIN, "SITE_SYMBOL": "shield_person", + "DASHBOARD_CALLBACK": "core.views.dashboard_callback", "SITE_FAVICONS": [ { "rel": "icon", diff --git a/backend/core/views.py b/backend/core/views.py new file mode 100644 index 0000000..ca70765 --- /dev/null +++ b/backend/core/views.py @@ -0,0 +1,190 @@ +import json +import random +from functools import lru_cache + +from django.contrib.humanize.templatetags.humanize import intcomma +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ +from django.views.generic import RedirectView, TemplateView +from unfold.views import UnfoldModelAdminViewMixin + +class HomeView(RedirectView): + pattern_name = "admin:index" + + + + +def dashboard_callback(request, context): + + context.update(random_data()) + return context + + +@lru_cache +def random_data(): + WEEKDAYS = [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun", + ] + + positive = [[1, random.randrange(8, 28)] for i in range(1, 28)] + negative = [[-1, -random.randrange(8, 28)] for i in range(1, 28)] + average = [r[1] - random.randint(3, 5) for r in positive] + performance_positive = [[1, random.randrange(8, 28)] for i in range(1, 28)] + performance_negative = [[-1, -random.randrange(8, 28)] for i in range(1, 28)] + + response = { + "navigation": [ + {"title": _("Dashboard"), "link": "/", "active": True}, + {"title": _("Products"), "link": "/admin/product/productmodel/"}, + {"title": _("Orders"), "link": "/admin/order/ordermodel/"}, + ], + "kpi": [ + { + "title": "Product A Performance", + "metric": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "footer": mark_safe( + f'+{intcomma(f"{random.uniform(1, 9):.02f}")}% progress from last week' + ), + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [{"data": average, "borderColor": "#9333ea"}], + } + ), + }, + { + "title": "Product B Performance", + "metric": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "footer": mark_safe( + f'+{intcomma(f"{random.uniform(1, 9):.02f}")}% progress from last week' + ), + }, + { + "title": "Product C Performance", + "metric": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "footer": mark_safe( + f'+{intcomma(f"{random.uniform(1, 9):.02f}")}% progress from last week' + ), + }, + ], + "progress": [ + { + "title": "🦆 Social marketing e-book", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦍 Freelancing tasks", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐋 Development coaching", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦑 Product consulting", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐨 Other income", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐶 Course sales", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🐻‍❄️ Ads revenue", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦩 Customer Retention Rate", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦊 Marketing ROI", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + { + "title": "🦁 Affiliate partnerships", + "description": f"${intcomma(f"{random.uniform(1000, 9999):.02f}")}", + "value": random.randint(10, 90), + }, + ], + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [ + { + "label": "Example 1", + "type": "line", + "data": average, + "borderColor": "var(--color-primary-500)", + }, + { + "label": "Example 2", + "data": positive, + "backgroundColor": "var(--color-primary-700)", + }, + { + "label": "Example 3", + "data": negative, + "backgroundColor": "var(--color-primary-300)", + }, + ], + } + ), + "performance": [ + { + "title": _("Last week revenue"), + "metric": "$1,234.56", + "footer": mark_safe( + '+3.14% progress from last week' + ), + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [ + { + "data": performance_positive, + "borderColor": "var(--color-primary-700)", + } + ], + } + ), + }, + { + "title": _("Last week expenses"), + "metric": "$1,234.56", + "footer": mark_safe( + '+3.14% progress from last week' + ), + "chart": json.dumps( + { + "labels": [WEEKDAYS[day % 7] for day in range(1, 28)], + "datasets": [ + { + "data": performance_negative, + "borderColor": "var(--color-primary-300)", + }, + ], + } + ), + }, + ], + } + return response diff --git a/backend/templates/admin/base_site.html b/backend/templates/admin/base_site.html new file mode 100644 index 0000000..f349bd7 --- /dev/null +++ b/backend/templates/admin/base_site.html @@ -0,0 +1,13 @@ +{% extends "admin/base.html" %} + +{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} + +{% block branding %} +

{{ site_header|default:_('Django administration') }}

+{% endblock %} + +{% block extrahead %} + {% if plausible_domain %} + + {% endif %} +{% endblock %} diff --git a/backend/templates/admin/index.html b/backend/templates/admin/index.html new file mode 100644 index 0000000..099c689 --- /dev/null +++ b/backend/templates/admin/index.html @@ -0,0 +1,93 @@ +{% extends 'admin/base.html' %} + +{% load i18n unfold %} + +{% block breadcrumbs %}{% endblock %} + +{% block title %} + {% trans 'Dashboard' %} | {{ site_title|default:_('Django site admin') }} +{% endblock %} + + + +{% block branding %} +

{{ site_header|default:_('Django administration') }}

+{% endblock %} + +{% block content %} + {% include "unfold/helpers/messages.html" %} + + {% component "unfold/components/container.html" %} +
+
+ {% component "unfold/components/navigation.html" with items=navigation %}{% endcomponent %} +
+ + {% include "formula/service.html" %} + +
+ {% for stats in kpi %} + {% component "unfold/components/card.html" with class="lg:w-1/3" label=_("Last 7 days") footer=stats.footer %} + {% component "unfold/components/text.html" %} + {{ stats.title }} + {% endcomponent %} + + {% component "unfold/components/title.html" %} + {{ stats.metric }} + {% endcomponent %} + {% endcomponent %} + {% endfor %} +
+ + + + {% component "unfold/components/card.html" with title=_("Product performance in last 28 days") %} + {% component "unfold/components/chart/bar.html" with data=chart height=320 %}{% endcomponent %} + {% endcomponent %} + +
+ {% component "unfold/components/card.html" with class="lg:w-1/2" title=_("The most trending products in last 2 weeks") %} + {% component "unfold/components/title.html" with class="mb-2" %} + $1,234,567.89 + {% endcomponent %} + + {% component "unfold/components/text.html" %} + {% blocktrans %} + Total revenue between 1 - 31 October. Increase +3.14% comparing to previous month 1 - 30 September. + {% endblocktrans %} + {% endcomponent %} + + {% component "unfold/components/separator.html" %}{% endcomponent %} + +
+ {% for metric in progress %} + {% component "unfold/components/progress.html" with title=metric.title description=metric.description value=metric.value %}{% endcomponent %} + {% endfor %} +
+ + + + {% endcomponent %} + + +
+ + + {% for stats in performance %} + {% component "unfold/components/card.html" %} + {% component "unfold/components/text.html" %} + {{ stats.title }} + {% endcomponent %} + + {% component "unfold/components/title.html" with class="mb-8" %} + {{ stats.metric }} + {% endcomponent %} + + {% component "unfold/components/chart/line.html" with data=stats.chart %}{% endcomponent %} + {% endcomponent %} + {% endfor %} +
+
+
+ {% endcomponent %} +{% endblock %} diff --git a/backend/templates/formula/service.html b/backend/templates/formula/service.html new file mode 100644 index 0000000..9020c6e --- /dev/null +++ b/backend/templates/formula/service.html @@ -0,0 +1,25 @@ +{% load unfold i18n %} + +
+
+

+ {% trans "Are you looking for a custom dashboard?" %} +

+ +

+ {% trans "Did you decide to start using Unfold in your application but you need help with integration? Feel free to get in touch for consulting, priority support on specific tickets or development services." %} +

+
+ +
+ {% component "unfold/components/flex.html" with class="flex-col gap-4 lg:flex-row" %} + {% component "unfold/components/button.html" with href="https://unfoldadmin.com/consulting/?utm_medium=referral&utm_source=formula" %} + {% trans "Book a call with Lukas" %} + {% endcomponent %} + + {% component "unfold/components/button.html" with href="https://unfoldadmin.com/docs" variant="default" %} + {% trans "I'm good with docs" %} + {% endcomponent %} + {% endcomponent %} +
+
\ No newline at end of file