fix: header 3-col layout, shopping_name migration, demo mode UI, kiosk buttons left
Header layout:
- Redesign header-content as 3-column flex (left / center / right)
- Add #header-left div: dedicated slot for kiosk buttons (empty by default)
- header-title: flex-shrink auto, no more position:absolute centering hack
- header-actions: flex:1 1 0 + justify-content:flex-end (right)
- header-left: flex:1 1 0 (left) — equal width balances the title visually
Kiosk exit/refresh buttons:
- _injectKioskOverlay() now appends to #header-left instead of
insertBefore(firstChild) — buttons appear on LEFT, not mixed with center
DB migration:
- Add shopping_name TEXT DEFAULT '' to CREATE TABLE products schema
- Add ALTER TABLE migration in migrateDB() for existing databases
- Avoids repeated ALTER TABLE in seed code on every request
Demo mode UI:
- _applyDemoModeUI(): hides ⚙️ settings nav button in demo mode
- Suppresses first-run setup wizard when _demoMode === true
- Shows a small DEMO badge in header-left
- Called from both syncSettingsFromDB() and _initApp()
This commit is contained in:
@@ -39,6 +39,7 @@ function initializeDB(PDO $db): void {
|
||||
unit TEXT DEFAULT 'pz',
|
||||
default_quantity REAL DEFAULT 1,
|
||||
notes TEXT DEFAULT '',
|
||||
shopping_name TEXT DEFAULT '',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
@@ -80,6 +81,9 @@ function migrateDB(PDO $db): void {
|
||||
if (!in_array('package_unit', $colNames)) {
|
||||
$db->exec("ALTER TABLE products ADD COLUMN package_unit TEXT DEFAULT ''");
|
||||
}
|
||||
if (!in_array('shopping_name', $colNames)) {
|
||||
$db->exec("ALTER TABLE products ADD COLUMN shopping_name TEXT DEFAULT ''");
|
||||
}
|
||||
|
||||
// Migrate transactions CHECK constraint to allow 'waste' type
|
||||
$sql = $db->query("SELECT sql FROM sqlite_master WHERE type='table' AND name='transactions'")->fetchColumn();
|
||||
|
||||
+17
-7
@@ -105,17 +105,23 @@ body {
|
||||
}
|
||||
|
||||
.header-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* Left zone: kiosk-only buttons (exit, refresh) — empty on normal browser */
|
||||
.header-left {
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
flex: 0 1 auto;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
@@ -123,9 +129,10 @@ body {
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
pointer-events: auto;
|
||||
/* prevent title overlapping action buttons on small screens */
|
||||
max-width: calc(100% - 200px);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-version {
|
||||
@@ -209,9 +216,12 @@ body {
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* ── Smart Scale status indicator in header ─────────────────────────── */
|
||||
|
||||
+28
-6
@@ -110,6 +110,26 @@ function _updateGeminiButtonState() {
|
||||
}
|
||||
}
|
||||
|
||||
function _applyDemoModeUI() {
|
||||
if (!_demoMode) return;
|
||||
// Hide the settings ⚙️ nav button
|
||||
document.querySelectorAll('.nav-btn[data-page="settings"]').forEach(btn => {
|
||||
btn.style.display = 'none';
|
||||
});
|
||||
// Prevent the setup wizard from showing
|
||||
const wizard = document.getElementById('setup-wizard');
|
||||
if (wizard) wizard.style.display = 'none';
|
||||
// Optionally show a small demo badge in the header
|
||||
const headerLeft = document.getElementById('header-left');
|
||||
if (headerLeft && !document.getElementById('_demo_badge')) {
|
||||
const badge = document.createElement('span');
|
||||
badge.id = '_demo_badge';
|
||||
badge.textContent = 'DEMO';
|
||||
badge.style.cssText = 'font-size:0.6rem;font-weight:800;letter-spacing:0.08em;background:rgba(251,191,36,0.35);color:#fef3c7;border:1px solid rgba(251,191,36,0.5);border-radius:4px;padding:2px 5px;white-space:nowrap;';
|
||||
headerLeft.appendChild(badge);
|
||||
}
|
||||
}
|
||||
|
||||
function _checkWebappUpdate() {
|
||||
const STORAGE_KEY = '_evershelf_update_checked_at';
|
||||
const SEEN_KEY = '_evershelf_update_seen_ts';
|
||||
@@ -1812,6 +1832,7 @@ async function syncSettingsFromDB() {
|
||||
_geminiAvailable = !!(serverSettings.gemini_key_set);
|
||||
_demoMode = !!serverSettings.demo_mode;
|
||||
_updateGeminiButtonState();
|
||||
_applyDemoModeUI();
|
||||
const s = getSettings();
|
||||
const serverKeys = ['default_persons','pref_veloce','pref_pocafame','pref_scadenze',
|
||||
'pref_healthy','pref_opened','pref_zerowaste','dietary','appliances',
|
||||
@@ -1980,18 +2001,18 @@ async function loadSettingsUI() {
|
||||
}
|
||||
|
||||
// ── Kiosk overlay: X (close) + ↻ (refresh) buttons ───────────────────
|
||||
// Injected inside the header-content div, BEFORE the title.
|
||||
// Injected into #header-left (left zone of the 3-column header).
|
||||
// Only shown when _kioskBridge JS interface is available (Android WebView).
|
||||
function _injectKioskOverlay() {
|
||||
if (typeof _kioskBridge === 'undefined') return;
|
||||
if (document.getElementById('_kiosk_overlay')) return;
|
||||
|
||||
const headerContent = document.querySelector('.header-content');
|
||||
if (!headerContent) return;
|
||||
const headerLeft = document.getElementById('header-left');
|
||||
if (!headerLeft) return;
|
||||
|
||||
const wrap = document.createElement('div');
|
||||
wrap.id = '_kiosk_overlay';
|
||||
wrap.style.cssText = 'display:flex;gap:6px;align-items:center;margin-right:8px;flex-shrink:0;';
|
||||
wrap.style.cssText = 'display:flex;gap:6px;align-items:center;';
|
||||
|
||||
const btnStyle = 'background:rgba(255,255,255,0.2);border:none;color:#fff;width:34px;height:34px;border-radius:50%;font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:transparent;touch-action:manipulation;';
|
||||
|
||||
@@ -2019,7 +2040,7 @@ function _injectKioskOverlay() {
|
||||
|
||||
wrap.appendChild(exitBtn);
|
||||
wrap.appendChild(refBtn);
|
||||
headerContent.insertBefore(wrap, headerContent.firstChild);
|
||||
headerLeft.appendChild(wrap);
|
||||
}
|
||||
|
||||
function renderAppliances(appliances) {
|
||||
@@ -11926,8 +11947,9 @@ async function _initApp() {
|
||||
_geminiAvailable = !!(serverSettings.gemini_key_set);
|
||||
_demoMode = !!serverSettings.demo_mode;
|
||||
_updateGeminiButtonState();
|
||||
_applyDemoModeUI();
|
||||
const missing = _getMissingSetupSteps(serverSettings);
|
||||
if (missing.length > 0) {
|
||||
if (missing.length > 0 && !_demoMode) {
|
||||
showSetupWizard(missing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
<!-- Top Header -->
|
||||
<header class="app-header">
|
||||
<div class="header-content">
|
||||
<div class="header-left" id="header-left"></div>
|
||||
<h1 class="header-title" onclick="showPage('dashboard')"><span data-i18n="nav.title">🏠 EverShelf</span><span class="header-version">v1.6.0</span></h1>
|
||||
<div class="header-actions">
|
||||
<span id="scale-status-indicator" class="scale-status-indicator scale-status-disconnected" style="display:none" data-i18n-title="scale.status_disconnected" title="⚖️ Bilancia">⚖️</span>
|
||||
|
||||
Reference in New Issue
Block a user