diff --git a/app/components/open_project/common/submenu_component.html.erb b/app/components/open_project/common/submenu_component.html.erb index 35e4f8e2a954..6f42ad549163 100644 --- a/app/components/open_project/common/submenu_component.html.erb +++ b/app/components/open_project/common/submenu_component.html.erb @@ -31,7 +31,7 @@ <% top_level_sidebar_menu_items.first.children.each do |menu_item| %>
  • <% selected = menu_item.selected ? 'selected' : '' %> - + <%= menu_item.title %>
  • @@ -60,7 +60,7 @@ <% menu_item.children.each do |child_item| %>
  • <% selected = child_item.selected ? 'selected' : '' %> - + <%= child_item.title %>
  • @@ -75,9 +75,10 @@ <%= render Primer::Beta::Button.new(scheme: :primary, tag: :a, href: @create_btn_options[:href], + test_selector: "#{@create_btn_options[:module_key]}--create-button", classes: "op-sidebar--footer-action") do |button| button.with_leading_visual_icon(icon: "plus") - @create_btn_options[:text] + I18n.t("label_#{@create_btn_options[:module_key]}") end %> <% end %> 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 a4ad79800833..571d539333e6 100644 --- a/frontend/src/app/core/setup/global-dynamic-components.const.ts +++ b/frontend/src/app/core/setup/global-dynamic-components.const.ts @@ -143,10 +143,6 @@ import { opInAppNotificationBellSelector, } from 'core-app/features/in-app-notifications/bell/in-app-notification-bell.component'; import { IanMenuComponent, ianMenuSelector } from 'core-app/features/in-app-notifications/center/menu/menu.component'; -import { - opTeamPlannerSidemenuSelector, - TeamPlannerSidemenuComponent, -} from 'core-app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component'; import { OpModalOverlayComponent, opModalOverlaySelector, @@ -213,7 +209,6 @@ export const globalDynamicComponents:OptionalBootstrapDefinition[] = [ { selector: headerProjectSelectSelector, cls: OpHeaderProjectSelectComponent }, { selector: wpOverviewGraphSelector, cls: WorkPackageOverviewGraphComponent }, { selector: opViewSelectSelector, cls: ViewSelectComponent }, - { selector: opTeamPlannerSidemenuSelector, cls: TeamPlannerSidemenuComponent }, { selector: triggerActionsEntryComponentSelector, cls: TriggerActionsEntryComponent, embeddable: true }, { selector: editableQueryPropsSelector, cls: EditableQueryPropsComponent }, { selector: backupSelector, cls: BackupComponent }, diff --git a/frontend/src/app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component.html b/frontend/src/app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component.html deleted file mode 100644 index 6d49c6203a3e..000000000000 --- a/frontend/src/app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - diff --git a/frontend/src/app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component.ts b/frontend/src/app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component.ts deleted file mode 100644 index a434c52cb8cc..000000000000 --- a/frontend/src/app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - ElementRef, - HostBinding, - Input, -} from '@angular/core'; -import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; -import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; -import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { BannersService } from 'core-app/core/enterprise/banners.service'; -import { map } from 'rxjs/operators'; - -export const opTeamPlannerSidemenuSelector = 'op-team-planner-sidemenu'; - -@Component({ - selector: opTeamPlannerSidemenuSelector, - templateUrl: './team-planner-sidemenu.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class TeamPlannerSidemenuComponent extends UntilDestroyedMixin { - @HostBinding('class.op-sidebar') className = true; - - @Input() menuItems:string[] = []; - - @Input() projectId:string|undefined; - - canAddTeamPlanner$ = this - .currentUserService - .hasCapabilities$( - 'team_planners/create', - this.currentProjectService.id || null, - ) - .pipe( - map((val) => val && !this.bannersService.eeShowBanners), - ); - - createButton = { - text: this.I18n.t('js.team_planner.create_label'), - title: this.I18n.t('js.team_planner.create_title'), - uiSref: 'team_planner.page.show', - uiParams: { - query_id: null, - query_props: '', - }, - }; - - constructor( - readonly elementRef:ElementRef, - readonly currentUserService:CurrentUserService, - readonly currentProjectService:CurrentProjectService, - readonly bannersService:BannersService, - readonly I18n:I18nService, - ) { - super(); - - populateInputsFromDataset(this); - } -} diff --git a/frontend/src/app/features/team-planner/team-planner/team-planner.module.ts b/frontend/src/app/features/team-planner/team-planner/team-planner.module.ts index 3d8f8316ee23..3d3d2c1e744f 100644 --- a/frontend/src/app/features/team-planner/team-planner/team-planner.module.ts +++ b/frontend/src/app/features/team-planner/team-planner/team-planner.module.ts @@ -14,7 +14,6 @@ import { TeamPlannerPageComponent } from 'core-app/features/team-planner/team-pl import { OpSharedModule } from 'core-app/shared/shared.module'; import { AddExistingPaneComponent } from './add-work-packages/add-existing-pane.component'; import { OpenprojectContentLoaderModule } from 'core-app/shared/components/op-content-loader/openproject-content-loader.module'; -import { TeamPlannerSidemenuComponent } from 'core-app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component'; import { TeamPlannerViewSelectMenuDirective } from 'core-app/features/team-planner/team-planner/view-select/view-select-menu.directive'; @NgModule({ @@ -23,7 +22,6 @@ import { TeamPlannerViewSelectMenuDirective } from 'core-app/features/team-plann TeamPlannerPageComponent, AddAssigneeComponent, AddExistingPaneComponent, - TeamPlannerSidemenuComponent, TeamPlannerViewSelectMenuDirective, ], imports: [ 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 2674e488e6f7..8efb9246cb73 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 @@ -33,6 +33,13 @@ import { WorkPackagesBaseComponent } from 'core-app/features/work-packages/routi import { TeamPlannerPageComponent } from 'core-app/features/team-planner/team-planner/page/team-planner-page.component'; import { TeamPlannerComponent } from 'core-app/features/team-planner/team-planner/planner/team-planner.component'; +export const sidemenuId = 'team_planner_sidemenu'; +export const sideMenuOptions = { + sidemenuId, + hardReloadOnBaseRoute: true, + defaultQuery: 'new', +}; + export const TEAM_PLANNER_ROUTES:Ng2StateDeclaration[] = [ { name: 'team_planner', @@ -56,12 +63,14 @@ export const TEAM_PLANNER_ROUTES:Ng2StateDeclaration[] = [ redirectTo: 'team_planner.page.show', data: { bodyClasses: 'router--team-planner', + sideMenuOptions, }, }, { name: 'team_planner.page.show', data: { baseRoute: 'team_planner.page.show', + sideMenuOptions, }, views: { 'content-left': { component: TeamPlannerComponent }, diff --git a/lookbook/previews/open_project/common/submenu_component_preview.rb b/lookbook/previews/open_project/common/submenu_component_preview.rb index b8d4bce36f26..b64d39f2c7b9 100644 --- a/lookbook/previews/open_project/common/submenu_component_preview.rb +++ b/lookbook/previews/open_project/common/submenu_component_preview.rb @@ -21,7 +21,7 @@ def searchable def with_create_button render_with_template(template: "open_project/common/submenu_preview/playground", locals: { sidebar_menu_items: menu_items, searchable: true, - create_btn_options: { href: "/#", text: "User" } }) + create_btn_options: { href: "/#", module_key: "user" } }) end private diff --git a/modules/calendar/app/controllers/calendar/menus_controller.rb b/modules/calendar/app/controllers/calendar/menus_controller.rb index b32d394b53d6..41da3240b9b9 100644 --- a/modules/calendar/app/controllers/calendar/menus_controller.rb +++ b/modules/calendar/app/controllers/calendar/menus_controller.rb @@ -33,7 +33,7 @@ class MenusController < ApplicationController def show @submenu_menu_items = ::Calendar::Menu.new(project: @project, params:).menu_items @create_btn_options = if User.current.allowed_in_project?(:manage_calendars, @project) - { href: new_project_calendars_path(@project), text: I18n.t("label_calendar") } + { href: new_project_calendars_path(@project), module_key: "calendar" } end render layout: nil diff --git a/modules/team_planner/app/controllers/team_planner/menus_controller.rb b/modules/team_planner/app/controllers/team_planner/menus_controller.rb new file mode 100644 index 000000000000..e782768bac5a --- /dev/null +++ b/modules/team_planner/app/controllers/team_planner/menus_controller.rb @@ -0,0 +1,43 @@ +# -- 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 ::TeamPlanner + class MenusController < ApplicationController + before_action :find_project_by_project_id, + :authorize + + def show + @submenu_menu_items = ::TeamPlanner::Menu.new(project: @project, params:).menu_items + if User.current.allowed_in_project?(:manage_team_planner, @project) && + EnterpriseToken.allows_to?(:team_planner_view) + @create_btn_options = { href: new_project_team_planners_path(@project), module_key: "team_planner" } + end + + render layout: nil + end + end +end diff --git a/modules/team_planner/app/menus/team_planner/menu.rb b/modules/team_planner/app/menus/team_planner/menu.rb new file mode 100644 index 000000000000..7041404b2872 --- /dev/null +++ b/modules/team_planner/app/menus/team_planner/menu.rb @@ -0,0 +1,56 @@ +# -- 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 TeamPlanner + class Menu < Submenu + attr_reader :view_type, :project + + def initialize(project: nil, params: nil) + @view_type = "team_planner" + @project = project + @params = params + + super(view_type:, project:, params:) + end + + def default_queries + [] + end + + def selected?(query_params) + query_params[:id].to_s == params[:id] + end + + def query_params(id) + { id: } + end + + def query_path(query_params) + project_team_planner_path(project, query_params) + end + end +end diff --git a/modules/team_planner/app/views/team_planner/menus/_menu.html.erb b/modules/team_planner/app/views/team_planner/menus/_menu.html.erb new file mode 100644 index 000000000000..7ceb63595fb1 --- /dev/null +++ b/modules/team_planner/app/views/team_planner/menus/_menu.html.erb @@ -0,0 +1,5 @@ + <%= turbo_frame_tag "team_planner_sidemenu", + src: menu_project_team_planners_path(@project, **params.permit(:id)), + target: '_top', + data: { turbo: false }, + loading: :lazy %> diff --git a/modules/team_planner/app/views/team_planner/menus/show.html.erb b/modules/team_planner/app/views/team_planner/menus/show.html.erb new file mode 100644 index 000000000000..7c6fb6985aeb --- /dev/null +++ b/modules/team_planner/app/views/team_planner/menus/show.html.erb @@ -0,0 +1,5 @@ +<%= turbo_frame_tag "team_planner_sidemenu" do %> + <%= render OpenProject::Common::SubmenuComponent.new(sidebar_menu_items: @submenu_menu_items, + searchable: true, + create_btn_options: @create_btn_options) %> +<% end %> diff --git a/modules/team_planner/app/views/team_planner/team_planner/_menu.html.erb b/modules/team_planner/app/views/team_planner/team_planner/_menu.html.erb deleted file mode 100644 index fed603f4a415..000000000000 --- a/modules/team_planner/app/views/team_planner/team_planner/_menu.html.erb +++ /dev/null @@ -1,7 +0,0 @@ - <%= - angular_component_tag 'op-team-planner-sidemenu', - inputs: { - projectId: (@project ? @project.id.to_s : ''), - menuItems: [parent_name, name], - } - %> diff --git a/modules/team_planner/config/routes.rb b/modules/team_planner/config/routes.rb index 2f694e154101..501607c86cc2 100644 --- a/modules/team_planner/config/routes.rb +++ b/modules/team_planner/config/routes.rb @@ -15,6 +15,7 @@ only: %i[index destroy], as: :team_planners do collection do + get "menu" => "team_planner/menus#show" get "/upsale", to: "team_planner/team_planner#upsale", as: :upsale get "/new", to: "team_planner/team_planner#show", as: :new 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 2a853694ae21..422c42bafe8c 100644 --- a/modules/team_planner/lib/open_project/team_planner/engine.rb +++ b/modules/team_planner/lib/open_project/team_planner/engine.rb @@ -38,7 +38,8 @@ class Engine < ::Rails::Engine settings: {} do project_module :team_planner_view, dependencies: :work_package_tracking, enterprise_feature: true do permission :view_team_planner, - { "team_planner/team_planner": %i[index show upsale overview] }, + { "team_planner/team_planner": %i[index show upsale overview], + "team_planner/menus": %i[show] }, permissible_on: :project, dependencies: %i[view_work_packages], contract_actions: { team_planner: %i[read] } @@ -80,7 +81,7 @@ class Engine < ::Rails::Engine :team_planner_menu, { controller: "/team_planner/team_planner", action: :index }, parent: :team_planner_view, - partial: "team_planner/team_planner/menu", + partial: "team_planner/menus/menu", last: true, caption: :"team_planner.label_team_planner_plural" diff --git a/modules/team_planner/spec/features/query_handling_spec.rb b/modules/team_planner/spec/features/query_handling_spec.rb index 7388ee6f9f8f..005c10402b88 100644 --- a/modules/team_planner/spec/features/query_handling_spec.rb +++ b/modules/team_planner/spec/features/query_handling_spec.rb @@ -75,7 +75,7 @@ let(:team_planner) { Pages::TeamPlanner.new project } let(:work_package_page) { Pages::WorkPackagesTable.new project } let(:query_title) { Components::WorkPackages::QueryTitle.new } - let(:query_menu) { Components::WorkPackages::QueryMenu.new } + let(:query_menu) { Components::Submenu.new } let(:filters) { team_planner.filters } current_user { user } @@ -114,8 +114,8 @@ it "shows only team planner queries" do # Go to team planner where no query is shown, only the create option - query_menu.expect_no_menu_entry - expect(page).to have_test_selector("team-planner--create-button") + query_menu.expect_no_items + expect(page).to have_test_selector("team_planner--create-button") # Change filter filters.open @@ -128,11 +128,11 @@ team_planner.expect_and_dismiss_toaster(message: I18n.t("js.notice_successful_create")) # The saved query appears in the side menu... - query_menu.expect_menu_entry "I am your Query" + query_menu.expect_item "I am your Query" # .. but not in the work packages module work_package_page.visit! - query_menu.expect_menu_entry_not_visible "I am your Query" + query_menu.expect_no_item "I am your Query" end it_behaves_like "module specific query view management" do 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 067fbb09ec3e..f06f50a88c4b 100644 --- a/modules/team_planner/spec/features/team_planner_index_spec.rb +++ b/modules/team_planner/spec/features/team_planner_index_spec.rb @@ -63,7 +63,7 @@ end it "can create an action through the sidebar" do - find_test_selector("team-planner--create-button").click + find_test_selector("team_planner--create-button").click team_planner.expect_no_toaster team_planner.expect_title 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 0732a7378215..e7b61a15cabc 100644 --- a/modules/team_planner/spec/features/team_planner_menu_spec.rb +++ b/modules/team_planner/spec/features/team_planner_menu_spec.rb @@ -77,7 +77,7 @@ click_link "Team planners" end - expect(page).not_to have_test_selector("team-planner--create-button") + expect(page).not_to have_test_selector("team_planner--create-button") end end @@ -89,7 +89,7 @@ click_link "Team planners" end - expect(page).not_to have_test_selector("team-planner--create-button") + expect(page).not_to have_test_selector("team_planner--create-button") end end end @@ -105,7 +105,7 @@ click_link "Team planners" end - expect(page).not_to have_test_selector("team-planner--create-button") + expect(page).not_to have_test_selector("team_planner--create-button") end end @@ -117,7 +117,7 @@ click_link "Team planners" end - expect(page).to have_test_selector("team-planner--create-button") + expect(page).to have_test_selector("team_planner--create-button") end end end diff --git a/spec/support/components/common/submenu.rb b/spec/support/components/common/submenu.rb index 77c59af075d4..f876bdf700fb 100644 --- a/spec/support/components/common/submenu.rb +++ b/spec/support/components/common/submenu.rb @@ -37,12 +37,13 @@ def expect_item(name, selected: false, visible: true) selected_specifier = selected ? ".selected" : ":not(.selected)" expect(page).to have_css(".op-sidemenu--item-action#{selected_specifier}", text: name, visible:) + # expect(page).to have_css("[data-test-selector='op-sidemenu--item-action']#{selected_specifier}", text: name, visible:) end end def expect_no_item(name) within "#main-menu" do - expect(page).to have_no_css(".op-sidemenu--item-action", text: name) + expect(page).not_to have_test_selector("op-sidemenu--item-action", text: name) end end @@ -52,6 +53,12 @@ def click_item(name) end end + def expect_no_items + within "#main-menu" do + expect(page).not_to have_test_selector("op-sidemenu--item-action") + end + end + def search_for_item(name) within "#main-menu" do page.find_test_selector("op-sidebar--search-input").set(name) diff --git a/spec/support/components/work_packages/query_menu.rb b/spec/support/components/work_packages/query_menu.rb index 10f541c862ba..a370d6bb68b4 100644 --- a/spec/support/components/work_packages/query_menu.rb +++ b/spec/support/components/work_packages/query_menu.rb @@ -69,10 +69,6 @@ def expect_menu_entry(name) def expect_menu_entry_not_visible(name) expect(page).to have_no_selector(autocompleter_item_selector, text: name) end - - def expect_no_menu_entry - expect(page).to have_no_selector(autocompleter_item_selector) - end end end end