Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
29fce21
[JIM-133] Jira Migrator UI refinements
as-op Jul 23, 2026
24f2fb5
add server_url column to Jira configuration table
as-op Jul 23, 2026
3c990e4
add action menu & column to Jira configuration table
as-op Jul 23, 2026
0d997a9
Replace Test configuration button with text below PAT input "You can …
as-op Jul 23, 2026
1d00913
Replace the button "Edit configuration" with an action menu popup
as-op Jul 23, 2026
b7c163e
add action menu to import runs table, remove status column, place sta…
as-op Jul 23, 2026
383abc4
add creator column to jira import runs table
as-op Jul 23, 2026
3288006
Add a "..." button behind the projects in the Projects column which s…
as-op Jul 27, 2026
4fb79b6
Move header status into new sidepanel
as-op Jul 27, 2026
6353786
Combine wizard sections
as-op Jul 27, 2026
a4c5b42
move the info box into the previous wizard section, update fetch_data…
as-op Jul 27, 2026
383ca74
import run table: declare main_column
as-op Jul 27, 2026
559847c
update import_scope section layout
as-op Jul 27, 2026
c766a0b
update confirm_import section layout
as-op Jul 27, 2026
801ee0e
update select projects layout
as-op Jul 27, 2026
fec5711
refactor SelectProjectsDialog controller concerns
as-op Jul 27, 2026
066cd52
update review section layout
as-op Jul 27, 2026
21a041b
Add import status component
as-op Jul 28, 2026
3a3cce4
obey eslint
as-op Jul 28, 2026
bf9a561
sort en.yml
as-op Jul 28, 2026
af668d5
update test with changed label content
as-op Jul 28, 2026
1dcb346
Merge remote-tracking branch 'origin/feature/jim-133-jira-migrator-ui…
as-op Jul 28, 2026
e965c82
update confirm_import section layout
as-op Jul 28, 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,16 @@
<%= render(Primer::Box.new(bg: :success, border: true, border_color: :success, border_radius: 3, p: 4)) do
concat(
render(Primer::Box.new(display: :flex, justify_content: :space_between, align_items: :center, mb: 3)) do
concat(render(Primer::Beta::Heading.new(tag: :h3, font_size: 3)) { title })
concat(action) if action?
end
)
concat(
render(Primer::Box.new(display: :flex, flex_wrap: :wrap, classes: "gap-3")) do
stats.each do |stat|
card = render(Users::WorkingHours::StatCardComponent.new(label: stat[:label], value: stat[:value], subtitle: stat[:subtitle]))
concat(stat[:url].present? ? render(Primer::Beta::Link.new(href: stat[:url], target: "_blank", underline: false, display: :flex, flex: 1)) { card } : card)
end
end
)
end %>
Original file line number Diff line number Diff line change
@@ -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 Admin::Import::Jira::ImportRuns
class ImportedStatsBannerComponent < ApplicationComponent
renders_one :action

attr_reader :title, :stats

# stats: an array of { label:, value:, subtitle:, url: } hashes, one per card.
# url is optional; when present the whole card links to it.
def initialize(title:, stats:, **)
super(nil, **)
@title = title
@stats = Array(stats)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ def initialize(title:, list:, subtitle: nil, show_icon: true, icon: :"dot-fill",
end

def call
render(OpPrimer::InsetBoxComponent.new(border: false, **system_arguments)) do
render(
Primer::Box.new(
border: true,
border_radius: 2,
p: 4,
**system_arguments
)
) do
flex_layout do |flex|
flex.with_row(mb: 1) do
render(Primer::Beta::Text.new(font_weight: :bold)) { title }
Expand Down
82 changes: 49 additions & 33 deletions app/components/admin/import/jira/import_runs/row_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,58 +34,74 @@ def id
render(
Primer::Beta::Link.new(
href: admin_import_jira_run_path(jira_id: model.jira.id, id: model.id),
font_weight: :bold
font_weight: :bold,
mr: 1
)
) do
"#{I18n.t('admin.jira.run.title')} ##{model.id}"
end
end +
render(Admin::Import::Jira::ImportRuns::StatusBadgeComponent.new(model.current_state))
end

def status
render(Admin::Import::Jira::ImportRuns::StatusBadgeComponent.new(model.current_state))
def creator
render(Users::AvatarComponent.new(user: model.author, size: :mini, link: true, show_name: true))
end

def last_changed
helpers.format_time(model.updated_at)
helpers.format_time(last_changed_date)
end

def last_changed_date
if model.last_transition
model.last_transition.created_at
else
model.updated_at
end
end

def projects
(model.projects || []).pluck("name").join(", ")
names = (model.projects || []).pluck("name").join(", ")
return names if names.blank?

render(OpPrimer::ExpandableTextComponent.new(flex: 1)) { names }
end

def button_links
[edit_button]
# buttons = []
# buttons.push(remove_button) if model.deletable?
# buttons.push(edit_button)
# buttons
[
action_menu
]
end

def edit_button
render(
Primer::Beta::IconButton.new(
icon: :pencil,
tag: :a,
href: admin_import_jira_run_path(jira_id: model.jira.id, id: model.id),
"aria-label": I18n.t(:button_edit)
def action_menu
render(Primer::Alpha::ActionMenu.new) do |menu|
menu.with_show_button(
scheme: :invisible,
size: :small,
icon: :"kebab-horizontal",
"aria-label": t(:button_actions),
tooltip_direction: :w
)
)
add_menu_item(menu, label: :"admin.jira.run.actions.button_edit", icon: :pencil, href: run_path)
add_menu_item(menu, label: :"admin.jira.run.actions.button_open_history", icon: :history, href: run_history_path)
end
end

def remove_button
render(
Primer::Beta::IconButton.new(
icon: :trash,
scheme: :danger,
tag: :a,
href: remove_admin_import_jira_run_path(jira_id: model.jira.id, id: model.id),
"aria-label": I18n.t(:button_delete),
data: {
turbo_method: :delete,
turbo_confirm: I18n.t(:text_are_you_sure)
}
)
)
private

def add_menu_item(menu, label:, icon:, href:)
menu.with_item(scheme: :default,
label: I18n.t(label),
content_arguments: { tag: :a, href: }) do |item|
item.with_leading_visual_icon(icon:)
end
end

def run_path
admin_import_jira_run_path(jira_id: model.jira.id, id: model.id)
end

def run_history_path
history_admin_import_jira_run_path(jira_id: model.jira.id, id: model.id)
end
end
end
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
<%= component_wrapper do
flex_layout(align_items: :center, mt: 2, flex_wrap: :wrap) do |flex|
flex.with_column(flex: 1) do
render(
Primer::Alpha::TextField.new(
name: :filter,
value: filter,
label: I18n.t(:"admin.jira.run.wizard.select_dialog.filter_projects"),
placeholder: I18n.t(:"admin.jira.run.wizard.select_dialog.filter_projects"),
visually_hide_label: true,
input_width: :large,
style: "min-width: 250px;",
leading_visual: { icon: :search },
data: { action: "input->admin--jira-projects#filterProjects" }
)
)
end
flex.with_column do
render(
Primer::Beta::Button.new(
scheme: :invisible,
tag: :a,
href: check_all_url,
data: { action: "click->admin--jira-projects#checkAll" }
)
) do |button|
button.with_leading_visual_icon(icon: :"check-circle")
I18n.t(:button_check_all)
flex_layout(align_items: :center, mb: 2, flex_wrap: :wrap) do |flex|
flex.with_column(flex: 1) do
render(
Primer::Alpha::TextField.new(
name: :filter,
value: filter,
label: I18n.t(:"admin.jira.run.wizard.select_projects.filter_projects"),
placeholder: I18n.t(:"admin.jira.run.wizard.select_projects.filter_projects"),
visually_hide_label: true,
input_width: :large,
style: "min-width: 250px;",
leading_visual: { icon: :search },
data: { action: "input->admin--jira-projects#filterProjects" }
)
)
end
flex.with_column do
render(
Primer::Beta::Button.new(
scheme: :invisible,
tag: :a,
href: check_all_url,
data: { action: "click->admin--jira-projects#checkAll" }
)
) do |button|
button.with_leading_visual_icon(icon: :"check-circle")
I18n.t(:"admin.jira.run.wizard.select_projects.button_select_all")
end
end
flex.with_column do
render(
Primer::Beta::Button.new(
scheme: :invisible,
tag: :a,
href: uncheck_all_url,
data: { action: "click->admin--jira-projects#uncheckAll" }
)
) do |button|
button.with_leading_visual_icon(icon: :"x-circle")
I18n.t(:"admin.jira.run.wizard.select_projects.button_unselect_all")
end
end
end
end
flex.with_column do
render(
Primer::Beta::Button.new(
scheme: :invisible,
tag: :a,
href: uncheck_all_url,
data: { action: "click->admin--jira-projects#uncheckAll" }
)
) do |button|
button.with_leading_visual_icon(icon: :"x-circle")
I18n.t(:button_uncheck_all)
end
end
end
end %>
end %>
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,18 @@
"keep-open-on-submit": true
}
)
) do |d|
d.with_header(
show_divider: true,
variant: :medium
) do
concat(render(Primer::Beta::Text.new(font_weight: :bold)) do
I18n.t(:"admin.jira.run.wizard.select_projects.title")
end)
) do |dialog|
dialog.with_header(show_divider: true)
dialog.with_body(classes: "Overlay-body_autocomplete_height") do
concat(
render(Primer::Alpha::Banner.new(scheme: :default, icon: :info, mb: 3)) do
I18n.t(:"admin.jira.run.wizard.select_projects.info")
end
)
concat(render(list_header_component))
end
d.with_body(classes: "Overlay-body_autocomplete_height") do
concat(render(list_component))
end
d.with_footer(
dialog.with_footer(
show_divider: true
) do
component_collection do |modal_footer|
Expand All @@ -39,12 +37,14 @@
I18n.t(:button_cancel)
end
modal_footer.with_component(
content_tag(:div, data: { "admin--jira-projects-target": "submitButton" }) {
render(Admin::Import::Jira::ImportRuns::SelectProjects::ModalSubmitComponent.new(
jira_import: @jira_import,
count: selected_count
))
}
content_tag(:div, data: { "admin--jira-projects-target": "submitButton" }) do
render(
Admin::Import::Jira::ImportRuns::SelectProjects::ModalSubmitComponent.new(
jira_import: @jira_import,
count: selected_count
)
)
end
)
modal_footer.with_component(
Primer::Beta::Button.new(
Expand All @@ -55,7 +55,7 @@
)
) do |spinner_button|
spinner_button.with_trailing_visual_icon(icon: :sync, animation: :rotate, style: "min-width: 2rem")
I18n.t(:button_continue)
I18n.t(:"admin.jira.run.wizard.select_projects.button_select")
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<%=
render(Primer::OpenProject::SidePanel::Section.new) do |section|
section.with_title { I18n.t(:"admin.jira.run.side_panel.run_status") }
flex_layout do |flex|
flex.with_row do
render(Admin::Import::Jira::ImportRuns::StreamableStatusBadgeComponent.new(jira_import))
end
flex.with_row(mt: 3) do
render(
Primer::Beta::Link.new(
href: history_admin_import_jira_run_path(jira_id: jira_import.jira.id, id: jira_import.id),
target: "_blank"
)
) do |link|
link.with_leading_visual_icon(icon: :clock)
link.with_trailing_visual_icon(icon: :"link-external")
I18n.t(:"admin.jira.run.side_panel.open_history")
end
end
end
end
%>
Loading
Loading