|
|
@ -14,21 +14,10 @@ |
|
|
/// limitations under the License.
|
|
|
/// limitations under the License.
|
|
|
///
|
|
|
///
|
|
|
|
|
|
|
|
|
import { |
|
|
import { AfterViewInit, Component, ElementRef, Inject, OnInit, SecurityContext, ViewChild } from '@angular/core'; |
|
|
AfterViewInit, |
|
|
|
|
|
Component, |
|
|
|
|
|
ElementRef, |
|
|
|
|
|
Inject, |
|
|
|
|
|
OnInit, |
|
|
|
|
|
SecurityContext, |
|
|
|
|
|
SkipSelf, |
|
|
|
|
|
ViewChild |
|
|
|
|
|
} from '@angular/core'; |
|
|
|
|
|
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 { 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'; |
|
|
@ -44,7 +33,7 @@ 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, isDefined } from '@core/utils'; |
|
|
import { deepClone, isDefined, isEqual } from '@core/utils'; |
|
|
import { |
|
|
import { |
|
|
DashboardStateDialogComponent, |
|
|
DashboardStateDialogComponent, |
|
|
DashboardStateDialogData |
|
|
DashboardStateDialogData |
|
|
@ -58,42 +47,42 @@ export interface ManageDashboardStatesDialogData { |
|
|
widgets: {[id: string]: Widget }; |
|
|
widgets: {[id: string]: Widget }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export interface ManageDashboardStatesDialogResult { |
|
|
|
|
|
states: {[id: string]: DashboardState }; |
|
|
|
|
|
addWidgets?: {[id: string]: Widget }; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@Component({ |
|
|
@Component({ |
|
|
selector: 'tb-manage-dashboard-states-dialog', |
|
|
selector: 'tb-manage-dashboard-states-dialog', |
|
|
templateUrl: './manage-dashboard-states-dialog.component.html', |
|
|
templateUrl: './manage-dashboard-states-dialog.component.html', |
|
|
providers: [{provide: ErrorStateMatcher, useExisting: ManageDashboardStatesDialogComponent}], |
|
|
|
|
|
styleUrls: ['./manage-dashboard-states-dialog.component.scss'] |
|
|
styleUrls: ['./manage-dashboard-states-dialog.component.scss'] |
|
|
}) |
|
|
}) |
|
|
export class ManageDashboardStatesDialogComponent |
|
|
export class ManageDashboardStatesDialogComponent |
|
|
extends DialogComponent<ManageDashboardStatesDialogComponent, {states: {[id: string]: DashboardState}; widgets: {[id: string]: Widget}}> |
|
|
extends DialogComponent<ManageDashboardStatesDialogComponent, ManageDashboardStatesDialogResult> |
|
|
implements OnInit, ErrorStateMatcher, AfterViewInit { |
|
|
implements OnInit, AfterViewInit { |
|
|
|
|
|
|
|
|
statesFormGroup: UntypedFormGroup; |
|
|
@ViewChild('searchInput', {static: false}) searchInputField: ElementRef; |
|
|
|
|
|
|
|
|
states: {[id: string]: DashboardState }; |
|
|
@ViewChild(MatPaginator, {static: false}) paginator: MatPaginator; |
|
|
widgets: {[id: string]: Widget}; |
|
|
@ViewChild(MatSort, {static: false}) sort: MatSort; |
|
|
|
|
|
|
|
|
|
|
|
isDirty = false; |
|
|
|
|
|
|
|
|
displayedColumns: string[]; |
|
|
displayedColumns: string[]; |
|
|
pageLink: PageLink; |
|
|
pageLink: PageLink; |
|
|
textSearchMode = false; |
|
|
textSearchMode = false; |
|
|
dataSource: DashboardStatesDatasource; |
|
|
dataSource: DashboardStatesDatasource; |
|
|
|
|
|
|
|
|
submitted = false; |
|
|
private states: {[id: string]: DashboardState }; |
|
|
|
|
|
private widgets: {[id: string]: Widget}; |
|
|
|
|
|
|
|
|
stateNames: Set<string> = new Set<string>(); |
|
|
private stateNames: Set<string> = new Set<string>(); |
|
|
|
|
|
private addWidgets: {[id: string]: Widget} = {}; |
|
|
@ViewChild('searchInput') searchInputField: ElementRef; |
|
|
|
|
|
|
|
|
|
|
|
@ViewChild(MatPaginator) paginator: MatPaginator; |
|
|
|
|
|
@ViewChild(MatSort) sort: MatSort; |
|
|
|
|
|
|
|
|
|
|
|
constructor(protected store: Store<AppState>, |
|
|
constructor(protected store: Store<AppState>, |
|
|
protected router: Router, |
|
|
protected router: Router, |
|
|
@Inject(MAT_DIALOG_DATA) public data: ManageDashboardStatesDialogData, |
|
|
@Inject(MAT_DIALOG_DATA) public data: ManageDashboardStatesDialogData, |
|
|
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, |
|
|
public dialogRef: MatDialogRef<ManageDashboardStatesDialogComponent, ManageDashboardStatesDialogResult>, |
|
|
public dialogRef: MatDialogRef<ManageDashboardStatesDialogComponent, |
|
|
|
|
|
{states: {[id: string]: DashboardState}; widgets: {[id: string]: Widget}}>, |
|
|
|
|
|
private fb: UntypedFormBuilder, |
|
|
|
|
|
private translate: TranslateService, |
|
|
private translate: TranslateService, |
|
|
private dialogs: DialogService, |
|
|
private dialogs: DialogService, |
|
|
private utils: UtilsService, |
|
|
private utils: UtilsService, |
|
|
@ -103,7 +92,6 @@ export class ManageDashboardStatesDialogComponent |
|
|
|
|
|
|
|
|
this.states = this.data.states; |
|
|
this.states = this.data.states; |
|
|
this.widgets = this.data.widgets; |
|
|
this.widgets = this.data.widgets; |
|
|
this.statesFormGroup = this.fb.group({}); |
|
|
|
|
|
Object.values(this.states).forEach(value => this.stateNames.add(value.name)); |
|
|
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 }; |
|
|
@ -258,77 +246,65 @@ export class ManageDashboardStatesDialogComponent |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
duplicateState($event: Event, state: DashboardStateInfo) { |
|
|
duplicateState($event: Event, state: DashboardStateInfo) { |
|
|
const originalState = state; |
|
|
const suffix = ` - ${this.translate.instant('action.copy')} `; |
|
|
const newStateName = this.getNextDuplicatedName(state.name); |
|
|
let counter = 0; |
|
|
if (newStateName) { |
|
|
const maxAttempts = 1000; |
|
|
const newStateId = newStateName.toLowerCase().replace(/\W/g, '_'); |
|
|
|
|
|
if (this.states[newStateId]) { |
|
|
while (counter++ < maxAttempts) { |
|
|
this.stateNames.add(newStateName); |
|
|
const candidateName = `${state.name}${suffix}${counter}`; |
|
|
this.duplicateState(null, state); |
|
|
if (this.stateNames.has(candidateName)) continue; |
|
|
|
|
|
|
|
|
|
|
|
const candidateId = candidateName.toLowerCase().replace(/\W/g, '_'); |
|
|
|
|
|
if (this.states[candidateId]) { |
|
|
|
|
|
continue; |
|
|
} |
|
|
} |
|
|
const duplicatedStates = deepClone(originalState); |
|
|
|
|
|
const duplicatedWidgets = deepClone(this.widgets); |
|
|
const duplicatedState = deepClone(state); |
|
|
const mainWidgets = {}; |
|
|
const mainWidgets = {}; |
|
|
const rightWidgets = {}; |
|
|
const rightWidgets = {}; |
|
|
duplicatedStates.id = newStateId; |
|
|
duplicatedState.id = candidateId; |
|
|
duplicatedStates.name = newStateName; |
|
|
duplicatedState.name = candidateName; |
|
|
duplicatedStates.root = false; |
|
|
duplicatedState.root = false; |
|
|
this.stateNames.add(duplicatedStates.name); |
|
|
this.stateNames.add(duplicatedState.name); |
|
|
|
|
|
|
|
|
for (const [key, value] of Object.entries(duplicatedStates.layouts.main.widgets)) { |
|
|
for (const [key, value] of Object.entries(duplicatedState.layouts.main.widgets)) { |
|
|
const guid = this.utils.guid(); |
|
|
const guid = this.utils.guid(); |
|
|
mainWidgets[guid] = value; |
|
|
mainWidgets[guid] = value; |
|
|
duplicatedWidgets[guid] = this.widgets[key]; |
|
|
this.addWidgets[guid] = deepClone(this.widgets[key] ?? this.addWidgets[key]); |
|
|
duplicatedWidgets[guid].id = guid; |
|
|
this.addWidgets[guid].id = guid; |
|
|
} |
|
|
} |
|
|
duplicatedStates.layouts.main.widgets = mainWidgets; |
|
|
duplicatedState.layouts.main.widgets = mainWidgets; |
|
|
|
|
|
|
|
|
if (isDefined(duplicatedStates.layouts?.right)) { |
|
|
if (isDefined(duplicatedState.layouts?.right)) { |
|
|
for (const [key, value] of Object.entries(duplicatedStates.layouts.right.widgets)) { |
|
|
for (const [key, value] of Object.entries(duplicatedState.layouts.right.widgets)) { |
|
|
const guid = this.utils.guid(); |
|
|
const guid = this.utils.guid(); |
|
|
rightWidgets[guid] = value; |
|
|
rightWidgets[guid] = value; |
|
|
duplicatedWidgets[guid] = this.widgets[key]; |
|
|
this.addWidgets[guid] = deepClone(this.widgets[key] ?? this.addWidgets[key]); |
|
|
duplicatedWidgets[guid].id = guid; |
|
|
this.addWidgets[guid].id = guid; |
|
|
} |
|
|
} |
|
|
duplicatedStates.layouts.right.widgets = rightWidgets; |
|
|
duplicatedState.layouts.right.widgets = rightWidgets; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.states[duplicatedStates.id] = duplicatedStates; |
|
|
this.states[duplicatedState.id] = duplicatedState; |
|
|
this.widgets = duplicatedWidgets; |
|
|
|
|
|
this.onStatesUpdated(); |
|
|
this.onStatesUpdated(); |
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
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.isDirty = true; |
|
|
this.updateData(true); |
|
|
this.updateData(true); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { |
|
|
|
|
|
const originalErrorState = this.errorStateMatcher.isErrorState(control, form); |
|
|
|
|
|
const customErrorState = !!(control && control.invalid && this.submitted); |
|
|
|
|
|
return originalErrorState || customErrorState; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cancel(): void { |
|
|
cancel(): void { |
|
|
this.dialogRef.close(null); |
|
|
this.dialogRef.close(null); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
save(): void { |
|
|
save(): void { |
|
|
this.submitted = true; |
|
|
const result: ManageDashboardStatesDialogResult = {states: this.states}; |
|
|
this.dialogRef.close({ states: this.states, widgets: this.widgets }); |
|
|
if (!isEqual(this.addWidgets, {})) { |
|
|
|
|
|
result.addWidgets = this.addWidgets; |
|
|
|
|
|
} |
|
|
|
|
|
this.dialogRef.close(result); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|