import {ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {Title} from '@angular/platform-browser'; import {EnvProperties} from '../openaireLibrary/utils/properties/env-properties'; import {Category, Stakeholder, SubCategory, Topic, Visibility} from "../openaireLibrary/monitor/entities/stakeholder"; import {StakeholderService} from "../openaireLibrary/monitor/services/stakeholder.service"; import {HelperFunctions} from "../openaireLibrary/utils/HelperFunctions.class"; import {AlertModal} from "../openaireLibrary/utils/modal/alert"; import {BehaviorSubject, Subject, Subscriber, Subscription} from "rxjs"; import {FormBuilder, FormGroup, Validators} from "@angular/forms"; import {StakeholderUtils} from "../utils/indicator-utils"; import {StringUtils} from "../openaireLibrary/utils/string-utils.class"; import {IDeactivateComponent} from "../openaireLibrary/utils/can-exit.guard"; import {LayoutService} from "../openaireLibrary/dashboard/sharedComponents/sidebar/layout.service"; import {Option} from "../openaireLibrary/sharedComponents/input/input.component"; import {properties} from "../../environments/environment"; import {Session, User} from "../openaireLibrary/login/utils/helper.class"; import {UserManagementService} from "../openaireLibrary/services/user-management.service"; import {TransitionGroupComponent} from "../openaireLibrary/utils/transition-group/transition-group.component"; import {NotificationHandler} from "../openaireLibrary/utils/notification-handler"; declare var UIkit; @Component({ selector: 'topic', templateUrl: './topic.component.html', }) export class TopicComponent implements OnInit, OnDestroy, IDeactivateComponent { private topicSubscriptions: any[] = []; private subscriptions: any[] = []; public properties: EnvProperties = properties; public stakeholderUtils: StakeholderUtils = new StakeholderUtils(); public loading: boolean = false; public stakeholder: Stakeholder; public user: User; /** * Stakeholder change event * */ public change: Subject = new Subject(); /** * Current topic **/ public topicIndexSubject: BehaviorSubject = new BehaviorSubject(0); public topicIndex: number = 0; /** * Current category */ public categoryIndexSubject: BehaviorSubject = new BehaviorSubject(0); public categoryIndex: number = 0; /** * Current Subcategory */ public subCategoryIndexSubject: BehaviorSubject = new BehaviorSubject(0); public subCategoryIndex: number = 0; /** * Current element and index of topic, category or subcategory to be deleted. */ public form: FormGroup; public element: Topic | Category | SubCategory; public type: 'topic' | 'category' | 'subcategory' = "topic"; public index: number = -1; @ViewChild('deleteModal', {static: true}) deleteModal: AlertModal; @ViewChild('editModal', {static: true}) editModal: AlertModal; @ViewChild("topics") topics: TransitionGroupComponent; @ViewChild("categories") categories: TransitionGroupComponent; @ViewChild("subCategories") subCategories: TransitionGroupComponent; public elementChildrenActionOnDelete: string; public filters: FormGroup; public all: Option = { value: 'all', label: 'All' }; constructor( private route: ActivatedRoute, private router: Router, private title: Title, private fb: FormBuilder, private stakeholderService: StakeholderService, private userManagementService: UserManagementService, private layoutService: LayoutService, private cdr: ChangeDetectorRef) { } public ngOnInit() { let subscription: Subscription; this.subscriptions.push(this.topicIndexSubject.asObservable().subscribe(index => { this.topicChanged(() => { this.topicIndex = index; }); })); this.subscriptions.push(this.categoryIndexSubject.asObservable().subscribe(index => { this.categoryChanged(() => { this.categoryIndex = index; }); })); this.subscriptions.push(this.subCategoryIndexSubject.asObservable().subscribe(index => { this.subCategoryChanged(() => { this.subCategoryIndex = index; }); })); this.subscriptions.push(this.route.params.subscribe(params => { if (subscription) { subscription.unsubscribe(); } subscription = this.stakeholderService.getStakeholderAsObservable().subscribe(stakeholder => { if (stakeholder) { this.stakeholder = stakeholder; if (params['topic']) { this.chooseTopic(this.stakeholder.topics.findIndex(topic => topic.alias === params['topic'])); } else { this.chooseTopic(0); } this.chooseCategory(0); this.filters = this.fb.group({ chartType: this.fb.control('all'), status: this.fb.control('all'), keyword: this.fb.control('') }); if (this.topicIndex === -1) { this.navigateToError(); } else { this.title.setTitle(stakeholder.name + " | Indicators"); } } }); this.topicSubscriptions.push(subscription); })); this.topicSubscriptions.push(this.userManagementService.getUserInfo().subscribe(user => { this.user = user; })) } public ngOnDestroy() { this.topicSubscriptions.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } }); this.subscriptions.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } }); } canExit(): boolean { this.topicSubscriptions.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } }); this.stakeholderService.setStakeholder(this.stakeholder); return true; } hide(element: any) { UIkit.dropdown(element).hide(); } stakeholderChanged() { this.change.next(); } public saveElement() { if (this.type === "topic") { this.saveTopic(); } else if (this.type === "category") { this.saveCategory(); } else { this.saveSubCategory(); } } public deleteElement() { if (this.type === "topic") { this.deleteTopic(); } else if (this.type === "category") { this.deleteCategory(); } else { this.deleteSubcategory(); } } public chooseTopic(topicIndex: number) { this.topicIndexSubject.next(topicIndex); } topicChanged(callback: Function, save: boolean = false) { if(this.topics && save) { this.topics.disable(); } if(this.categories) { this.categories.disable(); } if(this.subCategories) { this.subCategories.disable(); } if(callback) { callback(); } this.cdr.detectChanges(); if(this.topics && save) { this.topics.init(); this.topics.enable(); } if(this.categories) { this.categories.init(); this.categories.enable(); } if(this.subCategories) { this.subCategories.init(); this.subCategories.enable(); } } private buildTopic(topic: Topic) { let topics = this.stakeholder.topics.filter(element => element._id !== topic._id); this.form = this.fb.group({ _id: this.fb.control(topic._id), name: this.fb.control(topic.name, Validators.required), description: this.fb.control(topic.description), creationDate: this.fb.control(topic.creationDate), alias: this.fb.control(topic.alias, [ Validators.required, this.stakeholderUtils.aliasValidator(topics) ] ), visibility: this.fb.control(topic.visibility), defaultId: this.fb.control(topic.defaultId), categories: this.fb.control(topic.categories), icon: this.fb.control(topic.icon) }); this.topicSubscriptions.push(this.form.get('name').valueChanges.subscribe(value => { let i = 1; value = this.stakeholderUtils.generateAlias(value); this.form.controls['alias'].setValue(value); while (this.form.get('alias').invalid) { this.form.controls['alias'].setValue(value + i); i++; } })); } public editTopicOpen(index = -1) { this.index = index; this.type = 'topic'; if (index === -1) { this.buildTopic(new Topic(null, null, null, "PUBLIC")); } else { this.buildTopic(this.stakeholder.topics[index]); } this.editOpen(); } public saveTopic() { if (!this.form.invalid) { let path = [this.stakeholder._id]; let callback = (topic: Topic): void => { this.topicChanged(() => { if (this.index === -1) { this.stakeholder.topics.push(topic); } else { this.stakeholder.topics[this.index] = HelperFunctions.copy(topic); } }, true); }; if (this.index === -1) { this.save('Topic has been successfully created', path, this.form.value, callback); } else { this.save('Topic has been successfully saved', path, this.form.value, callback); } } } public changeTopicStatus(index: number, visibility: Visibility) { this.type = 'topic'; let path = [ this.stakeholder._id, this.stakeholder.topics[index]._id ]; this.changeStatus(this.stakeholder.topics[index], path, visibility); } public deleteTopicOpen(index = this.topicIndex, childrenAction: string = null) { this.type = 'topic'; this.index = index; this.element = this.stakeholder.topics[this.index]; this.deleteOpen(childrenAction); } public deleteTopic() { let path: string[] = [ this.stakeholder._id, this.stakeholder.topics[this.index]._id ]; let callback = (): void => { this.chooseTopic(Math.max(0, this.index - 1)); this.stakeholder.topics.splice(this.index, 1); this.topics.init(); }; this.delete('Topic has been successfully be deleted', path, callback, (this.topicIndex === this.index)); } public moveTopic(index: number, newIndex: number = index - 1) { this.topics.init(); let path = [this.stakeholder._id]; let ids = this.stakeholder.topics.map(topic => topic._id); HelperFunctions.swap(ids, index, newIndex); this.stakeholderService.reorderElements(properties.monitorServiceAPIURL, path, ids).subscribe(() => { HelperFunctions.swap(this.stakeholder.topics, index, newIndex); if(this.topicIndex === index) { this.chooseTopic(newIndex); } else if(this.topicIndex === newIndex) { this.chooseTopic(index); } }, error => { NotificationHandler.rise(error.error.message) }); } public chooseCategory(index: number) { this.categoryIndexSubject.next(index); this.chooseSubcategory(0); } categoryChanged(callback: Function, save: boolean = false) { if(this.categories && save) { this.categories.disable(); } if(this.subCategories) { this.subCategories.disable(); } if(callback) { callback(); } this.cdr.detectChanges(); if(this.categories && save) { this.categories.init(); this.categories.enable(); } if(this.subCategories) { this.subCategories.init(); this.subCategories.enable(); } } private buildCategory(category: Category) { let categories = this.stakeholder.topics[this.topicIndex].categories.filter(element => element._id !== category._id); this.form = this.fb.group({ _id: this.fb.control(category._id), name: this.fb.control(category.name, Validators.required), description: this.fb.control(category.description), creationDate: this.fb.control(category.creationDate), alias: this.fb.control(category.alias, [ Validators.required, this.stakeholderUtils.aliasValidator(categories) ] ), visibility: this.fb.control(category.visibility), defaultId: this.fb.control(category.defaultId), subCategories: this.fb.control(category.subCategories) }); this.topicSubscriptions.push(this.form.get('name').valueChanges.subscribe(value => { let i = 1; value = this.stakeholderUtils.generateAlias(value); this.form.controls['alias'].setValue(value); while (this.form.get('alias').invalid) { this.form.controls['alias'].setValue(value + i); i++; } })); } public editCategoryOpen(index: number = -1) { this.index = index; this.type = 'category'; if (index === -1) { this.buildCategory(new Category(null, null, null, "PUBLIC")); } else { this.buildCategory(this.stakeholder.topics[this.topicIndex].categories[index]); } this.editOpen(); } public saveCategory() { if (!this.form.invalid) { let path = [this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id]; let callback = (category: Category): void => { this.categoryChanged(() => { if (this.index === -1) { this.stakeholder.topics[this.topicIndex].categories.push(category); this.categories.init(); } else { this.stakeholder.topics[this.topicIndex].categories[this.index] = HelperFunctions.copy(category); } }, true); }; if (this.index === -1) { this.save('Category has been successfully created', path, this.form.value, callback); } else { this.save('Category has been successfully saved', path, this.form.value, callback); } } } public changeCategoryStatus(index: number, visibility: Visibility) { this.index = index; this.type = 'category'; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.index]._id ]; this.changeStatus(this.stakeholder.topics[this.topicIndex].categories[this.index], path, visibility); } public deleteCategoryOpen(index: number, childrenAction: string = null) { this.type = 'category'; this.index = index; this.element = this.stakeholder.topics[this.topicIndex].categories[this.index]; this.deleteOpen(childrenAction); } public deleteCategory() { let path: string[] = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.index]._id ]; let callback = (): void => { this.chooseCategory(Math.max(0, this.index - 1)); this.stakeholder.topics[this.topicIndex].categories.splice(this.index, 1); this.categories.init(); }; this.delete('Category has been successfully be deleted', path, callback); } public moveCategory(index: number, newIndex: number = index - 1) { this.categories.init(); let path = [this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id]; let ids = this.stakeholder.topics[this.topicIndex].categories.map(category => category._id); HelperFunctions.swap(ids, index, newIndex); this.stakeholderService.reorderElements(properties.monitorServiceAPIURL, path, ids).subscribe(() => { HelperFunctions.swap(this.stakeholder.topics[this.topicIndex].categories, index, newIndex); if(this.categoryIndex === index) { this.chooseCategory(newIndex); } else if(this.categoryIndex === newIndex) { this.chooseCategory(index); } }, error => { NotificationHandler.rise(error.error.message) }); } chooseSubcategory(subcategoryIndex: number) { this.subCategoryIndexSubject.next(subcategoryIndex); } subCategoryChanged(callback: Function, save: boolean = false) { if(this.subCategories && save) { this.subCategories.disable(); } if(callback) { callback(); } this.cdr.detectChanges(); if(this.subCategories && save) { this.subCategories.init(); this.subCategories.enable(); } } private buildSubcategory(subCategory: SubCategory) { let subCategories = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.filter(element => element._id !== subCategory._id); this.form = this.fb.group({ _id: this.fb.control(subCategory._id), name: this.fb.control(subCategory.name, Validators.required), description: this.fb.control(subCategory.description), creationDate: this.fb.control(subCategory.creationDate), alias: this.fb.control(subCategory.alias, [ Validators.required, this.stakeholderUtils.aliasValidator(subCategories) ] ), visibility: this.fb.control(subCategory.visibility), defaultId: this.fb.control(subCategory.defaultId), charts: this.fb.control(subCategory.charts), numbers: this.fb.control(subCategory.numbers) }); this.topicSubscriptions.push(this.form.get('name').valueChanges.subscribe(value => { let i = 1; value = this.stakeholderUtils.generateAlias(value); this.form.controls['alias'].setValue(value); while (this.form.get('alias').invalid) { this.form.controls['alias'].setValue(value + i); i++; } })); } public editSubCategoryOpen(index: number = -1) { this.index = index; this.type = 'subcategory'; if (index === -1) { this.buildSubcategory(new SubCategory(null, null, null, "PUBLIC")); } else { this.buildSubcategory(this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[index]); } this.editOpen(); } public saveSubCategory() { if (!this.form.invalid) { let path: string[] = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id ]; let callback = (subCategory: SubCategory): void => { this.subCategoryChanged(() => { if (this.index === -1) { this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.push(subCategory); } else { this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index] = HelperFunctions.copy(subCategory); } }, true); }; if (this.index === -1) { this.save('Subcategory has been successfully created', path, this.form.value, callback); } else { this.save('Subcategory has been successfully saved', path, this.form.value, callback); } } } public changeSubcategoryStatus(index: number, visibility: Visibility) { this.index = index; this.type = 'subcategory'; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id ]; this.changeStatus(this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index], path, visibility); } public deleteSubcategoryOpen(index, childrenAction: string = null) { this.type = 'subcategory'; this.index = index; this.element = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]; this.deleteOpen(childrenAction); } public deleteSubcategory() { let path: string[] = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id ]; let callback = (): void => { this.chooseSubcategory(Math.max(0, this.index - 1)); this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.splice(this.index, 1); }; this.delete('Subcategory has been successfully be deleted', path, callback); } public moveSubCategory(index: number, newIndex: number = index - 1) { this.subCategories.init(); let path = [this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id]; let ids = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.map(subCategory => subCategory._id); HelperFunctions.swap(ids, index, newIndex); this.stakeholderService.reorderElements(properties.monitorServiceAPIURL, path, ids).subscribe(() => { HelperFunctions.swap(this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories, index, newIndex); if(this.subCategoryIndex === index) { this.chooseSubcategory(newIndex); } else if(this.subCategoryIndex === newIndex) { this.chooseSubcategory(index); } }, error => { NotificationHandler.rise(error.error.message) }); } private navigateToError() { this.router.navigate([this.properties.errorLink], {queryParams: {'page': this.router.url}}); } get isCurator(): boolean { return Session.isPortalAdministrator(this.user) || Session.isCurator(this.stakeholder.type, this.user); } private editOpen() { this.editModal.cancelButtonText = 'Cancel'; this.editModal.okButtonLeft = false; this.editModal.alertMessage = false; if (this.index === -1) { this.editModal.alertTitle = 'Create a new ' + this.type; this.editModal.okButtonText = 'Create'; } else { this.editModal.alertTitle = 'Edit ' + this.type + '\'s information '; this.editModal.okButtonText = 'Save'; } this.editModal.stayOpen = true; this.editModal.open(); } private deleteOpen(childrenAction: string = null) { this.elementChildrenActionOnDelete = null; if (childrenAction == "delete") { this.elementChildrenActionOnDelete = childrenAction; } else if (childrenAction == "disconnect") { this.elementChildrenActionOnDelete = childrenAction; } this.deleteModal.alertTitle = 'Delete ' + this.type; this.deleteModal.cancelButtonText = 'No'; this.deleteModal.okButtonText = 'Yes'; // this.deleteModal.cancelButton = false; // this.deleteModal.okButton = false; this.deleteModal.stayOpen = true; this.deleteModal.open(); } private save(message: string, path: string[], saveElement: any, callback: Function, redirect = false) { this.loading = true; this.topicSubscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, saveElement, path).subscribe(saveElement => { callback(saveElement); this.stakeholderChanged(); this.loading = false; this.editModal.cancel(); NotificationHandler.rise(message); if (redirect) { this.router.navigate(['../' + saveElement.alias], { relativeTo: this.route }); } }, error => { this.loading = false; this.editModal.cancel(); NotificationHandler.rise(error.error.message, 'danger'); })); } private delete(message: string, path: string[], callback: Function, redirect = false) { this.loading = true; this.topicSubscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.elementChildrenActionOnDelete).subscribe(() => { callback(); this.stakeholderChanged(); this.loading = false; this.deleteModal.cancel(); NotificationHandler.rise(message); if (redirect) { this.back(); } }, error => { this.loading = false; this.deleteModal.cancel(); NotificationHandler.rise(error.error.message, 'danger'); })); } private changeStatus(element: Topic | Category | SubCategory, path: string[], visibility: Visibility) { this.topicSubscriptions.push(this.stakeholderService.changeVisibility(this.properties.monitorServiceAPIURL, path, visibility).subscribe(visibility => { element.visibility = visibility; NotificationHandler.rise(StringUtils.capitalize(this.type) + ' has been successfully changed to ' + element.visibility.toLowerCase()); }, error => { NotificationHandler.rise(error.error.message, 'danger'); })); } back() { this.router.navigate(['../'], { relativeTo: this.route }); } public getPluralTypeName(): string { if (this.type == "topic") { return "Topics"; } else if (this.type == "category") { return "Categories"; } else if (this.type == "subcategory") { return "Subcategories"; } else { return this.type; } } public get isSmallScreen() { return this.layoutService.isSmallScreen; } public get open() { return this.layoutService.open; } public toggleOpen(event: MouseEvent) { event.preventDefault(); this.layoutService.setOpen(!this.open); } }