// ============================================================ // Elegant Work โ Roles & Permissions Manager // Rendered inside Settings page // ============================================================ const CF_MODULES = [ { key: 'dashboard', label: '๐ Dashboard', actions: ['view'] }, { key: 'clients', label: '๐ข Clients', actions: ['view', 'create', 'edit', 'delete'] }, { key: 'employees', label: '๐ฅ Employees', actions: ['view', 'create', 'edit', 'delete', 'reports'] }, { key: 'projects', label: '๐ Projects', actions: ['view', 'create', 'edit', 'delete'] }, { key: 'jobcards', label: '๐ง Job Cards', actions: ['view', 'view_own', 'create', 'edit', 'delete', 'reports', 'view_costing'] }, { key: 'stock', label: '๐ฆ Stock Control', actions: ['view', 'create', 'edit', 'delete', 'reports'] }, { key: 'fleet', label: '๐ Fleet', actions: ['view', 'create', 'edit', 'delete', 'reports'] }, { key: 'slips', label: '๐งพ Slip Manager', actions: ['view', 'create', 'edit', 'delete', 'reports'] }, { key: 'cashflow', label: '๐ฐ Cash Flow', actions: ['view', 'create', 'edit', 'delete', 'reports'] }, { key: 'checklists', label: 'โ Checklists', actions: ['view', 'create', 'edit', 'delete'] }, { key: 'calendar', label: '๐ Calendar', actions: ['view', 'create', 'edit', 'delete'] }, { key: 'payroll', label: '๐ผ Payroll', actions: ['view', 'create', 'edit', 'delete', 'reports'] }, { key: 'settings', label: 'โ๏ธ Settings', actions: ['view', 'edit'] }, { key: 'users', label: '๐ค Users', actions: ['view', 'create', 'edit', 'delete'] }, ]; const ACTION_LABELS = { view: 'View', create: 'Create', edit: 'Edit', delete: 'Delete', reports: 'Reports', view_own: 'Own Only', view_costing: 'Costing' }; const ACTION_COLORS = { view: { bg: '#eff6ff', color: '#1d4ed8' }, create: { bg: '#f0fdf4', color: '#15803d' }, edit: { bg: '#fefce8', color: '#92400e' }, delete: { bg: '#fff1f2', color: '#dc2626' }, reports: { bg: '#fdf4ff', color: '#7c3aed' }, view_own: { bg: '#fff7ed', color: '#c2410c' }, view_costing: { bg: '#ecfdf5', color: '#065f46' }, }; // โโ Main entry โ render roles table into a container โโโโโโโโโโ async function renderRolesSection(containerId) { const wrap = document.getElementById(containerId); if (!wrap) return; wrap.innerHTML = '
'; const res = await API.post('roles/list', {}); if (!res.success) { wrap.innerHTML = `Failed to load roles.
`; return; } const roles = res.data.roles; const isAdmin = Auth.isAdmin(); wrap.innerHTML = `| Role | Description | Active Users | Type | |
|---|---|---|---|---|
| ${r.name} | ${r.description || 'โ'} | ${r.user_count} user${r.user_count != 1 ? 's' : ''} | ${r.is_system ? '๐ System' : 'Custom'} | ${!r.is_system && isAdmin ? ` ` : ''} |
Failed to load permissions.
`; return; } // Build a lookup: module.action => allowed const current = {}; (res.data.permissions || []).forEach(p => { current[`${p.module}.${p.action}`] = !!parseInt(p.allowed); }); body.innerHTML = `| Module | ${['view', 'view_own', 'create', 'edit', 'delete', 'reports', 'view_costing'].map(a => `${ACTION_LABELS[a] || a} | `).join('')}
|---|---|
| ${mod.label} | ${['view', 'view_own', 'create', 'edit', 'delete', 'reports', 'view_costing'].map(act => { const key = `${mod.key}.${act}`; const hasAction = mod.actions.includes(act); const checked = hasAction && (current[key] !== false) && (current[key] !== undefined ? current[key] : false); return `${hasAction ? `` : `โ`} | `; }).join('')}
๐ก Disabling View hides the module from the sidebar entirely. Actions only apply if View is also enabled.
`; } function permsSelectAll(on) { document.querySelectorAll('#perms-body input[data-perm]').forEach(cb => { if (!cb.disabled) cb.checked = on; }); } function permsSelectViewOnly() { document.querySelectorAll('#perms-body input[data-perm]').forEach(cb => { if (cb.disabled) return; const isView = cb.dataset.perm.endsWith('.view'); cb.checked = isView; }); } function permsUpdateRow(moduleKey) { // If view is unchecked, uncheck all other actions for that module const viewCb = document.querySelector(`input[data-perm="${moduleKey}.view"]`); if (viewCb && !viewCb.checked) { ['create', 'edit', 'delete'].forEach(a => { const cb = document.querySelector(`input[data-perm="${moduleKey}.${a}"]`); if (cb) cb.checked = false; }); } } async function savePermissions(roleId) { const btn = document.querySelector('#modal-perms-modal .btn-primary'); if (btn) btn.classList.add('loading'); const matrix = {}; document.querySelectorAll('#perms-body input[data-perm]').forEach(cb => { matrix[cb.dataset.perm] = cb.checked; }); const res = await API.post('roles/permissions', { action: 'set', role_id: roleId, matrix: JSON.stringify(matrix) }); if (btn) btn.classList.remove('loading'); if (res.success) { Toast.show('Permissions saved. โ ', 'success'); Modal.close(); // Refresh own permissions if editing current user's role const me = Auth.getCurrentUser(); if (me && parseInt(me.role_id) === roleId) { await Auth.loadPermissions(); Toast.show('Your permissions were updated โ refreshing navigation.', 'info'); Router.navigate('settings'); } } else Toast.show(res.message, 'error'); }