Autosave: 20260303-065452

This commit is contained in:
Flatlogic Bot 2026-03-03 06:54:52 +00:00
parent e9ceb4546d
commit d2a2461bda
11 changed files with 455 additions and 211 deletions

219
admin.php
View File

@ -2,11 +2,37 @@
$title = 'dashboard'; $title = 'dashboard';
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
// Helper functions for status colors
if (!function_exists('getStatusColor')) {
function getStatusColor($status) {
return [
'received' => 'secondary',
'processing' => 'primary',
'ready' => 'success',
'delivered' => 'dark',
'cancelled' => 'danger',
][$status] ?? 'info';
}
}
if (!function_exists('getPaymentStatusColor')) {
function getPaymentStatusColor($status) {
return [
'unpaid' => 'danger',
'partially_paid' => 'warning',
'paid' => 'success',
][$status] ?? 'info';
}
}
// Stats logic // Stats logic
$branch_id = $_SESSION['branch_id']; $branch_id = $_SESSION['branch_id'];
$is_super = ($current_role === 'super_admin'); $is_super = ($current_role === 'super_admin');
$is_limited = ($current_role === 'limited_viewer'); $is_limited = ($current_role === 'limited_viewer');
// For super_admin, "all" means no branch filtering.
// Otherwise, we filter by the selected branch.
$filter_branch = ($branch_id !== 'all');
if (!$is_limited) { if (!$is_limited) {
$stats = [ $stats = [
'today_revenue' => 0, 'today_revenue' => 0,
@ -16,47 +42,47 @@ if (!$is_limited) {
]; ];
// Today's revenue // Today's revenue
$rev_where = $is_super ? "" : " AND order_id IN (SELECT id FROM orders WHERE branch_id = ?)"; $rev_where = !$filter_branch ? "" : " AND order_id IN (SELECT id FROM orders WHERE branch_id = ?)";
$rev_params = $is_super ? [] : [$branch_id]; $rev_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT SUM(amount) as total FROM payments WHERE created_at >= CURDATE() $rev_where"); $stmt = db()->prepare("SELECT SUM(amount) as total FROM payments WHERE created_at >= CURDATE() $rev_where");
$stmt->execute($rev_params); $stmt->execute($rev_params);
$stats['today_revenue'] = $stmt->fetch()['total'] ?? 0; $stats['today_revenue'] = $stmt->fetch()['total'] ?? 0;
// Active orders (received, processing) // Active orders (received, processing)
$ord_where = $is_super ? "WHERE status IN ('received', 'processing')" : "WHERE branch_id = ? AND status IN ('received', 'processing')"; $ord_where = !$filter_branch ? "WHERE status IN ('received', 'processing')" : "WHERE branch_id = ? AND status IN ('received', 'processing')";
$ord_params = $is_super ? [] : [$branch_id]; $ord_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ord_where"); $stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ord_where");
$stmt->execute($ord_params); $stmt->execute($ord_params);
$stats['active_orders'] = $stmt->fetch()['count'] ?? 0; $stats['active_orders'] = $stmt->fetch()['count'] ?? 0;
// Ready orders // Ready orders
$ready_where = $is_super ? "WHERE status = 'ready'" : "WHERE branch_id = ? AND status = 'ready'"; $ready_where = !$filter_branch ? "WHERE status = 'ready'" : "WHERE branch_id = ? AND status = 'ready'";
$ready_params = $is_super ? [] : [$branch_id]; $ready_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ready_where"); $stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ready_where");
$stmt->execute($ready_params); $stmt->execute($ready_params);
$stats['ready_orders'] = $stmt->fetch()['count'] ?? 0; $stats['ready_orders'] = $stmt->fetch()['count'] ?? 0;
// New customers today // New customers today
$cust_where = $is_super ? "WHERE created_at >= CURDATE()" : "WHERE branch_id = ? AND created_at >= CURDATE()"; $cust_where = !$filter_branch ? "WHERE created_at >= CURDATE()" : "WHERE branch_id = ? AND created_at >= CURDATE()";
$cust_params = $is_super ? [] : [$branch_id]; $cust_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM customers $cust_where"); $stmt = db()->prepare("SELECT COUNT(*) as count FROM customers $cust_where");
$stmt->execute($cust_params); $stmt->execute($cust_params);
$stats['new_customers'] = $stmt->fetch()['count'] ?? 0; $stats['new_customers'] = $stmt->fetch()['count'] ?? 0;
// Recent orders // Monthly Orders (Last 12 months)
$recent_where = $is_super ? "" : "WHERE o.branch_id = ?"; $monthly_where = !$filter_branch ? "WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)" : "WHERE branch_id = ? AND created_at >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)";
$recent_params = $is_super ? [] : [$branch_id]; $monthly_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT o.*, c.name_en as customer_name_en, c.name_ar as customer_name_ar $stmt = db()->prepare("SELECT DATE_FORMAT(created_at, '%Y-%m') as month_year, COUNT(*) as count
FROM orders o FROM orders
LEFT JOIN customers c ON o.customer_id = c.id $monthly_where
$recent_where GROUP BY month_year
ORDER BY o.created_at DESC LIMIT 5"); ORDER BY month_year ASC");
$stmt->execute($recent_params); $stmt->execute($monthly_params);
$recent_orders = $stmt->fetchAll(); $monthly_orders = $stmt->fetchAll();
// Charts data for Dashboard (Last 7 days) // Charts data for Dashboard (Last 7 days)
$chart_where = $is_super ? "WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)" : "WHERE branch_id = ? AND created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)"; $chart_where = !$filter_branch ? "WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)" : "WHERE branch_id = ? AND created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)";
$chart_params = $is_super ? [] : [$branch_id]; $chart_params = !$filter_branch ? [] : [$branch_id];
// Daily Revenue // Daily Revenue
$stmt = db()->prepare("SELECT DATE(created_at) as date, SUM(total_price) as revenue $stmt = db()->prepare("SELECT DATE(created_at) as date, SUM(total_price) as revenue
@ -68,8 +94,8 @@ if (!$is_limited) {
$daily_revenue = $stmt->fetchAll(); $daily_revenue = $stmt->fetchAll();
// Orders by Status (All time or current) // Orders by Status (All time or current)
$status_where = $is_super ? "" : "WHERE branch_id = ?"; $status_where = !$filter_branch ? "" : "WHERE branch_id = ?";
$status_params = $is_super ? [] : [$branch_id]; $status_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT status, COUNT(*) as count FROM orders $status_where GROUP BY status"); $stmt = db()->prepare("SELECT status, COUNT(*) as count FROM orders $status_where GROUP BY status");
$stmt->execute($status_params); $stmt->execute($status_params);
$orders_by_status = $stmt->fetchAll(); $orders_by_status = $stmt->fetchAll();
@ -164,37 +190,8 @@ if (!$is_limited) {
<div class="row g-4"> <div class="row g-4">
<div class="col-md-8"> <div class="col-md-8">
<div class="card p-4 h-100"> <div class="card p-4 h-100">
<h5 class="fw-bold mb-4"><?= __('recent_orders') ?></h5> <h5 class="fw-bold mb-4"><?= __('orders_per_month') ?></h5>
<div class="table-responsive"> <canvas id="monthlyOrdersChart" height="120"></canvas>
<table class="table table-hover align-middle">
<thead>
<tr>
<th>#</th>
<th><?= __('customer_name') ?></th>
<th><?= __('total') ?></th>
<th><?= __('status') ?></th>
<th><?= __('payment_status') ?></th>
<th><?= __('actions') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($recent_orders as $order): ?>
<tr>
<td><?= $order['id'] ?></td>
<td><?= $lang === 'ar' ? ($order['customer_name_ar'] ?: $order['customer_name_en']) : $order['customer_name_en'] ?></td>
<td><?= format_amount($order['total_price']) ?></td>
<td><span class="badge bg-<?= getStatusColor($order['status']) ?> rounded-pill px-3 py-1"><?= __($order['status']) ?></span></td>
<td><span class="badge badge-soft-<?= getPaymentStatusColor($order['payment_status']) ?> rounded-pill px-3 py-1"><?= __($order['payment_status']) ?></span></td>
<td>
<a href="order_details.php?id=<?= $order['id'] ?>" class="btn btn-sm btn-light border-0" style="border-radius: 8px;">
<i class="bi bi-eye"></i>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
@ -216,62 +213,76 @@ if (!$is_limited) {
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script> <script>
// Revenue Chart // Revenue Chart
new Chart(document.getElementById('revenueChart'), { if (document.getElementById('revenueChart')) {
type: 'line', new Chart(document.getElementById('revenueChart'), {
data: { type: 'line',
labels: <?= json_encode(array_column($daily_revenue, 'date')) ?>, data: {
datasets: [{ labels: <?= json_encode(array_column($daily_revenue, 'date')) ?>,
label: '<?= __('revenue') ?>', datasets: [{
data: <?= json_encode(array_column($daily_revenue, 'revenue')) ?>, label: '<?= __('revenue') ?>',
borderColor: '#3b82f6', data: <?= json_encode(array_column($daily_revenue, 'revenue')) ?>,
backgroundColor: 'rgba(59, 130, 246, 0.1)', borderColor: '#3b82f6',
fill: true, backgroundColor: 'rgba(59, 130, 246, 0.1)',
tension: 0.4 fill: true,
}] tension: 0.4
}, }]
options: { },
responsive: true, options: {
plugins: { legend: { display: false } }, responsive: true,
scales: { y: { beginAtZero: true } } plugins: { legend: { display: false } },
} scales: { y: { beginAtZero: true } }
}); }
});
}
// Status Chart // Status Chart
new Chart(document.getElementById('statusChart'), { if (document.getElementById('statusChart')) {
type: 'doughnut', new Chart(document.getElementById('statusChart'), {
data: { type: 'doughnut',
labels: <?= json_encode(array_map(function($s) { return __($s['status']); }, $orders_by_status)) ?>, data: {
datasets: [{ labels: <?= json_encode(array_map(function($s) { return __($s['status']); }, $orders_by_status)) ?>,
data: <?= json_encode(array_column($orders_by_status, 'count')) ?>, datasets: [{
backgroundColor: ['#6c757d', '#3b82f6', '#10b981', '#111827', '#ef4444'] data: <?= json_encode(array_column($orders_by_status, 'count')) ?>,
}] backgroundColor: ['#6c757d', '#3b82f6', '#10b981', '#111827', '#ef4444']
}, }]
options: { },
responsive: true, options: {
plugins: { legend: { position: 'bottom' } } responsive: true,
} plugins: { legend: { position: 'bottom' } }
}); }
});
}
// Monthly Orders Chart
if (document.getElementById('monthlyOrdersChart')) {
new Chart(document.getElementById('monthlyOrdersChart'), {
type: 'bar',
data: {
labels: <?= json_encode(array_column($monthly_orders, 'month_year')) ?>,
datasets: [{
label: '<?= __('orders') ?>',
data: <?= json_encode(array_column($monthly_orders, 'count')) ?>,
backgroundColor: '#10b981',
borderRadius: 8
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
</script> </script>
<?php endif; ?> <?php endif; ?>
<?php <?php
if (!$is_limited) {
function getStatusColor($status) {
return [
'received' => 'secondary',
'processing' => 'primary',
'ready' => 'success',
'delivered' => 'dark',
'cancelled' => 'danger',
][$status] ?? 'info';
}
function getPaymentStatusColor($status) {
return [
'unpaid' => 'danger',
'partially_paid' => 'warning',
'paid' => 'success',
][$status] ?? 'info';
}
}
require_once __DIR__ . '/includes/footer.php'; require_once __DIR__ . '/includes/footer.php';
?> ?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -3,37 +3,39 @@ document.addEventListener('DOMContentLoaded', () => {
const chatInput = document.getElementById('chat-input'); const chatInput = document.getElementById('chat-input');
const chatMessages = document.getElementById('chat-messages'); const chatMessages = document.getElementById('chat-messages');
const appendMessage = (text, sender) => { if (chatForm && chatInput && chatMessages) {
const msgDiv = document.createElement('div'); const appendMessage = (text, sender) => {
msgDiv.classList.add('message', sender); const msgDiv = document.createElement('div');
msgDiv.textContent = text; msgDiv.classList.add('message', sender);
chatMessages.appendChild(msgDiv); msgDiv.textContent = text;
chatMessages.scrollTop = chatMessages.scrollHeight; chatMessages.appendChild(msgDiv);
}; chatMessages.scrollTop = chatMessages.scrollHeight;
};
chatForm.addEventListener('submit', async (e) => { chatForm.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
const message = chatInput.value.trim(); const message = chatInput.value.trim();
if (!message) return; if (!message) return;
appendMessage(message, 'visitor'); appendMessage(message, 'visitor');
chatInput.value = ''; chatInput.value = '';
try { try {
const response = await fetch('api/chat.php', { const response = await fetch('api/chat.php', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }) body: JSON.stringify({ message })
}); });
const data = await response.json(); const data = await response.json();
// Artificial delay for realism // Artificial delay for realism
setTimeout(() => { setTimeout(() => {
appendMessage(data.reply, 'bot'); appendMessage(data.reply, 'bot');
}, 500); }, 500);
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
appendMessage("Sorry, something went wrong. Please try again.", 'bot'); appendMessage("Sorry, something went wrong. Please try again.", 'bot');
} }
}); });
}
}); });

View File

@ -1,8 +1,81 @@
</main> </main>
</div> </div>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <!-- Separate Popper and Bootstrap for maximum compatibility and debugging -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script> <script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
// Robust dropdown initialization and diagnostic
(function() {
function checkDependencies() {
console.log('--- Dependency Check ---');
console.log('Popper.js:', typeof Popper !== 'undefined' ? 'Loaded' : 'MISSING');
console.log('Bootstrap:', typeof bootstrap !== 'undefined' ? 'Loaded' : 'MISSING');
if (typeof bootstrap !== 'undefined' && bootstrap.Dropdown) {
console.log('Bootstrap Dropdown:', 'Available');
} else {
console.warn('Bootstrap Dropdown: NOT Available');
}
}
function initDropdowns() {
checkDependencies();
if (typeof bootstrap !== 'undefined' && bootstrap.Dropdown) {
console.log('Initializing Bootstrap Dropdowns...');
var dropdownElementList = [].slice.call(document.querySelectorAll('.dropdown-toggle'));
dropdownElementList.forEach(function (dropdownToggleEl) {
try {
// Clean up any existing instances first
var existing = bootstrap.Dropdown.getInstance(dropdownToggleEl);
if (existing) existing.dispose();
// New instance
new bootstrap.Dropdown(dropdownToggleEl);
} catch (err) {
console.error('Error initializing dropdown for:', dropdownToggleEl, err);
}
});
} else {
console.error('Bootstrap JS or Dropdown module not loaded!');
// Emergency Fallback: simple manual toggle if Bootstrap fails
document.querySelectorAll('.dropdown-toggle').forEach(function(btn) {
btn.style.cursor = 'pointer';
btn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('Emergency fallback toggle triggered');
var menu = this.nextElementSibling;
if (menu && menu.classList.contains('dropdown-menu')) {
var isShown = menu.classList.contains('show');
// Close others
document.querySelectorAll('.dropdown-menu.show').forEach(function(m) {
m.classList.remove('show');
});
if (!isShown) menu.classList.add('show');
}
});
});
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initDropdowns);
} else {
initDropdowns();
}
// Global click listener to close dropdowns
document.addEventListener('click', function(e) {
if (!e.target.closest('.dropdown')) {
document.querySelectorAll('.dropdown-menu.show').forEach(function(menu) {
menu.classList.remove('show');
});
}
});
})();
</script>
</body> </body>
</html> </html>

View File

@ -14,17 +14,39 @@ $current_role = $_SESSION['role'] ?? 'cashier';
// Branch switching logic // Branch switching logic
if (isset($_GET['switch_branch']) && $current_user_id) { if (isset($_GET['switch_branch']) && $current_user_id) {
$new_branch_id = (int)$_GET['switch_branch']; $new_branch_id = $_GET['switch_branch'];
// Verify user has access to this branch
$stmt = db()->prepare("SELECT b.* FROM user_branches ub JOIN branches b ON ub.branch_id = b.id WHERE ub.user_id = ? AND ub.branch_id = ?"); if ($new_branch_id === 'all' && $current_role === 'super_admin') {
$stmt->execute([$current_user_id, $new_branch_id]); $_SESSION['branch_id'] = 'all';
$_SESSION['branch_name'] = __('all_branches');
// Redirect back to same page without the switch_branch param but keeping others
$params = $_GET;
unset($params['switch_branch']);
$query = count($params) > 0 ? '?' . http_build_query($params) : '';
$url = strtok($_SERVER["REQUEST_URI"], '?') . $query;
header("Location: $url");
exit;
}
$new_branch_id = (int)$new_branch_id;
// Verify user has access to this branch (or is super_admin)
if ($current_role === 'super_admin') {
$stmt = db()->prepare("SELECT * FROM branches WHERE id = ?");
$stmt->execute([$new_branch_id]);
} else {
$stmt = db()->prepare("SELECT b.* FROM user_branches ub JOIN branches b ON ub.branch_id = b.id WHERE ub.user_id = ? AND ub.branch_id = ?");
$stmt->execute([$current_user_id, $new_branch_id]);
}
$branch = $stmt->fetch(); $branch = $stmt->fetch();
if ($branch) { if ($branch) {
$_SESSION['branch_id'] = $branch['id']; $_SESSION['branch_id'] = $branch['id'];
$_SESSION['branch_name'] = $lang === 'ar' ? ($branch['name_ar'] ?: $branch['name_en']) : $branch['name_en']; $_SESSION['branch_name'] = $lang === 'ar' ? ($branch['name_ar'] ?: $branch['name_en']) : $branch['name_en'];
// Redirect back to same page without the query param // Redirect back to same page without the switch_branch param but keeping others
$url = strtok($_SERVER["REQUEST_URI"], '?'); $params = $_GET;
unset($params['switch_branch']);
$query = count($params) > 0 ? '?' . http_build_query($params) : '';
$url = strtok($_SERVER["REQUEST_URI"], '?') . $query;
header("Location: $url"); header("Location: $url");
exit; exit;
} }
@ -33,9 +55,29 @@ if (isset($_GET['switch_branch']) && $current_user_id) {
// Fetch user branches // Fetch user branches
$user_branches = []; $user_branches = [];
if ($current_user_id) { if ($current_user_id) {
$stmt = db()->prepare("SELECT b.* FROM user_branches ub JOIN branches b ON ub.branch_id = b.id WHERE ub.user_id = ?"); if ($current_role === 'super_admin') {
$stmt->execute([$current_user_id]); $user_branches = db()->query("SELECT * FROM branches")->fetchAll();
$user_branches = $stmt->fetchAll(); } else {
$stmt = db()->prepare("SELECT b.* FROM user_branches ub JOIN branches b ON ub.branch_id = b.id WHERE ub.user_id = ?");
$stmt->execute([$current_user_id]);
$user_branches = $stmt->fetchAll();
}
}
// Ensure branch_name is set if not already
if (!isset($_SESSION['branch_name']) || empty($_SESSION['branch_name'])) {
if (isset($_SESSION['branch_id'])) {
if ($_SESSION['branch_id'] === 'all') {
$_SESSION['branch_name'] = __('all_branches');
} else {
$stmt = db()->prepare("SELECT * FROM branches WHERE id = ?");
$stmt->execute([$_SESSION['branch_id']]);
$b = $stmt->fetch();
if ($b) {
$_SESSION['branch_name'] = $lang === 'ar' ? ($b['name_ar'] ?: $b['name_en']) : $b['name_en'];
}
}
}
} }
// Fetch Global Company Info // Fetch Global Company Info
@ -54,9 +96,6 @@ if ($current_user_id) {
$current_page = basename($_SERVER['PHP_SELF']); $current_page = basename($_SERVER['PHP_SELF']);
if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logout.php') { if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logout.php') {
if (!has_permission('view', $current_page)) { if (!has_permission('view', $current_page)) {
// Allow access to admin.php if it's the dashboard and maybe it's named differently?
// No, it's admin.php.
// If they don't have view permission for the current page, redirect to a safe page or show error.
if ($current_page !== 'admin.php' && has_permission('view', 'admin.php')) { if ($current_page !== 'admin.php' && has_permission('view', 'admin.php')) {
header('Location: admin.php'); header('Location: admin.php');
exit; exit;
@ -81,8 +120,8 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
<link rel="icon" type="image/x-icon" href="<?= $company_info['favicon'] ?>"> <link rel="icon" type="image/x-icon" href="<?= $company_info['favicon'] ?>">
<?php endif; ?> <?php endif; ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.<?= is_rtl() ? 'rtl.' : '' ?>min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.<?= is_rtl() ? 'rtl.' : '' ?>min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>"> <link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@ -140,6 +179,27 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
margin-right: 0.5rem; margin-right: 0.5rem;
<?= is_rtl() ? 'margin-left: 0.5rem; margin-right: 0;' : '' ?> <?= is_rtl() ? 'margin-left: 0.5rem; margin-right: 0;' : '' ?>
} }
.user-dropdown-btn {
background-color: white;
border: 1px solid #dee2e6;
border-radius: 50rem;
padding: 0.25rem 1rem;
display: flex;
align-items: center;
cursor: pointer;
transition: all 0.2s;
}
.user-dropdown-btn:hover {
background-color: #f8f9fa;
border-color: #ced4da;
}
/* Ensure dropdowns are above everything */
.dropdown-menu {
z-index: 9999 !important;
}
.dropdown-toggle {
cursor: pointer;
}
</style> </style>
</head> </head>
<body> <body>
@ -240,15 +300,6 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
<hr class="mx-3 my-2 text-secondary"> <hr class="mx-3 my-2 text-secondary">
<?php if (has_permission('view', 'profile.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'profile.php' ? 'active' : '' ?>" href="profile.php">
<i class="bi bi-person-circle"></i>
<?= __('user_profile') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'company_profile.php')): ?> <?php if (has_permission('view', 'company_profile.php')): ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'company_profile.php' ? 'active' : '' ?>" href="company_profile.php"> <a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'company_profile.php' ? 'active' : '' ?>" href="company_profile.php">
@ -274,17 +325,25 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content"> <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
<header class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> <header class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2"><?= __($title ?? 'dashboard') ?></h1> <h1 class="h2"><?= __($title ?? 'dashboard') ?></h1>
<div class="btn-toolbar mb-2 mb-md-0"> <div class="mb-2 mb-md-0">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<?php if (count($user_branches) > 1): ?> <?php if (count($user_branches) > 1 || $current_role === 'super_admin'): ?>
<div class="dropdown me-2"> <div class="dropdown me-2">
<button class="btn btn-sm btn-outline-primary dropdown-toggle rounded-pill px-3" type="button" data-bs-toggle="dropdown"> <button class="btn btn-sm btn-outline-primary dropdown-toggle rounded-pill px-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-shop me-1"></i> <?= $_SESSION['branch_name'] ?? '' ?> <i class="bi bi-shop me-1"></i> <?= $_SESSION['branch_name'] ?? '' ?>
</button> </button>
<ul class="dropdown-menu shadow border-0 mt-2"> <ul class="dropdown-menu shadow border-0 mt-2">
<?php if ($current_role === 'super_admin'): ?>
<li>
<a class="dropdown-item <?= ($_SESSION['branch_id'] ?? '') === 'all' ? 'active' : '' ?>" href="<?= branch_url('all') ?>">
<?= __('all_branches') ?>
</a>
</li>
<li><hr class="dropdown-divider"></li>
<?php endif; ?>
<?php foreach ($user_branches as $ub): ?> <?php foreach ($user_branches as $ub): ?>
<li> <li>
<a class="dropdown-item <?= $ub['id'] == $_SESSION['branch_id'] ? 'active' : '' ?>" href="?switch_branch=<?= $ub['id'] ?>"> <a class="dropdown-item <?= $ub['id'] == ($_SESSION['branch_id'] ?? '') ? 'active' : '' ?>" href="<?= branch_url($ub['id']) ?>">
<?= $lang === 'ar' ? ($ub['name_ar'] ?: $ub['name_en']) : $ub['name_en'] ?> <?= $lang === 'ar' ? ($ub['name_ar'] ?: $ub['name_en']) : $ub['name_en'] ?>
</a> </a>
</li> </li>
@ -297,17 +356,17 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
<!-- Language Switch --> <!-- Language Switch -->
<div class="dropdown me-2"> <div class="dropdown me-2">
<button class="btn btn-sm btn-outline-secondary dropdown-toggle rounded-pill px-3" type="button" data-bs-toggle="dropdown"> <button class="btn btn-sm btn-outline-secondary dropdown-toggle rounded-pill px-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-translate me-1"></i> <?= $lang === 'ar' ? 'العربية' : 'English' ?> <i class="bi bi-translate me-1"></i> <?= $lang === 'ar' ? 'العربية' : 'English' ?>
</button> </button>
<ul class="dropdown-menu shadow border-0 mt-2"> <ul class="dropdown-menu shadow border-0 mt-2">
<li> <li>
<a class="dropdown-item <?= $lang === 'en' ? 'active' : '' ?>" href="?lang=en"> <a class="dropdown-item <?= $lang === 'en' ? 'active' : '' ?>" href="<?= lang_url('en') ?>">
English English
</a> </a>
</li> </li>
<li> <li>
<a class="dropdown-item <?= $lang === 'ar' ? 'active' : '' ?>" href="?lang=ar"> <a class="dropdown-item <?= $lang === 'ar' ? 'active' : '' ?>" href="<?= lang_url('ar') ?>">
العربية العربية
</a> </a>
</li> </li>
@ -315,14 +374,14 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
</div> </div>
<div class="dropdown"> <div class="dropdown">
<div class="d-flex align-items-center bg-white border rounded-pill px-3 py-1 dropdown-toggle" role="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false" style="cursor: pointer;"> <button class="user-dropdown-btn dropdown-toggle" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<?php if ($current_user_data['profile_picture'] ?? null): ?> <?php if ($current_user_data['profile_picture'] ?? null): ?>
<img src="<?= $current_user_data['profile_picture'] ?>" alt="Profile" class="user-avatar-sm me-2"> <img src="<?= $current_user_data['profile_picture'] ?>" alt="Profile" class="user-avatar-sm me-2">
<?php else: ?> <?php else: ?>
<i class="bi bi-person-circle fs-5 me-2 text-secondary"></i> <i class="bi bi-person-circle fs-5 me-2 text-secondary"></i>
<?php endif; ?> <?php endif; ?>
<span class="small fw-bold"><?= $_SESSION['full_name'] ?? '' ?></span> <span class="small fw-bold d-none d-sm-inline"><?= $_SESSION['full_name'] ?? '' ?></span>
</div> </button>
<ul class="dropdown-menu dropdown-menu-end shadow border-0 mt-2" aria-labelledby="userDropdown" style="border-radius: 12px;"> <ul class="dropdown-menu dropdown-menu-end shadow border-0 mt-2" aria-labelledby="userDropdown" style="border-radius: 12px;">
<?php if (has_permission('view', 'profile.php')): ?> <?php if (has_permission('view', 'profile.php')): ?>
<li> <li>

View File

@ -148,7 +148,7 @@ $translations = [
'next' => 'Next', 'next' => 'Next',
'page' => 'Page', 'page' => 'Page',
'of' => 'of', 'of' => 'of',
'Access Denied' => 'Access Denied', 'Access Denied' => 'تم رفض الوصول',
'active_orders' => 'Active Orders', 'active_orders' => 'Active Orders',
'add_branch' => 'Add Branch', 'add_branch' => 'Add Branch',
'add_new_branch' => 'Add New Branch', 'add_new_branch' => 'Add New Branch',
@ -193,7 +193,8 @@ $translations = [
'confirm_delete_user' => 'Are you sure you want to delete this user?', 'confirm_delete_user' => 'Are you sure you want to delete this user?',
'leave_blank_to_keep_current' => 'leave blank to keep current', 'leave_blank_to_keep_current' => 'leave blank to keep current',
'permissions' => 'Permissions', 'permissions' => 'Permissions',
'user_permissions' => 'User Permissions' 'user_permissions' => 'User Permissions',
'orders_per_month' => 'Orders per Month'
], ],
'ar' => [ 'ar' => [
'dashboard' => 'لوحة القيادة', 'dashboard' => 'لوحة القيادة',
@ -380,7 +381,8 @@ $translations = [
'confirm_delete_user' => 'هل أنت متأكد من حذف هذا المستخدم؟', 'confirm_delete_user' => 'هل أنت متأكد من حذف هذا المستخدم؟',
'leave_blank_to_keep_current' => 'اتركه فارغاً للاحتفاظ بكلمة المرور الحالية', 'leave_blank_to_keep_current' => 'اتركه فارغاً للاحتفاظ بكلمة المرور الحالية',
'permissions' => 'الصلاحيات', 'permissions' => 'الصلاحيات',
'user_permissions' => 'صلاحيات المستخدم' 'user_permissions' => 'صلاحيات المستخدم',
'orders_per_month' => 'الطلبات شهرياً'
] ]
]; ];
@ -410,3 +412,15 @@ function decimals() {
function format_amount($amount) { function format_amount($amount) {
return number_format((float)$amount, decimals()) . ' ' . currency(); return number_format((float)$amount, decimals()) . ' ' . currency();
} }
function lang_url($new_lang) {
$params = $_GET;
$params['lang'] = $new_lang;
return '?' . http_build_query($params);
}
function branch_url($new_branch_id) {
$params = $_GET;
$params['switch_branch'] = $new_branch_id;
return '?' . http_build_query($params);
}

View File

@ -1,5 +1,6 @@
<?php <?php
session_start(); require_once __DIR__ . '/includes/lang.php';
if (isset($_SESSION['user_id'])) { if (isset($_SESSION['user_id'])) {
header('Location: admin.php'); header('Location: admin.php');
} else { } else {

114
items.php
View File

@ -202,11 +202,38 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$title = 'items'; $title = 'items';
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
// Filter logic
$search = $_GET['search'] ?? '';
$category_filter = $_GET['category_id'] ?? '';
$categories = db()->query("SELECT * FROM categories ORDER BY name_en ASC")->fetchAll(); $categories = db()->query("SELECT * FROM categories ORDER BY name_en ASC")->fetchAll();
$items = db()->query("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
FROM items i $query = "SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
LEFT JOIN categories c ON i.category_id = c.id FROM items i
ORDER BY i.name_en ASC")->fetchAll(); LEFT JOIN categories c ON i.category_id = c.id";
$params = [];
$where = [];
if ($search) {
$where[] = "(i.name_en LIKE ? OR i.name_ar LIKE ?)";
$params[] = "%$search%";
$params[] = "%$search%";
}
if ($category_filter) {
$where[] = "i.category_id = ?";
$params[] = $category_filter;
}
if ($where) {
$query .= " WHERE " . implode(" AND ", $where);
}
$query .= " ORDER BY i.name_en ASC";
$stmt = db()->prepare($query);
$stmt->execute($params);
$items = $stmt->fetchAll();
$services = db()->query("SELECT * FROM services ORDER BY name_en ASC")->fetchAll(); $services = db()->query("SELECT * FROM services ORDER BY name_en ASC")->fetchAll();
$stmt = db()->prepare("SELECT * FROM prices "); $stmt = db()->prepare("SELECT * FROM prices ");
@ -235,6 +262,42 @@ foreach ($prices_raw as $p) {
</div> </div>
</div> </div>
<!-- Filter Bar -->
<div class="card border-0 shadow-sm mb-4" style="border-radius: 20px;">
<div class="card-body p-4">
<form method="GET" class="row g-3 align-items-end">
<div class="col-md-5">
<label class="form-label fw-bold small text-muted text-uppercase mb-2"><?= __('search') ?></label>
<div class="input-group">
<span class="input-group-text bg-light border-0" style="border-radius: 12px 0 0 12px;">
<i class="bi bi-search"></i>
</span>
<input type="text" name="search" class="form-control bg-light border-0" value="<?= htmlspecialchars($search) ?>" placeholder="<?= __('search_placeholder') ?? 'Search items...' ?>" style="border-radius: 0 12px 12px 0;">
</div>
</div>
<div class="col-md-4">
<label class="form-label fw-bold small text-muted text-uppercase mb-2"><?= __('category') ?></label>
<select name="category_id" class="form-select bg-light border-0" style="border-radius: 12px;">
<option value=""><?= __('all') ?></option>
<?php foreach($categories as $cat): ?>
<option value="<?= $cat['id'] ?>" <?= $category_filter == $cat['id'] ? 'selected' : '' ?> >
<?= $lang === 'ar' ? ($cat['name_ar'] ?: $cat['name_en']) : $cat['name_en'] ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 d-flex gap-2">
<button type="submit" class="btn btn-primary flex-grow-1 fw-bold" style="border-radius: 12px; height: 45px;">
<i class="bi bi-filter me-2"></i><?= __('submit') ?>
</button>
<a href="items.php" class="btn btn-light fw-bold" style="border-radius: 12px; height: 45px;">
<i class="bi bi-x-lg"></i>
</a>
</div>
</form>
</div>
</div>
<?php if(isset($_GET['success'])): ?> <?php if(isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show rounded-4 shadow-sm mb-4 border-0" role="alert"> <div class="alert alert-success alert-dismissible fade show rounded-4 shadow-sm mb-4 border-0" role="alert">
<i class="bi bi-check-circle-fill me-2"></i> <i class="bi bi-check-circle-fill me-2"></i>
@ -263,23 +326,31 @@ foreach ($prices_raw as $p) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach($items as $item): <?php if (empty($items)): ?>
<tr>
<td colspan="5" class="text-center py-5">
<i class="bi bi-box-seam display-4 text-muted mb-3 d-block"></i>
<p class="text-muted"><?= __('no_items_found') ?></p>
</td>
</tr>
<?php else: ?>
<?php foreach($items as $item):
?><tr> ?><tr>
<td class="ps-4"> <td class="ps-4">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="bg-light rounded-3 me-3 d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; overflow: hidden;"> <div class="bg-light rounded-3 me-3 d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; overflow: hidden;">
<?php if($item['image_url']): <?php if($item['image_url']): ?>
?><img src="<?= $item['image_url'] ?>?v=<?= time() ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;"> <img src="<?= $item['image_url'] ?>?v=<?= time() ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else: <?php else: ?>
?><i class="bi bi-image text-muted"></i> <i class="bi bi-image text-muted"></i>
<?php endif; ?> <?php endif; ?>
</div> </div>
<div> <div>
<div class="fw-bold"><?= $lang === 'ar' ? ($item['name_ar'] ?: $item['name_en']) : $item['name_en'] ?></div> <div class="fw-bold"><?= $lang === 'ar' ? ($item['name_ar'] ?: $item['name_en']) : $item['name_en'] ?></div>
<?php if($lang === 'ar' && $item['name_ar']): <?php if($lang === 'ar' && $item['name_ar']): ?>
?><small class="text-muted"><?= $item['name_en'] ?></small> <small class="text-muted"><?= $item['name_en'] ?></small>
<?php elseif($lang === 'en' && $item['name_ar']): <?php elseif($lang === 'en' && $item['name_ar']): ?>
?><small class="text-muted"><?= $item['name_ar'] ?></small> <small class="text-muted"><?= $item['name_ar'] ?></small>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
@ -296,19 +367,20 @@ foreach ($prices_raw as $p) {
</button> </button>
</td> </td>
<td class="pe-4 text-end"> <td class="pe-4 text-end">
<?php if (has_permission('edit')): <?php if (has_permission('edit')): ?>
?><button class="btn btn-sm btn-light p-2 me-1" style="border-radius: 10px;" onclick="openItemModal(<?= htmlspecialchars(json_encode($item)) ?>)"> <button class="btn btn-sm btn-light p-2 me-1" style="border-radius: 10px;" onclick="openItemModal(<?= htmlspecialchars(json_encode($item)) ?>)">
<i class="bi bi-pencil text-primary"></i> <i class="bi bi-pencil text-primary"></i>
</button> </button>
<?php endif; ?> <?php endif; ?>
<?php if (has_permission('delete')): <?php if (has_permission('delete')): ?>
?><button class="btn btn-sm btn-light p-2" style="border-radius: 10px;" onclick="confirmDelete('item', <?= $item['id'] ?>)"> <button class="btn btn-sm btn-light p-2" style="border-radius: 10px;" onclick="confirmDelete('item', <?= $item['id'] ?>)">
<i class="bi bi-trash text-danger"></i> <i class="bi bi-trash text-danger"></i>
</button> </button>
<?php endif; ?> <?php endif; ?>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -49,7 +49,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<link rel="icon" type="image/x-icon" href="<?= $company_info['favicon'] ?>"> <link rel="icon" type="image/x-icon" href="<?= $company_info['favicon'] ?>">
<?php endif; ?> <?php endif; ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.<?= is_rtl() ? 'rtl.' : '' ?>min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.<?= is_rtl() ? 'rtl.' : '' ?>min.css">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Cairo:wght@400;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Cairo:wght@400;600;700&display=swap" rel="stylesheet">
@ -76,6 +76,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
width: auto; width: auto;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.btn-primary {
background-color: #3b82f6;
border-color: #3b82f6;
}
.btn-primary:hover {
background-color: #2563eb;
border-color: #2563eb;
}
.form-control:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25);
}
</style> </style>
</head> </head>
<body> <body>
@ -106,9 +118,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="text-center mt-3 pt-3 border-top"> <div class="text-center mt-3 pt-3 border-top">
<div class="nav-link text-muted small"> <div class="nav-link text-muted small">
<a href="?lang=en" class="text-decoration-none <?= $lang === 'en' ? 'fw-bold text-primary' : '' ?>">English</a> <a href="<?= lang_url('en') ?>" class="text-decoration-none <?= $lang === 'en' ? 'fw-bold text-primary' : '' ?>">English</a>
<span class="mx-2 text-secondary opacity-25">|</span> <span class="mx-2 text-secondary opacity-25">|</span>
<a href="?lang=ar" class="text-decoration-none <?= $lang === 'ar' ? 'fw-bold text-primary' : '' ?>">العربية</a> <a href="<?= lang_url('ar') ?>" class="text-decoration-none <?= $lang === 'ar' ? 'fw-bold text-primary' : '' ?>">العربية</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,12 +3,13 @@ $title = 'orders';
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
$current_role = $_SESSION['role'] ?? 'cashier'; $current_role = $_SESSION['role'] ?? 'cashier';
$session_branch_id = $_SESSION['branch_id']; $session_branch_id = $_SESSION['branch_id'] ?? 'all';
// Filters // Filters
$status_filter = $_GET['status'] ?? ''; $status_filter = $_GET['status'] ?? '';
$payment_filter = $_GET['payment_status'] ?? ''; $payment_filter = $_GET['payment_status'] ?? '';
$branch_filter = $_GET['branch_id'] ?? ''; // Default to session branch if no explicit GET filter
$branch_filter = $_GET['branch_id'] ?? $session_branch_id;
$from_date = $_GET['from_date'] ?? ''; $from_date = $_GET['from_date'] ?? '';
$to_date = $_GET['to_date'] ?? ''; $to_date = $_GET['to_date'] ?? '';
$search = $_GET['search'] ?? ''; $search = $_GET['search'] ?? '';
@ -26,10 +27,7 @@ $sql_base = " FROM orders o
$params = []; $params = [];
// Branch restriction // Branch restriction
if ($current_role !== 'super_admin') { if ($branch_filter !== 'all') {
$sql_base .= " AND o.branch_id = ?";
$params[] = $session_branch_id;
} elseif ($branch_filter) {
$sql_base .= " AND o.branch_id = ?"; $sql_base .= " AND o.branch_id = ?";
$params[] = $branch_filter; $params[] = $branch_filter;
} }
@ -98,7 +96,7 @@ if ($current_role === 'super_admin') {
<?php if ($current_role === 'super_admin'): ?> <?php if ($current_role === 'super_admin'): ?>
<div class="col-md-2"> <div class="col-md-2">
<select name="branch_id" class="form-select" style="border-radius: 12px;" onchange="this.form.submit()"> <select name="branch_id" class="form-select" style="border-radius: 12px;" onchange="this.form.submit()">
<option value=""><?= __('all_branches') ?></option> <option value="all"><?= __('all_branches') ?></option>
<?php foreach($branches as $b): ?> <?php foreach($branches as $b): ?>
<option value="<?= $b['id'] ?>" <?= $branch_filter == $b['id'] ? 'selected' : '' ?>> <option value="<?= $b['id'] ?>" <?= $branch_filter == $b['id'] ? 'selected' : '' ?>>
<?= $lang === 'ar' ? ($b['name_ar'] ?: $b['name_en']) : $b['name_en'] ?> <?= $lang === 'ar' ? ($b['name_ar'] ?: $b['name_en']) : $b['name_en'] ?>

View File

@ -5,9 +5,11 @@ require_once __DIR__ . '/includes/header.php';
// View check is already handled by header.php global check. // View check is already handled by header.php global check.
// If someone needs specific action permission on reports, we could check has_permission('add') etc. // If someone needs specific action permission on reports, we could check has_permission('add') etc.
$session_branch_id = $_SESSION['branch_id'] ?? 'all';
$from_date = $_GET['from_date'] ?? date('Y-m-01'); $from_date = $_GET['from_date'] ?? date('Y-m-01');
$to_date = $_GET['to_date'] ?? date('Y-m-t'); $to_date = $_GET['to_date'] ?? date('Y-m-t');
$branch_filter = $_GET['branch_id'] ?? 'all'; // Default to session branch if no explicit GET filter
$branch_filter = $_GET['branch_id'] ?? $session_branch_id;
$user_filter = $_GET['user_id'] ?? 'all'; $user_filter = $_GET['user_id'] ?? 'all';
// Base queries // Base queries