diff --git a/dashboard/divId/divIds.component.html b/dashboard/divId/divIds.component.html index dd03452e..3dc4a283 100644 --- a/dashboard/divId/divIds.component.html +++ b/dashboard/divId/divIds.component.html @@ -77,11 +77,9 @@ -
- - + +
diff --git a/dashboard/divId/divIds.module.ts b/dashboard/divId/divIds.module.ts index e4221fc3..24815b02 100644 --- a/dashboard/divId/divIds.module.ts +++ b/dashboard/divId/divIds.module.ts @@ -20,7 +20,7 @@ import {ClassesRoutingModule} from "./classes-routing.module"; import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module"; import {IconsModule} from "../../utils/icons/icons.module"; import {IconsService} from "../../utils/icons/icons.service"; -import {add} from "../../utils/icons/icons"; +import {add, edit, remove} from "../../utils/icons/icons"; import {LoadingModule} from "../../utils/loading/loading.module"; @NgModule({ @@ -34,6 +34,6 @@ import {LoadingModule} from "../../utils/loading/loading.module"; }) export class DivIdsModule { constructor(private iconsService: IconsService) { - this.iconsService.registerIcons([add]); + this.iconsService.registerIcons([add, edit, remove]); } } diff --git a/dashboard/divhelpcontent/class-help-contents.component.html b/dashboard/divhelpcontent/class-help-contents.component.html index a4dddb10..5472a224 100644 --- a/dashboard/divhelpcontent/class-help-contents.component.html +++ b/dashboard/divhelpcontent/class-help-contents.component.html @@ -94,22 +94,15 @@
-
- - - Edit - -
-
- - - Delete - -
+ +
diff --git a/dashboard/divhelpcontent/class-help-contents.module.ts b/dashboard/divhelpcontent/class-help-contents.module.ts index 59843842..5ab74f17 100644 --- a/dashboard/divhelpcontent/class-help-contents.module.ts +++ b/dashboard/divhelpcontent/class-help-contents.module.ts @@ -12,7 +12,7 @@ import {InputModule} from '../../sharedComponents/input/input.module'; import {SearchInputModule} from '../../sharedComponents/search-input/search-input.module'; import {IconsModule} from '../../utils/icons/icons.module'; import {IconsService} from '../../utils/icons/icons.service'; -import {add, arrow_left} from '../../utils/icons/icons'; +import {add, arrow_left, edit, remove} from '../../utils/icons/icons'; import {LoadingModule} from '../../utils/loading/loading.module'; import {HTMLToStringPipeModule} from '../../utils/pipes/HTMLToStringPipe.module'; import {ClassHelpContentsRoutingModule} from './class-help-contents-routing.module'; @@ -33,6 +33,6 @@ import {PageContentModule} from '../sharedComponents/page-content/page-content.m }) export class ClassHelpContentsModule { constructor(private iconsService: IconsService) { - this.iconsService.registerIcons([add, arrow_left]) + this.iconsService.registerIcons([add, arrow_left, edit, remove]) } } diff --git a/dashboard/entity/entities.component.html b/dashboard/entity/entities.component.html index 94d870b0..06d26acc 100644 --- a/dashboard/entity/entities.component.html +++ b/dashboard/entity/entities.component.html @@ -91,9 +91,8 @@
- - + +
diff --git a/dashboard/entity/entities.module.ts b/dashboard/entity/entities.module.ts index 349c2adf..69d8d4a4 100644 --- a/dashboard/entity/entities.module.ts +++ b/dashboard/entity/entities.module.ts @@ -13,7 +13,7 @@ import {EntitiesRoutingModule} from "./entities-routing.module"; import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module"; import {IconsModule} from "../../utils/icons/icons.module"; import {IconsService} from "../../utils/icons/icons.service"; -import {add} from "../../utils/icons/icons"; +import {add, edit, remove} from "../../utils/icons/icons"; import {LoadingModule} from "../../utils/loading/loading.module"; @NgModule({ @@ -26,6 +26,6 @@ import {LoadingModule} from "../../utils/loading/loading.module"; }) export class EntitiesModule { constructor(private iconsService: IconsService) { - this.iconsService.registerIcons([add]); + this.iconsService.registerIcons([add, edit, remove]); } } diff --git a/dashboard/helpTexts/page-help-contents.component.html b/dashboard/helpTexts/page-help-contents.component.html index 783ce5ee..6758cf75 100644 --- a/dashboard/helpTexts/page-help-contents.component.html +++ b/dashboard/helpTexts/page-help-contents.component.html @@ -61,60 +61,56 @@
+ +
diff --git a/dashboard/helpTexts/page-help-contents.module.ts b/dashboard/helpTexts/page-help-contents.module.ts index c4350d7d..ad23dd57 100644 --- a/dashboard/helpTexts/page-help-contents.module.ts +++ b/dashboard/helpTexts/page-help-contents.module.ts @@ -13,7 +13,7 @@ import {PageContentModule} from '../sharedComponents/page-content/page-content.m import {SearchInputModule} from '../../sharedComponents/search-input/search-input.module'; import {IconsModule} from '../../utils/icons/icons.module'; import {IconsService} from '../../utils/icons/icons.service'; -import {add, arrow_left} from '../../utils/icons/icons'; +import {add, arrow_left, edit, remove} from '../../utils/icons/icons'; import {LoadingModule} from '../../utils/loading/loading.module'; import {HTMLToStringPipeModule} from '../../utils/pipes/HTMLToStringPipe.module'; @@ -30,6 +30,6 @@ import {HTMLToStringPipeModule} from '../../utils/pipes/HTMLToStringPipe.module' }) export class PageHelpContentsModule { constructor(private iconsService: IconsService) { - this.iconsService.registerIcons([add, arrow_left]) + this.iconsService.registerIcons([add, arrow_left, edit, remove]) } } diff --git a/dashboard/menu/menu.component.html b/dashboard/menu/menu.component.html index 1d96a974..f7d949f5 100644 --- a/dashboard/menu/menu.component.html +++ b/dashboard/menu/menu.component.html @@ -1,17 +1,18 @@
-
- -
-
- +
+
+ +
+
+
+ +
+
+ +
No menu items found
-
-
+
+
- -
-
-
- -
- -
-
Select one of the pages
-
+ + +
+
+ +
+ +
+
Select one of the pages
+
- -
-
- -
Create New Page
-
- -
-
- -
- -
- -
-
- + +
+
+ +
Create New Page
+
+
+
+
+
+ +
+
+
+
+ - \ No newline at end of file + diff --git a/dashboard/menu/menu.component.ts b/dashboard/menu/menu.component.ts index fd7d76a8..412189c7 100644 --- a/dashboard/menu/menu.component.ts +++ b/dashboard/menu/menu.component.ts @@ -1,4 +1,4 @@ -import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {Component, ElementRef, OnInit, ViewChild, Input} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {HelpContentService} from '../../services/help-content.service'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; @@ -36,12 +36,12 @@ export class MenuComponent implements OnInit { public activeRootMenuId: string; public activeRootMenu: MenuItem; public childrenMenuItems: MenuItem[] = []; + private index: number; - + public menuItemForm: FormGroup; public pageForm: FormGroup; public rootMenuItems = []; - public menuItems = []; public allPages = []; public selectedMenuItem: string; @@ -123,11 +123,11 @@ export class MenuComponent implements OnInit { data => { this.rootMenuItems = data; if(data && data.length > 0) { - // this.activeRootMenuId = data[0]['_id']; this.changeActiveRootMenuItem(data[0]); } }, - err => console.error("Server error fetching menu items: ", err) + // err => console.error("Server error fetching menu items: ", err) + error => this.handleError("Server error fetching menu items", error) ) ); } @@ -163,7 +163,7 @@ export class MenuComponent implements OnInit { } } }, - err => console.error("Server error fetching pages: ", err) + error => this.handleError("Server error fetching pages", error) ) ); } @@ -172,7 +172,7 @@ export class MenuComponent implements OnInit { this.newPageWindowOpen = !this.newPageWindowOpen; this.pageForm = this._fb.group({ _id: this._fb.control(null), - route: this._fb.control('', Validators.required), + route: this._fb.control('', [Validators.required, StringUtils.validRoute(this.allPages, 'value')]), name: this._fb.control('', Validators.required), isEnabled: this._fb.control(true), portalType: this._fb.control(this.properties.adminToolsPortalType, Validators.required), @@ -202,7 +202,10 @@ export class MenuComponent implements OnInit { }); this.newPageWindowOpen = !this.newPageWindowOpen; this.menuItemForm.get('route').setValue(page.route); - } + this.menuItemForm.get('route').markAsDirty(); + this.showLoading = false; + }, + error => this.handleError('System error creating page', error) ) ); } @@ -211,14 +214,15 @@ export class MenuComponent implements OnInit { this.destroyTypeSubscription(); this.typeSub = this.menuItemForm.get('type').valueChanges.subscribe(value => { setTimeout(() => { - // console.log(value); + this.menuItemForm.controls['route'].clearValidators(); + this.menuItemForm.controls['url'].clearValidators(); if(value === "internal") { this.menuItemForm.controls['route'].setValidators([Validators.required]); - this.menuItemForm.controls['route'].updateValueAndValidity(); } else if(value === "external") { - this.menuItemForm.controls['url'].setValidators([Validators.required]); - this.menuItemForm.controls['url'].updateValueAndValidity(); + this.menuItemForm.controls['url'].setValidators([Validators.required, StringUtils.urlValidator()]); } + this.menuItemForm.controls['route'].updateValueAndValidity(); + this.menuItemForm.controls['url'].updateValueAndValidity(); }, 0); }); } @@ -243,8 +247,8 @@ export class MenuComponent implements OnInit { _id: this._fb.control(menuItem['_id']), title: this._fb.control(menuItem.title,Validators.required), type: this._fb.control(menuItem['type'],Validators.required), - route: this._fb.control(menuItem.route), - url: this._fb.control(menuItem.url), + route: this._fb.control(menuItem.route, (menuItem['type'] == "internal") ? [Validators.required] : []), + url: this._fb.control(menuItem.url, (menuItem['type'] == "external") ? [Validators.required, StringUtils.urlValidator()] : []), parentItemId: this._fb.control(menuItem['parentItemId']) }); this.isChild = isChild; @@ -284,7 +288,8 @@ export class MenuComponent implements OnInit { pos: 'bottom-right' }); this.showLoading = false; - } + }, + error => this.handleError("Server error deleting menu item", error) ) ) } @@ -323,7 +328,8 @@ export class MenuComponent implements OnInit { timeout: 6000, pos: 'bottom-right' }); - } + }, + error => this.handleError("System error creating menu item", error) ) ) } else { @@ -336,7 +342,8 @@ export class MenuComponent implements OnInit { timeout: 6000, pos: 'bottom-right' }); - } + }, + error => this.handleError("System error updating menu item", error) ) ) } @@ -360,8 +367,18 @@ export class MenuComponent implements OnInit { this.showLoading = false; } + handleError(message: string, error) { + UIkit.notification(message, { + status: 'danger', + timeout: 6000, + pos: 'bottom-right' + }); + console.log('Server responded: ' + error); + this.showLoading = false; + } + public applyFilters() { - this.childrenMenuItems = this.activeRootMenu.items.filter(item => item.title.toLowerCase().includes(this.searchText)); + this.childrenMenuItems = this.activeRootMenu.items.filter(item => item.title.toLowerCase().includes(this.searchText) || (item.url||'').toLowerCase().includes(this.searchText) || (item.route||'').toLowerCase().includes(this.searchText)); } public onSearchClose() { diff --git a/dashboard/menu/menu.module.ts b/dashboard/menu/menu.module.ts index 450812d8..a57d6f90 100644 --- a/dashboard/menu/menu.module.ts +++ b/dashboard/menu/menu.module.ts @@ -13,7 +13,7 @@ import {MenuRoutingModule} from "./menu-routing.module"; import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module"; import {IconsModule} from "../../utils/icons/icons.module"; import {IconsService} from "../../utils/icons/icons.service"; -import {add} from "../../utils/icons/icons"; +import {add, edit, remove} from "../../utils/icons/icons"; import {LoadingModule} from "../../utils/loading/loading.module"; @NgModule({ @@ -26,6 +26,6 @@ import {LoadingModule} from "../../utils/loading/loading.module"; }) export class MenuModule { constructor(private iconsService: IconsService) { - this.iconsService.registerIcons([add]); + this.iconsService.registerIcons([add, edit, remove]); } } diff --git a/dashboard/page/pages.component.html b/dashboard/page/pages.component.html index 9fb4796c..5437ff27 100644 --- a/dashboard/page/pages.component.html +++ b/dashboard/page/pages.component.html @@ -63,57 +63,71 @@
- +
@@ -171,7 +185,7 @@ By disabling a position, all contents in this position will be deleted.
-
+ diff --git a/dashboard/page/pages.component.ts b/dashboard/page/pages.component.ts index af631036..59785e37 100644 --- a/dashboard/page/pages.component.ts +++ b/dashboard/page/pages.component.ts @@ -242,7 +242,7 @@ export class PagesComponent implements OnInit { this.index = this.pages.findIndex(value => value._id === page._id); this.pageForm = this._fb.group({ _id: this._fb.control(page._id), - route: this._fb.control(page.route, Validators.required), + route: this._fb.control(page.route, [Validators.required, StringUtils.validRoute(this.pages, 'route', page.route)]), name: this._fb.control(page.name, Validators.required), isEnabled: this._fb.control(page.isEnabled), portalType: this._fb.control(page.portalType, Validators.required), @@ -268,7 +268,7 @@ export class PagesComponent implements OnInit { this.entitiesCtrl = this._fb.array([]); this.pageForm = this._fb.group({ _id: this._fb.control(null), - route: this._fb.control('', Validators.required), + route: this._fb.control('', [Validators.required, StringUtils.validRoute(this.pages, 'route')]), name: this._fb.control('', Validators.required), isEnabled: this._fb.control(true), portalType: this._fb.control('', Validators.required), @@ -364,7 +364,7 @@ export class PagesComponent implements OnInit { if (error == null) { // this.formComponent.reset(); this.pageForm = this._fb.group({ - route: this._fb.control('', Validators.required), + route: this._fb.control('', [Validators.required, StringUtils.validRoute(this.pages, 'route')]), name: this._fb.control('', Validators.required), isEnabled: this._fb.control(true), portalType: this._fb.control('', Validators.required), diff --git a/dashboard/page/pages.module.ts b/dashboard/page/pages.module.ts index bdac5062..ba6a54a5 100644 --- a/dashboard/page/pages.module.ts +++ b/dashboard/page/pages.module.ts @@ -16,7 +16,7 @@ import {PagesRoutingModule} from "./pages-routing.module"; import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module"; import {IconsModule} from "../../utils/icons/icons.module"; import {IconsService} from "../../utils/icons/icons.service"; -import {add} from "../../utils/icons/icons"; +import {add, edit, remove} from "../../utils/icons/icons"; import {LoadingModule} from "../../utils/loading/loading.module"; @NgModule({ @@ -29,6 +29,6 @@ import {LoadingModule} from "../../utils/loading/loading.module"; }) export class PagesModule { constructor(private iconsService: IconsService) { - this.iconsService.registerIcons([add]); + this.iconsService.registerIcons([add, edit, remove]); } } diff --git a/dashboard/portal/portals.component.html b/dashboard/portal/portals.component.html index c741e7b2..54fbeec3 100644 --- a/dashboard/portal/portals.component.html +++ b/dashboard/portal/portals.component.html @@ -69,9 +69,8 @@
- - + +
diff --git a/dashboard/portal/portals.module.ts b/dashboard/portal/portals.module.ts index d578f9b4..98f55248 100644 --- a/dashboard/portal/portals.module.ts +++ b/dashboard/portal/portals.module.ts @@ -10,7 +10,7 @@ import {AdminTabsModule} from "../sharedComponents/admin-tabs/admin-tabs.module" import {PageContentModule} from "../sharedComponents/page-content/page-content.module"; import {PortalsRoutingModule} from "./portals-routing.module"; import {IconsService} from "../../utils/icons/icons.service"; -import {add} from "../../utils/icons/icons"; +import {add, edit, remove} from "../../utils/icons/icons"; import {IconsModule} from "../../utils/icons/icons.module"; import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module"; import {LoadingModule} from "../../utils/loading/loading.module"; @@ -26,6 +26,6 @@ import {LoadingModule} from "../../utils/loading/loading.module"; }) export class PortalsModule { constructor(private iconsService: IconsService) { - this.iconsService.registerIcons([add]); + this.iconsService.registerIcons([add, edit, remove]); } } diff --git a/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts b/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts index 2fc24a99..788d5fac 100644 --- a/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts +++ b/dashboard/sharedComponents/admin-tabs/admin-tabs.component.ts @@ -12,7 +12,7 @@ import { properties } from 'src/environments/environment';
  • Portals
  • Pages
  • Entities
  • -
  • Menu
  • +
  • Menu
  • Classes
  • ` diff --git a/sharedComponents/input/input.component.ts b/sharedComponents/input/input.component.ts index 96de7b4b..d33aa0b5 100644 --- a/sharedComponents/input/input.component.ts +++ b/sharedComponents/input/input.component.ts @@ -77,7 +77,7 @@ export interface Option { (openedChange)="stopPropagation()" [formControl]="formControl" [disableOptionCentering]="true"> {{placeholder}} - + {{option.label}} @@ -200,7 +200,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges { @Input() panelWidth: number = 300; @Input() panelClass: string = null; @Input() showOptionsOnEmpty: boolean = true; - @Input() validators: ValidatorFn[]; + @Input() validators: ValidatorFn[] | ValidatorFn; @Output() focusEmitter: EventEmitter = new EventEmitter(); /** LogoUrl information */ public secure: boolean = true; @@ -211,6 +211,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges { public filteredOptions: Observable; public searchControl: FormControl; private subscriptions: any[] = []; + @Input() tooltip: boolean = true; @ViewChild('select') select: MatSelect; @ViewChild('searchInput') searchInput: ElementRef; focused: boolean = false; @@ -229,7 +230,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges { } ngOnChanges(changes: SimpleChanges) { - if (changes.formControl) { + if (changes.formControl || changes.validators) { this.reset(); } } @@ -346,6 +347,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges { resetSearch(event: any) { event.stopPropagation(); this.searchControl.setValue(''); + this.formControl.markAsDirty(); // TODO check - should it also become dirty on addition? this.formControl.setValue(null); } } diff --git a/sharedComponents/navigationBar.component.html b/sharedComponents/navigationBar.component.html index 9f17d5bc..e41bac00 100644 --- a/sharedComponents/navigationBar.component.html +++ b/sharedComponents/navigationBar.component.html @@ -241,6 +241,57 @@ + + + +
  • + + + {{menu.title}} + + + + {{menu.title}} + + + + {{menu.title}} + + +
  • +
    +
    diff --git a/sharedComponents/navigationBar.component.ts b/sharedComponents/navigationBar.component.ts index 7ae98b83..2b357877 100644 --- a/sharedComponents/navigationBar.component.ts +++ b/sharedComponents/navigationBar.component.ts @@ -5,6 +5,7 @@ import {ConfigurationService} from '../utils/configuration/configuration.service import {MenuItem, RootMenuItem} from './menu'; import {EnvProperties} from '../utils/properties/env-properties'; import {Subscription} from 'rxjs'; +import {HelpContentService} from '../services/help-content.service'; export interface Header { route?: string, @@ -49,12 +50,14 @@ export class NavigationBarComponent implements OnInit, OnDestroy { showPage = {}; specialAnnouncementContent: string = null; + public customMenuItems: MenuItem[] = []; constructor(private router: Router, private route: ActivatedRoute, - private config: ConfigurationService) { + private config: ConfigurationService, + private _helpContentService: HelpContentService) { } - + ngOnInit() { this.initialize(); } @@ -100,6 +103,16 @@ export class NavigationBarComponent implements OnInit, OnDestroy { this.handleError('Error getting community information (e.g. pages,entities) for community with id: ' + this.communityId, error); })); } + if(this.portal != 'connect') { + this.subs.push( + this._helpContentService.getMenuItems(this.portal).subscribe( + data => { + this.customMenuItems = data; + }, + error => this.handleError("Server error fetching custom menu items", error) + ) + ); + } } diff --git a/sharedComponents/navigationBar.module.ts b/sharedComponents/navigationBar.module.ts index 8fe20f36..f302555b 100644 --- a/sharedComponents/navigationBar.module.ts +++ b/sharedComponents/navigationBar.module.ts @@ -8,6 +8,7 @@ import { NavigationBarComponent} from './navigationBar.component'; import { UserMiniModule} from '../login/userMiniModule.module'; import {SearchBarModule} from "./searchBar/searchBar.module"; +import {HelpContentService} from '../services/help-content.service'; @NgModule({ imports: [ @@ -19,7 +20,7 @@ import {SearchBarModule} from "./searchBar/searchBar.module"; declarations: [ NavigationBarComponent ], - providers:[], + providers:[HelpContentService], exports: [ NavigationBarComponent ] diff --git a/utils/string-utils.class.ts b/utils/string-utils.class.ts index 441ea639..114dcdb5 100644 --- a/utils/string-utils.class.ts +++ b/utils/string-utils.class.ts @@ -1,8 +1,9 @@ import {UrlSegment} from '@angular/router'; -import {AbstractControl, ValidatorFn, Validators} from "@angular/forms"; +import {AbstractControl, ValidationErrors, ValidatorFn, Validators} from "@angular/forms"; import {Stakeholder} from "../monitor/entities/stakeholder"; import {CommunityInfo} from "../connect/community/communityInfo"; import {properties} from "../../../environments/environment"; +import {Page} from "./entities/adminTool/page"; export class Dates { public static yearMin = 1800; @@ -247,6 +248,8 @@ export class StringUtils { '[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.' + '[a-zA-Z0-9]+\.[^\s]{2,}'; + public static routeRegex = '^[a-zA-Z0-9\/][a-zA-Z0-9\/-]*$'; + public static urlPrefix(url: string): string { if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//")) { return ""; @@ -302,6 +305,21 @@ export class StringUtils { return Validators.pattern(StringUtils.urlRegex); } + public static validRoute(pages: any[], field: string, initial: string = null): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if(control.value) { + if(!new RegExp(this.routeRegex).test(control.value)) { + return {error: 'Route should contain only letters or numbers, e.g /route or route'} + } + if(pages && pages.length > 0 && control.value !== initial) { + const forbidden = pages.filter(page => page[field].replace('/', '') === control.value.replace('/', '')).length > 0; + return forbidden ? {error: 'This route is used by an other page'} : null; + } + } + return null; + }; + } + public static sliceString(mystr, size: number): string { const sliced = String(mystr).substr(0, size); return sliced + (String(mystr).length > size ? '...' : '');