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

Implementation/59288 add stages and gates to the project overview page #17223

Merged
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5885b03
wip
dombesz Nov 18, 2024
96d1f2e
Display life cycles in the sidebar
dombesz Nov 18, 2024
479b9c8
Update variable names
dombesz Nov 25, 2024
f0c3df9
Add Stages and Gates permissions
dombesz Nov 26, 2024
a115cc5
Allow instantiation of Project::LifeCycleStepDefinition in order to l…
dombesz Nov 26, 2024
13ce90b
Rename overview page spec helper methods
dombesz Nov 26, 2024
9b82812
Add sidebar specs for displaying life cycles
dombesz Nov 26, 2024
b60407e
Add view permission specs
dombesz Nov 26, 2024
bfe6a7f
Add edit permission specs
dombesz Nov 26, 2024
002fdf3
Add Coloured icons, initial rendering of the form fields
dombesz Nov 28, 2024
56445a9
Add DatePicker component and dsl for single_date_picker and range_dat…
dombesz Dec 2, 2024
ddf938c
Display the datepicker input fields on the life cycle edit form
dombesz Dec 3, 2024
8bb61b7
Add ProjecLifeCycleSteps::BaseContract and UpdateContract.
dombesz Dec 3, 2024
bf65858
Make form validations work for nested project life cycle attributes.
dombesz Dec 5, 2024
667d1d5
Add Project::Stage date_range setter and validation.
dombesz Dec 6, 2024
f29a905
Remove condition for invalid LifeCycleStep initialization, add valida…
dombesz Dec 6, 2024
76d8899
Use date and date_range in the life cycle steps dialog, automatically…
dombesz Dec 6, 2024
6e2f780
Add life cycle step increasing dates validation to the ProjectLifeCyc…
dombesz Dec 9, 2024
851f504
Activate associated validations for project life cycle steps only whe…
dombesz Dec 10, 2024
59bf7cc
Display duration in days caption for Project::Stage entries.
dombesz Dec 10, 2024
8306416
Create form preview to interactively calculate duration of selected d…
dombesz Dec 11, 2024
1eb9eac
Allow empty date and date range for Project::LifeCycleStep
dombesz Dec 12, 2024
4bd144d
exclude OP custom dom elements from being morphed
ulferts Dec 6, 2024
8040262
Enable morphing the Stages and Gates form preview action
dombesz Dec 13, 2024
583a2f3
Add validation error border around the datepicker's input.
dombesz Dec 14, 2024
5ad7378
Add specs for stages and gates edit dialog
dombesz Dec 16, 2024
6ce663b
Addd more Stage and Gates dialog specs wip.
dombesz Dec 16, 2024
b8b0bde
Fix unit specs
dombesz Dec 17, 2024
89de3bb
Attempt to fix flaky specs.
dombesz Dec 17, 2024
7aee3c1
Use the correct Project Lifecycle label
dombesz Dec 17, 2024
a96fdd1
Address CR comments
dombesz Dec 17, 2024
0b89658
Fix specs
dombesz Dec 17, 2024
b488419
Fix single quote character
dombesz Dec 17, 2024
3b7b6c3
Allow retry_block on RSpec::Expectations::ExpectationNotMetError too.
dombesz Dec 17, 2024
f00f051
Fix rubocop issues
dombesz Dec 17, 2024
f0fce0e
Address CR comments
dombesz Dec 18, 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
Next Next commit
wip
dombesz committed Dec 17, 2024
commit 5885b03b8ae1d270f84bbcfc3216985471672e3c
55 changes: 51 additions & 4 deletions frontend/src/app/features/overview/overview.component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
//-- 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.
//++

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { GridPageComponent } from 'core-app/shared/components/grids/grid/page/grid-page.component';
import { GRID_PROVIDERS } from 'core-app/shared/components/grids/grid/grid.component';
@@ -17,15 +45,34 @@ export class OverviewComponent extends GridPageComponent {
}

protected isTurboFrameSidebarEnabled():boolean {
const sidebarEnabledTag:HTMLMetaElement|null = document.querySelector('meta[name="sidebar_enabled"]');
return sidebarEnabledTag?.dataset.enabled === 'true';
return this.isCustomFieldsSidebarEnabled() || this.isLifeCyclesSidebarEnabled();
}

protected isCustomFieldsSidebarEnabled():boolean {
const customFieldsSidebarEnabledTag:HTMLMetaElement|null = document.querySelector('meta[name="custom_fields_sidebar_enabled"]');

return customFieldsSidebarEnabledTag?.dataset.enabled === 'true';
}

protected isLifeCyclesSidebarEnabled():boolean {
const lifeCyclesSidebarEnabledTag:HTMLMetaElement|null = document.querySelector('meta[name="life_cycles_sidebar_enabled"]');

return lifeCyclesSidebarEnabledTag?.dataset.enabled === 'true';
}

protected lifeCyclesSidebarSrc():string {
return `${this.pathHelper.staticBase}/projects/${this.currentProject.identifier ?? ''}/project_life_cycles_sidebar`;
}

protected lifeCyclesSidebarId():string {
return 'project-life-cycles-sidebar';
}

protected turboFrameSidebarSrc():string {
protected projectCustomFieldsSidebarSrc():string {
return `${this.pathHelper.staticBase}/projects/${this.currentProject.identifier ?? ''}/project_custom_fields_sidebar`;
}

protected turboFrameSidebarId():string {
protected projectCustomFieldsSidebarId():string {
return 'project-custom-fields-sidebar';
}

Original file line number Diff line number Diff line change
@@ -17,7 +17,27 @@ <h2 [textContent]="text.title"></h2>
<grid *ngIf="grid" [grid]="grid"></grid>
</div>
<div class="op-grid-page--sidebar">
<turbo-frame [src]="turboFrameSidebarSrc()" [id]="turboFrameSidebarId()">
<turbo-frame
*ngIf="isLifeCyclesSidebarEnabled()"
[src]="lifeCyclesSidebarSrc()"
[id]="lifeCyclesSidebarId()"
>
<op-content-loader viewBox="0 0 100 100">
<svg:rect x="0" y="0" width="70" height="5" rx="1" />

<svg:rect x="75" y="0" width="25" height="5" rx="1" />

<svg:rect x="0" y="10" width="100" height="8" rx="1" />

<svg:rect x="0" y="25" width="100" height="12" rx="1" />
</op-content-loader>
</turbo-frame>

<turbo-frame
*ngIf="isCustomFieldsSidebarEnabled()"
[src]="projectCustomFieldsSidebarSrc()"
[id]="projectCustomFieldsSidebarId()"
>
<op-content-loader viewBox="0 0 100 100">
<svg:rect x="0" y="0" width="70" height="5" rx="1" />

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<%=
component_wrapper do
render(Primer::OpenProject::SidePanel::Section.new(
classes: 'op-project-custom-field-section-container',
test_selector: "project-custom-field-section-#{@project_custom_field_section.id}"
)) do |section|
section.with_title { "Life Cycles" }

if allowed_to_edit?
section.with_action_icon(
icon: :pencil,
tag: :a,
href: project_custom_field_section_dialog_path(project_id: @project.id, section_id: @project_custom_field_section.id),
data: {
controller: 'async-dialog'
},
test_selector: "project-custom-field-section-edit-button",
aria: { label: I18n.t(:label_edit) }
)
end

flex_layout do |details_container|
@project_custom_fields.each_with_index do |project_custom_field, i|
margin = i == @project_custom_fields.size - 1 ? 0 : 3
details_container.with_row(mb: margin) do
render(ProjectCustomFields::Sections::ProjectCustomFields::ShowComponent.new(
project_custom_field:,
project_custom_field_values: get_eager_loaded_project_custom_field_values_for(project_custom_field.id)
))
end
end
end
end
end
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#-- 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 ProjectLifeCycles
module Sections
class ShowComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable

def initialize(project:, project_custom_field_section:, project_custom_fields:)
super

@project = project
@project_custom_field_section = project_custom_field_section
@project_custom_fields = project_custom_fields

eager_load_project_custom_field_values
end

private

def allowed_to_edit?
User.current.allowed_in_project?(:edit_project_attributes, @project)
end

def eager_load_project_custom_field_values
# TODO: move to service
@eager_loaded_project_custom_field_values = CustomValue
.includes(custom_field: :custom_options)
.where(
custom_field_id: @project_custom_fields.pluck(:id),
customized_id: @project.id
)
.to_a
end

def get_eager_loaded_project_custom_field_values_for(custom_field_id)
@eager_loaded_project_custom_field_values.select { |pcfv| pcfv.custom_field_id == custom_field_id }
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<%=
component_wrapper do
if available_project_custom_fields_grouped_by_section.any?
render(Primer::OpenProject::SidePanel.new(
spacious: true,
test_selector: "project-life-cycles-sidebar-async-content"
)) do |panel|
available_project_custom_fields_grouped_by_section.each do |project_custom_field_section, project_custom_fields|
panel.with_section(
ProjectLifeCycles::Sections::ShowComponent.new(
project: @project,
project_custom_field_section:,
project_custom_fields: project_custom_fields
)
)
end
end
end
end
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#-- 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 ProjectLifeCycles
class SidePanelComponent < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable

def initialize(project:)
super

@project = project
end

private

def available_project_custom_fields_grouped_by_section
@available_project_custom_fields_grouped_by_section ||=
@project.available_custom_fields.group_by(&:project_custom_field_section)
end
end
end
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# -- 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 ::Overviews
class OverviewsController < ::Grids::BaseInProjectController
include OpTurbo::ComponentStream
@@ -16,6 +44,10 @@ def project_custom_fields_sidebar
render :project_custom_fields_sidebar, layout: false
end

def project_life_cycles_sidebar
render :project_life_cycles_sidebar, layout: false
end

def project_custom_field_section_dialog
respond_with_dialog(
ProjectCustomFields::Sections::EditDialogComponent.new(
@@ -63,9 +95,10 @@ def find_project_custom_field_section
end

def set_sidebar_enabled
@sidebar_enabled =
@custom_fields_sidebar_enabled =
User.current.allowed_in_project?(:view_project_attributes, @project) &&
@project.project_custom_fields.visible.any?
@life_cycles_sidebar_enabled = OpenProject::FeatureDecisions.stages_and_gates_active?
end

def handle_errors(project_with_errors, section)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<%#-- 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.

++#%>
<%= content_tag("turbo-frame", id: "project-life-cycles-sidebar") do %>
<%= render(ProjectLifeCycles::SidePanelComponent.new(project: @project)) %>
<% end %>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<% content_for :header_tags do %>
<meta name="sidebar_enabled" data-enabled="<%= @sidebar_enabled %>">
<meta name="custom_fields_sidebar_enabled" data-enabled="<%= @custom_fields_sidebar_enabled %>">
<meta name="life_cycles_sidebar_enabled" data-enabled="<%= @life_cycles_sidebar_enabled %>">
<% end -%>

<%=
3 changes: 3 additions & 0 deletions modules/overviews/config/routes.rb
Original file line number Diff line number Diff line change
@@ -9,5 +9,8 @@
as: :project_custom_field_section_dialog
put "projects/:project_id/update_project_custom_values/:section_id", to: "overviews/overviews#update_project_custom_values",
as: :update_project_custom_values

get "projects/:project_id/project_life_cycles_sidebar", to: "overviews/overviews#project_life_cycles_sidebar",
as: :project_life_cycles_sidebar
end
end
3 changes: 2 additions & 1 deletion modules/overviews/lib/overviews/engine.rb
Original file line number Diff line number Diff line change
@@ -47,7 +47,8 @@ class Engine < ::Rails::Engine
OpenProject::AccessControl.permission(:view_project)
.controller_actions
.push(
"overviews/overviews/show"
"overviews/overviews/show",
"overviews/overviews/project_life_cycles_sidebar"
dombesz marked this conversation as resolved.
Show resolved Hide resolved
)

OpenProject::AccessControl.permission(:view_project_attributes)
Loading