3.9 KiB
3.9 KiB
v2 RepairOrders Dashboard — Time Management & Interactive Features
File: src/pages/RepairOrders.tsx
Statuses (6 total): active | in_progress | waiting_parts | waiting_pickup | completed | delivered
Time Calculation
calcDueTime(ro)
const baseTime = ro.writeupTime || ro.created; // fall back to creation timestamp
const duration = ro.estimatedDuration || 60; // default 1 hour
if (baseTime) return new Date(baseTime).getTime() + duration * 60000;
// fallback: ro.promisedTime (legacy field)
formatTimeRemaining(ro, now)
Returns "2h 15m", "45m", or "-10m" (past due).
- Completed/delivered →
"—" - Null due time →
"—"
getUrgencyClass(ro, now)
- Completed/delivered →
'' - Past due →
'urgent'(red row bg) - Within 30 min →
'warning'(amber text) - Otherwise →
''
Multi-Tier Sort (sortOrders)
- Waiting For Parts → pinned to bottom, sorted by due time ascending within group
- Customer type: Waiters above Drop-Offs
- Due time: Most urgent (closest to due) first
Adding a New Status
To add a new status (e.g. waiting_pickup), update ALL of these:
types.ts: Add toRepairOrder.statusunion typeSTATUS_CONFIGin RepairOrders.tsx: Add label + Tailwind color classes- ROModal status dropdown: Add
<option> - ExpandedDetail action buttons: Add conditional button — guard with
ro.status !== 'new_status' && ro.status !== 'completed' && ro.status !== 'delivered' - Existing button guards: Update all other button conditions to exclude the new status where appropriate
- Active tab filter (auto): If
new_statusis notcompletedordelivered, it's already active - Sort (auto): If not
waiting_parts, it sorts normally by customer type → due time
Live Countdown
const [now, setNow] = useState(Date.now());
const tickRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => {
tickRef.current = setInterval(() => setNow(Date.now()), 1000);
return () => { if (tickRef.current) clearInterval(tickRef.current); };
}, []);
Drives re-render of TimeRemainingCell and urgency styling in the table + expanded detail every second. The sortOrders function is called inside the filtered-orders computation which re-runs on now changes.
Interactive Waiter/Drop-Off Toggle
CustomerTypeBadgecomponent: clickable badge that callsonToggleCustomerType- Uses
window.confirm()for safety ("Are you sure you want to change...") - Optimistic update on
setOrders, then PB update; rolls back on failure - Triggers re-sort automatically (sort re-evaluates customerType)
Add Time Feature
- "+ Add Time" button appears in ExpandedDetail action bar for non-completed, non-delivered, non-waiting-parts ROs
- Uses
window.prompt('Add extra time (minutes):', '15') - Adds to
estimatedDurationfield in PocketBase - Optimistic update via
setOrders→ updates the countdown, sorting, and urgency instantly
Urgency Styling (table rows)
const rowClass = urgency === 'urgent'
? 'bg-red-50 hover:bg-red-100 dark:bg-red-900/15 dark:hover:bg-red-900/25'
: urgency === 'warning'
? 'hover:bg-gray-50 dark:hover:bg-gray-700/50'
: 'hover:bg-gray-50 dark:hover:bg-gray-700/50';
Past-due expanded details also get bg-red-50 via a className on the <tr>.
Duration Input (Create/Edit Modal)
Two separate inputs — Hours and Minutes:
- Hours:
Math.floor(estimatedDuration / 60) - Minutes:
estimatedDuration % 60 - On change:
setEstimatedDuration(hours * 60 + Math.min(minutes, 59))
Fields in PocketBase
New fields added to RepairOrders collection (auto-created on first save):
customerType: 'waiter' | 'drop-off'(default: 'drop-off')estimatedDuration: number(minutes, default: 60)writeupTime: string(ISO timestamp, set tonew Date().toISOString()on create)
Legacy field kept for backward compat:
promisedTime?: string— used as fallback in calcDueTime