39728-vm/index.php
2026-04-26 17:31:59 +00:00

509 lines
23 KiB
PHP

<?php
require_once __DIR__ . '/includes/app.php';
$user = require_auth();
$pageTitle = tr('لوحة التحكم', 'Dashboard');
$activeNav = 'dashboard';
$bodyClass = 'theme-preview-sunset';
$dbError = null;
$metrics = ['today_sales' => 0, 'today_revenue' => 0.0, 'pos_count' => 0, 'normal_count' => 0, 'recent' => []];
$reportMetrics = ['branch_totals' => [], 'payment_totals' => [], 'product_totals' => []];
try {
$metrics = dashboard_metrics();
$reportMetrics = report_metrics();
} catch (Throwable $e) {
$dbError = $e->getMessage();
}
$branchLabels = [];
$branchData = [];
foreach ($reportMetrics['branch_totals'] as $code => $total) {
$branchLabels[] = branch_label((string)$code);
$branchData[] = (float)$total;
}
$productLabels = [];
$productData = [];
$topProducts = array_slice($reportMetrics['product_totals'], 0, 5, true);
foreach ($topProducts as $sku => $qty) {
$productLabels[] = product_label((string)$sku);
$productData[] = (int)$qty;
}
$monthlyLabels = [];
$monthlyData = [];
if (isset($reportMetrics['monthly_totals'])) {
foreach ($reportMetrics['monthly_totals'] as $month => $total) {
// format month: "2026-04" -> "Apr 2026"
$time = strtotime($month . '-01');
$monthlyLabels[] = date('M Y', $time);
$monthlyData[] = (float)$total;
}
}
require __DIR__ . '/includes/header.php';
?>
<?php if ($dbError): ?>
<div class="alert alert-danger mb-4"><i class="bi bi-exclamation-triangle me-2"></i><?= h(tr('تعذر تحميل بيانات قاعدة البيانات حالياً:', 'Database data could not be loaded right now:')) ?> <?= h($dbError) ?></div>
<?php endif; ?>
<div class="dashboard-sunset-sample">
<section class="dashboard-hero card border-0 overflow-hidden mb-4">
<div class="card-body p-4 p-lg-5">
<div class="row align-items-center g-4">
<div class="col-xl-8">
<span class="dashboard-kicker">
<i class="bi bi-stars"></i>
<?= h(tr('معاينة الهوية الرسمية الجديدة', 'Official style preview')) ?>
</span>
<h1 class="dashboard-hero-title mt-3 mb-3"><?= h(tr('لوحة تحكم بلمسة غروب أكثر تميزاً للإدارة اليومية', 'A more distinctive sunset-style dashboard for daily operations')) ?></h1>
<p class="dashboard-hero-copy mb-0"><?= h(tr('هذه ما تزال مجرد معاينة على لوحة التحكم فقط، ولكن الآن باتجاه Sunset Contrast بدرجات برتقالية ودافئة مع لمسات تركواز وذهبية. إذا أعجبك الشكل سأطبّق نفس الهوية على الشريط الجانبي والجداول والنماذج وباقي الصفحات.', 'This is still a dashboard-only preview, but now in the Sunset Contrast direction with warm orange, teal, gold, and rose accents. If you like it, I can extend the same identity to the sidebar, tables, forms, and the rest of the app.')) ?></p>
</div>
<div class="col-xl-4">
<div class="dashboard-hero-panel">
<div class="dashboard-chip-grid">
<span class="dashboard-chip dashboard-chip--orange">Sunset Orange</span>
<span class="dashboard-chip dashboard-chip--teal">Teal</span>
<span class="dashboard-chip dashboard-chip--gold">Gold</span>
<span class="dashboard-chip dashboard-chip--rose">Rose</span>
</div>
<div class="dashboard-hero-meta mt-4">
<div>
<small><?= h(tr('النمط', 'Style')) ?></small>
<strong>Sunset Contrast</strong>
</div>
<div>
<small><?= h(tr('النطاق', 'Scope')) ?></small>
<strong><?= h(tr('الصفحة الرئيسية فقط', 'Dashboard only')) ?></strong>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Metrics Row -->
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card dashboard-stat-card dashboard-stat-card--cyan h-100 border-0">
<div class="card-body d-flex flex-column justify-content-between">
<div class="d-flex justify-content-between align-items-start mb-3">
<h6 class="text-white-50 text-uppercase mb-0 fw-bold"><?= h(tr('مبيعات اليوم', 'Today sales')) ?></h6>
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-receipt fs-5 text-white"></i></div>
</div>
<h2 class="display-6 fw-bold mb-0"><?= h((string) $metrics['today_sales']) ?></h2>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card dashboard-stat-card dashboard-stat-card--coral h-100 border-0">
<div class="card-body d-flex flex-column justify-content-between">
<div class="d-flex justify-content-between align-items-start mb-3">
<h6 class="text-white-50 text-uppercase mb-0 fw-bold"><?= h(tr('إيراد اليوم', 'Today revenue')) ?></h6>
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-cash-stack fs-5 text-white"></i></div>
</div>
<h2 class="display-6 fw-bold mb-0"><?= h(currency((float) $metrics['today_revenue'])) ?></h2>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card dashboard-stat-card dashboard-stat-card--lime h-100 border-0">
<div class="card-body d-flex flex-column justify-content-between">
<div class="d-flex justify-content-between align-items-start mb-3">
<h6 class="text-white-50 text-uppercase mb-0 fw-bold">POS</h6>
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-cart fs-5 text-white"></i></div>
</div>
<h2 class="display-6 fw-bold mb-0"><?= h((string) $metrics['pos_count']) ?></h2>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card dashboard-stat-card dashboard-stat-card--gold h-100 border-0">
<div class="card-body d-flex flex-column justify-content-between">
<div class="d-flex justify-content-between align-items-start mb-3">
<h6 class="text-white-50 text-uppercase mb-0 fw-bold"><?= h(tr('فاتورة', 'Invoice')) ?></h6>
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-basket fs-5 text-white"></i></div>
</div>
<h2 class="display-6 fw-bold mb-0"><?= h((string) $metrics['normal_count']) ?></h2>
</div>
</div>
</div>
</div>
<!-- Charts Row -->
<div class="row g-4 mb-4">
<div class="col-xl-6">
<div class="card h-100">
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-semibold"><i class="bi bi-bar-chart-line me-2 text-primary"></i><?= h(tr('أفضل الأصناف مبيعاً', 'Top Selling Products')) ?></h5>
</div>
<div class="card-body">
<?php if(empty($productLabels)): ?>
<div class="text-center text-muted py-5">
<i class="bi bi-graph-down text-secondary fs-1 d-block mb-3"></i>
<p><?= h(tr('لا توجد بيانات كافية لعرض الرسم البياني', 'Not enough data to show chart')) ?></p>
</div>
<?php else: ?>
<div style="position: relative; height:300px; width:100%">
<canvas id="topProductsChart"></canvas>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-xl-6">
<div class="card h-100">
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-semibold"><i class="bi bi-pie-chart me-2 text-success"></i><?= h(tr('المبيعات حسب الفرع', 'Sales by Branch')) ?></h5>
</div>
<div class="card-body d-flex justify-content-center align-items-center">
<?php if(empty($branchLabels)): ?>
<div class="text-center text-muted py-5">
<i class="bi bi-pie-chart text-secondary fs-1 d-block mb-3"></i>
<p><?= h(tr('لا توجد بيانات كافية لعرض الرسم البياني', 'Not enough data to show chart')) ?></p>
</div>
<?php else: ?>
<div style="position: relative; height:300px; width:100%">
<canvas id="branchSalesChart"></canvas>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<!-- Recent Sales -->
<div class="col-xl-8">
<div class="card h-100">
<div class="card-header bg-white d-flex justify-content-between align-items-center py-3 border-0">
<h5 class="mb-0 fw-semibold"><i class="bi bi-clock-history me-2 text-info"></i><?= h(tr('آخر المبيعات', 'Latest sales')) ?></h5>
<a class="btn btn-sm btn-outline-primary rounded-pill px-3" href="<?= h(url_for('sales.php')) ?>"><?= h(tr('عرض الكل', 'View all')) ?></a>
</div>
<div class="card-body p-0">
<?php if (!$metrics['recent']): ?>
<div class="text-center p-5 text-muted">
<i class="bi bi-inbox fs-1 mb-3 d-block text-secondary"></i>
<h5><?= h(tr('لا توجد مبيعات بعد', 'No sales yet')) ?></h5>
<p class="mb-3"><?= h(tr('ابدأ بأول فاتورة من صفحة نقاط البيع.', 'Start your first receipt from the POS page.')) ?></p>
<a class="btn btn-primary" href="<?= h(url_for('pos.php')) ?>"><?= h(tr('إنشاء بيع POS', 'Create POS sale')) ?></a>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0 text-center">
<thead class="bg-light text-muted">
<tr>
<th class="ps-3 border-0 py-3 fw-semibold"><?= h(tr('رقم الإيصال', 'Receipt No')) ?></th>
<th class="border-0 py-3 fw-semibold"><?= h(tr('الفرع', 'Branch')) ?></th>
<th class="border-0 py-3 fw-semibold"><?= h(tr('النوع', 'Type')) ?></th>
<th class="border-0 py-3 fw-semibold"><?= h(tr('التاريخ', 'Date')) ?></th>
<th class="text-end pe-3 border-0 py-3 fw-semibold"><?= h(tr('الإجمالي', 'Total')) ?></th>
</tr>
</thead>
<tbody class="border-top-0">
<?php foreach ($metrics['recent'] as $sale): ?>
<tr>
<td class="ps-3">
<a href="<?= h(url_for('sale.php', ['id' => $sale['id']])) ?>" class="fw-bold text-decoration-none text-primary">
#<?= h($sale['receipt_no']) ?>
</a>
</td>
<td>
<span class="badge bg-light text-dark border"><?= h(branch_label((string) $sale['branch_code'])) ?></span>
</td>
<td>
<?php if($sale['sale_mode'] === 'pos'): ?>
<span class="badge bg-info bg-opacity-10 text-info border border-info"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span>
<?php else: ?>
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span>
<?php endif; ?>
</td>
<td><small class="text-muted fw-medium"><?= h(date('M d, H:i', strtotime((string) $sale['sale_date']))) ?></small></td>
<td class="text-end pe-3 fw-bold text-success"><?= h(currency((float) $sale['total_amount'])) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Quick Actions / Status -->
<div class="col-xl-4">
<div class="card h-100">
<div class="card-header bg-white py-3 border-0">
<h5 class="mb-0 fw-semibold"><i class="bi bi-lightning-charge me-2 text-warning"></i><?= h(tr('إجراءات سريعة', 'Quick Actions')) ?></h5>
</div>
<div class="card-body">
<div class="d-grid gap-3">
<a href="<?= h(url_for('pos.php')) ?>" class="btn btn-primary btn-lg d-flex align-items-center justify-content-between" style="border-radius: 10px; box-shadow: 0 4px 6px rgba(13, 110, 253, 0.2);">
<span><i class="bi bi-cart-plus me-2"></i> <?= h(tr('نقطة بيع جديدة', 'New POS Sale')) ?></span>
<i class="bi bi-chevron-right"></i>
</a>
<a href="<?= h(url_for('normal_sale.php')) ?>" class="btn btn-outline-primary btn-lg d-flex align-items-center justify-content-between" style="border-radius: 10px;">
<span><i class="bi bi-receipt me-2"></i> <?= h(tr('فاتورة', 'Invoice')) ?></span>
<i class="bi bi-chevron-right"></i>
</a>
<button type="button" class="btn btn-light btn-lg d-flex align-items-center justify-content-between border" data-bs-toggle="modal" data-bs-target="#quickNoteModal" style="border-radius: 10px;">
<span class="text-dark"><i class="bi bi-pencil-square me-2"></i> <?= h(tr('ملاحظة سريعة', 'Quick Note')) ?></span>
<i class="bi bi-chevron-right text-dark"></i>
</button>
</div>
<div class="mt-4 pt-4 border-top">
<?php if ($user): ?>
<h6 class="text-muted text-uppercase small fw-bold mb-3"><?= h(tr('معلومات الحساب', 'Account Info')) ?></h6>
<div class="d-flex align-items-center mb-2 p-3 bg-light rounded-3 border">
<div class="bg-white p-2 rounded-circle shadow-sm me-3">
<i class="bi bi-person-badge fs-4 text-primary"></i>
</div>
<div>
<p class="mb-0 fw-bold"><?= h(current_lang() === 'ar' ? $user['name_ar'] : $user['name_en']) ?></p>
<small class="text-muted fw-medium"><?= h(role_label($user['role'])) ?> &bull; <?= h(branch_label($user['branch_code'])) ?></small>
</div>
</div>
<?php else: ?>
<div class="text-center p-3 bg-light rounded-3 border">
<p class="text-muted mb-3 small"><?= h(tr('جرّب المالك أو مدير الفرع أو الكاشير لرؤية اختلاف الصلاحيات.', 'Try owner, branch manager, or cashier to see different permissions.')) ?></p>
<a class="btn btn-dark w-100 rounded-pill" href="<?= h(url_for('login.php')) ?>"><?= h(tr('تسجيل الدخول للبدء', 'Sign in to start')) ?></a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<!-- Monthly Sales Chart Row -->
<div class="row g-4 mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-semibold"><i class="bi bi-graph-up me-2 text-primary"></i><?= h(tr('مبيعات الأشهر', 'Monthly Sales')) ?></h5>
</div>
<div class="card-body">
<?php if(empty($monthlyLabels)): ?>
<div class="text-center text-muted py-5">
<i class="bi bi-graph-up text-secondary fs-1 d-block mb-3"></i>
<p><?= h(tr('لا توجد بيانات كافية لعرض الرسم البياني', 'Not enough data to show chart')) ?></p>
</div>
<?php else: ?>
<div style="position: relative; height:350px; width:100%">
<canvas id="monthlySalesChart"></canvas>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Modal Form Example -->
<div class="modal fade" id="quickNoteModal" tabindex="-1" aria-labelledby="quickNoteModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" style="border-radius: 16px; border: 0; box-shadow: 0 10px 30px rgba(0,0,0,0.1);">
<form onsubmit="handleQuickNote(event)">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold" id="quickNoteModalLabel"><i class="bi bi-journal-text me-2 text-primary"></i><?= h(tr('إضافة ملاحظة سريعة', 'Add Quick Note')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="noteTitle" class="form-label fw-medium"><?= h(tr('العنوان', 'Title')) ?></label>
<input type="text" class="form-control form-control-lg bg-light border-0" id="noteTitle" required>
</div>
<div class="mb-3">
<label for="noteContent" class="form-label fw-medium"><?= h(tr('التفاصيل', 'Details')) ?></label>
<textarea class="form-control bg-light border-0" id="noteContent" rows="4" required></textarea>
</div>
</div>
<div class="modal-footer border-0 pt-0">
<button type="button" class="btn btn-light px-4" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
<button type="submit" class="btn btn-primary px-4 shadow-sm"><?= h(tr('حفظ الملاحظة', 'Save Note')) ?></button>
</div>
</form>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
<?php if(!empty($branchLabels)): ?>
const branchCtx = document.getElementById('branchSalesChart').getContext('2d');
new Chart(branchCtx, {
type: 'doughnut',
data: {
labels: <?= json_encode($branchLabels) ?>,
datasets: [{
data: <?= json_encode($branchData) ?>,
backgroundColor: [
'rgba(255, 122, 0, 0.88)',
'rgba(0, 209, 178, 0.88)',
'rgba(255, 209, 102, 0.88)',
'rgba(255, 77, 109, 0.86)',
'rgba(125, 145, 175, 0.82)'
],
borderWidth: 2,
borderColor: '#ffffff',
hoverOffset: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '65%',
plugins: {
legend: { position: 'bottom', labels: { color: '#667085', usePointStyle: true, padding: 20 } },
tooltip: {
backgroundColor: 'rgba(28, 36, 52, 0.94)',
padding: 12,
callbacks: {
label: function(context) {
let value = context.raw;
return ' ' + value.toFixed(3) + ' <?= tr("ر.ع", "OMR") ?>';
}
}
}
}
}
});
<?php endif; ?>
<?php if(!empty($productLabels)): ?>
const productsCtx = document.getElementById('topProductsChart').getContext('2d');
// Create gradient for bars
let gradient = productsCtx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(0, 'rgba(255, 122, 0, 0.95)');
gradient.addColorStop(1, 'rgba(0, 209, 178, 0.2)');
new Chart(productsCtx, {
type: 'bar',
data: {
labels: <?= json_encode($productLabels) ?>,
datasets: [{
label: '<?= h(tr("الكمية المباعة", "Qty Sold")) ?>',
data: <?= json_encode($productData) ?>,
backgroundColor: gradient,
borderColor: '#FF7A00',
borderWidth: 1,
borderRadius: 6,
barPercentage: 0.6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(28, 36, 52, 0.94)',
padding: 12
}
},
scales: {
y: {
beginAtZero: true,
ticks: { precision: 0 },
grid: { borderDash: [5, 5], color: 'rgba(148, 163, 184, 0.14)' },
ticks: { color: '#667085' }
},
x: {
grid: { display: false },
ticks: { color: '#98A2B3' }
}
}
}
});
<?php endif; ?>
<?php if(!empty($monthlyLabels)): ?>
const monthlyCtx = document.getElementById('monthlySalesChart').getContext('2d');
// Create gradient for line chart area
let gradientMonthly = monthlyCtx.createLinearGradient(0, 0, 0, 350);
gradientMonthly.addColorStop(0, 'rgba(255, 209, 102, 0.32)');
gradientMonthly.addColorStop(1, 'rgba(255, 122, 0, 0.04)');
new Chart(monthlyCtx, {
type: 'line',
data: {
labels: <?= json_encode($monthlyLabels) ?>,
datasets: [{
label: '<?= h(tr("إجمالي المبيعات", "Total Sales")) ?>',
data: <?= json_encode($monthlyData) ?>,
backgroundColor: gradientMonthly,
borderColor: '#FFD166',
borderWidth: 3,
pointBackgroundColor: '#ffffff',
pointBorderColor: '#FF7A00',
pointBorderWidth: 2,
pointRadius: 4,
pointHoverRadius: 6,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(28, 36, 52, 0.94)',
padding: 12,
callbacks: {
label: function(context) {
let value = context.raw;
return ' ' + value.toFixed(3) + ' <?= tr("ر.ع", "OMR") ?>';
}
}
}
},
scales: {
y: {
beginAtZero: true,
grid: { borderDash: [5, 5], color: 'rgba(148, 163, 184, 0.14)' },
ticks: { color: '#667085' }
},
x: {
grid: { display: false },
ticks: { color: '#98A2B3' }
}
}
}
});
<?php endif; ?>
});
function handleQuickNote(e) {
e.preventDefault();
var modalEl = document.getElementById('quickNoteModal');
var modal = bootstrap.Modal.getInstance(modalEl);
modal.hide();
Swal.fire({
title: '<?= h(tr('تم الحفظ!', 'Saved!')) ?>',
text: '<?= h(tr('تم حفظ الملاحظة بنجاح.', 'Note saved successfully.')) ?>',
icon: 'success',
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3000
});
e.target.reset();
}
</script>
<?php require __DIR__ . '/includes/footer.php'; ?>