MenuFacile2.0

Widget Piatti ConsigliatiDEV

Mostra in homepage 3 piatti consigliati che ruotano automaticamente, leggendoli dall'API.

Obiettivo: un widget compatto da mettere in homepage del sito ristorante, che mostra 3 piatti consigliati scelti dal menu MenuFacile. Quando il cliente sul pannello sposta il flag "Consigliato" su un altro piatto, il widget si aggiorna automaticamente entro pochi minuti.

Caso d'uso

Codice (HTML + Vanilla JS)

Self-contained, niente dipendenze. Da incollare ovunque (anche in un widget HTML di Wix/Webflow/Squarespace).

<div id="mf-highlights" class="mf-highlights">
  <h3>I nostri consigli del giorno</h3>
  <div class="mf-highlights-grid"></div>
</div>

<style>
  .mf-highlights {
    max-width: 1080px;
    margin: 2rem auto;
    padding: 1rem;
    font-family: system-ui, -apple-system, sans-serif;
  }
  .mf-highlights h3 {
    text-align: center;
    font-size: 1.5rem;
    margin: 0 0 1.5rem;
  }
  .mf-highlights-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 1rem;
  }
  .mf-highlight-card {
    padding: 1rem;
    border: 1px solid #e5e5e5;
    border-radius: 8px;
    background: #fff;
    transition: transform .2s, box-shadow .2s;
  }
  .mf-highlight-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0,0,0,.06);
  }
  .mf-highlight-card h4 {
    margin: 0 0 .5rem;
    font-size: 1.05rem;
  }
  .mf-highlight-card .desc {
    color: #666;
    font-size: .9rem;
    margin: 0 0 .75rem;
    min-height: 2.7em;
  }
  .mf-highlight-card .price {
    font-weight: 700;
    color: #c0392b;
  }
  .mf-highlight-card .badge {
    display: inline-block;
    background: #fff3cd;
    color: #856404;
    font-size: .75rem;
    padding: 2px 8px;
    border-radius: 4px;
    margin-bottom: .5rem;
  }
</style>

<script>
(function () {
  const TENANT = 'ducatarocco';   // ← cambia con il tuo subdomain
  const LOCALE = 'it';
  const LIMIT  = 3;
  const API = `https://${TENANT}.menufacile.it/api/v1/items?is_recommended=1&locale=${LOCALE}`;

  const grid = document.querySelector('#mf-highlights .mf-highlights-grid');

  function format(price) {
    return '€ ' + Number(price).toFixed(2).replace('.', ',');
  }

  function escapeHtml(s) {
    return String(s).replace(/[&<>"']/g, c => ({
      '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
    }[c]));
  }

  function render(items) {
    if (!items.length) {
      grid.innerHTML = '<p>Nessun piatto consigliato al momento.</p>';
      return;
    }

    grid.innerHTML = items.slice(0, LIMIT).map(item => `
      <article class="mf-highlight-card">
        <span class="badge">⭐ Consigliato</span>
        <h4>${escapeHtml(item.name)}</h4>
        <p class="desc">${escapeHtml(item.description || '')}</p>
        <span class="price">${format(item.price)}</span>
      </article>
    `).join('');
  }

  fetch(API, { headers: { 'Accept': 'application/json' } })
    .then(r => r.json())
    .then(({ success, data }) => {
      if (!success || !Array.isArray(data)) throw new Error('Invalid response');
      render(data);
    })
    .catch(err => {
      console.error('MenuFacile highlights:', err);
      grid.innerHTML = '<p>Caricamento non riuscito.</p>';
    });
})();
</script>

Variante con rotazione random

Se hai 10+ piatti consigliati e vuoi mostrarne 3 a caso ad ogni page view (perché ogni pagina mostri qualcosa di diverso):

function shuffle(arr) {
  return arr.map(x => [Math.random(), x]).sort().map(p => p[1]);
}

// nel .then():
render(shuffle(data));

Variante con cache lato browser

Per evitare di chiamare l'API ad ogni page view della stessa sessione utente:

const CACHE_KEY = `mf_highlights_${TENANT}_${LOCALE}`;
const CACHE_TTL = 5 * 60 * 1000; // 5 minuti

const cached = JSON.parse(sessionStorage.getItem(CACHE_KEY) || 'null');
if (cached && Date.now() - cached.t < CACHE_TTL) {
  render(cached.items);
} else {
  fetch(API)
    .then(r => r.json())
    .then(({ data }) => {
      sessionStorage.setItem(CACHE_KEY, JSON.stringify({ t: Date.now(), items: data }));
      render(data);
    });
}

Considerazioni

Performance

L'API ritorna ~1-3 KB di JSON per i piatti consigliati (filtro server-side ?is_recommended=1). La chiamata si conclude in 50-150 ms. Niente di percepibile dall'utente.

Sicurezza

L'API è read-only senza token: nessun secret da nascondere lato browser. Lo script può essere copiato direttamente sul sito del cliente.

Fallback graceful

Se l'API è giù o rete del cliente è lenta, il widget mostra "Caricamento non riuscito" senza rompere il resto della pagina. Considera di nascondere completamente la sezione in fallback (più elegante):

.catch(() => {
  document.getElementById('mf-highlights').style.display = 'none';
});

Aggiornamento real-time

Lo script chiama l'API ad ogni page load. Se il ristoratore aggiorna i flag "Consigliato" sul pannello, le pagine ricaricate vedono il nuovo set immediatamente (modulo cache CDN).

Hai bisogno di aiuto?

Scrivi a info@menufacile.it.