From 38843a996a45f5524087020d6b6d056719d46ed7 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 6 Jun 2025 10:47:11 +0200 Subject: [PATCH] add shop dashboard --- shop/admin.py | 115 +++++++++++- shop/templates/admin/shop/dashboard.html | 163 ++++++++++++++++++ .../admin/shop/order/change_list.html | 5 + 3 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 shop/templates/admin/shop/dashboard.html diff --git a/shop/admin.py b/shop/admin.py index bce87cf..f1f968f 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -4,12 +4,72 @@ from django.utils.html import format_html from django.urls import path from django.http import HttpResponseRedirect from django import forms +from django.db.models import Sum, Count, Avg +from datetime import datetime, timedelta +from django.utils import timezone from .models import ( Product, Color, Size, Order, OrderItem, GuestUser, Coupon, CouponUsage, OrderStatus, ShippingAddress ) +class ShopAdminSite(admin.AdminSite): + site_header = "Shop Administration" + site_title = "Shop Admin Portal" + index_title = "Welcome to Shop Administration" + + def index(self, request, extra_context=None): + """Custom admin index view with dashboard""" + # Calculate order statistics + order_status_data = [] + total_orders = Order.objects.count() + total_revenue = Order.objects.aggregate(Sum('total_price'))['total_price__sum'] or 0 + + # Get data for each status + for status_choice in OrderStatus.choices: + status_code, status_label = status_choice + orders_for_status = Order.objects.filter(status=status_code) + count = orders_for_status.count() + total_amount = orders_for_status.aggregate(Sum('total_price'))['total_price__sum'] or 0 + avg_order_value = orders_for_status.aggregate(Avg('total_price'))['total_price__avg'] or 0 + percentage = (count / total_orders * 100) if total_orders > 0 else 0 + + order_status_data.append({ + 'status': status_code, + 'label': status_label, + 'count': count, + 'total_amount': total_amount, + 'avg_order_value': avg_order_value, + 'percentage': percentage + }) + + # Recent activity calculations + now = timezone.now() + today = now.date() + week_ago = today - timedelta(days=7) + month_ago = today - timedelta(days=30) + + orders_today = Order.objects.filter(date_ordered__date=today).count() + orders_this_week = Order.objects.filter(date_ordered__date__gte=week_ago).count() + orders_this_month = Order.objects.filter(date_ordered__date__gte=month_ago).count() + orders_to_prepare = Order.objects.filter(status=OrderStatus.PAID).count() + + extra_context = extra_context or {} + extra_context.update({ + 'order_status_data': order_status_data, + 'total_orders': total_orders, + 'total_revenue': total_revenue, + 'orders_today': orders_today, + 'orders_this_week': orders_this_week, + 'orders_this_month': orders_this_month, + 'orders_to_prepare': orders_to_prepare, + }) + + return render(request, 'admin/shop/dashboard.html', extra_context) + +# Create an instance of the custom admin site +shop_admin_site = ShopAdminSite(name='shop_admin') + @admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display = ("title", "ordering_value", "price", "cut") @@ -131,6 +191,57 @@ class OrderAdmin(admin.ModelAdmin): }), ) + def dashboard_view(self, request): + """Dashboard view with order statistics""" + # Calculate order statistics + order_status_data = [] + total_orders = Order.objects.count() + total_revenue = Order.objects.aggregate(Sum('total_price'))['total_price__sum'] or 0 + + # Get data for each status + for status_choice in OrderStatus.choices: + status_code, status_label = status_choice + orders_for_status = Order.objects.filter(status=status_code) + count = orders_for_status.count() + total_amount = orders_for_status.aggregate(Sum('total_price'))['total_price__sum'] or 0 + avg_order_value = orders_for_status.aggregate(Avg('total_price'))['total_price__avg'] or 0 + percentage = (count / total_orders * 100) if total_orders > 0 else 0 + + order_status_data.append({ + 'status': status_code, + 'label': status_label, + 'count': count, + 'total_amount': total_amount, + 'avg_order_value': avg_order_value, + 'percentage': percentage + }) + + # Recent activity calculations + now = timezone.now() + today = now.date() + week_ago = today - timedelta(days=7) + month_ago = today - timedelta(days=30) + + orders_today = Order.objects.filter(date_ordered__date=today).count() + orders_this_week = Order.objects.filter(date_ordered__date__gte=week_ago).count() + orders_this_month = Order.objects.filter(date_ordered__date__gte=month_ago).count() + orders_to_prepare = Order.objects.filter(status=OrderStatus.PAID).count() + + context = { + 'title': 'Shop Dashboard', + 'app_label': 'shop', + 'opts': Order._meta, + 'order_status_data': order_status_data, + 'total_orders': total_orders, + 'total_revenue': total_revenue, + 'orders_today': orders_today, + 'orders_this_week': orders_this_week, + 'orders_this_month': orders_this_month, + 'orders_to_prepare': orders_to_prepare, + } + + return render(request, 'admin/shop/dashboard.html', context) + def changelist_view(self, request, extra_context=None): # If 'show_preparation' parameter is in the request, show the preparation view if 'show_preparation' in request.GET: @@ -194,9 +305,7 @@ class OrderAdmin(admin.ModelAdmin): def get_urls(self): urls = super().get_urls() custom_urls = [ - path('prepare-all-orders/', self.admin_site.admin_view(self.prepare_all_orders), name='prepare_all_orders'), - path('prepare-order//', self.admin_site.admin_view(self.prepare_order), name='prepare_order'), - path('cancel-and-refund-order//', self.admin_site.admin_view(self.cancel_and_refund_order), name='cancel_and_refund_order'), + path('dashboard/', self.admin_site.admin_view(self.dashboard_view), name='shop_order_dashboard'), ] return custom_urls + urls diff --git a/shop/templates/admin/shop/dashboard.html b/shop/templates/admin/shop/dashboard.html new file mode 100644 index 0000000..b8b3a5d --- /dev/null +++ b/shop/templates/admin/shop/dashboard.html @@ -0,0 +1,163 @@ +{% extends "admin/base_site.html" %} +{% load admin_urls %} + +{% block title %}Shop Dashboard{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +
+
+ + +
+

Orders by Status

+
+ {% for status_data in order_status_data %} +
+ {{ status_data.label }} +
+ + {{ status_data.count }} + + + €{{ status_data.total_amount|floatformat:2 }} + +
+
+ {% endfor %} +
+
+ + +
+

Total Summary

+
+
+
{{ total_orders }}
+
Total Orders
+
+
+
€{{ total_revenue|floatformat:2 }}
+
Total Revenue
+
+
+
+ + +
+

Recent Activity

+
+
+
{{ orders_today }}
+
Orders Today
+
+
+
{{ orders_this_week }}
+
Orders This Week
+
+
+
{{ orders_this_month }}
+
Orders This Month
+
+
+
+ + + +
+ + +
+

Status Breakdown

+
+ + + + + + + + + + + + {% for status_data in order_status_data %} + + + + + + + + {% endfor %} + +
StatusCountPercentageTotal ValueAvg Order Value
{{ status_data.label }} + + {{ status_data.count }} + + {{ status_data.percentage|floatformat:1 }}% + €{{ status_data.total_amount|floatformat:2 }} + + {% if status_data.count > 0 %} + €{{ status_data.avg_order_value|floatformat:2 }} + {% else %} + €0.00 + {% endif %} +
+
+
+
+ + +{% endblock %} diff --git a/shop/templates/admin/shop/order/change_list.html b/shop/templates/admin/shop/order/change_list.html index e3e7fdc..f5ccfbc 100644 --- a/shop/templates/admin/shop/order/change_list.html +++ b/shop/templates/admin/shop/order/change_list.html @@ -2,6 +2,11 @@ {% block object-tools %}