431 lines
22 KiB
PHP
431 lines
22 KiB
PHP
<?php
|
|
require_once __DIR__ . '/includes/app.php';
|
|
$user = require_permission('users', 'show');
|
|
$pageTitle = tr('المستخدمون والأدوار', 'Users & Roles');
|
|
$activeNav = 'users';
|
|
|
|
$flash = pull_flash();
|
|
|
|
// Handle POST actions (Create, Update, Delete)
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$action = $_POST['action'] ?? '';
|
|
|
|
if ($action === 'add') {
|
|
if (!has_permission('users', 'add')) { set_flash('error', tr('ليس لديك صلاحية', 'No permission')); redirect_to('users.php'); }
|
|
$username = trim($_POST['username'] ?? '');
|
|
$password = $_POST['password'] ?? '';
|
|
$name_ar = trim($_POST['name_ar'] ?? '');
|
|
$name_en = trim($_POST['name_en'] ?? '');
|
|
$role = $_POST['role'] ?? 'cashier';
|
|
$branch_code = $_POST['branch_code'] ?? 'muscat';
|
|
|
|
if ($username && $password && $name_ar) {
|
|
$hash = password_hash($password, PASSWORD_DEFAULT);
|
|
try {
|
|
$stmt = db()->prepare("INSERT INTO users (username, password, role, branch_code, allowed_branches, name_ar, name_en, permissions) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
$perms = isset($_POST["permissions"]) ? json_encode($_POST["permissions"]) : "{}";
|
|
$allowed_branches = isset($_POST["allowed_branches"]) && is_array($_POST["allowed_branches"]) ? implode(",", $_POST["allowed_branches"]) : null;
|
|
$stmt->execute([$username, $hash, $role, $branch_code, $allowed_branches, $name_ar, $name_en, $perms]);
|
|
set_flash('success', tr('تمت إضافة المستخدم بنجاح.', 'User added successfully.'));
|
|
} catch (PDOException $e) {
|
|
set_flash('error', tr('حدث خطأ، قد يكون اسم المستخدم موجوداً مسبقاً.', 'Error occurred, username might already exist.'));
|
|
}
|
|
} else {
|
|
set_flash('error', tr('يرجى تعبئة الحقول المطلوبة.', 'Please fill required fields.'));
|
|
}
|
|
redirect_to('users.php');
|
|
}
|
|
|
|
if ($action === 'edit') {
|
|
if (!has_permission('users', 'edit')) { set_flash('error', tr('ليس لديك صلاحية', 'No permission')); redirect_to('users.php'); }
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
$username = trim($_POST['username'] ?? '');
|
|
$name_ar = trim($_POST['name_ar'] ?? '');
|
|
$name_en = trim($_POST['name_en'] ?? '');
|
|
$role = $_POST['role'] ?? 'cashier';
|
|
$branch_code = $_POST['branch_code'] ?? 'muscat';
|
|
$password = $_POST['password'] ?? '';
|
|
|
|
if ($id && $username && $name_ar) {
|
|
try {
|
|
if ($password) {
|
|
$hash = password_hash($password, PASSWORD_DEFAULT);
|
|
$stmt = db()->prepare("UPDATE users SET username=?, password=?, role=?, branch_code=?, allowed_branches=?, name_ar=?, name_en=?, permissions=? WHERE id=?");
|
|
$perms = isset($_POST["permissions"]) ? json_encode($_POST["permissions"]) : "{}";
|
|
$allowed_branches = isset($_POST["allowed_branches"]) && is_array($_POST["allowed_branches"]) ? implode(",", $_POST["allowed_branches"]) : null;
|
|
$stmt->execute([$username, $hash, $role, $branch_code, $allowed_branches, $name_ar, $name_en, $perms, $id]);
|
|
} else {
|
|
$stmt = db()->prepare("UPDATE users SET username=?, role=?, branch_code=?, allowed_branches=?, name_ar=?, name_en=?, permissions=? WHERE id=?");
|
|
$perms = isset($_POST["permissions"]) ? json_encode($_POST["permissions"]) : "{}";
|
|
$allowed_branches = isset($_POST["allowed_branches"]) && is_array($_POST["allowed_branches"]) ? implode(",", $_POST["allowed_branches"]) : null;
|
|
$stmt->execute([$username, $role, $branch_code, $allowed_branches, $name_ar, $name_en, $perms, $id]);
|
|
}
|
|
set_flash('success', tr('تم تعديل المستخدم بنجاح.', 'User updated successfully.'));
|
|
} catch (PDOException $e) {
|
|
set_flash('error', tr('حدث خطأ أثناء التعديل.', 'Error occurred during update.'));
|
|
}
|
|
}
|
|
redirect_to('users.php');
|
|
}
|
|
|
|
if ($action === 'delete') {
|
|
if (!has_permission('users', 'del')) { set_flash('error', tr('ليس لديك صلاحية', 'No permission')); redirect_to('users.php'); }
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
if ($id && $id !== $user['id']) {
|
|
$stmt = db()->prepare("DELETE FROM users WHERE id=?");
|
|
$stmt->execute([$id]);
|
|
set_flash('success', tr('تم حذف المستخدم بنجاح.', 'User deleted successfully.'));
|
|
} else {
|
|
set_flash('error', tr('لا يمكن حذف حسابك الحالي.', 'Cannot delete your own account.'));
|
|
}
|
|
redirect_to('users.php');
|
|
}
|
|
}
|
|
|
|
// Search logic
|
|
$search = $_GET['q'] ?? '';
|
|
$searchQuery = "%{$search}%";
|
|
|
|
if ($search) {
|
|
$stmt = db()->prepare("SELECT * FROM users WHERE name_ar LIKE ? OR name_en LIKE ? OR username LIKE ? ORDER BY id DESC");
|
|
$stmt->execute([$searchQuery, $searchQuery, $searchQuery]);
|
|
} else {
|
|
$stmt = db()->query("SELECT * FROM users ORDER BY id DESC");
|
|
}
|
|
$filteredAccounts = $stmt->fetchAll();
|
|
|
|
// Pagination logic
|
|
$page = max(1, (int)($_GET['p'] ?? 1));
|
|
$limit = 10;
|
|
$total = count($filteredAccounts);
|
|
$totalPages = max(1, ceil($total / $limit));
|
|
$offset = ($page - 1) * $limit;
|
|
$accounts = array_slice($filteredAccounts, $offset, $limit, true);
|
|
|
|
$availableBranches = branches();
|
|
|
|
require __DIR__ . '/includes/header.php';
|
|
?>
|
|
<section class="surface-card mb-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<div>
|
|
<h3 class="h5 mb-2"><i class="bi bi-people me-2"></i><?= h(tr('الوصول حسب الدور', 'Role-based access')) ?></h3>
|
|
<p class="text-muted mb-0"><?= h(tr('إدارة المستخدمين وصلاحيات الوصول للنظام.', 'Manage users and system access permissions.')) ?></p>
|
|
</div>
|
|
<?php if(has_permission("users", "add")): ?>
|
|
<button type="button" class="btn btn-primary" onclick="openAddModal()">
|
|
<i class="bi bi-person-plus"></i> <?= h(tr('إضافة مستخدم', 'Add User')) ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<form class="d-flex mb-3" method="GET" action="users.php">
|
|
<div class="input-group" style="max-width: 400px;">
|
|
<input type="text" name="q" class="form-control" placeholder="<?= h(tr('بحث...', 'Search...')) ?>" value="<?= h($search) ?>">
|
|
<button class="btn btn-outline-secondary" type="submit"><i class="bi bi-search"></i></button>
|
|
</div>
|
|
</form>
|
|
|
|
<?php if ($flash): ?>
|
|
<div class="alert alert-<?= h($flash['type'] === 'error' ? 'danger' : $flash['type']) ?> alert-dismissible fade show mt-3" role="alert">
|
|
<?= h($flash['message']) ?>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</section>
|
|
|
|
<section class="surface-card">
|
|
<div class="table-responsive shadow-sm" style="border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.05);">
|
|
<table class="table table-hover align-middle mb-0 text-center" style="background-color: #fff;">
|
|
<thead style="background: linear-gradient(90deg, #0d6efd, #0dcaf0);">
|
|
<tr>
|
|
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('المستخدم', 'User')) ?></th>
|
|
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الدور', 'Role')) ?></th>
|
|
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الفرع', 'Branch')) ?></th>
|
|
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr("صلاحيات مخصصة", "Custom Permissions")) ?></th>
|
|
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('إجراءات', 'Actions')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="border-top-0">
|
|
<?php if(empty($accounts)): ?>
|
|
<tr><td colspan="7" class="text-center text-muted py-4"><?= h(tr('لا توجد بيانات', 'No data found')) ?></td></tr>
|
|
<?php endif; ?>
|
|
<?php foreach ($accounts as $account): ?>
|
|
<tr>
|
|
<td>
|
|
<div class="fw-semibold"><?= h(current_lang() === 'ar' ? $account['name_ar'] : $account['name_en']) ?></div>
|
|
<div class="small text-muted"><?= h($account['username']) ?></div>
|
|
</td>
|
|
<td><?= h(role_label($account['role'])) ?></td>
|
|
<td><?= h(branch_label($account['branch_code'])) ?></td>
|
|
<td><span class="badge text-bg-light border"><?= h(empty($account["permissions"]) || $account["permissions"] === "{}" ? tr("الافتراضي", "Default") : tr("مخصصة", "Custom")) ?></span></td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;"
|
|
onclick='openEditModal(<?= htmlspecialchars(json_encode($account), ENT_QUOTES, "UTF-8") ?>)' title="<?= h(tr('تعديل', 'Edit')) ?>">
|
|
<i class="bi bi-pencil"></i>
|
|
</button>
|
|
<?php if ($account['id'] !== $user['id'] && has_permission("users", "del")): ?>
|
|
<form method="POST" class="d-inline" onsubmit="confirmSwal(event, '<?= h(tr('هل أنت متأكد؟', 'Are you sure?')) ?>');">
|
|
<input type="hidden" name="action" value="delete">
|
|
<input type="hidden" name="id" value="<?= h($account['id']) ?>">
|
|
<button type="submit" class="btn btn-sm btn-outline-danger rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" title="<?= h(tr('حذف', 'Delete')) ?>">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</form>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<?php if ($totalPages > 1): ?>
|
|
<nav class="mt-4">
|
|
<ul class="pagination justify-content-center mb-0">
|
|
<?php for($i=1; $i<=$totalPages; $i++): ?>
|
|
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
|
|
<a class="page-link" href="<?= h(url_for('users.php', ['p' => $i, 'q' => $search])) ?>"><?= $i ?></a>
|
|
</li>
|
|
<?php endfor; ?>
|
|
</ul>
|
|
</nav>
|
|
<?php endif; ?>
|
|
</section>
|
|
|
|
<!-- User Modal -->
|
|
<div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<form id="userForm" method="POST" action="users.php">
|
|
<input type="hidden" name="action" id="userAction" value="add">
|
|
<input type="hidden" name="id" id="userId" value="">
|
|
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="userModalLabel"><?= h(tr('إضافة مستخدم جديد', 'Add New User')) ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="userNameAr" class="form-label"><?= h(tr('الاسم (عربي)', 'Name (AR)')) ?></label>
|
|
<input type="text" class="form-control" id="userNameAr" name="name_ar" required>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="userNameEn" class="form-label"><?= h(tr('الاسم (إنجليزي)', 'Name (EN)')) ?></label>
|
|
<input type="text" class="form-control" id="userNameEn" name="name_en">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="username" class="form-label"><?= h(tr('اسم المستخدم للدخول', 'Login Username')) ?></label>
|
|
<input type="text" class="form-control" id="username" name="username" required autocomplete="new-username">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="password" class="form-label"><?= h(tr('كلمة المرور', 'Password')) ?> <small class="text-muted" id="passwordHelp"></small></label>
|
|
<input type="password" class="form-control" id="password" name="password" autocomplete="new-password">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="userRole" class="form-label"><?= h(tr('الدور', 'Role')) ?></label>
|
|
<select class="form-select" id="userRole" name="role" required>
|
|
<option value="cashier"><?= h(tr('كاشير', 'Cashier')) ?></option>
|
|
<option value="manager"><?= h(tr('مدير فرع', 'Branch Manager')) ?></option>
|
|
<option value="owner"><?= h(tr('مالك', 'Owner')) ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="userBranch" class="form-label"><?= h(tr('الفرع', 'Branch')) ?></label>
|
|
<select class="form-select" id="userBranch" name="branch_code" required>
|
|
<?php foreach ($availableBranches as $code => $b): ?>
|
|
<option value="<?= h($code) ?>"><?= h(current_lang() === 'ar' ? $b['name_ar'] : $b['name_en']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-12 mb-3">
|
|
<label class="form-label"><?= h(tr('فروع إضافية يمكن الوصول إليها', 'Additional Accessible Branches')) ?></label>
|
|
<div class="d-flex flex-wrap gap-3 p-2 border rounded bg-light">
|
|
<?php foreach ($availableBranches as $code => $b): ?>
|
|
<div class="form-check">
|
|
<input class="form-check-input additional-branch" type="checkbox" name="allowed_branches[]" value="<?= h($code) ?>" id="ab_<?= h($code) ?>">
|
|
<label class="form-check-label ms-1" for="ab_<?= h($code) ?>">
|
|
<?= h(current_lang() === 'ar' ? $b['name_ar'] : $b['name_en']) ?>
|
|
</label>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
|
|
</div> <!-- Close row -->
|
|
|
|
<h6 class="mt-4 mb-3"><?= h(tr('صلاحيات الوصول المخصصة', 'Custom Access Permissions')) ?></h6>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-bordered">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>
|
|
<div class="form-check d-inline-block mb-0">
|
|
<input class="form-check-input" type="checkbox" id="selectAllPerms" onchange="toggleAllPerms(this)">
|
|
<label class="form-check-label ms-1 fw-bold" for="selectAllPerms"><?= h(tr('الواجهة', 'Module')) ?></label>
|
|
</div>
|
|
</th>
|
|
<th class="text-center"><?= h(tr('عرض', 'Show')) ?></th>
|
|
<th class="text-center"><?= h(tr('إضافة', 'Add')) ?></th>
|
|
<th class="text-center"><?= h(tr('تعديل', 'Edit')) ?></th>
|
|
<th class="text-center"><?= h(tr('حذف', 'Delete')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach (get_app_modules() as $key => $module): ?>
|
|
<tr>
|
|
<td>
|
|
<div class="form-check mb-0">
|
|
<input class="form-check-input row-perm-check" type="checkbox" id="row_<?= $key ?>" onchange="toggleRowPerms(this, '<?= $key ?>')">
|
|
<label class="form-check-label ms-1" for="row_<?= $key ?>">
|
|
<?= h(current_lang() === 'ar' ? $module['name_ar'] : $module['name_en']) ?>
|
|
</label>
|
|
</div>
|
|
</td>
|
|
<?php foreach (['show', 'add', 'edit', 'del'] as $act): ?>
|
|
<td class="text-center">
|
|
<?php if (in_array($act, $module['actions'])): ?>
|
|
<div class="form-check d-flex justify-content-center mb-0">
|
|
<input class="form-check-input perm-check perm-row-<?= $key ?>" type="checkbox" name="permissions[<?= $key ?>][<?= $act ?>]" id="perm_<?= $key ?>_<?= $act ?>" value="1">
|
|
</div>
|
|
<?php else:
|
|
?>
|
|
<span class="text-muted">-</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<?php endforeach; ?>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
|
|
<button type="submit" class="btn btn-primary"><?= h(tr('حفظ', 'Save')) ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let userModal;
|
|
|
|
function toggleAllPerms(source) {
|
|
document.querySelectorAll('.perm-check').forEach(cb => {
|
|
if (!cb.disabled) cb.checked = source.checked;
|
|
});
|
|
document.querySelectorAll('.row-perm-check').forEach(cb => {
|
|
if (!cb.disabled) cb.checked = source.checked;
|
|
});
|
|
}
|
|
|
|
function toggleRowPerms(source, key) {
|
|
document.querySelectorAll('.perm-row-' + key).forEach(cb => {
|
|
if (!cb.disabled) cb.checked = source.checked;
|
|
});
|
|
updateSelectAllCheckbox();
|
|
}
|
|
|
|
function updateSelectAllCheckbox() {
|
|
let allPerms = document.querySelectorAll('.perm-check');
|
|
let allChecked = document.querySelectorAll('.perm-check:checked');
|
|
let selectAllCb = document.getElementById('selectAllPerms');
|
|
if (selectAllCb) {
|
|
selectAllCb.checked = (allPerms.length > 0 && allPerms.length === allChecked.length);
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
userModal = new bootstrap.Modal(document.getElementById('userModal'));
|
|
|
|
document.querySelectorAll('.perm-check').forEach(cb => {
|
|
cb.addEventListener('change', function() {
|
|
let rowClass = Array.from(this.classList).find(c => c.startsWith('perm-row-'));
|
|
if (rowClass) {
|
|
let key = rowClass.replace('perm-row-', '');
|
|
let rowPerms = document.querySelectorAll('.' + rowClass);
|
|
let rowChecked = document.querySelectorAll('.' + rowClass + ':checked');
|
|
let rowCb = document.getElementById('row_' + key);
|
|
if (rowCb) {
|
|
rowCb.checked = (rowPerms.length > 0 && rowPerms.length === rowChecked.length);
|
|
}
|
|
}
|
|
updateSelectAllCheckbox();
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
function openAddModal() {
|
|
document.getElementById('userAction').value = 'add';
|
|
document.getElementById('userId').value = '';
|
|
document.getElementById('userForm').reset();
|
|
document.querySelectorAll('.additional-branch').forEach(cb => cb.checked = false);
|
|
document.querySelectorAll('.perm-check').forEach(cb => cb.checked = false);
|
|
document.querySelectorAll('.row-perm-check').forEach(cb => cb.checked = false);
|
|
if(document.getElementById('selectAllPerms')) document.getElementById('selectAllPerms').checked = false;
|
|
document.getElementById('userModalLabel').innerText = '<?= h(tr('إضافة مستخدم جديد', 'Add New User')) ?>';
|
|
document.getElementById('password').required = true;
|
|
document.getElementById('passwordHelp').innerText = '';
|
|
userModal.show();
|
|
}
|
|
|
|
function openEditModal(account) {
|
|
document.getElementById('userAction').value = 'edit';
|
|
document.getElementById('userId').value = account.id;
|
|
|
|
document.getElementById('userNameAr').value = account.name_ar;
|
|
document.getElementById('userNameEn').value = account.name_en;
|
|
document.getElementById('username').value = account.username;
|
|
|
|
document.getElementById('userRole').value = account.role;
|
|
document.getElementById('userBranch').value = account.branch_code;
|
|
|
|
document.getElementById('password').required = false;
|
|
document.getElementById('password').value = '';
|
|
document.getElementById('passwordHelp').innerText = '(<?= h(tr('اتركه فارغاً لعدم التغيير', 'Leave blank to keep unchanged')) ?>)';
|
|
|
|
document.querySelectorAll('.additional-branch').forEach(cb => cb.checked = false);
|
|
if (account.allowed_branches) {
|
|
let abs = account.allowed_branches.split(',');
|
|
abs.forEach(b => {
|
|
let cb = document.getElementById('ab_' + b.trim());
|
|
if (cb) cb.checked = true;
|
|
});
|
|
}
|
|
|
|
document.querySelectorAll('.perm-check').forEach(cb => cb.checked = false);
|
|
document.querySelectorAll('.row-perm-check').forEach(cb => cb.checked = false);
|
|
if(document.getElementById('selectAllPerms')) document.getElementById('selectAllPerms').checked = false;
|
|
if (account.permissions) {
|
|
try {
|
|
let perms = typeof account.permissions === 'string' ? JSON.parse(account.permissions) : account.permissions;
|
|
for (let mod in perms) {
|
|
for (let act in perms[mod]) {
|
|
let cb = document.getElementById('perm_' + mod + '_' + act);
|
|
if (cb) cb.checked = true;
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
document.querySelectorAll('.row-perm-check').forEach(rowCb => {
|
|
let key = rowCb.id.replace('row_', '');
|
|
let rowPerms = document.querySelectorAll('.perm-row-' + key);
|
|
let rowChecked = document.querySelectorAll('.perm-row-' + key + ':checked');
|
|
rowCb.checked = (rowPerms.length > 0 && rowPerms.length === rowChecked.length);
|
|
});
|
|
updateSelectAllCheckbox();
|
|
|
|
document.getElementById('userModalLabel').innerText = '<?= h(tr('تعديل مستخدم', 'Edit User')) ?>';
|
|
userModal.show();
|
|
}
|
|
</script>
|
|
|
|
<?php require __DIR__ . '/includes/footer.php'; ?>
|