/* LEMONA storefront — shared customer pieces: sign-in modal, session hook and the wishlist heart. Loaded by index.html, product.html, category.html and account.html so there is ONE sign-in dialog and ONE wishlist path across the whole storefront. Authentication is the existing customer session (?r=customer_login / customer_register / customer_me) — no second login system, no separate customer store. */ (function () { const NS = window.LemonaSportswearDesignSystem_c8f307; const { Button, Input, Modal, Alert, IconButton, Icon, Badge } = NS; const Store = window.LemonaStore; /* ---------------- session hook ---------------- Every screen shares one source of truth: Store's cached customer + wishlist. */ function useCustomer() { const [customer, setCustomer] = React.useState(Store.currentCustomer()); const [ready, setReady] = React.useState(false); const [wishlist, setWishlist] = React.useState(Store.getWishlist()); React.useEffect(() => { let live = true; Store.customerMe() .then((c) => { if (live) setCustomer(c); return Store.loadWishlist(); }) .catch(() => {}) .finally(() => { if (live) setReady(true); }); const sync = (e) => { const d = (e && e.detail) || {}; setWishlist(d.items || Store.getWishlist()); setCustomer(Store.currentCustomer()); }; window.addEventListener("lemona:wishlist-changed", sync); window.addEventListener("lemona:customer-changed", sync); return () => { live = false; window.removeEventListener("lemona:wishlist-changed", sync); window.removeEventListener("lemona:customer-changed", sync); }; }, []); const refresh = React.useCallback(async () => { const c = await Store.customerMe({ force: true }); setCustomer(c); await Store.loadWishlist({ force: true }); setWishlist(Store.getWishlist()); try { window.dispatchEvent(new CustomEvent("lemona:customer-changed", { detail: { customer: c } })); } catch (e) { /* ignore */ } return c; }, []); return { customer, ready, wishlist, refresh, signedIn: !!customer }; } /* ---------------- sign-in / create account ---------------- One dialog, reused wherever a signed-in customer is required. `reason` explains why it opened, so the wishlist and checkout prompts read naturally. */ function SignInModal({ open, reason, onClose, onSignedIn }) { const [mode, setMode] = React.useState("signin"); const [f, setF] = React.useState({ name: "", email: "", phone: "", password: "" }); const [err, setErr] = React.useState(null); const [busy, setBusy] = React.useState(false); const set = (k) => (e) => setF({ ...f, [k]: e.target.value }); React.useEffect(() => { if (open) { setErr(null); setF({ name: "", email: "", phone: "", password: "" }); } }, [open]); if (!open) return null; const submit = async (e) => { if (e) e.preventDefault(); setErr(null); setBusy(true); try { const c = mode === "signin" ? await Store.customerLogin(f.email.trim(), f.password) : await Store.customerRegister({ name: f.name.trim(), email: f.email.trim(), phone: f.phone.trim() || null, password: f.password }); // Anything hearted before accounts existed in this browser is offered up once. const pending = Store.pendingGuestWishlist(); if (pending.length) { try { await Store.mergeGuestWishlist(); } catch (e2) { Store.discardGuestWishlist(); } } onSignedIn && onSignedIn(c); } catch (e2) { setErr(e2.message); } finally { setBusy(false); } }; return (
{reason ? : null} {err ? {err} : null} {mode === "register" ? ( ) : null}

{mode === "signin" ? "New to Lemona? " : "Already have an account? "}

); } /* ---------------- wishlist heart ---------------- Guests never write: the gate opens the sign-in dialog instead, and the product they wanted is added automatically once they are in. */ function WishlistHeart({ product, size = 20, className = "cust__heart", onRequireSignIn }) { const [on, setOn] = React.useState(() => Store.isWishlisted(product && product.id)); const [busy, setBusy] = React.useState(false); React.useEffect(() => { const sync = () => setOn(Store.isWishlisted(product && product.id)); sync(); window.addEventListener("lemona:wishlist-changed", sync); return () => window.removeEventListener("lemona:wishlist-changed", sync); }, [product && product.id]); const click = async (e) => { e.preventDefault(); e.stopPropagation(); if (!product || product.id == null || busy) return; setBusy(true); try { const r = await Store.toggleWishlist(product); if (r && r.error === "SIGN_IN_REQUIRED") { onRequireSignIn && onRequireSignIn(product); return; } setOn(!!(r && r.added)); } catch (e2) { /* surfaced by the caller's toast if it wants to */ } finally { setBusy(false); } }; return ( ); } /* ---------------- gate controller ---------------- Holds the sign-in dialog for a page and remembers what the visitor was trying to do, so the action completes itself after sign-in. Cart is never touched. */ function useSignInGate({ onSignedIn } = {}) { const [gate, setGate] = React.useState(null); // { reason, then } const require = (reason, then) => setGate({ reason, then }); const close = () => setGate(null); const done = async (c) => { const pending = gate && gate.then; setGate(null); if (onSignedIn) await onSignedIn(c); if (pending) { try { await pending(c); } catch (e) { /* ignore */ } } }; const modal = ; return { require, modal, open: !!gate }; } /* ---------------- checkout notice ---------------- There is no checkout page yet (Razorpay and the order flow are a later phase), so the bag drawers show this instead of navigating anywhere. It is display-only: it never reads, writes or clears the cart. */ function CheckoutSoonModal({ open, onClose }) { if (!open) return null; return (
We're preparing secure online checkout for Lemona. Please check back soon — everything in your bag is saved.
); } Object.assign(window, { useCustomer, SignInModal, WishlistHeart, useSignInGate, CheckoutSoonModal }); })();