# `MishkaGervaz.Table.Web.Events.BulkActionHandler`
[🔗](https://github.com/mishka-group/mishka_gervaz/blob/v0.0.1-alpha.3/lib/mishka_gervaz/table/web/events/bulk_action_handler.ex#L1)

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:

  1. `:after_bulk_action` runs with `(summary, state)` regardless of outcome.
  2. Branch on `summary.status`:
     - `:success` — `:on_bulk_action_success` runs; **no default flash**
       (the table reload is the user-visible feedback).
     - `:partial_success` — `:on_bulk_action_success` runs 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_error` runs first; same override
       semantics, defaulting to the formatted error flash. The selection
       is preserved so the user can retry.
  3. For the unarchive partial-skip path (rows whose identity is already
     taken by an active row), `:on_bulk_action_success` runs with a
     summary whose `:skipped_count` / `:skipped_record_ids` describe the
     skipped rows; default flash is `"X unarchived, Y skipped — a record
     with the same name already exists."`. Skipped rows stay selected.

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
    end

Wired in the DSL:

    mishka_gervaz do
      table do
        events do
          bulk_action MyApp.CustomBulkActionHandler
        end
      end
    end

**2. 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
    end

See `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`.

# `bulk_action`

```elixir
@type bulk_action() :: map()
```

# `selected_ids`

```elixir
@type selected_ids() :: list() | :all | {:all_except, list()}
```

# `socket`

```elixir
@type socket() :: Phoenix.LiveView.Socket.t()
```

# `state`

```elixir
@type state() :: MishkaGervaz.Table.Web.State.t()
```

# `build_bulk_query`

```elixir
@callback build_bulk_query(
  resource :: module(),
  state :: state(),
  filter :: {:exclude, list()} | nil
) :: Ash.Query.t()
```

Builds a query for bulk operations.

Applies any necessary filters based on the selection.

# `execute`

```elixir
@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.

# `execute_ash_bulk_action`

```elixir
@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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
