# Conflicts: # modules/bim/app/views/bim/ifc_models/ifc_models/_panels.html.erbpull/9957/head
commit
e24e3f1ba4
@ -0,0 +1,13 @@ |
||||
import { ID } from '@datorama/akita'; |
||||
import { HalResourceLinks } from 'core-app/core/state/hal-resource'; |
||||
|
||||
export interface GroupHalResourceLinks extends HalResourceLinks { } |
||||
|
||||
export interface Group { |
||||
id:ID; |
||||
name:string; |
||||
createdAt:string; |
||||
updatedAt:string; |
||||
|
||||
_links:GroupHalResourceLinks; |
||||
} |
@ -0,0 +1,17 @@ |
||||
import { ID } from '@datorama/akita'; |
||||
import { HalResourceLink, HalResourceLinks } from 'core-app/core/state/hal-resource'; |
||||
|
||||
export interface PlaceholderUserHalResourceLinks extends HalResourceLinks { |
||||
updateImmediately:HalResourceLink; |
||||
delete:HalResourceLink; |
||||
showUser:HalResourceLink; |
||||
} |
||||
|
||||
export interface PlaceholderUser { |
||||
id:ID; |
||||
name:string; |
||||
createdAt:string; |
||||
updatedAt:string; |
||||
|
||||
_links:PlaceholderUserHalResourceLinks; |
||||
} |
@ -0,0 +1,5 @@ |
||||
import { User } from './user.model'; |
||||
import { Group } from './group.model'; |
||||
import { PlaceholderUser } from './placeholder-user.model'; |
||||
|
||||
export type Principal = User|Group|PlaceholderUser; |
@ -0,0 +1,10 @@ |
||||
import { QueryEntity } from '@datorama/akita'; |
||||
import { Observable } from 'rxjs'; |
||||
import { Principal } from './principal.model'; |
||||
import { PrincipalsState } from './principals.store'; |
||||
|
||||
export class PrincipalsQuery extends QueryEntity<PrincipalsState> { |
||||
public byIds(ids:string[]):Observable<Principal[]> { |
||||
return this.selectMany(ids); |
||||
} |
||||
} |
@ -0,0 +1,110 @@ |
||||
import { Injectable } from '@angular/core'; |
||||
import { |
||||
catchError, |
||||
tap, |
||||
} from 'rxjs/operators'; |
||||
import { Observable } from 'rxjs'; |
||||
import { |
||||
applyTransaction, |
||||
ID, |
||||
} from '@datorama/akita'; |
||||
import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; |
||||
import { ToastService } from 'core-app/shared/components/toaster/toast.service'; |
||||
import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type'; |
||||
import { HttpClient } from '@angular/common/http'; |
||||
import { PrincipalsQuery } from 'core-app/core/state/principals/principals.query'; |
||||
import { Apiv3ListParameters } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; |
||||
import { collectionKey } from 'core-app/core/state/collection-store'; |
||||
import { |
||||
EffectHandler, |
||||
} from 'core-app/core/state/effects/effect-handler.decorator'; |
||||
import { ActionsService } from 'core-app/core/state/actions/actions.service'; |
||||
import { PrincipalsStore } from './principals.store'; |
||||
import { Principal } from './principal.model'; |
||||
|
||||
@EffectHandler |
||||
@Injectable() |
||||
export class PrincipalsResourceService { |
||||
protected store = new PrincipalsStore(); |
||||
|
||||
readonly query = new PrincipalsQuery(this.store); |
||||
|
||||
private get principalsPath():string { |
||||
return this |
||||
.apiV3Service |
||||
.principals |
||||
.path; |
||||
} |
||||
|
||||
constructor( |
||||
readonly actions$:ActionsService, |
||||
private http:HttpClient, |
||||
private apiV3Service:APIV3Service, |
||||
private toastService:ToastService, |
||||
) { |
||||
} |
||||
|
||||
fetchPrincipals(params:Apiv3ListParameters):Observable<IHALCollection<Principal>> { |
||||
const collectionURL = collectionKey(params); |
||||
|
||||
return this |
||||
.http |
||||
.get<IHALCollection<Principal>>(this.principalsPath + collectionURL) |
||||
.pipe( |
||||
tap((events) => { |
||||
applyTransaction(() => { |
||||
this.store.add(events._embedded.elements); |
||||
this.store.update(({ collections }) => ( |
||||
{ |
||||
collections: { |
||||
...collections, |
||||
[collectionURL]: { |
||||
...collections[collectionURL], |
||||
ids: events._embedded.elements.map((el) => el.id), |
||||
}, |
||||
}, |
||||
} |
||||
)); |
||||
}); |
||||
}), |
||||
catchError((error) => { |
||||
this.toastService.addError(error); |
||||
throw error; |
||||
}), |
||||
); |
||||
} |
||||
|
||||
update(id:ID, principal:Partial<Principal>):void { |
||||
this.store.update(id, principal); |
||||
} |
||||
|
||||
modifyCollection(params:Apiv3ListParameters, callback:(collection:ID[]) => ID[]):void { |
||||
const key = collectionKey(params); |
||||
this.store.update(({ collections }) => ( |
||||
{ |
||||
collections: { |
||||
...collections, |
||||
[key]: { |
||||
...collections[key], |
||||
ids: [...callback(collections[key]?.ids || [])], |
||||
}, |
||||
}, |
||||
} |
||||
)); |
||||
} |
||||
|
||||
removeFromCollection(params:Apiv3ListParameters, ids:ID[]):void { |
||||
const key = collectionKey(params); |
||||
this.store.update(({ collections }) => ( |
||||
{ |
||||
collections: { |
||||
...collections, |
||||
[key]: { |
||||
...collections[key], |
||||
ids: (collections[key]?.ids || []).filter((id) => !ids.includes(id)), |
||||
}, |
||||
}, |
||||
} |
||||
)); |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
import { EntityStore, StoreConfig } from '@datorama/akita'; |
||||
import { Principal } from './principal.model'; |
||||
import { CollectionState, createInitialCollectionState } from 'core-app/core/state/collection-store'; |
||||
|
||||
export interface PrincipalsState extends CollectionState<Principal> { |
||||
} |
||||
|
||||
@StoreConfig({ name: 'principals' }) |
||||
export class PrincipalsStore extends EntityStore<PrincipalsState> { |
||||
constructor() { |
||||
super(createInitialCollectionState()); |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
import { ID } from '@datorama/akita'; |
||||
import { HalResourceLink, HalResourceLinks } from 'core-app/core/state/hal-resource'; |
||||
|
||||
export interface UserHalResourceLinks extends HalResourceLinks { |
||||
lock:HalResourceLink; |
||||
unlock:HalResourceLink; |
||||
delete:HalResourceLink; |
||||
showUser:HalResourceLink; |
||||
} |
||||
|
||||
export interface User { |
||||
id:ID; |
||||
name:string; |
||||
createdAt:string; |
||||
updatedAt:string; |
||||
|
||||
// Properties
|
||||
login:string; |
||||
|
||||
firstName:string; |
||||
|
||||
lastName:string; |
||||
|
||||
email:string; |
||||
|
||||
avatar:string; |
||||
|
||||
status:string; |
||||
|
||||
_links:UserHalResourceLinks; |
||||
} |
@ -1,37 +0,0 @@ |
||||
<div class="op-bcf-list-container--result-overlay" |
||||
*ngIf="(showResultOverlay$ | async) && showTableView"></div> |
||||
|
||||
<!-- TABLE + TIMELINE horizontal split --> |
||||
<div class="op-bcf-list-container--work-packages-split-view--tabletimeline-container" |
||||
[ngClass]="{ '_with-resizer': showResizerInCardView() }" |
||||
*ngIf="tableInformationLoaded && showTableView"> |
||||
<wp-resizer elementClass="work-packages-partitioned-page--content-right" |
||||
localStorageKey="openProject-splitViewFlexBasis"> |
||||
</wp-resizer> |
||||
|
||||
<wp-table [projectIdentifier]="CurrentProject.identifier" |
||||
[configuration]="wpTableConfiguration" |
||||
(itemClicked)="handleWorkPackageClicked($event)" |
||||
(stateLinkClicked)="openStateLink($event)" |
||||
class="work-packages-split-view--tabletimeline-content"> |
||||
</wp-table> |
||||
</div> |
||||
|
||||
<!-- GRID representation of the WP --> |
||||
<div *ngIf="!showTableView" |
||||
class="op-bcf-list-container--work-packages--card-view-container" |
||||
[ngClass]="{ '_with-resizer': showResizerInCardView() }" > |
||||
<wp-grid [configuration]="wpTableConfiguration" |
||||
[showResizer]="showResizerInCardView()" |
||||
(itemClicked)="handleWorkPackageCardClicked($event)" |
||||
(stateLinkClicked)="openStateLink($event)" |
||||
resizerClass="work-packages-partitioned-page--content-right" |
||||
resizerStorageKey="openProject-splitViewFlexBasis"> |
||||
</wp-grid> |
||||
</div> |
||||
|
||||
<!-- Footer --> |
||||
<div class="work-packages-split-view--tabletimeline-footer hide-when-print" |
||||
*ngIf="tableInformationLoaded"> |
||||
<wp-table-pagination></wp-table-pagination> |
||||
</div> |
@ -0,0 +1,38 @@ |
||||
<div class="op-bcf-list--result-overlay" |
||||
*ngIf="(showResultOverlay$ | async) && showTableView"></div> |
||||
|
||||
<!-- TABLE representation of the WP --> |
||||
<div class="op-bcf-list--work-packages-split-view--tabletimeline-container" |
||||
[ngClass]="{ '_with-resizer': showResizerInCardView() }" |
||||
*ngIf="tableInformationLoaded && showTableView"> |
||||
<wp-resizer *ngIf="showResizer" |
||||
elementClass="work-packages-partitioned-page--content-right" |
||||
localStorageKey="openProject-splitViewFlexBasis"> |
||||
</wp-resizer> |
||||
|
||||
<wp-table [projectIdentifier]="CurrentProject.identifier" |
||||
[configuration]="wpTableConfiguration" |
||||
(itemClicked)="handleWorkPackageClicked($event)" |
||||
(stateLinkClicked)="openStateLink($event)" |
||||
class="work-packages-split-view--tabletimeline-content"> |
||||
</wp-table> |
||||
</div> |
||||
|
||||
<!-- GRID representation of the WP --> |
||||
<div *ngIf="!showTableView" |
||||
class="op-bcf-list--work-packages--card-view-container" |
||||
[ngClass]="{ '_with-resizer': showResizerInCardView() }"> |
||||
<wp-grid [configuration]="wpTableConfiguration" |
||||
[showResizer]="showResizerInCardView()" |
||||
(itemClicked)="handleWorkPackageCardClicked($event)" |
||||
(stateLinkClicked)="openStateLink($event)" |
||||
resizerClass="work-packages-partitioned-page--content-right" |
||||
resizerStorageKey="openProject-splitViewFlexBasis"> |
||||
</wp-grid> |
||||
</div> |
||||
|
||||
<!-- Footer --> |
||||
<div class="work-packages-split-view--tabletimeline-footer hide-when-print" |
||||
*ngIf="tableInformationLoaded"> |
||||
<wp-table-pagination></wp-table-pagination> |
||||
</div> |
@ -1,6 +1,6 @@ |
||||
@import "src/assets/sass/helpers" |
||||
|
||||
.op-bcf-list-container |
||||
.op-bcf-list |
||||
&--result-overlay |
||||
@include overlay-background |
||||
background-color: #FFFFFF |
@ -1,24 +0,0 @@ |
||||
<div |
||||
class="work-packages--details work-packages--new" |
||||
*ngIf="newWorkPackage" |
||||
> |
||||
<edit-form [resource]="newWorkPackage" |
||||
[skippedFields]="['status', 'type']" |
||||
[inEditMode]="true" |
||||
(onSaved)="onSaved($event)"> |
||||
<div class="work-packages--details-content -create-mode"> |
||||
<div class="work-packages--new-details-header"> |
||||
<wp-type-status [workPackage]="newWorkPackage"></wp-type-status> |
||||
</div> |
||||
<wp-single-view [workPackage]="newWorkPackage" |
||||
[showProject]="copying"> |
||||
</wp-single-view> |
||||
</div> |
||||
|
||||
<div class="work-packages--details-toolbar-container"> |
||||
<wp-edit-actions-bar |
||||
(onCancel)="cancelAndBackToList()"> |
||||
</wp-edit-actions-bar> |
||||
</div> |
||||
</edit-form> |
||||
</div> |
@ -0,0 +1,7 @@ |
||||
<op-ifc-viewer |
||||
*ngIf="showViewer$ | async" |
||||
></op-ifc-viewer> |
||||
|
||||
<op-bcf-list |
||||
*ngIf="(showViewer$ | async) === false" |
||||
></op-bcf-list> |
@ -0,0 +1,4 @@ |
||||
<op-bcf-list |
||||
[showResizer]="true" |
||||
*ngIf="showWorkPackages$ | async" |
||||
></op-bcf-list> |
@ -0,0 +1,54 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2021 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, |
||||
OnInit, |
||||
} from '@angular/core'; |
||||
import { Observable } from 'rxjs'; |
||||
import { BcfViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bcf-view.service'; |
||||
import { map } from 'rxjs/operators'; |
||||
|
||||
@Component({ |
||||
templateUrl: './bcf-split-right.component.html', |
||||
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
selector: 'op-bcf-content-right', |
||||
}) |
||||
export class BcfSplitRightComponent implements OnInit { |
||||
showWorkPackages$:Observable<boolean>; |
||||
|
||||
constructor(private readonly bcfView:BcfViewService) {} |
||||
|
||||
ngOnInit():void { |
||||
this.showWorkPackages$ = this.bcfView.live$() |
||||
.pipe( |
||||
map((state) => state === 'splitTable' || state === 'splitCards'), |
||||
); |
||||
} |
||||
} |
@ -1,7 +0,0 @@ |
||||
import { Component } from '@angular/core'; |
||||
|
||||
@Component({ |
||||
template: '<div></div>', |
||||
}) |
||||
export class EmptyComponent { |
||||
} |
@ -0,0 +1,98 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2021 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 { Injectable } from '@angular/core'; |
||||
import { I18nService } from 'core-app/core/i18n/i18n.service'; |
||||
import { WorkPackageQueryStateService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service'; |
||||
import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; |
||||
import { QueryResource } from 'core-app/features/hal/resources/query-resource'; |
||||
import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; |
||||
|
||||
export const bcfCardsViewIdentifier = 'cards'; |
||||
export const bcfViewerViewIdentifier = 'viewer'; |
||||
export const bcfSplitViewTableIdentifier = 'splitTable'; |
||||
export const bcfSplitViewCardsIdentifier = 'splitCards'; |
||||
export const bcfTableViewIdentifier = 'table'; |
||||
|
||||
export type BcfViewState = 'cards'|'viewer'|'splitTable'|'splitCards'|'table'; |
||||
|
||||
@Injectable() |
||||
export class BcfViewService extends WorkPackageQueryStateService<BcfViewState> { |
||||
public text:{ [key:string]:string } = { |
||||
cards: this.I18n.t('js.views.card'), |
||||
viewer: this.I18n.t('js.ifc_models.views.viewer'), |
||||
splitTable: this.I18n.t('js.ifc_models.views.split'), |
||||
splitCards: this.I18n.t('js.ifc_models.views.split_cards'), |
||||
table: this.I18n.t('js.views.list'), |
||||
}; |
||||
|
||||
public icon:{ [key:string]:string } = { |
||||
cards: 'icon-view-card', |
||||
viewer: 'icon-view-model', |
||||
splitTable: 'icon-view-split-viewer-table', |
||||
splitCards: 'icon-view-split2', |
||||
table: 'icon-view-list', |
||||
}; |
||||
|
||||
constructor( |
||||
private readonly I18n:I18nService, |
||||
private readonly viewerBridgeService:ViewerBridgeService, |
||||
protected readonly querySpace:IsolatedQuerySpace, |
||||
) { |
||||
super(querySpace); |
||||
} |
||||
|
||||
hasChanged(query:QueryResource):boolean { |
||||
return this.current !== query.displayRepresentation; |
||||
} |
||||
|
||||
applyToQuery(query:QueryResource):boolean { |
||||
// eslint-disable-next-line no-param-reassign
|
||||
query.displayRepresentation = this.current; |
||||
return true; |
||||
} |
||||
|
||||
public valueFromQuery(query:QueryResource):BcfViewState|undefined { |
||||
const dr = query.displayRepresentation; |
||||
|
||||
switch (dr) { |
||||
case 'splitCards': |
||||
case 'splitTable': |
||||
case 'cards': |
||||
case 'table': |
||||
case 'viewer': |
||||
return dr; |
||||
default: |
||||
return this.viewerBridgeService.shouldShowViewer ? 'splitCards' : 'cards'; |
||||
} |
||||
} |
||||
|
||||
public currentViewerState():BcfViewState|undefined { |
||||
return this.current; |
||||
} |
||||
} |
@ -1,112 +0,0 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2021 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 { Injectable, OnDestroy } from '@angular/core'; |
||||
import { I18nService } from 'core-app/core/i18n/i18n.service'; |
||||
import { Observable } from 'rxjs'; |
||||
import { StateService, TransitionService } from '@uirouter/core'; |
||||
import { input } from 'reactivestates'; |
||||
import { takeUntil } from 'rxjs/operators'; |
||||
|
||||
export const bimListViewIdentifier = 'list'; |
||||
export const bimTableViewIdentifier = 'table'; |
||||
export const bimSplitViewCardsIdentifier = 'splitCards'; |
||||
export const bimSplitViewListIdentifier = 'splitList'; |
||||
export const bimViewerViewIdentifier = 'viewer'; |
||||
|
||||
export type BimViewState = 'list'|'viewer'|'splitList'|'splitCards'|'table'; |
||||
|
||||
@Injectable() |
||||
export class BimViewService implements OnDestroy { |
||||
private _state = input<BimViewState>(); |
||||
|
||||
public text:any = { |
||||
list: this.I18n.t('js.views.card'), |
||||
viewer: this.I18n.t('js.ifc_models.views.viewer'), |
||||
splitList: this.I18n.t('js.ifc_models.views.split'), |
||||
splitCards: this.I18n.t('js.ifc_models.views.split_cards'), |
||||
table: this.I18n.t('js.views.list'), |
||||
}; |
||||
|
||||
public icon:any = { |
||||
list: 'icon-view-card', |
||||
viewer: 'icon-view-model', |
||||
splitList: 'icon-view-split-viewer-table', |
||||
splitCards: 'icon-view-split2', |
||||
table: 'icon-view-list', |
||||
}; |
||||
|
||||
private transitionFn:Function; |
||||
|
||||
constructor(readonly I18n:I18nService, |
||||
readonly transitions:TransitionService, |
||||
readonly state:StateService) { |
||||
this.detectView(); |
||||
|
||||
this.transitionFn = this.transitions.onSuccess({}, (transition) => { |
||||
this.detectView(); |
||||
}); |
||||
} |
||||
|
||||
get view$():Observable<BimViewState> { |
||||
return this._state.values$(); |
||||
} |
||||
|
||||
public observeUntil(unsubscribe:Observable<any>) { |
||||
return this.view$.pipe(takeUntil(unsubscribe)); |
||||
} |
||||
|
||||
get current():BimViewState { |
||||
return this._state.getValueOr(bimSplitViewCardsIdentifier); |
||||
} |
||||
|
||||
public currentViewerState():BimViewState { |
||||
if (this.state.includes('bim.partitioned.list')) { |
||||
return this.state.params?.cards |
||||
? bimListViewIdentifier |
||||
: bimTableViewIdentifier; |
||||
} if (this.state.includes('bim.**.model')) { |
||||
return bimViewerViewIdentifier; |
||||
} if (this.state.includes('bim.partitioned.show')) { |
||||
return this.state.params?.cards || this.state.params?.cards == null |
||||
? bimListViewIdentifier |
||||
: bimTableViewIdentifier; |
||||
} |
||||
return this.state.params?.cards || this.state.params?.cards == null |
||||
? bimSplitViewCardsIdentifier |
||||
: bimSplitViewListIdentifier; |
||||
} |
||||
|
||||
private detectView() { |
||||
this._state.putValue(this.currentViewerState()); |
||||
} |
||||
|
||||
ngOnDestroy() { |
||||
this.transitionFn(); |
||||
} |
||||
} |
@ -0,0 +1,8 @@ |
||||
<op-autocompleter |
||||
data-qa-selector="tp-add-assignee" |
||||
(change)="selectUser($event)" |
||||
[fetchDataDirectly]="true" |
||||
[getOptionsFn]="getOptionsFn" |
||||
resource="user" |
||||
appendTo="body" |
||||
></op-autocompleter> |
@ -0,0 +1,96 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2021 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 { |
||||
Component, |
||||
ElementRef, |
||||
EventEmitter, |
||||
Injector, |
||||
Input, |
||||
Output, |
||||
ChangeDetectionStrategy, |
||||
} from '@angular/core'; |
||||
import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; |
||||
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; |
||||
import { I18nService } from 'core-app/core/i18n/i18n.service'; |
||||
import { Observable } from 'rxjs'; |
||||
import { map } from 'rxjs/operators'; |
||||
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; |
||||
import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; |
||||
import { HalResource } from 'core-app/features/hal/resources/hal-resource'; |
||||
import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; |
||||
import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; |
||||
|
||||
@Component({ |
||||
templateUrl: './add-assignee.component.html', |
||||
selector: 'op-tp-add-assignee', |
||||
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
}) |
||||
export class AddAssigneeComponent { |
||||
@Output() public selectAssignee = new EventEmitter<HalResource>(); |
||||
|
||||
@Input() alreadySelected:string[] = []; |
||||
|
||||
public getOptionsFn = (query:string):Observable<unknown[]> => this.autocomplete(query); |
||||
|
||||
constructor( |
||||
protected elementRef:ElementRef, |
||||
protected halResourceService:HalResourceService, |
||||
protected I18n:I18nService, |
||||
protected halNotification:HalResourceNotificationService, |
||||
readonly pathHelper:PathHelperService, |
||||
readonly apiV3Service:APIV3Service, |
||||
readonly injector:Injector, |
||||
readonly currentProjectService:CurrentProjectService, |
||||
) { } |
||||
|
||||
public autocomplete(term:string|null):Observable<HalResource[]> { |
||||
const filters = new ApiV3FilterBuilder(); |
||||
|
||||
filters.add('member', '=', [this.currentProjectService.id || '']); |
||||
|
||||
if (term) { |
||||
filters.add('name_and_identifier', '~', [term]); |
||||
} |
||||
|
||||
return this |
||||
.apiV3Service |
||||
.principals |
||||
.filtered(filters) |
||||
.get() |
||||
.pipe( |
||||
map((collection) => collection.elements.filter( |
||||
(user) => !this.alreadySelected.find((selected) => selected === user.id), |
||||
)), |
||||
); |
||||
} |
||||
|
||||
public selectUser(user:HalResource):void { |
||||
this.selectAssignee.emit(user); |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
.tp-assignee |
||||
display: flex |
||||
max-width: 100% |
||||
|
||||
&--principal |
||||
max-width: 100% |
||||
min-width: 0 // See: https://css-tricks.com/flexbox-truncated-text/ |
||||
flex-grow: 1 |
||||
flex-shrink: 1 |
||||
|
||||
&--remove |
||||
background: white |
||||
border-radius: 50% |
||||
margin: 0 |
||||
padding: 0 |
||||
padding-left: 0.5rem |
||||
flex-grow: 0 |
||||
flex-shrink: 0 |
||||
width: 0 |
||||
border: 0 |
||||
background: transparent |
||||
cursor: normal |
||||
pointer-events: none |
||||
opacity: 0 |
||||
|
||||
@at-root &--remove:focus, &:hover &--remove, #{$spec-active-selector} &--remove |
||||
width: unset |
||||
cursor: pointer |
||||
opacity: 1 |
||||
pointer-events: all |
@ -1,3 +1,5 @@ |
||||
@import "~app/features/team-planner/team-planner/planner/tp-assignee" |
||||
|
||||
.router--team-planner |
||||
#content |
||||
height: 100% |
||||
|
@ -0,0 +1,2 @@ |
||||
// A selector that checks whether we are running in a test environment |
||||
$spec-active-selector: '.env-test' |
Loading…
Reference in new issue