From 70e0c68f9ae86da39b871a01ebed5a8f6c55ead6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 21 Feb 2022 17:15:38 +0100 Subject: [PATCH 1/3] [41138] Add index action to team planner https://community.openproject.org/wp/41138 --- .../boards-index-page.component.html | 2 +- .../index-page/boards-index-page.component.ts | 1 + .../features/calendar/op-calendar.service.ts | 2 +- .../team-planner/team-planner.routes.ts | 4 +- .../spec/features/support/board_index_page.rb | 2 +- .../app/cells/team_planner/row_cell.rb | 76 ++++++++++ .../app/cells/team_planner/table_cell.rb | 48 ++++++ .../team_planner/team_planner_controller.rb | 40 +++-- .../team_planner/team_planner/index.html.erb | 17 ++- .../team_planner/team_planner/show.html.erb | 1 + modules/team_planner/config/locales/en.yml | 2 + modules/team_planner/config/routes.rb | 11 +- .../lib/open_project/team_planner/engine.rb | 11 +- .../spec/features/team_planner_index_spec.rb | 96 ++++++++++++ .../spec/features/team_planner_menu_spec.rb | 4 +- .../features/team_planner_navigation_spec.rb | 139 ------------------ .../spec/features/team_planner_spec.rb | 5 +- .../features/team_planner_split_view_spec.rb | 6 +- .../spec/routing/team_planner_routing_spec.rb | 29 +++- .../spec/support/pages/team_planner.rb | 2 +- 20 files changed, 320 insertions(+), 178 deletions(-) create mode 100644 modules/team_planner/app/cells/team_planner/row_cell.rb create mode 100644 modules/team_planner/app/cells/team_planner/table_cell.rb create mode 100644 modules/team_planner/app/views/team_planner/team_planner/show.html.erb create mode 100644 modules/team_planner/spec/features/team_planner_index_spec.rb delete mode 100644 modules/team_planner/spec/features/team_planner_navigation_spec.rb diff --git a/frontend/src/app/features/boards/index-page/boards-index-page.component.html b/frontend/src/app/features/boards/index-page/boards-index-page.component.html index 7e4f6cbbc9..5df41858e2 100644 --- a/frontend/src/app/features/boards/index-page/boards-index-page.component.html +++ b/frontend/src/app/features/boards/index-page/boards-index-page.component.html @@ -12,7 +12,7 @@ + [textContent]="text.create"> diff --git a/frontend/src/app/features/boards/index-page/boards-index-page.component.ts b/frontend/src/app/features/boards/index-page/boards-index-page.component.ts index bc2078ace2..833efb65cb 100644 --- a/frontend/src/app/features/boards/index-page/boards-index-page.component.ts +++ b/frontend/src/app/features/boards/index-page/boards-index-page.component.ts @@ -26,6 +26,7 @@ import { map } from 'rxjs/operators'; export class BoardsIndexPageComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { public text = { name: this.I18n.t('js.modals.label_name'), + create: this.I18n.t('js.button_create'), board: this.I18n.t('js.label_board'), boards: this.I18n.t('js.label_board_plural'), type: this.I18n.t('js.boards.label_board_type'), diff --git a/frontend/src/app/features/calendar/op-calendar.service.ts b/frontend/src/app/features/calendar/op-calendar.service.ts index 8a3ac3ed55..032f6b9269 100644 --- a/frontend/src/app/features/calendar/op-calendar.service.ts +++ b/frontend/src/app/features/calendar/op-calendar.service.ts @@ -159,7 +159,7 @@ export class OpCalendarService extends UntilDestroyedMixin { const endDate = moment(fetchInfo.end).format('YYYY-MM-DD'); let queryId:string|null = null; - if (this.urlParams.query_id) { + if (this.urlParams.query_id && this.urlParams.query_id !== 'new') { queryId = this.urlParams.query_id as string; } diff --git a/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts b/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts index 1438648553..4fbc0854f1 100644 --- a/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts +++ b/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts @@ -37,13 +37,13 @@ export const TEAM_PLANNER_ROUTES:Ng2StateDeclaration[] = [ { name: 'team_planner', parent: 'optional_project', - url: '/team_planner?query_id&query_props&cdate&cview', + url: '/team_planners/{query_id}?query_props&cdate&cview', redirectTo: 'team_planner.page', views: { '!$default': { component: WorkPackagesBaseComponent }, }, params: { - query_id: { type: 'query', dynamic: true }, + query_id: { type: 'query' }, cdate: { type: 'string', dynamic: true }, cview: { type: 'string', dynamic: true }, // Use custom encoder/decoder that ensures validity of URL string diff --git a/modules/boards/spec/features/support/board_index_page.rb b/modules/boards/spec/features/support/board_index_page.rb index 9e552d43de..b32f7adb0e 100644 --- a/modules/boards/spec/features/support/board_index_page.rb +++ b/modules/boards/spec/features/support/board_index_page.rb @@ -57,7 +57,7 @@ module Pages end def create_board(action: nil, expect_empty: false) - page.find('.toolbar-item a', text: 'Board').click + page.find('.toolbar-item a', text: I18n.t('js.button_create')).click if action == nil find('.tile-block-title', text: 'Basic').click diff --git a/modules/team_planner/app/cells/team_planner/row_cell.rb b/modules/team_planner/app/cells/team_planner/row_cell.rb new file mode 100644 index 0000000000..e3464808b9 --- /dev/null +++ b/modules/team_planner/app/cells/team_planner/row_cell.rb @@ -0,0 +1,76 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 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-2017 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 TeamPlanner + class RowCell < ::RowCell + include ApplicationHelper + include ::Redmine::I18n + + def query + model + end + + delegate :project, to: :query + + def name + link_to query.name, project_team_planner_path(project, query.id) + end + + def created_at + format_time(query.created_at) + end + + def assignees + query + .filters + .detect { |filter| filter.name == :assigned_to_id } + .then { |filter| filter ? filter.values.count : 0 } + end + + def button_links + [delete_link].compact + end + + def delete_link + if table.current_user.allowed_to?(:manage_team_planner, project) + link_to( + op_icon('icon icon-delete'), + project_team_planner_path(project, query.id), + method: :delete, + data: { + confirm: I18n.t(:text_are_you_sure), + 'qa-selector': "team-planner-remove-#{query.id}" + }, + title: t(:button_delete) + ) + end + end + end +end diff --git a/modules/team_planner/app/cells/team_planner/table_cell.rb b/modules/team_planner/app/cells/team_planner/table_cell.rb new file mode 100644 index 0000000000..ea1de4be6c --- /dev/null +++ b/modules/team_planner/app/cells/team_planner/table_cell.rb @@ -0,0 +1,48 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 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-2017 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 TeamPlanner + class TableCell < ::TableCell + options :current_user + columns :name, :assignees, :created_at + + def sortable? + false + end + + def headers + [ + ['name', { caption: I18n.t(:label_name) }], + ['assignees', { caption: I18n.t('team_planner.label_assignees') }], + ['created_at', { caption: I18n.t('attributes.created_at') }] + ] + end + end +end diff --git a/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb b/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb index 3eebbfcd44..8b8831b248 100644 --- a/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb +++ b/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb @@ -2,21 +2,35 @@ module ::TeamPlanner class TeamPlannerController < BaseController include EnterpriseTrialHelper before_action :find_optional_project - before_action :authorize, only: %i[index] + before_action :authorize before_action :require_ee_token, only: %i[index] - before_action :redirect_to_first_plan, only: :index + before_action :find_plan_view, only: %i[destroy] menu_item :team_planner_view def index + @views = visible_plans + end + + def show render layout: 'angular/angular' end def upsale; end + def destroy + if @view.destroy + flash[:notice] = t(:notice_successful_delete) + else + flash[:error] = t(:error_can_not_delete_entry) + end + + redirect_to action: :index + end + def require_ee_token unless EnterpriseToken.allows_to?(:team_planner_view) - redirect_to project_team_planner_upsale_path + redirect_to action: :upsale end end @@ -26,27 +40,21 @@ module ::TeamPlanner private - def redirect_to_first_plan - return unless @project - return if team_planner_query_params? - - if (query_id = find_existing_plan) - redirect_to action: :index, query_id: query_id - end + def find_plan_view + @view = Query + .visible(current_user) + .find(params[:id]) + rescue ActiveRecord::RecordNotFound + render_404 end - def find_existing_plan + def visible_plans Query .visible(current_user) .joins(:views) .where('views.type' => 'team_planner') .where('queries.project_id' => @project.id) .order('queries.name ASC') - .pick('queries.id') - end - - def team_planner_query_params? - params[:query_id] || params[:query_props] end end end diff --git a/modules/team_planner/app/views/team_planner/team_planner/index.html.erb b/modules/team_planner/app/views/team_planner/team_planner/index.html.erb index 43b8883cd5..e1f4de15fd 100644 --- a/modules/team_planner/app/views/team_planner/team_planner/index.html.erb +++ b/modules/team_planner/app/views/team_planner/team_planner/index.html.erb @@ -1 +1,16 @@ -<% html_title(t('team_planner.label_team_planner')) -%> +<% html_title(t('team_planner.label_team_planner_plural')) -%> + +<%= toolbar title: t(:'team_planner.label_team_planner_plural') do %> + <% if current_user.allowed_to?(:manage_team_planner, @project) %> +
  • + <%= link_to new_project_team_planners_path(@project), + class: 'button -alt-highlight', + title: t(:button_create) do %> + <%= op_icon('button--icon icon-add') %> + <%= t(:button_create) %> + <% end %> +
  • + <% end %> +<% end %> + +<%= rails_cell ::TeamPlanner::TableCell, @views, current_user: current_user %> \ No newline at end of file diff --git a/modules/team_planner/app/views/team_planner/team_planner/show.html.erb b/modules/team_planner/app/views/team_planner/team_planner/show.html.erb new file mode 100644 index 0000000000..309d396f8f --- /dev/null +++ b/modules/team_planner/app/views/team_planner/team_planner/show.html.erb @@ -0,0 +1 @@ +<% html_title(t('team_planner.label_team_planner')) -%> \ No newline at end of file diff --git a/modules/team_planner/config/locales/en.yml b/modules/team_planner/config/locales/en.yml index 6315be3bfe..1ffe91276e 100644 --- a/modules/team_planner/config/locales/en.yml +++ b/modules/team_planner/config/locales/en.yml @@ -6,6 +6,8 @@ en: team_planner: label_team_planner: "Team planner" + label_team_planner_plural: "Team planners" + label_assignees: "Assignees" upsale: title: "Team planner" description: "Team planner gives you a complete overview what each of team member is working on, one week at a time. Move, stretch and shrink work packages visually, and even drag them from one assignee to another to organise workload. You can even create new work packages or add existing ones, all from within team planner!" diff --git a/modules/team_planner/config/routes.rb b/modules/team_planner/config/routes.rb index e6f969994c..fe351bbdd0 100644 --- a/modules/team_planner/config/routes.rb +++ b/modules/team_planner/config/routes.rb @@ -1,6 +1,13 @@ OpenProject::Application.routes.draw do scope 'projects/:project_id', as: 'project' do - get '/team_planner/upsale', to: 'team_planner/team_planner#upsale', as: :team_planner_upsale - get '/team_planner(/*state)', to: 'team_planner/team_planner#index', as: :team_planner + resources :team_planners, + controller: 'team_planner/team_planner', + only: %i[index destroy], + as: :team_planners do + get :upsale, to: 'team_planner/team_planner#upsale', on: :collection, as: :upsale + + get '/new' => 'team_planner/team_planner#show', on: :collection, as: 'new' + get '(/*state)' => 'team_planner/team_planner#show', on: :member, as: '' + end end end diff --git a/modules/team_planner/lib/open_project/team_planner/engine.rb b/modules/team_planner/lib/open_project/team_planner/engine.rb index 253884362d..cfc5229c70 100644 --- a/modules/team_planner/lib/open_project/team_planner/engine.rb +++ b/modules/team_planner/lib/open_project/team_planner/engine.rb @@ -29,11 +29,11 @@ module OpenProject::TeamPlanner name: 'OpenProject Team Planner' do project_module :team_planner_view, dependencies: :work_package_tracking do permission :view_team_planner, - { 'team_planner/team_planner': %i[index upsale] }, + { 'team_planner/team_planner': %i[index show upsale] }, dependencies: %i[view_work_packages], contract_actions: { team_planner: %i[read] } permission :manage_team_planner, - { 'team_planner/team_planner': %i[index upsale] }, + { 'team_planner/team_planner': %i[index show new destroy upsale] }, dependencies: %i[view_team_planner add_work_packages edit_work_packages save_queries manage_public_queries], contract_actions: { team_planner: %i[create update destroy] } end @@ -41,10 +41,9 @@ module OpenProject::TeamPlanner menu :project_menu, :team_planner_view, { controller: '/team_planner/team_planner', action: :index }, - caption: :'team_planner.label_team_planner', + caption: :'team_planner.label_team_planner_plural', after: :work_packages, - icon: 'icon2 icon-calendar', - badge: 'label_menu_badge.pre_alpha' + icon: 'icon2 icon-calendar' menu :project_menu, :team_planner_menu, @@ -52,7 +51,7 @@ module OpenProject::TeamPlanner parent: :team_planner_view, partial: 'team_planner/team_planner/menu', last: true, - caption: :'team_planner.label_team_planner' + caption: :'team_planner.label_team_planner_plural' end add_view :TeamPlanner, diff --git a/modules/team_planner/spec/features/team_planner_index_spec.rb b/modules/team_planner/spec/features/team_planner_index_spec.rb new file mode 100644 index 0000000000..7cd138e21a --- /dev/null +++ b/modules/team_planner/spec/features/team_planner_index_spec.rb @@ -0,0 +1,96 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 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. +#++ + +require 'spec_helper' +require_relative './shared_context' + +describe 'Team planner index', type: :feature, js: true, with_ee: %i[team_planner_view] do + include_context 'with team planner full access' + + let(:current_user) { user } + let(:query) { create :query, user: user, project: project, public: true } + let(:team_plan) { create :view_team_planner, query: query } + + before do + login_as current_user + team_plan + visit project_team_planners_path(project) + end + + context 'with no view' do + let(:team_plan) { nil } + + it 'shows an index action' do + expect(page).to have_text 'There is currently nothing to display.' + expect(page).to have_selector '.button', text: 'Create' + end + end + + context 'with an existing view' do + it 'shows that view' do + expect(page).to have_selector 'td', text: query.name + expect(page).to have_selector "[data-qa-selector='team-planner-remove-#{query.id}']" + end + + context 'with another user with limited access' do + let(:current_user) do + create :user, + firstname: 'Bernd', + member_in_project: project, + member_with_permissions: %w[view_work_packages view_team_planner] + end + + it 'does not show the create button' do + expect(page).to have_selector 'td', text: query.name + + # Does not show the delete + expect(page).to have_no_selector "[data-qa-selector='team-planner-remove-#{query.id}']" + + # Does not show the create button + expect(page).to have_no_selector '.button', text: 'Create' + end + + context 'when the view is non-public' do + let(:query) { create :query, user: user, project: project, public: false } + + it 'does not show a non-public view' do + expect(page).to have_text 'There is currently nothing to display.' + expect(page).to have_no_selector 'td', text: query.name + + # Does not show the delete + expect(page).to have_no_selector "[data-qa-selector='team-planner-remove-#{query.id}']" + + # Does not show the create button + expect(page).to have_no_selector '.button', text: 'Create' + end + end + end + end +end diff --git a/modules/team_planner/spec/features/team_planner_menu_spec.rb b/modules/team_planner/spec/features/team_planner_menu_spec.rb index 93cc75137d..dc4099247f 100644 --- a/modules/team_planner/spec/features/team_planner_menu_spec.rb +++ b/modules/team_planner/spec/features/team_planner_menu_spec.rb @@ -53,7 +53,7 @@ describe 'Team planner sidemenu', type: :feature, js: true do visit project_path(project) within '#main-menu' do - click_link 'Team planner' + click_link 'Team planners' end query_menu.expect_menu_entry_not_visible('Create new planner') @@ -76,7 +76,7 @@ describe 'Team planner sidemenu', type: :feature, js: true do visit project_path(project) within '#main-menu' do - click_link 'Team planner' + click_link 'Team planners' end query_menu.expect_menu_entry('Create new planner') diff --git a/modules/team_planner/spec/features/team_planner_navigation_spec.rb b/modules/team_planner/spec/features/team_planner_navigation_spec.rb deleted file mode 100644 index 5a8d70db98..0000000000 --- a/modules/team_planner/spec/features/team_planner_navigation_spec.rb +++ /dev/null @@ -1,139 +0,0 @@ -#-- encoding: UTF-8 - -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) 2012-2021 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. -#++ - -require 'spec_helper' -require_relative './shared_context' - -describe 'Team planner', type: :feature, js: true, with_ee: %i[team_planner_view] do - include_context 'with team planner full access' - - # We need a default status - shared_let(:default_status) { create :default_status } - - it 'redirects to the first active plan' do - visit project_path(project) - - within '#main-menu' do - click_link 'Team planner' - end - - team_planner.expect_title - team_planner.save_as 'Foobar' - - visit project_path(project) - - within '#main-menu' do - click_link 'Team planner' - end - - query = View.where(type: 'team_planner').last.query - expect(query.name).to eq 'Foobar' - - expect(page).to have_current_path /query_id=#{query.id}/ - end - - shared_examples 'loads a new team planner' do - it do - visit project_path(project) - - within '#main-menu' do - click_link 'Team planner' - end - - team_planner.expect_title - expect(page).to have_no_current_path /query_id=#{query.id}/ - end - end - - shared_examples 'loads the query view' do - it do - visit project_path(project) - - within '#main-menu' do - click_link 'Team planner' - end - - team_planner.expect_title query.name - expect(page).to have_current_path /query_id=#{query.id}/ - end - end - - context 'with an existing saved plan' do - shared_let(:other_user) { create :user } - let!(:view) { create :view_team_planner, query: query } - - context 'when the query is from another user and private' do - let!(:query) { create :query, user: other_user, project: project, public: false } - - it_behaves_like 'loads a new team planner' - end - - context 'when the query is from another user and public' do - let!(:query) { create :query, user: other_user, project: project, public: true } - - it_behaves_like 'loads the query view' - end - - context 'when the query is from the same user and private' do - let!(:query) { create :query, user: user, project: project, public: false } - - it_behaves_like 'loads the query view' - end - end - - context 'with an existing plan and creating a new one' do - let!(:view) { create :view_team_planner, query: query } - let!(:query) { create :query, user: user, project: project, public: true } - - it 'allows to reload with query props active' do - team_planner.visit! - - team_planner.expect_assignee(user, present: false) - - click_on 'Create new planner' - team_planner.expect_assignee(user, present: false) - - retry_block do - team_planner.click_add_user - page.find('[data-qa-selector="tp-add-assignee"] input') - team_planner.select_user_to_add user.name - end - - team_planner.expect_assignee(user) - - page.driver.refresh - - expect(page).to have_current_path /query_props=/ - expect(page).to have_current_path /cview=/ - - team_planner.expect_assignee(user) - end - end -end diff --git a/modules/team_planner/spec/features/team_planner_spec.rb b/modules/team_planner/spec/features/team_planner_spec.rb index c323b635e4..1e72fe4e6d 100644 --- a/modules/team_planner/spec/features/team_planner_spec.rb +++ b/modules/team_planner/spec/features/team_planner_spec.rb @@ -42,9 +42,12 @@ describe 'Team planner', type: :feature, js: true do visit project_path(project) within '#main-menu' do - click_link 'Team planner' + click_link 'Team planners' end + expect(page).to have_content 'There is currently nothing to display.' + click_on 'Create' + team_planner.expect_title filters.expect_filter_count("1") diff --git a/modules/team_planner/spec/features/team_planner_split_view_spec.rb b/modules/team_planner/spec/features/team_planner_split_view_spec.rb index 4054b1a0ff..6ea69ab550 100644 --- a/modules/team_planner/spec/features/team_planner_split_view_spec.rb +++ b/modules/team_planner/spec/features/team_planner_split_view_spec.rb @@ -68,14 +68,14 @@ describe 'Team planner split view navigation', type: :feature, js: true, with_ee # Expect clicking on a work package does not open the details page.find('[data-qa-selector="op-wp-single-card--content-subject"]', text: work_package1.subject).click - expect(page).to have_no_current_path /team_planner\/details\/#{work_package1.id}/ + expect(page).to have_no_current_path /team_planners\/new\/details\/#{work_package1.id}/ # Open split view through info icon team_planner.open_split_view_by_info_icon work_package1 - expect(page).to have_current_path /team_planner\/details\/#{work_package1.id}/ + expect(page).to have_current_path /team_planners\/new\/details\/#{work_package1.id}/ # now clicking on another card switches page.find('[data-qa-selector="op-wp-single-card--content-subject"]', text: work_package2.subject).click - expect(page).to have_current_path /team_planner\/details\/#{work_package2.id}/ + expect(page).to have_current_path /team_planners\/new\/details\/#{work_package2.id}/ end end diff --git a/modules/team_planner/spec/routing/team_planner_routing_spec.rb b/modules/team_planner/spec/routing/team_planner_routing_spec.rb index cc9beb9e9c..7104844183 100644 --- a/modules/team_planner/spec/routing/team_planner_routing_spec.rb +++ b/modules/team_planner/spec/routing/team_planner_routing_spec.rb @@ -31,7 +31,32 @@ require 'spec_helper' describe 'Team planner routing', type: :routing do it 'routes to team_planner#index' do expect(subject) - .to route(:get, '/projects/foobar/team_planner/state') - .to(controller: 'team_planner/team_planner', action: 'index', project_id: 'foobar', state: 'state') + .to route(:get, '/projects/foobar/team_planners') + .to(controller: 'team_planner/team_planner', action: :index, project_id: 'foobar') + end + + it 'routes to team_planner#show' do + expect(subject) + .to route(:get, '/projects/foobar/team_planners/1234') + .to(controller: 'team_planner/team_planner', action: :show, project_id: 'foobar', id: '1234') + end + + it 'routes to team_planner#new' do + expect(subject) + .to route(:get, '/projects/foobar/team_planners/new') + .to(controller: 'team_planner/team_planner', action: :show, project_id: 'foobar') + end + + it 'routes to team_planner#show with state' do + expect(subject) + .to route(:get, '/projects/foobar/team_planners/1234/details/555') + .to(controller: 'team_planner/team_planner', action: :show, project_id: 'foobar', id: '1234', + state: 'details/555') + end + + it 'routes to team_planner#destroy' do + expect(subject) + .to route(:delete, '/projects/foobar/team_planners/1234') + .to(controller: 'team_planner/team_planner', action: :destroy, project_id: 'foobar', id: '1234') end end diff --git a/modules/team_planner/spec/support/pages/team_planner.rb b/modules/team_planner/spec/support/pages/team_planner.rb index 88dbfbef07..5f03ce9b87 100644 --- a/modules/team_planner/spec/support/pages/team_planner.rb +++ b/modules/team_planner/spec/support/pages/team_planner.rb @@ -42,7 +42,7 @@ module Pages end def path - project_team_planner_path(project) + new_project_team_planners_path(project) end def expect_title(title = 'Unnamed team planner') From 94595e908c1dc31079a49f2cd8ef91ca65e75436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Tue, 22 Feb 2022 07:42:12 +0100 Subject: [PATCH 2/3] Identify "new" query path as an empty query_id --- frontend/src/app/core/routing/openproject.routes.ts | 12 ++++++++++++ .../src/app/features/calendar/op-calendar.service.ts | 8 +++++--- .../team-planner/team-planner/team-planner.routes.ts | 4 ++-- .../op-view-select/op-static-queries.service.ts | 2 +- .../op-view-select/op-view-select.component.ts | 2 +- .../spec/features/team_planner_index_spec.rb | 12 ++++++++++++ 6 files changed, 33 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/core/routing/openproject.routes.ts b/frontend/src/app/core/routing/openproject.routes.ts index 10cc37d808..b7e64375bd 100644 --- a/frontend/src/app/core/routing/openproject.routes.ts +++ b/frontend/src/app/core/routing/openproject.routes.ts @@ -208,6 +208,18 @@ export function uiRouterConfiguration(uiRouter:UIRouter, injector:Injector, modu equals: (a:any, b:any) => _.isEqual(a, b), }, ); + + uiRouter.urlService.config.type( + 'opQueryId', + { + encode: (id:string|null) => id || 'new', + decode: (id:string) => (id === 'new' ? null : id), + raw: true, + dynamic: true, + is: (val:unknown) => typeof (val) === 'string', + equals: (a:unknown, b:unknown) => _.isEqual(a, b), + }, + ); } export function initializeUiRouterListeners(injector:Injector) { diff --git a/frontend/src/app/features/calendar/op-calendar.service.ts b/frontend/src/app/features/calendar/op-calendar.service.ts index 032f6b9269..218bcf34c4 100644 --- a/frontend/src/app/features/calendar/op-calendar.service.ts +++ b/frontend/src/app/features/calendar/op-calendar.service.ts @@ -159,7 +159,7 @@ export class OpCalendarService extends UntilDestroyedMixin { const endDate = moment(fetchInfo.end).format('YYYY-MM-DD'); let queryId:string|null = null; - if (this.urlParams.query_id && this.urlParams.query_id !== 'new') { + if (this.urlParams.query_id) { queryId = this.urlParams.query_id as string; } @@ -314,14 +314,16 @@ export class OpCalendarService extends UntilDestroyedMixin { } private get initializingWithQuery():boolean { - return (this.areFiltersEmpty && this.urlParams.query_id && !this.urlParams.query_props) as boolean; + return this.areFiltersEmpty + && !!this.urlParams.query_id + && !this.urlParams.query_props; } private get urlParams() { return this.uiRouterGlobals.params; } - private get areFiltersEmpty() { + private get areFiltersEmpty():boolean { return this.wpTableFilters.isEmpty; } diff --git a/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts b/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts index 4fbc0854f1..e91bbf147a 100644 --- a/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts +++ b/frontend/src/app/features/team-planner/team-planner/team-planner.routes.ts @@ -37,13 +37,13 @@ export const TEAM_PLANNER_ROUTES:Ng2StateDeclaration[] = [ { name: 'team_planner', parent: 'optional_project', - url: '/team_planners/{query_id}?query_props&cdate&cview', + url: '/team_planners/:query_id?query_props&cdate&cview', redirectTo: 'team_planner.page', views: { '!$default': { component: WorkPackagesBaseComponent }, }, params: { - query_id: { type: 'query' }, + query_id: { type: 'opQueryId', dynamic: true }, cdate: { type: 'string', dynamic: true }, cview: { type: 'string', dynamic: true }, // Use custom encoder/decoder that ensures validity of URL string diff --git a/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts b/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts index 9bf0d43872..a8d42cfb00 100644 --- a/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts +++ b/frontend/src/app/shared/components/op-view-select/op-static-queries.service.ts @@ -194,7 +194,7 @@ export class StaticQueriesService { title: this.text.create_new_team_planner, uiSref: 'team_planner.page.show', uiParams: { - query_id: '', + query_id: null, query_props: '', }, view: 'TeamPlanner', diff --git a/frontend/src/app/shared/components/op-view-select/op-view-select.component.ts b/frontend/src/app/shared/components/op-view-select/op-view-select.component.ts index 12df6454b1..c2903ce42e 100644 --- a/frontend/src/app/shared/components/op-view-select/op-view-select.component.ts +++ b/frontend/src/app/shared/components/op-view-select/op-view-select.component.ts @@ -55,7 +55,7 @@ import { ApiV3ListParameters } from 'core-app/core/apiv3/paths/apiv3-list-resour import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -export type ViewType = 'WorkPackagesTable'|'Bim'|'TeamPlanner'; +export type ViewType = 'WorkPackagesTable'|'Bim'|'TeamPlanner'|'WorkPackagesCalendar'; export const opViewSelectSelector = 'op-view-select'; diff --git a/modules/team_planner/spec/features/team_planner_index_spec.rb b/modules/team_planner/spec/features/team_planner_index_spec.rb index 7cd138e21a..ee5441cbe8 100644 --- a/modules/team_planner/spec/features/team_planner_index_spec.rb +++ b/modules/team_planner/spec/features/team_planner_index_spec.rb @@ -51,6 +51,18 @@ describe 'Team planner index', type: :feature, js: true, with_ee: %i[team_planne expect(page).to have_text 'There is currently nothing to display.' expect(page).to have_selector '.button', text: 'Create' end + + it 'can create an action through the sidebar' do + click_on 'Create new planner' + + team_planner.expect_title + + # Also works from the frontend + click_on 'Create new planner' + + team_planner.expect_no_toaster + team_planner.expect_title + end end context 'with an existing view' do From 03cb319c18fb8c7e41c91b9d51de0199aae82b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 28 Feb 2022 10:05:12 +0100 Subject: [PATCH 3/3] Fix upsale routes with new show action --- .../team_planner/team_planner_controller.rb | 2 +- .../spec/features/team_planner_upsale_spec.rb | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 modules/team_planner/spec/features/team_planner_upsale_spec.rb diff --git a/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb b/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb index 8b8831b248..c230a21af5 100644 --- a/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb +++ b/modules/team_planner/app/controllers/team_planner/team_planner_controller.rb @@ -3,7 +3,7 @@ module ::TeamPlanner include EnterpriseTrialHelper before_action :find_optional_project before_action :authorize - before_action :require_ee_token, only: %i[index] + before_action :require_ee_token, except: %i[upsale] before_action :find_plan_view, only: %i[destroy] menu_item :team_planner_view diff --git a/modules/team_planner/spec/features/team_planner_upsale_spec.rb b/modules/team_planner/spec/features/team_planner_upsale_spec.rb new file mode 100644 index 0000000000..65b3534ebc --- /dev/null +++ b/modules/team_planner/spec/features/team_planner_upsale_spec.rb @@ -0,0 +1,56 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 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. +#++ + +require 'spec_helper' +require_relative './shared_context' + +describe 'Team planner index', type: :feature, js: true do + include_context 'with team planner full access' + + let(:current_user) { user } + + before do + login_as current_user + end + + it 'redirects routes to upsale' do + visit project_team_planners_path(project) + + expect(page).to have_text 'Upgrade now' + + click_on 'Create new planner' + + expect(page).to have_text 'Upgrade now' + + visit project_team_planner_path(project, id: 'new') + + expect(page).to have_text 'Upgrade now' + end +end