schon/engine/core/templates/admin/index.html
Egor fureunoir Gorbunov be9940cdc0 Features: 1) Added arith template tags for sub and neg operations to support arithmetic in templates;
Fixes: 1) Replaced `add:-X` with `sub:X` in template logic for consistent arithmetic;

Extra: 1) Added `arith` to template load context; 2) Introduced `_to_float` helper for safe type conversion; 3) Used `suppress` to handle invalid conversions gracefully.
2025-11-17 15:59:39 +03:00

357 lines
No EOL
22 KiB
HTML

{% extends 'admin/base.html' %}
{% load i18n unfold static arith %}
{% block title %}
{% if subtitle %}
{{ subtitle }} |
{% endif %}
{{ title }} | {{ site_title|default:_('Django site admin') }}
{% endblock %}
{% block branding %}
{% include "unfold/helpers/site_branding.html" %}
{% endblock %}
{% block content %}
{% component "unfold/components/container.html" %}
<div class="flex flex-col min-h-screen">
{% component "unfold/components/title.html" %}
{% trans "Dashboard" %}
<br/>
{% endcomponent %}
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 mb-6">
{% component "unfold/components/card.html" %}
{% component "unfold/components/text.html" %}
{% trans "Revenue (gross, 30d)" %}
{% endcomponent %}
{% component "unfold/components/title.html" %}
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ revenue_gross_30|default:0 }}
{% endcomponent %}
{% endcomponent %}
{% component "unfold/components/card.html" %}
{% component "unfold/components/text.html" %}
{% trans "Revenue (net, 30d)" %}
{% endcomponent %}
{% component "unfold/components/title.html" %}
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ revenue_net_30|default:0 }}
{% endcomponent %}
{% endcomponent %}
{% component "unfold/components/card.html" %}
{% component "unfold/components/text.html" %}
{% trans "Returns (30d)" %}
{% endcomponent %}
{% component "unfold/components/title.html" %}
{% if currency_symbol %}{{ currency_symbol }}{% endif %}{{ returns_30|default:0 }}
{% endcomponent %}
{% endcomponent %}
{% component "unfold/components/card.html" %}
{% component "unfold/components/text.html" %}
{% trans "Processed orders (30d)" %}
{% endcomponent %}
{% component "unfold/components/title.html" %}
{{ processed_orders_30|default:0 }}
{% endcomponent %}
{% endcomponent %}
</div>
<div class="grid grid-cols-1 xl:grid-cols-3 gap-6 items-start">
{% with gross=revenue_gross_30|default:0 returns=returns_30|default:0 %}
{% with total=gross|add:returns %}
{% component "unfold/components/card.html" with class="xl:col-span-2" %}
{% component "unfold/components/title.html" %}
{% trans "Sales vs Returns (30d)" %}
{% endcomponent %}
{% if total and total > 0 %}
{% with net=revenue_net_30|default:0 %}
{% with tax_amt=gross|sub:net %}
{% with returns_capped=returns %}
{% if returns > gross %}
{% with returns_capped=gross %}{% endwith %}
{% endif %}
{% with tax_amt_pos=tax_amt %}
{% if tax_amt_pos < 0 %}
{% with tax_amt_pos=0 %}{% endwith %}
{% endif %}
{% with net_for_pie=gross|sub:tax_amt_pos|sub:returns_capped %}
{% if net_for_pie < 0 %}
{% with net_for_pie=0 %}{% endwith %}
{% endif %}
{# Degrees per slice #}
{% widthratio net_for_pie gross 360 as net_deg %}
{% widthratio tax_amt_pos gross 360 as tax_deg %}
{% widthratio returns_capped gross 360 as ret_deg %}
{% with net_end=net_deg %}
{% with tax_end=net_end|add:tax_deg %}
{% with ret_end=tax_end|add:ret_deg %}
<div class="flex flex-col sm:flex-row items-center gap-6">
<div class="relative w-40 h-40">
<div class="w-40 h-40 rounded-full"
style="background:
conic-gradient(
rgb(34,197,94) 0 {{ net_end }}deg,
rgb(249,115,22) {{ net_end }}deg {{ tax_end }}deg,
rgb(239,68,68) {{ tax_end }}deg 360deg
);">
</div>
</div>
<div>
<div class="flex items-center gap-2 mb-2">
<span class="inline-block w-3 h-3 rounded-sm"
style="background:rgb(34,197,94)"></span>
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Net" %}:</span>
<span class="font-semibold">{% if currency_symbol %}
{{ currency_symbol }}{% endif %}{{ net }}</span>
</div>
{% if tax_amt_pos > 0 %}
<div class="flex items-center gap-2 mb-2">
<span class="inline-block w-3 h-3 rounded-sm"
style="background:rgb(249,115,22)"></span>
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Taxes" %}:</span>
<span class="font-semibold">{% if currency_symbol %}
{{ currency_symbol }}{% endif %}{{ tax_amt_pos|floatformat:2 }}</span>
</div>
{% endif %}
<div class="flex items-center gap-2 mb-2">
<span class="inline-block w-3 h-3 rounded-sm"
style="background:rgb(239,68,68)"></span>
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Returns" %}:</span>
<span class="font-semibold">{% if currency_symbol %}
{{ currency_symbol }}{% endif %}{{ returns }}</span>
</div>
<div class="flex items-center gap-2">
<span class="inline-block w-3 h-3 rounded-sm"
style="background:linear-gradient(90deg, rgba(0,0,0,0.15), rgba(0,0,0,0.15))"></span>
<span class="text-sm text-gray-600 dark:text-gray-300">{% trans "Gross (pie total)" %}:</span>
<span class="font-semibold">{% if currency_symbol %}
{{ currency_symbol }}{% endif %}{{ gross }}</span>
</div>
</div>
</div>
{% endwith %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endwith %}
{% else %}
{% component "unfold/components/text.html" %}
{% trans "Not enough data for chart yet." %}
{% endcomponent %}
{% endif %}
{% endcomponent %}
{% endwith %}
{% endwith %}
{% component "unfold/components/card.html" %}
{% component "unfold/components/title.html" %}
{% trans "Quick Links" %}
{% endcomponent %}
{% if quick_links %}
{% component "unfold/components/navigation.html" with class="flex flex-col gap-1" items=quick_links %}
{% endcomponent %}
{% else %}
{% component "unfold/components/text.html" %}
{% trans "No links available." %}
{% endcomponent %}
{% endif %}
{% endcomponent %}
</div>
{% component "unfold/components/card.html" %}
{% component "unfold/components/title.html" %}
{% trans "Daily sales (30d)" %}
{% endcomponent %}
{% if daily_labels and daily_labels|length > 0 %}
<div class="w-full">
<canvas id="dailySalesChart" height="120"></canvas>
</div>
<script src="{% static 'js/chart.js' %}"></script>
{{ daily_labels|json_script:"daily-labels" }}
{{ daily_orders|json_script:"daily-orders" }}
{{ daily_gross|json_script:"daily-gross" }}
<script>
(function () {
try {
const labels = JSON.parse(document.getElementById('daily-labels').textContent);
const orders = JSON.parse(document.getElementById('daily-orders').textContent);
const gross = JSON.parse(document.getElementById('daily-gross').textContent);
const ctx = document.getElementById('dailySalesChart').getContext('2d');
const green = 'rgb(34,197,94)';
const blue = 'rgb(59,130,246)';
const currency = "{% if currency_symbol %}{{ currency_symbol }}{% endif %}";
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: '{{ _("Orders (FINISHED)") }}',
data: orders,
borderColor: green,
backgroundColor: green,
borderWidth: 2,
tension: 0.25,
pointRadius: 2,
yAxisID: 'yOrders',
},
{
label: '{{ _("Gross revenue") }}',
data: gross,
borderColor: blue,
backgroundColor: blue,
borderWidth: 2,
tension: 0.25,
pointRadius: 2,
yAxisID: 'yRevenue',
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {mode: 'index', intersect: false},
plugins: {
legend: {display: true},
tooltip: {
callbacks: {
label: function (context) {
const label = context.dataset.label || '';
const val = context.parsed.y;
if (context.dataset.yAxisID === 'yRevenue') {
return `${label}: ${currency}${val}`;
}
return `${label}: ${val}`;
}
}
}
},
scales: {
x: {
grid: {display: false}
},
yOrders: {
type: 'linear',
position: 'left',
title: {display: true, text: '{{ _("Orders") }}'},
grid: {color: 'rgba(0,0,0,0.06)'},
ticks: {precision: 0}
},
yRevenue: {
type: 'linear',
position: 'right',
title: {display: true, text: '{{ _("Gross") }}'},
grid: {drawOnChartArea: false},
ticks: {
callback: function (value) {
return `${currency}${value}`;
}
}
}
}
}
});
} catch (e) {
console && console.warn && console.warn('Chart init failed', e);
}
})();
</script>
{% else %}
{% component "unfold/components/text.html" %}
{% trans "Not enough data for chart yet." %}
{% endcomponent %}
{% endif %}
{% endcomponent %}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6">
{% component "unfold/components/card.html" %}
{% component "unfold/components/title.html" %}
{% trans "Most wished product" %}
{% endcomponent %}
{% if most_wished_products %}
<ul class="flex flex-col divide-y divide-gray-200 dark:divide-base-700/50">
{% for p in most_wished_products %}
<li class="py-2 first:pt-0 last:pb-0">
<a href="{{ p.admin_url }}" class="flex items-center gap-4">
{% if p.image %}
<img src="{{ p.image }}" alt="{{ p.name }}"
class="w-12 h-12 object-cover rounded"/>
{% endif %}
<span class="font-medium flex-1 truncate">{{ p.name }}</span>
<span class="text-xs px-2 py-0.5 rounded bg-base-700/[.06] dark:bg-white/[.06] text-gray-700 dark:text-gray-200">{{ p.count }}</span>
</a>
</li>
{% endfor %}
</ul>
{% elif most_wished_product %}
<a href="{{ most_wished_product.admin_url }}" class="flex items-center gap-4">
{% if most_wished_product.image %}
<img src="{{ most_wished_product.image }}" alt="{{ most_wished_product.name }}"
class="w-16 h-16 object-cover rounded"/>
{% endif %}
<span class="font-medium">{{ most_wished_product.name }}</span>
</a>
{% else %}
{% component "unfold/components/text.html" %}
{% trans "No data yet." %}
{% endcomponent %}
{% endif %}
{% endcomponent %}
{% component "unfold/components/card.html" %}
{% component "unfold/components/title.html" %}
{% trans "Most popular product" %}
{% endcomponent %}
{% if most_popular_products %}
<ul class="flex flex-col divide-y divide-gray-200 dark:divide-base-700/50">
{% for p in most_popular_products %}
<li class="py-2 first:pt-0 last:pb-0">
<a href="{{ p.admin_url }}" class="flex items-center gap-4">
{% if p.image %}
<img src="{{ p.image }}" alt="{{ p.name }}"
class="w-12 h-12 object-cover rounded"/>
{% endif %}
<span class="font-medium flex-1 truncate">{{ p.name }}</span>
<span class="text-xs px-2 py-0.5 rounded bg-base-700/[.06] dark:bg-white/[.06] text-gray-700 dark:text-gray-200">{{ p.count }}</span>
</a>
</li>
{% endfor %}
</ul>
{% elif most_popular_product %}
<a href="{{ most_popular_product.admin_url }}" class="flex items-center gap-4">
{% if most_popular_product.image %}
<img src="{{ most_popular_product.image }}" alt="{{ most_popular_product.name }}"
class="w-16 h-16 object-cover rounded"/>
{% endif %}
<span class="font-medium">{{ most_popular_product.name }}</span>
</a>
{% else %}
{% component "unfold/components/text.html" %}
{% trans "No data yet." %}
{% endcomponent %}
{% endif %}
{% endcomponent %}
</div>
{% component "unfold/components/separator.html" %}
{% endcomponent %}
<div class="mt-4 mt-auto">
{% component "unfold/components/text.html" with class="text-center text-xs text-gray-500 dark:text-gray-400" %}
eVibes {{ evibes_version }} · Wiseless Team
{% endcomponent %}
</div>
</div>
{% endcomponent %}
{% endblock %}