Browse Source

Merge pull request #9735 from ArtemDzhereleiko/AD/imp/copy-states

Copy dashboard state
pull/9782/head
Igor Kulikov 3 years ago
committed by GitHub
parent
commit
399cbc89c7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  2. 11
      ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html
  3. 83
      ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts
  4. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

18
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -55,7 +55,7 @@ import {
} from '@app/shared/models/dashboard.models'; } from '@app/shared/models/dashboard.models';
import { WINDOW } from '@core/services/window.service'; import { WINDOW } from '@core/services/window.service';
import { WindowMessage } from '@shared/models/window-message.model'; import { WindowMessage } from '@shared/models/window-message.model';
import { deepClone, guid, isDefined, isDefinedAndNotNull, isNotEmptyStr } from '@app/core/utils'; import { deepClone, guid, isDefined, isDefinedAndNotNull, isEqual, isNotEmptyStr } from '@app/core/utils';
import { import {
DashboardContext, DashboardContext,
DashboardPageInitData, DashboardPageInitData,
@ -868,15 +868,21 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
$event.stopPropagation(); $event.stopPropagation();
} }
this.dialog.open<ManageDashboardStatesDialogComponent, ManageDashboardStatesDialogData, this.dialog.open<ManageDashboardStatesDialogComponent, ManageDashboardStatesDialogData,
{[id: string]: DashboardState }>(ManageDashboardStatesDialogComponent, { {states: {[id: string]: DashboardState}; widgets: {[id: string]: Widget}}>(ManageDashboardStatesDialogComponent, {
disableClose: true, disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
states: deepClone(this.dashboard.configuration.states) states: deepClone(this.dashboard.configuration.states),
widgets: deepClone(this.dashboard.configuration.widgets) as {[id: string]: Widget}
} }
}).afterClosed().subscribe((states) => { }).afterClosed().subscribe((result) => {
if (states) { if (result) {
this.updateStates(states); if (!isEqual(result.widgets, this.dashboard.configuration.widgets)) {
this.dashboard.configuration.widgets = result.widgets;
}
if (result.states) {
this.updateStates(result.states);
}
} }
}); });
} }

11
ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<form [formGroup]="statesFormGroup" (ngSubmit)="save()" style="min-width: 480px;"> <form [formGroup]="statesFormGroup" (ngSubmit)="save()" style="width: 750px;">
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<h2 translate>dashboard.manage-states</h2> <h2 translate>dashboard.manage-states</h2>
<span fxFlex></span> <span fxFlex></span>
@ -96,7 +96,7 @@
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<ng-container matColumnDef="actions" stickyEnd> <ng-container matColumnDef="actions" stickyEnd>
<mat-header-cell *matHeaderCellDef style="min-width: 80px; max-width: 80px; width: 80px"> <mat-header-cell *matHeaderCellDef style="min-width: 120px; max-width: 120px; width: 120px">
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let state"> <mat-cell *matCellDef="let state">
<div fxFlex fxLayout="row"> <div fxFlex fxLayout="row">
@ -107,6 +107,13 @@
(click)="editState($event, state)"> (click)="editState($event, state)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button mat-icon-button [disabled]="isLoading$ | async"
type="button"
matTooltip="{{ 'dashboard.duplicate-state-action' | translate }}"
matTooltipPosition="above"
(click)="duplicateState($event, state)">
<mat-icon>content_copy</mat-icon>
</button>
<button [fxShow]="!state.root" mat-icon-button [disabled]="isLoading$ | async" <button [fxShow]="!state.root" mat-icon-button [disabled]="isLoading$ | async"
type="button" type="button"
matTooltip="{{ 'dashboard.delete-state' | translate }}" matTooltip="{{ 'dashboard.delete-state' | translate }}"

83
ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts

@ -19,7 +19,7 @@ import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDirective, NgForm } from '@angular/forms'; import { FormGroupDirective, NgForm, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component'; import { DialogComponent } from '@app/shared/components/dialog.component';
import { DashboardState } from '@app/shared/models/dashboard.models'; import { DashboardState } from '@app/shared/models/dashboard.models';
@ -35,14 +35,17 @@ import { fromEvent, merge } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { deepClone, isUndefined } from '@core/utils'; import { deepClone, isDefined } from '@core/utils';
import { import {
DashboardStateDialogComponent, DashboardStateDialogComponent,
DashboardStateDialogData DashboardStateDialogData
} from '@home/components/dashboard-page/states/dashboard-state-dialog.component'; } from '@home/components/dashboard-page/states/dashboard-state-dialog.component';
import { UtilsService } from '@core/services/utils.service';
import { Widget } from '@shared/models/widget.models';
export interface ManageDashboardStatesDialogData { export interface ManageDashboardStatesDialogData {
states: {[id: string]: DashboardState }; states: {[id: string]: DashboardState };
widgets: {[id: string]: Widget };
} }
@Component({ @Component({
@ -51,13 +54,14 @@ export interface ManageDashboardStatesDialogData {
providers: [{provide: ErrorStateMatcher, useExisting: ManageDashboardStatesDialogComponent}], providers: [{provide: ErrorStateMatcher, useExisting: ManageDashboardStatesDialogComponent}],
styleUrls: ['./manage-dashboard-states-dialog.component.scss'] styleUrls: ['./manage-dashboard-states-dialog.component.scss']
}) })
export class ManageDashboardStatesDialogComponent extends export class ManageDashboardStatesDialogComponent
DialogComponent<ManageDashboardStatesDialogComponent, {[id: string]: DashboardState }> extends DialogComponent<ManageDashboardStatesDialogComponent, {states: {[id: string]: DashboardState}; widgets: {[id: string]: Widget}}>
implements OnInit, ErrorStateMatcher, AfterViewInit { implements OnInit, ErrorStateMatcher, AfterViewInit {
statesFormGroup: UntypedFormGroup; statesFormGroup: UntypedFormGroup;
states: {[id: string]: DashboardState }; states: {[id: string]: DashboardState };
widgets: {[id: string]: Widget};
displayedColumns: string[]; displayedColumns: string[];
pageLink: PageLink; pageLink: PageLink;
@ -66,6 +70,8 @@ export class ManageDashboardStatesDialogComponent extends
submitted = false; submitted = false;
stateNames: Set<string> = new Set<string>();
@ViewChild('searchInput') searchInputField: ElementRef; @ViewChild('searchInput') searchInputField: ElementRef;
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ -75,15 +81,19 @@ export class ManageDashboardStatesDialogComponent extends
protected router: Router, protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: ManageDashboardStatesDialogData, @Inject(MAT_DIALOG_DATA) public data: ManageDashboardStatesDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<ManageDashboardStatesDialogComponent, {[id: string]: DashboardState }>, public dialogRef: MatDialogRef<ManageDashboardStatesDialogComponent,
{states: {[id: string]: DashboardState}; widgets: {[id: string]: Widget}}>,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private translate: TranslateService, private translate: TranslateService,
private dialogs: DialogService, private dialogs: DialogService,
private utils: UtilsService,
private dialog: MatDialog) { private dialog: MatDialog) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.states = this.data.states; this.states = this.data.states;
this.widgets = this.data.widgets;
this.statesFormGroup = this.fb.group({}); this.statesFormGroup = this.fb.group({});
Object.values(this.states).forEach(value => this.stateNames.add(value.name));
const sortOrder: SortOrder = { property: 'name', direction: Direction.ASC }; const sortOrder: SortOrder = { property: 'name', direction: Direction.ASC };
this.pageLink = new PageLink(5, 0, null, sortOrder); this.pageLink = new PageLink(5, 0, null, sortOrder);
@ -142,6 +152,7 @@ export class ManageDashboardStatesDialogComponent extends
this.translate.instant('action.yes')).subscribe( this.translate.instant('action.yes')).subscribe(
(res) => { (res) => {
if (res) { if (res) {
this.stateNames.delete(state.name);
delete this.states[state.id]; delete this.states[state.id];
this.onStatesUpdated(); this.onStatesUpdated();
} }
@ -171,8 +182,10 @@ export class ManageDashboardStatesDialogComponent extends
} }
const isAdd = state === null; const isAdd = state === null;
let prevStateId = null; let prevStateId = null;
let prevStateName = '';
if (!isAdd) { if (!isAdd) {
prevStateId = state.id; prevStateId = state.id;
prevStateName = state.name;
} }
this.dialog.open<DashboardStateDialogComponent, DashboardStateDialogData, this.dialog.open<DashboardStateDialogComponent, DashboardStateDialogData,
DashboardStateInfo>(DashboardStateDialogComponent, { DashboardStateInfo>(DashboardStateDialogComponent, {
@ -186,13 +199,13 @@ export class ManageDashboardStatesDialogComponent extends
}).afterClosed().subscribe( }).afterClosed().subscribe(
(res) => { (res) => {
if (res) { if (res) {
this.saveState(res, prevStateId); this.saveState(res, prevStateId, prevStateName);
} }
} }
); );
} }
saveState(state: DashboardStateInfo, prevStateId: string) { saveState(state: DashboardStateInfo, prevStateId: string, prevStateName: string) {
const newState: DashboardState = { const newState: DashboardState = {
name: state.name, name: state.name,
root: state.root, root: state.root,
@ -204,6 +217,10 @@ export class ManageDashboardStatesDialogComponent extends
} else { } else {
this.states[state.id] = newState; this.states[state.id] = newState;
} }
if (prevStateName && prevStateName !== state.name) {
this.stateNames.delete(prevStateName);
this.stateNames.add(state.name);
}
if (state.root) { if (state.root) {
for (const id of Object.keys(this.states)) { for (const id of Object.keys(this.states)) {
const otherState = this.states[id]; const otherState = this.states[id];
@ -228,6 +245,56 @@ export class ManageDashboardStatesDialogComponent extends
this.onStatesUpdated(); this.onStatesUpdated();
} }
duplicateState($event: Event, state: DashboardStateInfo) {
const originalState = state;
const newStateName = this.getNextDuplicatedName(state.name);
if (newStateName) {
const duplicatedStates = deepClone(originalState);
const duplicatedWidgets = deepClone(this.widgets);
const mainWidgets = {};
const rightWidgets = {};
duplicatedStates.id = newStateName.toLowerCase().replace(/\W/g, '_');
duplicatedStates.name = newStateName;
duplicatedStates.root = false;
this.stateNames.add(duplicatedStates.name);
for (const [key, value] of Object.entries(duplicatedStates.layouts.main.widgets)) {
const guid = this.utils.guid();
mainWidgets[guid] = value;
duplicatedWidgets[guid] = this.widgets[key];
duplicatedWidgets[guid].id = guid;
}
duplicatedStates.layouts.main.widgets = mainWidgets;
if (isDefined(duplicatedStates.layouts?.right)) {
for (const [key, value] of Object.entries(duplicatedStates.layouts.right.widgets)) {
const guid = this.utils.guid();
rightWidgets[guid] = value;
duplicatedWidgets[guid] = this.widgets[key];
duplicatedWidgets[guid].id = guid;
}
duplicatedStates.layouts.right.widgets = rightWidgets;
}
this.states[duplicatedStates.id] = duplicatedStates;
this.widgets = duplicatedWidgets;
this.onStatesUpdated();
}
}
private getNextDuplicatedName(stateName: string): string {
const suffix = ` - ${this.translate.instant('action.copy')} `;
let counter = 0;
while (++counter < Number.MAX_SAFE_INTEGER) {
const newName = `${stateName}${suffix}${counter}`;
if (!this.stateNames.has(newName)) {
return newName;
}
}
return null;
}
private onStatesUpdated() { private onStatesUpdated() {
this.statesFormGroup.markAsDirty(); this.statesFormGroup.markAsDirty();
this.updateData(true); this.updateData(true);
@ -245,6 +312,6 @@ export class ManageDashboardStatesDialogComponent extends
save(): void { save(): void {
this.submitted = true; this.submitted = true;
this.dialogRef.close(this.states); this.dialogRef.close({ states: this.states, widgets: this.widgets });
} }
} }

3
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1191,7 +1191,8 @@
"assign-dashboard-to-edge": "Assign Dashboard(s) To Edge", "assign-dashboard-to-edge": "Assign Dashboard(s) To Edge",
"assign-dashboard-to-edge-text": "Please select the dashboards to assign to the edge", "assign-dashboard-to-edge-text": "Please select the dashboards to assign to the edge",
"non-existent-dashboard-state-error": "Dashboard state with id \"{{ stateId }}\" is not found", "non-existent-dashboard-state-error": "Dashboard state with id \"{{ stateId }}\" is not found",
"edit-mode": "Edit mode" "edit-mode": "Edit mode",
"duplicate-state-action": "Duplicate state"
}, },
"datakey": { "datakey": {
"settings": "Settings", "settings": "Settings",

Loading…
Cancel
Save