UI-2: Monitor issue Gantt → view tab on issue detail (kill /monitor/issue/.../gantt) #398

Closed
opened 2026-04-26 19:58:49 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator, I want graph and Gantt views of an issue's pipeline timeline to be tabs on a single page, not separate routes, so that switching visualisations doesn't trigger a fresh page load and a redundant SSE re-subscription.

Why

monitor.issue.$owner.$repo.$issueNumber.tsx and monitor.issue.$owner.$repo.$issueNumber.gantt.tsx are two routes that:

  • Run identical fetch (/issues/pipeline?repo=...&issue=...) with identical query keys
  • Duplicate the onPipelineStage SSE patch logic line-for-line
  • Tick now on a 1s interval (only the Gantt needs it, but both subscribe)
  • Differ only in whether <PipelineGraph> or <PipelineGantt> is rendered

Toggling between them triggers full route remount + new SSE subscription, despite the data being identical.

Acceptance criteria

Route consolidation

  • Extend monitor.issue.$owner.$repo.$issueNumber.tsx search schema to accept ?view=graph|gantt (default: graph)
  • Conditional render based on view:
    • graph<PipelineGraph> + <DependencyGraph> (current default behaviour)
    • gantt<PipelineGantt> + active now ticker
  • Tab toggle UI lives in the breadcrumb or sub-header — Graph | Timeline, both linking to the same route with different ?view=
  • Delete apps/web/src/routes/monitor.issue.$owner.$repo.$issueNumber.gantt.tsx
  • Add redirect: /monitor/issue/$owner/$repo/$issueNumber/gantt/monitor/issue/$owner/$repo/$issueNumber?view=gantt

Shared logic extraction

  • Pull onPipelineStage SSE patch handler into a single useIssuePipelineSSE(repo, issue) hook used by both renders
  • now ticker (useEffect + setInterval(1000)) only runs when view === "gantt" (effect cleanup on toggle)
  • PipelineGraph + PipelineGantt components stay as-is; only the route owner changes

Tests

  • Existing monitor.issue.$owner.$repo.$issueNumber.tsx tests extended: ?view=gantt renders Gantt, ?view=graph renders graph, default is graph, toggle updates URL without unmount
  • now ticker only active in Gantt view (assertion via render-count or interval-spy)
  • Redirect test for the deleted gantt route
  • bun run qa clean

Out of scope

  • Changing PipelineGraph or PipelineGantt internals
  • Adding a third view (e.g., raw JSON / log) — separate ticket if desired
  • Folding the dependency graph into a tab (it's a complementary overlay on the graph view, not an alternative)

References

  • UX audit 2026-04-26
  • apps/web/src/routes/monitor.issue.$owner.$repo.$issueNumber.tsx L56-208 — primary surface
  • apps/web/src/routes/monitor.issue.$owner.$repo.$issueNumber.gantt.tsx L48-205 — to be deleted
  • Sibling: UI-1 (#397) — drawer consolidation; this ticket is independent and can land in either order
As an operator, I want graph and Gantt views of an issue's pipeline timeline to be tabs on a single page, not separate routes, so that switching visualisations doesn't trigger a fresh page load and a redundant SSE re-subscription. ## Why `monitor.issue.$owner.$repo.$issueNumber.tsx` and `monitor.issue.$owner.$repo.$issueNumber.gantt.tsx` are two routes that: - Run identical fetch (`/issues/pipeline?repo=...&issue=...`) with identical query keys - Duplicate the `onPipelineStage` SSE patch logic line-for-line - Tick `now` on a 1s interval (only the Gantt needs it, but both subscribe) - Differ only in whether `<PipelineGraph>` or `<PipelineGantt>` is rendered Toggling between them triggers full route remount + new SSE subscription, despite the data being identical. ## Acceptance criteria ### Route consolidation - [ ] Extend `monitor.issue.$owner.$repo.$issueNumber.tsx` search schema to accept `?view=graph|gantt` (default: `graph`) - [ ] Conditional render based on `view`: - `graph` → `<PipelineGraph>` + `<DependencyGraph>` (current default behaviour) - `gantt` → `<PipelineGantt>` + active `now` ticker - [ ] Tab toggle UI lives in the breadcrumb or sub-header — `Graph | Timeline`, both linking to the same route with different `?view=` - [ ] Delete `apps/web/src/routes/monitor.issue.$owner.$repo.$issueNumber.gantt.tsx` - [ ] Add redirect: `/monitor/issue/$owner/$repo/$issueNumber/gantt` → `/monitor/issue/$owner/$repo/$issueNumber?view=gantt` ### Shared logic extraction - [ ] Pull `onPipelineStage` SSE patch handler into a single `useIssuePipelineSSE(repo, issue)` hook used by both renders - [ ] `now` ticker (`useEffect` + `setInterval(1000)`) only runs when `view === "gantt"` (effect cleanup on toggle) - [ ] `PipelineGraph` + `PipelineGantt` components stay as-is; only the route owner changes ### Tests - [ ] Existing `monitor.issue.$owner.$repo.$issueNumber.tsx` tests extended: `?view=gantt` renders Gantt, `?view=graph` renders graph, default is graph, toggle updates URL without unmount - [ ] `now` ticker only active in Gantt view (assertion via render-count or interval-spy) - [ ] Redirect test for the deleted gantt route - [ ] `bun run qa` clean ## Out of scope - Changing `PipelineGraph` or `PipelineGantt` internals - Adding a third view (e.g., raw JSON / log) — separate ticket if desired - Folding the dependency graph into a tab (it's a complementary overlay on the graph view, not an alternative) ## References - UX audit 2026-04-26 - `apps/web/src/routes/monitor.issue.$owner.$repo.$issueNumber.tsx` L56-208 — primary surface - `apps/web/src/routes/monitor.issue.$owner.$repo.$issueNumber.gantt.tsx` L48-205 — to be deleted - Sibling: UI-1 (#397) — drawer consolidation; this ticket is independent and can land in either order
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#398
No description provided.