mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Merge branch 'release/16.1' into dev
This commit is contained in:
@@ -144,6 +144,21 @@ If you want to be reminded about a work package at a later point in time, you ca
|
||||
|
||||

|
||||
|
||||
A list with helpful pre-defined options will open, from which you can select:
|
||||
|
||||
- tomorrow
|
||||
- in 3 days
|
||||
- in a week
|
||||
- in a month
|
||||
- at a particular date/time
|
||||
|
||||
Selecting any of these options will display a modal. The time will be set to 9 am for the date you selected (apart from the last option). This modal allows you to adjust the pre-filled date and time and to add a note. This note will be visible when the reminder is triggered in Notification center.
|
||||
|
||||
> [!TIP]
|
||||
> All the pre-defined reminder options will be set to 9 am of the selected date.
|
||||
|
||||

|
||||
|
||||
Specify the time and date on which you would like to be reminded and optionally add a note for more context. Then click the **Set reminder** button.
|
||||
|
||||

|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 119 KiB |
@@ -218,6 +218,7 @@ import {
|
||||
import {
|
||||
OpWpDatePickerInstanceComponent,
|
||||
} from 'core-app/shared/components/datepicker/wp-date-picker-modal/wp-date-picker-instance.component';
|
||||
import { ReportingPageComponent } from 'core-app/features/reporting/reporting-page/reporting-page.component';
|
||||
|
||||
export function initializeServices(injector:Injector) {
|
||||
return () => {
|
||||
@@ -462,5 +463,6 @@ export class OpenProjectModule implements DoBootstrap {
|
||||
registerCustomElement('opce-global-search-tabs', GlobalSearchTabsComponent, { injector });
|
||||
registerCustomElement('opce-zen-mode-toggle-button', ZenModeButtonComponent, { injector });
|
||||
registerCustomElement('opce-colors-autocompleter', ColorsAutocompleterComponent, { injector });
|
||||
registerCustomElement('opce-reporting-page', ReportingPageComponent, { injector });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,12 +80,6 @@ export const OPENPROJECT_ROUTES:Ng2StateDeclaration[] = [
|
||||
url: '/bcf',
|
||||
loadChildren: () => import('../../features/bim/ifc_models/openproject-ifc-models.module').then((m) => m.OpenprojectIFCModelsModule),
|
||||
},
|
||||
{
|
||||
name: 'reporting.**',
|
||||
parent: 'optional_project',
|
||||
url: '/cost_reports',
|
||||
loadChildren: () => import('../../features/reporting/openproject-reporting.module').then((m) => m.OpenprojectReportingModule),
|
||||
},
|
||||
...TEAM_PLANNER_LAZY_ROUTES,
|
||||
...CALENDAR_LAZY_ROUTES,
|
||||
];
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
//-- 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 { NgModule } from '@angular/core';
|
||||
import { UIRouterModule } from '@uirouter/angular';
|
||||
import {
|
||||
REPORTING_ROUTES,
|
||||
} from 'core-app/features/reporting/openproject-reporting.routes';
|
||||
import { ReportingPageComponent } from 'core-app/features/reporting/reporting-page/reporting-page.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
// Routes for /cost_reports
|
||||
UIRouterModule.forChild({
|
||||
states: REPORTING_ROUTES,
|
||||
}),
|
||||
],
|
||||
declarations: [
|
||||
ReportingPageComponent,
|
||||
],
|
||||
})
|
||||
export class OpenprojectReportingModule {
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
//-- 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 { Ng2StateDeclaration } from '@uirouter/angular';
|
||||
import { ReportingPageComponent } from 'core-app/features/reporting/reporting-page/reporting-page.component';
|
||||
|
||||
export const REPORTING_ROUTES:Ng2StateDeclaration[] = [
|
||||
{
|
||||
name: 'reporting',
|
||||
parent: 'optional_project',
|
||||
url: '/cost_reports',
|
||||
component: ReportingPageComponent,
|
||||
},
|
||||
{
|
||||
name: 'reporting.show',
|
||||
url: '/:id',
|
||||
component: ReportingPageComponent,
|
||||
},
|
||||
];
|
||||
@@ -26,6 +26,8 @@
|
||||
// See COPYRIGHT and LICENSE files for more details.
|
||||
//++
|
||||
|
||||
import * as jQuery from 'jquery';
|
||||
|
||||
/*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */
|
||||
/*global window, $, $$, Reporting, Element */
|
||||
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { registerTableSorter } from 'core-app/features/reporting/reporting-page/functionality/tablesorter';
|
||||
|
||||
import './functionality/reporting_engine';
|
||||
import './functionality/reporting_engine/filters';
|
||||
import './functionality/reporting_engine/group_bys';
|
||||
import './functionality/reporting_engine/restore_query';
|
||||
import './functionality/reporting_engine/controls';
|
||||
|
||||
export const reportingPageComponentSelector = 'op-reporting-page';
|
||||
|
||||
@Component({
|
||||
selector: reportingPageComponentSelector,
|
||||
// Empty wrapper around legacy backlogs for CSS loading
|
||||
// that got removed in the Rails assets pipeline
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
@@ -18,10 +9,20 @@ export const reportingPageComponentSelector = 'op-reporting-page';
|
||||
styleUrls: [
|
||||
'./styles/reporting.sass',
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
export class ReportingPageComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
document.getElementById('projected-content')!.hidden = false;
|
||||
async ngOnInit() {
|
||||
// @ts-expect-error imported JS is not typed
|
||||
await import('./functionality/reporting_engine');
|
||||
// @ts-expect-error imported JS is not typed
|
||||
await import('./functionality/reporting_engine/filters');
|
||||
// @ts-expect-error imported JS is not typed
|
||||
await import('./functionality/reporting_engine/group_bys');
|
||||
// @ts-expect-error imported JS is not typed
|
||||
await import('./functionality/reporting_engine/restore_query');
|
||||
// @ts-expect-error imported JS is not typed
|
||||
await import('./functionality/reporting_engine/controls');
|
||||
|
||||
// Register table sorting functionality after reporting engine loaded
|
||||
registerTableSorter();
|
||||
|
||||
@@ -53,7 +53,9 @@ module Meetings::PDF
|
||||
|
||||
def write_section(section, section_index)
|
||||
write_optional_page_break
|
||||
write_section_title(section, section_index) unless section.title.empty?
|
||||
unless section.title.blank? && section_index == 0 && meeting.sections.count == 1
|
||||
write_section_title(section, section_index)
|
||||
end
|
||||
write_agenda_items(section)
|
||||
end
|
||||
|
||||
|
||||
@@ -130,12 +130,20 @@ RSpec.describe Meetings::Exporter do
|
||||
let(:type_task) { create(:type_task) }
|
||||
let(:status) { create(:status, is_default: true, name: "Workin' on it") }
|
||||
let(:work_package) { create(:work_package, project:, status:, subject: "Important task", type: type_task) }
|
||||
let(:meeting_section) { create(:meeting_section, meeting:, title: "Section Work in Progress") }
|
||||
let(:meeting_section) { create(:meeting_section, meeting:, title: nil) }
|
||||
let(:meeting_section_second) { create(:meeting_section, meeting:, title: "Second section") }
|
||||
let(:meeting_agenda_item) do
|
||||
create(:meeting_agenda_item, meeting_section:, duration_in_minutes: 15, title: "Agenda Item TOP 1", presenter: user,
|
||||
notes: "**foo**")
|
||||
end
|
||||
let(:wp_agenda_item) { create(:wp_meeting_agenda_item, meeting:, work_package:, duration_in_minutes: 10, notes: "*bar*") }
|
||||
let(:wp_agenda_item) do
|
||||
create(:wp_meeting_agenda_item,
|
||||
meeting:,
|
||||
meeting_section: meeting_section_second,
|
||||
work_package:,
|
||||
duration_in_minutes: 10,
|
||||
notes: "*bar*")
|
||||
end
|
||||
let(:outcome) { create(:meeting_outcome, meeting_agenda_item:, notes: "An outcome") }
|
||||
let(:attachment) { create(:attachment, container: meeting) }
|
||||
let(:meeting_backlog_item) do
|
||||
@@ -173,13 +181,15 @@ RSpec.describe Meetings::Exporter do
|
||||
|
||||
"Meeting agenda",
|
||||
|
||||
"Section Work in Progress", " ", "25 mins",
|
||||
"Untitled section", " ", "15 mins",
|
||||
|
||||
"Agenda Item TOP 1", " ", "15 mins", " ", "Export User",
|
||||
"foo",
|
||||
"Outcome",
|
||||
"An outcome",
|
||||
|
||||
"Second section", " ", "10 mins",
|
||||
|
||||
"Task", "##{work_package.id}", "Important task", " (Workin' on it)", " ", "10 mins",
|
||||
"bar",
|
||||
|
||||
@@ -215,11 +225,11 @@ RSpec.describe Meetings::Exporter do
|
||||
*single_meeting_head,
|
||||
"Meeting agenda",
|
||||
|
||||
"Section Work in Progress", " ", "25 mins",
|
||||
|
||||
"Untitled section", " ", "15 mins",
|
||||
"Agenda Item TOP 1", " ", "15 mins", " ", "Export User",
|
||||
"foo",
|
||||
|
||||
"Second section", " ", "10 mins",
|
||||
"Task", "##{work_package.id}", "Important task", " (Workin' on it)", " ", "10 mins",
|
||||
"bar",
|
||||
|
||||
|
||||
@@ -67,8 +67,6 @@ class CostReportsController < ApplicationController
|
||||
|
||||
before_action :set_cost_types # has to be set AFTER the Report::Controller filters run
|
||||
|
||||
layout "angular/angular"
|
||||
|
||||
# Checks if custom fields have been updated, added or removed since we
|
||||
# last saw them, to rebuild the filters and group bys.
|
||||
# Called once per request.
|
||||
|
||||
@@ -35,6 +35,8 @@ See COPYRIGHT and LICENSE files for more details.
|
||||
|
||||
<% html_title (@query.persisted? ? "#{t(:label_cost_report)}: #{@query.name}" : t(:label_new_report)) %>
|
||||
|
||||
<%= angular_component_tag "opce-reporting-page" %>
|
||||
|
||||
<%= render CostReports::IndexPageHeaderComponent.new(query: @query, project: @project) %>
|
||||
|
||||
<%= render_widget Widget::Settings, @query, cost_types: @cost_types, selected_type_id: @unit_id %>
|
||||
|
||||
@@ -85,34 +85,5 @@ RSpec.describe "project menu" do
|
||||
it_behaves_like "it leads to the project costs reports"
|
||||
end
|
||||
end
|
||||
|
||||
describe "link to global cost reports" do
|
||||
shared_examples "it leads to the cost reports" do
|
||||
before do
|
||||
visit current_path
|
||||
end
|
||||
|
||||
it "leads to cost reports" do
|
||||
# doing what no human can - click on invisible items.
|
||||
# This way, we avoid having to use selenium and by that increase stability.
|
||||
find("#main-menu #{test_selector('op-menu--item-action')}", text: "Time and costs").click
|
||||
|
||||
# to make sure we're not seeing the project cost reports:
|
||||
expect(page).to have_no_text("Ponyo")
|
||||
end
|
||||
end
|
||||
|
||||
context "when on the project's activity page" do
|
||||
let(:current_path) { "/projects/ponyo/activity" }
|
||||
|
||||
it_behaves_like "it leads to the cost reports"
|
||||
end
|
||||
|
||||
context "when on the project's calendar" do
|
||||
let(:current_path) { "/projects/ponyo/calendars" }
|
||||
|
||||
it_behaves_like "it leads to the cost reports"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user