feat(permissions): granular module permissions + workshop permissions matrix
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled

This commit is contained in:
2026-07-03 07:55:17 +00:00
parent 4718a09f15
commit 6e85497e42
7 changed files with 484 additions and 136 deletions

View File

@@ -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
};
})();