/* ============================================================
GlowMerald — App shell: router, cart, wishlist, toasts
============================================================ */
const { useState:useStateA, useEffect:useEffectA, useCallback } = React;
const ROUTE_COMPONENTS = {
home:'HomePage', shop:'ShopPage', product:'ProductPage', insight:'InsightPage',
account:'AccountPage', cart:'CartPage', checkout:'CheckoutPage', confirm:'ConfirmPage',
about:'AboutPage', contact:'ContactPage',
};
function priceOf(id, vi=0){ const it=findItem(id); if(it&&it.variants&&it.variants[vi]) return it.variants[vi].price; return it?it.price:0; }
function labelOf(id, vi=0){ const it=findItem(id); if(it&&it.variants&&it.variants.length>1&&it.variants[vi]) return it.variants[vi].label; return it?(it.size||''):''; }
function readHash(){
const h = (location.hash||'').replace(/^#\/?/,'');
if(!h) return { route:'home', param:null };
const [route,param] = h.split('/');
return { route: ROUTE_COMPONENTS[route]?route:'home', param: param||null };
}
function App(){
const init = readHash();
const [route,setRoute] = useStateA(init.route);
const [param,setParam] = useStateA(init.param);
const [cart,setCart] = useStateA(()=>{ try{ return JSON.parse(localStorage.getItem('gm_cart'))||[]; }catch(e){ return []; } });
const [wishlist,setWishlist] = useStateA(()=>{ try{ return JSON.parse(localStorage.getItem('gm_wish'))||['mask']; }catch(e){ return []; } });
const [toasts,setToasts] = useStateA([]);
const [searchOpen,setSearchOpen] = useStateA(false);
useEffectA(()=>{ try{ localStorage.setItem('gm_cart', JSON.stringify(cart)); }catch(e){} },[cart]);
useEffectA(()=>{ try{ localStorage.setItem('gm_wish', JSON.stringify(wishlist)); }catch(e){} },[wishlist]);
useEffectA(()=>{
const onHash=()=>{ const r=readHash(); setRoute(r.route); setParam(r.param); };
window.addEventListener('hashchange',onHash); return ()=>window.removeEventListener('hashchange',onHash);
},[]);
const navigate = useCallback((r, p=null)=>{
setRoute(r); setParam(p);
const hash = '#/'+r+(p?('/'+p):'');
if(location.hash!==hash) history.pushState(null,'',hash);
window.scrollTo({ top:0, behavior:'instant' in document.documentElement.style ? 'instant':'auto' });
},[]);
const toast = useCallback((msg, icon='check')=>{
const id = Math.random().toString(36).slice(2);
setToasts(t=>[...t,{ id, msg, icon }]);
setTimeout(()=>setToasts(t=>t.filter(x=>x.id!==id)), 2600);
},[]);
const addToCart = useCallback((id, qty=1, vi=0)=>{
const key = id+'::'+vi;
setCart(c=>{
const ex = c.find(x=>x.key===key);
if(ex) return c.map(x=>x.key===key?{...x,qty:x.qty+qty}:x);
return [...c,{ key, id, vi, qty }];
});
const it = findItem(id);
toast('Ditambahkan ke keranjang — '+(it?it.name:''),'cart');
},[toast]);
const removeFromCart = useCallback((key)=> setCart(c=>c.filter(x=>x.key!==key)),[]);
const updateQty = useCallback((key,qty)=> setCart(c=>c.map(x=>x.key===key?{...x,qty:Math.max(1,qty)}:x).filter(x=>x.qty>0)),[]);
const clearCart = useCallback(()=>setCart([]),[]);
const toggleWishlist = useCallback((id)=>{
setWishlist(w=>{
if(w.includes(id)){ toast('Dihapus dari wishlist'); return w.filter(x=>x!==id); }
toast('Disimpan ke wishlist','heart'); return [...w,id];
});
},[toast]);
const cartCount = cart.reduce((s,x)=>s+x.qty,0);
const cartTotal = cart.reduce((s,x)=>s+priceOf(x.id,x.vi)*x.qty,0);
const ctx = {
route, param, navigate, cart, cartCount, cartTotal, addToCart, removeFromCart, updateQty, clearCart,
wishlist, toggleWishlist, toast, priceOf, labelOf,
openSearch:()=>setSearchOpen(true),
};
const CompName = ROUTE_COMPONENTS[route] || 'HomePage';
const Comp = window[CompName];
return (
Halaman ini sedang dipersiapkan.
Tidak ada hasil untuk “{q}”.
} {results.map(p=>(