From 273e10d7f5cbc5b164c19d14d080de40dd1518a1 Mon Sep 17 00:00:00 2001 From: Florian Arens <60519307+Flo0807@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:58:10 +0100 Subject: [PATCH 1/3] Support ash destroy action --- demo/lib/demo/helpdesk/ticket.ex | 2 +- demo/lib/demo_web/live/ticket_live.ex | 2 +- lib/backpex/adapters/ash.ex | 13 ++++++-- lib/backpex/adapters/ecto.ex | 2 ++ lib/backpex/html/resource.ex | 5 ---- .../html/resource/resource_index.html.heex | 2 +- lib/backpex/item_actions/delete.ex | 2 +- lib/backpex/item_actions/item_action.ex | 22 +++++++++++++- lib/backpex/live_components/form_component.ex | 29 ++++++++++++------ lib/backpex/live_resource.ex | 30 +++++++++++-------- lib/backpex/resource.ex | 3 +- 11 files changed, 77 insertions(+), 35 deletions(-) diff --git a/demo/lib/demo/helpdesk/ticket.ex b/demo/lib/demo/helpdesk/ticket.ex index b9ce2f32..bcfab282 100644 --- a/demo/lib/demo/helpdesk/ticket.ex +++ b/demo/lib/demo/helpdesk/ticket.ex @@ -11,7 +11,7 @@ defmodule Demo.Helpdesk.Ticket do end actions do - defaults [:read] + defaults [:read, :destroy] end attributes do diff --git a/demo/lib/demo_web/live/ticket_live.ex b/demo/lib/demo_web/live/ticket_live.ex index 7326176c..66ec4bbf 100644 --- a/demo/lib/demo_web/live/ticket_live.ex +++ b/demo/lib/demo_web/live/ticket_live.ex @@ -32,7 +32,7 @@ defmodule DemoWeb.TicketLive do end @impl Backpex.LiveResource - def can?(_assigns, action, _item) when action in [:index, :show], do: true + def can?(_assigns, action, _item) when action in [:index, :show, :delete], do: true def can?(_assigns, _action, _item), do: false @impl Backpex.LiveResource diff --git a/lib/backpex/adapters/ash.ex b/lib/backpex/adapters/ash.ex index fb605fcb..70100ba6 100644 --- a/lib/backpex/adapters/ash.ex +++ b/lib/backpex/adapters/ash.ex @@ -75,8 +75,17 @@ if Code.ensure_loaded?(Ash) do Deletes multiple items. """ @impl Backpex.Adapter - def delete_all(_items, _config) do - raise "not implemented yet" + def delete_all(items, live_resource) do + config = live_resource.config(:adapter_config) + primary_key = live_resource.config(:primary_key) + + ids = Enum.map(items, &Map.fetch!(&1, primary_key)) + + config[:resource] + |> Ash.Query.filter(^Ash.Expr.ref(primary_key) in ^ids) + |> Ash.bulk_destroy!(:destroy, %{}) + + :ok end @doc """ diff --git a/lib/backpex/adapters/ecto.ex b/lib/backpex/adapters/ecto.ex index b9d52007..c530b765 100644 --- a/lib/backpex/adapters/ecto.ex +++ b/lib/backpex/adapters/ecto.ex @@ -249,6 +249,8 @@ defmodule Backpex.Adapters.Ecto do config[:schema] |> where([i], field(i, ^primary_key) in ^Enum.map(items, &Map.get(&1, primary_key))) |> config[:repo].delete_all() + + :ok end @doc """ diff --git a/lib/backpex/html/resource.ex b/lib/backpex/html/resource.ex index 02130f08..7af968ca 100644 --- a/lib/backpex/html/resource.ex +++ b/lib/backpex/html/resource.ex @@ -929,11 +929,6 @@ defmodule Backpex.HTML.Resource do """ end - @doc """ - Checks the given module if it has a `confirm/1` function exported or a list with fields. - """ - def has_modal?(module), do: function_exported?(module, :confirm, 1) or module.fields() != [] - defp metric_toggle(assigns) do visible = Backpex.Metric.metrics_visible?(assigns.metric_visibility, assigns.live_resource) diff --git a/lib/backpex/html/resource/resource_index.html.heex b/lib/backpex/html/resource/resource_index.html.heex index 9d07af5e..24692df3 100644 --- a/lib/backpex/html/resource/resource_index.html.heex +++ b/lib/backpex/html/resource/resource_index.html.heex @@ -11,7 +11,7 @@ <% end %> - <%= if @action_to_confirm && has_modal?(@action_to_confirm.module) do %> + <%= if @action_to_confirm do %> <.modal title={@action_to_confirm.module.label(assigns, nil)}>
<%= @action_to_confirm.module.confirm(assigns) %> diff --git a/lib/backpex/item_actions/delete.ex b/lib/backpex/item_actions/delete.ex index 093a0eae..4c27bef6 100644 --- a/lib/backpex/item_actions/delete.ex +++ b/lib/backpex/item_actions/delete.ex @@ -42,7 +42,7 @@ defmodule Backpex.ItemActions.Delete do def handle(socket, items, _data) do socket = try do - {:ok, _items} = Resource.delete_all(items, socket.assigns.live_resource) + {:ok, items} = Resource.delete_all(items, socket.assigns.live_resource) for item <- items do socket.assigns.live_resource.on_item_deleted(socket, item) diff --git a/lib/backpex/item_actions/item_action.ex b/lib/backpex/item_actions/item_action.ex index 15401b9a..013e945d 100644 --- a/lib/backpex/item_actions/item_action.ex +++ b/lib/backpex/item_actions/item_action.ex @@ -22,6 +22,8 @@ defmodule Backpex.ItemAction do @callback fields() :: list() @doc """ + TODO: rename to `init_schema` + Initial change. The result will be passed to `c:changeset/3` in order to generate a changeset. This function is optional and can be used to use changesets with schemas in item actions. If this function @@ -111,10 +113,28 @@ defmodule Backpex.ItemAction do @impl Backpex.ItemAction def init_change(_assigns) do - types = Backpex.Field.changeset_types(fields()) + types = fields() |> Backpex.Field.changeset_types() {%{}, types} end end end + + @doc """ + Checks whether item action has confirmation modal. + """ + def has_confirm_modal?(item_action) do + module = Map.fetch!(item_action, :module) + + function_exported?(module, :confirm, 1) + end + + @doc """ + Checks whether item action has form. + """ + def has_form?(item_action) do + module = Map.fetch!(item_action, :module) + + module.fields() != [] + end end diff --git a/lib/backpex/live_components/form_component.ex b/lib/backpex/live_components/form_component.ex index 030677cd..23800232 100644 --- a/lib/backpex/live_components/form_component.ex +++ b/lib/backpex/live_components/form_component.ex @@ -72,7 +72,9 @@ defmodule Backpex.FormComponent do end def handle_event("validate", %{"change" => change, "_target" => target}, %{assigns: %{action_type: :item}} = socket) do - %{assigns: %{item_action_types: item_action_types, live_resource: live_resource, fields: fields} = assigns} = socket + %{assigns: %{init_schema: init_schema, fields: fields} = assigns} = socket + + changeset_function = &assigns.action_to_confirm.module.changeset/3 target = Enum.at(target, 1) @@ -81,9 +83,12 @@ defmodule Backpex.FormComponent do |> drop_readonly_changes(fields, assigns) |> put_upload_change(socket, :validate) + metadata = Resource.build_changeset_metadata(socket.assigns, target) + changeset = - item_action_types - |> Resource.change(change, fields, assigns, live_resource, target: target) + init_schema + |> changeset_function.(change, metadata) + |> Map.put(:action, :validate) form = Phoenix.Component.to_form(changeset, as: :change) @@ -332,19 +337,25 @@ defmodule Backpex.FormComponent do %{ assigns: %{ - live_resource: live_resource, selected_items: selected_items, action_to_confirm: action_to_confirm, return_to: return_to, - item_action_types: item_action_types, - fields: fields } = assigns } = socket result = - item_action_types - |> Backpex.Resource.change(params, fields, assigns, live_resource) - |> Ecto.Changeset.apply_action(:insert) + if Backpex.ItemAction.has_form?(action_to_confirm) do + changeset_function = &action_to_confirm.module.changeset/3 + + metadata = Resource.build_changeset_metadata(assigns) + + assigns.init_schema + |> changeset_function.(params, metadata) + |> Map.put(:action, :insert) + |> Ecto.Changeset.apply_action(:insert) + else + {:ok, %{}} + end case result do {:ok, data} -> diff --git a/lib/backpex/live_resource.ex b/lib/backpex/live_resource.ex index 8b4a7742..0169a5a1 100644 --- a/lib/backpex/live_resource.ex +++ b/lib/backpex/live_resource.ex @@ -708,7 +708,7 @@ defmodule Backpex.LiveResource do action = socket.assigns.item_actions[key] items = socket.assigns.selected_items - if has_modal?(action.module) do + if Backpex.ItemAction.has_confirm_modal?(action) do open_action_confirm_modal(socket, action, key) else handle_item_action(socket, action, key, items) @@ -716,21 +716,22 @@ defmodule Backpex.LiveResource do end defp open_action_confirm_modal(socket, action, key) do - init_change = action.module.init_change(socket.assigns) - changeset_function = &action.module.changeset/3 + socket = + if Backpex.ItemAction.has_form?(action) do + changeset_function = &action.module.changeset/3 + init_schema = action.module.init_change(socket.assigns) - metadata = Resource.build_changeset_metadata(socket.assigns) + metadata = Resource.build_changeset_metadata(socket.assigns) - changeset = - init_change - |> Ecto.Changeset.change() - |> changeset_function.(%{}, metadata) + changeset = changeset_function.(init_schema, %{}, metadata) - socket = - socket - |> assign(:item_action_types, init_change) - |> assign(:changeset_function, changeset_function) - |> assign(:changeset, changeset) + socket + |> assign(:init_schema, init_schema) + |> assign(:changeset, changeset) + else + socket + |> assign(:changeset, %{}) + end |> assign(:action_to_confirm, Map.put(action, :key, key)) {:noreply, socket} @@ -891,6 +892,9 @@ defmodule Backpex.LiveResource do select_all = length(updated_selected_items) == length(socket.assigns.items) + IO.inspect(updated_selected_items, label: :updated_selected_items) + + socket = socket |> assign(:selected_items, updated_selected_items) diff --git a/lib/backpex/resource.ex b/lib/backpex/resource.ex index 5c0ce27a..39151c1e 100644 --- a/lib/backpex/resource.ex +++ b/lib/backpex/resource.ex @@ -85,8 +85,9 @@ defmodule Backpex.Resource do pubsub = live_resource.config(:pubsub) case adapter.delete_all(items, live_resource) do - {_count_, nil} -> + :ok -> Enum.each(items, fn item -> broadcast({:ok, item}, "deleted", pubsub) end) + {:ok, items} _error -> From 9b5e1b95d478ff5c2145d66af10f90c663997243 Mon Sep 17 00:00:00 2001 From: Florian Arens <60519307+Flo0807@users.noreply.github.com> Date: Fri, 15 Nov 2024 06:32:08 +0100 Subject: [PATCH 2/3] Format --- lib/backpex/live_components/form_component.ex | 2 +- lib/backpex/live_resource.ex | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/backpex/live_components/form_component.ex b/lib/backpex/live_components/form_component.ex index 23800232..ec954e9d 100644 --- a/lib/backpex/live_components/form_component.ex +++ b/lib/backpex/live_components/form_component.ex @@ -339,7 +339,7 @@ defmodule Backpex.FormComponent do %{ selected_items: selected_items, action_to_confirm: action_to_confirm, - return_to: return_to, + return_to: return_to } = assigns } = socket diff --git a/lib/backpex/live_resource.ex b/lib/backpex/live_resource.ex index 0169a5a1..5e042cc2 100644 --- a/lib/backpex/live_resource.ex +++ b/lib/backpex/live_resource.ex @@ -894,7 +894,6 @@ defmodule Backpex.LiveResource do IO.inspect(updated_selected_items, label: :updated_selected_items) - socket = socket |> assign(:selected_items, updated_selected_items) From 8365d52085c0e8c83a56a014ea89cd3106f5ed24 Mon Sep 17 00:00:00 2001 From: Florian Arens <60519307+Flo0807@users.noreply.github.com> Date: Fri, 15 Nov 2024 06:33:01 +0100 Subject: [PATCH 3/3] Remove io inspect call --- lib/backpex/live_resource.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/backpex/live_resource.ex b/lib/backpex/live_resource.ex index 5e042cc2..da8c7890 100644 --- a/lib/backpex/live_resource.ex +++ b/lib/backpex/live_resource.ex @@ -892,8 +892,6 @@ defmodule Backpex.LiveResource do select_all = length(updated_selected_items) == length(socket.assigns.items) - IO.inspect(updated_selected_items, label: :updated_selected_items) - socket = socket |> assign(:selected_items, updated_selected_items)