Files
shahikitchen/components/CartDrawer.tsx
T

243 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
/**
* =============================================================================
* CART DRAWER (SLIDE-IN BASKET PANEL)
* =============================================================================
*
* This is the visual "basket" that slides in from the right when the user
* clicks the cart icon in the navbar or the "View Cart" toast action.
*
* KEY DESIGN CHOICES:
* - Fixed position, full-height, max-w-md (beautiful on both mobile and desktop)
* - Backdrop click closes it (standard mobile pattern)
* - z-[990] sits above almost everything (including the sticky category nav) (below mobile menu)
* - All cart mutations go through the context — this component is "dumb" UI only
*
* THE ORDERING FLOW (very important for restaurant context):
* Instead of a traditional Stripe checkout, we generate a pre-filled WhatsApp
* message containing every line item + quantities + grand total.
* This matches the restaurant's current real-world ordering process.
* The number 46739381089 is the same one used in the homepage reservation form.
*
* FUTURE ENHANCEMENTS (documented here so nothing is forgotten):
* - Add "Special requests" textarea per order
* - Show estimated preparation time
* - Allow "Order for later" date/time picker
* - Split "Pickup" vs "Delivery" with different messaging
* - After WhatsApp opens, optionally clear the cart (or keep it — current choice)
*/
import { useCart } from './CartContext';
import Link from 'next/link';
import { useLanguage } from "@/lib/language-context";
import { getTranslation } from "@/lib/translations";
export default function CartDrawer() {
const {
items,
isOpen,
closeCart,
totalPrice,
removeFromCart,
updateQuantity,
clearCart
} = useCart();
const { language } = useLanguage();
const t = getTranslation(language);
/**
* WHATSAPP DEEP LINK ORDERING
*
* Builds a human-readable, copy-paste friendly message that the restaurant staff
* can immediately understand and action.
*
* The format deliberately mirrors how the restaurant currently receives orders
* over the phone or via Instagram DMs.
*/
const orderViaWhatsApp = () => {
if (items.length === 0) return;
const lines = items
.map((item) => `${item.quantity} × ${item.name}${item.price * item.quantity} kr`)
.join('\n');
const now = new Date();
const suggested = new Date(now.getTime() + 30 * 60 * 1000);
// Use locale-appropriate time formatting so the suggested time in the draft feels native
const timeLocale = language === 'sv' ? 'sv-SE' : 'en-GB';
const suggestedTime = suggested.toLocaleTimeString(timeLocale, { hour: '2-digit', minute: '2-digit' });
const msg = t.cartDrawer;
const message =
`${msg.messageHello}
${msg.messageIntro}
${lines}
${msg.messageConfirm.replace('{time}', suggestedTime)}
${msg.messageTotal}: ${totalPrice} kr
${msg.messageThanks}`;
const encoded = encodeURIComponent(message);
// This is the same WhatsApp business number used for table reservations on the homepage
window.open(`https://wa.me/46739381089?text=${encoded}`, '_blank');
};
// Guard clause — drawer only renders when explicitly opened via context
if (!isOpen) return null;
return (
<>
{/* SEMI-TRANSPARENT BACKDROP */}
{/* Clicking anywhere outside the drawer closes it (standard mobile pattern) */}
<div
className="fixed inset-0 bg-black/40 z-[980]"
onClick={closeCart}
/>
{/* THE ACTUAL DRAWER — slides in from right */}
{/* z-[990] ensures it sits above sticky nav, category pills, and most other UI (below mobile menu) */}
<div className="fixed top-0 right-0 h-full w-full max-w-md bg-[#F8F5F0] z-[990] shadow-2xl flex flex-col">
{/* HEADER — Title + item count + quick clear + close button */}
<div className="flex items-center justify-between p-6 border-b border-[#EDE6D9]">
<div className="flex items-center gap-3">
<h2 className="text-2xl tracking-[-0.5px]">{t.cartDrawer.title}</h2>
{items.length > 0 && (
<span className="text-xs px-2.5 py-0.5 rounded-full bg-[#EDE6D9] text-[#6B665F]">
{items.length} {items.length === 1 ? t.cartDrawer.item : t.cartDrawer.items}
</span>
)}
</div>
<div className="flex items-center gap-3">
{items.length > 0 && (
<button
onClick={clearCart}
className="text-xs text-[#B38B4D] hover:underline"
>
{t.cartDrawer.clear}
</button>
)}
<button
onClick={closeCart}
className="text-[#6B665F] hover:text-[#2C2A26] text-2xl leading-none pl-1"
>
×
</button>
</div>
</div>
{/* SCROLLABLE CONTENT AREA */}
{/* Empty state is friendly and routes the user back to the menu */}
<div className="flex-1 overflow-y-auto p-6">
{items.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full text-center">
<div className="text-6xl mb-4">🛒</div>
<p className="text-lg text-[#6B665F]">{t.cartDrawer.empty}</p>
<Link
href="/menu"
onClick={closeCart}
className="mt-6 text-[#B38B4D] hover:underline"
>
{t.cartDrawer.browseMenu}
</Link>
</div>
) : (
<div className="space-y-6">
{items.map((item) => (
<div key={item.id} className="flex gap-4 border-b border-[#EDE6D9] pb-6">
<div className="flex-1">
<div className="flex justify-between">
<div>
<h4 className="font-medium tracking-[-0.3px]">{item.name}</h4>
<p className="text-sm text-[#6B665F]">{item.price} kr × {item.quantity}</p>
</div>
<div className="text-right font-medium">
{(item.price * item.quantity).toFixed(0)} kr
</div>
</div>
{/* Quantity controls */}
<div className="flex items-center gap-3 mt-3">
<button
onClick={() => updateQuantity(item.id, item.quantity - 1)}
className="w-8 h-8 flex items-center justify-center border border-[#EDE6D9] rounded hover:bg-white transition"
>
</button>
<span className="w-6 text-center font-medium">{item.quantity}</span>
<button
onClick={() => updateQuantity(item.id, item.quantity + 1)}
className="w-8 h-8 flex items-center justify-center border border-[#EDE6D9] rounded hover:bg-white transition"
>
+
</button>
<button
onClick={() => removeFromCart(item.id)}
className="ml-auto text-xs text-[#B38B4D] hover:underline"
>
{t.cartDrawer.remove}
</button>
</div>
</div>
</div>
))}
</div>
)}
</div>
{/* STICKY FOOTER — always visible when cart has items */}
{/* Contains the two primary actions: WhatsApp (primary) + Phone (secondary) */}
{items.length > 0 && (
<div className="p-6 border-t border-[#EDE6D9] bg-white">
<button
onClick={clearCart}
className="text-xs text-[#8A8478] hover:text-[#B38B4D] mb-3 underline"
>
{t.cartDrawer.clearBasket}
</button>
<div className="flex justify-between text-lg font-medium mb-4">
<span>{t.cartDrawer.total}</span>
<span>{totalPrice.toFixed(0)} kr</span>
</div>
<p className="text-xs text-center text-[#6B665F] mb-3">
{t.cartDrawer.pickupNote}
</p>
<button
onClick={orderViaWhatsApp}
className="btn-primary w-full py-4 rounded-full text-base tracking-[0.5px] font-medium mb-2 flex items-center justify-center gap-2"
>
{t.cartDrawer.sendViaWhatsApp}
</button>
<button
onClick={() => window.open('tel:031288910', '_self')}
className="btn-outline w-full py-3 rounded-full text-sm tracking-[0.5px] font-medium mb-3"
>
{t.cartDrawer.call}
</button>
<button
onClick={closeCart}
className="w-full text-sm text-[#6B665F] hover:text-[#2C2A26] pt-1"
>
{t.cartDrawer.continueBrowsing}
</button>
<p className="text-[10px] text-center text-[#8A8478] mt-4">
{t.cartDrawer.whatsappHint}
</p>
</div>
)}
</div>
</>
);
}