diff --git a/app/components/work_packages/admin/settings/identifier_autofix_row_component.rb b/app/components/work_packages/admin/settings/identifier_autofix_row_component.rb new file mode 100644 index 000000000000..2cca49038bb3 --- /dev/null +++ b/app/components/work_packages/admin/settings/identifier_autofix_row_component.rb @@ -0,0 +1,70 @@ +# 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 WorkPackages + module Admin + module Settings + class IdentifierAutofixRowComponent < OpPrimer::BorderBoxRowComponent + def project + render(Primer::Beta::Link.new(href: project_path(model[:project]))) { model[:project].name } + end + + def previous_identifier + flex_layout(direction: :column) do |col| + col.with_row { render(Primer::Beta::Text.new) { model[:current_identifier] } } + if (label = error_label).present? + col.with_row do + render(Primer::OpenProject::InlineMessage.new(scheme: :critical, size: :small)) { label } + end + end + end + end + + def autofixed_suggestion + model[:suggested_identifier] + end + + # The sequence number is derived deterministically from the identifier so it looks + # varied across projects but is stable across renders. Range: 1–500. + def example_work_package_id + identifier = model[:suggested_identifier] + "#{identifier}-#{(identifier.bytes.sum % 500) + 1}" + end + + private + + def error_label + I18n.t("admin.settings.work_packages_identifier.autofix_preview.error_#{model[:error_reason]}", + default: "") + end + end + end + end +end diff --git a/app/components/work_packages/admin/settings/identifier_autofix_section_component.html.erb b/app/components/work_packages/admin/settings/identifier_autofix_section_component.html.erb index a9ba02a04458..3386b800f4cf 100644 --- a/app/components/work_packages/admin/settings/identifier_autofix_section_component.html.erb +++ b/app/components/work_packages/admin/settings/identifier_autofix_section_component.html.erb @@ -38,72 +38,11 @@ end %> -<%= - render(border_box_container(mb: 3)) do |component| - component.with_header(font_weight: :bold) do - flex_layout do |header| - header.with_column(flex: 1) do - render(Primer::Beta::Text.new(font_weight: :semibold)) do - I18n.t("admin.settings.work_packages_identifier.box_header.label_project") - end - end - header.with_column(flex: 1) do - render(Primer::Beta::Text.new(font_weight: :semibold)) do - I18n.t("admin.settings.work_packages_identifier.box_header.label_previous_identifier") - end - end - header.with_column(flex: 1) do - render(Primer::Beta::Text.new(font_weight: :semibold)) do - I18n.t("admin.settings.work_packages_identifier.box_header.label_autofixed_suggestion") - end - end - header.with_column(flex: 1) do - render(Primer::Beta::Text.new(font_weight: :semibold)) do - I18n.t("admin.settings.work_packages_identifier.box_header.label_example_work_package_id") - end - end - end - end - - displayed.each do |entry| - component.with_row do - flex_layout(align_items: :center) do |row| - row.with_column(flex: 1) do - render(Primer::Beta::Link.new(href: project_path(entry[:project]))) do - entry[:project].name - end - end - row.with_column(flex: 1) do - flex_layout(direction: :column) do |col| - col.with_row do - render(Primer::Beta::Text.new) { entry[:current_identifier] } - end - col.with_row do - render(Primer::Beta::Text.new(color: :danger, font_size: :small)) do - error_label(entry[:error_reason]) - end - end - end - end - row.with_column(flex: 1) do - render(Primer::Beta::Text.new) { entry[:suggested_identifier] } - end - row.with_column(flex: 1) do - render(Primer::Beta::Text.new) { sample_wp_id(entry[:suggested_identifier]) } - end - end - end - end - - if remaining_count.positive? - component.with_row do - render(Primer::Beta::Text.new(color: :muted)) do - I18n.t( - "admin.settings.work_packages_identifier.autofix_preview.remaining_projects", - count: remaining_count - ) - end - end - end - end -%> +<%= render(Primer::Box.new(mb: 3)) do %> + <%= render( + WorkPackages::Admin::Settings::IdentifierAutofixTableComponent.new( + rows: displayed, + remaining_count: + ) + ) %> +<% end %> diff --git a/app/components/work_packages/admin/settings/identifier_autofix_section_component.rb b/app/components/work_packages/admin/settings/identifier_autofix_section_component.rb index 7ca1d1acc621..8dbeea1b6d67 100644 --- a/app/components/work_packages/admin/settings/identifier_autofix_section_component.rb +++ b/app/components/work_packages/admin/settings/identifier_autofix_section_component.rb @@ -32,8 +32,6 @@ module WorkPackages module Admin module Settings class IdentifierAutofixSectionComponent < ApplicationComponent - include OpPrimer::ComponentHelpers - DISPLAY_COUNT = ProjectIdentifiers::IdentifierAutofix::PreviewQuery::DISPLAY_COUNT def initialize(projects_data:, total_count: projects_data.size) @@ -46,19 +44,6 @@ def initialize(projects_data:, total_count: projects_data.size) private attr_reader :total_count, :displayed, :remaining_count - - def error_label(error_reason) - I18n.t("admin.settings.work_packages_identifier.autofix_preview.error_#{error_reason}", - default: "") - end - - # Produces a realistic-looking example work package ID for the preview table. - # The sequence number is derived deterministically from the identifier so it looks - # varied across projects but is stable across renders. Range: 1–500. - def sample_wp_id(identifier) - n = (identifier.bytes.sum % 500) + 1 - "#{identifier}-#{n}" - end end end end diff --git a/app/components/work_packages/admin/settings/identifier_autofix_table_component.rb b/app/components/work_packages/admin/settings/identifier_autofix_table_component.rb new file mode 100644 index 000000000000..d912f24f5ec0 --- /dev/null +++ b/app/components/work_packages/admin/settings/identifier_autofix_table_component.rb @@ -0,0 +1,80 @@ +# 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 WorkPackages + module Admin + module Settings + class IdentifierAutofixTableComponent < OpPrimer::BorderBoxTableComponent + columns :project, :previous_identifier, :autofixed_suggestion, :example_work_package_id + # Project and previous identifier hold the long content; spanning two grid columns lets + # them wrap instead of truncating, while the short handle columns stay compact. + main_column :project, :previous_identifier + mobile_labels :previous_identifier, :autofixed_suggestion, :example_work_package_id + + def initialize(rows:, remaining_count: 0, **) + super(rows:, **) + @remaining_count = remaining_count + end + + def row_class + IdentifierAutofixRowComponent + end + + def mobile_title + header(:table_title) + end + + def headers + [ + [:project, { caption: header(:label_project) }], + [:previous_identifier, { caption: header(:label_previous_identifier) }], + [:autofixed_suggestion, { caption: header(:label_autofixed_suggestion) }], + [:example_work_package_id, { caption: header(:label_example_work_package_id) }] + ] + end + + def has_footer? + @remaining_count.positive? + end + + def footer + I18n.t("admin.settings.work_packages_identifier.autofix_preview.remaining_projects", + count: @remaining_count) + end + + private + + def header(key) + I18n.t("admin.settings.work_packages_identifier.box_header.#{key}") + end + end + end + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 2786c28db4fe..ce93f6db9a83 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -452,6 +452,7 @@ en: OpenProject can automatically update these so that they are valid as in the examples below. Click on 'Convert identifiers' to update identifiers for all projects in this manner and enable project-based semantic identifiers. box_header: + table_title: Projects with identifiers to update label_project: Project label_previous_identifier: Previous identifier label_autofixed_suggestion: Future identifier