/* ============================================================================ MOWDEW PRO — Worker mobile app (MOBILE ONLY) Privacy-first: the Pro NEVER sees customer names — only a property label like "Crestline Ave pool". No pricing, no payment. Just the route, auto check-in/out, before/after + chemistry capture, and field notes to the office. ========================================================================= */ const PRO = { name: 'Marco Ruiz', role: 'Pool & garden Pro' }; // name-hidden route stops const PRO_STOPS = [ { id: 's1', label: 'Oak Ridge Ct', vert: 'garden', svc: 'Mow & edge', status: 'done', in: '9:06 AM', out: '9:41 AM', dur: '35 min', eta: null, geo: true }, { id: 's2', label: 'Crestline Ave', vert: 'pool', svc: 'Pool service', status: 'onsite', in: '10:12 AM', out: null, dur: null, eta: null, geo: true }, { id: 's3', label: 'Brookhaven Ln', vert: 'garden', svc: 'Mow & blow', status: 'upcoming', in: null, out: null, dur: null, eta: '11:15 AM', geo: true }, { id: 's4', label: 'Marin Vista', vert: 'pool', svc: 'Pool service', status: 'upcoming', in: null, out: null, dur: null, eta: '2:30 PM', movedFrom: '12:30 PM', geo: true }, { id: 's5', label: 'Cypress Ct', vert: 'pool', svc: 'Pool service', status: 'upcoming', in: null, out: null, dur: null, eta: '1:20 PM', geo: false, isNew: true }, ]; // field notes sent to the office (owner), not the customer const PRO_NOTES_LOG = [ { id: 'n1', label: 'Crestline Ave', vert: 'pool', t: 'Customer asked if we can add a monthly filter deep-clean. Routing to you.', at: '10:31 AM', status: 'sent' }, { id: 'n2', label: 'Oak Ridge Ct', vert: 'garden', t: 'Back gate latch is loose — flagged for the owner to mention.', at: '9:38 AM', status: 'seen' }, { id: 'n3', label: 'Marin Vista', vert: 'pool', t: 'Skimmer basket cracked, replaced from truck stock.', at: 'Yesterday', status: 'seen' }, ]; function proChip(s) { if (s.status === 'done') return Done; if (s.status === 'onsite') return On-site; return Upcoming; } /* privacy banner */ function PrivacyNote({ style = {} }) { return (
Customer names stay private. You see property labels only.
); } /* ============================== PRO · TODAY / ROUTE ================== Calm by design: ONE stop — the active (or next) one — gets the hero treatment and the only button. Every other stop is a quiet numbered row. No status pills; record-mark type only. */ function ProToday({ go, openFab, toast }) { const doneCount = PRO_STOPS.filter(s => s.status === 'done').length; const active = PRO_STOPS.find(s => s.status === 'onsite') || PRO_STOPS.find(s => s.status === 'upcoming'); const av = VERT(active.vert); const onsite = active.status === 'onsite'; return ( }>
Tue, May 12 · {doneCount} of {PRO_STOPS.length} done
Hi, Marco
{/* map */}
{}} />
{/* heading-to-next — ETA only, no live trail */} {/* THE one thing that matters now */}
{onsite ? 'ON-SITE NOW' : 'UP NEXT'}
go('pro_visit', { id: active.id })} className="card" style={{ padding: 16, cursor: 'pointer', minWidth: 0, boxSizing: 'border-box', borderColor: onsite ? 'var(--blue-300)' : 'var(--ink-100)', boxShadow: onsite ? '0 0 0 3px var(--blue-50), var(--shadow-md)' : 'var(--shadow-md)' }}>
{active.label} {av.name.toLowerCase()}
{active.svc}{onsite ? ` · on-site since ${active.in}` : ` · ETA ${active.eta}`}
{/* everything else — one quiet list */}
auto check-in on}>The rest of today
{PRO_STOPS.filter(st => st.id !== active.id).map((st, ri) => { const i = PRO_STOPS.indexOf(st); const v = VERT(st.vert); const isDone = st.status === 'done'; return (
go('pro_visit', { id: st.id })} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px', borderTop: ri ? '1px solid var(--ink-100)' : 'none', cursor: 'pointer', opacity: isDone ? 0.55 : 1 }}> {isDone ? : i + 1}
{st.label} {v.name.toLowerCase()}
{(st.movedFrom || st.isNew) && !isDone && (
{st.isNew ? 'NEW ON YOUR ROUTE' : `MOVED · WAS ${st.movedFrom}`}
)}
{isDone ? `${st.in} – ${st.out}` : `ETA ${st.eta}`}
); })}
{/* approved work on his day — homeowner OK'd it in advance */}
); } /* ============================== PRO · ACTIVE VISIT (+ CHEMISTRY) ===== */ function ProVisit({ go, params, toast }) { const s = PRO_STOPS.find(x => x.id === params.id) || PRO_STOPS[1]; const v = VERT(s.vert); const isPool = s.vert === 'pool'; const [sec, setSec] = React.useState(764); const [before, setBefore] = React.useState(s.vert + '-before'); const [after, setAfter] = React.useState(null); const [chem, setChem] = React.useState(null); // null | 'scanning' | reading const [notes, setNotes] = React.useState(''); const [flag, setFlag] = React.useState(false); React.useEffect(() => { const t = setInterval(() => setSec(x => x + 1), 1000); return () => clearInterval(t); }, []); const reading = { ph: 7.4, cl: 0.8, alk: 100 }; const scanChem = () => { setChem('scanning'); setTimeout(() => setChem(reading), 900); }; const cap = (which) => { if (which === 'after') setAfter(s.vert + '-after'); }; const Tile = ({ which, val, scene }) => (
cap(which)} style={{ cursor: 'pointer' }}> {val ? : Tap to capture}
); return ( }> go('pro_today')} right={Private} /> {/* verified on-site hero */}
On-site now Verified on-site
ON-SITE TIMER
{fmtClock(sec)}
Auto check-in
{s.in} · 40 FT
{/* before / after */}
Before & after
{/* CHEMISTRY — pool only → full logging screen */} {isPool && (
Water chemistry {!chem || chem === 'scanning' ? (
go('pro_chem', { id: s.id })} className="card" style={{ cursor: 'pointer', padding: 16, borderColor: 'var(--aqua-100)', display: 'flex', alignItems: 'center', gap: 14 }}>
Log water chemistry
Snap the test kit or enter readings — shared with the homeowner
) : (
go('pro_chem', { id: s.id })} className="card" style={{ padding: 16, borderColor: 'var(--aqua-100)', cursor: 'pointer' }}>
Readings logged
{s.in} · shared with the homeowner
Tap to edit readings
)}
)} {/* equipment on this property — pool only */} {isPool && (
go('pro_equipment', { id: s.id })} className="card" style={{ cursor: 'pointer', padding: 14, display: 'flex', alignItems: 'center', gap: 12 }}>
Equipment on the record
5 parts · add or flag for repair
)} {/* field notes -> office */}
Goes to the office, not the customer}>Field note