update users who has multi branch
This commit is contained in:
parent
2a38c2ffdb
commit
78166012df
387
admin.php
387
admin.php
@ -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';
|
||||
?>
|
||||
@ -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([
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
21
api/get_user_permissions.php
Normal file
21
api/get_user_permissions.php
Normal 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);
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
74
branches.php
74
branches.php
@ -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'; ?>
|
||||
|
||||
@ -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') {
|
||||
|
||||
@ -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'; ?>
|
||||
@ -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]);
|
||||
}
|
||||
22
db/migrations/10_make_shared_entities.sql
Normal file
22
db/migrations/10_make_shared_entities.sql
Normal 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;
|
||||
1
db/migrations/11_add_limited_viewer_role.sql
Normal file
1
db/migrations/11_add_limited_viewer_role.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE users MODIFY COLUMN role ENUM('super_admin', 'branch_manager', 'cashier', 'limited_viewer') DEFAULT 'cashier';
|
||||
55
db/migrations/12_page_based_permissions.sql
Normal file
55
db/migrations/12_page_based_permissions.sql
Normal 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';
|
||||
14
db/migrations/13_multiple_user_branches.sql
Normal file
14
db/migrations/13_multiple_user_branches.sql
Normal 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
|
||||
@ -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') ?>
|
||||
|
||||
@ -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
154
items.php
@ -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
40
lab.php
@ -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';
|
||||
?>
|
||||
?>
|
||||
@ -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
|
||||
|
||||
@ -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
31
pos.php
@ -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'; ?>
|
||||
|
||||
222
profile.php
222
profile.php
@ -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'; ?>
|
||||
@ -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;
|
||||
|
||||
122
reports.php
122
reports.php
@ -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
356
users.php
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user