i18n: replace all hardcoded Italian strings with English
- api/index.php: health check hints (disk space, DB, Gemini, TTS, scale,
internet) translated to English; Bring! error strings (credentials,
list not found, fetch error, missing params) translated; Gemini chat
and identify_product error strings translated; transaction note
[Correzione manuale] -> [Manual correction]
- assets/js/app.js: expiry scanner result strings now use t() keys
(scanner.expiry_found, scanner.expiry_read_fail, scanner.expiry_raw_label);
removed Italian fallback from kiosk native_update_hint toast
- translations/{it,en,de}.json: added scanner.expiry_found,
scanner.expiry_read_fail, scanner.expiry_raw_label keys
- README.md: 'Generali' tab label -> 'General' (2 occurrences)
This commit is contained in:
@@ -39,7 +39,7 @@
|
|||||||
## ✨ Features
|
## ✨ Features
|
||||||
|
|
||||||
> ⚙️ **New in v1.7.23 — Global settings tab, DB auto-cleanup, vacuum-sealed expiry**
|
> ⚙️ **New in v1.7.23 — Global settings tab, DB auto-cleanup, vacuum-sealed expiry**
|
||||||
> A new **Generali** tab groups all global settings (language, currency, theme, screensaver, zero-waste, export) in one place.
|
> A new **General** tab groups all global settings (language, currency, theme, screensaver, zero-waste, export) in one place.
|
||||||
> Recipes older than `RECIPE_RETENTION_DAYS` and transactions older than `TRANSACTION_RETENTION_DAYS` are deleted automatically every cron cycle, followed by a SQLite `VACUUM` to keep the database small.
|
> Recipes older than `RECIPE_RETENTION_DAYS` and transactions older than `TRANSACTION_RETENTION_DAYS` are deleted automatically every cron cycle, followed by a SQLite `VACUUM` to keep the database small.
|
||||||
> Vacuum-sealed products get an extended grace period (`VACUUM_EXPIRY_EXTENSION_DAYS`, default 30 days) before being flagged as expired.
|
> Vacuum-sealed products get an extended grace period (`VACUUM_EXPIRY_EXTENSION_DAYS`, default 30 days) before being flagged as expired.
|
||||||
> Auto theme now follows **time of day** (dark 20:00–07:00) instead of the OS setting, making it server-friendly.
|
> Auto theme now follows **time of day** (dark 20:00–07:00) instead of the OS setting, making it server-friendly.
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
|
|
||||||
### 🌙 Appearance
|
### 🌙 Appearance
|
||||||
- **Dark mode** — Three modes: Light, Dark, and Auto (time-based: dark from 20:00 to 07:00, light otherwise); applies immediately without page reload; auto mode re-evaluates every 5 minutes, so night/day transitions happen automatically even on always-on kiosk displays; theme is applied before the first render to prevent a white flash
|
- **Dark mode** — Three modes: Light, Dark, and Auto (time-based: dark from 20:00 to 07:00, light otherwise); applies immediately without page reload; auto mode re-evaluates every 5 minutes, so night/day transitions happen automatically even on always-on kiosk displays; theme is applied before the first render to prevent a white flash
|
||||||
- **Global settings tab** — A dedicated **⚙️ Generali** tab groups all system-wide settings (language, currency, theme, screensaver, zero-waste tips, export) at the top of the Settings panel
|
- **Global settings tab** — A dedicated **⚙️ General** tab groups all system-wide settings (language, currency, theme, screensaver, zero-waste tips, export) at the top of the Settings panel
|
||||||
|
|
||||||
### �️ Database Maintenance
|
### �️ Database Maintenance
|
||||||
- **Automatic cleanup** — Recipes older than `RECIPE_RETENTION_DAYS` (default 7) and transactions older than `TRANSACTION_RETENTION_DAYS` (default 7) are deleted automatically on every cron cycle; SQLite `VACUUM` runs after each cleanup to keep the file compact
|
- **Automatic cleanup** — Recipes older than `RECIPE_RETENTION_DAYS` (default 7) and transactions older than `TRANSACTION_RETENTION_DAYS` (default 7) are deleted automatically on every cron cycle; SQLite `VACUUM` runs after each cleanup to keep the file compact
|
||||||
|
|||||||
+33
-33
@@ -391,7 +391,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
'ok' => $freeBytes === false || $freeBytes > 50*1048576,
|
'ok' => $freeBytes === false || $freeBytes > 50*1048576,
|
||||||
'value' => $freeMB !== null ? $freeMB.' MB liberi' : null,
|
'value' => $freeMB !== null ? $freeMB.' MB liberi' : null,
|
||||||
'optional' => true,
|
'optional' => true,
|
||||||
'hint' => $freeBytes !== false && $freeBytes <= 50*1048576 ? 'Meno di 50 MB liberi — libera spazio sul disco' : null,
|
'hint' => $freeBytes !== false && $freeBytes <= 50*1048576 ? 'Less than 50 MB free — free up disk space' : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
// ── 8. SQLite database ────────────────────────────────────────────────────
|
// ── 8. SQLite database ────────────────────────────────────────────────────
|
||||||
@@ -419,11 +419,11 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
$checks['db_legacy'] = [
|
$checks['db_legacy'] = [
|
||||||
'ok' => !$hasLegacy,
|
'ok' => !$hasLegacy,
|
||||||
'optional' => true,
|
'optional' => true,
|
||||||
'hint' => $hasLegacy ? 'Trovato vecchio dispensa.db — il file è ormai obsoleto, puoi eliminarlo manualmente' : null,
|
'hint' => $hasLegacy ? 'Legacy dispensa.db found — the file is obsolete, you can delete it manually' : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($isFresh) {
|
if ($isFresh) {
|
||||||
$checks['db_connect'] = ['ok' => true, 'fresh' => true, 'value' => 'nuovo impianto'];
|
$checks['db_connect'] = ['ok' => true, 'fresh' => true, 'value' => 'fresh install'];
|
||||||
$checks['db_tables'] = ['ok' => true, 'fresh' => true];
|
$checks['db_tables'] = ['ok' => true, 'fresh' => true];
|
||||||
$checks['db_integrity'] = ['ok' => true, 'fresh' => true];
|
$checks['db_integrity'] = ['ok' => true, 'fresh' => true];
|
||||||
$checks['db_wal'] = ['ok' => true, 'fresh' => true, 'optional' => true];
|
$checks['db_wal'] = ['ok' => true, 'fresh' => true, 'optional' => true];
|
||||||
@@ -441,7 +441,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
$checks['db_connect'] = ['ok' => true, 'value' => basename($dbPath)];
|
$checks['db_connect'] = ['ok' => true, 'value' => basename($dbPath)];
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$checks['db_connect'] = ['ok' => false, 'error' => $e->getMessage(),
|
$checks['db_connect'] = ['ok' => false, 'error' => $e->getMessage(),
|
||||||
'hint' => 'Impossibile aprire il database — verifica permessi su data/evershelf.db'];
|
'hint' => 'Cannot open the database — check permissions on data/evershelf.db'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dbConnOk && $pdo) {
|
if ($dbConnOk && $pdo) {
|
||||||
@@ -452,7 +452,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
$checks['db_tables'] = [
|
$checks['db_tables'] = [
|
||||||
'ok' => empty($missing),
|
'ok' => empty($missing),
|
||||||
'missing' => $missing,
|
'missing' => $missing,
|
||||||
'hint' => !empty($missing) ? 'Tabelle mancanti: ' . implode(', ', $missing) . ' — esegui una chiamata API per auto-inizializzare il DB' : null,
|
'hint' => !empty($missing) ? 'Missing tables: ' . implode(', ', $missing) . ' — call any API endpoint to auto-initialize the DB' : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Integrity
|
// Integrity
|
||||||
@@ -466,7 +466,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
// WAL
|
// WAL
|
||||||
$wal = $pdo->query("PRAGMA journal_mode")->fetchColumn();
|
$wal = $pdo->query("PRAGMA journal_mode")->fetchColumn();
|
||||||
$checks['db_wal'] = ['ok' => $wal === 'wal', 'value' => $wal, 'optional' => true,
|
$checks['db_wal'] = ['ok' => $wal === 'wal', 'value' => $wal, 'optional' => true,
|
||||||
'hint' => $wal !== 'wal' ? 'Modalità journal non ottimale — sarà corretta automaticamente al primo avvio' : null];
|
'hint' => $wal !== 'wal' ? 'Journal mode not optimal — will be corrected automatically on next startup' : null];
|
||||||
|
|
||||||
// Size & rows
|
// Size & rows
|
||||||
$checks['db_size'] = ['ok' => true, 'value' => round(filesize($dbPath)/1024).' KB', 'optional' => true];
|
$checks['db_size'] = ['ok' => true, 'value' => round(filesize($dbPath)/1024).' KB', 'optional' => true];
|
||||||
@@ -474,7 +474,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
$checks['db_row_count'] = ['ok' => true, 'value' => $cnt.' prodotti in inventario', 'optional' => true];
|
$checks['db_row_count'] = ['ok' => true, 'value' => $cnt.' prodotti in inventario', 'optional' => true];
|
||||||
} else {
|
} else {
|
||||||
foreach (['db_tables', 'db_integrity'] as $k)
|
foreach (['db_tables', 'db_integrity'] as $k)
|
||||||
$checks[$k] = ['ok' => false, 'hint' => 'Impossibile verificare — connessione DB fallita'];
|
$checks[$k] = ['ok' => false, 'hint' => 'Cannot verify — DB connection failed'];
|
||||||
foreach (['db_wal', 'db_size', 'db_row_count'] as $k)
|
foreach (['db_wal', 'db_size', 'db_row_count'] as $k)
|
||||||
$checks[$k] = ['ok' => false, 'optional' => true];
|
$checks[$k] = ['ok' => false, 'optional' => true];
|
||||||
}
|
}
|
||||||
@@ -492,10 +492,10 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
$geminiKey = $envGet('GEMINI_API_KEY');
|
$geminiKey = $envGet('GEMINI_API_KEY');
|
||||||
if (!empty($geminiKey)) {
|
if (!empty($geminiKey)) {
|
||||||
$checks['gemini_key'] = ['ok' => strlen($geminiKey) > 20, 'optional' => true,
|
$checks['gemini_key'] = ['ok' => strlen($geminiKey) > 20, 'optional' => true,
|
||||||
'hint' => strlen($geminiKey) <= 20 ? 'Chiave Gemini AI sembra troppo corta — verifica il valore in .env' : null];
|
'hint' => strlen($geminiKey) <= 20 ? 'Gemini AI key looks too short — check the value in .env' : null];
|
||||||
} else {
|
} else {
|
||||||
$checks['gemini_key'] = ['ok' => true, 'optional' => true,
|
$checks['gemini_key'] = ['ok' => true, 'optional' => true,
|
||||||
'value' => 'non configurata', 'hint' => 'Configura GEMINI_API_KEY in .env per abilitare le funzioni AI'];
|
'value' => 'not configured', 'hint' => 'Set GEMINI_API_KEY in .env to enable AI features'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 11. Bring! — solo se EMAIL+PASSWORD sono impostate ───────────────────
|
// ── 11. Bring! — solo se EMAIL+PASSWORD sono impostate ───────────────────
|
||||||
@@ -511,7 +511,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
$checks['tts_url'] = [
|
$checks['tts_url'] = [
|
||||||
'ok' => !empty($ttsUrl),
|
'ok' => !empty($ttsUrl),
|
||||||
'optional' => true,
|
'optional' => true,
|
||||||
'hint' => empty($ttsUrl) ? 'TTS_ENABLED=true ma TTS_URL non configurata' : null,
|
'hint' => empty($ttsUrl) ? 'TTS_ENABLED=true but TTS_URL not configured' : null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,7 +521,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
$checks['scale_gateway'] = [
|
$checks['scale_gateway'] = [
|
||||||
'ok' => !empty($scaleUrl),
|
'ok' => !empty($scaleUrl),
|
||||||
'optional' => true,
|
'optional' => true,
|
||||||
'hint' => empty($scaleUrl) ? 'SCALE_ENABLED=true ma SCALE_GATEWAY_URL non configurata' : null,
|
'hint' => empty($scaleUrl) ? 'SCALE_ENABLED=true but SCALE_GATEWAY_URL not configured' : null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,7 +546,7 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
|||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
$internetOk = $httpCode > 0 || $curlErrNo === 0;
|
$internetOk = $httpCode > 0 || $curlErrNo === 0;
|
||||||
$checks['internet'] = ['ok' => $internetOk, 'optional' => true,
|
$checks['internet'] = ['ok' => $internetOk, 'optional' => true,
|
||||||
'hint' => !$internetOk ? 'Impossibile raggiungere i server Gemini — le funzioni AI non funzioneranno senza connessione internet' : null];
|
'hint' => !$internetOk ? 'Cannot reach Gemini servers — AI features will not work without an internet connection' : null];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Compute overall result ────────────────────────────────────────────────
|
// ── Compute overall result ────────────────────────────────────────────────
|
||||||
@@ -2129,7 +2129,7 @@ function updateInventory(PDO $db): void {
|
|||||||
if (abs($diff) > 0.001) {
|
if (abs($diff) > 0.001) {
|
||||||
$txType = $diff > 0 ? 'in' : 'out';
|
$txType = $diff > 0 ? 'in' : 'out';
|
||||||
$txQty = abs($diff);
|
$txQty = abs($diff);
|
||||||
$db->prepare("INSERT INTO transactions (product_id, type, quantity, location, notes) VALUES (?, ?, ?, ?, '[Correzione manuale]')")
|
$db->prepare("INSERT INTO transactions (product_id, type, quantity, location, notes) VALUES (?, ?, ?, ?, '[Manual correction]')")
|
||||||
->execute([$pid, $txType, $txQty, $loc]);
|
->execute([$pid, $txType, $txQty, $loc]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2331,7 +2331,7 @@ function undoTransaction(PDO $db): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Log counter-transaction
|
// Log counter-transaction
|
||||||
$db->prepare("INSERT INTO transactions (product_id, type, quantity, location, notes) VALUES (?, 'out', ?, ?, '[Annullato]')")->execute([$productId, $quantity, $location]);
|
$db->prepare("INSERT INTO transactions (product_id, type, quantity, location, notes) VALUES (?, 'out', ?, ?, '[Undone]')")->execute([$productId, $quantity, $location]);
|
||||||
|
|
||||||
} elseif ($type === 'out' || $type === 'waste') {
|
} elseif ($type === 'out' || $type === 'waste') {
|
||||||
// Reverse a USE: add quantity back to inventory
|
// Reverse a USE: add quantity back to inventory
|
||||||
@@ -2345,7 +2345,7 @@ function undoTransaction(PDO $db): void {
|
|||||||
$db->prepare("INSERT INTO inventory (product_id, location, quantity) VALUES (?, ?, ?)")->execute([$productId, $location, $quantity]);
|
$db->prepare("INSERT INTO inventory (product_id, location, quantity) VALUES (?, ?, ?)")->execute([$productId, $location, $quantity]);
|
||||||
}
|
}
|
||||||
// Log counter-transaction
|
// Log counter-transaction
|
||||||
$db->prepare("INSERT INTO transactions (product_id, type, quantity, location, notes) VALUES (?, 'in', ?, ?, '[Annullato]')")->execute([$productId, $quantity, $location]);
|
$db->prepare("INSERT INTO transactions (product_id, type, quantity, location, notes) VALUES (?, 'in', ?, ?, '[Undone]')")->execute([$productId, $quantity, $location]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark original as undone
|
// Mark original as undone
|
||||||
@@ -3599,7 +3599,7 @@ function geminiChat(PDO $db): void {
|
|||||||
$langName = recipeLangName($lang);
|
$langName = recipeLangName($lang);
|
||||||
|
|
||||||
if (empty($message)) {
|
if (empty($message)) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Messaggio vuoto']);
|
echo json_encode(['success' => false, 'error' => 'Empty message']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3703,7 +3703,7 @@ PROMPT;
|
|||||||
$httpCode = $result['http_code'];
|
$httpCode = $result['http_code'];
|
||||||
|
|
||||||
if ($httpCode !== 200) {
|
if ($httpCode !== 200) {
|
||||||
$errMsg = $result['data']['error']['message'] ?? 'Errore API Gemini';
|
$errMsg = $result['data']['error']['message'] ?? 'Gemini API error';
|
||||||
echo json_encode(['success' => false, 'error' => $errMsg, 'http_code' => $httpCode]);
|
echo json_encode(['success' => false, 'error' => $errMsg, 'http_code' => $httpCode]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3711,7 +3711,7 @@ PROMPT;
|
|||||||
$reply = $result['data']['candidates'][0]['content']['parts'][0]['text'] ?? '';
|
$reply = $result['data']['candidates'][0]['content']['parts'][0]['text'] ?? '';
|
||||||
|
|
||||||
if (empty($reply)) {
|
if (empty($reply)) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Risposta vuota da Gemini']);
|
echo json_encode(['success' => false, 'error' => 'Empty response from Gemini']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5371,7 +5371,7 @@ PROMPT;
|
|||||||
$httpCode = $result['http_code'];
|
$httpCode = $result['http_code'];
|
||||||
|
|
||||||
if ($httpCode !== 200) {
|
if ($httpCode !== 200) {
|
||||||
$errMsg = $result['data']['error']['message'] ?? 'Errore API Gemini';
|
$errMsg = $result['data']['error']['message'] ?? 'Gemini API error';
|
||||||
echo json_encode(['success' => false, 'error' => $errMsg, 'http_code' => $httpCode]);
|
echo json_encode(['success' => false, 'error' => $errMsg, 'http_code' => $httpCode]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5386,7 +5386,7 @@ PROMPT;
|
|||||||
$identified = json_decode($text, true);
|
$identified = json_decode($text, true);
|
||||||
|
|
||||||
if (!$identified || empty($identified['name'])) {
|
if (!$identified || empty($identified['name'])) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Impossibile identificare il prodotto', 'raw' => $text]);
|
echo json_encode(['success' => false, 'error' => 'Cannot identify the product', 'raw' => $text]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6290,7 +6290,7 @@ function bringGetList(): void {
|
|||||||
$auth = bringAuth();
|
$auth = bringAuth();
|
||||||
if (!$auth) {
|
if (!$auth) {
|
||||||
EverLog::info('bringGetList');
|
EverLog::info('bringGetList');
|
||||||
echo json_encode(['success' => false, 'error' => 'Credenziali Bring! non configurate. Aggiungi BRING_EMAIL e BRING_PASSWORD al file .env']);
|
echo json_encode(['success' => false, 'error' => 'Bring! credentials not configured. Add BRING_EMAIL and BRING_PASSWORD to .env']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6301,14 +6301,14 @@ function bringGetList(): void {
|
|||||||
if ($lists && isset($lists['lists'][0]['listUuid'])) {
|
if ($lists && isset($lists['lists'][0]['listUuid'])) {
|
||||||
$listUUID = $lists['lists'][0]['listUuid'];
|
$listUUID = $lists['lists'][0]['listUuid'];
|
||||||
} else {
|
} else {
|
||||||
echo json_encode(['success' => false, 'error' => 'Nessuna lista Bring! trovata']);
|
echo json_encode(['success' => false, 'error' => 'No Bring! list found']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
|
$data = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
|
||||||
if (!$data) {
|
if (!$data) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Errore nel recupero della lista']);
|
echo json_encode(['success' => false, 'error' => 'Error fetching the list']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6368,7 +6368,7 @@ function bringAddItems(): void {
|
|||||||
$auth = bringAuth();
|
$auth = bringAuth();
|
||||||
if (!$auth) {
|
if (!$auth) {
|
||||||
EverLog::info('bringAddItems');
|
EverLog::info('bringAddItems');
|
||||||
echo json_encode(['success' => false, 'error' => 'Credenziali Bring! non configurate']);
|
echo json_encode(['success' => false, 'error' => 'Bring! credentials not configured']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6377,7 +6377,7 @@ function bringAddItems(): void {
|
|||||||
$listUUID = $input['listUUID'] ?? $auth['bringListUUID'];
|
$listUUID = $input['listUUID'] ?? $auth['bringListUUID'];
|
||||||
|
|
||||||
if (empty($listUUID)) {
|
if (empty($listUUID)) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Lista non trovata']);
|
echo json_encode(['success' => false, 'error' => 'List not found']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6446,7 +6446,7 @@ function bringRemoveItem(): void {
|
|||||||
$auth = bringAuth();
|
$auth = bringAuth();
|
||||||
if (!$auth) {
|
if (!$auth) {
|
||||||
EverLog::info('bringRemoveItem');
|
EverLog::info('bringRemoveItem');
|
||||||
echo json_encode(['success' => false, 'error' => 'Credenziali Bring! non configurate']);
|
echo json_encode(['success' => false, 'error' => 'Bring! credentials not configured']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6455,7 +6455,7 @@ function bringRemoveItem(): void {
|
|||||||
$listUUID = $input['listUUID'] ?? $auth['bringListUUID'];
|
$listUUID = $input['listUUID'] ?? $auth['bringListUUID'];
|
||||||
|
|
||||||
if (empty($name) || empty($listUUID)) {
|
if (empty($name) || empty($listUUID)) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Parametri mancanti']);
|
echo json_encode(['success' => false, 'error' => 'Missing parameters']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6494,19 +6494,19 @@ function bringCleanSpecs(): void {
|
|||||||
$auth = bringAuth();
|
$auth = bringAuth();
|
||||||
if (!$auth) {
|
if (!$auth) {
|
||||||
EverLog::info('bringCleanSpecs');
|
EverLog::info('bringCleanSpecs');
|
||||||
echo json_encode(['success' => false, 'error' => 'Credenziali Bring! non configurate']);
|
echo json_encode(['success' => false, 'error' => 'Bring! credentials not configured']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$listUUID = $auth['bringListUUID'];
|
$listUUID = $auth['bringListUUID'];
|
||||||
if (empty($listUUID)) {
|
if (empty($listUUID)) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Lista non trovata']);
|
echo json_encode(['success' => false, 'error' => 'List not found']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
|
$data = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
|
||||||
if (!$data || !isset($data['purchase'])) {
|
if (!$data || !isset($data['purchase'])) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Errore nel recupero della lista']);
|
echo json_encode(['success' => false, 'error' => 'Error fetching the list']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6605,17 +6605,17 @@ function bringMigrateNames(PDO $db): void {
|
|||||||
$auth = bringAuth();
|
$auth = bringAuth();
|
||||||
if (!$auth) {
|
if (!$auth) {
|
||||||
EverLog::info('bringMigrateNames');
|
EverLog::info('bringMigrateNames');
|
||||||
echo json_encode(['success' => false, 'error' => 'Credenziali Bring! non configurate']);
|
echo json_encode(['success' => false, 'error' => 'Bring! credentials not configured']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$listUUID = $auth['bringListUUID'];
|
$listUUID = $auth['bringListUUID'];
|
||||||
if (empty($listUUID)) {
|
if (empty($listUUID)) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Lista non trovata']);
|
echo json_encode(['success' => false, 'error' => 'List not found']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$data = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
|
$data = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
|
||||||
if (!$data || !isset($data['purchase'])) {
|
if (!$data || !isset($data['purchase'])) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Errore nel recupero della lista']);
|
echo json_encode(['success' => false, 'error' => 'Error fetching the list']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -2744,7 +2744,7 @@ function _openKioskNativeSettings() {
|
|||||||
_kioskBridge.openNativeSettings();
|
_kioskBridge.openNativeSettings();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// Older APK without openNativeSettings bridge — inform user to update
|
// Older APK without openNativeSettings bridge — inform user to update
|
||||||
showToast(t('settings.kiosk.native_update_hint') || 'Aggiorna l\'app kiosk per usare questa funzione', 'warning', 4000);
|
showToast(t('settings.kiosk.native_update_hint'), 'warning', 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11452,14 +11452,14 @@ async function analyzeExpiryImage(dataUrl) {
|
|||||||
if (expiryInput) {
|
if (expiryInput) {
|
||||||
expiryInput.value = result.expiry_date;
|
expiryInput.value = result.expiry_date;
|
||||||
}
|
}
|
||||||
statusDiv.innerHTML = `<p style="color:var(--success);font-weight:600">✅ Data trovata: ${formatDate(result.expiry_date)}</p>`;
|
statusDiv.innerHTML = `<p style="color:var(--success);font-weight:600">✅ ${t('scanner.expiry_found')}: ${formatDate(result.expiry_date)}</p>`;
|
||||||
|
|
||||||
// Close modal after delay
|
// Close modal after delay
|
||||||
setTimeout(() => closeExpiryScanner(), 1500);
|
setTimeout(() => closeExpiryScanner(), 1500);
|
||||||
} else if (result.error === 'no_api_key') {
|
} else if (result.error === 'no_api_key') {
|
||||||
statusDiv.innerHTML = `<p style="color:var(--warning)">${t('ai.no_api_key').replace(/\n/g, '<br>')}</p>`;
|
statusDiv.innerHTML = `<p style="color:var(--warning)">${t('ai.no_api_key').replace(/\n/g, '<br>')}</p>`;
|
||||||
} else {
|
} else {
|
||||||
statusDiv.innerHTML = `<p style="color:var(--danger)">❌ Non riesco a leggere la data. ${result.raw_text ? '<br><small>Letto: ' + escapeHtml(result.raw_text) + '</small>' : ''}</p>
|
statusDiv.innerHTML = `<p style="color:var(--danger)">❌ ${t('scanner.expiry_read_fail')} ${result.raw_text ? '<br><small>' + t('scanner.expiry_raw_label') + ': ' + escapeHtml(result.raw_text) + '</small>' : ''}</p>
|
||||||
<button class="btn btn-secondary" onclick="retakeExpiry()" style="margin-top:8px">${t('btn.retry')}</button>`;
|
<button class="btn btn-secondary" onclick="retakeExpiry()" style="margin-top:8px">${t('btn.retry')}</button>`;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1034,7 +1034,10 @@
|
|||||||
"retake_btn": "🔄 Erneut aufnehmen",
|
"retake_btn": "🔄 Erneut aufnehmen",
|
||||||
"camera_error_hint": "Stelle sicher, dass du HTTPS verwendest und Kameraberechtigungen erteilt hast.<br>Du kannst den Barcode manuell eingeben oder die KI-Identifikation verwenden.",
|
"camera_error_hint": "Stelle sicher, dass du HTTPS verwendest und Kameraberechtigungen erteilt hast.<br>Du kannst den Barcode manuell eingeben oder die KI-Identifikation verwenden.",
|
||||||
"no_barcode": "Kein Barcode",
|
"no_barcode": "Kein Barcode",
|
||||||
"save_new_btn": "🆕 Keines davon — als neu speichern"
|
"save_new_btn": "🆕 Keines davon — als neu speichern",
|
||||||
|
"expiry_found": "Datum gefunden",
|
||||||
|
"expiry_read_fail": "Datum konnte nicht gelesen werden.",
|
||||||
|
"expiry_raw_label": "Erkannt"
|
||||||
},
|
},
|
||||||
"lowstock": {
|
"lowstock": {
|
||||||
"title": "⚠️ Wird knapp!",
|
"title": "⚠️ Wird knapp!",
|
||||||
|
|||||||
@@ -1034,7 +1034,10 @@
|
|||||||
"retake_btn": "🔄 Retake",
|
"retake_btn": "🔄 Retake",
|
||||||
"camera_error_hint": "Ensure you use HTTPS and have granted camera permissions.<br>You can enter the barcode manually or use AI identification.",
|
"camera_error_hint": "Ensure you use HTTPS and have granted camera permissions.<br>You can enter the barcode manually or use AI identification.",
|
||||||
"no_barcode": "No barcode",
|
"no_barcode": "No barcode",
|
||||||
"save_new_btn": "🆕 None of these — save as new"
|
"save_new_btn": "🆕 None of these — save as new",
|
||||||
|
"expiry_found": "Date found",
|
||||||
|
"expiry_read_fail": "Cannot read the date.",
|
||||||
|
"expiry_raw_label": "Read"
|
||||||
},
|
},
|
||||||
"lowstock": {
|
"lowstock": {
|
||||||
"title": "⚠️ Running low!",
|
"title": "⚠️ Running low!",
|
||||||
|
|||||||
@@ -1034,7 +1034,10 @@
|
|||||||
"retake_btn": "🔄 Riscatta",
|
"retake_btn": "🔄 Riscatta",
|
||||||
"camera_error_hint": "Assicurati di usare HTTPS e di aver concesso i permessi della fotocamera.<br>Puoi inserire il barcode manualmente o usare l'identificazione AI.",
|
"camera_error_hint": "Assicurati di usare HTTPS e di aver concesso i permessi della fotocamera.<br>Puoi inserire il barcode manualmente o usare l'identificazione AI.",
|
||||||
"no_barcode": "Senza barcode",
|
"no_barcode": "Senza barcode",
|
||||||
"save_new_btn": "🆕 Non è nessuno di questi — salva come nuovo"
|
"save_new_btn": "🆕 Non è nessuno di questi — salva come nuovo",
|
||||||
|
"expiry_found": "Data trovata",
|
||||||
|
"expiry_read_fail": "Non riesco a leggere la data.",
|
||||||
|
"expiry_raw_label": "Letto"
|
||||||
},
|
},
|
||||||
"lowstock": {
|
"lowstock": {
|
||||||
"title": "⚠️ Sta per finire!",
|
"title": "⚠️ Sta per finire!",
|
||||||
|
|||||||
Reference in New Issue
Block a user