diff --git a/pos/blueprints/config_bp.py b/pos/blueprints/config_bp.py index 61252f9..1adfb6d 100644 --- a/pos/blueprints/config_bp.py +++ b/pos/blueprints/config_bp.py @@ -577,3 +577,34 @@ def update_whatsapp_config(): conn.close() return jsonify({'message': 'WhatsApp configuration updated'}) + + +@config_bp.route('/onboarding-status', methods=['GET']) +@require_auth('pos.view') +def get_onboarding_status(): + """Check if tenant onboarding wizard has been completed.""" + conn = get_tenant_conn(g.tenant_id) + cur = conn.cursor() + cur.execute("SELECT value FROM tenant_config WHERE key = 'onboarding_completed'") + row = cur.fetchone() + cur.close() + conn.close() + return jsonify({'completed': row[0] == 'true' if row else False}) + + +@config_bp.route('/onboarding-status', methods=['POST']) +@require_auth('pos.view') +def set_onboarding_status(): + """Mark tenant onboarding wizard as completed.""" + data = request.get_json() or {} + completed = 'true' if data.get('completed') else 'false' + conn = get_tenant_conn(g.tenant_id) + cur = conn.cursor() + cur.execute(""" + INSERT INTO tenant_config (key, value) VALUES (%s, %s) + ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value + """, ('onboarding_completed', completed)) + conn.commit() + cur.close() + conn.close() + return jsonify({'completed': completed == 'true'}) diff --git a/pos/static/css/config.css b/pos/static/css/config.css index 2a46c55..37e298a 100644 --- a/pos/static/css/config.css +++ b/pos/static/css/config.css @@ -1029,6 +1029,7 @@ display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--space-4); + min-width: 0; } .device-card { @@ -1042,7 +1043,10 @@ box-shadow: var(--shadow-sm); transition: var(--transition-normal); min-width: 0; + max-width: 100%; + overflow: hidden; overflow-wrap: break-word; + word-break: break-word; } [data-theme="modern"] .device-card { diff --git a/pos/static/js/onboarding.js b/pos/static/js/onboarding.js index c757df4..f280018 100644 --- a/pos/static/js/onboarding.js +++ b/pos/static/js/onboarding.js @@ -1,18 +1,41 @@ /* ========================================================================== NEXUS POS — Onboarding Wizard for New Tenants Shows a step-by-step setup guide on first login. - Persists completion in localStorage('pos_onboarding_done'). + Persists completion in localStorage('pos_onboarding_done') and server. ========================================================================== */ (function () { 'use strict'; /* ------------------------------------------------------------------ - GUARD — skip if already completed + GUARD — skip if already completed locally (fast path) ------------------------------------------------------------------ */ if (localStorage.getItem('pos_onboarding_done') === 'true') return; + /* ------------------------------------------------------------------ + CHECK SERVER — if completed on server, cache locally and skip + ------------------------------------------------------------------ */ + + function checkServerAndMaybeInit() { + var token = ''; + try { token = localStorage.getItem('pos_token') || ''; } catch (e) {} + fetch('/pos/api/config/onboarding-status', { + headers: token ? { 'Authorization': 'Bearer ' + token } : {} + }).then(function (r) { + if (!r.ok) return null; + return r.json(); + }).then(function (data) { + if (data && data.completed) { + localStorage.setItem('pos_onboarding_done', 'true'); + return; + } + initWizard(); + }).catch(function () { + initWizard(); + }); + } + /* ------------------------------------------------------------------ STATE ------------------------------------------------------------------ */ @@ -335,6 +358,13 @@ function finish() { localStorage.setItem('pos_onboarding_done', 'true'); + var token = ''; + try { token = localStorage.getItem('pos_token') || ''; } catch (e) {} + fetch('/pos/api/config/onboarding-status', { + method: 'POST', + headers: token ? { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' } : { 'Content-Type': 'application/json' }, + body: JSON.stringify({ completed: true }) + }).catch(function () {}); if (overlay && overlay.parentNode) { overlay.style.opacity = '0'; overlay.style.transition = 'opacity var(--duration-normal) var(--ease-in)'; @@ -439,11 +469,15 @@ renderCurrentStep(); } - /* Wait for DOM */ - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', init); - } else { + function initWizard() { init(); } + /* Wait for DOM, then check server before showing wizard */ + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', checkServerAndMaybeInit); + } else { + checkServerAndMaybeInit(); + } + })(); diff --git a/pos/templates/catalog.html b/pos/templates/catalog.html index dda4078..a9c3d11 100644 --- a/pos/templates/catalog.html +++ b/pos/templates/catalog.html @@ -276,10 +276,10 @@

Catalogo por Marca

-
+
- -
+
+
@@ -292,7 +292,7 @@ - + - +