> For the complete documentation index, see [llms.txt](https://t420.gitbook.io/t420/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://t420.gitbook.io/t420/prd.md).

# FleetOS UI/UX Overhaul — PRD for Claude Code

## February 7, 2026 | Sprint 2: Dashboard & In-App Views

> **How to use this:** Drop this file + the reference JSX into your repo, then tell Claude Code:
>
> ```
> Read docs/PRD-UI-Overhaul.md and the reference component at docs/design-specs/fleetos-preview.jsx.
> Start with Phase 0: Audit. Report current state before changing anything.
> ```

***

## 1. CONTEXT

### What is FleetOS?

FleetOS is a fleet maintenance coordination platform. A fleet manager creates a service request, a supplier gets notified via WhatsApp magic link, taps it, sees the job, accepts with a quote, provides an ETA, and later marks the job complete with a final cost. No app download required for suppliers.

### Tech Stack

* **Frontend:** Vite + React + TypeScript
* **Backend:** Supabase (Auth, DB, RLS, Realtime)
* **Hosting:** Vercel
* **Live URL:** <https://fleetos.us>
* **Repo:** ZBO19/FleetOS

### What happened in Sprint 1 (Feb 7)

Sprint 1 focused on:

* ✅ Quote/cost tracking fields in Supabase schema
* ✅ Step-based flow for magic link (external supplier view): VIEW → QUOTE → ETA → CONFIRMED
* ✅ Status filters on dashboard
* ⚠️ Final cost on magic link (COMPLETE step) — needs verification
* ⚠️ Cost summary on fleet dashboard — needs verification
* ❌ In-app supplier view — still uses old cramped layout (7+ buttons on one screen)
* ⚠️ Mobile responsiveness — not fully tested

### What Sprint 2 fixes

The **in-app experience** for both fleet managers and suppliers. We have a complete reference design (`fleetos-preview.jsx`) that shows exactly how both dashboards should look and behave. This PRD tells you how to implement it.

***

## 2. REFERENCE DESIGN SPECIFICATION

The file `docs/design-specs/fleetos-preview.jsx` is the **target UI**. Study it carefully. Here's what it defines:

### 2.1 App Structure

```
┌─────────────────────────────────┐
│  View Toggle (Fleet | Supplier) │  ← sticky top bar
├─────────────────────────────────┤
│  FleetOS Brand + Page Title     │
│  Company Name + Date            │
├─────────────────────────────────┤
│  Summary Metrics (4 columns)    │  ← different per view
├─────────────────────────────────┤
│  Filter Pills (scrollable)      │
├─────────────────────────────────┤
│  Job Cards (sorted by priority) │
│  └─ Expandable w/ audit trail   │
│  └─ Inline actions per status   │
└─────────────────────────────────┘
```

### 2.2 Fleet Manager View ("Today's Dispatch")

**Summary Metrics Row (4 columns):**

| Metric     | Source                                   | Highlight Color |
| ---------- | ---------------------------------------- | --------------- |
| Unassigned | Count of status=UNASSIGNED               | Red if > 0      |
| Awaiting   | Count of status=SENT                     | Yellow if > 0   |
| Active     | Count of ACCEPTED/EN\_ROUTE/IN\_PROGRESS | Slate           |
| Today ($)  | Sum of finalCost or quotedAmount         | Slate           |

**Filter Pills:** All | Urgent | Today | Active | Done — each with count badge

**Job Card (collapsed):**

* Urgency tag (URGENT/TODAY/SCHEDULED) + ETA if available
* Job title (bold, 15px)
* Vehicle + Location
* Status badge (top-right)
* Person chips: Supplier, Technician, Owner
* Cost line: quoted (struck through if final exists) → final + diff (+/- colored)
* Notes preview (truncated, single line)
* Expand chevron

**Job Card (expanded):**

* Full notes in a padded card
* Audit Trail timeline with dot indicators and timestamps
* Context-appropriate action buttons:
  * If status != COMPLETED: "Call Tech" + "WhatsApp" (small, secondary)
  * No other buttons for fleet manager — they observe, suppliers act

### 2.3 Supplier View ("My Jobs")

**Summary Metrics Row (4 columns):**

| Metric     | Source                                   | Highlight Color |
| ---------- | ---------------------------------------- | --------------- |
| Action     | Count of PENDING\_RESPONSE               | Yellow if > 0   |
| Active     | Count of ACCEPTED/EN\_ROUTE/IN\_PROGRESS | Slate           |
| Done       | Count of COMPLETED                       | Slate           |
| Earned ($) | Sum of finalCost on completed jobs       | Slate           |

**Filter Pills:** All | Action Needed | Active | Done — each with count badge

**Job Card — PENDING\_RESPONSE (Action Needed):**

* Auto-expanded (card starts open)
* Shows: Respond to this request panel
  * Technician name input
  * Currency selector + Quote input (side by side)
  * Accept (green, primary) + Decline (red outline, secondary)

**Job Card — IN\_PROGRESS (Can Complete):**

* Expanded shows: Mark complete panel
  * Currency display + Final cost input (pre-filled with quoted amount)
  * Note input (optional)
  * "Complete Job" button (green, full width)

**Job Card — Other statuses:**

* Same as fleet view but:
  * "Contact" label instead of "Owner" for the overseer
  * "Call Fleet" instead of "Call Tech" for action buttons

### 2.4 Design System (MUST MATCH)

**Colors:**

```
Primary green:  #10b981 (emerald-500)
Alert red:      #D4564E
Warning yellow: #FACC15
Background:     #0B1120
Card bg:        rgba(17,24,39,0.7)
Primary text:   #f1f5f9
Secondary text: #94a3b8
Muted text:     #475569
Border default: rgba(100,116,139,0.08)
```

**Status Badge Styles:**

| Status            | Label          | Color  | Background            |
| ----------------- | -------------- | ------ | --------------------- |
| UNASSIGNED        | Unassigned     | Red    | rgba(212,86,78,0.1)   |
| SENT              | Awaiting Reply | Yellow | rgba(250,204,21,0.10) |
| ACCEPTED          | Accepted       | Green  | rgba(16,185,129,0.08) |
| EN\_ROUTE         | En Route       | Yellow | rgba(250,204,21,0.10) |
| IN\_PROGRESS      | In Progress    | Yellow | rgba(250,204,21,0.10) |
| COMPLETED         | Done           | Green  | rgba(16,185,129,0.08) |
| PENDING\_RESPONSE | Action Needed  | Yellow | rgba(250,204,21,0.10) |

**Typography:**

* Font: system stack (`-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`)
* Page title: 20px, weight 800, -0.02em tracking
* Card title: 15px, weight 700
* Labels: 9-10px, weight 700, uppercase, 0.06-0.1em tracking
* Body: 11-12px
* Metrics: 18px monospace, weight 800

**Component Patterns:**

* Border radius: 14px on cards, 10px on action panels, 8px on inputs, 20px on filter pills
* Status badges: 16px border-radius (pill), 10px font, 700 weight
* Urgency tags: 10px, 800 weight, 0.08em tracking, color only (no background)
* Inputs: dark bg (rgba(17,24,39,0.6)), 1px border rgba(100,116,139,0.15), 13px font
* Primary buttons: #10b981 bg, black text, 14px font, 700 weight
* Secondary buttons: transparent bg, 1px border, slate text

### 2.5 Sort Order

Jobs are sorted by priority:

1. UNASSIGNED / PENDING\_RESPONSE (action needed — top)
2. SENT (awaiting reply)
3. EN\_ROUTE
4. IN\_PROGRESS
5. ACCEPTED
6. COMPLETED (bottom)

Within same status, sort by urgency: URGENT → TODAY → SCHEDULED

### 2.6 Cost Display Logic

The `CostLine` component shows:

* If only `quotedAmount`: show it in monospace, color #94a3b8
* If both `quotedAmount` and `finalCost`: show quoted with strikethrough in #475569, then final in bold #e2e8f0
* If finalCost differs from quoted: show diff in red (over) or green (under)
* Currency prefix before amount
* Format: `{currency} {amount.toLocaleString()}`

### 2.7 Audit Trail

Each job has a `timeline` array of events. Display as:

* Vertical line on left (1px, rgba(100,116,139,0.12))
* Dot per event (6px circle)
* Last event dot is brighter (#94a3b8), others are dimmer
* Event text + "by" attribution + timestamp (right-aligned)
* Section header: "Audit Trail" in 9px uppercase

***

## 3. IMPLEMENTATION PHASES

### Phase 0: Audit (DO THIS FIRST — DO NOT SKIP)

```
Before making ANY changes, audit the current codebase and report:

1. List ALL React components related to the dashboard and job views
   - grep -r "export.*function\|export default" src/ --include="*.tsx" --include="*.ts" | grep -i "dash\|job\|request\|card\|supplier\|fleet"

2. Identify the main dashboard component(s) — what renders when a logged-in user sees their jobs?

3. Identify the in-app supplier view — what renders when a supplier sees incoming jobs inside the app (NOT the magic link)?

4. Check: Does a view toggle (Fleet Manager | Supplier) exist? If so, where?

5. Check: Do summary metrics exist on the dashboard? What do they show?

6. Check: Do filter pills exist? What filters are available?

7. Check: What does an expanded job card show? List all visible elements.

8. Check: Does the "Respond to this request" panel exist for suppliers with PENDING_RESPONSE jobs?

9. Check: Does the "Mark complete" panel exist for IN_PROGRESS jobs?

10. Check Supabase schema — do these fields exist?
    - quoted_amount (or quotedAmount)
    - final_cost (or finalCost)
    - currency
    - technician name/assignment

Report format for each item: ✅ EXISTS | ⚠️ PARTIAL (explain what's missing) | ❌ MISSING

DO NOT proceed to Phase 1 until you've reported on all 10 items.
```

### Phase 1: Data Layer Verification

```
Ensure the Supabase schema and TypeScript types support the reference design.

Required fields on the service_requests table (or equivalent):
- id (uuid)
- title (text)
- vehicle (text)
- location (text)
- urgency (enum: URGENT, TODAY, SCHEDULED)
- status (enum: UNASSIGNED, SENT, ACCEPTED, EN_ROUTE, IN_PROGRESS, COMPLETED, PENDING_RESPONSE)
- supplier_id (uuid, nullable)
- supplier_name (text, nullable)
- technician_name (text, nullable)
- overseer_name (text, nullable)
- overseer_role (text, nullable)
- quoted_amount (numeric, nullable)
- final_cost (numeric, nullable)
- currency (text, default 'USD')
- eta (text, nullable)
- notes (text, nullable)
- timeline (jsonb — array of {event, time, by} objects)
- created_at, updated_at

If any fields are missing, add them via Supabase migration.
If the TypeScript types don't match, update them.
DO NOT rename existing fields that are already wired up — add new ones alongside.

Commit: "feat: ensure data layer supports full job card schema"
```

### Phase 2: Shared Components

```
Create/update these reusable components to match the reference design EXACTLY.
Put them in src/components/shared/ or equivalent.

Components to create/update:
1. StatusBadge — pill badge with status-specific colors per the table in section 2.4
2. UrgencyTag — text-only urgency label (no background)
3. PersonChip — label + name + optional role
4. CostLine — quoted/final/diff display with strikethrough logic
5. Timeline — audit trail with vertical line, dots, events
6. Metric — summary metric card (value + label)
7. Filters — horizontal scrollable pill filters with counts
8. SmallBtn — secondary action button (transparent bg, border)

Each component must:
- Use inline styles matching the reference (or equivalent Tailwind classes if the project uses Tailwind)
- Accept the same props as shown in the reference
- Handle null/undefined gracefully (e.g., PersonChip with no name returns null)

Commit: "feat: add shared UI components for dashboard redesign"
```

### Phase 3: Dashboard Layout

```
Refactor the main dashboard to match the reference design structure:

1. STICKY TOP BAR with view toggle (Fleet Manager | Supplier)
   - Tab style: underline active tab with green border-bottom
   - Switching views resets filter to "ALL"
   - Background: rgba(11,17,32,0.95) with backdrop-filter: blur(8px)

2. HEADER SECTION
   - Left: FleetOS branding (small green "F" badge + "FleetOS" text) + page title
   - Right: Company name + date (fleet) or supplier name + fleet relationship (supplier)
   - Page title changes: "Today's Dispatch" (fleet) vs "My Jobs" (supplier)

3. SUMMARY METRICS ROW (4-column grid)
   - Fleet: Unassigned | Awaiting | Active | Today ($)
   - Supplier: Action | Active | Done | Earned ($)
   - Colors highlight items needing attention (red for unassigned > 0, yellow for action > 0)
   - Compute from actual job data, not hardcoded

4. FILTER PILLS (horizontal scroll)
   - Fleet: All | Urgent | Today | Active | Done
   - Supplier: All | Action Needed | Active | Done
   - Each pill shows count in parentheses
   - Active filter has subtle background + border

5. JOB CARDS LIST
   - Use the sortJobs function from reference (status priority, then urgency)
   - Empty state: "No jobs match" centered message

Container: max-width 520px, centered, 16px horizontal padding.

Commit: "feat: implement dashboard layout with toggle, metrics, and filters"
```

### Phase 4: Job Card Component

```
This is the most complex component. Build it to match the reference exactly.

COLLAPSED STATE (default for most jobs):
- Click anywhere to expand/collapse
- Shows: urgency tag, ETA, title, vehicle + location, status badge, person chips, cost line, notes preview
- Border color: red if UNASSIGNED, yellow if SENT/PENDING_RESPONSE, subtle default otherwise
- Expand chevron at bottom (rotates 180° when expanded)

EXPANDED STATE:
- Notes in padded card (if notes exist)
- Timeline component (always shown when expanded)
- CONDITIONAL ACTION PANELS:

  A) PENDING_RESPONSE + isSupplier → "Respond to this request" panel:
     - Yellow-tinted background with yellow border
     - Technician name input
     - Currency dropdown (USD, ZAR, EUR, GBP) + quote amount input (side by side)
     - Accept button (green, primary) + Decline button (red outline)

  B) IN_PROGRESS + isSupplier → "Mark complete" panel:
     - Subtle gray background
     - Currency display (read-only) + final cost input (pre-filled with quotedAmount)
     - Note input (optional)
     - "Complete Job" button (green, full width)

  C) NOT completed + NOT pending → Secondary action buttons:
     - "Call Fleet"/"Call Tech" + "WhatsApp" (small, secondary style)

AUTO-EXPAND: Jobs with status PENDING_RESPONSE start expanded.

IMPORTANT: Wire the Accept, Decline, and Complete Job buttons to actual Supabase mutations.
- Accept: update status to ACCEPTED, set technician_name, quoted_amount
- Decline: update status to DECLINED (or equivalent)
- Complete: update status to COMPLETED, set final_cost

Commit: "feat: implement expandable job cards with inline actions"
```

### Phase 5: Data Integration

```
Wire everything to real Supabase data.

1. Fetch jobs for the current user based on their role:
   - Fleet manager: all jobs for their fleet
   - Supplier: jobs assigned to their supplier account

2. Summary metrics should compute from real data:
   - Use .filter() and .reduce() on the fetched jobs array
   - Update in real-time (subscribe to Supabase realtime if already set up)

3. Filter pills should filter the real data (client-side filtering is fine)

4. Job actions (Accept/Decline/Complete) should:
   - Call the existing QueryService or Supabase client methods
   - Optimistically update the UI
   - Handle errors gracefully (show error state, revert optimistic update)

5. If the existing data structure uses different field names than the reference,
   create a mapping/adapter — do NOT rename database columns.

Commit: "feat: wire dashboard to Supabase data layer"
```

### Phase 6: Mobile Responsiveness

```
Test every view at 375px width (iPhone SE). Fix:

1. Summary metrics: 4-column grid should NOT overflow. If needed, reduce padding or font size.
2. Filter pills: must scroll horizontally, not wrap to multiple lines.
3. Job cards: all text must be readable without horizontal scroll.
4. Input fields in action panels: must be full-width, not cut off.
5. Buttons: minimum 44px touch targets.
6. View toggle: must be tappable without mis-taps.
7. Cost line: should wrap gracefully on narrow screens.

Test at these widths: 375px, 390px, 414px, 768px
Fix any overflow, truncation, or layout breaking.

Commit: "fix: mobile responsiveness for dashboard views"
```

### Phase 7: Polish & Cleanup

```
1. Transitions: Add subtle expand/collapse animation on job cards (CSS transition on max-height or similar)
2. Loading state: Show skeleton or spinner while jobs load from Supabase
3. Error state: Show "Unable to load jobs" with retry button if fetch fails
4. Empty state: "No jobs match" when filter returns zero results
5. Page title: Ensure <title> is "FleetOS" not "T420 ROBO-HUB"
6. Remove any dead code, unused imports, or commented-out blocks
7. Verify no Robo-Hub references leak into FleetOS views

Commit: "chore: polish, loading states, and cleanup"
```

***

## 4. ACCEPTANCE CRITERIA

Sprint 2 is DONE when:

1. ✅ Dashboard has working Fleet Manager / Supplier toggle
2. ✅ Summary metrics compute correctly from real job data
3. ✅ Filter pills work and show accurate counts
4. ✅ Job cards expand/collapse with all fields visible
5. ✅ Audit trail renders for every job
6. ✅ Cost display shows quoted vs final with diff
7. ✅ Supplier can accept a PENDING\_RESPONSE job with technician + quote
8. ✅ Supplier can complete an IN\_PROGRESS job with final cost
9. ✅ All actions persist to Supabase
10. ✅ Everything renders correctly at 375px width
11. ✅ Page loads in < 3 seconds on mobile
12. ✅ No Robo-Hub references in FleetOS UI

***

## 5. WHAT NOT TO DO

* ❌ Do NOT change the magic link (external supplier view) — it already works
* ❌ Do NOT change the request creation flow — it works
* ❌ Do NOT change Supabase auth or RLS policies unless required for a specific feature
* ❌ Do NOT add new features not listed here (CSV export, analytics = Sprint 3)
* ❌ Do NOT refactor to a different state management library
* ❌ Do NOT change the routing structure
* ❌ Do NOT touch T420 token, portal, Robo-Hub, or Robo-Dapp code
* ❌ Do NOT introduce new npm dependencies unless absolutely necessary
* ❌ Do NOT split components into separate CSS files — keep styles co-located (inline or Tailwind)
* ❌ Do NOT change the Vercel deployment configuration

***

## 6. REFERENCE FILES

| File                                                      | Purpose                                             |
| --------------------------------------------------------- | --------------------------------------------------- |
| `docs/design-specs/fleetos-preview.jsx`                   | **THE TARGET UI** — study this thoroughly           |
| `docs/PRD-UI-Overhaul.md`                                 | This document                                       |
| Previous: `docs/design-specs/supplier-view-reference.jsx` | Magic link design (Sprint 1, already implemented)   |
| Previous: `docs/PRD-Feb7.md`                              | Sprint 1 PRD (for context on what was already done) |

***

## 7. HOW TO USE THIS PRD

### For Zane (in terminal):

```bash
# 1. Copy files into your repo
mkdir -p docs/design-specs
cp fleetos-ui-prd.md docs/PRD-UI-Overhaul.md
cp fleetos-preview.jsx docs/design-specs/fleetos-preview.jsx
git add docs/ && git commit -m "Add Sprint 2 PRD and reference design"

# 2. Start Claude Code with:
Read docs/PRD-UI-Overhaul.md and the reference component at docs/design-specs/fleetos-preview.jsx.
Start with Phase 0: Audit. Report current state before changing anything.

# 3. After reviewing audit results:
Proceed to Phase 1. Work through each phase in order, committing after each.

# 4. After all phases:
Deploy to Vercel and test the full flow on your phone at fleetos.us
```

### For Claude Code (reading this):

1. **Read the reference JSX first.** Every component, every style, every interaction pattern is defined there. Your implementation should look and behave identically.
2. **Audit before building.** Phase 0 exists because Sprint 1 may have already implemented some of these pieces. Don't rebuild what exists — extend and refine.
3. **One commit per phase.** This keeps the git history clean and makes rollback easy if something breaks.
4. **Preserve existing API integration.** The app already talks to Supabase. Don't rewrite the data layer — wire the new UI to existing services (QueryService, ContactService, etc.).
5. **Test at 375px.** Open DevTools, set to iPhone SE width, verify every screen. This is the primary device for suppliers in the field.

***

*PRD Version 2.0 | February 7, 2026 | Sprint 2: Dashboard & In-App Views* *Reference design: fleetos-preview\.jsx (559 lines)* *Created by Claude Opus 4.6 for Zane/T420*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://t420.gitbook.io/t420/prd.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
