feat: v1.7.9 — category badges, category search, AI guards

- Category badge on every inventory item (icon + label); 'altro' items
  refined asynchronously via new guess_category Gemini endpoint
  (data/category_ai_cache.json) — no AI call when key not configured
- Category search: inventory search now matches by macro-category key
  and translated label (e.g. 'biscotti' finds all cookie items)
- Brand fast-path in guessCategoryFromName (Oreo, Barilla, Lavazza…)
- Fix: duplicate banner alerts — _bannerLoading guard + _queuedItemIds Set
- Fix: mapToLocalCategory with en:dairies (dairi stem added)
- Fix: mapToLocalCategory no longer blocks on 'altro' — falls back to
  guessCategoryFromName(productName) before returning 'altro'
- Fix: 'Tonno all'olio' was resolving to condimenti — moved tonno\b
  check before olio\b in conserve regex block
- AI guards: _refineCategoryBadgesAsync and fetchAllPrices now check
  _geminiAvailable (JS); getShoppingPrice returns no_api_key (PHP)
  when GEMINI_API_KEY is not set — all AI functions are now explicit
This commit is contained in:
dadaloop82
2026-05-11 05:53:15 +00:00
parent 763b7fd057
commit da62647089
10 changed files with 333 additions and 44 deletions
+50
View File
@@ -378,6 +378,51 @@ body {
/* (scan active is defined above in .header-scan-btn:active) */
/* ── Offline / server-unreachable banner ──────────────────────────────── */
.offline-banner {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
background: #dc2626;
color: #fff;
padding: 9px 16px;
font-size: 0.88rem;
font-weight: 600;
position: sticky;
top: 0;
z-index: 950;
box-shadow: 0 2px 8px rgba(220,38,38,0.35);
}
.offline-banner-icon { font-size: 1rem; line-height: 1; }
.offline-banner-text { flex: 1; text-align: center; }
.offline-banner-retry {
background: rgba(255,255,255,0.22);
border: 1px solid rgba(255,255,255,0.55);
color: #fff;
border-radius: 6px;
padding: 3px 11px;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
transition: background 0.15s;
}
.offline-banner-retry:hover { background: rgba(255,255,255,0.38); }
/* When server is offline, block interactions with the main content */
body.server-offline .app-content {
opacity: 0.4;
pointer-events: none;
user-select: none;
transition: opacity 0.3s;
}
body.server-offline .bottom-nav {
opacity: 0.4;
pointer-events: none;
transition: opacity 0.3s;
}
/* Spesa mode banner */
.spesa-mode-banner {
display: flex;
@@ -1098,6 +1143,11 @@ body {
color: #1d4ed8;
}
.badge-category {
background: #f1f5f9;
color: #475569;
}
.badge-qty {
background: #d1fae5;
color: #047857;