Redraw the workflow form on empty statuses. wp/72383

This commit is contained in:
David F
2026-04-09 15:55:55 +02:00
parent 669c827935
commit b670ac19a6
5 changed files with 237 additions and 65 deletions
@@ -0,0 +1,88 @@
<%#-- 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.
++#%>
<%= component_wrapper do %>
<% if @statuses.any? %>
<%= form_tag(
{ action: :update },
id: "workflow_form",
method: :patch,
autocomplete: "off",
data: {
controller: "admin--workflow-checkbox-state",
"admin--workflow-checkbox-state-has-status-changes-value": @has_status_changes,
turbo_temporary: true
}
) do %>
<%= hidden_field_tag "type_id", @type.id %>
<%= hidden_field_tag "role_id", @role.id %>
<%= hidden_field_tag "tab", @tab %>
<%= helpers.render_tabs helpers.workflow_tabs(@type) %>
<div class="workflow-save-bar">
<%=
render Primer::Beta::Button.new(scheme: :primary, type: :submit, mt: 3, ml: 3, mb: 4) do
t(:button_save)
end
%>
</div>
<%=
render(
Primer::OpenProject::FeedbackDialog.new(
title: t("admin.workflows.leave_confirmation.title"),
data: {
"admin--workflow-checkbox-state-target": "confirmationDialog",
}
)
) do |dialog|
dialog.with_feedback_message(icon_arguments: { icon: :none }) do |message|
message.with_heading(tag: :h2) { t("admin.workflows.leave_confirmation.title") }
message.with_description_content(t("admin.workflows.leave_confirmation.description"))
end
dialog.with_footer do
component_collection do |footer|
footer.with_component(
Primer::Beta::Button.new(scheme: :danger, data: { "admin--workflow-checkbox-state-target": "ignoreButton" })
) { t("admin.workflows.leave_confirmation.ignore") }
footer.with_component(
Primer::Beta::Button.new(scheme: :primary, data: { "admin--workflow-checkbox-state-target": "saveButton" })
) { t("admin.workflows.leave_confirmation.save") }
end
end
end
%>
<% end %>
<% else %>
<%= render Workflows::BlankslateComponent.new(role: @role, type: @type, tab: @tab) %>
<% end %>
<% end %>
@@ -0,0 +1,45 @@
# 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 Workflows
class MatrixFormComponent < ApplicationComponent
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers
def initialize(tab:, role:, type:, statuses:, has_status_changes:)
super
@tab = tab
@role = role
@type = type
@statuses = statuses
@has_status_changes = has_status_changes
end
end
end
+13 -3
View File
@@ -56,10 +56,8 @@ class WorkflowsController < ApplicationController
end
def update # rubocop:disable Metrics/AbcSize
tab = params[:tab] || "always"
call = Workflows::BulkUpdateService
.new(role: @role, type: @type, tab:)
.new(role: @role, type: @type, tab: current_tab)
.call(permitted_status_params)
if call.success?
@@ -67,6 +65,18 @@ class WorkflowsController < ApplicationController
message: I18n.t(:notice_successful_update),
scheme: :success
)
if statuses_for_form.empty?
# Need to replace with the blankslate.
update_via_turbo_stream(
component: Workflows::MatrixFormComponent.new(
tab: current_tab,
role: @role,
type: @type,
statuses: @statuses,
has_status_changes: false
)
)
end
else
render_flash_message_via_turbo_stream(
message: I18n.t(:notice_unsuccessful_update),
+1 -57
View File
@@ -36,62 +36,6 @@ See COPYRIGHT and LICENSE files for more details.
<%= turbo_frame_tag "workflow-table", data: { turbo_cache: false } do %>
<%= render Workflows::EditSubHeaderComponent.new(tab: @current_tab, current_role: @role, type: @type, available_roles: @roles, status_ids: @statuses.pluck(:id)) %>
<% if @statuses.any? %>
<%= form_tag(
{ action: :update },
id: "workflow_form",
method: :patch,
autocomplete: "off",
data: {
controller: "admin--workflow-checkbox-state",
"admin--workflow-checkbox-state-has-status-changes-value": @has_status_changes,
turbo_temporary: true
}
) do %>
<%= hidden_field_tag "type_id", @type.id %>
<%= hidden_field_tag "role_id", @role.id %>
<%= hidden_field_tag "tab", @current_tab %>
<%= render_tabs workflow_tabs(@type) %>
<div class="workflow-save-bar">
<%=
render Primer::Beta::Button.new(scheme: :primary, type: :submit, mt: 3, ml: 3, mb: 4) do
t(:button_save)
end
%>
</div>
<%=
render(
Primer::OpenProject::FeedbackDialog.new(
title: t("admin.workflows.leave_confirmation.title"),
data: {
"admin--workflow-checkbox-state-target": "confirmationDialog",
}
)
) do |dialog|
dialog.with_feedback_message(icon_arguments: { icon: :none }) do |message|
message.with_heading(tag: :h2) { t("admin.workflows.leave_confirmation.title") }
message.with_description_content(t("admin.workflows.leave_confirmation.description"))
end
dialog.with_footer do
component_collection do |footer|
footer.with_component(
Primer::Beta::Button.new(scheme: :danger, data: { "admin--workflow-checkbox-state-target": "ignoreButton" })
) { t("admin.workflows.leave_confirmation.ignore") }
footer.with_component(
Primer::Beta::Button.new(scheme: :primary, data: { "admin--workflow-checkbox-state-target": "saveButton" })
) { t("admin.workflows.leave_confirmation.save") }
end
end
end
%>
<% end %>
<% else %>
<%= render Workflows::BlankslateComponent.new(role: @role, type: @type, tab: @current_tab) %>
<% end %>
<%= render Workflows::MatrixFormComponent.new(tab: @current_tab, role: @role, type: @type, statuses: @statuses, has_status_changes: @has_status_changes) %>
<% end %>
<% end %>
+90 -5
View File
@@ -396,7 +396,7 @@ RSpec.describe "Workflow edit", :js do
it "shows a confirmation dialog when changing roles after adding a status" do
add_status_via_dialog(statuses[2])
expect(page).to have_no_field workflow_checkbox(0, 2)
expect(page).to have_field workflow_checkbox(0, 2)
click_button role.name
click_link other_role.name
@@ -650,11 +650,96 @@ RSpec.describe "Workflow edit", :js do
end
end
it "allows navigating to any Copy page", :js do
within ".PageHeader-actions" do
click_on "Copy"
context "with copy dialog" do
it "allows navigating to any Copy page", :js do
within ".PageHeader-actions" do
click_on "Copy"
end
expect(page).to have_dialog "Copy workflow"
end
expect(page).to have_heading "Copy workflow"
context "with unsaved checkbox" do
it "loses unsaved checkbox changes when clicking on copy and ignoring" do
within "#workflow_form_always" do
check workflow_checkbox(1, 0)
end
click_link "Copy"
within_dialog "Save changes before continuing?" do
click_button "Ignore changes"
end
within "#workflow_form_always" do
expect(page).to have_field workflow_checkbox(1, 0), checked: false
end
expect(page).to have_dialog "Copy workflow"
end
it "saves changes and switches to the new role when clicking 'Save changes and continue'" do
within "#workflow_form_always" do
check workflow_checkbox(1, 0)
end
click_link "Copy"
within_dialog "Save changes before continuing?" do
click_button "Save changes and continue"
end
expect_flash(message: "Successful update.")
expect(Workflow.exists?(role_id: role.id, type_id: type.id,
old_status_id: statuses[1].id, new_status_id: statuses[0].id,
author: false, assignee: false)).to be true
expect(page).to have_dialog "Copy workflow"
end
it "keeps unsaved changes and stays on the same role when closing the dialog via 'X'" do
within "#workflow_form_always" do
check workflow_checkbox(1, 0)
end
click_link "Copy"
within_dialog "Save changes before continuing?" do
find(".close-button").click
end
expect(page).to have_no_dialog("Save changes before continuing?")
within "#workflow_form_always" do
expect(page).to have_field workflow_checkbox(1, 0), checked: true
end
expect(page).to have_no_dialog "Copy workflow"
end
end
context "with unsaved new status" do
it "shows a confirmation dialog when copying after adding a status" do
add_status_via_dialog(statuses[2])
expect(page).to have_field workflow_checkbox(0, 2)
click_link "Copy"
expect(page).to have_dialog("Save changes before continuing?")
end
it "reverts the added status on changes ignored" do
add_status_via_dialog(statuses[2])
expect(page).to have_field workflow_checkbox(0, 2)
click_link "Copy"
within_dialog "Save changes before continuing?" do
click_button "Ignore changes"
end
expect(page).to have_no_field workflow_checkbox(0, 2)
end
end
end
end