From 8e8d5c77ad1f68be19ab20dab0c5c23478841770 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Wed, 16 Oct 2024 10:57:48 +0200 Subject: [PATCH 1/2] [#57817] add deletion action to item action menu - https://community.openproject.org/work_packages/57817 - added dialog component to safe guard item deletion - added controller actions and routes to open dialog and delete item --- .../delete_item_dialog_component.html.erb | 56 +++++++++++++++++++ .../hierarchy/delete_item_dialog_component.rb | 47 ++++++++++++++++ .../hierarchy/item_component.html.erb | 10 +++- .../custom_fields/hierarchy/item_component.rb | 16 +++++- .../hierarchy/items_component.html.erb | 5 +- .../hierarchy/items_component.rb | 2 + .../hierarchy/items_controller.rb | 23 +++++++- config/locales/en.yml | 2 +- config/routes.rb | 4 +- 9 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.html.erb create mode 100644 app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.rb diff --git a/app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.html.erb b/app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.html.erb new file mode 100644 index 000000000000..3ddbcb1ccb9b --- /dev/null +++ b/app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.html.erb @@ -0,0 +1,56 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + render(Primer::Alpha::Dialog.new(id: DIALOG_ID, title: "Delete item")) do |dialog| + dialog.with_header(variant: :large) + dialog.with_body do + "Are you sure you want to delete this item from the current hierarchy level?" + end + + dialog.with_footer do + concat(render(Primer::ButtonComponent.new(data: { "close-dialog-id": DIALOG_ID })) do + I18n.t(:button_cancel) + end) + + concat(primer_form_with( + model: @custom_field, + url: custom_field_item_path(custom_field_id: @custom_field.id, id: @hierarchy_item.id), + method: :delete, + data: { turbo: true } + ) do + render(Primer::ButtonComponent.new(scheme: :danger, + type: :submit, + data: { "close-dialog-id": DIALOG_ID })) do + I18n.t(:button_delete) + end + end) + end + end +%> diff --git a/app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.rb b/app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.rb new file mode 100644 index 000000000000..e7b9307bcb4a --- /dev/null +++ b/app/components/admin/custom_fields/hierarchy/delete_item_dialog_component.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module CustomFields + module Hierarchy + class DeleteItemDialogComponent < ApplicationComponent + include OpTurbo::Streamable + + DIALOG_ID = "op-hierarchy-item--deletion-confirmation" + + def initialize(custom_field:, hierarchy_item:) + super + @custom_field = custom_field + @hierarchy_item = hierarchy_item + end + end + end + end +end diff --git a/app/components/admin/custom_fields/hierarchy/item_component.html.erb b/app/components/admin/custom_fields/hierarchy/item_component.html.erb index 369d9234804f..5ff3bf6cf6a1 100644 --- a/app/components/admin/custom_fields/hierarchy/item_component.html.erb +++ b/app/components/admin/custom_fields/hierarchy/item_component.html.erb @@ -46,10 +46,14 @@ See COPYRIGHT and LICENSE files for more details. # Actions item_container.with_column do - render(Primer::Beta::IconButton.new(scheme: :default, - icon: "kebab-horizontal", - "aria-label": I18n.t("custom_fields.admin.items.more_actions"))) + render(Primer::Alpha::ActionMenu.new(data: { test_selector: "op-hierarchy-item--action-menu" })) do |menu| + menu.with_show_button(icon: "kebab-horizontal", + scheme: :invisible, + "aria-label": I18n.t("custom_fields.admin.items.actions")) + delete_item(menu) + end end end end + %> diff --git a/app/components/admin/custom_fields/hierarchy/item_component.rb b/app/components/admin/custom_fields/hierarchy/item_component.rb index ddf8b1e16fcc..c9f06cb11dd2 100644 --- a/app/components/admin/custom_fields/hierarchy/item_component.rb +++ b/app/components/admin/custom_fields/hierarchy/item_component.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH @@ -33,14 +35,26 @@ class ItemComponent < ApplicationComponent include OpTurbo::Streamable include OpPrimer::ComponentHelpers - def initialize(hierarchy_item:) + def initialize(custom_field:, hierarchy_item:) super + @custom_field = custom_field @hierarchy_item = hierarchy_item end def short_text "(#{@hierarchy_item.short})" end + + def delete_item(menu) + menu.with_item(label: I18n.t(:button_delete), + scheme: :danger, + tag: :a, + href: deletion_dialog_custom_field_item_path(custom_field_id: @custom_field.id, + id: @hierarchy_item.id), + content_arguments: { data: { controller: "async-dialog" } }) do |item| + item.with_leading_visual_icon(icon: :trash) + end + end end end end diff --git a/app/components/admin/custom_fields/hierarchy/items_component.html.erb b/app/components/admin/custom_fields/hierarchy/items_component.html.erb index 46ad4c347698..f729fcbe7f81 100644 --- a/app/components/admin/custom_fields/hierarchy/items_component.html.erb +++ b/app/components/admin/custom_fields/hierarchy/items_component.html.erb @@ -44,7 +44,10 @@ See COPYRIGHT and LICENSE files for more details. item_box.with_header { @custom_field.name } items.each do |item| - item_box.with_row { render Admin::CustomFields::Hierarchy::ItemComponent.new(hierarchy_item: item) } + item_box.with_row do + render Admin::CustomFields::Hierarchy::ItemComponent.new(custom_field: @custom_field, + hierarchy_item: item) + end end if show_new_item_form? diff --git a/app/components/admin/custom_fields/hierarchy/items_component.rb b/app/components/admin/custom_fields/hierarchy/items_component.rb index d98747609159..a0c91411f7d4 100644 --- a/app/components/admin/custom_fields/hierarchy/items_component.rb +++ b/app/components/admin/custom_fields/hierarchy/items_component.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH diff --git a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb index 416e71f6e9f3..4314da2f8a93 100644 --- a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb +++ b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb @@ -33,13 +33,15 @@ module CustomFields module Hierarchy class ItemsController < ApplicationController include OpTurbo::ComponentStream + include OpTurbo::DialogStreamHelper layout "admin" model_object CustomField before_action :require_admin - before_action :find_model_object + before_action :find_model_object, except: %i[destroy deletion_dialog] + before_action :find_custom_field_and_item, only: %i[destroy deletion_dialog] menu_item :custom_fields @@ -63,6 +65,18 @@ def create respond_with_turbo_streams end + def destroy + # TODO: user persistence service + @hierarchy_item.destroy + update_via_turbo_stream(component: ItemsComponent.new(custom_field: @custom_field)) + respond_with_turbo_streams + end + + def deletion_dialog + respond_with_dialog DeleteItemDialogComponent.new(custom_field: @custom_field, + hierarchy_item: @hierarchy_item) + end + private def item_input @@ -86,6 +100,13 @@ def find_model_object(object_id = :custom_field_id) super @custom_field = @object end + + def find_custom_field_and_item + @custom_field = CustomField.find(params[:custom_field_id]) + @hierarchy_item = CustomField::Hierarchy::Item.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render_404 + end end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 6b07ea4f6a1e..2305500a6fa4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -231,7 +231,7 @@ en: blankslate: title: "Your list of items is empty" description: "Start by adding items to the custom field of type hierarchy. Each item can be used to create a hierarchy bellow it. To navigate and create sub-items inside a hierarchy click on the created item." - more_actions: "More actions" + actions: "Item actions" placeholder: label: "Item label" short: "Short name" diff --git a/config/routes.rb b/config/routes.rb index 5c26f37f499d..9f15c3a2abe9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -180,7 +180,9 @@ only: :destroy resources :items, controller: "/admin/custom_fields/hierarchy/items", - only: %i[index new create] + only: %i[index new create destroy] do + get :deletion_dialog, on: :member + end end end end From 63fbdbdb049ce1394600ca727d01558c89ed7043 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Thu, 17 Oct 2024 15:30:35 +0200 Subject: [PATCH 2/2] [#57817] replaced record destroy with persistence service - updated action menu item construction method --- .../custom_fields/hierarchy/item_component.html.erb | 3 +-- .../admin/custom_fields/hierarchy/item_component.rb | 2 +- .../admin/custom_fields/hierarchy/items_controller.rb | 11 ++++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/components/admin/custom_fields/hierarchy/item_component.html.erb b/app/components/admin/custom_fields/hierarchy/item_component.html.erb index 5ff3bf6cf6a1..a1f989219537 100644 --- a/app/components/admin/custom_fields/hierarchy/item_component.html.erb +++ b/app/components/admin/custom_fields/hierarchy/item_component.html.erb @@ -50,10 +50,9 @@ See COPYRIGHT and LICENSE files for more details. menu.with_show_button(icon: "kebab-horizontal", scheme: :invisible, "aria-label": I18n.t("custom_fields.admin.items.actions")) - delete_item(menu) + deletion_action_item(menu) end end end end - %> diff --git a/app/components/admin/custom_fields/hierarchy/item_component.rb b/app/components/admin/custom_fields/hierarchy/item_component.rb index c9f06cb11dd2..2a22a4b7d537 100644 --- a/app/components/admin/custom_fields/hierarchy/item_component.rb +++ b/app/components/admin/custom_fields/hierarchy/item_component.rb @@ -45,7 +45,7 @@ def short_text "(#{@hierarchy_item.short})" end - def delete_item(menu) + def deletion_action_item(menu) menu.with_item(label: I18n.t(:button_delete), scheme: :danger, tag: :a, diff --git a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb index b9dbaf383d3a..542b2c80563d 100644 --- a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb +++ b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb @@ -66,9 +66,14 @@ def create end def destroy - # TODO: user persistence service - @hierarchy_item.destroy - update_via_turbo_stream(component: ItemsComponent.new(custom_field: @custom_field)) + ::CustomFields::Hierarchy::HierarchicalItemService + .new + .delete_branch(item: @hierarchy_item) + .either( + ->(_) { update_via_turbo_stream(component: ItemsComponent.new(custom_field: @custom_field)) }, + ->(errors) { update_flash_message_via_turbo_stream(message: errors.full_messages, scheme: :danger) } + ) + respond_with_turbo_streams end