From f19d9b3d879cb4115a01e6464d435d3a2a8cb44a Mon Sep 17 00:00:00 2001 From: argirok Date: Mon, 25 Sep 2023 11:36:05 +0300 Subject: [PATCH] Initial commit for plugins functionality: Manage Plugin Templates --- .../pluginTemplates-routing.module.ts | 13 + .../templates/pluginTemplates.component.html | 144 ++++++++ .../templates/pluginTemplates.component.ts | 347 ++++++++++++++++++ .../templates/pluginTemplates.module.ts | 36 ++ .../admin-tabs/admin-tabs.component.ts | 3 +- services/plugins.service.ts | 36 ++ utils/entities/adminTool/plugin.ts | 11 + utils/entities/adminTool/pluginTemplate.ts | 32 ++ 8 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 dashboard/plugins/templates/pluginTemplates-routing.module.ts create mode 100644 dashboard/plugins/templates/pluginTemplates.component.html create mode 100644 dashboard/plugins/templates/pluginTemplates.component.ts create mode 100644 dashboard/plugins/templates/pluginTemplates.module.ts create mode 100644 services/plugins.service.ts create mode 100644 utils/entities/adminTool/plugin.ts create mode 100644 utils/entities/adminTool/pluginTemplate.ts diff --git a/dashboard/plugins/templates/pluginTemplates-routing.module.ts b/dashboard/plugins/templates/pluginTemplates-routing.module.ts new file mode 100644 index 00000000..6adc4e63 --- /dev/null +++ b/dashboard/plugins/templates/pluginTemplates-routing.module.ts @@ -0,0 +1,13 @@ +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {PluginTemplatesComponent} from "./pluginTemplates.component"; + + +@NgModule({ + imports: [ + RouterModule.forChild([ + { path: '', component: PluginTemplatesComponent} + ]) + ] +}) +export class PluginTemplatesRoutingModule { } diff --git a/dashboard/plugins/templates/pluginTemplates.component.html b/dashboard/plugins/templates/pluginTemplates.component.html new file mode 100644 index 00000000..8a656da1 --- /dev/null +++ b/dashboard/plugins/templates/pluginTemplates.component.html @@ -0,0 +1,144 @@ +
+
+ + +
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+ +
+
No templates found
+
+
+
+
+
+ +
+
{{check.template.name}}
+
+ Pages: {{getPagesAsString(check.template.pages)}} +
+
+ Placements: {{check.template.placements.join(", ")}} +
+
+ Portal type: {{check.template.portalType}} +
+
+ {{check.template.description}} +
+ +
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+ + +
+
+
Field #{{i +1}}
+
+ Remove +
+ +
+
+
+
+
+ +
+
+
+
+ + +
+
+
+ +
+
+ diff --git a/dashboard/plugins/templates/pluginTemplates.component.ts b/dashboard/plugins/templates/pluginTemplates.component.ts new file mode 100644 index 00000000..b9c8965b --- /dev/null +++ b/dashboard/plugins/templates/pluginTemplates.component.ts @@ -0,0 +1,347 @@ +import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {ActivatedRoute, Router} from "@angular/router"; +import {HelpContentService} from "../../../services/help-content.service"; +import { + FormArray, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup, + ValidatorFn, + Validators +} from "@angular/forms"; +import {Page} from "../../../utils/entities/adminTool/page"; +import {EnvProperties} from '../../../utils/properties/env-properties'; +import {HelperFunctions} from "../../../utils/HelperFunctions.class"; +import {Subscriber} from "rxjs"; +import {properties} from "../../../../../environments/environment"; +import {PortalUtils} from "../../portal/portalHelper"; +import {AlertModal} from "../../../utils/modal/alert"; +import {Option} from "../../../sharedComponents/input/input.component"; +import {Title} from "@angular/platform-browser"; +import {ClearCacheService} from "../../../services/clear-cache.service"; +import {NotificationHandler} from "../../../utils/notification-handler"; +import {PluginsService} from "../../../services/plugins.service"; +import {PluginTemplate} from "../../../utils/entities/adminTool/pluginTemplate"; +import {Entity} from "../../../utils/entities/adminTool/entity"; +import {StringUtils} from "../../../utils/string-utils.class"; + +@Component({ + selector: 'plugin-templates', + templateUrl: './pluginTemplates.component.html', +}) +export class PluginTemplatesComponent implements OnInit { + @ViewChild('editModal') editModal: AlertModal; + @ViewChild('deleteModal') deleteModal: AlertModal; + private selectedId: string; + public checkboxes: { + template: PluginTemplate; + checked: boolean; + }[] = []; + public templates: PluginTemplate[] = []; + public templateForm: UntypedFormGroup; + public pagesCtrl: UntypedFormArray; + urlValidator: ValidatorFn = StringUtils.urlValidator; + private searchText: RegExp = new RegExp(''); + public keyword: string = ""; + public properties: EnvProperties = properties; + public formPages: Page[] = []; + public showLoading: boolean = true; + public filterForm: UntypedFormGroup; + private subscriptions: any[] = []; + public allPages: Option[] = []; + public attrTypeOptions: Option[] = [ + {label:"Text", value:"text"}, + {label:"HTML", value:"HTML"}, + {label:"Boolean", value:"boolean"}, + {label:"URL", value:"URL"}, + ]; + selectedCommunityPid = null; + public portalUtils: PortalUtils = new PortalUtils(); + private index: number; + + constructor(private element: ElementRef, private route: ActivatedRoute, private _router: Router, + private title: Title,private _helpContentService: HelpContentService, + private _pluginsService: PluginsService, private _fb: UntypedFormBuilder, + private _clearCacheService: ClearCacheService) { + } + + ngOnInit() { + this.title.setTitle('Administrator Dashboard | Classes'); + this.filterForm = this._fb.group({ + keyword: [''], + type: ['all', Validators.required] + }); + this.subscriptions.push(this.filterForm.get('keyword').valueChanges.subscribe(value => { + this.searchText = new RegExp(value, 'i'); + this.applyFilters(); + })); + this.subscriptions.push(this.filterForm.get('type').valueChanges.subscribe(value => { + this.applyFilters(); + })); + this.getTemplates(); + this.subscriptions.push(this.route.queryParams.subscribe(params => { + HelperFunctions.scroll(); + this.selectedCommunityPid = params['communityId']; + this.getPages(); + })); + + } + + ngOnDestroy(): void { + this.subscriptions.forEach(value => { + if (value instanceof Subscriber) { + value.unsubscribe(); + } else if (value instanceof Function) { + value(); + } + }); + } + + getTemplates() { + this.showLoading = true; + this.subscriptions.push(this._pluginsService.getAllPluginTemplates(this.properties.adminToolsAPIURL).subscribe( + templates => { + this.templates = templates; + this.checkboxes = []; + + let self = this; + templates.forEach(_ => { + self.checkboxes.push( {template: _, checked: false}); + }); + + this.showLoading = false; + }, + error => this.handleError('System error retrieving classes', error))); + } + + // public showModal():void { + // this.modal.showModal(); + // } + + public toggleCheckBoxes(event) { + this.checkboxes.forEach(_ => _.checked = event.target.checked); + } + + public applyCheck(flag: boolean) { + this.checkboxes.forEach(_ => _.checked = flag); + } + + private deleteFromArray(id: string): void { + + let i = this.templates.findIndex(_ => _._id == id); + this.templates.splice(i, 1); + + this.applyFilters(); + } + + public confirmDelete(id: string) { + this.selectedId = id; + this.confirmModalOpen(); + } + + + private confirmModalOpen() { + this.deleteModal.alertTitle = "Delete Confirmation"; + this.deleteModal.message = "Are you sure you want to delete the selected template(s)?"; + this.deleteModal.okButtonText = "Yes"; + this.deleteModal.open(); + } + + public confirmedDelete() { + this.showLoading = true; + this.subscriptions.push(this._pluginsService.deletePluginTemplate(this.selectedId, this.properties.adminToolsAPIURL).subscribe( + _ => { + this.deleteFromArray(this.selectedId); + NotificationHandler.rise('Template have been successfully deleted'); + this.showLoading = false; + this._clearCacheService.clearCache("Template id deleted"); + }, + error => this.handleUpdateError('System error deleting the selected Template', error) + )); + } + + public edit(i: number) { + let pluginTemplate: PluginTemplate = this.checkboxes[i].template; + this.index = this.templates.findIndex(value => value._id === pluginTemplate._id); + // this.formPages = pluginTemplate.pages; + this.pagesCtrl = this._fb.array([], Validators.required); + this.templateForm = this._fb.group({ + _id: this._fb.control(pluginTemplate._id), + name: this._fb.control(pluginTemplate.name, Validators.required), + pages: this.pagesCtrl, + portalType: this._fb.control(pluginTemplate.portalType, Validators.required), + + code: this._fb.control(pluginTemplate.code, Validators.required), + description: this._fb.control(pluginTemplate.description), + placements: this._fb.array(pluginTemplate.placements), + attributes:this._fb.array([]) + }); + this.templateForm.get('portalType').disable(); + for (let i = 0; i < pluginTemplate.pages.length; i++) { + this.pagesCtrl.push(this._fb.control(this.getPageById(pluginTemplate.pages[i]))); + } + if(pluginTemplate.attributes) { + for (let attrKey of Object.keys(pluginTemplate.attributes)) { + (this.templateForm.get("attributes") as FormArray).push(this._fb.group({ + key: this._fb.control(attrKey, Validators.required), + name: this._fb.control(pluginTemplate.attributes[attrKey].name, Validators.required), + type: this._fb.control(pluginTemplate.attributes[attrKey].type, Validators.required), + value: this._fb.control(pluginTemplate.attributes[attrKey].value) + })); + } + } + this.modalOpen("Edit Template", "Save Changes"); + } + + public newPlugin() { + this.pagesCtrl = this._fb.array([], Validators.required); + if (this.templateForm) { + this.templateForm.get('portalType').enable(); + } + this.templateForm = this._fb.group({ + _id: this._fb.control(null), + name: this._fb.control('aa', Validators.required), + code: this._fb.control('aa', Validators.required), + description: this._fb.control('aa'), + pages: this.pagesCtrl, + portalType: this._fb.control('community', Validators.required), + placements: this._fb.array([]), + attributes:this._fb.array([]) + }); + this.addNewAttr(); + this.modalOpen("Create template", "Create"); + } + + private modalOpen(title: string, yesBtn: string) { + this.editModal.okButtonLeft = false; + this.editModal.alertTitle = title; + this.editModal.okButtonText = yesBtn; + this.editModal.open(); + } + + public saveConfirmed(data: any) { + this.showLoading = true; + let template:PluginTemplate = this.templateForm.getRawValue(); + template.pages = this.pagesCtrl.getRawValue().map(page => page._id?page._id:page); + template.attributes = new Map(); + for (let attr of this.templateForm.getRawValue().attributes) { + template.attributes[attr.key]={name: attr.name, type: attr.type, value: attr.value}; + } + let update = template._id?true:false; + this.subscriptions.push(this._pluginsService.savePluginTemplate(template, this.properties.adminToolsAPIURL).subscribe( + saved => { + this.savedSuccessfully(saved, update ); + NotificationHandler.rise('Template ' + saved.name + ' has been successfully' + (update?' updated ':' created ') + ''); + this._clearCacheService.clearCache("Template id saved"); + }, + error => this.handleUpdateError("System error creating template", error) + )); + } + + public savedSuccessfully(template: PluginTemplate, update:boolean) { + if(update){ + this.templates[this.index] = template; + }else{ + this.templates.push(template); + } + this.applyFilters(); + this.applyCheck(false); + this.showLoading = false; + } + + + public applyFilters() { + this.checkboxes = []; + this.templates.filter(item => this.filterByType(item)).forEach( + item => this.checkboxes.push({template: item, checked: false}) + ); + this.checkboxes = this.checkboxes.filter(item => this.filter(item.template)); + } + + public filterByType(template: PluginTemplate): boolean { + let type = this.filterForm.get("type").value; + return type == "all" || (type == template.portalType); + } + + public filter(plugin: PluginTemplate): boolean { + return this.searchText.toString() == '' || (plugin.name + ' ' + plugin.portalType).match(this.searchText) != null; + } + + handleUpdateError(message: string, error = null) { + if (error) { + console.log('Server responded: ' + error); + } + NotificationHandler.rise(message,'danger'); + this.showLoading = false; + } + + handleError(message: string, error = null) { + if (error) { + console.log('Server responded: ' + error); + } + NotificationHandler.rise(message,'danger'); + this.showLoading = false; + } + + getPages() { + this.showLoading = true; + this.subscriptions.push(this._helpContentService.getAllPages(this.properties.adminToolsAPIURL).subscribe( + pages => { + this.allPages = []; + pages.forEach(page => { + this.allPages.push({ + label: page.name + " [" + page.portalType + "]", + value: page + }); + }); + this.showLoading = false; + }, + error => this.handleError('System error retrieving pages', error) + )); + } + + addNewAttr(){ + (this.templateForm.get("attributes") as FormArray).push(this._fb.group({ + key:this._fb.control("", Validators.required), + name: this._fb.control("", Validators.required), + type: this._fb.control("text", Validators.required), + value: this._fb.control("") + })); + } + + removeAttr(index){ + this.attrFormArray.removeAt(index); + this.attrFormArray.markAsDirty(); + } + get attrFormArray(){ + + return this.templateForm.get("attributes") as FormArray; + + } + + attributeTypeChanged(form){ + let type = form.get("value").get("type"); + form.get("value").setValue(""); + if(type == "boolean"){ + form.get("value").setValue(false); + } + } + public getPagesAsString(pageIds): string { + + let pages = []; + for(let id of pageIds) { + pages.push(this.allPages.filter(option => option.value._id == id).map((option => option.value.name))); + + } + return pages.join(", "); + } + public getPageById(pageId) { + for(let option of this.allPages) { + if(option.value._id == pageId){ + return option.value; + } + } + return pageId; + } + +} diff --git a/dashboard/plugins/templates/pluginTemplates.module.ts b/dashboard/plugins/templates/pluginTemplates.module.ts new file mode 100644 index 00000000..aeafa647 --- /dev/null +++ b/dashboard/plugins/templates/pluginTemplates.module.ts @@ -0,0 +1,36 @@ +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {CommonModule} from '@angular/common'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {AlertModalModule} from '../../../utils/modal/alertModal.module'; +import {PluginTemplatesComponent} from './pluginTemplates.component'; +import {AdminToolServiceModule} from "../../../services/adminToolService.module"; +import {InputModule} from "../../../sharedComponents/input/input.module"; + + +import {MatAutocompleteModule} from '@angular/material/autocomplete'; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { MatFormFieldModule } from "@angular/material/form-field"; + + +import {MatChipsModule} from '@angular/material/chips'; +import {AdminTabsModule} from "../../sharedComponents/admin-tabs/admin-tabs.module"; +import {PageContentModule} from "../../sharedComponents/page-content/page-content.module"; +import {PluginTemplatesRoutingModule} from "./pluginTemplates-routing.module"; +import {SearchInputModule} from "../../../sharedComponents/search-input/search-input.module"; +import {IconsModule} from "../../../utils/icons/icons.module"; +import {LoadingModule} from "../../../utils/loading/loading.module"; +import {PluginsService} from "../../../services/plugins.service"; +import {CKEditorModule} from "ng2-ckeditor"; + +@NgModule({ + imports: [ + CommonModule, RouterModule, FormsModule, + AlertModalModule, ReactiveFormsModule, AdminToolServiceModule, InputModule, MatAutocompleteModule, MatFormFieldModule, MatChipsModule, + MatCheckboxModule, AdminTabsModule, PageContentModule, PluginTemplatesRoutingModule, SearchInputModule, IconsModule, LoadingModule, CKEditorModule + ], + providers:[PluginsService], + declarations: [PluginTemplatesComponent], + exports: [PluginTemplatesComponent] +}) +export class PluginTemplatesModule {} diff --git a/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts b/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts index 99677e59..4e5a107e 100644 --- a/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts +++ b/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts @@ -13,6 +13,7 @@ import {ActivatedRoute} from "@angular/router";
  • Entities
  • Menus
  • Classes
  • +
  • Templates
  • Customization
  • ` @@ -25,7 +26,7 @@ export class AdminTabsComponent implements OnInit { @Input() public user: User; @Input() - public tab: "portal" | "page" | "entity" | "menu" | "class" | "customization" = 'page'; + public tab: "portal" | "page" | "entity" | "menu" | "class" | "customization" | "template" = 'page'; private subscriptions: any[] = []; constructor(private route: ActivatedRoute, private userManagementService: UserManagementService) { diff --git a/services/plugins.service.ts b/services/plugins.service.ts new file mode 100644 index 00000000..9f756967 --- /dev/null +++ b/services/plugins.service.ts @@ -0,0 +1,36 @@ +import {Injectable} from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {DivId} from "../utils/entities/adminTool/divId"; +import {catchError} from "rxjs/operators"; +import {CustomOptions} from "./servicesUtils/customOptions.class"; +import {PluginTemplate} from "../utils/entities/adminTool/pluginTemplate"; + +@Injectable() +export class PluginsService { + + constructor(private http:HttpClient) { + } + + getAllPluginTemplates(api:string) { + return this.http.get>(api + 'pluginTemplates') + + } + getAllPlugins(api:string) { + return this.http.get>(api + 'plugins') + + } + + savePluginTemplate(pluginTemplate:PluginTemplate, api:string) { + // console.log(pluginTemplate, pluginTemplate.toJsonObj()) + return this.http.post(api + 'pluginTemplate/save', pluginTemplate, CustomOptions.getAuthOptionsWithBody()); + } + savePlugin(plugin, api:string) { + return this.http.post(api + 'plugin/save', JSON.stringify(plugin), CustomOptions.getAuthOptionsWithBody()); + } + deletePlugin(id, api:string) { + return this.http.delete(api + 'plugin/'+id, CustomOptions.getAuthOptionsWithBody()); + } + deletePluginTemplate(id, api:string) { + return this.http.delete(api + 'pluginTemplate/'+id, CustomOptions.getAuthOptionsWithBody()); + } +} diff --git a/utils/entities/adminTool/plugin.ts b/utils/entities/adminTool/plugin.ts new file mode 100644 index 00000000..897d5157 --- /dev/null +++ b/utils/entities/adminTool/plugin.ts @@ -0,0 +1,11 @@ +export class Plugin { + _id: string; + code: string; + page: string; + placement: string; + order: string; + isActive:boolean; + isPriorTo:boolean; + values:Map = new Map(); + +} diff --git a/utils/entities/adminTool/pluginTemplate.ts b/utils/entities/adminTool/pluginTemplate.ts new file mode 100644 index 00000000..5fb4cc06 --- /dev/null +++ b/utils/entities/adminTool/pluginTemplate.ts @@ -0,0 +1,32 @@ + + +export class PluginTemplate /*extends Map*/ { + _id: string; + name: string; + code: string; + description: string; + image: string; + pages: string[]; + placements: string[]; + portalType: string; + attributes: {};//Map = new Map(); + + +/* + toJsonObj(){ + let jsonObj = Object.assign({}, this); + var objectMap = { }; + for (let [key, value] of jsonObj['attributes']) objectMap[key] = value; + jsonObj['attributes'] = objectMap; + return jsonObj; + } + *//* + toJSON() { + // this.attributes + var object = { }; + for (let [key, value] of this.attributes) object[key] = value; + return object; + } + */ + +}