feat(desktop): group, drag, and bulk-edit automations

Merged
KI
1d ago
+3468-186

Stack (merge in order)

  1. #9208: migration
  2. #9209: API, CLI, MCP
  3. this PR: desktop UI and docs

Film and walkthrough (78s, real input in the dev app): https://app.superset.sh/page/automation-groups-mlki3d

Summary

This PR bundles four related changes:

  1. Grouped table: collapsible section rows (A–Z, "No group" last). Headers sit close to the edge and are shorter than rows, so rows read as children. Hover a header for + to create an automation already in that group.
  2. Move to group: a submenu in the row's right-click and ⋯ menus and the detail page's ⋯ menu. A filter field sits on top; typing a name that matches nothing shows "Create group", like Linear's "Move to project". The detail breadcrumb shows the group.
  3. Multiselect and bulk bar: ⌘-click starts a selection, and checkboxes appear only then. A bar offers Move to group, Pause, Resume, and Delete.
  4. Drag between groups: drag a row, or the whole selection, onto another section. Dropping on "No group" ungroups.

Added during review:

  • New group… at the end of every Move to group menu. It keeps the menu open, the field becomes "New group name…", and Enter creates the group.
  • Right-click a group header to choose Delete group…. It confirms first, then moves the group's automations to No group; the automations are kept. Mine tab only, and not on "No group".
  • Review fixes:
    • IME composition no longer triggers a move.
    • Bulk pause and resume refresh the detail page.
    • Hidden rows leave the selection.
    • Deleting one row keeps an unrelated selection.
    • ⌘X no longer toggles a row.
    • The optimistic move cancels any in-flight list fetch first.
    • The i18n catalog-integrity check now handles a nested placeholder at the end of a plural branch.

Keyboard and mouse

InputAction
⌘ / Ctrl click a rowStart a selection, or toggle the row (does not open it)
⇧ clickSelect the on-screen range, across groups
XToggle the focused row
⌘ click a group headerSelect or clear the group (a plain click collapses it)
⌘A / EscSelect everything on screen / clear the selection
Drag a rowMove it, or the selection it belongs to, to the section under the pointer

How it works

  • Sorting applies within each group; the groups themselves stay A–Z. Search filters within groups, and an empty group disappears.
  • Moves are optimistic: utils.automation.list.setData, then invalidate. Bulk actions call setGroup, setEnabled, and delete per row through Promise.allSettled, so no batch endpoint is needed.
  • Drag uses @dnd-kit/core, like the Linear board. The mouse sensor starts after 8px so clicks still open rows. A one-shot capture listener swallows the click that follows a drop. Auto-scroll starts only in the outer 10% of the list.
  • Selection and drag are on the Mine tab only, because every mutation is owner-scoped on the server.

Design decisions

  • A group, not run tags. automations.tags file run workspaces into sidebar folders and default to ["automation"]. Grouping by them would put every row in one section and tie list organization to the sidebar.
  • One group per automation. Each row appears in exactly one section.
  • No hover checkboxes. They appear only while selecting, at the request of the feature owner.

Manual QA (dev app over CDP, real mouse and keyboard input)

  • Create a group from a row by typing a new name and pressing Enter; partial filter ("gro" picks Growth); keyboard only (→, ↓, Enter)
  • Typing while the pointer still rests on "Move to group" goes into the filter
  • ⌘-click, ⇧-click range across sections, X, ⌘A, Esc, ⌘-click a group header; no stray text selection
  • Bulk move into a new group, bulk pause then resume, bulk delete ("Delete 2 automations?"); Esc with the dialog open keeps the selection
  • Drag one row onto a header, a 2-row selection onto a row, onto "No group", onto a collapsed header, and within the same group (no API call)
  • A plain click still opens the row after drag support was added; the drop never opens the row under it
  • Header + creates in the group and opens with the group in the breadcrumb
  • Team tab: flat-padded where ungrouped, no checkboxes, no drag, no "Move to group" on teammates' rows
  • Checkboxes are centered (the shared TableCell nudges checkboxes 2px; overridden); names do not shift when checkboxes appear
  • No console errors across the runs

Testing

  • bun run typecheck, ./scripts/lint.sh, bun run check:i18n (strict compile; new strings translated in all 17 locales with plural forms)
  • bun test apps/desktop/src/renderer/routes/_authenticated/_dashboard: 1,797 tests, 0 failures, including new groupAutomations and selectRange tests

Known limitations

  • Collapsed sections and the selection reset when you leave the page. Keeping them needs a registered persisted key.
  • No rename-group action, and no manual ordering within a group (sort applies).
  • Bulk actions are one request per row: fine for tens of rows; a batch endpoint would matter only at hundreds.

Deployment

Merge after #9209 is live in production; this PR calls automation.setGroup and reads group.


Summary by cubic

Groups automations into collapsible A–Z sections on the Mine tab, with "No group" last, and adds "Move to group" (filter with inline create), drag-and-drop between sections, and multiselect (⌘‑click, ⇧‑range, X, ⌘A, Esc) with a bulk bar for moving, pausing, resuming, and deleting. The detail breadcrumb shows the group, and a hover + on a section header creates an automation in that group. The "Move to group" menu always ends with "New group…": picking it turns the field into a name field, and Enter creates the group (or joins an exact match). Right-clicking a section header offers "Delete group…", which confirms first and then moves that group's automations to "No group".

Groups are now also settable through the API (automation.setGroup, create/update), the CLI (--group, --clear-group, list --group), and the MCP create/update tools. Names are trimmed and capped at 64 characters and matched case-insensitively to existing groups, so CLI/MCP spelling joins the current spelling.

Fixes in this update

  • The group filter ignores Enter and arrows while an IME composes text.
  • Bulk pause and resume now refresh each automation's detail query too.
  • Rows that leave the screen (filtered out or collapsed) leave the selection, so they don't return selected.
  • Deleting one row no longer clears an unrelated selection; deleted rows leave the selection on their own.
  • X toggles the focused row only without ⌘/Ctrl/Alt, so ⌘X still cuts.
  • The optimistic move cancels any in-flight list refetch first.
  • automation.update no longer drops extra schedules when only non-schedule fields (name, group) change.
  • The translation catalog check now reads nested plural branches, so valid strings ending in a placeholder aren't flagged as missing.

Moves are optimistic and applied per row through existing endpoints, so no batch API is needed. Docs and all 17 locales are included.