fix: compound shopping names + auto-Bring on depletion + panna da cucina

1. shopping_name compound-phrase map (computeShoppingName)
   Add phraseMap checked against the full product name BEFORE the single-token
   keyword loop.  Prevents 'pane grattugiato' → 'Pane', 'panna da cucina' → 'Panna', etc.
   Key new phrases:
   - pane/pan grattugiato → Pangrattato
   - panna da cucina / panna cucina / panna chef → Panna da cucina
   - fette biscottate → Fette biscottate
   - aceto balsamico / glassa balsamico → Aceto balsamico
   - latte condensato/evaporato/vegetale/di soia/mandorla/avena/riso/cocco → specific
   - prosciutto cotto → Prosciutto cotto
   - farina di riso/mais/integrale → specific
   - pasta fresca, zucchero di canna, acqua minerale/frizzante/gassata, brodo, …
   Also added single-token safety-net entries: 'grattugiato'/'grattato'/'pangrattato'
   → 'Pangrattato', 'biscottate' → 'Fette biscottate'.

2. DB migration (sqlite3 UPDATE)
   Re-classified 10 products that had wrong shopping_name:
   Pane grattugiato → Pangrattato
   Panna da cucina (×4) → Panna da cucina
   Fette biscottate (×2) → Fette biscottate
   Aceto balsamico (×3) → Aceto balsamico
   Cleared 2 stale Gemini cache entries.

3. showLowStockBringPrompt (app.js)
   When totalRemaining <= 0 (product fully depleted), skip the modal entirely.
   The backend already auto-adds to Bring! on depletion; the JS only asks as a
   fallback if that failed (fire-and-forget async, never blocks the UI).
   The afterCallback (e.g. move-remainder modal, navigate to dashboard) is called
   immediately without user interaction.
This commit is contained in:
dadaloop82
2026-04-27 12:04:48 +00:00
parent 95389ebe87
commit 1a73ed91dd
3 changed files with 92 additions and 2 deletions
+69
View File
@@ -3665,6 +3665,70 @@ function computeShoppingName(string $name, string $category = '', string $brand
fn($w) => mb_strlen($w) > 2 && !in_array($w, $stop)
));
// 0. Compound-phrase map — checked against the FULL lowercase name (stop words included)
// so multi-word product types are classified BEFORE single-token lookup.
// This prevents "Pane grattugiato" → "Pane", "Panna da cucina" → "Panna", etc.
$phraseMap = [
// Breadcrumbs (MUST come before generic "pane")
'pangrattato' => 'Pangrattato',
'pan grattato' => 'Pangrattato',
'pane grattato' => 'Pangrattato',
'pane grattugiato' => 'Pangrattato',
'pan grattugiato' => 'Pangrattato',
// Cooking cream (MUST come before generic "panna")
'panna da cucina' => 'Panna da cucina',
'panna cucina' => 'Panna da cucina',
'panna chef' => 'Panna da cucina',
'panna acida' => 'Panna acida',
// Plant-based milks (MUST come before generic "latte")
'latte condensato' => 'Latte condensato',
'latte evaporato' => 'Latte condensato',
'latte di soia' => 'Latte di soia',
'latte soia' => 'Latte di soia',
'latte vegetale' => 'Latte vegetale',
'latte di mandorla' => 'Latte di mandorla',
'latte mandorla' => 'Latte di mandorla',
'latte di avena' => 'Latte di avena',
'latte avena' => 'Latte di avena',
'latte di riso' => 'Latte di riso',
'latte riso' => 'Latte di riso',
'latte di cocco' => 'Latte di cocco',
'latte cocco' => 'Latte di cocco',
// Baked bakery — different from bread
'fette biscottate' => 'Fette biscottate',
'pan di spagna' => 'Pan di Spagna',
// Specific vinegars
'aceto balsamico' => 'Aceto balsamico',
'glassa balsamico' => 'Aceto balsamico',
'glassa balsamic' => 'Aceto balsamico',
// Cold cuts — specific cuts
'prosciutto cotto' => 'Prosciutto cotto',
// Flour subtypes
'farina di riso' => 'Farina di riso',
'farina riso' => 'Farina di riso',
'farina di mais' => 'Farina di mais',
'farina mais' => 'Farina di mais',
'farina integrale' => 'Farina integrale',
// Fresh pasta
'pasta fresca' => 'Pasta fresca',
// Broth / stock
'brodo vegetale' => 'Brodo',
'brodo pollo' => 'Brodo',
'brodo manzo' => 'Brodo',
// Sugar subtypes
'zucchero di canna' => 'Zucchero di canna',
'zucchero canna' => 'Zucchero di canna',
// Water
'acqua frizzante' => 'Acqua',
'acqua gassata' => 'Acqua',
'acqua minerale' => 'Acqua',
];
foreach ($phraseMap as $phrase => $canonical) {
if (mb_strpos($lower, $phrase) !== false) {
return $canonical;
}
}
// 1. Curated keyword → canonical group name.
// Extended list covers the most common Italian pantry items and avoids Gemini calls.
$keywordMap = [
@@ -3702,6 +3766,11 @@ function computeShoppingName(string $name, string $category = '', string $brand
'piadelle' => 'Piadina',
'biscotto' => 'Biscotti',
'biscotti' => 'Biscotti',
// Breadcrumbs single-token safety net (phrase map has priority, but just in case)
'grattugiato' => 'Pangrattato',
'grattato' => 'Pangrattato',
'pangrattato' => 'Pangrattato',
'biscottate' => 'Fette biscottate',
// Dairy
'latte' => 'Latte',
'yogurt' => 'Yogurt',