/* Pickup / Dismissal plan */ function PickupScreen({ onBack }) { const days = ['Mon','Tue','Wed','Thu','Fri']; const [day, setDay] = React.useState('Wed'); const [person, setPerson] = React.useState(null); const METHODS = [ { id:'car', label:'Car Rider', icon:'car-front', time:'3:30 PM' }, { id:'bus', label:'Bus Rider', icon:'bus-front', time:'3:25 PM' }, { id:'walk', label:'Walker', icon:'footprints', time:'3:35 PM' }, { id:'after', label:'Aftercare', icon:'clock', time:'5:30 PM' }, ]; const [method, setMethod] = React.useState('car'); const [sheet, setSheet] = React.useState(false); const [people, setPeople] = React.useState([ { initials:'JH', color:'navy', name:'Jermaine Hatten', rel:'Parent', phone:'(281) 555-0142' }, { initials:'AH', color:'cyan', name:'Ashley Hatten', rel:'Parent', phone:'(281) 555-0177' }, ]); const [addOpen, setAddOpen] = React.useState(false); const [qr, setQr] = React.useState(false); const [checkedIn, setCheckedIn] = React.useState(false); const [pos, setPos] = React.useState(4); React.useEffect(() => { if (!checkedIn || pos <= 1) return; const t = setTimeout(() => setPos(p => Math.max(1, p - 1)), 3200); return () => clearTimeout(t); }, [checkedIn, pos]); const doCheckIn = () => { setQr(false); setCheckedIn(true); setPos(4); jlToast('Checked in · the school knows you\u2019re here', { icon:'check-check' }); }; const [fName, setFName] = React.useState(''); const [fRel, setFRel] = React.useState(''); const [fPhone, setFPhone] = React.useState(''); const ADD_COLORS = ['gold','green','red','blue','navy','cyan']; const initialsOf = (n) => n.trim().split(/\s+/).map(w=>w[0]).slice(0,2).join('').toUpperCase() || '?'; const canSave = fName.trim() && fRel.trim(); const savePerson = () => { if (!canSave) return; const p = { initials: initialsOf(fName), color: ADD_COLORS[people.length % ADD_COLORS.length], name: fName.trim(), rel: fRel.trim(), phone: fPhone.trim() || 'No phone added' }; setPeople(ps => [...ps, p]); setAddOpen(false); setFName(''); setFRel(''); setFPhone(''); jlToast(p.name + ' added as authorized', { icon:'user-check' }); }; const cur = METHODS.find(m => m.id===method); return (
{/* day picker */}
{days.map(d => ( ))}
{/* today's pickup */}
TODAY'S PICKUP
{cur.label}
{cur.time}
You · Katy Middle School
{!checkedIn ? ( ) : (
Checked in at {new Date().toLocaleTimeString([], {hour:'numeric', minute:'2-digit'})}
)}
{/* live queue (after check-in) */} {checkedIn && (
LIVE PICKUP QUEUE
Live
{pos>1 ? '#'+pos : 'Next'}
{pos>1 ? 'in the car-rider line' : 'EJ is heading out now'}
{[1,2,3,4,5].map(n => (
= pos ? 'var(--gold-400)' : 'rgba(255,255,255,.22)' }} /> ))}
{pos>1 ? 'Staff are calling students as cars arrive. Please stay in your vehicle.' : 'A staff member is walking EJ to your car.'}
)} {/* authorized pickups */} Authorized Pickups
{people.map((p, i) => ( {i > 0 && } setPerson(p)} /> ))}
{/* proximity banner */}
You are 2 mins away
Live from your location · we'll tell the school you're on the way.
{/* bottom sheet: choose method */} {sheet && (
setSheet(false)} style={{ position:'absolute', inset:0, zIndex:60, background:'rgba(10,20,40,.4)', display:'flex', alignItems:'flex-end' }}>
e.stopPropagation()} style={{ width:'100%', background:'#fff', borderRadius:'22px 22px 0 0', padding:'10px 18px 34px' }}>
Change Pickup Method
{METHODS.map(m => { const on = m.id===method; return ( ); })}
)} {/* bottom sheet: QR check-in */} {qr && (
setQr(false)} style={{ position:'absolute', inset:0, zIndex:60, background:'rgba(10,20,40,.4)', display:'flex', alignItems:'flex-end' }}>
e.stopPropagation()} style={{ width:'100%', background:'#fff', borderRadius:'22px 22px 0 0', padding:'10px 18px 32px' }}>
Check In for Pickup
Hold this code up at the gate scanner
EJ Hatten · Car Rider
or
)} {/* bottom sheet: authorized person detail */} {person && (
setPerson(null)} style={{ position:'absolute', inset:0, zIndex:60, background:'rgba(10,20,40,.4)', display:'flex', alignItems:'flex-end' }}>
e.stopPropagation()} style={{ width:'100%', background:'#fff', borderRadius:'22px 22px 0 0', padding:'10px 18px 34px' }}>
{person.name}
{person.rel} · Authorized
Verified
{person.phone}
Can pick up EJ Hatten
)} {/* bottom sheet: add authorized person */} {addOpen && (
setAddOpen(false)} style={{ position:'absolute', inset:0, zIndex:60, background:'rgba(10,20,40,.4)', display:'flex', alignItems:'flex-end' }}>
e.stopPropagation()} style={{ width:'100%', background:'#fff', borderRadius:'22px 22px 0 0', padding:'10px 18px 30px' }}>
Add Authorized Person
Who can pick up EJ Hatten?
)}
); } function AddField({ label, value, onChange, placeholder, icon }) { return (
{label}
onChange(e.target.value)} placeholder={placeholder} style={{ flex:1, border:0, background:'transparent', outline:'none', fontSize:14.5, fontFamily:'var(--font-sans)', color:'var(--ink)' }} />
); } function PersonRow({ initials, color, name, rel, onClick }) { return ( ); } /* Stylized QR — deterministic module grid (a visual mock, not a real code). */ function FakeQR({ size = 176 }) { const N = 21; const finderCell = (r, c) => { // relative position within whichever 7x7 finder this cell is in let rr = r, cc = c; if (r >= N - 7) rr = r - (N - 7); if (c >= N - 7) cc = c - (N - 7); rr = rr % 7; cc = cc % 7; const ring = rr === 0 || rr === 6 || cc === 0 || cc === 6; const core = rr >= 2 && rr <= 4 && cc >= 2 && cc <= 4; return ring || core; }; const cells = []; for (let r = 0; r < N; r++) { for (let c = 0; c < N; c++) { const inFinder = (r < 7 && c < 7) || (r < 7 && c >= N - 7) || (r >= N - 7 && c < 7); let on; if (inFinder) on = finderCell(r, c); else on = ((r * 13 + c * 7 + r * c * 3 + 5) % 5) < 2; cells.push(on); } } return (
{cells.map((on, i) => (
))}
); } Object.assign(window, { PickupScreen });