// Towbook-specific patterns: status pill, call card, driver row, equipment inspection row, photo capture, signature, bottom action bar. const { useState: useStateP } = React; // Status pill — semantic for tow ops. Variant: 'pill' (rounded), 'tag' (square left number tag). function TBStatusPill({ status = 'waiting', variant = 'pill', size = 'md' }) { const dark = typeof document !== 'undefined' && document.documentElement.getAttribute('data-theme') === 'dark'; const palette = dark ? TBT.statusDark[status] : TBT.status[status]; const label = TBT.status[status]?.label || status; const fs = size === 'sm' ? 11 : 12; const padY = size === 'sm' ? 2 : 3; const padX = size === 'sm' ? 8 : 10; return ( {label} ); } // Call card — the dispatch list row. Borrows from screenshots: green/blue/amber accent strip, // number tag, status sentence, vehicle, account, time + ETA, address(es), action bar. function TBCallCard({ call, onEdit, onUpdateStatus, onCallContact, onClick, expanded = true }) { const dark = typeof document !== 'undefined' && document.documentElement.getAttribute('data-theme') === 'dark'; const status = call.status; const palette = dark ? TBT.statusDark[status] : TBT.status[status]; return (
{/* header line: number tag + status sentence */}
{call.number} {call.statusSentence}
{/* call type */}
{call.type}
{/* vehicle line */}
{call.vehicleYear} {call.vehicleMakeModel} ({call.vehicleColor})
{/* account */}
{call.account}
{/* time row */}
{call.receivedTime} ({call.timeAgo}) {call.eta && ( <> ETA {call.eta} )}
{expanded && ( <> {/* addresses */}
{call.pickup}
{call.dropoff}
{/* action bar */}
{ e.stopPropagation(); onEdit?.(call); }}/> { e.stopPropagation(); onUpdateStatus?.(call); }}/> {onCallContact && { e.stopPropagation(); onCallContact?.(call); }}/>}
)}
); } function CardAction({ icon, label, onClick }) { const [pressed, setPressed] = useStateP(false); return ( ); } // Driver row — for assignment / map driver list. function TBDriverRow({ name, unit, status, onClick, distance }) { return ( {(name || '').split(' ').map(s => s[0]).join('').slice(0,2)}} headline={name} supporting={`Unit ${unit}${distance ? ' · ' + distance : ''}`} trailing={} onClick={onClick} /> ); } // Equipment inspection row — checklist with Pass / Fail / N/A + camera. function TBInspectionRow({ label, value, onChange, photoCount = 0, onPhoto }) { const opts = [ { key: 'pass', label: 'Pass', activeBg: 'var(--green-3)', activeFg: 'var(--green-11)' }, { key: 'fail', label: 'Fail', activeBg: 'var(--red-3)', activeFg: 'var(--red-11)' }, { key: 'na', label: 'N/A', activeBg: 'var(--slate-3)', activeFg: 'var(--slate-12)' }, ]; return (
{label}
{opts.map((o, i) => { const active = value === o.key; return ( ); })}
); } // Photo capture tile (used in call detail / inspection) function TBPhotoTile({ src, label, onClick, count }) { return ( ); } // Bottom action bar (used in call detail — Change Status / Add Photo / Show Map / More) function TBBottomActionBar({ items }) { return (
{items.map((it, i) => ( ))}
); } // Section header — in detail screens function TBSectionHeader({ children, action }) { return (
{children} {action}
); } // Detail row — label / value pair used in call detail function TBDetailRow({ icon, label, value, action }) { return (
{icon &&
{React.createElement(icon, { size: 18 })}
}
{label &&
{label}
}
{value}
{action}
); } // Pull-to-refresh indicator (visual only; logic shown via tweak/animation) function TBPullToRefresh({ pulling = false, refreshing = false }) { return (
); } Object.assign(window, { TBStatusPill, TBCallCard, TBDriverRow, TBInspectionRow, TBPhotoTile, TBBottomActionBar, TBSectionHeader, TBDetailRow, TBPullToRefresh, });