/* =========================================================================== VESTIRÉ STUDIO — bag, checkout & customer requests -> window Cart · Checkout · Confirmation · My Requests (3 layouts) · Request tracking (3 status treatments) · Profile · Contact =========================================================================== */ const { useState, useEffect, useRef, useMemo } = React; /* ---- BAG / CART ---------------------------------------------------------- */ function CCart({ ctx }) { const cart = ctx.cart; const subtotal = cart.reduce((a, b) => a + b.unitPrice * b.qty, 0); const deposit = cart.some((c) => c.lineType === "rental") ? 1500 : 0; if (cart.length === 0) return (

Your bag is empty

Add a gown to start a rental or purchase request.

ctx.nav("browse")}>Browse the collection
); return (

Your bag

{cart.length} item{cart.length !== 1 ? "s" : ""} · review before submitting your request

{cart.map((c, i) => (

{c.name}

{c.lineType} Size {c.size}{c.lineType === "rental" && c.rentalStart ? " · " + fmtDate(c.rentalStart) + " · " + c.rentalDays + "d" : ""}
{c.qty}
{VS.peso(c.unitPrice * c.qty)}
))}

Summary

Subtotal{VS.peso(subtotal)}
{deposit > 0 &&
Refundable deposit{VS.peso(deposit)}
}
Fitting & steamingIncluded
Estimated total {VS.peso(subtotal + deposit)}

No payment is taken now. Submit your request and we'll confirm availability & payment.

ctx.nav("checkout")}>Continue to request ctx.nav("browse")}>Keep browsing
); } /* ---- CHECKOUT ------------------------------------------------------------ */ function CCheckout({ ctx }) { const u = ctx.currentUser || VS.currentUser; const cart = ctx.cart; const [form, setForm] = useState({ name: u.fullname, email: u.email, phone: u.phone, address: u.address }); const [pay, setPay] = useState("GCash"); const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); const subtotal = cart.reduce((a, b) => a + b.unitPrice * b.qty, 0); const deposit = cart.some((c) => c.lineType === "rental") ? 1500 : 0; if (cart.length === 0) { ctx.nav("browse"); return null; } async function submit() { const req = await ctx.submitRequest(form, pay); if (req) ctx.nav("confirm", { id: req.id }); } return (
ctx.nav("cart")}>Bag

Submit your request

Contact & delivery

This is saved as a snapshot on your request, so future profile edits won't change it.

Payment method

{[["GCash", "phone"], ["Cash", "bag"], ["Credit", "box"]].map(([m, ic]) => ( ))}

Request summary

{cart.map((c, i) => (
{c.name} {c.lineType} · {c.size} · x{c.qty}
{VS.peso(c.unitPrice * c.qty)}
))}
Subtotal{VS.peso(subtotal)}
{deposit > 0 &&
Deposit{VS.peso(deposit)}
}
Total{VS.peso(subtotal + deposit)}
Submit request

You'll be able to track & cancel this from My Requests.

); } /* ---- CONFIRMATION -------------------------------------------------------- */ function CConfirm({ ctx }) { const req = ctx.requests.find((r) => r.id === ctx.route.params.id); if (!req) { ctx.nav("requests"); return null; } return (
Request {req.code}

Your request is in.

We've received your {req.type} request and will confirm availability shortly. Track every step from My Requests.

{req.items.map((it, i) => (
{it.name}{it.lineType} · {it.size}
{VS.peso(it.subtotal)}
))}
ctx.nav("requests")}>Track my requests ctx.nav("browse")}>Continue browsing
); } /* ---- STATUS RENDERER (variation-aware) ---------------------------------- */ function StatusBlock({ req, ui, compact }) { if (ui === "pill") { const order = VS.statusOrder; const idx = req.status === "Cancelled" ? 1 : order.indexOf(req.status); const pct = req.status === "Cancelled" ? 100 : ((idx + 1) / order.length) * 100; return (
{req.status === "Cancelled" ? "—" : Math.round(pct) + "%"}
); } if (ui === "timeline") return ; return ; } /* ---- MY REQUESTS --------------------------------------------------------- */ function CRequests({ ctx }) { const layout = ctx.t.requestsLayout; const ui = ctx.t.statusUI; const cu = ctx.currentUser || VS.currentUser; const mine = ctx.requests.filter((r) => r.userId === cu.id).sort((a, b) => b.createdAt.localeCompare(a.createdAt)); const [filter, setFilter] = useState("All"); const [sel, setSel] = useState(mine[0] ? mine[0].id : null); const FILTERS = { All: () => true, Active: (r) => r.status === "Pending" || r.status === "Processing", Completed: (r) => r.status === "Completed", Cancelled: (r) => r.status === "Cancelled" }; const list = mine.filter(FILTERS[filter]); const header = (
Your account

My requests

ctx.nav("browse")}>New request
{Object.keys(FILTERS).map((f) => { const n = mine.filter(FILTERS[f]).length; return ; })}
); if (mine.length === 0) return (
{header}

No requests yet

ctx.nav("browse")}>Browse the collection
); /* --- LAYOUT: SPLIT (master-detail) --- */ if (layout === "split") { const current = list.find((r) => r.id === sel) || list[0]; return (
{header}
{list.map((r) => ( ))}
{current && }
); } /* --- LAYOUT: TABLE --- */ if (layout === "table") { return (
{header}
{list.map((r) => ( ctx.nav("request", { id: r.id })}> ))}
RequestItemsTypeSubmittedStatusTotal
{r.code}
{r.items.slice(0, 3).map((it, i) => )}{r.items.length} piece{r.items.length !== 1 ? "s" : ""}
{r.type} {fmtDate(r.createdAt)} {VS.peso(r.total)}
); } /* --- LAYOUT: CARDS (default) --- */ return (
{header}
{list.map((r) => (
{r.code}{r.type}
Submitted {fmtDate(r.createdAt)} · {r.payment}
{VS.peso(r.total)}{r.items.length} piece{r.items.length !== 1 ? "s" : ""}
{r.items.map((it, i) => (
{it.name}{it.lineType} · {it.size} · x{it.qty}
))}
{r.status === "Pending" && "Awaiting our confirmation"} {r.status === "Processing" && (r.type === "rental" ? "In preparation · pickup " + fmtDate(r.rentalStart) : "Preparing your gown")} {r.status === "Completed" && "Completed " + fmtDate(r.updatedAt)} {r.status === "Cancelled" && "Cancelled " + fmtDate(r.updatedAt)}
{r.status === "Pending" && ctx.cancelRequest(r.id)}>Cancel} ctx.nav("request", { id: r.id })}>Track
))}
); } /* ---- REQUEST DETAIL CARD (reused in split + full page) ------------------- */ function RequestDetailCard({ req, ctx, ui, embedded }) { return (

{req.code}

{req.type}
Submitted {fmtDate(req.createdAt)} · paid via {req.payment}
{ui === "timeline" ? : }
Pieces in this request {req.items.map((it, i) => (
{it.name} {it.lineType} · size {it.size} · qty {it.qty}{it.lineType === "rental" && req.rentalStart ? " · " + fmtDate(req.rentalStart) + " (" + req.rentalDays + "d)" : ""}
{VS.peso(it.subtotal)}
))}
Delivery to {req.customer.name} {req.customer.address} {req.customer.phone} · {req.customer.email}
Total {VS.peso(req.total)}
ctx.nav("contact")}>Message the studio
{req.status === "Pending" && ctx.cancelRequest(req.id)}>Cancel request} {!embedded && ctx.nav("requests")}>Back to requests}
); } function CRequestDetail({ ctx }) { const req = ctx.requests.find((r) => r.id === ctx.route.params.id); if (!req) { ctx.nav("requests"); return null; } return (
ctx.nav("requests")}>My requests
); } /* ---- PROFILE ------------------------------------------------------------- */ function CProfile({ ctx }) { const u = ctx.currentUser || VS.currentUser; const mine = ctx.requests.filter((r) => r.userId === u.id); const stats = [["Total requests", mine.length], ["Completed", mine.filter((r) => r.status === "Completed").length], ["Active", mine.filter((r) => r.status === "Pending" || r.status === "Processing").length]]; return (

My profile

{initials(u.fullname)}

{u.fullname}

@{u.username} · Member since {fmtDate(u.createdAt)}
ctx.logout()}>Sign out
{stats.map(([l, v]) => (
{v}
{l}
))}
Save changes
); } /* ---- CONTACT ------------------------------------------------------------- */ function CContact({ ctx }) { const me = ctx.currentUser; const cu = me || { fullname: "", email: "" }; const [form, setForm] = useState({ name: cu.fullname, email: cu.email, subject: "", body: "" }); const [sent, setSent] = useState(false); const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value })); function send() { if (!form.body.trim()) return; ctx.addMessage(form); setSent(true); } return (
Get in touch

Visit the atelier

Questions about a fitting, a rental window, or a custom piece? Send a note and we'll reply within a day.

{[["pin", "Door 2, Madapo Hills, Davao City"], ["phone", "+63 912 345 6789"], ["mail", "hello@vestirestudio.com"], ["clock", "Mon-Sat · 10am - 7pm"]].map(([ic, t]) => (
{t}
))}
{!me ? (

Sign in to message us

Messaging the studio requires an account so we can reply to you and keep your conversation on record.

ctx.nav("login")}>Sign in ctx.nav("register")}>Create account
) : (
{sent ? (

Message sent

Thank you, {form.name.split(" ")[0]}. We'll be in touch at {form.email}.

{ setSent(false); setForm((f) => ({ ...f, subject: "", body: "" })); }}>Send another
) : (<>

Send a message