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:
2026-05-28 00:21:52 +00:00
parent 999591e248
commit 718fa06888
26 changed files with 2614 additions and 429 deletions

View File

@@ -16,6 +16,7 @@ sys.path.insert(0, os.path.join(_base, '..', 'pos')) # pos/ for auth, services
sys.path.insert(0, os.path.join(_base, '..')) # root config.py (has DB_URL)
from config import DB_URL
from auth import hash_password, check_password, create_access_token, create_refresh_token, decode_token, require_auth
from tenant_db import get_tenant_conn
from services.translations import translate_part_name, translate_category
sys.path.insert(0, os.path.join(_base, '..', 'pos'))
@@ -4628,6 +4629,76 @@ def part_aftermarket(part_id):
session.close()
# ============================================================================
# Tenant Module Config Endpoints
# ============================================================================
MODULE_CONFIG_KEYS = [
'whatsapp_enabled',
'marketplace_enabled',
'meli_enabled',
]
@app.route('/api/admin/tenants')
def api_admin_tenants():
session = Session()
try:
rows = session.execute(text(
"SELECT id, name, db_name, is_active, is_seller FROM tenants WHERE is_active = true ORDER BY id"
)).mappings().all()
return jsonify({'tenants': [dict(r) for r in rows]})
except Exception as e:
return jsonify({'error': str(e)}), 500
finally:
session.close()
@app.route('/api/admin/tenants/<int:tenant_id>/modules')
def api_admin_tenant_modules(tenant_id):
try:
conn = get_tenant_conn(tenant_id)
cur = conn.cursor()
result = {}
for key in MODULE_CONFIG_KEYS:
cur.execute("SELECT value FROM tenant_config WHERE key = %s", (key,))
row = cur.fetchone()
result[key] = (row[0] or '').lower() == 'true' if row else False
cur.close()
conn.close()
return jsonify(result)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/admin/tenants/<int:tenant_id>/modules', methods=['PUT'])
def api_admin_tenant_modules_update(tenant_id):
data = request.get_json() or {}
if not data:
return jsonify({'error': 'No data provided'}), 400
try:
conn = get_tenant_conn(tenant_id)
cur = conn.cursor()
for key, value in data.items():
if key not in MODULE_CONFIG_KEYS:
continue
cur.execute(
"""
INSERT INTO tenant_config (key, value, updated_at)
VALUES (%s, %s, NOW())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()
""",
(key, 'true' if value else 'false'),
)
conn.commit()
cur.close()
conn.close()
return jsonify({'ok': True})
except Exception as e:
return jsonify({'error': str(e)}), 500
# ============================================================================
# Static files from dashboard root (CSS/JS/HTML)
# ============================================================================