Bridge Training

Deal batch actions

One list of actions on a batch of deals, declared once in the teacher app and shared by the explorer, the session picker and the collection detail.

An action on a batch of deals (add to a session, add to a collection, create a collection, bookmark, export as PBN, move to trash) is declared once in apps/app/src/features/deals/batch-actions and consumed by every screen. No screen re-implements an action or its dialog.

The pieces

PieceWhereRole
DealBatchbatch-actions/DealBatch.tsThe boards the action applies to, the user's bookmarks among them, and the context: a free selection (DEALS_CONTEXT) or a collection
One definition per actionbatch-actions/actions/*id, availability(batch), isActive(batch), opensDialog and the useRunner(batch) hook that executes it (mutation, toast, dialog)
SessionPicker, CollectionPickerbatch-actions/pickers/*Connected pickers that run the mutation on a pick; the action dialogs and the deal preview sheet both mount them. Action files import them, never the reverse
DEAL_BATCH_ACTIONSbatch-actions/DealBatchActions.tsThe ordered list of definitions
DEAL_BATCH_SURFACESbatch-actions/DealBatchActions.tsWhich actions each screen offers
useDealBatchActions(batch, surface)batch-actions/useDealBatchActions.tsxViewModels, run(id) and the dialogs of a surface
DealBatchMenuItemsbatch-actions/deal-batch-menu-items.tsxConnected items to drop into an open dropdown menu
DealBatchActionItems@workspace/ui-training/deal/actionsPassive rendering: label, icon, tone, disabled reason, delete confirmation (see Deal)
DealBatchActionDialog, DealCollectionPicker@workspace/ui-training/deal/actionsPassive dialog shell and collection list of the picker actions; the app feeds them (toDealCollectionPickerItems) and runs the mutation

Label and icon follow the action id in ui-training; the copy comes from DealActionsStringsBridge, which binds DealActionsStrings to the deals namespace.

Availability

An action the batch cannot run stays visible and disabled, with its reason under the label; it is never hidden.

ActionAvailable whenOtherwise
addToSessionthe batch holds a deal; always on a collection (the collection is linked as a whole)emptySelection
addToCollectionthe batch holds a deal, mine or notemptySelection
createCollectionthe batch holds a deal, mine or notemptySelection
bookmarkthe batch holds a deal; removes when every deal is bookmarkedemptySelection
exportPbnthe batch holds a dealemptySelection
deleteevery deal can be deleted by me (permissions.canDelete)notOwned, or emptySelection

Availability is a pure function of the batch, unit-tested per action for my deals, other authors' deals and a mixed selection (apps/app/__tests__/features/deals/batch-actions). run(id) checks it again through canRunOn: an action the surface does not offer, or that the batch cannot run, is a no-op even when a caller triggers it outside the menu (the degraded notice calls run("exportPbn")). It shapes the UI only: the API still checks ownership on every mutation.

Context

The same action runs differently by context:

  • Free selection ({ kind: "deals" }): addToSession copies the deals into the picked session; exportPbn names the file and the PBN Event after "Deals".
  • Collection ({ kind: "collection", collectionId, collectionName }): addToSession links the collection to the picked session; exportPbn uses the collection name.

Surfaces

SurfaceScreenActions
explorerRowRow menu of the deals exploreradd to session, add to collection, bookmark, delete
sessionPickerRowRow menu of the session deal pickerdelete
collectionCollection detail menus (header, mobile FAB)add to session
degradedCollectionNotice of a collection above the plan ceilingexport as PBN

The deal preview sheet reuses the connected pickers of the actions (SessionPicker, CollectionPicker, exported by actions/add-to-session and actions/add-to-collection) inline. The explorer e2e bridge (bulkAddToCollection) runs the add-to-collection action on its selection.

Adding an action

  1. Add its id to DealBatchActionId in ui-training, with its icon, tone and label in deal-batch-action-items.tsx and its copy in DealActionsStrings.
  2. Write its definition in batch-actions/actions/, with a pure availability.
  3. Insert it in DEAL_BATCH_ACTIONS and mount its runner in useDealBatchActions.
  4. Offer it on the surfaces that need it in DEAL_BATCH_SURFACES, and cover its availability in the unit tests.