// Composants partagés : Header, Footer, CartDrawer, PizzaCard, Toast
const { useState, useEffect, useRef } = React;
const BADGE_LABEL = {
spicy: '🌶 Piquante',
veg: 'Végé',
nouveau: 'Nouveau',
premium: 'Premium',
};
function PizzaCard({ pizza, onAdd }) {
const [added, setAdded] = useState(false);
const [showSaucePicker, setShowSaucePicker] = useState(false);
const [pickedSauces, setPickedSauces] = useState([]);
const eligible = itemHasSauce(pizza);
const toggleSauce = (sid) => {
setPickedSauces(arr => arr.includes(sid) ? arr.filter(x => x !== sid) : [...arr, sid]);
};
const handleAddDirect = () => {
if (eligible) { setShowSaucePicker(true); setPickedSauces([]); return; }
addToCart(pizza.id);
setAdded(true);
onAdd && onAdd(pizza);
setTimeout(() => setAdded(false), 1400);
};
const confirmSauces = () => {
addToCart(pizza.id, { sauceIds: pickedSauces });
setShowSaucePicker(false);
setPickedSauces([]);
setAdded(true);
onAdd && onAdd(pizza);
setTimeout(() => setAdded(false), 1400);
};
const sauceExtra = pickedSauces.reduce((s, sid) => {
const sa = SAUCES.find(x => x.id === sid);
return s + (sa ? sa.price : 0);
}, 0);
return (
{pizza.photo && (
)}
{pizza.badge && (
{BADGE_LABEL[pizza.badge] || pizza.badge}
)}
{pizza.name}
{pizza.italianName}
{formatPrice(pizza.price)}
{!pizza.photo &&
{pizza.ingredients}
}
{showSaucePicker ? (
Choisissez vos sauces
(plusieurs possibles)
{SAUCES.map(s => {
const on = pickedSauces.includes(s.id);
return (
toggleSauce(s.id)}>
{on ? '✓' : '+'}
{s.label}{s.price > 0 && +{formatPrice(s.price)} }
);
})}
{ setShowSaucePicker(false); setPickedSauces([]); }}>Annuler
Ajouter {pickedSauces.length > 0 && `— ${pickedSauces.length} sauce${pickedSauces.length > 1 ? 's' : ''}`}
{sauceExtra > 0 && ` (+${formatPrice(sauceExtra)})`}
) : (
{added ? <>✓ Ajouté> : <>+ Ajouter au panier>}
)}
);
}
function Header({ activePage, onCartOpen }) {
const [cart] = useCart();
const [navOpen, setNavOpen] = useState(false);
const count = cartCount(cart);
return (
);
}
function Footer() {
return (
);
}
function CartDrawer({ open, onClose }) {
const [cart] = useCart();
const [serviceMode, setServiceMode] = useServiceMode();
const [editingSauceUid, setEditingSauceUid] = useState(null);
const [checkoutOpen, setCheckoutOpen] = useState(false);
const [confirmed, setConfirmed] = useState(false);
const [orderInfo, setOrderInfo] = useState({ name: '', phone: '', district: '', table: '', notes: '' });
const [orderNumber, setOrderNumber] = useState(null);
useEffect(() => {
if (!open) return;
const handler = (e) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', handler);
return () => document.removeEventListener('keydown', handler);
}, [open, onClose]);
useEffect(() => {
if (open) document.body.style.overflow = 'hidden';
else document.body.style.overflow = '';
return () => { document.body.style.overflow = ''; };
}, [open]);
const subtotal = cartSubtotal(cart);
const fee = serviceFee(serviceMode);
const total = subtotal + fee;
const toggleLineSauce = (uid, sauceId) => {
const line = cart.find(l => l.uid === uid);
if (!line) return;
const current = lineSauces(line);
const next = current.includes(sauceId)
? current.filter(x => x !== sauceId)
: [...current, sauceId];
setLineSauces(uid, next);
};
return (
<>
Votre panier
✕
{cart.length === 0 ? (
Mamma mia !
Votre panier est vide. Ajoutez quelques bonnes choses 🍕
) : cart.map(line => {
const item = ITEMS.find(i => i.id === line.itemId);
if (!item) return null;
const sauceIds = lineSauces(line);
const unit = lineUnitPrice(line);
return (
{item.name}
{itemHasSauce(item) && (
editingSauceUid === line.uid ? (
{SAUCES.map(s => {
const on = sauceIds.includes(s.id);
return (
toggleLineSauce(line.uid, s.id)}>
{on ? '✓' : '+'}
{s.label}{s.price > 0 && +{formatPrice(s.price)} }
);
})}
setEditingSauceUid(null)}>Terminer
) : (
setEditingSauceUid(line.uid)}>
{sauceIds.length > 0 ? (
<>🥫 Sauces : {sauceIds.map(sid => (SAUCES.find(s => s.id === sid) || {}).label).filter(Boolean).join(', ')} modifier >
) : (
<>🥫 + Ajouter des sauces >
)}
)
)}
{formatPrice(unit)} × {line.qty} = {formatPrice(unit * line.qty)}
setLineQuantity(line.uid, line.qty - 1)} aria-label="Diminuer">−
{line.qty}
setLineQuantity(line.uid, line.qty + 1)} aria-label="Augmenter">+
);
})}
{cart.length > 0 && (
<>
Mode de service
{SERVICE_MODES.map(m => (
setServiceMode(m.id)}>
{m.icon}
{m.label}
{m.fee > 0 && +{formatPrice(m.fee)} }
))}
Sous-total
{formatPrice(subtotal)}
{fee > 0 && (
Frais de livraison
{formatPrice(fee)}
)}
>
)}
Total
{formatPrice(total)}
setCheckoutOpen(true)}>
{cart.length === 0 ? 'Panier vide' : 'Passer commande'}
{checkoutOpen && (
!confirmed && setCheckoutOpen(false)}>
e.stopPropagation()}>
{confirmed ? (
✓
Grazie mille !
Votre commande est enregistrée
Numéro de commande : {orderNumber}
{serviceMode === 'livraison' && <>Livraison estimée : 30–40 min Nous vous appelons en arrivant.>}
{serviceMode === 'emporter' && <>Prête à emporter dans 15–20 min Présentez votre numéro au comptoir.>}
{serviceMode === 'sur-place' && <>Commande envoyée pour la table {orderInfo.table} . Nos équipes vous l'apportent !>}
{
localStorage.removeItem('pizzeria-ilyes-cart');
window.dispatchEvent(new CustomEvent('cart-changed'));
setConfirmed(false);
setOrderNumber(null);
setCheckoutOpen(false);
onClose();
}}>Fermer
) : (
<>
Récapitulatif de commande
setCheckoutOpen(false)}>✕
Vos articles
{cart.map(line => {
const it = ITEMS.find(i => i.id === line.itemId);
if (!it) return null;
return (
{line.qty}× {it.name}{lineSauces(line).length > 0 ? ` — sauces : ${lineSauces(line).map(sid => (SAUCES.find(x => x.id === sid) || {}).label).filter(Boolean).join(', ')}` : ''}
{formatPrice(lineUnitPrice(line) * line.qty)}
);
})}
Mode : {(SERVICE_MODES.find(m => m.id === serviceMode) || {}).label}
Sous-total {formatPrice(subtotal)}
{fee > 0 &&
Frais de livraison {formatPrice(fee)}
}
Total {formatPrice(total)}
{
const newOrderNumber = 'ILY-' + Math.floor(1000 + Math.random() * 9000);
setOrderNumber(newOrderNumber);
setConfirmed(true);
// Construction du message WhatsApp
const lines = [];
lines.push('🍕 *NOUVELLE COMMANDE — Pizzeria ILYES*');
lines.push('');
lines.push('📋 N° : *' + newOrderNumber + '*');
lines.push('');
lines.push('🛒 *Articles :*');
cart.forEach(line => {
const item = ITEMS.find(i => i.id === line.itemId);
if (!item) return;
const unit = lineUnitPrice(line);
lines.push('• ' + item.name + ' × ' + line.qty + ' — ' + formatPrice(unit * line.qty));
const sauceIds = lineSauces(line);
if (sauceIds.length > 0) {
const sauceNames = sauceIds.map(sid => (SAUCES.find(s => s.id === sid) || {}).label).filter(Boolean).join(', ');
lines.push(' Sauces : ' + sauceNames);
}
});
lines.push('');
if (serviceMode === 'sur-place') {
lines.push('🍽️ *Mode : Sur place*');
lines.push('Table : *' + orderInfo.table + '*');
} else if (serviceMode === 'a-emporter') {
lines.push('🥡 *Mode : À emporter*');
lines.push('Client : ' + orderInfo.name);
if (orderInfo.phone) lines.push('Téléphone : ' + orderInfo.phone);
} else if (serviceMode === 'livraison') {
lines.push('🛵 *Mode : Livraison*');
lines.push('Client : ' + orderInfo.name);
lines.push('Téléphone : ' + orderInfo.phone);
lines.push('Quartier : ' + orderInfo.district);
}
lines.push('');
lines.push('💰 Sous-total : ' + formatPrice(subtotal));
if (fee > 0) lines.push('🛵 Livraison : ' + formatPrice(fee));
lines.push('💰 *TOTAL : ' + formatPrice(total) + '*');
if (orderInfo.notes && orderInfo.notes.trim()) {
lines.push('');
lines.push('📝 Notes : ' + orderInfo.notes);
}
const message = encodeURIComponent(lines.join('\n'));
const whatsappURL = 'https://wa.me/213771787473?text=' + message;
// Ouvre WhatsApp dans un nouvel onglet (mobile = app, desktop = web)
window.open(whatsappURL, '_blank');
}}>
Confirmer la commande — {formatPrice(total)}
>
)}
)}
>
);
}
function Toast({ message, show }) {
return (
✓ {message}
);
}
function useToast() {
const [toast, setToast] = useState({ message: '', show: false });
const timerRef = useRef();
const showToast = (message) => {
setToast({ message, show: true });
clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => setToast(t => ({ ...t, show: false })), 2200);
};
return [toast, showToast];
}
Object.assign(window, {
PizzaCard, Header, Footer, CartDrawer, Toast, useToast,
});