# `PhoenixKit.Modules.Storage.Reorganizer.Action`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.28.1/lib/modules/storage/reorganizer/action.ex#L1)

The one action shape `PhoenixKit.Modules.Storage.Reorganizer` understands.

A `Source` returns plain maps (so a module can build them without
compiling against this module — see `Source`'s moduledoc). `new!/1`
validates the shape and fills defaults for anything the source left out;
the engine works with the normalized result from here on.

# `on_conflict`

```elixir
@type on_conflict() :: :suffix | :report
```

# `op`

```elixir
@type op() :: :move | :trash | :report
```

# `t`

```elixir
@type t() :: %{
  :source =&gt; String.t(),
  :kind =&gt; atom(),
  :label =&gt; String.t(),
  :op =&gt; op(),
  optional(:folder) =&gt;
    %PhoenixKit.Modules.Storage.Folder{
      __meta__: term(),
      children: term(),
      color: term(),
      cover_file_uuid: term(),
      description: term(),
      files: term(),
      folder_links: term(),
      header_show_background: term(),
      header_show_creator: term(),
      header_show_date: term(),
      header_show_description: term(),
      header_show_file_count: term(),
      header_show_icon: term(),
      header_show_title: term(),
      header_size: term(),
      inserted_at: term(),
      logo_file_uuid: term(),
      name: term(),
      parent: term(),
      parent_uuid: term(),
      trashed_at: term(),
      updated_at: term(),
      user: term(),
      user_uuid: term(),
      uuid: term()
    }
    | nil,
  optional(:parent_uuid) =&gt; String.t() | nil,
  optional(:name) =&gt; String.t() | nil,
  optional(:counts) =&gt; {non_neg_integer(), non_neg_integer()} | nil,
  optional(:on_conflict) =&gt; on_conflict(),
  optional(:after_move) =&gt; (-&gt; :ok | {:ok, term()} | {:error, term()}) | nil,
  optional(:reason) =&gt; String.t() | nil,
  optional(:outcome) =&gt; atom(),
  optional(:changes) =&gt; [:moved | :renamed | :restored],
  optional(:error) =&gt; term()
}
```

# `new!`

```elixir
@spec new!(map()) :: t()
```

Validates a plain map from a `Source`, raising `ArgumentError` naming the
offending key on any problem, and fills defaults for keys the source
didn't set.

# `noop?`

```elixir
@spec noop?(t()) :: boolean()
```

A `:move` action is a no-op when the folder it targets already sits at the
wanted `parent_uuid` with the wanted `name` (or an accepted `"name (N)"`
variant of it — matches the previous run's own suffix-on-collision output,
so re-running `plan/2` doesn't propose renaming it back and forth). A `nil`
`name` means "keep the current name", so it always matches.

An action carrying `after_move` is never a noop, even when the folder is
already in place: the folder position matching doesn't mean the pointer
back-fill it exists to run has happened — the engine still needs to apply
it (see `PhoenixKit.Modules.Storage.Reorganizer`'s move path, which skips
`update_folder` but still runs `after_move` in that case).

A trashed folder is never a noop either, even when its parent/name already
match: restoring it is itself a change the engine must apply (outcome
`:restored` when nothing else about it changes).

---

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