# Dashboard UI Redesign — Design Spec

## Overview

Redesign the Jenkins Playwright Dashboard from a dark, uniform card grid to a triage-optimized hybrid layout using the Catppuccin Latte color palette. The dashboard monitors ~83 pipelines; the primary user workflow is triaging failures from a browser tab.

## Design Decisions

| Decision | Choice | Rationale |
|----------|--------|-----------|
| Color scheme | Catppuccin Latte (light) | User preference |
| Layout approach | Hybrid: cards for failures, chips for passing | Best density for 83 pipelines with triage-first workflow |
| Building pipelines | Stay in status section of last completed build | A rebuilding red pipeline is still a failure worth watching |
| Link style | Pill row (reports + GitHub) | Scales to multiple Playwright reports, wraps naturally |
| Expand behavior | Click card to expand inline | Shows stage breakdown without leaving the page |
| Filter persistence | URL query params | Shareable links, survives refresh |
| Passing section | Collapsible mini-chips with name + build number | Minimizes noise, still accessible |
| Data visualization | Per-card sparklines (last 10 builds) | Shows trend at a glance |

## Catppuccin Latte Palette

```
Rosewater  #dc8a78    Flamingo   #dd7878    Pink       #ea76cb
Mauve      #8839ef    Red        #d20f39    Maroon     #e64553
Peach      #fe640b    Yellow     #df8e1d    Green      #40a02b
Teal       #179299    Sky        #04a5e5    Sapphire   #209fb5
Blue       #1e66f5    Lavender   #7287fd
Text       #4c4f69    Subtext1   #5c5f77    Subtext0   #6c6f85
Overlay2   #7c7f93    Overlay1   #8c8fa1    Overlay0   #9ca0b0
Surface2   #acb0be    Surface1   #bcc0cc    Surface0   #ccd0da
Base       #eff1f5    Mantle     #e6e9ef    Crust      #dce0e8
```

### Status Color Mapping

| Status | Color | Hex |
|--------|-------|-----|
| Failing (red) | Red | `#d20f39` |
| Other issues (yellow) | Yellow | `#df8e1d` |
| Passing (green) | Green | `#40a02b` |
| Building | Blue | `#1e66f5` |

Background: `Base` (#eff1f5). Card backgrounds: `Mantle` (#e6e9ef). Secondary text: `Subtext0` (#6c6f85). Muted text: `Overlay1` (#8c8fa1).

## Layout Structure (top to bottom)

### 1. Header Bar

- Left: title "Pipeline Monitor" (15px, semi-bold, `Text` color)
- Right: search input + "Polled Xs ago" + connection indicator
- Search: client-side filter by pipeline name, `Surface0` background with `Surface1` border

### 2. Stats Ribbon

Three clickable stat cards in a row:

| Card | Background | Border | Label |
|------|-----------|--------|-------|
| Failing | `rgba(210,15,57,0.08)` | `rgba(210,15,57,0.15)` | Count + "FAILING" |
| Issues | `rgba(223,142,29,0.08)` | `rgba(223,142,29,0.15)` | Count + "ISSUES" |
| Passing | `rgba(64,160,43,0.06)` | `rgba(64,160,43,0.1)` | Count + "PASSING" |

Clicking a stat card sets the URL filter to that status. The count is large (22px, bold), the label is small uppercase (10px).

Building pipelines are counted in their last-completed-build status group, not separately.

### 3. Failing Section

Section header: "FAILING" in uppercase Red, 11px, semi-bold, 0.5px letter-spacing.

3-column grid of failure cards. Each card:

- `Mantle` background, 8px border-radius, 3px solid Red left border
- **Row 1:** Pipeline name (12px, bold, Red) | build number + relative time (10px, `Overlay1`, clickable link to Jenkins)
- **Row 2:** Failed stage names + "FAILED" (11px, `Subtext0`)
- **Row 3:** Sparkline — last 10 builds as 4px-wide colored bars (Red/Yellow/Green), 14px tall
- **Row 4:** Pill-row links — report links (Blue pills with ↗ icon, labeled with stage name) + GitHub repo link (gray pill with GitHub logo + `{org}/{repo}` label)

**Building indicator on failure cards:**
- 2px shimmer bar at card bottom (Blue gradient animating left→right)
- "BUILDING #N" badge: Blue text on `rgba(30,102,245,0.1)` background, 9px font, rounded, clickable link to the in-progress Jenkins build
- Card still displays last completed build's failure info, reports, and sparkline

**Expanded card (on click):**
- Spans 2 grid columns on md+ screens; on smaller screens (1-2 col grid), expands in place at full width
- If the card is in the last column position, it spans left instead of overflowing
- Subtle box-shadow (`0 2px 8px rgba(0,0,0,0.06)`)
- Adds "Stage Breakdown" section below sparkline:
  - Each stage as a row: ✓/✗ icon (Green/Red) + stage name + duration
  - Failed stages are bold Red, passing stages are `Subtext1`
- Shows total build duration in header row

### 4. Other Issues Section

Section header: "OTHER ISSUES" in uppercase Yellow.

Same 3-column grid, simpler cards:
- `Mantle` background, 3px solid Yellow left border
- Pipeline name (12px, bold, Yellow) | build number + relative time (clickable link to Jenkins)
- Failure description (11px, `Subtext0`)
- Building shimmer + badge if applicable
- Expandable with same stage breakdown on click

### 5. Passing Section (collapsible)

Section header: "PASSING" (Green) + "N pipelines ▾" (clickable toggle).

**Collapsed (default):** Flex-wrap row of mini-chips:
- Each chip: `Mantle` background, 4px border-radius, `Surface0` border
- Content: pipeline name (10px, `Subtext0`) + build number (10px, `Overlay0`)
- Building indicator: Blue ⟳ icon next to name
- "+N more" chip at end if many pipelines
- Clicking a chip opens Jenkins URL

**Expanded:** Shows all chips.

## New Features

### Search

- Input in header, filters pipelines client-side by name (case-insensitive substring match)
- When search is active, all sections show only matching pipelines
- Empty search shows all pipelines

### Sparklines

- Display last 10 completed builds as colored bars per failure/issue card
- Each bar: 4px wide, 14px tall, 1px gap, rounded top corners
- Color matches build status: Red, Yellow, or Green
- If fewer than 10 builds exist, render only available bars (left-aligned, no empty slots)
- Data source: `recentBuilds` field on the `/api/pipelines` response (no separate API call needed)

### Click-to-Expand Cards

- Clicking a failure/issue card expands it to show stage breakdown
- Expanded card takes 2 grid columns, other cards reflow
- Stage breakdown shows: status icon, stage name, duration
- Click again to collapse
- Only one card expanded at a time (expanding another collapses the previous)

### URL Filter Persistence

- Active status filter stored as URL query parameter: `?filter=red`, `?filter=yellow`, `?filter=green`
- No param or `?filter=all` shows all sections
- Clicking stat ribbon cards updates URL
- On page load, read filter from URL and apply
- Use `URLSearchParams` + `window.history.replaceState` (no full navigation)

### GitHub Repo Link

- Each pipeline card includes a GitHub repo link pill
- URL derived server-side from the Jenkins URL structure: Jenkins URLs follow the pattern `{jenkinsBase}/job/{org}/job/{repo}/job/{branch}`, so the repo name is extracted from the second `/job/` segment
- Combined with `config.github.org` (already exists in `config.json` as `github.org: "theorchard"`) to form: `https://github.com/{org}/{repo}`
- If extraction fails (unexpected URL format), `githubUrl` is `null` and the pill is not shown
- Pill styled as gray variant with GitHub SVG logo + `{org}/{repo}` label (e.g. `theorchard/my-repo`)

### Per-Stage URL Extraction

- The poller fetches console output per failed Playwright stage (not the whole build) using Jenkins' per-node log endpoint: `{jobPath}/{buildNumber}/execution/node/{nodeId}/wfapi/log`
- This ensures extracted report URLs are associated with the correct stage (e.g. "E2E Tests" vs "E2E Tests on PR Instance")
- Report link pills display the stage name as their label
- URLs are deduplicated across stages
- Fallback: if per-stage fetching yields no URLs, the full build console is fetched and URLs are assigned to the first Playwright stage

## API Changes

### `GET /api/pipelines` — Extended Response

**New field: `recentBuilds`**

```typescript
recentBuilds: Array<{ buildNumber: number; status: "green" | "red" | "yellow" }>
```

Last 10 completed builds (status only), used for sparklines. Queried per-pipeline inside `buildPipelinesResponse` using a single query with `WHERE status != 'building' ORDER BY build_number DESC LIMIT 10`. For 83 pipelines this adds 83 lightweight queries; since the DB is local SQLite with WAL mode, this is fast (<1ms each). If profiling shows it's slow, batch into a single query with `GROUP BY pipeline_id` using a window function.

If a pipeline has fewer than 10 builds, return however many exist. The sparkline component renders only available bars.

**New field: `githubUrl`**

```typescript
githubUrl: string | null
```

Derived server-side by parsing the Jenkins URL. Jenkins URLs follow the pattern `{base}/job/{org}/job/{repo}/job/{branch}`. Extract the repo name from the second `/job/` segment, combine with `config.github.org` to produce `https://github.com/{org}/{repo}`. Return `null` if the URL doesn't match the expected pattern.

**New field: `buildingBuildNumber`**

```typescript
buildingBuildNumber: number | null
```

When `isBuilding` is true, this is the build number of the in-progress run (from the existing `buildingRun` query in `buildPipelinesResponse`). When not building, this is `null`.

### Updated `PipelineResponse` type:

```typescript
interface PipelineResponse {
  id: number;
  name: string;
  jenkinsUrl: string;
  githubUrl: string | null;
  latestRun: {
    buildNumber: number;
    status: string;
    failedStages: string[];
    startedAt: number | null;
    durationMs: number | null;
    extractedUrls: { url: string; label: string | null; stageName: string }[];
  } | null;
  isBuilding: boolean;
  buildingBuildNumber: number | null;
  recentBuilds: { buildNumber: number; status: "green" | "red" | "yellow" }[];
}
```

Note: `isBuilding` is now only at the top level (removed from inside `latestRun` to eliminate the previous duplication).

### `GET /api/pipelines/:id/runs` — No changes

Existing endpoint already returns full stage data needed for expanded card view.

## Frontend Component Changes

### New/Modified Components

| Component | Change |
|-----------|--------|
| `App.tsx` | Switch from `bg-gray-950` to Catppuccin Latte `Base`; remove dark classes from `index.html` body |
| `Dashboard.tsx` | New layout: header, stats ribbon, sectioned cards, passing chips |
| `PipelineCard.tsx` | Redesign: left border, sparkline, pill links, expand/collapse |
| `FilterBar.tsx` | Delete and replace with new `StatsRibbon.tsx` — clickable stat cards with counts |
| `types.ts` | Add `recentBuilds`, `githubUrl`, `buildingBuildNumber`; remove `isBuilding` from `LatestRun`; `StatusFilter` type unchanged |
| `usePipelines.ts` | No changes to hook logic |
| New: `useUrlFilter.ts` | Custom hook: read/write filter to URL query params |
| New: `Sparkline.tsx` | Reusable sparkline component |
| New: `StageBreakdown.tsx` | Expanded card stage list (fetches from `/api/pipelines/:id/runs`) |
| `index.css` | Define Catppuccin Latte CSS custom properties + shimmer animation |

### CSS Custom Properties (index.css)

```css
:root {
  --ctp-red: #d20f39;
  --ctp-yellow: #df8e1d;
  --ctp-green: #40a02b;
  --ctp-blue: #1e66f5;
  --ctp-text: #4c4f69;
  --ctp-subtext0: #6c6f85;
  --ctp-overlay1: #8c8fa1;
  --ctp-overlay0: #9ca0b0;
  --ctp-surface1: #bcc0cc;
  --ctp-surface0: #ccd0da;
  --ctp-base: #eff1f5;
  --ctp-mantle: #e6e9ef;
  --ctp-crust: #dce0e8;
}
```

Tailwind config extended with these as named colors for use in utility classes.

## Data Flow

```
Jenkins API → Poller → SQLite → /api/pipelines (with recentBuilds + githubUrl)
                                       ↓
                               usePipelines hook (10s poll)
                                       ↓
                        Dashboard (sections by status)
                              ↓              ↓
                      PipelineCard     PassingChips
                    (sparkline, pills)  (mini chips)
                           ↓
                   StageBreakdown (on expand, fetches /api/pipelines/:id/runs)
```

## Out of Scope

- Dark mode / theme toggle (Latte only for now)
- Grouping by team/project
- Keyboard shortcuts
- Desktop notifications
- Sort options within sections
