fix(config): prevent card text overflow; fix(onboarding): persist completion server-side

Cards/Grid:
- Add min-width:0 to .device-grid to prevent grid overflow
- Add max-width:100%, overflow:hidden, word-break:break-word to .device-card
- Add min-width:0 and overflow-wrap to .device-card__body
- Bump config.css cache-bust to v=2

Onboarding:
- Add GET/POST /pos/api/config/onboarding-status endpoints in config_bp.py
- onboarding.js now checks server first before showing wizard
- On finish, POSTs completion to server (tenant_config table)
- Falls back to localStorage for fast path and offline resilience
- Bump onboarding.js cache-bust to v=2 in catalog.html
This commit is contained in:
2026-05-18 07:31:31 +00:00
parent dbf45e374b
commit 0b1dc89faf
4 changed files with 80 additions and 11 deletions

View File

@@ -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 {

View File

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