# `PhoenixKit.Utils.Reorder`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.64.1/lib/phoenix_kit/utils/reorder.ex#L1)

Two-phase index rewrite for drag-to-reorder list views.

Given a list of UUIDs in their new display order and an Ecto schema,
rewrites a position field on the matching rows to `1..N` matching
the order of the input list. The write runs in two passes inside a
transaction — first to negative indices, then to positive — so a
unique index on the position column (should one ever be added)
wouldn't trip mid-update.

Consumers that need pre-write validation (scope checks, permission
guards) or post-write side effects (activity logging, PubSub
broadcasts) should wrap this helper rather than fold the logic in
here. `PhoenixKitProjects.reorder_projects/2` is the reference
consumer doing exactly that — this module owns only the index-rewrite
primitive.

Rows are matched on `uuid` by default; pass `key: :id` (any field) for
a schema keyed some other way — an integer primary key, a slug. The
payload's ids are cast with that field's Ecto type, so the strings a
drag hook sends (`"42"`) match an integer column.

Entries that are not a valid key (a non-UUID for `:uuid`, a non-integer
for an integer key) are silently filtered — a stale or malformed drop
event can't poison the rewrite. Duplicates dedup last-write-wins via
`Enum.uniq/1`.

## Example

    defmodule MyApp.Endpoints do
      alias PhoenixKit.Utils.Reorder

      def reorder_endpoints(ordered_ids) do
        Reorder.reorder(MyApp.Endpoint, ordered_ids, :sort_order, repo: repo())
      end
    end

# `result`

```elixir
@type result() :: {:ok, non_neg_integer()} | {:error, :too_many_uuids}
```

# `reorder`

```elixir
@spec reorder(module(), [String.t() | integer()], atom(), keyword()) :: result()
```

Rewrites `field` on the rows whose `uuid` appears in `ordered_ids`,
setting it to each UUID's 1-based position in the list.

Returns `{:ok, count}` where `count` is the number of rows actually
updated in the positive-write phase (matches `Repo.update_all`'s
count semantics — UUIDs in the payload that don't resolve to real
rows aren't counted). Returns `{:error, :too_many_uuids}` when the
dedup'd payload exceeds the configured cap. An empty / fully-filtered
payload returns `{:ok, 0}`.

## Options

- `:repo` — the Ecto repo to use. Defaults to
  `PhoenixKit.RepoHelper.repo/0` so it picks up the host app's repo.
- `:key` — the field the ids are matched on. Default `:uuid`.
- `:max_ids` (or the older `:max_uuids`) — payload cap, checked after
  dedup. Default `500`. Guards against runaway drop events from a
  misbehaving client.

---

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