This is an automated email from the ASF dual-hosted git repository. riemer pushed a commit to branch 3059-extend-capabilities-of-time-selection-bar in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 7075d028142c7e63827ed2c21644dc2cae746e09 Author: Dominik Riemer <[email protected]> AuthorDate: Thu Jul 25 10:45:02 2024 +0200 feat(#3059): Replace time selection bar with menu --- ui/cypress/support/utils/datalake/DataLakeUtils.ts | 40 ++++- .../tests/datalake/timeRangeSelectors.spec.ts | 160 +++++++++--------- .../lib/apis/data-view-data-explorer.service.ts | 4 + .../src/lib/model/datalake/DateRange.ts | 31 ++++ .../dashboard-toolbar.component.html | 4 +- .../dashboard-toolbar.component.ts | 3 + .../data-view-preview.component.html | 4 +- .../data-explorer-dashboard-panel.component.html | 3 + .../data-explorer-dashboard-panel.component.ts | 13 +- .../data-view/data-explorer-data-view.component.ts | 8 +- .../data-explorer-designer-panel.component.html | 9 -- .../data-explorer-designer-panel.component.ts | 19 --- .../data-explorer-data-view-toolbar.component.html | 2 +- ...data-explorer-dashboard-overview.component.html | 2 +- .../data-explorer-dashboard-overview.component.ts | 4 +- .../overview/data-explorer-overview.component.ts | 6 +- .../time-range-selector.component.html | 65 ++++++++ ...ent.scss => time-range-selector.component.scss} | 15 ++ .../time-selector/time-range-selector.component.ts | 143 ++++++++++++++++ .../custom-time-range-selection.component.html | 87 ++++++++++ .../custom-time-range-selection.component.scss} | 27 +--- .../custom-time-range-selection.component.ts | 107 ++++++++++++ .../time-selector-menu.component.html | 53 ++++++ .../time-selector-menu.component.scss} | 33 ++-- .../time-selector-menu.component.ts | 69 ++++++++ .../time-selector/timeRangeSelector.component.html | 82 ---------- .../time-selector/timeRangeSelector.component.ts | 180 --------------------- .../widgets/table/table-widget.component.ts | 12 +- ui/src/app/data-explorer/data-explorer.module.ts | 18 ++- ...-explorer-edit-dashboard-dialog.component.html} | 47 +++--- ...-explorer-edit-dashboard-dialog.component.scss} | 0 ...ta-explorer-edit-dashboard-dialog.component.ts} | 18 ++- .../services/data-explorer-dashboard.service.ts | 12 +- .../services/time-selection.service.ts | 123 +++++++++++++- 34 files changed, 938 insertions(+), 465 deletions(-) diff --git a/ui/cypress/support/utils/datalake/DataLakeUtils.ts b/ui/cypress/support/utils/datalake/DataLakeUtils.ts index dd52f048e4..0dee52c86d 100644 --- a/ui/cypress/support/utils/datalake/DataLakeUtils.ts +++ b/ui/cypress/support/utils/datalake/DataLakeUtils.ts @@ -24,6 +24,7 @@ import { FileManagementUtils } from '../FileManagementUtils'; import { ConnectUtils } from '../connect/ConnectUtils'; import { ConnectBtns } from '../connect/ConnectBtns'; import { AdapterBuilder } from '../../builder/AdapterBuilder'; +import { differenceInMonths } from 'date-fns'; export class DataLakeUtils { public static goToDatalake() { @@ -349,8 +350,41 @@ export class DataLakeUtils { } public static selectTimeRange(from: Date, to: Date) { - DataLakeUtils.setTimeInput('time-range-from', from); - DataLakeUtils.setTimeInput('time-range-to', to); + DataLakeUtils.openTimeSelectorMenu(); + const monthsBack = Math.abs(differenceInMonths(from, new Date())); + DataLakeUtils.navigateCalendar('previous', monthsBack); + DataLakeUtils.selectDay(from.getDay()); + + const monthsForward = Math.abs(differenceInMonths(from, to)); + DataLakeUtils.navigateCalendar('next', monthsForward); + + DataLakeUtils.selectDay(to.getDay()); + + DataLakeUtils.setTimeInput('time-selector-start-time', from); + DataLakeUtils.setTimeInput('time-selector-end-time', to); + DataLakeUtils.applyCustomTimeSelection(); + } + + public static navigateCalendar(direction: string, numberOfMonths: number) { + for (let i = 0; i < numberOfMonths; i++) { + cy.get(`button.mat-calendar-${direction}-button`).click(); + } + } + + public static selectDay(day: number) { + cy.get( + `button:has(span.mat-calendar-body-cell-content:contains("${day}"))`, + ) + .first() + .click(); + } + + public static openTimeSelectorMenu() { + cy.dataCy('time-selector-menu').click(); + } + + public static applyCustomTimeSelection() { + cy.dataCy('apply-custom-time').click(); } public static setTimeInput(field: string, date: Date) { @@ -358,7 +392,7 @@ export class DataLakeUtils { } public static makeTimeString(date: Date) { - return date.toISOString().slice(0, 16); + return date.toTimeString().slice(0, 5); } public static getFutureDate() { diff --git a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts index fd60b73af7..f83ae8ee6b 100644 --- a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts +++ b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts @@ -17,20 +17,29 @@ */ import { DataLakeUtils } from '../../support/utils/datalake/DataLakeUtils'; +import { + subDays, + subHours, + subMinutes, + subMonths, + subWeeks, + subYears, +} from 'date-fns'; describe('Test Time Range Selectors in Data Explorer', () => { const periods = [ - { selector: '15_min', offset: 15 }, - { selector: '1_hour', offset: 60 }, - { selector: '1_day', offset: 1440 }, - { selector: '1_week', offset: 10080 }, - { selector: '1_month', offset: 43200 }, - { selector: '1_year', offset: 525600 }, + { selector: 1, start: (now: Date) => subMinutes(now, 15) }, + { selector: 2, start: (now: Date) => subHours(now, 1) }, + { selector: 4, start: (now: Date) => subDays(now, 1) }, + { selector: 6, start: (now: Date) => subWeeks(now, 1) }, + { selector: 8, start: (now: Date) => subMonths(now, 1) }, + { selector: 10, start: (now: Date) => subYears(now, 1) }, ]; - const dateAttribute = 'ng-reflect-date'; - const timeRangeFrom = 'time-range-from'; - const timeRangeTo = 'time-range-to'; + const timeRangeFrom = 'time-selector-start-time'; + const timeRangeTo = 'time-selector-end-time'; + const dateRangeFrom = 'time-selector-start-date'; + const dateRangeTo = 'time-selector-end-date'; before('Setup Tests', () => { cy.initStreamPipesTest(); @@ -42,74 +51,59 @@ describe('Test Time Range Selectors in Data Explorer', () => { it('Perform Test', () => { periods.forEach(period => { cy.log('Testing period: ' + period.selector); + DataLakeUtils.openTimeSelectorMenu(); // Choosing time period and saving initial start and end dates - cy.dataCy(period.selector).click(); - cy.dataCy(timeRangeTo) - .invoke('attr', dateAttribute) - .then(initialEndDateString => { - cy.dataCy(timeRangeFrom) - .invoke('attr', dateAttribute) - .then(initialStartDateString => { - const initialStartDate = parseDate( - initialStartDateString, - ); - const initialEndDate = - parseDate(initialEndDateString); - - cy.wrap(initialStartDate).should( - 'deep.eq', - getExpectedStartDate( - initialEndDate, - period.offset, - ), - ); - // Updating time range to previous one and checking start and end dates - cy.dataCy('decrease-time-button').click({ - force: true, - }); - cy.dataCy(timeRangeTo) - .invoke('attr', dateAttribute) - .then(updatedEndDateString => { - cy.dataCy(timeRangeFrom) - .invoke('attr', dateAttribute) - .then(updatedStartDateString => { - const updatedStartDate = parseDate( - updatedStartDateString, - ); - const updatedEndDate = - parseDate(updatedEndDateString); + cy.dataCy(`time-selector-quick-${period.selector}`).click(); + const expectedEndDate = new Date(); + DataLakeUtils.openTimeSelectorMenu(); + // check if dates can differ from the selected dates + const expectedStartDate = getExpectedStartDate( + expectedEndDate, + period.start, + ); + cy.dataCy(dateRangeFrom).should( + 'have.text', + getLocalizedDateString(expectedStartDate), + ); + cy.dataCy(dateRangeTo).should( + 'have.text', + getLocalizedDateString(expectedEndDate), + ); - cy.wrap(updatedStartDate).should( - 'deep.eq', - getExpectedStartDate( - updatedEndDate, - period.offset, - ), - ); - cy.wrap(updatedEndDate).should( - 'deep.eq', - initialStartDate, - ); - }); - }); - // Updating time range to the next one and comparing start and end dates with initial values - cy.dataCy('increase-time-button').click({ - force: true, - }); - cy.dataCy(timeRangeFrom) - .invoke('attr', dateAttribute) - .should('eq', initialStartDateString); - cy.dataCy(timeRangeTo) - .invoke('attr', dateAttribute) - .should('eq', initialEndDateString); - }); + cy.dataCy(timeRangeFrom) + .invoke('val') + .then(actualTime => { + const expectedDate = + getLocalizedTimeString(expectedStartDate); + expect( + isTimeWithinTolerance( + actualTime as string, + expectedDate, + 3, + ), + ).to.be.true; + }); + cy.dataCy(timeRangeTo) + .invoke('val') + .then(actualTime => { + const expectedDate = + getLocalizedTimeString(expectedEndDate); + expect( + isTimeWithinTolerance( + actualTime as string, + expectedDate, + 3, + ), + ).to.be.true; }); + + DataLakeUtils.applyCustomTimeSelection(); }); }); }); -function getExpectedStartDate(endDate: Date, offset: number): Date { - const startDate = new Date(endDate.getTime() - offset * 60000); +function getExpectedStartDate(endDate: Date, startFn: (Date) => Date): Date { + const startDate = startFn(endDate); startDate.setMinutes( startDate.getMinutes() + getTimezoneDifference(endDate, startDate), ); @@ -120,6 +114,28 @@ function getTimezoneDifference(endDate: Date, startDate: Date): number { return endDate.getTimezoneOffset() - startDate.getTimezoneOffset(); } -function parseDate(dateString: string): Date { - return new Date(Date.parse(dateString)); +function getLocalizedDateString(date: Date) { + return date.toLocaleDateString(); +} + +function getLocalizedTimeString(date: Date) { + return date.toLocaleTimeString(); +} + +function parseTimeStringToSeconds(timeString: string) { + const [hours, minutes, seconds] = timeString.split(':').map(Number); + return hours * 3600 + minutes * 60 + seconds || 0; +} + +function isTimeWithinTolerance( + actualTimeString: string, + expectedTimeString: string, + toleranceInSeconds: number, +) { + const actualTimeInSeconds = parseTimeStringToSeconds(actualTimeString); + const expectedTimeInSeconds = parseTimeStringToSeconds(expectedTimeString); + return ( + Math.abs(actualTimeInSeconds - expectedTimeInSeconds) <= + toleranceInSeconds + ); } diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts index 68c1988cb4..af101d07a0 100644 --- a/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts +++ b/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts @@ -40,6 +40,10 @@ export class DataViewDataExplorerService { return this.sharedDatalakeRestService.getDashboards(this.dashboardUrl); } + getDashboard(dashboardId: string): Observable<Dashboard> { + return this.http.get(`${this.dashboardUrl}/${dashboardId}`); + } + updateDashboard(dashboard: Dashboard): Observable<Dashboard> { return this.sharedDatalakeRestService.updateDashboard( this.dashboardUrl, diff --git a/ui/projects/streampipes/platform-services/src/lib/model/datalake/DateRange.ts b/ui/projects/streampipes/platform-services/src/lib/model/datalake/DateRange.ts index 87151909ca..0a3eb650f1 100644 --- a/ui/projects/streampipes/platform-services/src/lib/model/datalake/DateRange.ts +++ b/ui/projects/streampipes/platform-services/src/lib/model/datalake/DateRange.ts @@ -20,6 +20,37 @@ export interface TimeSettings { startTime: number; endTime: number; dynamicSelection: 15 | 60 | 1440 | 10080 | 43800 | 525600 | -1; + timeSelectionId?: TimeSelectionId; +} + +export interface TimeString { + startDate: string; + startTime: string; + endDate: string; + endTime: string; +} + +export enum TimeSelectionId { + CUSTOM, + LAST_15_MINUTES, + LAST_HOUR, + CURRENT_HOUR, + LAST_DAY, + CURRENT_DAY, + LAST_WEEK, + CURRENT_WEEK, + LAST_MONTH, + CURRENT_MONTH, + LAST_YEAR, + CURRENT_YEAR, +} + +export interface QuickTimeSelection { + label: string; + timeSelectionId: TimeSelectionId; + startTime: (now: Date) => Date; + endTime: (now: Date) => Date; + addDividerAfter?: boolean; } export class DateRange { diff --git a/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.html b/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.html index ff1b2ee7b6..56ad0025db 100644 --- a/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.html +++ b/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.html @@ -78,9 +78,9 @@ fxLayout="row" > <sp-time-range-selector - *ngIf="editMode || timeRangeVisible" + *ngIf="(editMode || timeRangeVisible) && globalTimeEnabled" (dateRangeEmitter)="updateDateRangeEmitter.emit($event)" - [dateRange]="timeSettings" + [timeSettings]="timeSettings" > </sp-time-range-selector> diff --git a/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.ts b/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.ts index e2da2c238d..e0e64fa740 100644 --- a/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.ts +++ b/ui/src/app/data-explorer/components/dashboard/dashboard-toolbar/dashboard-toolbar.component.ts @@ -43,6 +43,9 @@ export class DataExplorerDashboardToolbarComponent { @Input() timeSettings: TimeSettings; + @Input() + globalTimeEnabled = true; + @Output() viewModeChange: EventEmitter<string> = new EventEmitter<string>(); diff --git a/ui/src/app/data-explorer/components/dashboard/dashboard-widget-selection-panel/data-view-selection/data-view-preview/data-view-preview.component.html b/ui/src/app/data-explorer/components/dashboard/dashboard-widget-selection-panel/data-view-selection/data-view-preview/data-view-preview.component.html index dc77d00e81..e6727f9e1b 100644 --- a/ui/src/app/data-explorer/components/dashboard/dashboard-widget-selection-panel/data-view-selection/data-view-preview/data-view-preview.component.html +++ b/ui/src/app/data-explorer/components/dashboard/dashboard-widget-selection-panel/data-view-selection/data-view-preview/data-view-preview.component.html @@ -21,7 +21,7 @@ <h5>{{ dataView.baseAppearanceConfig.widgetTitle }}</h5> <div fxLayout="row" fxLayoutGap="15px" class="data-view-preview-info"> <span fxLayoutAlign="start center" - ><i class="material-icons color-primary">insert_chart</i></span + ><mat-icon color="primary">insert_chart</mat-icon></span > <span fxFlex fxLayoutAlign="start center">{{ widgetTypeLabel @@ -29,7 +29,7 @@ </div> <div fxLayout="row" fxLayoutGap="15px" class="data-view-preview-info"> <span fxLayoutAlign="start center" - ><i class="material-icons">folder</i></span + ><mat-icon color="primary">folder</mat-icon></span > <span fxFlex fxLayoutAlign="start center">{{ dataView.dataConfig.sourceConfigs[0].measureName diff --git a/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html b/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html index f5e4d0e2be..0a02874ae2 100644 --- a/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html +++ b/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html @@ -23,6 +23,9 @@ > <div nav fxFlex="100" class="ml-10"> <sp-data-explorer-dashboard-toolbar + [globalTimeEnabled]=" + dashboard.dashboardGeneralSettings.globalTimeEnabled + " [editMode]="editMode" [(viewMode)]="viewMode" [hasDataExplorerDeletePrivileges]="hasDataExplorerDeletePrivileges" diff --git a/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.ts b/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.ts index d29b19a2bd..55b6557584 100644 --- a/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.ts +++ b/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.ts @@ -179,10 +179,8 @@ export class DataExplorerDashboardPanelComponent implements OnInit, OnDestroy { } getDashboard(dashboardId: string, startTime: number, endTime: number) { - this.dataViewService.getDataViews().subscribe(data => { - this.dashboard = data.filter( - dashboard => dashboard.elementId === dashboardId, - )[0]; + this.dataViewService.getDashboard(dashboardId).subscribe(dashboard => { + this.dashboard = dashboard; this.breadcrumbService.updateBreadcrumb( this.breadcrumbService.makeRoute( [SpDataExplorerRoutes.BASE], @@ -192,6 +190,13 @@ export class DataExplorerDashboardPanelComponent implements OnInit, OnDestroy { this.viewMode = this.dashboard.dashboardGeneralSettings.defaultViewMode || 'grid'; + if ( + this.dashboard.dashboardGeneralSettings.globalTimeEnabled === + undefined + ) { + this.dashboard.dashboardGeneralSettings.globalTimeEnabled = + true; + } this.timeSettings = startTime && endTime ? this.overrideTime(+startTime, +endTime) diff --git a/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.ts b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.ts index e7c6225ff4..1530403e25 100644 --- a/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.ts +++ b/ui/src/app/data-explorer/components/data-view/data-explorer-data-view.component.ts @@ -26,7 +26,6 @@ import { import { ActivatedRoute } from '@angular/router'; import { TimeSelectionService } from '../../services/time-selection.service'; import { DataExplorerRoutingService } from '../../services/data-explorer-routing.service'; -import { DialogService } from '@streampipes/shared-ui'; import { DataExplorerDashboardService } from '../../services/data-explorer-dashboard.service'; @Component({ @@ -46,7 +45,6 @@ export class DataExplorerDataViewComponent implements OnInit { @ViewChild('panel', { static: false }) outerPanel: ElementRef; constructor( - private dialogService: DialogService, private dashboardService: DataExplorerDashboardService, private route: ActivatedRoute, private routingService: DataExplorerRoutingService, @@ -95,11 +93,7 @@ export class DataExplorerDataViewComponent implements OnInit { } makeDefaultTimeSettings(): TimeSettings { - return { - dynamicSelection: 1440, - endTime: new Date().getTime(), - startTime: new Date().getTime() - 1000 * 24 * 60 * 60, - }; + return this.timeSelectionService.getDefaultTimeSettings(); } createWidget() { diff --git a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.html b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.html index 6aa928dba7..7a13a6b959 100644 --- a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.html +++ b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.html @@ -32,15 +32,6 @@ }} </span> </div> - <div fxFlex fxLayoutAlign="end end"> - <button - mat-icon-button - matTooltip="Close Widget Configuration" - (click)="closeDesignerPanel()" - > - <mat-icon>close</mat-icon> - </button> - </div> </div> <div fxFlex="100" fxLayout="column" class="no-overflow"> <mat-tab-group diff --git a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.ts b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.ts index f2400158ac..901f51f188 100644 --- a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.ts +++ b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-explorer-designer-panel.component.ts @@ -74,25 +74,6 @@ export class DataExplorerDesignerPanelComponent { }); } - modifyWidgetMode(widget: DataExplorerWidgetModel, newWidgetMode: boolean) { - this.currentlyConfiguredWidget = widget; - this.newWidgetMode = newWidgetMode; - if (this.dataSettingsPanel) { - setTimeout(() => { - this.dataSettingsPanel.checkSourceTypes(); - }); - } - } - - closeDesignerPanel() { - this.closeDesignerPanelEmitter.emit(); - } - - resetIndex() { - this.selectedIndex = 0; - this.newWidgetMode = true; - } - @ViewChild('dataSettingsPanel') public set content( dataSettingsPanel: DataExplorerWidgetDataSettingsComponent, diff --git a/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html b/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html index 04ae11e225..3f54d2b57b 100644 --- a/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html +++ b/ui/src/app/data-explorer/components/data-view/data-view-toolbar/data-explorer-data-view-toolbar.component.html @@ -65,7 +65,7 @@ <sp-time-range-selector *ngIf="editMode || timeRangeVisible" (dateRangeEmitter)="updateDateRangeEmitter.emit($event)" - [dateRange]="timeSettings" + [timeSettings]="timeSettings" > </sp-time-range-selector> </div> diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.html b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.html index 4ada6cdaed..b4a789b851 100644 --- a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.html +++ b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.html @@ -83,7 +83,7 @@ color="accent" matTooltip="Edit data view settings" *ngIf="hasDataExplorerWritePrivileges" - (click)="openEditDataViewDialog(element)" + (click)="openEditDashboardDialog(element)" > <i class="material-icons">settings</i> </button> diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.ts b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.ts index 1ccaeb0f4d..4d6b8ca7dd 100644 --- a/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.ts +++ b/ui/src/app/data-explorer/components/overview/data-explorer-dashboard-overview/data-explorer-dashboard-overview.component.ts @@ -82,9 +82,9 @@ export class SpDataExplorerDashboardOverviewComponent extends SpDataExplorerOver }); } - openEditDataViewDialog(dashboard: Dashboard) { + openEditDashboardDialog(dashboard: Dashboard) { const dialogRef = - this.dataExplorerDashboardService.openDataViewModificationDialog( + this.dataExplorerDashboardService.openDashboardModificationDialog( false, dashboard, ); diff --git a/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts index 45d057adc4..2d872f7561 100644 --- a/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts +++ b/ui/src/app/data-explorer/components/overview/data-explorer-overview.component.ts @@ -62,16 +62,16 @@ export class DataExplorerOverviewComponent extends SpDataExplorerOverviewDirecti dataViewDashboard.widgets = []; dataViewDashboard.name = ''; - this.openDataViewModificationDialog(true, dataViewDashboard); + this.openDashboardModificationDialog(true, dataViewDashboard); } createNewDataView(): void { this.routingService.navigateToDataView(true); } - openDataViewModificationDialog(createMode: boolean, dashboard: Dashboard) { + openDashboardModificationDialog(createMode: boolean, dashboard: Dashboard) { const dialogRef = - this.dataExplorerDashboardService.openDataViewModificationDialog( + this.dataExplorerDashboardService.openDashboardModificationDialog( createMode, dashboard, ); diff --git a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.html b/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.html new file mode 100644 index 0000000000..4ca8900256 --- /dev/null +++ b/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.html @@ -0,0 +1,65 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<div fxFlex fxLayoutAlign="center center"> + <button mat-icon-button color="accent" (click)="applyPreviousInterval()"> + <i class="material-icons">navigate_before</i> + </button> + <button + mat-button + color="accent" + [matMenuTriggerFor]="menu" + #menuTrigger="matMenuTrigger" + matTooltip="Modify time" + data-cy="time-selector-menu" + (menuClosed)="menuTrigger.closeMenu()" + > + <div *ngIf="timeStringMode === 'advanced'"> + <div class="formatted-datetime"> + <span class="formatted-date">{{ timeString.startDate }}</span + > + <span class="formatted-time">{{ timeString.startTime }}</span> + </div> + - + <div class="formatted-datetime"> + <span class="formatted-date">{{ timeString.endDate }}</span + > + <span class="formatted-time">{{ timeString.endTime }}</span> + </div> + </div> + <div class="formatted-datetime" *ngIf="timeStringMode === 'simple'"> + {{ simpleTimeString }} + </div> + </button> + <button mat-icon-button color="accent" (click)="applyNextInterval()"> + <i class="material-icons">navigate_next</i> + </button> + + <mat-menu #menu="matMenu" fxFlex="100" class="large-menu"> + <sp-time-selector-menu + [timeSettings]="timeSettings" + (timeSettingsEmitter)="applyCurrentDateRange($event)" + class="w-100" + > + </sp-time-selector-menu> + </mat-menu> + + <button mat-icon-button color="accent" (click)="refreshData()"> + <i class="material-icons">autorenew</i> + </button> +</div> diff --git a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.scss b/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.scss similarity index 87% rename from ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.scss rename to ui/src/app/data-explorer/components/time-selector/time-range-selector.component.scss index fb24a7dc6f..aef8593a37 100644 --- a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.scss +++ b/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.scss @@ -55,3 +55,18 @@ .mr--5 { margin-right: -5px; } + +.formatted-date { + font-weight: bolder; +} + +.formatted-time { + font-weight: lighter; +} + +.formatted-datetime { + display: inline; + background: var(--color-bg-2); + border-radius: 5px; + padding: 5px; +} diff --git a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.ts b/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.ts new file mode 100644 index 0000000000..dafc89cc0f --- /dev/null +++ b/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.ts @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { + Component, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import { + TimeSelectionId, + TimeSettings, + TimeString, +} from '@streampipes/platform-services'; +import { MatMenuTrigger } from '@angular/material/menu'; +import { TimeSelectionService } from '../../services/time-selection.service'; + +@Component({ + selector: 'sp-time-range-selector', + templateUrl: 'time-range-selector.component.html', + styleUrls: ['./time-range-selector.component.scss'], + encapsulation: ViewEncapsulation.None, +}) +export class TimeRangeSelectorComponent implements OnInit, OnChanges { + @ViewChild('menuTrigger') menu: MatMenuTrigger; + + @Output() dateRangeEmitter = new EventEmitter<TimeSettings>(); + + @Input() + timeSettings: TimeSettings; + + simpleTimeString: string = ''; + timeString: TimeString; + timeStringMode: 'simple' | 'advanced' = 'simple'; + + constructor(private timeSelectionService: TimeSelectionService) {} + + ngOnInit() { + this.createDateString(); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.timeSettings) { + this.createDateString(); + } + } + + applyPreviousInterval(): void { + this.changeTimeByInterval((a, b) => a - b); + } + + applyNextInterval(): void { + this.changeTimeByInterval((a, b) => a + b); + } + + compare(newDateRange: TimeSettings, oldDateRange: TimeSettings): boolean { + return ( + newDateRange && + oldDateRange && + newDateRange.startTime === oldDateRange.startTime && + newDateRange.endTime === oldDateRange.endTime && + newDateRange.dynamicSelection === oldDateRange.dynamicSelection + ); + } + + reloadData() { + this.dateRangeEmitter.emit(this.timeSettings); + } + + refreshData() { + const difference = + this.timeSettings.endTime - this.timeSettings.startTime; + + const current = new Date().getTime(); + this.timeSettings = { + startTime: current - difference, + endTime: current, + dynamicSelection: this.timeSettings.dynamicSelection, + timeSelectionId: this.timeSettings.timeSelectionId, + }; + + this.reloadData(); + } + + private changeTimeByInterval(func) { + const difference = + this.timeSettings.endTime - this.timeSettings.startTime; + const newStartTime = func(this.timeSettings.startTime, difference); + const newEndTime = func(this.timeSettings.endTime, difference); + + this.timeSettings.startTime = newStartTime; + this.timeSettings.endTime = newEndTime; + this.timeSettings.timeSelectionId = TimeSelectionId.CUSTOM; + this.createDateString(); + this.reloadData(); + } + + applyCurrentDateRange(timeSettings: TimeSettings) { + this.timeSettings = timeSettings; + this.createDateString(); + this.menu.closeMenu(); + this.reloadData(); + } + + createDateString(): void { + if (this.timeSettings.timeSelectionId !== TimeSelectionId.CUSTOM) { + this.simpleTimeString = this.timeSelectionService.getTimeSelection( + this.timeSettings.timeSelectionId, + ).label; + this.timeStringMode = 'simple'; + } else { + const startDate = new Date(this.timeSettings.startTime); + const endDate = new Date(this.timeSettings.endTime); + this.timeString = { + startDate: startDate.toLocaleDateString(), + endDate: endDate.toLocaleDateString(), + startTime: startDate.toLocaleTimeString(), + endTime: endDate.toLocaleTimeString(), + }; + this.timeStringMode = 'advanced'; + } + } +} diff --git a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html new file mode 100644 index 0000000000..9e277f0ca6 --- /dev/null +++ b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html @@ -0,0 +1,87 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<div fxLayout="column" fxFlex="100" class="w-100"> + <div fxLayout="column" fxFlex="100" class="mt-10"> + <mat-card style="width: 100%"> + <mat-calendar + [(selected)]="currentDateRange" + (selectedChange)="onDateChange($event)" + > + </mat-calendar> + </mat-card> + + <div fxLayout="column" class="mt-10 mr-5"> + <div fxLayout="row" fxLayoutGap="10px"> + <div fxFlex="30"> + <span + class="time-selector" + data-cy="time-selector-start-date" + >{{ currentStartDate }}</span + > + </div> + <div fxFlex> + <input + matNativeControl + data-cy="time-selector-start-time" + class="w-100 time-selector" + type="time" + step="1" + [(ngModel)]="currentStartTime" + (ngModelChange)=" + updateDateTime(currentDateRange.start, $event) + " + /> + </div> + </div> + <div fxLayout="row" fxLayoutGap="10px" class="mt-10"> + <div fxFlex="30"> + <span + class="time-selector" + data-cy="time-selector-end-date" + >{{ currentEndDate }}</span + > + </div> + <div fxFlex> + <input + matNativeControl + data-cy="time-selector-end-time" + class="w-100 time-selector" + type="time" + step="1" + [(ngModel)]="currentEndTime" + (ngModelChange)=" + updateDateTime(currentDateRange.end, $event) + " + /> + </div> + </div> + </div> + </div> + <div fxLayout="row" fxLayoutAlign="end center" class="mt-10"> + <button + mat-raised-button + color="accent" + data-cy="apply-custom-time" + (click)="saveSelection()" + [disabled]="!dateSelectionComplete" + > + Apply + </button> + </div> +</div> diff --git a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss similarity index 69% copy from ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss copy to ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss index 92f0108de0..ce53cf59ea 100644 --- a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss +++ b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss @@ -16,28 +16,7 @@ * */ -@import '../../../../scss/sp/sp-dialog.scss'; - -.element-margin { - margin-top: 10px; - margin-bottom: 10px; -} - -.mat-radio-button { - padding: 10px; -} - -.mr-10 { - margin-right: 10px; -} - -.view-radio-group { - display: flex; - flex-direction: column; - margin: 15px 0; - align-items: flex-start; -} - -.view-radio-button { - margin: 5px; +.time-selector { + font-family: inherit; + font-size: 11pt; } diff --git a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts new file mode 100644 index 0000000000..a0ad73dab2 --- /dev/null +++ b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { TimeSelectionId, TimeSettings } from '@streampipes/platform-services'; +import { + DateRange, + DefaultMatCalendarRangeStrategy, + MatRangeDateSelectionModel, +} from '@angular/material/datepicker'; + +@Component({ + selector: 'sp-custom-time-range-selection', + templateUrl: 'custom-time-range-selection.component.html', + styleUrls: ['./custom-time-range-selection.component.scss'], +}) +export class CustomTimeRangeSelectionComponent implements OnInit { + @Input() timeSettings: TimeSettings; + @Output() timeSettingsEmitter = new EventEmitter<TimeSettings>(); + + currentStartDate: string; + currentEndDate: string; + currentStartTime: string; + currentEndTime: string; + currentDateRange: DateRange<Date>; + dateSelectionComplete = false; + + constructor( + private readonly selectionModel: MatRangeDateSelectionModel<Date>, + private readonly selectionStrategy: DefaultMatCalendarRangeStrategy<Date>, + ) {} + + ngOnInit(): void { + this.initializeDateRange(); + this.updateTimeStrings(); + this.updateDateStrings(); + this.dateSelectionComplete = true; + } + + initializeDateRange(): void { + this.currentDateRange = new DateRange( + new Date(this.timeSettings.startTime), + new Date(this.timeSettings.endTime), + ); + } + + updateTimeStrings(): void { + this.currentStartTime = this.formatTime(this.currentDateRange.start); + this.currentEndTime = this.formatTime(this.currentDateRange.end); + } + + formatTime(date: Date): string { + return date.toLocaleTimeString(); //.slice(0, 5); + } + + updateDateStrings(): void { + this.currentStartDate = this.formatDate(this.currentDateRange.start); + this.currentEndDate = this.formatDate(this.currentDateRange.end); + } + + formatDate(date: Date): string { + return date?.toLocaleDateString() || '-'; + } + + onDateChange(selectedDate: Date): void { + const newSelection = this.selectionStrategy.selectionFinished( + selectedDate, + this.selectionModel.selection, + ); + this.selectionModel.updateSelection(newSelection, this); + this.currentDateRange = new DateRange<Date>( + newSelection.start, + newSelection.end, + ); + this.dateSelectionComplete = this.selectionModel.isComplete(); + this.updateDateStrings(); + } + + saveSelection(): void { + this.updateDateTime(this.currentDateRange.start, this.currentStartTime); + this.updateDateTime(this.currentDateRange.end, this.currentEndTime); + this.timeSettings.startTime = this.currentDateRange.start.getTime(); + this.timeSettings.endTime = this.currentDateRange.end.getTime(); + this.timeSettings.timeSelectionId = TimeSelectionId.CUSTOM; + this.timeSettingsEmitter.emit(this.timeSettings); + } + + updateDateTime(date: Date, time: string): void { + const [hours, minutes, seconds] = time.split(':').map(Number); + date.setHours(hours, minutes, seconds || 0); + } +} diff --git a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.html b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.html new file mode 100644 index 0000000000..70ded63676 --- /dev/null +++ b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.html @@ -0,0 +1,53 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<div class="menu-content" (click)="$event.stopPropagation()"> + <div fxLayout="column" fxLayoutGap="10px"> + <div fxLayout="row" fxFlex="100" fxLayoutGap="10px"> + <div fxLayout="column" fxFlex="50" fxLayoutGap="10px"> + <div class="filter-header">Quick selection</div> + <div fxLayout="column" fxLayoutGap="10px" fxFlex="100"> + <div *ngFor="let quickSelection of quickSelections"> + <span + class="quick-link" + [attr.data-cy]=" + 'time-selector-quick-' + + quickSelection.timeSelectionId + " + (click)="applyQuickSelection(quickSelection)" + >{{ quickSelection.label }}</span + > + <mat-divider + *ngIf="quickSelection.addDividerAfter" + ></mat-divider> + </div> + </div> + </div> + <div fxLayout="column" fxFlex="50"> + <div class="filter-header">Custom</div> + <sp-custom-time-range-selection + #timeRangeSelection + [timeSettings]="timeSettings" + (timeSettingsEmitter)="timeSettingsEmitter.emit($event)" + class="w-100" + > + </sp-custom-time-range-selection> + </div> + </div> + </div> +</div> diff --git a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.scss similarity index 68% copy from ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss copy to ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.scss index 92f0108de0..b9c614c4d3 100644 --- a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss +++ b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.scss @@ -16,28 +16,29 @@ * */ -@import '../../../../scss/sp/sp-dialog.scss'; - -.element-margin { - margin-top: 10px; - margin-bottom: 10px; +.menu-content { + padding: 10px; + min-width: 550px; } -.mat-radio-button { - padding: 10px; +.filter-header { + font-weight: bold; + white-space: nowrap; + font-size: 12pt; } -.mr-10 { - margin-right: 10px; +.filter-section { + padding: 5px 10px 5px 5px; + border: 1px solid var(--color-bg-3); + background: var(--color-bg-1); } -.view-radio-group { - display: flex; - flex-direction: column; - margin: 15px 0; - align-items: flex-start; +.quick-link { + color: var(--color-accent); + cursor: pointer; + font-size: 11pt; } -.view-radio-button { - margin: 5px; +.quick-link:hover { + font-weight: bold; } diff --git a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.ts b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.ts new file mode 100644 index 0000000000..ff9e4e166d --- /dev/null +++ b/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.ts @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, +} from '@angular/core'; +import { + QuickTimeSelection, + TimeSettings, +} from '@streampipes/platform-services'; +import { TimeSelectionService } from '../../../services/time-selection.service'; +import { CustomTimeRangeSelectionComponent } from './custom-time-range-selection/custom-time-range-selection.component'; + +@Component({ + selector: 'sp-time-selector-menu', + templateUrl: 'time-selector-menu.component.html', + styleUrls: ['./time-selector-menu.component.scss'], +}) +export class TimeRangeSelectorMenuComponent implements OnInit { + @Input() + timeSettings: TimeSettings; + + @Output() + timeSettingsEmitter: EventEmitter<TimeSettings> = + new EventEmitter<TimeSettings>(); + + quickSelections: QuickTimeSelection[] = []; + + @ViewChild('timeRangeSelection') + timeRangeSelection: CustomTimeRangeSelectionComponent; + + constructor(private timeSelectionService: TimeSelectionService) {} + + ngOnInit(): void { + this.quickSelections = this.timeSelectionService.quickTimeSelections; + } + + applyQuickSelection(quickSelection: QuickTimeSelection): void { + const selectedDateRange = + this.timeSelectionService.getDateRange(quickSelection); + this.timeSettings.timeSelectionId = quickSelection.timeSelectionId; + this.timeSettings.startTime = selectedDateRange.startDate.getTime(); + this.timeSettings.endTime = selectedDateRange.endDate.getTime(); + this.timeRangeSelection.initializeDateRange(); + this.timeRangeSelection.updateTimeStrings(); + this.timeRangeSelection.updateDateStrings(); + this.timeSettingsEmitter.emit(this.timeSettings); + } +} diff --git a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.html b/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.html deleted file mode 100644 index 29073a02a0..0000000000 --- a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.html +++ /dev/null @@ -1,82 +0,0 @@ -<!-- - ~ Licensed to the Apache Software Foundation (ASF) under one or more - ~ contributor license agreements. See the NOTICE file distributed with - ~ this work for additional information regarding copyright ownership. - ~ The ASF licenses this file to You under the Apache License, Version 2.0 - ~ (the "License"); you may not use this file except in compliance with - ~ the License. You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - ~ - --> - -<div fxLayout="row" fxLayoutAlign="start center" class="form-field-small"> - <div class="time-range-wrapper" fxFlex fxLayoutAlign="center center"> - <button - mat-button - mat-raised-button - *ngFor="let item of possibleTimeButtons" - [color]="selectedTimeButton.value === item.value ? 'accent' : ''" - [attr.data-cy]="item.value.replace(' ', '_')" - [ngClass]="'button-margin smaller-button-font-size'" - (click)="this.setCurrentDateRange(item)" - > - {{ item.value }} - </button> - </div> - <div class="time-wrapper" fxFlex fxLayoutAlign="center center"> - <button - mat-button - class="button-icon icon-button-font-size mr--15" - color="accent" - (click)="decreaseTime()" - data-cy="decrease-time-button" - > - <mat-icon>navigate_before</mat-icon> - </button> - <sp-date-input - class="start-date-margin" - [(date)]="startDate" - (dateChanged)="changeCustomDateRange()" - data-cy="time-range-from" - > - </sp-date-input> - - <sp-date-input - [(date)]="endDate" - (dateChanged)="changeCustomDateRange()" - data-cy="time-range-to" - > - </sp-date-input> - - <button - mat-button - class="button-icon icon-button-font-size ml--5 mr--5" - color="accent" - (click)="increaseTime()" - data-cy="increase-time-button" - > - <mat-icon>navigate_next</mat-icon> - </button> - </div> - <div class="time-range-wrapper" fxFlex fxLayoutAlign="center center"> - <button - mat-button - mat-raised-button - color="accent" - class="button-margin smaller-button-font-size" - matTooltip="Reload" - matTooltipPosition="above" - (click)="reloadData()" - data-cy="reload-data-view-button" - > - <i class="material-icons mr-0">autorenew</i> - </button> - </div> -</div> diff --git a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.ts b/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.ts deleted file mode 100644 index 6dcea2232c..0000000000 --- a/ui/src/app/data-explorer/components/time-selector/timeRangeSelector.component.ts +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { - Component, - EventEmitter, - Input, - OnInit, - Output, - ViewEncapsulation, -} from '@angular/core'; -import { TimeSettings } from '@streampipes/platform-services'; - -@Component({ - selector: 'sp-time-range-selector', - templateUrl: 'timeRangeSelector.component.html', - styleUrls: ['./timeRangeSelector.component.scss'], - encapsulation: ViewEncapsulation.None, -}) -export class TimeRangeSelectorComponent implements OnInit { - @Output() dateRangeEmitter = new EventEmitter<TimeSettings>(); - - _dateRange: TimeSettings; - - startDate: Date; - endDate: Date; - - public possibleTimeButtons = [ - { value: '15 min', offset: 15 }, - { value: '1 hour', offset: 60 }, - { value: '1 day', offset: 1440 }, - { value: '1 week', offset: 10080 }, - { value: '1 month', offset: 43200 }, - { value: '1 year', offset: 525600 }, - { value: 'custom', offset: -1 }, - ]; - - public selectedTimeButton; - - constructor() {} - - ngOnInit() { - if (!this.dateRange.startTime) { - this.setCurrentDateRange(this.possibleTimeButtons[0]); - } else if (this.dateRange.dynamicSelection !== -1) { - this.setCurrentDateRange( - this.possibleTimeButtons.find( - tb => tb.offset === this.dateRange.dynamicSelection, - ), - ); - } else { - this.startDate = new Date(this._dateRange.startTime); - this.endDate = new Date(this._dateRange.endTime); - } - } - - @Input() - set dateRange(dateRange: TimeSettings) { - if (!this.compare(dateRange, this._dateRange)) { - this._dateRange = dateRange; - this.updateTimeSettings(); - } - } - - get dateRange(): TimeSettings { - return this._dateRange; - } - - compare(newDateRange: TimeSettings, oldDateRange: TimeSettings): boolean { - return ( - newDateRange && - oldDateRange && - newDateRange.startTime === oldDateRange.startTime && - newDateRange.endTime === oldDateRange.endTime && - newDateRange.dynamicSelection === oldDateRange.dynamicSelection - ); - } - - updateTimeSettings() { - this.startDate = new Date(this.dateRange.startTime); - this.endDate = new Date(this.dateRange.endTime); - this.selectedTimeButton = this.findOffset( - this.dateRange.dynamicSelection, - ); - this.reloadData(); - } - - findOffset(dynamicSelection: number) { - return ( - this.possibleTimeButtons.find( - el => el.offset === dynamicSelection, - ) || this.possibleTimeButtons[0] - ); - } - - reloadData() { - this.dateRangeEmitter.emit(this.dateRange); - } - - increaseTime() { - this.changeTimeByInterval((a, b) => a + b); - } - - decreaseTime() { - this.changeTimeByInterval((a, b) => a - b); - } - - refreshData() { - const difference = this.endDate.getTime() - this.startDate.getTime(); - - const current = new Date().getTime(); - this.dateRange = { - startTime: current - difference, - endTime: current, - dynamicSelection: this.dateRange.dynamicSelection, - }; - - this.reloadData(); - } - - private changeTimeByInterval(func) { - const difference = this.endDate.getTime() - this.startDate.getTime(); - const newStartTime = func(this.startDate.getTime(), difference); - const newEndTime = func(this.endDate.getTime(), difference); - - this.startDate = new Date(newStartTime); - this.endDate = new Date(newEndTime); - this.selectedTimeButton = - this.possibleTimeButtons[this.possibleTimeButtons.length - 1]; - this.dateRange = { - startTime: newStartTime, - endTime: newEndTime, - dynamicSelection: -1, - }; - } - - changeCustomDateRange() { - this.selectedTimeButton = - this.possibleTimeButtons[this.possibleTimeButtons.length - 1]; - const newStartTime = this.startDate.getTime(); - const newEndTime = this.endDate.getTime(); - - this.dateRange = { - startTime: newStartTime, - endTime: newEndTime, - dynamicSelection: -1, - }; - } - - /** - * Sets the current date range from now to the value of offset in the past - * @param offset in minutes - */ - setCurrentDateRange(item) { - this.selectedTimeButton = item; - const current = new Date().getTime(); - this.startDate = new Date(current - item.offset * 60000); - this.endDate = new Date(current); - this.dateRange = { - startTime: this.startDate.getTime(), - endTime: this.endDate.getTime(), - dynamicSelection: item.offset, - }; - } -} diff --git a/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts b/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts index a156b11b14..40acdf5a48 100644 --- a/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts +++ b/ui/src/app/data-explorer/components/widgets/table/table-widget.component.ts @@ -51,7 +51,9 @@ export class TableWidgetComponent } regenerateColumnNames(): void { - this.groupByColumnNames = this.makeGroupByColumns(); + this.groupByColumnNames = this.makeGroupByColumns( + this.dataExplorerWidget.visualizationConfig.selectedColumns, + ); this.columnNames = ['time'].concat( this.dataExplorerWidget.visualizationConfig.selectedColumns.map( c => c.fullDbName, @@ -60,10 +62,16 @@ export class TableWidgetComponent ); } - makeGroupByColumns(): string[] { + makeGroupByColumns(selectedColumns: DataExplorerField[]): string[] { return this.dataExplorerWidget.dataConfig.sourceConfigs.flatMap(sc => { return sc.queryConfig.groupBy .filter(g => g.selected) + .filter( + g => + selectedColumns.find( + column => column.runtimeName === g.runtimeName, + ) === undefined, + ) .map(g => g.runtimeName); }); } diff --git a/ui/src/app/data-explorer/data-explorer.module.ts b/ui/src/app/data-explorer/data-explorer.module.ts index 62dc60706a..02cdad7b9b 100644 --- a/ui/src/app/data-explorer/data-explorer.module.ts +++ b/ui/src/app/data-explorer/data-explorer.module.ts @@ -23,7 +23,11 @@ import { FlexLayoutModule } from '@ngbracket/ngx-layout'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatChipsModule } from '@angular/material/chips'; import { MatNativeDateModule } from '@angular/material/core'; -import { MatDatepickerModule } from '@angular/material/datepicker'; +import { + DefaultMatCalendarRangeStrategy, + MatDatepickerModule, + MatRangeDateSelectionModel, +} from '@angular/material/datepicker'; import { MatGridListModule } from '@angular/material/grid-list'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSliderModule } from '@angular/material/slider'; @@ -39,7 +43,7 @@ import { CoreUiModule } from '../core-ui/core-ui.module'; import { DataExplorerDashboardGridComponent } from './components/widget-view/grid-view/data-explorer-dashboard-grid.component'; import { DataExplorerOverviewComponent } from './components/overview/data-explorer-overview.component'; import { DataExplorerDashboardPanelComponent } from './components/dashboard/data-explorer-dashboard-panel.component'; -import { TimeRangeSelectorComponent } from './components/time-selector/timeRangeSelector.component'; +import { TimeRangeSelectorComponent } from './components/time-selector/time-range-selector.component'; import { DataExplorerDashboardWidgetComponent } from './components/widget/data-explorer-dashboard-widget.component'; import { ImageWidgetComponent } from './components/widgets/image/image-widget.component'; import { TableWidgetComponent } from './components/widgets/table/table-widget.component'; @@ -48,7 +52,7 @@ import { LoadDataSpinnerComponent } from './components/widgets/utils/load-data-s import { NoDataInDateRangeComponent } from './components/widgets/utils/no-data/no-data-in-date-range.component'; import { SelectPropertiesComponent } from './components/widgets/utils/select-properties/select-properties.component'; import { SelectColorPropertiesComponent } from './components/widgets/utils/select-color-properties/select-color-properties.component'; -import { DataExplorerEditDataViewDialogComponent } from './dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component'; +import { DataExplorerEditDashboardDialogComponent } from './dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component'; import { GroupConfigurationComponent } from './components/widgets/utils/group-configuration/group-configuration.component'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { DataExplorerDesignerPanelComponent } from './components/data-view/data-view-designer-panel/data-explorer-designer-panel.component'; @@ -121,6 +125,8 @@ import { DataExplorerDataViewSelectionComponent } from './components/dashboard/d import { DataExplorerDashboardWidgetSelectionPanelComponent } from './components/dashboard/dashboard-widget-selection-panel/dashboard-widget-selection-panel.component'; import { DataExplorerDataViewPreviewComponent } from './components/dashboard/dashboard-widget-selection-panel/data-view-selection/data-view-preview/data-view-preview.component'; import { DataExplorerDashboardToolbarComponent } from './components/dashboard/dashboard-toolbar/dashboard-toolbar.component'; +import { TimeRangeSelectorMenuComponent } from './components/time-selector/time-selector-menu/time-selector-menu.component'; +import { CustomTimeRangeSelectionComponent } from './components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component'; @NgModule({ imports: [ @@ -201,6 +207,7 @@ import { DataExplorerDashboardToolbarComponent } from './components/dashboard/da ], declarations: [ AggregateConfigurationComponent, + CustomTimeRangeSelectionComponent, DataExplorerDashboardGridComponent, DataExplorerOverviewComponent, DataExplorerDashboardPanelComponent, @@ -211,7 +218,7 @@ import { DataExplorerDashboardToolbarComponent } from './components/dashboard/da DataExplorerDataViewPreviewComponent, DataExplorerDataViewSelectionComponent, DataExplorerDesignerPanelComponent, - DataExplorerEditDataViewDialogComponent, + DataExplorerEditDashboardDialogComponent, DataExplorerWidgetAppearanceSettingsComponent, DataExplorerWidgetDataSettingsComponent, DataExplorerDataViewComponent, @@ -239,6 +246,7 @@ import { DataExplorerDashboardToolbarComponent } from './components/dashboard/da HeatmapWidgetConfigComponent, ImageViewerComponent, TimeRangeSelectorComponent, + TimeRangeSelectorMenuComponent, DataExplorerVisualisationSettingsComponent, GroupSelectionPanelComponent, DataExplorerVisualisationSettingsComponent, @@ -260,7 +268,7 @@ import { DataExplorerDashboardToolbarComponent } from './components/dashboard/da SpTimeSeriesAppearanceConfigComponent, SpDataZoomConfigComponent, ], - providers: [], + providers: [DefaultMatCalendarRangeStrategy, MatRangeDateSelectionModel], exports: [], }) export class DataExplorerModule { diff --git a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.html b/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.html similarity index 66% rename from ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.html rename to ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.html index b65bc1e047..de73ccaeff 100644 --- a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.html +++ b/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.html @@ -40,25 +40,36 @@ <mat-label>Description</mat-label> <input matInput [(ngModel)]="dashboard.description" /> </mat-form-field> - <label>Default view mode</label> - <mat-radio-group - [(ngModel)]=" - dashboard.dashboardGeneralSettings.defaultViewMode - " - > - <mat-radio-button - class="view-radio-button" - [value]="'grid'" + <div class="mt-10" fxLayout="column"> + <label>Default view mode</label> + <mat-radio-group + [(ngModel)]=" + dashboard.dashboardGeneralSettings.defaultViewMode + " > - Grid View - </mat-radio-button> - <mat-radio-button - class="view-radio-button" - [value]="'slide'" - > - Slide View - </mat-radio-button> - </mat-radio-group> + <mat-radio-button + class="view-radio-button" + [value]="'grid'" + > + Grid View + </mat-radio-button> + <mat-radio-button + class="view-radio-button" + [value]="'slide'" + > + Slide View + </mat-radio-button> + </mat-radio-group> + </div> + <div class="mt-10" fxLayout="column"> + <label>Time Settings</label> + <mat-checkbox + [(ngModel)]=" + dashboard.dashboardGeneralSettings.globalTimeEnabled + " + >Use global time instead of widget time settings + </mat-checkbox> + </div> <!--<mat-checkbox [(ngModel)]="dashboard.displayHeader">Show name and description in dashboard</mat-checkbox>--> </div> </div> diff --git a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss b/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.scss similarity index 100% rename from ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.scss rename to ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.scss diff --git a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.ts b/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.ts similarity index 77% rename from ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.ts rename to ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.ts index 5fdce655c6..b0e8a66a6d 100644 --- a/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component.ts +++ b/ui/src/app/data-explorer/dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component.ts @@ -18,22 +18,22 @@ import { Component, Input, OnInit } from '@angular/core'; import { - DataViewDataExplorerService, Dashboard, + DataViewDataExplorerService, } from '@streampipes/platform-services'; import { DialogRef } from '@streampipes/shared-ui'; @Component({ - selector: 'sp-data-explorer-edit-data-view-dialog-component', - templateUrl: './data-explorer-edit-data-view-dialog.component.html', - styleUrls: ['./data-explorer-edit-data-view-dialog.component.scss'], + selector: 'sp-data-explorer-edit-dashboard-dialog-component', + templateUrl: './data-explorer-edit-dashboard-dialog.component.html', + styleUrls: ['./data-explorer-edit-dashboard-dialog.component.scss'], }) -export class DataExplorerEditDataViewDialogComponent implements OnInit { +export class DataExplorerEditDashboardDialogComponent implements OnInit { @Input() createMode: boolean; @Input() dashboard: Dashboard; constructor( - private dialogRef: DialogRef<DataExplorerEditDataViewDialogComponent>, + private dialogRef: DialogRef<DataExplorerEditDashboardDialogComponent>, private dashboardService: DataViewDataExplorerService, ) {} @@ -41,6 +41,12 @@ export class DataExplorerEditDataViewDialogComponent implements OnInit { if (!this.dashboard.dashboardGeneralSettings.defaultViewMode) { this.dashboard.dashboardGeneralSettings.defaultViewMode = 'grid'; } + if ( + this.dashboard.dashboardGeneralSettings.globalTimeEnabled === + undefined + ) { + this.dashboard.dashboardGeneralSettings.globalTimeEnabled = true; + } } onCancel(): void { diff --git a/ui/src/app/data-explorer/services/data-explorer-dashboard.service.ts b/ui/src/app/data-explorer/services/data-explorer-dashboard.service.ts index e7177e9fa1..dcc5bba228 100644 --- a/ui/src/app/data-explorer/services/data-explorer-dashboard.service.ts +++ b/ui/src/app/data-explorer/services/data-explorer-dashboard.service.ts @@ -24,7 +24,7 @@ import { DateRange, TimeSettings, } from '@streampipes/platform-services'; -import { DataExplorerEditDataViewDialogComponent } from '../dialogs/edit-dashboard/data-explorer-edit-data-view-dialog.component'; +import { DataExplorerEditDashboardDialogComponent } from '../dialogs/edit-dashboard/data-explorer-edit-dashboard-dialog.component'; import { DialogService, PanelType } from '@streampipes/shared-ui'; import { DataDownloadDialogComponent } from '../../core-ui/data-download-dialog/data-download-dialog.component'; @@ -32,13 +32,13 @@ import { DataDownloadDialogComponent } from '../../core-ui/data-download-dialog/ export class DataExplorerDashboardService { constructor(private dialogService: DialogService) {} - openDataViewModificationDialog(createMode: boolean, dashboard: Dashboard) { + openDashboardModificationDialog(createMode: boolean, dashboard: Dashboard) { return this.dialogService.open( - DataExplorerEditDataViewDialogComponent, + DataExplorerEditDashboardDialogComponent, { - panelType: PanelType.STANDARD_PANEL, - title: createMode ? 'New Data View' : 'Edit Data View', - width: '70vw', + panelType: PanelType.SLIDE_IN_PANEL, + title: createMode ? 'New Dashboard' : 'Edit Dashboard', + width: '60vw', data: { createMode: createMode, dashboard: dashboard, diff --git a/ui/src/app/data-explorer/services/time-selection.service.ts b/ui/src/app/data-explorer/services/time-selection.service.ts index 81090a04d2..7ac09f0c3c 100644 --- a/ui/src/app/data-explorer/services/time-selection.service.ts +++ b/ui/src/app/data-explorer/services/time-selection.service.ts @@ -18,10 +18,131 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; -import { TimeSettings } from '@streampipes/platform-services'; +import { + DateRange, + QuickTimeSelection, + TimeSelectionId, + TimeSettings, +} from '@streampipes/platform-services'; +import { + startOfDay, + startOfHour, + startOfMonth, + startOfWeek, + startOfYear, + subDays, + subHours, + subMinutes, + subMonths, + subWeeks, + subYears, +} from 'date-fns'; @Injectable({ providedIn: 'root' }) export class TimeSelectionService { + quickTimeSelections: QuickTimeSelection[] = [ + { + label: 'Last 15 min', + timeSelectionId: TimeSelectionId.LAST_15_MINUTES, + startTime: now => subMinutes(now, 15), + endTime: now => now, + }, + { + label: 'Last 1 hour', + timeSelectionId: TimeSelectionId.LAST_HOUR, + startTime: now => subHours(now, 1), + endTime: now => now, + }, + { + label: 'Last 1 day', + timeSelectionId: TimeSelectionId.LAST_DAY, + startTime: now => subDays(now, 1), + endTime: now => now, + }, + { + label: 'Last 1 week', + timeSelectionId: TimeSelectionId.LAST_WEEK, + startTime: now => subWeeks(now, 1), + endTime: now => now, + }, + { + label: 'Last 1 month', + timeSelectionId: TimeSelectionId.LAST_MONTH, + startTime: now => subMonths(now, 1), + endTime: now => now, + }, + { + label: 'Last 1 year', + timeSelectionId: TimeSelectionId.LAST_YEAR, + startTime: now => subYears(now, 1), + endTime: now => now, + }, + { + label: 'Current day', + timeSelectionId: TimeSelectionId.CURRENT_DAY, + startTime: now => startOfDay(now), + endTime: now => now, + }, + { + label: 'Current hour', + timeSelectionId: TimeSelectionId.CURRENT_HOUR, + startTime: now => startOfHour(now), + endTime: now => now, + }, + { + label: 'Current week', + timeSelectionId: TimeSelectionId.CURRENT_WEEK, + startTime: now => startOfWeek(now), + endTime: now => now, + }, + { + label: 'Current month', + timeSelectionId: TimeSelectionId.CURRENT_MONTH, + startTime: now => startOfMonth(now), + endTime: now => now, + }, + { + label: 'Current year', + timeSelectionId: TimeSelectionId.CURRENT_YEAR, + startTime: now => startOfYear(now), + endTime: now => now, + }, + ]; + + public getDateRange(quickSelection: QuickTimeSelection): DateRange { + const now = new Date(); + return { + startDate: quickSelection.startTime(now), + endDate: quickSelection.endTime(now), + }; + } + + public getDefaultTimeSettings(): TimeSettings { + return this.getTimeSettings( + TimeSelectionId.LAST_15_MINUTES, + new Date(), + ); + } + + public getTimeSettings( + timeSelectionId: TimeSelectionId, + now: Date, + ): TimeSettings { + const selection = this.getTimeSelection(timeSelectionId); + return { + startTime: selection.startTime(now).getTime(), + endTime: selection.endTime(now).getTime(), + dynamicSelection: -1, + timeSelectionId: timeSelectionId, + }; + } + + public getTimeSelection(timeSelectionId: TimeSelectionId) { + return this.quickTimeSelections.find( + s => s.timeSelectionId === timeSelectionId, + ); + } + public timeSelectionChangeSubject: Subject<TimeSettings> = new Subject<TimeSettings>();
