// Prodz — App shell. Provides language + navigation context, the 5-item // bottom nav (Home · Network · Crea · Shoutouts · Threads), global top-bar // surfaces (Inbox · Profile), detail stack, and the conversation funnel: // Shoutouts "Rispondi" + Network "Collab" -> Collab Proposal -> Inbox. const { useState: useStateA, useMemo: useMemoA, useEffect: useEffectA } = React; function AppShell() { const { L } = useL(); const { BottomNav } = window.ProdzDesignSystem_d0b87b; const [onboarded, setOnboarded] = useStateA(false); const [tab, setTab] = useStateA('home'); const [stack, setStack] = useStateA([]); // overlay routes const [conversations, setConversations] = useStateA(() => CONVERSATIONS.map((c) => ({ ...c }))); const [recs, setRecs] = useStateA(() => RECS_SEED); const [moodboards, setMoodboards] = useStateA(() => [ { id: 'mb1', name: { it: 'Riferimenti luce', en: 'Lighting refs' }, cover: IMG.fashion, items: [{ t: 'production', id: 'noir' }, { t: 'location', id: 'loft' }, { t: 'moment', id: 'm2' }, { t: 'gear', id: 'g3' }] }, { id: 'mb2', name: { it: 'Location moda', en: 'Fashion locations' }, cover: IMG.interior, items: [{ t: 'location', id: 'loft' }, { t: 'location', id: 'rooftop' }, { t: 'production', id: 'luce' }] }, { id: 'mb3', name: { it: 'Da provare', en: 'To try' }, cover: IMG.studio, items: [{ t: 'gear', id: 'g5' }, { t: 'moment', id: 'm5' }] }] ); const [toast, setToast] = useStateA(null); const [collabFor, setCollabFor] = useStateA(null); // person -> Collab chooser sheet const toastTimer = React.useRef(null); const fireToast = (msg, actionLabel, onAction) => { if (toastTimer.current) clearTimeout(toastTimer.current); setToast({ msg, actionLabel, onAction }); toastTimer.current = setTimeout(() => setToast(null), 3600); }; const newConv = (conv) => { setConversations((cs) => [conv, ...cs]); setStack((s) => [...s, { name: 'conversation', params: { id: conv.id } }]); }; const nav = useMemoA(() => ({ conversations, get inboxCount() {return conversations.reduce((n, c) => n + (c.unread || 0), 0);}, goTab: (id) => {setStack([]);setTab(id);}, pop: () => setStack((s) => s.slice(0, -1)), openInbox: () => setStack((s) => [...s, { name: 'inbox' }]), openProfile: (id) => setStack((s) => [...s, { name: 'profile', params: { id } }]), openThread: (id) => setStack((s) => [...s, { name: 'thread', params: { id } }]), openSaved: () => setStack((s) => [...s, { name: 'saved' }]), openLocations: () => setStack((s) => [...s, { name: 'locations' }]), openAllProductions: () => setStack((s) => [...s, { name: 'allProductions' }]), openProduction: (id) => setStack((s) => [...s, { name: 'production', params: { id } }]), openStories: (i = 0) => setStack((s) => [...s, { name: 'stories', params: { i } }]), moodboards, recs, recOf: (id) => recs[id] || { sum: 0, count: 0 }, recommend: (id, stars, text) => setRecs((r) => { const cur = r[id] || { sum: 0, count: 0, entries: [] }; const entry = { by: 'me', stars, time: { it: 'adesso', en: 'now' }, text: text && text.trim() ? { it: text.trim(), en: text.trim() } : null }; return { ...r, [id]: { sum: cur.sum + stars, count: cur.count + 1, entries: [entry, ...(cur.entries || [])] } }; }), openConversation: (id) => { setConversations((cs) => cs.map((c) => c.id === id ? { ...c, unread: 0 } : c)); setStack((s) => [...s, { name: 'conversation', params: { id } }]); }, toast: fireToast, // Shoutouts "Rispondi" -> creates a Collab Proposal / Job Call conversation replyShoutout: (card) => { const first = card.author.split(' ')[0]; newConv({ id: 'conv-' + Date.now(), person: card.person || null, org: card.person ? null : card.author, kind: card.kind, subject: card.title, unread: 0, time: { it: 'adesso', en: 'now' }, preview: { it: 'Conversazione avviata dai Shoutouts', en: 'Conversation started from Shoutouts' }, messages: [ { me: true, time: L('adesso', 'now'), text: { it: 'Ciao! Mi interessa: ' + card.title.it + '. Possiamo parlarne?', en: 'Hi! I’m interested in: ' + card.title.en + '. Can we talk?' } }, { me: false, time: L('adesso', 'now'), text: { it: 'Perfetto, parliamone. Che disponibilità hai?', en: 'Great, let’s talk. What’s your availability?' } }] }); fireToast(L('Risposta inviata · è nell’Inbox', 'Reply sent · it’s in your Inbox'), null); }, // Network / Profile "Collab" -> opens a chooser (Invite to job / Collab proposal) sendCollab: (person) => setCollabFor(person) }), [conversations, recs, moodboards, L]); if (!onboarded) { return (
{ if (profile) { if (profile.role) PEOPLE.me.role = profile.role; if (profile.city) PEOPLE.me.city = profile.city; window.__prodzProfile = profile; } setOnboarded(true);setTab('home'); }} />
); } const top = stack[stack.length - 1]; const overlay = top && (() => { switch (top.name) { case 'inbox':return ; case 'conversation':return ; case 'thread':return ; case 'profile':return ; case 'saved':return ; case 'locations':return ; case 'allProductions':return ; case 'production':return ; case 'stories':return ; default:return null; } })(); const tabScreen = () => { switch (tab) { case 'home':return ; case 'network':return ; case 'add':return ; case 'shoutouts':return ; case 'threads':return ; default:return ; } }; const navItems = [ { id: 'home', label: L('Home', 'Home'), icon: }, { id: 'network', label: 'Network', icon: }, { id: 'add', create: true, label: L('Crea', 'Create'), icon: }, { id: 'shoutouts', label: 'Shoutouts', icon: }, { id: 'threads', label: 'Threads', icon: }]; return (
{/* base tab screen */}
{tabScreen()}
{/* overlay detail screen */} {overlay &&
{overlay}
}
{!overlay &&
nav.goTab(id)} />
} {/* Collab chooser: Invite to job / Collab proposal */} setCollabFor(null)}> {collabFor && setCollabFor(null)} onSubmit={(conv, toastMsg) => {setCollabFor(null);newConv(conv);fireToast(toastMsg, null);}} /> } {if (toast && toast.onAction) toast.onAction();setToast(null);}} />
); } function Root() { return ( ); } const __prodzRootEl = document.getElementById('root'); // Live-reload guard: the preview re-runs scripts against a #root that may still // carry React's internal container marker — clear it so createRoot stays quiet. if (!__prodzRootEl.__prodzRoot) { Object.keys(__prodzRootEl).forEach((k) => {if (k.indexOf('__reactContainer') === 0 || k.indexOf('_reactRootContainer') === 0) {try {delete __prodzRootEl[k];} catch (_) {}}}); __prodzRootEl.__prodzRoot = ReactDOM.createRoot(__prodzRootEl); } __prodzRootEl.__prodzRoot.render();