Files
amuri_siciliano/index.html
2025-12-12 16:02:44 +00:00

849 lines
35 KiB
HTML

<!doctype html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Amuri Siciliano - Ordini Online</title>
<!-- Tailwind CSS per lo stile -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome per le icone -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
/>
<style>
/* Nascondi scrollbar per i filtri orizzontali */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
/* Animazioni Carrello */
#cart-overlay {
transition: opacity 0.3s ease-in-out;
}
#cart-drawer {
transition: transform 0.3s ease-in-out;
}
/* Utility per nascondere elementi */
.hidden {
display: none !important;
}
/* Animazione bounce sottile per il tasto mobile */
@keyframes bounce-subtle {
0%,
100% {
transform: translateY(-5%);
}
50% {
transform: translateY(5%);
}
}
.animate-bounce-subtle {
animation: bounce-subtle 2s infinite;
}
</style>
</head>
<body class="bg-gray-50 font-sans text-slate-800 pb-20">
<!-- HEADER -->
<header class="bg-orange-600 text-white sticky top-0 z-50 shadow-md">
<div
class="container mx-auto px-4 py-3 flex justify-between items-center"
>
<div class="flex items-center gap-2">
<div
class="bg-white text-orange-600 p-2 rounded-full w-10 h-10 flex items-center justify-center"
>
<i class="fas fa-lemon text-xl"></i>
</div>
<div>
<h1 class="text-xl font-bold leading-none">
Amuri Siciliano
</h1>
<p class="text-xs text-orange-100 opacity-90">
Produttori per Passione
</p>
</div>
</div>
<button
onclick="toggleCart(true)"
class="relative p-2 hover:bg-orange-700 rounded-full transition-colors"
>
<i class="fas fa-shopping-cart text-2xl"></i>
<span
id="header-cart-count"
class="hidden absolute -top-1 -right-1 bg-white text-orange-600 text-xs font-bold w-5 h-5 flex items-center justify-center rounded-full border-2 border-orange-600"
>0</span
>
</button>
</div>
</header>
<!-- INFO BANNER -->
<div
id="info-banner"
class="border-b p-4 text-sm transition-colors bg-orange-50 border-orange-100 text-gray-700"
>
<!-- Contenuto generato via JS -->
</div>
<!-- MAIN CONTENT -->
<main class="container mx-auto px-4 py-6">
<!-- Category Filter -->
<div
id="category-filters"
class="flex gap-2 overflow-x-auto pb-4 mb-2 no-scrollbar"
>
<!-- Pulsanti generati via JS -->
</div>
<!-- Product Grid -->
<div
id="product-grid"
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4"
>
<!-- Prodotti generati via JS -->
</div>
</main>
<!-- MOBILE FLOATING BUTTON -->
<div
id="mobile-cart-btn"
class="hidden fixed bottom-6 right-6 z-40 md:hidden"
>
<button
onclick="toggleCart(true)"
class="bg-orange-600 text-white p-4 rounded-full shadow-2xl flex items-center gap-2 animate-bounce-subtle"
>
<i class="fas fa-shopping-cart"></i>
<span id="mobile-total-price" class="font-bold">€ 0.00</span>
</button>
</div>
<!-- CART DRAWER (MODAL) -->
<div
id="cart-modal"
class="hidden relative z-50"
aria-labelledby="modal-title"
role="dialog"
aria-modal="true"
>
<!-- Overlay -->
<div
id="cart-overlay"
class="fixed inset-0 bg-black/40 backdrop-blur-sm opacity-0"
onclick="toggleCart(false)"
></div>
<!-- Drawer Panel -->
<div
class="fixed inset-0 z-50 flex justify-end pointer-events-none"
>
<div
id="cart-drawer"
class="pointer-events-auto relative w-full max-w-md bg-white h-full shadow-2xl flex flex-col transform translate-x-full"
>
<!-- Drawer Header -->
<div
class="p-4 border-b border-gray-100 flex justify-between items-center bg-orange-50"
>
<h2
class="font-bold text-xl flex items-center gap-2 text-gray-800"
>
<i class="fas fa-shopping-bag text-orange-600"></i>
Il tuo ordine
</h2>
<button
onclick="toggleCart(false)"
class="p-2 hover:bg-white rounded-full transition-colors"
>
<i class="fas fa-times text-gray-500 text-xl"></i>
</button>
</div>
<!-- Drawer Items (Scrollable) -->
<div
id="cart-items-container"
class="flex-grow overflow-y-auto p-4"
>
<!-- Items o Messaggio Vuoto generati via JS -->
</div>
<!-- Drawer Footer -->
<div
id="cart-footer"
class="hidden p-4 border-t border-gray-100 bg-gray-50"
>
<div class="space-y-2 mb-4">
<div class="flex justify-between text-gray-600">
<span>Articoli</span>
<span id="cart-total-items">0</span>
</div>
<div
class="flex justify-between font-bold text-xl text-gray-900 border-t border-gray-200 pt-2"
>
<span>Totale</span>
<span id="cart-total-price">€ 0.00</span>
</div>
</div>
<div id="order-form-container" class="space-y-3">
<!-- Input o Messaggio Blocco generati via JS -->
</div>
</div>
</div>
</div>
</div>
<!-- JAVASCRIPT LOGIC -->
<script>
// --- DATA ---
const products = [
// Agrumi
{
id: 1,
category: "Agrumi",
name: "Arancia Navel da Tavola",
unit: "Cassetta 8kg",
price: 18.0,
},
{
id: 2,
category: "Agrumi",
name: "Arancia Navel da Spremuta",
unit: "Cassetta 8kg",
price: 14.0,
},
{
id: 3,
category: "Agrumi",
name: "Arancia Navel Extra Calibro",
unit: "Cassetta 8kg",
price: 20.0,
},
{
id: 4,
category: "Agrumi",
name: "Arancia Sfusa",
unit: "Al Kg",
price: 2.5,
},
{
id: 5,
category: "Agrumi",
name: "Clementine",
unit: "Cassetta 8kg",
price: 20.0,
},
{
id: 6,
category: "Agrumi",
name: "Clementine (Retina)",
unit: "Retina 2kg",
price: 5.0,
},
{
id: 7,
category: "Agrumi",
name: "Limoni",
unit: "Cassetta 10kg",
price: 25.0,
},
{
id: 8,
category: "Agrumi",
name: "Limoni (Retina)",
unit: "Retina 2kg",
price: 5.0,
},
{
id: 9,
category: "Agrumi",
name: "Pompelmo",
unit: "Al Kg",
price: 3.0,
},
// Frutta
{
id: 10,
category: "Frutta & Altro",
name: "Mele",
unit: "Cassetta 6kg",
price: 13.0,
},
{
id: 11,
category: "Frutta & Altro",
name: "Mandorle",
unit: "500g",
price: 10.0,
},
{
id: 12,
category: "Frutta & Altro",
name: "Pistacchio",
unit: "500g",
price: 10.0,
},
// Conserve
{
id: 13,
category: "Conserve",
name: "Acciughe",
unit: "580g",
price: 14.0,
},
{
id: 14,
category: "Conserve",
name: "Acciughe al Peperoncino",
unit: "580g",
price: 14.0,
},
{
id: 15,
category: "Conserve",
name: "Acciughe (Piccole)",
unit: "160g",
price: 5.0,
},
{
id: 16,
category: "Conserve",
name: "Sgombro",
unit: "720g",
price: 15.0,
},
{
id: 17,
category: "Conserve",
name: "Sardine sotto sale",
unit: "850g",
price: 12.0,
},
{
id: 18,
category: "Conserve",
name: "Tonno",
unit: "540g",
price: 14.0,
},
// Dispensa
{
id: 19,
category: "Dispensa",
name: "Miele d'Arancia",
unit: "500g",
price: 10.0,
},
{
id: 20,
category: "Dispensa",
name: "Origano",
unit: "Conf.",
price: 3.0,
},
{
id: 21,
category: "Dispensa",
name: "Olio EVO (Conf. 2 Litri)",
unit: "2 Litri",
price: 30.0,
},
{
id: 22,
category: "Dispensa",
name: "Olio EVO (Conf. 3 Litri)",
unit: "3 Litri",
price: 45.0,
},
{
id: 23,
category: "Dispensa",
name: "Olive Condite Arrabbiata",
unit: "500g",
price: 6.0,
},
{
id: 24,
category: "Dispensa",
name: "Olive In Salamoia",
unit: "500g",
price: 6.0,
},
];
const categories = [
"Tutti",
...new Set(products.map((p) => p.category)),
];
const timeSlots = [
"11:00 - 11:30",
"11:30 - 12:00",
"12:00 - 12:30",
"12:30 - 13:00",
"13:00 - 13:30",
"13:30 - 14:00",
"14:00 - 14:30",
"14:30 - 15:00",
];
// --- STATE ---
let cart = {}; // Object like { id: { ...product, quantity: 1 } }
let activeCategory = "Tutti";
let marketInfo = {
dateString: "",
shortDate: "",
isBlocked: false,
};
// --- LOGIC: DATE & MARKET STATUS ---
function calculateMarketStatus() {
const today = new Date();
today.setHours(0, 0, 0, 0);
const dayOfWeek = today.getDay(); // 0=Dom, 1=Lun, ..., 4=Gio
// Scegli un giovedì di riferimento per stabilire il ciclo bisettimanale
const referenceThursday = new Date("2025-12-11T00:00:00Z"); // Esempio: 11 Dic 2025 è una settimana di mercato
// Trova il giovedì della settimana corrente o della prossima se già passato
let currentOrNextThursday = new Date(today);
const daysUntilThursday = (4 - dayOfWeek + 7) % 7;
currentOrNextThursday.setDate(
today.getDate() + daysUntilThursday,
);
// Calcola la differenza in settimane dal giovedì di riferimento
const weekDiff = Math.round(
(currentOrNextThursday - referenceThursday) /
(1000 * 60 * 60 * 24 * 7),
);
let targetDate = currentOrNextThursday;
// Se la differenza di settimane è dispari, il mercato è la settimana successiva
if (weekDiff % 2 !== 0) {
targetDate.setDate(targetDate.getDate() + 7);
}
// Se la data del mercato calcolata è già passata (es. oggi è ven/sab/dom),
// il prossimo mercato utile è tra 14 giorni.
if (today > targetDate) {
targetDate.setDate(targetDate.getDate() + 14);
}
const options = {
weekday: "long",
day: "numeric",
month: "long",
};
let dateString = targetDate.toLocaleDateString(
"it-IT",
options,
);
dateString =
dateString.charAt(0).toUpperCase() + dateString.slice(1);
const shortOptions = { day: "numeric", month: "short" };
let shortDate = targetDate.toLocaleDateString(
"it-IT",
shortOptions,
);
// Logica di blocco: blocca solo il mercoledì e giovedì della settimana di mercato effettiva
const daysFromTodayToMarket =
(targetDate - today) / (1000 * 60 * 60 * 24);
const isMarketThisWeek = daysFromTodayToMarket < 7;
const isClosingDay = dayOfWeek === 3 || dayOfWeek === 4; // Mercoledì o Giovedì
const isBlocked = isMarketThisWeek && isClosingDay;
return { dateString, shortDate, isBlocked };
}
// --- RENDERING FUNCTIONS ---
function renderInfoBanner() {
const banner = document.getElementById("info-banner");
const { dateString, isBlocked } = marketInfo;
// Cambia stile se bloccato
if (isBlocked) {
banner.className =
"border-b p-4 text-sm transition-colors bg-red-50 border-red-100 text-red-900";
} else {
banner.className =
"border-b p-4 text-sm transition-colors bg-orange-50 border-orange-100 text-gray-700";
}
const blockedBadge = isBlocked
? `<span class="inline-flex items-center gap-1 bg-red-100 text-red-700 px-2 py-0.5 rounded text-xs font-bold uppercase border border-red-200 ml-2"><i class="fas fa-lock text-xs"></i> Ordini Chiusi</span>`
: "";
const blockedMessage = isBlocked
? `<div class="mt-2 pt-2 border-t border-red-100 text-center text-xs md:text-sm text-red-700 font-medium flex justify-center items-center gap-2">
<i class="fas fa-info-circle"></i>
Gli ordini online sono chiusi il giorno prima della consegna. Ci vediamo al parcheggio!
</div>`
: "";
const iconClass = isBlocked
? "text-red-500"
: "text-orange-600";
banner.innerHTML = `
<div class="container mx-auto flex flex-col gap-3 md:flex-row md:justify-between md:items-center">
<div class="flex items-center gap-2 font-medium">
<i class="fas fa-map-marker-alt ${iconClass}"></i>
<span>Foligno, Parcheggio Stadio</span>
</div>
<div class="flex items-center gap-2 font-medium">
<i class="fas fa-calendar-alt ${iconClass}"></i>
<span>${dateString} | 11:00 - 15:00</span>
${blockedBadge}
</div>
<div class="flex items-center gap-2">
<i class="fas fa-phone ${iconClass}"></i>
<a href="tel:+393272335432" class="underline decoration-orange-300">+39 327 233 5432</a>
</div>
</div>
${blockedMessage}
`;
}
function renderCategories() {
const container = document.getElementById("category-filters");
container.innerHTML = categories
.map((cat) => {
const isActive = activeCategory === cat;
const classes = isActive
? "bg-orange-600 text-white border-orange-600"
: "bg-white text-gray-600 border-gray-200 hover:bg-gray-50";
return `<button onclick="setCategory('${cat}')" class="px-4 py-2 rounded-full whitespace-nowrap text-sm font-medium transition-colors border ${classes}">${cat}</button>`;
})
.join("");
}
function renderProducts() {
const grid = document.getElementById("product-grid");
const filtered =
activeCategory === "Tutti"
? products
: products.filter((p) => p.category === activeCategory);
grid.innerHTML = filtered
.map((product) => {
const quantity = cart[product.id]?.quantity || 0;
let buttonHtml = "";
if (marketInfo.isBlocked) {
buttonHtml = `
<button disabled class="w-full py-2 bg-gray-200 text-gray-500 rounded-lg font-medium flex items-center justify-center gap-2 cursor-not-allowed">
<i class="fas fa-lock"></i> Non disponibile
</button>`;
} else if (quantity === 0) {
buttonHtml = `
<button onclick="updateQuantity(${product.id}, 1)" class="w-full py-2 bg-orange-600 hover:bg-orange-700 text-white rounded-lg font-medium transition-colors flex items-center justify-center gap-2">
<i class="fas fa-plus"></i> Aggiungi
</button>`;
} else {
buttonHtml = `
<div class="flex items-center justify-between bg-white rounded-lg border border-gray-200 p-1 shadow-sm">
<button onclick="updateQuantity(${product.id}, -1)" class="w-10 h-10 flex items-center justify-center text-orange-600 hover:bg-orange-50 rounded-md transition-colors">
<i class="fas fa-minus"></i>
</button>
<span class="font-bold text-lg w-8 text-center">${quantity}</span>
<button onclick="updateQuantity(${product.id}, 1)" class="w-10 h-10 flex items-center justify-center text-orange-600 hover:bg-orange-50 rounded-md transition-colors">
<i class="fas fa-plus"></i>
</button>
</div>`;
}
return `
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden flex flex-col h-full hover:shadow-md transition-shadow">
<div class="p-4 flex-grow">
<div class="flex justify-between items-start mb-1">
<span class="text-xs font-semibold text-orange-600 uppercase tracking-wider bg-orange-50 px-2 py-0.5 rounded">
${product.category}
</span>
</div>
<h3 class="font-bold text-gray-800 text-lg mb-1">${product.name}</h3>
<p class="text-gray-500 text-sm mb-3">${product.unit}</p>
<div class="font-bold text-xl text-gray-900">€ ${product.price.toFixed(2)}</div>
</div>
<div class="p-4 bg-gray-50 border-t border-gray-100">
${buttonHtml}
</div>
</div>`;
})
.join("");
}
function renderCart() {
const items = Object.values(cart);
const totalItems = items.reduce(
(sum, item) => sum + item.quantity,
0,
);
const totalPrice = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0,
);
// Update UI Counters
const countBadge = document.getElementById("header-cart-count");
if (totalItems > 0) {
countBadge.innerText = totalItems;
countBadge.classList.remove("hidden");
document
.getElementById("mobile-cart-btn")
.classList.remove("hidden");
document.getElementById("mobile-total-price").innerText =
`${totalPrice.toFixed(2)}`;
document
.getElementById("cart-footer")
.classList.remove("hidden");
} else {
countBadge.classList.add("hidden");
document
.getElementById("mobile-cart-btn")
.classList.add("hidden");
document
.getElementById("cart-footer")
.classList.add("hidden");
}
document.getElementById("cart-total-items").innerText =
totalItems;
document.getElementById("cart-total-price").innerText =
`${totalPrice.toFixed(2)}`;
// Render Items List
const container = document.getElementById(
"cart-items-container",
);
if (items.length === 0) {
container.innerHTML = `
<div class="h-full flex flex-col items-center justify-center text-gray-400 space-y-4">
<i class="fas fa-shopping-bag text-6xl opacity-20"></i>
<p>Il carrello è vuoto</p>
<button onclick="toggleCart(false)" class="text-orange-600 font-medium hover:underline">
Torna al listino
</button>
</div>`;
} else {
container.innerHTML =
`<div class="space-y-4">` +
items
.map(
(item) => `
<div class="flex justify-between items-center bg-white border border-gray-100 p-3 rounded-lg shadow-sm">
<div class="flex-grow">
<h4 class="font-medium text-gray-800">${item.name}</h4>
<p class="text-xs text-gray-500">${item.unit}</p>
<p class="text-orange-600 font-semibold mt-1">€ ${(item.price * item.quantity).toFixed(2)}</p>
</div>
${
!marketInfo.isBlocked
? `
<div class="flex items-center gap-3 bg-gray-50 p-1 rounded-lg">
<button onclick="updateQuantity(${item.id}, -1)" class="p-1 hover:bg-white rounded shadow-sm text-gray-600">
<i class="fas fa-minus text-xs"></i>
</button>
<span class="font-bold w-4 text-center text-sm">${item.quantity}</span>
<button onclick="updateQuantity(${item.id}, 1)" class="p-1 hover:bg-white rounded shadow-sm text-gray-600">
<i class="fas fa-plus text-xs"></i>
</button>
</div>`
: ""
}
</div>
`,
)
.join("") +
`</div>`;
}
// Render Footer Form area
const formContainer = document.getElementById(
"order-form-container",
);
if (marketInfo.isBlocked) {
formContainer.innerHTML = `
<div class="bg-red-50 border border-red-200 rounded-lg p-4 text-center">
<i class="fas fa-lock mx-auto text-red-500 mb-2 block text-xl"></i>
<h3 class="font-bold text-red-800">Ordini Chiusi</h3>
<p class="text-sm text-red-600 mt-1">
Non è più possibile inviare ordini per ${marketInfo.dateString}. Riprova venerdì per la prossima settimana!
</p>
</div>`;
} else {
const timeOptions = timeSlots
.map((t) => `<option value="${t}">${t}</option>`)
.join("");
formContainer.innerHTML = `
<div class="space-y-3">
<label class="block text-sm font-medium text-gray-700">
Il tuo nome (per il ritiro)
<input type="text" id="customer-name" placeholder="Mario Rossi" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-orange-500 focus:ring focus:ring-orange-200 focus:ring-opacity-50 p-2 border">
</label>
<label class="block text-sm font-medium text-gray-700">
Orario di ritiro indicativo
<select id="pickup-time" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-orange-500 focus:ring focus:ring-orange-200 focus:ring-opacity-50 p-2 border">
${timeOptions}
</select>
</label>
<button onclick="sendOrder()" id="send-btn" class="w-full py-3 px-4 rounded-lg font-bold text-white flex items-center justify-center gap-2 shadow-lg transition-all bg-gray-400 cursor-not-allowed">
<i class="fas fa-paper-plane"></i> Invia Ordine su WhatsApp
</button>
<p id="name-error" class="text-center text-xs text-red-500">Inserisci il nome per inviare l'ordine</p>
</div>
`;
// Add Listener for input validation immediately after rendering
const input = document.getElementById("customer-name");
const btn = document.getElementById("send-btn");
const error = document.getElementById("name-error");
if (input) {
input.addEventListener("input", (e) => {
const val = e.target.value.trim();
if (val.length > 0) {
btn.classList.remove(
"bg-gray-400",
"cursor-not-allowed",
);
btn.classList.add(
"bg-green-500",
"hover:bg-green-600",
"hover:shadow-xl",
);
error.classList.add("hidden");
btn.disabled = false;
} else {
btn.classList.add(
"bg-gray-400",
"cursor-not-allowed",
);
btn.classList.remove(
"bg-green-500",
"hover:bg-green-600",
"hover:shadow-xl",
);
error.classList.remove("hidden");
btn.disabled = true;
}
});
// Trigger once to set initial state
btn.disabled = true;
}
}
}
// --- ACTIONS ---
function setCategory(cat) {
activeCategory = cat;
renderCategories();
renderProducts();
}
function updateQuantity(id, delta) {
if (marketInfo.isBlocked) return;
const product = products.find((p) => p.id === id);
if (!cart[id]) {
if (delta > 0) cart[id] = { ...product, quantity: 0 };
else return;
}
cart[id].quantity += delta;
if (cart[id].quantity <= 0) {
delete cart[id];
}
renderProducts(); // Refresh buttons in grid
renderCart(); // Refresh drawer content
}
function toggleCart(show) {
const modal = document.getElementById("cart-modal");
const overlay = document.getElementById("cart-overlay");
const drawer = document.getElementById("cart-drawer");
if (show) {
modal.classList.remove("hidden");
// Small timeout to allow removing 'hidden' to render before animating opacity
setTimeout(() => {
overlay.classList.remove("opacity-0");
drawer.classList.remove("translate-x-full");
}, 10);
} else {
overlay.classList.add("opacity-0");
drawer.classList.add("translate-x-full");
setTimeout(() => {
modal.classList.add("hidden");
}, 300); // Wait for transition
}
}
function sendOrder() {
const nameInput = document.getElementById("customer-name");
const timeSelect = document.getElementById("pickup-time");
const customerName = nameInput ? nameInput.value.trim() : "";
const pickupTime = timeSelect ? timeSelect.value : "";
if (!customerName) return;
const items = Object.values(cart);
const totalPrice = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0,
);
// Nuovo formato richiesto
let message = `Ciao Amuri Siciliano!\n`;
message += `Vorrei ordinare per Foligno (${marketInfo.shortDate}):\n\n`;
items.forEach((item) => {
message += `- ${item.quantity}x ${item.name} (${item.unit}) - €${(item.price * item.quantity).toFixed(2)}\n`;
});
message += `\nTotale: €${totalPrice.toFixed(2)}\n\n`;
message += `Nome: ${customerName}\n`;
message += `Orario indicativo: ${pickupTime}\n\n`;
message += `Grazie!`;
const encodedMessage = encodeURIComponent(message);
window.open(
`https://wa.me/393272335432?text=${encodedMessage}`,
"_blank",
);
}
// --- INIT ---
window.onload = function () {
marketInfo = calculateMarketStatus();
renderInfoBanner();
renderCategories();
renderProducts();
renderCart();
};
</script>
</body>
</html>