Merge pull request #8073 from opf/bim/feature/31979-Allow-filtering-WPs-within-IFC-Models-module
[31979] Allow filtering WPs within IFC Models module [ci skip]pull/8086/head
commit
041dc3fbdd
@ -0,0 +1,74 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 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 docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {Injectable, Injector} from '@angular/core'; |
||||
import {WorkPackagesListChecksumService} from "core-components/wp-list/wp-list-checksum.service"; |
||||
import {WorkPackagesListService} from "core-components/wp-list/wp-list.service"; |
||||
import {TransitionService} from "@uirouter/core"; |
||||
import {Subject} from "rxjs"; |
||||
|
||||
@Injectable() |
||||
export class QueryParamListenerService { |
||||
readonly wpListChecksumService:WorkPackagesListChecksumService = this.injector.get(WorkPackagesListChecksumService); |
||||
readonly wpListService:WorkPackagesListService = this.injector.get(WorkPackagesListService); |
||||
readonly $transitions:TransitionService = this.injector.get(TransitionService); |
||||
|
||||
public observe$ = new Subject<any>(); |
||||
public queryChangeListener:Function; |
||||
|
||||
constructor(readonly injector:Injector) { |
||||
this.listenForQueryParamsChanged(); |
||||
} |
||||
|
||||
public listenForQueryParamsChanged():any { |
||||
// Listen for param changes
|
||||
return this.queryChangeListener = this.$transitions.onSuccess({}, (transition):any => { |
||||
let options = transition.options(); |
||||
const params = transition.params('to'); |
||||
|
||||
let newChecksum = this.wpListService.getCurrentQueryProps(params); |
||||
let newId:string = params.query_id ? params.query_id.toString() : null; |
||||
|
||||
// Avoid performing any changes when we're going to reload
|
||||
if (options.reload || (options.custom && options.custom.notify === false)) { |
||||
return true; |
||||
} |
||||
|
||||
return this.wpListChecksumService |
||||
.executeIfOutdated(newId, |
||||
newChecksum, |
||||
() => { |
||||
this.observe$.next(newChecksum); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
public removeQueryChangeListener() { |
||||
this.queryChangeListener(); |
||||
} |
||||
} |
@ -1,51 +1,84 @@ |
||||
import {Component} from "@angular/core"; |
||||
import {ChangeDetectorRef, Component, Injector, OnDestroy, OnInit} from "@angular/core"; |
||||
import {I18nService} from "core-app/modules/common/i18n/i18n.service"; |
||||
import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service"; |
||||
import {CurrentProjectService} from "core-components/projects/current-project.service"; |
||||
import {WorkPackageTableConfigurationObject} from "core-components/wp-table/wp-table-configuration"; |
||||
import { StateService } from '@uirouter/core'; |
||||
import {StateService} from '@uirouter/core'; |
||||
import {GonService} from "core-app/modules/common/gon/gon.service"; |
||||
import {QueryParamListenerService} from "core-components/wp-query/query-param-listener.service"; |
||||
import {InjectField} from "core-app/helpers/angular/inject-field.decorator"; |
||||
import {WorkPackagesListService} from "core-components/wp-list/wp-list.service"; |
||||
import {UrlParamsHelperService} from "core-components/wp-query/url-params-helper"; |
||||
import {untilComponentDestroyed} from "ng2-rx-componentdestroyed"; |
||||
|
||||
@Component({ |
||||
templateUrl: './bcf-container.component.html' |
||||
templateUrl: './bcf-container.component.html', |
||||
providers: [ |
||||
QueryParamListenerService |
||||
] |
||||
}) |
||||
export class BCFContainerComponent { |
||||
export class BCFContainerComponent implements OnInit, OnDestroy { |
||||
@InjectField() public queryParamListener:QueryParamListenerService; |
||||
@InjectField() public wpListService:WorkPackagesListService; |
||||
@InjectField() public urlParamsHelper:UrlParamsHelperService; |
||||
|
||||
public queryProps:{ [key:string]:any }; |
||||
|
||||
public configuration:WorkPackageTableConfigurationObject = { |
||||
actionsColumnEnabled: false, |
||||
columnMenuEnabled: false, |
||||
contextMenuEnabled: false, |
||||
inlineCreateEnabled: false, |
||||
withFilters: true, |
||||
withFilters: false, |
||||
showFilterButton: false, |
||||
isCardView: true |
||||
}; |
||||
|
||||
private filters:any[] = []; |
||||
|
||||
constructor(readonly state:StateService, |
||||
readonly i18n:I18nService, |
||||
readonly paths:PathHelperService, |
||||
readonly currentProject:CurrentProjectService, |
||||
readonly gon:GonService) { |
||||
readonly gon:GonService, |
||||
readonly injector:Injector, |
||||
readonly cdRef:ChangeDetectorRef) { |
||||
} |
||||
|
||||
ngOnInit():void { |
||||
this.refresh(); |
||||
|
||||
this.applyFilters(); |
||||
this.queryParamListener |
||||
.observe$ |
||||
.pipe( |
||||
untilComponentDestroyed(this) |
||||
).subscribe((queryProps) => { |
||||
this.refresh(this.urlParamsHelper.buildV3GetQueryFromJsonParams(queryProps)); |
||||
}); |
||||
} |
||||
|
||||
private applyFilters() { |
||||
// TODO: Limit to project
|
||||
this.filters.push({ |
||||
ngOnDestroy():void { |
||||
this.queryParamListener.removeQueryChangeListener(); |
||||
} |
||||
|
||||
private defaultQueryProps() { |
||||
let filters = []; |
||||
filters.push({ |
||||
status: { |
||||
operator: 'o', |
||||
values: [] |
||||
} |
||||
}); |
||||
|
||||
this.queryProps = { |
||||
return { |
||||
'columns[]': ['id', 'subject'], |
||||
filters: JSON.stringify(this.filters), |
||||
filters: JSON.stringify(filters), |
||||
sortBy: JSON.stringify([['updatedAt', 'desc']]), |
||||
showHierarchies: false |
||||
}; |
||||
} |
||||
|
||||
public refresh(queryProps:{ [key:string]:any }|undefined = undefined) { |
||||
this.wpListService.loadCurrentQueryFromParams(this.currentProject.identifier!); |
||||
this.queryProps = queryProps || this.state.params.query_props || this.defaultQueryProps(); |
||||
this.cdRef.detectChanges(); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,133 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 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-2017 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 docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
require_relative '../support/pages/ifc_models/show' |
||||
require_relative '../support/pages/ifc_models/show_default' |
||||
|
||||
describe 'BIM filter spec', type: :feature, js: true do |
||||
let(:project) { FactoryBot.create :project, enabled_module_names: %w(bim work_package_tracking) } |
||||
let(:open_status) { FactoryBot.create(:status, is_closed: false) } |
||||
let(:closed_status) { FactoryBot.create(:status, is_closed: true) } |
||||
|
||||
let(:wp1) { FactoryBot.create(:work_package, project: project, status: open_status) } |
||||
let(:wp2) { FactoryBot.create(:work_package, project: project, status: closed_status) } |
||||
|
||||
let(:admin) { FactoryBot.create :admin } |
||||
|
||||
let!(:model) do |
||||
FactoryBot.create(:ifc_model_converted, |
||||
project: project, |
||||
uploader: admin) |
||||
end |
||||
|
||||
let(:card_view) { ::Pages::WorkPackageCards.new(project) } |
||||
let(:filters) { ::Components::WorkPackages::Filters.new } |
||||
let(:model_page) { ::Pages::IfcModels::ShowDefault.new project } |
||||
|
||||
before do |
||||
wp1 |
||||
wp2 |
||||
|
||||
login_as(admin) |
||||
model_page.visit! |
||||
model_page.finished_loading |
||||
end |
||||
|
||||
context 'on default page' do |
||||
before do |
||||
# Per default all open work packages are shown |
||||
filters.expect_loaded |
||||
filters.expect_filter_count 1 |
||||
filters.open |
||||
filters.expect_filter_by('Status', 'open', nil) |
||||
|
||||
card_view.expect_work_package_listed wp1 |
||||
card_view.expect_work_package_not_listed wp2 |
||||
end |
||||
|
||||
it 'shows a filter button when there is a list shown' do |
||||
model_page.page_shows_a_filter_button true |
||||
|
||||
model_page.switch_view 'Viewer only' |
||||
model_page.page_shows_a_filter_button false |
||||
end |
||||
|
||||
it 'the filter is applied even after browser back' do |
||||
# Change filter |
||||
filters.set_operator('Status', 'closed', nil) |
||||
filters.expect_filter_count 1 |
||||
|
||||
# Otherwise the check for the loading indicator is done |
||||
# before it is even shown and the next steps will fail |
||||
sleep 0.5 |
||||
loading_indicator_saveguard |
||||
|
||||
card_view.expect_work_package_not_listed wp1 |
||||
card_view.expect_work_package_listed wp2 |
||||
|
||||
# Using the browser back will reload the filter and the work packages |
||||
page.go_back |
||||
loading_indicator_saveguard |
||||
|
||||
filters.expect_loaded |
||||
filters.expect_filter_count 1 |
||||
filters.expect_filter_by('Status', 'open', nil) |
||||
|
||||
card_view.expect_work_package_listed wp1 |
||||
card_view.expect_work_package_not_listed wp2 |
||||
end |
||||
|
||||
it 'the filter is applied even after reload' do |
||||
# Change filter |
||||
filters.set_operator('Status', 'closed', nil) |
||||
filters.expect_filter_count 1 |
||||
|
||||
# Otherwise the check for the loading indicator is done |
||||
# before it is even shown and the next steps will fail |
||||
sleep 0.5 |
||||
loading_indicator_saveguard |
||||
|
||||
card_view.expect_work_package_not_listed wp1 |
||||
card_view.expect_work_package_listed wp2 |
||||
|
||||
# Reload and the filter is still correctly applied |
||||
page.driver.browser.navigate.refresh |
||||
loading_indicator_saveguard |
||||
|
||||
filters.expect_loaded |
||||
filters.expect_filter_count 1 |
||||
filters.open |
||||
filters.expect_filter_by('Status', 'closed', nil) |
||||
|
||||
card_view.expect_work_package_not_listed wp1 |
||||
card_view.expect_work_package_listed wp2 |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue