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| %>
@@ -60,7 +60,7 @@
<% menu_item.children.each do |child_item| %>
@@ -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