update users who has multi branch

This commit is contained in:
Flatlogic Bot 2026-03-03 05:22:06 +00:00
parent 2a38c2ffdb
commit 78166012df
24 changed files with 1384 additions and 367 deletions

387
admin.php
View File

@ -4,167 +4,274 @@ require_once __DIR__ . '/includes/header.php';
// Stats logic
$branch_id = $_SESSION['branch_id'];
$stats = [
'today_revenue' => 0,
'active_orders' => 0,
'new_customers' => 0,
'ready_orders' => 0,
];
$is_super = ($current_role === 'super_admin');
$is_limited = ($current_role === 'limited_viewer');
// Today's revenue
$stmt = db()->prepare("SELECT SUM(amount) as total FROM payments WHERE created_at >= CURDATE() AND order_id IN (SELECT id FROM orders WHERE branch_id = ?)");
$stmt->execute([$branch_id]);
$stats['today_revenue'] = $stmt->fetch()['total'] ?? 0;
if (!$is_limited) {
$stats = [
'today_revenue' => 0,
'active_orders' => 0,
'new_customers' => 0,
'ready_orders' => 0,
];
// Active orders (received, processing)
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders WHERE branch_id = ? AND status IN ('received', 'processing')");
$stmt->execute([$branch_id]);
$stats['active_orders'] = $stmt->fetch()['count'] ?? 0;
// Today's revenue
$rev_where = $is_super ? "" : " AND order_id IN (SELECT id FROM orders WHERE branch_id = ?)";
$rev_params = $is_super ? [] : [$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;
// Ready orders
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders WHERE branch_id = ? AND status = 'ready'");
$stmt->execute([$branch_id]);
$stats['ready_orders'] = $stmt->fetch()['count'] ?? 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];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ord_where");
$stmt->execute($ord_params);
$stats['active_orders'] = $stmt->fetch()['count'] ?? 0;
// New customers today
$stmt = db()->prepare("SELECT COUNT(*) as count FROM customers WHERE branch_id = ? AND created_at >= CURDATE()");
$stmt->execute([$branch_id]);
$stats['new_customers'] = $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];
$stmt = db()->prepare("SELECT COUNT(*) as count FROM orders $ready_where");
$stmt->execute($ready_params);
$stats['ready_orders'] = $stmt->fetch()['count'] ?? 0;
// Recent orders
$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
WHERE o.branch_id = ?
ORDER BY o.created_at DESC LIMIT 5");
$stmt->execute([$branch_id]);
$recent_orders = $stmt->fetchAll();
// New customers today
$cust_where = $is_super ? "WHERE created_at >= CURDATE()" : "WHERE branch_id = ? AND created_at >= CURDATE()";
$cust_params = $is_super ? [] : [$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();
// 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];
// Daily Revenue
$stmt = db()->prepare("SELECT DATE(created_at) as date, SUM(total_price) as revenue
FROM orders
$chart_where
GROUP BY DATE(created_at)
ORDER BY DATE(created_at) ASC");
$stmt->execute($chart_params);
$daily_revenue = $stmt->fetchAll();
// Orders by Status (All time or current)
$status_where = $is_super ? "" : "WHERE branch_id = ?";
$status_params = $is_super ? [] : [$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();
}
?>
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-primary text-white p-3 rounded-4 me-3">
<i class="bi bi-cash-stack fs-3"></i>
<?php if ($is_limited): ?>
<div class="d-flex flex-column align-items-center justify-content-center" style="min-height: 60vh;">
<div class="text-center p-5 card shadow-sm" style="border-radius: 30px; max-width: 600px;">
<?php if ($company_info['logo']): ?>
<img src="<?= $company_info['logo'] ?>" alt="Logo" class="img-fluid mb-4" style="max-height: 150px;">
<?php endif; ?>
<h1 class="fw-bold text-primary mb-3">
<?= htmlspecialchars(is_arabic() ? $company_info['name_ar'] : $company_info['name_en']) ?>
</h1>
<p class="text-muted fs-5">
<?= is_arabic() ? 'مرحباً بك في لوحة التحكم' : 'Welcome to your dashboard' ?>
</p>
</div>
</div>
<?php else: ?>
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-primary text-white p-3 rounded-4 me-3">
<i class="bi bi-cash-stack fs-3"></i>
</div>
<div>
<div class="text-muted small"><?= __('today_revenue') ?></div>
<div class="fw-bold fs-5"><?= format_amount($stats['today_revenue']) ?></div>
</div>
</div>
<div>
<div class="text-muted small"><?= __('today_revenue') ?? 'Today Revenue' ?></div>
<div class="fw-bold fs-5"><?= format_amount($stats['today_revenue']) ?></div>
</div>
</div>
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-warning text-white p-3 rounded-4 me-3">
<i class="bi bi-clock-history fs-3"></i>
</div>
<div>
<div class="text-muted small"><?= __('active_orders') ?></div>
<div class="fw-bold fs-5"><?= $stats['active_orders'] ?></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-success text-white p-3 rounded-4 me-3">
<i class="bi bi-check-circle fs-3"></i>
</div>
<div>
<div class="text-muted small"><?= __('ready_orders') ?></div>
<div class="fw-bold fs-5"><?= $stats['ready_orders'] ?></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-info text-white p-3 rounded-4 me-3">
<i class="bi bi-people fs-3"></i>
</div>
<div>
<div class="text-muted small"><?= __('new_customers') ?></div>
<div class="fw-bold fs-5"><?= $stats['new_customers'] ?></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-warning text-white p-3 rounded-4 me-3">
<i class="bi bi-clock-history fs-3"></i>
</div>
<div>
<div class="text-muted small"><?= __('active_orders') ?></div>
<div class="fw-bold fs-5"><?= $stats['active_orders'] ?></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-success text-white p-3 rounded-4 me-3">
<i class="bi bi-check-circle fs-3"></i>
</div>
<div>
<div class="text-muted small"><?= __('ready_orders') ?? 'Ready Orders' ?></div>
<div class="fw-bold fs-5"><?= $stats['ready_orders'] ?></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card p-3">
<div class="d-flex align-items-center">
<div class="bg-info text-white p-3 rounded-4 me-3">
<i class="bi bi-people fs-3"></i>
</div>
<div>
<div class="text-muted small"><?= __('new_customers') ?? 'New Customers' ?></div>
<div class="fw-bold fs-5"><?= $stats['new_customers'] ?></div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="card p-4 h-100">
<h5 class="fw-bold mb-4"><?= __('recent_orders') ?? '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 class="row g-4 mb-4">
<div class="col-md-8">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('revenue_last_7_days') ?? 'Revenue (Last 7 Days)' ?></h6>
<canvas id="revenueChart" height="100"></canvas>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('orders_by_status') ?></h6>
<canvas id="statusChart"></canvas>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 h-100 bg-primary text-white">
<h5 class="fw-bold mb-4"><?= __('quick_actions') ?? 'Quick Actions' ?></h5>
<a href="pos.php" class="btn btn-light w-100 mb-3 text-start py-3 px-4" style="border-radius: 15px; font-weight: 600;">
<i class="bi bi-plus-circle-fill me-2"></i> <?= __('new_order') ?>
</a>
<a href="customers.php" class="btn btn-light w-100 mb-3 text-start py-3 px-4" style="border-radius: 15px; font-weight: 600;">
<i class="bi bi-person-plus-fill me-2"></i> <?= __('add_new_customer') ?? 'Add New Customer' ?>
</a>
<a href="reports.php" class="btn btn-light w-100 text-start py-3 px-4" style="border-radius: 15px; font-weight: 600;">
<i class="bi bi-file-earmark-bar-graph-fill me-2"></i> <?= __('reports') ?? 'Reports' ?>
</a>
<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>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 h-100 bg-primary text-white">
<h5 class="fw-bold mb-4"><?= __('quick_actions') ?></h5>
<a href="pos.php" class="btn btn-light w-100 mb-3 text-start py-3 px-4" style="border-radius: 15px; font-weight: 600;">
<i class="bi bi-plus-circle-fill me-2"></i> <?= __('new_order') ?>
</a>
<a href="customers.php" class="btn btn-light w-100 mb-3 text-start py-3 px-4" style="border-radius: 15px; font-weight: 600;">
<i class="bi bi-person-plus-fill me-2"></i> <?= __('add_new_customer') ?>
</a>
<a href="reports.php" class="btn btn-light w-100 text-start py-3 px-4" style="border-radius: 15px; font-weight: 600;">
<i class="bi bi-file-earmark-bar-graph-fill me-2"></i> <?= __('reports') ?>
</a>
</div>
</div>
</div>
</div>
<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 } }
}
});
// 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' } }
}
});
</script>
<?php endif; ?>
<?php
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';
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';
?>

View File

@ -19,8 +19,8 @@ if (!$phone || !$name_en) {
}
try {
$stmt = db()->prepare("INSERT INTO customers (branch_id, phone, name_en, name_ar) VALUES (?, ?, ?, ?)");
$stmt->execute([$branch_id, $phone, $name_en, $name_ar]);
$stmt = db()->prepare("INSERT INTO customers (phone, name_en, name_ar) VALUES (?, ?, ?)");
$stmt->execute([$phone, $name_en, $name_ar]);
$customer_id = db()->lastInsertId();
echo json_encode([

View File

@ -15,8 +15,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$branch_id = $_SESSION['branch_id'];
if ($phone && $name_en) {
$stmt = db()->prepare("INSERT INTO customers (branch_id, phone, name_en, name_ar, email) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$branch_id, $phone, $name_en, $name_ar, $email]);
$stmt = db()->prepare("INSERT INTO customers (phone, name_en, name_ar, email) VALUES (?, ?, ?, ?)");
$stmt->execute([$phone, $name_en, $name_ar, $email]);
}
}

View File

@ -0,0 +1,21 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../includes/lang.php';
if (!isset($_SESSION['user_id'])) {
echo json_encode(['error' => 'Unauthorized']);
exit;
}
$user_id = $_GET['user_id'] ?? null;
if (!$user_id) {
echo json_encode(['error' => 'User ID required']);
exit;
}
$stmt = db()->prepare("SELECT page, can_view, can_add, can_edit, can_delete FROM user_permissions WHERE user_id = ?");
$stmt->execute([$user_id]);
$permissions = $stmt->fetchAll();
echo json_encode($permissions);

View File

@ -107,4 +107,33 @@ body {
.item-card:hover {
transform: scale(1.02);
}
}
/* Print Styles */
@media print {
.sidebar, .navbar, .no-print, .btn, .card form {
display: none !important;
}
body {
background-color: white !important;
margin: 0;
padding: 0;
}
.main-content {
margin-left: 0 !important;
width: 100% !important;
}
.card {
box-shadow: none !important;
border: 1px solid #eee !important;
margin-bottom: 20px !important;
page-break-inside: avoid;
}
.container-fluid {
padding: 0 !important;
}
canvas {
max-width: 100% !important;
height: auto !important;
}
}

View File

@ -8,14 +8,24 @@ if (!isset($_SESSION['user_id'])) {
exit;
}
$current_role = $_SESSION['role'] ?? 'cashier';
if ($current_role !== 'super_admin') {
// Initial view check
if (!has_permission('view')) {
header('Location: admin.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if ($_POST['action'] === 'add_branch') {
$action = $_POST['action'];
if ($action === 'add_branch' && !has_permission('add')) {
header('Location: branches.php?error=no_permission');
exit;
}
if ($action === 'edit_branch' && !has_permission('edit')) {
header('Location: branches.php?error=no_permission');
exit;
}
if ($action === 'add_branch') {
$name_en = $_POST['name_en'];
$name_ar = $_POST['name_ar'];
$company_id = $_POST['company_id'];
@ -23,11 +33,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$prefix = strtoupper(substr($_POST['prefix'] ?? '', 0, 3));
$stmt = db()->prepare("INSERT INTO branches (name_en, name_ar, company_id, phone, prefix) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $company_id, $phone, $prefix]);
header('Location: branches.php');
header('Location: branches.php?success=branch_added');
exit;
}
if ($_POST['action'] === 'edit_branch') {
if ($action === 'edit_branch') {
$id = $_POST['id'];
$name_en = $_POST['name_en'];
$name_ar = $_POST['name_ar'];
@ -36,16 +46,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$prefix = strtoupper(substr($_POST['prefix'] ?? '', 0, 3));
$stmt = db()->prepare("UPDATE branches SET name_en = ?, name_ar = ?, company_id = ?, phone = ?, prefix = ? WHERE id = ?");
$stmt->execute([$name_en, $name_ar, $company_id, $phone, $prefix, $id]);
header('Location: branches.php');
header('Location: branches.php?success=branch_updated');
exit;
}
}
if (isset($_GET['delete'])) {
if (!has_permission('delete')) {
header('Location: branches.php?error=no_permission');
exit;
}
$id = $_GET['delete'];
$stmt = db()->prepare("DELETE FROM branches WHERE id = ?");
$stmt->execute([$id]);
header('Location: branches.php');
header('Location: branches.php?success=branch_deleted');
exit;
}
@ -57,9 +71,25 @@ $branches = db()->query("SELECT b.*, c.name_en as company_name_en FROM branches
$companies = db()->query("SELECT * FROM companies")->fetchAll();
?>
<?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>
<?= __($_GET['success']) ?? 'Action completed successfully' ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if(isset($_GET['error'])): ?>
<div class="alert alert-danger alert-dismissible fade show rounded-4 shadow-sm mb-4 border-0" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
<?= __($_GET['error']) ?? 'Permission Denied' ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="row">
<div class="col-md-4">
<div class="card p-4 shadow-sm border-0" style="border-radius: 20px;">
<div class="card p-4 shadow-sm border-0 mb-4" style="border-radius: 20px;">
<?php if (has_permission('add')): ?>
<h5 class="fw-bold mb-4"><?= __('add_new_branch') ?? 'Add New Branch' ?></h5>
<form method="POST">
<input type="hidden" name="action" value="add_branch">
@ -88,8 +118,14 @@ $companies = db()->query("SELECT * FROM companies")->fetchAll();
<label class="form-label small fw-bold"><?= __('phone') ?></label>
<input type="text" name="phone" class="form-control" style="border-radius: 12px;">
</div>
<button type="submit" class="btn btn-primary w-100 py-3 fw-bold" style="border-radius: 15px;"><?= __('add_branch') ?></button>
<button type="submit" class="btn btn-primary w-100 py-3 fw-bold shadow-sm" style="border-radius: 15px;"><?= __('add_branch') ?></button>
</form>
<?php else: ?>
<div class="text-center py-4">
<i class="bi bi-lock fs-1 text-muted opacity-25"></i>
<p class="text-muted mt-2 small">You don't have permission to add branches.</p>
</div>
<?php endif; ?>
</div>
</div>
<div class="col-md-8">
@ -115,6 +151,7 @@ $companies = db()->query("SELECT * FROM companies")->fetchAll();
<td><?= $b['company_name_en'] ?></td>
<td><?= $b['phone'] ?></td>
<td class="pe-4 text-end">
<?php if (has_permission('edit')): ?>
<button class="btn btn-sm btn-outline-primary border-0 edit-branch"
data-id="<?= $b['id'] ?>"
data-name_en="<?= htmlspecialchars($b['name_en']) ?>"
@ -125,14 +162,25 @@ $companies = db()->query("SELECT * FROM companies")->fetchAll();
style="border-radius: 8px;">
<i class="bi bi-pencil"></i>
</button>
<?php endif; ?>
<?php if (has_permission('delete')): ?>
<a href="?delete=<?= $b['id'] ?>" class="btn btn-sm btn-outline-danger border-0"
onclick="return confirm('<?= __('are_you_sure') ?>')"
style="border-radius: 8px;">
<i class="bi bi-trash"></i>
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($branches)): ?>
<tr>
<td colspan="6" class="text-center py-5 text-muted">
<i class="bi bi-shop fs-1 d-block mb-3 opacity-25"></i>
No branches found.
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
@ -141,9 +189,10 @@ $companies = db()->query("SELECT * FROM companies")->fetchAll();
</div>
<!-- Edit Branch Modal -->
<?php if (has_permission('edit')): ?>
<div class="modal fade" id="editBranchModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow" style="border-radius: 20px;">
<div class="modal-content border-0 shadow-lg" style="border-radius: 20px;">
<div class="modal-header border-0 pb-0">
<h5 class="fw-bold"><?= __('edit') ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
@ -180,7 +229,7 @@ $companies = db()->query("SELECT * FROM companies")->fetchAll();
</div>
<div class="modal-footer border-0 pt-0">
<button type="button" class="btn btn-light fw-bold" data-bs-dismiss="modal" style="border-radius: 12px;"><?= __('cancel') ?></button>
<button type="submit" class="btn btn-primary fw-bold" style="border-radius: 12px;"><?= __('save_changes') ?></button>
<button type="submit" class="btn btn-primary fw-bold shadow-sm" style="border-radius: 12px;"><?= __('save_changes') ?></button>
</div>
</form>
</div>
@ -201,5 +250,6 @@ document.querySelectorAll('.edit-branch').forEach(btn => {
});
});
</script>
<?php endif; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,6 +1,7 @@
<?php
$title = 'company_profile';
require_once __DIR__ . '/includes/header.php';
if ($current_role === 'limited_viewer') { header('Location: admin.php'); exit; }
// Only super_admin can access company profile
if ($current_role !== 'super_admin') {

View File

@ -5,8 +5,8 @@ require_once __DIR__ . '/includes/header.php';
$branch_id = $_SESSION['branch_id'];
$search = $_GET['search'] ?? '';
$sql = "SELECT * FROM customers WHERE branch_id = ?";
$params = [$branch_id];
$sql = "SELECT * FROM customers WHERE 1=1";
$params = [];
if ($search) {
$sql .= " AND (name_en LIKE ? OR name_ar LIKE ? OR phone LIKE ?)";
@ -22,21 +22,23 @@ $customers = $stmt->fetchAll();
?>
<div class="card p-4">
<div class="card p-4 border-0 shadow-sm" style="border-radius: 20px;">
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
<h5 class="fw-bold mb-3 mb-md-0"><?= __('customers_list') ?? 'Customers List' ?></h5>
<div class="d-flex gap-2">
<form action="" method="GET" class="d-flex gap-2">
<div class="input-group">
<input type="text" name="search" class="form-control" placeholder="<?= __('search') ?>" value="<?= htmlspecialchars($search) ?>" style="border-radius: 12px 0 0 12px;">
<button class="btn btn-outline-secondary" type="submit" style="border-radius: 0 12px 12px 0;">
<div class="input-group shadow-sm rounded-4 overflow-hidden border">
<input type="text" name="search" class="form-control border-0" placeholder="<?= __('search') ?>" value="<?= htmlspecialchars($search) ?>">
<button class="btn btn-white border-0" type="submit">
<i class="bi bi-search"></i>
</button>
</div>
</form>
<button class="btn btn-primary px-4" style="border-radius: 12px;" data-bs-toggle="modal" data-bs-target="#addCustomerModal">
<?php if (has_permission('add')): ?>
<button class="btn btn-primary px-4 shadow-sm" style="border-radius: 12px;" data-bs-toggle="modal" data-bs-target="#addCustomerModal">
<i class="bi bi-person-plus-fill me-1"></i> <?= __('add_new') ?>
</button>
<?php endif; ?>
</div>
</div>
@ -49,7 +51,7 @@ $customers = $stmt->fetchAll();
<th><?= __('phone') ?></th>
<th><?= __('email') ?></th>
<th><?= __('date') ?></th>
<th><?= __('actions') ?></th>
<th class="text-end"><?= __('actions') ?></th>
</tr>
</thead>
<tbody>
@ -61,23 +63,34 @@ $customers = $stmt->fetchAll();
</td>
<td><?= $c['phone'] ?></td>
<td><?= $c['email'] ?: '-' ?></td>
<td class="small"><?= date('d/m/Y', strtotime($c['created_at'])) ?></td>
<td>
<button class="btn btn-sm btn-light border-0 p-2" style="border-radius: 8px;">
<td class="small text-muted"><?= date('d/m/Y', strtotime($c['created_at'])) ?></td>
<td class="text-end">
<?php if (has_permission('edit')): ?>
<button class="btn btn-sm btn-light border-0 p-2 text-primary" style="border-radius: 8px;">
<i class="bi bi-pencil-fill"></i>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($customers)): ?>
<tr>
<td colspan="6" class="text-center py-5 text-muted">
<i class="bi bi-people fs-1 d-block mb-3 opacity-25"></i>
<?= __('no_customers_found') ?? 'No customers found' ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Add Customer Modal -->
<?php if (has_permission('add')): ?>
<div class="modal fade" id="addCustomerModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0" style="border-radius: 20px;">
<div class="modal-content border-0 shadow-lg" style="border-radius: 20px;">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold"><?= __('add_new_customer') ?? 'Add New Customer' ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
@ -85,26 +98,27 @@ $customers = $stmt->fetchAll();
<div class="modal-body p-4">
<form action="api/add_customer_redirect.php" method="POST">
<div class="mb-3">
<label class="form-label"><?= __('phone') ?></label>
<label class="form-label small fw-bold"><?= __('phone') ?></label>
<input type="text" name="phone" class="form-control" required style="border-radius: 12px;">
</div>
<div class="mb-3">
<label class="form-label"><?= __('name_en') ?? 'Name (English)' ?></label>
<label class="form-label small fw-bold"><?= __('name_en') ?? 'Name (English)' ?></label>
<input type="text" name="name_en" class="form-control" required style="border-radius: 12px;">
</div>
<div class="mb-3">
<label class="form-label"><?= __('name_ar') ?? 'Name (Arabic)' ?></label>
<label class="form-label small fw-bold"><?= __('name_ar') ?? 'Name (Arabic)' ?></label>
<input type="text" name="name_ar" class="form-control" style="border-radius: 12px;">
</div>
<div class="mb-3">
<label class="form-label"><?= __('email') ?></label>
<label class="form-label small fw-bold"><?= __('email') ?></label>
<input type="email" name="email" class="form-control" style="border-radius: 12px;">
</div>
<button type="submit" class="btn btn-primary w-100 py-3 mt-2 fw-bold" style="border-radius: 15px;"><?= __('save') ?></button>
<button type="submit" class="btn btn-primary w-100 py-3 mt-2 fw-bold shadow-sm" style="border-radius: 15px;"><?= __('save') ?></button>
</form>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -15,3 +15,35 @@ function db() {
}
return $pdo;
}
function check_permission($page = null, $user_id = null) {
if (!$user_id) $user_id = $_SESSION['user_id'] ?? null;
if (!$page) $page = basename($_SERVER['PHP_SELF']);
if (!$user_id) return ['view' => 0, 'add' => 0, 'edit' => 0, 'delete' => 0];
static $permissions_cache = [];
$cache_key = $user_id . '_' . $page;
if (isset($permissions_cache[$cache_key])) return $permissions_cache[$cache_key];
$stmt = db()->prepare("SELECT can_view as view, can_add as `add`, can_edit as edit, can_delete as `delete` FROM user_permissions WHERE user_id = ? AND page = ?");
$stmt->execute([$user_id, $page]);
$perms = $stmt->fetch();
if (!$perms) {
return ['view' => 0, 'add' => 0, 'edit' => 0, 'delete' => 0];
}
$result = [
'view' => (int)$perms['view'],
'add' => (int)$perms['add'],
'edit' => (int)$perms['edit'],
'delete' => (int)$perms['delete']
];
$permissions_cache[$cache_key] = $result;
return $result;
}
function has_permission($action, $page = null, $user_id = null) {
$perms = check_permission($page, $user_id);
return !empty($perms[$action]);
}

View File

@ -0,0 +1,22 @@
-- Migration to make items, services, categories and customers shared between branches.
-- Only sales (orders) remain branch-specific.
-- Step 0: Ensure branch_id is indexed separately so we can drop the unique index later.
ALTER TABLE prices ADD INDEX idx_branch_id (branch_id);
-- Step 1: Cleanup duplicate prices across different branches for the same item/service.
-- We keep the oldest record (lowest ID).
DELETE p1 FROM prices p1
INNER JOIN prices p2
ON p1.item_id = p2.item_id
AND p1.service_id = p2.service_id
AND p1.id > p2.id;
-- Step 2: Update unique index for prices to be global (item_id + service_id only).
-- Note: Dropping the index might require idx_branch_id to exist for the foreign key.
DROP INDEX unique_price ON prices;
CREATE UNIQUE INDEX unique_price ON prices (item_id, service_id);
-- Step 3: Make branch_id optional in prices and customers tables.
ALTER TABLE prices MODIFY branch_id INT NULL;
ALTER TABLE customers MODIFY branch_id INT NULL;

View File

@ -0,0 +1 @@
ALTER TABLE users MODIFY COLUMN role ENUM('super_admin', 'branch_manager', 'cashier', 'limited_viewer') DEFAULT 'cashier';

View File

@ -0,0 +1,55 @@
CREATE TABLE IF NOT EXISTS user_permissions (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
page VARCHAR(50) NOT NULL,
can_view TINYINT(1) DEFAULT 0,
can_add TINYINT(1) DEFAULT 0,
can_edit TINYINT(1) DEFAULT 0,
can_delete TINYINT(1) DEFAULT 0,
UNIQUE KEY (user_id, page),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Seed existing users with default permissions based on their current roles
-- For Super Admins: everything
INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, p.page, 1, 1, 1, 1
FROM users
CROSS JOIN (
SELECT 'admin.php' as page UNION SELECT 'pos.php' UNION SELECT 'orders.php' UNION SELECT 'lab.php' UNION
SELECT 'customers.php' UNION SELECT 'reports.php' UNION SELECT 'items.php' UNION SELECT 'branches.php' UNION
SELECT 'users.php' UNION SELECT 'profile.php' UNION SELECT 'company_profile.php' UNION
SELECT 'order_details.php' UNION SELECT 'receipt.php'
) p
WHERE role = 'super_admin';
-- For Branch Managers: most things, but not company-wide settings or user management (usually)
-- Following the existing role logic from header.php
INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, p.page, 1, 1, 1, 1
FROM users
CROSS JOIN (
SELECT 'admin.php' as page UNION SELECT 'pos.php' UNION SELECT 'orders.php' UNION SELECT 'lab.php' UNION
SELECT 'customers.php' UNION SELECT 'reports.php' UNION SELECT 'items.php' UNION SELECT 'branches.php' UNION
SELECT 'profile.php' UNION SELECT 'order_details.php' UNION SELECT 'receipt.php'
) p
WHERE role = 'branch_manager';
-- For Cashiers:
INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, p.page, 1, 1, 1, 1
FROM users
CROSS JOIN (
SELECT 'admin.php' as page UNION SELECT 'pos.php' UNION SELECT 'orders.php' UNION SELECT 'lab.php' UNION
SELECT 'customers.php' UNION SELECT 'profile.php' UNION SELECT 'order_details.php' UNION SELECT 'receipt.php'
) p
WHERE role = 'cashier';
-- For Limited Viewers:
INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, p.page, 1, 0, 0, 0
FROM users
CROSS JOIN (
SELECT 'admin.php' as page UNION SELECT 'profile.php'
) p
WHERE role = 'limited_viewer';

View File

@ -0,0 +1,14 @@
-- Create user_branches table
CREATE TABLE IF NOT EXISTS user_branches (
user_id INT NOT NULL,
branch_id INT NOT NULL,
PRIMARY KEY (user_id, branch_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Migrate existing data from users table
INSERT INTO user_branches (user_id, branch_id)
SELECT id, branch_id FROM users WHERE branch_id IS NOT NULL;
-- Keep branch_id in users for now as fallback or default, but we should eventually rely on user_branches

View File

@ -12,6 +12,32 @@ $current_user_id = $_SESSION['user_id'] ?? null;
$current_branch = $_SESSION['branch_id'] ?? null;
$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]);
$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"], '?');
header("Location: $url");
exit;
}
}
// 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();
}
// Fetch Global Company Info
$stmt = db()->query("SELECT * FROM companies LIMIT 1");
$company_info = $stmt->fetch();
@ -24,6 +50,25 @@ if ($current_user_id) {
$current_user_data = $stmt->fetch();
}
// Global Permission Check for current page
$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;
} elseif ($current_page !== 'profile.php' && has_permission('view', 'profile.php')) {
header('Location: profile.php');
exit;
} elseif ($current_page !== 'admin.php' && $current_page !== 'profile.php') {
die('Access Denied. You do not have permission to view this page.');
}
}
}
?>
<!doctype html>
<html lang="<?= $lang ?>" dir="<?= is_rtl() ? 'rtl' : 'ltr' ?>">
@ -112,55 +157,79 @@ if ($current_user_id) {
<?php endif; ?>
</div>
<ul class="nav flex-column px-3">
<?php if (has_permission('view', 'admin.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'admin.php' ? 'active' : '' ?>" href="admin.php">
<i class="bi bi-speedometer2"></i>
<?= __('dashboard') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'pos.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'pos.php' ? 'active' : '' ?>" href="pos.php">
<i class="bi bi-pc-display-horizontal"></i>
<?= __('pos') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'orders.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'orders.php' ? 'active' : '' ?>" href="orders.php">
<i class="bi bi-cart"></i>
<?= __('orders') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'lab.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'lab.php' ? 'active' : '' ?>" href="lab.php">
<i class="bi bi-flask"></i>
<?= __('lab') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'customers.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'customers.php' ? 'active' : '' ?>" href="customers.php">
<i class="bi bi-people"></i>
<?= __('customers') ?>
</a>
</li>
<?php if ($current_role !== 'cashier'): ?>
<?php endif; ?>
<?php if (has_permission('view', 'reports.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'reports.php' ? 'active' : '' ?>" href="reports.php">
<i class="bi bi-file-earmark-bar-graph"></i>
<?= __('reports') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'items.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'items.php' ? 'active' : '' ?>" href="items.php">
<i class="bi bi-tags"></i>
<?= __('items') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'branches.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'branches.php' ? 'active' : '' ?>" href="branches.php">
<i class="bi bi-shop"></i>
<?= __('branches') ?>
</a>
</li>
<?php endif; ?>
<?php if (has_permission('view', 'users.php')): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'users.php' ? 'active' : '' ?>" href="users.php">
<i class="bi bi-person-badge"></i>
@ -171,14 +240,16 @@ if ($current_user_id) {
<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 ($current_role === 'super_admin'): ?>
<?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">
<i class="bi bi-building"></i>
@ -211,11 +282,28 @@ if ($current_user_id) {
<h1 class="h2"><?= __($title ?? 'dashboard') ?></h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="d-flex align-items-center">
<span class="badge bg-primary px-3 py-2 me-2"><?= $_SESSION['branch_name'] ?? '' ?></span>
<?php if (count($user_branches) > 1): ?>
<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">
<i class="bi bi-shop me-1"></i> <?= $_SESSION['branch_name'] ?? '' ?>
</button>
<ul class="dropdown-menu shadow border-0 mt-2">
<?php foreach ($user_branches as $ub): ?>
<li>
<a class="dropdown-item <?= $ub['id'] == $_SESSION['branch_id'] ? 'active' : '' ?>" href="?switch_branch=<?= $ub['id'] ?>">
<?= $lang === 'ar' ? ($ub['name_ar'] ?: $ub['name_en']) : $ub['name_en'] ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php else: ?>
<span class="badge bg-primary px-3 py-2 me-2"><?= $_SESSION['branch_name'] ?? '' ?></span>
<?php endif; ?>
<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;">
<?php if ($current_user_data['profile_picture']): ?>
<?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>
@ -223,12 +311,14 @@ if ($current_user_id) {
<span class="small fw-bold"><?= $_SESSION['full_name'] ?? '' ?></span>
</div>
<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>
<a class="dropdown-item py-2" href="profile.php">
<i class="bi bi-person-circle"></i> <?= __('user_profile') ?>
</a>
</li>
<?php if ($current_role === 'super_admin'): ?>
<?php endif; ?>
<?php if (has_permission('view', 'company_profile.php')): ?>
<li>
<a class="dropdown-item py-2" href="company_profile.php">
<i class="bi bi-building"></i> <?= __('company_profile') ?>

View File

@ -185,7 +185,15 @@ $translations = [
'top_items' => 'Top Items',
'top_services' => 'Top Services',
'cashier' => 'Cashier',
'revenue_by_payment_method' => 'Revenue by Payment Method'
'revenue_by_payment_method' => 'Revenue by Payment Method',
'print_report' => 'Print Report',
'revenue_last_7_days' => 'Revenue (Last 7 Days)',
'date_range' => 'Date Range',
'edit_user' => 'Edit User',
'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'
],
'ar' => [
'dashboard' => 'لوحة القيادة',
@ -364,7 +372,15 @@ $translations = [
'top_items' => 'أفضل الأصناف',
'top_services' => 'أفضل الخدمات',
'cashier' => 'الكاشير',
'revenue_by_payment_method' => 'الإيرادات حسب طريقة الدفع'
'revenue_by_payment_method' => 'الإيرادات حسب طريقة الدفع',
'print_report' => 'طباعة التقرير',
'revenue_last_7_days' => 'الإيرادات (آخر 7 أيام)',
'date_range' => 'نطاق التاريخ',
'edit_user' => 'تعديل مستخدم',
'confirm_delete_user' => 'هل أنت متأكد من حذف هذا المستخدم؟',
'leave_blank_to_keep_current' => 'اتركه فارغاً للاحتفاظ بكلمة المرور الحالية',
'permissions' => 'الصلاحيات',
'user_permissions' => 'صلاحيات المستخدم'
]
];

154
items.php
View File

@ -13,8 +13,8 @@ if (!isset($_SESSION['user_id'])) {
exit;
}
$current_role = $_SESSION['role'] ?? 'cashier';
if ($current_role === 'cashier') {
// Initial view check
if (!has_permission('view')) {
if (isset($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'error' => 'Forbidden']);
@ -69,7 +69,25 @@ function deleteOldImage($imageUrl) {
// Handle Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if ($_POST['action'] === 'add_item') {
$action = $_POST['action'];
// Permission mapping for actions
$required_permission = 'view';
if (strpos($action, 'add_') === 0) $required_permission = 'add';
if (strpos($action, 'edit_') === 0 || strpos($action, 'update_') === 0) $required_permission = 'edit';
if (strpos($action, 'delete_') === 0) $required_permission = 'delete';
if (!has_permission($required_permission)) {
if (isset($_POST['ajax'])) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'error' => 'Forbidden']);
exit;
}
header('Location: items.php?error=no_permission');
exit;
}
if ($action === 'add_item') {
$name_en = $_POST['name_en'];
$name_ar = $_POST['name_ar'];
$category_id = $_POST['category_id'] ?: null;
@ -81,7 +99,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$stmt->execute([$category_id, $name_en, $name_ar, $image_url, $vat_percent]);
header('Location: items.php?success=item_added');
exit;
} elseif ($_POST['action'] === 'edit_item') {
} elseif ($action === 'edit_item') {
$id = $_POST['id'];
$name_en = $_POST['name_en'];
$name_ar = $_POST['name_ar'];
@ -92,7 +110,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$stmt = db()->prepare("SELECT image_url FROM items WHERE id = ?");
$stmt->execute([$id]);
$currentItem = $stmt->fetch();
$image_url = $currentItem['image_url'];
$image_url = $currentItem['image_url'] ?? null;
$new_image = handleImageUpload($_FILES['image'] ?? null);
if ($new_image) {
@ -104,27 +122,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$stmt->execute([$category_id, $name_en, $name_ar, $image_url, $vat_percent, $id]);
header('Location: items.php?success=item_updated');
exit;
} elseif ($_POST['action'] === 'delete_item') {
} elseif ($action === 'delete_item') {
$id = $_POST['id'];
// Delete image file if exists
$stmt = db()->prepare("SELECT image_url FROM items WHERE id = ?");
$stmt->execute([$id]);
$item = $stmt->fetch();
deleteOldImage($item['image_url']);
deleteOldImage($item['image_url'] ?? null);
$stmt = db()->prepare("DELETE FROM items WHERE id = ?");
$stmt->execute([$id]);
header('Location: items.php?success=item_deleted');
exit;
} elseif ($_POST['action'] === 'add_category') {
} elseif ($action === 'add_category') {
$name_en = $_POST['cat_name_en'];
$name_ar = $_POST['cat_name_ar'];
$stmt = db()->prepare("INSERT INTO categories (name_en, name_ar) VALUES (?, ?)");
$stmt->execute([$name_en, $name_ar]);
header('Location: items.php?success=category_added');
exit;
} elseif ($_POST['action'] === 'edit_category') {
} elseif ($action === 'edit_category') {
$id = $_POST['id'];
$name_en = $_POST['cat_name_en'];
$name_ar = $_POST['cat_name_ar'];
@ -132,20 +150,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$stmt->execute([$name_en, $name_ar, $id]);
header('Location: items.php?success=category_updated');
exit;
} elseif ($_POST['action'] === 'delete_category') {
} elseif ($action === 'delete_category') {
$id = $_POST['id'];
$stmt = db()->prepare("DELETE FROM categories WHERE id = ?");
$stmt->execute([$id]);
header('Location: items.php?success=category_deleted');
exit;
} elseif ($_POST['action'] === 'add_service') {
} elseif ($action === 'add_service') {
$name_en = $_POST['svc_name_en'];
$name_ar = $_POST['svc_name_ar'];
$stmt = db()->prepare("INSERT INTO services (name_en, name_ar) VALUES (?, ?)");
$stmt->execute([$name_en, $name_ar]);
header('Location: items.php?success=service_added');
exit;
} elseif ($_POST['action'] === 'edit_service') {
} elseif ($action === 'edit_service') {
$id = $_POST['id'];
$name_en = $_POST['svc_name_en'];
$name_ar = $_POST['svc_name_ar'];
@ -153,21 +171,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$stmt->execute([$name_en, $name_ar, $id]);
header('Location: items.php?success=service_updated');
exit;
} elseif ($_POST['action'] === 'delete_service') {
} elseif ($action === 'delete_service') {
$id = $_POST['id'];
$stmt = db()->prepare("DELETE FROM services WHERE id = ?");
$stmt->execute([$id]);
header('Location: items.php?success=service_deleted');
exit;
} elseif ($_POST['action'] === 'update_price') {
} elseif ($action === 'update_price') {
$item_id = $_POST['item_id'];
$service_id = $_POST['service_id'];
$price = $_POST['price'];
$stmt = db()->prepare("INSERT INTO prices (branch_id, item_id, service_id, price)
VALUES (?, ?, ?, ?)
$stmt = db()->prepare("INSERT INTO prices (item_id, service_id, price)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE price = ?");
$stmt->execute([$branch_id, $item_id, $service_id, $price, $price]);
$stmt->execute([$item_id, $service_id, $price, $price]);
if (isset($_POST['ajax'])) {
header('Content-Type: application/json');
@ -191,8 +209,8 @@ $items = db()->query("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar
ORDER BY i.name_en ASC")->fetchAll();
$services = db()->query("SELECT * FROM services ORDER BY name_en ASC")->fetchAll();
$stmt = db()->prepare("SELECT * FROM prices WHERE branch_id = ?");
$stmt->execute([$branch_id]);
$stmt = db()->prepare("SELECT * FROM prices ");
$stmt->execute();
$prices_raw = $stmt->fetchAll();
$prices = [];
foreach ($prices_raw as $p) {
@ -209,9 +227,11 @@ foreach ($prices_raw as $p) {
<button class="btn btn-outline-primary px-4 fw-bold me-2" style="border-radius: 12px;" data-bs-toggle="modal" data-bs-target="#serviceModal">
<i class="bi bi-gear me-2"></i><?= __('services') ?? 'Services' ?>
</button>
<button class="btn btn-primary px-4 fw-bold" style="border-radius: 12px;" onclick="openItemModal()">
<?php if (has_permission('add')):
?><button class="btn btn-primary px-4 fw-bold" style="border-radius: 12px;" onclick="openItemModal()">
<i class="bi bi-plus-lg me-2"></i><?= __('add_item') ?>
</button>
<?php endif; ?>
</div>
</div>
@ -222,6 +242,13 @@ foreach ($prices_raw as $p) {
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if(isset($_GET['error'])): ?>
<div class="alert alert-danger alert-dismissible fade show rounded-4 shadow-sm mb-4 border-0" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
<?= __($_GET['error']) ?? 'Permission Denied' ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="card p-0 border-0 shadow-sm mb-4" style="border-radius: 20px; overflow: hidden;">
<div class="table-responsive">
@ -236,23 +263,23 @@ foreach ($prices_raw as $p) {
</tr>
</thead>
<tbody>
<?php foreach($items as $item): ?>
<tr>
<?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>
@ -269,12 +296,16 @@ foreach ($prices_raw as $p) {
</button>
</td>
<td class="pe-4 text-end">
<button class="btn btn-sm btn-light p-2 me-1" style="border-radius: 10px;" onclick="openItemModal(<?= htmlspecialchars(json_encode($item)) ?>)">
<?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>
<button class="btn btn-sm btn-light p-2" style="border-radius: 10px;" onclick="confirmDelete('item', <?= $item['id'] ?>)">
<?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 endif; ?>
</td>
</tr>
<?php endforeach; ?>
@ -313,8 +344,8 @@ foreach ($prices_raw as $p) {
<label class="form-label fw-bold small"><?= __('category') ?></label>
<select name="category_id" id="itemCategory" class="form-select" style="border-radius: 12px;" required>
<option value=""><?= __('select_category') ?></option>
<?php foreach($categories as $cat): ?>
<option value="<?= $cat['id'] ?>"><?= $lang === 'ar' ? ($cat['name_ar'] ?: $cat['name_en']) : $cat['name_en'] ?></option>
<?php foreach($categories as $cat):
?><option value="<?= $cat['id'] ?>"><?= $lang === 'ar' ? ($cat['name_ar'] ?: $cat['name_en']) : $cat['name_en'] ?></option>
<?php endforeach; ?>
</select>
</div>
@ -369,7 +400,8 @@ foreach ($prices_raw as $p) {
<div class="row">
<div class="col-md-5">
<div class="bg-light p-4" style="border-radius: 20px;">
<h6 class="fw-bold mb-3" id="catFormLabel"><?= __('add_new_category') ?></h6>
<?php if (has_permission('add')):
?><h6 class="fw-bold mb-3" id="catFormLabel"><?= __('add_new_category') ?></h6>
<form id="categoryForm" method="POST">
<input type="hidden" name="action" id="catAction" value="add_category">
<input type="hidden" name="id" id="catId">
@ -388,6 +420,9 @@ foreach ($prices_raw as $p) {
<?= __('cancel') ?>
</button>
</form>
<?php else:
?><p class="text-muted small">You don't have permission to add categories.</p>
<?php endif; ?>
</div>
</div>
<div class="col-md-7">
@ -400,19 +435,23 @@ foreach ($prices_raw as $p) {
</tr>
</thead>
<tbody>
<?php foreach($categories as $cat): ?>
<tr>
<?php foreach($categories as $cat):
?><tr>
<td>
<strong><?= $cat['name_en'] ?></strong><br>
<small class="text-muted"><?= $cat['name_ar'] ?></small>
</td>
<td class="text-end">
<button class="btn btn-sm btn-link text-primary" onclick="editCategory(<?= htmlspecialchars(json_encode($cat)) ?>)">
<?php if (has_permission('edit')):
?><button class="btn btn-sm btn-link text-primary" onclick="editCategory(<?= htmlspecialchars(json_encode($cat)) ?>)">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-link text-danger" onclick="confirmDelete('category', <?= $cat['id'] ?>)">
<?php endif; ?>
<?php if (has_permission('delete')):
?><button class="btn btn-sm btn-link text-danger" onclick="confirmDelete('category', <?= $cat['id'] ?>)">
<i class="bi bi-trash"></i>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
@ -438,7 +477,8 @@ foreach ($prices_raw as $p) {
<div class="row">
<div class="col-md-5">
<div class="bg-light p-4" style="border-radius: 20px;">
<h6 class="fw-bold mb-3" id="svcFormLabel"><?= __('add_new_service') ?></h6>
<?php if (has_permission('add')):
?><h6 class="fw-bold mb-3" id="svcFormLabel"><?= __('add_new_service') ?></h6>
<form id="serviceForm" method="POST">
<input type="hidden" name="action" id="svcAction" value="add_service">
<input type="hidden" name="id" id="svcId">
@ -457,6 +497,9 @@ foreach ($prices_raw as $p) {
<?= __('cancel') ?>
</button>
</form>
<?php else:
?><p class="text-muted small">You don't have permission to add services.</p>
<?php endif; ?>
</div>
</div>
<div class="col-md-7">
@ -469,19 +512,23 @@ foreach ($prices_raw as $p) {
</tr>
</thead>
<tbody>
<?php foreach($services as $svc): ?>
<tr>
<?php foreach($services as $svc):
?><tr>
<td>
<strong><?= $svc['name_en'] ?></strong><br>
<small class="text-muted"><?= $svc['name_ar'] ?></small>
</td>
<td class="text-end">
<button class="btn btn-sm btn-link text-primary" onclick="editService(<?= htmlspecialchars(json_encode($svc)) ?>)">
<?php if (has_permission('edit')):
?><button class="btn btn-sm btn-link text-primary" onclick="editService(<?= htmlspecialchars(json_encode($svc)) ?>)">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-link text-danger" onclick="confirmDelete('service', <?= $svc['id'] ?>)">
<?php endif; ?>
<?php if (has_permission('delete')):
?><button class="btn btn-sm btn-link text-danger" onclick="confirmDelete('service', <?= $svc['id'] ?>)">
<i class="bi bi-trash"></i>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
@ -496,8 +543,8 @@ foreach ($prices_raw as $p) {
</div>
<!-- Pricing Modals -->
<?php foreach($items as $item): ?>
<div class="modal fade pricing-modal" id="pricingModal<?= $item['id'] ?>" tabindex="-1">
<?php foreach($items as $item):
?><div class="modal fade pricing-modal" id="pricingModal<?= $item['id'] ?>" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content" style="border-radius: 25px;">
<div class="modal-header border-0 p-4">
@ -517,8 +564,8 @@ foreach ($prices_raw as $p) {
</tr>
</thead>
<tbody>
<?php foreach($services as $service): ?>
<tr>
<?php foreach($services as $service):
?><tr>
<td class="fw-bold">
<?= $lang === 'ar' ? $service['name_ar'] : $service['name_en'] ?>
</td>
@ -528,7 +575,8 @@ foreach ($prices_raw as $p) {
value="<?= number_format($prices[$item['id']][$service['id']] ?? 0, decimals(), '.', '') ?>"
data-item-id="<?= $item['id'] ?>"
data-service-id="<?= $service['id'] ?>"
style="border-radius: 8px;">
style="border-radius: 8px;"
<?= !has_permission('edit') ? 'readonly disabled' : '' ?>>
<span class="input-group-text bg-transparent border-0 d-none success-indicator" style="position: absolute; right: -25px; top: 5px;">
<i class="bi bi-check-lg text-success"></i>
</span>
@ -540,7 +588,8 @@ foreach ($prices_raw as $p) {
</table>
</div>
<div class="alert alert-info py-2 small mt-3 mb-0" style="border-radius: 10px;">
<i class="bi bi-info-circle me-2"></i> Prices are saved automatically as you type.
<i class="bi bi-info-circle me-2"></i>
<?= has_permission('edit') ? 'Prices are saved automatically as you type.' : 'You can view prices but cannot edit them.' ?>
</div>
</div>
</div>
@ -667,6 +716,7 @@ function confirmDelete(type, id) {
document.querySelectorAll('.price-input').forEach(input => {
let timeout = null;
input.addEventListener('input', function() {
if (this.readOnly || this.disabled) return;
clearTimeout(timeout);
const self = this;
timeout = setTimeout(async () => {
@ -685,6 +735,8 @@ document.querySelectorAll('.price-input').forEach(input => {
const data = await response.json();
if (data.success) {
showIndicator(self);
} else {
alert(data.error || 'Failed to update price');
}
} catch (error) {
console.error('Error updating price:', error);
@ -717,4 +769,4 @@ document.getElementById('itemImageFile').addEventListener('change', function(e)
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

40
lab.php
View File

@ -2,7 +2,7 @@
$title = 'lab';
require_once __DIR__ . '/includes/header.php';
$current_role = $_SESSION['role'] ?? 'cashier';
// View check is handled by header.php global check.
// Filters
$status_filter = $_GET['status'] ?? '';
@ -102,7 +102,7 @@ $branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll()
<div class="col-md-5 d-flex gap-2">
<input type="date" name="from_date" class="form-control" value="<?= $from_date ?>" style="border-radius: 12px;" title="<?= __('from_date') ?>">
<input type="date" name="to_date" class="form-control" value="<?= $to_date ?>" style="border-radius: 12px;" title="<?= __('to_date') ?>">
<button type="submit" class="btn btn-light" style="border-radius: 12px;"><i class="bi bi-search"></i></button>
<button type="submit" class="btn btn-light shadow-sm" style="border-radius: 12px;"><i class="bi bi-search"></i></button>
</div>
</form>
@ -137,12 +137,14 @@ $branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll()
<td class="small text-muted"><?= date('d/m/Y H:i', strtotime($order['created_at'])) ?></td>
<td class="text-end pe-4">
<div class="d-flex gap-1 justify-content-end">
<button class="btn btn-sm btn-light border-0 p-2 text-primary view-items-btn" data-id="<?= $order['id'] ?>" title="<?= __('view_items') ?>" style="border-radius: 8px;">
<button class="btn btn-sm btn-light border-0 p-2 text-primary view-items-btn shadow-sm" data-id="<?= $order['id'] ?>" title="<?= __('view_items') ?>" style="border-radius: 8px;">
<i class="bi bi-eye-fill"></i>
</button>
<button class="btn btn-sm btn-light border-0 p-2 text-info status-change-btn" data-id="<?= $order['id'] ?>" data-status="<?= $order['status'] ?>" title="<?= __('update_status') ?>" style="border-radius: 8px;">
<?php if (has_permission('edit')): ?>
<button class="btn btn-sm btn-light border-0 p-2 text-info status-change-btn shadow-sm" data-id="<?= $order['id'] ?>" data-status="<?= $order['status'] ?>" title="<?= __('update_status') ?>" style="border-radius: 8px;">
<i class="bi bi-arrow-repeat"></i>
</button>
<?php endif; ?>
</div>
</td>
</tr>
@ -150,7 +152,7 @@ $branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll()
<?php if (empty($orders)): ?>
<tr>
<td colspan="7" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-1 d-block mb-3 opacity-25"></i>
<i class="bi bi-flask fs-1 d-block mb-3 opacity-25"></i>
<?= __('no_orders_found') ?? 'No orders found' ?>
</td>
</tr>
@ -164,15 +166,15 @@ $branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll()
<nav class="mt-4">
<ul class="pagination justify-content-center">
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="?<?= http_build_query(array_merge($_GET, ['page' => $page - 1])) ?>" style="border-radius: 8px 0 0 8px;"><?= __('previous') ?></a>
<a class="page-link shadow-sm" href="?<?= http_build_query(array_merge($_GET, ['page' => $page - 1])) ?>" style="border-radius: 8px 0 0 8px;"><?= __('previous') ?></a>
</li>
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?= $page == $i ? 'active' : '' ?>">
<a class="page-link" href="?<?= http_build_query(array_merge($_GET, ['page' => $i])) ?>"><?= $i ?></a>
<a class="page-link shadow-sm" href="?<?= http_build_query(array_merge($_GET, ['page' => $i])) ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<li class="page-item <?= $page >= $total_pages ? 'disabled' : '' ?>">
<a class="page-link" href="?<?= http_build_query(array_merge($_GET, ['page' => $page + 1])) ?>" style="border-radius: 0 8px 8px 0;"><?= __('next') ?></a>
<a class="page-link shadow-sm" href="?<?= http_build_query(array_merge($_GET, ['page' => $page + 1])) ?>" style="border-radius: 0 8px 8px 0;"><?= __('next') ?></a>
</li>
</ul>
</nav>
@ -195,13 +197,14 @@ $branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll()
</div>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-light rounded-3" data-bs-dismiss="modal"><?= __('close') ?></button>
<button type="button" class="btn btn-light rounded-3 shadow-sm" data-bs-dismiss="modal"><?= __('close') ?></button>
</div>
</div>
</div>
</div>
<!-- Status Update Modal -->
<?php if (has_permission('edit')): ?>
<div class="modal fade" id="statusModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 rounded-4 shadow-lg">
@ -213,29 +216,31 @@ $branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll()
<input type="hidden" id="modalOrderId">
<div class="row g-2">
<div class="col-6">
<button class="btn btn-outline-secondary w-100 py-3 rounded-4 status-opt" data-status="received"><?= __('received') ?></button>
<button class="btn btn-outline-secondary w-100 py-3 rounded-4 status-opt shadow-sm" data-status="received"><?= __('received') ?></button>
</div>
<div class="col-6">
<button class="btn btn-outline-primary w-100 py-3 rounded-4 status-opt" data-status="processing"><?= __('processing') ?></button>
<button class="btn btn-outline-primary w-100 py-3 rounded-4 status-opt shadow-sm" data-status="processing"><?= __('processing') ?></button>
</div>
<div class="col-6">
<button class="btn btn-outline-success w-100 py-3 rounded-4 status-opt" data-status="ready"><?= __('ready') ?></button>
<button class="btn btn-outline-success w-100 py-3 rounded-4 status-opt shadow-sm" data-status="ready"><?= __('ready') ?></button>
</div>
<div class="col-6">
<button class="btn btn-outline-dark w-100 py-3 rounded-4 status-opt" data-status="delivered"><?= __('delivered') ?></button>
<button class="btn btn-outline-dark w-100 py-3 rounded-4 status-opt shadow-sm" data-status="delivered"><?= __('delivered') ?></button>
</div>
<div class="col-12 mt-2">
<button class="btn btn-outline-danger w-100 py-3 rounded-4 status-opt" data-status="cancelled"><?= __('cancelled') ?></button>
<button class="btn btn-outline-danger w-100 py-3 rounded-4 status-opt shadow-sm" data-status="cancelled"><?= __('cancelled') ?></button>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
const statusModal = new bootstrap.Modal(document.getElementById('statusModal'));
const statusModalEl = document.getElementById('statusModal');
const statusModal = statusModalEl ? new bootstrap.Modal(statusModalEl) : null;
const itemsModal = new bootstrap.Modal(document.getElementById('itemsModal'));
// View Items Logic
@ -261,7 +266,7 @@ document.addEventListener('DOMContentLoaded', () => {
html += `
<li class="list-group-item d-flex justify-content-between align-items-center py-3 border-0 border-bottom">
<div class="fw-bold text-dark">${itemName}</div>
<span class="badge bg-primary rounded-pill px-3 fs-6">${item.quantity}</span>
<span class="badge bg-primary rounded-pill px-3 fs-6 shadow-sm">${item.quantity}</span>
</li>
`;
});
@ -279,6 +284,7 @@ document.addEventListener('DOMContentLoaded', () => {
// Status Change Logic
document.querySelectorAll('.status-change-btn').forEach(btn => {
btn.onclick = () => {
if (!statusModal) return;
const id = btn.dataset.id;
const currentStatus = btn.dataset.status;
document.getElementById('modalOrderId').value = id;
@ -337,4 +343,4 @@ function getStatusColor($status) {
][$status] ?? 'info';
}
require_once __DIR__ . '/includes/footer.php';
?>
?>

View File

@ -51,6 +51,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
// NOW Include header
$title = 'order_details';
require_once __DIR__ . '/includes/header.php';
if ($current_role === 'limited_viewer') { header('Location: admin.php'); exit; }
$stmt = db()->prepare("SELECT oi.*, i.name_en as item_en, i.name_ar as item_ar,
s.name_en as service_en, s.name_ar as service_ar

View File

@ -83,9 +83,11 @@ if ($current_role === 'super_admin') {
<div class="card p-4 border-0 shadow-sm" style="border-radius: 20px;">
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
<h5 class="fw-bold mb-3 mb-md-0"><?= __('orders_list') ?? 'Orders List' ?></h5>
<?php if (has_permission('add')): ?>
<a href="pos.php" class="btn btn-primary px-4" style="border-radius: 12px;">
<i class="bi bi-plus-lg me-1"></i> <?= __('new_order') ?>
</a>
<?php endif; ?>
</div>
<!-- Filters -->
@ -168,15 +170,19 @@ if ($current_role === 'super_admin') {
<a href="order_details.php?id=<?= $order['id'] ?>" class="btn btn-sm btn-light border-0 p-2 text-primary" title="<?= __('view') ?? 'View' ?>" style="border-radius: 8px;">
<i class="bi bi-eye-fill"></i>
</a>
<?php if (has_permission('edit')): ?>
<button class="btn btn-sm btn-light border-0 p-2 text-info status-change-btn" data-id="<?= $order['id'] ?>" data-status="<?= $order['status'] ?>" title="<?= __('update_status') ?? 'Update Status' ?>" style="border-radius: 8px;">
<i class="bi bi-arrow-repeat"></i>
</button>
<a href="pos.php?edit_order_id=<?= $order['id'] ?>" class="btn btn-sm btn-light border-0 p-2 text-warning" title="<?= __('edit') ?>" style="border-radius: 8px;">
<i class="bi bi-pencil-fill"></i>
</a>
<?php endif; ?>
<?php if (has_permission('delete')): ?>
<button class="btn btn-sm btn-light border-0 p-2 text-danger delete-order-btn" data-id="<?= $order['id'] ?>" title="<?= __('delete') ?>" style="border-radius: 8px;">
<i class="bi bi-trash-fill"></i>
</button>
<?php endif; ?>
</div>
</td>
</tr>
@ -345,4 +351,4 @@ function getPaymentStatusColor($status) {
][$status] ?? 'info';
}
require_once __DIR__ . '/includes/footer.php';
?>
?>

31
pos.php
View File

@ -2,10 +2,17 @@
$title = 'pos';
require_once __DIR__ . '/includes/header.php';
// Global check in header.php handles 'view' permission.
// We should check 'edit' permission if we are editing an order.
$edit_order_id = $_GET['edit_order_id'] ?? null;
if ($edit_order_id && !has_permission('edit')) {
header('Location: pos.php?error=no_edit_permission');
exit;
}
$branch_id = $_SESSION['branch_id'] ?? 1;
// Handle Edit Order
$edit_order_id = $_GET['edit_order_id'] ?? null;
$edit_order = null;
$edit_items = [];
if ($edit_order_id) {
@ -50,11 +57,9 @@ $items_raw = $stmt->fetchAll();
// Get all services
$services_raw = db()->query("SELECT * FROM services ORDER BY name_en ASC")->fetchAll();
// Get all prices for this branch
$stmt = db()->prepare("SELECT p.item_id, p.service_id, p.price
FROM prices p
WHERE p.branch_id = ?");
$stmt->execute([$branch_id]);
// Get all prices
$stmt = db()->prepare("SELECT p.item_id, p.service_id, p.price FROM prices p");
$stmt->execute();
$prices_raw = $stmt->fetchAll();
$prices = [];
@ -91,8 +96,8 @@ foreach ($items_raw as $i) {
}
// Get all customers for this branch
$stmt = db()->prepare("SELECT * FROM customers WHERE branch_id = ? ORDER BY name_en ASC");
$stmt->execute([$branch_id]);
$stmt = db()->prepare("SELECT * FROM customers ORDER BY name_en ASC");
$stmt->execute();
$customers = $stmt->fetchAll();
$pageTitle = $edit_order ? ($lang == 'en' ? 'Edit Order #' . $edit_order['order_number'] : 'تعديل طلب رقم ' . $edit_order['order_number']) : ($lang == 'en' ? 'Point of Sale' : 'نقطة البيع');
@ -199,9 +204,11 @@ $pageTitle = $edit_order ? ($lang == 'en' ? 'Edit Order #' . $edit_order['order_
<input type="hidden" id="customerId" value="<?= $edit_order ? $edit_order['customer_id'] : '' ?>">
</div>
<?php if (has_permission('add', 'customers.php')): ?>
<button class="btn btn-primary rounded-3" data-bs-toggle="modal" data-bs-target="#addCustomerModal">
<i class="bi bi-plus-lg"></i>
</button>
<?php endif; ?>
</div>
</div>
@ -228,9 +235,15 @@ $pageTitle = $edit_order ? ($lang == 'en' ? 'Edit Order #' . $edit_order['order_
<h5 id="cartTotal" class="mb-0 fw-bold text-primary">0.000 <?= currency() ?></h5>
</div>
</div>
<?php if (($edit_order && has_permission('edit')) || (!$edit_order && has_permission('add'))): ?>
<button class="btn btn-primary w-100 py-3 rounded-4 fw-bold shadow-sm" onclick="checkout()">
<i class="bi bi-credit-card me-2"></i> <?= $edit_order ? ($lang == 'en' ? 'Update Order' : 'تحديث الطلب') : ($lang == 'en' ? 'Complete Order' : 'إتمام الطلب') ?>
</button>
<?php else: ?>
<button class="btn btn-secondary w-100 py-3 rounded-4 fw-bold shadow-sm" disabled>
<i class="bi bi-lock me-2"></i> <?= $lang == 'en' ? 'No Permission' : 'لا تملك صلاحية' ?>
</button>
<?php endif; ?>
</div>
</div>
</div>
@ -722,4 +735,4 @@ async function saveCustomer() {
.customer-result-item:hover { background-color: #f8f9fa; }
</style>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -6,7 +6,10 @@ $success = '';
$error = '';
// Get user data
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
$stmt = db()->prepare("SELECT u.*, b.name_en as branch_name_en, b.name_ar as branch_name_ar
FROM users u
LEFT JOIN branches b ON u.branch_id = b.id
WHERE u.id = ?");
$stmt->execute([$current_user_id]);
$user = $stmt->fetch();
@ -23,6 +26,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$ext = pathinfo($_FILES['profile_picture']['name'], PATHINFO_EXTENSION);
$filename = 'user_' . $current_user_id . '_' . time() . '.' . $ext;
$target = 'assets/images/users/' . $filename;
if (!is_dir('assets/images/users/')) {
mkdir('assets/images/users/', 0775, true);
}
if (move_uploaded_file($_FILES['profile_picture']['tmp_name'], $target)) {
$profile_picture = $target;
}
@ -46,7 +52,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$success = __('success_update');
// Refresh data
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
$stmt = db()->prepare("SELECT u.*, b.name_en as branch_name_en, b.name_ar as branch_name_ar
FROM users u
LEFT JOIN branches b ON u.branch_id = b.id
WHERE u.id = ?");
$stmt->execute([$current_user_id]);
$user = $stmt->fetch();
@ -62,71 +71,172 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="h3 mb-0"><?= __('user_profile') ?></h2>
<button type="button" class="btn btn-primary px-4 fw-bold shadow-sm" style="border-radius: 12px;" data-bs-toggle="modal" data-bs-target="#editProfileModal">
<i class="bi bi-pencil-square me-2"></i> <?= __('edit_profile') ?? 'Edit Profile' ?>
</button>
</div>
<?php if ($success): ?>
<div class="alert alert-success"><?= $success ?></div>
<div class="alert alert-success alert-dismissible fade show rounded-4 border-0 shadow-sm" role="alert">
<i class="bi bi-check-circle-fill me-2"></i> <?= $success ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger"><?= $error ?></div>
<div class="alert alert-danger alert-dismissible fade show rounded-4 border-0 shadow-sm" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i> <?= $error ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<div class="card p-4">
<form action="profile.php" method="POST" enctype="multipart/form-data">
<div class="row align-items-center mb-4">
<div class="col-md-3 text-center border-end">
<div class="mb-3 position-relative d-inline-block">
<?php if ($user['profile_picture']): ?>
<img src="<?= $user['profile_picture'] ?>" alt="Profile Picture" class="rounded-circle border p-1" style="width: 150px; height: 150px; object-fit: cover;">
<?php else: ?>
<div class="rounded-circle bg-light d-flex align-items-center justify-content-center mx-auto text-primary border" style="width: 150px; height: 150px; font-size: 3rem;">
<i class="bi bi-person"></i>
</div>
<?php endif; ?>
</div>
<div class="mt-2">
<label class="btn btn-outline-primary btn-sm px-3">
<i class="bi bi-camera me-1"></i> <?= __('profile_picture') ?>
<input type="file" name="profile_picture" class="d-none" accept="image/*">
</label>
</div>
<div class="row">
<div class="col-lg-4 mb-4">
<div class="card text-center p-4 border-0 shadow-sm h-100" style="border-radius: 20px;">
<div class="mb-4">
<?php if ($user['profile_picture']): ?>
<img src="<?= $user['profile_picture'] ?>?v=<?= time() ?>" alt="Profile Picture" class="rounded-circle border p-2 shadow-sm" style="width: 180px; height: 180px; object-fit: cover; background-color: #fff;">
<?php else: ?>
<div class="rounded-circle bg-light d-flex align-items-center justify-content-center mx-auto text-primary border shadow-sm" style="width: 180px; height: 180px; font-size: 4.5rem; background-color: #fff !important;">
<i class="bi bi-person"></i>
</div>
<?php endif; ?>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label"><?= __('full_name_en') ?></label>
<input type="text" name="full_name_en" class="form-control" value="<?= htmlspecialchars($user['full_name_en'] ?? '') ?>" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label"><?= __('full_name_ar') ?></label>
<input type="text" name="full_name_ar" class="form-control" value="<?= htmlspecialchars($user['full_name_ar'] ?? '') ?>" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label"><?= __('email') ?></label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($user['email'] ?? '') ?>" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label"><?= __('username') ?></label>
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($user['username']) ?>" disabled>
</div>
<hr class="my-3">
<h5 class="mb-3"><?= __('change_password') ?></h5>
<div class="col-md-6 mb-3">
<label class="form-label"><?= __('password') ?></label>
<input type="password" name="password" class="form-control" autocomplete="new-password">
</div>
<div class="col-md-6 mb-3">
<label class="form-label"><?= __('confirm_password') ?></label>
<input type="password" name="confirm_password" class="form-control" autocomplete="new-password">
</div>
<h4 class="fw-bold mb-1"><?= htmlspecialchars($lang === 'ar' ? ($user['full_name_ar'] ?: $user['full_name_en']) : $user['full_name_en']) ?></h4>
<p class="text-muted mb-3"><?= htmlspecialchars($user['email']) ?></p>
<div class="d-flex justify-content-center gap-2">
<span class="badge bg-soft-primary text-primary px-3 py-2" style="border-radius: 8px;"><?= ucfirst(str_replace('_', ' ', $user['role'])) ?></span>
<?php if ($user['branch_name_en']): ?>
<span class="badge bg-soft-info text-info px-3 py-2" style="border-radius: 8px;">
<i class="bi bi-shop me-1"></i> <?= $lang === 'ar' ? ($user['branch_name_ar'] ?: $user['branch_name_en']) : $user['branch_name_en'] ?>
</span>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-lg-8">
<div class="card p-4 border-0 shadow-sm h-100" style="border-radius: 20px;">
<h5 class="fw-bold mb-4 pb-2 border-bottom"><?= __('personal_information') ?? 'Personal Information' ?></h5>
<div class="row g-4">
<div class="col-md-6">
<label class="text-muted small d-block mb-1"><?= __('full_name_en') ?></label>
<div class="fw-bold fs-5"><?= htmlspecialchars($user['full_name_en'] ?? '-') ?></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block mb-1"><?= __('full_name_ar') ?></label>
<div class="fw-bold fs-5"><?= htmlspecialchars($user['full_name_ar'] ?? '-') ?></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block mb-1"><?= __('email') ?></label>
<div class="fw-bold fs-5"><?= htmlspecialchars($user['email'] ?? '-') ?></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block mb-1"><?= __('username') ?></label>
<div class="fw-bold fs-5"><?= htmlspecialchars($user['username'] ?? '-') ?></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block mb-1"><?= __('role') ?></label>
<div class="fw-bold fs-5"><?= ucfirst(str_replace('_', ' ', $user['role'])) ?></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block mb-1"><?= __('branch') ?></label>
<div class="fw-bold fs-5"><?= $lang === 'ar' ? ($user['branch_name_ar'] ?: $user['branch_name_en']) : ($user['branch_name_en'] ?: '-') ?></div>
</div>
</div>
</div>
<div class="text-end border-top pt-3">
<button type="submit" class="btn btn-primary px-5 fw-bold"><?= __('update_profile') ?></button>
</div>
</form>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<!-- Edit Profile Modal -->
<div class="modal fade" id="editProfileModal" tabindex="-1" aria-labelledby="editProfileModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content border-0 shadow" style="border-radius: 25px;">
<form action="profile.php" method="POST" enctype="multipart/form-data">
<div class="modal-header border-0 p-4 pb-0">
<h5 class="modal-title fw-bold" id="editProfileModalLabel"><?= __('update_profile') ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<div class="row g-4">
<div class="col-md-12 text-center mb-2">
<div class="position-relative d-inline-block">
<?php if ($user['profile_picture']): ?>
<img id="modalPreview" src="<?= $user['profile_picture'] ?>?v=<?= time() ?>" alt="Profile Picture" class="rounded-circle border p-1 shadow-sm" style="width: 120px; height: 120px; object-fit: cover; background-color: #fff;">
<?php else: ?>
<div id="modalPlaceholder" class="rounded-circle bg-light d-flex align-items-center justify-content-center mx-auto text-primary border shadow-sm" style="width: 120px; height: 120px; font-size: 3rem; background-color: #fff !important;">
<i class="bi bi-person"></i>
</div>
<img id="modalPreview" src="" alt="Profile Picture" class="rounded-circle border p-1 shadow-sm d-none" style="width: 120px; height: 120px; object-fit: cover; background-color: #fff;">
<?php endif; ?>
<label class="btn btn-sm btn-primary rounded-circle position-absolute bottom-0 end-0 p-2 shadow" style="cursor: pointer;">
<i class="bi bi-camera-fill"></i>
<input type="file" name="profile_picture" class="d-none" accept="image/*" onchange="previewImage(this)">
</label>
</div>
<div class="mt-2 small text-muted"><?= __('profile_picture') ?></div>
</div>
<div class="col-md-6">
<label class="form-label fw-bold small"><?= __('full_name_en') ?></label>
<input type="text" name="full_name_en" class="form-control" value="<?= htmlspecialchars($user['full_name_en'] ?? '') ?>" required style="border-radius: 12px;">
</div>
<div class="col-md-6">
<label class="form-label fw-bold small"><?= __('full_name_ar') ?></label>
<input type="text" name="full_name_ar" class="form-control" value="<?= htmlspecialchars($user['full_name_ar'] ?? '') ?>" required style="border-radius: 12px;">
</div>
<div class="col-md-6">
<label class="form-label fw-bold small"><?= __('email') ?></label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($user['email'] ?? '') ?>" required style="border-radius: 12px;">
</div>
<div class="col-md-6">
<label class="form-label fw-bold small"><?= __('username') ?></label>
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($user['username']) ?>" disabled style="border-radius: 12px;">
</div>
<div class="col-md-12">
<hr class="my-2">
<h6 class="fw-bold mb-3"><?= __('change_password') ?></h6>
</div>
<div class="col-md-6">
<label class="form-label fw-bold small"><?= __('password') ?></label>
<input type="password" name="password" class="form-control" autocomplete="new-password" style="border-radius: 12px;" placeholder="<?= __('leave_blank_to_keep') ?? 'Leave blank to keep current' ?>">
</div>
<div class="col-md-6">
<label class="form-label fw-bold small"><?= __('confirm_password') ?></label>
<input type="password" name="confirm_password" class="form-control" autocomplete="new-password" style="border-radius: 12px;" placeholder="<?= __('confirm_new_password') ?? 'Confirm new password' ?>">
</div>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0">
<button type="button" class="btn btn-light px-4 fw-bold" data-bs-dismiss="modal" style="border-radius: 12px;"><?= __('cancel') ?></button>
<button type="submit" class="btn btn-primary px-5 fw-bold shadow-sm" style="border-radius: 12px;"><?= __('save_changes') ?></button>
</div>
</form>
</div>
</div>
</div>
<style>
.bg-soft-primary { background-color: rgba(13, 110, 253, 0.1); }
.bg-soft-info { background-color: rgba(13, 202, 240, 0.1); }
</style>
<script>
function previewImage(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
const preview = document.getElementById('modalPreview');
const placeholder = document.getElementById('modalPlaceholder');
preview.src = e.target.result;
preview.classList.remove('d-none');
if (placeholder) placeholder.classList.add('d-none');
}
reader.readAsDataURL(input.files[0]);
}
}
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,5 +1,6 @@
<?php
require_once __DIR__ . '/db/config.php';
if ($_SESSION['role'] === 'limited_viewer') { die('Access Denied'); }
require_once __DIR__ . '/includes/lang.php';
$order_id = $_GET['id'] ?? null;

View File

@ -2,11 +2,8 @@
$title = 'reports';
require_once __DIR__ . '/includes/header.php';
if ($current_role === 'cashier') {
echo '<div class="alert alert-danger">' . __('Access Denied') . '</div>';
require_once __DIR__ . '/includes/footer.php';
exit;
}
// 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.
$from_date = $_GET['from_date'] ?? date('Y-m-01');
$to_date = $_GET['to_date'] ?? date('Y-m-t');
@ -103,22 +100,64 @@ $top_services = $stmt->fetchAll();
$branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll();
$users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchAll();
// Filter display names
$branch_name = __('all_branches');
if ($branch_filter !== 'all') {
foreach($branches as $b) {
if ($b['id'] == $branch_filter) {
$branch_name = is_arabic() ? $b['name_ar'] : $b['name_en'];
break;
}
}
}
$user_name = __('all');
if ($user_filter !== 'all') {
foreach($users as $u) {
if ($u['id'] == $user_filter) {
$user_name = is_arabic() ? $u['full_name_ar'] : $u['full_name_en'];
break;
}
}
}
?>
<div class="card p-4 mb-4">
<div class="d-flex justify-content-between align-items-center mb-4 no-print">
<h4 class="fw-bold mb-0"><?= __('reports') ?></h4>
<button onclick="window.print()" class="btn btn-primary shadow-sm" style="border-radius: 12px;">
<i class="bi bi-printer me-2"></i> <?= __('print_report') ?>
</button>
</div>
<!-- Print Header -->
<div class="d-none d-print-block mb-4 border-bottom pb-3">
<div class="row">
<div class="col-6">
<h2 class="fw-bold mb-0"><?= __('reports') ?></h2>
<p class="text-muted mb-0"><?= date('Y-m-d H:i') ?></p>
</div>
<div class="col-6 text-end">
<p class="mb-0"><strong><?= __('date_range') ?>:</strong> <?= $from_date ?> - <?= $to_date ?></p>
<p class="mb-0"><strong><?= __('outlet') ?>:</strong> <?= $branch_name ?></p>
<p class="mb-0"><strong><?= __('cashier') ?>:</strong> <?= $user_name ?></p>
</div>
</div>
</div>
<div class="card p-4 mb-4 no-print border-0 shadow-sm" style="border-radius: 20px;">
<form method="GET" class="row g-3 align-items-end">
<div class="col-md-2">
<label class="form-label small fw-bold"><?= __('from_date') ?></label>
<input type="date" name="from_date" class="form-control" value="<?= $from_date ?>">
<input type="date" name="from_date" class="form-control" value="<?= $from_date ?>" style="border-radius: 12px;">
</div>
<div class="col-md-2">
<label class="form-label small fw-bold"><?= __('to_date') ?></label>
<input type="date" name="to_date" class="form-control" value="<?= $to_date ?>">
<input type="date" name="to_date" class="form-control" value="<?= $to_date ?>" style="border-radius: 12px;">
</div>
<?php if ($current_role === 'super_admin'): ?>
<?php if ($_SESSION['role'] === 'super_admin'): ?>
<div class="col-md-3">
<label class="form-label small fw-bold"><?= __('outlet') ?></label>
<select name="branch_id" class="form-select">
<select name="branch_id" class="form-select" style="border-radius: 12px;">
<option value="all"><?= __('all_branches') ?></option>
<?php foreach($branches as $b): ?>
<option value="<?= $b['id'] ?>" <?= $branch_filter == $b['id'] ? 'selected' : '' ?>>
@ -130,7 +169,7 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
<?php endif; ?>
<div class="col-md-3">
<label class="form-label small fw-bold"><?= __('cashier') ?></label>
<select name="user_id" class="form-select">
<select name="user_id" class="form-select" style="border-radius: 12px;">
<option value="all"><?= __('all') ?></option>
<?php foreach($users as $u): ?>
<option value="<?= $u['id'] ?>" <?= $user_filter == $u['id'] ? 'selected' : '' ?>>
@ -140,7 +179,7 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">
<button type="submit" class="btn btn-primary w-100 shadow-sm fw-bold" style="border-radius: 12px;">
<i class="bi bi-filter me-2"></i> <?= __('generate_report') ?>
</button>
</div>
@ -149,19 +188,19 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
<div class="row g-4 mb-4">
<div class="col-md-4">
<div class="card p-4 text-center border-start border-primary border-5">
<div class="card p-4 text-center border-0 shadow-sm border-start border-primary border-5" style="border-radius: 15px;">
<div class="text-muted small mb-1"><?= __('total_revenue') ?></div>
<div class="fs-3 fw-bold text-primary"><?= format_amount($summary['total_revenue'] ?? 0) ?></div>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 text-center border-start border-success border-5">
<div class="card p-4 text-center border-0 shadow-sm border-start border-success border-5" style="border-radius: 15px;">
<div class="text-muted small mb-1"><?= __('total_orders') ?></div>
<div class="fs-3 fw-bold text-success"><?= $summary['total_orders'] ?? 0 ?></div>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 text-center border-start border-info border-5">
<div class="card p-4 text-center border-0 shadow-sm border-start border-info border-5" style="border-radius: 15px;">
<div class="text-muted small mb-1"><?= __('average_order_value') ?></div>
<div class="fs-3 fw-bold text-info"><?= format_amount($summary['avg_order_value'] ?? 0) ?></div>
</div>
@ -170,40 +209,48 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
<div class="row g-4 mb-4">
<div class="col-md-6">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm" style="border-radius: 20px;">
<h6 class="fw-bold mb-4"><?= __('revenue_by_outlet') ?></h6>
<canvas id="revenueByBranchChart"></canvas>
<div style="height: 300px; position: relative;">
<canvas id="revenueByBranchChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm" style="border-radius: 20px;">
<h6 class="fw-bold mb-4"><?= __('orders_by_status') ?></h6>
<canvas id="ordersByStatusChart"></canvas>
<div style="height: 300px; position: relative;">
<canvas id="ordersByStatusChart"></canvas>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-md-6">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm" style="border-radius: 20px;">
<h6 class="fw-bold mb-4"><?= __('revenue_by_cashier') ?></h6>
<canvas id="revenueByCashierChart"></canvas>
<div style="height: 300px; position: relative;">
<canvas id="revenueByCashierChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('revenue_by_payment_method') ?? 'Revenue by Payment Method' ?></h6>
<canvas id="revenueByMethodChart"></canvas>
<div class="card p-4 h-100 border-0 shadow-sm" style="border-radius: 20px;">
<h6 class="fw-bold mb-4"><?= __('revenue_by_payment_method') ?></h6>
<div style="height: 300px; position: relative;">
<canvas id="revenueByMethodChart"></canvas>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-md-6">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm" style="border-radius: 20px;">
<h6 class="fw-bold mb-4"><?= __('top_items') ?></h6>
<div class="table-responsive">
<table class="table table-sm">
<table class="table table-sm align-middle">
<thead>
<tr>
<th><?= __('item') ?></th>
@ -225,10 +272,10 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
</div>
</div>
<div class="col-md-6">
<div class="card p-4 h-100">
<div class="card p-4 h-100 border-0 shadow-sm" style="border-radius: 20px;">
<h6 class="fw-bold mb-4"><?= __('top_services') ?></h6>
<div class="table-responsive">
<table class="table table-sm">
<table class="table table-sm align-middle">
<thead>
<tr>
<th><?= __('service') ?></th>
@ -253,13 +300,18 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Set default chart options
Chart.defaults.font.size = 12;
Chart.defaults.responsive = true;
Chart.defaults.maintainAspectRatio = false;
// Revenue by Branch Chart
new Chart(document.getElementById('revenueByBranchChart'), {
type: 'bar',
data: {
labels: <?= json_encode(array_map(function($b) use ($lang) {
return $lang === 'ar' ? $b['name_ar'] : $b['name_en'];
}, $revenue_by_branch)) ?>,
}, $revenue_by_branch), JSON_UNESCAPED_UNICODE) ?>,
datasets: [{
label: '<?= __('revenue') ?>',
data: <?= json_encode(array_column($revenue_by_branch, 'revenue')) ?>,
@ -269,7 +321,6 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
}]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: true }
}
@ -282,7 +333,7 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
data: {
labels: <?= json_encode(array_map(function($s) {
return __($s['status']);
}, $orders_by_status)) ?>,
}, $orders_by_status), JSON_UNESCAPED_UNICODE) ?>,
datasets: [{
data: <?= json_encode(array_column($orders_by_status, 'count')) ?>,
backgroundColor: [
@ -291,7 +342,6 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom' }
}
@ -304,7 +354,7 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
data: {
labels: <?= json_encode(array_map(function($u) use ($lang) {
return $lang === 'ar' ? $u['full_name_ar'] : $u['full_name_en'];
}, $revenue_by_user)) ?>,
}, $revenue_by_user), JSON_UNESCAPED_UNICODE) ?>,
datasets: [{
label: '<?= __('revenue') ?>',
data: <?= json_encode(array_column($revenue_by_user, 'revenue')) ?>,
@ -314,7 +364,6 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
}]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: true }
}
@ -327,7 +376,7 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
data: {
labels: <?= json_encode(array_map(function($m) {
return __($m['payment_method']);
}, $revenue_by_method)) ?>,
}, $revenue_by_method), JSON_UNESCAPED_UNICODE) ?>,
datasets: [{
data: <?= json_encode(array_column($revenue_by_method, 'revenue')) ?>,
backgroundColor: [
@ -336,7 +385,6 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom' }
}
@ -344,4 +392,4 @@ $users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchA
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

356
users.php
View File

@ -8,40 +8,166 @@ if (!isset($_SESSION['user_id'])) {
exit;
}
$current_role = $_SESSION['role'] ?? 'cashier';
if ($current_role === 'cashier') {
// Permission check for users.php
if (!has_permission('view', 'users.php')) {
header('Location: admin.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if ($_POST['action'] === 'add_user') {
if ($_POST['action'] === 'add_user' && has_permission('add', 'users.php')) {
$username = $_POST['username'];
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
$full_name_en = $_POST['full_name_en'];
$full_name_ar = $_POST['full_name_ar'];
$role = $_POST['role'];
$branch_id = $_POST['branch_id'] ?: null;
$branch_ids = $_POST['branch_ids'] ?? [];
$company_id = 1; // Default for now
$primary_branch_id = !empty($branch_ids) ? $branch_ids[0] : null;
$stmt = db()->prepare("INSERT INTO users (username, password_hash, full_name_en, full_name_ar, role, branch_id, company_id) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$username, $password, $full_name_en, $full_name_ar, $role, $branch_id, $company_id]);
$stmt->execute([$username, $password, $full_name_en, $full_name_ar, $role, $primary_branch_id, $company_id]);
$new_user_id = db()->lastInsertId();
// Sync branches
foreach ($branch_ids as $bid) {
$stmt = db()->prepare("INSERT INTO user_branches (user_id, branch_id) VALUES (?, ?)");
$stmt->execute([$new_user_id, $bid]);
}
// Seed default permissions based on role for the new user
$default_pages = [
'super_admin' => ['admin.php', 'pos.php', 'orders.php', 'lab.php', 'customers.php', 'reports.php', 'items.php', 'branches.php', 'users.php', 'profile.php', 'company_profile.php', 'order_details.php', 'receipt.php'],
'branch_manager' => ['admin.php', 'pos.php', 'orders.php', 'lab.php', 'customers.php', 'reports.php', 'items.php', 'branches.php', 'profile.php', 'order_details.php', 'receipt.php'],
'cashier' => ['admin.php', 'pos.php', 'orders.php', 'lab.php', 'customers.php', 'profile.php', 'order_details.php', 'receipt.php'],
'limited_viewer' => ['admin.php', 'profile.php']
];
$pages = $default_pages[$role] ?? [];
foreach ($pages as $p) {
$can_add_edit_del = ($role !== 'limited_viewer' ? 1 : 0);
$stmt = db()->prepare("INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete) VALUES (?, ?, 1, ?, ?, ?)");
$stmt->execute([$new_user_id, $p, $can_add_edit_del, $can_add_edit_del, $can_add_edit_del]);
}
header('Location: users.php');
exit;
}
if ($_POST['action'] === 'edit_user' && has_permission('edit', 'users.php')) {
$id = $_POST['id'];
$username = $_POST['username'];
$full_name_en = $_POST['full_name_en'];
$full_name_ar = $_POST['full_name_ar'];
$role = $_POST['role'];
$branch_ids = $_POST['branch_ids'] ?? [];
$primary_branch_id = !empty($branch_ids) ? $branch_ids[0] : null;
if (!empty($_POST['password'])) {
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
$stmt = db()->prepare("UPDATE users SET username = ?, password_hash = ?, full_name_en = ?, full_name_ar = ?, role = ?, branch_id = ? WHERE id = ?");
$stmt->execute([$username, $password, $full_name_en, $full_name_ar, $role, $primary_branch_id, $id]);
} else {
$stmt = db()->prepare("UPDATE users SET username = ?, full_name_en = ?, full_name_ar = ?, role = ?, branch_id = ? WHERE id = ?");
$stmt->execute([$username, $full_name_en, $full_name_ar, $role, $primary_branch_id, $id]);
}
// Sync branches
$stmt = db()->prepare("DELETE FROM user_branches WHERE user_id = ?");
$stmt->execute([$id]);
foreach ($branch_ids as $bid) {
$stmt = db()->prepare("INSERT INTO user_branches (user_id, branch_id) VALUES (?, ?)");
$stmt->execute([$id, $bid]);
}
header('Location: users.php?msg=user_updated');
exit;
}
if ($_POST['action'] === 'delete_user' && has_permission('delete', 'users.php')) {
$id = $_POST['id'];
// Prevent self-deletion
if ($id == $_SESSION['user_id']) {
header('Location: users.php?error=cannot_delete_self');
exit;
}
$stmt = db()->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$id]);
header('Location: users.php?msg=user_deleted');
exit;
}
if ($_POST['action'] === 'update_permissions' && has_permission('edit', 'users.php')) {
$target_user_id = $_POST['user_id'];
$permissions = $_POST['perms'] ?? []; // format: [page][action] = 1
// Clear existing permissions for this user
$stmt = db()->prepare("DELETE FROM user_permissions WHERE user_id = ?");
$stmt->execute([$target_user_id]);
$all_pages = ['admin.php', 'pos.php', 'orders.php', 'lab.php', 'customers.php', 'reports.php', 'items.php', 'branches.php', 'users.php', 'profile.php', 'company_profile.php', 'order_details.php', 'receipt.php'];
foreach ($all_pages as $page) {
$can_view = isset($permissions[$page]['view']) ? 1 : 0;
$can_add = isset($permissions[$page]['add']) ? 1 : 0;
$can_edit = isset($permissions[$page]['edit']) ? 1 : 0;
$can_delete = isset($permissions[$page]['delete']) ? 1 : 0;
if ($can_view || $can_add || $can_edit || $can_delete) {
$stmt = db()->prepare("INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$target_user_id, $page, $can_view, $can_add, $can_edit, $can_delete]);
}
}
header('Location: users.php?msg=permissions_updated');
exit;
}
}
// NOW Include header
$title = 'users';
require_once __DIR__ . '/includes/header.php';
$users = db()->query("SELECT u.*, b.name_en as branch_name_en FROM users u LEFT JOIN branches b ON u.branch_id = b.id")->fetchAll();
$users = db()->query("SELECT u.* FROM users u")->fetchAll();
$branches = db()->query("SELECT * FROM branches")->fetchAll();
// Fetch branches for all users in one go for efficiency
$user_branches_mapping = [];
$ub_stmt = db()->query("SELECT ub.user_id, b.name_en, b.name_ar FROM user_branches ub JOIN branches b ON ub.branch_id = b.id");
while ($row = $ub_stmt->fetch()) {
$user_branches_mapping[$row['user_id']][] = $lang === 'ar' ? $row['name_ar'] : $row['name_en'];
}
// Also need branch IDs for edit modal
$user_branch_ids_mapping = [];
$ub_id_stmt = db()->query("SELECT user_id, branch_id FROM user_branches");
while ($row = $ub_id_stmt->fetch()) {
$user_branch_ids_mapping[$row['user_id']][] = (int)$row['branch_id'];
}
$all_pages = [
'admin.php' => 'dashboard',
'pos.php' => 'pos',
'orders.php' => 'orders',
'lab.php' => 'lab',
'customers.php' => 'customers',
'reports.php' => 'reports',
'items.php' => 'items',
'branches.php' => 'branches',
'users.php' => 'users',
'profile.php' => 'user_profile',
'company_profile.php' => 'company_profile',
'order_details.php' => 'order_details',
'receipt.php' => 'receipt'
];
?>
<div class="row">
<?php if (has_permission('add', 'users.php')): ?>
<div class="col-md-4">
<div class="card p-4 border-0 shadow-sm" style="border-radius: 20px;">
<div class="card p-4 border-0 shadow-sm mb-4" style="border-radius: 20px;">
<h5 class="fw-bold mb-4"><?= __('add_new_user') ?? 'Add New User' ?></h5>
<form method="POST">
<input type="hidden" name="action" value="add_user">
@ -67,22 +193,30 @@ $branches = db()->query("SELECT * FROM branches")->fetchAll();
<option value="super_admin">Super Admin</option>
<option value="branch_manager">Branch Manager</option>
<option value="cashier" selected>Cashier</option>
<option value="limited_viewer">Limited Viewer</option>
</select>
</div>
<div class="mb-3">
<label class="form-label small fw-bold"><?= __('branch') ?></label>
<select name="branch_id" class="form-select" style="border-radius: 12px;">
<option value="">None (Super Admin)</option>
<label class="form-label small fw-bold"><?= __('branches') ?? 'Branches' ?></label>
<div class="p-3 border rounded" style="max-height: 150px; overflow-y: auto; border-radius: 12px !important;">
<?php foreach($branches as $b): ?>
<option value="<?= $b['id'] ?>"><?= $lang === 'ar' ? $b['name_ar'] : $b['name_en'] ?></option>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="branch_ids[]" value="<?= $b['id'] ?>" id="add_b_<?= $b['id'] ?>">
<label class="form-check-label" for="add_b_<?= $b['id'] ?>">
<?= $lang === 'ar' ? $b['name_ar'] : $b['name_en'] ?>
</label>
</div>
<?php endforeach; ?>
</select>
</div>
</div>
<button type="submit" class="btn btn-primary w-100 py-3 fw-bold" style="border-radius: 15px;"><?= __('add_user') ?></button>
</form>
</div>
</div>
<div class="col-md-8">
<?php else: ?>
<div class="col-md-12">
<?php endif; ?>
<div class="card p-0 border-0 shadow-sm" style="border-radius: 20px; overflow: hidden;">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
@ -92,7 +226,8 @@ $branches = db()->query("SELECT * FROM branches")->fetchAll();
<th class="py-3"><?= __('username') ?></th>
<th class="py-3"><?= __('name') ?></th>
<th class="py-3"><?= __('role') ?></th>
<th class="pe-4 py-3"><?= __('branch') ?></th>
<th class="py-3"><?= __('branches') ?? 'Branches' ?></th>
<th class="pe-4 py-3 text-end"><?= __('actions') ?></th>
</tr>
</thead>
<tbody>
@ -106,7 +241,32 @@ $branches = db()->query("SELECT * FROM branches")->fetchAll();
<?= $u['role'] ?>
</span>
</td>
<td class="pe-4"><?= $u['branch_name_en'] ?: '-' ?></td>
<td>
<?php if (isset($user_branches_mapping[$u['id']])): ?>
<?php foreach ($user_branches_mapping[$u['id']] as $bname): ?>
<span class="badge bg-light text-dark border me-1"><?= $bname ?></span>
<?php endforeach; ?>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="pe-4 text-end">
<div class="btn-group">
<?php if (has_permission('edit', 'users.php')): ?>
<button class="btn btn-sm btn-outline-primary px-3" onclick="openPermissions(<?= $u['id'] ?>, '<?= htmlspecialchars($u['username']) ?>')" style="border-radius: 8px 0 0 8px;" title="<?= __('permissions') ?>">
<i class="bi bi-shield-lock"></i>
</button>
<button class="btn btn-sm btn-outline-info px-3" onclick="editUser(<?= htmlspecialchars(json_encode($u)) ?>, <?= htmlspecialchars(json_encode($user_branch_ids_mapping[$u['id']] ?? [])) ?>)" style="border-radius: 0;" title="<?= __('edit') ?>">
<i class="bi bi-pencil-square"></i>
</button>
<?php endif; ?>
<?php if (has_permission('delete', 'users.php')): ?>
<button class="btn btn-sm btn-outline-danger px-3" onclick="deleteUser(<?= $u['id'] ?>)" style="border-radius: 0 8px 8px 0;" title="<?= __('delete') ?>" <?= $u['id'] == $_SESSION['user_id'] ? 'disabled' : '' ?>>
<i class="bi bi-trash"></i>
</button>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
@ -116,6 +276,174 @@ $branches = db()->query("SELECT * FROM branches")->fetchAll();
</div>
</div>
<!-- Edit User Modal -->
<div class="modal fade" id="editUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content" style="border-radius: 20px;">
<div class="modal-header border-0 p-4 pb-0">
<h5 class="modal-title fw-bold"><?= __('edit_user') ?? 'Edit User' ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST">
<input type="hidden" name="action" value="edit_user">
<input type="hidden" name="id" id="editUserId">
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label small fw-bold"><?= __('username') ?></label>
<input type="text" name="username" id="editUsername" class="form-control" required style="border-radius: 12px;">
</div>
<div class="mb-3">
<label class="form-label small fw-bold"><?= __('password') ?> (<?= __('leave_blank_to_keep_current') ?? 'leave blank to keep current' ?>)</label>
<input type="password" name="password" class="form-control" style="border-radius: 12px;">
</div>
<div class="mb-3">
<label class="form-label small fw-bold"><?= __('full_name_en') ?></label>
<input type="text" name="full_name_en" id="editFullNameEn" class="form-control" required style="border-radius: 12px;">
</div>
<div class="mb-3">
<label class="form-label small fw-bold"><?= __('full_name_ar') ?></label>
<input type="text" name="full_name_ar" id="editFullNameAr" class="form-control" style="border-radius: 12px;">
</div>
<div class="mb-3">
<label class="form-label small fw-bold"><?= __('role') ?></label>
<select name="role" id="editRole" class="form-select" style="border-radius: 12px;">
<option value="super_admin">Super Admin</option>
<option value="branch_manager">Branch Manager</option>
<option value="cashier">Cashier</option>
<option value="limited_viewer">Limited Viewer</option>
</select>
</div>
<div class="mb-3">
<label class="form-label small fw-bold"><?= __('branches') ?? 'Branches' ?></label>
<div class="p-3 border rounded" style="max-height: 150px; overflow-y: auto; border-radius: 12px !important;">
<?php foreach($branches as $b): ?>
<div class="form-check">
<input class="form-check-input branch-checkbox" type="checkbox" name="branch_ids[]" value="<?= $b['id'] ?>" id="edit_b_<?= $b['id'] ?>">
<label class="form-check-label" for="edit_b_<?= $b['id'] ?>">
<?= $lang === 'ar' ? $b['name_ar'] : $b['name_en'] ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0">
<button type="button" class="btn btn-light px-4" data-bs-dismiss="modal" style="border-radius: 12px;"><?= __('cancel') ?></button>
<button type="submit" class="btn btn-primary px-4" style="border-radius: 12px;"><?= __('save_changes') ?></button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Confirm Modal -->
<form id="deleteForm" method="POST" style="display:none;">
<input type="hidden" name="action" value="delete_user">
<input type="hidden" name="id" id="deleteUserId">
</form>
<!-- Permissions Modal -->
<div class="modal fade" id="permissionsModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content" style="border-radius: 20px;">
<div class="modal-header border-0 p-4 pb-0">
<h5 class="modal-title fw-bold"><i class="bi bi-shield-lock me-2"></i> <?= __('user_permissions') ?? 'User Permissions' ?>: <span id="permsUsername"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST">
<input type="hidden" name="action" value="update_permissions">
<input type="hidden" name="user_id" id="permsUserId">
<div class="modal-body p-4">
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead>
<tr>
<th><?= __('page') ?></th>
<th class="text-center"><?= __('view') ?></th>
<th class="text-center"><?= __('add') ?></th>
<th class="text-center"><?= __('edit') ?></th>
<th class="text-center"><?= __('delete') ?></th>
</tr>
</thead>
<tbody id="permsTableBody">
<?php foreach($all_pages as $file => $label): ?>
<tr>
<td class="fw-bold small"><?= __($label) ?></td>
<td class="text-center">
<input class="form-check-input" type="checkbox" name="perms[<?= $file ?>][view]" id="v_<?= str_replace('.', '_', $file) ?>">
</td>
<td class="text-center">
<input class="form-check-input" type="checkbox" name="perms[<?= $file ?>][add]" id="a_<?= str_replace('.', '_', $file) ?>">
</td>
<td class="text-center">
<input class="form-check-input" type="checkbox" name="perms[<?= $file ?>][edit]" id="e_<?= str_replace('.', '_', $file) ?>">
</td>
<td class="text-center">
<input class="form-check-input" type="checkbox" name="perms[<?= $file ?>][delete]" id="d_<?= str_replace('.', '_', $file) ?>">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0">
<button type="button" class="btn btn-light px-4" data-bs-dismiss="modal" style="border-radius: 12px;"><?= __('cancel') ?></button>
<button type="submit" class="btn btn-primary px-4" style="border-radius: 12px;"><?= __('save_changes') ?></button>
</div>
</form>
</div>
</div>
</div>
<script>
function openPermissions(userId, username) {
document.getElementById('permsUserId').value = userId;
document.getElementById('permsUsername').innerText = username;
// Reset all checkboxes
document.querySelectorAll('#permsTableBody input[type="checkbox"]').forEach(cb => cb.checked = false);
// Fetch current permissions via AJAX
fetch('api/get_user_permissions.php?user_id=' + userId)
.then(response => response.json())
.then(data => {
data.forEach(p => {
const pageId = p.page.replace('.', '_');
if (p.can_view == 1) document.getElementById('v_' + pageId).checked = true;
if (p.can_add == 1) document.getElementById('a_' + pageId).checked = true;
if (p.can_edit == 1) document.getElementById('e_' + pageId).checked = true;
if (p.can_delete == 1) document.getElementById('d_' + pageId).checked = true;
});
const modal = new bootstrap.Modal(document.getElementById('permissionsModal'));
modal.show();
});
}
function editUser(user, branchIds) {
document.getElementById('editUserId').value = user.id;
document.getElementById('editUsername').value = user.username;
document.getElementById('editFullNameEn').value = user.full_name_en;
document.getElementById('editFullNameAr').value = user.full_name_ar || '';
document.getElementById('editRole').value = user.role;
// Reset and set checkboxes
document.querySelectorAll('.branch-checkbox').forEach(cb => {
cb.checked = branchIds.includes(parseInt(cb.value));
});
const modal = new bootstrap.Modal(document.getElementById('editUserModal'));
modal.show();
}
function deleteUser(id) {
if (confirm('<?= __('confirm_delete_user') ?? 'Are you sure you want to delete this user?' ?>')) {
document.getElementById('deleteUserId').value = id;
document.getElementById('deleteForm').submit();
}
}
</script>
<style>
.bg-soft-info { background-color: rgba(13, 202, 240, 0.1); }
</style>