// Wander — App router & shell const SCREENS = { ONBOARDING:'onboarding', MAP:'map', BROWSE:'browse', SAVED:'saved', PROFILE:'profile', DETAIL:'detail', PLAYER:'player', PAYWALL:'paywall', COMPLETION:'completion', SEARCH:'search', OFFLINE:'offline', }; const TWEAKS_DEFAULTS = /*EDITMODE-BEGIN*/{ "density":"comfortable", "brand":"#FFC93C" }/*EDITMODE-END*/; function App() { const [screen, setScreen] = React.useState(SCREENS.ONBOARDING); const [tourId, setTourId] = React.useState('merlion-marina'); const [tours, setTours] = React.useState(window.WANDER_DATA.TOURS); const [tweaks, setTweak] = window.useTweaks ? window.useTweaks(TWEAKS_DEFAULTS) : [TWEAKS_DEFAULTS, ()=>{}]; const dense = tweaks.density==='compact'; // Load tours from API on mount React.useEffect(() => { window.loadTours().then(data => { window.WANDER_DATA.TOURS = data; setTours(data); }); }, []); // Apply brand colour React.useEffect(() => { document.documentElement.style.setProperty('--w-yellow', tweaks.brand); }, [tweaks.brand]); const goTab = (t) => { if (t==='map') setScreen(SCREENS.MAP); if (t==='browse') setScreen(SCREENS.BROWSE); if (t==='saved') setScreen(SCREENS.SAVED); if (t==='profile') setScreen(SCREENS.PROFILE); }; let body; switch (screen) { case SCREENS.ONBOARDING: body = setScreen(SCREENS.MAP)} dense={dense}/>; break; case SCREENS.MAP: body = {setTourId(id);setScreen(SCREENS.DETAIL);}} onTab={goTab} dense={dense} brand={tweaks.brand}/>; break; case SCREENS.BROWSE: body = {setTourId(id);setScreen(SCREENS.DETAIL);}} onTab={goTab} onSearch={()=>setScreen(SCREENS.SEARCH)} dense={dense}/>; break; case SCREENS.SAVED: body = {setTourId(id);setScreen(SCREENS.DETAIL);}} onOpenOffline={()=>setScreen(SCREENS.OFFLINE)} dense={dense}/>; break; case SCREENS.PROFILE: body = ; break; case SCREENS.SEARCH: body = setScreen(SCREENS.BROWSE)} onOpenTour={(id)=>{setTourId(id);setScreen(SCREENS.DETAIL);}}/>; break; case SCREENS.DETAIL: body = setScreen(SCREENS.MAP)} onStart={()=>setScreen(SCREENS.PLAYER)} onPaywall={()=>setScreen(SCREENS.PAYWALL)} dense={dense}/>; break; case SCREENS.PLAYER: body = setScreen(SCREENS.DETAIL)} onComplete={()=>setScreen(SCREENS.COMPLETION)} dense={dense}/>; break; case SCREENS.PAYWALL: body = setScreen(SCREENS.DETAIL)} onSuccess={()=>setScreen(SCREENS.PLAYER)}/>; break; case SCREENS.COMPLETION: body = setScreen(SCREENS.MAP)}/>; break; case SCREENS.OFFLINE: body = setScreen(SCREENS.SAVED)}/>; break; default: body = null; } const labels = { [SCREENS.ONBOARDING]:'01 Onboarding', [SCREENS.MAP]:'02 Map', [SCREENS.BROWSE]:'03 Browse', [SCREENS.SAVED]:'04 Saved', [SCREENS.PROFILE]:'05 Profile', [SCREENS.SEARCH]:'06 Search', [SCREENS.DETAIL]:'07 Tour Detail', [SCREENS.PLAYER]:'08 Tour Player', [SCREENS.PAYWALL]:'09 Paywall', [SCREENS.COMPLETION]:'10 Completion', [SCREENS.OFFLINE]:'11 Offline', }; return (
{body}
{window.TweaksPanel && ( setTweak('density',v)} options={[{value:'comfortable',label:'Comfortable'},{value:'compact',label:'Compact'}]}/> setTweak('brand',v)}/>
{['#FFC93C','#FF6B5A','#2DD4A7','#9B7EDC','#4FB7E8','#E89B6C'].map(c => (
setScreen(v)} options={Object.entries(labels).map(([k,v])=>({value:k,label:v}))}/>
)}
); } function ScreenJumper({ current, onJump }) { const items = [ {id:'onboarding',label:'Intro'},{id:'map',label:'Map'},{id:'browse',label:'Browse'}, {id:'search',label:'Search'},{id:'detail',label:'Detail'},{id:'paywall',label:'Paywall'}, {id:'player',label:'Player'},{id:'completion',label:'Done'},{id:'saved',label:'Saved'}, {id:'offline',label:'Offline'},{id:'profile',label:'You'}, ]; return (
Screens
{items.map(s => ( ))}
); } const root = ReactDOM.createRoot(document.getElementById('app')); root.render();