Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/58161 global stage administration #17358

Draft
wants to merge 44 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d9ccb20
boilerplate for lifecycle settings
ulferts Nov 13, 2024
e8b9c63
add header component to life cycle show page
ulferts Nov 13, 2024
c4cae9c
highlight menu item
ulferts Nov 13, 2024
7eb0e4f
list all existing life cycle elements
ulferts Nov 13, 2024
2c7b1bc
add ability to toggle the active state of a step
ulferts Nov 14, 2024
fc3b99b
display life cycle type next to name
ulferts Nov 15, 2024
4524b43
adapt section header
ulferts Nov 15, 2024
a4e6804
use css class for icon colorization
ulferts Nov 18, 2024
22452c9
decrease font size for type
ulferts Nov 21, 2024
65c917f
include life cycle type component in lookbook
ulferts Nov 22, 2024
79e0ba4
adapt names after rebasing
ulferts Nov 22, 2024
cf176cc
apply feature flag guard
ulferts Nov 22, 2024
866b739
split project setting spec support pages
ulferts Nov 22, 2024
801d33f
add bulk actions for project life cycle admin
ulferts Nov 25, 2024
45b060d
turn toggling into upsert
ulferts Nov 26, 2024
24a627d
clarify variable names
ulferts Nov 26, 2024
6771911
order definitions by position
ulferts Nov 27, 2024
ec5e6b5
enable filtering on the life cycle page
ulferts Nov 27, 2024
ed225b5
spec insufficient access
ulferts Nov 27, 2024
a2d8949
add new permission to roles having edit_project
ulferts Nov 27, 2024
55e5a52
remove unnecessary wrapper
ulferts Nov 28, 2024
aa7152f
add permission description according to specification
ulferts Nov 28, 2024
054a670
remove unnecessary turbo data attribute
ulferts Nov 28, 2024
713f114
use options for components
ulferts Dec 9, 2024
bd2b31a
change to a more restricted property definition
ulferts Dec 9, 2024
47fbe81
remove unnecessary check
ulferts Dec 9, 2024
59b8f14
exclude searching on non user relevant text
ulferts Dec 9, 2024
e0eb17b
improve expressiveness of spec
ulferts Dec 9, 2024
9dd3d4d
add no results text
ulferts Dec 9, 2024
16d5bd9
simplify variable handling of definition_id between view and controller
ulferts Dec 9, 2024
5112651
use not found in case lifecycle feature flag is disabled
ulferts Dec 9, 2024
737f6fc
linting
ulferts Dec 9, 2024
3b9e838
remove by now unnecessary turbo wrapper
ulferts Dec 9, 2024
8b9059c
rely on default naming of index
ulferts Dec 9, 2024
ee94d56
[#58161] Global stage administration
toy Dec 4, 2024
5e8c998
add menu entry
toy Dec 4, 2024
f29eaa1
make life cycle step definitions default to order by position
toy Dec 4, 2024
64dcb5e
comment out validation that Project::LifeCycleStepDefinition is not i…
toy Dec 9, 2024
4abfa7f
routing
toy Dec 11, 2024
d46d1c4
index header
toy Dec 4, 2024
622e504
add form and new/edit actions
toy Dec 11, 2024
6d93667
Project::LifeCycleStepDefinition.with_project_count
toy Dec 11, 2024
e476214
basic index - list definitions
toy Dec 11, 2024
516961c
show project count in index
toy Dec 11, 2024
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
8 changes: 8 additions & 0 deletions app/components/projects/life_cycle_type_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<%= flex_layout(align_items: :center) do |type_container|
type_container.with_column(mr: 1, classes: icon_color_class) do
render Primer::Beta::Octicon.new(icon: icon)
end
type_container.with_column do
render(Primer::Beta::Text.new(**text_options)) { text }
end
end %>
60 changes: 60 additions & 0 deletions app/components/projects/life_cycle_type_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2024 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 Projects
class LifeCycleTypeComponent < ApplicationComponent
include OpPrimer::ComponentHelpers

def text
model.model_name.human
end

def icon
case model
when Project::StageDefinition
:"git-commit"
when Project::GateDefinition
:diamond
else
raise NotImplementedError, "Unknown model #{model.class} to render a LifeCycleTypeComponent with"
end
end

def icon_color_class
helpers.hl_inline_class("life_cycle_step_definition", model)
end

def text_options
# The tag: :div is is a hack to fix the line height difference
# caused by font_size: :small. That line height difference
# would otherwise lead to the text being not on the same height as the icon
{ color: :muted, font_size: :small, tag: :div }.merge(options)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<%=
flex_layout(data: wrapper_data_attributes) do |flex|
flex.with_row do
render(Primer::OpenProject::SubHeader.new) do |subheader|
subheader.with_filter_input(name: "border-box-filter",
label: t('projects.settings.life_cycle.filter.label'),
visually_hide_label: true,
placeholder: t('projects.settings.life_cycle.filter.label'),
leading_visual: {
icon: :search,
size: :small
},
show_clear_button: true,
clear_button_id: clear_button_id,
data: {
action: "input->projects--settings--border-box-filter#filterLists",
"projects--settings--border-box-filter-target": "filter"
})
end
end

flex.with_row do
render(border_box_container(mb: 3, data: { test_selector: "project-life-cycle-administration" })) do |component|
component.with_header(font_weight: :bold, py: 2) do
flex_layout(justify_content: :space_between, align_items: :center) do |header_container|
header_container.with_column(py: 2) do
# adding py: 2 here to match the padding of the actions_container
# otherwise the header height changes when the actions gets hidden when filtering
render(Primer::Beta::Text.new(font_weight: :bold)) do
I18n.t('projects.settings.life_cycle.section_header')
end
end
header_container.with_column(flex_layout: true, justify_content: :flex_end) do |actions_container|
actions_container.with_column(data: { 'projects--settings--border-box-filter-target': 'bulkActionContainer' }) do
render(Primer::Beta::Button.new(
tag: :a,
href: enable_all_project_settings_life_cycle_path(project_id: project),
scheme: :invisible,
font_weight: :bold,
color: :subtle,
'aria-label': t('projects.settings.actions.label_enable_all'),
data: { 'turbo-method': :post, test_selector: "enable-all-life-cycle-steps" }
)) do |button|
button.with_leading_visual_icon(icon: 'check-circle', color: :subtle)
t('projects.settings.actions.label_enable_all')
end
end
actions_container.with_column(data: { 'projects--settings--border-box-filter-target': 'bulkActionContainer' }) do
render(Primer::Beta::Button.new(
tag: :a,
href: disable_all_project_settings_life_cycle_path(project_id: project),
scheme: :invisible,
font_weight: :bold,
color: :subtle,
'aria-label': t('projects.settings.actions.label_disable_all'),
data: { 'turbo-method': :post, test_selector: "disable-all-life-cycle-steps" }
)) do |button|
button.with_leading_visual_icon(icon: 'x-circle', color: :subtle)
t('projects.settings.actions.label_disable_all')
end
end
end
end
end
if life_cycle_definitions.empty?
component.with_row do
render(Primer::Beta::Text.new(color: :subtle)) { t("projects.settings.life_cycle.non_defined") }
end
else
life_cycle_definitions.each do |definition|
component.with_row(data: { 'projects--settings--border-box-filter-target': 'searchItem' },
test_selector: "project-life-cycle-step-#{definition.id}") do
render(Projects::Settings::LifeCycles::StepComponent.new(project:, definition:))
end
end
end
end
end
flex.with_row do
render Primer::Beta::Text.new(display: :none,
data: {
"projects--settings--border-box-filter-target": "noResultsText",
}) do
I18n.t("js.autocompleter.notFoundText")
end
end
end
%>
56 changes: 56 additions & 0 deletions app/components/projects/settings/life_cycles/index_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2024 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 Projects
module Settings
module LifeCycles
class IndexComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable

options :project,
:life_cycle_definitions

private

def wrapper_data_attributes
{
controller: "projects--settings--border-box-filter",
"application-target": "dynamic",
"projects--settings--border-box-filter-clear-button-id-value": clear_button_id
}
end

def clear_button_id
"border-box-filter-clear-button"
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<%= render Primer::OpenProject::PageHeader.new do |header| %>
<%= header.with_title { t('projects.settings.life_cycle.header.title') } %>
<%= header.with_description do
t('projects.settings.life_cycle.header.description_html',
overview_url: project_path(project),
admin_settings_url: admin_settings_project_life_cycles_path)
end %>
<%= header.with_breadcrumbs(breadcrumb_items) %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2024 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 Projects::Settings::LifeCycles
class ShowPageHeaderComponent < ApplicationComponent
include ApplicationHelper

options :project

def breadcrumb_items
[{ href: project_overview_path(project), text: project.name },
{ href: project_settings_general_path(project), text: I18n.t("label_project_settings") },
t("projects.settings.life_cycle.header.title")]
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<%=
flex_layout(align_items: :center,
justify_content: :space_between) do |step_container|
step_container.with_column(flex_layout: true) do |title_container|
title_container.with_column(pt: 1, mr: 3) do
render(Primer::Beta::Text.new(classes: 'filter-target-visible-text')) { definition.name }
end
title_container.with_column(pt: 1) do
render(Projects::LifeCycleTypeComponent.new(definition))
end
end
# py: 1 quick fix: prevents the row from bouncing as the toggle switch currently changes height while toggling
step_container.with_column(py: 1, mr: 2) do
# buggy currently:
# small variant + status_label_position: :start leads to a small bounce while toggling
# behavior can be seen on primer's viewbook as well -> https://view-components-storybook.eastus.cloudapp.azure.com/view-components/lookbook/inspect/primer/alpha/toggle_switch/small
# quick fix: don't display loading indicator which is causing the bounce
render(Primer::Alpha::ToggleSwitch.new(
src: toggle_project_settings_life_cycle_path(definition_id: definition.id),
csrf_token: form_authenticity_token,
data: { test_selector: "toggle-project-life-cycle-#{definition.id}" },
aria: { label: toggle_aria_label },
checked: active_in_project?,
size: :small,
status_label_position: :start,
classes: "op-primer-adjustments__toggle-switch--hidden-loading-indicator",
))
end
end
%>
53 changes: 53 additions & 0 deletions app/components/projects/settings/life_cycles/step_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2024 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 Projects
module Settings
module LifeCycles
class StepComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable

options :definition,
:project

def active_in_project?
project
.life_cycle_steps
.detect { |project_lc| project_lc.definition_id == definition.id }
&.active
end

def toggle_aria_label
I18n.t("projects.settings.life_cycle.step.use_in_project", step: definition.name)
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
}) do |custom_field_container|
custom_field_container.with_column(flex_layout: true) do |title_container|
title_container.with_column(pt: 1, mr: 2) do
render(Primer::Beta::Text.new) do
render(Primer::Beta::Text.new(classes: 'filter-target-visible-text')) do
@project_custom_field.name
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
flex_layout do |flex|
flex.with_row do
render(Primer::OpenProject::SubHeader.new) do |subheader|
subheader.with_filter_input(name: "project-custom-fields-mapping-filter",
subheader.with_filter_input(name: "border-box-filter",
label: t('projects.settings.project_custom_fields.filter.label'),
visually_hide_label: true,
placeholder: t('projects.settings.project_custom_fields.filter.label'),
Expand All @@ -14,8 +14,8 @@
show_clear_button: true,
clear_button_id: clear_button_id,
data: {
action: "input->projects--settings--project-custom-fields-mapping-filter#filterLists",
"projects--settings--project-custom-fields-mapping-filter-target": "filter"
action: "input->projects--settings--border-box-filter#filterLists",
"projects--settings--border-box-filter-target": "filter"
})
end
end
Expand Down
Loading
Loading