-
Notifications
You must be signed in to change notification settings - Fork 64
Add item actions to show view #1235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Add item actions to show view #1235
Conversation
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.
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 |
There was a problem hiding this comment.
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)
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 |
There was a problem hiding this comment.
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)
<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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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"]}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<div class={["flex items-center justify-end space-x-2"]}> | |
<div class="flex items-center justify-end space-x-2"> |
<.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> |
There was a problem hiding this comment.
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
Good job and thank you for the contribution! 🙌 I've requested some changes. Also, there are some errors in the CI. @ricmarinovic |
Closes #872.
Changes:
item
toform_item
in theform_component
to avoid the name clash in thesocket.assigns
.item_actions
inresource.ex
to be more generic and allow it to be used with the newshow
parameter.Tested manually, couldn't find a way to easily run the tests for the
demo
application.