'use client'; import { useState, useEffect, useCallback } from 'react'; import { Shield, Edit, Users, Check, X, Plus } from 'lucide-react'; import { positionsAPI } from '@/lib/api/admin'; import { departmentsAPI } from '@/lib/api/employees'; import type { PositionRole, PositionPermission, CreatePositionData } from '@/lib/api/admin'; import Modal from '@/components/Modal'; import LoadingSpinner from '@/components/LoadingSpinner'; const MODULES = [ { id: 'contacts', name: 'إدارة جهات الاتصال', nameEn: 'Contact Management' }, { id: 'suppliers', name: 'إدارة الموردين', nameEn: 'Supplier Management' }, { id: 'crm', name: 'إدارة علاقات العملاء', nameEn: 'CRM' }, { id: 'tenders', name: 'إدارة المناقصات', nameEn: 'Tender Management' }, { id: 'inventory', name: 'المخزون والأصول', nameEn: 'Inventory & Assets' }, { id: 'projects', name: 'المهام والمشاريع', nameEn: 'Tasks & Projects' }, { id: 'hr', name: 'الموارد البشرية', nameEn: 'HR Management' }, { id: 'leave_requests', name: 'طلبات الإجازات', nameEn: 'Leave Requests' }, { id: 'overtime_requests', name: 'طلبات الساعات الإضافية', nameEn: 'Overtime Requests' }, { id: 'loan_requests', name: 'طلبات القروض', nameEn: 'Loan Requests' }, { id: 'purchase_requests', name: 'طلبات الشراء', nameEn: 'Purchase Requests' }, { id: 'department_leave_requests', name: 'طلبات إجازات القسم', nameEn: 'Department Leave Requests' }, { id: 'department_overtime_requests', name: 'طلبات الساعات الإضافية للقسم', nameEn: 'Department Overtime Requests' }, { id: 'department_expense_claims', name: 'طلبات كشف المصاريف للقسم', nameEn: 'Department Expense Claims' }, { id: 'portal', name: 'البوابة الذاتية', nameEn: 'My Portal' }, { id: 'marketing', name: 'التسويق', nameEn: 'Marketing' }, { id: 'admin', name: 'لوحة الإدارة', nameEn: 'Admin' }, ]; const ACTIONS = [ { id: 'read', name: 'عرض' }, { id: 'create', name: 'إنشاء' }, { id: 'update', name: 'تعديل' }, { id: 'delete', name: 'حذف' }, { id: 'export', name: 'تصدير' }, { id: 'approve', name: 'اعتماد' }, { id: 'mark-as-paid', name: 'تأكيد القبض' }, { id: 'notify', name: 'إشعار' }, { id: 'merge', name: 'دمج' }, ]; function hasAction(permission: PositionPermission | undefined, action: string): boolean { if (!permission?.actions) return false; const actions = Array.isArray(permission.actions) ? permission.actions : []; return actions.includes('*') || actions.includes('all') || actions.includes(action); } function buildPermissionsFromMatrix(matrix: Record>) { return MODULES.filter((m) => Object.values(matrix[m.id] || {}).some(Boolean)).map((m) => { const actions = ACTIONS.filter((a) => matrix[m.id]?.[a.id]).map((a) => a.id); return { module: m.id, resource: '*', actions: actions.length === ACTIONS.length ? ['*'] : actions, }; }); } function buildMatrixFromPermissions(permissions: PositionPermission[]): Record> { const matrix: Record> = {}; for (const m of MODULES) { matrix[m.id] = {}; const perm = permissions.find((p) => p.module === m.id && (p.resource === '*' || p.resource === m.id)); const hasAll = perm && (Array.isArray(perm.actions) ? (perm.actions as string[]).includes('*') || (perm.actions as string[]).includes('all') : false); for (const a of ACTIONS) { matrix[m.id][a.id] = hasAll || hasAction(perm, a.id); } } return matrix; } const initialCreateForm: CreatePositionData & { description?: string } = { title: '', titleAr: '', code: '', departmentId: '', level: 5, description: '', }; export default function RolesManagement() { const [roles, setRoles] = useState([]); const [departments, setDepartments] = useState<{ id: string; name: string; nameAr?: string | null }[]>([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedRoleId, setSelectedRoleId] = useState(null); const [showEditModal, setShowEditModal] = useState(false); const [showCreateModal, setShowCreateModal] = useState(false); const [createForm, setCreateForm] = useState(initialCreateForm); const [createErrors, setCreateErrors] = useState>({}); const [permissionMatrix, setPermissionMatrix] = useState>>({}); const [saving, setSaving] = useState(false); const fetchRoles = useCallback(async () => { setLoading(true); setError(null); try { const pos = await positionsAPI.getAll(); setRoles(pos); if (selectedRoleId && !pos.find((p) => p.id === selectedRoleId)) { setSelectedRoleId(null); } } catch (err: unknown) { setError(err instanceof Error ? err.message : 'فشل تحميل الأدوار'); } finally { setLoading(false); } }, [selectedRoleId]); useEffect(() => { fetchRoles(); }, [fetchRoles]); useEffect(() => { departmentsAPI.getAll().then((depts) => setDepartments(depts)).catch(() => {}); }, []); const currentRole = roles.find((r) => r.id === selectedRoleId); const handleCreateRole = async (e: React.FormEvent) => { e.preventDefault(); const errs: Record = {}; if (!createForm.title?.trim()) errs.title = 'Required'; if (!createForm.code?.trim()) errs.code = 'Required'; if (!createForm.departmentId) errs.departmentId = 'Required'; setCreateErrors(errs); if (Object.keys(errs).length > 0) return; setSaving(true); try { const position = await positionsAPI.create({ title: createForm.title.trim(), titleAr: createForm.titleAr?.trim() || undefined, code: createForm.code.trim(), departmentId: createForm.departmentId, level: createForm.level ?? 5, description: createForm.description?.trim() || undefined, }); setShowCreateModal(false); setCreateForm(initialCreateForm); setCreateErrors({}); await fetchRoles(); setSelectedRoleId(position.id); setShowEditModal(true); } catch (err: unknown) { setCreateErrors({ form: err instanceof Error ? err.message : 'Failed to create role' }); } finally { setSaving(false); } }; useEffect(() => { if (currentRole) { setPermissionMatrix(buildMatrixFromPermissions(currentRole.permissions || [])); } }, [currentRole?.id, currentRole?.permissions]); const handleTogglePermission = (moduleId: string, actionId: string) => { setPermissionMatrix((prev) => ({ ...prev, [moduleId]: { ...(prev[moduleId] || {}), [actionId]: !prev[moduleId]?.[actionId], }, })); }; const handleSavePermissions = async () => { if (!selectedRoleId) return; setSaving(true); try { const permissions = buildPermissionsFromMatrix(permissionMatrix); await positionsAPI.updatePermissions(selectedRoleId, permissions); setShowEditModal(false); fetchRoles(); } catch (err: unknown) { alert(err instanceof Error ? err.message : 'فشل حفظ الصلاحيات'); } finally { setSaving(false); } }; const handleSelectRole = (id: string) => { setSelectedRoleId(id); setShowEditModal(false); }; return (

الأدوار والصلاحيات

إدارة أدوار المستخدمين ومصفوفة الصلاحيات

{loading ? (
) : error ? (
{error}
) : (
{/* Roles List */}

الأدوار ({roles.length})

{roles.map((role) => (
handleSelectRole(role.id)} className={`p-4 rounded-xl border-2 cursor-pointer transition-all ${ selectedRoleId === role.id ? 'border-purple-600 bg-purple-50 shadow-md' : 'border-gray-200 bg-white hover:border-purple-300 hover:shadow-sm' }`} >

{role.titleAr || role.title}

{role.title}

{role.usersCount ?? role._count?.employees ?? 0} مستخدم
))}
{/* Permission Matrix */}
{currentRole ? (

{currentRole.titleAr || currentRole.title}

{currentRole.title}

مصفوفة الصلاحيات

{ACTIONS.map((perm) => ( ))} {MODULES.map((module) => ( {ACTIONS.map((action) => { const hasPermission = permissionMatrix[module.id]?.[action.id]; return ( ); })} ))}
الوحدة {perm.name}

{module.name}

{module.nameEn}

{hasPermission ? : }
) : (

اختر دوراً لعرض الصلاحيات

اختر دور من القائمة لعرض وتعديل صلاحياته

)}
)} {/* Create Role Modal */} { setShowCreateModal(false); setCreateForm(initialCreateForm); setCreateErrors({}); }} title="إضافة دور جديد" size="md" >
setCreateForm((p) => ({ ...p, title: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500" placeholder="e.g. Sales Representative" /> {createErrors.title &&

{createErrors.title}

}
setCreateForm((p) => ({ ...p, titleAr: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500" placeholder="مندوب مبيعات" />
setCreateForm((p) => ({ ...p, code: e.target.value }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500" placeholder="SALES_REP" /> {createErrors.code &&

{createErrors.code}

}
{createErrors.departmentId &&

{createErrors.departmentId}

}
setCreateForm((p) => ({ ...p, level: parseInt(e.target.value, 10) || 5 }))} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500" />