diff --git a/modules/meeting/app/components/meetings/delete_dialog_component.html.erb b/modules/meeting/app/components/meetings/delete_dialog_component.html.erb new file mode 100644 index 000000000000..ea5bf63bf674 --- /dev/null +++ b/modules/meeting/app/components/meetings/delete_dialog_component.html.erb @@ -0,0 +1,46 @@ +<%#-- 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::OpenProject::DangerConfirmationDialog.new( + id:, + title:, + form_arguments: { + action: meeting_path(@meeting), + method: :delete, + data: { turbo: true } + } + )) do |dialog| + dialog.with_confirmation_message do |message| + message.with_heading(tag: :h2) { I18n.t("meeting.delete_dialog.heading") } + message.with_description_content(confirmation_message) + end + dialog.with_confirmation_check_box_content(I18n.t("text_permanent_delete_confirmation_checkbox_label")) + end +%> diff --git a/modules/meeting/app/components/meetings/delete_dialog_component.rb b/modules/meeting/app/components/meetings/delete_dialog_component.rb new file mode 100644 index 000000000000..ee1003f7d7d5 --- /dev/null +++ b/modules/meeting/app/components/meetings/delete_dialog_component.rb @@ -0,0 +1,67 @@ +# 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 Meetings + class DeleteDialogComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + + def initialize(meeting:, project:) + super + + @meeting = meeting + @project = project + end + + private + + def id = "delete-meeting-dialog" + + def title = I18n.t("meeting.delete_dialog.title") + + def confirmation_message + if recurring_meeting.present? + I18n.t(:label_recurring_meeting_delete_confirmation, name: recurring_meeting.title) + else + I18n.t("text_are_you_sure") + end + end + + def recurring? + @meeting.is_a?(RecurringMeeting) + end + + def recurring_meeting + return if recurring? + + @meeting.recurring_meeting + end + end +end diff --git a/modules/meeting/app/components/meetings/header_component.html.erb b/modules/meeting/app/components/meetings/header_component.html.erb index ee89a02dd97d..2f493e5051f7 100644 --- a/modules/meeting/app/components/meetings/header_component.html.erb +++ b/modules/meeting/app/components/meetings/header_component.html.erb @@ -95,9 +95,10 @@ menu.with_item(label: delete_label, scheme: :danger, - href: meeting_path(@meeting), - form_arguments: { - method: :delete, data: { confirm: t("text_are_you_sure"), turbo: 'false' } + href: delete_dialog_meeting_path(@meeting), + tag: :a, + content_arguments: { + data: { controller: "async-dialog" } }) do |item| item.with_leading_visual_icon(icon: :trash) end if delete_enabled? diff --git a/modules/meeting/app/components/meetings/row_component.rb b/modules/meeting/app/components/meetings/row_component.rb index 88377b0f65ec..0d961841dd42 100644 --- a/modules/meeting/app/components/meetings/row_component.rb +++ b/modules/meeting/app/components/meetings/row_component.rb @@ -140,22 +140,15 @@ def delete_action(menu) menu.with_item(label: recurring_meeting.present? ? I18n.t(:label_recurring_meeting_delete) : I18n.t(:label_meeting_delete), scheme: :danger, - href: meeting_path(model), - form_arguments: { - method: :delete, data: { confirm: delete_confirm_message, turbo: false } + href: delete_dialog_meeting_path(model), + tag: :a, + content_arguments: { + data: { controller: "async-dialog" } }) do |item| item.with_leading_visual_icon(icon: :trash) end end - def delete_confirm_message - if recurring_meeting.present? - I18n.t(:label_recurring_meeting_delete_confirmation, name: recurring_meeting.title) - else - I18n.t("text_are_you_sure") - end - end - def recurring_label render(Primer::BaseComponent.new(tag: :span, color: :muted)) do concat render(Primer::Beta::Octicon.new(icon: :iterations, mr: 1, ml: 1)) diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index af3e93508f02..fdffc9d0566a 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -178,6 +178,13 @@ def copy end end + def delete_dialog + respond_with_dialog Meetings::DeleteDialogComponent.new( + meeting: @meeting, + project: @project + ) + end + def destroy # rubocop:disable Metrics/AbcSize recurring = @meeting.recurring_meeting diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index 3cfaa7d1883e..e34eb3647db5 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -213,6 +213,9 @@ en: structured_text: "Organize your meeting as a dynamic list of agenda items, optionally linking them to a work package." structured_text_copy: "Copying a meeting will currently not copy the associated meeting agenda items, just the details" copied: "Copied from Meeting #%{id}" + delete_dialog: + title: "Delete meeting" + heading: "Permanently delete this meeting?" meeting_section: untitled_title: "Untitled section" diff --git a/modules/meeting/config/routes.rb b/modules/meeting/config/routes.rb index d50325e1c03c..0d887b2e115e 100644 --- a/modules/meeting/config/routes.rb +++ b/modules/meeting/config/routes.rb @@ -84,6 +84,7 @@ put :change_state post :notify get :history + get :delete_dialog end resources :agenda_items, controller: "meeting_agenda_items" do collection do diff --git a/modules/meeting/lib/open_project/meeting/engine.rb b/modules/meeting/lib/open_project/meeting/engine.rb index 936e1e22e9ea..d99a38b93228 100644 --- a/modules/meeting/lib/open_project/meeting/engine.rb +++ b/modules/meeting/lib/open_project/meeting/engine.rb @@ -69,7 +69,7 @@ class Engine < ::Rails::Engine require: :member permission :delete_meetings, { - meetings: [:destroy], + meetings: %i[delete_dialog destroy], recurring_meetings: %i[delete_dialog destroy delete_scheduled] }, permissible_on: :project, diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb index c762251f9a59..55528ab3d076 100644 --- a/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb +++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_crud_spec.rb @@ -213,11 +213,17 @@ it "can delete a meeting and get back to the index page" do click_on("op-meetings-header-action-trigger") - accept_confirm(I18n.t("text_are_you_sure")) do - click_on "Delete meeting" + click_on "Delete meeting" + + within "#delete-meeting-dialog" do + check "I understand that this deletion cannot be reversed" + + click_on "Delete permanently" end expect(page).to have_current_path project_meetings_path(project) + + expect_flash(type: :success, message: "Successful deletion.") end context "when exporting as ICS" do diff --git a/modules/meeting/spec/support/pages/meetings/index.rb b/modules/meeting/spec/support/pages/meetings/index.rb index 624ed36a3322..601ba38107b9 100644 --- a/modules/meeting/spec/support/pages/meetings/index.rb +++ b/modules/meeting/spec/support/pages/meetings/index.rb @@ -126,7 +126,7 @@ def expect_no_copy_action(meeting) def expect_delete_action(meeting) within more_menu(meeting) do - expect(page).to have_button("Delete meeting") + expect(page).to have_link("Delete meeting") end end