function loadStored(key,fallback){ try{const raw=window.localStorage.getItem(key);return raw?JSON.parse(raw):fallback;}catch(e){return fallback;} } function App(){ const {Navbar,Footer} = window.IDHDesignSystem_6f4674; // PayDunya redirects the browser back to index.html?paydunya=return|cancel&order=... after // the customer pays (or gives up) on its hosted checkout page — that's a full page reload, // so this is the only place we can pick it up. lastOrder was already saved to localStorage // by Paiement.jsx right before the redirect away, so no extra fetch is needed on return. const paydunyaParam = React.useMemo(()=>new URLSearchParams(window.location.search).get('paydunya'),[]); const [route,setRoute]=React.useState(()=>{ if(paydunyaParam==='return') return 'confirmation'; if(paydunyaParam==='cancel') return 'paiement'; return 'accueil'; }); const [cart,setCart]=React.useState(()=>loadStored('idh_cart',[])); React.useEffect(()=>{ if(paydunyaParam){ window.history.replaceState(null,'',window.location.pathname); } // The order already exists server-side once PayDunya redirects back here (success or not) — // only clear the cart on a completed attempt, so a cancelled payment leaves it intact to retry. if(paydunyaParam==='return'){ setCart([]); } },[paydunyaParam]); const [orderInfo,setOrderInfo]=React.useState(()=>loadStored('idh_order_info',{})); const [lastOrder,setLastOrder]=React.useState(()=>loadStored('idh_last_order',null)); const [selectedProduct,setSelectedProduct]=React.useState(null); const openProduct=(p)=>{setSelectedProduct(p);setRoute('produit');}; React.useEffect(()=>{window.localStorage.setItem('idh_cart',JSON.stringify(cart));},[cart]); React.useEffect(()=>{window.localStorage.setItem('idh_order_info',JSON.stringify(orderInfo));},[orderInfo]); React.useEffect(()=>{window.localStorage.setItem('idh_last_order',JSON.stringify(lastOrder));},[lastOrder]); const [addedToast,setAddedToast]=React.useState(null); const toastTimer=React.useRef(null); const addToCart=(p)=>{ setCart(c=>{ const ex=c.find(it=>it.ref===p.ref); if(ex) return c.map(it=>it.ref===p.ref?{...it,qty:it.qty+1}:it); return [...c,{...p,qty:1}]; }); // Confirms the add right where the user tapped instead of relying on them noticing the cart // badge count change in the navbar (easy to miss, especially scrolled far down a long list). setAddedToast({name:p.name}); if(toastTimer.current) clearTimeout(toastTimer.current); toastTimer.current=setTimeout(()=>setAddedToast(null),4000); }; const updateQty=(ref,qty)=>setCart(c=>c.map(it=>it.ref===ref?{...it,qty}:it)); const removeItem=(ref)=>setCart(c=>c.filter(it=>it.ref!==ref)); const clearCart=()=>setCart([]); const links=[{href:'accueil',label:'Accueil'},{href:'catalogue',label:'Cartouches'},{href:'materiel',label:'Matériel Informatique'},{href:'suivi',label:'Suivre ma commande'},{href:'services',label:'Services'},{href:'contact',label:'Contact'}]; function Icon({name,size=18}){const pascal=name.replace(/(^\w|-\w)/g,c=>c.replace('-','').toUpperCase());const nodes=window.lucide&&window.lucide.icons[pascal];const inner=nodes?nodes.map(([tag,attrs])=>'<'+tag+' '+Object.entries(attrs).map(([k,v])=>k+'="'+v+'"').join(' ')+'/>').join(''):'';const svg=''+inner+'';return React.createElement('span',{style:{display:'inline-flex',width:size,height:size},dangerouslySetInnerHTML:{__html:svg}});} const screens={ accueil:React.createElement(window.Home,{go:setRoute,addToCart,openProduct}), catalogue:React.createElement(window.Catalogue,{go:setRoute,addToCart,openProduct}), materiel:React.createElement(window.Materiel,{go:setRoute,addToCart,openProduct}), produit:React.createElement(window.Produit,{go:setRoute,addToCart,openProduct,product:selectedProduct}), boutique:React.createElement(window.Boutique,{go:setRoute,cart,updateQty,removeItem}), commande:React.createElement(window.Commande,{go:setRoute,cart,orderInfo,setOrderInfo}), paiement:React.createElement(window.Paiement,{go:setRoute,cart,orderInfo,setLastOrder,clearCart,cancelled:paydunyaParam==='cancel'}), confirmation:React.createElement(window.Confirmation,{go:setRoute,lastOrder}), suivi:React.createElement(window.Suivi,{go:setRoute}), services:React.createElement(window.Services,{go:setRoute}), contact:React.createElement(window.Contact,{go:setRoute}) }; return React.createElement('div',{style:{background:'var(--surface-page)',minHeight:'100vh',fontFamily:'var(--font-body)'}}, React.createElement(Navbar,{ logo:React.createElement('img',{src:'assets/logo-full-color.png',style:{height:32}}), links,activeHref:route,cartCount:cart.reduce((s,it)=>s+it.qty,0), cartIcon:React.createElement(Icon,{name:'shopping-cart'}), onLinkClick:setRoute,onCartClick:()=>setRoute('boutique')}), screens[route], addedToast&&React.createElement('div',{ style:{position:'fixed',left:'50%',bottom:24,transform:'translateX(-50%)',zIndex:30, background:'var(--brand-primary)',color:'#fff',borderRadius:'var(--radius-md)', boxShadow:'var(--shadow-lg, 0 8px 24px rgba(0,0,0,0.2))', padding:'14px 16px',display:'flex',alignItems:'center',gap:14,maxWidth:'calc(100vw - 32px)'} }, React.createElement('div',{style:{fontFamily:'var(--font-body)',fontSize:13.5,lineHeight:1.4,minWidth:0,flex:'1 1 auto'}}, React.createElement('div',{style:{fontWeight:700,marginBottom:2}},'✓ Ajouté au panier'), React.createElement('div',{style:{opacity:0.85,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}},addedToast.name) ), React.createElement('button',{type:'button',onClick:function(){setAddedToast(null);setRoute('boutique');}, style:{flexShrink:0,background:'#fff',color:'var(--brand-primary)',border:'none',borderRadius:'var(--radius-sm)', padding:'8px 14px',fontFamily:'var(--font-body)',fontSize:13,fontWeight:700,cursor:'pointer',whiteSpace:'nowrap'} },'Voir le panier') ), React.createElement(Footer,{ logo:React.createElement('div',null, React.createElement('img',{src:'assets/logo-white.png',style:{height:28}}), React.createElement('div',{style:{fontFamily:'var(--font-body)',fontSize:12.5,color:'var(--text-on-navy-muted)',marginTop:12,maxWidth:260,lineHeight:1.5}},'Une seule holding, un interlocuteur unique, un engagement de sérieux.') ), contact:React.createElement('div',null,'Plateau, Immeuble SIDAM',React.createElement('br'),'Rue Gaoussou Ouattara, Abidjan',React.createElement('br'),'+225 07 09 34 33 33',React.createElement('br'),'internationaldjayiholding@gmail.com', React.createElement('a',{href:window.IDH_DATA.facebookUrl,target:'_blank',rel:'noopener noreferrer','aria-label':'IDH sur Facebook',style:{display:'inline-flex',alignItems:'center',justifyContent:'center',width:34,height:34,borderRadius:'var(--radius-sm)',background:'rgba(255,255,255,0.1)',color:'#fff',marginTop:14}}, // Lucide doesn't ship brand/social icons (no "Facebook" entry), so the Icon() lookup used // everywhere else silently rendered an empty here — this is a hand-drawn glyph instead. React.createElement('svg',{width:17,height:17,viewBox:'0 0 24 24',fill:'currentColor'},React.createElement('path',{d:'M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.987C18.343 21.128 22 16.991 22 12z'})))), columns:[ {title:'Produits',links:[{label:'Cartouches & Toners'},{label:'Matériel Informatique'},{label:'Électricité (bientôt)'}]}, {title:'Entreprise',links:[{label:'Services'},{label:'Contact'}]}, {title:'Support',links:[{label:'WhatsApp 7j/7'},{label:'Livraison Abidjan & intérieur'}]}], bottomNote:'© 2026 International Djayi Holding — Fourniture, Innovation, Performance.'})); } ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App));