From afc9b030085bf81096465b5c1c84521126874051 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Wed, 11 Sep 2024 11:03:38 +0200 Subject: [PATCH] Add a turboFrame in the partitioned query space to render a Primer PageHeader above the work package table view. This is work in progress --- .../index_page_header_component.html.erb | 99 +++++++++++++++++++ .../index_page_header_component.rb | 90 +++++++++++++++++ .../index_sub_header_component.html.erb | 34 +++++++ .../index_sub_header_component.rb | 46 +++++++++ .../work_packages/page_header_controller.rb | 37 +++++++ .../work_packages/page_header/index.html.erb | 4 + config/initializers/permissions.rb | 3 +- config/routes.rb | 2 + .../board-partitioned-page.component.ts | 3 + ...artitioned-query-space-page.component.html | 7 +- .../partitioned-query-space-page.component.ts | 12 +++ .../layout/work_packages/_mobile.sass | 4 +- .../global_styles/openproject/_mixins.sass | 4 +- 13 files changed, 341 insertions(+), 4 deletions(-) create mode 100644 app/components/work_packages/index_page_header_component.html.erb create mode 100644 app/components/work_packages/index_page_header_component.rb create mode 100644 app/components/work_packages/index_sub_header_component.html.erb create mode 100644 app/components/work_packages/index_sub_header_component.rb create mode 100644 app/controllers/work_packages/page_header_controller.rb create mode 100644 app/views/work_packages/page_header/index.html.erb diff --git a/app/components/work_packages/index_page_header_component.html.erb b/app/components/work_packages/index_page_header_component.html.erb new file mode 100644 index 000000000000..cb814ab9a2a7 --- /dev/null +++ b/app/components/work_packages/index_page_header_component.html.erb @@ -0,0 +1,99 @@ +<%= + render(Primer::OpenProject::PageHeader.new(state: :show)) do |header| + header.with_title do |component| + component.with_editable_form(update_path: "/todo", cancel_path: "/todo") + title + end + header.with_breadcrumbs(breadcrumb_items) + + header.with_action_dialog(mobile_icon: :"op-include-projects", + mobile_label: I18n.t("js.include_projects.toggle_title"), + dialog_arguments: { id: "my_dialog", title: "A great dialog" }, + button_arguments: { button_block: project_include_button_callback, test_selector: "project-include-button" }) do |d| + d.with_body { "TODO" } + end + + header.with_action_dialog(mobile_icon: :"op-baseline", + mobile_label: I18n.t("js.baseline.toggle_title"), + dialog_arguments: { id: "my_dialog2", title: "A great dialog" }, + button_arguments: { button_block: baseline_button_callback, test_selector: "baseline-button" }) do |d| + d.with_body { "TODO" } + end + + header.with_action_zen_mode_button + + header.with_action_menu( + menu_arguments: { + anchor_align: :end + }, + button_arguments: { + icon: "kebab-horizontal", + "aria-label": t(:label_more), + data: { "test-selector": "project-more-dropdown-menu" } + } + ) do |menu| + menu.with_item( + tag: :a, + label: t(:'queries.configure_view.heading'), + href: "TODO", + content_arguments: { data: { controller: "async-dialog" }} + ) do |item| + item.with_leading_visual_icon(icon: :gear) + end + + if can_rename? + menu.with_item( + label: t("button_rename"), + href: "TODO", + ) do |item| + item.with_leading_visual_icon(icon: :pencil) + end + end + + if can_save? + menu.with_item( + label: t("button_save"), + href: "TODO", + content_arguments: { + data: { "turbo-stream": true, method: :patch } + } + ) do |item| + item.with_leading_visual_icon(icon: :"op-save") + end + end + + if can_save_as? + menu.with_item( + label: t("button_save_as"), + href: "TODO", + content_arguments: { + data: { "turbo-stream": true, method: :patch } + } + ) do |item| + item.with_leading_visual_icon(icon: :"op-save") + end + end + + menu.with_item( + tag: :a, + label: t('js.label_export'), + href: "TODO", + content_arguments: { data: { controller: "async-dialog" }, rel: "nofollow" } + ) do |item| + item.with_leading_visual_icon(icon: 'sign-out') + end + + if can_delete? + menu.with_item( + tag: :a, + label: t(:button_delete), + scheme: :danger, + href: "TODO", + content_arguments: { data: { controller: "async-dialog" }} + ) do |item| + item.with_leading_visual_icon(icon: 'trash') + end + end + end + end +%> diff --git a/app/components/work_packages/index_page_header_component.rb b/app/components/work_packages/index_page_header_component.rb new file mode 100644 index 000000000000..405ae979aa23 --- /dev/null +++ b/app/components/work_packages/index_page_header_component.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +# ++ + +class WorkPackages::IndexPageHeaderComponent < ApplicationComponent + include OpPrimer::ComponentHelpers + include ApplicationHelper + + def initialize(query: nil, project: nil) + super + + @query = query + @project = project + end + + def breadcrumb_items + items = [{ href: @project ? project_work_packages_path : work_packages_path, text: t(:label_work_package_plural) }, + title] + + if @project + items.prepend({ href: project_overview_path(@project.id), text: @project.name }) + end + + items + end + + def title + @query&.name ? @query.name : t(:label_work_package_plural) + end + + def baseline_button_callback + lambda do |button| + button.with_leading_visual_icon(icon: :"op-baseline") + I18n.t("js.baseline.toggle_title") + end + end + + def project_include_button_callback + lambda do |button| + button.with_leading_visual_icon(icon: :"op-include-projects") + I18n.t("js.include_projects.toggle_title") + end + end + + def can_rename? + # TODO + true + end + + def can_save? + # TODO + true + end + + def can_save_as? + # TODO + true + end + + def can_delete? + # TODO + true + end +end diff --git a/app/components/work_packages/index_sub_header_component.html.erb b/app/components/work_packages/index_sub_header_component.html.erb new file mode 100644 index 000000000000..2f0c59055d55 --- /dev/null +++ b/app/components/work_packages/index_sub_header_component.html.erb @@ -0,0 +1,34 @@ +<%= render(Primer::OpenProject::SubHeader.new) do |subheader| %> + <%= + subheader.with_filter_component do + # TODO replace with FilterButtonComponent + render(Primer::Beta::Button.new(scheme: :secondary)) do |button| + button.with_trailing_visual_counter(count: 10) + t(:label_filter) + end + end + %> + + <%= + subheader.with_bottom_pane_component(mt: 0) do + # TODO render(Projects::ProjectsFiltersComponent.new(query: @query)) + end + %> + + <% if can_create_work_packages? %> + <% subheader.with_action_component do %> + <%= + render Primer::Alpha::ActionMenu.new do |menu| + menu.with_show_button(scheme: :primary, + aria: { label: I18n.t(:label_work_package_new) }) do |button| + button.with_trailing_action_icon(icon: :"triangle-down") + button.with_leading_visual_icon(icon: :plus) + I18n.t(:button_create) + end + menu.with_item(label: "TODO Subitem 1") + menu.with_item(label: "TODO Subitem 2") + end + %> + <% end %> + <% end %> +<% end %> diff --git a/app/components/work_packages/index_sub_header_component.rb b/app/components/work_packages/index_sub_header_component.rb new file mode 100644 index 000000000000..afdbe0ffa4aa --- /dev/null +++ b/app/components/work_packages/index_sub_header_component.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +# ++ + +module WorkPackages + class IndexSubHeaderComponent < ApplicationComponent + include ApplicationHelper + + def initialize(query: nil, project: nil) + super + @query = query + @project = project + end + + def can_create_work_packages? + # TODO + true + end + end +end diff --git a/app/controllers/work_packages/page_header_controller.rb b/app/controllers/work_packages/page_header_controller.rb new file mode 100644 index 000000000000..c17c11aa0af7 --- /dev/null +++ b/app/controllers/work_packages/page_header_controller.rb @@ -0,0 +1,37 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ +module WorkPackages + class PageHeaderController < ApplicationController + before_action :load_and_authorize_in_optional_project, only: [:index] + # TODO before_action :load_query, only: %i[index] + + def index + render layout: nil + end + end +end diff --git a/app/views/work_packages/page_header/index.html.erb b/app/views/work_packages/page_header/index.html.erb new file mode 100644 index 000000000000..f099eec596ed --- /dev/null +++ b/app/views/work_packages/page_header/index.html.erb @@ -0,0 +1,4 @@ +<%= turbo_frame_tag "work-package-index-page-header" do %> + <%= render WorkPackages::IndexPageHeaderComponent.new(project: @project) %> + <%= render WorkPackages::IndexSubHeaderComponent.new(project: @project) %> +<% end %> diff --git a/config/initializers/permissions.rb b/config/initializers/permissions.rb index d0ea7360287d..2ea7f8222882 100644 --- a/config/initializers/permissions.rb +++ b/config/initializers/permissions.rb @@ -217,7 +217,8 @@ work_packages: %i[show index], work_packages_api: [:get], "work_packages/reports": %i[report report_details], - "work_packages/menus": %i[show] + "work_packages/menus": %i[show], + "work_packages/page_header": %i[index] }, permissible_on: %i[work_package project], contract_actions: { work_packages: %i[read] } diff --git a/config/routes.rb b/config/routes.rb index f1c68581dc47..eb9293fc1745 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -319,6 +319,7 @@ get "/report/:detail" => "work_packages/reports#report_details" get "/report" => "work_packages/reports#report" get "menu" => "work_packages/menus#show" + get "index_page_header" => "work_packages/page_header#index" get "/export_dialog" => "work_packages#export_dialog" end @@ -549,6 +550,7 @@ namespace :work_packages do get "menu" => "menus#show" + get "index_page_header" => "page_header#index" match "auto_complete" => "auto_completes#index", via: %i[get post] resource :bulk, controller: "bulk", only: %i[edit update destroy] diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts index 9aa35c277222..cde7290db476 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts @@ -136,6 +136,9 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin { }, ]; + // TODO + PageHeaderTurboFrameSrc:string = ''; + constructor( readonly I18n:I18nService, readonly cdRef:ChangeDetectorRef, diff --git a/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.html b/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.html index ba769626b4ca..0db12c5a85af 100644 --- a/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.html +++ b/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.html @@ -1,6 +1,6 @@
-
+
+ + +
; @@ -87,6 +88,8 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp @InjectField() configuration:ConfigurationService; + @InjectField() pathHelper:PathHelperService; + text:{ [key:string]:string } = { jump_to_pagination: this.I18n.t('js.work_packages.jump_marks.pagination'), text_jump_to_pagination: this.I18n.t('js.work_packages.jump_marks.label_pagination'), @@ -130,9 +133,18 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp component: WorkPackageFilterContainerComponent, }; + PageHeaderTurboFrameSrc:string = ''; + ngOnInit():void { super.ngOnInit(); + // TODO: Limit to WP module + if (this.currentProject.inProjectContext) { + this.PageHeaderTurboFrameSrc = `${this.pathHelper.projectWorkPackagesPath(this.currentProject.identifier!)}/index_page_header`; + } else { + this.PageHeaderTurboFrameSrc = `${this.pathHelper.workPackagesPath()}/index_page_header`; + } + this.showToolbarSaveButton = !!this.$state.params.query_props; this.setPartition(this.$state.current); this.removeTransitionSubscription = this.$transitions.onSuccess({}, (transition):any => { diff --git a/frontend/src/global_styles/layout/work_packages/_mobile.sass b/frontend/src/global_styles/layout/work_packages/_mobile.sass index 8955fe375fcb..c79dd4b54c32 100644 --- a/frontend/src/global_styles/layout/work_packages/_mobile.sass +++ b/frontend/src/global_styles/layout/work_packages/_mobile.sass @@ -121,5 +121,7 @@ padding: 15px 0 !important .toolbar-container, - .work-packages--filters-optional-container + .work-packages--filters-optional-container, + page-header, + sub-header margin-left: 15px diff --git a/frontend/src/global_styles/openproject/_mixins.sass b/frontend/src/global_styles/openproject/_mixins.sass index 3f60adeef5ba..5b4cb4c3cbe4 100644 --- a/frontend/src/global_styles/openproject/_mixins.sass +++ b/frontend/src/global_styles/openproject/_mixins.sass @@ -242,7 +242,9 @@ $scrollbar-size: 10px .toolbar-container padding-right: 15px - .work-packages--filters-optional-container + .work-packages--filters-optional-container, + page-header, + sub-header margin-right: 15px @mixin modifying--placeholder