Autosave: 20260207-145753
This commit is contained in:
parent
37d8922069
commit
5021756176
Binary file not shown.
@ -21,7 +21,7 @@
|
||||
<!-- Stats Cards -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card glass-card border-0 p-3 stat-card">
|
||||
<div class="card glass-card border-0 p-3 stat-card h-100">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="stat-icon bg-primary text-white bg-opacity-10 text-primary rounded-3 p-3 me-3">
|
||||
<i class="bi bi-cash-stack fs-4 text-primary"></i>
|
||||
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card glass-card border-0 p-3 stat-card">
|
||||
<div class="card glass-card border-0 p-3 stat-card h-100">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="stat-icon bg-success bg-opacity-10 rounded-3 p-3 me-3">
|
||||
<i class="bi bi-cart-check fs-4 text-success"></i>
|
||||
@ -47,7 +47,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card glass-card border-0 p-3 stat-card">
|
||||
<div class="card glass-card border-0 p-3 stat-card h-100">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="stat-icon bg-info bg-opacity-10 rounded-3 p-3 me-3">
|
||||
<i class="bi bi-box-seam fs-4 text-info"></i>
|
||||
@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card glass-card border-0 p-3 stat-card">
|
||||
<div class="card glass-card border-0 p-3 stat-card h-100">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="stat-icon bg-warning bg-opacity-10 rounded-3 p-3 me-3">
|
||||
<i class="bi bi-people fs-4 text-warning"></i>
|
||||
@ -74,21 +74,84 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Charts Row 1: Monthly Trend & Category Distribution -->
|
||||
<div class="row g-4 mb-4">
|
||||
<!-- Sales Chart -->
|
||||
<!-- Monthly Sales Chart -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card border-0 shadow-sm p-4 h-100">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="fw-bold mb-0">{% trans "Sales Revenue" %}</h5>
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary">{% trans "Last 7 Days" %}</span>
|
||||
<div>
|
||||
<h5 class="fw-bold mb-0">{% trans "Sales Analytics" %}</h5>
|
||||
<small class="text-muted">{% trans "Monthly revenue performance" %}</small>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<button type="button" class="btn btn-outline-primary active" id="btnMonthly">{% trans "Monthly" %}</button>
|
||||
<button type="button" class="btn btn-outline-primary" id="btnDaily">{% trans "Last 7 Days" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="position: relative; height: 300px; width: 100%;">
|
||||
<canvas id="salesChart"></canvas>
|
||||
<canvas id="mainSalesChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inventory Status -->
|
||||
<!-- Sales by Category -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm p-4 h-100">
|
||||
<h5 class="fw-bold mb-4">{% trans "Sales by Category" %}</h5>
|
||||
<div style="position: relative; height: 250px; width: 100%;">
|
||||
<canvas id="categoryChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Top Products & Payment Methods & Inventory -->
|
||||
<div class="row g-4 mb-4">
|
||||
<!-- Top Selling Products -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm p-4 h-100">
|
||||
<h5 class="fw-bold mb-4">{% trans "Top Selling Products" %}</h5>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>{% trans "Product" %}</th>
|
||||
<th class="text-end">{% trans "Qty" %}</th>
|
||||
<th class="text-end">{% trans "Revenue" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in top_products %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="fw-semibold text-dark">
|
||||
{% if LANGUAGE_CODE == 'ar' %}{{ item.product__name_ar }}{% else %}{{ item.product__name_en }}{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end">{{ item.total_qty|floatformat:0 }}</td>
|
||||
<td class="text-end fw-bold text-success">{{ site_settings.currency_symbol }}{{ item.total_rev|floatformat:1 }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="3" class="text-center text-muted">{% trans "No sales data yet." %}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment Methods -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm p-4 h-100">
|
||||
<h5 class="fw-bold mb-4">{% trans "Payment Methods" %}</h5>
|
||||
<div style="position: relative; height: 250px; width: 100%;">
|
||||
<canvas id="paymentChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inventory Alerts -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm p-4 h-100">
|
||||
<h5 class="fw-bold mb-4">{% trans "Low Stock Alerts" %}</h5>
|
||||
@ -126,25 +189,16 @@
|
||||
<p class="mt-3 text-muted">{% trans "All stock levels are healthy!" %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h5 class="fw-bold mb-4 mt-4">{% trans "Expired Items Alert" %}</h5>
|
||||
|
||||
{% if expired_count > 0 %}
|
||||
<div class="alert alert-danger border-0 rounded-4 d-flex align-items-center mb-0">
|
||||
<div class="alert alert-danger border-0 rounded-4 d-flex align-items-center mt-3 mb-0">
|
||||
<i class="bi bi-exclamation-triangle-fill fs-4 me-3"></i>
|
||||
<div>
|
||||
<p class="mb-0 fw-bold">{{ expired_count }} {% trans "Items have expired!" %}</p>
|
||||
<a href="{% url 'inventory' %}#expired-list" class="alert-link small">{% trans "View and manage expired stock" %}</a>
|
||||
<a href="{% url 'inventory' %}#expired-list" class="alert-link small">{% trans "View expired stock" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-shield-check text-success display-6"></i>
|
||||
<p class="mt-2 text-muted small">{% trans "No expired items in stock." %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-auto pt-3 border-top">
|
||||
<a href="{% url 'inventory' %}" class="btn btn-light btn-sm w-100 fw-bold">{% trans "View Full Inventory" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -203,49 +257,115 @@
|
||||
{% block scripts %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
const ctx = document.getElementById('salesChart').getContext('2d');
|
||||
const salesChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
// --- Data from Django ---
|
||||
const monthlyLabels = {{ monthly_labels|safe }};
|
||||
const monthlyData = {{ monthly_data|safe }};
|
||||
const dailyLabels = {{ chart_labels|safe }};
|
||||
const dailyData = {{ chart_data|safe }};
|
||||
|
||||
const categoryLabels = {{ category_labels|safe }};
|
||||
const categoryData = {{ category_data|safe }};
|
||||
|
||||
const paymentLabels = {{ payment_labels|safe }};
|
||||
const paymentData = {{ payment_data|safe }};
|
||||
const currency = "{{ site_settings.currency_symbol }}";
|
||||
|
||||
// --- Main Sales Chart ---
|
||||
const ctxMain = document.getElementById('mainSalesChart').getContext('2d');
|
||||
let mainChart = new Chart(ctxMain, {
|
||||
type: 'bar', // Default to Monthly Bar Chart
|
||||
data: {
|
||||
labels: {{ chart_labels|safe }},
|
||||
labels: monthlyLabels,
|
||||
datasets: [{
|
||||
label: '{% trans "Revenue" %} ({{ site_settings.currency_symbol }})',
|
||||
data: {{ chart_data|safe }},
|
||||
borderColor: '#2E5BFF',
|
||||
backgroundColor: 'rgba(46, 91, 255, 0.1)',
|
||||
borderWidth: 3,
|
||||
tension: 0.4,
|
||||
fill: true,
|
||||
pointBackgroundColor: '#fff',
|
||||
pointBorderColor: '#2E5BFF',
|
||||
pointBorderWidth: 2,
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6
|
||||
label: '{% trans "Revenue" %} (' + currency + ')',
|
||||
data: monthlyData,
|
||||
backgroundColor: '#2E5BFF',
|
||||
borderRadius: 4,
|
||||
barPercentage: 0.6,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: {
|
||||
y: { beginAtZero: true, grid: { borderDash: [2, 2] } },
|
||||
x: { grid: { display: false } }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle Monthly / Daily
|
||||
document.getElementById('btnMonthly').addEventListener('click', function() {
|
||||
mainChart.config.type = 'bar';
|
||||
mainChart.data.labels = monthlyLabels;
|
||||
mainChart.data.datasets[0].data = monthlyData;
|
||||
mainChart.update();
|
||||
this.classList.add('active');
|
||||
document.getElementById('btnDaily').classList.remove('active');
|
||||
});
|
||||
|
||||
document.getElementById('btnDaily').addEventListener('click', function() {
|
||||
mainChart.config.type = 'line';
|
||||
mainChart.data.labels = dailyLabels;
|
||||
mainChart.data.datasets[0].data = dailyData;
|
||||
mainChart.data.datasets[0].borderColor = '#2E5BFF';
|
||||
mainChart.data.datasets[0].backgroundColor = 'rgba(46, 91, 255, 0.1)';
|
||||
mainChart.data.datasets[0].fill = true;
|
||||
mainChart.data.datasets[0].tension = 0.4;
|
||||
mainChart.update();
|
||||
this.classList.add('active');
|
||||
document.getElementById('btnMonthly').classList.remove('active');
|
||||
});
|
||||
|
||||
// --- Category Chart (Doughnut) ---
|
||||
if (categoryData.length > 0) {
|
||||
new Chart(document.getElementById('categoryChart'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: categoryLabels,
|
||||
datasets: [{
|
||||
data: categoryData,
|
||||
backgroundColor: ['#2E5BFF', '#00C9A7', '#FFC107', '#886CFF', '#FF5252', '#00D1FF'],
|
||||
borderWidth: 0
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
cutout: '70%',
|
||||
plugins: {
|
||||
legend: { position: 'right', labels: { boxWidth: 12 } }
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Placeholder if no data
|
||||
document.getElementById('categoryChart').parentNode.innerHTML = '<div class="text-center text-muted py-5">{% trans "No category data available" %}</div>';
|
||||
}
|
||||
|
||||
// --- Payment Method Chart (Pie) ---
|
||||
if (paymentData.length > 0) {
|
||||
new Chart(document.getElementById('paymentChart'), {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: paymentLabels,
|
||||
datasets: [{
|
||||
data: paymentData,
|
||||
backgroundColor: ['#00C9A7', '#2E5BFF', '#FFC107', '#886CFF'],
|
||||
borderWidth: 0
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
color: 'rgba(0, 0, 0, 0.05)'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
legend: { position: 'bottom', labels: { boxWidth: 12 } }
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
document.getElementById('paymentChart').parentNode.innerHTML = '<div class="text-center text-muted py-5">{% trans "No payment data available" %}</div>';
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
2420
core/views.py
2420
core/views.py
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user