diff --git a/api/database.php b/api/database.php
index e8cccda..7805fd0 100644
--- a/api/database.php
+++ b/api/database.php
@@ -126,6 +126,16 @@ function initializeDB(PDO $db): void {
}
function migrateDB(PDO $db): void {
+ // Guard: if core tables don't exist yet (e.g. DB file present but empty / partial init),
+ // run initializeDB first so all tables are created, then return — no ALTER TABLE needed.
+ $productsExists = $db->query(
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='products'"
+ )->fetchColumn();
+ if (!$productsExists) {
+ initializeDB($db);
+ return;
+ }
+
// Add package_unit column if missing
$cols = $db->query("PRAGMA table_info(products)")->fetchAll();
$colNames = array_column($cols, 'name');
diff --git a/api/index.php b/api/index.php
index 254c1bb..7b5d916 100644
--- a/api/index.php
+++ b/api/index.php
@@ -477,9 +477,13 @@ if (($_GET['action'] ?? '') === 'health_check') {
'hint' => $wal !== 'wal' ? 'Journal mode not optimal — will be corrected automatically on next startup' : null];
// Size & rows
- $checks['db_size'] = ['ok' => true, 'value' => round(filesize($dbPath)/1024).' KB', 'optional' => true];
- $cnt = $pdo->query("SELECT COUNT(*) FROM inventory WHERE quantity > 0")->fetchColumn();
- $checks['db_row_count'] = ['ok' => true, 'value' => $cnt.' prodotti in inventario', 'optional' => true];
+ $checks['db_size'] = ['ok' => true, 'value' => round(filesize($dbPath)/1024).' KB', 'optional' => true];
+ if (empty($missing) || !in_array('inventory', $missing)) {
+ $cnt = $pdo->query("SELECT COUNT(*) FROM inventory WHERE quantity > 0")->fetchColumn();
+ $checks['db_row_count'] = ['ok' => true, 'value' => $cnt.' prodotti in inventario', 'optional' => true];
+ } else {
+ $checks['db_row_count'] = ['ok' => true, 'value' => '0 prodotti in inventario', 'optional' => true];
+ }
} else {
foreach (['db_tables', 'db_integrity'] as $k)
$checks[$k] = ['ok' => false, 'hint' => 'Cannot verify — DB connection failed'];
@@ -2185,7 +2189,8 @@ function _offFetchProduct(string $barcode): ?array {
}
$ingredients = $p['ingredients_text_it'] ?? $p['ingredients_text'] ?? '';
- $category = $p['categories_tags'][0] ?? end($p['categories_hierarchy'] ?? []) ?? $p['categories'] ?? '';
+ $catHierarchy = $p['categories_hierarchy'] ?? [];
+ $category = $p['categories_tags'][0] ?? (empty($catHierarchy) ? null : end($catHierarchy)) ?? $p['categories'] ?? '';
$allergens = '';
if (!empty($p['allergens_tags'])) {
$allergens = implode(', ', array_map(fn($a) => str_replace('en:', '', $a), $p['allergens_tags']));
diff --git a/assets/js/app.js b/assets/js/app.js
index 78dad45..272382e 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -14186,7 +14186,7 @@ async function testTTS() {
window._kioskTtsDone = (uid) => {
clearTimeout(_ttsTestTimer);
window._kioskTtsDone = null; window._kioskTtsError = null;
- if (statusEl) { statusEl.className = 'settings-status success'; statusEl.textContent = '✅ Voce riprodotta correttamente.'; }
+ if (statusEl) { statusEl.className = 'settings-status success'; statusEl.textContent = '✅ ' + t('settings.tts.test_ok_kiosk'); }
};
window._kioskTtsError = (uid, code) => {
clearTimeout(_ttsTestTimer);
@@ -14194,11 +14194,28 @@ async function testTTS() {
const msg = code == -1 ? 'sintesi non riuscita' : code == -2 ? 'lingua non supportata' : code == -3 ? 'servizio non disponibile' : ('codice ' + code);
if (statusEl) { statusEl.className = 'settings-status error'; statusEl.textContent = '❌ Errore TTS Android (' + msg + ') — installa o aggiorna Google Text-to-Speech dal Play Store.'; }
};
- // Timeout: if Android doesn't callback within 4s, warn about media volume
+ // Timeout: if Android doesn't callback within 10s, ask user if they heard the voice
+ // (speech can take 6-8 s; UtteranceProgressListener may not fire on all firmware)
_ttsTestTimer = setTimeout(() => {
window._kioskTtsDone = null; window._kioskTtsError = null;
- if (statusEl) { statusEl.className = 'settings-status error'; statusEl.textContent = '⚠️ Nessun feedback ricevuto. Controlla: 1) volume media del dispositivo non sia 0; 2) Google Text-to-Speech installato e aggiornato; 3) pacchetto vocale italiano scaricato.'; }
- }, 4000);
+ if (!statusEl) return;
+ statusEl.className = 'settings-status';
+ statusEl.style.display = 'block';
+ statusEl.innerHTML =
+ '🔊 ' + t('settings.tts.heard_question') + '
' +
+ '