feat: preloader smooth fade ticker; fix asiago shelf life; kiosk 5-lang wizard (ES/FR + Gemini/Bring steps)

Preloader:
- Replace 3D wheel with smooth fade-in ticker queue
- Bigger text (clamp 1.1–1.35rem), green/amber/red per check state
- Previous items fade upward at decreasing opacity
- Wider container (min(96vw,860px)) — no more awkward line-wrapping
- JS already used ticker-item/state-ok/warn/error classes (CSS was missing)

Shelf life — Asiago sottovuoto fix:
- estimateSealedExpiryDaysPHP() and estimateExpiryDays() JS:
  asiago/fontina/emmental/gruyere/scamorza now grouped with hard cheeses (60d base)
  vacuum sealed: 60 × 2.5 = 150 days — correct for fridge + sottovuoto
- Cleared stale opened_shelf_cache entry for 'Formaggio Asiago fresco'

Kiosk wizard:
- 5 languages: values-es/ and values-fr/ created (97 strings each)
- values/, values-it/, values-de/: complete rewrite with new keys
  (ble_connecting, ble_connecting_to, summary_scale_ok/warn, Gemini/Bring step strings)
  stepDone hardcoded Italian → @string refs; screensaver nav → @string/setup_step_back/next
- SetupActivity.kt: steps 0-8 fully implemented; ES/FR language selection;
  auto-skip Gemini/Bring if already configured; buildSummary() localised;
  finishSetup() sends gemini_api_key + bring_email/password; BLE connecting
  strings localised; scale summary lines use R.string
This commit is contained in:
dadaloop82
2026-05-17 16:23:22 +00:00
parent 47ce849311
commit 9541e3a385
11 changed files with 818 additions and 192 deletions
+14 -13
View File
@@ -1601,7 +1601,7 @@ function estimateExpiryDays(product, location) {
else if (/yogurt/.test(name)) days = 21;
else if (/mozzarella|burrata|stracciatella/.test(name)) days = 5;
else if (/formaggio\s+(fresco|ricotta|mascarpone|stracchino|crescenza)/.test(name)) days = 10;
else if (/parmigiano|grana|pecorino|provolone/.test(name)) days = 60;
else if (/parmigiano|grana|pecorino|provolone|asiago|fontina|emmental|gruyere|scamorza|groviera/.test(name)) days = 60;
else if (/burro/.test(name)) days = 60;
else if (/panna/.test(name)) days = 14;
else if (/prosciutto\s+cotto|mortadella|wurstel/.test(name)) days = 7;
@@ -1741,7 +1741,7 @@ function estimateOpenedExpiryDays(product, location) {
if (/mozzarella|burrata|stracciatella/.test(name)) return 3;
if (/philadelphia|spalmabile/.test(name)) return 7;
if (/formaggio.*(fresco|ricotta|mascarpone|stracchino|crescenza)/.test(name)) return 5;
if (/parmigiano|grana|pecorino|provolone/.test(name)) return 21;
if (/parmigiano|grana|pecorino|provolone|asiago|fontina|emmental|gruyere|scamorza/.test(name)) return 28;
if (/formaggio/.test(name)) return 10;
if (/\bburro\b/.test(name)) return 30;
if (/\bpanna\b/.test(name)) return 4;
@@ -14899,24 +14899,25 @@ async function _runStartupCheck() {
if (spinnerEl) spinnerEl.style.display = 'none';
wrapEl.style.display = '';
// Helper: set progress bar + 3D check wheel
// Helper: set progress bar + fade ticker queue
let _curPct = 0;
const _tickerHistory = [];
const setProgress = (pct, label, state) => {
_curPct = pct;
if (barEl) {
barEl.style.width = pct + '%';
barEl.className = 'preloader-bar' + (state === 'error' ? ' bar-error' : state === 'warn' ? ' bar-warn' : '');
}
const wheelPrev = document.getElementById('check-wheel-prev');
const wheelCurr = document.getElementById('check-wheel-current');
if (wheelCurr) {
const prevText = wheelCurr.textContent.trim();
if (wheelPrev && prevText && prevText !== '\u00a0') wheelPrev.textContent = prevText;
wheelCurr.textContent = label || '';
const sc = state === 'error' ? 'state-error' : state === 'warn' ? 'state-warn' : 'state-ok';
wheelCurr.className = 'check-wheel-current ' + sc;
void wheelCurr.offsetWidth; // re-trigger CSS animation
}
if (!label) return;
const ticker = document.getElementById('check-ticker');
if (!ticker) return;
_tickerHistory.unshift({ label, state: state || 'ok' });
if (_tickerHistory.length > 4) _tickerHistory.pop();
const posClass = ['ticker-current','ticker-prev-1','ticker-prev-2','ticker-prev-3'];
ticker.innerHTML = _tickerHistory.map((item, i) => {
const sc = item.state === 'error' ? 'state-error' : item.state === 'warn' ? 'state-warn' : 'state-ok';
return `<div class="ticker-item ${posClass[i]}${i === 0 ? ' ' + sc : ''}">${item.label}</div>`;
}).join('');
};
// Phase 1: animate 0→15% while fetching (so it never looks stuck)