Skip to content

Conversation

ricmarinovic
Copy link

Closes #872.

  • Display Item Actions on show view by default
  • Handle Item Actions clicked on show view (you might need to handle some edge cases)
    • Handles the case of navigating to another page, opening a confirm dialog, opening the soft delete with a form to delete and the custom duplicate on the demo in case one wants to enable it from show.
  • Allow :show key to be added to only configuration to restrict placement
  • Update docs

Changes:

  • Renamed item to form_item in the form_component to avoid the name clash in the socket.assigns.
  • Changed the functions to get the item_actions in resource.ex to be more generic and allow it to be used with the new show parameter.

Tested manually, couldn't find a way to easily run the tests for the demo application.

In the example with soft delete of users, it would be best if the user handles the case to redirect to the index page after deleting, but if it is not done, we display an error message instead of raising an error.

Remove some redundant imports because the BakcpexWeb html already imports it.
@ricmarinovic ricmarinovic changed the title Add action items to show view Add item actions to show view Jun 17, 2025
@Flo0807 Flo0807 self-requested a review June 27, 2025 06:47
Comment on lines +104 to +156
defp assign_item_actions(socket) do
item_actions = Backpex.ItemAction.default_actions() |> socket.assigns.live_resource.item_actions()
assign(socket, :item_actions, item_actions)
end

defp maybe_handle_item_action(socket, key) do
key = String.to_existing_atom(key)
action = socket.assigns.item_actions[key]
item = socket.assigns.item

if Backpex.ItemAction.has_confirm_modal?(action) do
open_action_confirm_modal(socket, action, key)
else
handle_item_action(socket, action, item)
end
end

defp open_action_confirm_modal(socket, action, key) do
if Backpex.ItemAction.has_form?(action) do
changeset_function = &action.module.changeset/3
base_schema = action.module.base_schema(socket.assigns)

metadata = Resource.build_changeset_metadata(socket.assigns)
changeset = changeset_function.(base_schema, %{}, metadata)

socket
|> assign(:form_item, base_schema)
|> assign(:changeset, changeset)
else
assign(socket, :changeset, %{})
end
|> assign(:action_to_confirm, Map.put(action, :key, key))
end

defp handle_item_action(socket, action, item) do
case action.module.handle(socket, [item], %{}) do
{:ok, socket} ->
socket
|> assign(action_to_confirm: nil)
|> assign(selected_items: [])
|> assign(select_all: false)

unexpected_return ->
raise ArgumentError, """
Invalid return value from #{inspect(action.module)}.handle/3.
Expected: {:ok, socket}
Got: #{inspect(unexpected_return)}
Item Actions with no form fields must return {:ok, socket}.
"""
end
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have essentially the same code in index.ex. Could these functions be moved to another module (we might have to make slight adjustments to them to cover both use cases)? What do you think of Backpex.ItemAction?

(Credo also doesn't like the code duplication)

Comment on lines +49 to +64
def handle_event("item-action", %{"action-key" => key}, socket) do
item = socket.assigns.item

socket
|> assign(selected_items: [item])
|> maybe_handle_item_action(key)
|> noreply()
end

def handle_event("cancel-action-confirm", _params, socket) do
socket
|> assign(:form_item, nil)
|> assign(:changeset, nil)
|> assign(:action_to_confirm, nil)
|> noreply()
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might be able to move these functions to Backpex.ItemAction, too (see other comment)

Comment on lines +410 to +424
<div class={["flex items-center justify-end space-x-2"]}>
<button
:for={{key, action} <- item_actions_for(:show, @item_actions)}
:if={@live_resource.can?(assigns, key, @item)}
id={"item-action-#{key}-#{LiveResource.primary_value(@item, @live_resource)}"}
type="button"
phx-click="item-action"
phx-value-action-key={key}
aria-label={action.module.label(assigns, @item)}
phx-hook="BackpexTooltip"
data-tooltip={action.module.label(assigns, @item)}
>
{action.module.icon(assigns, @item)}
</button>
</div>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, we need another div for the tooltip that has the button inside.

It looks like there is too much space between the text and the tooltip:

image

class="h-6 w-6 cursor-pointer transition duration-75 hover:text-primary hover:scale-110"
/>
</.link>
<div class={["flex items-center justify-end space-x-2"]}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<div class={["flex items-center justify-end space-x-2"]}>
<div class="flex items-center justify-end space-x-2">

Comment on lines +2 to +22
<.modal
:if={@action_to_confirm}
id="action-confirm-modal"
title={@action_to_confirm.module.label(assigns, nil)}
on_cancel={JS.push("cancel-action-confirm")}
open={true}
close_label={Backpex.__("Close modal", @live_resource)}
>
<div class="px-5 py-3">
{@action_to_confirm.module.confirm(assigns)}
</div>
<div>
<.live_component
module={Backpex.FormComponent}
id={:item_action_modal}
live_resource={@live_resource}
action_type={:item}
{Map.drop(assigns, [:socket, :flash, :fields])}
/>
</div>
</.modal>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to move this to another component as this is duplicated in resource_show.html.heex and resource_index.html.heex

@Flo0807
Copy link
Collaborator

Flo0807 commented Jun 27, 2025

Good job and thank you for the contribution! 🙌 I've requested some changes. Also, there are some errors in the CI. @ricmarinovic

@Flo0807 Flo0807 added the feature New feature label Jun 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Display Item Actions on show views
2 participants