MishkaGervaz. Table. Web. Events. BulkActionHandler behaviour
(MishkaGervaz v0.0.5)
Copy Markdown
View Source
Handles bulk action execution for Events module.
Runs Ash bulk operations on the selection, classifies the outcome, and routes through the lifecycle hooks before falling back to a sensible default flash.
Lifecycle (Ash bulk paths)
Per bulk action:
:after_bulk_actionruns with(summary, state)regardless of outcome.- Branch on
summary.status::success—:on_bulk_action_successruns; no default flash (the table reload is the user-visible feedback).:partial_success—:on_bulk_action_successruns first; if the hook returns the socket, a default info flash fires ("X succeeded, Y failed."). Hooks return{:halt, socket}to suppress the default.:error—:on_bulk_action_errorruns first; same override semantics, defaulting to the formatted error flash. The selection is preserved so the user can retry.
- For the unarchive partial-skip path (rows whose identity is already
taken by an active row),
:on_bulk_action_successruns with a summary whose:skipped_count/:skipped_record_idsdescribe the skipped rows; default flash is"X unarchived, Y skipped — a record with the same name already exists.". Skipped rows stay selected. - The table then loads its first page again. A table with
keep_loaded_records truetakes the rows a destroy, an archive or a restore removed out of the rows it keeps and reads none, while some kept row stays or no more rows are left to load; after any other action it loads every page it had loaded, so the rows the reader loaded stay. A function handler that returns:reloadreloads the same way.
See MishkaGervaz.Table.Web.Events.BulkActionResult for the summary
shape; MishkaGervaz.Table.Web.Events.BulkActionHooks for silence/1
and use_default/1.
Customization
Two layers of customization:
1. Replace the handler module for full control:
defmodule MyApp.CustomBulkActionHandler do
use MishkaGervaz.Table.Web.Events.BulkActionHandler
def execute_ash_bulk_action(action, ash_action, selected_ids, state, socket) do
MyApp.Logger.info("bulk", action: ash_action, count: length(selected_ids))
super(action, ash_action, selected_ids, state, socket)
end
endWired in the DSL:
mishka_gervaz do
table do
events do
bulk_action MyApp.CustomBulkActionHandler
end
end
end2. Just adjust messaging via per-action hooks (most common):
hooks do
on_bulk_action_success :master_destroy, fn summary, _state, socket ->
socket
|> Phoenix.LiveView.put_flash(:info, "#{summary.succeeded_count} deleted.")
|> MishkaGervaz.Table.Web.Events.BulkActionHooks.silence()
end
on_bulk_action_error :master_destroy, fn summary, _state, socket ->
Logger.error("delete failed: #{inspect(summary.failed_errors)}")
MishkaGervaz.Table.Web.Events.BulkActionHooks.silence(socket)
end
endSee MishkaGervaz.Table.Web.Events,
MishkaGervaz.Table.Entities.BulkAction,
MishkaGervaz.Table.Web.DataLoader,
MishkaGervaz.Table.Web.Events.BulkActionResult,
MishkaGervaz.Table.Web.Events.BulkActionHooks,
and the sibling handlers SanitizationHandler, RecordHandler,
SelectionHandler, HookRunner, RelationFilterHandler.
Summary
Callbacks
Builds the query a bulk action runs on.
Executes a bulk action based on its handler type.
Executes an Ash bulk action.
Functions
The flash a bulk action that failed on some records puts: how many it did and how many failed.
The flash a bulk restore puts when it skipped records whose name another record already has.
Types
@type bulk_action() :: map()
@type socket() :: Phoenix.LiveView.Socket.t()
@type state() :: MishkaGervaz.Table.Web.State.t()
Callbacks
@callback build_bulk_query( resource :: module(), state :: state(), filter :: {:exclude, list()} | nil ) :: Ash.Query.t()
Builds the query a bulk action runs on.
It is the table's own read (MishkaGervaz.Table.Web.DataLoader.read_query/1): its
path_params, filters, search and on_load hook, under the view's read action and the tenant,
so picking every row reaches the rows the table shows and no other. {:exclude, ids} leaves out
the rows unpicked after picking every row.
@callback execute( bulk_action :: bulk_action() | nil, selected_ids :: selected_ids(), state :: state(), socket :: socket() ) :: {:noreply, socket()}
Executes a bulk action based on its handler type.
Dispatches to the appropriate handler: :parent, function, or Ash action.
@callback execute_ash_bulk_action( action :: bulk_action(), ash_action :: atom(), selected_ids :: selected_ids(), state :: state(), socket :: socket() ) :: {:noreply, socket()}
Executes an Ash bulk action.
Handles both bulk_update and bulk_destroy based on action type.
Functions
@spec partial_summary(MishkaGervaz.Table.Web.Events.BulkActionResult.t()) :: String.t()
The flash a bulk action that failed on some records puts: how many it did and how many failed.
@spec unarchive_skip_summary(MishkaGervaz.Table.Web.Events.BulkActionResult.t()) :: String.t()
The flash a bulk restore puts when it skipped records whose name another record already has.