feat(permissions): granular module permissions + workshop permissions matrix
This commit is contained in:
@@ -9,8 +9,6 @@ const Config = (() => {
|
||||
|
||||
// Cache for branches (used by employee modal selector)
|
||||
let _branches = [];
|
||||
let _rolePermissions = {};
|
||||
let _availablePermissions = [];
|
||||
|
||||
function token() {
|
||||
return localStorage.getItem('pos_token') || '';
|
||||
@@ -1035,6 +1033,12 @@ const Config = (() => {
|
||||
// -------------------------------------------------------------------------
|
||||
// Role permissions editor
|
||||
// -------------------------------------------------------------------------
|
||||
var _rolePermissions = {};
|
||||
var _availablePermissions = [];
|
||||
var _workshopPermissions = {};
|
||||
var _workshopSchema = { statuses: [], actions: [] };
|
||||
var _currentPermTab = 'modules';
|
||||
|
||||
async function loadRolePermissions() {
|
||||
try {
|
||||
var res = await fetch(API + '/role-permissions', { headers: headers() });
|
||||
@@ -1106,6 +1110,108 @@ const Config = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
// Workshop-specific permissions editor
|
||||
// -------------------------------------------------------------------------
|
||||
async function loadWorkshopPermissions() {
|
||||
try {
|
||||
var res = await fetch(API + '/role-permissions/workshop', { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
var data = await res.json();
|
||||
_workshopPermissions = data.roles || {};
|
||||
_workshopSchema = { statuses: data.statuses || [], actions: data.actions || [] };
|
||||
renderWorkshopPermissions();
|
||||
} catch (e) {
|
||||
console.error('Config.loadWorkshopPermissions:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function renderWorkshopPermissions() {
|
||||
var container = document.getElementById('workshop-permissions-container');
|
||||
var roleSel = document.getElementById('cfg-perm-role');
|
||||
if (!container || !roleSel) return;
|
||||
var role = roleSel.value;
|
||||
if (!role) {
|
||||
container.innerHTML = '<p style="color:var(--color-text-muted);">Selecciona un rol para ver y editar sus permisos de Taller.</p>';
|
||||
return;
|
||||
}
|
||||
var cfg = _workshopPermissions[role] || { statuses: [], actions: [] };
|
||||
var visibleStatuses = cfg.statuses || [];
|
||||
var visibleActions = cfg.actions || [];
|
||||
|
||||
var html = '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:var(--space-4);">';
|
||||
|
||||
// Statuses
|
||||
html += '<div style="border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-3);background:var(--color-surface-2);">';
|
||||
html += '<h4 style="margin:0 0 var(--space-3);font-size:var(--text-body-sm);color:var(--color-text-primary);">Estatus visibles en Taller</h4>';
|
||||
(_workshopSchema.statuses || []).forEach(function(s) {
|
||||
var checked = visibleStatuses.indexOf(s.key) !== -1 ? 'checked' : '';
|
||||
html += '<label style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-2);cursor:pointer;font-size:var(--text-body-sm);">';
|
||||
html += '<input type="checkbox" data-ws-status="' + escapeHtml(s.key) + '" ' + checked + ' style="width:auto;" />';
|
||||
html += '<span>' + escapeHtml(s.label) + '</span>';
|
||||
html += '</label>';
|
||||
});
|
||||
html += '</div>';
|
||||
|
||||
// Actions
|
||||
html += '<div style="border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-3);background:var(--color-surface-2);">';
|
||||
html += '<h4 style="margin:0 0 var(--space-3);font-size:var(--text-body-sm);color:var(--color-text-primary);">Acciones permitidas en Taller</h4>';
|
||||
(_workshopSchema.actions || []).forEach(function(a) {
|
||||
var checked = visibleActions.indexOf(a.key) !== -1 ? 'checked' : '';
|
||||
html += '<label style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-2);cursor:pointer;font-size:var(--text-body-sm);">';
|
||||
html += '<input type="checkbox" data-ws-action="' + escapeHtml(a.key) + '" ' + checked + ' style="width:auto;" />';
|
||||
html += '<span>' + escapeHtml(a.label) + '</span>';
|
||||
html += '</label>';
|
||||
});
|
||||
html += '</div>';
|
||||
|
||||
html += '</div>';
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
async function saveWorkshopPermissions() {
|
||||
var roleSel = document.getElementById('cfg-perm-role');
|
||||
var status = document.getElementById('workshop-permissions-status');
|
||||
if (!roleSel || !roleSel.value) {
|
||||
if (status) status.textContent = 'Selecciona un rol';
|
||||
return;
|
||||
}
|
||||
var role = roleSel.value;
|
||||
var statuses = [];
|
||||
var actions = [];
|
||||
document.querySelectorAll('#workshop-permissions-container input[data-ws-status]').forEach(function(cb) {
|
||||
if (cb.checked) statuses.push(cb.dataset.wsStatus);
|
||||
});
|
||||
document.querySelectorAll('#workshop-permissions-container input[data-ws-action]').forEach(function(cb) {
|
||||
if (cb.checked) actions.push(cb.dataset.wsAction);
|
||||
});
|
||||
var payload = { roles: {} };
|
||||
payload.roles[role] = { statuses: statuses, actions: actions };
|
||||
try {
|
||||
var res = await fetch(API + '/role-permissions/workshop', {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
var data = await res.json().catch(function() { return { error: res.statusText }; });
|
||||
if (!res.ok) throw new Error(data.error || 'Error al guardar');
|
||||
_workshopPermissions[role] = { statuses: statuses, actions: actions };
|
||||
if (status) status.textContent = 'Permisos de Taller guardados';
|
||||
setTimeout(function() { if (status) status.textContent = ''; }, 4000);
|
||||
} catch (e) {
|
||||
if (status) status.textContent = e.message;
|
||||
toast(e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function switchPermTab(tab) {
|
||||
_currentPermTab = tab;
|
||||
document.querySelectorAll('#perm-panel-modules, #perm-panel-workshop').forEach(function(el) { el.style.display = 'none'; el.classList.remove('is-active'); });
|
||||
document.querySelectorAll('#tab-perm-modules, #tab-perm-workshop').forEach(function(el) { el.classList.remove('is-active'); });
|
||||
document.getElementById('perm-panel-' + tab).style.display = 'block';
|
||||
document.getElementById('perm-panel-' + tab).classList.add('is-active');
|
||||
document.getElementById('tab-perm-' + tab).classList.add('is-active');
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Init
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1172,7 +1278,10 @@ const Config = (() => {
|
||||
loadAllowedBrands();
|
||||
loadModules();
|
||||
loadReceiptConfig();
|
||||
if (isAdmin) loadRolePermissions();
|
||||
if (isAdmin) {
|
||||
loadRolePermissions();
|
||||
loadWorkshopPermissions();
|
||||
}
|
||||
|
||||
// Activate default or stored tab
|
||||
var defaultTab = 'general';
|
||||
@@ -1203,7 +1312,8 @@ const Config = (() => {
|
||||
loadModules, saveModules,
|
||||
loadReceiptConfig, saveReceiptConfig, handleReceiptLogo, removeReceiptLogo,
|
||||
openModal, closeModal, openBranchModal, editBranch,
|
||||
switchTab, loadRolePermissions, renderRolePermissions, saveRolePermissions
|
||||
switchTab, loadRolePermissions, renderRolePermissions, saveRolePermissions,
|
||||
loadWorkshopPermissions, renderWorkshopPermissions, saveWorkshopPermissions, switchPermTab
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user