← Files WixARCHIVED FILE
skills/wix-headless-templates/restaurants/app/components/restaurants/OrderCartDrawer.tsx
5.09 KB · Oct 8, 2026 · 12:02 UTC
// REFERENCE slide-over order cart with quantity stepper, remove, live subtotal, and the
// Wix-hosted checkout. Mount ONCE per page; opens via useOrderCart().openCart(). Correct and
// complete; per the skill's model you design your own chrome on useOrderCart.
import { useOrderCart } from "../../hooks/restaurants/useOrderCart";
import { imgAttrs } from "../../wix/media";
export default function OrderCartDrawer() {
const { cart, open, closeCart, busy, error, updateQuantity, removeLine, checkout } = useOrderCart();
if (!open) return null;
const lines = cart?.lines ?? [];
return (
<div
className="fixed inset-0 z-50 flex justify-end bg-foreground/40 backdrop-blur-[2px]"
onClick={closeCart}
>
<aside
role="dialog"
aria-label="Your order"
onClick={(e) => e.stopPropagation()}
className="flex h-full w-full max-w-md flex-col bg-background text-foreground shadow-2xl"
>
<div className="flex items-center justify-between border-b border-border px-6 py-5">
<span className="text-base font-semibold">
Your order{cart && cart.itemCount > 0 ? ` (${cart.itemCount})` : ""}
</span>
<button
type="button"
aria-label="Close order"
onClick={closeCart}
className="text-xl leading-none text-muted-foreground transition-colors hover:text-foreground"
>
×
</button>
</div>
<div className="flex-1 overflow-y-auto px-6 py-5">
{lines.length === 0 && (
<p className="py-16 text-center text-sm text-muted-foreground">Your order is empty.</p>
)}
{lines.map((line) => (
<div className="mb-6 flex gap-4" key={line.lineItemId}>
{line.imageUrl ? (
<img
{...imgAttrs(line.imageUrl, "4rem")}
alt=""
className="h-16 w-16 flex-shrink-0 rounded-md bg-secondary object-cover"
/>
) : (
<div aria-hidden="true" className="h-16 w-16 flex-shrink-0 rounded-md bg-secondary" />
)}
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">{line.itemName}</p>
{line.descriptionLines.map((d) => (
<p className="mt-0.5 text-xs text-muted-foreground" key={d}>
{d}
</p>
))}
{line.status !== "IN_STOCK" && (
<p className="mt-1 text-xs text-destructive">No longer available</p>
)}
<div className="mt-2 flex items-center">
<div className="inline-flex items-center gap-3 rounded-control border border-border px-2 py-0.5">
<button
type="button"
aria-label="Decrease quantity"
disabled={busy || line.quantity <= 1}
onClick={() => updateQuantity(line.lineItemId, line.quantity - 1).catch(() => {})}
className="px-1 text-base disabled:opacity-40"
>
−
</button>
<span className="text-sm tabular-nums">{line.quantity}</span>
<button
type="button"
aria-label="Increase quantity"
disabled={busy}
onClick={() => updateQuantity(line.lineItemId, line.quantity + 1).catch(() => {})}
className="px-1 text-base disabled:opacity-40"
>
+
</button>
</div>
<button
type="button"
disabled={busy}
onClick={() => removeLine(line.lineItemId).catch(() => {})}
className="ml-3 text-xs text-muted-foreground underline transition-colors hover:text-foreground disabled:opacity-40"
>
Remove
</button>
</div>
</div>
<p className="text-sm font-medium">{line.linePrice}</p>
</div>
))}
</div>
{lines.length > 0 && (
<div className="border-t border-border px-6 py-5">
{cart?.subtotal && (
<div className="mb-4 flex justify-between text-sm">
<span className="text-muted-foreground">Subtotal</span>
<strong className="font-semibold">{cart.subtotal}</strong>
</div>
)}
{error && <p className="mb-3 text-xs text-destructive">{error}</p>}
<button
type="button"
disabled={busy}
onClick={() => checkout().catch(() => {})}
className="w-full rounded-control bg-primary py-3.5 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-50"
>
{busy ? "One moment…" : "Checkout"}
</button>
</div>
)}
</aside>
</div>
);
}
SHA-256: 021c851cd03529e5b2ea599bf3260aa1980efd82deef14ac7055350d079bef24