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
- Homepage di un sito istituzionale: sezione "I nostri must-try" sopra la fold
- Newsletter automatica settimanale: top 3 piatti consigliati
- Schermo digitale all'ingresso del locale (kiosk con browser)
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 => ({
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
}[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.