taskana/web/src/app/shared/store/classification-store/classification.state.ts

267 lines
9.4 KiB
TypeScript

import { Action, NgxsAfterBootstrap, State, StateContext } from '@ngxs/store';
import { Observable, of } from 'rxjs';
import { mergeMap, take, tap } from 'rxjs/operators';
import { TaskanaDate } from 'app/shared/util/taskana.date';
import {
CategoriesResponse,
ClassificationCategoriesService
} from '../../services/classification-categories/classification-categories.service';
import {
CopyClassification,
CreateClassification,
DeselectClassification,
GetClassifications,
RemoveSelectedClassification,
RestoreSelectedClassification,
SaveCreatedClassification,
SaveModifiedClassification,
SelectClassification,
SetSelectedClassificationType,
UpdateClassification
} from './classification.actions';
import { ClassificationsService } from '../../services/classifications/classifications.service';
import { DomainService } from '../../services/domain/domain.service';
import { Classification } from '../../models/classification';
import { ClassificationSummary } from '../../models/classification-summary';
import { ClassificationQueryFilterParameter } from '../../models/classification-query-filter-parameter';
import { ClassificationQuerySortParameter, Direction, Sorting } from '../../models/sorting';
class InitializeStore {
static readonly type = '[ClassificationState] Initializing state';
}
@State<ClassificationStateModel>({ name: 'classification' })
export class ClassificationState implements NgxsAfterBootstrap {
constructor(
private categoryService: ClassificationCategoriesService,
private classificationsService: ClassificationsService,
private domainService: DomainService
) {}
@Action(InitializeStore)
initializeStore(ctx: StateContext<ClassificationStateModel>): Observable<any> {
return this.categoryService.getClassificationCategoriesByType().pipe(
take(1),
tap((classificationTypes) => {
ctx.patchState({
classificationTypes,
classifications: undefined,
selectedClassificationType: Object.keys(classificationTypes)[0]
});
})
);
}
@Action(SetSelectedClassificationType)
setSelectedClassificationType(
ctx: StateContext<ClassificationStateModel>,
action: SetSelectedClassificationType
): Observable<null> {
if (ctx.getState().classificationTypes[action.selectedType]) {
ctx.patchState({
selectedClassificationType: action.selectedType,
selectedClassification: undefined
});
}
return of(null);
}
@Action(SelectClassification)
selectClassification(
ctx: StateContext<ClassificationStateModel>,
action: SelectClassification
): Observable<Classification | null> {
if (typeof action.classificationId !== 'undefined') {
return this.classificationsService.getClassification(action.classificationId).pipe(
take(1),
tap((selectedClassification) =>
ctx.patchState({
selectedClassification,
selectedClassificationType: selectedClassification.type
})
)
);
}
return of(null);
}
@Action(DeselectClassification)
deselectClassification(ctx: StateContext<ClassificationStateModel>): Observable<null> {
ctx.patchState({
selectedClassification: undefined
});
return of(null);
}
@Action(GetClassifications)
getClassifications(ctx: StateContext<ClassificationStateModel>): Observable<any> {
const { selectedClassificationType } = ctx.getState();
return this.domainService.getSelectedDomain().pipe(
mergeMap((domain) => {
const filter: ClassificationQueryFilterParameter = {
domain: [domain],
type: [selectedClassificationType]
};
const sort: Sorting<ClassificationQuerySortParameter> = {
'sort-by': ClassificationQuerySortParameter.KEY,
order: Direction.ASC
};
return this.classificationsService.getClassifications(filter, sort).pipe(
take(1),
tap((list) => ctx.patchState({ classifications: list.classifications }))
);
}),
tap(() => this.domainService.domainChangedComplete())
);
}
@Action(SaveCreatedClassification)
// this action is called when a copied or a new classification is saved
createClassification(
ctx: StateContext<ClassificationStateModel>,
action: SaveCreatedClassification
): Observable<any> {
return this.classificationsService.postClassification(action.classification).pipe(
take(1),
tap((classification) =>
ctx.patchState({
classifications: [...ctx.getState().classifications, classification],
selectedClassification: classification
})
)
);
}
@Action(SaveModifiedClassification)
saveClassification(ctx: StateContext<ClassificationStateModel>, action: SaveModifiedClassification): Observable<any> {
return this.classificationsService.putClassification(action.classification).pipe(
take(1),
tap((savedClassification) =>
ctx.patchState({
classifications: updateClassificationList(ctx.getState().classifications, savedClassification),
selectedClassification: savedClassification
})
)
);
}
@Action(RestoreSelectedClassification)
restoreSelectedClassification(
ctx: StateContext<ClassificationStateModel>,
action: RestoreSelectedClassification
): Observable<any> {
const state = ctx.getState();
// check whether the classification already exists
// returns true in case the classification was edited or copied
if (state.classifications.some((classification) => classification.classificationId === action.classificationId)) {
return this.classificationsService.getClassification(action.classificationId).pipe(
take(1),
tap((selectedClassification) => {
ctx.patchState({ selectedClassification });
})
);
}
// the classification is restored to a new classification
const category = state.classificationTypes[state.selectedClassificationType][0];
const { type, created, modified, domain, parentId, parentKey } = state.selectedClassification;
ctx.patchState({ selectedClassification: { type, created, category, modified, domain, parentId, parentKey } });
return of(null);
}
@Action(CreateClassification)
newCreateClassification(ctx: StateContext<ClassificationStateModel>): Observable<null> {
// Initialization of a new classification
const state: ClassificationStateModel = ctx.getState();
const date = TaskanaDate.getDate();
const initialClassification: Classification = {
type: state.selectedClassificationType,
category: state.classificationTypes[state.selectedClassificationType][0],
created: date,
modified: date,
domain: this.domainService.getSelectedDomainValue()
};
if (state.selectedClassification) {
initialClassification.parentId = state.selectedClassification.classificationId;
initialClassification.parentKey = state.selectedClassification.key;
}
ctx.patchState({
selectedClassification: initialClassification,
badgeMessage: 'Creating new classification'
});
return of(null);
}
@Action(CopyClassification)
newCopyClassification(ctx: StateContext<ClassificationStateModel>): Observable<null> {
const copy = { ...ctx.getState().selectedClassification };
copy.key = null;
copy.classificationId = undefined;
ctx.patchState({
selectedClassification: copy,
badgeMessage: `Copying Classification: ${copy.name}`
});
return of(null);
}
@Action(RemoveSelectedClassification)
removeSelectedClassification(ctx: StateContext<ClassificationStateModel>): Observable<any> {
const sel = ctx.getState().selectedClassification;
return this.classificationsService.deleteClassification(sel.classificationId).pipe(
take(1),
tap(() => {
const classifications = ctx
.getState()
.classifications.filter((el) => el.classificationId !== sel.classificationId);
ctx.patchState({ selectedClassification: undefined, classifications });
})
);
}
@Action(UpdateClassification)
updateClassification(
ctx: StateContext<ClassificationStateModel>,
action: SaveModifiedClassification
): Observable<any> {
return this.classificationsService.putClassification(action.classification).pipe(
take(1),
tap((classification) => {
const state = ctx.getState();
let { selectedClassification } = state;
if (selectedClassification && selectedClassification.classificationId === classification.classificationId) {
selectedClassification = classification;
}
ctx.patchState({
classifications: updateClassificationList(state.classifications, classification),
selectedClassification
});
})
);
}
// initialize after Startup service has configured the taskanaRestUrl properly.
ngxsAfterBootstrap(ctx: StateContext<ClassificationStateModel>): Observable<null> {
ctx.dispatch(new InitializeStore());
return of(null);
}
}
function updateClassificationList(classifications: ClassificationSummary[], classification: Classification) {
return classifications.map((c) => {
if (c.classificationId === classification.classificationId) {
return classification;
}
return c;
});
}
export interface ClassificationStateModel {
classifications: ClassificationSummary[];
selectedClassification: Classification;
selectedClassificationType: string;
classificationTypes: CategoriesResponse;
badgeMessage: string;
}