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)