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';
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
$branch_id = $_SESSION['branch_id'];
$is_super = ($current_role === 'super_admin');
$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) {
$stats = [
'today_revenue' => 0,
@ -16,47 +42,47 @@ if (!$is_limited) {
];
// Today's revenue
$rev_where = $is_super ? "" : " AND order_id IN (SELECT id FROM orders WHERE branch_id = ?)";
$rev_params = $is_super ? [] : [$branch_id];
$rev_where = !$filter_branch ? "" : " AND order_id IN (SELECT id FROM orders WHERE 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->execute($rev_params);
$stats['today_revenue'] = $stmt->fetch()['total'] ?? 0;
// Active orders (received, processing)
$ord_where = $is_super ? "WHERE status IN ('received', 'processing')" : "WHERE branch_id = ? AND status IN ('received', 'processing')";
$ord_params = $is_super ? [] : [$branch_id];
$ord_where = !$filter_branch ? "WHERE status IN ('received', 'processing')" : "WHERE branch_id = ? AND status IN ('received', 'processing')";
$ord_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ord_where");
$stmt->execute($ord_params);
$stats['active_orders'] = $stmt->fetch()['count'] ?? 0;
// Ready orders
$ready_where = $is_super ? "WHERE status = 'ready'" : "WHERE branch_id = ? AND status = 'ready'";
$ready_params = $is_super ? [] : [$branch_id];
$ready_where = !$filter_branch ? "WHERE status = 'ready'" : "WHERE branch_id = ? AND status = 'ready'";
$ready_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ready_where");
$stmt->execute($ready_params);
$stats['ready_orders'] = $stmt->fetch()['count'] ?? 0;
// New customers today
$cust_where = $is_super ? "WHERE created_at >= CURDATE()" : "WHERE branch_id = ? AND created_at >= CURDATE()";
$cust_params = $is_super ? [] : [$branch_id];
$cust_where = !$filter_branch ? "WHERE created_at >= CURDATE()" : "WHERE branch_id = ? AND created_at >= CURDATE()";
$cust_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM customers $cust_where");
$stmt->execute($cust_params);
$stats['new_customers'] = $stmt->fetch()['count'] ?? 0;
// Recent orders
$recent_where = $is_super ? "" : "WHERE o.branch_id = ?";
$recent_params = $is_super ? [] : [$branch_id];
$stmt = db()->prepare("SELECT o.*, c.name_en as customer_name_en, c.name_ar as customer_name_ar
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
$recent_where
ORDER BY o.created_at DESC LIMIT 5");
$stmt->execute($recent_params);
$recent_orders = $stmt->fetchAll();
// Monthly Orders (Last 12 months)
$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)";
$monthly_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT DATE_FORMAT(created_at, '%Y-%m') as month_year, COUNT(*) as count
FROM orders
$monthly_where
GROUP BY month_year
ORDER BY month_year ASC");
$stmt->execute($monthly_params);
$monthly_orders = $stmt->fetchAll();
// 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_params = $is_super ? [] : [$branch_id];
$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 = !$filter_branch ? [] : [$branch_id];
// Daily 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();
// Orders by Status (All time or current)
$status_where = $is_super ? "" : "WHERE branch_id = ?";
$status_params = $is_super ? [] : [$branch_id];
$status_where = !$filter_branch ? "" : "WHERE branch_id = ?";
$status_params = !$filter_branch ? [] : [$branch_id];
$stmt = db()->prepare("SELECT status, COUNT(*) as count FROM orders $status_where GROUP BY status");
$stmt->execute($status_params);
$orders_by_status = $stmt->fetchAll();
@ -164,37 +190,8 @@ if (!$is_limited) {
<div class="row g-4">
<div class="col-md-8">
<div class="card p-4 h-100">
<h5 class="fw-bold mb-4"><?= __('recent_orders') ?></h5>
<div class="table-responsive">
<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>
<h5 class="fw-bold mb-4"><?= __('orders_per_month') ?></h5>
<canvas id="monthlyOrdersChart" height="120"></canvas>
</div>
</div>
<div class="col-md-4">
@ -216,62 +213,76 @@ if (!$is_limited) {
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Revenue Chart
new Chart(document.getElementById('revenueChart'), {
type: 'line',
data: {
labels: <?= json_encode(array_column($daily_revenue, 'date')) ?>,
datasets: [{
label: '<?= __('revenue') ?>',
data: <?= json_encode(array_column($daily_revenue, 'revenue')) ?>,
borderColor: '#3b82f6',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: { y: { beginAtZero: true } }
}
});
if (document.getElementById('revenueChart')) {
new Chart(document.getElementById('revenueChart'), {
type: 'line',
data: {
labels: <?= json_encode(array_column($daily_revenue, 'date')) ?>,
datasets: [{
label: '<?= __('revenue') ?>',
data: <?= json_encode(array_column($daily_revenue, 'revenue')) ?>,
borderColor: '#3b82f6',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: { y: { beginAtZero: true } }
}
});
}
// Status Chart
new Chart(document.getElementById('statusChart'), {
type: 'doughnut',
data: {
labels: <?= json_encode(array_map(function($s) { return __($s['status']); }, $orders_by_status)) ?>,
datasets: [{
data: <?= json_encode(array_column($orders_by_status, 'count')) ?>,
backgroundColor: ['#6c757d', '#3b82f6', '#10b981', '#111827', '#ef4444']
}]
},
options: {
responsive: true,
plugins: { legend: { position: 'bottom' } }
}
});
if (document.getElementById('statusChart')) {
new Chart(document.getElementById('statusChart'), {
type: 'doughnut',
data: {
labels: <?= json_encode(array_map(function($s) { return __($s['status']); }, $orders_by_status)) ?>,
datasets: [{
data: <?= json_encode(array_column($orders_by_status, 'count')) ?>,
backgroundColor: ['#6c757d', '#3b82f6', '#10b981', '#111827', '#ef4444']
}]
},
options: {
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>
<?php endif; ?>
<?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';
?>

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

View File

@ -1,8 +1,81 @@
</main>
</main>
</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>
// 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>
</html>
</html>

View File

@ -14,17 +14,39 @@ $current_role = $_SESSION['role'] ?? 'cashier';
// Branch switching logic
if (isset($_GET['switch_branch']) && $current_user_id) {
$new_branch_id = (int)$_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 = ?");
$stmt->execute([$current_user_id, $new_branch_id]);
$new_branch_id = $_GET['switch_branch'];
if ($new_branch_id === 'all' && $current_role === 'super_admin') {
$_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();
if ($branch) {
$_SESSION['branch_id'] = $branch['id'];
$_SESSION['branch_name'] = $lang === 'ar' ? ($branch['name_ar'] ?: $branch['name_en']) : $branch['name_en'];
// Redirect back to same page without the query param
$url = strtok($_SERVER["REQUEST_URI"], '?');
// 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;
}
@ -33,9 +55,29 @@ if (isset($_GET['switch_branch']) && $current_user_id) {
// Fetch user branches
$user_branches = [];
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 = ?");
$stmt->execute([$current_user_id]);
$user_branches = $stmt->fetchAll();
if ($current_role === 'super_admin') {
$user_branches = db()->query("SELECT * FROM branches")->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
@ -54,9 +96,6 @@ if ($current_user_id) {
$current_page = basename($_SERVER['PHP_SELF']);
if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logout.php') {
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')) {
header('Location: admin.php');
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'] ?>">
<?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://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.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://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="preconnect" href="https://fonts.googleapis.com">
<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;
<?= 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>
</head>
<body>
@ -240,15 +300,6 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
<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')): ?>
<li class="nav-item">
<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">
<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>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="mb-2 mb-md-0">
<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">
<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'] ?? '' ?>
</button>
<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): ?>
<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'] ?>
</a>
</li>
@ -297,17 +356,17 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
<!-- Language Switch -->
<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' ?>
</button>
<ul class="dropdown-menu shadow border-0 mt-2">
<li>
<a class="dropdown-item <?= $lang === 'en' ? 'active' : '' ?>" href="?lang=en">
<a class="dropdown-item <?= $lang === 'en' ? 'active' : '' ?>" href="<?= lang_url('en') ?>">
English
</a>
</li>
<li>
<a class="dropdown-item <?= $lang === 'ar' ? 'active' : '' ?>" href="?lang=ar">
<a class="dropdown-item <?= $lang === 'ar' ? 'active' : '' ?>" href="<?= lang_url('ar') ?>">
العربية
</a>
</li>
@ -315,14 +374,14 @@ if ($current_user_id && $current_page !== 'login.php' && $current_page !== 'logo
</div>
<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): ?>
<img src="<?= $current_user_data['profile_picture'] ?>" alt="Profile" class="user-avatar-sm me-2">
<?php else: ?>
<i class="bi bi-person-circle fs-5 me-2 text-secondary"></i>
<?php endif; ?>
<span class="small fw-bold"><?= $_SESSION['full_name'] ?? '' ?></span>
</div>
<span class="small fw-bold d-none d-sm-inline"><?= $_SESSION['full_name'] ?? '' ?></span>
</button>
<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')): ?>
<li>

View File

@ -148,7 +148,7 @@ $translations = [
'next' => 'Next',
'page' => 'Page',
'of' => 'of',
'Access Denied' => 'Access Denied',
'Access Denied' => 'تم رفض الوصول',
'active_orders' => 'Active Orders',
'add_branch' => 'Add Branch',
'add_new_branch' => 'Add New Branch',
@ -193,7 +193,8 @@ $translations = [
'confirm_delete_user' => 'Are you sure you want to delete this user?',
'leave_blank_to_keep_current' => 'leave blank to keep current',
'permissions' => 'Permissions',
'user_permissions' => 'User Permissions'
'user_permissions' => 'User Permissions',
'orders_per_month' => 'Orders per Month'
],
'ar' => [
'dashboard' => 'لوحة القيادة',
@ -380,7 +381,8 @@ $translations = [
'confirm_delete_user' => 'هل أنت متأكد من حذف هذا المستخدم؟',
'leave_blank_to_keep_current' => 'اتركه فارغاً للاحتفاظ بكلمة المرور الحالية',
'permissions' => 'الصلاحيات',
'user_permissions' => 'صلاحيات المستخدم'
'user_permissions' => 'صلاحيات المستخدم',
'orders_per_month' => 'الطلبات شهرياً'
]
];
@ -409,4 +411,16 @@ function decimals() {
function format_amount($amount) {
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,8 +1,9 @@
<?php
session_start();
require_once __DIR__ . '/includes/lang.php';
if (isset($_SESSION['user_id'])) {
header('Location: admin.php');
} else {
header('Location: login.php');
}
exit;
exit;

116
items.php
View File

@ -202,11 +202,38 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$title = 'items';
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();
$items = db()->query("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
FROM items i
LEFT JOIN categories c ON i.category_id = c.id
ORDER BY i.name_en ASC")->fetchAll();
$query = "SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
FROM items i
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();
$stmt = db()->prepare("SELECT * FROM prices ");
@ -235,6 +262,42 @@ foreach ($prices_raw as $p) {
</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'])): ?>
<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>
@ -263,23 +326,31 @@ foreach ($prices_raw as $p) {
</tr>
</thead>
<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>
<td class="ps-4">
<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;">
<?php if($item['image_url']):
?><img src="<?= $item['image_url'] ?>?v=<?= time() ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else:
?><i class="bi bi-image text-muted"></i>
<?php if($item['image_url']): ?>
<img src="<?= $item['image_url'] ?>?v=<?= time() ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else: ?>
<i class="bi bi-image text-muted"></i>
<?php endif; ?>
</div>
<div>
<div class="fw-bold"><?= $lang === 'ar' ? ($item['name_ar'] ?: $item['name_en']) : $item['name_en'] ?></div>
<?php if($lang === 'ar' && $item['name_ar']):
?><small class="text-muted"><?= $item['name_en'] ?></small>
<?php elseif($lang === 'en' && $item['name_ar']):
?><small class="text-muted"><?= $item['name_ar'] ?></small>
<?php if($lang === 'ar' && $item['name_ar']): ?>
<small class="text-muted"><?= $item['name_en'] ?></small>
<?php elseif($lang === 'en' && $item['name_ar']): ?>
<small class="text-muted"><?= $item['name_ar'] ?></small>
<?php endif; ?>
</div>
</div>
@ -296,19 +367,20 @@ foreach ($prices_raw as $p) {
</button>
</td>
<td class="pe-4 text-end">
<?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)) ?>)">
<i class="bi bi-pencil text-primary"></i>
</button>
<?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)) ?>)">
<i class="bi bi-pencil text-primary"></i>
</button>
<?php endif; ?>
<?php if (has_permission('delete')):
?><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>
</button>
<?php if (has_permission('delete')): ?>
<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>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
@ -769,4 +841,4 @@ document.getElementById('itemImageFile').addEventListener('change', function(e)
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -49,7 +49,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<link rel="icon" type="image/x-icon" href="<?= $company_info['favicon'] ?>">
<?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.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">
@ -76,6 +76,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
width: auto;
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>
</head>
<body>
@ -106,9 +118,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="text-center mt-3 pt-3 border-top">
<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>
<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>

View File

@ -3,12 +3,13 @@ $title = 'orders';
require_once __DIR__ . '/includes/header.php';
$current_role = $_SESSION['role'] ?? 'cashier';
$session_branch_id = $_SESSION['branch_id'];
$session_branch_id = $_SESSION['branch_id'] ?? 'all';
// Filters
$status_filter = $_GET['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'] ?? '';
$to_date = $_GET['to_date'] ?? '';
$search = $_GET['search'] ?? '';
@ -26,10 +27,7 @@ $sql_base = " FROM orders o
$params = [];
// Branch restriction
if ($current_role !== 'super_admin') {
$sql_base .= " AND o.branch_id = ?";
$params[] = $session_branch_id;
} elseif ($branch_filter) {
if ($branch_filter !== 'all') {
$sql_base .= " AND o.branch_id = ?";
$params[] = $branch_filter;
}
@ -98,7 +96,7 @@ if ($current_role === 'super_admin') {
<?php if ($current_role === 'super_admin'): ?>
<div class="col-md-2">
<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): ?>
<option value="<?= $b['id'] ?>" <?= $branch_filter == $b['id'] ? 'selected' : '' ?>>
<?= $lang === 'ar' ? ($b['name_ar'] ?: $b['name_en']) : $b['name_en'] ?>
@ -351,4 +349,4 @@ function getPaymentStatusColor($status) {
][$status] ?? 'info';
}
require_once __DIR__ . '/includes/footer.php';
?>
?>

View File

@ -5,9 +5,11 @@ require_once __DIR__ . '/includes/header.php';
// 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.
$session_branch_id = $_SESSION['branch_id'] ?? 'all';
$from_date = $_GET['from_date'] ?? date('Y-m-01');
$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';
// Base queries
@ -392,4 +394,4 @@ if ($user_filter !== 'all') {
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>