From e049cb03a48b14ddde9e7b3e65f8a955144b9fbb Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Wed, 26 Jun 2024 14:03:59 +0200 Subject: [PATCH 1/5] Replace angular sidemenu with rails based Submenu component for WorkPackages module --- .../work_packages/menus_controller.rb | 37 ++++ app/menus/submenu.rb | 2 +- app/menus/work_packages/menu.rb | 79 ++++++++ .../default_query_generator_service.rb | 180 ++++++++++++++++++ .../work_packages/_menu_query_select.html.erb | 12 -- app/views/work_packages/menus/_menu.html.erb | 5 + app/views/work_packages/menus/show.html.erb | 3 + config/initializers/menus.rb | 4 +- config/initializers/permissions.rb | 3 +- config/locales/js-en.yml | 1 + config/routes.rb | 3 + .../routing/work-packages-routes.ts | 15 +- 12 files changed, 327 insertions(+), 17 deletions(-) create mode 100644 app/controllers/work_packages/menus_controller.rb create mode 100644 app/menus/work_packages/menu.rb create mode 100644 app/services/work_packages/default_query_generator_service.rb delete mode 100644 app/views/work_packages/_menu_query_select.html.erb create mode 100644 app/views/work_packages/menus/_menu.html.erb create mode 100644 app/views/work_packages/menus/show.html.erb diff --git a/app/controllers/work_packages/menus_controller.rb b/app/controllers/work_packages/menus_controller.rb new file mode 100644 index 000000000000..7c5061c9513c --- /dev/null +++ b/app/controllers/work_packages/menus_controller.rb @@ -0,0 +1,37 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-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 WorkPackages + class MenusController < ApplicationController + before_action :load_and_authorize_in_optional_project + + def show + @sidebar_menu_items = WorkPackages::Menu.new(project: @project, params:).menu_items + render layout: nil + end + end +end diff --git a/app/menus/submenu.rb b/app/menus/submenu.rb index 0cdbcbbb8611..e35b092cb48d 100644 --- a/app/menus/submenu.rb +++ b/app/menus/submenu.rb @@ -102,7 +102,7 @@ def selected?(query_params) end end - if query_params.empty? && params[:filters].present? + if query_params.empty? && (%i[filters query_props query_id name].any? { |k| params.key? k }) return false end diff --git a/app/menus/work_packages/menu.rb b/app/menus/work_packages/menu.rb new file mode 100644 index 000000000000..64ae6cbcc314 --- /dev/null +++ b/app/menus/work_packages/menu.rb @@ -0,0 +1,79 @@ +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2010-2023 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 + class Menu < Submenu + attr_reader :view_type, :project, :params + + def initialize(project: nil, params: nil) + @view_type = "work_packages_table" + + super(view_type:, project:, params:) + end + + def default_queries + query_generator = WorkPackages::DefaultQueryGeneratorService.new(with_project: project) + WorkPackages::DefaultQueryGeneratorService::QUERY_OPTIONS.filter_map do |query_key| + params = query_generator.call(query_key:) + next if params.nil? + + menu_item( + I18n.t("js.work_packages.default_queries.#{query_key}"), + params + ) + end + end + + def query_path(query_params) + if !EnterpriseToken.allows_to?(:work_package_sharing) && + %i[shared_with_users shared_with_me].any?(query_params[:name]) + return ee_upsale_path(query_params) + end + + if project.present? + return report_project_work_packages_path(project, { name: query_params[:name] }) if query_params[:name] == :summary + + project_work_packages_path(project, query_params) + else + work_packages_path(query_params) + end + end + + def selected?(query_params) + # Special rules, as those are redirected to completely new pages where only the name parameter is preserved + return true if query_params[:name] == :summary && params[:name] == "summary" + return true if query_params[:name] == :shared_with_me && params[:name] == "shared_with_me" + return true if query_params[:name] == :shared_with_users && params[:name] == "shared_with_users" + + super + end + + def ee_upsale_path(query_params) + share_upsale_work_packages_path({ name: query_params[:name] }) + end + end +end diff --git a/app/services/work_packages/default_query_generator_service.rb b/app/services/work_packages/default_query_generator_service.rb new file mode 100644 index 000000000000..0a86b78cc255 --- /dev/null +++ b/app/services/work_packages/default_query_generator_service.rb @@ -0,0 +1,180 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-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 ::WorkPackages + class DefaultQueryGeneratorService + DEFAULT_QUERY = :all_open + QUERY_OPTIONS = [ + DEFAULT_QUERY, + :latest_activity, + :recently_created, + :overdue, + :summary, + :created_by_me, + :assigned_to_me, + :shared_with_users, + :shared_with_me + ].freeze + + DEFAULT_PARAMS = + { + g: "", + hi: false, + t: "updatedAt:desc,id:asc" + }.freeze + + attr_reader :project + + def initialize(with_project:) + @project = with_project + end + + def call(query_key: DEFAULT_QUERY) + return {} if query_key == DEFAULT_QUERY + + params = self.class.assign_params(query_key, project) + + return if params.nil? + + { query_props: params.to_json, name: query_key } + end + + class << self + def all_open_query + {} + end + + def latest_activity_query + DEFAULT_PARAMS.merge( + { + c: %w[id subject type status assignee updatedAt], + t: "updatedAt:desc", + f: [{ "n" => "status", "o" => "*", "v" => [] }] + } + ) + end + + def recently_created_query + DEFAULT_PARAMS.merge( + { + c: %w[id subject type status assignee createdAt], + t: "createdAt:desc", + f: [{ "n" => "status", "o" => "o", "v" => [] }] + } + ) + end + + def overdue_query + DEFAULT_PARAMS.merge( + { + c: %w[id type subject status startDate dueDate duration], + t: "createdAt:desc", + f: [{ "n" => "dueDate", "o" => " ["1"] }, + { "n" => "status", "o" => "o", "v" => [] }] + } + ) + end + + def summary_query + {} + end + + def created_by_me_query + DEFAULT_PARAMS.merge( + { + c: %w[id subject type status assignee updatedAt], + f: [{ "n" => "status", "o" => "o", "v" => [] }, + { "n" => "author", "o" => "=", "v" => ["me"] }] + } + ) + end + + def assigned_to_me_query + DEFAULT_PARAMS.merge( + { + c: %w[id subject type status author updatedAt], + f: [{ "n" => "status", "o" => "o", "v" => [] }, + { "n" => "assigneeOrGroup", "o" => "=", "v" => ["me"] }] + } + ) + end + + def shared_with_users_query + DEFAULT_PARAMS.merge( + { + c: %w[id subject type project sharedWithUsers], + f: [{ "n" => "sharedWithUser", "o" => "*", "v" => [] }] + } + ) + end + + def shared_with_me_query + DEFAULT_PARAMS.merge( + { + c: %w[id subject type project], + f: [{ "n" => "sharedWithMe", "o" => "=", "v" => "t" }] + } + ) + end + + def assign_params(query_key, project) + case query_key + when DEFAULT_QUERY + all_open_query + when :latest_activity + latest_activity_query + when :recently_created + recently_created_query + when :overdue + overdue_query + when :summary + return if project.blank? + + summary_query + else + return unless User.current.logged? + + user_specific_queries(query_key) + end + end + + def user_specific_queries(query_key) + case query_key + when :created_by_me + created_by_me_query + when :assigned_to_me + assigned_to_me_query + when :shared_with_users + shared_with_users_query + when :shared_with_me + shared_with_me_query + end + end + end + end +end diff --git a/app/views/work_packages/_menu_query_select.html.erb b/app/views/work_packages/_menu_query_select.html.erb deleted file mode 100644 index 3713f99ad8a7..000000000000 --- a/app/views/work_packages/_menu_query_select.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -
- <%= - angular_component_tag 'op-view-select', - inputs: { - projectId: (@project ? @project.id.to_s : ''), - menuItems: [parent_name, name], - baseRoute: 'work-packages', - viewType: 'WorkPackagesTable', - }, - class: 'op-sidebar--body' - %> -
diff --git a/app/views/work_packages/menus/_menu.html.erb b/app/views/work_packages/menus/_menu.html.erb new file mode 100644 index 000000000000..6391327f2b94 --- /dev/null +++ b/app/views/work_packages/menus/_menu.html.erb @@ -0,0 +1,5 @@ +<%= turbo_frame_tag "work_packages_sidemenu", + src: @project ? menu_project_work_packages_path(@project, params.permit(:query_props, :query_id, :name)) : work_packages_menu_path(params.permit(:query_props, :query_id, :name)), + target: "_top", + data: { turbo: false }, + loading: :lazy %> diff --git a/app/views/work_packages/menus/show.html.erb b/app/views/work_packages/menus/show.html.erb new file mode 100644 index 000000000000..dffc0df93003 --- /dev/null +++ b/app/views/work_packages/menus/show.html.erb @@ -0,0 +1,3 @@ +<%= turbo_frame_tag "work_packages_sidemenu" do %> + <%= render OpenProject::Common::SubmenuComponent.new(sidebar_menu_items: @sidebar_menu_items, searchable: true) %> +<% end %> diff --git a/config/initializers/menus.rb b/config/initializers/menus.rb index 86a096fffee9..66fb06440750 100644 --- a/config/initializers/menus.rb +++ b/config/initializers/menus.rb @@ -168,7 +168,7 @@ menu.push :work_packages_query_select, { controller: "/work_packages", action: "index" }, parent: :work_packages, - partial: "work_packages/menu_query_select" + partial: "work_packages/menus/menu" # News menu.push :news, @@ -580,7 +580,7 @@ menu.push :work_packages_query_select, { controller: "/work_packages", action: "index" }, parent: :work_packages, - partial: "work_packages/menu_query_select", + partial: "work_packages/menus/menu", last: true, caption: :label_all_open_wps diff --git a/config/initializers/permissions.rb b/config/initializers/permissions.rb index 8b4ab0de9719..710a479b7a7b 100644 --- a/config/initializers/permissions.rb +++ b/config/initializers/permissions.rb @@ -204,7 +204,8 @@ journals: %i[index], work_packages: %i[show index], work_packages_api: [:get], - "work_packages/reports": %i[report report_details] + "work_packages/reports": %i[report report_details], + "work_packages/menus": %i[show] }, permissible_on: %i[work_package project], contract_actions: { work_packages: %i[read] } diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index 3ab5a7eaa6d9..64fc797b90c7 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -1110,6 +1110,7 @@ en: assigned_to_me: "Assigned to me" recently_created: "Recently created" all_open: "All open" + overdue: "Overdue" summary: "Summary" shared_with_users: "Shared with users" shared_with_me: "Shared with me" diff --git a/config/routes.rb b/config/routes.rb index 10d63e1decb6..4b9e96270364 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -300,6 +300,7 @@ collection do get "/report/:detail" => "work_packages/reports#report_details" get "/report" => "work_packages/reports#report" + get "menu" => "work_packages/menus#show" end # states managed by client-side routing on work_package#index @@ -530,6 +531,8 @@ end namespace :work_packages do + get "menu" => "menus#show" + match "auto_complete" => "auto_completes#index", via: %i[get post] resource :bulk, controller: "bulk", only: %i[edit update destroy] # FIXME: this is kind of evil!! We need to remove this soonest and diff --git a/frontend/src/app/features/work-packages/routing/work-packages-routes.ts b/frontend/src/app/features/work-packages/routing/work-packages-routes.ts index e6ca08b2dc71..0d5b7526bc35 100644 --- a/frontend/src/app/features/work-packages/routing/work-packages-routes.ts +++ b/frontend/src/app/features/work-packages/routing/work-packages-routes.ts @@ -40,12 +40,17 @@ import { KeepTabService } from 'core-app/features/work-packages/components/wp-si import { ShareUpsaleComponent } from 'core-app/features/enterprise/share-upsale/share-upsale.component'; export const menuItemClass = 'work-packages-menu-item'; +export const sidemenuId = 'work_packages_sidemenu'; +export const sideMenuOptions = { + sidemenuId, + hardReloadOnBaseRoute: true, +}; export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ { name: 'work-packages', parent: 'optional_project', - url: '/work_packages?query_id&query_props&start_onboarding_tour', + url: '/work_packages?query_id&query_props&name&start_onboarding_tour', redirectTo: 'work-packages.partitioned.list', views: { '!$default': { component: WorkPackagesBaseComponent }, @@ -53,6 +58,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ data: { bodyClasses: 'router--work-packages-base', menuItem: menuItemClass, + sideMenuOptions, }, params: { query_id: { type: 'query', dynamic: true }, @@ -60,6 +66,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ query_props: { type: 'opQueryString' }, // Optional initial tour param start_onboarding_tour: { type: 'query', squash: true, value: undefined }, + name: { type: 'string', dynamic: true }, }, }, { @@ -78,6 +85,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ bodyClasses: 'router--work-packages-full-create', menuItem: menuItemClass, successState: 'work-packages.show', + sideMenuOptions, }, }, { @@ -90,6 +98,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ allowMovingInEditMode: true, bodyClasses: 'router--work-packages-full-create', menuItem: menuItemClass, + sideMenuOptions, }, }, { @@ -111,6 +120,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ bodyClasses: ['router--work-packages-full-view', 'router--work-packages-base'], newRoute: 'work-packages.new', menuItem: menuItemClass, + sideMenuOptions, }, }, { @@ -120,6 +130,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ data: { parent: 'work-packages.show', menuItem: menuItemClass, + sideMenuOptions, }, }, { @@ -129,6 +140,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ data: { // This has to be empty to avoid inheriting the parent bodyClasses bodyClasses: '', + sideMenuOptions, }, }, { @@ -142,6 +154,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ bodyClasses: ['router--work-packages-partitioned-split-view', 'router--work-packages-base'], menuItem: menuItemClass, partition: '-left-only', + sideMenuOptions, }, }, ...makeSplitViewRoutes( From b3cdcec8da642a4c9fb5a8e7c7d3a68faa93f0f2 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Mon, 1 Jul 2024 09:10:40 +0200 Subject: [PATCH 2/5] Remove `op-view-select` component as it was not completely replaced by the Rails SubmenuComponent. Further the static-queries.service was reduced as the static queries are now also build by the Rails SubmenuComponent --- .../setup/global-dynamic-components.const.ts | 5 - .../op-static-queries.service.ts | 182 +------------ .../op-view-select.component.ts | 244 ------------------ .../op-view-select.template.html | 28 -- frontend/src/app/shared/shared.module.ts | 5 - 5 files changed, 2 insertions(+), 462 deletions(-) delete mode 100644 frontend/src/app/shared/components/op-view-select/op-view-select.component.ts delete mode 100644 frontend/src/app/shared/components/op-view-select/op-view-select.template.html diff --git a/frontend/src/app/core/setup/global-dynamic-components.const.ts b/frontend/src/app/core/setup/global-dynamic-components.const.ts index a309bca94dee..eeaa5c739538 100644 --- a/frontend/src/app/core/setup/global-dynamic-components.const.ts +++ b/frontend/src/app/core/setup/global-dynamic-components.const.ts @@ -69,10 +69,6 @@ import { WorkPackageOverviewGraphComponent, wpOverviewGraphSelector, } from 'core-app/shared/components/work-package-graphs/overview/wp-overview-graph.component'; -import { - opViewSelectSelector, - ViewSelectComponent, -} from 'core-app/shared/components/op-view-select/op-view-select.component'; import { GlobalSearchTitleComponent, globalSearchTitleSelector, @@ -201,7 +197,6 @@ export const globalDynamicComponents:OptionalBootstrapDefinition[] = [ { selector: enterpriseActiveSavedTrialSelector, cls: EEActiveSavedTrialComponent }, { selector: headerProjectSelectSelector, cls: OpHeaderProjectSelectComponent }, { selector: wpOverviewGraphSelector, cls: WorkPackageOverviewGraphComponent }, - { selector: opViewSelectSelector, cls: ViewSelectComponent }, { selector: triggerActionsEntryComponentSelector, cls: TriggerActionsEntryComponent, embeddable: true }, { selector: editableQueryPropsSelector, cls: EditableQueryPropsComponent }, { selector: backupSelector, cls: BackupComponent }, 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 51566ed8c9d0..49a88d2e29a2 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 @@ -29,70 +29,25 @@ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Injectable } from '@angular/core'; -import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { StateService } from '@uirouter/core'; -import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; -import { IOpSidemenuItem } from 'core-app/shared/components/sidemenu/sidemenu.component'; -import { ViewType } from 'core-app/shared/components/op-view-select/op-view-select.component'; -import { BannersService } from 'core-app/core/enterprise/banners.service'; - -interface IStaticQuery extends IOpSidemenuItem { - view:ViewType; -} @Injectable() export class StaticQueriesService { - private staticQueries:IStaticQuery[] = []; - constructor( private readonly I18n:I18nService, private readonly $state:StateService, - private readonly CurrentProject:CurrentProjectService, - private readonly PathHelper:PathHelperService, - private readonly CurrentUser:CurrentUserService, - private readonly bannersService:BannersService, ) { - this.staticQueries = this.buildQueries(); } public text = { - assignee: this.I18n.t('js.work_packages.properties.assignee'), - author: this.I18n.t('js.work_packages.properties.author'), - created_at: this.I18n.t('js.work_packages.properties.createdAt'), - updated_at: this.I18n.t('js.work_packages.properties.updatedAt'), - status: this.I18n.t('js.work_packages.properties.status'), work_packages: this.I18n.t('js.label_work_package_plural'), - gantt: this.I18n.t('js.gantt_chart.label'), - latest_activity: this.I18n.t('js.work_packages.default_queries.latest_activity'), - created_by_me: this.I18n.t('js.work_packages.default_queries.created_by_me'), - assigned_to_me: this.I18n.t('js.work_packages.default_queries.assigned_to_me'), - recently_created: this.I18n.t('js.work_packages.default_queries.recently_created'), all_open: this.I18n.t('js.work_packages.default_queries.all_open'), - shared_with_users: this.I18n.t('js.work_packages.default_queries.shared_with_users'), - shared_with_me: this.I18n.t('js.work_packages.default_queries.shared_with_me'), - summary: this.I18n.t('js.work_packages.default_queries.summary'), - overdue: this.I18n.t('js.notifications.date_alerts.overdue'), }; public getStaticName(query:QueryResource):string { if (this.$state.params.query_props) { - const queryProps = JSON.parse(this.$state.params.query_props) as { pa:unknown, pp:unknown }&unknown; - delete queryProps.pp; - delete queryProps.pa; - const queryPropsString = JSON.stringify(queryProps); - - const matched = this.staticQueries.find((item) => { - const uiParams = item.uiParams as { query_id:string, query_props:string }; - return uiParams && uiParams.query_props === queryPropsString; - }); - - if (matched) { - return matched.title; - } - - if (this.$state.params.name) { - const nameKey = this.$state.params.name as string; + const nameKey = this.$state.params.name as string; + if (nameKey) { return this.I18n.t(`js.work_packages.default_queries.${nameKey}`); } } @@ -107,137 +62,4 @@ export class StaticQueriesService { // Otherwise, fall back to work packages return this.text.work_packages; } - - public buildQueries():IStaticQuery[] { - let items:IStaticQuery[] = [ - { - title: this.text.all_open, - uiSref: 'work-packages', - uiParams: { query_id: undefined, query_props: undefined }, - view: 'WorkPackagesTable', - }, - { - title: this.text.latest_activity, - uiSref: 'work-packages', - uiParams: { - query_id: undefined, - query_props: '{"c":["id","subject","type","status","assignee","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc","f":[{"n":"status","o":"*","v":[]}]}', - }, - view: 'WorkPackagesTable', - }, - { - title: this.text.recently_created, - uiSref: 'work-packages', - uiParams: { - query_id: undefined, - query_props: '{"c":["id","subject","type","status","assignee","createdAt"],"hi":false,"g":"","t":"createdAt:desc","f":[{"n":"status","o":"o","v":[]}]}', - }, - view: 'WorkPackagesTable', - }, - { - title: this.text.overdue, - uiSref: 'work-packages', - uiParams: { - query_id: undefined, - query_props: '{"c":["id","type","subject","status","startDate","dueDate","duration"],"hi":false,"g":"","t":"createdAt:desc","f":[{"n":"dueDate","o":" query.view === view); - } - - private projectDependentQueries(projectIdentifier:string):IStaticQuery[] { - return [ - { - title: this.text.summary, - href: `${this.PathHelper.projectWorkPackagesPath(projectIdentifier)}/report`, - view: 'WorkPackagesTable', - }, - ]; - } - - private userDependentQueries():IStaticQuery[] { - return [ - { - title: this.text.created_by_me, - uiSref: 'work-packages', - uiParams: { - query_id: undefined, - query_props: '{"c":["id","subject","type","status","assignee","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"status","o":"o","v":[]},{"n":"author","o":"=","v":["me"]}]}', - }, - view: 'WorkPackagesTable', - }, - { - title: this.text.assigned_to_me, - uiSref: 'work-packages', - uiParams: { - query_id: undefined, - query_props: '{"c":["id","subject","type","status","author","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"status","o":"o","v":[]},{"n":"assigneeOrGroup","o":"=","v":["me"]}]}', - }, - view: 'WorkPackagesTable', - }, - { - title: this.text.shared_with_users, - view: 'WorkPackagesTable', - isEnterprise: true, - ...this.eeGuardedShareRoute, - }, - { - title: this.text.shared_with_me, - view: 'WorkPackagesTable', - isEnterprise: true, - ...this.eeGuardedShareWithMeRoute, - }, - ]; - } - - private get eeGuardedShareWithMeRoute() { - if (this.bannersService.eeShowBanners) { - return { uiSref: 'work-packages.share_upsale', uiParams: null, uiOptions: { inherit: false } }; - } - - return { - uiSref: 'work-packages', - uiParams: { - query_id: undefined, - query_props: '{"c":["id","subject","type","project"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"sharedWithMe","o":"=","v":"t"}]}', - }, - }; - } - - private get eeGuardedShareRoute() { - if (this.bannersService.eeShowBanners) { - return { uiSref: 'work-packages.share_upsale', uiParams: null, uiOptions: { inherit: false } }; - } - - return { - uiSref: 'work-packages', - uiParams: { - query_id: undefined, - query_props: '{"c":["id","subject","type","project","sharedWithUsers"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"sharedWithUser","o":"*","v":[]}]}', - }, - }; - } } 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 deleted file mode 100644 index 1225fb100147..000000000000 --- a/frontend/src/app/shared/components/op-view-select/op-view-select.component.ts +++ /dev/null @@ -1,244 +0,0 @@ -// -- copyright -// OpenProject is an open source project management software. -// Copyright (C) 2012-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. -//++ - -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - Input, - OnInit, -} from '@angular/core'; -import { map } from 'rxjs/operators'; -import { - BehaviorSubject, - combineLatest, -} from 'rxjs'; -import { States } from 'core-app/core/states/states.service'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; -import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; -import { MainMenuNavigationService } from 'core-app/core/main-menu/main-menu-navigation.service'; -import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; -import { IOpSidemenuItem } from 'core-app/shared/components/sidemenu/sidemenu.component'; -import { StaticQueriesService } from 'core-app/shared/components/op-view-select/op-static-queries.service'; -import { ViewsResourceService } from 'core-app/core/state/views/views.service'; -import { IView } from 'core-app/core/state/views/view.model'; -import idFromLink from 'core-app/features/hal/helpers/id-from-link'; -import { ApiV3ListParameters } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; -import { MAGIC_PAGE_NUMBER } from 'core-app/core/apiv3/helpers/get-paginated-results'; - -export type ViewType = 'WorkPackagesTable'|'Bim'|'TeamPlanner'|'WorkPackagesCalendar'|'Gantt'; - -export const opViewSelectSelector = 'op-view-select'; - -@Component({ - selector: opViewSelectSelector, - changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './op-view-select.template.html', -}) -export class ViewSelectComponent extends UntilDestroyedMixin implements OnInit { - public text = { - search: this.I18n.t('js.global_search.search'), - label: this.I18n.t('js.toolbar.search_query_label'), - scope_default: this.I18n.t('js.label_default_queries'), - scope_starred: this.I18n.t('js.label_starred_queries'), - scope_global: this.I18n.t('js.label_global_queries'), - scope_private: this.I18n.t('js.label_custom_queries'), - no_results: this.I18n.t('js.autocompleter.notFoundText'), - }; - - @Input() menuItems:string[] = []; - - @Input() projectId:string|undefined; - - @Input() baseRoute:string; - - @Input() viewType:ViewType; - - public items:IOpSidemenuItem[] = []; - - private apiViewType:string; - - private viewCategories$ = new BehaviorSubject([]); - - private search$ = new BehaviorSubject(''); - - private initialized = false; - - constructor( - readonly elementRef:ElementRef, - readonly apiV3Service:ApiV3Service, - readonly I18n:I18nService, - readonly states:States, - readonly opStaticQueries:StaticQueriesService, - readonly mainMenuService:MainMenuNavigationService, - readonly cdRef:ChangeDetectorRef, - readonly viewsService:ViewsResourceService, - ) { - super(); - - populateInputsFromDataset(this); - } - - public set search(input:string) { - if (this.search$.value !== input) { - this.search$.next(input); - } - } - - ngOnInit():void { - this.apiViewType = `Views::${this.viewType}`; - - // When activating the work packages submenu, - // either initially or through click on the toggle, load the results - this.mainMenuService - .onActivate(...this.menuItems) - .subscribe(() => this.initializeAutocomplete()); - - combineLatest([ - this.search$, - this.viewCategories$, - ]).pipe( - map(([searchText, categories]) => { - // We go the way of assigning the variable instead of using the observable directly with the async pipe. - // For whatever the reason Angular's change detection does not catch the changes made here. - // Thus, the sidemenu items were only updated with the next global change event (e.g. notifications push or some user interaction). - this.items = categories - .map((category) => { - if (ViewSelectComponent.matchesText(category.title, searchText)) { - return category; - } - const filteredChildren = category.children - ?.filter((query) => ViewSelectComponent.matchesText(query.title, searchText)); - - return { title: category.title, children: filteredChildren, collapsible: true }; - }) - .filter((category) => category.children && category.children.length > 0); - - this.cdRef.detectChanges(); - - return this.items; - }), - ).subscribe(); - } - - private initializeAutocomplete():void { - if (this.initialized) { - return; - } - - // Set focus on collapsible menu's back button. - // This improves accessibility for blind users to tell them their current location. - const buttonArrowLeft = document.getElementById('main-menu-work-packages-wrapper')?.parentElement - ?.getElementsByClassName('main-menu--arrow-left-to-project')[0] as HTMLElement; - if (buttonArrowLeft) { - buttonArrowLeft.focus(); - } - - this.updateMenuOnChanges(); - this.initializeViews(); - this.initialized = true; - } - - private static matchesText(text:string, searchText:string):boolean { - return text.toLowerCase().includes(searchText.toLowerCase()); - } - - private initializeViews():void { - const categories:{ [category:string]:IOpSidemenuItem[] } = { - starred: [], - default: [], - public: [], - private: [], - createNew: [], - }; - - const params:ApiV3ListParameters = { - filters: [ - ['type', '=', [this.apiViewType]], - ], - pageSize: MAGIC_PAGE_NUMBER, - }; - - if (this.projectId) { - params.filters?.push( - ['project', '=', [this.projectId]], - ); - } else { - params.filters?.push( - ['project', '!*', []], - ); - } - - this.viewsService.fetchResults(params) - .pipe(this.untilDestroyed()) - .subscribe((views) => { - views - .sort((a, b) => a._links.query.title.localeCompare(b._links.query.title)) - .forEach((view) => { - let cat = 'private'; - if (view.public) { - cat = 'public'; - } - if (view.starred) { - cat = 'starred'; - } - - categories[cat].push(this.toOpSideMenuItem(view)); - }); - - const staticQueries = this.opStaticQueries.getStaticQueriesForView(this.viewType); - const viewCategories = [ - { title: this.text.scope_starred, children: categories.starred, collapsible: true }, - { title: this.text.scope_default, children: staticQueries, collapsible: true }, - { title: this.text.scope_global, children: categories.public, collapsible: true }, - { title: this.text.scope_private, children: categories.private, collapsible: true }, - ]; - - this.viewCategories$.next(viewCategories); - }); - } - - private toOpSideMenuItem(view:IView):IOpSidemenuItem { - const { query } = view._links; - return { - title: query.title, - uiSref: this.baseRoute, - uiParams: { query_id: idFromLink(query.href), query_props: undefined }, - uiOptions: { reload: true }, - }; - } - - // Listens on all changes of queries (via an observable in the service), e.g. delete, create, rename, toggle starred - private updateMenuOnChanges() { - this.states.changes.queries - .pipe(this.untilDestroyed()) - .subscribe(() => this.initializeViews()); - } -} diff --git a/frontend/src/app/shared/components/op-view-select/op-view-select.template.html b/frontend/src/app/shared/components/op-view-select/op-view-select.template.html deleted file mode 100644 index f7c4cb93c79e..000000000000 --- a/frontend/src/app/shared/components/op-view-select/op-view-select.template.html +++ /dev/null @@ -1,28 +0,0 @@ -
-
- - -

- - - -
-
diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index c16f1726f3e4..678d3c53f405 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -58,7 +58,6 @@ import { FreeTrialButtonComponent } from 'core-app/features/enterprise/free-tria import { HomescreenNewFeaturesBlockComponent } from 'core-app/features/homescreen/blocks/new-features.component'; import { TablePaginationComponent } from 'core-app/shared/components/table-pagination/table-pagination.component'; import { HookService } from 'core-app/features/plugins/hook-service'; -import { ViewSelectComponent } from 'core-app/shared/components/op-view-select/op-view-select.component'; import { StaticQueriesService } from 'core-app/shared/components/op-view-select/op-static-queries.service'; import { highlightColSelector, @@ -207,8 +206,6 @@ export function bootstrapModule(injector:Injector):void { OpProjectIncludeListComponent, OpLoadingProjectListComponent, - ViewSelectComponent, - // Old datepickers OpMultiDatePickerComponent, @@ -220,8 +217,6 @@ export function bootstrapModule(injector:Injector):void { ViewsResourceService, ], declarations: [ - ViewSelectComponent, - ToastsContainerComponent, ToastComponent, UploadProgressComponent, From daf94280b9ce3ca29f91c5a22391fee93db17a46 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Mon, 1 Jul 2024 10:40:04 +0200 Subject: [PATCH 3/5] Sort options of Submenu alphabetically, except for default queries --- app/menus/submenu.rb | 3 +++ modules/boards/app/menus/boards/menu.rb | 1 + modules/reporting/app/menus/cost_reports/menu.rb | 2 ++ 3 files changed, 6 insertions(+) diff --git a/app/menus/submenu.rb b/app/menus/submenu.rb index e35b092cb48d..de581ac4d9e9 100644 --- a/app/menus/submenu.rb +++ b/app/menus/submenu.rb @@ -49,6 +49,7 @@ def starred_queries .where("starred" => "t") .pluck(:id, :name) .map { |id, name| menu_item(name, query_params(id)) } + .sort_by(&:title) end def default_queries @@ -61,6 +62,7 @@ def global_queries .where("public" => "t") .pluck(:id, :name) .map { |id, name| menu_item(name, query_params(id)) } + .sort_by(&:title) end def custom_queries @@ -69,6 +71,7 @@ def custom_queries .where("public" => "f") .pluck(:id, :name) .map { |id, name| menu_item(name, query_params(id)) } + .sort_by(&:title) end def base_query diff --git a/modules/boards/app/menus/boards/menu.rb b/modules/boards/app/menus/boards/menu.rb index b907c5344621..12b3cabd8079 100644 --- a/modules/boards/app/menus/boards/menu.rb +++ b/modules/boards/app/menus/boards/menu.rb @@ -42,6 +42,7 @@ def global_queries .where(project: @project) .pluck(:id, :name) .map { |id, name| menu_item(name, query_params(id)) } + .sort_by(&:title) end def starred_queries diff --git a/modules/reporting/app/menus/cost_reports/menu.rb b/modules/reporting/app/menus/cost_reports/menu.rb index f65d4ca26e4f..76aa9e8d9adc 100644 --- a/modules/reporting/app/menus/cost_reports/menu.rb +++ b/modules/reporting/app/menus/cost_reports/menu.rb @@ -47,12 +47,14 @@ def global_queries CostQuery.public(project) .pluck(:id, :name) .map { |id, name| menu_item(name, query_params(id)) } + .sort_by(&:title) end def custom_queries CostQuery.private(project, User.current) .pluck(:id, :name) .map { |id, name| menu_item(name, query_params(id)) } + .sort_by(&:title) end def selected?(query_params) From ee37ae08fa639264a50ee1ad109d7e50d6b691eb Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Mon, 1 Jul 2024 10:40:43 +0200 Subject: [PATCH 4/5] Adapt tests to new rails based submenu of the workpackages module --- .../common/submenu_component.html.erb | 2 +- .../spec/features/boards_sorting_spec.rb | 8 +- .../timeline/timeline_navigation_spec.rb | 6 +- .../spec/features/navigation_spec.rb | 4 +- .../features/work_packages/navigation_spec.rb | 2 +- .../table/queries/query_menu_refresh_spec.rb | 1 - .../table/queries/query_menu_spec.rb | 2 +- .../components/work_packages/query_menu.rb | 74 ------------------- .../work_packages/work_packages_table.rb | 4 +- 9 files changed, 16 insertions(+), 87 deletions(-) delete mode 100644 spec/support/components/work_packages/query_menu.rb diff --git a/app/components/open_project/common/submenu_component.html.erb b/app/components/open_project/common/submenu_component.html.erb index 5b38527ce187..1bbcfc582a8f 100644 --- a/app/components/open_project/common/submenu_component.html.erb +++ b/app/components/open_project/common/submenu_component.html.erb @@ -24,7 +24,7 @@ <% end %> -
+
<% if top_level_sidebar_menu_items.any? %>
    diff --git a/modules/boards/spec/features/boards_sorting_spec.rb b/modules/boards/spec/features/boards_sorting_spec.rb index 75fbf14b38b4..52cfb8856a5e 100644 --- a/modules/boards/spec/features/boards_sorting_spec.rb +++ b/modules/boards/spec/features/boards_sorting_spec.rb @@ -36,7 +36,7 @@ let(:board_index) { Pages::BoardIndex.new(project) } let!(:status) { create(:default_status) } let(:version) { @version ||= create(:version, project:) } - let(:query_menu) { Components::WorkPackages::QueryMenu.new } + let(:query_menu) { Components::Submenu.new } before do project @@ -51,7 +51,7 @@ board_page.back_to_index board_index.expect_boards_listed "My Basic Board" - query_menu.expect_menu_entry "My Basic Board" + query_menu.expect_item "My Basic Board" board_page = board_index.create_board title: "My Action Board", action: "Version", @@ -59,7 +59,7 @@ board_page.back_to_index board_index.expect_boards_listed "My Action Board", "My Basic Board" - query_menu.expect_menu_entry "My Action Board" + query_menu.expect_item "My Action Board" board_page = board_index.create_board title: "My Status Board", action: "Status" @@ -68,6 +68,6 @@ board_index.expect_boards_listed "My Status Board", "My Action Board", "My Basic Board" - query_menu.expect_menu_entry "My Status Board" + query_menu.expect_item "My Status Board" end end diff --git a/modules/gantt/spec/features/timeline/timeline_navigation_spec.rb b/modules/gantt/spec/features/timeline/timeline_navigation_spec.rb index 598d39f94744..4abb52d25388 100644 --- a/modules/gantt/spec/features/timeline/timeline_navigation_spec.rb +++ b/modules/gantt/spec/features/timeline/timeline_navigation_spec.rb @@ -34,7 +34,7 @@ let(:user) { create(:admin) } let(:enabled_module_names) { %i[work_package_tracking gantt] } let(:project) { create(:project, enabled_module_names:) } - let(:query_menu) { Components::WorkPackages::QueryMenu.new } + let(:query_menu) { Components::Submenu.new } let(:wp_timeline) { Pages::WorkPackagesTimeline.new(project) } let(:wp_table) { Pages::WorkPackagesTable.new(project) } let(:settings_menu) { Components::WorkPackages::SettingsMenu.new } @@ -121,7 +121,9 @@ page.find_test_selector("main-menu-toggler--work_packages").click # Select other query - query_menu.select query + query_menu.search_for_item query.name + query_menu.expect_item query.name + query_menu.click_item query.name wp_timeline.expect_timeline!(open: false) wp_table.expect_work_package_listed work_package wp_table.ensure_work_package_not_listed! work_package2 diff --git a/modules/overviews/spec/features/navigation_spec.rb b/modules/overviews/spec/features/navigation_spec.rb index e38a43185da0..006af16e2dbb 100644 --- a/modules/overviews/spec/features/navigation_spec.rb +++ b/modules/overviews/spec/features/navigation_spec.rb @@ -36,6 +36,8 @@ member_with_permissions: { project => permissions }) end + let(:query_menu) { Components::Submenu.new } + before do login_as user end @@ -80,7 +82,7 @@ page.find_test_selector("main-menu-toggler--work_packages").click # Click on a saved query - page.find_test_selector("op-sidemenu--item-action--MyimportantQuery", wait: 10).click + query_menu.click_item "My important Query" loading_indicator_saveguard diff --git a/spec/features/work_packages/navigation_spec.rb b/spec/features/work_packages/navigation_spec.rb index 3375e552ec36..3c281aa8c089 100644 --- a/spec/features/work_packages/navigation_spec.rb +++ b/spec/features/work_packages/navigation_spec.rb @@ -160,7 +160,7 @@ visit project_path(project) page.find_test_selector("main-menu-toggler--work_packages").click - expect(page).to have_css(".op-view-select--search-results") + expect(page).to have_test_selector("op-sidebar--body") find(".op-sidemenu--item-action", text: query.name).click expect(page).to have_no_css(".title-container", text: "Overview") diff --git a/spec/features/work_packages/table/queries/query_menu_refresh_spec.rb b/spec/features/work_packages/table/queries/query_menu_refresh_spec.rb index 8d7e967062b3..f64e9764cc79 100644 --- a/spec/features/work_packages/table/queries/query_menu_refresh_spec.rb +++ b/spec/features/work_packages/table/queries/query_menu_refresh_spec.rb @@ -79,7 +79,6 @@ find_by_id("show-public").set false find(".button", text: "Save").click - wp_table.expect_and_dismiss_toaster message: "Successful update." expect(page).to have_current_path(project_work_packages_path(project)) end end diff --git a/spec/features/work_packages/table/queries/query_menu_spec.rb b/spec/features/work_packages/table/queries/query_menu_spec.rb index 542f6da8d910..ae11a7caedd1 100644 --- a/spec/features/work_packages/table/queries/query_menu_spec.rb +++ b/spec/features/work_packages/table/queries/query_menu_spec.rb @@ -62,7 +62,7 @@ it "shows the query menu with queries stored for the global page" do wp_table.visit! - expect(page).to have_css(".op-view-select--search-results") + expect(page).to have_test_selector("op-sidebar--body") expect(page).to have_css(".op-sidemenu--item-action", wait: 20, minimum: 1) within ".op-sidebar" do diff --git a/spec/support/components/work_packages/query_menu.rb b/spec/support/components/work_packages/query_menu.rb deleted file mode 100644 index a370d6bb68b4..000000000000 --- a/spec/support/components/work_packages/query_menu.rb +++ /dev/null @@ -1,74 +0,0 @@ -#-- copyright -# OpenProject is an open source project management software. -# Copyright (C) 2012-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. -#++ -require "support/components/autocompleter/autocomplete_helpers" - -module Components - module WorkPackages - class QueryMenu - include Capybara::DSL - include Capybara::RSpecMatchers - include RSpec::Matchers - include ::Components::Autocompleter::AutocompleteHelpers - - def select(query) - select_autocomplete autocompleter, - results_selector: autocompleter_results_selector, - item_selector: autocompleter_item_selector, - query: - end - - def autocompleter - page.find autocompleter_selector - end - - def autocompleter_results_selector - ".op-view-select--search-results" - end - - def autocompleter_item_selector - ".op-sidemenu--item-title" - end - - def autocompleter_selector - "#query-title-filter" - end - - def click_item(name) - page.find(autocompleter_item_selector, text: name).click - end - - def expect_menu_entry(name) - expect(page).to have_selector(autocompleter_item_selector, text: name) - end - - def expect_menu_entry_not_visible(name) - expect(page).to have_no_selector(autocompleter_item_selector, text: name) - end - end - end -end diff --git a/spec/support/pages/work_packages/work_packages_table.rb b/spec/support/pages/work_packages/work_packages_table.rb index c066bb290016..400869833651 100644 --- a/spec/support/pages/work_packages/work_packages_table.rb +++ b/spec/support/pages/work_packages/work_packages_table.rb @@ -189,8 +189,8 @@ def expect_title(name, editable: true) def expect_query_in_select_dropdown(name) page.find(".title-container").click - page.within("#viewSelect") do - expect(page).to have_css(".op-sidemenu--item-action", text: name) + page.within('[data-test-selector="op-sidebar--body"]') do + expect(page).to have_test_selector("op-sidemenu--item-action", text: name) end end From 414101c3a0d3cb62ad3e8496ae43d5479331f3cb Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Thu, 4 Jul 2024 09:54:41 +0200 Subject: [PATCH 5/5] Do not highlight the "all open" query when simply navigating through the menu --- .../work_packages/menus_controller.rb | 2 +- app/menus/work_packages/menu.rb | 22 ++++++++++++++----- .../default_query_generator_service.rb | 8 +------ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/app/controllers/work_packages/menus_controller.rb b/app/controllers/work_packages/menus_controller.rb index 7c5061c9513c..f05a98d8160c 100644 --- a/app/controllers/work_packages/menus_controller.rb +++ b/app/controllers/work_packages/menus_controller.rb @@ -30,7 +30,7 @@ class MenusController < ApplicationController before_action :load_and_authorize_in_optional_project def show - @sidebar_menu_items = WorkPackages::Menu.new(project: @project, params:).menu_items + @sidebar_menu_items = WorkPackages::Menu.new(project: @project, params:, request:).menu_items render layout: nil end end diff --git a/app/menus/work_packages/menu.rb b/app/menus/work_packages/menu.rb index 64ae6cbcc314..d4c4b3628e65 100644 --- a/app/menus/work_packages/menu.rb +++ b/app/menus/work_packages/menu.rb @@ -29,8 +29,9 @@ module WorkPackages class Menu < Submenu attr_reader :view_type, :project, :params - def initialize(project: nil, params: nil) + def initialize(project: nil, params: nil, request: nil) @view_type = "work_packages_table" + @request = request super(view_type:, project:, params:) end @@ -64,10 +65,13 @@ def query_path(query_params) end def selected?(query_params) - # Special rules, as those are redirected to completely new pages where only the name parameter is preserved - return true if query_params[:name] == :summary && params[:name] == "summary" - return true if query_params[:name] == :shared_with_me && params[:name] == "shared_with_me" - return true if query_params[:name] == :shared_with_users && params[:name] == "shared_with_users" + return true if check_for_redirected_urls(query_params) + + if query_params[:work_package_default] && + (%i[filters query_props query_id name].none? { |k| params.key? k }) && + @request.referer.include?("work_packages") + return true + end super end @@ -75,5 +79,13 @@ def selected?(query_params) def ee_upsale_path(query_params) share_upsale_work_packages_path({ name: query_params[:name] }) end + + def check_for_redirected_urls(query_params) + # Special rules, as those are redirected to completely new pages where only the name parameter is preserved + return true if query_params[:name] == :shared_with_me && params[:name] == "shared_with_me" + return true if query_params[:name] == :shared_with_users && params[:name] == "shared_with_users" + + true if query_params[:name] == :summary && params[:name] == "summary" + end end end diff --git a/app/services/work_packages/default_query_generator_service.rb b/app/services/work_packages/default_query_generator_service.rb index 0a86b78cc255..0db02e7e77d1 100644 --- a/app/services/work_packages/default_query_generator_service.rb +++ b/app/services/work_packages/default_query_generator_service.rb @@ -55,7 +55,7 @@ def initialize(with_project:) end def call(query_key: DEFAULT_QUERY) - return {} if query_key == DEFAULT_QUERY + return { work_package_default: true } if query_key == DEFAULT_QUERY params = self.class.assign_params(query_key, project) @@ -65,10 +65,6 @@ def call(query_key: DEFAULT_QUERY) end class << self - def all_open_query - {} - end - def latest_activity_query DEFAULT_PARAMS.merge( { @@ -144,8 +140,6 @@ def shared_with_me_query def assign_params(query_key, project) case query_key - when DEFAULT_QUERY - all_open_query when :latest_activity latest_activity_query when :recently_created