🚀 مُصادَق: تحديث برمجي جديد 2026-05-03 02:51
This commit is contained in:
@@ -13,6 +13,12 @@ final class UsersController
|
|||||||
|
|
||||||
public function list(Request $request): void
|
public function list(Request $request): void
|
||||||
{
|
{
|
||||||
|
$currentUserRole = $request->user->role ?? 'viewer';
|
||||||
|
if (!in_array($currentUserRole, ['super_admin', 'admin'])) {
|
||||||
|
Response::error('ليس لديك صلاحية لعرض المستخدمين', 'FORBIDDEN', 403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$tenantId = $request->tenantId;
|
$tenantId = $request->tenantId;
|
||||||
$db = Database::getInstance();
|
$db = Database::getInstance();
|
||||||
@@ -31,11 +37,30 @@ final class UsersController
|
|||||||
|
|
||||||
public function create(Request $request): void
|
public function create(Request $request): void
|
||||||
{
|
{
|
||||||
|
$currentUserRole = $request->user->role ?? 'viewer';
|
||||||
|
if (!in_array($currentUserRole, ['super_admin', 'admin'])) {
|
||||||
|
Response::error('ليس لديك صلاحية لإضافة مستخدمين', 'FORBIDDEN', 403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$name = $request->input('name');
|
$name = $request->input('name');
|
||||||
$email = $request->input('email');
|
$email = $request->input('email');
|
||||||
$password = $request->input('password');
|
$password = $request->input('password');
|
||||||
$role = $request->input('role', 'accountant');
|
$role = $request->input('role', 'accountant');
|
||||||
|
|
||||||
|
// Admin can only create accountants and employees. Only super_admin can create admins.
|
||||||
|
if ($currentUserRole === 'admin' && in_array($role, ['admin', 'super_admin'])) {
|
||||||
|
Response::error('لا تملك الصلاحية لإضافة مدراء', 'FORBIDDEN', 403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate valid roles
|
||||||
|
$validRoles = ['super_admin', 'admin', 'accountant', 'employee', 'viewer'];
|
||||||
|
if (!in_array($role, $validRoles)) {
|
||||||
|
Response::error('صلاحية غير صالحة', 'VALIDATION_ERROR', 422);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$name || !$email || !$password) {
|
if (!$name || !$email || !$password) {
|
||||||
Response::error('Name, email, and password are required', 'VALIDATION_ERROR', 422);
|
Response::error('Name, email, and password are required', 'VALIDATION_ERROR', 422);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ final class FileStorageService
|
|||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->storagePath = $_ENV['STORAGE_PATH'] ?? dirname(__DIR__, 2) . '/storage';
|
// Use dynamic path to avoid issues if Mac .env is deployed to Linux server
|
||||||
|
$this->storagePath = dirname(__DIR__, 2) . '/storage';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(array $file, string $tenantId, string $companyId): string
|
public function store(array $file, string $tenantId, string $companyId): string
|
||||||
|
|||||||
@@ -150,6 +150,7 @@
|
|||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
localStorage.removeItem('access_token');
|
localStorage.removeItem('access_token');
|
||||||
|
localStorage.removeItem('user_role');
|
||||||
API.accessToken = null;
|
API.accessToken = null;
|
||||||
initApp();
|
initApp();
|
||||||
}
|
}
|
||||||
@@ -170,6 +171,14 @@
|
|||||||
// ── Users View ───────────────────────────────────────────
|
// ── Users View ───────────────────────────────────────────
|
||||||
async function renderUsers() {
|
async function renderUsers() {
|
||||||
document.getElementById('page-title').textContent = 'إدارة المستخدمين';
|
document.getElementById('page-title').textContent = 'إدارة المستخدمين';
|
||||||
|
|
||||||
|
// Check RBAC
|
||||||
|
const role = localStorage.getItem('user_role');
|
||||||
|
if (role !== 'super_admin' && role !== 'admin') {
|
||||||
|
contentDiv.innerHTML = `<div class="text-red-400 p-8 glass-panel rounded-3xl text-center">عذراً، ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await API.get('/users');
|
const res = await API.get('/users');
|
||||||
const users = res.data;
|
const users = res.data;
|
||||||
@@ -187,8 +196,14 @@
|
|||||||
html += `<div class="col-span-full text-center py-12 text-slate-500 glass-panel rounded-3xl">لا يوجد مستخدمين مسجلين.</div>`;
|
html += `<div class="col-span-full text-center py-12 text-slate-500 glass-panel rounded-3xl">لا يوجد مستخدمين مسجلين.</div>`;
|
||||||
} else {
|
} else {
|
||||||
users.forEach(user => {
|
users.forEach(user => {
|
||||||
const roleColor = user.role === 'admin' ? 'text-primary' : (user.role === 'manager' ? 'text-blue-400' : 'text-slate-400');
|
let roleColor = 'text-slate-400';
|
||||||
const roleLabel = user.role === 'admin' ? 'سوبر أدمن' : (user.role === 'manager' ? 'مدير' : 'محاسب');
|
let roleLabel = 'مستخدم';
|
||||||
|
|
||||||
|
if (user.role === 'super_admin') { roleColor = 'text-primary'; roleLabel = 'سوبر أدمن'; }
|
||||||
|
else if (user.role === 'admin') { roleColor = 'text-blue-400'; roleLabel = 'مدير النظام'; }
|
||||||
|
else if (user.role === 'accountant') { roleColor = 'text-purple-400'; roleLabel = 'محاسب'; }
|
||||||
|
else if (user.role === 'employee') { roleColor = 'text-orange-400'; roleLabel = 'موظف'; }
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="glass-panel p-6 rounded-3xl flex flex-col h-full border-t-4 border-t-primary">
|
<div class="glass-panel p-6 rounded-3xl flex flex-col h-full border-t-4 border-t-primary">
|
||||||
<div class="flex items-center gap-4 mb-4">
|
<div class="flex items-center gap-4 mb-4">
|
||||||
@@ -218,11 +233,20 @@
|
|||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
contentDiv.innerHTML = html;
|
contentDiv.innerHTML = html;
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
contentDiv.innerHTML = `<div class="text-red-400">خطأ في جلب المستخدمين</div>`;
|
contentDiv.innerHTML = `<div class="text-red-400 p-8 glass-panel rounded-3xl">خطأ في جلب المستخدمين: ${err.error?.message_ar || err.message}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAddUserModal() {
|
function showAddUserModal() {
|
||||||
|
const currentRole = localStorage.getItem('user_role');
|
||||||
|
let optionsHtml = `
|
||||||
|
<option value="accountant">محاسب</option>
|
||||||
|
<option value="employee">موظف</option>
|
||||||
|
`;
|
||||||
|
if (currentRole === 'super_admin') {
|
||||||
|
optionsHtml += `<option value="admin">مدير نظام</option>`;
|
||||||
|
}
|
||||||
|
|
||||||
const modals = document.getElementById('modals');
|
const modals = document.getElementById('modals');
|
||||||
modals.innerHTML = `
|
modals.innerHTML = `
|
||||||
<div class="fixed inset-0 bg-black/60 backdrop-blur-sm z-[100] flex items-center justify-center p-4 overflow-y-auto" id="user-modal">
|
<div class="fixed inset-0 bg-black/60 backdrop-blur-sm z-[100] flex items-center justify-center p-4 overflow-y-auto" id="user-modal">
|
||||||
@@ -233,9 +257,7 @@
|
|||||||
<input type="email" id="usr-email" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" placeholder="البريد الإلكتروني" required>
|
<input type="email" id="usr-email" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" placeholder="البريد الإلكتروني" required>
|
||||||
<input type="password" id="usr-password" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" placeholder="كلمة المرور" required>
|
<input type="password" id="usr-password" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" placeholder="كلمة المرور" required>
|
||||||
<select id="usr-role" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" required>
|
<select id="usr-role" class="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-white focus:border-primary outline-none" required>
|
||||||
<option value="accountant">محاسب</option>
|
${optionsHtml}
|
||||||
<option value="admin">مدير</option>
|
|
||||||
<option value="employee">موظف</option>
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div class="flex gap-3 mt-6 pt-4 border-t border-white/10">
|
<div class="flex gap-3 mt-6 pt-4 border-t border-white/10">
|
||||||
@@ -301,6 +323,7 @@
|
|||||||
const password = document.getElementById('login-password').value;
|
const password = document.getElementById('login-password').value;
|
||||||
const res = await API.post('/auth/login', { email, password });
|
const res = await API.post('/auth/login', { email, password });
|
||||||
localStorage.setItem('access_token', res.data.access_token);
|
localStorage.setItem('access_token', res.data.access_token);
|
||||||
|
localStorage.setItem('user_role', res.data.user.role);
|
||||||
API.accessToken = res.data.access_token;
|
API.accessToken = res.data.access_token;
|
||||||
initApp();
|
initApp();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -677,6 +700,15 @@
|
|||||||
document.getElementById('sidebar').classList.add('flex');
|
document.getElementById('sidebar').classList.add('flex');
|
||||||
document.getElementById('header').classList.add('flex');
|
document.getElementById('header').classList.add('flex');
|
||||||
|
|
||||||
|
// Hide Users menu if not admin or super_admin
|
||||||
|
const role = localStorage.getItem('user_role');
|
||||||
|
const usersNav = document.getElementById('nav-users');
|
||||||
|
if (role !== 'super_admin' && role !== 'admin') {
|
||||||
|
if (usersNav) usersNav.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
if (usersNav) usersNav.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
// AI Chat Listener
|
// AI Chat Listener
|
||||||
document.getElementById('ai-query').onkeydown = async (e) => {
|
document.getElementById('ai-query').onkeydown = async (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
|
|||||||
Reference in New Issue
Block a user