import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; import { HttpErrorResponse } from '@angular/common/http'; import { Component, ElementRef, OnInit } 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'; @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html' }) export class DashboardComponent implements OnInit { dashboard?: Dashboard = undefined; pristineDashboardJSON?: string = undefined; error = ""; plotWidgetRenderData: PlotWidgetRenderData[] = []; constructor( private route: ActivatedRoute, private service: DashboardService, private dialog: MatDialog, private snackBar: MatSnackBar, private plotService: PlotService, private element: ElementRef) {} ngOnInit(): void { this.service.getDashboard(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 = (window).submitterId + (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"; } } }); } 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{ 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 || []; if (arrangement.length == 0){ arrangement[0] = []; } 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: '600px' }).afterClosed().subscribe((text: string) => { const widget = new TextWidget((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((window).randomId(), 'MEDIUM', config); this.dashboard!.plots.push(widget); this.dashboard!.arrangement[0].push(widget.id); this.plotWidgetRenderData.push(new PlotWidgetRenderData(widget, (window).randomId())); this.loadImages(this.plotWidgetRenderData.length-1, this.plotWidgetRenderData); } }); } save() { const arrangement = []; const dashboardColumns = (this.element.nativeElement).querySelectorAll('.dashboard-column'); for(let i =0; i < dashboardColumns.length; i++){ const ids = []; const column = dashboardColumns.item(i); for(let c = 0; c 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) { if (event.previousContainer === event.container) { moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); } else { transferArrayItem( event.previousContainer.data, 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); } }