taskana/web/src/app/administration/components/classification-details/classification-details.comp...

251 lines
9.2 KiB
TypeScript

import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Select, Store } from '@ngxs/store';
import { combineLatest, Observable, Subject, Subscription, timer } from 'rxjs';
import { highlight } from 'theme/animations/validation.animation';
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
import { DomainService } from 'app/shared/services/domain/domain.service';
import { Pair } from 'app/shared/models/pair';
import { NgForm, NgModel } from '@angular/forms';
import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service';
import { ImportExportService } from 'app/administration/services/import-export.service';
import { map, take, takeUntil } from 'rxjs/operators';
import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors';
import { ClassificationSelectors } from 'app/shared/store/classification-store/classification.selectors';
import { Location } from '@angular/common';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { ClassificationCategoryImages, CustomField, getCustomFields } from '../../../shared/models/customisation';
import { Classification } from '../../../shared/models/classification';
import { customFieldCount } from '../../../shared/models/classification-summary';
import { CategoriesResponse } from '../../../shared/services/classification-categories/classification-categories.service';
import {
SaveCreatedClassification,
RemoveSelectedClassification,
RestoreSelectedClassification,
SaveModifiedClassification,
SelectClassification,
CopyClassification
} from '../../../shared/store/classification-store/classification.actions';
@Component({
selector: 'taskana-administration-classification-details',
templateUrl: './classification-details.component.html',
animations: [highlight],
styleUrls: ['./classification-details.component.scss']
})
export class ClassificationDetailsComponent implements OnInit, OnDestroy {
classification: Classification;
requestInProgress = false;
@Select(ClassificationSelectors.selectCategories) categories$: Observable<string[]>;
@Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable<ClassificationCategoryImages>;
@Select(ClassificationSelectors.selectedClassificationType) selectedClassificationType$: Observable<string>;
@Select(ClassificationSelectors.selectClassificationTypesObject) classificationTypes$: Observable<CategoriesResponse>;
@Select(ClassificationSelectors.selectedClassification) selectedClassification$: Observable<Classification>;
@Select(ClassificationSelectors.getBadgeMessage) badgeMessage$: Observable<string>;
spinnerIsRunning = false;
customFields$: Observable<CustomField[]>;
isCreatingNewClassification: boolean = false;
@ViewChild('ClassificationForm', { static: false }) classificationForm: NgForm;
toggleValidationMap = new Map<string, boolean>();
destroy$ = new Subject<void>();
private timeout = new Map<string, Subscription>();
readonly lengthError = 'You have reached the maximal length';
tooLongMap = new Map<string, boolean>();
constructor(
private location: Location,
private requestInProgressService: RequestInProgressService,
private domainService: DomainService,
private formsValidatorService: FormsValidatorService,
private notificationsService: NotificationService,
private importExportService: ImportExportService,
private store: Store
) {}
ngOnInit() {
this.customFields$ = this.store.select(EngineConfigurationSelectors.classificationsCustomisation).pipe(
map((customisation) => customisation.information),
getCustomFields(customFieldCount)
);
this.selectedClassification$.pipe(takeUntil(this.destroy$)).subscribe((classification) => {
this.classification = { ...classification };
this.isCreatingNewClassification = typeof this.classification.classificationId === 'undefined';
});
this.importExportService
.getImportingFinished()
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.store.dispatch(new SelectClassification(this.classification.classificationId));
});
}
removeClassification() {
this.notificationsService.showDialog(
`You are going to delete classification: ${this.classification.key}. Can you confirm this action?`,
this.removeClassificationConfirmation.bind(this)
);
}
isFieldValid(field: string): boolean {
return this.formsValidatorService.isFieldValid(this.classificationForm, field);
}
onSubmit() {
this.formsValidatorService.formSubmitAttempt = true;
this.formsValidatorService.validateFormInformation(this.classificationForm, this.toggleValidationMap).then(value => {
if (value) {
this.onSave();
}
});
}
onRestore() {
this.formsValidatorService.formSubmitAttempt = false;
this.store
.dispatch(new RestoreSelectedClassification(this.classification.classificationId))
.pipe(take(1))
.subscribe(() => {
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT);
});
}
onCopy() {
if (this.isCreatingNewClassification) {
this.notificationsService.showToast(NOTIFICATION_TYPES.WARNING_CANT_COPY);
} else {
this.store.dispatch(new CopyClassification());
}
}
selectCategory(category: string) {
this.classification.category = category;
}
getCategoryIcon(category: string): Observable<Pair> {
return this.categoryIcons$.pipe(
map((iconMap) =>
iconMap[category]
? new Pair(iconMap[category], category)
: new Pair(iconMap.missing, 'Category does not match with the configuration')
)
);
}
spinnerRunning(value) {
this.spinnerIsRunning = value;
}
validChanged(): void {
this.classification.isValidInDomain = !this.classification.isValidInDomain;
}
masterDomainSelected(): boolean {
return this.domainService.getSelectedDomainValue() === '';
}
getClassificationCustom(customNumber: number): string {
return `custom${customNumber}`;
}
getAvailableCategories(type: string): Observable<string[]> {
return this.classificationTypes$.pipe(
take(1),
map((classTypes) => classTypes[type])
);
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
private async onSave() {
this.requestInProgressService.setRequestInProgress(true);
if (typeof this.classification.classificationId === 'undefined') {
this.store
.dispatch(new SaveCreatedClassification(this.classification))
.pipe(take(1))
.subscribe(
(store) => {
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_2,
new Map<string, string>([['classificationKey', store.classification.selectedClassification.key]])
);
this.location.go(
this.location
.path()
.replace(
/(classifications).*/g,
`classifications/(detail:${store.classification.selectedClassification.classificationId})`
)
);
this.afterRequest();
},
(error) => {
this.notificationsService.triggerError(NOTIFICATION_TYPES.CREATE_ERR, error);
this.afterRequest();
}
);
} else {
try {
this.store
.dispatch(new SaveModifiedClassification(this.classification))
.pipe(take(1))
.subscribe(() => {
this.afterRequest();
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_3,
new Map<string, string>([['classificationKey', this.classification.key]])
);
});
} catch (error) {
this.notificationsService.triggerError(NOTIFICATION_TYPES.SAVE_ERR, error);
this.afterRequest();
}
}
}
private afterRequest() {
this.requestInProgressService.setRequestInProgress(false);
}
private removeClassificationConfirmation() {
if (!this.classification || !this.classification.classificationId) {
this.notificationsService.triggerError(NOTIFICATION_TYPES.SELECT_ERR);
return;
}
this.requestInProgressService.setRequestInProgress(true);
this.store
.dispatch(new RemoveSelectedClassification())
.pipe(take(1))
.subscribe(() => {
this.notificationsService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_4,
new Map<string, string>([['classificationKey', this.classification.key]])
);
this.afterRequest();
});
this.location.go(this.location.path().replace(/(classifications).*/g, 'classifications'));
}
onKeyPressed(event: KeyboardEvent, model: NgModel, max: Number): void {
if (this.timeout.has(model.name)) {
this.timeout.get(model.name).unsubscribe();
}
if (model.value.length >= max && !event.altKey && !event.ctrlKey) {
this.tooLongMap.set(model.name, true);
this.timeout.set(model.name, timer(3000).subscribe(() => this.tooLongMap.set(model.name, false)));
}
}
}