Milestones page: explicit View-issues button instead of title-link, right-aligned Close/Delete with danger red, closed milestones collapse to one line #314

Closed
opened 2026-09-10 22:36:39 +00:00 by crueber · 3 comments
Owner

What's requested

Rework the milestones page card layout (/:owner/:name/milestones, web/src/pages/Milestones.jsx):

  1. The linked-issues list becomes a button, not an inline list. Today the milestone title is the link to the filtered issue list (?milestone=<id>) — undiscoverable (nothing says the title is clickable). Instead: below the listed issues, an explicit "View N issues"-style button that navigates to the milestone-filtered issue list.
  2. Action buttons move right; Delete goes danger-red. The Close and Delete buttons sit in a left-aligned row (<div class="flex gap-1">); move them to the right side of the card (ml-auto / justify-end), and style Delete with the existing btn danger treatment (used elsewhere, e.g. release delete).
  3. Closed milestones collapse out of the card grid — like closed comments on an issue page. A closed milestone renders as one line:
    • the milestone title, linked to the issue filter (same ?milestone=<id> link),
    • a Reopen button,
    • the open/closed issue counts ({open_issues} open · {closed_issues} closed).
      No progress bar, no issues list, no Delete button for closed milestones.

Current implementation (code evidence)

  • One card shape for both states: <li class="card grid gap-1 p-3"> renders title-link (state chip + counts) → progress bar → <MilestoneIssues> inline list (one GET …/issues?milestone=<id>&n=100 per milestone, MilestoneIssues at :21-52) → Close/Delete button row (:139-147).
  • State toggle exists (close() → milestones.update(id, {state: open|closed}), triage-gated); delete 409s while open issues reference the milestone (documented in the file header) — which is why closed milestones currently still show Delete; removing Delete from the closed shape is safe since a closed milestone's deletion can be done after reopening (or via API) — note this trade in the PR.
  • The ?milestone= filter link already exists on the title (:116-123); reuse the same href for the new button.

Design details

  • Open milestone card: title (still linked, but as plain text-style heading now that a button carries the affordance) + state chip + counts on the first line; progress bar; MilestoneIssues list; then a footer row: "View issues" button left, Close / Delete right (Delete = btn danger, keep the existing no-confirm behavior or add the typed-confirm if trivial — implementer's call, note it).
  • Closed milestone row: single flex line — linked title, Reopen button, counts right-aligned. Render these in a separate section below the open cards (a muted "Closed" heading or simple divider), mirroring how the issues page separates closed comments/state.
  • Keep the existing triage gating on Close/Reopen/Delete; the "View issues" button is a read affordance (no gate).
  • Empty milestones (0 issues): the button still renders ("View issues" → empty filtered list) so the affordance is consistent; alternatively hide it at 0 — pick one, note it.

Acceptance criteria

  • Open milestone cards show an explicit issues-list button below the issue list; clicking navigates to /:owner/:name/issues?milestone=<id>.
  • Close and Delete are right-aligned on the card; Delete is btn danger red.
  • Closed milestones render as a single line (linked title → issue filter, Reopen button, open/closed counts) in their own section below open milestones — no bar, no list, no Delete.
  • Reopen restores the full card shape.
  • Triage gating unchanged on Close/Reopen/Delete; counts and progress math unchanged for open milestones.
  • Light/dark themes consistent; layout intact at 390px mobile width.
  • Headless test update if MilestoneIssues/the page's state logic is factored (the closed-vs-open render split should live in testable pure logic where practical).
## What's requested Rework the milestones page card layout (`/:owner/:name/milestones`, `web/src/pages/Milestones.jsx`): 1. **The linked-issues list becomes a button, not an inline list.** Today the milestone title is the link to the filtered issue list (`?milestone=<id>`) — undiscoverable (nothing says the title is clickable). Instead: below the listed issues, an explicit **"View N issues"**-style button that navigates to the milestone-filtered issue list. 2. **Action buttons move right; Delete goes danger-red.** The Close and Delete buttons sit in a left-aligned row (`<div class="flex gap-1">`); move them to the **right side** of the card (`ml-auto` / `justify-end`), and style Delete with the existing `btn danger` treatment (used elsewhere, e.g. release delete). 3. **Closed milestones collapse out of the card grid** — like closed comments on an issue page. A closed milestone renders as **one line**: - the milestone **title, linked to the issue filter** (same `?milestone=<id>` link), - a **Reopen** button, - the **open/closed issue counts** (`{open_issues} open · {closed_issues} closed`). No progress bar, no issues list, no Delete button for closed milestones. ## Current implementation (code evidence) - One card shape for both states: `<li class="card grid gap-1 p-3">` renders title-link (state chip + counts) → progress bar → `<MilestoneIssues>` inline list (one `GET …/issues?milestone=<id>&n=100` per milestone, `MilestoneIssues` at :21-52) → Close/Delete button row (:139-147). - State toggle exists (`close()` → `milestones.update(id, {state: open|closed})`, triage-gated); delete 409s while open issues reference the milestone (documented in the file header) — which is why closed milestones currently still show Delete; removing Delete from the closed shape is safe since a closed milestone's deletion can be done after reopening (or via API) — note this trade in the PR. - The `?milestone=` filter link already exists on the title (`:116-123`); reuse the same href for the new button. ## Design details - Open milestone card: title (still linked, but as plain text-style heading now that a button carries the affordance) + state chip + counts on the first line; progress bar; `MilestoneIssues` list; then a footer row: **"View issues" button** left, **Close / Delete** right (Delete = `btn danger`, keep the existing no-confirm behavior or add the typed-confirm if trivial — implementer's call, note it). - Closed milestone row: single flex line — linked title, Reopen button, counts right-aligned. Render these in a separate section below the open cards (a muted "Closed" heading or simple divider), mirroring how the issues page separates closed comments/state. - Keep the existing triage gating on Close/Reopen/Delete; the "View issues" button is a read affordance (no gate). - Empty milestones (0 issues): the button still renders ("View issues" → empty filtered list) so the affordance is consistent; alternatively hide it at 0 — pick one, note it. ## Acceptance criteria - [ ] Open milestone cards show an explicit issues-list button below the issue list; clicking navigates to `/:owner/:name/issues?milestone=<id>`. - [ ] Close and Delete are right-aligned on the card; Delete is `btn danger` red. - [ ] Closed milestones render as a single line (linked title → issue filter, Reopen button, open/closed counts) in their own section below open milestones — no bar, no list, no Delete. - [ ] Reopen restores the full card shape. - [ ] Triage gating unchanged on Close/Reopen/Delete; counts and progress math unchanged for open milestones. - [ ] Light/dark themes consistent; layout intact at 390px mobile width. - [ ] Headless test update if `MilestoneIssues`/the page's state logic is factored (the closed-vs-open render split should live in testable pure logic where practical).
crueber added this to the v1 milestone 2026-09-10 22:36:39 +00:00
Author
Owner

PR #317 (fix/issue-314) implements the rework: open cards get the explicit View N issues button left + right-aligned Close/Delete (danger-red), closed milestones collapse to one-line rows in a Closed section. Trades noted in the PR (delete-after-reopen; View 0 issues still rendered). Tests: 600/600 node --test green, vite build green.

PR #317 (fix/issue-314) implements the rework: open cards get the explicit View N issues button left + right-aligned Close/Delete (danger-red), closed milestones collapse to one-line rows in a Closed section. Trades noted in the PR (delete-after-reopen; View 0 issues still rendered). Tests: 600/600 node --test green, vite build green.
Author
Owner

Review of PR #317 (fix/issue-314, commit 5ee195c) — verified in scratch worktree /tmp/pr317 (node tests + reasoning, no browser per instructions).

PASS — all checklist items hold:

  • Open cards (Milestones.jsx:122-161): plain h3 + chip + counts first line; progressbar intact (130-138); MilestoneIssues list intact (139); footer has View button left with filter href + total counts (141-147, via milestoneFilterHref/milestoneTotal) and Close + danger Delete right-aligned via ml-auto (148-159).
  • Closed rows (170-184): single line — linked title + Reopen + counts right; no bar/list/Delete; grouped under Closed heading (165-188), section hidden when empty. Empty-state fallbacks sane (118: no open milestones vs no milestones yet).
  • Triage gating preserved: no client-side gate existed (server-side per header comment); close()/remove() handlers byte-identical to main. No gating added or lost.
  • No-delete-on-closed tradeoff sane and documented in header comment (12-19): reopen-then-delete path works (close() toggles on m.state), API delete remains.
  • Helpers pure + tested (lib/milestones.js:56-77): splitMilestones null-safe, order-preserving, unknown states fail visible into open (tested); milestoneFilterHref shared by View button + closed title link, encodes ids (tested); milestoneTotal null-safe (tested).
  • No new deps (one relative import); dark+light variants retained (progress track, closed title link); header comment matches implementation (law 12; no other docs describe the page shape, so nothing else to update).

NIT (non-blocking): an unknown-state milestone renders in the open section with a Close label, but close() would PATCH state->open for it (toggle logic, Milestones.jsx:82). Unreachable in practice (server emits only open/closed) and fail-visible placement is correct — leaving as-is.

VERIFY: node --test web/test/unit/*.test.js → 600/600 pass (incl. 11/11 milestones incl. 4 new #314 tests); vite build OK in 1.84s (only pre-existing chunk-size warning). Note: scratch worktree initially lacked web/node_modules (gitignored), so markdown-dependent suites errored until node_modules was symlinked from the main worktree — environmental, not PR-caused.

RECOMMENDATION: ready to merge.

Review of PR #317 (fix/issue-314, commit 5ee195c) — verified in scratch worktree /tmp/pr317 (node tests + reasoning, no browser per instructions). PASS — all checklist items hold: - Open cards (Milestones.jsx:122-161): plain h3 + chip + counts first line; progressbar intact (130-138); MilestoneIssues list intact (139); footer has View button left with filter href + total counts (141-147, via milestoneFilterHref/milestoneTotal) and Close + danger Delete right-aligned via ml-auto (148-159). - Closed rows (170-184): single line — linked title + Reopen + counts right; no bar/list/Delete; grouped under Closed heading (165-188), section hidden when empty. Empty-state fallbacks sane (118: no open milestones vs no milestones yet). - Triage gating preserved: no client-side gate existed (server-side per header comment); close()/remove() handlers byte-identical to main. No gating added or lost. - No-delete-on-closed tradeoff sane and documented in header comment (12-19): reopen-then-delete path works (close() toggles on m.state), API delete remains. - Helpers pure + tested (lib/milestones.js:56-77): splitMilestones null-safe, order-preserving, unknown states fail visible into open (tested); milestoneFilterHref shared by View button + closed title link, encodes ids (tested); milestoneTotal null-safe (tested). - No new deps (one relative import); dark+light variants retained (progress track, closed title link); header comment matches implementation (law 12; no other docs describe the page shape, so nothing else to update). NIT (non-blocking): an unknown-state milestone renders in the open section with a Close label, but close() would PATCH state->open for it (toggle logic, Milestones.jsx:82). Unreachable in practice (server emits only open/closed) and fail-visible placement is correct — leaving as-is. VERIFY: node --test web/test/unit/*.test.js → 600/600 pass (incl. 11/11 milestones incl. 4 new #314 tests); vite build OK in 1.84s (only pre-existing chunk-size warning). Note: scratch worktree initially lacked web/node_modules (gitignored), so markdown-dependent suites errored until node_modules was symlinked from the main worktree — environmental, not PR-caused. RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #317 (review clean; open/closed shapes, gating, helpers verified; 600/600), merged. Closing.

Fixed by PR #317 (review clean; open/closed shapes, gating, helpers verified; 600/600), merged. Closing.
Sign in to join this conversation.
No milestone
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
crueber/walhub#314
No description provided.