One row at a time was tedious for anything beyond a handful of items.
New useSelection hook (id-based Set, so picks made on page 1 survive
moving to page 2 rather than resetting per page) backs a checkbox
column and a bulk-actions bar on each of the three tables:
- Secrets / IP Addresses: bulk delete.
- Tailscale devices: bulk authorize/deauthorize/remove.
No new backend endpoints — each bulk action is a client-side
Promise.allSettled loop over the existing single-item endpoints, so
one failure doesn't block the rest, and the bar reports how many (if
any) failed. Selection clears after a bulk action completes or when
switching Tailscale integrations (device ids aren't comparable across
different tailnets).
Verified the selection hook's Set logic directly (extracted as pure
functions, no React renderer needed): select-all/deselect-all
toggling, individual toggle, and — the case most likely to have a
subtle bug — that selecting items on one page and then selecting
different items on another page keeps both, with toggleAll on either
page only ever touching that page's own ids.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>