diff --git a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts index abc93cf65..7b689c84b 100644 --- a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts +++ b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts @@ -22,7 +22,7 @@ import { ButtonAction } from '../../models/button-action'; import { Pair } from '../../../shared/models/pair'; import { WorkbasketQueryFilterParameter } from '../../../shared/models/workbasket-query-filter-parameter'; import { FilterSelectors } from '../../../shared/store/filter-store/filter.selectors'; -import { SetFilter } from '../../../shared/store/filter-store/filter.actions'; +import { SetWorkbasketFilter } from '../../../shared/store/filter-store/filter.actions'; export enum Side { AVAILABLE, @@ -283,8 +283,10 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnDestroy this.availableDistributionTargetsFilterClone = this.availableDistributionTargets; this.selectAllRight = true; this.selectAllLeft = true; - this.store.dispatch(new SetFilter(this.selectedDistributionTargetsFilter, 'selectedDistributionTargets')); - this.store.dispatch(new SetFilter(this.availableDistributionTargetsFilter, 'availableDistributionTargets')); + this.store.dispatch(new SetWorkbasketFilter(this.selectedDistributionTargetsFilter, 'selectedDistributionTargets')); + this.store.dispatch( + new SetWorkbasketFilter(this.availableDistributionTargetsFilter, 'availableDistributionTargets') + ); } onClear() { diff --git a/web/src/app/shared/components/task-filter/task-filter.component.html b/web/src/app/shared/components/task-filter/task-filter.component.html index 4bb8f092e..b7f7dbc71 100644 --- a/web/src/app/shared/components/task-filter/task-filter.component.html +++ b/web/src/app/shared/components/task-filter/task-filter.component.html @@ -1,46 +1,39 @@ -
-
- +
+ + +
+ +
+ + + Filter by name + + + + + + Filter by priority + + +
+ +
+ + Filter by owner + + + + + + Filter by status + + + {{ state.value }} + + + +
+
-
- -
-
- -
- -
-
- - +
diff --git a/web/src/app/shared/components/task-filter/task-filter.component.scss b/web/src/app/shared/components/task-filter/task-filter.component.scss index 73f6630ad..f64f086a3 100644 --- a/web/src/app/shared/components/task-filter/task-filter.component.scss +++ b/web/src/app/shared/components/task-filter/task-filter.component.scss @@ -1,15 +1,34 @@ -.dropdown-menu-users { - & > li { - margin-bottom: 5px; +@import 'src/theme/_colors.scss'; + +.task-filter { + display: flex; + flex-direction: column; + + &__button--primary { + background: $aquamarine; + color: white; + + position: relative; + top: 30px; } - margin-left: 15px; -} + &__button--secondary { + position: relative; + top: 24px; + } -button.btn.btn-default.pull-right.margin-right { - margin-top: 1px; -} + .task-filter__row { + display: flex; + flex-direction: row; + } + + &__input-field--large { + width: 320px; + } + + &__input-field--small { + flex-grow: 1; + min-width: 140px; + } -.blue { - color: #2e9eca; } diff --git a/web/src/app/shared/components/task-filter/task-filter.component.ts b/web/src/app/shared/components/task-filter/task-filter.component.ts index dda3ec010..b8f94fe64 100644 --- a/web/src/app/shared/components/task-filter/task-filter.component.ts +++ b/web/src/app/shared/components/task-filter/task-filter.component.ts @@ -1,29 +1,39 @@ -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { ALL_STATES, TaskState } from '../../models/task-state'; import { TaskQueryFilterParameter } from '../../models/task-query-filter-parameter'; +import { Actions, ofActionCompleted, Store } from '@ngxs/store'; +import { ClearTaskFilter, SetTaskFilter } from '../../store/filter-store/filter.actions'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; @Component({ selector: 'taskana-shared-task-filter', templateUrl: './task-filter.component.html', styleUrls: ['./task-filter.component.scss'] }) -export class TaskFilterComponent implements OnInit { +export class TaskFilterComponent implements OnInit, OnDestroy { filter: TaskQueryFilterParameter; - - @Output() performFilter = new EventEmitter(); + destroy$ = new Subject(); allStates: Map = ALL_STATES; - ngOnInit(): void { + constructor(private store: Store, private ngxsActions$: Actions) {} + + ngOnInit() { this.clear(); + this.ngxsActions$.pipe(ofActionCompleted(ClearTaskFilter), takeUntil(this.destroy$)).subscribe(() => this.clear()); } - selectState(state: TaskState) { + setStatus(state: TaskState) { this.filter.state = state ? [state] : []; + this.updateState(); } - search() { - this.performFilter.emit(this.filter); + // TODO: filter tasks when pressing 'enter' + search() {} + + updateState() { + this.store.dispatch(new SetTaskFilter(this.filter)); } clear() { @@ -33,4 +43,9 @@ export class TaskFilterComponent implements OnInit { 'owner-like': [] }; } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/web/src/app/shared/components/workbasket-filter/workbasket-filter.component.ts b/web/src/app/shared/components/workbasket-filter/workbasket-filter.component.ts index b5f28462d..9c2a19052 100644 --- a/web/src/app/shared/components/workbasket-filter/workbasket-filter.component.ts +++ b/web/src/app/shared/components/workbasket-filter/workbasket-filter.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ALL_TYPES, WorkbasketType } from '../../models/workbasket-type'; import { WorkbasketQueryFilterParameter } from '../../models/workbasket-query-filter-parameter'; import { Select, Store } from '@ngxs/store'; -import { ClearFilter, SetFilter } from '../../store/filter-store/filter.actions'; +import { ClearWorkbasketFilter, SetWorkbasketFilter } from '../../store/filter-store/filter.actions'; import { FilterSelectors } from '../../store/filter-store/filter.selectors'; import { Observable, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -60,7 +60,7 @@ export class WorkbasketFilterComponent implements OnInit, OnDestroy { } clear() { - this.store.dispatch(new ClearFilter(this.component)); + this.store.dispatch(new ClearWorkbasketFilter(this.component)); } selectType(type: WorkbasketType) { @@ -68,7 +68,7 @@ export class WorkbasketFilterComponent implements OnInit, OnDestroy { } search() { - this.store.dispatch(new SetFilter(this.filter, this.component)); + this.store.dispatch(new SetWorkbasketFilter(this.filter, this.component)); } ngOnDestroy() { diff --git a/web/src/app/shared/store/filter-store/filter.actions.ts b/web/src/app/shared/store/filter-store/filter.actions.ts index a746cb681..f1bb75e42 100644 --- a/web/src/app/shared/store/filter-store/filter.actions.ts +++ b/web/src/app/shared/store/filter-store/filter.actions.ts @@ -1,11 +1,23 @@ import { WorkbasketQueryFilterParameter } from '../../models/workbasket-query-filter-parameter'; +import { TaskQueryFilterParameter } from '../../models/task-query-filter-parameter'; -export class SetFilter { - static readonly type = '[Workbasket filter] Set filter parameter'; +// Workbasket Filter +export class SetWorkbasketFilter { + static readonly type = '[Workbasket filter] Set workbasket filter parameter'; constructor(public parameters: WorkbasketQueryFilterParameter, public component: string) {} } -export class ClearFilter { - static readonly type = '[Workbasket filter] Clear filter parameter'; +export class ClearWorkbasketFilter { + static readonly type = '[Workbasket filter] Clear workbasket filter parameter'; constructor(public component: string) {} } + +// Task Filter +export class SetTaskFilter { + static readonly type = '[Task filter] Set task filter parameter'; + constructor(public parameters: TaskQueryFilterParameter) {} +} + +export class ClearTaskFilter { + static readonly type = '[Task filter] Clear task filter parameter'; +} diff --git a/web/src/app/shared/store/filter-store/filter.selectors.ts b/web/src/app/shared/store/filter-store/filter.selectors.ts index 06a0aff32..18f7a9eef 100644 --- a/web/src/app/shared/store/filter-store/filter.selectors.ts +++ b/web/src/app/shared/store/filter-store/filter.selectors.ts @@ -1,6 +1,7 @@ import { FilterState, FilterStateModel } from './filter.state'; import { Selector } from '@ngxs/store'; import { WorkbasketQueryFilterParameter } from '../../models/workbasket-query-filter-parameter'; +import { TaskQueryFilterParameter } from '../../models/task-query-filter-parameter'; export class FilterSelectors { @Selector([FilterState]) @@ -17,4 +18,9 @@ export class FilterSelectors { static getWorkbasketListFilter(state: FilterStateModel): WorkbasketQueryFilterParameter { return state.workbasketList; } + + @Selector([FilterState]) + static getTaskFilter(state: FilterStateModel): TaskQueryFilterParameter { + return state.tasks; + } } diff --git a/web/src/app/shared/store/filter-store/filter.state.ts b/web/src/app/shared/store/filter-store/filter.state.ts index b8a3a8b0f..bc50e7d2b 100644 --- a/web/src/app/shared/store/filter-store/filter.state.ts +++ b/web/src/app/shared/store/filter-store/filter.state.ts @@ -1,9 +1,10 @@ import { Action, NgxsOnInit, State, StateContext } from '@ngxs/store'; import { Observable, of } from 'rxjs'; import { WorkbasketQueryFilterParameter } from '../../models/workbasket-query-filter-parameter'; -import { ClearFilter, SetFilter } from './filter.actions'; +import { ClearTaskFilter, ClearWorkbasketFilter, SetTaskFilter, SetWorkbasketFilter } from './filter.actions'; +import { TaskQueryFilterParameter } from '../../models/task-query-filter-parameter'; -const emptyFilter: WorkbasketQueryFilterParameter = { +const emptyWorkbasketFilter: WorkbasketQueryFilterParameter = { 'description-like': [], 'key-like': [], 'name-like': [], @@ -11,10 +12,20 @@ const emptyFilter: WorkbasketQueryFilterParameter = { type: [] }; +const emptyTaskFilter: TaskQueryFilterParameter = { + 'name-like': [], + 'owner-like': [], + state: [], + priority: [], + 'por.value': [], + 'wildcard-search-fields': [], + 'wildcard-search-value': [] +}; + @State({ name: 'FilterState' }) export class FilterState implements NgxsOnInit { - @Action(SetFilter) - setAvailableDistributionTargetsFilter(ctx: StateContext, action: SetFilter): Observable { + @Action(SetWorkbasketFilter) + setWorkbasketFilter(ctx: StateContext, action: SetWorkbasketFilter): Observable { const currentState = ctx.getState()[action.component]; const param = action.parameters; const filter: WorkbasketQueryFilterParameter = { @@ -33,22 +44,66 @@ export class FilterState implements NgxsOnInit { return of(null); } - @Action(ClearFilter) - clearFilter(ctx: StateContext, action: ClearFilter): Observable { + @Action(ClearWorkbasketFilter) + clearWorkbasketFilter(ctx: StateContext, action: ClearWorkbasketFilter): Observable { ctx.setState({ ...ctx.getState(), - [action.component]: { ...emptyFilter } + [action.component]: { ...emptyWorkbasketFilter } }); return of(null); } + @Action(SetTaskFilter) + setTaskFilter(ctx: StateContext, action: SetTaskFilter): Observable { + const param = action.parameters; + let filter = { ...ctx.getState().tasks }; + + Object.keys(param).forEach((key) => { + filter[key] = [...param[key]]; + }); + + const isWildcardSearch = filter['wildcard-search-value'].length !== 0 && filter['wildcard-search-value'] !== ['']; + filter['wildcard-search-fields'] = isWildcardSearch ? this.initWildcardFields() : []; + + // Delete wildcard search field 'NAME' if 'name-like' exists + if (filter['name-like'].length > 0 && filter['name-like'][0] !== '') { + filter['wildcard-search-fields'].shift(); + } + + ctx.setState({ + ...ctx.getState(), + tasks: filter + }); + + return of(null); + } + + @Action(ClearTaskFilter) + clearTaskFilter(ctx: StateContext): Observable { + ctx.setState({ + ...ctx.getState(), + tasks: { ...emptyTaskFilter } + }); + + return of(null); + } + + initWildcardFields() { + let wildcardSearchFields = ['NAME', 'DESCRIPTION']; + [...Array(16).keys()].map((number) => { + wildcardSearchFields.push(`CUSTOM_${number + 1}`); + }); + return wildcardSearchFields; + } + ngxsOnInit(ctx: StateContext): void { ctx.setState({ ...ctx.getState(), - availableDistributionTargets: emptyFilter, - selectedDistributionTargets: emptyFilter, - workbasketList: emptyFilter + availableDistributionTargets: emptyWorkbasketFilter, + selectedDistributionTargets: emptyWorkbasketFilter, + workbasketList: emptyWorkbasketFilter, + tasks: emptyTaskFilter }); } } @@ -57,4 +112,5 @@ export interface FilterStateModel { availableDistributionTargets: WorkbasketQueryFilterParameter; selectedDistributionTargets: WorkbasketQueryFilterParameter; workbasketList: WorkbasketQueryFilterParameter; + tasks: TaskQueryFilterParameter; } diff --git a/web/src/app/shared/store/workbasket-store/workbasket.state.ts b/web/src/app/shared/store/workbasket-store/workbasket.state.ts index 1124afc08..790840e9c 100644 --- a/web/src/app/shared/store/workbasket-store/workbasket.state.ts +++ b/web/src/app/shared/store/workbasket-store/workbasket.state.ts @@ -37,7 +37,7 @@ import { RequestInProgressService } from '../../services/request-in-progress/req import { WorkbasketType } from '../../models/workbasket-type'; import { TaskanaDate } from '../../util/taskana.date'; import { DomainService } from '../../services/domain/domain.service'; -import { ClearFilter } from '../filter-store/filter.actions'; +import { ClearWorkbasketFilter } from '../filter-store/filter.actions'; class InitializeStore { static readonly type = '[Workbasket] Initializing state'; @@ -136,8 +136,8 @@ export class WorkbasketState implements NgxsAfterBootstrap { .replace(/(workbaskets).*/g, `workbaskets/(detail:${action.workbasketId})?tab=${selectedComponent}`) ); - ctx.dispatch(new ClearFilter('selectedDistributionTargets')); - ctx.dispatch(new ClearFilter('availableDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('selectedDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('availableDistributionTargets')); }) ); } @@ -224,8 +224,8 @@ export class WorkbasketState implements NgxsAfterBootstrap { badgeMessage: `Copying workbasket: ${workbasket.key}` }); - ctx.dispatch(new ClearFilter('selectedDistributionTargets')); - ctx.dispatch(new ClearFilter('availableDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('selectedDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('availableDistributionTargets')); return of(null); } @@ -261,8 +261,8 @@ export class WorkbasketState implements NgxsAfterBootstrap { workbasketDistributionTargets: distributionTargets }); - ctx.dispatch(new ClearFilter('selectedDistributionTargets')); - ctx.dispatch(new ClearFilter('availableDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('selectedDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('availableDistributionTargets')); return of(null); }) @@ -433,8 +433,8 @@ export class WorkbasketState implements NgxsAfterBootstrap { selectedWorkbasket, action: ACTION.READ }); - ctx.dispatch(new ClearFilter('selectedDistributionTargets')); - ctx.dispatch(new ClearFilter('availableDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('selectedDistributionTargets')); + ctx.dispatch(new ClearWorkbasketFilter('availableDistributionTargets')); }); } this.requestInProgressService.setRequestInProgress(false); diff --git a/web/src/app/workplace/components/task-list-toolbar/task-list-toolbar.component.html b/web/src/app/workplace/components/task-list-toolbar/task-list-toolbar.component.html index 252b14bb1..a67722b59 100644 --- a/web/src/app/workplace/components/task-list-toolbar/task-list-toolbar.component.html +++ b/web/src/app/workplace/components/task-list-toolbar/task-list-toolbar.component.html @@ -35,13 +35,13 @@ - Type + Filter by type - Value + Filter by value diff --git a/web/src/app/workplace/components/task-list/task-list.component.html b/web/src/app/workplace/components/task-list/task-list.component.html index ce8fc91bd..efe51155c 100644 --- a/web/src/app/workplace/components/task-list/task-list.component.html +++ b/web/src/app/workplace/components/task-list/task-list.component.html @@ -33,7 +33,7 @@
-

Select a workbasket

+

Select a Workbasket