// app.jsx — Main App component // t, useState, useEffect, useCallback etc. available from components.jsx (loaded first) const LS_KEY = 'dopo_me_v1'; function App() { const [route, setRoute] = useState('home'); const [data, setData] = useState(() => { try { const saved = localStorage.getItem(LS_KEY); if (saved) { const parsed = JSON.parse(saved); // Merge with seed to ensure all fields present return { ...DOPO.SEED, ...parsed, services: parsed.services || DOPO.SEED.services, assets: parsed.assets || DOPO.SEED.assets, memories: parsed.memories || DOPO.SEED.memories, people: parsed.people || DOPO.SEED.people, bequests: parsed.bequests || DOPO.SEED.bequests, mailboxes: parsed.mailboxes || DOPO.SEED.mailboxes, inactivity: parsed.inactivity || DOPO.SEED.inactivity, }; } } catch (e) { console.warn('Could not load saved state:', e); } return DOPO.SEED; }); // Persist to localStorage on change useEffect(() => { try { localStorage.setItem(LS_KEY, JSON.stringify(data)); } catch (e) { console.warn('Could not save state:', e); } }, [data]); // Destiny drawer state const [destinyItem, setDestinyItem] = useState(null); const [destinyItemType, setDestinyItemType] = useState('service'); const openDestiny = useCallback((item, type) => { setDestinyItem(item); setDestinyItemType(type); }, []); const closeDestiny = useCallback(() => { setDestinyItem(null); }, []); const saveDestiny = useCallback((updated) => { setData(prev => { if (destinyItemType === 'service') { return { ...prev, services: prev.services.map(s => s.id === updated.id ? updated : s), }; } else if (destinyItemType === 'asset') { return { ...prev, assets: prev.assets.map(a => a.id === updated.id ? updated : a), }; } else if (destinyItemType === 'memory') { return { ...prev, memories: prev.memories.map(m => m.id === updated.id ? updated : m), }; } return prev; }); }, [destinyItemType]); // Recalculate profile progress useEffect(() => { const total = data.services.length + data.assets.length + data.memories.length + data.people.length; const assigned = data.services.filter(s => s.destiny).length + data.assets.filter(a => a.destiny).length + data.memories.filter(m => m.assignees.length > 0).length + data.people.filter(p => p.invited).length; const pct = total > 0 ? Math.round((assigned / total) * 100) : 0; if (pct !== data.profileProgress) { setData(prev => ({ ...prev, profileProgress: pct })); } }, [data.services, data.assets, data.memories, data.people]); function renderPage() { switch (route) { case 'home': return ; case 'inventory': return ; case 'memories': return ; case 'circle': return ; case 'bequests': return ; case 'profile': return ; default: return ; } } return (
{/* Sidebar */} {/* Main content */}
{renderPage()}
{/* Destiny drawer */} {destinyItem && ( )}
); } // Mount the app const root = ReactDOM.createRoot(document.getElementById('root')); root.render();