diff --git a/package-lock.json b/package-lock.json index d1d7d2301b0b59cf6f3fc01cee5a40ad7ba4a799..034baa47c2ade305678e7be9e2c3a0ce9f88bc49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15368,6 +15368,12 @@ "tslib": "^2.0.0" } }, + "ng2-completer": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ng2-completer/-/ng2-completer-9.0.1.tgz", + "integrity": "sha512-zEKehHdCK8E/k4Y0HepprGdYBHr2AOsaq4QpeqoCyUElOOC5M3qi3ZEHV1VF54I7heBQktswwXe5UyWduJ0Xeg==", + "dev": true + }, "ng2-smart-table": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ng2-smart-table/-/ng2-smart-table-1.6.0.tgz", diff --git a/package.json b/package.json index e4e59323acda71e633e480f8acfd09028465ba95..e5450467ff22408f3d118a115e3c4830df4dbda8 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,7 @@ "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", + "ng2-completer": "^9.0.1", "npm-run-all": "4.0.2", "protractor": "~7.0.0", "rimraf": "2.6.1", diff --git a/src/app/model/clone-dashboard.model.ts b/src/app/model/clone-dashboard.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..924bf77d5bf331830b98fd97e87f2b80b28f16e5 --- /dev/null +++ b/src/app/model/clone-dashboard.model.ts @@ -0,0 +1,20 @@ +import { CloningOptionType } from "./enumeration/cloning-option-type.model"; + +export interface ICloneDashboard { + id?: number; + dashboardName?: string; + option?: CloningOptionType | null; +} + +export class CloneDashboard implements ICloneDashboard { + constructor( + public id?: number, + public dashboardName?: string, + public option?: CloningOptionType | null + ) { + } +} + +export function getCloneDashboardIdentifier(cloneDashboard: ICloneDashboard): number | undefined { + return cloneDashboard.id; +} \ No newline at end of file diff --git a/src/app/model/dashboard-component.model.ts b/src/app/model/dashboard-component.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6e02aa25aee191f526e06d3aff330b47e169f3f --- /dev/null +++ b/src/app/model/dashboard-component.model.ts @@ -0,0 +1,30 @@ +import { IDashboardPage } from "./dashboard-page.model"; +import { ComponentType } from "./enumeration/component-type.model"; + +export interface IDashboardComponent { + id?: number; + componentType?: ComponentType; + componentName?: string; + componentcontent?: string | null; + componentPosition?: string | null; + //createdDate?: dayjs.Dayjs | null; + createdBy?: string | null; + dashboardPage?: IDashboardPage | null; +} + +export class DashboardComponent implements IDashboardComponent { + constructor( + public id?: number, + public componentType?: ComponentType, + public componentName?: string, + public componentcontent?: string | null, + public componentPosition?: string | null, + //public createdDate?: dayjs.Dayjs | null, + public createdBy?: string | null, + public dashboardPage?: IDashboardPage | null + ) {} +} + +export function getDashboardComponentIdentifier(dashboardComponent: IDashboardComponent): number | undefined { + return dashboardComponent.id; +} diff --git a/src/app/model/dashboard-page.model.ts b/src/app/model/dashboard-page.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..fbf9a1c20ebed736b8c1a7ae2ef7bbf979936b69 --- /dev/null +++ b/src/app/model/dashboard-page.model.ts @@ -0,0 +1,44 @@ +import { IDashboardComponent } from "./dashboard-component.model"; +import { MenuType } from "./enumeration/menu-type.model"; +import { IMenuItem } from "./menu-item.model"; +import { ITarget } from "./target.model"; + +export interface IDashboardPage { + id?: number; + name?: string; + description?: string | null; + content?: string; + note?: string | null; + shared?: boolean | null; + pageOwner?: boolean | null; + type?: MenuType | null, + //createdDate?: dayjs.Dayjs | null; + createdBy?: string | null; + dashboardComponents?: IDashboardComponent[] | null; + menuItem?: IMenuItem | null; + targets?: ITarget[] | null; +} + +export class DashboardPage implements IDashboardPage { + constructor( + public id?: number, + public name?: string, + public description?: string | null, + public content?: string, + public note?: string | null, + public shared?: boolean | null, + public pageOwner?: boolean | null, + public type?: MenuType | null, + //public createdDate?: dayjs.Dayjs | null, + public createdBy?: string | null, + public dashboardComponents?: IDashboardComponent[] | null, + public menuItem?: IMenuItem | null, + public targets?: ITarget[] | null + ) { + this.shared = this.shared ?? false; + } +} + +export function getDashboardPageIdentifier(dashboardPage: IDashboardPage): number | undefined { + return dashboardPage.id; +} \ No newline at end of file diff --git a/src/app/model/enumeration/cloning-option-type.model.ts b/src/app/model/enumeration/cloning-option-type.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..8cad56724b22eb1cf44c12a24606f77b9eb53145 --- /dev/null +++ b/src/app/model/enumeration/cloning-option-type.model.ts @@ -0,0 +1,20 @@ +export enum CloningOptionType { + NONE = 'NONE', + MENU = 'MENU', + TARGETS = 'TARGETS', + COMPONENTS ='COMPONENTS', + ALL = 'ALL', + } + + export namespace CloningOptionType { + export function values() { + return Object.keys(CloningOptionType) + .filter((type) => isNaN(<any>type) && type !== 'values') + .map((x) => { + return { + enumVal: x, + value: x, + }; + }); + } + } \ No newline at end of file diff --git a/src/app/model/enumeration/component-type.model.ts b/src/app/model/enumeration/component-type.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c7947a6fe227f069d32201a3cfc5e10d26dac51 --- /dev/null +++ b/src/app/model/enumeration/component-type.model.ts @@ -0,0 +1,23 @@ +export enum ComponentType { + TEXT = 'TEXT', + IMAGE = 'IMAGE', + CHART = 'CHART', + IFRAME = 'IFRAME', + WEATHER = 'WEATHER', + MAP = 'MAP', + STATISTICS = 'STATISTICS' +} + + +export namespace ComponentType { + export function values() { + return Object.keys(ComponentType) + .filter((type) => isNaN(<any>type) && type !== 'values') + .map((x) => { + return { + enumVal: x, + value: x, + }; + }); + } +} diff --git a/src/app/model/enumeration/menu-type.model.ts b/src/app/model/enumeration/menu-type.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..08ad953ec02da0fb9bb41227a6413a639d489984 --- /dev/null +++ b/src/app/model/enumeration/menu-type.model.ts @@ -0,0 +1,17 @@ +export enum MenuType { + SHARED = 'SHARED', + PERSONAL = 'PERSONAL', +} + +export namespace MenuType { + export function values() { + return Object.keys(MenuType) + .filter((type) => isNaN(<any>type) && type !== 'values') + .map((x) => { + return { + enumVal: x, + value: x, + }; + }); + } +} \ No newline at end of file diff --git a/src/app/model/enumeration/target-type.model.ts b/src/app/model/enumeration/target-type.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4788fd82245dbbe5b3b164a01440c3ca76a782d --- /dev/null +++ b/src/app/model/enumeration/target-type.model.ts @@ -0,0 +1,18 @@ +export enum TargetType { + PERSON = 'PERSON', + GROUP = 'GROUP', + ROLE = 'ROLE', +} + +export namespace TargetType { + export function values() { + return Object.keys(TargetType) + .filter((type) => isNaN(<any>type) && type !== 'values') + .map((x) => { + return { + enumVal: x, + value: x, + }; + }); + } +} \ No newline at end of file diff --git a/src/app/model/group-representation.model.ts b/src/app/model/group-representation.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..90a3ed963d767f4bdffe73c236c280389d0592e6 --- /dev/null +++ b/src/app/model/group-representation.model.ts @@ -0,0 +1,20 @@ + +export interface IGroupRepresentation { + id?: string; + name?: string; + path?: string; + subGroups?: Array<IGroupRepresentation>; + } + + export class GroupRepresentation implements IGroupRepresentation { + constructor( + public id?: string, + public name?: string, + public path?: string, + public subGroups?: Array<IGroupRepresentation> + ) {} + } + + export function getGroupRepresentationIdentifier(groupRepresentation: IGroupRepresentation): string | undefined { + return groupRepresentation.id; + } \ No newline at end of file diff --git a/src/app/model/menu-block.model.ts b/src/app/model/menu-block.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..7f8c9c05cfba07ef54f3158420ef03f40286389c --- /dev/null +++ b/src/app/model/menu-block.model.ts @@ -0,0 +1,32 @@ +import { MenuType } from "./enumeration/menu-type.model"; +import { IMenuItem } from "./menu-item.model"; + +export interface IMenuBlock { + id?: number; + code?: string; + label?: string; + order?: number | null; + type?: MenuType; + icon?: string; + //createdDate?: dayjs.Dayjs | null; + createdBy?: string | null; + linkedItems?: IMenuItem[] | null; + } + + export class MenuBlock implements IMenuBlock { + constructor( + public id?: number, + public code?: string, + public label?: string, + public order?: number | null, + public type?: MenuType, + public icon?: string, + //public createdDate?: dayjs.Dayjs | null, + public createdBy?: string | null, + public linkedItems?: IMenuItem[] | null + ) {} + } + + export function getMenuBlockIdentifier(menuBlock: IMenuBlock): number | undefined { + return menuBlock.id; + } \ No newline at end of file diff --git a/src/app/model/menu-item.model.ts b/src/app/model/menu-item.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..0606319f344a272a87b6a3439e88958121a6e0dd --- /dev/null +++ b/src/app/model/menu-item.model.ts @@ -0,0 +1,35 @@ +import { IDashboardPage } from "./dashboard-page.model"; +import { MenuType } from "./enumeration/menu-type.model"; +import { IMenuBlock } from "./menu-block.model"; + +export interface IMenuItem { + id?: number; + code?: string; + label?: string; + order?: number | null; + type?: MenuType; + icon?: string; + //createdDate?: dayjs.Dayjs | null; + createdBy?: string | null; + dashboardPage?: IDashboardPage | null; + menuBlock?: IMenuBlock | null; +} + +export class MenuItem implements IMenuItem { + constructor( + public id?: number, + public code?: string, + public label?: string, + public order?: number | null, + public type?: MenuType, + public icon?: string, + //public createdDate?: dayjs.Dayjs | null, + public createdBy?: string | null, + public dashboardPage?: IDashboardPage | null, + public menuBlock?: IMenuBlock | null + ) {} +} + +export function getMenuItemIdentifier(menuItem: IMenuItem): number | undefined { + return menuItem.id; +} diff --git a/src/app/model/role-representation.model.ts b/src/app/model/role-representation.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..8756614218cb0756f889de7e79497ff1c4df0995 --- /dev/null +++ b/src/app/model/role-representation.model.ts @@ -0,0 +1,22 @@ + +export interface IRoleRepresentation { + id?: string; + name?: string; + description?: string; + composite?: boolean; + clientRole?: boolean; + } + + export class RoleRepresentation implements IRoleRepresentation { + constructor( + public id?: string, + public name?: string, + public description?: string, + public composite?: boolean, + public clientRole?: boolean + ) {} + } + + export function getRoleRepresentationIdentifier(roleRepresentation: IRoleRepresentation): string | undefined { + return roleRepresentation.id; + } \ No newline at end of file diff --git a/src/app/model/target-available.model.ts b/src/app/model/target-available.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..96a1d45b66226de70f2e83cc21fe3ce0eab04e2f --- /dev/null +++ b/src/app/model/target-available.model.ts @@ -0,0 +1,25 @@ +import { TargetType } from "./enumeration/target-type.model"; + +export interface ITargetAvailable { + id?: number; + type?: TargetType; + code?: string; + value?: string; + //createdDate?: dayjs.Dayjs | null; + createdBy?: string | null; +} + +export class TargetAvailable implements ITargetAvailable { + constructor( + public id?: number, + public type?: TargetType, + public code?: string, + public value?: string, + //public createdDate?: dayjs.Dayjs | null, + public createdBy?: string | null + ) {} +} + +export function getTargetAvailableIdentifier(targetAvailable: ITargetAvailable): number | undefined { + return targetAvailable.id; +} \ No newline at end of file diff --git a/src/app/model/target.model.ts b/src/app/model/target.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f34f2d460bce8924a97a325b4a5a12873e8ebd2 --- /dev/null +++ b/src/app/model/target.model.ts @@ -0,0 +1,28 @@ +import { IDashboardPage } from "./dashboard-page.model"; +import { TargetType } from "./enumeration/target-type.model"; + +export interface ITarget { + id?: number; + type?: TargetType; + name?: string; + note?: string | null; + //createdDate?: dayjs.Dayjs | null; + createdBy?: string | null; + dashboardPage?: IDashboardPage | null; +} + +export class Target implements ITarget { + constructor( + public id?: number, + public type?: TargetType, + public name?: string, + public note?: string | null, + //public createdDate?: dayjs.Dayjs | null, + public createdBy?: string | null, + public dashboardPage?: IDashboardPage | null + ) {} +} + +export function getTargetIdentifier(target: ITarget): number | undefined { + return target.id; +} \ No newline at end of file diff --git a/src/app/model/user.model.ts b/src/app/model/user.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..32a565db04bf2880d7a299e226c4a31ad9d18c11 --- /dev/null +++ b/src/app/model/user.model.ts @@ -0,0 +1,22 @@ + +export interface IUser { + id?: string; + username?: string; + firstName?: string; + lastName?: string; + email?: string; +} + +export class User implements IUser { + constructor( + public id?: string, + public username?: string, + public firstName?: string, + public lastName?: string, + public email?: string + ) {} +} + +export function getUserIdentifier(user: IUser): string | undefined { + return user.id; +} \ No newline at end of file diff --git a/src/app/pages/dashboard-management/chart-view/chart-view.component.html b/src/app/pages/dashboard-management/chart-view/chart-view.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e85cf3ee88a3ed3c6650c41a1d0766fa66b13b9c --- /dev/null +++ b/src/app/pages/dashboard-management/chart-view/chart-view.component.html @@ -0,0 +1,7 @@ + +<div [innerHTML]="chartContent | safeHtml"> +</div> + +<!-- +<iframe [src]="chartContent | safeHtml"></iframe> +--> diff --git a/src/app/pages/dashboard-management/chart-view/chart-view.component.scss b/src/app/pages/dashboard-management/chart-view/chart-view.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/chart-view/chart-view.component.spec.ts b/src/app/pages/dashboard-management/chart-view/chart-view.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..68b0e808a512b4336c9fcc8fc591127e76c089f4 --- /dev/null +++ b/src/app/pages/dashboard-management/chart-view/chart-view.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChartViewComponent } from './chart-view.component'; + +describe('ChartViewComponent', () => { + let component: ChartViewComponent; + let fixture: ComponentFixture<ChartViewComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ChartViewComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ChartViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/chart-view/chart-view.component.ts b/src/app/pages/dashboard-management/chart-view/chart-view.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a4cb1f66fa44e642e7dd3a18465061352e45eb1 --- /dev/null +++ b/src/app/pages/dashboard-management/chart-view/chart-view.component.ts @@ -0,0 +1,17 @@ +import { Component, Input, OnInit } from '@angular/core'; + +@Component({ + selector: 'ngx-chart-view', + templateUrl: './chart-view.component.html', + styleUrls: ['./chart-view.component.scss'] +}) +export class ChartViewComponent implements OnInit { + @Input() chartContent: string; + + constructor() { + } + + ngOnInit(): void { + } + +} diff --git a/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.html b/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.html new file mode 100644 index 0000000000000000000000000000000000000000..0deea456a0c68a64600749a8c7194cef9c3eb84a --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.html @@ -0,0 +1,156 @@ +<nb-card> + <nb-card-body> + <nb-stepper orientation="horizontal"> + <nb-step [label]="labelOne" + [stepControl]="selectedDashboardFormGroup.invalid || !isEmptyObject(selectedDashboardFormGroup.value.selectedDashboard)"> + <form [formGroup]="selectedDashboardFormGroup" (ngSubmit)="onSubmitFirst(selectedDashboard)"> + <ng-template #labelOne>{{'dashboardClone.step_one'|translate}}</ng-template> + <h4>{{'dashboardClone.question_one'|translate}}</h4> + + <div class="d-flex flex-row align-items-end"> + <div class="d-flex flex-column"> + <label for="name" class="label mr-2"> + {{'dashboardClone.source_dashboard'|translate}} * + </label> + <nb-select size="medium" placeholder="{{'dashboardClone.select_dashboard'|translate}}" + formControlName="selectedDashboard" [(selected)]="selectedDashboard"> + <nb-option-group title="{{'MenuType.SHARED'|translate}}"> + <nb-option *ngFor="let dashboardPage of fullListDashboardPageShared;" + [value]="dashboardPage"> + {{dashboardPage.name}} + </nb-option> + </nb-option-group> + <nb-option-group title="{{'MenuType.PERSONAL'|translate}}"> + <nb-option *ngFor="let dashboardPage of fullListDashboardPagePersonal;" + [value]="dashboardPage"> + {{dashboardPage.name}} + </nb-option> + </nb-option-group> + </nb-select> + </div> + + + <div class="ml-3"> + <button nbButton size="small" status="info" + nbTooltip="{{'dashboardClone.action_preview'|translate}}" + (click)="previewDashboard(selectedDashboardFormGroup.value.selectedDashboard.id)"> + {{'dashboardClone.action_preview'|translate}} + </button> + <nb-icon class="ml-2" icon="info-outline" nbTooltip="{{'dashboardClone.info'|translate}}"> + </nb-icon> + </div> + </div> + + + <div class="row my-2 mt-50"> + <div class="col-sm text-center"> + <button nbButton size="small" status="primary" nbStepperPrevious disabled type="button"> + {{'dashboardClone.action_prev'|translate}} + </button> + </div> + <div class="col-sm text-center"> + <button nbButton size="small" status="primary" nbStepperNext + [disabled]="selectedDashboardFormGroup.invalid || !isEmptyObject(selectedDashboardFormGroup.value.selectedDashboard)" + type="submit" + (click)="onSubmitFirst(selectedDashboardFormGroup.value.selectedDashboard)"> + {{'dashboardClone.action_next'|translate}} + </button> + </div> + </div> + </form> + </nb-step> + + + + <nb-step [label]="labelTwo" [stepControl]="createdDashboardFormGroup"> + <form [formGroup]="createdDashboardFormGroup" (ngSubmit)="onSubmitSecond(createdDashboardFormGroup.value.createdDashboardName, + createdDashboardFormGroup.value.createdDashboardCloneTargets)"> + <ng-template #labelTwo> + {{'dashboardClone.step_two'|translate}} + </ng-template> + <h4>{{'dashboardClone.question_two'|translate}}</h4> + + <div class="d-flex flex-column"> + <label for="name" class="label mr-2"> + {{'dashboardClone.cloned_dashboard_name'|translate}} * + </label> + <input type="text" nbInput placeholder=" {{'dashboardClone.add_dashboard_name'|translate}}" + formControlName="createdDashboardName"> + </div> + <div *ngIf="getSharedStatus()" class="m-3"> + <nb-toggle labelPosition="end" formControlName="createdDashboardCloneTargets"> + {{'dashboardClone.clone_targets'|translate}} + </nb-toggle> + </div> + <div class="row my-2"> + <div class="col-sm text-center"> + <button nbButton size="small" status="primary" nbStepperPrevious type="button"> + {{'dashboardClone.action_prev'|translate}} + </button> + </div> + <div class="col-sm text-center"> + <button nbButton size="small" status="primary" nbStepperNext + [disabled]="createdDashboardFormGroup.invalid" type="submit" + (click)="onSubmitSecond(createdDashboardFormGroup.value.createdDashboardName, createdDashboardFormGroup.value.createdDashboardCloneTargets)"> + + {{'dashboardClone.action_next'|translate}} + </button> + </div> + </div> + </form> + </nb-step> + + + + <nb-step label="{{'dashboardClone.step_three'|translate}}"> + <h4>{{'dashboardClone.question_three'|translate}}</h4> + <nb-card> + <nb-list> + <nb-list-item> + <nb-user name="{{'dashboardClone.question_one'|translate}}" + title="{{selectedDashboardFormGroup.value.selectedDashboard.name}}" + picture="/assets/images/browser-outline.png" shape="semi-round"> + </nb-user> + </nb-list-item> + <nb-icon icon=" info-outline" nbTooltip="{{'dashboardClone.action_preview'|translate}}" + (click)="previewDashboard(selectedDashboardFormGroup.value.selectedDashboard.id)"> + </nb-icon> + <nb-list-item> + <nb-user name="{{'dashboardClone.question_two'|translate}}" + title="{{createdDashboardFormGroup.value.createdDashboardName}}" + picture="/assets/images/edit-outline.png" shape="semi-round"> + </nb-user> + </nb-list-item> + <nb-list-item> + <nb-user name="{{'dashboardClone.question_targets'|translate}}" + title="{{createdDashboardFormGroup.value.createdDashboardCloneTargets}}" + picture="/assets/images/checkmark-outline.png" shape="semi-round" + *ngIf="createdDashboardFormGroup.value.createdDashboardCloneTargets !== null && createdDashboardFormGroup.value.createdDashboardCloneTargets !== false"> + </nb-user> + <nb-user name="{{'dashboardClone.question_targets'|translate}}" + title="{{createdDashboardFormGroup.value.createdDashboardCloneTargets}}" + picture="/assets/images/close-outline.png" shape="semi-round" + *ngIf="!(createdDashboardFormGroup.value.createdDashboardCloneTargets !== null && createdDashboardFormGroup.value.createdDashboardCloneTargets !== false)"> + </nb-user> + </nb-list-item> + </nb-list> + </nb-card> + <div class="row my-2"> + <div class="col-sm text-center"> + <button nbButton size="small" status="primary" nbStepperPrevious type="button"> + {{'dashboardClone.action_prev'|translate}} + </button> + </div> + <div class="col-sm text-center"> + <button nbButton size="small" status="primary" nbStepperNext (click)="executeClone()" + type="submit"> + {{'dashboardClone.action_clone'|translate}} + </button> + </div> + </div> + </nb-step> + + + </nb-stepper> + </nb-card-body> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.scss b/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.spec.ts b/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..8fa4049b58a21194cc9424a6b28d7cdc2e30b791 --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DashboardCloneWizardComponent } from './dashboard-clone-wizard.component'; + +describe('DashboardCloneWizardComponent', () => { + let component: DashboardCloneWizardComponent; + let fixture: ComponentFixture<DashboardCloneWizardComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DashboardCloneWizardComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardCloneWizardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.ts b/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..d65a19ea5311916a30f82e43908dd9ae9ae7ebb7 --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-clone-wizard/dashboard-clone-wizard.component.ts @@ -0,0 +1,138 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NbToastrService } from '@nebular/theme'; +import { TranslateService } from '@ngx-translate/core'; +import { ICloneDashboard } from '../../../model/clone-dashboard.model'; +import { IDashboardPage } from '../../../model/dashboard-page.model'; +import { CloningOptionType } from '../../../model/enumeration/cloning-option-type.model'; +import { MenuType } from '../../../model/enumeration/menu-type.model'; +import { ITarget } from '../../../model/target.model'; +import { ManageDashboardPageContentComponent } from '../manage-dashboard-page-content/manage-dashboard-page-content.component'; +import { DashboardPageCrudService } from '../services/dashboard-page-crud.service'; + +@Component({ + selector: 'ngx-dashboard-clone-wizard', + templateUrl: './dashboard-clone-wizard.component.html', + styleUrls: ['./dashboard-clone-wizard.component.scss'] +}) +export class DashboardCloneWizardComponent implements OnInit { + selectedDashboardFormGroup: FormGroup; + createdDashboardFormGroup: FormGroup; + selectedDashboard: IDashboardPage = {}; + createdDashboard: ICloneDashboard = {}; + createdDashboardCloneTargets: ITarget; + selectedDashboardFromRoute: IDashboardPage = {}; + private foundDashboard: IDashboardPage; + + fullListEditableDashboards: Array<IDashboardPage> = []; + fullListDashboardPageShared: Array<IDashboardPage> = []; + fullListDashboardPagePersonal: Array<IDashboardPage> = []; + + public parsedId = 0; + + private CLONED_SUFFIX = " (cloned)"; + + constructor( + private readonly formBuilder: FormBuilder, + private dashboardPageCrudService: DashboardPageCrudService, + private toastrService: NbToastrService, + private translateService: TranslateService, + private router: Router, + private route: ActivatedRoute, + private dialogService: MatDialog + ) { } + + ngOnInit(): void { + this.selectedDashboardFormGroup = this.formBuilder.group({ + selectedDashboard: [this.selectedDashboard, Validators.nullValidator] + }); + + this.createdDashboardFormGroup = this.formBuilder.group({ + createdDashboardName: [this.createdDashboard.dashboardName, Validators.required], + createdDashboardCloneTargets: [this.createdDashboardCloneTargets, ''], + }); + + this.dashboardPageCrudService.getClonablePages().subscribe( + (res) => { + this.fullListEditableDashboards = res; + this.fullListDashboardPageShared = this.fullListEditableDashboards.filter(x => x.type == MenuType.SHARED); + this.fullListDashboardPagePersonal = this.fullListEditableDashboards.filter(x => x.type == MenuType.PERSONAL); + + this.route.queryParams.subscribe(params => { + if (params['parsedId'] != undefined && params['parsedId'] != null) { + this.parsedId = params['parsedId']; + this.foundDashboard = this.fullListEditableDashboards.find(x => x.id == this.parsedId); + this.selectedDashboardFormGroup.patchValue( { selectedDashboard: this.foundDashboard }); + } + }); + }, + error => (this.showError(error)) + ); + } + + getSharedStatus(){ + return this.selectedDashboardFormGroup.value.selectedDashboard?.type == 'SHARED' ? true : false; + } + + isEmptyObject(object: any){ + return !!Object.keys(object).length; + } + + onSubmitFirst(selected : IDashboardPage){ + this.selectedDashboard = selected; + this.createdDashboardFormGroup.patchValue( { createdDashboardName: (this.selectedDashboard.name + this.CLONED_SUFFIX) }); + } + + onSubmitSecond(name: any , targets: any){ + this.createdDashboard.dashboardName = name; + if (targets !== undefined && targets !== null){ + this.createdDashboardCloneTargets = targets; + } + } + + /** Invoke API to clone the Dashboard */ + executeClone(){ + //console.log("## selectedDashboard.id: "+ this.selectedDashboard.id + " selectedDashboard:" + this.selectedDashboard); + if (this.selectedDashboard.id == null || this.selectedDashboard.id == undefined){ + this.showError(this.translateService.instant('dashboardClone.clone_missing_parameter')); + } + this.createdDashboard.id = this.selectedDashboard.id; + if (this.createdDashboard){ + this.createdDashboard.option = CloningOptionType.TARGETS; + } + this.dashboardPageCrudService.duplicatePageWithParams(this.createdDashboard) + .subscribe( (res) => { + console.log('## Dashboard has been succesfully cloned, (original) id: ' + this.createdDashboard.id); + this.router.navigate(['/pages/dashboard-management/manage-dashboard-pages']); + this.showSuccess(); + },err => { + console.log("# Error during cloning due to e: " + err); + this.showError(err); + }); + + } + + showSuccess() { + this.toastrService.success(this.translateService.instant('dashboardPage.succesfully_cloned'), this.translateService.instant('Success')); + } + + showError(error) { + this.toastrService.danger(error.message, this.translateService.instant('general.error')) + } + + previewDashboard(id: number) { + if (id !== null && id !== undefined) { + let dialogRef = this.dialogService.open(ManageDashboardPageContentComponent, { + height: '720px', + width: '1280px', + data: { + parsedId: this.selectedDashboard.id, + parsedEditMode: false, + } + }); + } + } + +} diff --git a/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.html b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..db76a42a97745894841a30d16e62a0e43019d271 --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.html @@ -0,0 +1,150 @@ +<nb-card> + <nb-card-header>{{'dashboardComponent.properties'|translate}}</nb-card-header> + + <nb-card-body> + <div class="d-flex flex-column"> + <div class="item-select"> + <label class="label mr-2">{{'dashboardComponent.selectComponentType'|translate}}</label> + <nb-select size="small" placeholder="{{'dashboardComponent.selectComponentType'|translate}}" + [(selected)]="selectedItemType"> + <nb-option value='TEXT'>{{'ComponentType.TEXT'|translate}}</nb-option> + <nb-option value='IMAGE'>{{'ComponentType.IMAGE'|translate}}</nb-option> + <nb-option value='CHART'>{{'ComponentType.CHART'|translate}}</nb-option> + <nb-option value='IFRAME'>{{'ComponentType.IFRAME'|translate}}</nb-option> + <nb-option value='STATISTICS'>{{'ComponentType.STATISTICS'|translate}}</nb-option> + <!--<nb-option value='MAP'>{{'ComponentType.MAP'|translate}}</nb-option>--> + <nb-option value='WEATHER'>{{'ComponentType.WEATHER'|translate}}</nb-option> + </nb-select> + </div> + </div> + <div class="d-flex flex-column"> + <p></p> + </div> + + <div *ngIf="selectedItemType=='TEXT'"> + <angular-editor [placeholder]="'dashboardComponent.fill_text'|translate" [(ngModel)]="item.text" + [config]="editorConfig"></angular-editor> + </div> + + <div *ngIf="selectedItemType=='IMAGE'"> + <div *ngIf="item.image !== undefined && item.image !== null && item.url == false" class="image-preview"> + <img [src]="item.image" [alt]="item.image"> + </div> + <div *ngIf="item.imageUrl !== undefined && item.imageUrl !== null && item.url == true" + class="image-preview"> + <img [src]="item.imageUrl" [alt]="item.imageUrl"> + </div> + <nb-radio-group [(ngModel)]="selectedOption"> + <nb-radio value="upload-file">{{'dashboardComponent.upload_from_file'|translate}}</nb-radio> + <nb-radio value="upload-url">{{'dashboardComponent.upload_from_url/base64'|translate}}</nb-radio> + </nb-radio-group> + <div [hidden]="getHiddenFileUploadBlock()"> + <label class="label mr-2">{{'dashboardComponent.label_upload_image'|translate}}</label> + <button type="button" nbButton + (click)="fileInput.click()">{{'dashboardComponent.choose_file'|translate}}</button> + + <input hidden type="file" id="file" (change)="fileChangeEvent($event)" #fileInput /> + <image-cropper [imageChangedEvent]="imageChangedEvent" + [maintainAspectRatio]="maintainAspectRatioVariable" [aspectRatio]="aspectRatioVariable" format="jpg" + (imageCropped)="imageCropped($event)" (imageLoaded)="imageLoaded()" (cropperReady)="cropperReady()" + (loadImageFailed)="loadImageFailed()"> + </image-cropper> + </div> + + <div *ngIf="item.url=='true' || selectedOption == 'upload-url'"> + <label class="label mr-2">{{'dashboardComponent.label_image_url'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_image'|translate}}" rows="8" + [(ngModel)]="item.imageUrl"></textarea> + </div> + </div> + + <div *ngIf="selectedItemType=='CHART'" class="chart-select"> + <label class="label mr-2">{{'dashboardComponent.label_datalet'|translate}}</label> + <nb-select placeholder="{{'dashboardComponent.select_datalet'|translate}}" [(selected)]="dataletObject"> + <nb-option *ngFor="let datalet of datalets" [value]='datalet'> {{ datalet.title }} + </nb-option> + </nb-select> + </div> + + <div *ngIf="selectedItemType=='CHART' + && dataletObject !== undefined && dataletObject !== null + && dataletObject.datalet_html !== undefined && dataletObject.datalet_html !== null" + [innerHTML]="dataletObject.datalet_html | safeHtml"> + </div> + + <div *ngIf="selectedItemType=='IFRAME'"> + <div> + <label class="label mr-2">{{'dashboardComponent.label_content'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_iframe'|translate}}" rows="8" + [(ngModel)]="item.iframe"></textarea> + </div> + </div> + + <!-- MAP --> + <div *ngIf="selectedItemType=='MAP'"> + <div> + <label class="label mr-2">{{'dashboardComponent.label_title'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_map_title'|translate}}" + [(ngModel)]="item.map_title"></textarea> + </div> + <div> + <label class="label mr-2">{{'dashboardComponent.label_geoJsonurl'|translate}}</label> + <textarea nbInput fullWidth + placeholder="{{'dashboardComponent.fill_map'|translate}} {{'dashboardComponent.example_map_url'|translate}}" + rows="8" [(ngModel)]="item.map"></textarea> + </div> + </div> + + <div *ngIf="selectedItemType=='WEATHER'"> + <div> + <label class="label mr-2">{{'dashboardComponent.label_title'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_weather_title'|translate}}" + [(ngModel)]="item.weather_title"></textarea> + </div> + <div> + <label class="label mr-2">{{'dashboardComponent.label_subtitle'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_weather_subtitle'|translate}}" + [(ngModel)]="item.weather_subtitle"></textarea> + </div> + </div> + + <div *ngIf="selectedItemType=='STATISTICS'"> + <div> + <label class="label mr-2">{{'dashboardComponent.label_title'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_statistics_title'|translate}}" + [(ngModel)]="item.statistics_title"></textarea> + </div> + <div> + <label class="label mr-2">{{'dashboardComponent.label_subtitle'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_statistics_subtitle'|translate}}" + [(ngModel)]="item.statistics_subtitle"></textarea> + </div> + <div> + <label class="label mr-2">{{'dashboardComponent.label_value'|translate}}</label> + <textarea nbInput fullWidth placeholder="{{'dashboardComponent.fill_statistics_value'|translate}}" + [(ngModel)]="item.statistics_value"></textarea> + </div> + <div> + <label class="label mr-2">{{'dashboardComponent.label_icon'|translate}}</label> + <nb-icon [icon]="item.statistics_icon">{{item.statistics_icon}}</nb-icon> + <nb-select fullWidth size="small" placeholder="{{'dashboardComponent.fill_statistics_icon'|translate}}" + [(ngModel)]="item.statistics_icon"> + <nb-option *ngFor="let icon of statisticsIcons" [value]='icon'> {{ icon }} </nb-option> + </nb-select> + </div> + </div> + </nb-card-body> + + <nb-card-footer> + <div class="row my-2"> + <div class="col-sm text-center"> + <button nbButton status="danger" size="small" + (click)="onCancelClick()">{{'general.cancel'|translate}}</button> + </div> + <div class="col-sm text-center"> + <button nbButton status="info" size="small" (click)="onSaveClick(item, selectedItemType)" + cdkFocusInitial>{{'general.save'|translate}}</button> + </div> + </div> + </nb-card-footer> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.scss b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..434730d041210cb7496b4b668a5fe00d3ac749f7 --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.scss @@ -0,0 +1,29 @@ +nb-card { + min-width: 800px; + min-height: 500px; + max-width: 95vw; + max-height: 95vh; +} + +.item-select { + display: inline-block; +} + +.chart-select { + display: inline-block; + margin-left: 5px; +} + +.image-preview { + margin: 5px; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; +} + +/*.image-picker { + display: flex; + justify-content: center; +}*/ diff --git a/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.spec.ts b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc6602f77dfe1f6218efbd8dfb96146959b71fde --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DashboardComponentDialogComponent } from './dashboard-component-dialog.component'; + +describe('DashboardComponentDialogComponent', () => { + let component: DashboardComponentDialogComponent; + let fixture: ComponentFixture<DashboardComponentDialogComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DashboardComponentDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardComponentDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.ts b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..7a574662c96a4a07a2c459117844ffbe1c84b520 --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-component-dialog/dashboard-component-dialog.component.ts @@ -0,0 +1,247 @@ +import { Component, OnInit, Input } from "@angular/core"; +import { AngularEditorConfig } from "@kolkov/angular-editor"; +import { NbDialogRef, NbToastrService } from "@nebular/theme"; +import { TranslateService } from "@ngx-translate/core"; +import { GridsterItem } from "angular-gridster2"; +import { ImageCroppedEvent } from "ngx-image-cropper"; +import { Datalet } from "../../data-catalogue/model/datalet"; +import { DataCatalogueAdminApiService } from "../../data-catalogue/services/data-catalogue-admin-api.service"; + +export interface DialogData { + componentType: string; + name: string; +} + +@Component({ + selector: 'ngx-dashboard-component-dialog', + templateUrl: './dashboard-component-dialog.component.html', + styleUrls: ['./dashboard-component-dialog.component.scss'] +}) +export class DashboardComponentDialogComponent implements OnInit { + @Input() selectedItemType: string; + @Input() items: GridsterItem[]; + @Input() item: GridsterItem; + + itemIndex; + datalets: Array<Datalet>; + selectedItem: Datalet; + dataletObject: Datalet = {}; + imageChangedEvent: any = ''; + imageFile: any = ''; + croppedImage: any = ''; + imageBackup; + imageUrlBackup; + selectedOption: string; + maintainAspectRatioVariable: boolean = false; + aspectRatioVariable: string = ""; + statisticsIcons: Array<string>; + + editorConfig: AngularEditorConfig = { + editable: true, + spellcheck: true, + height: 'auto', + minHeight: '0', + maxHeight: 'auto', + width: 'auto', + minWidth: '0', + translate: 'yes', + enableToolbar: true, + showToolbar: true, + placeholder: 'Enter text here...', + defaultParagraphSeparator: '', + defaultFontName: '', + defaultFontSize: '', + fonts: [ + { class: 'arial', name: 'Arial' }, + { class: 'times-new-roman', name: 'Times New Roman' }, + { class: 'calibri', name: 'Calibri' }, + { class: 'comic-sans-ms', name: 'Comic Sans MS' } + ], + customClasses: [ + { + name: 'quote', + class: 'quote', + }, + { + name: 'redText', + class: 'redText' + }, + { + name: 'titleText', + class: 'titleText', + tag: 'h1', + }, + ], + uploadUrl: 'v1/image', + // upload: (file: File) => { ... } + uploadWithCredentials: false, + sanitize: false, + toolbarPosition: 'top', + toolbarHiddenButtons: [ + [ + 'fontName', + // 'undo', + // 'redo', + // 'bold', + // 'italic', + // 'underline', + // 'strikeThrough', + // 'subscript', + // 'superscript', + // 'justifyLeft', + // 'justifyCenter', + // 'justifyRight', + // 'justifyFull', + // 'indent', + // 'outdent', + // 'insertUnorderedList', + // 'insertOrderedList', + // 'heading', + ], + [ + 'customClasses', + 'insertImage', + 'insertVideo', + 'removeFormat', + 'toggleEditorMode', + // 'fontSize', + // 'textColor', + // 'backgroundColor', + // 'link', + // 'unlink', + // 'insertHorizontalRule', + ] + ] + }; + + constructor( + public dialogRef: NbDialogRef<DashboardComponentDialogComponent>, + private dataCatalogueAdminApiservice: DataCatalogueAdminApiService, + private toastrService: NbToastrService, + private translateService: TranslateService + ) { + } + + ngOnInit() { + this.itemIndex = this.items.indexOf(this.item); + this.selectedItemType = this.item.type; + this.statisticsIcons = this.getEvaIcon(); + + this.dataCatalogueAdminApiservice.getAllDatalets().subscribe( + (response: Array<Datalet>) => { + if (response !== undefined && response !== null) { + try{ + this.datalets = response; + if (this.selectedItemType == 'CHART'){ + this.dataletObject = this.datalets.find(x => x.datalet_html === this.item.chart); + // console.log(this.dataletObject); + } + } catch(e) { + console.log("# Error in Dashboard page content deserialization due to " + e); + this.toastrService.danger(this.translateService.instant('dashboardComponent.catch_error')); + } + } + }, + (error: any) => { + // console.log("# Data Catalogue Error => "+ error); + this.toastrService.danger(error.message, this.translateService.instant('general.datacatalogue_error')) + } + ); + + //Radio Button for Image upload + this.selectedOption = (this.item.url == true) ? 'upload-url' : 'upload-file'; + } + + onCancelClick(): void { + if (this.imageBackup !== undefined) { + this.item.image = this.imageBackup; + } + if (this.imageUrlBackup !== undefined) { + this.item.imageUrl = this.imageUrlBackup; + } + this.dialogRef.close(); + } + + onSaveClick( item, selectedItemType ): void { + item.type = selectedItemType; + + if (selectedItemType == 'CHART') { + // console.log(this.dataletObject); + item.chart = this.dataletObject.datalet_html; + item.chartTitle = this.dataletObject.title; + + } else if (selectedItemType == 'IMAGE') { + if (this.croppedImage !== undefined && this.croppedImage !== null && (this.croppedImage.length > 3 || this.croppedImage.length == 0)){ + if (this.croppedImage.length > 3){ + item.image = this.croppedImage; + } + } + if (this.selectedOption=='upload-url'){ + item.url = true; + item.image = null; + } else{ + item.url = false; + item.imageUrl = null; + } + + } else if (selectedItemType == 'MAP'){ + // console.log("# Here saved map: ", item); + } + + this.items[this.itemIndex] = item; + this.dialogRef.close(this.items); + } + + // Move into a property file + getEvaIcon() { + return [ + 'award-outline', + 'at-outline', + 'car-outline', + 'checkmark-circle-2-outline', + 'cloud-download-outline', + 'color-palette-outline', + 'email-outline', + 'external-link-outline', + 'facebook-outline', + 'flag-outline', + 'globe-outline', + 'github-outline', + 'google-outline', + 'heart-outline', + 'linkedin-outline', + 'monitor-outline', + 'message-square-outline', + 'percent-outline', + 'pie-chart-outline', + 'person-outline', + 'pin-outline', + 'sun-outline', + 'unlock-outline', + ]; + } + + fileChangeEvent(event: any): void { + this.imageChangedEvent = event; + } + + imageCropped(event: ImageCroppedEvent) { + this.croppedImage = event.base64; + } + + imageLoaded() { + this.imageBackup = this.item.image; + this.imageUrlBackup = this.item.imageUrl; + this.item.image = undefined; + this.item.imageUrl = undefined; + } + + getHiddenUrlBlock(){ + return (this.selectedOption!=='upload-url'); + } + + getHiddenFileUploadBlock(){ + return (this.selectedOption!=='upload-file'); + } + +} diff --git a/src/app/pages/dashboard-management/dashboard-management-routing.module.ts b/src/app/pages/dashboard-management/dashboard-management-routing.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..2c96c90b7b43a266cb5c51b4249c9adb031351ea --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-management-routing.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { DashboardCloneWizardComponent } from './dashboard-clone-wizard/dashboard-clone-wizard.component'; +import { DashboardPageComponent } from './dashboard-page/dashboard-page.component'; +import { ManageDashboardPageContentComponent } from './manage-dashboard-page-content/manage-dashboard-page-content.component'; +import { ManageDashboardPageComponent } from './manage-dashboard-page/manage-dashboard-page.component'; +import { ManageMenuBlocksComponent } from './manage-menu-blocks/manage-menu-blocks.component'; +import { MenuBlocksPageComponent } from './menu-blocks-page/menu-blocks-page.component'; + +const routes: Routes = [ + { + path: 'manage-dashboard-pages', + component: ManageDashboardPageComponent + }, + { + path: 'add-dashboard-page', + component: DashboardPageComponent + }, + { + path: 'edit-dashboard-page/:id', + component: DashboardPageComponent + }, + { + path: 'edit-dashboard-page-content/:id', + component: ManageDashboardPageContentComponent + }, + { + path: 'manage-menu-blocks', + component: ManageMenuBlocksComponent + }, + { + path: 'add-menu-block-page', + component: MenuBlocksPageComponent + }, + { + path: 'edit-menu-block-page/:id', + component: MenuBlocksPageComponent + }, + { + path: 'dashboard-clone-wizard', + component: DashboardCloneWizardComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class DashboardManagementRoutingModule { } + diff --git a/src/app/pages/dashboard-management/dashboard-management.module.ts b/src/app/pages/dashboard-management/dashboard-management.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b11c7148c8db72d65691579c7b4bb706264b60e --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-management.module.ts @@ -0,0 +1,98 @@ +import { NgModule } from '@angular/core'; +import { ManageDashboardPageComponent } from './manage-dashboard-page/manage-dashboard-page.component'; +import { GoogleMapsModule } from '@angular/google-maps'; +import { LeafletMarkerClusterModule } from '@asymmetrik/ngx-leaflet-markercluster'; +import { NgxEchartsModule } from 'ngx-echarts'; +import { NbCardModule, NbRadioModule, NbSpinnerModule, NbDatepickerModule, NbInputModule, NbButtonModule, NbSelectModule, NbTimepickerModule, NbIconModule, NbTooltipModule, NbTabsetModule, NbListModule, NbFormFieldModule, NbActionsModule, NbAutocompleteModule, NbDialogModule, NbAccordionModule, NbOptionModule, NbLayoutModule, NbCheckboxModule, NbToggleModule, NbStepperModule, NbUserModule} from '@nebular/theme'; +import { ThemeModule } from '../../@theme/theme.module'; +import { DashboardManagementRoutingModule } from './dashboard-management-routing.module'; +import { MatCardModule } from '@angular/material/card'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgApexchartsModule } from "ng-apexcharts"; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { CommonModule } from '@angular/common'; +import { DashboardPageComponent } from './dashboard-page/dashboard-page.component'; +import { SharedModule } from '../shared/shared.module'; +import { AngularEditorModule } from "@kolkov/angular-editor"; +import { GridsterModule } from 'angular-gridster2'; +import { MatIconModule } from '@angular/material/icon'; +import { ManageDashboardPageContentComponent } from './manage-dashboard-page-content/manage-dashboard-page-content.component'; +import { DashboardComponentDialogComponent } from './dashboard-component-dialog/dashboard-component-dialog.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { TargetFilterComponent } from './target-filter/target-filter.component'; +import { MenuFilterComponent } from './menu-filter/menu-filter.component'; +import { ManageMenuBlocksComponent } from './manage-menu-blocks/manage-menu-blocks.component'; +import { WeatherComponent } from './weather/weather.component'; +import { StatisticsComponent } from './statistics/statistics.component'; +import { MenuBlocksPageComponent } from './menu-blocks-page/menu-blocks-page.component'; +import { ImageCropperModule } from 'ngx-image-cropper'; +import { LeafletModule } from '@asymmetrik/ngx-leaflet'; +import { DashboardCloneWizardComponent } from './dashboard-clone-wizard/dashboard-clone-wizard.component'; +import { ChartViewComponent } from './chart-view/chart-view.component'; +import {MatDialogModule} from '@angular/material/dialog'; + + +@NgModule({ + imports: [ + CommonModule, + ThemeModule, + GoogleMapsModule, + NbDialogModule.forChild(), + DashboardManagementRoutingModule, + NgxEchartsModule, + NbCardModule, + Ng2SmartTableModule, + MatCardModule, + NbRadioModule, + NbTabsetModule, + FormsModule, + NbSpinnerModule, + ReactiveFormsModule, + NbListModule, + NbFormFieldModule, + NbActionsModule, + NbAutocompleteModule, + NbInputModule, + NbDatepickerModule, + NbButtonModule, + NbSelectModule, + NbTimepickerModule, + LeafletMarkerClusterModule, + NbIconModule, + NbTooltipModule, + SharedModule, + NbAccordionModule, + + NgApexchartsModule, + NbOptionModule, + NbLayoutModule, + NbCheckboxModule, + AngularEditorModule, + GridsterModule, + MatIconModule, + TranslateModule, + ImageCropperModule, + NbToggleModule, + NbStepperModule, + NbUserModule, + LeafletModule.forRoot(), + MatDialogModule, + + ], + exports: [], + declarations: [ + ManageDashboardPageComponent, + DashboardPageComponent, + ManageDashboardPageContentComponent, + DashboardComponentDialogComponent, + TargetFilterComponent, + MenuFilterComponent, + ManageMenuBlocksComponent, + MenuBlocksPageComponent, + WeatherComponent, + StatisticsComponent, + DashboardCloneWizardComponent, + ChartViewComponent, + ], +}) +export class DashboardManagementModule { } diff --git a/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.html b/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.html new file mode 100644 index 0000000000000000000000000000000000000000..a876c8e90efdc4c10d81fcdc601527553beb14e2 --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.html @@ -0,0 +1,83 @@ +<nb-card> + <nb-card-header> + <h4>{{'general.manage'|translate}} {{'general.dashboard'|translate}}</h4> + </nb-card-header> + + <nb-card-body [formGroup]="page" > + + <div class="row"> + <nb-form-field class="col-6 mb-1 mt-1"> + <div class="d-flex flex-column"> + <label for="name" class="label mr-2"> + {{'dashboardPage.name'|translate}} * + </label> + <input type="text" nbInput fullWidth fieldSize="small" placeholder="{{'dashboardPage.name'|translate}}" + name="name" formControlName="name" nbTooltip="{{'general.mandatory_field'|translate}}"> + </div> + </nb-form-field> + + <nb-form-field class="col-6 mb-1 mt-1"> + <div class="d-flex flex-column"> + <label for="shared" class="label mr-2"> + <nb-icon icon="info-outline" nbTooltip="{{'dashboardPage.shared_info'|translate}}" ></nb-icon> + {{'dashboardPage.shared'|translate}} {{disabledTarget?'':'*'}} + </label> + + <nb-select fullWidth formControlName="shared" size="small" name="shared" + placeholder="{{'dashboardPage.shared'|translate}}" + [nbTooltip]="getMessageDisabledTarget()"> + <nb-option *ngFor="let item of sharedDomain" [value]="item"> + {{item}} + </nb-option> + </nb-select> + + <!-- toggle --> + <!-- + <nb-card-body class="example-items-col"> + <nb-toggle [nbTooltip]="getMessageDisabledTarget()" formControlName="shared">{{'dashboardPage.shared'|translate}}</nb-toggle> + </nb-card-body> + --> + + </div> + </nb-form-field> + + + <nb-form-field class="col-12 mb-1 mt-1"> + <div class="d-flex flex-column"> + <label for="description" class="label mr-2"> + {{'dashboardPage.description'|translate}} + </label> + <input type="text" nbInput fullWidth fieldSize="small" placeholder="{{'dashboardPage.description'|translate}}" + description="name" formControlName="description"> + </div> + </nb-form-field> + </div> + + <div class="row"> + <nb-form-field class="col-12 mb-1 mt-1"> + <div class="d-flex flex-column"> + <label for="note" class="label mr-2"> + {{'dashboardPage.note'|translate}} + </label> + <textarea nbInput fullWidth rows="3" type="text" fieldSize="small" placeholder="{{'dashboardPage.note'|translate}}" + name="note" formControlName="note"></textarea> + </div> + </nb-form-field> + </div> + </nb-card-body> + + <nb-card-footer class="d-flex justify-content-between"> + <button nbButton status="primary" (click)="backClicked()" size="small"> + <nb-icon icon="backspace-outline"></nb-icon> {{'general.back'|translate}} + </button> + <button type="submit" nbButton status="primary" (click)="storePage()" size="small" [disabled]="page.invalid"> + <nb-icon icon="save"></nb-icon>{{'general.save'|translate}} + </button> + </nb-card-footer> + +</nb-card> + + +<ngx-menu-filter [pageId]="pageId" [pageName]="pageName" (sendFilter)="updateSharedPersonalTypology($event)" ></ngx-menu-filter> + +<ngx-target-filter [pageId]="pageId" [disabledTarget]="disabledTarget"></ngx-target-filter> diff --git a/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.scss b/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.spec.ts b/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..865185a9c69327b258eba2bbf0c7075c35697448 --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardPageComponent } from './dashboard-page.component'; + +describe('DashboardPageComponent', () => { + let component: DashboardPageComponent; + let fixture: ComponentFixture<DashboardPageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DashboardPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.ts b/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a362e7dac70ed9b7138af77733a31deed095b12c --- /dev/null +++ b/src/app/pages/dashboard-management/dashboard-page/dashboard-page.component.ts @@ -0,0 +1,131 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NbToastrService } from '@nebular/theme'; +import { TranslateService } from '@ngx-translate/core'; +import { DashboardPage } from '../../../model/dashboard-page.model'; +import { DashboardPageCrudService } from '../services/dashboard-page-crud.service'; +import { Location } from '@angular/common'; +import { FormManagerService } from '../services/form-manager.service'; +import { MenuType } from '../../../model/enumeration/menu-type.model'; +import { MenuItemCrudService } from '../services/menu-item-crud.service'; + +@Component({ + selector: 'ngx-dashboard-page', + templateUrl: './dashboard-page.component.html', + styleUrls: ['./dashboard-page.component.scss'] +}) +export class DashboardPageComponent implements OnInit { + @Input() page: FormGroup = new FormGroup({}); + sharedDomain = [true, false]; + + pageId: string; //For Targets + pageName: string; //For MenuItem + disabledTarget: boolean; //For Targets + + constructor( + private formService: FormManagerService, + private service: DashboardPageCrudService, + private menuItemService: MenuItemCrudService, + private router: Router, + private route: ActivatedRoute, + private toastrService: NbToastrService, + private translateService: TranslateService, + private _location: Location + ) { } + + ngOnInit(): void { + this.page = this.formService.pageFormCreator(new DashboardPage()); + + this.route.paramMap.subscribe(params => { + this.pageId = params.get('id'); + if (this.pageId) { + this.service.getPageById(this.pageId).subscribe( + (event: DashboardPage) => { this.editPage(event); }, + error => this.showError(error) + ); + } + }); + + } + + editPage(page: DashboardPage) { + this.page = this.formService.pageFormCreator(page); + this.pageName = page.name; + + //this.disabledTarget = page.type!=undefined ? page.type != MenuType.SHARED : true; + this.menuItemService.getByPageId(this.pageId).subscribe( + (data) => { this.disabledTarget = data!=null && data.length>0 ? data[0].type != MenuType.SHARED : true; }, + error => (this.showError(error)) + ); + + } + + storePage(): void { + if (this.page.value.id == null) { + console.log('## CREATE DASHBOARD WITH ID:' + this.page.value.id); + let tmp = <DashboardPage>this.page.getRawValue(); + this.pageName = tmp.name; + //this.disabledTarget = tmp.type!=undefined ? tmp.type != MenuType.SHARED : true; + this.service.createPage(tmp).subscribe( + (data: DashboardPage) => { + // console.log("# DASHBOARD CREATED, DATA => " + data); + this.router.navigate(['/pages/dashboard-management/manage-dashboard-pages']); + this.showSuccess(); + }, + error => (this.showError(error)) + ); + + } + else { + console.log('# MODIFY DASHBOARD WITH ID:' + this.page.value.id); + let tmp = <DashboardPage>this.page.getRawValue(); + this.pageName = tmp.name; + //this.disabledTarget = tmp.type!=undefined ? tmp.type != MenuType.SHARED : true; + this.service.updatePageById(tmp).subscribe( + (data: DashboardPage) => { + this.editPage(data); + this.showSuccess(); + }, + error => (this.showError(error)) + ); + + } + } + + showSuccess() { + this.page.value.id == null + ? this.toastrService.success(this.translateService.instant('dashboardPage.created'), this.translateService.instant('Success')) + : this.toastrService.success(this.translateService.instant('dashboardPage.updated'), this.translateService.instant('Success')); + } + + showError(error) { + this.toastrService.danger(error.error.message, this.translateService.instant('general.error')) + } + + backClicked() { + this._location.back(); + } + + updateSharedPersonalTypology($event) { + this.menuItemService.getByPageId(this.pageId).subscribe( + (data) => { + this.disabledTarget = data!=null && data.length>0 ? data[0].type != MenuType.SHARED : true; + // console.log("# Broadcast event updateSharedPersonalTypology disabledTarget => " + this.disabledTarget); + if (this.disabledTarget){ + this.page.get("shared").disable(); + }else{ + this.page.get("shared").enable(); + } + }, + error => (this.showError(error)) + ); + } + + getMessageDisabledTarget(){ + return this.disabledTarget + ? this.translateService.instant('dashboardPage.not_mandatory_field') + : this.translateService.instant('general.mandatory_field'); + } + +} diff --git a/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.html b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.html new file mode 100644 index 0000000000000000000000000000000000000000..05b9e89a122b33d989b9eae7e11a89cd8f9a478b --- /dev/null +++ b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.html @@ -0,0 +1,142 @@ +<div class="d-flex justify-content-between" *ngIf="isEditable==true"> + <nb-card class="col-md-3 col-sm-12"> + <nb-card-header> + {{'dashboardPage.name'|translate}} + </nb-card-header> + <nb-card-body> + {{dashboardPage.name}} + </nb-card-body> + </nb-card> + <nb-card class="col-md-2 col-sm-12"> + <nb-card-header> + {{'dashboardPage.margin_label'|translate}} + </nb-card-header> + <nb-card-body> + <div class="row "> + <div class="col-sm justify-content-between"> + {{'dashboardPage.margin'|translate}} + <input nbInput fieldSize="small" [(ngModel)]="options.margin" min="0" + nbTooltip="{{'dashboardPage.margin_description'|translate}}" max="30" step="1" type="number" + (ngModelChange)="changedOptions()"> + </div> + </div> + <div class="row"> + <div class="col-sm justify-content-between"> + <nb-checkbox [(ngModel)]="options.outerMargin" (ngModelChange)="changedOptions()" + nbTooltip="{{'dashboardPage.outer_margin_description'|translate}}"> + {{'dashboardPage.outer_margin'|translate}} + </nb-checkbox> + </div> + </div> + </nb-card-body> + </nb-card> + <nb-card class="col-md-4 col-sm-12"> + <nb-card-header> + {{'dashboardPage.checkbox_label'|translate}} + </nb-card-header> + <nb-card-body> + <div class="row"> + <div class="col-sm justify-content-between"> + <nb-checkbox [(ngModel)]="options.draggable.enabled" (ngModelChange)="changedOptions()" + nbTooltip="{{'dashboardPage.drag_items'|translate}}"> + {{'dashboardPage.drag_items'|translate}} + </nb-checkbox> + </div> + <div class="col-sm justify-content-between"> + <nb-checkbox [(ngModel)]="options.disablePushOnDrag" (ngModelChange)="changedOptions()" + nbTooltip="{{'dashboardPage.disable_push_on_drag_description'|translate}}"> + {{'dashboardPage.disable_push_on_drag'|translate}} + </nb-checkbox> + </div> + </div> + <div class="row"> + <div class="col-sm justify-content-between"> + <nb-checkbox [(ngModel)]="options.pushItems" (ngModelChange)="changedOptions()" + nbTooltip="{{'dashboardPage.push_items_description'|translate}}"> + {{'dashboardPage.push_items'|translate}} + </nb-checkbox> + </div> + <div class="col-sm justify-content-between"> + <nb-checkbox [(ngModel)]="options.swap" (ngModelChange)="changedOptions()" + nbTooltip="{{'dashboardPage.swap_items_description'|translate}}"> + {{'dashboardPage.swap_items'|translate}} + </nb-checkbox> + </div> + </div> + </nb-card-body> + </nb-card> + <div> + <button class="add-button" nbButton size="medium" hero status="primary" nbTooltip="{{'general.back'|translate}}" + (click)="backClicked()" class="add-button cols-2"> + <nb-icon icon="arrow-back-outline"></nb-icon> + </button> + <button class="save-button" nbButton size="medium" hero status="primary" + nbTooltip="{{'general.save'|translate}} {{'general.component'|translate}}" (click)="saveDashboard()" + class="add-button cols-2"> + <nb-icon icon="save-outline"></nb-icon> + </button> + <button class="add-button" nbButton size="medium" hero status="primary" + nbTooltip="{{'general.add'|translate}} {{'general.component'|translate}}" (click)="addItem()" + class="add-button cols-2"> + <nb-icon icon="plus-square-outline"></nb-icon> + </button> + </div> +</div> + + +<gridster [options]="options"> + <gridster-item [item]="i" *ngFor="let i of items"> + <div *ngIf="i.type=='IMAGE'" class="image-container"> + <img [src]="buildImage(i)" [alt]="i.image"> + </div> + + <div *ngIf="i.type=='TEXT'" class="text-container" [innerHTML]="i.text | safeHtml"> + </div> + + <div *ngIf="i.type=='WEATHER'"> + <ngx-weather [weather_title]="i.weather_title" [weather_subtitle]="i.weather_subtitle"> + </ngx-weather> + </div> + + <!-- MAP --> + <div class="map" *ngIf="i.type=='MAP'"> + <h2>{{i.map_title}}</h2> + <div #map class="map" leaflet [leafletOptions]="mapOptions" (leafletMapReady)="onMapReady($event, i.map)"> + </div> + </div> + + <div *ngIf="i.type=='STATISTICS'"> + <ngx-statistics [statistics_title]="i.statistics_title" [statistics_subtitle]="i.statistics_subtitle" + [statistics_value]="i.statistics_value" [statistics_icon]="i.statistics_icon"> + </ngx-statistics> + </div> + + + <!-- + <div *ngIf="i.type=='CHART'" [innerHTML]="i.chart | safeHtml"></div> + --> + <div *ngIf="i.type=='CHART'"> + <ngx-chart-view [chartContent]="i.chart"></ngx-chart-view> + </div> + + + <div *ngIf="i.type=='IFRAME'" [innerHTML]="i.iframe | safeHtml"> + </div> + + <div class="itemButtons" *ngIf="isEditable==true"> + <button nbButton class="remove-button" (mousedown)="$event.stopPropagation()" + nbTooltip="{{'general.delete'|translate}} {{'general.component'|translate}}" (click)="removeItem(i)"> + <nb-icon icon="trash-2-outline"></nb-icon> + </button> + <button nbButton class="component-button" (mousedown)="$event.stopPropagation()" + nbTooltip="{{'general.edit'|translate}} {{'general.component'|translate}}" (click)="editItem(i, items)"> + <nb-icon icon="edit-2-outline"></nb-icon> + </button> + <button nbButton class="component-button" + nbTooltip="{{'general.drag'|translate}} {{'general.component'|translate}}"> + <nb-icon icon="move-outline"></nb-icon> + </button> + </div> + + </gridster-item> +</gridster> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.scss b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..f7c7db0747bafd64c308b5fb98059b213fcdc0a8 --- /dev/null +++ b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.scss @@ -0,0 +1,43 @@ +gridster { + background-color: #dddddd; +} + +.addItemButton { + margin: 20px !important; +} + +.itemButtons { + position: absolute; + bottom: 5px; + right: 5px; +} + +.component-button { + margin: 3px !important; +} + +.image-container { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; +} + +.text-container { + margin: 5px; +} + +img { + max-width: 100%; + max-height: 100%; +} +.add-button { + margin: 10px; +} + +.map { + height: 24em; + width: 100%; + margin-left: 0.45em; +} diff --git a/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.spec.ts b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd18b5be12b0086438dc7aa6f68a22008b452254 --- /dev/null +++ b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ManageDashboardPageContentComponent } from './manage-dashboard-page-content.component'; + +describe('ManageDashboardPageContentComponent', () => { + let component: ManageDashboardPageContentComponent; + let fixture: ComponentFixture<ManageDashboardPageContentComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ManageDashboardPageContentComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ManageDashboardPageContentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.ts b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3ddfa4049a414c154261cc4b6f82dc175c2c978 --- /dev/null +++ b/src/app/pages/dashboard-management/manage-dashboard-page-content/manage-dashboard-page-content.component.ts @@ -0,0 +1,280 @@ +import { Component, Inject, OnInit, Optional } from "@angular/core"; +import { ActivatedRoute } from "@angular/router"; +import { NbDialogService, NbToastrService } from "@nebular/theme"; +import { Draggable, GridsterConfig, GridsterItem, PushDirections, Resizable } from "angular-gridster2"; +import { DashboardPage, IDashboardPage } from "../../../model/dashboard-page.model"; +import { DashboardComponentDialogComponent } from "../dashboard-component-dialog/dashboard-component-dialog.component"; +import { DashboardPageCrudService } from "../services/dashboard-page-crud.service"; +import { TranslateService } from "@ngx-translate/core"; +import { DialogComponent } from "../../shared/dialog/dialog.component"; +import { Location } from '@angular/common'; +import { DomSanitizer } from "@angular/platform-browser"; +import * as L from 'leaflet'; +import { MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { ConfigService } from '@ngx-config/core'; + +interface Safe extends GridsterConfig { + draggable: Draggable; + resizable: Resizable; + pushDirections: PushDirections; +} + +interface DashboardModalData { + parsedId: number; + parsedEditMode: boolean; +} + +@Component({ + selector: 'ngx-manage-dashboard-page-content', + templateUrl: './manage-dashboard-page-content.component.html', + styleUrls: ['./manage-dashboard-page-content.component.scss'] +}) +export class ManageDashboardPageContentComponent implements OnInit { + public options: GridsterConfig; + public items:Array<GridsterItem>; + public itemsClone:Array<GridsterItem>; + public dashboardPage: DashboardPage = new DashboardPage(); + public id: string; + public componentType: string; + public isEditable = true; //Change options to disable drag and drop + + //WorkAroud for BUG on ng-gridster+iframe + private ENABLE_DELAY:boolean; + private DELAY:number; + + constructor(public dialogService: NbDialogService, + public dashboardPageService: DashboardPageCrudService, + private route: ActivatedRoute, + private _sanitizer: DomSanitizer, + private toastrService: NbToastrService, + private translateService: TranslateService, + private _location: Location, + private configService: ConfigService, + @Optional() @Inject(MAT_DIALOG_DATA) public dashboardModalData: DashboardModalData + ) { + this.ENABLE_DELAY = this.configService.getSettings("dashboard-controller.enable_delay"); + this.DELAY = this.configService.getSettings("dashboard-controller.delay"); + } + + ngOnInit(){ + this.options = this.getDefaultOption(); + this.items = []; + this.itemsClone = []; + + this.route.paramMap.subscribe( paramMap => { + this.items = []; + this.id = paramMap.get('id'); + + if (this.dashboardModalData != null && this.dashboardModalData != undefined){ + this.id = this.dashboardModalData.parsedId as unknown as string; + this.isEditable = this.dashboardModalData.parsedEditMode; + this.options = this.getDefaultOption(); + } + //console.log("Retrieved Content for Page with Id: " + this.id); + + this.dashboardPageService.getPageById(this.id).subscribe((data) => { + this.dashboardPage=data; + if (data.content !== "" && data.content !== undefined && data.content !== null){ + + if (this.ENABLE_DELAY == true) { + this.fillItemsWithDelay(data); + } else { + this.items = JSON.parse(data.content); + } + + } + }); + + }); + + this.route.queryParams.subscribe(params => { + if (params['isEditable'] != undefined && params['isEditable'] != null){ + this.isEditable = params['isEditable'] == "true" ? true : false; + this.options = this.getDefaultOption(); + } + }); + + } + + private fillItemsWithDelay (data: IDashboardPage) { + try { + let itemsTemp = JSON.parse(data.content); + itemsTemp.forEach((element, i) => { + if (element['chart'] != undefined && element['chart'] != null) { + setTimeout(() => { + // console.log(element); + this.items.push(element); + }, i * this.DELAY); + } else { + this.items.push(element); + } + }); + } catch (e) { + console.log("## Error in Dashboard page content during visualization due to " + e); + this.toastrService.danger(this.translateService.instant('dashboardPage.visualization_catch_error')); + } + } + + + removeItem(item) { + this.dialogService.open(DialogComponent, + { context: { title: this.translateService.instant('general.delete') + " " + this.translateService.instant('general.component') , + message: this.translateService.instant('general.deleteConfirm')} }) + .onClose.subscribe( + (res) => { + this.items.splice(this.items.indexOf(item), 1); + }, err => { + // console.log(err); + this.toastrService.danger(this.translateService.instant('dashboardPage.operation_failed'), this.translateService.instant('general.error')); + } + ); + } + + addItem() { + let item: GridsterItem = { cols: 2, rows: 2, y: 0, x: 0 }; //default added item + this.items.push(item); + } + + editItem(item: GridsterItem, items: GridsterItem[]) { + const dialogRef = this.dialogService + .open(DashboardComponentDialogComponent, { + context: { + item, + items, + }, + }) + .onClose.subscribe( + (item) => { + item && (this.items = item) && this.options.api.optionsChanged() // optionsChanged() refreshes and applies changes + } + ); + } + + changedOptions(): void { + if (this.options.api && this.options.api.optionsChanged) { + this.options.api.optionsChanged(); + } + } + + saveDashboard(){ + this.dashboardPage.content = JSON.stringify(this.items); + this.dashboardPageService.updatePageById(this.dashboardPage) + .subscribe((res) => { + // console.log('## Saved Dashboard with id:' + this.dashboardPage.id); + this.toastrService.success(this.translateService.instant('dashboardPage.updated'), this.translateService.instant('general.success')); + },err => { + // console.log(err); + this.toastrService.danger(this.translateService.instant('dashboardPage.operation_failed'), this.translateService.instant('general.error')); + }); + } + + + /** Ask confirm before to go back */ + backClicked() { + this.dialogService.open(DialogComponent, + { context: { title: this.translateService.instant('dashboardComponent.title') , + message: this.translateService.instant('dashboardPage.back_modal') } }) + .onClose.subscribe(res => { + if (res) { + this._location.back(); + } else return; + }); + } + + /** + * Return modified string trusted (in case of base 64 string) or untouched image string in case or URL + * @param item object with Image URL or image string in BASE 64 + * @returns trasted image url or string + */ + buildImage(item: any) { + if (item.url == false){ + if (item.image != undefined && item.image.length>10 && "data:image/jpeg;base64" == item.image.substr(0,10) ) { + return this._sanitizer.bypassSecurityTrustResourceUrl(item.image); + } else{ + return item.image; + } + }else{ + if (item.imageUrl != undefined && item.imageUrl.length>10 && "data:image/jpeg;base64" == item.imageUrl.substr(0,10) ) { + return this._sanitizer.bypassSecurityTrustResourceUrl(item.imageUrl); + } else{ + return item.imageUrl; + } + } + } + + private getDefaultOption(): any{ + return { + pushItems: true, + minCols: 20, + minRows: 12, + fixedRowHeight: 120, + setGridSize: true, + mobileBreakpoint: 0, + gridType: "scrollVertical", + resizable: { + enabled: this.isEditable, + }, + draggable: { + enabled: this.isEditable, + }, + margin: 10, + outerMargin: true, + swap: this.isEditable + }; + } + + + /** MAP Section WIP **/ + mapOptions = { + layers: [ + L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' }), + ], + zoom: 9, + center: L.latLng({ lat: 52.372664783594274, lng: 4.8950958251953125 }), + }; + + + onMapReady(map: L.Map, mapUrl: string) { + console.log("### onMapReady, map ==> " + map); + this.initializeMap(mapUrl, map); + } + + public getMapLayer(title, l): L.GeoJSON { + let layer = L.geoJSON(l, + { + onEachFeature: (feature, layer) => { + let text = ""; + for (let p in feature.properties) { + text += `${p}: ${feature.properties[p]}\n`; + } + layer.bindPopup(text); + }, + style: function () { + return { color: '#248175', weight: 7, opacity: 0.8 } + } + }, + ).on('popupclose', ($event) => {}) + layer['_id'] = title; + return layer; + } + + + private initializeMap(mapUrl: string, map: L.Map) { + this.dashboardPageService.getGeoJsonMap(mapUrl).subscribe(res => { + let currentLayer = this.getMapLayer(mapUrl, <any>res); + console.log("# initializeMap, currentLayer => " + currentLayer); + setTimeout(() => { + map.invalidateSize(); + map.addLayer(currentLayer); + map.fitBounds(currentLayer.getBounds(), { + padding: [50, 50] + }); + }, 50); + L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' + }).addTo(map); + }); + } + + +} diff --git a/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.html b/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.html new file mode 100644 index 0000000000000000000000000000000000000000..f020b7940e6237edd72c3b369cdfa0e9627e6081 --- /dev/null +++ b/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.html @@ -0,0 +1,10 @@ +<nb-card> + <nb-card-header class="d-flex justify-content-between"> + <h4>{{'dashboardPage.title'|translate}}</h4> + <button nbButton size="small" status="info" routerLink="../add-dashboard-page" status="primary">{{'general.add'|translate}} {{'general.dashboard'|translate}}</button> + </nb-card-header> + + <nb-card-body> + <ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table> + </nb-card-body> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.scss b/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.spec.ts b/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..cef3dd1c07909813926f7e553c52a24d253ef76e --- /dev/null +++ b/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ManageDashboardPageComponent } from './manage-dashboard-page.component'; + +describe('ManageDashboardPageComponent', () => { + let component: ManageDashboardPageComponent; + let fixture: ComponentFixture<ManageDashboardPageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ManageDashboardPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ManageDashboardPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.ts b/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebe35ccdae7a02c690856cf04853c117318eb1af --- /dev/null +++ b/src/app/pages/dashboard-management/manage-dashboard-page/manage-dashboard-page.component.ts @@ -0,0 +1,164 @@ +import { Component, OnInit} from '@angular/core'; +import { Router } from '@angular/router'; +import { NbDialogService, NbToastrService } from '@nebular/theme'; +import { TranslateService } from '@ngx-translate/core'; +import { LocalDataSource } from 'ng2-smart-table'; +import { DashboardPage } from '../../../model/dashboard-page.model'; +import { MenuType } from '../../../model/enumeration/menu-type.model'; +import { DialogComponent } from '../../shared/dialog/dialog.component'; +import { TableActionsComponent } from '../../shared/table-actions/table-actions.component'; +import { DashboardPageCrudService } from '../services/dashboard-page-crud.service'; + +@Component({ + selector: 'ngx-manage-dashboard-page', + templateUrl: './manage-dashboard-page.component.html', + styleUrls: ['./manage-dashboard-page.component.scss'] +}) +export class ManageDashboardPageComponent implements OnInit { + source: LocalDataSource; + settings: any; + pages: Array<DashboardPage>=[]; + + constructor( private service: DashboardPageCrudService, + private router: Router, + private dialogService: NbDialogService, + private translateService: TranslateService, + private toastrService: NbToastrService) + { + this.initTableSettings(); + } + + ngOnInit(): void { + this.source = new LocalDataSource(); + this.fetchTableData(); + } + + fetchTableData() { + this.service.getEditablePages().subscribe( + (data) => { + this.pages=data; + this.source.load(data); + }, + error => (this.showError(error)) + ); + } + + showSuccess() { + this.toastrService.success(this.translateService.instant('dashboardPage.deleted'), this.translateService.instant('general.success')); + } + + showError(error) { + this.toastrService.danger(error.message, this.translateService.instant('general.error')) + } + + initTableSettings(): void { + let sharedFilter=[]; + let booleans = [true, false] + for(let myShared of booleans){ + sharedFilter.push({ value: myShared, title: myShared }) + } + + let menuTypeFilter=[]; + for(let mType of MenuType.values()){ + menuTypeFilter.push({ value: mType.enumVal, title: this.translateService.instant("MenuType."+mType.enumVal) }) + } + + + this.settings = { + mode: 'external', + columns: { + shared: { + title: this.translateService.instant('dashboardPage.shared'), + filter: { + type: 'list', + config: { + selectText: '', + list: sharedFilter + }, + } + }, + type: { + title: this.translateService.instant('menuItem.title'), + filter: { + type: 'list', + config: { + selectText: '', + list: menuTypeFilter + }, + } + }, + name: { + title: this.translateService.instant('dashboardPage.name') + }, + description: { + title: this.translateService.instant('dashboardPage.description') + }, + note: { + title: this.translateService.instant('dashboardPage.note') + }, + + /*content: { + title: this.translateService.instant('dashboardPage.content'), + filter: false + },*/ + + actions: { + title: this.translateService.instant('general.actions'), + filter: false, + sort: false, + type: 'custom', + valuePrepareFunction: (cell, row) => { + return [ + {action:'edit', enabled:true, status:"info", icon:"edit-2", tooltip:this.translateService.instant('general.edit')}, + {action:'delete', enabled:true, status:"danger", icon:"trash-2", tooltip:this.translateService.instant('general.delete')}, + {action:'editDashboard', enabled:true, status:"alert", icon:"edit", tooltip:this.translateService.instant('general.editContent')} + ]; + }, + renderComponent: TableActionsComponent, + onComponentInitFunction: (instance) => { + instance.onCustom.subscribe(value => { + this.onCustom(value,instance.rowData); + }); + }, + } + }, + + actions: { + add: false, + edit: false, + delete: false, + }, + + }; + } + + onCustom(event, data) { + + if (event == 'edit') { + this.router.navigate(['/pages/dashboard-management/edit-dashboard-page/' + data.id]) + }; + + if (event == 'editDashboard') { + this.router.navigate(['/pages/dashboard-management/edit-dashboard-page-content/' + data.id]) + } + + if (event == 'delete') { + + this.dialogService.open(DialogComponent, { context: { title: this.translateService.instant('general.delete') + " " + this.translateService.instant('general.dashboard') , message: this.translateService.instant('general.deleteConfirm') } }).onClose.subscribe(res => { + if (res) { + //this.service.deletePageById(data.id).subscribe(() => { + this.service.deleteDeepPageById(data.id).subscribe(() => { + this.fetchTableData(); + console.log(`Dashboard Page with id=${data.id} has been deleted.`), + this.showSuccess(); + }, + error => (this.showError(error)) + ); + } else return; + + }); + + }; + } + +} diff --git a/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.html b/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.html new file mode 100644 index 0000000000000000000000000000000000000000..97c5807e4fbd02169506e142e5b258623bd3958f --- /dev/null +++ b/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.html @@ -0,0 +1,11 @@ +<nb-card> + <nb-card-header class="d-flex justify-content-between"> + <h4>{{'general.menu_blocks'|translate}}</h4> + <button nbButton size="small" status="info" routerLink="../add-menu-block-page" + status="primary">{{'general.add'|translate}} {{'general.menu_blocks'|translate}}</button> + </nb-card-header> + + <nb-card-body> + <ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table> + </nb-card-body> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.scss b/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.spec.ts b/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0420e7c68b23fc96139a0d594e3731c3913da5df --- /dev/null +++ b/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ManageMenuBlocksComponent } from './manage-menu-blocks.component'; + +describe('ManageMenuBlocksComponent', () => { + let component: ManageMenuBlocksComponent; + let fixture: ComponentFixture<ManageMenuBlocksComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ManageMenuBlocksComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ManageMenuBlocksComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.ts b/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3905f59580c44fb59d2aa364e926a8eda2981348 --- /dev/null +++ b/src/app/pages/dashboard-management/manage-menu-blocks/manage-menu-blocks.component.ts @@ -0,0 +1,151 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { NbDialogService, NbToastrService } from '@nebular/theme'; +import { TranslateService } from '@ngx-translate/core'; +import { LocalDataSource } from 'ng2-smart-table'; +import { MenuType } from '../../../model/enumeration/menu-type.model'; +import { MenuBlock } from '../../../model/menu-block.model'; +import { DialogComponent } from '../../shared/dialog/dialog.component'; +import { TableActionsComponent } from '../../shared/table-actions/table-actions.component'; +import { MenuBlockCrudService } from '../services/menu-block-crud.service'; + +@Component({ + selector: 'ngx-manage-menu-blocks', + templateUrl: './manage-menu-blocks.component.html', + styleUrls: ['./manage-menu-blocks.component.scss'] +}) +export class ManageMenuBlocksComponent implements OnInit { + source: LocalDataSource; + settings: any; + menuBlocks: Array<MenuBlock>=[]; + + constructor( private service: MenuBlockCrudService, + private router: Router, + private dialogService: NbDialogService, + private translateService: TranslateService, + private toastrService: NbToastrService) + { + this.initTableSettings(); + } + + ngOnInit(): void { + this.source = new LocalDataSource(); + this.fetchTableData(); + } + + fetchTableData() { + this.service.getAllEditable().subscribe( + (data) => { + this.menuBlocks=data; + this.source.load(data); + }, + error => (this.showError(error)) + ); + } + + showSuccess() { + this.toastrService.success(this.translateService.instant('menuBlocks.deleted'), this.translateService.instant('general.success')); + } + + showError(error) { + if (error.status == '500' && error.ok == false ) { + // More Menu Items linked to the Menu Block. + this.toastrService.danger(this.translateService.instant('menuBlock.delete_fk_error'), this.translateService.instant('general.error')) + } else { + this.toastrService.danger(error.message, this.translateService.instant('general.error')) + } + } + + initTableSettings(): void { + let typeFilter=[]; //FIX + let types = MenuType.values(); + for(let curType of types) { + typeFilter.push({ value: curType.value, title: curType.enumVal }) + } + + this.settings = { + mode: 'external', + columns: { + type: { + title: this.translateService.instant('menuBlock.type'), + filter: { + type: 'list', + config: { + selectText: '', + list: typeFilter + }, + } + }, + code: { + title: this.translateService.instant('menuBlock.code') + }, + label: { + title: this.translateService.instant('menuBlock.label') + }, + + /* + id: { + title: this.translateService.instant('menuBlock.id'), + filter: false + }, + order: { + title: this.translateService.instant('menuBlock.order'), + filter: false + }, + */ + + actions: { + title: this.translateService.instant('general.actions'), + filter: false, + sort: false, + type: 'custom', + valuePrepareFunction: (cell, row) => { + return [ + {action:'edit', enabled:true, status:"info", icon:"edit-2", tooltip:this.translateService.instant('general.edit')}, + {action:'delete', enabled:true, status:"danger", icon:"trash-2", tooltip:this.translateService.instant('general.delete')}, + ]; + }, + renderComponent: TableActionsComponent, + onComponentInitFunction: (instance) => { + instance.onCustom.subscribe(value => { + this.onCustom(value,instance.rowData); + }); + }, + } + }, + + actions: { + add: false, + edit: false, + delete: false, + }, + + }; + } + + onCustom(event, data) { + if (event == 'edit') { + this.router.navigate(['/pages/dashboard-management/edit-menu-block-page/' + data.id]) + }; + + if (event == 'delete') { + this.dialogService.open(DialogComponent, { context: { + title: this.translateService.instant('general.delete') + " " + this.translateService.instant('menuBlock.title') , + message: this.translateService.instant('general.deleteConfirm') } }) + .onClose.subscribe(res => { + if (res) { + this.service.deleteById(data.id).subscribe(() => { + this.fetchTableData(); + console.log(`# Menu Block with id=${data.id} has been deleted.`); + this.showSuccess(); + }, + error => (this.showError(error)) + ); + } else return; + + }); + + }; + } + +} diff --git a/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.html b/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.html new file mode 100644 index 0000000000000000000000000000000000000000..940eff3b668379da8b95d5b57e13d5872a3a09c8 --- /dev/null +++ b/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.html @@ -0,0 +1,42 @@ +<nb-card> + <nb-card-header> + <h4>{{'general.manage'|translate}} {{'general.menu_blocks'|translate}}</h4> + </nb-card-header> + + <nb-card-body [formGroup]="block" class="col-md-6 d-flex flex-row justify-content-around"> + + <div class="form-group d-flex flex-column" > + <label for="type" class="label"> + <nb-icon icon="info-outline" nbTooltip="{{'menuBlock.type_info'|translate}}" ></nb-icon> + {{'menuBlock.type'|translate}} * + </label> + + <nb-select fullWidth formControlName="type" size="small" name="type" + placeholder="{{'menuBlock.type'|translate}}" + nbTooltip="{{'general.mandatory_field'|translate}}"> + <nb-option *ngFor="let item of typeDomain" [value]="item.enumVal"> + {{item.enumVal}} + </nb-option> + </nb-select> + + </div> + + <div class="form-group d-flex flex-column" > + <label for="label" class="label"> + {{'menuBlock.label'|translate}} * + </label> + <input type="text" nbInput fullWidth fieldSize="small" placeholder="{{'menuBlock.label'|translate}}" + label="label" formControlName="label" nbTooltip="{{'general.mandatory_field'|translate}}"> + </div> + + </nb-card-body> + + <nb-card-footer class="d-flex justify-content-between"> + <button nbButton status="primary" (click)="backClicked()" size="small"> + <nb-icon icon="backspace-outline"></nb-icon> {{'general.back'|translate}} + </button> + <button type="submit" nbButton status="primary" (click)="storeBlock()" size="small" [disabled]="block.invalid"> + <nb-icon icon="save"></nb-icon>{{'general.save'|translate}} + </button> + </nb-card-footer> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.scss b/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.spec.ts b/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2e5cde2f541f721e6388bffa5f4e874b7d1e5700 --- /dev/null +++ b/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MenuBlocksPageComponent } from './menu-blocks-page.component'; + +describe('MenuBlocksPageComponent', () => { + let component: MenuBlocksPageComponent; + let fixture: ComponentFixture<MenuBlocksPageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MenuBlocksPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MenuBlocksPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.ts b/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd41928f35a2cb09b4a3b81dc5905101118012eb --- /dev/null +++ b/src/app/pages/dashboard-management/menu-blocks-page/menu-blocks-page.component.ts @@ -0,0 +1,97 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NbToastrService } from '@nebular/theme'; +import { TranslateService } from '@ngx-translate/core'; +import { MenuBlock } from '../../../model/menu-block.model'; +import { MenuBlockCrudService } from '../services/menu-block-crud.service'; +import { Location } from '@angular/common'; +import { MenuFormManagerService } from '../services/menu-form-manager.service'; +import { MenuType } from '../../../model/enumeration/menu-type.model'; + +@Component({ + selector: 'ngx-menu-blocks-page', + templateUrl: './menu-blocks-page.component.html', + styleUrls: ['./menu-blocks-page.component.scss'] +}) +export class MenuBlocksPageComponent implements OnInit { + + @Input() block: FormGroup = new FormGroup({}); + typeDomain = MenuType.values(); //Fix + blockId: string; //For targets + + constructor( + private formService: MenuFormManagerService, + private service: MenuBlockCrudService, + private router: Router, + private route: ActivatedRoute, + private toastrService: NbToastrService, + private translateService: TranslateService, + private _location: Location + ) { } + + ngOnInit(): void { + this.block = this.formService.pageFormCreator(new MenuBlock()); + this.route.paramMap.subscribe(params => { + this.blockId = params.get('id'); + if (this.blockId) { + this.service.getById(this.blockId).subscribe( + (event: MenuBlock) =>{ this.editBlock(event); }, + (err: any) => console.log(err) + );; + } + }); + } + + editBlock(menuBlock: MenuBlock) { + this.block = this.formService.pageFormCreator(menuBlock); + } + + storeBlock(): void { + if (this.block.value.id == null) { + //console.log('## CREATE MENU-BLOCK WITH ID:' + this.block.value.id); + let objToAdd = <MenuBlock> this.block.getRawValue(); + objToAdd.code = objToAdd.label!=null ? objToAdd.label.replace(/\s/g, "_").toUpperCase() : objToAdd.label; + //console.log('objToAdd => ' , JSON.stringify(objToAdd)); + + this.service.create(objToAdd).subscribe( + (data: MenuBlock) => { + //console.log("Menu block created, data => " + data); + this.router.navigate(['/pages/dashboard-management/manage-menu-blocks']); + this.showSuccess(); + }, + error => (this.showError(error)) + ); + + } + else { + //console.log('## EDIT MENU-BLOCK WITH ID:' + this.block.value.id); + let objToModify = <MenuBlock>this.block.getRawValue() + objToModify.code = objToModify.label!=null ? objToModify.label.replace(/\s/g, "_").toUpperCase() : objToModify.label; + this.service.update(objToModify).subscribe( + (data: MenuBlock) => { + this.editBlock(data); + this.showSuccess(); + }, + error => (this.showError(error)) + ); + } + } + + showSuccess() { + this.block.value.id == null ? + this.toastrService.success(this.translateService.instant('menuBlock.created'), this.translateService.instant('Success')) : + this.toastrService.success(this.translateService.instant('menuBlock.updated'), this.translateService.instant('Success')); + } + + showError(error) { + this.toastrService.danger(error.error.message, this.translateService.instant('general.error')) + } + + backClicked() { + this._location.back(); + } + +} + + diff --git a/src/app/pages/dashboard-management/menu-filter/menu-filter.component.html b/src/app/pages/dashboard-management/menu-filter/menu-filter.component.html new file mode 100644 index 0000000000000000000000000000000000000000..d99c25278ab1a1bbb4ae3b583a1029e39f3eda2a --- /dev/null +++ b/src/app/pages/dashboard-management/menu-filter/menu-filter.component.html @@ -0,0 +1,40 @@ +<nb-card> + <nb-card-header> + <div class="d-flex flex-row justify-content-between align-items-center mt-2" [formGroup]="form"> + <h4>{{'menuItem.title'|translate}} + <nb-icon icon="info-outline" nbTooltip="{{'menuItem.add_button_info'|translate}}"></nb-icon> + <nb-icon icon="message-circle" nbTooltip="{{'menuBlock.select_menu_block_info'|translate}}"></nb-icon> + </h4> + + <nb-select size="small" placeholder="{{'menuBlock.select_menu_block'|translate}}" + formControlName="selectedMenuBlock" nbTooltip="{{'menuBlock.select_menu_block'|translate}}"> + + <nb-option-group title="{{'MenuType.SHARED'|translate}}"> + <nb-option *ngFor="let blockMenu of fullListBlockShared; " [value]="blockMenu"> + {{blockMenu.label}} + </nb-option> + </nb-option-group> + + <nb-option-group title="{{'MenuType.PERSONAL'|translate}}"> + <nb-option *ngFor="let blockMenu of fullListBlockPersonal; " [value]="blockMenu"> + {{blockMenu.label}} + </nb-option> + </nb-option-group> + </nb-select> + + <input nbInput fieldSize="small" placeholder="{{'menuItem.menu_item_label'|translate}}" + formControlName="selectedMenuItemLabel"> + <button nbButton size="small" status="primary" nbTooltip="{{'menuItem.copy_info'|translate}}" (click)="generateLabel()" [disabled]="disableButton()"> + <nb-icon icon="copy-outline"></nb-icon>{{'general.copy' | translate}} + </button> + + <button nbButton size="small" status="primary" (click)="addMenuItem()" [disabled]="disableButton()"> + <nb-icon icon="list-outline"></nb-icon>{{'general.add' | translate}} {{'menuItem.title'| translate}} + </button> + </div> + + </nb-card-header> + <nb-card-body> + <ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table> + </nb-card-body> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/menu-filter/menu-filter.component.scss b/src/app/pages/dashboard-management/menu-filter/menu-filter.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/menu-filter/menu-filter.component.spec.ts b/src/app/pages/dashboard-management/menu-filter/menu-filter.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa3543efe7fc4c89fb6d6d7110c1241b2217ba43 --- /dev/null +++ b/src/app/pages/dashboard-management/menu-filter/menu-filter.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MenuFilterComponent } from './menu-filter.component'; + +describe('MenuFilterComponent', () => { + let component: MenuFilterComponent; + let fixture: ComponentFixture<MenuFilterComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MenuFilterComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MenuFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/menu-filter/menu-filter.component.ts b/src/app/pages/dashboard-management/menu-filter/menu-filter.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..68d586748877b0c5b3a071e0a6d634ff17b78774 --- /dev/null +++ b/src/app/pages/dashboard-management/menu-filter/menu-filter.component.ts @@ -0,0 +1,254 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NbDialogService, NbToastrService } from '@nebular/theme'; +import { TranslateService } from '@ngx-translate/core'; +import { LocalDataSource } from 'ng2-smart-table'; +import { DashboardPage } from '../../../model/dashboard-page.model'; +import { MenuType } from '../../../model/enumeration/menu-type.model'; +import { IMenuBlock } from '../../../model/menu-block.model'; +import { IMenuItem, MenuItem } from '../../../model/menu-item.model'; +import { DialogComponent } from '../../shared/dialog/dialog.component'; +import { TableActionsComponent } from '../../shared/table-actions/table-actions.component'; +import { MenuBlockCrudService } from '../services/menu-block-crud.service'; +import { MenuItemCrudService } from '../services/menu-item-crud.service'; + +@Component({ + selector: 'ngx-menu-filter', + templateUrl: './menu-filter.component.html', + styleUrls: ['./menu-filter.component.scss'] +}) +export class MenuFilterComponent implements OnInit { + @Input() pageId: string; + @Input() pageName: string; + @Output() sendFilter = new EventEmitter<any>() //Broadcast event for disabledTarget + + //SMART TABLES + source: LocalDataSource; + settings: any; + sharedMenuItems: Array<IMenuItem>=[]; + + toAddMenuItem: IMenuItem; + fullListBlockEditable: Array<IMenuBlock> = []; + fullListBlockShared: Array<IMenuBlock> = []; + fullListBlockPersonal: Array<IMenuBlock> = []; + + form: FormGroup; + selectedMenuBlock: IMenuBlock; + selectedMenuItemLabel: string; + + constructor( + private fb: FormBuilder, + private menuItemService: MenuItemCrudService, + private menuBlockCrudService: MenuBlockCrudService, + private dialogService: NbDialogService, + private toastrService: NbToastrService, + private translateService: TranslateService) { } + + ngOnInit(): void { + this.initTableSettings(); + this.source = new LocalDataSource(); + this.fetchTableData(); + + this.form = this.fb.group({ + selectedMenuBlock: [this.selectedMenuBlock, Validators.nullValidator], + selectedMenuItemLabel: [this.selectedMenuItemLabel, Validators.nullValidator], + }) + + this.menuBlockCrudService.getAllEditable().subscribe(res => { + this.fullListBlockEditable = res; + this.fullListBlockShared = this.fullListBlockEditable.filter(x => x.type == MenuType.SHARED); + this.fullListBlockPersonal = this.fullListBlockEditable.filter(x => x.type == MenuType.PERSONAL); + }); + + } + + fetchTableData() { + if (this.pageId!=undefined) { + this.menuItemService.getByPageId(this.pageId).subscribe( + //Success + (data: Array<IMenuItem>) => { + this.sharedMenuItems = data; + for(let currMenuItem of this.sharedMenuItems){ + // Read MenuItem Label + this.menuBlockCrudService.getById(''+currMenuItem.menuBlock.id).subscribe(res2 => { + if (res2 != undefined && res2 != null && res2.label != undefined && res2.label != null) { + this.sharedMenuItems[0].menuBlock.label = res2.label; //Only a Menu Block is linked to item!! + this.source.load(this.sharedMenuItems); + } else { + this.source.load(this.sharedMenuItems); + } + }); + } + if (this.sharedMenuItems){ + this.source.load(this.sharedMenuItems); + } + }, + //Error + error => (this.showError(error))); + } + } + + addMenuItem() { + console.log("# Add MenuItem linked to dashboard with pageId =>" + this.pageId); + const menuBlock = this.form.get('selectedMenuBlock').value; + const menuItemLabel = this.form.get('selectedMenuItemLabel').value; + + if (menuBlock == undefined || menuBlock == null){ + this.showWarning('menuBlock.select_menu_block'); + return; + } + + if (menuItemLabel == undefined || menuItemLabel == null){ + this.showWarning('menuItem.add_menu_item_label'); + return; + } + + this.toAddMenuItem = this.buildMenuItem(menuBlock, menuItemLabel, menuBlock.type, Number(this.pageId)); + this.menuItemService.create(this.toAddMenuItem).subscribe( + (data: MenuItem) => { + this.showSuccess('menuItem.created'); + this.fetchTableData(); + this.form.reset(); //Clean select and input + + this.broadcastInfo(); //Broadcast event for disabledTarget + }, + error => (this.showError(error)) + ); + } + + private buildMenuItem(menuBlock: IMenuBlock, menuItemLabel: string, type:MenuType, pageId: number) { + const newItem = new MenuItem(); + newItem.dashboardPage = new DashboardPage(); + newItem.dashboardPage.id = pageId; + newItem.type = type; + newItem.label = menuItemLabel; + newItem.code = menuItemLabel!=null ? menuItemLabel.replace(/\s/g, "_").toUpperCase() : menuItemLabel; + newItem.menuBlock = menuBlock; + return newItem; + } + + showSuccess(operation:string) { + this.toastrService.success(this.translateService.instant(operation), this.translateService.instant('general.success')); + } + + showError(error) { + this.toastrService.danger(error.error.message, this.translateService.instant('general.error')) + } + + showWarning(operation:string) { + this.toastrService.warning(this.translateService.instant(operation), this.translateService.instant('general.warning')); + } + + + initTableSettings(): void { + let menuTypeFilter=[]; + for(let mType of MenuType.values()){ + menuTypeFilter.push({ value: mType.enumVal, title: this.translateService.instant("MenuType."+mType.enumVal) }) + } + + this.settings = { + mode: 'external', + attr: { + class: 'table table-bordered' + }, + columns: { + /*id: { + title: this.translateService.instant('menuItem.id'), + }, + */ + type: { + title: this.translateService.instant('menuItem.type'), + valuePrepareFunction: (cell, row) => { + if(row.type!=undefined && row.type!=null) + return this.translateService.instant('MenuType.' + row.type) + else return ''; + }, + filter: { + type: 'list', + config: { + selectText: '', + list: menuTypeFilter + }, + } + }, + menuBlock: { + title: this.translateService.instant('menuBlock.title') + " " + this.translateService.instant('menuBlock.label'), + valuePrepareFunction: (cell, row) => { + if(row.menuBlock!=undefined && row.menuBlock!=null){ + return row.menuBlock.label; + } + else return ''; + } + }, + /*code: { + title: this.translateService.instant('menuItem.code'), + }, + */ + label: { + title: this.translateService.instant('menuItem.title') + " " + this.translateService.instant('menuItem.label') + }, + actions: { + title: this.translateService.instant('general.actions'), + filter: false, + sort: false, + type: 'custom', + valuePrepareFunction: (cell, row) => { + return [ + {action:'delete', enabled:true, status:"danger", icon:"trash-2", tooltip:this.translateService.instant('general.delete')+ " " + this.translateService.instant('menuItem.title')} + ]; + }, + renderComponent: TableActionsComponent, + onComponentInitFunction: (instance) => { + instance.onCustom.subscribe(value => { + this.onCustom(value,instance.rowData); + }); + }, + } + + }, + + actions: { + add: false, + edit: false, + delete: false, + }, + + }; + } + + onCustom(event, data) { + if (event == 'delete') { + this.dialogService + .open( DialogComponent, { context: { + title: this.translateService.instant('general.delete') + " " + this.translateService.instant('menuItem.title') , + message: this.translateService.instant('general.deleteConfirm')} }) + .onClose.subscribe(res => { + if (res) { + this.menuItemService.deleteById(data.id).subscribe( (res) => { + this.fetchTableData(); + console.log(`## MenuItem with id=${data.id} has been deleted.`), + this.showSuccess('menuItem.deleted'); + + this.broadcastInfo(); //Broadcast event for disabledTarget + }, + error => (this.showError(error)) + ); + } else return; + + }); + }; + } + + disableButton() { + return this.pageId == null || (this.sharedMenuItems!=undefined && this.sharedMenuItems.length > 0) ? true : false; + } + + generateLabel(){ + this.form.patchValue( { selectedMenuItemLabel: this.pageName }); + } + + broadcastInfo() { + this.sendFilter.emit(this.form.getRawValue()); + } + +} diff --git a/src/app/pages/dashboard-management/services/dashboard-page-crud.service.spec.ts b/src/app/pages/dashboard-management/services/dashboard-page-crud.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..bdae39c829b6e8df12e9bc0cf3ca90b6b5780ada --- /dev/null +++ b/src/app/pages/dashboard-management/services/dashboard-page-crud.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed } from '@angular/core/testing'; +import { DashboardPageCrudService } from './dashboard-page-crud.service'; + +describe('DashboardPageCrudService', () => { + let service: DashboardPageCrudService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DashboardPageCrudService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/dashboard-page-crud.service.ts b/src/app/pages/dashboard-management/services/dashboard-page-crud.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..c615538ac65d3f04921e27e04f28a073018b555b --- /dev/null +++ b/src/app/pages/dashboard-management/services/dashboard-page-crud.service.ts @@ -0,0 +1,81 @@ +import { Injectable } from '@angular/core'; +import { ConfigService } from '@ngx-config/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { IDashboardPage } from '../../../model/dashboard-page.model'; +import { ICloneDashboard } from '../../../model/clone-dashboard.model'; +import AMTram2020 from '../../../../assets/map/am_tram_2020.json'; + +@Injectable({ + providedIn: 'root' +}) +export class DashboardPageCrudService { + private apiURL: any; + + constructor(configService: ConfigService, private http: HttpClient) { + this.apiURL = configService.getSettings("dashboard-controller.api_base_url"); + } + + getAllPages(): Observable<Array<IDashboardPage>> { + return this.http.get<Array<IDashboardPage>>(`${this.apiURL}/api/all-dashboard-pages`); + } + + getSharedPages(): Observable<Array<IDashboardPage>> { + return this.http.get<Array<IDashboardPage>>(`${this.apiURL}/api/shared-dashboard-pages`); + } + + getPersonalPages(): Observable<Array<IDashboardPage>> { + return this.http.get<Array<IDashboardPage>>(`${this.apiURL}/api/personal-dashboard-pages`); + } + + getEditablePages(): Observable<Array<IDashboardPage>> { + return this.http.get<Array<IDashboardPage>>(`${this.apiURL}/api/editable-dashboard-pages`); + } + + getClonablePages(): Observable<Array<IDashboardPage>> { + return this.http.get<Array<IDashboardPage>>(`${this.apiURL}/api/clonable-dashboard-pages`); + } + + getPageById(id: string): Observable<IDashboardPage> { + return this.http.get<IDashboardPage>(`${this.apiURL}/api/dashboard-pages/${id}`); + } + + createPage(page: IDashboardPage): Observable<IDashboardPage> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.post<IDashboardPage>(`${this.apiURL}/api/dashboard-pages`, page, { headers: httpHeaders }); + } + + deletePageById(id: string): any { + return this.http.delete<IDashboardPage>(`${this.apiURL}/api/dashboard-pages/${id}`); + } + + deleteDeepPageById(id: string): any { + return this.http.delete<IDashboardPage>(`${this.apiURL}/api/dashboard-pages-deep/${id}`); + } + + updatePageById(page: IDashboardPage): Observable<IDashboardPage> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.put<IDashboardPage>(`${this.apiURL}/api/dashboard-pages/${page.id}`, page, { + headers: httpHeaders, + }); + } + + duplicatePage(id: string): Observable<IDashboardPage> { + return this.http.get<IDashboardPage>(`${this.apiURL}/api/duplicate-dashboard-pages/${id}`); + } + + duplicatePageWithParams(cloneDashboard: ICloneDashboard): Observable<IDashboardPage> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.post<IDashboardPage>(`${this.apiURL}/api/duplicate-by-params-dashboard-pages`, cloneDashboard, { headers: httpHeaders }); + } + + getGeoJsonMap(url: string): Observable<any> { + //Mocked mode A => http://localhost:4200/assets/map/am_tram_2018.json + //Mocked mode B => http://localhost:4200/assets/map/2020-March-heatgeo-5minutes.json + if (url == undefined || url == null) + return of(AMTram2020); + else + return this.http.get<any>(`${url}`); + } + +} diff --git a/src/app/pages/dashboard-management/services/form-manager.service.spec.ts b/src/app/pages/dashboard-management/services/form-manager.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..39e8d3f822efd1d245603db8f986f51308cfdaa5 --- /dev/null +++ b/src/app/pages/dashboard-management/services/form-manager.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed } from '@angular/core/testing'; +import { FormManagerService } from './form-manager.service'; + +describe('FormManagerService', () => { + let service: FormManagerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(FormManagerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/form-manager.service.ts b/src/app/pages/dashboard-management/services/form-manager.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..7fafacdf2d5a348db580650b94366f9b6b2dcc04 --- /dev/null +++ b/src/app/pages/dashboard-management/services/form-manager.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { FormGroup, FormControl, FormArray, Validators } from '@angular/forms'; +import { IDashboardPage } from '../../../model/dashboard-page.model'; + + +@Injectable({ + + providedIn: 'root' +}) +export class FormManagerService { + + constructor() { } + + + pageFormCreator(page: IDashboardPage): FormGroup { + + let group: any = { + id: new FormControl(page.id || null), + name: new FormControl(page.name || null, Validators.required), + description: new FormControl(page.description || null), + note: new FormControl(page.note || null), + shared: new FormControl(page.shared || null, Validators.required), + content: new FormControl(page.content || null), + }; + + return new FormGroup(group); + + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard-management/services/menu-block-crud.service.spec.ts b/src/app/pages/dashboard-management/services/menu-block-crud.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..dae1ff56c4d043c984c190fbeb65e53570f8e06d --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-block-crud.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MenuBlockCrudService } from './menu-block-crud.service'; + +describe('MenuBlockCrudService', () => { + let service: MenuBlockCrudService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MenuBlockCrudService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/menu-block-crud.service.ts b/src/app/pages/dashboard-management/services/menu-block-crud.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..08403a36a69851bf5c3afc222d0a69368eb68378 --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-block-crud.service.ts @@ -0,0 +1,57 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ConfigService } from '@ngx-config/core'; +import { Observable } from 'rxjs'; +import { IMenuBlock } from '../../../model/menu-block.model'; + +@Injectable({ + providedIn: 'root' +}) +export class MenuBlockCrudService { + private apiURL: any; + + constructor(configService: ConfigService, private http: HttpClient) { + this.apiURL = configService.getSettings("dashboard-controller.api_base_url"); + } + + getAll(): Observable<Array<IMenuBlock>> { + return this.http.get<Array<IMenuBlock>>(`${this.apiURL}/api/menu-blocks`); + } + + getAllShared(): Observable<Array<IMenuBlock>> { + return this.http.get<Array<IMenuBlock>>(`${this.apiURL}/api/shared-menu-blocks`); + } + + getAllPersonal(): Observable<Array<IMenuBlock>> { + return this.http.get<Array<IMenuBlock>>(`${this.apiURL}/api/personal-menu-blocks`); + } + + getAllLinked(): Observable<Array<IMenuBlock>> { + return this.http.get<Array<IMenuBlock>>(`${this.apiURL}/api/linked-menu-blocks`); + } + + getAllEditable(): Observable<Array<IMenuBlock>> { + return this.http.get<Array<IMenuBlock>>(`${this.apiURL}/api/editable-menu-blocks`); + } + + getById(id: string): Observable<IMenuBlock> { + return this.http.get<IMenuBlock>(`${this.apiURL}/api/menu-blocks/${id}`); + } + + create(menuBlock: IMenuBlock): Observable<IMenuBlock> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.post<IMenuBlock>(`${this.apiURL}/api/menu-blocks`, menuBlock, { headers: httpHeaders }); + } + + deleteById(id: string): any { + return this.http.delete<IMenuBlock>(`${this.apiURL}/api/menu-blocks/${id}`); + } + + update(menuBlock: IMenuBlock): Observable<IMenuBlock> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.put<IMenuBlock>(`${this.apiURL}/api/menu-blocks/${menuBlock.id}`, menuBlock, { + headers: httpHeaders, + }); + } + +} diff --git a/src/app/pages/dashboard-management/services/menu-form-manager.service.spec.ts b/src/app/pages/dashboard-management/services/menu-form-manager.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0274df300d5d66a42630d4d7a43cc91f177a6c6 --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-form-manager.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MenuFormManagerService } from './menu-form-manager.service'; + +describe('MenuFormManagerService', () => { + let service: MenuFormManagerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MenuFormManagerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/menu-form-manager.service.ts b/src/app/pages/dashboard-management/services/menu-form-manager.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..57b80fde460eaaf2757809883be731732ce0e506 --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-form-manager.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { IMenuBlock } from '../../../model/menu-block.model'; + +@Injectable({ + providedIn: 'root' +}) +export class MenuFormManagerService { + + constructor() { } + + + pageFormCreator(block: IMenuBlock): FormGroup { + + let group: any = { + id: new FormControl(block.id || null), + code: new FormControl(block.code || null), + label: new FormControl(block.label || null, Validators.required), + order: new FormControl(block.order || null), + type: new FormControl(block.type || null, Validators.required), + // createdDate: new FormControl(block.createdDate || null), + createdBy: new FormControl(block.createdBy || null), + menuItems: new FormControl(block.linkedItems || null), + }; + + return new FormGroup(group); + } +} diff --git a/src/app/pages/dashboard-management/services/menu-item-crud.service.spec.ts b/src/app/pages/dashboard-management/services/menu-item-crud.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..28c6183f614c280c3aaf2d4d937e9426e27f7531 --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-item-crud.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MenuItemCrudService } from './menu-item-crud.service'; + +describe('MenuItemCrudService', () => { + let service: MenuItemCrudService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MenuItemCrudService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/menu-item-crud.service.ts b/src/app/pages/dashboard-management/services/menu-item-crud.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..3bda87ff86287a43689233f98cbe478ee2323997 --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-item-crud.service.ts @@ -0,0 +1,45 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ConfigService } from '@ngx-config/core'; +import { Observable } from 'rxjs'; +import { IMenuItem } from '../../../model/menu-item.model'; + +@Injectable({ + providedIn: 'root' +}) +export class MenuItemCrudService { + private apiURL: any; + + constructor(configService: ConfigService, private http: HttpClient) { + this.apiURL = configService.getSettings("dashboard-controller.api_base_url"); + } + + getAll(): Observable<Array<IMenuItem>> { + return this.http.get<Array<IMenuItem>>(`${this.apiURL}/api/menu-items`); + } + + getById(id: string): Observable<IMenuItem> { + return this.http.get<IMenuItem>(`${this.apiURL}/api/menu-items/${id}`); + } + + getByPageId(pageId: string): Observable<Array<IMenuItem>> { + return this.http.get<Array<IMenuItem>>(`${this.apiURL}/api/menu-items-bypage/${pageId}`); + } + + create(menuItem: IMenuItem): Observable<IMenuItem> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.post<IMenuItem>(`${this.apiURL}/api/menu-items`, menuItem, { headers: httpHeaders }); + } + + deleteById(id: string): any { + return this.http.delete<IMenuItem>(`${this.apiURL}/api/menu-items/${id}`); + } + + update(menuItem: IMenuItem): Observable<IMenuItem> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.put<IMenuItem>(`${this.apiURL}/api/menu-items/${menuItem.id}`, menuItem, { + headers: httpHeaders, + }); + } + +} diff --git a/src/app/pages/dashboard-management/services/menu-mapper.service.spec.ts b/src/app/pages/dashboard-management/services/menu-mapper.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c0f429e3e32e97bf7fcee0bb35de93910425051f --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-mapper.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed } from '@angular/core/testing'; +import { MenuMapperService } from './menu-mapper.service'; + +describe('MenuMapperService', () => { + let service: MenuMapperService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MenuMapperService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/menu-mapper.service.ts b/src/app/pages/dashboard-management/services/menu-mapper.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..23330d72ecfb0c7d574567ebc10dc172e92e7378 --- /dev/null +++ b/src/app/pages/dashboard-management/services/menu-mapper.service.ts @@ -0,0 +1,67 @@ +import { Injectable } from '@angular/core'; +import { NbMenuItem } from '@nebular/theme'; +import { MenuType } from '../../../model/enumeration/menu-type.model'; +import { IMenuBlock } from '../../../model/menu-block.model'; +import { IMenuItem } from '../../../model/menu-item.model'; + +@Injectable({ + providedIn: 'root' +}) +export class MenuMapperService { + + constructor() { + } + + /** + * JMA - This is a mapper to convert IMenuItem into NbMenuItem child. + * + * @param IMenuItem + * @returns NbMenuItem + */ + mapItemToNbMenuItem(menuItem: IMenuItem): any { + //console.log('Mapping item: ', menuItem); + let nbMenuItem = new NbMenuItem; + if (menuItem.dashboardPage !== undefined && menuItem.dashboardPage !== null && menuItem.dashboardPage.id !== null) { + nbMenuItem.link ='/pages/dashboard-management/edit-dashboard-page-content/'+menuItem.dashboardPage.id; + nbMenuItem.queryParams = {isEditable: false}; + } + if (menuItem.label !== undefined && menuItem.label !== null) { + nbMenuItem.title = menuItem.label; + } + /*if (menuItem.type !== undefined && menuItem.type !== null) { + nbMenuItem.icon = menuItem.type == MenuType.SHARED ? 'globe-outline' : 'lock-outline'; + }*/ + return nbMenuItem; + } + + + /** + * JMA - This is a mapper to convert IMenuBlock into NbMenuItem father. + * + * @param IMenuBlock + * @returns NbMenuItem + */ + mapBlockToNbMenuItem(menuBlock: IMenuBlock): any { + let nbMenuItem = new NbMenuItem(); + //console.log('link ' + menuBlock.code); + if (menuBlock.code !== undefined && menuBlock.code !== null) { + nbMenuItem.link = menuBlock.code; + } + //console.log('title ' + menuBlock.label); + if (menuBlock.label !== undefined && menuBlock.label !== null) { + nbMenuItem.title = menuBlock.label; + } + if (menuBlock.type !== undefined && menuBlock.type !== null) { + nbMenuItem.icon = menuBlock.type == MenuType.SHARED ? 'globe-outline' : 'lock-outline'; + } + //console.log('Check not undef: ', menuBlock.linkedItems !== undefined, 'Check not null: ', menuBlock.linkedItems !== null); + //console.log('Menu items: ', menuBlock.linkedItems); + + if (menuBlock.linkedItems !== undefined || menuBlock.linkedItems !== null) { + // console.log('### menuBlock.linkedItems ===> ', menuBlock.linkedItems); + nbMenuItem.children = menuBlock.linkedItems.map(item => this.mapItemToNbMenuItem(item)); // TODO: finish + } + return nbMenuItem; + } + +} diff --git a/src/app/pages/dashboard-management/services/target-available-crud.service.spec.ts b/src/app/pages/dashboard-management/services/target-available-crud.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..60b126839b2339f7037afa5c56d1abade5e1ffd5 --- /dev/null +++ b/src/app/pages/dashboard-management/services/target-available-crud.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { TargetAvailableCrudService } from './target-available-crud.service'; + +describe('TargetAvailableCrudService', () => { + let service: TargetAvailableCrudService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(TargetAvailableCrudService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/target-available-crud.service.ts b/src/app/pages/dashboard-management/services/target-available-crud.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..1885fe9c67790d67e977ce49b53b9e3ce31b4501 --- /dev/null +++ b/src/app/pages/dashboard-management/services/target-available-crud.service.ts @@ -0,0 +1,45 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ConfigService } from '@ngx-config/core'; +import { Subject, Observable } from 'rxjs'; +import { ITargetAvailable } from '../../../model/target-available.model'; + +@Injectable({ + providedIn: 'root' +}) +export class TargetAvailableCrudService { + private apiURL: any; + + constructor(configService: ConfigService, private http: HttpClient) { + this.apiURL = configService.getSettings("dashboard-controller.api_base_url"); + } + + getAll(): Observable<Array<ITargetAvailable>> { + return this.http.get<Array<ITargetAvailable>>(`${this.apiURL}/api/target-availables`); + } + + getAvailableTargetById(id: string): Observable<ITargetAvailable> { + return this.http.get<ITargetAvailable>(`${this.apiURL}/api/target-availables/${id}`); + } + + getAvailableTargetsByType(type: string): Observable<Array<ITargetAvailable>> { + return this.http.get<Array<ITargetAvailable>>(`${this.apiURL}/api/target-availables-bytype/${type}`); + } + + createAvailableTarget(target: ITargetAvailable): Observable<ITargetAvailable> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.post<ITargetAvailable>(`${this.apiURL}/api/target-availables`, target, { headers: httpHeaders }); + } + + deleteAvailableTargetById(id: string): any { + return this.http.delete<ITargetAvailable>(`${this.apiURL}/api/target-availables/${id}`); + } + + updateAvailableTarget(target: ITargetAvailable): Observable<ITargetAvailable> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.put<ITargetAvailable>(`${this.apiURL}/api/target-availables/${target.id}`, target, { + headers: httpHeaders, + }); + } + +} diff --git a/src/app/pages/dashboard-management/services/target-crud.service.spec.ts b/src/app/pages/dashboard-management/services/target-crud.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4358d3bd4f3adabb0ba1736c3111d6961108c05 --- /dev/null +++ b/src/app/pages/dashboard-management/services/target-crud.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed } from '@angular/core/testing'; +import { TargetCrudService } from './target-crud.service'; + +describe('TargetCrudService', () => { + let service: TargetCrudService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(TargetCrudService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/target-crud.service.ts b/src/app/pages/dashboard-management/services/target-crud.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..6793209f7a87ba4bdc201fe390a890d7c29f8484 --- /dev/null +++ b/src/app/pages/dashboard-management/services/target-crud.service.ts @@ -0,0 +1,45 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ConfigService } from '@ngx-config/core'; +import { Subject, Observable } from 'rxjs'; +import { ITarget } from '../../../model/target.model'; + +@Injectable({ + providedIn: 'root' +}) +export class TargetCrudService { + private apiURL: any; + + constructor(configService: ConfigService, private http: HttpClient) { + this.apiURL = configService.getSettings("dashboard-controller.api_base_url"); + } + + getAll(): Observable<Array<ITarget>> { + return this.http.get<Array<ITarget>>(`${this.apiURL}/api/targets`); + } + + getTargetById(id: string): Observable<ITarget> { + return this.http.get<ITarget>(`${this.apiURL}/api/targets/${id}`); + } + + getTargetsByPageId(pageId: string): Observable<Array<ITarget>> { + return this.http.get<Array<ITarget>>(`${this.apiURL}/api/targets-bypage/${pageId}`); + } + + createTarget(target: ITarget): Observable<ITarget> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.post<ITarget>(`${this.apiURL}/api/targets`, target, { headers: httpHeaders }); + } + + deleteTargetById(id: string): any { + return this.http.delete<ITarget>(`${this.apiURL}/api/targets/${id}`); + } + + updateTarget(target: ITarget): Observable<ITarget> { + let httpHeaders = new HttpHeaders({ 'Content-type': 'application/json' }); + return this.http.put<ITarget>(`${this.apiURL}/api/targets/${target.id}`, target, { + headers: httpHeaders, + }); + } + +} diff --git a/src/app/pages/dashboard-management/services/user-crud.service.spec.ts b/src/app/pages/dashboard-management/services/user-crud.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..aadcfe87dc4e76b706a22074e85a819265567313 --- /dev/null +++ b/src/app/pages/dashboard-management/services/user-crud.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed } from '@angular/core/testing'; +import { UserCrudService } from './user-crud.service'; + +describe('UserCrudService', () => { + let service: UserCrudService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UserCrudService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/services/user-crud.service.ts b/src/app/pages/dashboard-management/services/user-crud.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..522075074b3c895be9551f33daf71ab3d4f5f4b1 --- /dev/null +++ b/src/app/pages/dashboard-management/services/user-crud.service.ts @@ -0,0 +1,35 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ConfigService } from '@ngx-config/core'; +import { Observable } from 'rxjs'; +import { IGroupRepresentation } from '../../../model/group-representation.model'; +import { IRoleRepresentation } from '../../../model/role-representation.model'; +import { IUser } from '../../../model/user.model'; + +@Injectable({ + providedIn: 'root' +}) +export class UserCrudService { + private apiURL: any; + + constructor(configService: ConfigService, private http: HttpClient) { + this.apiURL = configService.getSettings("dashboard-controller.api_base_url"); + } + + getIdmUsers(): Observable<Array<IUser>> { + return this.http.get<Array<IUser>>(`${this.apiURL}/user/get-idm-users`); + } + + getIdmRoles(): Observable<Array<IRoleRepresentation>> { + return this.http.get<Array<IRoleRepresentation>>(`${this.apiURL}/user/get-idm-roles`); + } + + getIdmGroups(): Observable<Array<IGroupRepresentation>> { + return this.http.get<Array<IGroupRepresentation>>(`${this.apiURL}/user/get-idm-groups`); + } + + getUserRoles(): Observable<Array<string>> { + return this.http.get<Array<string>>(`${this.apiURL}/user/get-user-roles`); + } + +} diff --git a/src/app/pages/dashboard-management/statistics/statistics.component.html b/src/app/pages/dashboard-management/statistics/statistics.component.html new file mode 100644 index 0000000000000000000000000000000000000000..edfc9c96fced11c9477c585a0958f803fa28c3f7 --- /dev/null +++ b/src/app/pages/dashboard-management/statistics/statistics.component.html @@ -0,0 +1,12 @@ +<nb-card size="medium"> + <nb-card-body> + <span class="h3 location">{{statistics_title}}</span> + <span class="date">{{statistics_subtitle}}</span> + + <div class="today"> + <span class="today-temperature h1">{{statistics_value}}</span> + <nb-icon [icon]="statistics_icon" pack="eva" class="today-icon"></nb-icon> + </div> + + </nb-card-body> +</nb-card> diff --git a/src/app/pages/dashboard-management/statistics/statistics.component.scss b/src/app/pages/dashboard-management/statistics/statistics.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..2ac7e09720a4ae2bb9367eefa46b25c683fb3d64 --- /dev/null +++ b/src/app/pages/dashboard-management/statistics/statistics.component.scss @@ -0,0 +1,76 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + + nb-card-body { + display: flex; + flex-direction: column; + } + + .location, + .date { + display: block; + } + + .location { + margin-bottom: 0.1rem; + } + + .today { + display: flex; + justify-content: space-around; + } + + .today-temperature { + display: flex; + flex-direction: column; + justify-content: center; + margin: 2rem 1.5rem; + } + + .today-icon { + color: nb-theme(color-primary-default); + font-size: 10rem; + line-height: 1; + margin-top: -4rem; + margin-left: auto; + margin-right: 0.4rem; + } + + .today-details { + display: flex; + justify-content: space-around; + margin-top: 2rem; + } + + .parameter { + flex: 1 1 auto; + text-align: center; + } + + .parameter-name, + .parameter-value { + display: block; + } + + .caption { + text-transform: uppercase; + } + + .weekly-forecast { + display: flex; + justify-content: space-around; + margin: auto 0; + } + + .day { + display: flex; + flex-direction: column; + text-align: center; + } + + .weather-icon { + font-size: 2.5rem; + line-height: 2.5rem; + } +} diff --git a/src/app/pages/dashboard-management/statistics/statistics.component.ts b/src/app/pages/dashboard-management/statistics/statistics.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..59922474b9a000e281d3a34b776774d5ab7567f9 --- /dev/null +++ b/src/app/pages/dashboard-management/statistics/statistics.component.ts @@ -0,0 +1,22 @@ +import { Component, Input, OnInit } from '@angular/core'; + +@Component({ + selector: 'ngx-statistics', + styleUrls: ['./statistics.component.scss'], + templateUrl: './statistics.component.html', +}) + +export class StatisticsComponent implements OnInit { + + @Input() statistics_title: string; + @Input() statistics_subtitle: string; + @Input() statistics_value: string; + @Input() statistics_icon: string; + + ngOnInit(): void { + //console.log("This is my statistics => " + this.statistics); + //this.statistics = JSON.parse(this.statistics); + // external-link-outline, sun-outline + } + +} diff --git a/src/app/pages/dashboard-management/target-filter/target-filter.component.html b/src/app/pages/dashboard-management/target-filter/target-filter.component.html new file mode 100644 index 0000000000000000000000000000000000000000..0515d1defb3ee06202c18aa1c07f19e202910f51 --- /dev/null +++ b/src/app/pages/dashboard-management/target-filter/target-filter.component.html @@ -0,0 +1,112 @@ +<nb-card *ngIf="!disabledTarget"> + <nb-card-header > + + <div class="d-flex flex-row justify-content-between align-items-center mt-2" [formGroup]="form" > + <h4> + {{'general.targets'|translate}} + <nb-icon icon="info-outline" nbTooltip="{{'target.add_button_info'|translate}}" ></nb-icon> + </h4> + + <!-- USERS --> + <nb-select *ngIf="!useIDM4Target" size="small" placeholder="{{'target.select_person'|translate}}" formControlName="selectedAvailablePerson" + nbTooltip="{{'target.select_person'|translate}}"> + <nb-option *ngFor="let person of filterTarget(fullListAvailablePerson,'PERSON'); " [value]="person"> + {{person.code}} - {{person.value}} + </nb-option> + </nb-select> + <nb-select *ngIf="useIDM4Target" size="small" placeholder="{{'target.select_person'|translate}}" formControlName="selectedAvailablePersonIDM" + nbTooltip="{{'target.select_person'|translate}}"> + <nb-option *ngFor="let person of filterIDMPersons(fullListAvailablePersonIDM); " [value]="person"> + <!-- {{person.firstName}} {{person.lastName}} ({{person.email}}) --> + {{person.username}} + </nb-option> + </nb-select> + <button nbButton size="small" status="primary" (click)="addTargetPerson()" > + <nb-icon icon="person-outline"></nb-icon>{{'general.add' | translate}} {{'TargetType.PERSON'| translate}} + </button> + + + <!-- ROLES --> + <nb-select *ngIf="!useIDM4Target" size="small" placeholder="{{'target.select_role'|translate}}" formControlName="selectedAvailableRole" + nbTooltip="{{'target.select_role'|translate}}"> + <nb-option *ngFor="let role of filterTarget(fullListAvailableRole,'ROLE'); " [value]="role"> + {{role.code}} - {{role.value}} + </nb-option> + </nb-select> + <nb-select *ngIf="useIDM4Target" size="small" placeholder="{{'target.select_role'|translate}}" formControlName="selectedAvailableRoleIDM" + nbTooltip="{{'target.select_role'|translate}}"> + <nb-option *ngFor="let role of filterIDMRoles(fullListAvailableRoleIDM); " [value]="role"> + <!--{{role.name}} - {{role.description}}--> + {{role.name}} + </nb-option> + </nb-select> + <button nbButton size="small" status="primary" (click)="addTargetRole()" > + <nb-icon icon="globe-2-outline"></nb-icon>{{'general.add' | translate}} {{'TargetType.ROLE'| translate}} + </button> + + <!-- GROUPS --> + <nb-select *ngIf="!useIDM4Target" size="small" placeholder="{{'target.select_group'|translate}}" formControlName="selectedAvailableGroup" + nbTooltip="{{'target.select_group'|translate}}"> + <nb-option *ngFor="let group of filterTarget(fullListAvailableGroup,'GROUP'); " [value]="group"> + {{group.code}} - {{group.value}} + </nb-option> + </nb-select> + <nb-select *ngIf="useIDM4Target" size="small" placeholder="{{'target.select_group'|translate}}" formControlName="selectedAvailableGroupIDM" + nbTooltip="{{'target.select_group'|translate}}"> + <nb-option *ngFor="let group of filterIDMGroups(fullListAvailableGroupsIDM); " [value]="group"> + {{group.name}} + </nb-option> + </nb-select> + <button nbButton size="small" status="primary" (click)="addTargetGroup()"> + <nb-icon icon="globe-2-outline"></nb-icon>{{'general.add' | translate}} {{'TargetType.GROUP'| translate}} + </button> + + </div> + + </nb-card-header> + <nb-card-body> + <ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table> + </nb-card-body> +</nb-card> + + +<nb-card *ngIf="disabledTarget"> + <nb-card-header > + <div class="d-flex flex-row justify-content-between align-items-center mt-2"> + <h4> + {{'general.targets'|translate}} + <nb-icon icon="info-outline" nbTooltip="{{'target.add_button_info_disabled'|translate}}" ></nb-icon> + </h4> + <!-- USERS --> + <nb-select size="small" placeholder="{{'target.select_person'|translate}}" disabled + nbTooltip="{{'target.select_person'|translate}}"> + <nb-option></nb-option> + </nb-select> + <button nbButton size="small" status="primary" disabled> + <nb-icon icon="person-outline"></nb-icon>{{'general.add' | translate}} {{'TargetType.PERSON'| translate}} + </button> + + <!-- ROLES --> + <nb-select size="small" placeholder="{{'target.select_role'|translate}}" disabled + nbTooltip="{{'target.select_role'|translate}}"> + <nb-option></nb-option> + </nb-select> + <button nbButton size="small" status="primary" disabled> + <nb-icon icon="globe-2-outline"></nb-icon>{{'general.add' | translate}} {{'TargetType.ROLE'| translate}} + </button> + + <!-- GROUPS --> + <nb-select size="small" placeholder="{{'target.select_group'|translate}}" disabled + nbTooltip="{{'target.select_group'|translate}}"> + <nb-option></nb-option> + </nb-select> + <button nbButton size="small" status="primary" disabled> + <nb-icon icon="globe-2-outline"></nb-icon>{{'general.add' | translate}} {{'TargetType.GROUP'| translate}} + </button> + + </div> + </nb-card-header> + <nb-card-body> + <!-- <ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table> --> + </nb-card-body> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/dashboard-management/target-filter/target-filter.component.scss b/src/app/pages/dashboard-management/target-filter/target-filter.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/dashboard-management/target-filter/target-filter.component.spec.ts b/src/app/pages/dashboard-management/target-filter/target-filter.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..56c5d31cf4c3d0a70a3da3bb725e5f647611cb29 --- /dev/null +++ b/src/app/pages/dashboard-management/target-filter/target-filter.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TargetFilterComponent } from './target-filter.component'; + +describe('TargetFilterComponent', () => { + let component: TargetFilterComponent; + let fixture: ComponentFixture<TargetFilterComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ TargetFilterComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TargetFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/dashboard-management/target-filter/target-filter.component.ts b/src/app/pages/dashboard-management/target-filter/target-filter.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..9f163a40e9b90f98f7674c41fc44c90ef987089e --- /dev/null +++ b/src/app/pages/dashboard-management/target-filter/target-filter.component.ts @@ -0,0 +1,405 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NbDialogService, NbToastrService } from '@nebular/theme'; +import { ConfigService } from '@ngx-config/core'; +import { TranslateService } from '@ngx-translate/core'; +import { LocalDataSource } from 'ng2-smart-table'; +import { DashboardPage } from '../../../model/dashboard-page.model'; +import { TargetType } from '../../../model/enumeration/target-type.model'; +import { IGroupRepresentation } from '../../../model/group-representation.model'; +import { IRoleRepresentation } from '../../../model/role-representation.model'; +import { ITargetAvailable } from '../../../model/target-available.model'; +import { ITarget, Target } from '../../../model/target.model'; +import { IUser } from '../../../model/user.model'; +import { DialogComponent } from '../../shared/dialog/dialog.component'; +import { TableActionsComponent } from '../../shared/table-actions/table-actions.component'; +import { TargetAvailableCrudService } from '../services/target-available-crud.service'; +import { TargetCrudService } from '../services/target-crud.service'; +import { UserCrudService } from '../services/user-crud.service'; + +@Component({ + selector: 'ngx-target-filter', + templateUrl: './target-filter.component.html', + styleUrls: ['./target-filter.component.scss'] +}) +export class TargetFilterComponent implements OnInit { + @Input() pageId: string; + @Input() disabledTarget: boolean = true; // USED TO HIDE TARGET + + //SMART TABLES + source: LocalDataSource; + settings: any; + sharedTargets: Array<ITarget>=[]; + + form:FormGroup; + toAddTarget: ITarget; + + //Available Targets + useIDM4Target: boolean; + + //(option B) Uses data from internal table (TARGET_AVAILABLE) + fullListAvailablePerson:Array<ITargetAvailable>=[]; + fullListAvailableRole:Array<ITargetAvailable>=[]; + fullListAvailableGroup:Array<ITargetAvailable>=[]; + selectedAvailablePerson: ITargetAvailable; + selectedAvailableRole: ITargetAvailable; + selectedAvailableGroup: ITargetAvailable; + + //(option A) Uses data from IDM + fullListAvailablePersonIDM:Array<IUser>=[]; + fullListAvailableRoleIDM:Array<IRoleRepresentation>=[]; + fullListAvailableGroupsIDM:Array<IGroupRepresentation>=[]; + selectedAvailablePersonIDM: IUser; + selectedAvailableRoleIDM: IRoleRepresentation; + selectedAvailableGroupIDM: IGroupRepresentation; + + constructor( + private fb: FormBuilder, + private targetService: TargetCrudService, + private configService: ConfigService, + private targetAvailableCrudService: TargetAvailableCrudService, + private userCrudService: UserCrudService, + private dialogService: NbDialogService, + private toastrService: NbToastrService, + private translateService: TranslateService) + { } + + ngOnInit(): void { + // CHANGE THIS TO READS USERS/ROLES (GROUPS) FROM IDM or INTERNAL TABLE: + // true => (option A) IDM + // false => (option B) INTERNAL TABLE (DEMO) + this.useIDM4Target = this.configService.getSettings("dashboard-controller.useIDM4Target"); + + this.initTableSettings(); + this.source = new LocalDataSource(); + this.fetchTargetTableData(); + + this.form = this.fb.group({ + selectedAvailablePerson: [this.selectedAvailablePerson, Validators.nullValidator], + selectedAvailableRole: [this.selectedAvailableRole, Validators.nullValidator], + selectedAvailableGroup: [this.selectedAvailableGroup, Validators.nullValidator], + selectedAvailablePersonIDM: [this.selectedAvailablePersonIDM, Validators.nullValidator], + selectedAvailableRoleIDM: [this.selectedAvailableRoleIDM, Validators.nullValidator], + selectedAvailableGroupIDM: [this.selectedAvailableGroupIDM, Validators.nullValidator], + }); + + if (this.useIDM4Target == true) { + //Retrieve list of Person/Roles/Groups from IDM + this.userCrudService.getIdmUsers().subscribe( res =>{ + this.fullListAvailablePersonIDM = res; + }); + this.userCrudService.getIdmRoles().subscribe( res =>{ + this.fullListAvailableRoleIDM = res; + }); + this.userCrudService.getIdmGroups().subscribe( res =>{ + this.fullListAvailableGroupsIDM = res; + }); + } else { + //Retrieve list of Person/Role/Groups from internal table + this.targetAvailableCrudService.getAvailableTargetsByType(TargetType.PERSON).subscribe( res =>{ + this.fullListAvailablePerson = res; + }); + this.targetAvailableCrudService.getAvailableTargetsByType(TargetType.ROLE).subscribe( res =>{ + this.fullListAvailableRole = res; + }); + this.targetAvailableCrudService.getAvailableTargetsByType(TargetType.GROUP).subscribe( res =>{ + this.fullListAvailableGroup = res; + }); + } + + } + + fetchTargetTableData() { + //Retrieve Targets of the selected PageId + if (this.pageId!=undefined) { + this.targetService.getTargetsByPageId(this.pageId).subscribe(data => { + this.sharedTargets=data; + this.source.load(data); + }); + } + + } + + addTargetPerson() { + console.log("# Add Person linked to dashboard with pageId =>" + this.pageId); + const person = this.useIDM4Target + ? this.form.get('selectedAvailablePersonIDM').value + : this.form.get('selectedAvailablePerson').value; + + if (person == undefined || person == null){ + this.showWarning('target.fill_person'); + return; + } + + this.toAddTarget = this.useIDM4Target + ? this.buildUserTargetIDM(person, TargetType.PERSON, Number(this.pageId)) + : this.buildTarget(person, TargetType.PERSON, Number(this.pageId)); + + this.targetService.createTarget(this.toAddTarget).subscribe( + (data: Target) => { + this.showSuccess('target.created'); + this.fetchTargetTableData(); + this.form.reset(); //Clean select person + }, + error => (this.showError(error)) + ); + } + + addTargetRole() { + console.log("# Add Role linked to dashboard with pageId =>" + this.pageId); + const role = this.useIDM4Target + ? this.form.get('selectedAvailableRoleIDM').value + : this.form.get('selectedAvailableRole').value; + + if (role == undefined || role == null){ + this.showWarning('target.fill_role'); + return; + } + + this.toAddTarget = this.useIDM4Target + ? this.buildRoleTargetIDM(role, TargetType.ROLE, Number(this.pageId)) + : this.buildTarget(role, TargetType.ROLE, Number(this.pageId)); + + this.targetService.createTarget(this.toAddTarget).subscribe( + (data: Target) => { + this.showSuccess('target.created'); + this.fetchTargetTableData(); + this.form.reset(); //Clean select person + }, + error => (this.showError(error)) + ); + } + + addTargetGroup() { + console.log("# Add Group linked to dashboard with pageId =>" + this.pageId); + const group = this.useIDM4Target + ? this.form.get('selectedAvailableGroupIDM').value + : this.form.get('selectedAvailableGroup').value; + + if (group == undefined || group == null){ + this.showWarning('target.fill_group'); + return; + } + + this.toAddTarget = this.useIDM4Target + ? this.buildGroupTargetIDM(group, TargetType.GROUP, Number(this.pageId)) + : this.buildTarget(group, TargetType.GROUP, Number(this.pageId)); + + this.targetService.createTarget(this.toAddTarget).subscribe( + (data: Target) => { + this.showSuccess('target.created'); + this.fetchTargetTableData(); + this.form.reset(); //Clean select person + }, + error => (this.showError(error)) + ); + } + + private buildTarget(target: ITargetAvailable, type: TargetType, pageId: number) { + const curTarget = new Target(); + curTarget.dashboardPage = new DashboardPage(); + curTarget.dashboardPage.id = pageId; + curTarget.type = type; + curTarget.name = target.code; + curTarget.note = target.value; + return curTarget; + } + + private buildUserTargetIDM(person: IUser, type: TargetType, pageId: number) { + const curTarget = new Target(); + curTarget.dashboardPage = new DashboardPage(); + curTarget.dashboardPage.id = pageId; + curTarget.type = type; + curTarget.name = person.username; + //curTarget.note = person.firstName + " " + person.lastName + "("+person.email+")"; + curTarget.note = person.username; + return curTarget; + } + + private buildRoleTargetIDM(role: IRoleRepresentation, type: TargetType, pageId: number) { + const curTarget = new Target(); + curTarget.dashboardPage = new DashboardPage(); + curTarget.dashboardPage.id = pageId; + curTarget.type = type; + curTarget.name = role.name; + //curTarget.note = role.description; + curTarget.note = role.name; + return curTarget; + } + + private buildGroupTargetIDM(group: IGroupRepresentation, type: TargetType, pageId: number) { + const curTarget = new Target(); + curTarget.dashboardPage = new DashboardPage(); + curTarget.dashboardPage.id = pageId; + curTarget.type = type; + curTarget.name = group.name; + curTarget.note = group.name; + return curTarget; + } + + initTableSettings(): void { + let targetTypeFilter=[]; + for(let targetType of TargetType.values()){ + targetTypeFilter.push({ value: targetType.enumVal, title: this.translateService.instant("TargetType."+targetType.enumVal) }) + } + + this.settings = { + mode: 'external', + attr: { + class: 'table table-bordered' + }, + columns: { + /*id: { + title: this.translateService.instant('target.id'), + },*/ + type: { + title: this.translateService.instant('target.type'), + valuePrepareFunction: (cell, row) => { + if(row.type!=undefined && row.type!=null) + return this.translateService.instant('TargetType.' + row.type) + else return ''; + }, + filter: { + type: 'list', + config: { + selectText: '', + list: targetTypeFilter + }, + } + + + }, + name: { + title: this.translateService.instant('target.name'), + }, + note: { + title: this.translateService.instant('target.value') + }, + /* + dashboardPage: { + title: this.translateService.instant('target.dashboardPage'), + }, */ + + actions: { + title: this.translateService.instant('general.actions'), + filter: false, + sort: false, + type: 'custom', + valuePrepareFunction: (cell, row) => { + return [ + {action:'delete', enabled:true, status:"danger", icon:"trash-2", tooltip:this.translateService.instant('general.delete')+" " + this.translateService.instant('target.title')} + ]; + }, + renderComponent: TableActionsComponent, + onComponentInitFunction: (instance) => { + instance.onCustom.subscribe(value => { + this.onCustom(value,instance.rowData); + }); + }, + } + + }, + + actions: { + add: false, + edit: false, + delete: false, + }, + + }; + } + + + onCustom(event, data) { + if (event == 'delete') { + this.dialogService + .open( DialogComponent, { context: { + title: this.translateService.instant('general.delete') + " " + this.translateService.instant('general.targets') , message: this.translateService.instant('general.deleteConfirm') } }) + .onClose.subscribe(res => { + if (res) { + this.targetService.deleteTargetById(data.id).subscribe(() => { + this.fetchTargetTableData(); + console.log(`# Target with id=${data.id} has been deleted.`), + this.showSuccess('target.deleted'); + }, + error => (this.showError(error)) + ); + } else return; + + }); + }; + } + + showSuccess(operation:string) { + this.toastrService.success(this.translateService.instant(operation), this.translateService.instant('general.success')); + } + + showError(error) { + this.toastrService.danger(error.error.message, this.translateService.instant('general.error')) + } + + showWarning(operation:string) { + this.toastrService.warning(this.translateService.instant(operation), this.translateService.instant('general.warning')); + } + + /** + * Filter to exclude from Available Person List the ones that are into the Target list + * @param persons Array<IUser> + * @returns Array<IUser> + */ + filterIDMPersons(persons: Array<IUser>) { + let result :Array<IUser> = []; + if ( this.sharedTargets != undefined && this.sharedTargets.length>0 && persons != undefined && persons.length>0){ + persons.forEach(person => { + let found = this.sharedTargets.filter(x=> x.name==person.username).map(x=>x.name); + if ( found.length == 0 ) { result.push(person) } + }); + } else { result = persons; } + return result; + } + + /** + * Filter to exclude from Available Groups List the ones that are into the Target list + * @param groups Array<IGroupRepresentation> + * @returns Array<IGroupRepresentation> + */ + filterIDMGroups(groups: Array<IGroupRepresentation>) { + let result :Array<IGroupRepresentation> = []; + if ( this.sharedTargets != undefined && this.sharedTargets.length>0 && groups != undefined && groups.length>0){ + groups.forEach(group => { + let found = this.sharedTargets.filter(x=> x.name==group.name).map(x=>x.name); + if ( found.length == 0 ) { result.push(group) } + }); + } else { result = groups; } + return result; + } + + /** + * Filter to exclude from Available Roles List the ones that are into the Target list + * @param roles Array<IRoleRepresentation> + * @returns Array<IRoleRepresentation> + */ + filterIDMRoles(roles: Array<IRoleRepresentation>) { + let result :Array<IRoleRepresentation> = []; + if ( this.sharedTargets != undefined && this.sharedTargets.length>0 && roles != undefined && roles.length>0){ + roles.forEach(role => { + let found = this.sharedTargets.filter(x=> x.name==role.name).map(x=>x.name); + if ( found.length == 0 ) { result.push(role) } + }); + } else { result = roles; } + return result; + } + + filterTarget(avTarget: Array<ITargetAvailable>, targetType: string) { + let result :Array<ITargetAvailable> = []; + const selectedType = (targetType==TargetType.GROUP ? TargetType.GROUP : (targetType==TargetType.ROLE ? TargetType.ROLE : TargetType.PERSON) ); + if ( this.sharedTargets != undefined && this.sharedTargets.length>0 && avTarget != undefined && avTarget.length>0){ + avTarget.forEach(curTarget => { + let found = this.sharedTargets.filter(x=> x.name==curTarget.code && curTarget.type == selectedType).map(x=>x.name); + if ( found.length == 0 ) { result.push(curTarget) } + }); + } else { result = avTarget; } + return result; + } + +} diff --git a/src/app/pages/dashboard-management/weather/weather.component.html b/src/app/pages/dashboard-management/weather/weather.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4351f30f1ef18b75a5c1a7ee0dab90f778aa789a --- /dev/null +++ b/src/app/pages/dashboard-management/weather/weather.component.html @@ -0,0 +1,54 @@ +<nb-card size="medium"> + <nb-card-body> + <span class="h3 location">{{weather_title}}</span> + <span class="date">{{weather_subtitle}}</span> + + <div class="today"> + <span class="today-temperature h1">Total items: 20°</span> + <nb-icon icon="sun-outline" pack="eva" class="today-icon"></nb-icon> + </div> + + <div class="today-details"> + <div class="parameter"> + <span class="caption parameter-name">max</span> + <span class="parameter-value">23°</span> + </div> + <div class="parameter"> + <span class="caption parameter-name">min</span> + <span class="parameter-value">19°</span> + </div> + <div class="parameter"> + <span class="caption parameter-name">wind</span> + <span class="parameter-value">4 km/h</span> + </div> + <div class="parameter"> + <span class="caption parameter-name">hum</span> + <span class="parameter-value">87%</span> + </div> + </div> + + <div class="weekly-forecast"> + <div class="day"> + <span class="caption">Sun</span> + <i class="weather-icon ion-ios-cloudy-outline"></i> + <span class="temperature">17°</span> + </div> + <div class="day"> + <span class="caption">Mon</span> + <i class="weather-icon ion-ios-sunny-outline"></i> + <span class="temperature">19°</span> + </div> + <div class="day"> + <span class="caption">Tue</span> + <i class="weather-icon ion-ios-rainy-outline"></i> + <span class="temperature">22°</span> + </div> + <div class="day"> + <span class="caption">Wed</span> + <i class="weather-icon ion-ios-partlysunny-outline"></i> + <span class="temperature">21°</span> + </div> + </div> + + </nb-card-body> +</nb-card> diff --git a/src/app/pages/dashboard-management/weather/weather.component.scss b/src/app/pages/dashboard-management/weather/weather.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..2ac7e09720a4ae2bb9367eefa46b25c683fb3d64 --- /dev/null +++ b/src/app/pages/dashboard-management/weather/weather.component.scss @@ -0,0 +1,76 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + + nb-card-body { + display: flex; + flex-direction: column; + } + + .location, + .date { + display: block; + } + + .location { + margin-bottom: 0.1rem; + } + + .today { + display: flex; + justify-content: space-around; + } + + .today-temperature { + display: flex; + flex-direction: column; + justify-content: center; + margin: 2rem 1.5rem; + } + + .today-icon { + color: nb-theme(color-primary-default); + font-size: 10rem; + line-height: 1; + margin-top: -4rem; + margin-left: auto; + margin-right: 0.4rem; + } + + .today-details { + display: flex; + justify-content: space-around; + margin-top: 2rem; + } + + .parameter { + flex: 1 1 auto; + text-align: center; + } + + .parameter-name, + .parameter-value { + display: block; + } + + .caption { + text-transform: uppercase; + } + + .weekly-forecast { + display: flex; + justify-content: space-around; + margin: auto 0; + } + + .day { + display: flex; + flex-direction: column; + text-align: center; + } + + .weather-icon { + font-size: 2.5rem; + line-height: 2.5rem; + } +} diff --git a/src/app/pages/dashboard-management/weather/weather.component.ts b/src/app/pages/dashboard-management/weather/weather.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..659ef4ab4e9f9283ae1c2a092d864c85f262f722 --- /dev/null +++ b/src/app/pages/dashboard-management/weather/weather.component.ts @@ -0,0 +1,19 @@ +import { Component, Input, OnInit } from '@angular/core'; + +@Component({ + selector: 'ngx-weather', + styleUrls: ['./weather.component.scss'], + templateUrl: './weather.component.html', +}) + +export class WeatherComponent implements OnInit { + + @Input() weather_title: any; + @Input() weather_subtitle: any; + + ngOnInit(): void { + //console.log("This is my weather => " + this.weather); + //this.weather = JSON.parse(this.weather); + } + +} diff --git a/src/app/pages/data-catalogue/services/data-catalogue-admin-api.service.spec.ts b/src/app/pages/data-catalogue/services/data-catalogue-admin-api.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ea1f3b437f0bb9822e86210236c45c48d08ca644 --- /dev/null +++ b/src/app/pages/data-catalogue/services/data-catalogue-admin-api.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { DataCatalogueAdminApiService } from './data-catalogue-admin-api.service'; + +describe('DataCatalogueAdminApiService', () => { + let service: DataCatalogueAdminApiService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DataCatalogueAdminApiService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/data-catalogue/services/data-catalogue-admin-api.service.ts b/src/app/pages/data-catalogue/services/data-catalogue-admin-api.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..5db056137592a4d477824c6236ee378ca8e9d76a --- /dev/null +++ b/src/app/pages/data-catalogue/services/data-catalogue-admin-api.service.ts @@ -0,0 +1,21 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ConfigService } from '@ngx-config/core'; +import { Observable } from 'rxjs'; +import { Datalet } from '../model/datalet'; + +@Injectable({ + providedIn: 'root' +}) +export class DataCatalogueAdminApiService { + private apiEndpoint; + + constructor(private config:ConfigService,private http:HttpClient) { + this.apiEndpoint=this.config.getSettings("idra_base_url"); + } + + getAllDatalets(): Observable<Array<Datalet>> { + return this.http.get<Array<Datalet>>(`${this.apiEndpoint}/Idra/api/v1/administration/datalets`); + } + +} diff --git a/src/app/pages/shared/dialog/dialog.component.html b/src/app/pages/shared/dialog/dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..78c8f5bd2c03c2435249aeed4dc85b731ba5e901 --- /dev/null +++ b/src/app/pages/shared/dialog/dialog.component.html @@ -0,0 +1,16 @@ +<nb-card> + <nb-card-header>{{title}}</nb-card-header> + <!-- <nb-card-body>{{'general.deleteConfirm'}}</nb-card-body> --> + <!-- <nb-card-body>{{'Do you want delete the selected item?'}}</nb-card-body> --> + <nb-card-body>{{message}}</nb-card-body> + <nb-card-footer> + <div class="row my-2"> + <div class="col-sm text-center"> + <button nbButton status="danger" size="small" (click)="close(true)">{{'Yes'}}</button> + </div> + <div class="col-sm text-center"> + <button nbButton status="info" size="small" (click)="close(false)">{{'No'}}</button> + </div> + </div> + </nb-card-footer> +</nb-card> \ No newline at end of file diff --git a/src/app/pages/shared/dialog/dialog.component.scss b/src/app/pages/shared/dialog/dialog.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/shared/dialog/dialog.component.spec.ts b/src/app/pages/shared/dialog/dialog.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a32e0ea6939a52db96a1582ed8b6a738def82d27 --- /dev/null +++ b/src/app/pages/shared/dialog/dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DialogComponent } from './dialog.component'; + +describe('DialogComponent', () => { + let component: DialogComponent; + let fixture: ComponentFixture<DialogComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/shared/dialog/dialog.component.ts b/src/app/pages/shared/dialog/dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc2a0c140c6a34a09fd5f6c17fd18189d4e214fb --- /dev/null +++ b/src/app/pages/shared/dialog/dialog.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from '@angular/core'; +import { NbDialogRef } from '@nebular/theme'; + +@Component({ + selector: 'ngx-dialog', + templateUrl: './dialog.component.html', + styleUrls: ['./dialog.component.scss'] +}) +export class DialogComponent implements OnInit { + + + constructor(protected dialogRef: NbDialogRef<DialogComponent>) { } + + title: string; + message: string; + + ngOnInit(): void { + } + + close(answer){ + this.dialogRef.close(answer); + } + + +} diff --git a/src/app/pages/shared/shared.module.ts b/src/app/pages/shared/shared.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..b6087c9ed4e966a22e610844f8046a4ef430b157 --- /dev/null +++ b/src/app/pages/shared/shared.module.ts @@ -0,0 +1,53 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TableActionsComponent } from '../shared/table-actions/table-actions.component'; +import { NbAccordionModule, NbActionsModule, NbAutocompleteModule, NbBadgeModule, NbButtonModule, NbCardModule, NbChatModule, NbCheckboxModule, NbDialogModule, NbFormFieldModule, NbIconModule, NbInputModule, NbListModule, NbSelectModule, NbSpinnerModule, NbTabsetModule, NbTimepickerModule, NbTooltipModule, NbTreeGridModule } from '@nebular/theme'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgxPaginationModule } from 'ngx-pagination'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { LeafletModule } from '@asymmetrik/ngx-leaflet'; +import { DialogComponent } from './dialog/dialog.component'; +import { TranslateModule } from '@ngx-translate/core'; + + + +@NgModule({ + declarations: [ + TableActionsComponent, + DialogComponent, + ], + imports: [ + CommonModule, + NbCardModule, + FormsModule, + NbFormFieldModule, + NbButtonModule, + NbInputModule, + NbListModule, + NgxPaginationModule, + NbActionsModule, + NbIconModule, + NbSpinnerModule, + NbTooltipModule, + NbAccordionModule, + NbButtonModule, + NbTabsetModule, + NbDialogModule.forChild(), + NbTreeGridModule, + Ng2SmartTableModule, + NbSelectModule, + NbAutocompleteModule, + ReactiveFormsModule, + NbTimepickerModule, + //SharedRoutingModule, + NbInputModule, + NbCheckboxModule, + NbChatModule, + LeafletModule, + NbBadgeModule, + TranslateModule + ], + exports: [DialogComponent], + entryComponents:[TableActionsComponent] +}) +export class SharedModule { } diff --git a/src/app/pages/shared/table-actions/table-actions.component.html b/src/app/pages/shared/table-actions/table-actions.component.html new file mode 100644 index 0000000000000000000000000000000000000000..63b45aca6a2ca43797543ede8297a8e93bd90a27 --- /dev/null +++ b/src/app/pages/shared/table-actions/table-actions.component.html @@ -0,0 +1,6 @@ +<div class="d-flex flex-row justify-content-around"> + <button *ngFor="let action of actions" nbButton ghost [status]="action.status" (click)="onCustomClicked(action.action)" [disabled]="!action.enabled"> + <nb-icon [icon]="action.icon" [nbTooltip]="action.tooltip"></nb-icon> + <nb-badge *ngIf="action.badge!=undefined && action.badge" text="+1" status="success" position="bottom"></nb-badge> + </button> +</div> diff --git a/src/app/pages/shared/table-actions/table-actions.component.scss b/src/app/pages/shared/table-actions/table-actions.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/pages/shared/table-actions/table-actions.component.spec.ts b/src/app/pages/shared/table-actions/table-actions.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b3ee9860db713c6385a12509a59ddda5a68f4f38 --- /dev/null +++ b/src/app/pages/shared/table-actions/table-actions.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TableActionsComponent } from './table-actions.component'; + +describe('TableActionsComponent', () => { + let component: TableActionsComponent; + let fixture: ComponentFixture<TableActionsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ TableActionsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TableActionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/shared/table-actions/table-actions.component.ts b/src/app/pages/shared/table-actions/table-actions.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..619a3ca4b53f6884712ed6b7a763d59bc910068a --- /dev/null +++ b/src/app/pages/shared/table-actions/table-actions.component.ts @@ -0,0 +1,34 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +interface Action{ + action:string; + enabled:boolean; + status:string; + icon:string; + tooltip:string; +} + +@Component({ + selector: 'ngx-table-actions', + templateUrl: './table-actions.component.html', + styleUrls: ['./table-actions.component.scss'] +}) +export class TableActionsComponent implements OnInit { + + constructor() { } + + @Input() value; + + @Output() onCustom = new EventEmitter<string>(); + + actions:Array<Action>; + + ngOnInit(): void { + this.actions=this.value; + } + + onCustomClicked(val){ + this.onCustom.emit(val); + } + +} diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json new file mode 100644 index 0000000000000000000000000000000000000000..1d44c90669bf4008dae7d688029f5b08ca034b68 --- /dev/null +++ b/src/assets/i18n/es.json @@ -0,0 +1,241 @@ +{ + "homepage": { + "homepage": "Homepage", + "welcome": "Welcome " + }, + "general": { + "success": "Success", + "error": "Error", + "warning": "Warning", + "copy": "Copy", + "manage": "Manage", + "add": "Add", + "save": "Save", + "back": "Back", + "edit": "Edit", + "drag": "Drag", + "cancel": "Cancel", + "delete": "Delete", + "created": "created", + "updated": "updated", + "reset": "Ripristina", + "actions": "Actions", + "close": "Close", + "username": "Username", + "log_in": "Login", + "log_out": "Logout", + "page": "Page", + "dashboard": "Dashboard", + "component": "Component", + "menu_items": "Menu Items", + "menu_blocks": "Menu Blocks", + "targets": "Targets", + "manage_targets": "Manage Targets", + "editContent":"Edit Content", + "deleteConfirm": "Do you want delete the selected item?", + "yes": "Yes", + "no": "No", + "mandatory_field": "Mandatory field", + "catch_error": "Error loading the results, please try again.", + "operation_failed": "Operation failed", + "datacatalogue_error": "Error in retrieving data from Data Catalogue. Please contact admin" + }, + "dashboardPage":{ + "title": "Manage Dashboard Pages", + "created": "A new Dashboard Page has been sucessfully created", + "updated": "The Dashboard Page has been sucessfully updated", + "deleted": "The Dashboard Page has been sucessfully deleted", + "succesfully_cloned": "The Dashboard Page has been sucessfully cloned", + "notFound": "No Dashboard Pages found", + "createLabel": "Create a new Dashboard Page", + "createOrEditLabel": "Create or edit a Dashboard Page", + "id": "ID", + "name": "Name", + "description": "Description", + "content": "Content", + "note": "Note", + "shared": "Published", + "createdDate": "Created Date", + "createdBy": "Created By", + "dashboardComponent": "Dashboard Component", + "menuItem": "Menu Item", + "target": "Target", + "margin": "Margin", + "drag_items": "Drag items", + "outer_margin": "Outer Margin", + "push_items": "Push Items", + "disable_push_on_drag": "Disable Push On Drag", + "swap_items": "Swap Items", + "shared_info": "Field to enable(true) or disable(false) the Dashboard publishing.", + "margin_description": "Spacing between the components", + "outer_margin_description": "Spacing around the components", + "push_items_description": "Push items when resizing and dragging", + "disable_push_on_drag_description": "Disable push on drag", + "swap_items_description": "Swap items when dragging", + "not_mandatory_field": "This field in only for SHARED Dashboard", + "checkbox_label":"Flags", + "margin_label":"Margin", + "back_modal": "Are you sure you want to go back without saving the dashboard?", + "catch_error": "Error loading the dashboard page, please try again.", + "wait_for_components": "Please wait for components to load.", + "visualization_catch_error": "Error in Dashboard page content during visualization. Please contact admin o try again." + }, + "dashboardComponent": { + "list": "Dashboard Components", + "title": "Dashboard Component", + "id": "ID", + "componentType": "Component Type", + "componentName": "Component Name", + "componentcontent": "Componentcontent", + "componentPosition": "Component Position", + "choose_file": "Choose file", + "createdDate": "Created Date", + "createdBy": "Created By", + "dashboardPage": "Dashboard Page", + "properties":"Component properties", + "selectComponentType":"Select a component type", + "deleteConfirm":"Are you sure to delete component?", + "fill_text": "Enter text...", + "fill_image": "Paste image url or base 64 image string...", + "fill_chart": "Select datalet...", + "fill_iframe": "Enter iframe content...", + "fill_map": "Enter GeoJson URL of the map...", + "fill_map_title": "Enter Title", + "fill_weather_title": "Enter Title", + "fill_weather_subtitle": "Enter Subtitle", + "fill_statistics_title": "Enter Title", + "fill_statistics_subtitle": "Enter Subtitle", + "fill_statistics_value": "Enter Value", + "fill_statistics_icon": "Enter Icon", + "select_datalet": "Select a datalet...", + "select_aspect_ratio": "Select aspect ratio", + "example_map_url": "http://localhost:4200/assets/map/2020-March-heatgeo-5minutes.json", + "label_geoJsonurl": "GeoJSON URL", + "label_title": "Title", + "label_subtitle": "Subtitle", + "label_value": "Value", + "label_icon": "Icon", + "label_content": "Content", + "label_datalet": "Datalet", + "label_image_url": "Image URL", + "label_upload_image": "Upload Image", + "upload_from_file": "Upload from file", + "upload_from_url/base64": "Upload from url/base64", + "catch_error": "Error loading the dashboard component, please try again." + }, + "target": { + "list": "Targets", + "title": "Target", + "notFound": "No Targets found", + "createLabel": "Create a new Target", + "createOrEditLabel": "Create or edit a Target", + "created": "Target has been succesfully created", + "updated": "Target has been succesfully updated", + "deleted": "Target has been succesfully deleted", + "deleteConfirm": "Are you sure you want to delete Target ?", + "id": "ID", + "type": "Type", + "name": "Name", + "note": "Note", + "value": "Value", + "fill_person":"Select person first!", + "fill_group":"Select group first!", + "fill_role":"Select role first!", + "createdDate": "Created Date", + "createdBy": "Created By", + "dashboardPage": "Dashboard Page", + "select_person":"Select Available Person", + "select_group":"Select Available Group", + "select_role":"Select Available Role", + "add_button_info": "Select a Person and/or a Role to share with him the Dashboard", + "add_button_info_disabled": "Disabled Section. To enable 'Targets' the Dashboard have to be of type SHARED" + }, + "menuBlock": { + "list": "Menu Blocks", + "title": "Menu Block", + "created": "Menu Block has been succesfully created", + "updated": "Menu Block has been succesfully updated", + "deleted": "Menu Block has been succesfully deleted", + "deleteConfirm": "Are you sure you want to delete Menu Block?", + "id": "ID", + "code": "Code", + "label": "Label", + "order": "Order", + "type": "Type", + "createdDate": "Created Date", + "createdBy": "Created By", + "menuItem": "Menu Item", + "select_menu_block":"Select a menu block", + "select_menu_block_info": "Selecting a SHARED block the Dashboard will be of type SHARED, otherwise the Dashboard will be of type PERSONAL.", + "shared": "Shared", + "delete_fk_error": "Delete failed cause this menu block is linked to more menu items.", + "type_info": "SHARED is for sharing the menu and the dashboard linked, PERSONAL is only for private visibility (not sharing)." + }, + "menuItem": { + "list": "Menu Items", + "title": "Menu Item", + "created": "Menu Item has been succesfully created", + "updated": "Menu Item has been succesfully updated", + "deleted": "Menu Item has been succesfully deleted", + "deleteConfirm": "Are you sure you want to delete Menu Item?", + "id": "ID", + "code": "Code", + "label": "Label", + "order": "Order", + "type": "Type", + "createdDate": "Created Date", + "createdBy": "Created By", + "dashboardPage": "Dashboard Page", + "menuBlock": "Menu Block", + "menu_item_label":"Menu Item label", + "add_menu_item_label": "Add the label of MenuItem", + "add_button_info": "Select a menu block and then write the menu item label to add a link to the Dashboard", + "copy_info": "Copy the Dashboard name into menu item label (to generate the link Name)" + }, + "dashboardClone":{ + "step_one": "First step", + "step_two": "Second step", + "step_three": "Third step", + "question_one": "Which Dashboard do you want to clone?", + "question_two": "Which Dashboard want to create?", + "question_three": "Do you want to proceed with cloning?", + "question_targets": "Do you want to clone targets?", + "action_clone": "Clone", + "action_prev": "Prev", + "action_next": "Next", + "action_preview": "Preview", + "clone_targets": "Clone the targets", + "summary_one": "Selected dashboard name:", + "summary_two": "Created dashboard name:", + "summary_three": "Created dashboard targets:", + "select_dashboard": "Select a dashboard", + "add_dashboard_name":"Dashboard name", + "info": "Click on the Preview button to visualize it", + "source_dashboard": "Source Dashboard", + "parameter_error": "Error: missing 1 or more parameters", + "cloned_dashboard_name": "Cloned Dashboard name", + "clone_missing_parameter": "Clone Missing Parameters" + }, + "ComponentType": { + "null": "", + "TEXT": "TEXT", + "IMAGE": "IMAGE", + "CHART": "CHART", + "IFRAME": "IFRAME", + "STATISTICS": "SUMMARY", + "MAP": "MAP", + "WEATHER": "WEATHER EXAMPLE" + }, + "MenuType": { + "null": "", + "SHARED": "SHARED", + "PERSONAL": "PERSONAL", + "OTHER": "EMPTY TYPE" + }, + "TargetType": { + "null": "", + "PERSON": "PERSON", + "GROUP": "GROUP", + "ROLE": "ROLE" + } +} \ No newline at end of file diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json new file mode 100644 index 0000000000000000000000000000000000000000..5c1a48b0c5b4e6407662d70b4b04ed4e5943ad26 --- /dev/null +++ b/src/assets/i18n/it.json @@ -0,0 +1,241 @@ +{ + "homepage": { + "homepage": "Pagina principale", + "welcome": "Benvenuto/a " + }, + "general": { + "success": "Successo", + "error": "Errore", + "copy": "Copy", + "warning": "Warning", + "manage": "Manage", + "add": "Aggiungi", + "save": "Salva", + "back": "Indietro", + "edit": "Modifica", + "cancel": "Annulla", + "delete": "Elimina", + "drag": "Trascina", + "created": "creato", + "updated": "aggiornato", + "reset": "Ripristina", + "actions": "Azioni", + "close": "Chiudi", + "username": "Nome utente", + "log_in": "Accedi", + "log_out": "Esci", + "page": "Pagina", + "dashboard": "Dashboard", + "component": "Componente", + "menu_items": "Menu Items", + "menu_blocks": "Menu Blocks", + "targets": "Destinatari", + "manage_targets": "Gestione Destinatari", + "editContent":"Modifica Contenuto", + "deleteConfirm": "Sei sicuro di eliminare l'elemento?", + "yes": "Si", + "no": "No", + "mandatory_field": "Campo obbligatorio", + "catch_error": "Errore nel caricamento dei risultati, prego riprovare", + "operation_failed": "Operazione fallita.", + "datacatalogue_error": "Error in retrieving data from Data Catalogue. Please contact admin" + }, + "dashboardPage":{ + "title": "Gestisci Dashboards", + "created": "Nuova Dashboard creata con sucesso", + "updated": "Dashboard aggiornata con sucesso", + "succesfully_cloned": "Dashboard clonata con sucesso", + "deleted": "Dashboard eliminata con sucesso", + "notFound": "Dashboard non trovata", + "createLabel": "Crea una nuova Dashboard", + "createOrEditLabel": "Crea oppure modifica una Dashboard", + "id": "ID", + "name": "Nome", + "description": "Descrizione", + "content": "Contenuto", + "note": "Note", + "shared": "Pubblicata", + "createdDate": "Data di creazione", + "createdBy": "Creato da", + "dashboardComponent": "Componente della Dashboard", + "menuItem": "Menu Item", + "target": "Destinatari", + "margin": "Margine", + "drag_items": "Drag elementi", + "outer_margin": "Margine esterno", + "push_items": "Push elementi", + "disable_push_on_drag": "Disabilita Push On Drag", + "swap_items": "Swap elementi", + "shared_info": "Field to enable(true) or disable(false) the Dashboard publishing.", + "margin_description": "Spaziatura tra i componenti", + "outer_margin_description": "Spaziatura intorno ai componenti", + "push_items_description": "Spingi gli elementi durante il ridimensionamento e il trascinamento", + "disable_push_on_drag_description": "Disabilita la spinta al trascinamento", + "swap_items_description": "Scambia gli elementi durante il trascinamento", + "not_mandatory_field": "This field in only for SHARED Dashboard", + "checkbox_label":"Opzioni", + "margin_label":"Margini", + "back_modal": "Are you sure you want to go back without saving the dashboard?", + "catch_error": "Error loading the dashboard page, please try again.", + "wait_for_components": "Please wait for components to load.", + "visualization_catch_error": "Error in Dashboard page content during visualization. Please contact admin o try again." + }, + "dashboardComponent": { + "list": "Dashboard Components", + "title": "Dashboard Component", + "id": "ID", + "componentType": "Component Type", + "componentName": "Component Name", + "componentcontent": "Componentcontent", + "componentPosition": "Component Position", + "choose_file": "Choose file", + "createdDate": "Created Date", + "createdBy": "Created By", + "dashboardPage": "Dashboard Page", + "properties":"Dettagli del componente", + "selectComponentType":"Seleziona un tipo di componente", + "deleteConfirm":"Sei sicuro di eliminare il componente?", + "fill_text": "Inserisci testo...", + "fill_image": "Inserisci immagine oppure la stringa in base 64...", + "fill_chart": "Seleziona datalet...", + "fill_iframe": "Inserisci iframe...", + "fill_map": "Inserisci URL del GeoJSON della mappa...", + "fill_map_title": "Inserisci Titolo", + "fill_weather_title": "Inserisci Titolo", + "fill_weather_subtitle": "Inserisci Sottotitolo", + "fill_statistics_title": "Inserisci Titolo", + "fill_statistics_subtitle": "Inserisci Sottotitolo", + "fill_statistics_value": "Inserisci Valore", + "fill_statistics_icon": "Seleziona icona", + "select_datalet": "Seleziona una datalet...", + "example_map_url": "http://localhost:4200/assets/map/2020-March-heatgeo-5minutes.json", + "select_aspect_ratio": "Select aspect ratio", + "label_geoJsonurl": "GeoJSON URL", + "label_title": "Titolo", + "label_subtitle": "Sottotitolo", + "label_value": "Valore", + "label_icon": "Icona", + "label_content": "Contenuto", + "label_datalet": "Datalet", + "label_image_url": "Image URL", + "label_upload_image": "Upload Image", + "upload_from_file": "Upload from file", + "upload_from_url/base64": "Upload from url/base64", + "catch_error": "Error loading the dashboard component, please try again." + }, + "target": { + "list": "Destinatari", + "title": "Destinatario", + "notFound": "Nessun Destinatario", + "createLabel": "Crea nuovo Destinatario", + "createOrEditLabel": "Crea o modifica Destinatario", + "created": "Destinatario creato con successo", + "updated": "Destinatario aggiornato con successo", + "deleted": "Destinatario eliminato con successo", + "deleteConfirm": "Sei sicuro di volere eliminare il Destinatario ?", + "id": "ID", + "type": "Tipo", + "name": "Nome", + "note": "Note", + "value": "Value", + "fill_person":"Seleziona Persona prima del click!", + "fill_group":"Seleziona un gruppo prima del click!", + "fill_role":"Seleziona un ruolo prima del click!", + "createdDate": "Data di creazione", + "createdBy": "Creato da", + "dashboardPage": "Dashboard", + "select_person":"Seleziona Persona", + "select_group":"Seleziona Gruppo", + "select_role":"Seleziona Ruolo", + "add_button_info": "Seleziona una Persona e/o un Ruolo per condividere la Dashboard", + "add_button_info_disabled": "Sezione disabilitata. Per abilitare la sezione la Dashboard deve essere di tipo SHARED" + }, + "menuBlock": { + "list": "Menu Blocks", + "title": "Menu Block", + "created": "Menu Block has been succesfully created", + "updated": "Menu Block has been succesfully updated", + "deleted": "Menu Block has been succesfully deleted", + "deleteConfirm": "Are you sure you want to delete Menu Block?", + "id": "ID", + "code": "Codice", + "label": "Etichetta", + "order": "Ordine", + "type": "Tipo", + "createdDate": "Data di creazione", + "createdBy": "Creato da", + "menuItem": "Menu Item", + "select_menu_block":"Seleziona un menu block", + "select_menu_block_info": "Selecting a SHARED block the Dashboard will be of type SHARED, otherwise the Dashboard will be of type PERSONAL.", + "shared": "Condiviso", + "delete_fk_error": "Eliminazione fallita poichè il menu block è collegato a più voci di menù.", + "type_info": "SHARED is for sharing the menu and the dashboard linked, PERSONAL is only for private visibility (not sharing)." + }, + "menuItem": { + "list": "Menu Items", + "title": "Menu Item", + "created": "Menu Item has been succesfully created", + "updated": "Menu Item has been succesfully updated", + "deleted": "Menu Item has been succesfully deleted", + "deleteConfirm": "Are you sure you want to delete Menu Item?", + "id": "ID", + "code": "Code", + "label": "Label", + "order": "Order", + "type": "Type", + "createdDate": "Created Date", + "createdBy": "Created By", + "dashboardPage": "Dashboard Page", + "menuBlock": "Menu Block", + "menu_item_label":"Menu Item label", + "add_menu_item_label": "Add the label of MenuItem", + "add_button_info": "Select a menu block and then write the menu item label to add a link to the Dashboard", + "copy_info": "Copy the Dashboard name into menu item label (to generate the link Name)" + }, + "dashboardClone":{ + "step_one": "First step", + "step_two": "Second step", + "step_three": "Third step", + "question_one": "Which Dashboard do you want to clone?", + "question_two": "Which Dashboard want to create?", + "question_three": "Do you want to proceed with cloning?", + "question_targets": "Do you want to clone targets?", + "action_clone": "Clone", + "action_prev": "Prev", + "action_next": "Next", + "action_preview": "Preview", + "clone_targets": "Clone the targets", + "summary_one": "Selected dashboard name:", + "summary_two": "Created dashboard name:", + "summary_three": "Created dashboard targets:", + "select_dashboard": "Select a dashboard", + "add_dashboard_name":"Dashboard name", + "info": "Click on the Preview button to visualize it", + "source_dashboard": "Source Dashboard", + "parameter_error": "Error: missing 1 or more parameters", + "cloned_dashboard_name": "Cloned Dashboard name", + "clone_missing_parameter": "Clone Missing Parameters" + }, + "ComponentType": { + "null": "", + "TEXT": "TESTO", + "IMAGE": "IMMAGINE", + "CHART": "GRAFICO", + "IFRAME": "IFRAME", + "STATISTICS": "SOMMARIO", + "MAP": "MAPPA", + "WEATHER": "WEATHER EXAMPLE" + }, + "MenuType": { + "null": "", + "SHARED": "CONDIVISA", + "PERSONAL": "PERSONALE", + "OTHER": "EMPTY TYPE" + }, + "TargetType": { + "null": "", + "PERSON": "PERSON", + "GROUP": "GROUP", + "ROLE": "ROLE" + } +} \ No newline at end of file diff --git a/src/assets/images/browser-outline.png b/src/assets/images/browser-outline.png new file mode 100644 index 0000000000000000000000000000000000000000..fc16168b533a053ec7758510ae65f72b53dd6217 Binary files /dev/null and b/src/assets/images/browser-outline.png differ diff --git a/src/assets/images/checkmark-outline.png b/src/assets/images/checkmark-outline.png new file mode 100644 index 0000000000000000000000000000000000000000..2f321adeb0748aad679bffd6ce22d77438fdece3 Binary files /dev/null and b/src/assets/images/checkmark-outline.png differ diff --git a/src/assets/images/close-outline.png b/src/assets/images/close-outline.png new file mode 100644 index 0000000000000000000000000000000000000000..a52c413421043b26a60f0a7e07c11fe74d608ad0 Binary files /dev/null and b/src/assets/images/close-outline.png differ diff --git a/src/assets/images/edit-outline.png b/src/assets/images/edit-outline.png new file mode 100644 index 0000000000000000000000000000000000000000..bfee63b4ca2cad6306a6c58c8f6af5731de665a5 Binary files /dev/null and b/src/assets/images/edit-outline.png differ diff --git a/src/assets/images/urbanite-spinner.gif b/src/assets/images/urbanite-spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..def5f9de57e2bc96cef100af6841e8d9e4cc3688 Binary files /dev/null and b/src/assets/images/urbanite-spinner.gif differ