aging inventory logics, user list page and password set page ui change

This commit is contained in:
Faheedkhan 2025-08-20 12:53:49 +03:00
parent d0ac8eba9a
commit 589417ca6d
22 changed files with 1806 additions and 2983 deletions

View File

@ -42,14 +42,16 @@ urlpatterns = [
),
#dashboards
path("dashboards/dealer/", views.DealerDashboard,name="dealer_dashboard"),
path( "dashboards/manager/", views.ManagerDashboard.as_view(),name="manager_dashboard"),
path("dashboards/sales/", views.SalesDashboard.as_view(), name="sales_dashboard"),
path("dashboards/accountant/", views.AccountantDashboard.as_view(), name="accountant_dashboard"),
path("dashboards/inventory/", views.InventoryDashboard.as_view(), name="inventory_dashboard"),
#dashboards for manager, dealer, inventory and accounatant
path("dashboards/<slug:dealer_slug>/general/", views.general_dashboard,name="general_dashboard"),
#dashboard for sales
path("dashboards/<slug:dealer_slug>/sales/", views.sales_dashboard, name="sales_dashboard"),
path(
"<slug:dealer_slug>/cars/aging-inventory/list",
views.aging_inventory_list_view,
name="aging_inventory_list",
),
path("cars/inventory/table/", views.CarListViewTable.as_view(), name="car_table"),
path("export/format/", TableExport.export, name="export"),
# Dealer URLs

View File

@ -52,6 +52,7 @@ from django.forms import CharField, HiddenInput, ValidationError
from django.shortcuts import HttpResponse
from django.db.models import Sum, F, Count
from django.db.models.functions import ExtractMonth
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib.auth.models import User, Group
from django.db.models import Value
@ -396,522 +397,426 @@ class TestView(TemplateView):
template_name = "inventory/cars_list_api.html"
# class DealerDashboard(LoginRequiredMixin, TemplateView):
# """
# ManagerDashboard class is a view handling the dashboard for a manager.
# Provides functionality to manage and view various statistics and data specific
# to the dealer associated with the authenticated manager. It uses a specific
# template and ensures authentication before granting access. The class
# aggregates data about cars, leads, financial statistics, and other related
# business information for display in the manager's dashboard.
# :ivar template_name: Path to the template used for rendering the manager's dashboard.
# :type template_name: str
# """
# template_name = "dashboards/dealer_dashbaord.html"
# # def dispatch(self, request, *args, **kwargs):
# # if not request.user.is_authenticated:
# # return redirect("welcome")
# # if not getattr(request.user, "dealer", False):
# # return HttpResponseForbidden(
# # "You are not authorized to view this dashboard."
# # )
# # return super().dispatch(request, *args, **kwargs)
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# dealer = get_user_type(self.request)
# entity = dealer.entity
# qs = models.Car.objects.filter(dealer=dealer)
# total_cars = qs.count()
# # stats = models.CarFinance.objects.filter(car__dealer=dealer).aggregate(
# # total_cost_price=Sum("cost_price"),
# # total_selling_price=Sum("selling_price"),
# # )
# # total_cost_price = stats["total_cost_price"] or 0
# # total_selling_price = stats["total_selling_price"] or 0
# # total_profit = total_selling_price - total_cost_price
# # new_leads = models.Lead.objects.filter(
# # dealer=dealer, status=models.Status.NEW
# # ).count()
# # pending_leads = models.Lead.objects.filter(
# # dealer=dealer, status=models.Status.CONTACTED
# # ).count()
# # canceled_leads = models.Lead.objects.filter(
# # dealer=dealer, status=models.Status.UNQUALIFIED
# # ).count()
# # car_status_qs = qs.values("status").annotate(count=Count("status"))
# # car_status = {status: count for status, count in car_status_qs}
# # car_by_make_qs = qs.values("id_car_make__name").annotate(count=Count("id"))
# # car_by_make = list(car_by_make_qs)
# # context["dealer"] = dealer
# # context["total_activity"] = models.UserActivityLog.objects.filter(
# # user=dealer.user
# # ).count()
# # context["total_cars"] = total_cars
# # context["total_reservations"] = models.CarReservation.objects.filter(
# # reserved_until__gte=timezone.now()
# # ).count()
# # context["total_cost_price"] = total_cost_price
# # context["total_selling_price"] = total_selling_price
# # context["total_profit"] = total_profit
# # context["new_leads"] = new_leads
# # context["pending_leads"] = pending_leads
# # context["canceled_leads"] = canceled_leads
# # context["car"] = json.dumps(car_by_make)
# # context["available_cars"] =qs.filter(status='available').count()
# # context["sold_cars"] = qs.filter(status='sold').count()
# # context["reserved_cars"] = qs.filter(status='reserved').count()
# # context["hold_cars"] =qs.filter(status='hold').count()
# # context["damaged_cars"] = qs.filter(status='damaged').count()
# # context["transfer_cars"] = qs.filter(status='transfer').count()
# # context["present_inventory_count"]=total_cars-context["sold_cars"]-context["damaged_cars"]
# # cars_sold=qs.filter(status='sold')
# # cars_sold.aggregate(total_inventory_value=sum())
# context["customers"] = entity.get_customers().count()
# context["staff"] = models.Staff.objects.filter(dealer=dealer).count()
# context["total_leads"] = models.Lead.objects.filter(dealer=dealer).count()
# context["invoices"] = entity.get_invoices().count()
# context["estimates"] = entity.get_estimates().count()
# context["purchase_orders"] = entity.get_purchase_orders().count()
# return context
from django.db.models import Sum, F, Count
from django.db.models.functions import ExtractMonth
from django.utils import timezone
from datetime import timedelta
def DealerDashboard(request):
dealer = request.dealer
# This part is correct as is, it filters cars by dealer and sold status
cars_sold = models.Car.objects.filter(dealer=dealer, status='sold')
@login_required
def general_dashboard(request,dealer_slug):
"""
Renders the dealer dashboard with key performance indicators and chart data.
"""
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
vat = models.VatRate.objects.filter(dealer=dealer,is_active=True).first()
VAT_RATE=vat.rate
today_local = timezone.localdate()
# ----------------------------------------------------
# 1. Date Filtering
# ----------------------------------------------------
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
# This part needs to be made timezone-aware and robust
if not start_date_str:
start_date = today_local - timedelta(days=30)
else:
if start_date_str and end_date_str:
start_date = timezone.datetime.strptime(start_date_str, '%Y-%m-%d').date()
if not end_date_str:
end_date = today_local
else:
end_date = timezone.datetime.strptime(end_date_str, '%Y-%m-%d').date()
# Existing inventory calculations
active_cars = models.Car.objects.filter(status__in=['available', 'reserved', 'damaged'])
else:
start_date = today_local - timedelta(days=30)
end_date = today_local
# ----------------------------------------------------
# 2. Inventory KPIs
# ----------------------------------------------------
active_cars = models.Car.objects.filter(dealer=dealer).exclude(status='sold')
total_cars_in_inventory = active_cars.count()
new_cars_qs= active_cars.filter(stock_type='new')
used_cars_qs=active_cars.filter(stock_type='old')
total_inventory_value = active_cars.aggregate(total=Sum('cost_price'))['total'] or 0
new_cars_qs = active_cars.filter(stock_type='new')
total_new_cars_in_inventory = new_cars_qs.count()
new_car_value = new_cars_qs.aggregate(total=Sum('cost_price'))['total'] or 0
used_cars_qs = active_cars.filter(stock_type='used')
total_used_cars_in_inventory = used_cars_qs.count()
total_inventory_value = sum([car.cost_price for car in active_cars])
new_car_value=sum([car.cost_price for car in new_cars_qs])
used_car_value=sum([car.cost_price for car in used_cars_qs])
# Define the aging threshold (e.g., 90 days)
used_car_value = used_cars_qs.aggregate(total=Sum('cost_price'))['total'] or 0
aging_threshold_days = 60
# Efficiently query the database for aging cars
aging_inventory = active_cars.filter(created_at__lte=today_local - timedelta(days=aging_threshold_days))
# You can get the count for display on the dashboard
aging_inventory_count = aging_inventory.count()
aging_inventory_count = active_cars.filter(receiving_date__date__lte=today_local - timedelta(days=aging_threshold_days)).count()
# The database query will automatically be handled in a timezone-aware manner
cars_sold_filtered = cars_sold.filter(
# ----------------------------------------------------
# 3. Sales KPIs (filtered by date)
# ----------------------------------------------------
cars_sold_filtered = models.Car.objects.filter(
dealer=dealer,
status='sold',
sold_date__date__gte=start_date,
sold_date__date__lte=end_date
)
# Calculate summary data for the filtered results
# General sales KPIs
total_cars_sold = cars_sold_filtered.count()
new_cars_sold=cars_sold_filtered.filter(stock_type='new')
used_cars_sold=cars_sold_filtered.filter(stock_type='used')
total_new_cars_sold=new_cars_sold.count()
total_used_cars_sold=used_cars_sold.count()
total_cost_of_cars_sold = cars_sold_filtered.aggregate(total=Sum('cost_price'))['total'] or 0
total_revenue_from_cars = cars_sold_filtered.aggregate(
total=Sum(F('selling_price') - F('discount_amount'))
)['total'] or 0
# Calculations for all cars
total_cost_of_cars_sold = sum([car.cost_price for car in cars_sold_filtered])
total_revenue_from_cars = sum([car.final_price for car in cars_sold_filtered])
# Correct calculation for VAT based on the selling price
total_vat_collected_from_cars = cars_sold_filtered.aggregate(
total=Sum(F('selling_price') * VAT_RATE)
)['total'] or 0
net_profit_from_cars = total_revenue_from_cars - total_cost_of_cars_sold
total_vat_collected_from_cars = sum([car.vat_amount for car in cars_sold_filtered])
total_discount = cars_sold_filtered.aggregate(total=Sum('discount_amount'))['total'] or 0
# Calculations specifically for used cars
total_cost_of_used_cars_sold = sum([car.cost_price for car in used_cars_sold])
total_revenue_from_used_cars = sum([car.final_price for car in used_cars_sold])
net_profit_from_used_cars = total_revenue_from_used_cars - total_cost_of_used_cars_sold
total_vat_collected_from_used_cars = sum([car.vat_amount for car in used_cars_sold])
# Calculations specifically for new cars
total_cost_of_new_cars_sold = sum([car.cost_price for car in new_cars_sold])
total_revenue_from_new_cars = sum([car.final_price for car in new_cars_sold])
# Sales breakdown by type
new_cars_sold = cars_sold_filtered.filter(stock_type='new')
total_new_cars_sold = new_cars_sold.count()
total_cost_of_new_cars_sold = new_cars_sold.aggregate(total=Sum('cost_price'))['total'] or 0
total_revenue_from_new_cars = new_cars_sold.aggregate(
total=Sum(F('selling_price') - F('discount_amount'))
)['total'] or 0
net_profit_from_new_cars = total_revenue_from_new_cars - total_cost_of_new_cars_sold
total_vat_collected_from_new_cars = sum([car.vat_amount for car in new_cars_sold])
total_vat_collected_from_new_cars = new_cars_sold.aggregate(
total=Sum(F('selling_price') * VAT_RATE)
)['total'] or 0
used_cars_sold = cars_sold_filtered.filter(stock_type='used')
total_used_cars_sold = used_cars_sold.count()
total_cost_of_used_cars_sold = used_cars_sold.aggregate(total=Sum('cost_price'))['total'] or 0
total_revenue_from_used_cars = used_cars_sold.aggregate(
total=Sum(F('selling_price') - F('discount_amount'))
)['total'] or 0
net_profit_from_used_cars = total_revenue_from_used_cars - total_cost_of_used_cars_sold
total_vat_collected_from_used_cars = used_cars_sold.aggregate(
total=Sum(F('selling_price') * VAT_RATE)
)['total'] or 0
# Service & Overall KPIs
total_revenue_from_services = sum([car.get_additional_services()['total'] for car in cars_sold_filtered])
total_vat_collected_from_services = sum([car.get_additional_services()['services_vat'] for car in cars_sold_filtered])
total_vat_collected = total_vat_collected_from_cars + total_vat_collected_from_services
total_revenue_generated = total_revenue_from_cars + total_revenue_from_services
total_discount = sum([car.discount for car in cars_sold_filtered])
# Filter for expenses
expenses = ItemModel.objects.filter(entity__admin__dealer=dealer, item_role='expense')
total_expenses = sum([expense.default_amount for expense in expenses])
gross_profit = net_profit_from_cars - total_expenses
# ----------------------------------------------------
# NEW LOGIC FOR CHARTS - Aggregating data by month
# ----------------------------------------------------
# Group cars sold by month and aggregate totals
expenses = models.ItemModel.objects.filter(entity__admin__dealer=dealer, item_role='expense')
total_expenses = expenses.aggregate(total=Sum('default_amount'))['total'] or 0
gross_profit = net_profit_from_cars - total_expenses
# ----------------------------------------------------
# 4. Chart Data Aggregation
# ----------------------------------------------------
monthly_sales_data = cars_sold_filtered.annotate(
month=ExtractMonth('sold_date')
).values('month').annotate(
total_cars=Count('pk'),
total_revenue=Sum(F('marked_price') - F('discount_amount')), # Corrected for marked price and discount_amount
total_profit=Sum(F('marked_price') - F('discount_amount') - F('cost_price')) # Corrected profit calculation
total_revenue=Sum(F('selling_price') - F('discount_amount')),
total_profit=Sum(F('selling_price') - F('discount_amount') - F('cost_price'))
).order_by('month')
# Initialize lists for chart data
monthly_cars_sold = [0] * 12
monthly_revenue = [0] * 12
monthly_net_profit = [0] * 12
# Populate the lists from the queryset results
for data in monthly_sales_data:
month_index = data['month'] - 1 # Months are 1-12, so we need to adjust to 0-11
month_index = data['month'] - 1
monthly_cars_sold[month_index] = data['total_cars']
monthly_revenue[month_index] = data['total_revenue']
monthly_net_profit[month_index] = data['total_profit']
# the monthly revenue is of this form: [0, 0, 0, 0, 0, 0, 0, Decimal('21000'), 0, 0, 0, 0] so javascript cannot recognise Decimale('21000') as no.
monthly_revenue[month_index] = float(data['total_revenue']) if data['total_revenue'] else 0
monthly_net_profit[month_index] = float(data['total_profit']) if data['total_profit'] else 0
monthly_cars_sold_json = json.dumps(monthly_cars_sold)
monthly_revenue_json = json.dumps(monthly_revenue)
monthly_net_profit_json = json.dumps(monthly_net_profit)
# ----------------------------------------------------
# Sales by MAKE
# ----------------------------------------------------
sales_by_make_data = cars_sold_filtered.values('id_car_make__name').annotate(
car_count=Count('id_car_make__name')
).order_by('-car_count')
sales_by_make_labels = [data['id_car_make__name'] for data in sales_by_make_data]
sales_by_make_counts = [data['car_count'] for data in sales_by_make_data]
# NEW: Convert the lists to JSON strings
monthly_cars_sold_json = json.dumps(monthly_cars_sold)
monthly_revenue_json = json.dumps([float(x) for x in monthly_revenue]) # Convert Decimal to float
monthly_net_profit_json = json.dumps([float(x) for x in monthly_net_profit])
# ----------------------------------------------------
# DATA FOR CAR SALES BY MODELS (for the new interactive chart)
# ----------------------------------------------------
# Update the context dictionary with the new monthly data lists
# Get the selected make from the URL query parameter
selected_make_sales= request.GET.get('make_sold', None)
# Get a list of all unique makes for the dropdown
all_makes_sold = list(cars_sold_filtered.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name'))
if selected_make_sales:
# If a make is selected, filter the queryset
sales_data_by_model = cars_sold_filtered.filter(
id_car_make__name=selected_make_sales
).values('id_car_model__name').annotate(
count=Count('id_car_model__name')
).order_by('-count')
else:
# If no make is selected, pass an empty list or some default data
sales_data_by_model = []
# 1. Inventory by Make (Pie Chart)
inventory_by_make_data = active_cars.values('id_car_make__name').annotate(
car_count=Count('id_car_make__name')
).order_by('-car_count')
inventory_by_make_labels = [data['id_car_make__name'] for data in inventory_by_make_data]
inventory_by_make_counts = [data['car_count'] for data in inventory_by_make_data]
# 2. Inventory by Model (Bar Chart)
selected_make_inventory = request.GET.get('make_inventory', None)
# Get all unique makes in inventory for the dropdown
all_makes_inventory = list(active_cars.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name'))
if selected_make_inventory:
inventory_data_by_model = active_cars.filter(
id_car_make__name=selected_make_inventory
).values('id_car_model__name').annotate(
count=Count('id_car_model__name')
).order_by('-count')
else:
# Default data
inventory_data_by_model = []
context = {
'start_date': start_date,
'end_date': end_date,
'cars_sold': cars_sold_filtered,
'today': today_local,
# Inventory KPIs
'total_cars_in_inventory': total_cars_in_inventory,
'total_inventory_value': total_inventory_value,
'total_new_cars_in_inventory': total_new_cars_in_inventory,
'total_used_cars_in_inventory': total_used_cars_in_inventory,
'new_car_value': new_car_value,
'used_car_value': used_car_value,
'aging_inventory_count': aging_inventory_count,
# Sales KPIs
'total_cars_sold': total_cars_sold,
'total_cost_of_cars_sold': total_cost_of_cars_sold,
'total_revenue_from_cars': total_revenue_from_cars,
'net_profit_from_cars': net_profit_from_cars,
'total_vat_collected_from_cars': total_vat_collected_from_cars,
'total_discount_on_cars': total_discount,
# Sales by Type
'total_new_cars_sold': total_new_cars_sold,
'total_used_cars_sold': total_used_cars_sold,
'total_cost_of_new_cars_sold': total_cost_of_new_cars_sold,
'total_revenue_from_new_cars': total_revenue_from_new_cars,
'net_profit_from_new_cars': net_profit_from_new_cars,
'total_vat_collected_from_new_cars': total_vat_collected_from_new_cars,
'total_cost_of_used_cars_sold': total_cost_of_used_cars_sold,
'total_revenue_from_used_cars': total_revenue_from_used_cars,
'net_profit_from_used_cars': net_profit_from_used_cars,
'total_vat_collected_from_used_cars': total_vat_collected_from_used_cars,
# Services and Overall KPIs
'total_revenue_from_services': total_revenue_from_services,
'total_vat_collected_from_services': total_vat_collected_from_services,
'total_revenue_generated': total_revenue_generated,
'total_vat_collected': total_vat_collected,
'total_expenses': total_expenses,
"gross_profit": gross_profit,
'total_cars_in_inventory': total_cars_in_inventory,
'total_used_cars_in_inventory': total_used_cars_in_inventory,
'total_new_cars_in_inventory': total_new_cars_in_inventory,
'total_inventory_value': total_inventory_value,
'new_car_value':new_car_value,
'used_car_value':used_car_value,
'aging_inventory_count':aging_inventory_count,
'total_new_cars_sold':total_new_cars_sold,
'total_used_cars_sold':total_used_cars_sold,
'total_cost_of_used_cars_sold': total_cost_of_used_cars_sold,
'total_revenue_from_used_cars':total_revenue_from_used_cars,
'net_profit_from_used_cars':net_profit_from_used_cars,
'total_vat_collected_from_used_cars':total_vat_collected_from_used_cars,
'total_cost_of_new_cars_sold': total_cost_of_new_cars_sold,
'total_revenue_from_new_cars':total_revenue_from_new_cars,
'net_profit_from_new_cars':net_profit_from_new_cars,
'total_vat_collected_from_new_cars':total_vat_collected_from_new_cars,
'gross_profit': gross_profit,
# Chart Data
# New variables for charts
'monthly_cars_sold_json': monthly_cars_sold_json,
'monthly_revenue_json': monthly_revenue_json,
'monthly_net_profit_json': monthly_net_profit_json,
# Sales Chart Data
'sales_by_make_labels_json': json.dumps(sales_by_make_labels),
'sales_by_make_counts_json': json.dumps(sales_by_make_counts),
'all_makes_sold': all_makes_sold,
'selected_make_sales': selected_make_sales,
'sales_data_by_model_json': json.dumps(list(sales_data_by_model)),
# New Inventory Chart Data
'inventory_by_make_labels_json': json.dumps(inventory_by_make_labels),
'inventory_by_make_counts_json': json.dumps(inventory_by_make_counts),
'all_makes_inventory': all_makes_inventory,
'selected_make_inventory': selected_make_inventory,
'inventory_data_by_model_json': json.dumps(list(inventory_data_by_model)),
}
return render(request, 'dashboards/dealer_dashboard.html', context)
class ManagerDashboard(LoginRequiredMixin, TemplateView):
"""
ManagerDashboard class is a view handling the dashboard for a manager.
Provides functionality to manage and view various statistics and data specific
to the dealer associated with the authenticated manager. It uses a specific
template and ensures authentication before granting access. The class
aggregates data about cars, leads, financial statistics, and other related
business information for display in the manager's dashboard.
:ivar template_name: Path to the template used for rendering the manager's dashboard.
:type template_name: str
"""
template_name = "dashboards/manager_dashboard.html"
# def dispatch(self, request, *args, **kwargs):
# if not request.user.is_authenticated:
# return redirect("welcome")
# if not getattr(request.user, "dealer", False):
# return HttpResponseForbidden(
# "You are not authorized to view this dashboard."
# )
# return super().dispatch(request, *args, **kwargs)
return render(request, 'dashboards/general_dashboard.html', context)
@login_required
def sales_dashboard(request,dealer_slug):
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
today_local = timezone.localdate()
# ----------------------------------------------------
# 1. Date Filtering
# ----------------------------------------------------
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
if start_date_str and end_date_str:
start_date = timezone.datetime.strptime(start_date_str, '%Y-%m-%d').date()
end_date = timezone.datetime.strptime(end_date_str, '%Y-%m-%d').date()
else:
start_date = today_local - timedelta(days=30)
end_date = today_local
# Filter leads by date range and dealer
leads_filtered = models.Lead.objects.filter(
dealer=dealer,
created__date__gte=start_date,
created__date__lte=end_date
)
# ----------------------------------------------------
# 2. Lead Sources Chart Logic
# ----------------------------------------------------
# Group leads by source and count them
# This generates a list of dictionaries like [{'source': 'Showroom', 'count': 45}, ...]
lead_sources_data = leads_filtered.values('source').annotate(
count=Count('source')
).order_by('-count')
# Separate the labels and counts for the chart
lead_sources_labels = [item['source'] for item in lead_sources_data]
lead_sources_counts = [item['count'] for item in lead_sources_data]
# ----------------------------------------------------
# 2. Lead Funnel Chart Logic
# ----------------------------------------------------
opportunity_filtered = models.Opportunity.objects.filter(
dealer=dealer,
created__date__gte=start_date,
created__date__lte=end_date
)
opportunity_stage_data = opportunity_filtered.values('stage').annotate(
count=Count('stage')
).order_by('-count')
# Separate the labels and counts for the chart
opportunity_stage_labels = [item['stage'] for item in opportunity_stage_data ]
opportunity_stage_counts = [item['count'] for item in opportunity_stage_data ]
# 2. Inventory KPIs
# ----------------------------------------------------
active_cars = models.Car.objects.filter(dealer=dealer).exclude(status='sold')
total_cars_in_inventory = active_cars.count()
new_cars_qs = active_cars.filter(stock_type='new')
total_new_cars_in_inventory = new_cars_qs.count()
used_cars_qs = active_cars.filter(stock_type='used')
total_used_cars_in_inventory = used_cars_qs.count()
aging_threshold_days = 60
aging_inventory_count = active_cars.filter(receiving_date__date__lte=today_local - timedelta(days=aging_threshold_days)).count()
context = {
'start_date': start_date,
'end_date': end_date,
'lead_sources_labels_json': json.dumps(lead_sources_labels),
'lead_sources_counts_json': json.dumps(lead_sources_counts),
'opportunity_stage_labels_json': json.dumps(opportunity_stage_labels),
'opportunity_stage_counts_json': json.dumps(opportunity_stage_counts),
# Inventory KPIs
'total_cars_in_inventory': total_cars_in_inventory,
'total_new_cars_in_inventory': total_new_cars_in_inventory,
'total_used_cars_in_inventory': total_used_cars_in_inventory,
'aging_inventory_count': aging_inventory_count,
}
return render(request, 'dashboards/sales_dashboard.html', context)
class SalesDashboard(LoginRequiredMixin, TemplateView):
def aging_inventory_list_view(request, dealer_slug):
"""
SalesDashboard class provides a view for the sales dashboard.
This class is responsible for generating the context data required to
display various statistics and information on the sales dashboard. It
inherits from `LoginRequiredMixin` and `TemplateView`, ensuring only
authenticated users can access the view. The dashboard includes data
such as the total number of cars, reservations, sold percentages,
reserved percentages, and cars categorized by various statuses. The
purpose of this dashboard is to provide dealership staff an overview
of their inventory and related sales metrics.
:ivar template_name: The name of the HTML template used for rendering
the sales dashboard.
:type template_name: str
Renders a paginated list of aging inventory for a specific dealer, with filters.
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
today_local = timezone.localdate()
aging_threshold_days = 60
# Get filter parameters from the request
selected_make = request.GET.get('make')
selected_model = request.GET.get('model')
selected_series = request.GET.get('series') # Changed 'serie' to 'series' for consistency
selected_year = request.GET.get('year')
selected_stock_type = request.GET.get('stock_type')
template_name = "dashboards/sales_dashboard.html"
# Start with the base queryset for all aging cars.
aging_cars_queryset = models.Car.objects.filter(
dealer=dealer,
receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days)
).exclude(status='sold')
total_aging_inventory_value=aging_cars_queryset.aggregate(total=Sum('cost_price'))['total']
# Apply filters to the queryset if they exist. Chaining is fine here.
if selected_make:
aging_cars_queryset = aging_cars_queryset.filter(id_car_make__name=selected_make)
if selected_model:
aging_cars_queryset = aging_cars_queryset.filter(id_car_model__name=selected_model)
if selected_series:
aging_cars_queryset = aging_cars_queryset.filter(id_car_series__name=selected_series)
if selected_year:
aging_cars_queryset = aging_cars_queryset.filter(id_car_year__year=selected_year)
if selected_stock_type:
aging_cars_queryset = aging_cars_queryset.filter(stock_type=selected_stock_type)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
dealer = self.request.dealer
staff = getattr(self.request, "staff", None)
total_cars = models.Car.objects.filter(dealer=dealer).count()
total_reservations = models.CarReservation.objects.filter(
reserved_by=self.request.user, reserved_until__gte=timezone.now()
).count()
available_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.AVAILABLE
).count()
sold_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.SOLD
).count()
reserved_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.RESERVED
).count()
hold_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.HOLD
).count()
damaged_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.DAMAGED
).count()
transfer_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.TRANSFER
).count()
reserved_percentage = reserved_cars / total_cars * 100
sold_percentage = sold_cars / total_cars * 100
qs = (
models.Car.objects.values("id_car_make__name")
.annotate(count=Count("id"))
.order_by("id_car_make__name")
)
car_by_make = list(qs)
context["dealer"] = dealer
context["staff"] = staff
context["total_cars"] = total_cars
context["total_reservations"] = total_reservations
context["reserved_percentage"] = reserved_percentage
context["sold_percentage"] = sold_percentage
context["available_cars"] = available_cars
context["sold_cars"] = sold_cars
context["reserved_cars"] = reserved_cars
context["hold_cars"] = hold_cars
context["damaged_cars"] = damaged_cars
context["transfer_cars"] = transfer_cars
context["car"] = json.dumps(car_by_make)
return context
class InventoryDashboard(LoginRequiredMixin, TemplateView):
"""
SalesDashboard class provides a view for the sales dashboard.
This class is responsible for generating the context data required to
display various statistics and information on the sales dashboard. It
inherits from `LoginRequiredMixin` and `TemplateView`, ensuring only
authenticated users can access the view. The dashboard includes data
such as the total number of cars, reservations, sold percentages,
reserved percentages, and cars categorized by various statuses. The
purpose of this dashboard is to provide dealership staff an overview
of their inventory and related sales metrics.
:ivar template_name: The name of the HTML template used for rendering
the sales dashboard.
:type template_name: str
"""
template_name = "dashboards/inventory_dashboard.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
dealer = self.request.dealer
staff = getattr(self.request, "staff", None)
total_cars = models.Car.objects.filter(dealer=dealer).count()
total_reservations = models.CarReservation.objects.filter(
reserved_by=self.request.user, reserved_until__gte=timezone.now()
).count()
available_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.AVAILABLE
).count()
sold_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.SOLD
).count()
reserved_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.RESERVED
).count()
hold_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.HOLD
).count()
damaged_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.DAMAGED
).count()
transfer_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.TRANSFER
).count()
reserved_percentage = reserved_cars / total_cars * 100
sold_percentage = sold_cars / total_cars * 100
qs = (
models.Car.objects.values("id_car_make__name")
.annotate(count=Count("id"))
.order_by("id_car_make__name")
)
car_by_make = list(qs)
context["dealer"] = dealer
context["staff"] = staff
context["total_cars"] = total_cars
context["total_reservations"] = total_reservations
context["reserved_percentage"] = reserved_percentage
context["sold_percentage"] = sold_percentage
context["available_cars"] = available_cars
context["sold_cars"] = sold_cars
context["reserved_cars"] = reserved_cars
context["hold_cars"] = hold_cars
context["damaged_cars"] = damaged_cars
context["transfer_cars"] = transfer_cars
context["car"] = json.dumps(car_by_make)
return context
class AccountantDashboard(LoginRequiredMixin, TemplateView):
"""
SalesDashboard class provides a view for the sales dashboard.
This class is responsible for generating the context data required to
display various statistics and information on the sales dashboard. It
inherits from `LoginRequiredMixin` and `TemplateView`, ensuring only
authenticated users can access the view. The dashboard includes data
such as the total number of cars, reservations, sold percentages,
reserved percentages, and cars categorized by various statuses. The
purpose of this dashboard is to provide dealership staff an overview
of their inventory and related sales metrics.
:ivar template_name: The name of the HTML template used for rendering
the sales dashboard.
:type template_name: str
"""
template_name = "dashboards/accountant_dashboard.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
dealer = self.request.dealer
staff = getattr(self.request, "staff", None)
total_cars = models.Car.objects.filter(dealer=dealer).count()
total_reservations = models.CarReservation.objects.filter(
reserved_by=self.request.user, reserved_until__gte=timezone.now()
).count()
available_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.AVAILABLE
).count()
sold_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.SOLD
).count()
reserved_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.RESERVED
).count()
hold_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.HOLD
).count()
damaged_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.DAMAGED
).count()
transfer_cars = models.Car.objects.filter(
dealer=dealer, status=models.CarStatusChoices.TRANSFER
).count()
reserved_percentage = reserved_cars / total_cars * 100
sold_percentage = sold_cars / total_cars * 100
qs = (
models.Car.objects.values("id_car_make__name")
.annotate(count=Count("id"))
.order_by("id_car_make__name")
)
car_by_make = list(qs)
context["dealer"] = dealer
context["staff"] = staff
context["total_cars"] = total_cars
context["total_reservations"] = total_reservations
context["reserved_percentage"] = reserved_percentage
context["sold_percentage"] = sold_percentage
context["available_cars"] = available_cars
context["sold_cars"] = sold_cars
context["reserved_cars"] = reserved_cars
context["hold_cars"] = hold_cars
context["damaged_cars"] = damaged_cars
context["transfer_cars"] = transfer_cars
context["car"] = json.dumps(car_by_make)
return context
# Get distinct values for filter dropdowns based on the initial, unfiltered aging cars queryset.
# This ensures all possible filter options are always available.
aging_base_queryset = models.Car.objects.filter(
dealer=dealer,
receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days)
).exclude(status='sold')
all_makes = aging_base_queryset.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name')
all_models = aging_base_queryset.values_list('id_car_model__name', flat=True).distinct().order_by('id_car_model__name')
all_series = aging_base_queryset.values_list('id_car_serie__name', flat=True).distinct().order_by('id_car_serie__name')
all_stock_types = aging_base_queryset.values_list('stock_type', flat=True).distinct().order_by('stock_type')
all_years = aging_base_queryset.values_list('year', flat=True).distinct().order_by('-year')
#
# Set up pagination
paginator = Paginator(aging_cars_queryset, 10)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
# Iterate only on the cars for the current page to add the age attribute.
for car in page_obj.object_list:
car.age_in_days = (today_local - car.receiving_date.date()).days
context = {
"is_paginated": page_obj.has_other_pages,
"cars": page_obj.object_list,
'selected_make': selected_make,
'selected_model': selected_model,
'selected_series': selected_series, # Corrected variable name
'selected_year': selected_year,
'selected_stock_type': selected_stock_type,
'all_makes': all_makes,
'all_models': all_models,
'all_series': all_series,
'all_stock_types': all_stock_types,
'all_years': all_years,
'total_aging_inventory_value':total_aging_inventory_value
}
return render(request, 'dashboards/aging_inventory_list.html', context)
def terms_and_privacy(request):
return render(request, "terms_and_privacy.html")
@ -7134,14 +7039,14 @@ def send_lead_email(request, dealer_slug, slug, email_pk=None):
لقد أنشأنا ملفاً شخصياً لك في نظامنا لتتبع تفضيلاتك والسيارات التي تهتم بها. سنتواصل معك قريباً للمتابعة والإجابة على أي أسئلة أخرى قد تكون لديك.
في هذه الأثناء، لا تتردد في الاتصال بنا مباشرة على {lead.dealer.phone_number} أو زيارتنا مرة أخرى في أي وقت يناسبك.
في هذه الأثناء، لا تتردد في الاتصال بنا مباشرة أو زيارتنا مرة أخرى في أي وقت يناسبك.
نتطلع إلى مساعدتك في العثور على سيارتك القادمة!
تحياتي،
{lead.dealer.arabic_name}
{lead.dealer.address}
{lead.dealer.phone_number}
{lead.dealer.phone_number}
-----
Dear {lead.full_name},
@ -7222,7 +7127,7 @@ class OpportunityCreateView(
dealer = get_object_or_404(models.Dealer, slug=self.kwargs.get("dealer_slug"))
form = super().get_form(form_class)
form.fields["car"].queryset = models.Car.objects.filter(
dealer=dealer, status="available", finances__marked_price__gt=0
dealer=dealer, status="available", marked_price__gt=0
)
if self.request.is_dealer:
form.fields["lead"].queryset = models.Lead.objects.filter(
@ -7281,7 +7186,7 @@ class OpportunityUpdateView(
dealer = get_object_or_404(models.Dealer, slug=self.kwargs.get("dealer_slug"))
staff = getattr(self.request.user, "staff", None)
form.fields["car"].queryset = models.Car.objects.filter(
dealer=dealer, status="available", finances__marked_price__gt=0
dealer=dealer, status="available",marked_price__gt=0
)
form.fields["lead"].queryset = models.Lead.objects.filter(
dealer=dealer, staff=staff
@ -11154,13 +11059,6 @@ def purchase_report_csv_export(request,dealer_slug):
])
return response
# @login_required
# def car_sale_report_view(request,dealer_slug):
# dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
# cars_sold = models.Car.objects.filter(dealer=dealer,status='sold')
# current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
# context={'cars_sold':cars_sold,'current_time':current_time }
# return render(request,'ledger/reports/car_sale_report.html',context)
@login_required
@ -11188,15 +11086,6 @@ def car_sale_report_view(request, dealer_slug):
if selected_stock_type:
cars_sold = cars_sold.filter(stock_type=selected_stock_type)
# Get distinct values for filter dropdowns
makes = models.Car.objects.filter(dealer=dealer, status='sold').values_list('id_car_make__name', flat=True).distinct()
models_qs = models.Car.objects.filter(dealer=dealer, status='sold').values_list('id_car_model__name', flat=True).distinct()
series = models.Car.objects.filter(dealer=dealer, status='sold').values_list('id_car_serie__name', flat=True).distinct()
stock_types=models.Car.objects.filter(dealer=dealer, status='sold').values_list('stock_type', flat=True).distinct()
years = models.Car.objects.filter(dealer=dealer, status='sold').values_list('year', flat=True).distinct().order_by('-year')
# # Calculate summary data for the filtered results
total_cars_sold=cars_sold.count()
@ -11211,6 +11100,15 @@ def car_sale_report_view(request, dealer_slug):
current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
# Get distinct values for filter dropdowns
base_sold_cars_queryset = models.Car.objects.filter(dealer=dealer, status='sold')
makes =base_sold_cars_queryset.values_list('id_car_make__name', flat=True).distinct()
models_qs =base_sold_cars_queryset.values_list('id_car_model__name', flat=True).distinct()
series =base_sold_cars_queryset.values_list('id_car_serie__name', flat=True).distinct()
stock_types=base_sold_cars_queryset.values_list('stock_type', flat=True).distinct()
years = base_sold_cars_queryset.values_list('year', flat=True).distinct().order_by('-year')
context = {
'cars_sold': cars_sold,
'total_cars_sold':total_cars_sold,
@ -11238,7 +11136,6 @@ def car_sale_report_view(request, dealer_slug):
return render(request, 'ledger/reports/car_sale_report.html', context)
@login_required
def car_sale_report_csv_export(request, dealer_slug):
response = HttpResponse(content_type='text/csv')

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,218 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="main-content flex-grow-1 container-fluid mt-4 mb-3">
<div class="d-flex justify-content-between align-items-center mb-5 pb-3 border-bottom">
<h2 class="h3 fw-bold mb-0">Accountant Dashboard<i class="fas fa-chart-area text-primary ms-2"></i></h2>
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Last 30 Days
</button>
<ul class="dropdown-menu dropdown-menu-end shadow">
<li><a class="dropdown-item" href="#">Today</a></li>
<li><a class="dropdown-item" href="#">Last 7 Days</a></li>
<li><a class="dropdown-item" href="#">Last 90 Days</a></li>
</ul>
</div>
</div>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Revenue</p>
<h4 class="fw-bolder text-primary mb-3">$1.25M</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Net Profit</p>
<h4 class="fw-bolder text-success mb-3">$1.25M</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Gross Profit</p>
<h4 class="fw-bolder text-info mb-3">$1.25M</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Expense</p>
<h4 class="fw-bolder text-danger mb-3">$1.25M</h4>
</div>
<span class="badge bg-danger-subtle text-danger fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
</div>
<h4 class="h5 fw-bold mb-3 text-dark">Breakdown by Vehicle Type</h4>
<div class="row g-4 mb-5">
<div class="col-md-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-primary fw-bold mb-1">New Cars</p>
<h4 class="fw-bolder mb-0">$1.25M</h4>
<p class="text-muted small">Total Revenue</p>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-success fw-bold mb-1">Used Cars</p>
<h4 class="fw-bolder mb-0">$0.75M</h4>
<p class="text-muted small">Total Revenue</p>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-info fw-bold mb-1">New Cars</p>
<h4 class="fw-bolder mb-0">$0.25M</h4>
<p class="text-muted small">Net Profit</p>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-warning fw-bold mb-1">Used Cars</p>
<h4 class="fw-bolder mb-0">$0.15M</h4>
<p class="text-muted small">Net Profit</p>
</div>
</div>
</div>
<div class="row g-4">
<div class="col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Monthly Revenue & Profit</h5>
</div>
<div class="card-body" style="height: 400px;">
<canvas id="revenueProfitChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
{% endblock %}
{% block customJS %}
<script>
// Define a color palette that aligns with the Phoenix template
const primaryColor = '#7249b6'; // A vibrant purple
const secondaryColor = '#8193a6'; // A muted gray/blue
const successColor = '#00d074'; // A bright green
const dangerColor = '#e63757'; // A deep red
const ctx = document.getElementById('revenueProfitChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [
{
label: 'Monthly Revenue',
data: [120000, 150000, 130000, 180000, 200000, 175000, 190000, 220000, 210000, 250000, 240000, 280000],
borderColor: primaryColor,
backgroundColor: 'rgba(114, 73, 182, 0.1)',
tension: 0.4,
fill: true,
pointBackgroundColor: primaryColor,
pointRadius: 5,
pointHoverRadius: 8
},
{
label: 'Monthly Net Profit',
data: [25000, 35000, 28000, 40000, 45000, 38000, 42000, 50000, 48000, 55000, 52000, 60000],
borderColor: successColor,
backgroundColor: 'rgba(0, 208, 116, 0.1)',
tension: 0.4,
fill: true,
pointBackgroundColor: successColor,
pointRadius: 5,
pointHoverRadius: 8
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
labels: {
color: secondaryColor,
boxWidth: 20
}
},
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: 'white',
bodyColor: 'white',
padding: 10,
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(context.parsed.y);
}
return label;
}
}
}
},
scales: {
x: {
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
color: secondaryColor
},
border: {
color: secondaryColor
}
},
y: {
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
color: secondaryColor
},
border: {
color: secondaryColor
}
}
}
}
});
</script>
{% endblock %}

View File

@ -0,0 +1,124 @@
{% extends 'base.html' %}
{% load i18n %}
{% load tenhal_tag %}
{% block content %}
<div class="main-content flex-grow-1 container-fluid mt-4 mb-3">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 pb-3 border-bottom">
<h2 class="h3 fw-bold mb-3 mb-md-0">
{% trans "Aging Inventory" %}
<i class="fas fa-box-open text-danger ms-2"></i>
</h2>
<h4 class="text-muted mb-3 ">{% trans "Aging Inventory Total" %} :: <span class=" text-danger">{{total_aging_inventory_value}}<span class="icon-saudi_riyal"></span></span></h4>
<p class="text-muted mb-0">{% trans "Cars in inventory for more than 60 days." %}</p>
</div>
<form method="GET" class="d-flex flex-wrap align-items-center mb-4 g-3">
<div class="col-sm-6 col-md-2 me-2">
<label for="make-filter" class="form-label mb-0 small text-uppercase fw-bold">{% trans "Make:" %}</label>
<select class="form-select" name="make" id="make-filter">
<option value="">{% trans "All" %}</option>
{% for make in all_makes %}
<option value="{{ make }}" {% if make == selected_make %}selected{% endif %}>{{ make }}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-6 col-md-2 me-2">
<label for="model-filter" class="form-label mb-0 small text-uppercase fw-bold">{% trans "Model:" %}</label>
<select class="form-select" name="model" id="model-filter">
<option value="">{% trans "All" %}</option>
{% for model in all_models %}
<option value="{{ model }}" {% if model == selected_model %}selected{% endif %}>{{ model }}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-6 col-md-2 me-2">
<label for="series-filter" class="form-label mb-0 small text-uppercase fw-bold">{% trans "Series:" %}</label>
<select class="form-select" name="series" id="series-filter">
<option value="">{% trans "All" %}</option>
{% for series in all_series %}
<option value="{{ series }}" {% if series == selected_series %}selected{% endif %}>{{ series }}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-6 col-md-2 me-2">
<label for="year-filter" class="form-label mb-0 small text-uppercase fw-bold">{% trans "Year:" %}</label>
<select class="form-select" name="year" id="year-filter">
<option value="">{% trans "All" %}</option>
{% for year in all_years %}
<option value="{{ year }}" {% if year|stringformat:"s" == selected_year %}selected{% endif %}>{{ year }}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-6 col-md-2 me-2">
<label for="stock-type-filter" class="form-label mb-0 small text-uppercase fw-bold">{% trans "Stock Type:" %}</label>
<select class="form-select" name="stock_type" id="stock-type-filter">
<option value="">{% trans "All" %}</option>
{% for stock_type in all_stock_types %}
<option value="{{ stock_type }}" {% if stock_type == selected_stock_type %}selected{% endif %}>{{ stock_type|title }}</option>
{% endfor %}
</select>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mt-4">{% trans "Filter" %}</button>
</div>
</form>
{% if is_paginated %}
<div class="d-flex justify-content-between mb-4">
<span class="text-muted">{% trans "Page" %} {{ page_obj.number }} {% trans "of" %} {{ page_obj.paginator.num_pages }}</span>
<span class="text-muted">{% trans "Total Aging Cars:" %} {{ page_obj.paginator.count }}</span>
</div>
{% endif %}
{% if cars %}
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{% for car in cars %}
<div class="col">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column">
<h5 class="card-title text-danger fw-bold">
<img src="{{car.logo}}" width="40" height="40" class="">&nbsp;&nbsp;{{ car.id_car_make.name }}&nbsp;&nbsp;{{ car.id_car_model.name }}&nbsp;&nbsp;{{ car.id_car_serie.name }}&nbsp;&nbsp;{{ car.year}}
</h5>
<p class="card-text text-muted mb-2">
<strong>{% trans "VIN:" %}</strong> {{ car.vin }}
</p>
<p class="card-text mb-2">
<strong>{% trans "Age:" %}</strong>
<span class="badge bg-danger">{{ car.age_in_days }} {% trans "days" %}</span>
</p>
<p class="card-text mb-2">
<strong>{% trans "Acquisition Date:" %}</strong> {{ car.receiving_date|date:"F j, Y" }}
</p>
<div class="mt-auto pt-3 border-top">
<a href="{% url 'car_detail' request.dealer.slug car.slug %}" class="btn btn-outline-primary btn-sm w-100">
{% trans "View Details" %}
</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="alert alert-success d-flex align-items-center" role="alert">
<i class="fas fa-check-circle me-2"></i>
<div>
{% trans "Excellent! There are no cars in the aging inventory at the moment." %}
</div>
</div>
{% endif %}
<div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% if is_paginated %}
{% include 'partials/pagination.html' %}
{% endif %}
</div>
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,109 @@
{% load i18n %}
{% if request.is_dealer or request.is_manager or request.is_accountant %}
<h3 class="fw-bold mb-3">
{% blocktrans with start_date=start_date|date:"F j, Y" end_date=end_date|date:"F j, Y" %}
Monthly Performance Trends ({{ start_date }} - {{ end_date }})
{% endblocktrans %}
</h3>
<div class="row g-4 mb-5">
<div class="col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">{% trans "Monthly Revenue & Profit" %}</h5>
</div>
<div class="card-body" style="height: 400px;">
<canvas id="revenueProfitChart"></canvas>
</div>
</div>
</div>
<div class="col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">{% trans "Monthly Cars Sold" %}</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="CarsSoldByMonthChart"></canvas>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-5">
<div class="col-lg-6 col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">{% trans "Sales by Make" %}</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="salesByBrandChart"></canvas>
</div>
</div>
</div>
<div class="col-lg-6 col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0 d-flex justify-content-between align-items-center">
<h5 class="fw-bold mb-0 text-dark">{% trans "Models Sold" %}</h5>
<form method="GET" class="d-flex align-items-center">
<div class="form-group d-flex align-items-center me-2">
<label for="carMakeSelectSales" class="form-label mb-0 me-2">{% trans "Select Make:" %}</label>
<select id="carMakeSelectSales" class="form-select" name="make_sold">
<option value="">{% trans "All Makes" %}</option>
{% for make_sold in all_makes_sold %}
<option value="{{ make_sold }}" {% if make_sold == selected_make_sales %}selected{% endif %}>{{ make_sold }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">{% trans "Go" %}</button>
<input type="hidden" name="start_date" value="{{ start_date|date:'Y-m-d' }}">
<input type="hidden" name="end_date" value="{{ end_date|date:'Y-m-d' }}">
</form>
</div>
<div class="card-body" style="height: 400px;">
<canvas id="salesChartByModel"></canvas>
</div>
</div>
</div>
</div>
{% endif %}
{% if request.is_dealer or request.is_manager or request.is_inventory %}
<h3 class="fw-bold mb-3">{% trans "Inventory Trends" %}</h3>
<div class="row g-4 mb-5">
<div class="col-lg-6 col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">{% trans "Inventory by Make" %}</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="inventoryByMakeChart"></canvas>
</div>
</div>
</div>
<div class="col-lg-6 col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0 d-flex justify-content-between align-items-center">
<h5 class="fw-bold mb-0 text-dark">{% trans "Models in Inventory" %}</h5>
<form method="GET" class="d-flex align-items-center">
<div class="form-group d-flex align-items-center me-2">
<label for="carMakeSelectInventory" class="form-label mb-0 me-2">{% trans "Select Make:" %}</label>
<select id="carMakeSelectInventory" class="form-select" name="make_inventory">
<option value="">{% trans "All Makes" %}</option>
{% for make_inv in all_makes_inventory %}
<option value="{{ make_inv }}" {% if make_inv == selected_make_inventory %}selected{% endif %}>{{ make_inv }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">{% trans "Go" %}</button>
</form>
</div>
<div class="card-body" style="height: 400px;">
<canvas id="inventoryByModelChart"></canvas>
</div>
</div>
</div>
</div>
{% endif %}

View File

@ -1,508 +0,0 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block content %}
<div class="content">
<div class="row gy-3 mb-4 justify-content-between">
<div class="col-xxl-6">
<h2 class="mb-2 text-body-emphasis">CRM Dashboard</h2>
<h5 class="text-body-tertiary fw-semibold mb-4">Check your business growth in one place</h5>
<div class="row g-3 mb-3">
<div class="col-sm-6 col-md-4 col-xl-3 col-xxl-4">
<div class="card h-100">
<div class="card-body">
<div class="d-flex d-sm-block justify-content-between">
<div class="border-bottom-sm border-translucent mb-sm-4">
<div class="d-flex align-items-center">
<div class="d-flex align-items-center icon-wrapper-sm shadow-primary-100"
style="transform: rotate(-7.45deg)">
<span class="fa-solid fa-phone-alt text-primary fs-7 z-1 ms-2"></span>
</div>
<p class="text-body-tertiary fs-9 mb-0 ms-2 mt-3">Outgoing call</p>
</div>
<p class="text-primary mt-2 fs-6 fw-bold mb-0 mb-sm-4">
3 <span class="fs-8 text-body lh-lg">Leads Today</span>
</p>
</div>
<div class="d-flex flex-column justify-content-center flex-between-end d-sm-block text-end text-sm-start">
<span class="badge badge-phoenix badge-phoenix-success fs-10 mb-2">+24.5%</span>
<p class="mb-0 fs-9 text-body-tertiary">Than Yesterday</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-xl-3 col-xxl-4">
<div class="card h-100">
<div class="card-body">
<div class="d-flex d-sm-block justify-content-between">
<div class="border-bottom-sm border-translucent mb-sm-4">
<div class="d-flex align-items-center">
<div class="d-flex align-items-center icon-wrapper-sm shadow-info-100"
style="transform: rotate(-7.45deg)">
<span class="fa-solid fa-calendar text-info fs-7 z-1 ms-2"></span>
</div>
<p class="text-body-tertiary fs-9 mb-0 ms-2 mt-3">Outgoing meeting</p>
</div>
<p class="text-info mt-2 fs-6 fw-bold mb-0 mb-sm-4">
12 <span class="fs-8 text-body lh-lg">This Week</span>
</p>
</div>
<div class="d-flex flex-column justify-content-center flex-between-end d-sm-block text-end text-sm-start">
<span class="badge badge-phoenix badge-phoenix-warning fs-10 mb-2">+24.5%</span>
<p class="mb-0 fs-9 text-body-tertiary">Than last week</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4 col-xl-6 col-xxl-4 gy-5 gy-md-3">
<div class="border-bottom border-translucent">
<h5 class="pb-4 border-bottom border-translucent">Top 5 Lead Sources</h5>
<ul class="list-group list-group-flush">
<li class="list-group-item bg-transparent list-group-crm fw-bold text-body fs-9 py-2">
<div class="d-flex justify-content-between">
<span class="fw-normal fs-9 mx-1"> <span class="fw-bold">1.</span>None </span><span>(65)</span>
</div>
</li>
<li class="list-group-item bg-transparent list-group-crm fw-bold text-body fs-9 py-2">
<div class="d-flex justify-content-between">
<span class="fw-normal mx-1"><span class="fw-bold">2.</span>Online Store</span><span>(74)</span>
</div>
</li>
<li class="list-group-item bg-transparent list-group-crm fw-bold text-body fs-9 py-2">
<div class="d-flex justify-content-between">
<span class="fw-normal fs-9 mx-1"><span class="fw-bold">3.</span> Advertisement</span><span>(32)</span>
</div>
</li>
<li class="list-group-item bg-transparent list-group-crm fw-bold text-body fs-9 py-2">
<div class="d-flex justify-content-between">
<span class="fw-normal fs-9 mx-1"><span class="fw-bold">4.</span> Seminar Partner</span><span>(25)</span>
</div>
</li>
<li class="list-group-item bg-transparent list-group-crm fw-bold text-body fs-9 py-2">
<div class="d-flex justify-content-between">
<span class="fw-normal fs-9 mx-1"> <span class="fw-bold">5.</span> Partner</span><span>(23)</span>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="col-xxl-6 mb-6">
<h3>Contacts Created</h3>
<p class="text-body-tertiary mb-1">Payment received across all channels</p>
<div class="echart-contacts-created" style="min-height:270px; width:100%"></div>
</div>
<div class="col-12 col-xxl-6 mb-3 mb-sm-0">
<div class="row">
<div class="col-sm-7 col-md-8 col-xxl-8 mb-md-3 mb-lg-0">
<h3>New Contacts by Source</h3>
<p class="text-body-tertiary">Payment received across all channels</p>
<div class="row g-0">
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-bottom border-end border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-primary"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">Organic</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">80</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-bottom border-end-md-0 border-end-xl border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-success"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">Paid Search</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">65</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-bottom border-end border-end-md border-end-xl-0 border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-info"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">Direct</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">40</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-end-xl border-bottom border-bottom-xl-0 border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-info-light"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">Social</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">220</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-end border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-danger-lighter"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">Referrals</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">120</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-warning-light"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">Others</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">35</h3>
</div>
</div>
</div>
</div>
<div class="col-sm-5 col-md-4 col-xxl-4 my-3 my-sm-0">
<div class="position-relative d-flex flex-center mb-sm-4 mb-xl-0 echart-contact-by-source-row mt-sm-7 mt-lg-4 mt-xl-0">
<div class="echart-contact-by-source" style="min-height:245px;width:100%"></div>
<div class="position-absolute rounded-circle bg-primary-subtle top-50 start-50 translate-middle d-flex flex-center"
style="height:100px;
width:100px">
<h3 class="mb-0 text-primary-dark fw-bolder" data-label="data-label"></h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-xxl-6 mb-8">
<div class="mb-3">
<h3>New Users &amp; Leads</h3>
<p class="text-body-tertiary mb-0">Payment received across all channels</p>
</div>
<div class="row g-6">
<div class="col-md-6 mb-2 mb-sm-0">
<div class="d-flex align-items-center">
<span class="me-2 text-info"
data-feather="users"
style="min-height:24px;
width:24px"></span>
<h4 class="text-body-tertiary mb-0">
New Users :<span class="text-body-emphasis">42</span>
</h4>
<span class="badge badge-phoenix fs-10 badge-phoenix-success d-inline-flex align-items-center ms-2"><span class="badge-label d-inline-block lh-base">+24.5%</span><span class="ms-1 fa-solid fa-caret-up d-inline-block lh-1"></span></span>
</div>
<div class="pb-0 pt-4">
<div class="echarts-new-users" style="min-height:110px;width:100%;"></div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center">
<span class="me-2 text-primary"
data-feather="zap"
style="height:24px;
width:24px"></span>
<h4 class="text-body-tertiary mb-0">
New Leads :<span class="text-body-emphasis">45</span>
</h4>
<span class="badge badge-phoenix fs-10 badge-phoenix-success d-inline-flex align-items-center ms-2"><span class="badge-label d-inline-block lh-base">+30.5%</span><span class="ms-1 fa-solid fa-caret-up d-inline-block lh-1"></span></span>
</div>
<div class="pb-0 pt-4">
<div class="echarts-new-leads" style="min-height:110px;width:100%;"></div>
</div>
</div>
</div>
</div>
<div class="col-12 col-xxl-6">
<div class="row align-items-start justify-content-between mb-4 g-3">
<div class="col-auto">
<h3>Ad Clicks</h3>
<p class="text-body-tertiary lh-sm mb-0">Check effectiveness of your ads</p>
</div>
<div class="col-12 col-sm-4">
<select class="form-select form-select-sm" id="select-ad-clicks-month">
<option>Mar 1 - 31, 2022</option>
<option>April 1 - 30, 2022</option>
<option>May 1 - 31, 2022</option>
</select>
</div>
</div>
<div class="echart-add-clicks-chart" style="min-height:385px;width:100%"></div>
</div>
<div class="col-12 col-xxl-6 mb-6 gy-0 gy-xxl-3">
<div class="row align-items-start justify-content-between mb-4 g-3">
<div class="col-auto">
<h3>
Deal Forecast<span class="fw-semibold">- $90,303</span>
</h3>
<p class="text-body-tertiary mb-1">Show what you offer here</p>
</div>
<div class="col-12 col-sm-4">
<select class="form-select form-select-sm" id="select-ad-forcast-month">
<option>Mar 1 - 31, 2022</option>
<option>April 1 - 30, 2022</option>
<option>May 1 - 31, 2022</option>
</select>
</div>
</div>
<div class="w-100">
<div class="d-flex flex-start">
<p class="mb-2 text-body-tertiary fw-semibold fs-9"
style="width: 20.72%">$21.0k</p>
<p class="mb-2 text-body-tertiary fw-semibold fs-9"
style="width: 35.76%">$3.4k</p>
<p class="mb-2 text-body-tertiary fw-semibold fs-9"
style="width: 25.38%">$15.1k</p>
<p class="mb-2 text-body-tertiary fw-semibold fs-9"
style="width: 25.14%">$4.6k</p>
</div>
<div class="progress mb-3 rounded-3" style="height: 10px;">
<div class="progress-bar border-end border-2 bg-primary-dark"
role="progressbar"
style="width: 20.72%"
aria-valuenow="20.72"
aria-valuemin="0"
aria-valuemax="100"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Appointment"></div>
<div class="progress-bar border-end border-2"
role="progressbar"
style="width: 35.76%"
aria-valuenow="35.76"
aria-valuemin="0"
aria-valuemax="100"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Qualified"></div>
<div class="progress-bar bg-success border-end border-2"
role="progressbar"
style="width: 25.38%"
aria-valuenow="25.38"
aria-valuemin="0"
aria-valuemax="100"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Closed Won"></div>
<div class="progress-bar bg-info"
role="progressbar"
style="width: 25.14%"
aria-valuenow="25.14"
aria-valuemin="0"
aria-valuemax="100"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Contact Sent"></div>
</div>
</div>
<h4 class="mt-4 mb-3">Deal Forecast by Owner</h4>
<div class="border-top border-bottom-0"
id="dealForecastTable"
data-list='{"valueNames":["contact","appointment","qualified","closed-won","contact-sent"],"page":5}'>
<div class="table-responsive scrollbar">
<table class="table fs-9 mb-0">
<thead>
<tr>
<th class="sort border-end border-translucent white-space-nowrap align-middle ps-0 text-uppercase text-body-tertiary"
scope="col"
data-sort="contact"
style="width:15%;
min-width:100px">Contact</th>
<th class="sort border-end border-translucent align-middle text-end px-3 text-uppercase text-body-tertiary"
scope="col"
data-sort="appointment"
style="width:15%;
min-width:95px">
<div class="d-inline-flex flex-center">
<span class="fa-solid fa-square fs-11 text-primary me-2"
data-fa-transform="up-2"></span><span class="mb-0 fs-9">Appointment</span>
</div>
</th>
<th class="sort border-end border-translucent align-middle text-end px-3 text-uppercase text-body-tertiary"
scope="col"
data-sort="qualified"
style="width:20%;
min-width:100px">
<div class="d-inline-flex flex-center">
<span class="fa-solid fa-square fs-11 text-primary-light me-2"
data-fa-transform="up-2"></span><span class="mb-0 fs-9">Qualified</span>
</div>
</th>
<th class="sort border-end border-translucent align-middle text-end px-3 text-uppercase text-body-tertiary"
scope="col"
data-sort="closed-won"
style="width:20%;
min-width:100px">
<div class="d-inline-flex flex-center">
<span class="fa-solid fa-square fs-11 text-success me-2"
data-fa-transform="up-2"></span><span class="mb-0 fs-9">Closed Won</span>
</div>
</th>
<th class="sort align-middle text-end ps-3 text-uppercase text-body-tertiary"
scope="col"
data-sort="contact-sent"
style="width:20%;
min-width:100px">
<div class="d-inline-flex flex-center">
<span class="fa-solid fa-square fs-11 text-info me-2"
data-fa-transform="up-2"></span><span class="mb-0 fs-9">Contact Sent</span>
</div>
</th>
</tr>
</thead>
<tbody class="list" id="table-deal-forecast-body">
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="contact border-end border-translucent align-middle white-space-nowrap py-2 ps-0 px-3">
<a class="fw-semibold" href="#!">Carrie Anne</a>
</td>
<td class="appointment border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
1000
</td>
<td class="qualified border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$1256
</td>
<td class="closed-won border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$1200
</td>
<td class="contact-sent border-end-0 align-middle white-space-nowrap text-end fw-semibold text-body ps-3 py-2">
$1200
</td>
</tr>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="contact border-end border-translucent align-middle white-space-nowrap py-2 ps-0 px-3">
<a class="fw-semibold" href="#!">Milind Mikuja</a>
</td>
<td class="appointment border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
558
</td>
<td class="qualified border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$2531
</td>
<td class="closed-won border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$2200
</td>
<td class="contact-sent border-end-0 align-middle white-space-nowrap text-end fw-semibold text-body ps-3 py-2">
$2200
</td>
</tr>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="contact border-end border-translucent align-middle white-space-nowrap py-2 ps-0 px-3">
<a class="fw-semibold" href="#!">Stanley Drinkwater</a>
</td>
<td class="appointment border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
1100
</td>
<td class="qualified border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$100
</td>
<td class="closed-won border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$100
</td>
<td class="contact-sent border-end-0 align-middle white-space-nowrap text-end fw-semibold text-body ps-3 py-2">
$100
</td>
</tr>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="contact border-end border-translucent align-middle white-space-nowrap py-2 ps-0 px-3">
<a class="fw-semibold" href="#!">Josef Stravinsky</a>
</td>
<td class="appointment border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
856
</td>
<td class="qualified border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$326
</td>
<td class="closed-won border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$265
</td>
<td class="contact-sent border-end-0 align-middle white-space-nowrap text-end fw-semibold text-body ps-3 py-2">
$265
</td>
</tr>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="contact border-end border-translucent align-middle white-space-nowrap py-2 ps-0 px-3">
<a class="fw-semibold" href="#!">Roy Anderson</a>
</td>
<td class="appointment border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
1200
</td>
<td class="qualified border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$1452
</td>
<td class="closed-won border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$865
</td>
<td class="contact-sent border-end-0 align-middle white-space-nowrap text-end fw-semibold text-body ps-3 py-2">
$865
</td>
</tr>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="contact border-end border-translucent align-middle white-space-nowrap py-2 ps-0 px-3">
<a class="fw-semibold" href="#!">Oscar Wilde</a>
</td>
<td class="appointment border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
1020
</td>
<td class="qualified border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$950
</td>
<td class="closed-won border-end border-translucent align-middle white-space-nowrap text-end fw-semibold text-body py-2 px-3">
$1000
</td>
<td class="contact-sent border-end-0 align-middle white-space-nowrap text-end fw-semibold text-body ps-3 py-2">
$800
</td>
</tr>
</tbody>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="align-middle border-bottom-0 border-end border-translucent white-space-nowrap text-end fw-bold text-body-emphasis pt-2 lh-sm pb-0 px-3">
</td>
<td class="align-middle border-bottom-0 border-end border-translucent white-space-nowrap text-end fw-bold text-body-emphasis pt-2 lh-sm pb-0 px-3">
4,744
</td>
<td class="align-middle border-bottom-0 border-end border-translucent white-space-nowrap text-end fw-bold text-body-emphasis pt-2 lh-sm pb-0 px-3">
$5,665
</td>
<td class="align-middle border-bottom-0 border-end border-translucent white-space-nowrap text-end fw-bold text-body-emphasis pt-2 lh-sm pb-0 px-3">
$4630
</td>
<td class="border-bottom-0 align-middle white-space-nowrap text-end fw-bold text-body-emphasis pt-2 pb-0 ps-3">
$4630
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="mx-lg-n4">
<div class="row g-3 mb-9 mt-n7">
<div class="col-xl-5">
<div class="card h-100">
<div class="card-body">
<h3>Lead Conversion</h3>
<p class="text-body-tertiary mb-0">Stages of deals &amp; conversion</p>
<div class="echart-lead-conversion" style="min-height: 250px;"></div>
</div>
</div>
</div>
<div class="col-xl-7">
<div class="card h-100">
<div class="card-body">
<h3>Revenue Target</h3>
<p class="text-body-tertiary">Country-wise target fulfilment</p>
<div class="echart-revenue-target-conversion" style="min-height: 230px;"></div>
</div>
</div>
</div>
</div>
</div>
<footer class="footer position-absolute">
<div class="row g-0 justify-content-between align-items-center h-100">
<div class="col-12 col-sm-auto text-center">
<p class="mb-0 mt-2 mt-sm-0 text-body">
Thank you for creating with Phoenix<span class="d-none d-sm-inline-block"></span><span class="d-none d-sm-inline-block mx-1">|</span>
<br class="d-sm-none" />
2024 &copy;<a class="mx-1" href="https://themewagon.com">Themewagon</a>
</p>
</div>
<div class="col-12 col-sm-auto text-center">
<p class="mb-0 text-body-tertiary text-opacity-85">v1.20.1</p>
</div>
</div>
</footer>
</div>
{% endblock %}

View File

@ -1,477 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% load tenhal_tag %}
{% block title %}
{{ _("Dealership Dashboard") |capfirst }}
{% endblock title %}
{% block content%}
<div class="main-content flex-grow-1 container-fluid mt-4 mb-3">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 pb-3 border-bottom">
<h2 class="h3 fw-bold text-dark mb-3 mb-md-0">Business Health Dashboard <i class="fas fa-chart-area text-primary ms-2"></i></h2>
<form method="GET" class="date-filter-form">
<div class="row g-3">
<div class="col-12 col-md-4">
<label for="start-date" class="form-label">Start Date</label>
<input type="date" class="form-control" id="start-date" name="start_date"
value="{{ start_date|date:'Y-m-d' }}" required>
</div>
<div class="col-12 col-md-4">
<label for="end-date" class="form-label">End Date</label>
<input type="date" class="form-control" id="end-date" name="end_date"
value="{{ end_date|date:'Y-m-d' }}" required>
</div>
<div class="col-12 col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Apply Filter</button>
</div>
</div>
</form>
</div>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Cost of Cars Sold</p>
<h4 class="fw-bolder text-secondary mb-3">{{total_cost_of_cars_sold|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Revenue from cars</p>
<h4 class="fw-bolder text-primary mb-3">{{total_revenue_from_cars|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Net Profit From Cars</p>
<h4 class="fw-bolder text-success mb-3">{{net_profit_from_cars|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Discount on cars</p>
<h4 class="fw-bolder text-primary mb-3">{{total_discount_on_cars|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total VAT collected from cars</p>
<h4 class="fw-bolder text-primary mb-3">{{total_vat_collected_from_cars|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Revenue from Services</p>
<h4 class="fw-bolder text-primary mb-3">{{total_revenue_from_services|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total VAT collected from Services</p>
<h4 class="fw-bolder text-primary mb-3">{{total_vat_collected_from_services|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Revenue Generated" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{total_revenue_generated|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total VAT Collected" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{total_vat_collected|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Expense" %}</p>
<h4 class="fw-bolder text-danger mb-3">{{total_expenses|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-danger-subtle text-danger fw-bold p-2 rounded-pill d-inline-flex align-self-start">
-2% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Gross Profit" %}</p>
<h4 class="fw-bolder text-info mb-3">{{gross_profit|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total VAT Collected" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{total_vat_collected|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Cars Sold</p>
<h4 class="fw-bolder text-success mb-3">{{ total_cars_sold }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">New Cars Sold</p>
<h4 class="fw-bolder text-success mb-3">{{ total_cars_sold }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Used Cars Sold</p>
<h4 class="fw-bolder text-success mb-3">{{ total_cars_sold }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans 'Inventory Value '%}</p>
<h4 class="fw-bolder text-success mb-3">{{ total_inventory_value|floatformat:2 }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Cars in Inventory</p>
<h4 class="fw-bolder text-success mb-3">{{ total_cars_in_inventory }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Old Cars in Inventory</p>
<h4 class="fw-bolder text-success mb-3">{{ total_old_cars_in_inventory}}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total New Cars in Inventory</p>
<h4 class="fw-bolder text-success mb-3">{{ total_new_cars_in_inventory }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">New Cars Value</p>
<h4 class="fw-bolder text-success mb-3">{{ new_car_value }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Old Cars Value</p>
<h4 class="fw-bolder text-success mb-3">{{ old_car_value }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+8% from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Aging Inventory</p>
<h4 class="fw-bolder text-success mb-3">{{ aging_inventory_count}}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
</div>
<!--charts-->
<div class="row g-4">
<div class="col-lg-8">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Monthly Revenue & Profit</h5>
</div>
<div class="card-body" style="height: 400px;">
<canvas id="revenueProfitChart"></canvas>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Monthly Cars Sold</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="CarsSoldByMonthChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
{% endblock content %}
{% block customJS %}
<script>
// Define a color palette that aligns with the Phoenix template
const primaryColor = '#7249b6'; // A vibrant purple
const secondaryColor = '#8193a6'; // A muted gray/blue
const successColor = '#00d074'; // A bright green
const dangerColor = '#e63757'; // A deep red
// Monthly Cars Sold (Bar Chart)
const ctx1 = document.getElementById('CarsSoldByMonthChart').getContext('2d');
new Chart(ctx1, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [{
label: 'Total Cars Sold',
data: {{monthly_cars_sold_json|safe}},
backgroundColor: primaryColor,
borderColor: primaryColor,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
y: {
beginAtZero: true,
grid: { color: 'rgba(0, 0, 0, 0.05)' },
ticks: { color: secondaryColor }
},
x: {
grid: { display: false },
ticks: { color: secondaryColor }
}
}
}
});
// Monthly Revenue & Profit (Line Chart)
const ctx2 = document.getElementById('revenueProfitChart').getContext('2d');
new Chart(ctx2, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [
{
label: 'Monthly Revenue',
data: {{monthly_revenue_json|safe}},
borderColor: primaryColor,
backgroundColor: 'rgba(114, 73, 182, 0.1)', // Using primaryColor with transparency
tension: 0.4,
fill: true,
pointBackgroundColor: primaryColor,
pointRadius: 5,
pointHoverRadius: 8
},
{
label: 'Monthly Net Profit',
data: {{monthly_net_profit_json|safe}},
borderColor: successColor,
backgroundColor: 'rgba(0, 208, 116, 0.1)', // Using successColor with transparency
tension: 0.4,
fill: true,
pointBackgroundColor: successColor,
pointRadius: 5,
pointHoverRadius: 8
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
labels: { color: '#495057', boxWidth: 20 }
},
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: 'white',
bodyColor: 'white',
padding: 10,
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'SAR' }).format(context.parsed.y);
}
return label;
}
}
}
},
scales: {
x: {
grid: { color: 'rgba(0, 0, 0, 0.05)' },
ticks: { color: secondaryColor },
border: { color: secondaryColor }
},
y: {
grid: { color: 'rgba(0, 0, 0, 0.05)' },
ticks: { color: secondaryColor },
border: { color: secondaryColor }
}
}
}
});
</script>
{% endblock %}

View File

@ -0,0 +1,266 @@
{% load i18n %}
{% if request.is_dealer or request.is_manager or request.is_accountant %}
<h3 class="fw-bold mb-3">
{% blocktrans with start_date=start_date|date:"F j, Y" end_date=end_date|date:"F j, Y" %}
Sales KPIs ({{ start_date }} - {{ end_date }})
{% endblocktrans %}
</h3>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Cars Sold" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_cars_sold }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Revenue from Cars" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_revenue_from_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Net Profit from Cars" %}</p>
<h4 class="fw-bolder text-success mb-3">{{ net_profit_from_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Discount on Cars" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_discount_on_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Cost of Cars Sold" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_cost_of_cars_sold|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total VAT from Cars" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_vat_collected_from_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
</div>
<h4 class="fw-bold my-4">{% trans "Sales of New Cars" %}</h4>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars Sold" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_new_cars_sold }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars Revenue" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_revenue_from_new_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars Net Profit" %}</p>
<h4 class="fw-bolder text-success mb-3">{{ net_profit_from_new_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars VAT" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_vat_collected_from_new_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars Cost" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_cost_of_new_cars_sold|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
</div>
<h4 class="fw-bold my-4">{% trans "Sales of Used Cars" %}</h4>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars Sold" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_used_cars_sold }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars Revenue" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_revenue_from_used_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars Net Profit" %}</p>
<h4 class="fw-bolder text-success mb-3">{{ net_profit_from_used_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars VAT" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_vat_collected_from_used_cars|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars Cost" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_cost_of_used_cars_sold|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
</div>
{% endif %}
{% if request.is_dealer or request.is_manager or request.is_inventory %}
<h3 class="fw-bold mb-3">{% trans "Inventory KPIs" %}</h3>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Cars in Inventory" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_cars_in_inventory }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Inventory Value" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_inventory_value|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars in Inventory" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_new_cars_in_inventory }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars in Inventory" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_used_cars_in_inventory }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars Inventory Value" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ new_car_value|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars Inventory Value" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ used_car_value|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-danger fw-bold small mb-1"><a class="text-danger" href="{% url 'aging_inventory_list' request.dealer.slug %}">{% trans "Aging Inventory (> 60 days)" %}</a></p>
<h4 class="fw-bolder text-danger mb-3"><a class="text-danger" href="{% url 'aging_inventory_list' request.dealer.slug %}">{{ aging_inventory_count }}</a></h4>
</div>
</div>
</div>
</div>
{% endif %}
{% if request.is_dealer or request.is_manager or request.is_accountant %}
<h3 class="fw-bold mb-3">
{% blocktrans with start_date=start_date|date:"F j, Y" end_date=end_date|date:"F j, Y" %}
Financial Health KPIs ({{ start_date }} - {{ end_date }})
{% endblocktrans %}
</h3>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Revenue from Services" %}</p>
<h4 class="fw-bolder text-info mb-3">{{ total_revenue_from_services|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total VAT from Services" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_vat_collected_from_services|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Revenue Generated" %}</p>
<h4 class="fw-bolder text-success mb-3">{{ total_revenue_generated|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total VAT Collected" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_vat_collected|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Expenses" %}</p>
<h4 class="fw-bolder text-danger mb-3">{{ total_expenses|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Gross Profit" %}</p>
<h4 class="fw-bolder text-success mb-3">{{ gross_profit|floatformat:2 }}<span class="icon-saudi_riyal"></span></h4>
</div>
</div>
</div>
</div>
{% endif %}

View File

@ -0,0 +1,429 @@
{% extends 'base.html' %}
{% load i18n %}
{% load tenhal_tag %}
{% block title %}
{% trans "Dealership Dashboard"|capfirst %}
{% endblock title %}
{% block content %}
<div class="main-content flex-grow-1 container-fluid mt-4 mb-3">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 pb-3 border-bottom">
<h2 class="h3 fw-bold mb-3 mb-md-0">
{% if request.is_dealer %}
{% trans "Business Health Dashboard" %}
{% elif request.is_manger %}
{% trans "Manager Dashboard" %}
{% elif request.is_inventory %}
{% trans "Inventory Dashboard" %}
{% else %}
{% trans "Accountant Dashboard" %}
{% endif %}
<i class="fas fa-chart-area text-primary ms-2"></i>
</h2>
<form method="GET" class="date-filter-form">
<div class="row g-3">
<div class="col-12 col-md-4">
<label for="start-date" class="form-label">{% trans "Start Date" %}</label>
<input type="date" class="form-control" id="start-date" name="start_date"
value="{{ start_date|date:'Y-m-d' }}" required>
</div>
<div class="col-12 col-md-4">
<label for="end-date" class="form-label">{% trans "End Date" %}</label>
<input type="date" class="form-control" id="end-date" name="end_date"
value="{{ end_date|date:'Y-m-d' }}" required>
</div>
<div class="col-12 col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">{% trans "Apply Filter" %}</button>
</div>
</div>
<input type="hidden" name="make_sold" value="{{ selected_make_sales }}">
</form>
</div>
<div class="row g-4 mb-5">
{% include 'dashboards/financial_data_cards.html' %}
</div>
<div class="row g-4 mb-5">
{% include 'dashboards/chart.html' %}
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
{% endblock content %}
{% block customJS %}
<script>
// Define a color palette that aligns with the Phoenix template
const primaryColor = '#7249b6';
const secondaryColor = '#8193a6';
const successColor = '#00d074';
const dangerColor = '#e63757';
const chartColors = [
'#7249b6', '#00d074', '#e63757', '#17a2b8', '#ffc107',
'#8193a6', '#28a745', '#6c757d', '#fd7e14', '#dc3545',
'#20c997', '#6f42c1', '#e83e8c', '#6610f2', '#007bff',
'#495057'
];
// Pass translated strings from Django to JavaScript
const translatedStrings = {
monthlyCarsSoldLabel: "{% trans 'Total Cars Sold' %}",
monthlyRevenueLabel: "{% trans 'Monthly Revenue' %}",
monthlyNetProfitLabel: "{% trans 'Monthly Net Profit' %}",
salesByMakeLabel: "{% trans 'Car Count by Make' %}",
salesByModelPrefix: "{% trans 'Cars Sold for' %}",
inventoryByMakeLabel: "{% trans 'Car Count by Make' %}",
inventoryByModelLabel: "{% trans 'Cars in Inventory' %}",
jan: "{% trans 'Jan' %}",
feb: "{% trans 'Feb' %}",
mar: "{% trans 'Mar' %}",
apr: "{% trans 'Apr' %}",
may: "{% trans 'May' %}",
jun: "{% trans 'Jun' %}",
jul: "{% trans 'Jul' %}",
aug: "{% trans 'Aug' %}",
sep: "{% trans 'Sep' %}",
oct: "{% trans 'Oct' %}",
nov: "{% trans 'Nov' %}",
dec: "{% trans 'Dec' %}",
cars: "{% trans 'cars' %}"
};
// Monthly Cars Sold (Bar Chart)
const ctx1 = document.getElementById('CarsSoldByMonthChart').getContext('2d');
new Chart(ctx1, {
type: 'bar',
data: {
labels: [
translatedStrings.jan, translatedStrings.feb, translatedStrings.mar, translatedStrings.apr,
translatedStrings.may, translatedStrings.jun, translatedStrings.jul, translatedStrings.aug,
translatedStrings.sep, translatedStrings.oct, translatedStrings.nov, translatedStrings.dec
],
datasets: [{
label: translatedStrings.monthlyCarsSoldLabel,
data: {{ monthly_cars_sold_json|safe }},
backgroundColor: primaryColor,
borderColor: primaryColor,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
y: {
beginAtZero: true,
grid: { color: 'rgba(0, 0, 0, 0.05)' },
ticks: {
color: secondaryColor,
callback: function(value) {
if (Number.isInteger(value)) {
return value;
}
}
}
},
x: {
grid: { display: false },
ticks: { color: secondaryColor }
}
}
}
});
// Monthly Revenue & Profit (Line Chart)
const ctx2 = document.getElementById('revenueProfitChart').getContext('2d');
new Chart(ctx2, {
type: 'line',
data: {
labels: [
translatedStrings.jan, translatedStrings.feb, translatedStrings.mar, translatedStrings.apr,
translatedStrings.may, translatedStrings.jun, translatedStrings.jul, translatedStrings.aug,
translatedStrings.sep, translatedStrings.oct, translatedStrings.nov, translatedStrings.dec
],
datasets: [
{
label: translatedStrings.monthlyRevenueLabel,
data: {{ monthly_revenue_json|safe }},
borderColor: primaryColor,
backgroundColor: 'rgba(114, 73, 182, 0.1)', // Using primaryColor with transparency
tension: 0.4,
fill: true,
pointBackgroundColor: primaryColor,
pointRadius: 5,
pointHoverRadius: 8
},
{
label: translatedStrings.monthlyNetProfitLabel,
data: {{ monthly_net_profit_json|safe }},
borderColor: successColor,
backgroundColor: 'rgba(0, 208, 116, 0.1)', // Using successColor with transparency
tension: 0.4,
fill: true,
pointBackgroundColor: successColor,
pointRadius: 5,
pointHoverRadius: 8
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
labels: { color: '#495057', boxWidth: 20 }
},
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: 'white',
bodyColor: 'white',
padding: 10,
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'SAR' }).format(context.parsed.y);
}
return label;
}
}
}
},
scales: {
x: {
grid: { color: 'rgba(0, 0, 0, 0.05)' },
ticks: { color: secondaryColor },
border: { color: secondaryColor }
},
y: {
grid: { color: 'rgba(0, 0, 0, 0.05)' },
ticks: { color: secondaryColor },
border: { color: secondaryColor }
}
}
}
});
// Sales by Make (Pie Chart)
function getChartColors(count) {
const colors = [];
for (let i = 0; i < count; i++) {
colors.push(chartColors[i % chartColors.length]);
}
return colors;
}
const ctx3 = document.getElementById('salesByBrandChart').getContext('2d');
new Chart(ctx3, {
type: 'pie',
data: {
labels: {{ sales_by_make_labels_json|safe }},
datasets: [{
label: translatedStrings.salesByMakeLabel,
data: {{ sales_by_make_counts_json|safe }},
backgroundColor: getChartColors({{ sales_by_make_counts_json|safe }}.length),
hoverOffset: 15,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: { color: '#343a40', font: { size: 14 } }
},
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = ((value / total) * 100).toFixed(2);
return `${label}: ${value} ${translatedStrings.cars} (${percentage}%)`;
}
}
}
}
}
});
// -----------------------------------------------------------
// 4. Sales by Model (Bar Chart)
// -----------------------------------------------------------
const salesDataByModel = JSON.parse('{{ sales_data_by_model_json|safe }}');
const canvasElementSales = document.getElementById('salesChartByModel');
let chartInstanceSales = null;
if (salesDataByModel.length > 0) {
const labels = salesDataByModel.map(item => item.id_car_model__name);
const counts = salesDataByModel.map(item => item.count);
const backgroundColor = labels.map((_, index) => getChartColors(labels.length)[index]);
chartInstanceSales = new Chart(canvasElementSales, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: `${translatedStrings.salesByModelPrefix} {{ selected_make_sales }}`,
data: counts,
backgroundColor: backgroundColor,
borderColor: backgroundColor,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
if (Number.isInteger(value)) {
return value;
}
}
}
}
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
label += Math.round(context.parsed.y);
return label;
}
}
}
}
}
});
}
// -----------------------------------------------------------
// 5. Inventory by Make (Pie Chart)
// -----------------------------------------------------------
const ctxInventoryMake = document.getElementById('inventoryByMakeChart').getContext('2d');
new Chart(ctxInventoryMake, {
type: 'pie',
data: {
labels: {{ inventory_by_make_labels_json|safe }},
datasets: [{
label: translatedStrings.inventoryByMakeLabel,
data: {{ inventory_by_make_counts_json|safe }},
backgroundColor: getChartColors({{ inventory_by_make_counts_json|safe }}.length),
hoverOffset: 15,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: { color: '#343a40', font: { size: 14 } }
},
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = ((value / total) * 100).toFixed(2);
return `${label}: ${value} ${translatedStrings.cars} (${percentage}%)`;
}
}
}
}
}
});
// -----------------------------------------------------------
// 6. Inventory by Model (Bar Chart)
// -----------------------------------------------------------
const inventoryDataByModel = JSON.parse('{{ inventory_data_by_model_json|safe }}');
const canvasInventoryModel = document.getElementById('inventoryByModelChart');
const messageInventoryModel = document.getElementById('inventoryByModelMessage');
if (inventoryDataByModel.length > 0) {
canvasInventoryModel.style.display = 'block';
if (messageInventoryModel) {
messageInventoryModel.style.display = 'none';
}
const labels = inventoryDataByModel.map(item => item.id_car_model__name);
const counts = inventoryDataByModel.map(item => item.count);
const backgroundColor = getChartColors(labels.length);
new Chart(canvasInventoryModel, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: translatedStrings.inventoryByModelLabel,
data: counts,
backgroundColor: backgroundColor,
borderColor: backgroundColor,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
if (Number.isInteger(value)) {
return value;
}
}
}
}
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
label += Math.round(context.parsed.y);
return label;
}
}
}
}
}
});
} else {
canvasInventoryModel.style.display = 'none';
if (messageInventoryModel) {
messageInventoryModel.style.display = 'flex';
}
}
</script>
{% endblock %}

View File

@ -1,300 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="main-content flex-grow-1 container-fluid mt-4 mb-3">
<div class="d-flex justify-content-between align-items-center mb-5 pb-3 border-bottom">
<h2 class="h3 fw-bold mb-0">Inventory Dashboard <i class="fas fa-warehouse text-primary ms-2"></i></h2>
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Last 30 Days
</button>
<ul class="dropdown-menu dropdown-menu-end shadow">
<li><a class="dropdown-item" href="#">Today</a></li>
<li><a class="dropdown-item" href="#">Last 7 Days</a></li>
<li><a class="dropdown-item" href="#">Last 90 Days</a></li>
</ul>
</div>
</div>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Cars in Inventory</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_cars_in_inventory }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">New Cars in Inventory</p>
<h4 class="fw-bolder text-success mb-3">{{ new_cars_in_inventory }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Used Cars in Inventory</p>
<h4 class="fw-bolder text-info mb-3">{{ old_cars_in_inventory }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Inventory Value</p>
<h4 class="fw-bolder text-warning mb-3">$5.8M</h4>
</div>
<span class="badge bg-danger-subtle text-danger fw-bold p-2 rounded-pill d-inline-flex align-self-start">
-2% from last month
</span>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-warning fw-bold mb-1">New Car Value</p>
<h4 class="fw-bolder mb-0">$3.2M</h4>
<p class="text-muted small">Total new cars value</p>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-info fw-bold mb-1">Used Car Value</p>
<h4 class="fw-bolder mb-0">$2.6M</h4>
<p class="text-muted small">Total used cars value</p>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-danger fw-bold mb-1">Average Time on Lot</p>
<h4 class="fw-bolder mb-0">10 days</h4>
<p class="text-muted small">Average for all vehicles</p>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card h-100 p-4 shadow-sm border-0">
<p class="text-danger fw-bold mb-1">Aging Inventory</p>
<h4 class="fw-bolder mb-0">12 cars</h4>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
-4 cars from last month
</span>
</div>
</div>
</div>
<!--charts-->
<div class="row g-4">
<div class="col-lg-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Inventory by Make</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="inventoryByBrandChart"></canvas>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Car Models by Make in Inventory</h5>
</div>
<div class="row justify-content-center">
<div class="col-md-6 mb-4 mt-3">
<label for="carMakeSelect" class="form-label fs-8 mb-2">Select a Car Make:</label>
<select id="carMakeSelect" class="form-select">
<option value="" disabled selected>-- Choose a make --</option>
</select>
</div>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="inventoryChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
{% endblock content %}
{% block customJS %}
<script>
// Define a custom color palette that matches the Phoenix template
const primaryColor = '#7249b6'; // A vibrant purple
const secondaryColor = '#8193a6'; // A muted gray/blue
const successColor = '#00d074'; // A bright green
const infoColor = '#0085ff'; // A medium blue
const warningColor = '#ffc83b'; // A yellow
const dangerColor = '#e63757'; // A deep red
const ctx4 = document.getElementById('inventoryByBrandChart').getContext('2d');
// Chart.js configuration for the Pie Chart
new Chart(ctx4, {
type: 'pie',
data: {
labels: ['Toyota', 'Ford', 'Honda', 'BMW', 'Other'],
datasets: [{
label: 'Car Count by Make',
data: [45, 30, 25, 15, 10], // Sample data for car counts
backgroundColor: [
primaryColor,
successColor,
warningColor,
infoColor,
secondaryColor
],
hoverOffset: 15,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right', // Places the legend on the right side
labels: {
color: secondaryColor, // Use a muted color for a subtle look
font: {
size: 14
}
}
},
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)', // Bootstrap dark
titleColor: '#fff',
bodyColor: '#fff',
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = ((value / total) * 100).toFixed(2);
return `${label}: ${value} cars (${percentage}%)`;
}
}
}
}
}
});
//chart for number of cars of each model in the inventory
// Sample data representing your inventory
const inventoryData = [
{ make: 'Ford', model: 'Mustang', count: 5 },
{ make: 'Ford', model: 'F-150', count: 12 },
{ make: 'Ford', model: 'Explorer', count: 8 },
{ make: 'Toyota', model: 'Camry', count: 10 },
{ make: 'Toyota', model: 'Corolla', count: 15 },
{ make: 'Toyota', model: 'RAV4', count: 7 },
{ make: 'BMW', model: 'X5', count: 4 },
{ make: 'BMW', model: '3 Series', count: 6 },
{ make: 'BMW', model: 'i8', count: 2 },
];
// Get the unique list of car makes for the dropdown
const carMakes = [...new Set(inventoryData.map(item => item.make))];
const selectElement = document.getElementById('carMakeSelect');
const canvasElement = document.getElementById('inventoryChart');
let chartInstance = null; // Stores the current chart instance
// Populate the dropdown menu
carMakes.forEach(make => {
const option = document.createElement('option');
option.value = make;
option.textContent = make;
selectElement.appendChild(option);
});
// Function to create or update the chart
function renderChart(data, type = 'bar') {
// Destroy the old chart instance if it exists
if (chartInstance) {
chartInstance.destroy();
}
const chartLabels = data.map(item => item.model);
const chartValues = data.map(item => item.count);
// Generate random colors for the chart segments
const backgroundColor = chartLabels.map(() => `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.6)`);
const borderColor = backgroundColor.map(color => color.replace('0.6', '1'));
// Create the new chart
chartInstance = new Chart(canvasElement, {
type: type, // Can be 'bar' or 'pie'
data: {
labels: chartLabels,
datasets: [{
label: 'Number of Cars',
data: chartValues,
backgroundColor: backgroundColor,
borderColor: borderColor,
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
},
plugins: {
legend: {
display: true
}
}
}
});
}
// Event listener for the dropdown menu
selectElement.addEventListener('change', (event) => {
const selectedMake = event.target.value;
if (selectedMake) {
const filteredData = inventoryData.filter(item => item.make === selectedMake);
// The second parameter can be 'pie' to switch chart types
renderChart(filteredData, 'bar'); // Use 'bar' for a bar chart
}
});
</script>
{% endblock %}

View File

@ -1,452 +0,0 @@
{% load i18n static custom_filters django_ledger %}
{% block content %}
<div class="row justify-content-between">
<div class="col-12 col-lg-6">
<div class="row g-2">
<h3 class="fs-4 fs-md-4 fs-xl-4 fw-black mb-4">
<span class="text-gradient-info me-3">{{ staff }}</span>
</h3>
<div class="card mb-3">
<div class="bg-holder"
style="background-image:url({% static 'images/bg/38.png' %});
background-position:left bottom;
background-size:auto"></div>
<div class="card-body d-flex justify-content-between position-relative">
<div class="col-sm-7 col-md-8 col-xxl-8 mb-md-3 mb-lg-0">
<h3 class="mb-3">{{ _("Inventory by Status") }}</h3>
<div class="row g-0">
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-bottom border-end border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-success"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">{{ _("Available") }}</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">{{ available_cars }}</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-bottom border-end-md-0 border-end-xl border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-warning"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">{{ _("Sold") }}</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">{{ sold_cars }}</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-bottom border-end border-end-md border-end-xl-0 border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-danger"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">{{ _("Reserved") }}</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">{{ reserved_cars }}</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-end-xl border-bottom border-bottom-xl-0 border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-primary"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">{{ _("Transfer") }}</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">{{ transfer_cars }}</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100 border-1 border-end border-translucent">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-warning-light"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">{{ _("Hold") }}</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">{{ hold_cars }}</h3>
</div>
</div>
<div class="col-6 col-xl-4">
<div class="d-flex flex-column flex-center align-items-sm-start flex-md-row justify-content-md-between flex-xxl-column p-3 ps-sm-3 ps-md-4 p-md-3 h-100">
<div class="d-flex align-items-center mb-1">
<span class="fa-solid fa-square fs-11 me-2 text-secondary-dark"
data-fa-transform="up-2"></span><span class="mb-0 fs-9 text-body">{{ _("Damaged") }}</span>
</div>
<h3 class="fw-semibold ms-xl-3 ms-xxl-0 pe-md-2 pe-xxl-0 mb-0 mb-sm-3">{{ damaged_cars }}</h3>
</div>
</div>
</div>
</div>
<div class="col-sm-5 col-md-4 col-xxl-4 my-3 my-sm-0">
<div class="position-relative d-flex flex-center mb-sm-4 mb-xl-0 echart-cars-by-status-container mt-sm-7 mt-lg-4 mt-xl-0">
<div id="echart-cars-by-status" style="min-height:245px;width:100%"></div>
<div class="position-absolute rounded-circle bg-primary-subtle top-50 start-50 translate-middle d-flex flex-center"
style="height:100px;
width:100px">
<h3 class="mb-0 text-primary-dark fw-bolder" data-label="data-label"></h3>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-2">
<div class="col-12 mb-8">
<div class="mb-3">
<h3>{{ _("New Leads and Customers") }}</h3>
<p class="text-body-tertiary mb-0">{{ _("Payment received across all channels") }}</p>
</div>
<div class="row g-6">
<div class="col-xl-6 mb-2 mb-sm-0">
<div class="d-flex align-items-center">
<span class="me-2 text-info"
data-feather="users"
style="min-height:24px;
width:24px"></span>
<h4 class="text-body-tertiary mb-0">
{{ _("New Customers") }} :
<span class="text-body-emphasis">42</span>
</h4>
<span class="badge badge-phoenix fs-10 badge-phoenix-success d-inline-flex align-items-center ms-2">
<span class="badge-label d-inline-block lh-base">+24.5%</span>
<span class="ms-1 fa-solid fa-caret-up d-inline-block lh-1"></span>
</span>
</div>
<div class="pb-0 pt-4">
<div class="echarts-new-users" style="min-height:110px;width:100%;"></div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center">
<span class="me-2 text-primary"
data-feather="zap"
style="height:24px;
width:24px"></span>
<h4 class="text-body-tertiary mb-0">
{{ _("New Leads") }} :<span class="text-body-emphasis">45</span>
</h4>
<span class="badge badge-phoenix fs-10 badge-phoenix-success d-inline-flex align-items-center ms-2">
<span class="badge-label d-inline-block lh-base">+30.5%</span>
<span class="ms-1 fa-solid fa-caret-up d-inline-block lh-1"></span>
</span>
</div>
<div class="pb-0 pt-4">
<div class="echarts-new-leads" style="min-height:110px;width:100%;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="row g-3 pe-xxl-3">
<div class="col-12 col-xl-6 col-xxl-12">
<div class="row">
<div class="col-6 col-xl-12 col-xxl-6 border-bottom border-end border-end-xl-0 border-end-xxl pb-4 pt-4 pt-xl-0 pt-xxl-4 pe-4 pe-sm-5 pe-xl-0 pe-xxl-5">
<h4 class="text-body mb-4">{% trans 'inventory'|upper %}</h4>
<div class="d-md-flex flex-between-center">
<div id="car-chart-by-make"
class="order-sm-0 order-md-1"
style="height:64px;
width: 128px"></div>
<div class="mt-4 mt-md-0">
<h1 class="text-body-highlight mb-2">{{ total_cars }}</h1>
<span class="badge badge-phoenix badge-phoenix-primary me-2 fs-10"> <span class="fs-10 text-body-secondary me-1">{{ _("As of") }}</span>{% now "SHORT_DATETIME_FORMAT" %}</span>
</div>
</div>
</div>
<div class="col-6 col-xl-12 col-xxl-6 border-bottom py-4 ps-4 ps-sm-5 ps-xl-0 ps-xxl-5">
<h4 class="text-body mb-4">{% trans 'inventory value'|upper %}</h4>
<div class="d-md-flex flex-between-center">
<div class="d-md-flex align-items-center gap-2 order-sm-0 order-md-1 fa-2x align-items-center">
<i class="fas fa-money-check-alt fs-4 text-success-light dark__text-opacity-75"></i>
<div class="d-flex d-md-block gap-2 align-items-center mt-1 mt-md-0">
<p class="fs-9 mb-0 mb-md-2 text-body-tertiary text-nowrap"></p>
<h4 class="text-body-highlight mb-0"></h4>
</div>
</div>
<div class="mt-3 mt-md-0">
<h3 class="text-body-highlight mb-2">
{{ total_selling_price|currency_format }} <span class="icon-saudi_riyal"></span>
</h3>
</div>
</div>
</div>
<div class="col-6 col-xl-12 col-xxl-6 border-bottom-xl border-bottom-xxl-0 border-end border-end-xl-0 border-end-xxl py-4 pe-4 pe-sm-5 pe-xl-0 pe-xxl-5">
<h4 class="text-body mb-4">{% trans "Profits"|upper %}</h4>
<div class="d-md-flex flex-between-center">
<div class="d-md-flex align-items-center gap-2 order-sm-0 order-md-1">
<span class="fa-solid fa-money-bill-trend-up fs-4 text-warning-light dark__text-opacity-75"
data-bs-theme="light"></span>
<div class="d-flex d-md-block gap-2 align-items-center mt-1 mt-md-0">
<p class="fs-9 mb-0 mb-md-2 text-body-tertiary text-nowrap"></p>
<h4 class="text-body-highlight mb-0"></h4>
</div>
</div>
<div class="mt-3 mt-md-0">
<h3 class="text-body-highlight mb-2">
{{ total_profit|currency_format }} <span class="icon-saudi_riyal"></span>
</h3>
</div>
</div>
</div>
<div class="col-6 col-xl-12 col-xxl-6 py-4 ps-4 ps-sm-5 ps-xl-0 ps-xxl-5">
<h5 class="text-body mb-4">{{ _("Canceled Invoices") }}</h5>
<div class="d-md-flex flex-between-center">
<div class="chart-cancel-booking order-sm-0 order-md-1"
style="height:54px;
width:78px"></div>
<div class="mt-3 mt-md-0">
<h3 class="text-body-highlight mb-2">120.00</h3>
<span class="badge badge-phoenix badge-phoenix-danger me-2 fs-10"> <span class="fa-solid fa-plus me-1"></span>5.76%</span>
<span class="fs-9 text-body-secondary d-block d-sm-inline mt-1">{{ _("From last month") }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-6 col-xxl-12 mb-3">
<div class="card h-100">
<div class="card-header pb-3">
<div class="row justify-content-between g-3">
<div class="col-auto">
<h3 class="text-body-highlight">{{ _("Gross Profit") }}</h3>
<p class="mb-0">Annual income according to the board</p>
</div>
</div>
</div>
<div class="card-body">
<div class="row align-items-center h-100 gy-5">
<div class="col-12 col-md-auto col-xl-12 col-xxl-auto order-md-1 order-xl-0 order-xxl-1 px-md-8 px-xl-6">
<div class="echart-gross-profit mx-auto mt-3 mt-md-0 mt-xl-3 mt-xxl-0"
style="width: 250px;
height: 250px"></div>
</div>
<div class="col-12 col-md-auto col-xl-12 col-xxl-auto flex-1 h-md-100">
<div class="d-flex flex-column justify-content-between h-md-100 h-xl-auto h-xxl-100">
<div class="d-flex align-items-center justify-content-between">
<div class="d-flex gap-2">
<div class="bullet-item bg-primary-light" data-bs-theme="light"></div>
<div>
<h6 class="mb-0 text-body fw-semibold mb-2">Flight</h6>
<h5 class="mb-0 text-body">$162,791,400</h5>
</div>
</div>
<div class="d-flex align-items-center gap-2 text-primary">
<span class="fw-bold"
data-feather="trending-up"
style="width: 24px;
height: 24px"></span>
<p class="mb-0 fw-bold">15.50%</p>
</div>
</div>
<hr />
<div class="d-flex align-items-center justify-content-between">
<div class="d-flex gap-2">
<div class="bullet-item bg-info-light" data-bs-theme="light"></div>
<div>
<h6 class="mb-0 text-body fw-semibold mb-2">Flight (Package)</h6>
<h5 class="mb-0 text-body">$135,659,500</h5>
</div>
</div>
<div class="d-flex align-items-center gap-2 text-danger">
<span class="fw-bold"
data-feather="trending-down"
style="width: 24px;
height: 24px"></span>
<p class="mb-0 fw-bold">11.09%</p>
</div>
</div>
<hr />
<div class="d-flex align-items-center justify-content-between">
<div class="d-flex gap-2">
<div class="bullet-item bg-warning-light" data-bs-theme="light"></div>
<div>
<h6 class="mb-0 text-body fw-semibold mb-2">Hotel</h6>
<h5 class="mb-0 text-body">$271,319,000</h5>
</div>
</div>
<div class="d-flex align-items-center gap-2 text-warning">
<span class="fw-bold"
data-feather="trending-up"
style="width: 24px;
height: 24px"></span>
<p class="mb-0 fw-bold">29.98%</p>
</div>
</div>
<hr />
<div class="d-flex align-items-center justify-content-between">
<div class="d-flex gap-2">
<div class="bullet-item bg-success-light" data-bs-theme="light"></div>
<div>
<h6 class="mb-0 text-body fw-semibold mb-2">Hotel (Package)</h6>
<h5 class="mb-0 text-body">$162,791,400</h5>
</div>
</div>
<div class="d-flex align-items-center gap-2 text-success">
<span class="fw-bold"
data-feather="trending-up"
style="width: 24px;
height: 24px"></span>
<p class="mb-0 fw-bold">03.90%</p>
</div>
</div>
<hr class="d-none" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
/* Car Chart By Make */
const getColor = (name, dom = document.documentElement) => {
return getComputedStyle(dom).getPropertyValue(`--phoenix-${name}`).trim();
};
const handleTooltipPosition = ([pos, , dom, , size]) => {
// only for mobile device
if (window.innerWidth <= 540) {
const tooltipHeight = dom.offsetHeight;
const obj = {top: pos[1] - tooltipHeight - 20};
obj[pos[0] < size.viewSize[0] / 2 ? 'left' : 'right'] = 5;
return obj;
}
return null; // else default behaviour
};
const carData = {{ car|safe }}
const carNames = carData.map(item => item.id_car_make__name);
const carCounts = carData.map(item => item.count);
const car_chart = echarts.init(document.getElementById('car-chart-by-make'));
option = {
color: getColor("danger"),
tooltip: {
trigger: 'axis',
position: (...params) => handleTooltipPosition(params),
padding: [7, 10],
axisPointer: {
type: 'none'
},
},
extraCssText: 'z-index: 1000',
responsive: true,
xAxis: [
{
type: 'category',
boundaryGap: false,
data: carNames,
axisLine: {
show: true,
lineStyle: {color: getColor('secondary-bg')}
},
axisTick: {
show: false
},
}
],
yAxis: [
{
show: false,
type: 'value',
}
],
series: [
{
type: 'bar',
barWidth: 3,
backgroundStyle: {
borderRadius: [0.5, 0.5, 0, 0],
},
data: carCounts
},
],
grid: {
bottom: 0,
top: 0,
left: 10,
right: 10,
containLabel: false
}
};
car_chart.setOption(option);
/* Car Status Chart */
const chartElContainer = document.querySelector('.echart-cars-by-status-container');
const car_status = echarts.init(document.getElementById('echart-cars-by-status'));
const chartLabel = chartElContainer.querySelector('[data-label]');
const data = [
{value: {{available_cars}}, name: '{{ _("Available") }}'},
{value: {{sold_cars}}, name: '{{ _("Sold")}}'},
{value: {{reserved_cars}}, name: '{{ _("Reserved") }}'},
{value: {{transfer_cars}}, name: '{{ _("Transfer") }}'},
{value: {{hold_cars}}, name: '{{ _("Hold") }}'},
{value: {{damaged_cars}}, name: '{{ _("Damaged") }}'}
];
const totalCars = data.reduce((acc, val) => val.value + acc, 0);
if (chartLabel) {
chartLabel.innerHTML = totalCars;
}
option = {
color: [
getColor('success'),
getColor('warning'),
getColor('danger'),
getColor('primary'),
getColor('warning-lighter'),
getColor('secondary-dark')
],
tooltip: {
trigger: 'item',
padding: [7, 10],
backgroundColor: getColor('body-highlight-bg'),
borderColor: getColor('border-color'),
textStyle: {color: getColor('light-text-emphasis')},
borderWidth: 1,
transitionDuration: 0,
extraCssText: 'z-index: 1000'
},
responsive: true,
maintainAspectRatio: false,
series: [
{
name: '',
type: 'pie',
radius: ['55%', '90%'],
startAngle: 90,
avoidLabelOverlap: false,
itemStyle: {
borderColor: getColor('body-bg'),
borderWidth: 3
},
label: {
show: false
},
emphasis: {
label: {
show: false
}
},
labelLine: {
show: false
},
data
}
],
grid: {
bottom: 0,
top: 0,
left: 0,
right: 0,
containLabel: false
}
};
car_status.setOption(option);
});
</script>
{% endblock %}

View File

@ -1,58 +1,63 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class="main-content flex-grow-1 container-fluid mt-4 mb-3">
<div class="d-flex justify-content-between align-items-center mb-5 pb-3 border-bottom">
<h2 class="h3 fw-bold mb-0">Sales & Leads Dashboard <i class="fas fa-chart-area text-primary ms-2"></i></h2>
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Last 30 Days
</button>
<ul class="dropdown-menu dropdown-menu-end shadow">
<li><a class="dropdown-item" href="#">Today</a></li>
<li><a class="dropdown-item" href="#">Last 7 Days</a></li>
<li><a class="dropdown-item" href="#">Last 90 Days</a></li>
</ul>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 pb-3 border-bottom">
<h2 class="h3 fw-bold mb-3 mb-md-0">{% trans "Sales Dashboard" %} <i class="fas fa-chart-area text-primary ms-2"></i></h2>
<form method="GET" class="date-filter-form">
<div class="row g-3">
<div class="col-12 col-md-4">
<label for="start-date" class="form-label">{% trans "Start Date" %}</label>
<input type="date" class="form-control" id="start-date" name="start_date"
value="{{ start_date|date:'Y-m-d' }}" required>
</div>
<div class="col-12 col-md-4">
<label for="end-date" class="form-label">{% trans "End Date" %}</label>
<input type="date" class="form-control" id="end-date" name="end_date"
value="{{ end_date|date:'Y-m-d' }}" required>
</div>
<div class="col-12 col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">{% trans "Apply Filter" %}</button>
</div>
</div>
</form>
</div>
<div class="row g-4 mb-5">
<div class="col-sm-6 col-md-4">
<h3 class="fw-bold mb-3">{% trans "Inventory KPIs" %}</h3>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Total Cars Sold</p>
<h4 class="fw-bolder text-success mb-3">{{ sold_cars }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Cars in Inventory" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_cars_in_inventory }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">Used Cars Sold</p>
<h4 class="fw-bolder text-info mb-3">{{ sold_used_cars }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "New Cars in Inventory" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_new_cars_in_inventory }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body d-flex flex-column justify-content-between p-4">
<div>
<p class="text-uppercase text-muted fw-bold small mb-1">New Cars Sold</p>
<h4 class="fw-bolder text-primary mb-3">{{ sold_new_cars }}</h4>
</div>
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
+5 units from last month
</span>
<div class="card-body p-4">
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Used Cars in Inventory" %}</p>
<h4 class="fw-bolder text-primary mb-3">{{ total_used_cars_in_inventory }}</h4>
</div>
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-4">
<p class="text-uppercase text-danger fw-bold small mb-1"><a class="text-danger" href="{% url 'aging_inventory_list' request.dealer.slug %}">{% trans "Aging Inventory (> 60 days)" %}</a></p>
<h4 class="fw-bolder text-danger mb-3"><a class="text-danger" href="{% url 'aging_inventory_list' request.dealer.slug %}">{{ aging_inventory_count }}</a></h4>
</div>
</div>
</div>
@ -62,60 +67,27 @@
<div class="col-md-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Monthly Cars Sold</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="CarsSoldByMonthChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Sales by Make</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="salesByBrandChart"></canvas>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-5">
<div class="col-md-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Top 5 Lead Sources</h5>
<h5 class="fw-bold mb-0 text-dark">{% trans "Top Lead Sources" %}</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="leadSourcesChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Appointments by Weekday</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="appointmentsChart"></canvas>
</div>
<div class="col-md-6">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">{% trans "Lead Conversion Funnel" %}</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="leadFunnelChart"></canvas>
</div>
</div>
</div>
</div>
<div class="row g-4">
<div class="col-12">
<div class="card h-100 shadow-sm border-0">
<div class="card-header bg-white border-bottom-0">
<h5 class="fw-bold mb-0 text-dark">Lead Conversion Funnel</h5>
</div>
<div class="card-body d-flex align-items-center justify-content-center" style="height: 400px;">
<canvas id="leadFunnelChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
@ -124,233 +96,174 @@
{% block customJS%}
<script>
// Define a custom color palette that matches the Phoenix template
// Define your color palette at the top
const primaryColor = '#7249b6'; // A vibrant purple
const secondaryColor = '#8193a6'; // A muted gray/blue
const successColor = '#00d074'; // A bright green
const infoColor = '#0085ff'; // A medium blue
const warningColor = '#ffc83b'; // A yellow
const dangerColor = '#e63757'; // A deep red
const infoColor = '#17a2b8'; // Correcting the missing variable
const warningColor = '#ffc107'; // Add other colors if needed
// Sales by Month Chart (Bar Chart)
const ctx1 = document.getElementById('CarsSoldByMonthChart').getContext('2d');
new Chart(ctx1, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [{
label: 'Total Cars Sold',
data: [2, 3, 10, 4, 30, 12, 8, 9, 20, 12, 15, 35],
backgroundColor: primaryColor,
borderColor: primaryColor,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
y: {
beginAtZero: true,
grid: { color: 'rgba(0, 0, 0, 0.05)' },
ticks: { color: secondaryColor }
},
x: {
grid: { display: false },
ticks: { color: secondaryColor }
}
}
const chartColors = [
'#7249b6', '#00d074', '#e63757', '#17a2b8', '#ffc107',
'#8193a6', '#28a745', '#6c757d', '#fd7e14', '#dc3545',
'#20c997', '#6f42c1', '#e83e8c', '#6610f2', '#007bff',
'#495057'
];
// Pass translated strings from Django to JavaScript
const translatedStrings = {
numberOfLeads: "{% trans 'Number of Leads' %}",
leads: "{% trans 'Leads' %}",
numberOfOpportunities: "{% trans 'Number of Opportunities' %}"
};
// Get the canvas and message elements
const ctx_leadSources = document.getElementById('leadSourcesChart').getContext('2d');
const leadSourcesMessage = document.getElementById('leadSourcesMessage');
// Parse the JSON data from Django
const leadSourcesLabels = JSON.parse('{{ lead_sources_labels_json|safe }}');
const leadSourcesCounts = JSON.parse('{{ lead_sources_counts_json|safe }}');
// Check if there is any data to display
if (leadSourcesCounts.length > 0) {
// Show the chart and hide the message
ctx_leadSources.canvas.style.display = 'block';
if (leadSourcesMessage) {
leadSourcesMessage.style.display = 'none';
}
});
// Sales by Make (Pie Chart)
const ctx3 = document.getElementById('salesByBrandChart').getContext('2d');
new Chart(ctx3, {
type: 'pie',
data: {
labels: ['Toyota', 'Ford', 'Honda', 'BMW', 'Other'],
datasets: [{
label: 'Car Count by Make',
data: [45, 30, 25, 15, 10],
backgroundColor: [primaryColor, successColor, warningColor, infoColor, secondaryColor],
hoverOffset: 15,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: { color: secondaryColor, font: { size: 14 } }
},
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = ((value / total) * 100).toFixed(2);
return `${label}: ${value} cars (${percentage}%)`;
new Chart(ctx_leadSources, {
type: 'bar',
data: {
labels: leadSourcesLabels,
datasets: [{
label: translatedStrings.numberOfLeads,
data: leadSourcesCounts,
backgroundColor: infoColor,
borderColor: infoColor,
borderWidth: 1
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
title: { display: false },
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
callbacks: {
label: function(context) {
return `${translatedStrings.leads}: ${context.parsed.x}`;
}
}
}
},
scales: {
x: {
beginAtZero: true,
title: { display: true, text: translatedStrings.numberOfLeads, color: secondaryColor },
ticks: {
color: secondaryColor,
callback: function(value) {
if (Number.isInteger(value)) {
return value;
}
}
},
grid: { color: 'rgba(0, 0, 0, 0.05)' }
},
y: {
grid: { display: false },
ticks: { color: secondaryColor }
}
}
}
});
} else {
// Hide the chart and show the message
ctx_leadSources.canvas.style.display = 'none';
if (leadSourcesMessage) {
leadSourcesMessage.style.display = 'flex';
}
});
// Top 5 Lead Sources (Horizontal Bar Chart)
const ctx_leadSources = document.getElementById('leadSourcesChart').getContext('2d');
new Chart(ctx_leadSources, {
type: 'bar',
data: {
labels: ['Showroom', 'Referrals', 'WhatsApp', 'Facebook', 'TikTok'],
datasets: [{
label: 'Number of Leads',
data: [45, 35, 25, 20, 15],
backgroundColor: infoColor,
borderColor: infoColor,
borderWidth: 1
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
title: { display: false },
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
}
},
scales: {
x: {
beginAtZero: true,
title: { display: true, text: 'Number of Leads', color: secondaryColor },
ticks: { color: secondaryColor },
grid: { color: 'rgba(0, 0, 0, 0.05)' }
},
y: {
grid: { display: false },
ticks: { color: secondaryColor }
}
}
}
});
// Appointments by Weekday (Bar Chart)
const ctx_appointments = document.getElementById('appointmentsChart').getContext('2d');
new Chart(ctx_appointments, {
type: 'bar',
data: {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
datasets: [{
label: 'Number of Appointments',
data: [10, 15, 20, 18, 25, 30, 12],
backgroundColor: successColor,
borderColor: successColor,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
title: { display: false },
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
}
},
scales: {
x: {
title: { display: true, text: 'Day of the Week', color: secondaryColor },
ticks: { color: secondaryColor },
grid: { display: false }
},
y: {
beginAtZero: true,
title: { display: true, text: 'Number of Appointments', color: secondaryColor },
ticks: { color: secondaryColor },
grid: { color: 'rgba(0, 0, 0, 0.05)' }
}
}
}
});
}
// Lead Conversion Funnel (Horizontal Bar Chart)
const ctx_funnel = document.getElementById('leadFunnelChart').getContext('2d');
const funnelData = {
labels: ['Initial Contact', 'Qualified Leads', 'Test Drives', 'Negotiation', 'Closed Deals'],
data: [250, 180, 120, 80, 45],
};
new Chart(ctx_funnel, {
type: 'bar',
data: {
labels: funnelData.labels,
datasets: [{
label: 'Number of Leads',
data: funnelData.data,
backgroundColor: [
primaryColor,
infoColor,
successColor,
warningColor,
dangerColor
],
borderColor: [
primaryColor,
infoColor,
successColor,
warningColor,
dangerColor
],
borderWidth: 1
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
title: { display: false },
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
callbacks: {
label: function(context) {
const totalLeads = funnelData.data[0];
const currentLeads = context.parsed.x;
const percentage = ((currentLeads / totalLeads) * 100).toFixed(1);
return `Leads: ${currentLeads} (${percentage}%)`;
const leadFunnelMessage = document.getElementById('leadFunnelMessage');
// Parse the dynamic data from Django
const opportunityStagesLabels = JSON.parse('{{ opportunity_stage_labels_json|safe }}');
const opportunityStagesCounts = JSON.parse('{{ opportunity_stage_counts_json|safe }}');
if (opportunityStagesCounts.length > 0) {
// Show the chart and hide the message
ctx_funnel.canvas.style.display = 'block';
if (leadFunnelMessage) {
leadFunnelMessage.style.display = 'none';
}
// Get a subset of colors based on the number of data points
const backgroundColors = chartColors.slice(0, opportunityStagesCounts.length);
new Chart(ctx_funnel, {
type: 'bar',
data: {
labels: opportunityStagesLabels,
datasets: [{
label: translatedStrings.numberOfOpportunities,
data: opportunityStagesCounts,
// Use the new backgroundColors array
backgroundColor: backgroundColors,
// Set borders to match the fill color
borderColor: backgroundColors,
borderWidth: 1
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
title: { display: false },
tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)',
titleColor: '#fff',
bodyColor: '#fff',
callbacks: {
label: function(context) {
const totalOpportunities = opportunityStagesCounts[0] || 0;
const currentOpportunities = context.parsed.x;
const percentage = totalOpportunities > 0 ? ((currentOpportunities / totalOpportunities) * 100).toFixed(1) : 0;
return `${translatedStrings.leads}: ${currentOpportunities} (${percentage}%)`;
}
}
}
}
},
scales: {
x: {
beginAtZero: true,
display: false
},
y: {
grid: { display: false },
ticks: { color: secondaryColor }
scales: {
x: {
beginAtZero: true,
display: false
},
y: {
grid: { display: false },
ticks: { color: secondaryColor }
}
}
}
});
} else {
// Hide the chart and show the message
ctx_funnel.canvas.style.display = 'none';
if (leadFunnelMessage) {
leadFunnelMessage.style.display = 'flex';
}
});
}
</script>
{% endblock %}

View File

@ -125,7 +125,7 @@
{% endfor %}
</ul>
<div class="d-flex justify-content-end gap-2">
{% comment %} <div class="d-flex justify-content-end gap-2">
{% if dealer.user.userplan.is_expired %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-warning"><span class="fas fa-redo-alt me-2"></span>{{ _("Renew") }}</a>
{% endif %}
@ -135,6 +135,15 @@
{% if not dealer.user.userplan %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-success"><span class="fas fa-cart-plus me-2"></span>{{ _("Subscribe Now") }}</a>
{% endif %}
</div> {% endcomment %}
<div class="d-flex justify-content-end gap-2">
{% if not dealer.user.userplan %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-success"><span class="fas fa-cart-plus me-2"></span>{{ _("Subscribe Now") }}</a>
{% elif dealer.user.userplan.is_expired %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-warning"><span class="fas fa-redo-alt me-2"></span>{{ _("Renew") }}</a>
{% elif dealer.user.userplan.plan.name != "Enterprise" %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-primary"><span class="fas fa-rocket me-2"></span>{{ _("Upgrade Plan") }}</a>
{% endif %}
</div>
</div>
</div>

View File

@ -3,16 +3,10 @@
{% block content %}
{% if request.user.is_authenticated %}
<div id="dashboard-content"
hx-get="{% if request.is_dealer %}
{% url 'dealer_dashboard' %}
{% elif request.is_manger %}
{% url 'manager_dashboard' %}
{% elif request.is_sales %}
{% url 'sales_dashboard' %}
{% elif request.is_inventory %}
{% url 'inventory_dashboard' %}
{% else %}
{% url 'accountant_dashboard' %}
hx-get="{% if request.is_sales and not request.is_manager %}
{% url 'sales_dashboard' request.dealer.slug %}
{% else %}
{% url 'general_dashboard' request.dealer.slug %}
{% endif %}"
hx-trigger="load"
hx-target="#dashboard-content"
@ -23,4 +17,6 @@
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -3,75 +3,72 @@
{% block title %}
{{ _("View Bank Account") }}
{% endblock title %}
{% block content %}
<!-- Delete Modal -->
<div class="modal fade"
id="deleteModal"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="deleteModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-sm ">
<div class="modal-content rounded">
<div class="modal-body d-flex justify-content-center">
<h1 class="text-danger me-2">
<i class="bi bi-exclamation-diamond-fill"></i>
</h1>
<span class="text-danger">{% trans "Are you sure you want to delete this bank account?" %}</span>
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4 shadow-lg border-0">
<div class="modal-body p-5 text-center">
<div class="d-flex justify-content-center align-items-center mb-4">
<svg class="h-6 w-6 text-danger" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.398 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
<h5 class="mb-2">{% trans "Are you sure you want to delete this bank account?" %}</h5>
<p class="text-secondary small">This action cannot be undone.</p>
</div>
<div class="btn-group">
<button type="button"
class="btn btn-sm btn-phoenix-secondary"
data-bs-dismiss="modal">{% trans 'No' %}</button>
<a type="button"
class="btn btn-sm btn-phoenix-danger"
href="{% url 'bank_account_delete' request.dealer.slug bank_account.pk %}">{% trans 'Yes' %}</a>
<div class="modal-footer d-flex justify-content-center border-0 p-3 bg-200 rounded-bottom-4">
<button type="button" class="btn btn-sm btn-phoenix-secondary me-2 rounded-pill" data-bs-dismiss="modal">{% trans 'No' %}</button>
<a href="{% url 'bank_account_delete' request.dealer.slug bank_account.pk %}" class="btn btn-sm btn-phoenix-danger rounded-pill">{% trans 'Yes' %}</a>
</div>
</div>
</div>
</div>
<div class="row my-5">
<div class="card rounded ">
<div class="card-header bg-primary text-white ">
<p class="mb-0">{{ _("Bank Account Details") }}</p>
<!-- Main Content Container -->
<div class="container my-5">
<div class="card rounded-4 border-0">
<!-- Card Header -->
<div class="card-header p-4 rounded-top-4">
<h4 class="mb-0 fw-bold">{{ _("Bank Account Details") }}</h4>
</div>
<div class="card-body">
<div class="row">
<!-- Card Body - Details Section -->
<div class="card-body p-5">
<div class="row g-4">
<div class="col-md-6">
<p>
<strong>{{ _("Bank Account Name") }}:</strong> {{ bank_account.name }}
</p>
<p>
<strong>{{ _("Cash Account") }}:</strong> {{ bank_account }}
</p>
<p class="mb-1 text-muted">{{ _("Bank Account Name") }}</p>
<h5 class="fw-bold text-dark">{{ bank_account.name }}</h5>
</div>
<div class="col-md-6">
<p>
<strong>{{ _("Amount") }}:</strong> {{ bank_account.amount }}
</p>
<p class="mb-1 text-muted">{{ _("Amount") }}</p>
<h5 class="fw-bold text-success">{{ bank_account.amount }}</h5>
</div>
<div class="col-12">
<p class="mb-1 text-muted">{{ _("Cash Account") }}</p>
<h5 class="fw-bold text-dark">{{ bank_account }}</h5>
</div>
</div>
</div>
<div class="card-footer d-flex ">
<!-- Card Footer - Action Buttons -->
<div class="card-footer bg-light border-0 p-4 d-flex justify-content-end align-items-center">
{% if perms.django_ledger.change_bankaccountmodel %}
<a class="btn btn-sm btn-phoenix-primary me-1"
<a class="btn btn-sm btn-phoenix-primary me-2 rounded-pill"
href="{% url 'bank_account_update' request.dealer.slug bank_account.pk %}">
<!--<i class="bi bi-pencil-square"></i> -->
{{ _("Edit") }}
</a>
{% endif %}
{% if perms.django_ledger.delete_bankaccountmodel %}
<a class="btn btn-sm btn-phoenix-danger me-1"
<a class="btn btn-sm btn-phoenix-danger me-2 rounded-pill"
data-bs-toggle="modal"
data-bs-target="#deleteModal">
<!--<i class="bi bi-trash-fill"></i>-->
{{ _("Delete") }}
</a>
{% endif %}
<a class="btn btn-sm btn-phoenix-secondary"
<a class="btn btn-sm btn-phoenix-secondary rounded-pill"
href="{% url 'bank_account_list' request.dealer.slug %}">
<!--<i class="bi bi-arrow-left-square-fill"></i>-->
{% trans "Back to List" %}
</a>
</div>

View File

@ -1,8 +1,7 @@
{% extends "base.html" %}
{% extends 'base.html' %}
{% load i18n %}
{% load crispy_forms_filters %}
{% block title %}
{# Check if an 'object' exists in the context #}
{% if object %}
{% trans 'Update Purchase Order' %}
{% else %}
@ -11,39 +10,47 @@
{% endblock %}
{% block content %}
<!---->
<div class="row justify-content-center mt-5 mb-3">
<div class="col-lg-8 col-md-10 ">
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-300 py-3 border-0 rounded-top-3">
<h3 class="mb-0 fs-4 text-center ">
{{ _("Add New Purchase Order") }}<span class="fas fa-cart-plus ms-2 text-primary "></span>
</h3>
<main class="d-flex align-items-center justify-content-center min-vh-100 py-5">
<div class="col-12 col-lg-8 col-xl-7">
<div class="card shadow-lg border-0 rounded-4 overflow-hidden animate__animated animate__fadeInUp">
<div class="card-header bg-gradient py-4 border-0 rounded-top-4">
<div class="d-flex justify-content-between align-items-center text-white">
<a href="{% url 'purchase_order_list' request.dealer.slug request.entity.slug %}" class="btn btn-sm btn-light-subtle text-primary">
<i class="fa-solid fa-circle-arrow-left me-2"></i>{% trans "Back" %}
</a>
<h3 class="mb-0 fs-4 fw-bold text-center flex-grow-1">
{% if object %}
{% trans 'Update Purchase Order' %}<i class="fa-solid fa-file-pen ms-2"></i>
{% else %}
{% trans 'Create Purchase Order' %}<i class="fa-solid fa-file-circle-plus ms-2"></i>
{% endif %}
</h3>
</div>
</div>
<div class="card-body bg-light-subtle">
<form class="row g-3 " method="post" class="form" enctype="multipart/form-data" novalidate >
{% csrf_token %}
{{ redirect_field }}
{{ form|crispy }}
{% for error in form.errors %}
<div class="text-danger">{{ error }}</div>
{% endfor %}
<div class="card-body p-4 p-md-5">
<form method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %}
{{ redirect_field }}
{{ form|crispy }}
<hr class="my-2">
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
<button class="btn btn-lg btn-phoenix-primary md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
<a href="{% url 'purchase_order_list' request.dealer.slug request.entity.slug %}" class="btn btn-lg btn-phoenix-secondary"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
{% if form.errors %}
<div class="alert alert-danger mt-4" role="alert">
<h4 class="alert-heading small">{% trans "Please correct the following errors:" %}</h4>
<ul class="mb-0">
{% for field, errors in form.errors.items %}
<li><strong>{{ field|capfirst }}:</strong> {% for error in errors %}{{ error }}{% endfor %}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="d-grid gap-3 d-sm-flex justify-content-sm-center mt-4">
<button type="submit" class="btn btn-phoenix-primary btn-lg px-5"><i class="fa-solid fa-floppy-disk me-2"></i>{% trans "Save" %}</button>
<a href="{% url 'purchase_order_list' request.dealer.slug request.entity.slug %}" class="btn btn-phoenix-secondary btn-lg px-5"><i class="fa-solid fa-ban me-2"></i>{% trans "Cancel" %}</a>
</div>
</form>
</div>
</div>
</div>
<!---->
{% endblock %}
</main>
{% endblock %}

View File

@ -141,7 +141,7 @@
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
<h3 class="mb-0 fs-4 text-center">
{% trans "Create Quotation" %}<i class="fa-regular fa-file-lines text-primary me-2"></i>
{% trans "Create Quotation" %}<i class="fa-regular fa-file-lines text-primary ms-2"></i>
</h3>
</div>
<div class="card-body bg-light-subtle">

View File

@ -1,108 +1,118 @@
{% extends "base.html" %}
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block title %}
{{ user_.name }}
{% endblock title %}
{% block content %}
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<div class="d-flex align-items-center">
<a href="{% url 'user_list' request.dealer.slug %}" class="btn btn-phoenix-secondary btn-sm me-3">
<i class="fa-regular fa-circle-left me-2"></i>{% trans "Back to Staffs" %}
</a>
<h1 class="h5 fw-bold mb-0">{% trans "Staff Profile" %}</h1>
</div>
<div>
<a href="{% url 'user_update' request.dealer.slug user_.slug %}" class="btn btn-phoenix-primary btn-sm me-2">
<i class="fa-solid fa-pen-to-square me-2"></i>{% trans "Edit" %}
</a>
<button class="btn btn-phoenix-danger btn-sm delete-btn" data-url="{% url 'user_delete' request.dealer.slug user_.slug %}"
data-message='{% trans "Are you sure you want to delete this user?" %}'
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash-alt me-2"></i>{% trans "Delete" %}
</button>
</div>
<div class="container py-4">
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
<div class="d-flex align-items-center mb-3 mb-md-0">
<a href="{% url 'user_list' request.dealer.slug %}" class="btn btn-phoenix-secondary btn-sm me-3">
<i class="fa-regular fa-circle-left me-2"></i>{% trans "Back to Staff" %}
</a>
<h1 class="h4 fw-bold mb-0">{% trans "Staff Profile" %}</h1>
</div>
<div class="d-flex flex-wrap gap-2">
<a href="{% url 'user_update' request.dealer.slug user_.slug %}" class="btn btn-phoenix-primary btn-sm">
<i class="fa-solid fa-pen-to-square me-2"></i>{% trans "Edit Profile" %}
</a>
<button class="btn btn-phoenix-danger btn-sm delete-btn" data-url="{% url 'user_delete' request.dealer.slug user_.slug %}"
data-message='{% trans "Are you sure you want to delete this user?" %}'
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash-alt me-2"></i>{% trans "Delete" %}
</button>
</div>
</div>
<div class="row g-3">
<div class="col-lg-4">
<div class="card h-100 border-0 shadow-sm rounded-3">
<div class="card-body text-center p-3">
<div class="position-relative d-inline-block mb-3">
{% if user_.logo %}
<img class="rounded-circle border border-4 border-body-tertiary"
src="{{ user_.logo.url }}" alt="{{ user_.name }}"
style="object-fit: cover; width: 100px; height: 100px;">
{% else %}
<div class="rounded-circle d-inline-flex align-items-center justify-content-center bg-primary text-white"
style="width: 100px; height: 100px; font-size: 2.5rem;">
{{ user_.name|first|upper }}
</div>
{% endif %}
</div>
<h2 class="h6 fw-bold mb-1">{{ user_.name }}</h2>
<p class="small mb-3"> <a href="mailto:{{ user_.email }}" class="text-secondary">{{ user_.email }}</a></p>
<div class="d-grid gap-1">
<a href="tel:{{ user_.phone_number }}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-phone-alt me-2"></i>{{ user_.phone_number|default:"N/A" }}
</a>
</div>
<div class="row g-4">
<div class="col-lg-4">
<div class="card h-100 border-0 shadow-sm rounded-4 hover-lift">
<div class="card-body d-flex flex-column align-items-center justify-content-center p-4">
<div class="position-relative d-inline-block mb-3">
{% if user_.logo %}
<img class="rounded-circle border border-4 border-body-tertiary"
src="{{ user_.logo.url }}" alt="{{ user_.name }}"
style="object-fit: cover; width: 120px; height: 120px;">
{% else %}
<div class="rounded-circle d-inline-flex align-items-center justify-content-center bg-primary text-white border border-4 border-body-tertiary"
style="width: 120px; height: 120px; font-size: 3rem;">
{{ user_.name|first|upper }}
</div>
{% endif %}
</div>
<h2 class="h5 fw-bold mb-1 text-center">{{ user_.name }}</h2>
<p class="small text-secondary mb-2">
<span class="badge rounded-pill bg-light text-dark fw-bold">{{ user_.groups.first.name|default:"No Role" }}</span>
</p>
<p class="small text-muted mb-3 text-center">
<a href="mailto:{{ user_.email }}" >{{ user_.email }}</a>
</p>
<div class="d-grid w-40">
<a href="tel:{{ user_.phone_number }}" class="btn btn-sm rounded-pill d-flex align-items-center justify-content-center gap-2">
<i class="fas fa-phone-alt"></i><span>{{ user_.phone_number|default:"N/A" }}</span>
</a>
</div>
</div>
</div>
</div>
<div class="col-lg-8">
<div class="card h-100 border-0 shadow-sm rounded-3">
<div class="card-body p-3">
<h4 class="mb-3 text-primary h6">{% trans "Personal Information" %}</h4>
<div class="row g-2 mb-4">
<div class="col-md-6">
<p class="small mb-0">{% trans "Arabic Name" %}</p>
<p class="fw-bold small mb-0">{{ user_.arabic_name|default:"N/A" }}</p>
</div>
<div class="col-md-6">
<p class="small mb-0">{% trans "Roles" %}</p>
<p class="fw-bold small mb-0">
{% for group in user_.groups %}
{{ group.name }} {% if not forloop.last %}&middot;{% endif %}
{% empty %}
<span class="text-muted">N/A</span>
{% endfor %}
</p>
</div>
</div>
<div class="col-lg-8">
<div class="card h-100 border-0 shadow-sm rounded-4">
<div class="card-body p-4">
<h5 class="mb-4 text-primary fw-bold">{% trans "Personal Information" %}</h5>
<dl class="row g-3 small">
<dt class="col-sm-3 text-muted">{% trans "Name (Arabic)" %}</dt>
<dd class="col-sm-9 fw-bold">{{ user_.arabic_name|default:"N/A" }}</dd>
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0 text-primary h6">{% trans "Assigned Groups" %}</h4>
<a class="btn btn-sm btn-phoenix-primary" href="{% url 'user_groups' request.dealer.slug user_.slug %}">
<i class="fa-solid fa-users me-2"></i>{% trans "Manage Groups" %}
</a>
</div>
<div class="table-responsive scrollbar">
<table class="table table-hover table-striped fs--1 mb-0">
<thead>
<tr>
<th scope="col" class="text-nowrap">{% trans 'Group Name'|capfirst %}</th>
<dt class="col-sm-3 text-muted">{% trans "Last Login" %}</dt>
<dd class="col-sm-9 fw-bold">
{{user_.user.last_login|date:"D, M d, Y H:i"|default:"N/A" }}
</dd>
<dt class="col-sm-3 text-muted">{% trans "Status" %}</dt>
<dd class="col-sm-9">
{% if user_.user.is_active %}
<span class="badge rounded-pill bg-success">{% trans "Active" %}</span>
{% else %}
<span class="badge rounded-pill bg-danger">{% trans "Inactive" %}</span>
{% endif %}
</dd>
</dl>
<hr class="my-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0 text-primary fw-bold">{% trans "Assigned Groups" %}</h5>
<a class="btn btn-sm btn-phoenix-primary rounded-pill" href="{% url 'user_groups' request.dealer.slug user_.slug %}">
<i class="fa-solid fa-users me-2"></i>{% trans "Manage Groups" %}
</a>
</div>
<div class="table-responsive">
<table class="table table-hover table-borderless fs--1 mb-0">
<thead class="bg-body-tertiary">
<tr>
<th scope="col">{% trans 'Group Name'|capfirst %}</th>
</tr>
</thead>
<tbody>
{% for group in user_.groups.all %}
<tr class="align-middle">
<td class="small text-dark">{{ group.name }}</td>
</tr>
</thead>
<tbody>
{% for group in user_.groups %}
<tr>
<td class="small">{{ group }}</td>
</tr>
{% empty %}
<tr>
<td class="text-secondary small">{% trans "This user is not assigned to any groups." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% empty %}
<tr>
<td class="text-secondary small">{% trans "This user is not assigned to any groups." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% include 'modal/delete_modal.html' %}
</div>
{% include 'modal/delete_modal.html' %}
{% endblock %}

View File

@ -7,94 +7,96 @@
{% endblock title %}
{% block content %}
<div class="container py-5">
{% if users or request.GET.q %}
<div class="d-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-muted d-flex align-items-center">
<i class="fa-solid fa-user-tie text-primary me-3 fs-2"></i>
{% trans "Staffs" %}
</h1>
{% if request.user.userplan %}
<div class="d-flex">
<a href="{% url 'user_create' request.dealer.slug %}" class="btn btn-phoenix-primary me-2 shadow-sm">
<i class="fa-solid fa-plus me-2"></i>{% trans "Add New Staff" %}
</a>
<a href="{% url 'group_list' request.dealer.slug %}" class="btn btn-phoenix-secondary shadow-sm">
<i class="fa-solid fa-users me-2"></i>{% trans "Manage Groups" %}
</a>
</div>
{% endif %}
</div>
{%if users or request.GET.q%}
<div class="container py-4">
<div class="d-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0">{% trans "Staffs" %}<i class="fa-solid fa-user-tie text-primary ms-2"></i></h1>
{% if request.user.userplan %}
<div>
<a href="{% url 'user_create' request.dealer.slug %}" class="btn btn-phoenix-primary me-2">
<i class="fa-solid fa-plus me-2"></i>{% trans "Add New Staff" %}
</a>
<a href="{% url 'group_list' request.dealer.slug %}" class="btn btn-phoenix-secondary">
<i class="fa-solid fa-user-group me-2"></i>{% trans "Manage Groups" %}
</a>
</div>
{% endif %}
</div>
<div class="card shadow-sm border-0">
<div class="card-body p-0">
<div class="table-responsive scrollbar">
<table class="table table-hover table-striped mb-0">
<thead>
<tr class="bg-body-highlight">
<th>{% trans 'name'|capfirst %}</th>
<th>{% trans 'email'|capfirst %}</th>
<th>{% trans 'phone number'|capfirst %}</th>
<th>{% trans 'role'|capfirst %}</th>
<th>{% trans 'actions'|capfirst %}</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<div class="card border-0 shadow-sm rounded-lg">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover table-borderless mb-0">
<thead class="bg-light">
<tr>
<td class="align-middle white-space-nowrap ps-2">
<div class="d-flex align-items-center">
<div class="avatar avatar-tiny me-2">
{% if user.logo %}
<img class="avatar-img rounded-circle"
src="{{ user.thumbnail.url }}"
onerror="this.src='/static/user-logo.jpg'"
alt="{{ user.name }}'s logo">
{% else %}
<div class="rounded-circle bg-light d-flex justify-content-center align-items-center" style="width: 2rem; height: 2rem; font-size: 0.8rem;">
{{ user.name|first|upper }}
</div>
{% endif %}
</div>
<a class="fw-bold text-decoration-none text-dark"
href="{% url 'user_detail' request.dealer.slug user.slug %}">
{{ user.name }}
</a>
</div>
</td>
<td class="align-middle white-space-nowrap">{{ user.email }}</td>
<td class="align-middle white-space-nowrap">{{ user.phone_number }}</td>
<td class="align-middle white-space-nowrap">
{% for group in user.groups %}
<span class="badge bg-primary me-1">{{ group.name|title }}</span>
{% endfor %}
</td>
<td class="align-middle white-space-nowrap">
<a class="btn btn-phoenix-success"
href="{% url 'user_detail' request.dealer.slug user.slug %}">
<i class="fa-solid fa-eye me-1"></i>{% trans 'View' %}
</a>
</td>
<th class="py-3 px-4">{% trans 'Name'|capfirst %}</th>
<th class="py-3 px-4">{% trans 'Email'|capfirst %}</th>
<th class="py-3 px-4">{% trans 'Phone number'|capfirst %}</th>
<th class="py-3 px-4">{% trans 'Role'|capfirst %}</th>
<th class="py-3 px-4 text-center">{% trans 'Actions'|capfirst %}</th>
</tr>
{% endfor %}
</tbody>
</table>
</thead>
<tbody>
{% for user in users %}
<tr class="border-bottom">
<td class="align-middle py-3 px-4">
<div class="d-flex align-items-center">
<div class="avatar avatar-sm me-3">
{% if user.logo %}
<img class="avatar-img rounded-circle"
src="{{ user.thumbnail.url }}"
onerror="this.src='/static/user-logo.jpg'"
alt="{{ user.name }}'s logo">
{% else %}
<div class="rounded-circle bg-light d-flex justify-content-center align-items-center text-secondary" style="width: 2.5rem; height: 2.5rem; font-size: 1rem;">
{{ user.name|first|upper }}
</div>
{% endif %}
</div>
<a class="fw-bold text-decoration-none text-dark"
href="{% url 'user_detail' request.dealer.slug user.slug %}">
{{ user.name }}
</a>
</div>
</td>
<td class="align-middle py-3 px-4 text-muted">{{ user.email }}</td>
<td class="align-middle py-3 px-4 text-muted">{{ user.phone_number }}</td>
<td class="align-middle py-3 px-4">
{% for group in user.groups %}
<span class="badge bg-primary-subtle text-primary border border-primary-subtle rounded-pill me-1 fw-normal">{{ group.name|title }}</span>
{% endfor %}
</td>
<td class="align-middle py-3 px-4 text-center">
<a class="btn btn-sm btn-phoenix-secondary"
href="{% url 'user_detail' request.dealer.slug user.slug %}">
<i class="fa-solid fa-eye me-1"></i>{% trans 'View' %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% if is_paginated %}
<div class="d-flex justify-content-center mt-3">
<div class="d-flex justify-content-center mt-4">
{% include 'partials/pagination.html' %}
</div>
{% endif %}
</div>
{% else %}
{% if request.user.userplan %}
{% url "user_create" request.dealer.slug as create_staff_url %}
{% include "empty-illustration-page.html" with value="staff" url=create_staff_url %}
{% else %}
{% url "pricing_page" request.dealer.slug as pricing_page_url %}
{% include "message-illustration.html" with value1=_("No active plan, Please create a subscription plan.") value2=_("Buy Plan") url=pricing_page_url %}
{% if request.user.userplan %}
{% url "user_create" request.dealer.slug as create_staff_url %}
{% include "empty-illustration-page.html" with value="staff" url=create_staff_url %}
{% else %}
{% url "pricing_page" request.dealer.slug as pricing_page_url %}
{% include "message-illustration.html" with value1=_("No active plan, Please create a subscription plan.") value2=_("Buy Plan") url=pricing_page_url %}
{% endif %}
{% endif %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,22 +1,39 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card mt-5">
<div class="card-body">
<h2 class="card-title">Set New Password</h2>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">
Change Password
</button>
</form>
<main class="d-flex align-items-center justify-content-center min-vh-100 py-5">
<div class="col-12 col-sm-10 col-md-8 col-lg-6 col-xl-5">
<div class="card shadow-lg border-0 rounded-4 overflow-hidden animate__animated animate__fadeInUp" style="background-color: #f8f9fa;">
<div class="card-body p-4 p-md-5">
<div class="text-center mb-4">
<h1 class="h3 fw-bold text-dark mb-2">{% trans "Set New Password" %}</h1>
<p class="text-muted">{% trans "Enter your new password below." %}</p>
</div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-floating mb-3">
{{ field.errors }}
{{ field }}
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
</div>
{% endfor %}
<div class="d-grid mt-4">
<button type="submit" class="btn btn-primary btn-lg rounded-pill fw-bold">
{% trans "Change Password" %}
</button>
</div>
</form>
</div>
<div class="card-footer bg-light border-0 py-3 text-center">
<small class="text-muted">
{% trans "Remember to choose a strong password." %}
</small>
</div>
</div>
</div>
</div>
</main>
{% endblock %}