// MobileView.jsx — operator/rigger phone view
// 3 tabs: Yesterday · Today · Tomorrow
// Yesterday → read-only history (completed jobs)
// Today → today's job(s) with "Submit Completion" CTA
// Tomorrow → locked until 7pm (server-side push timing) — toggleable in demo
// No tap-through flow. Operator just sees, does, completes.
function MobileOperatorView({ role = 'Operator', onSign }) {
const isRigger = role === 'Rigger';
const [tab, setTab] = React.useState('today');
// Demo toggle: in real app, server gates this. Here, let user flip between pre-7pm and post-7pm to see both states.
const [after7pm, setAfter7pm] = React.useState(true);
// Filter to bookings assigned to "this operator" (Razak Hassan for demo).
// In production this is the logged-in user.
const operatorName = isRigger ? 'Ahmad Faizal' : 'Razak Hassan';
const myBookings = BOOKINGS.filter(b =>
isRigger ? b.rigger === operatorName : b.operator === operatorName
);
const yIso = addDaysISO(TODAY, -1);
const tIso = TODAY;
const mIso = addDaysISO(TODAY, 1);
const shortDate = (iso) => new Date(iso + 'T00:00:00').toLocaleDateString('en-GB', { day: '2-digit', month: 'short' });
// Always show a day's jobs earliest-first (08:00 before 13:00).
const byStart = (a, b) => (a.start || '').localeCompare(b.start || '');
const yesterday = myBookings.filter(b => b.date === yIso).sort(byStart);
const today = myBookings.filter(b => b.date === tIso).sort(byStart);
const tomorrow = myBookings.filter(b => b.date === mIso).sort(byStart);
const tabs = [
{ id: 'yesterday', label: 'Yesterday', count: yesterday.length, sub: shortDate(yIso) },
{ id: 'today', label: 'Today', count: today.length, sub: shortDate(tIso) },
{ id: 'tomorrow', label: 'Tomorrow', count: tomorrow.length, sub: shortDate(mIso), locked: !after7pm },
];
const activeJobs = tab === 'yesterday' ? yesterday : tab === 'today' ? today : tomorrow;
return (
{/* Left: explainer */}
Mobile · {isRigger ? 'Rigger / Attendant App' : 'Operator App'}
Just my jobs. Nothing else.
{isRigger ? 'Riggers' : 'Operators'} see only their own bookings — yesterday for context, today's work, and tomorrow's plan. No fleet view, no other people's jobs.
The 7pm rule
Tomorrow's schedule is pushed to operators at 7:00 PM each evening. If Admin changes a tomorrow booking after 7pm, the operator gets an instant push: "Tomorrow's job changed — review now."
Demo toggle — flip to see both states:
setAfter7pm(false)}
style={{flex: 1, fontSize: 11, padding: '6px 8px'}}>
Before 7pm (locked)
setAfter7pm(true)}
style={{flex: 1, fontSize: 11, padding: '6px 8px'}}>
After 7pm (released)
{/* Phone frame */}
9:41
{/* App header */}
{isRigger ? 'Rigger' : 'Operator'}
{operatorName}
{/* Day tabs — selected day is wrapped in a solid navy frame so it's the
first thing the operator sees on a sunlit jobsite. */}
{tabs.map(t => {
const active = tab === t.id;
return (
setTab(t.id)}
style={{
flex: 1, padding: '8px 4px', display: 'flex', flexDirection: 'column',
alignItems: 'center', gap: 2, position: 'relative',
background: active ? 'var(--color-navy)' : 'white',
color: active ? 'white' : 'var(--color-grey-600)',
border: active ? '2px solid var(--color-navy)' : '2px solid var(--color-grey-200)',
borderRadius: 10,
boxShadow: active ? '0 2px 6px rgba(11, 31, 58, 0.25)' : 'none',
fontWeight: active ? 700 : 500,
cursor: 'pointer',
}}>
{t.label}
{t.sub} · {t.count}
{t.locked ? : null}
);
})}
{/* Tomorrow + locked → 7pm gate */}
{tab === 'tomorrow' && !after7pm ? (
Released at 7:00 PM
Tomorrow's schedule will be pushed to you tonight. You'll get a notification when ready.
Why the delay? Admin sometimes adjusts tomorrow's jobs late in the day. Locking until 7pm means you don't memorize an out-of-date plan.
) : null}
{/* Empty state (yesterday/tomorrow with no jobs) */}
{activeJobs.length === 0 && !(tab === 'tomorrow' && !after7pm) ? (
No jobs {tab === 'yesterday' ? 'yesterday' : tab === 'today' ? 'today' : 'tomorrow'}
{tab === 'tomorrow' ? 'Rest day — enjoy.' : 'Nothing on schedule.'}
) : null}
{/* Jobs list */}
{activeJobs.length > 0 && !(tab === 'tomorrow' && !after7pm) ? activeJobs.map((order, idx) => (
{order.id}
{order.shift === 'morning' ? '8am – 12pm' : order.shift === 'afternoon' ? '1pm – 5pm' : `${order.start} – ${order.end}`}
{order.customer}
{order.location}
Machine
{bookingMachineLabel(order)}
{isRigger ? 'Operator' : 'Rigger'}
{isRigger ? order.operator : order.rigger}
Person in Charge
{order.contact || '—'}
{order.phone ? (
{order.phone}
) : null}
{order.remarks ? (
⚠ Remarks
{order.remarks}
) : null}
{/* Today only: submit completion at end of job */}
{tab === 'today' ? (
onSign && onSign(order.id)}>
Sign
) : null}
{/* Yesterday only: read-only checkmark */}
{tab === 'yesterday' ? (
Job completed
) : null}
)) : null}
{/* Tomorrow + released — banner */}
{tab === 'tomorrow' && after7pm && activeJobs.length > 0 ? (
Pushed 7:00 PM · Tap a job to review
) : null}
{/* Right: tech notes */}
How it works
Yesterday tab: last 24h of completed jobs — read-only, useful for follow-up calls or fixing paperwork.
Today tab: current jobs. Tap Submit Completion at the end of the day to capture customer sign-off.
Tomorrow tab: locked until 7pm. Admin's daily finalisation cut-off.
Live push: if Admin edits a tomorrow job after 7pm, operator gets an instant notification.
No status taps
Operators don't tap "Accepted / On the Way / Arrived". A booking just exists. The assigned machine flips from available → booked, and back when completion is submitted.
);
}
Object.assign(window, { MobileOperatorView });