Public: anyone can read this; the crew can change it.

Remove on a board panel goes through the confirm page, which names the panel, and refuses if the board changed underneath #8037

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The Remove button on each board panel now goes through the confirm page instead of posting on one click. The panel's index and title are in the confirm target, `/boards/<slug>/remove?index=<i>&title=<title>`. If the board was reordered between the two clicks, nothing is removed, so the wrong panel can't go.

shipped production is serving it deploy b070674

opened this

The Remove button on each board panel now goes through the confirm page instead of posting on one click. The panel's index and title are in the confirm target, /boards/<slug>/remove?index=<i>&title=<title>. If the board was reordered between the two clicks, nothing is removed, so the wrong panel can't go.

  • src/boards/edit.ts: remove takes an optional title. If it doesn't match the panel at index, applyEdit refuses with "that panel moved; nothing was removed". Without a title it works as before, so the terminal's x is unchanged.
  • src/web/pages/boards-view.ts: the per-panel form is now confirmAction({ action: …, verb: "Remove", size: "sm" }). That drops the old per-panel screen-reader label "Remove <title>", because the issue gives the exact call.
  • src/web/pages/boards-edit.ts: editOf reads index and title from the form, or from ctx.url.searchParams when the form has no index.
  • src/web/pages/search.ts: one new DESTRUCTIVE entry, directly after /boards/…/delete. Its page title is "Remove the panel <title> from <slug>?", and the title is escaped by the template. The names are decoded with a small decodedOr that keeps the raw text if the encoding is malformed, so a hand-typed bad % can't make the page fail with a 500.
  • The pin line in the detail is true today: when home reads its pins, it drops any whose panel no longer exists (resolved in src/actions/board-pins.ts). So the pin goes with the panel, as the detail says.
  • Tests:

- test/danger-buttons.test.ts no longer lists the panel Remove as an exception. - The new test/confirm-board-panel.test.ts checks that the board page links to the confirm page, and that the confirm page answers 200 with the escaped title (using a panel called "Runs & kinds"). It also checks that a POST removes the right panel, that a POST whose title no longer matches removes nothing and shows the message, and that remove without a title works as before. All 5 fail with the source change stashed. - test/boards-store.test.ts was wrong after this change: it still expected the old one-click form. It now expects the confirm link.

Checks run: scripts/gate.ts, tsc --noEmit and design-lint are clean. scripts/fast.ts named 1000 files (over its budget), so I ran the covering board, confirm, pin and danger-button suites myself instead: all pass. My first push still had the old boards-store test failing; the second commit on the branch fixes it, and the tip passes.

To try it: open /boards/<slug> as an admin, click Remove on a panel, read the confirm page, then confirm.

🤖 Generated with Claude Code

Closes #8030

commented

The Remove button on each board panel now goes through the confirm page instead of posting on one click. The panel's index and title are in the confirm target, /boards/<slug>/remove?index=<i>&title=<title>. If the board was reordered between the two clicks, nothing is removed, so the wrong panel can't go.

  • src/boards/edit.ts: remove takes an optional title. If it doesn't match the panel at index, applyEdit refuses with "that panel moved; nothing was removed". Without a title it works as before, so the terminal's x is unchanged.
  • src/web/pages/boards-view.ts: the per-panel form is now confirmAction({ action: …, verb: "Remove", size: "sm" }). That drops the old per-panel screen-reader label "Remove <title>", because the issue gives the exact call.
  • src/web/pages/boards-edit.ts: editOf reads index and title from the form, or from ctx.url.searchParams when the form has no index.
  • src/web/pages/search.ts: one new DESTRUCTIVE entry, directly after /boards/…/delete. Its page title is "Remove the panel <title> from <slug>?", and the title is escaped by the template. The names are decoded with a small decodedOr that keeps the raw text if the encoding is malformed, so a hand-typed bad % can't make the page fail with a 500.
  • The pin line in the detail is true today: when home reads its pins, it drops any whose panel no longer exists (resolved in src/actions/board-pins.ts). So the pin goes with the panel, as the detail says.
  • Tests:

- test/danger-buttons.test.ts no longer lists the panel Remove as an exception. - The new test/confirm-board-panel.test.ts checks that the board page links to the confirm page, and that the confirm page answers 200 with the escaped title (using a panel called "Runs & kinds"). It also checks that a POST removes the right panel, that a POST whose title no longer matches removes nothing and shows the message, and that remove without a title works as before. All 5 fail with the source change stashed. - test/boards-store.test.ts was wrong after this change: it still expected the old one-click form. It now expects the confirm link.

Checks run: scripts/gate.ts, tsc --noEmit and design-lint are clean. scripts/fast.ts named 1000 files (over its budget), so I ran the covering board, confirm, pin and danger-button suites myself instead: all pass. My first push still had the old boards-store test failing; the second commit on the branch fixes it, and the tip passes.

To try it: open /boards/<slug> as an admin, click Remove on a panel, read the confirm page, then confirm.

🤖 Generated with Claude Code

Changes requested — before the latest push

The flow works and is tested: Remove links to the confirm page, the page names the panel (escaped), a stale title refuses, and the terminal's x is unchanged. One thing to fix first: the per-panel Remove links lost their accessible name. A board with N panels now has N links that all read just "Remove" to a screen reader, while Up and Down on the same row still say which panel they act on. Fix: give confirmAction an optional label that it passes to btn (btn already supports label), pass Remove ${p.title} from boards-view, and assert aria-label in the board-page test. Smaller point: the stale guard compares titles only, so two panels with the same title swapped still remove the wrong one. Say so in the README or compare the query as well.

commented

Remove on a board panel now asks first, on the confirm page, which names the panel and its board (escaped) and offers one Remove button. The confirm target is /boards/<slug>/remove?index=<i>&title=<title>. If a different panel sits at that index when the POST arrives, nothing is removed and the page says "that panel moved; nothing was removed". Without a title, removal by index works as before, so the terminal's x is unchanged.

  • src/boards/edit.ts: the remove edit takes an optional title and refuses when it doesn't match the panel at that index.
  • src/web/pages/boards-edit.ts: editOf reads index and title from the form, or from the URL query when the form has no index.
  • src/web/pages/boards-view.ts: each panel's Remove is a confirmAction link.
  • Remove link names (from review): every Remove link is named for its own panel ("Remove <title>"), as Up and Down already are. confirmAction in src/web/ui.ts takes an optional label and passes it to btn.
  • src/web/pages/search.ts: one new DESTRUCTIVE entry, directly after the board delete one.

- Its detail says a pin of the panel on home goes with it. That is true today: home drops pins whose panel is gone (resolved in src/actions/board-pins.ts). - Names are decoded with a small decodedOr, so a malformed % shows as typed instead of making the page fail with a 500.

  • Not done: duplicate titles. The check compares titles only. If two panels have the same title and are swapped between the two clicks, the one now at that index is removed. Board titles may repeat, but home pins already tell panels apart by title alone. The README says this and suggests distinct titles. Comparing the query as well would mean a longer confirm URL and a different pattern from the one the issue gives.
  • Tests:

- test/danger-buttons.test.ts no longer lists the panel Remove as an exception. - The new test/confirm-board-panel.test.ts checks: - the board page links Remove to the confirm page, with a per-panel aria-label; - the confirm page answers 200 with the escaped title; - the POST removes that panel; - a POST with a stale title removes nothing and says so; - removal by index alone still works. - All five failed with the source change stashed; I checked that before adding the aria-label assertions. - test/boards-store.test.ts was wrong after this change: it expected the old one-click form, and it now expects the confirm link.

  • Checks: scripts/gate.ts, tsc --noEmit and design-lint are clean, and the board, confirm, pin and danger-button suites pass. scripts/fast.ts named 1000 files, too many to run here, so I chose these suites myself.

To try it: open /boards/<slug> as an admin, click Remove on a panel, read the confirm page, and confirm.

🤖 Generated with Claude Code

Approved

Earlier asks: (1) each Remove link named for its own panel, met: confirmAction takes a label and the board page passes Remove <title>, and the test asserts the aria-labels. (2) The duplicate-title gap, met: the README states it and tells people to use distinct titles. I could not diff against 9fb4456ba932 because the clone lacks that commit, so I read the change as it stands. It is a plain link to the confirm page, so it works without JavaScript. The confirm page escapes the title. The stale-title refusal is tested, and the preview answers 200.

Two things I would do next:

  1. Put an index-plus-title check on the duplicate-title case, for example a per-panel id, so swapped same-title panels cannot remove the wrong one.
  2. Make the terminal x remove ask first, so it matches the page. It still removes by index with no title.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs