import React, { useState, useEffect, useMemo } from "react";
import {
ShoppingCart, Heart, Search, X, Plus, Minus, Trash2, Lock,
CheckCircle2, ChevronRight, ChevronLeft, Package,
CreditCard, Truck, Pencil, LogOut, Star, MessageCircle, Copy, Landmark,
User, Mail, KeyRound, Sun, Moon
} from "lucide-react";
/* ---------------------------------------------------------
SHOP ZONE
Design tokens:
bg #FFFFFF surface #FFF6EE chip #FFE4CC
text #211A14 textDim #8C7C6D border #F2DFC9
accent #FF6A1A
Display: Space Grotesk | Body: Inter | Data/mono: IBM Plex Mono
--------------------------------------------------------- */
const FONT_LINK_ID = "sz-fonts";
function useFonts() {
useEffect(() => {
if (document.getElementById(FONT_LINK_ID)) return;
const link = document.createElement("link");
link.id = FONT_LINK_ID;
link.rel = "stylesheet";
link.href =
"https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap";
document.head.appendChild(link);
}, []);
}
const ACCENT = "#FF6A1A";
const BANK_DETAILS = {
whatsapp: "08086061771",
accountNumber: "2079040532",
accountName: "Akerele James Ayomide",
bankName: "Kuda MFB",
};
const ADMIN_PASSWORD = "@ayomidey10";
const LIGHT = {
bg: "#FFFFFF",
surface: "#FFF6EE",
surfaceAlt: "#FFFFFF",
chip: "#FFE4CC",
border: "#F2DFC9",
text: "#211A14",
textDim: "#8C7C6D",
solidBg: "#211A14",
solidText: "#FFFFFF",
headerBg: "#FFFFFFEE",
overlayChip: "#FFFFFFCC",
scrollbar: "#E8D9C8",
};
const DARK = {
bg: "#0D0D12",
surface: "#1A1611",
surfaceAlt: "#221C16",
chip: "#2E2519",
border: "#3D3020",
text: "#F5EFE7",
textDim: "#B8A996",
solidBg: "#F5EFE7",
solidText: "#0D0D12",
headerBg: "#0D0D12EE",
overlayChip: "#0D0D12CC",
scrollbar: "#3D3020",
};
const money = (n) => `$${n.toFixed(2)}`;
const SWATCHES = [
["#4C6FFF", "#131A4A"], ["#FF6B5B", "#3E1712"], ["#3DDC97", "#0F3A2A"],
["#FFB84C", "#4A2E0A"], ["#B26CFF", "#2A1550"], ["#4CC9FF", "#0E2A3D"],
];
function Swatch({ product, size = "full" }) {
const colors = product.swatch || SWATCHES[0];
return (
);
}
export default function ShopZoneStore() {
useFonts();
const [products, setProducts] = useState([]);
const [loaded, setLoaded] = useState(false);
const [view, setView] = useState("shop"); // shop | wishlist | admin | account
const [darkMode, setDarkMode] = useState(false);
const t = (darkMode || view === "admin") ? DARK : LIGHT; // admin is always dark
const [search, setSearch] = useState("");
const [cart, setCart] = useState({});
const [wishlist, setWishlist] = useState({});
const [cartOpen, setCartOpen] = useState(false);
const [selectedProduct, setSelectedProduct] = useState(null);
const [checkoutStep, setCheckoutStep] = useState(0); // 0 closed,1 shipping,2 payment,3 done
const [orderNum, setOrderNum] = useState(null);
const [orders, setOrders] = useState([]);
const [processing, setProcessing] = useState(false);
const [paymentMethod, setPaymentMethod] = useState("card");
const [showBankModal, setShowBankModal] = useState(false);
const [shipForm, setShipForm] = useState({ fullName: "", email: "", phone: "", address: "" });
const [shipFormError, setShipFormError] = useState(null);
const [adminAuthed, setAdminAuthed] = useState(false);
const [pwInput, setPwInput] = useState("");
const [pwError, setPwError] = useState(false);
const [editingProduct, setEditingProduct] = useState(null);
const [accounts, setAccounts] = useState([]);
const [session, setSession] = useState(null); // logged-in email
const [toast, setToast] = useState(null);
const showToast = (msg) => {
setToast(msg);
setTimeout(() => setToast(null), 1800);
};
const copyText = (text, label) => {
if (navigator.clipboard) navigator.clipboard.writeText(text).catch(() => {});
showToast(`${label} copied`);
};
// Load persisted state
useEffect(() => {
(async () => {
try {
const [p, c, w, acc, sess, ord, dm] = await Promise.allSettled([
window.storage.get("shopzone:products"),
window.storage.get("shopzone:cart"),
window.storage.get("shopzone:wishlist"),
window.storage.get("shopzone:accounts", true),
window.storage.get("shopzone:session"),
window.storage.get("shopzone:orders", true),
window.storage.get("shopzone:darkmode"),
]);
if (p.status === "fulfilled" && p.value) setProducts(JSON.parse(p.value.value));
if (c.status === "fulfilled" && c.value) setCart(JSON.parse(c.value.value));
if (w.status === "fulfilled" && w.value) setWishlist(JSON.parse(w.value.value));
if (acc.status === "fulfilled" && acc.value) setAccounts(JSON.parse(acc.value.value));
if (sess.status === "fulfilled" && sess.value) setSession(JSON.parse(sess.value.value));
if (ord.status === "fulfilled" && ord.value) setOrders(JSON.parse(ord.value.value));
if (dm.status === "fulfilled" && dm.value) setDarkMode(JSON.parse(dm.value.value));
} catch (e) {
console.log("No prior data yet", e);
}
setLoaded(true);
})();
}, []);
useEffect(() => { if (loaded) window.storage.set("shopzone:products", JSON.stringify(products)).catch(() => {}); }, [products, loaded]);
useEffect(() => { if (loaded) window.storage.set("shopzone:cart", JSON.stringify(cart)).catch(() => {}); }, [cart, loaded]);
useEffect(() => { if (loaded) window.storage.set("shopzone:wishlist", JSON.stringify(wishlist)).catch(() => {}); }, [wishlist, loaded]);
useEffect(() => { if (loaded) window.storage.set("shopzone:session", JSON.stringify(session)).catch(() => {}); }, [session, loaded]);
useEffect(() => { if (loaded) window.storage.set("shopzone:darkmode", JSON.stringify(darkMode)).catch(() => {}); }, [darkMode, loaded]);
const productMap = useMemo(() => Object.fromEntries(products.map((p) => [p.id, p])), [products]);
const filtered = useMemo(() => {
return products.filter((p) => {
if (search.trim() && !p.name.toLowerCase().includes(search.trim().toLowerCase())) return false;
return true;
});
}, [products, search]);
const cartItems = Object.entries(cart)
.filter(([, qty]) => qty > 0)
.map(([id, qty]) => ({ ...productMap[id], qty }))
.filter((i) => i.id);
const cartCount = cartItems.reduce((s, i) => s + i.qty, 0);
const subtotal = cartItems.reduce((s, i) => s + i.price * i.qty, 0);
const shipping = subtotal > 0 && subtotal < 100 ? 8 : 0;
const total = subtotal + shipping;
const wishlistItems = Object.keys(wishlist).filter((id) => wishlist[id] && productMap[id]).map((id) => productMap[id]);
const addToCart = (id, qty = 1) => {
setCart((c) => ({ ...c, [id]: (c[id] || 0) + qty }));
showToast("Added to cart");
};
const setQty = (id, qty) => {
setCart((c) => {
const next = { ...c };
if (qty <= 0) delete next[id];
else next[id] = qty;
return next;
});
};
const toggleWishlist = (id) => setWishlist((w) => ({ ...w, [id]: !w[id] }));
const startCheckout = () => {
if (!session) { setCartOpen(false); setView("account"); showToast("Sign in to check out"); return; }
setCartOpen(false);
setShipForm((f) => ({ ...f, email: f.email || session, fullName: f.fullName || currentAccount?.name || "" }));
setCheckoutStep(1);
};
const proceedToPayment = () => {
if (!shipForm.fullName.trim() || !shipForm.address.trim() || !shipForm.phone.trim() || !shipForm.email.trim()) {
setShipFormError("Please fill in your name, address, phone number, and Gmail address.");
return;
}
if (!/^\S+@gmail\.com$/i.test(shipForm.email.trim())) {
setShipFormError("Please enter a valid Gmail address.");
return;
}
setShipFormError(null);
setCheckoutStep(2);
};
const saveOrders = (next) => {
setOrders(next);
window.storage.set("shopzone:orders", JSON.stringify(next), true).catch(() => {});
};
const notifyBuyer = (email, message) => {
showToast(`🔔 ${message}`);
};
const completeOrder = () => {
setProcessing(true);
setTimeout(() => {
setProcessing(false);
const id = `SZ-${Math.floor(100000 + Math.random() * 900000)}`;
const newOrder = {
id,
email: shipForm.email,
buyerName: shipForm.fullName || currentAccount?.name || "Guest",
phone: shipForm.phone,
address: shipForm.address,
items: cartItems.map((i) => ({ name: i.name, qty: i.qty, price: i.price })),
total,
status: "placed",
createdAt: new Date().toISOString(),
};
saveOrders([newOrder, ...orders]);
notifyBuyer(shipForm.email, `your order ${id} has been placed.`, { orderId: id, status: "placed", buyerName: newOrder.buyerName, total });
setOrderNum(id);
setCheckoutStep(3);
setCart({});
}, 1300);
};
const advanceOrderStatus = (orderId, status) => {
const next = orders.map((o) => (o.id === orderId ? { ...o, status } : o));
saveOrders(next);
const order = orders.find((o) => o.id === orderId);
if (order) {
const msg = status === "shipped" ? `your order ${orderId} has shipped.` : `your order ${orderId} was delivered.`;
notifyBuyer(order.email, msg, { orderId, status, buyerName: order.buyerName, total: order.total });
}
};
const saveProduct = (prod) => {
setProducts((prev) => {
const exists = prev.some((p) => p.id === prod.id);
return exists ? prev.map((p) => (p.id === prod.id ? prod : p)) : [prod, ...prev];
});
setEditingProduct(null);
showToast(prod._isNew ? "Product added" : "Product updated");
};
const deleteProduct = (id) => {
setProducts((prev) => prev.filter((p) => p.id !== id));
showToast("Product removed");
};
const signUp = ({ name, email, password }) => {
if (!name.trim() || !email.trim() || !password) return "Fill in every field.";
if (accounts.some((a) => a.email.toLowerCase() === email.toLowerCase())) return "An account with that email already exists.";
const next = [...accounts, { name, email, password }];
setAccounts(next);
window.storage.set("shopzone:accounts", JSON.stringify(next), true).catch(() => {});
setSession(email);
showToast(`Welcome, ${name}`);
return null;
};
const logIn = ({ email, password }) => {
const acc = accounts.find((a) => a.email.toLowerCase() === email.toLowerCase());
if (!acc || acc.password !== password) return "Incorrect email or password.";
setSession(email);
showToast(`Welcome back, ${acc.name}`);
return null;
};
const logOut = () => { setSession(null); showToast("Signed out"); };
const currentAccount = accounts.find((a) => a.email === session);
return (
{/* Header */}
{view === "shop" && (
<>
Shop
{filtered.length} {filtered.length === 1 ? "item" : "items"}
{filtered.length === 0 ? (
{products.length === 0 ? "No products yet — check back soon." : "Nothing matches that search."}
) : (
{filtered.map((p) => (
setSelectedProduct(p)}>
{p.name}
{money(p.price)}
))}
)}
>
)}
{view === "wishlist" && (
<>
Wishlist
{wishlistItems.length === 0 ? (
Save items you like and they'll show up here.
) : (
{wishlistItems.map((p) => (
setSelectedProduct(p)}>
{p.name}
{money(p.price)}
))}
)}
>
)}
{view === "account" && (
o.email === session)} onLogout={logOut} onSignUp={signUp} onLogIn={logIn} />
)}
{view === "admin" && (
{ if (pwInput === ADMIN_PASSWORD) { setAdminAuthed(true); setPwError(false); } else setPwError(true); }}
onLogout={() => { setAdminAuthed(false); setPwInput(""); }}
products={products}
editingProduct={editingProduct}
setEditingProduct={setEditingProduct}
saveProduct={saveProduct}
deleteProduct={deleteProduct}
orders={orders}
advanceOrderStatus={advanceOrderStatus}
/>
)}
{/* Product detail modal */}
{selectedProduct && (
setSelectedProduct(null)}>
e.stopPropagation()}>
{selectedProduct.name}
{selectedProduct.rating && (
{selectedProduct.rating}
)}
{money(selectedProduct.price)}
)}
{/* Cart drawer */}
{cartOpen && (
setCartOpen(false)}>
e.stopPropagation()}>
Your cart
{cartItems.length === 0 ? (
Your cart is empty.
) : (
{cartItems.map((item) => (
{item.name}
{money(item.price)}
{item.qty}
))}
)}
{cartItems.length > 0 && (
Subtotal{money(subtotal)}
Shipping{shipping === 0 ? "Free" : money(shipping)}
Total{money(total)}
{!session &&
You'll be asked to sign in first.
}
)}
)}
{/* Checkout flow */}
{checkoutStep > 0 && (
{checkoutStep !== 3 && (
{[1, 2].map((s) => (
= s ? ACCENT : t.border }} />))}
)}
{checkoutStep === 1 && (
)}
{checkoutStep === 2 && (
Payment
{paymentMethod === "card" ? (
<>
Demo checkout — no real payment is processed.
>
) : (
Pay by bank transfer — tap below to see the account to pay into.
)}
Total due{money(total)}
)}
{checkoutStep === 3 && (
Order confirmed
Order {orderNum} is on its way.
)}
)}
{/* Bank transfer account details pop-up */}
{showBankModal && (
setShowBankModal(false)}>
e.stopPropagation()}>
Pay by transfer
Transfer {money(total)} to the account below, then confirm in the app.
{[
{ label: "Bank", value: BANK_DETAILS.bankName },
{ label: "Account name", value: BANK_DETAILS.accountName },
{ label: "Account number", value: BANK_DETAILS.accountNumber },
].map((row) => (
))}
Send payment proof on WhatsApp
)}
{toast && (
{toast}
)}
);
}
const STATUS_META = {
placed: { label: "Placed", color: "#8C7C6D" },
shipped: { label: "Shipped", color: "#FFB84C" },
delivered: { label: "Delivered", color: "#3DDC97" },
};
function AccountPage({ t, session, account, orders, onLogout, onSignUp, onLogIn }) {
const [mode, setMode] = useState("login"); // login | signup
const [form, setForm] = useState({ name: "", email: "", password: "" });
const [error, setError] = useState(null);
if (session && account) {
return (
{account.name}
{account.email}
Order emails are sent to this address.
My orders
{orders.length === 0 ? (
No orders yet.
) : (
{orders.map((o) => (
{o.id}
{STATUS_META[o.status].label}
{o.items.map((i) => `${i.qty}× ${i.name}`).join(", ")}
Total{money(o.total)}
))}
)}
);
}
const submit = () => {
setError(null);
const err = mode === "signup" ? onSignUp(form) : onLogIn(form);
if (err) setError(err);
};
return (
{mode === "login" ? "Welcome back" : "Create your account"}
{mode === "login" ? "Log in to check out and track orders." : "Sign up to start shopping at Shop Zone."}
{error &&
{error}
}
);
}
function AdminPanel({ t, authed, pwInput, setPwInput, pwError, onLogin, onLogout, products, editingProduct, setEditingProduct, saveProduct, deleteProduct, orders, advanceOrderStatus }) {
const blank = { id: "", name: "", price: "", rating: "", swatch: SWATCHES[Math.floor(Math.random() * SWATCHES.length)], _isNew: true };
const [form, setForm] = useState(editingProduct || blank);
const [tab, setTab] = useState("products"); // products | orders
useEffect(() => { setForm(editingProduct || blank); }, [editingProduct]);
if (!authed) {
return (
Admin sign-in
Only admins can add products.
setPwInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && onLogin()} placeholder="Password" className="w-full px-3 py-2.5 rounded-lg text-sm outline-none text-center" style={{ background: t.surface, border: `1px solid ${pwError ? "#FF6B5B" : t.border}` }} />
{pwError &&
Incorrect password.
}
);
}
const submit = () => {
if (!form.name.trim() || !form.price) return;
const id = form.id || `p${Date.now()}`;
saveProduct({ ...form, id, price: parseFloat(form.price), _isNew: !form.id });
setForm(blank);
};
return (
Admin
{tab === "products" ? (
{form.id ? "Edit product" : "Add product"}
{products.length === 0 &&
No products yet. Add your first one on the left.
}
{products.map((p) => (
{p.name}
{money(p.price)}
))}
) : (
{orders.length === 0 ? (
No orders yet.
) : orders.map((o) => (
{o.id}
{o.buyerName} · {o.email}
{STATUS_META[o.status].label}
{o.items.map((i) => `${i.qty}× ${i.name}`).join(", ")} — {money(o.total)}
))}
)}
);
}