Files
perfdb/pdb-js/src/app/dashboard-page/dashboard/dashboard.component.ts

251 lines
8.2 KiB
TypeScript

import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { HttpErrorResponse } from '@angular/common/http';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute } from '@angular/router';
import { Dashboard, DashboardCreationData, DashboardService, PlotWidget, PlotWidgetRenderData, TextWidget } from 'src/app/dashboard.service';
import { PlotConfig, PlotResponse, PlotService } from 'src/app/plot.service';
import { NewDashboardComponent } from '../new-dashboard/new-dashboard.component';
import { AddPlotDialogComponent } from './add-plot-dialog/add-plot-dialog.component';
import { AddTextDialogComponent } from './add-text-dialog/add-text-dialog.component';
import { DatePickerChange, DatePickerComponent } from 'src/app/components/datepicker/date-picker.component';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html'
})
export class DashboardComponent implements OnInit{
dashboard?: Dashboard = undefined;
pristineDashboardJSON?: string = undefined;
error = "";
plotWidgetRenderData: PlotWidgetRenderData[] = [];
@ViewChild("datePicker")
datePicker!: DatePickerComponent;
constructor(
private route: ActivatedRoute,
private service: DashboardService,
private dialog: MatDialog,
private snackBar: MatSnackBar,
private plotService: PlotService,
private element: ElementRef) {}
ngOnInit(): void {
this.service.getDashboard(<string>this.route.snapshot.paramMap.get("id")).subscribe({
'next':(dashboard: Dashboard) => {
this.dashboard = dashboard;
this.pristineDashboardJSON = JSON.stringify(dashboard);
this.repairArrangement();
dashboard.plots.forEach(p => {
const submitterId = (<any>window).submitterId + (<any>window).randomId();
this.plotWidgetRenderData.push(new PlotWidgetRenderData(p, submitterId));
});
this.loadImages(0, this.plotWidgetRenderData);
},
'error': (error: HttpErrorResponse) =>{
if (error.status == 404) {
this.error = "Not Found";
}else if (error.status == 504) { // gateway timeout
this.error = "Server Unreachable";
}else{
this.error = "Failed to load dashboard";
}
}
});
}
updateDateRange(e: DatePickerChange) {
this.plotWidgetRenderData.forEach(r => {
r.widget.config.dateRange = e.value;
});
this.loadImages(0, this.plotWidgetRenderData);
}
isDirty() {
return this.pristineDashboardJSON !== JSON.stringify(this.dashboard);
}
loadImages(index: number, plotWidgetQueue: PlotWidgetRenderData[]) {
if (index < plotWidgetQueue.length){
const plot = plotWidgetQueue[index];
if (plot.isAborted) {
this.loadImages(index +1 , plotWidgetQueue);
}else{
plot.plotResponse = undefined; // remove old image and show loading icon
const request = PlotWidget.createPlotRequest(plot.widget, plot.submitterId);
this.plotService.sendPlotRequest(request).subscribe({
next: (response: PlotResponse)=> {
plot.plotResponse = response;
},
error: (error:any)=> {
plot.error = error;
this.loadImages(index +1 , plotWidgetQueue);
},
complete: () => {
this.loadImages(index +1 , plotWidgetQueue);
}
});
}
}
}
private repairArrangement(){
const arrangement = this.dashboard!.arrangement || [];
for (let i = 0; i < 2; i++){
arrangement[i] = arrangement[i] ?? [] ;
}
this.dashboard?.texts.forEach(t => {
if (!this.arrangmentContainsId(arrangement, t.id)){
arrangement[0].push(t.id);
}
});
this.dashboard?.plots.forEach(t => {
if (!this.arrangmentContainsId(arrangement, t.id)){
arrangement[0].push(t.id);
}
});
this.dashboard!.arrangement = arrangement;
}
private arrangmentContainsId(arrangement: string[][], id: string): boolean{
for ( let i = 0; i < arrangement.length; i++){
if (arrangement[i].includes(id)) {
return true;
}
}
return false;
}
addText() {
this.dialog.open(AddTextDialogComponent,{
data: {text:""},
width: '800px'
}).afterClosed().subscribe((text: string) => {
const widget = new TextWidget((<any>window).randomId(),'MEDIUM', text);
this.dashboard!.texts.push(widget);
this.dashboard!.arrangement[0].push(widget.id);
});
}
addPlot() {
this.dialog.open(AddPlotDialogComponent,{
data: {},
width: 'calc(100% - 1em)',
height: 'calc(100% - 1em)'
}).afterClosed().subscribe((config: PlotConfig | "") => {
if (config != "" && config.query.length > 0) {
const widget = new PlotWidget((<any>window).randomId(), 'MEDIUM', config);
this.dashboard!.plots.push(widget);
this.dashboard!.arrangement[0].push(widget.id);
this.plotWidgetRenderData.push(new PlotWidgetRenderData(widget, (<any>window).randomId()));
this.loadImages(this.plotWidgetRenderData.length-1, this.plotWidgetRenderData);
}
});
}
save() {
const arrangement = <string[][]>[];
const dashboardColumns = (<HTMLElement>this.element.nativeElement).querySelectorAll('.dashboard-column');
for(let i =0; i < dashboardColumns.length; i++){
const ids = [];
const column = <HTMLDivElement>dashboardColumns.item(i);
for(let c = 0; c <column.children.length; c++) {
const element = <Element>column.children.item(c)
const id = element!.getAttribute("widget-id");
if (id !== null) {
ids.push(id);
}
}
arrangement.push(ids);
}
this.dashboard!.arrangement = arrangement;
this.service.saveDashboard(this.dashboard!).subscribe({
'complete': () => {
const successMessages = [
"dashboard saved",
"saving the dashboard was a complete success",
"dashboard state securely stored",
"the save was successful",
"done",
"success",
"saved"
];
const randomMessage = successMessages[Math.floor(Math.random()*successMessages.length)];
this.snackBar.open(randomMessage,"", {
duration: 5000,
verticalPosition: 'top'
});
this.pristineDashboardJSON = JSON.stringify(this.dashboard);
}
});
}
editNameAndDescription() {
const dialogRef = this.dialog.open(NewDashboardComponent, {
data: {name: this.dashboard!.name, description: this.dashboard!.description},
hasBackdrop: true
});
dialogRef.afterClosed().subscribe((result?: DashboardCreationData) => {
if (result) {
this.dashboard!.name = result.name;
this.dashboard!.description = result.description;
}
});
}
isTextWidget(id: string): boolean {
return this.getTextWidget(id) !== undefined;
}
isPlotWidget(id: string): boolean {
return this.dashboard?.plots.find( x => x.id == id) !== undefined;
}
getTextWidget(id: string): TextWidget | undefined {
return this.dashboard?.texts.find( x => x.id == id);
}
getPlotWidget(id: string): PlotWidgetRenderData | undefined {
return this.plotWidgetRenderData.find( x => x.widget.id == id);
}
drop(event: CdkDragDrop<number>) {
if (event.previousContainer === event.container) {
moveItemInArray(this.dashboard!.arrangement[event.container.data], event.previousIndex, event.currentIndex);
} else {
window.console.log("from ",event.previousContainer.data, " to ", event.container.data);
transferArrayItem(
this.dashboard!.arrangement[event.previousContainer.data],
this.dashboard!.arrangement[event.container.data],
event.previousIndex,
event.currentIndex,
);
}
}
delete(dashboardId: string) {
this.dashboard!.arrangement[0] = this.dashboard!.arrangement[0].filter(a => a != dashboardId);
this.dashboard!.plots = this.dashboard!.plots.filter(p => p.id != dashboardId);
this.dashboard!.texts = this.dashboard!.texts.filter(t => t.id != dashboardId);
}
}