Add a tab for project attributes within the WP view

This commit is contained in:
Henriette Darge
2026-06-12 14:34:44 +02:00
parent 20e9771457
commit a1fded27ae
10 changed files with 215 additions and 3 deletions
@@ -0,0 +1,19 @@
<turbo-frame id="work-package-project-attributes-tab-content">
<%=
component_wrapper do
if project_custom_fields_grouped_by_section.any?
render(Primer::OpenProject::SidePanel.new(spacious: true)) do |panel|
project_custom_fields_grouped_by_section.each do |project_custom_field_section, project_custom_fields|
panel.with_section(
Overviews::ProjectCustomFields::SectionComponent.new(
project: @project,
project_custom_field_section:,
project_custom_fields:
)
)
end
end
end
end
%>
</turbo-frame>
@@ -0,0 +1,49 @@
# 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::ProjectAttributesTabComponent < ApplicationComponent
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable
def initialize(work_package:)
super
@work_package = work_package
@project = work_package.project
end
private
def project_custom_fields_grouped_by_section
@project_custom_fields_grouped_by_section ||=
@project.available_custom_fields
.group_by(&:project_custom_field_section)
end
end
@@ -0,0 +1,47 @@
# frozen_string_literal: true
# -- 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.
# ++
class WorkPackages::ProjectAttributesTabController < ApplicationController
before_action :find_work_package
before_action :authorize
def index
render(WorkPackages::ProjectAttributesTabComponent.new(work_package: @work_package))
end
private
def find_work_package
@work_package = WorkPackage.visible.find(params.expect(:id))
@project = @work_package.project
rescue ActiveRecord::RecordNotFound
render_404
end
end
+2 -1
View File
@@ -314,7 +314,8 @@ Rails.application.reloader.to_prepare do
"work_packages/menus": %i[show],
"work_packages/hover_card": %i[show],
work_package_relations_tab: %i[index],
"work_packages/reminders": %i[modal_body create update destroy]
"work_packages/reminders": %i[modal_body create update destroy],
"work_packages/project_attributes_tab": %i[index]
},
permissible_on: %i[work_package project],
contract_actions: { work_packages: %i[read] }
+3 -2
View File
@@ -968,11 +968,12 @@ en:
relation_filters:
filter_work_packages_by_relation_type: "Filter work packages by relation type"
tabs:
overview: Overview
activity: Activity
files: Files
project_attributes: Project attributes
overview: Overview
relations: Relations
watchers: Watchers
files: Files
time_relative:
days: "days"
weeks: "weeks"
+1
View File
@@ -909,6 +909,7 @@ Rails.application.routes.draw do
concerns :shareable
get "hover_card" => "work_packages/hover_card#show", on: :member
get "project_attributes" => "work_packages/project_attributes_tab#index", on: :member
get "generate_pdf_dialog" => "work_packages#generate_pdf_dialog", on: :member
post "generate_pdf" => "work_packages#generate_pdf", on: :member
@@ -0,0 +1,13 @@
<div id="work-package-project-attributes-container">
<turbo-frame [src]="turboFrameSrc" id="work-package-project-attributes-tab-content" loading="lazy">
<op-content-loader viewBox="0 0 100 100">
<svg:rect x="0" y="0" width="70" height="5" rx="1" />
<svg:rect x="75" y="0" width="25" height="5" rx="1" />
<svg:rect x="0" y="10" width="100" height="8" rx="1" />
<svg:rect x="0" y="25" width="100" height="12" rx="1" />
</op-content-loader>
</turbo-frame>
</div>
@@ -0,0 +1,71 @@
// -- 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,
inject,
Input,
OnInit,
} from '@angular/core';
import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
import { UIRouterGlobals } from '@uirouter/core';
@Component({
selector: 'op-project-attributes-tab',
templateUrl: './op-project-attributes-tab.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class WorkPackageProjectAttributesTabComponent implements OnInit {
readonly I18n = inject(I18nService);
readonly uiRouterGlobals = inject(UIRouterGlobals);
readonly pathHelper = inject(PathHelperService);
public turboFrameSrc:string;
public workPackageId:string;
@Input() public workPackage:WorkPackageResource;
ngOnInit() {
const { workPackageId } = this.uiRouterGlobals.params as unknown as { workPackageId:string };
this.workPackageId = (this.workPackage.id!) || workPackageId;
this.turboFrameSrc = this.buildTurboFrameSrc();
}
protected buildTurboFrameSrc():string {
const baseUrl = window.location.origin;
const url = new URL(`${this.pathHelper.staticBase}/work_packages/${this.workPackageId}/project_attributes`, baseUrl);
return url.toString();
}
}
@@ -59,6 +59,7 @@ import {
import {
workPackageFilesCount,
} from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-files-count.function';
import { WorkPackageProjectAttributesTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/project-attributes-tab/op-project-attributes-tab.component';
@Injectable({
providedIn: 'root',
@@ -146,6 +147,11 @@ export class WorkPackageTabsService {
count: workPackageNotificationsCount,
showCountAsBubble: true,
},
{
id: 'project_attributes',
component: WorkPackageProjectAttributesTabComponent,
name: I18n.t('js.work_packages.tabs.project_attributes'),
},
{
id: 'files',
component: WorkPackageFilesTabComponent,
@@ -409,6 +409,7 @@ import { WorkPackageFullViewEntryComponent } from 'core-app/features/work-packag
import {
WorkPackageSplitCreateEntryComponent,
} from 'core-app/features/work-packages/routing/wp-split-create/wp-split-create-entry.component';
import { WorkPackageProjectAttributesTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/project-attributes-tab/op-project-attributes-tab.component';
@NgModule({
imports: [
@@ -589,6 +590,9 @@ import {
// Files tab
WorkPackageFilesTabComponent,
// Project attributes tab
WorkPackageProjectAttributesTabComponent,
// Split view
WorkPackageDetailsViewButtonComponent,
WorkPackageSplitViewComponent,