haikal/templates/dashboards/sales_dashboard.html
2025-09-24 11:07:31 +03:00

279 lines
13 KiB
HTML

{% extends 'base.html' %}
{% load i18n %}
{% block title %}
{% trans "Sales Dashboard" %}
{% endblock %}
{% 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 "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">
<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 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 "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-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>
<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">{% trans "Top Lead Sources" %}&nbsp;&nbsp;&nbsp;&nbsp;{% trans "Total Leads: " %}{{total_leads}}</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">{% 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>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
{% endblock content %}
{% block customJS %}
<script>
// 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 dangerColor = '#e63757'; // A deep red
const infoColor = '#17a2b8'; // Correcting the missing variable
const warningColor = '#ffc107'; // Add other colors if needed
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';
}
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';
}
}
// Lead Conversion Funnel (Horizontal Bar Chart)
const ctx_funnel = document.getElementById('leadFunnelChart').getContext('2d');
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 }
}
}
}
});
} else {
// Hide the chart and show the message
ctx_funnel.canvas.style.display = 'none';
if (leadFunnelMessage) {
leadFunnelMessage.style.display = 'flex';
}
}
</script>
{% endblock %}