# RO Details Tab Architecture

## Overview

The Repair Orders page has a "RO Details" tab that provides inline editing of a selected repair order. Clicking any RO row navigates to this tab with a full edit form, info bar, and time management.

## Files Involved

- `src/pages/RepairOrders.tsx` — Main page with `RODetailsPanel` component, `handleSelectRO`, `handleInlineUpdate`, early-return rendering
- `src/components/ROForm.tsx` — Shared RO create/edit form (extracted from ROModal)
- `src/components/ROForm.tsx` — `readOnlyDuration` prop controls whether "Estimated Duration" is editable

## Component Architecture

```
RepairOrders (main component)
├── if activeTab === 'details' → early return with RODetailsPanel
│   └── RODetailsPanel
│       ├── Back button + RO header
│       ├── Info bar (Customer Type, Status, Write-up, Promised, Time Remaining + Add Time)
│       ├── Message banner (success/error)
│       ├── ROForm (inline, readOnlyDuration=true)
│       └── AddTimeModal
└── else → mainContent (tab bar + table)
    ├── TabButton (active/all/completed)
    ├── FragmentRow → onClick → handleSelectRO → sets selectedROId + activeTab='details'
    └── ROModal (uses ROForm for create/edit)
```

## State Flow

1. **Row click**: `handleSelectRO(id)` → `setSelectedROId(id)` + `setActiveTab('details')`
2. **Early return**: When `activeTab === 'details' && selectedRO`, the component returns RODetailsPanel directly
3. **Save**: `RODetailsPanel.onSave` → `handleInlineUpdate(roId, data)` → PB update → `fetchOrders()`
4. **Add Time**: `RODetailsPanel.onAddTime` → `handleAddTime(id, minutes)` → PB update + optimistic UI
5. **Back**: Clears `selectedROId`, sets `activeTab='active'`

## Key Details

### `handleInlineUpdate` vs `handleSave`
- `handleSave` uses the module-level `editRO` state (for the modal flow)
- `handleInlineUpdate(roId, data)` takes the RO id directly (for the details tab flow)
- Both map `estimatedDuration` → `estimatedTime` for PocketBase

### `ROForm` — Shared Form Component
- Extracted from the old ROModal (was ~600 lines, now ~50)
- Takes `editRO`, `existingRoNumbers`, `settings`, `onSave`, `onCancel`, `submitLabel`, `readOnlyDuration`
- Manages ALL form state internally (customer info, services, discount, totals, notes)
- Used by both `ROModal` (create/edit popup) and `RODetailsPanel` (inline edit)

### `readOnlyDuration` Prop
- When `true`: Shows "Original Estimated Duration of Service" as read-only text ("1h 30m")
- When `false` (default): Shows editable Hours/Minutes inputs
- The underlying `estimatedDuration` state is still populated and included in saves
- Helper text: "Add time via the info bar above to extend."

### Info Bar
5-column responsive grid showing:
1. **Customer Type** — Waiter/Drop-Off with icon
2. **Status** — StatusBadge
3. **Write-up Time** — `formatDateTime(ro.writeupTime || ro.created)`
4. **Promised Time** — `formatDueDateTime(ro)` (writeup + duration)
5. **Time Remaining** — Live countdown + urgency colors + "+ Add Time" button
   - Urgent (past due): red text + AlertTriangle icon
   - Warning (≤30min): amber text
   - Completed/delivered: "—" (no button)

### AddTimeModal
- Reused from the existing `AddTimeModal` component in RepairOrders.tsx
- Props: `open`, `initialMinutes` (15), `onCancel`, `onConfirm(totalMinutes)`
- Calls `onAddTime(ro.id, totalMinutes)` which updates `estimatedTime` in PB

## Pitfalls

- **Don't use `handleSave` for details tab** — it depends on `editRO` state which is null during details view
- **Don't remove the early return pattern** — the details tab renders BEFORE `mainContent`, so both can't be in the same JSX tree
- **ROForm manages its own state** — don't try to lift state up; populate happens via the `editRO` prop and `useEffect`
- **The `readOnlyDuration` form still includes `estimatedDuration` in save payload** — it's just not editable by the user

## Dirty Tracking (Unsaved Changes Confirmation)

ROForm has an `onDirtyChange?: (dirty: boolean) => void` prop. When provided:

1. After form population (from `editRO`), a JSON snapshot of initial values is captured via `useRef`
2. A `useEffect` watches all form state fields and compares to the snapshot
3. On first divergence, `onDirtyChange(true)` fires once (using `dirtyNotified` ref to prevent redundant calls)
4. The snapshot resets when `editRO` changes (navigating to a different RO)

In `RODetailsPanel`:
- Tracks `isDirty` state via `onDirtyChange={setIsDirty}`
- Back button calls `handleBack()` which checks `isDirty` and shows `window.confirm()` if true
- On successful save, `setIsDirty(false)` clears the flag

## RO Sort Order

The `sortOrders()` function in `RepairOrders.tsx` uses a 4-tier sort:

| Tier | Statuses | Secondary Sort |
|---|---|---|
| 1 | active, in_progress | Waiters first, then by due time (most urgent on top) |
| 2 | waiting_pickup | By due time ascending |
| 3 | waiting_parts | By due time ascending (absolute bottom) |

`completed` and `delivered` are filtered into the "Completed" tab and excluded from active sorts.
