// Shop pages — RTW grid, Studio editorial, Product detail
const { useState: useStateS, useEffect: useEffectS, useMemo } = React;
function ProductCard({ p, onOpen, currency }) {
const [hover, setHover] = useStateS(false);
return (
setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={() => onOpen(p)}
>
{p.new &&
New In }
{ e.stopPropagation(); onOpen(p); }}>
Quick View
{p.collection}
{p.name}
{fmtPrice(convertPrice(p.price, p.currency, currency), currency)}
);
}
function ShopRTW({ filter, setFilter, currency, openProduct }) {
const [sort, setSort] = useStateS('curated');
const [filterOpen, setFilterOpen] = useStateS(true);
const filtered = useMemo(() => {
let list = [...PRODUCTS_RTW];
if (filter.category) list = list.filter(p => p.category.toLowerCase().replace(/\s+/g,'-') === filter.category);
if (filter.collection) list = list.filter(p => p.collection.toLowerCase().replace(/\s+/g,'-') === filter.collection);
if (sort === 'price-asc') list.sort((a,b) => convertPrice(a.price,a.currency,'USD') - convertPrice(b.price,b.currency,'USD'));
if (sort === 'price-desc') list.sort((a,b) => convertPrice(b.price,b.currency,'USD') - convertPrice(a.price,a.currency,'USD'));
return list;
}, [filter, sort]);
return (
{filtered.length} pieces · live from the atelier
Ready to Wear
Pret-a-porter from the Mrs. Keepa house — eclectic, occasion-led silhouettes drawn from travel and vintage glamour, made in the Dubai workshop.
setFilterOpen(v => !v)}>
{filterOpen ? 'Hide' : 'Show'} Filters
{filter.category || filter.collection ? (
<>
Filtered by
{filter.category && setFilter({...filter, category: null})}>
{CATEGORIES_RTW.find(c => c.slug === filter.category)?.name} ✕
}
{filter.collection && setFilter({...filter, collection: null})}>
{COLLECTIONS.find(c => c.slug === filter.collection)?.name} ✕
}
>
) : All pieces }
Sort
setSort(e.target.value)}>
Curated
Price · low to high
Price · high to low
{filterOpen && (
Category
setFilter({...filter, category: null})}>
All {PRODUCTS_RTW.length}
{CATEGORIES_RTW.map(c => (
setFilter({...filter, category: c.slug})}
>
{c.name} {c.count}
))}
Collection
{COLLECTIONS.filter(c => !c.studio).map(c => (
setFilter({...filter, collection: c.slug})}
>
{c.name} {c.count}
))}
Price
Under $400
$400 – $800
$800 – $1,500
Above $1,500
Size
{['XS','S','M','L','XL','One Size'].map(s =>
{s}
)}
)}
{filtered.map(p => (
))}
{filtered.length === 0 && (
Nothing matches just yet.
Try fewer filters, or get in touch with the atelier directly.
)}
);
}
function StudioPage({ currency, openProduct, openAppt }) {
return (
Studio · Couture · Inhale
A breath, held in fabric.
Mrs. Keepa's couture line. First formalised for Sotheby's Dubai, made in the atelier, fitted by appointment.
01
Designed to be worn three ways.
Each Inhale piece is constructed so the wearer can decide how, and how much, of it is shown.
02
Cut in Dubai, by hand.
Pieces are patterned and finished in Mariam's d3 workshop. Small-scale, attributed quote — see the materials notes.
03
Made to measure.
Studio is appointment-led. Off-the-rack pieces exist; the closer the cut, the longer the conversation.
Inhale · presented at Sotheby's Dubai
The collection.
{PRODUCTS_STUDIO.map(p => (
openProduct(p)}>
{p.category}
{p.name}
{p.description}
{fmtPrice(convertPrice(p.price, p.currency, currency), currency)}
Enquire →
))}
Mariam, on the workshop
" The atelier is small-scale, zero-waste, local. Every off-cut becomes the next piece — or a cushion for the home."
Attributed to Mariam Yeya, in conversation with Sotheby's Dubai, Feb 2024.
);
}
function ProductOverlay({ product, onClose, currency, addToBag }) {
const [size, setSize] = useStateS(null);
const [color, setColor] = useStateS(null);
const [tab, setTab] = useStateS('description');
const [imgIdx, setImgIdx] = useStateS(0);
useEffectS(() => {
if (product) {
setSize(null);
setColor(product.colorways ? product.colorways[0] : null);
setTab('description');
setImgIdx(0);
}
}, [product]);
if (!product) return null;
const images = [product.img, product.alt].filter(Boolean);
const addable = !product.enquireOnly && size;
return (
<>
✕
{images.length > 1 && (
{images.map((src, i) => (
setImgIdx(i)}>
))}
)}
{product.collection} · {product.category}
{product.name}
{fmtPrice(convertPrice(product.price, product.currency, currency), currency)}
{product.currency !== currency && (
· {fmtPrice(product.price, product.currency)} at source
)}
{product.description}
{product.colorways && product.colorways.length > 0 && (
Colourway · {color}
{product.colorways.map(c => (
setColor(c)}>{c}
))}
)}
{product.enquireOnly ? 'Sizing' : 'Size'}
{product.sizes.map(s => (
setSize(s)}>{s}
))}
{!product.enquireOnly && !size &&
Choose a size to add to bag.
}
{product.enquireOnly ? (
<>
Enquire about this piece
Book a Studio fitting
>
) : (
<>
{ addToBag({ ...product, size, colorway: color || '—', qty: 1 }); onClose(); }}
>
{size ? `Add to Bag — ${fmtPrice(convertPrice(product.price, product.currency, currency), currency)}` : 'Select a size'}
Save to wishlist
>
)}
{['description','composition','shipping'].map(t => (
setTab(t)}>
{t === 'description' ? 'Details' : t === 'composition' ? 'Composition & Care' : 'Shipping'}
))}
{tab === 'description' &&
{product.description} Cut and finished in the Mrs. Keepa atelier, Dubai Design District.
}
{tab === 'composition' &&
{product.composition || 'Composition details available on request — please contact the atelier.'}
}
{tab === 'shipping' &&
Complimentary worldwide express shipping on orders over $400. UAE orders ship same-day. Returns within 14 days of receipt for unworn Ready to Wear; Studio pieces are final sale.
}
>
);
}
function CollectionsPage({ setRoute, currency }) {
return (
Seven worlds, one wardrobe
The Collections.
Each Mrs. Keepa collection holds together by mood, not by season. Fabrics return; silhouettes refine. Pieces are designed to be restyled across collections, year after year.
{COLLECTIONS.map((c, i) => (
setRoute(c.studio ? {page:'studio'} : {page:'shop', filter:{collection: c.slug}})}>
{c.studio ? 'Studio · Couture' : 'Ready to Wear'} · {c.count} pieces
{c.name}
{c.mood}
Enter the collection →
))}
);
}
Object.assign(window, {
ProductCard, ShopRTW, StudioPage, ProductOverlay, CollectionsPage,
});