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; @Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable; @Select(ClassificationSelectors.selectedClassificationType) selectedClassificationType$: Observable; @Select(ClassificationSelectors.selectClassificationTypesObject) classificationTypes$: Observable; @Select(ClassificationSelectors.selectedClassification) selectedClassification$: Observable; @Select(ClassificationSelectors.getBadgeMessage) badgeMessage$: Observable; spinnerIsRunning = false; customFields$: Observable; isCreatingNewClassification: boolean = false; @ViewChild('ClassificationForm', { static: false }) classificationForm: NgForm; toggleValidationMap = new Map(); destroy$ = new Subject(); private timeout = new Map(); readonly lengthError = 'You have reached the maximal length'; tooLongMap = new Map(); 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 { 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 { 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([['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([['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([['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))); } } }