Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1d33389
Add semantic work package ids feature flag
akabiru Feb 24, 2026
ff84a0b
Base formation of work packages identifier admin settings form
akabiru Feb 24, 2026
08f1e29
Add identifier admin settings UI with autofix preview
akabiru Feb 25, 2026
8ca8e9d
Refactor: SQL query, full-width layout, docs, FIXME markers
akabiru Feb 25, 2026
2576d57
Improve handle suggestion algo: Unicode support + real-DB spec
akabiru Feb 25, 2026
d38006c
Show realistic example WP IDs in autofix preview table
akabiru Feb 25, 2026
3baf3cc
Mark future :handle_reserved and :identifier_taken error cases
akabiru Feb 25, 2026
d7d9c84
Rename PreviewComponent to SectionComponent; merge banner into it
akabiru Feb 25, 2026
f02ef00
Fix WorkPackageIdentifier encapsulation and lazy-load guard
akabiru Feb 25, 2026
20b7a28
Revert lazy-load guard on ProjectHandleSuggestionGenerator
akabiru Feb 25, 2026
def97b7
Test non-Latin script fallback in handle generation
akabiru Feb 26, 2026
ad2d56e
Add SUFFIX_LIMIT guard to unique_handle to prevent infinite loop
akabiru Feb 26, 2026
5f83f29
Add confirmation dialog, in-progress UI, and extract radio form compo…
akabiru Feb 26, 2026
180a17a
Extend handle generator with in_use/reserved reasons and performance-…
akabiru Mar 5, 2026
b54d9dc
Remove excess comments
akabiru Mar 5, 2026
f1655e9
Extract PreviewQuery, collapse error_label, remove unused ERROR_REASONS
akabiru Mar 5, 2026
4701835
Relocate ProjectHandleSuggestionGenerator into WorkPackages::Identifi…
akabiru Mar 5, 2026
1ef8ee6
Touch up project handle suggestion generator specs
akabiru Mar 5, 2026
a5ea92d
Unify identifier settings form into single state-machine component
akabiru Mar 6, 2026
7a7f926
Fix state-machine bugs in identifier settings form
akabiru Mar 7, 2026
45b6177
Document single-job assumption on ApplyHandlesJob
akabiru Mar 7, 2026
1a59617
Suppress empty ?reference= param in poll-for-changes controller
akabiru Mar 7, 2026
ebc0e4d
Fix handle_from_name for single-word project names
akabiru Mar 7, 2026
36514d9
Address code review concerns for identifier settings UI
akabiru Mar 7, 2026
5a73898
Use accessible selectors and raw strings in identifier settings specs
akabiru Mar 7, 2026
17d417e
Merge branch 'dev' into implementation/72461-administration-setting-f…
akabiru Mar 7, 2026
88205c9
Async-fetch confirm dialog via respond_with_dialog
akabiru Mar 9, 2026
1c83d34
Assert both radio buttons disabled/enabled in identifier settings spec
akabiru Mar 9, 2026
0f38c6d
Fix component spec: use have_link for autofix button and raw strings
akabiru Mar 9, 2026
aaaaaef
Add suggest_handle static interface to ProjectHandleSuggestionGenerator
akabiru Mar 13, 2026
e9900df
Rename ProjectHandleSuggestionGenerator to ProjectIdentifierSuggestio…
akabiru Mar 13, 2026
e09288d
Address code review: rename column, remove padding, split constants, …
akabiru Mar 13, 2026
f2b23e0
Fix identifier generator to match semantic identifier spec
akabiru Mar 13, 2026
0053b14
Consolidate identifier length constants into IDENTIFIER_LENGTH hash
akabiru Mar 13, 2026
68ef621
Simplify multi-word pipeline, harden digit-stripping, add ordering test
akabiru Mar 13, 2026
61e20a0
Improve readability: split dense one-liner, clarify mutation and cont…
akabiru Mar 13, 2026
25e5c41
Reduce allocations: string slicing, single-pass filter, simpler set m…
akabiru Mar 16, 2026
f8d1663
Use it syntax and method chaining for single-expression blocks
akabiru Mar 16, 2026
d4a8ca1
Inline set union, remove combined_identifiers wrapper
akabiru Mar 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<%#
-- 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::DangerDialog.new(
id: "change-identifiers-dialog",
title: I18n.t("admin.settings.work_packages_identifier.dialog.title"),
confirm_button_text: I18n.t("admin.settings.work_packages_identifier.dialog.confirm_button"),
cancel_button_text: I18n.t("button_close"),
size: :large,
form_arguments: {
action: admin_settings_work_packages_identifier_path,
method: :patch
}
)
) do |dialog|
dialog.with_confirmation_message do |message|
message.with_heading(tag: :h2) do
I18n.t("admin.settings.work_packages_identifier.dialog.heading")
end

message.with_description_content(
I18n.t("admin.settings.work_packages_identifier.dialog.description")
)
end

dialog.with_confirmation_check_box_content(
I18n.t("admin.settings.work_packages_identifier.dialog.checkbox_label")
)

dialog.with_additional_details(display: :none) do
hidden_field_tag("settings[work_packages_identifier]", Setting::WorkPackageIdentifier::ALPHANUMERIC)
end
end
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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 ChangeIdentifiersDialogComponent < ApplicationComponent
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<%#
-- 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::Banner.new(scheme: :warning, my: 3)) do
I18n.t(
"admin.settings.work_packages_identifier.banner.existing_identifiers_notice",
project_count: total_count
)
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
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 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 IdentifierAutofixSectionComponent < ApplicationComponent
include OpPrimer::ComponentHelpers

DISPLAY_COUNT = WorkPackages::IdentifierAutofix::PreviewQuery::DISPLAY_COUNT

def initialize(projects_data:, total_count: projects_data.size)
super()
@total_count = total_count
@displayed = projects_data.first(DISPLAY_COUNT)
@remaining_count = [total_count - @displayed.size, 0].max
end

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
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<%#
-- 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(**wrapper_data_attrs) do %>
<%=
settings_primer_form_with(
scope: :settings, action: :update, method: :patch,
html: change_in_progress? ? {} : { id: form_id }
) do |f|
render_inline_settings_form(f) do |form|
form.radio_button_group(
name: :work_packages_identifier,
label: I18n.t("settings.work_packages.work_package_identifier"),
required: true,
**radio_button_options
)

form.html_content do
if change_in_progress?
render(Primer::Beta::Text.new(my: 3)) do
render(Primer::Beta::Spinner.new(size: :small, mr: 2)).to_s +
I18n.t("admin.settings.work_packages_identifier.in_progress.banner_message")
end
elsif completed?
render(Primer::Alpha::Banner.new(scheme: :success, dismiss_scheme: :remove, mb: 3)) do
I18n.t("admin.settings.work_packages_identifier.success_banner")
end
end
end
end
end
%>

<% unless change_in_progress? %>
<%= tag.div(
hidden: !show_autofix_section?,
data: { admin__work_packages_identifier_target: "autofixSection" }
) do %>
<%= render(
WorkPackages::Admin::Settings::IdentifierAutofixSectionComponent.new(
projects_data:,
total_count:
)
) %>
<% end %>
<div class="op-admin-settings-form-wrapper">
<%= render(
Primer::Beta::Button.new(
scheme: :primary,
type: :submit,
form: form_id,
hidden: show_autofix_section?,
data: { admin__work_packages_identifier_target: "saveButton" }
)
) { t("button_save") } %>

<%= render(
Primer::Beta::Button.new(
tag: :a,
href: confirm_dialog_admin_settings_work_packages_identifier_path,
scheme: :primary,
hidden: !show_autofix_section?,
data: {
turbo_stream: true,
admin__work_packages_identifier_target: "autofixButton"
}
)
) { t("admin.settings.work_packages_identifier.button_autofix") } %>
</div>
<% end %>
<% end %>
Loading
Loading