diff --git a/app/components/work_packages/baseline/modal_dialog_component.html.erb b/app/components/work_packages/baseline/modal_dialog_component.html.erb
new file mode 100644
index 000000000000..d0bae082340a
--- /dev/null
+++ b/app/components/work_packages/baseline/modal_dialog_component.html.erb
@@ -0,0 +1,23 @@
+<%= render(Primer::Alpha::Dialog.new(
+ title: I18n.t("js.baseline.baseline_comparison"),
+ subtitle: subtitle_text,
+ size: :medium_portrait,
+ id: MODAL_ID
+)) do |dialog| %>
+ <% dialog.with_header(variant: :large) %>
+ <% dialog.with_body do %>
+ <%= helpers.angular_component_tag "opce-baseline",
+ inputs: {
+ showHeaderText: false,
+ } %>
+ <% end %>
+ <% dialog.with_footer do %>
+ <%= render(Primer::ButtonComponent.new(data: { "close-dialog-id": MODAL_ID })) { I18n.t(:button_cancel) } %>
+ <%= render(Primer::ButtonComponent.new(
+ data: {
+ "close-dialog-id": MODAL_ID,
+ },
+ scheme: :primary,
+ type: :submit)) { I18n.t(:button_apply) } %>
+ <% end %>
+<% end %>
diff --git a/app/components/work_packages/baseline/modal_dialog_component.rb b/app/components/work_packages/baseline/modal_dialog_component.rb
new file mode 100644
index 000000000000..cda496cc4f20
--- /dev/null
+++ b/app/components/work_packages/baseline/modal_dialog_component.rb
@@ -0,0 +1,55 @@
+# 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
+ module Baseline
+ class ModalDialogComponent < ApplicationComponent
+ MODAL_ID = "op-work-packages-baseline-dialog"
+ EXPORT_FORM_ID = "op-work-packages-baseline-dialog-form"
+ include OpTurbo::Streamable
+ include OpPrimer::ComponentHelpers
+
+ attr_reader :query, :project, :query_params
+
+ def initialize(query:, project:, title:)
+ super
+
+ @query = query
+ @project = project
+ @query_params = ::API::V3::Queries::QueryParamsRepresenter.new(query).to_url_query(merge_params: { columns: [], title: })
+ end
+
+ def subtitle_text
+ # TODO: show different text and EE icon if no EE licence
+ I18n.t("js.baseline.header_description")
+ end
+ end
+ end
+end
diff --git a/app/components/work_packages/index_page_header_component.html.erb b/app/components/work_packages/index_page_header_component.html.erb
index 7557119a1c7d..be01aa22a882 100644
--- a/app/components/work_packages/index_page_header_component.html.erb
+++ b/app/components/work_packages/index_page_header_component.html.erb
@@ -13,13 +13,20 @@
d.with_body { "TODO" }
end
- header.with_action_dialog(mobile_icon: :"op-baseline",
+ header.with_action_button(tag: :a,
+ 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" }
+ href: @project.present? ? baseline_dialog_project_work_packages_path(@project) : baseline_dialog_project_work_packages_path(),
+ aria: { label: I18n.t("js.baseline.toggle_title") },
+ title: I18n.t("js.baseline.toggle_title"),
+ data: { controller: "async-dialog" },
+ rel: "nofollow") do |button|
+ button.with_leading_visual_icon(icon: :"op-baseline")
+ I18n.t("js.baseline.toggle_title")
end
+
+
header.with_action_zen_mode_button
header.with_action_menu(
diff --git a/app/components/work_packages/index_page_header_component.rb b/app/components/work_packages/index_page_header_component.rb
index 405ae979aa23..92a395a5703a 100644
--- a/app/components/work_packages/index_page_header_component.rb
+++ b/app/components/work_packages/index_page_header_component.rb
@@ -54,13 +54,6 @@ 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")
diff --git a/app/controllers/work_packages_controller.rb b/app/controllers/work_packages_controller.rb
index 69d6ca48e7c6..cd0270b29777 100644
--- a/app/controllers/work_packages_controller.rb
+++ b/app/controllers/work_packages_controller.rb
@@ -40,11 +40,11 @@ class WorkPackagesController < ApplicationController
:project, only: :show
before_action :check_allowed_export,
:protect_from_unauthorized_export, only: %i[index export_dialog]
- before_action :find_optional_project, only: %i[split_view split_create]
+ before_action :find_optional_project, only: %i[split_view split_create baseline_dialog]
before_action :load_and_authorize_in_optional_project, only: %i[index export_dialog new copy]
- authorization_checked! :index, :show, :new, :copy, :export_dialog, :split_view, :split_create
+ authorization_checked! :index, :show, :new, :copy, :export_dialog, :split_view, :split_create, :baseline_dialog
- before_action :load_and_validate_query, only: %i[index split_view split_create copy]
+ before_action :load_and_validate_query, only: %i[index split_view split_create copy baseline_dialog]
before_action :load_work_packages, only: :index, if: -> { request.format.atom? }
before_action :load_and_validate_query_for_export, only: :export_dialog
@@ -116,6 +116,10 @@ def export_dialog
respond_with_dialog WorkPackages::Exports::ModalDialogComponent.new(query: @query, project: @project, title: params[:title])
end
+ def baseline_dialog
+ respond_with_dialog WorkPackages::Baseline::ModalDialogComponent.new(query: @query, project: @project, title: params[:title])
+ end
+
protected
def split_view_base_route
diff --git a/config/routes.rb b/config/routes.rb
index e68138d46590..83287243a05a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -344,6 +344,7 @@
get "/report" => "work_packages/reports#report"
get "menu" => "work_packages/menus#show"
get "/export_dialog" => "work_packages#export_dialog"
+ get "/baseline_dialog" => "work_packages#baseline_dialog"
end
get "/copy" => "work_packages#copy", on: :member, as: "copy"
@@ -616,6 +617,7 @@
end
get "/export_dialog" => "work_packages#export_dialog", on: :collection, as: "export_dialog"
+ get "/baseline_dialog" => "work_packages#baseline_dialog", on: :collection, as: "baseline_dialog"
get "/split_view/update_counter" => "work_packages/split_view#update_counter",
on: :member
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index c9e9202560d7..37b326c93ead 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -168,6 +168,7 @@ import { WorkPackageSplitCreateEntryComponent } from 'core-app/features/work-pac
import { WorkPackageFullCopyEntryComponent } from 'core-app/features/work-packages/routing/wp-full-copy/wp-full-copy-entry.component';
import { WorkPackageFullCreateEntryComponent } from 'core-app/features/work-packages/routing/wp-full-create/wp-full-create-entry.component';
import { WorkPackageFullViewEntryComponent } from 'core-app/features/work-packages/routing/wp-full-view/wp-full-view-entry.component';
+import { OpBaselineEntryComponent } from 'core-app/features/work-packages/components/wp-baseline/baseline/baseline-entry.component';
export function initializeServices(injector:Injector) {
return () => {
@@ -364,6 +365,7 @@ export class OpenProjectModule implements DoBootstrap {
registerCustomElement('opce-wp-full-view', WorkPackageFullViewEntryComponent, { injector });
registerCustomElement('opce-wp-full-create', WorkPackageFullCreateEntryComponent, { injector });
registerCustomElement('opce-wp-full-copy', WorkPackageFullCopyEntryComponent, { injector });
+ registerCustomElement('opce-baseline', OpBaselineEntryComponent, { injector });
registerCustomElement('opce-timer-account-menu', TimerAccountMenuComponent, { injector });
registerCustomElement('opce-remote-field-updater', RemoteFieldUpdaterComponent, { injector });
registerCustomElement('opce-modal-single-date-picker', OpModalSingleDatePickerComponent, { injector });
diff --git a/frontend/src/app/features/work-packages/components/wp-baseline/baseline/baseline-entry.component.ts b/frontend/src/app/features/work-packages/components/wp-baseline/baseline/baseline-entry.component.ts
new file mode 100644
index 000000000000..098645c1db18
--- /dev/null
+++ b/frontend/src/app/features/work-packages/components/wp-baseline/baseline/baseline-entry.component.ts
@@ -0,0 +1,89 @@
+//-- 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.
+//++
+
+import {
+ ChangeDetectionStrategy,
+ Component,
+ ElementRef,
+ EventEmitter,
+ HostBinding,
+ Input,
+ OnInit,
+ Output,
+} from '@angular/core';
+
+import { I18nService } from 'core-app/core/i18n/i18n.service';
+import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin';
+import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service';
+import SpotDropAlignmentOption from 'core-app/spot/drop-alignment-options';
+import { WeekdayService } from 'core-app/core/days/weekday.service';
+import { DayResourceService } from 'core-app/core/state/days/day.service';
+import { IDay } from 'core-app/core/state/days/day.model';
+import { TimezoneService } from 'core-app/core/datetime/timezone.service';
+import { ConfigurationService } from 'core-app/core/config/configuration.service';
+import { Observable } from 'rxjs';
+import {
+ DEFAULT_TIMESTAMP,
+ WorkPackageViewBaselineService,
+} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-baseline.service';
+import { validDate } from 'core-app/shared/components/datepicker/helpers/date-modal.helpers';
+import {
+ baselineFilterFromValue,
+ getPartsFromTimestamp,
+ offsetToUtcString,
+} from 'core-app/features/work-packages/components/wp-baseline/baseline-helpers';
+import * as moment from 'moment-timezone';
+import { BannersService } from 'core-app/core/enterprise/banners.service';
+import { enterpriseDocsUrl } from 'core-app/core/setup/globals/constants.const';
+import { DayElement } from 'flatpickr/dist/types/instance';
+import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs';
+import { WorkPackageIsolatedQuerySpaceDirective } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive';
+
+
+@Component({
+ hostDirectives: [WorkPackageIsolatedQuerySpaceDirective],
+ template: `
+