JWS STORE

import React, { useEffect, useMemo, useState } from “react”;

// ———————————————
// JUICY WINE & SPIRITS — SINGLE‑FILE STOREFRONT
// ———————————————
// ✅ What this includes
// – Age gate (19+) with localStorage
// – Product grid with search, filters, sort
// – Cart drawer with quantity controls
// – Lightweight checkout form (creates a pre‑filled email order OR prints summary)
// – Newsletter capture (front‑end only)
// – Ontario shipping note & compliance copy blocks
// – All text in Canadian English
//
// 🚚 Next steps (when you’re ready):
// – Swap placeholder images with real bottle shots
// – Hook up real checkout (Shopify, Stripe, or LCBO/consignment workflow)
// – Connect inventory + fulfilment rules (Ontario‑only, age‑verified on delivery)
// – Add analytics + pixel events
// ———————————————

// — Helpers —
const currency = (n) => new Intl.NumberFormat(“en-CA”, { style: “currency”, currency: “CAD” }).format(n);
const save = (k, v) => localStorage.setItem(k, JSON.stringify(v));
const load = (k, d) => {
try { const v = JSON.parse(localStorage.getItem(k)); return v ?? d; } catch { return d; }
};

// — Sample Products (replace with real data) —
const PRODUCTS = [
{
id: “lm-tempranillo”,
name: “La Mundial Tempranillo”,
category: “Red”,
abv: 13,
origin: “Spain”,
price: 24.95,
tags: [“Small‑lot”, “Sustainable”],
image: bottleGradient(“#861657”, “#ffa69e”),
blurb: “Juicy red fruit, soft tannins. A crowd‑pleasing, food‑friendly red.”,
},
{
id: “pec-rose”,
name: “PEC Rosé”,
category: “Rosé”,
abv: 12.5,
origin: “Ontario, Canada”,
price: 21.95,
tags: [“From farmers, not factories”, “Dry”],
image: bottleGradient(“#ff758c”, “#ff7eb3”),
blurb: “Strawberry, watermelon, and a crisp mineral finish—made for patios.”,
},
{
id: “small-lot-orange”,
name: “Small‑Lot Orange Wine”,
category: “White/Orange”,
abv: 12,
origin: “Slovenia”,
price: 29.95,
tags: [“Skin‑contact”, “Natural”],
image: bottleGradient(“#ff9966”, “#ff5e62”),
blurb: “Apricot and tea notes with gentle grip. A sommelier favourite.”,
},
{
id: “sparkling-brut”,
name: “Brut Méthode Traditionnelle”,
category: “Sparkling”,
abv: 12,
origin: “France”,
price: 34.95,
tags: [“Celebrate”, “Crisp”],
image: bottleGradient(“#0f2027”, “#2c5364”),
blurb: “Fine mousse, green apple, and brioche. Party‑ready bubbles.”,
},
{
id: “low-abv-spritz”,
name: “Citrus Spritz (Low ABV)”,
category: “Low ABV”,
abv: 4.5,
origin: “Italy”,
price: 17.95,
tags: [“Sessionable”, “Refreshing”],
image: bottleGradient(“#00c6ff”, “#0072ff”),
blurb: “Zesty, lightly bitter, built for long afternoons.”,
},
];

const CATEGORIES = [“All”, “Red”, “Rosé”, “White/Orange”, “Sparkling”, “Low ABV”];

// Generates a nice gradient placeholder image per product
function bottleGradient(from = “#000”, to = “#444”) {
const svg = encodeURIComponent(`

`);
return `data:image/svg+xml;charset=utf-8,${svg}`;
}

export default function JuicyStorefront() {
// Age gate
const [ageOK, setAgeOK] = useState(false);
useEffect(() => {
setAgeOK(Boolean(load(“juicy_age_verified”, false)));
}, []);

const confirmAge = () => { setAgeOK(true); save(“juicy_age_verified”, true); };
const resetAge = () => { setAgeOK(false); save(“juicy_age_verified”, false); };

// Catalogue state
const [query, setQuery] = useState(“”);
const [category, setCategory] = useState(“All”);
const [sort, setSort] = useState(“featured”);

// Cart state
const [cartOpen, setCartOpen] = useState(false);
const [cart, setCart] = useState([]);
useEffect(() => { setCart(load(“juicy_cart”, [])); }, []);
useEffect(() => { save(“juicy_cart”, cart); }, [cart]);

// Newsletter
const [newsEmail, setNewsEmail] = useState(“”);
const [toast, setToast] = useState(“”);
useEffect(() => { if (toast) { const t = setTimeout(() => setToast(“”);, 2500); return () => clearTimeout(t); } }, [toast]);

const filtered = useMemo(() => {
let out = PRODUCTS.filter((p) => (category === “All” ? true : p.category === category));
if (query.trim()) {
const q = query.trim().toLowerCase();
out = out.filter((p) =>
[p.name, p.category, p.origin, p.tags.join(” “), p.blurb].join(” “).toLowerCase().includes(q)
);
}
switch (sort) {
case “price-asc”: out.sort((a, b) => a.price – b.price); break;
case “price-desc”: out.sort((a, b) => b.price – a.price); break;
case “alpha”: out.sort((a, b) => a.name.localeCompare(b.name)); break;
default: break; // featured = as‑is
}
return out;
}, [query, category, sort]);

const subtotal = cart.reduce((s, it) => s + it.price * it.qty, 0);
const hst = +(subtotal * 0.13).toFixed(2);
const total = +(subtotal + hst).toFixed(2);

const addToCart = (p) => {
setCart((c) => {
const i = c.findIndex((x) => x.id === p.id);
if (i >= 0) {
const next = […c];
next[i] = { …next[i], qty: Math.min(99, next[i].qty + 1) };
return next;
}
return […c, { id: p.id, name: p.name, price: p.price, qty: 1 }];
});
setCartOpen(true);
};

const updateQty = (id, delta) => setCart((c) => c.map((it) => it.id === id ? { …it, qty: Math.max(1, it.qty + delta) } : it));
const removeItem = (id) => setCart((c) => c.filter((it) => it.id !== id));
const clearCart = () => setCart([]);

// Checkout form state
const [checkoutOpen, setCheckoutOpen] = useState(false);
const [form, setForm] = useState({
name: “”,
email: “”,
phone: “”,
address: “”,
city: “”,
province: “ON”,
postal: “”,
notes: “”,
agreeAge: false,
});

const validPostal = (p) => /^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z]\s?\d[ABCEGHJ-NPRSTV-Z]\d$/i.test(p.trim());
const formValid = form.name && form.email && form.address && form.city && form.postal && validPostal(form.postal) && form.agreeAge && cart.length > 0;

const buildOrderText = () => {
const lines = [
`JUICY WINE & SPIRITS — New Order Request`,
`Date: ${new Date().toLocaleString(“en-CA”)}`,
“,
`Customer`,
`Name: ${form.name}`,
`Email: ${form.email}`,
`Phone: ${form.phone || “—”}`,
`Address: ${form.address}, ${form.city}, ${form.province} ${form.postal.toUpperCase()}`,
`Notes: ${form.notes || “—”}`,
“,
`Items`,
…cart.map((it) => `- ${it.name} × ${it.qty} @ ${currency(it.price)} = ${currency(it.price * it.qty)}`),
“,
`Subtotal: ${currency(subtotal)}`,
`HST (13%): ${currency(hst)}`,
`Total: ${currency(total)}`,
“,
`Fulfilment: Ontario only. 19+ ID required on delivery or pickup.`,
];
return lines.join(“\n”);
};

const mailtoHref = () => {
const subject = encodeURIComponent(“Juicy Order Request”);
const body = encodeURIComponent(buildOrderText());
return `mailto:[email protected]?subject=${subject}&body=${body}`;
};

const copyOrder = async () => {
try { await navigator.clipboard.writeText(buildOrderText()); setToast(“Order details copied.”); }
catch { setToast(“Couldn’t copy. You can still print or email.”); }
};

const printOrder = () => {
const w = window.open(“”, “_blank”);
if (!w) return;
w.document.write(`

${buildOrderText().replaceAll("<", "<")}

`);
w.document.close();
w.print();
};

return (

{/* Header */}

J

Juicy Wine & Spirits

From farmers, not factories • Ontario deliveries • 19+

{/* Hero */}

Small‑lot bottles, big flavour.

Boutique imports from makers we know by name. Ontario‑only delivery via LCBO consignment and compliant third‑party carriers.

Shop wines

Note: Pricing in CAD. HST calculated at checkout. ID required upon delivery.

{PRODUCTS.slice(0,5).map((p) => (
Bottle
))}

{/* Toolbar */}

setQuery(e.target.value)} placeholder="Search by name, style, or origin…" className="flex-1 rounded-xl border border-slate-300 px-3 py-2 outline-none focus:ring-2 focus:ring-rose-400" />

{/* Grid */}

{filtered.map((p) => (

{p.name}

{p.name}

{p.category}
{currency(p.price)}

{p.blurb}

{p.tags.map((t) => (
{t}
))}
ABV {p.abv}% • {p.origin}

))}

{/* Newsletter */}

Get first dibs on limited drops

Sign up for release alerts, tasting invites, and subscriber‑only packs.

setNewsEmail(e.target.value)} placeholder="[email protected]" className="flex-1 rounded-xl px-3 py-2 text-slate-900 outline-none" />

{/* Footer */}

J

Boutique imports for curious palates. Proudly based in Ontario.

Compliance

Sales fulfilled via LCBO consignment and approved carriers. Delivery in Ontario only. 19+ ID required.

Visit

Tastings by appointment. Pop‑ups and festivals across the GTA and beyond.

Contact

[email protected]
@juicywineandspirits

{/* Cart Drawer */}
{cartOpen && (

setCartOpen(false)} />

)}

{/* Checkout Modal */}
{checkoutOpen && (

setCheckoutOpen(false)} />

Checkout

Your details

setForm({...form,name:e.target.value})} placeholder="Full name" className="w-full rounded-xl border px-3 py-2" />
setForm({...form,email:e.target.value})} placeholder="Email" className="w-full rounded-xl border px-3 py-2" />
setForm({...form,phone:e.target.value})} placeholder="Phone (optional)" className="w-full rounded-xl border px-3 py-2" />