feat: module toggles in POS config and Instance Manager
- Add GET/PUT /pos/api/config/modules endpoints in POS config_bp.py - Update sidebar.js to filter nav items based on enabled modules - Add Modules section to POS config.html with toggles for WhatsApp, Marketplace, MercadoLibre - Add module load/save logic to POS config.js - Preload modules in app-init.js for sidebar caching - Add tenant module management to Instance Manager - get_tenant_modules / update_tenant_modules in tenant_service.py - GET/PUT /api/tenants/<id>/modules endpoints in tenants_bp.py - Add modules modal to manager index.html - Add module editing UI and logic to manager.js - Add toggle-switch CSS to manager.css
This commit is contained in:
@@ -180,4 +180,18 @@
|
||||
permissions: payload.permissions || []
|
||||
};
|
||||
|
||||
// ─── Preload enabled modules for sidebar filtering ───
|
||||
try {
|
||||
fetch('/pos/api/config/modules', {
|
||||
headers: { 'Authorization': 'Bearer ' + token }
|
||||
}).then(function(r) {
|
||||
if (r.ok) return r.json();
|
||||
}).then(function(data) {
|
||||
if (data) {
|
||||
localStorage.setItem('pos_modules', JSON.stringify(data));
|
||||
window.POS_USER.modules = data;
|
||||
}
|
||||
}).catch(function() {});
|
||||
} catch(e) {}
|
||||
|
||||
})();
|
||||
|
||||
@@ -689,6 +689,53 @@ const Config = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Modules / Integrations
|
||||
// -------------------------------------------------------------------------
|
||||
async function loadModules() {
|
||||
try {
|
||||
var res = await fetch(API + '/modules', { headers: headers() });
|
||||
if (!res.ok) return;
|
||||
var data = await res.json();
|
||||
var cbWa = document.getElementById('cfg-module-whatsapp');
|
||||
var cbMp = document.getElementById('cfg-module-marketplace');
|
||||
var cbMeli = document.getElementById('cfg-module-meli');
|
||||
if (cbWa) cbWa.checked = data.whatsapp !== false;
|
||||
if (cbMp) cbMp.checked = data.marketplace !== false;
|
||||
if (cbMeli) cbMeli.checked = data.meli !== false;
|
||||
localStorage.setItem('pos_modules', JSON.stringify(data));
|
||||
} catch (e) {
|
||||
console.error('Config.loadModules:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveModules() {
|
||||
var btn = event.target;
|
||||
if (btn) { btn.disabled = true; btn.textContent = 'Guardando...'; }
|
||||
try {
|
||||
var data = {
|
||||
whatsapp: document.getElementById('cfg-module-whatsapp').checked,
|
||||
marketplace: document.getElementById('cfg-module-marketplace').checked,
|
||||
meli: document.getElementById('cfg-module-meli').checked,
|
||||
};
|
||||
var res = await fetch(API + '/modules', {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
if (!res.ok) {
|
||||
var err = await res.json().catch(function() { return { error: res.statusText }; });
|
||||
throw new Error(err.error || 'Save failed');
|
||||
}
|
||||
localStorage.setItem('pos_modules', JSON.stringify(data));
|
||||
toast('Módulos actualizados');
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
} finally {
|
||||
if (btn) { btn.disabled = false; btn.textContent = 'Guardar módulos'; }
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Init
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -744,6 +791,7 @@ const Config = (() => {
|
||||
loadCurrency();
|
||||
loadVehicleCompatSource();
|
||||
loadAllowedBrands();
|
||||
loadModules();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
@@ -753,6 +801,7 @@ const Config = (() => {
|
||||
loadBranches, loadEmployees, saveBranch, saveEmployee, editEmployee,
|
||||
loadBusiness, saveBusiness, saveTaxParams,
|
||||
loadCurrency, saveCurrency,
|
||||
loadModules, saveModules,
|
||||
openModal, closeModal
|
||||
};
|
||||
// Register Cmd+K items
|
||||
|
||||
@@ -17,6 +17,16 @@
|
||||
var currentTheme = localStorage.getItem('pos_theme') || 'industrial';
|
||||
var currentLang = localStorage.getItem('pos_lang') || 'es';
|
||||
|
||||
var modules = {};
|
||||
try {
|
||||
modules = JSON.parse(localStorage.getItem('pos_modules') || '{}');
|
||||
} catch(e) { modules = {}; }
|
||||
|
||||
function moduleEnabled(key) {
|
||||
// Default to true if not configured yet
|
||||
return modules[key] !== false;
|
||||
}
|
||||
|
||||
var navSections = [
|
||||
{ label: _t('nav_main'), items: [
|
||||
{ name: _t('dashboard'), href: '/pos/dashboard', icon: '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>' },
|
||||
@@ -28,14 +38,14 @@
|
||||
{ label: _t('nav_management'), items: [
|
||||
{ name: _t('customers'), href: '/pos/customers', icon: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>' },
|
||||
{ name: 'Cotizaciones', href: '/pos/quotations', icon: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="15" x2="15" y2="15"/><line x1="12" y1="12" x2="12" y2="18"/>' },
|
||||
{ name: 'Marketplace', href: '/pos/marketplace', icon: '<circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>' },
|
||||
{ name: 'MercadoLibre', href: '/pos/marketplace-external', icon: '<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>' },
|
||||
moduleEnabled('marketplace') ? { name: 'Marketplace', href: '/pos/marketplace', icon: '<circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>' } : null,
|
||||
moduleEnabled('meli') ? { name: 'MercadoLibre', href: '/pos/marketplace-external', icon: '<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>' } : null,
|
||||
{ name: _t('invoicing'), href: '/pos/invoicing', icon: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>' },
|
||||
{ name: _t('accounting'), href: '/pos/accounting', icon: '<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>' },
|
||||
{ name: _t('reports'), href: '/pos/reports', icon: '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>' },
|
||||
{ name: _t('fleet'), href: '/pos/fleet', icon: '<path d="M1 13h22M1 13l2-6h6l2 6M9 7h6l2 6M15 13l2-6M5 17a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM19 17a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>' },
|
||||
{ name: _t('whatsapp'), href: '/pos/whatsapp', icon: '<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>' },
|
||||
]},
|
||||
moduleEnabled('whatsapp') ? { name: _t('whatsapp'), href: '/pos/whatsapp', icon: '<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>' } : null,
|
||||
].filter(Boolean)},
|
||||
{ label: _t('nav_system'), items: [
|
||||
{ name: _t('config'), href: '/pos/config', icon: '<circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/>' },
|
||||
]},
|
||||
|
||||
@@ -179,7 +179,7 @@ function cacheFirst(request) {
|
||||
return caches.match(request).then(function (cached) {
|
||||
if (cached) {
|
||||
fetch(request).then(function (response) {
|
||||
if (response && response.status === 200) {
|
||||
if (response && response.status === 200 && request.method === 'GET') {
|
||||
caches.open(CACHE_NAME).then(function (cache) {
|
||||
cache.put(request, response);
|
||||
});
|
||||
@@ -188,7 +188,7 @@ function cacheFirst(request) {
|
||||
return cached;
|
||||
}
|
||||
return fetch(request).then(function (response) {
|
||||
if (response && response.status === 200) {
|
||||
if (response && response.status === 200 && request.method === 'GET') {
|
||||
var clone = response.clone();
|
||||
caches.open(CACHE_NAME).then(function (cache) {
|
||||
cache.put(request, clone);
|
||||
@@ -201,7 +201,7 @@ function cacheFirst(request) {
|
||||
|
||||
function networkFirst(request) {
|
||||
return fetch(request).then(function (response) {
|
||||
if (response && response.status === 200) {
|
||||
if (response && response.status === 200 && request.method === 'GET') {
|
||||
var clone = response.clone();
|
||||
caches.open(CACHE_NAME).then(function (cache) {
|
||||
cache.put(request, clone);
|
||||
|
||||
Reference in New Issue
Block a user