/* ============================================================================ MOWDEW LIVE DEMO — the three phone shells Each shell maps the App.html screen ids onto its tabs. Static tabs render the REAL app screens (ProDone, ProNotes, Settings, OwnerCustomersM); synced tabs render app-spec clones from mowdew-demo-tabs.jsx, wired to the shared `world`. ========================================================================= */ /* shared header for synced home screens: brand + self portrait + bell */ function DmHeader({ persona, world, act, sub, title }) { return (
{sub}
{title}
); } /* ============================ PRO PHONE ============================== */ function ProTodayDemo({ world, act, nav }) { const v = world.visit; const chem = world.chem; return ( {}} />}> {/* route map — the same component as the main prototype */}
{}} />
{/* THE interactive stop */}
{v === 'scheduled' && UP NEXT} {v === 'driving' && EN ROUTE} {v === 'onsite' && ON-SITE NOW} {v === 'done' && DONE}
Crestline Ave pool
Pool service · weekly
{/* property tools — REAL app screens (equipment always; recap once done) */}
{v === 'done' && }
{v === 'scheduled' && (
ETA 10:05 AM Auto check-in armed
)} {v === 'driving' && (
Driving to Crestline Ave
Geofence will check you in on arrival
)} {(v === 'onsite' || v === 'done') && (
{v === 'done' ? 'ON-SITE TIME' : 'ON-SITE TIMER'} In at {world.checkInAt}
{dmClock(world.sec)}
{v === 'onsite' && ( <> {!chem || chem === 'scanning' ? (
{chem === 'scanning' ? : }
{chem === 'scanning' ? 'Reading test strip…' : 'Snap the pH test'}
{chem === 'scanning' ? 'Matching colors to the chart' : 'Photo → Mowdew reads pH, chlorine & alkalinity'}
{chem !== 'scanning' && }
) : (
)} {chem && chem !== 'scanning' && (
Shared with the homeowner
)}
Leaving the property clocks you out automatically — same as check-in.
)} {v === 'done' && (
{world.checkInAt} – {world.checkOutAt} · {dmClock(world.sec)} AUTO CHECK-IN
)}
)}
{/* field note — routes to the office, kicks off the chat storyline */} {(v === 'onsite' || v === 'done') && (
to the office}>Field note

“Emma asked about a monthly filter deep-clean — routing to you.”

{!world.note && } {world.note && world.note !== 'closed' &&
SENT · WITH THE OFFICE
} {world.note === 'closed' && (
{world.noteYes ? 'Resolved — deep-clean added to this stop.' : 'Resolved — Emma passed for now.'}
)}
)} {/* the rest of today — same quiet numbered list as the main prototype */}
auto check-in on}>The rest of today
{[ { n: , done: true, label: 'Oak Ridge Ct garden', meta: '9:06 – 9:41 AM' }, { n: 3, label: 'Brookhaven Ln garden', meta: 'ETA 11:15 AM' }, { n: 4, label: 'Marin Vista pool', meta: 'ETA 2:30 PM' }, ].map((r, i) => (
{r.n}
{r.label}
{r.meta}
))}
NEXT WEEK
Crestline Ave pool
{world.nextVisit.moved &&
MOVED · WAS MON, MAY 18
}
{world.nextVisit.when}
); } function ProDemoPhone({ world, act, toast, say }) { const [tab, setTab] = React.useState('pro_today'); const [autoCk, setAutoCk] = React.useState(true); const [screen, setScreen] = React.useState(null); // full-screen push: equipment / recap (REAL app components) const nav = (scr, params) => { const t = scr === 'pro_visit' ? 'pro_today' : scr; if (['pro_equipment', 'pro_equip_detail', 'pro_recap'].includes(scr)) { setScreen({ name: scr, params }); return; } if (['pro_today', 'pro_done', 'pro_notes', 'settings'].includes(t)) { setScreen(null); setTab(t); } if (scr === 'p_notifs') act.openNotifs('pro'); }; let body; if (screen) { if (screen.name === 'pro_equipment') body = ; else if (screen.name === 'pro_equip_detail') body = ; else body = ; } else if (tab === 'pro_done') body = {}} />; else if (tab === 'pro_notes') body = {}} toast={say} />; else if (tab === 'settings') body = setTab('pro_today')} role="provider" setRole={() => {}} autoCheckin={autoCk} setAutoCheckin={setAutoCk} toast={say} />; else body = ; return ( <> {body} ); } /* =========================== HOMEOWNER PHONE ========================= */ function HomeDemoPhone({ world, act, toast, say }) { const [tab, setTab] = React.useState('h_feed'); const [sheet, setSheet] = React.useState(false); const [reqSheet, setReqSheet] = React.useState(false); const [screen, setScreen] = React.useState(null); // full-screen push: equipment / recap (REAL app components) const [slot, setSlot] = React.useState('Thu, May 21 · 10 AM'); const [reason, setReason] = React.useState('Guests coming Monday'); const nav = (scr, params) => { if (['h_feed', 'h_chem', 'h_inbox', 'h_ledger'].includes(scr)) { setScreen(null); setTab(scr); } else if (['h_equipment', 'h_equip_detail', 'h_recap'].includes(scr)) setScreen({ name: scr, params }); else if (scr === 'h_request') setSheet(true); else if (scr === 'h_notifs') act.openNotifs('home'); }; const openRequest = () => { if (!world.resched) setSheet(true); }; const send = () => { setSheet(false); act.sendResched(slot, reason); }; let body; if (screen) { if (screen.name === 'h_equipment') body = ; else if (screen.name === 'h_equip_detail') body = ; else body = ; } else if (tab === 'h_chem') body = ; else if (tab === 'h_inbox') body = ; else if (tab === 'h_ledger') body = ; else body = setReqSheet(true)} toast={say} />; return ( <> {body} {/* request sheet — same options as the app's reschedule screen */} setSheet(false)} title="Move your visit">
Goes to Diego — he approves, suggests another time, or declines with a reason.
New time
{['Thu, May 21 · 10 AM', 'Fri, May 22 · 1 PM', 'Sat, May 23 · 9 AM'].map(s => { const on = slot === s; return ( ); })}
Why? Diego sees this
{['Guests coming Monday', 'Gate will be locked', "We'll be away"].map(r => ( ))}
setReqSheet(false)} act={act} /> ); } /* ============================ OWNER PHONE ============================ */ function OwnerHomeTab({ world, act, nav, setTab }) { const v = world.visit; const rq = world.resched; return ( setTab('o_money')} />}> {/* money + ops — the owner pulse: dark statement band + calm ops pair */}
setTab('o_money')} visitsValue={v === 'done' ? '2/5' : '1/5'} visitsSub={v === 'driving' ? 'Marco en route · Crestline' : v === 'onsite' ? `Marco on-site · ${dmClock(world.sec)}` : v === 'done' ? 'Crestline Ave verified' : 'Marco out · next ETA 10:05'} visitsLive={v === 'driving' || v === 'onsite'} homesValue="38" homesSub="21 garden · 17 pool" onHomes={() => setTab('o_customers')} />
{/* needs you — same rows as the app */}
Needs you
{/* a property at a glance — opens the REAL equipment & recap screens */}
Emma's property
); } function OwnerInboxTab({ world, act, nav }) { const rq = world.resched; return ( {}} />}>
{rq ? : world.req ? null : (
No requests right now.
When Emma asks to move a visit, it lands here instantly.
)}
{/* approve-before-work — photo'd request → priced quote */} {/* reviews — public 5★ or a private fix-it note */} {/* field note from Marco → chat with Emma → closure */} {world.note && (
Field notes
Crestline Ave pool
Marco · 10:31 AM
{world.note === 'sent' && NEEDS YOU} {(world.note === 'asked') && WAITING ON EMMA} {world.note === 'replied' && EMMA REPLIED} {world.note === 'closed' && RESOLVED}
“Emma asked about a monthly filter deep-clean — routing to you.”
{world.note !== 'sent' && (
Marco says you'd like a monthly filter deep-clean — I can add it for $20/visit. Want it?
{(world.note === 'replied' || world.note === 'closed') && (
{world.noteYes ? 'Yes please — add it.' : 'Not right now, thanks.'}
)}
)}
{world.note === 'sent' && } {world.note === 'replied' && } {world.note === 'closed' && (
{world.noteYes ? 'Added to Emma\u2019s weekly service — Marco notified.' : 'Closed — Emma passed for now. Marco notified.'}
)}
)}
); } function OwnerDemoPhone({ world, act, toast, say }) { const [tab, setTab] = React.useState('o_home'); const [screen, setScreen] = React.useState(null); // full-screen push: equipment / recap (REAL app components) const nav = (scr, params) => { if (['o_equipment', 'o_equip_detail', 'o_recap'].includes(scr)) { setScreen({ name: scr, params }); return; } if (['o_home', 'o_money', 'o_inbox', 'o_customers'].includes(scr)) { setScreen(null); setTab(scr); } else if (['o_customer', 'o_new_customer', 'o_thread', 'o_schedule_work'].includes(scr)) { setScreen(null); setTab('o_customers'); } if (scr === 'o_notifs') act.openNotifs('owner'); }; let body; if (screen) { if (screen.name === 'o_equipment') body = ; else if (screen.name === 'o_equip_detail') body = ; else body = ; } else if (tab === 'o_money') body = ; else if (tab === 'o_inbox') body = ; else if (tab === 'o_customers') body = {}} toast={say} />; else body = ; return ( <> {body} ); } /* owner's reschedule card — shared by Home + Inbox tabs */ function OwnerReschedCard({ world, act }) { const rq = world.resched; return (
Emma Walsh
{(() => { const [t, l, p] = rqMark(rq.state, 'owner'); return {l}; })()}
{rq.state === 'pending' && (
)} {rq.state === 'proposed' && } {rq.state === 'approved' && } {rq.state === 'declined' && }
); } Object.assign(window, { ProDemoPhone, HomeDemoPhone, OwnerDemoPhone, OwnerReschedCard, OwnerHomeTab, OwnerInboxTab, ProTodayDemo, DmHeader });