feat: v1.1.0 - Docker, i18n, setup wizard, rate limiting, OpenAPI
New features: - Docker support (Dockerfile + docker-compose.yml) - GitHub Actions CI pipeline (PHP lint, JS lint, Docker build, i18n validation) - Internationalization system with 3 languages (it, en, de) and 347 translation keys - First-run setup wizard (4-step configuration) - File-based API rate limiting (120/15/5 req/min tiers) - OpenAPI 3.1.0 specification for all 43 API endpoints - CONTRIBUTING.md with translation and development guide - Screenshots directory placeholder Modified: - README.md: Docker badges, install instructions, translations section - api/index.php: rate limiting middleware - assets/js/app.js: i18n system, setup wizard, t() function - assets/css/style.css: setup wizard styles - index.html: data-i18n attributes, setup wizard overlay, language settings - .gitignore: rate_limits exclusion
This commit is contained in:
+94
-65
@@ -20,12 +20,12 @@
|
||||
<!-- Top Header -->
|
||||
<header class="app-header">
|
||||
<div class="header-content">
|
||||
<h1 class="header-title" onclick="showPage('dashboard')">🏠 Dispensa</h1>
|
||||
<h1 class="header-title" onclick="showPage('dashboard')" data-i18n="nav.title">🏠 Dispensa</h1>
|
||||
<div class="header-actions">
|
||||
<button class="header-scan-btn header-gemini-btn" onclick="showPage('chat')" title="Chat con Gemini">
|
||||
<button class="header-scan-btn header-gemini-btn" onclick="showPage('chat')" title="Chat con Gemini" data-i18n-title="chat.title">
|
||||
<svg class="gemini-icon" viewBox="0 0 24 24" width="28" height="28" fill="white"><path d="M12 0C12 6.627 6.627 12 0 12c6.627 0 12 5.373 12 12 0-6.627 5.373-12 12-12-6.627 0-12-5.373-12-12z"/></svg>
|
||||
</button>
|
||||
<button class="header-scan-btn" id="btn-header-scan" title="Scansiona prodotto (tieni premuto per modalità spesa)">
|
||||
<button class="header-scan-btn" id="btn-header-scan" title="Scansiona prodotto (tieni premuto per modalità spesa)" data-i18n-title="scan.hint">
|
||||
📷
|
||||
</button>
|
||||
</div>
|
||||
@@ -41,22 +41,22 @@
|
||||
<div class="stat-card" onclick="showPage('inventory', 'dispensa')">
|
||||
<span class="stat-icon">🗄️</span>
|
||||
<span class="stat-value" id="stat-dispensa">0</span>
|
||||
<span class="stat-label">Dispensa</span>
|
||||
<span class="stat-label" data-i18n="locations.dispensa">Dispensa</span>
|
||||
</div>
|
||||
<div class="stat-card" onclick="showPage('inventory', 'frigo')">
|
||||
<span class="stat-icon">🧊</span>
|
||||
<span class="stat-value" id="stat-frigo">0</span>
|
||||
<span class="stat-label">Frigo</span>
|
||||
<span class="stat-label" data-i18n="locations.frigo">Frigo</span>
|
||||
</div>
|
||||
<div class="stat-card" onclick="showPage('inventory', 'freezer')">
|
||||
<span class="stat-icon">❄️</span>
|
||||
<span class="stat-value" id="stat-freezer">0</span>
|
||||
<span class="stat-label">Freezer</span>
|
||||
<span class="stat-label" data-i18n="locations.freezer">Freezer</span>
|
||||
</div>
|
||||
<div class="stat-card" onclick="showPage('shopping')">
|
||||
<span class="stat-icon">🛒</span>
|
||||
<span class="stat-value" id="stat-spesa">-</span>
|
||||
<span class="stat-label">Spesa</span>
|
||||
<span class="stat-label" data-i18n="nav.shopping">Spesa</span>
|
||||
<span class="stat-urgent" id="stat-urgent" style="display:none"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,39 +65,39 @@
|
||||
<div class="quick-recipe-bar" id="quick-recipe-bar" style="display:none">
|
||||
<button class="btn-quick-recipe" onclick="quickRecipeSuggestion()">
|
||||
<span>🍳</span>
|
||||
<span class="quick-recipe-text">Ricetta veloce con prodotti in scadenza</span>
|
||||
<span class="quick-recipe-text" data-i18n="dashboard.quick_recipe">🍳 Ricetta veloce con prodotti in scadenza</span>
|
||||
<span>→</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Alert for expired items (on top) -->
|
||||
<div class="alert-section alert-danger" id="alert-expired" style="display:none">
|
||||
<h3>🚫 Scaduti</h3>
|
||||
<h3 data-i18n="dashboard.expired_title">🚫 Scaduti</h3>
|
||||
<div id="expired-list"></div>
|
||||
</div>
|
||||
<!-- Alert for soonest expiring items -->
|
||||
<div class="alert-section" id="alert-expiring" style="display:none">
|
||||
<h3>⏰ Prossime Scadenze</h3>
|
||||
<h3 data-i18n="dashboard.expiring_title">⏰ Prossime Scadenze</h3>
|
||||
<div id="expiring-list"></div>
|
||||
</div>
|
||||
|
||||
<!-- Waste vs consumption mini chart -->
|
||||
<div class="waste-chart-section" id="waste-chart-section" style="display:none">
|
||||
<h3>📊 Ultimi 30 giorni</h3>
|
||||
<h3 data-i18n="dashboard.stats_period">📊 Ultimi 30 giorni</h3>
|
||||
<div class="waste-chart-bar" id="waste-chart-bar"></div>
|
||||
<div class="waste-chart-legend" id="waste-chart-legend"></div>
|
||||
</div>
|
||||
|
||||
<!-- Opened (partially used) products -->
|
||||
<div class="alert-section alert-opened" id="alert-opened" style="display:none">
|
||||
<h3>📦 Prodotti Aperti</h3>
|
||||
<h3 data-i18n="dashboard.opened_title">📦 Prodotti Aperti</h3>
|
||||
<div id="opened-list"></div>
|
||||
</div>
|
||||
|
||||
<!-- Review suspicious quantities -->
|
||||
<div class="alert-section alert-review" id="alert-review" style="display:none">
|
||||
<h3>🔍 Da revisionare</h3>
|
||||
<p class="review-hint">Quantità che sembrano anomale. Conferma se corrette o modifica.</p>
|
||||
<h3 data-i18n="dashboard.review_title">🔍 Da revisionare</h3>
|
||||
<p class="review-hint" data-i18n="dashboard.review_hint">Quantità che sembrano anomale. Conferma se corrette o modifica.</p>
|
||||
<div id="review-list"></div>
|
||||
</div>
|
||||
|
||||
@@ -106,18 +106,18 @@
|
||||
<!-- ===== INVENTORY LIST ===== -->
|
||||
<section class="page" id="page-inventory">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="showPage('dashboard')">← Indietro</button>
|
||||
<h2 id="inventory-title">Dispensa</h2>
|
||||
<button class="back-btn" onclick="showPage('dashboard')" data-i18n="btn.back">← Indietro</button>
|
||||
<h2 id="inventory-title" data-i18n="inventory.title">Dispensa</h2>
|
||||
</div>
|
||||
<div class="location-tabs" id="location-tabs">
|
||||
<button class="tab active" onclick="filterLocation('')" data-loc="">Tutti</button>
|
||||
<button class="tab active" onclick="filterLocation('')" data-loc="" data-i18n="inventory.filter_all">Tutti</button>
|
||||
<button class="tab" onclick="filterLocation('dispensa')" data-loc="dispensa">🗄️ Dispensa</button>
|
||||
<button class="tab" onclick="filterLocation('frigo')" data-loc="frigo">🧊 Frigo</button>
|
||||
<button class="tab" onclick="filterLocation('freezer')" data-loc="freezer">❄️ Freezer</button>
|
||||
<button class="tab" onclick="filterLocation('altro')" data-loc="altro">📦 Altro</button>
|
||||
</div>
|
||||
<div class="search-bar">
|
||||
<input type="text" id="inventory-search" placeholder="🔍 Cerca prodotto..." oninput="filterInventory()">
|
||||
<input type="text" id="inventory-search" placeholder="🔍 Cerca prodotto..." oninput="filterInventory()" data-i18n-placeholder="inventory.search_placeholder">
|
||||
</div>
|
||||
<div class="inventory-list" id="inventory-list"></div>
|
||||
</section>
|
||||
@@ -125,15 +125,15 @@
|
||||
<!-- ===== SCAN PAGE ===== -->
|
||||
<section class="page" id="page-scan">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="stopScanner(); showPage('dashboard')">← Indietro</button>
|
||||
<h2>Scansiona Prodotto</h2>
|
||||
<button class="back-btn" onclick="stopScanner(); showPage('dashboard')" data-i18n="btn.back">← Indietro</button>
|
||||
<h2 data-i18n="scan.title">Scansiona Prodotto</h2>
|
||||
</div>
|
||||
<div class="spesa-mode-banner" id="spesa-mode-banner" style="display:none">
|
||||
<div class="spesa-banner-left">
|
||||
<span>🛒 Modalità Spesa</span>
|
||||
<span data-i18n="scan.mode_shopping">🛒 Modalità Spesa</span>
|
||||
<span class="spesa-stat"></span>
|
||||
</div>
|
||||
<button class="btn btn-small" onclick="endSpesaMode()">✅ Fine spesa</button>
|
||||
<button class="btn btn-small" onclick="endSpesaMode()" data-i18n="scan.mode_shopping_end">✅ Fine spesa</button>
|
||||
</div>
|
||||
<div class="scan-container">
|
||||
<div class="scanner-viewport" id="scanner-viewport">
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="scan-result" id="scan-result" style="display:none"></div>
|
||||
<div class="barcode-manual-entry">
|
||||
<div class="barcode-input-row">
|
||||
<input type="text" id="manual-barcode-input" class="form-input" placeholder="Inserisci codice a barre..." inputmode="numeric" pattern="[0-9]*" onkeydown="if(event.key==='Enter')submitManualBarcode()">
|
||||
<input type="text" id="manual-barcode-input" class="form-input" placeholder="Inserisci codice a barre..." inputmode="numeric" pattern="[0-9]*" onkeydown="if(event.key==='Enter')submitManualBarcode()" data-i18n-placeholder="scan.barcode_placeholder">
|
||||
<button class="btn btn-primary" onclick="submitManualBarcode()">🔍 Cerca</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -504,11 +504,11 @@
|
||||
<!-- ===== RECIPE PAGE ===== -->
|
||||
<section class="page" id="page-recipe">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="showPage('dashboard')">← Indietro</button>
|
||||
<h2>🍳 Ricette</h2>
|
||||
<button class="back-btn" onclick="showPage('dashboard')" data-i18n="btn.back">← Indietro</button>
|
||||
<h2 data-i18n="recipes.title">🍳 Ricette</h2>
|
||||
</div>
|
||||
<div class="recipe-page-container">
|
||||
<button class="btn btn-large btn-success full-width" onclick="openRecipeDialog()">
|
||||
<button class="btn btn-large btn-success full-width" onclick="openRecipeDialog()" data-i18n="recipes.generate">
|
||||
✨ Genera nuova ricetta
|
||||
</button>
|
||||
<div id="recipe-archive" class="recipe-archive"></div>
|
||||
@@ -518,12 +518,12 @@
|
||||
<!-- ===== SHOPPING LIST (BRING!) PAGE ===== -->
|
||||
<section class="page" id="page-shopping">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="showPage('dashboard')">← Indietro</button>
|
||||
<h2>🛒 Lista della Spesa</h2>
|
||||
<button class="back-btn" onclick="showPage('dashboard')" data-i18n="btn.back">← Indietro</button>
|
||||
<h2 data-i18n="shopping.title">🛒 Lista della Spesa</h2>
|
||||
</div>
|
||||
<div class="shopping-container">
|
||||
<div class="bring-status" id="bring-status">
|
||||
<div class="bring-loading">Connessione a Bring!...</div>
|
||||
<div class="bring-loading" data-i18n="shopping.bring_loading">Connessione a Bring!...</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab navigation -->
|
||||
@@ -541,7 +541,7 @@
|
||||
<!-- Price total banner -->
|
||||
<div class="spesa-total-banner" id="spesa-total-banner" style="display:none">
|
||||
<div class="spesa-total-row">
|
||||
<span class="spesa-total-label">💰 Totale stimato</span>
|
||||
<span class="spesa-total-label" data-i18n="shopping.total_label">💰 Totale stimato</span>
|
||||
<span class="spesa-total-value" id="spesa-total-value">€ 0,00</span>
|
||||
</div>
|
||||
<div class="spesa-total-detail" id="spesa-total-detail"></div>
|
||||
@@ -642,10 +642,10 @@
|
||||
<!-- Log Page -->
|
||||
<section id="page-log" class="page">
|
||||
<div class="page-header">
|
||||
<h2>📒 Log Operazioni</h2>
|
||||
<h2 data-i18n="log.title">📒 Log Operazioni</h2>
|
||||
</div>
|
||||
<div id="log-list" class="log-list"></div>
|
||||
<button class="btn btn-secondary full-width mt-2" id="log-load-more" style="display:none" onclick="loadLog(true)">
|
||||
<button class="btn btn-secondary full-width mt-2" id="log-load-more" style="display:none" onclick="loadLog(true)" data-i18n="btn.load_more">
|
||||
Carica altri...
|
||||
</button>
|
||||
</section>
|
||||
@@ -653,8 +653,8 @@
|
||||
<!-- ===== SETTINGS PAGE ===== -->
|
||||
<section class="page" id="page-settings">
|
||||
<div class="page-header">
|
||||
<button class="back-btn" onclick="showPage('dashboard')">← Indietro</button>
|
||||
<h2>⚙️ Configurazione</h2>
|
||||
<button class="back-btn" onclick="showPage('dashboard')" data-i18n="btn.back">← Indietro</button>
|
||||
<h2 data-i18n="settings.title">⚙️ Configurazione</h2>
|
||||
</div>
|
||||
<div class="settings-tabs">
|
||||
<button class="settings-tab active" onclick="switchSettingsTab(this, 'tab-api')" data-tab="tab-api" title="API Keys">🔑</button>
|
||||
@@ -665,14 +665,15 @@
|
||||
<button class="settings-tab" onclick="switchSettingsTab(this, 'tab-spesa')" data-tab="tab-spesa" title="Spesa Online">🛍️</button>
|
||||
<button class="settings-tab" onclick="switchSettingsTab(this, 'tab-camera')" data-tab="tab-camera" title="Fotocamera">📷</button>
|
||||
<button class="settings-tab" onclick="switchSettingsTab(this, 'tab-security')" data-tab="tab-security" title="Sicurezza">🔒</button>
|
||||
<button class="settings-tab" onclick="switchSettingsTab(this, 'tab-tts')" data-tab="tab-tts" title="Voce (TTS)">🔊</button>
|
||||
<button class="settings-tab" onclick="switchSettingsTab(this, 'tab-tts')" data-tab="tab-tts" title="Voce (TTS)" data-i18n-title="settings.tab_tts">🔊</button>
|
||||
<button class="settings-tab" onclick="switchSettingsTab(this, 'tab-language')" data-tab="tab-language" title="Lingua" data-i18n-title="settings.tab_language">🌐</button>
|
||||
</div>
|
||||
<div class="settings-panels">
|
||||
<!-- API Keys Tab -->
|
||||
<div class="settings-panel active" id="tab-api">
|
||||
<div class="settings-card">
|
||||
<h4>🤖 Google Gemini AI</h4>
|
||||
<p class="settings-hint">Chiave API per identificazione prodotti, scadenze e ricette.</p>
|
||||
<h4 data-i18n="settings.gemini.title">🤖 Google Gemini AI</h4>
|
||||
<p class="settings-hint" data-i18n="settings.gemini.hint">Chiave API per identificazione prodotti, scadenze e ricette.</p>
|
||||
<div class="form-group">
|
||||
<label>API Key Gemini</label>
|
||||
<input type="password" id="setting-gemini-key" class="form-input" placeholder="AIza...">
|
||||
@@ -683,8 +684,8 @@
|
||||
<!-- Bring! Tab -->
|
||||
<div class="settings-panel" id="tab-bring">
|
||||
<div class="settings-card">
|
||||
<h4>🛒 Bring! Shopping List</h4>
|
||||
<p class="settings-hint">Credenziali per l'integrazione con la lista della spesa Bring!</p>
|
||||
<h4 data-i18n="settings.bring.title">🛒 Bring! Shopping List</h4>
|
||||
<p class="settings-hint" data-i18n="settings.bring.hint">Credenziali per l'integrazione con la lista della spesa Bring!</p>
|
||||
<div class="form-group">
|
||||
<label>📧 Email Bring!</label>
|
||||
<input type="email" id="setting-bring-email" class="form-input" placeholder="email@esempio.com">
|
||||
@@ -699,8 +700,8 @@
|
||||
<!-- Recipe Tab -->
|
||||
<div class="settings-panel" id="tab-recipe">
|
||||
<div class="settings-card">
|
||||
<h4>🍳 Preferenze Ricette</h4>
|
||||
<p class="settings-hint">Configura le opzioni predefinite per la generazione delle ricette.</p>
|
||||
<h4 data-i18n="settings.recipe.title">🍳 Preferenze Ricette</h4>
|
||||
<p class="settings-hint" data-i18n="settings.recipe.hint">Configura le opzioni predefinite per la generazione delle ricette.</p>
|
||||
<div class="form-group">
|
||||
<label>👥 Persone predefinite</label>
|
||||
<div class="qty-control">
|
||||
@@ -729,8 +730,8 @@
|
||||
<!-- Weekly Meal Plan Tab -->
|
||||
<div class="settings-panel" id="tab-mealplan">
|
||||
<div class="settings-card">
|
||||
<h4>📅 Piano Pasti Settimanale</h4>
|
||||
<p class="settings-hint">Imposta la tipologia di pasto per ogni giorno. Sarà usata come guida nella generazione delle ricette.</p>
|
||||
<h4 data-i18n="settings.mealplan.title">📅 Piano Pasti Settimanale</h4>
|
||||
<p class="settings-hint" data-i18n="settings.mealplan.hint">Imposta la tipologia di pasto per ogni giorno. Sarà usata come guida nella generazione delle ricette.</p>
|
||||
<div class="form-group" style="margin-bottom:10px">
|
||||
<label class="toggle-row">
|
||||
<span>✅ Attiva piano pasti settimanale</span>
|
||||
@@ -759,8 +760,8 @@
|
||||
<!-- Appliances Tab -->
|
||||
<div class="settings-panel" id="tab-appliances">
|
||||
<div class="settings-card">
|
||||
<h4>🔌 Elettrodomestici Disponibili</h4>
|
||||
<p class="settings-hint">Indica gli elettrodomestici che hai a disposizione. Saranno considerati nella generazione delle ricette.</p>
|
||||
<h4 data-i18n="settings.appliances.title">🔌 Elettrodomestici Disponibili</h4>
|
||||
<p class="settings-hint" data-i18n="settings.appliances.hint">Indica gli elettrodomestici che hai a disposizione. Saranno considerati nella generazione delle ricette.</p>
|
||||
<div class="appliances-list" id="appliances-list"></div>
|
||||
<div class="form-group mt-2">
|
||||
<div class="barcode-input-row">
|
||||
@@ -826,8 +827,8 @@
|
||||
<!-- Camera Tab -->
|
||||
<div class="settings-panel" id="tab-camera">
|
||||
<div class="settings-card">
|
||||
<h4>📷 Fotocamera</h4>
|
||||
<p class="settings-hint">Scegli quale fotocamera utilizzare per la scansione barcode e l'identificazione AI.</p>
|
||||
<h4 data-i18n="settings.camera.title">📷 Fotocamera</h4>
|
||||
<p class="settings-hint" data-i18n="settings.camera.hint">Scegli quale fotocamera utilizzare per la scansione barcode e l'identificazione AI.</p>
|
||||
<div class="form-group">
|
||||
<label>📸 Fotocamera predefinita</label>
|
||||
<select id="setting-camera-facing" class="form-input">
|
||||
@@ -866,8 +867,8 @@
|
||||
<!-- TTS Tab -->
|
||||
<div class="settings-panel" id="tab-tts">
|
||||
<div class="settings-card">
|
||||
<h4>🔊 Voce & TTS</h4>
|
||||
<p class="settings-hint">Configura la sintesi vocale tramite qualsiasi API REST esterna. I passi della ricetta e i timer scaduti verranno inviati all'endpoint configurato.</p>
|
||||
<h4 data-i18n="settings.tts.title">🔊 Voce & TTS</h4>
|
||||
<p class="settings-hint" data-i18n="settings.tts.hint">Configura la sintesi vocale tramite qualsiasi API REST esterna. I passi della ricetta e i timer scaduti verranno inviati all'endpoint configurato.</p>
|
||||
<div class="form-group" style="margin-bottom:10px">
|
||||
<label class="toggle-row">
|
||||
<span>✅ Attiva TTS</span>
|
||||
@@ -935,8 +936,21 @@
|
||||
<div id="tts-test-status" style="display:none;margin-top:8px"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Language Tab -->
|
||||
<div class="settings-panel" id="tab-language">
|
||||
<div class="settings-card">
|
||||
<h4 data-i18n="settings.language.title">🌐 Lingua / Language</h4>
|
||||
<p class="settings-hint" data-i18n="settings.language.hint">Seleziona la lingua dell'interfaccia. Select the interface language.</p>
|
||||
<div class="form-group">
|
||||
<label data-i18n="settings.language.label">🌐 Lingua</label>
|
||||
<select id="setting-language" class="form-input" onchange="changeLanguage(this.value)">
|
||||
</select>
|
||||
<p class="settings-hint mt-2" data-i18n="settings.language.restart_notice">La pagina verrà ricaricata per applicare la nuova lingua.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-large btn-success full-width mt-2" onclick="saveSettings()">💾 Salva Configurazione</button>
|
||||
<button class="btn btn-large btn-success full-width mt-2" onclick="saveSettings()" data-i18n="btn.save_config">💾 Salva Configurazione</button>
|
||||
<div id="settings-status" class="settings-status" style="display:none"></div>
|
||||
</section>
|
||||
|
||||
@@ -946,25 +960,25 @@
|
||||
<div class="chat-header-bar">
|
||||
<div class="chat-header-info">
|
||||
<svg class="gemini-icon-sm" viewBox="0 0 24 24" width="22" height="22" fill="#6366f1"><path d="M12 0C12 6.627 6.627 12 0 12c6.627 0 12 5.373 12 12 0-6.627 5.373-12 12-12-6.627 0-12-5.373-12-12z"/></svg>
|
||||
<span class="chat-title">Gemini Chef</span>
|
||||
<span class="chat-title" data-i18n="chat.title">Gemini Chef</span>
|
||||
</div>
|
||||
<button class="btn-chat-clear" onclick="clearChat()" title="Nuova conversazione">🗑️</button>
|
||||
<button class="btn-chat-clear" onclick="clearChat()" title="Nuova conversazione" data-i18n-title="chat.clear">🗑️</button>
|
||||
</div>
|
||||
<div class="chat-messages" id="chat-messages">
|
||||
<div class="chat-welcome">
|
||||
<svg class="gemini-icon-lg" viewBox="0 0 24 24" width="48" height="48" fill="#6366f1"><path d="M12 0C12 6.627 6.627 12 0 12c6.627 0 12 5.373 12 12 0-6.627 5.373-12 12-12-6.627 0-12-5.373-12-12z"/></svg>
|
||||
<h3>Ciao! Sono il tuo assistente cucina</h3>
|
||||
<p>Chiedimi di prepararti un succo, uno spuntino, un piatto veloce... Conosco la tua dispensa, i tuoi elettrodomestici e le tue preferenze!</p>
|
||||
<h3 data-i18n="chat.welcome">Ciao! Sono il tuo assistente cucina</h3>
|
||||
<p data-i18n="chat.welcome_desc">Chiedimi di prepararti un succo, uno spuntino, un piatto veloce... Conosco la tua dispensa, i tuoi elettrodomestici e le tue preferenze!</p>
|
||||
<div class="chat-suggestions">
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Cosa posso preparare per uno spuntino veloce?')">🍿 Spuntino veloce</button>
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Fammi un succo o frullato con quello che ho')">🥤 Succo/Frullato</button>
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Ho fame ma voglio qualcosa di leggero')">🥗 Qualcosa di leggero</button>
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Cosa sta per scadere e come posso usarlo?')">⏰ Usa le scadenze</button>
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Cosa posso preparare per uno spuntino veloce?')" data-i18n="chat.suggestion_snack">🍿 Spuntino veloce</button>
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Fammi un succo o frullato con quello che ho')" data-i18n="chat.suggestion_juice">🥤 Succo/Frullato</button>
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Ho fame ma voglio qualcosa di leggero')" data-i18n="chat.suggestion_light">🥗 Qualcosa di leggero</button>
|
||||
<button class="chat-suggestion" onclick="sendChatSuggestion('Cosa sta per scadere e come posso usarlo?')" data-i18n="chat.suggestion_expiry">⏰ Usa le scadenze</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input-bar">
|
||||
<input type="text" id="chat-input" class="chat-input" placeholder="Chiedi qualcosa..." onkeydown="if(event.key==='Enter')sendChatMessage()">
|
||||
<input type="text" id="chat-input" class="chat-input" placeholder="Chiedi qualcosa..." onkeydown="if(event.key==='Enter')sendChatMessage()" data-i18n-placeholder="chat.placeholder">
|
||||
<button class="btn-chat-send" id="btn-chat-send" onclick="sendChatMessage()">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="white"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
||||
</button>
|
||||
@@ -978,23 +992,23 @@
|
||||
<nav class="bottom-nav">
|
||||
<button class="nav-btn" onclick="showPage('dashboard')" data-page="dashboard">
|
||||
<span class="nav-icon">🏠</span>
|
||||
<span class="nav-label">Home</span>
|
||||
<span class="nav-label" data-i18n="nav.home">Home</span>
|
||||
</button>
|
||||
<button class="nav-btn" onclick="showPage('inventory', '')" data-page="inventory">
|
||||
<span class="nav-icon">📋</span>
|
||||
<span class="nav-label">Dispensa</span>
|
||||
<span class="nav-label" data-i18n="nav.inventory">Dispensa</span>
|
||||
</button>
|
||||
<button class="nav-btn" onclick="showPage('recipe')" data-page="recipe">
|
||||
<span class="nav-icon">🍳</span>
|
||||
<span class="nav-label">Ricette</span>
|
||||
<span class="nav-label" data-i18n="nav.recipes">Ricette</span>
|
||||
</button>
|
||||
<button class="nav-btn" onclick="showPage('shopping')" data-page="shopping">
|
||||
<span class="nav-icon">🛒</span>
|
||||
<span class="nav-label">Spesa</span>
|
||||
<span class="nav-label" data-i18n="nav.shopping">Spesa</span>
|
||||
</button>
|
||||
<button class="nav-btn" onclick="showPage('log')" data-page="log">
|
||||
<span class="nav-icon">📒</span>
|
||||
<span class="nav-label">Log</span>
|
||||
<span class="nav-label" data-i18n="nav.log">Log</span>
|
||||
</button>
|
||||
<button class="nav-btn" onclick="showPage('settings')" data-page="settings">
|
||||
<span class="nav-icon">⚙️</span>
|
||||
@@ -1060,13 +1074,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Setup Wizard (first-run) -->
|
||||
<div class="modal-overlay" id="setup-wizard" style="display:none">
|
||||
<div class="modal-content setup-wizard-content" onclick="event.stopPropagation()">
|
||||
<div class="setup-header">
|
||||
<h2>🏠 Dispensa Manager</h2>
|
||||
<div class="setup-progress" id="setup-progress"></div>
|
||||
</div>
|
||||
<div class="setup-body" id="setup-body"></div>
|
||||
<div class="setup-footer">
|
||||
<button class="btn btn-secondary" id="setup-prev" onclick="setupWizardNav(-1)" style="display:none">← Indietro</button>
|
||||
<button class="btn btn-accent" id="setup-next" onclick="setupWizardNav(1)">Avanti →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast notification -->
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div class="loading-overlay" id="loading" style="display:none">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Caricamento...</p>
|
||||
<p data-i18n="app.loading">Caricamento...</p>
|
||||
</div>
|
||||
|
||||
<!-- Modal for product details from inventory -->
|
||||
|
||||
Reference in New Issue
Block a user