initial commit

This commit is contained in:
ray
2026-07-12 10:17:17 -04:00
commit dab5a4ebc6
1424 changed files with 330463 additions and 0 deletions
@@ -0,0 +1,100 @@
# 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)
```typescript
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)
1. **Waiting For Parts** → pinned to bottom, sorted by due time ascending within group
2. **Customer type**: Waiters above Drop-Offs
3. **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:
1. **`types.ts`**: Add to `RepairOrder.status` union type
2. **`STATUS_CONFIG`** in RepairOrders.tsx: Add label + Tailwind color classes
3. **ROModal status dropdown**: Add `<option>`
4. **ExpandedDetail action buttons**: Add conditional button — guard with `ro.status !== 'new_status' && ro.status !== 'completed' && ro.status !== 'delivered'`
5. **Existing button guards**: Update all other button conditions to exclude the new status where appropriate
6. **Active tab filter** (auto): If `new_status` is not `completed` or `delivered`, it's already active
7. **Sort** (auto): If not `waiting_parts`, it sorts normally by customer type → due time
## Live Countdown
```typescript
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
- `CustomerTypeBadge` component: clickable badge that calls `onToggleCustomerType`
- 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 `estimatedDuration` field in PocketBase
- Optimistic update via `setOrders` → updates the countdown, sorting, and urgency instantly
## Urgency Styling (table rows)
```typescript
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 to `new Date().toISOString()` on create)
Legacy field kept for backward compat:
- `promisedTime?: string` — used as fallback in calcDueTime