edit plots

This commit is contained in:
2023-03-12 08:24:16 +01:00
parent b56b4c231e
commit b5028e03be
19 changed files with 222 additions and 97 deletions

2
pdb-js/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -1,14 +1,8 @@
<style>
:host {
/*
height: calc(100% - 29px);
*/
width: 100%;
position: absolute;
padding: 0.5em;
}
.center {
position: absolute;
top: 50%;

View File

@@ -14,9 +14,9 @@
max-height: unset;
}
</style>
<h1 mat-dialog-title>Add Plot</h1>
<h1 mat-dialog-title>{{data.title}}</h1>
<pdb-visualization-page mat-dialog-content #plot></pdb-visualization-page>
<pdb-visualization-page mat-dialog-content #plot [defaultConfig]="data.config" [galleryEnabled]="false"></pdb-visualization-page>
<div mat-dialog-actions align="end">
<button mat-button mat-dialog-close >Cancel</button>

View File

@@ -1,17 +1,22 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { AfterViewInit, Component, ElementRef, Inject, ViewChild } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { PlotConfig } from 'src/app/plot.service';
import { VisualizationPageComponent } from 'src/app/visualization-page/visualization-page.component';
@Component({
selector: 'app-add-plot-dialog',
templateUrl: './add-plot-dialog.component.html',
styleUrls: ['./add-plot-dialog.component.scss']
templateUrl: './add-plot-dialog.component.html'
})
export class AddPlotDialogComponent {
@ViewChild("plot") plotElement! :VisualizationPageComponent;
constructor(public dialogRef: MatDialogRef<string>){
constructor(
public dialogRef: MatDialogRef<PlotConfig | undefined>,
@Inject(MAT_DIALOG_DATA) public data: {config: PlotConfig, title: string}
){
}
onSaveClick(): void {

View File

@@ -58,7 +58,7 @@ export class DashboardComponent implements OnInit {
if (index < plotWidgetQueue.length){
const plot = plotWidgetQueue[index];
const request = this.createPlotRequest(plot.widget)
const request = PlotWidget.createPlotRequest(plot.widget);
this.plotService.sendPlotRequest(request).subscribe({
next: (response: PlotResponse)=> {
plot.plotResponse= response;
@@ -71,7 +71,7 @@ export class DashboardComponent implements OnInit {
}
}
/*
createPlotRequest(plotWidget: PlotWidget): PlotRequest {
const height = this.height(plotWidget.size);
@@ -111,6 +111,7 @@ export class DashboardComponent implements OnInit {
return 900;
}
}
*/
private repairArrangement(){
const arrangement = this.dashboard!.arrangement || [];
@@ -153,6 +154,7 @@ export class DashboardComponent implements OnInit {
addPlot() {
this.dialog.open(AddPlotDialogComponent,{
data: {title: "Add Plot"},
width: 'calc(100% - 1em)',
height: 'calc(100% - 1em)'
}).afterClosed().subscribe((config: PlotConfig | "") => {

View File

@@ -3,7 +3,7 @@
float: left;
}
.dashboard-card {
border: solid 1px red;
position: relative;
display: flex;
justify-content: center;
align-items: center;
@@ -19,8 +19,23 @@
img {
cursor: zoom-in;
}
.editable-hovered {
position: absolute;
right: 0;
top: 0;
}
.dashboard-card .editable-hovered {
visibility: hidden;
}
.dashboard-card:hover .editable-hovered {
visibility: visible;
}
</style>
<div class="dashboard-card" [ngClass]="{'size-medium' : true}">
<button mat-icon-button (click)="edit()" class="editable-hovered"><img src="/assets/img/edit-outline.svg"/></button>
<mat-spinner *ngIf="!hasRender('main') && !isError"></mat-spinner>
<img *ngIf="hasRender('main')" [src]="getImageUrl('main')" (click)="showFullScreenImage()" />
<div *ngIf="isError">

View File

@@ -1,8 +1,9 @@
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { PlotWidget, PlotWidgetRenderData } from 'src/app/dashboard.service';
import { PlotSize, PlotWidget, PlotWidgetRenderData } from 'src/app/dashboard.service';
import { PlotViewComponent } from 'src/app/plot-view/plot-view.component';
import { PlotRequest, PlotResponse, PlotService, RenderOptions } from 'src/app/plot.service';
import { PlotConfig, PlotRequest, PlotResponse, PlotService, RenderOptions } from 'src/app/plot.service';
import { AddPlotDialogComponent } from '../add-plot-dialog/add-plot-dialog.component';
import { FullScreenPlotDialogComponent } from '../full-screen-plot-dialog/full-screen-plot-dialog.component';
@Component({
@@ -19,7 +20,7 @@ export class PlotWidgetComponent implements AfterViewInit {
@ViewChild("plotView") plotView!: PlotViewComponent;
constructor(private dialog: MatDialog, ){}
constructor(private dialog: MatDialog, private service: PlotService){}
ngAfterViewInit(): void {
@@ -44,4 +45,31 @@ export class PlotWidgetComponent implements AfterViewInit {
});
}
edit() {
this.dialog.open(AddPlotDialogComponent, {
data: {config: this.data.widget.config, title:"Edit Plot"},
width: 'calc(100% - 15px)',
height: 'calc(100% - 15px)',
}).afterClosed().subscribe((config?: PlotConfig) => {
if (config !== undefined && config.query.length > 0) {
this.data.widget.config = config;
this.isError = false;
this.data.plotResponse = undefined;
const request = PlotWidget.createPlotRequest(this.data.widget);
this.service.sendPlotRequest(request).subscribe({
next: (response: PlotResponse)=> {
this.data.plotResponse = response;
},
error: (error:any)=> {
this.isError = true;
},
});
}
});
}
}

View File

@@ -1,3 +1,8 @@
<style>
div[mat-dialog-content] {
overflow: hidden;
}
</style>
<h1 mat-dialog-title>Create a new dashboard</h1>
<div mat-dialog-content>
<mat-form-field class="pdb-form-full-width">

View File

@@ -1,4 +0,0 @@
div[mat-dialog-content] {
overflow: hidden;
}

View File

@@ -1,11 +1,10 @@
import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
import {MatDialog, MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import { DashboardCreationData } from 'src/app/dashboard.service';
@Component({
selector: 'app-new-dashboard',
templateUrl: './new-dashboard.component.html',
styleUrls: ['./new-dashboard.component.scss']
templateUrl: './new-dashboard.component.html'
})
export class NewDashboardComponent implements OnInit {

View File

@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { PlotConfig, PlotResponse } from './plot.service';
import { PlotConfig, PlotRequest, PlotResponse, RenderOptions } from './plot.service';
@Injectable({
providedIn: 'root'
@@ -67,6 +67,47 @@ export class PlotWidget extends BaseWidget {
constructor(override id: string, override size: 'SMALL'|'MEDIUM'|'LARGE', public config: PlotConfig) {
super(id, 'PLOT', size);
}
public static createPlotRequest(widget: PlotWidget): PlotRequest {
const height = this.height(widget.size);
const width = this.width(widget.size);
const fullWidth = window.innerWidth-30;
const fullHeight = window.innerHeight-30;
const request = new PlotRequest(
(<any>window).submitterId+crypto.randomUUID(),
widget.config,
{
'main': new RenderOptions(height,width, false, true),
'fullScreen': new RenderOptions(fullHeight,fullWidth, false, true)
}
);
return request;
}
static height(size: PlotSize): number{
switch (size) {
case 'SMALL':
return 300;
case 'MEDIUM':
return 400;
case 'LARGE':
return 600;
}
}
static width(size: PlotSize): number{
switch (size) {
case 'SMALL':
return 400;
case 'MEDIUM':
return 600;
case 'LARGE':
return 900;
}
}
}
export type PlotSize = 'SMALL'|'MEDIUM'|'LARGE';

View File

@@ -17,6 +17,7 @@
<mat-form-field class="pdb-form-number"
*ngIf="limitBy !== 'NO_LIMIT'">
<mat-label><!--empty label needed for layout reasons--></mat-label>
<input
matInput
type="number"

View File

@@ -93,20 +93,15 @@ export class PlotService {
export class PlotType {
id: string;
name: string;
icon: string
active: boolean;
xAxis: DataType;
yAxis: DataType;
constructor(id: string, name: string, icon: string, active: boolean, xAxis: DataType, yAxis: DataType) {
this.id = id;
this.name = name;
this.icon = icon;
this.active = active;
this.xAxis = xAxis;
this.yAxis = yAxis;
constructor(
public id: string,
public name: string,
public icon: string,
public active: boolean,
public xAxis: DataType,
public yAxis: DataType) {
}
compatible(others: Array<PlotType>) : boolean {

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input, ViewChild } from '@angular/core';
import {UntypedFormControl} from '@angular/forms';
import { Component, OnInit, Input, ViewChild, AfterViewInit } from '@angular/core';
import {FormControl, UntypedFormControl} from '@angular/forms';
import {Observable} from 'rxjs';
import {startWith, map} from 'rxjs/operators';
import {MatAutocompleteTrigger } from '@angular/material/autocomplete';
@@ -12,7 +12,7 @@ import { PlotService, PlotType, AutocompleteResult, Suggestion, ResultMode } fro
})
export class QueryAutocompleteComponent implements OnInit {
queryField = new UntypedFormControl('');
queryField = new FormControl<Suggestion>(new Suggestion("","",0));
suggestions = new UntypedFormControl();
@@ -20,26 +20,33 @@ export class QueryAutocompleteComponent implements OnInit {
query : string = "";
suggestionFetcherEnabled = true;
@ViewChild(MatAutocompleteTrigger)
autocomplete!: MatAutocompleteTrigger;
constructor(private plotService: PlotService) {}
constructor(private plotService: PlotService) {
}
ngOnInit() {
const that = this;
this.query = "";
this.queryField.valueChanges.subscribe(function(value){
this.queryField.valueChanges.subscribe((value) =>{
if (value != null) {
if (typeof value == "string") {
that.query = value;
this.query = value;
}else{
that.query = value.newQuery;
this.query = value.newQuery;
var el : HTMLInputElement = <HTMLInputElement>document.getElementById('query-autocomplete-input');
const el : HTMLInputElement = <HTMLInputElement>document.getElementById('query-autocomplete-input');
el.selectionStart=value.newCaretPosition;
el.selectionEnd=value.newCaretPosition;
}
that.fetchSuggestions(value.newCaretPosition);
if (this.suggestionFetcherEnabled) {
this.fetchSuggestions(value.newCaretPosition);
}
}
});
this.filteredSuggestions = this.suggestions.valueChanges.pipe(
@@ -61,23 +68,21 @@ export class QueryAutocompleteComponent implements OnInit {
const that = this;
const query = typeof this.queryField.value == "string"
? this.queryField.value
: this.queryField.value.newQuery;
: this.queryField.value!.newQuery;
this.plotService
.autocomplete(query, caretIndex, ResultMode.CUT_AT_DOT)
.subscribe(
(data: AutocompleteResult) => {// success path
//console.log(JSON.stringify(data.proposals));
.subscribe({
next: (data: AutocompleteResult) => {
that.suggestions.setValue(data.proposals);
that.autocomplete.openPanel();
},
(error:any) => console.log(error)
error: (error:any) => console.log(error)
}
);
}
displaySuggestion(suggestion?: Suggestion): string {
//console.log("suggestion: "+JSON.stringify(suggestion));
return suggestion ? suggestion.newQuery : '';
}
}

View File

@@ -52,7 +52,7 @@
<pdb-y-axis-definition #y1AxisDefinitionComponent yIndex="1"></pdb-y-axis-definition>
<pdb-y-axis-definition #y2AxisDefinitionComponent yIndex="2" [hidden]="!y2AxisAvailable"></pdb-y-axis-definition>
<mat-checkbox [(ngModel)]="enableGallery">Gallery</mat-checkbox>
<mat-checkbox *ngIf="galleryEnabled" [(ngModel)]="enableGallery">Gallery</mat-checkbox>
<mat-form-field *ngIf="enableGallery" class="pdb-form-full-width">
<mat-label>Split By:</mat-label>

View File

@@ -15,11 +15,11 @@
grid:
"query-box query-box date-box" auto
"filters results results" 1fr
/ 25.5em 3fr 24em;
/ 25.5em 3fr 23.5em;
}
}
@media screen and (max-width: 1000px) {
@media screen and (max-width: 900px) {
#visualization {
display: grid;
margin: 0;
@@ -27,7 +27,7 @@
grid:
"query-box" auto
"date-box" auto
"filters" auto
"filters" min-content
"results" 1fr
/ 1fr;
}

View File

@@ -1,13 +1,12 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { PlotService, PlotType, PlotRequest, PlotResponse, TagField, FilterDefaults, DataType, YAxisDefinition, AxesTypes, PlotConfig, RenderOptions, RenderOptionsMap } from '../plot.service';
import { UntypedFormControl, Validators } from '@angular/forms';
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
import { PlotService, PlotType, PlotRequest, TagField, FilterDefaults, DataType, AxesTypes, PlotConfig, RenderOptions, RenderOptionsMap, Suggestion } from '../plot.service';
import { UntypedFormControl, } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { LimitByComponent } from '../limit-by/limit-by.component';
import { YAxisDefinitionComponent } from '../y-axis-definition/y-axis-definition.component';
import { QueryAutocompleteComponent } from '../query-autocomplete/query-autocomplete.component';
import { PlotViewComponent, SelectionRange, DateAnchor, LoadingEvent } from '../plot-view/plot-view.component';
import { PlotViewComponent, LoadingEvent } from '../plot-view/plot-view.component';
import { GalleryViewComponent } from '../gallery-view/gallery-view.component';
import * as moment from 'moment';
import { WidgetDimensions } from '../dashboard.service';
@Component({
@@ -15,14 +14,20 @@ import { WidgetDimensions } from '../dashboard.service';
templateUrl: './visualization-page.component.html',
styleUrls: ['./visualization-page.component.scss']
})
export class VisualizationPageComponent implements OnInit {
export class VisualizationPageComponent implements OnInit, AfterViewInit {
readonly DATE_PATTERN = "YYYY-MM-DD HH:mm:ss"; // for moment-JS
@Input()
defaultConfig?: PlotConfig;
@Input()
galleryEnabled = true;
dateRange = new UntypedFormControl('2019-10-05 00:00:00 - 2019-10-11 23:59:59');
selectedPlotType = new Array<PlotType>();
plotTypes: Array<any> = [];
plotTypes: PlotType[] = [];
tagFields: Array<TagField> = new Array<TagField>();
@@ -68,34 +73,69 @@ export class VisualizationPageComponent implements OnInit {
}
ngOnInit() {
const that = this;
(<any>window).initDatePicker();
this.plotTypes = this.plotService.getPlotTypes();
this.selectedPlotType.push(this.plotTypes[0]);
that.plotService.getFilterDefaults().subscribe(function(filterDefaults: FilterDefaults) {
this.plotService.getFilterDefaults().subscribe((filterDefaults: FilterDefaults) => {
filterDefaults.fields.forEach(function(name:string) {
that.tagFields.push(new TagField(name));
filterDefaults.fields.forEach((name:string) => {
this.tagFields.push(new TagField(name));
},
(error: any) => {
that.showError(error.error.message);
this.showError(error.error.message);
});
that.groupBy = that.tagFields.filter(val => filterDefaults.groupBy.includes(val.name));
that.splitBy = that.tagFields.find(val => filterDefaults.splitBy == val.name);
});
const groupByDefaults = this.defaultConfig ? this.defaultConfig.groupBy : filterDefaults.groupBy;
this.groupBy = this.tagFields.filter(val => groupByDefaults.includes(val.name));
this.splitBy = this.tagFields.find(val => filterDefaults.splitBy == val.name);
if (this.defaultConfig) {
this.plot();
}
});
}
ngAfterViewInit(): void {
if (this.defaultConfig) {
const c = this.defaultConfig;
this.query.suggestionFetcherEnabled = false;
this.query.queryField.setValue(new Suggestion(c.query, c.query, c.query.length));
this.query.suggestionFetcherEnabled = true;
this.selectedPlotType = this.plotTypes.filter(pt => c.aggregates.includes(pt.id));
this.changePlotType(this.selectedPlotType);
this.updateDateRange(c.dateRange, false);
this.limitbycomponent.limitBy = c.limitBy;
this.limitbycomponent.limit = c.limit;
this.y1AxisDefinitionComponent.yAxisScale = c.y1.axisScale;
this.y1AxisDefinitionComponent.minYValue = c.y1.rangeMin;
this.y1AxisDefinitionComponent.maxYValue = c.y1.rangeMax;
this.y1AxisDefinitionComponent.yAxisUnit = c.y1.rangeUnit;
if (c.y2) {
this.y2AxisDefinitionComponent.yAxisScale = c.y2.axisScale;
this.y2AxisDefinitionComponent.minYValue = c.y2.rangeMin;
this.y2AxisDefinitionComponent.maxYValue = c.y2.rangeMax;
this.y2AxisDefinitionComponent.yAxisUnit = c.y2.rangeUnit;
}
}
}
loading(event: LoadingEvent) {
this.plotJobActive = event.loading;
}
updateDateRange(newDateRange: string) {
updateDateRange(newDateRange: string, updatePlot=true) {
(<HTMLInputElement>document.getElementById("search-date-range")).value = newDateRange;
if (updatePlot){
this.plot();
}
}
changePlotType(selectedPlotTypes: Array<PlotType>) {
const compatiblePlotTypes = this.plotTypes.filter(pt => pt.compatible(selectedPlotTypes));

View File

@@ -48,9 +48,6 @@
}
$( document ).ready(function() {
//initDatePicker();
initInvaders('results');
document.addEventListener("invadersPause", function(event) {
pauseInvaders();