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 9f3cff7d29..f061e5daff 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 @@ -21,7 +21,50 @@ export interface TimeSettings { endTime: number; // deprecated dynamicSelection?: 15 | 60 | 1440 | 10080 | 43800 | 525600 | -1; - timeSelectionId?: TimeSelectionId; + timeSelectionId?: string; +} + +export class TimeSelectionConstants { + static CUSTOM = 'custom'; + static LAST_15_MINUTES = 'last-15-minutes'; + static LAST_HOUR = 'last-hour'; + static CURRENT_HOUR = 'current-hour'; + static LAST_DAY = 'last-day'; + static CURRENT_DAY = 'current-day'; + static LAST_WEEK = 'last-week'; + static CURRENT_WEEK = 'current-week'; + static LAST_MONTH = 'last-month'; + static CURRENT_MONTH = 'current-month'; + static LAST_YEAR = 'last-year'; + static CURRENT_YEAR = 'current-year'; + + static getLegacyTimeSelectionID(legacyID: number) { + if (legacyID === 0) { + return TimeSelectionConstants.CUSTOM; + } else if (legacyID === 1) { + return TimeSelectionConstants.LAST_15_MINUTES; + } else if (legacyID === 2) { + return TimeSelectionConstants.LAST_HOUR; + } else if (legacyID === 3) { + return TimeSelectionConstants.CURRENT_HOUR; + } else if (legacyID === 4) { + return TimeSelectionConstants.LAST_DAY; + } else if (legacyID === 5) { + return TimeSelectionConstants.CURRENT_DAY; + } else if (legacyID === 6) { + return TimeSelectionConstants.LAST_WEEK; + } else if (legacyID === 7) { + return TimeSelectionConstants.CURRENT_WEEK; + } else if (legacyID === 8) { + return TimeSelectionConstants.LAST_MONTH; + } else if (legacyID === 9) { + return TimeSelectionConstants.CURRENT_MONTH; + } else if (legacyID === 10) { + return TimeSelectionConstants.LAST_YEAR; + } else if (legacyID === 11) { + return TimeSelectionConstants.CURRENT_YEAR; + } + } } export interface WidgetTimeSettings { @@ -34,6 +77,7 @@ export interface TimeString { startTime: string; endDate: string; endTime: string; + sameDay: boolean; } export enum TimeSelectionId { @@ -53,7 +97,7 @@ export enum TimeSelectionId { export interface QuickTimeSelection { label: string; - timeSelectionId: TimeSelectionId; + timeSelectionId: string; startTime: (now: Date) => Date; endTime: (now: Date) => Date; addDividerAfter?: boolean; diff --git a/ui/projects/streampipes/shared-ui/package.json b/ui/projects/streampipes/shared-ui/package.json index f2f8f4d9b5..6f8f27f9ef 100644 --- a/ui/projects/streampipes/shared-ui/package.json +++ b/ui/projects/streampipes/shared-ui/package.json @@ -10,7 +10,8 @@ "@angular/material": "^17.3.3", "@angular/router": "^17.3.3", "@streampipes/platform-services": "0.0.1", - "rxjs": "^7.5.7" + "rxjs": "^7.5.7", + "date-fns": "^3.6.0" }, "dependencies": { "tslib": "^2.6.2" diff --git a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.html similarity index 70% rename from ui/src/app/data-explorer/components/time-selector/time-range-selector.component.html rename to ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.html index eb51825326..a959404dca 100644 --- a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.html @@ -30,25 +30,28 @@ color="accent" [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" - matTooltip="Modify time" + [matTooltip]="labels.timeRangeSelectorTooltip" data-cy="time-selector-menu" (menuClosed)="menuTrigger.closeMenu()" > -
-
- {{ - timeString.startDate - }}  - {{ +
+ {{ timeString.startDate }} + +  {{ timeString.startTime }} -
- - -
- {{ timeString.endDate }}  - {{ timeString.endTime }} + +
+  -  + {{ timeString.endDate }} + +  {{ + timeString.endTime + }} +
@@ -63,7 +66,11 @@ @@ -71,12 +78,12 @@
diff --git a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.scss similarity index 96% rename from ui/src/app/data-explorer/components/time-selector/time-range-selector.component.scss rename to ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.scss index aef8593a37..19107dc1c1 100644 --- a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.scss +++ b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.scss @@ -16,8 +16,6 @@ * */ -@import '../../../../scss/variables'; - .start-date-margin { margin-right: 5px; } @@ -65,8 +63,9 @@ } .formatted-datetime { - display: inline; + display: inline-flex; background: var(--color-bg-2); border-radius: 5px; padding: 5px; + white-space: nowrap; } diff --git a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.ts similarity index 69% rename from ui/src/app/data-explorer/components/time-selector/time-range-selector.component.ts rename to ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.ts index a2b663d5b3..ad8ef22523 100644 --- a/ui/src/app/data-explorer/components/time-selector/time-range-selector.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-range-selector.component.ts @@ -28,13 +28,16 @@ import { ViewEncapsulation, } from '@angular/core'; import { - TimeSelectionId, + QuickTimeSelection, + TimeSelectionConstants, TimeSettings, TimeString, } from '@streampipes/platform-services'; import { MatMenuTrigger } from '@angular/material/menu'; import { TimeSelectionService } from '../../services/time-selection.service'; import { TimeRangeSelectorMenuComponent } from './time-selector-menu/time-selector-menu.component'; +import { TimeSelectorLabel } from './time-selector.model'; +import { differenceInMilliseconds, isSameDay } from 'date-fns'; @Component({ selector: 'sp-time-range-selector', @@ -55,13 +58,41 @@ export class TimeRangeSelectorComponent implements OnInit, OnChanges { @Input() showTimeSelector = true; + @Input() + enableTimeChange = true; + + @Input() + maxDayRange = 0; + + @Input() + quickSelections: QuickTimeSelection[]; + + @Input() + labels: TimeSelectorLabel = { + quickSelectionLabel: 'Quick Selection', + customLabel: 'Custom', + maxDayRangeErrorLabel: + 'Maximum of ${this.maxDayRange} days can be displayed. Please select a smaller range.', + timeRangeSelectorTooltip: 'Modify time range', + }; + simpleTimeString: string = ''; timeString: TimeString; timeStringMode: 'simple' | 'advanced' = 'simple'; + dateFormat: Intl.DateTimeFormatOptions = { + weekday: 'short', + year: 'numeric', + month: 'numeric', + day: 'numeric', + }; constructor(private timeSelectionService: TimeSelectionService) {} ngOnInit() { + if (!this.quickSelections) { + this.quickSelections = + this.timeSelectionService.defaultQuickTimeSelections; + } this.createDateString(); } @@ -95,6 +126,7 @@ export class TimeRangeSelectorComponent implements OnInit, OnChanges { updateTimeSettingsAndReload() { this.timeSelectionService.updateTimeSettings( + this.quickSelections, this.timeSettings, new Date(), ); @@ -105,14 +137,19 @@ export class TimeRangeSelectorComponent implements OnInit, OnChanges { } private changeTimeByInterval(func: (a: number, b: number) => number) { - const difference = - this.timeSettings.endTime - this.timeSettings.startTime; - const newStartTime = func(this.timeSettings.startTime, difference); - const newEndTime = func(this.timeSettings.endTime, difference); + const timeDiff = + (differenceInMilliseconds( + this.timeSettings.startTime, + this.timeSettings.endTime, + ) - + 1) * + -1; + const newStartTime = func(this.timeSettings.startTime, timeDiff); + const newEndTime = func(this.timeSettings.endTime, timeDiff); this.timeSettings.startTime = newStartTime; this.timeSettings.endTime = newEndTime; - this.timeSettings.timeSelectionId = TimeSelectionId.CUSTOM; + this.timeSettings.timeSelectionId = TimeSelectionConstants.CUSTOM; this.timeSelectorMenu.triggerDisplayUpdate(); this.createDateString(); this.reloadData(); @@ -126,8 +163,11 @@ export class TimeRangeSelectorComponent implements OnInit, OnChanges { } createDateString(): void { - if (this.timeSettings.timeSelectionId !== TimeSelectionId.CUSTOM) { + if ( + this.timeSettings.timeSelectionId !== TimeSelectionConstants.CUSTOM + ) { this.simpleTimeString = this.timeSelectionService.getTimeSelection( + this.quickSelections, this.timeSettings.timeSelectionId, ).label; this.timeStringMode = 'simple'; @@ -135,12 +175,20 @@ export class TimeRangeSelectorComponent implements OnInit, OnChanges { const startDate = new Date(this.timeSettings.startTime); const endDate = new Date(this.timeSettings.endTime); this.timeString = { - startDate: startDate.toLocaleDateString(), - endDate: endDate.toLocaleDateString(), + startDate: this.formatDate(startDate), + endDate: this.formatDate(endDate), startTime: startDate.toLocaleTimeString(), endTime: endDate.toLocaleTimeString(), + sameDay: isSameDay(startDate, endDate), }; + this.timeStringMode = 'advanced'; } } + + private formatDate(date: Date): string { + return this.enableTimeChange + ? date.toLocaleDateString() + : date.toLocaleDateString(navigator.language, this.dateFormat); + } } 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/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html similarity index 86% rename from ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html rename to ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html index 1564d9b5dc..38fba4f86d 100644 --- a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.html @@ -26,7 +26,7 @@ -
+
+
+ {{ dateRangeString }} +
+
+ {{ + labels.maxDayRangeErrorLabel.replace( + '${this.maxDayRange}', + maxDayRange.toString() + ) + }} +
diff --git a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss similarity index 91% rename from ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss rename to ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss index ce53cf59ea..d181ae05dd 100644 --- a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss +++ b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.scss @@ -20,3 +20,8 @@ font-family: inherit; font-size: 11pt; } + +.max-date-range-error { + font-size: 9pt; + color: var(--color-warn); +} 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/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts similarity index 63% rename from ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts rename to ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts index 2129a521c9..5843f03a68 100644 --- a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/custom-time-range-selection/custom-time-range-selection.component.ts @@ -17,12 +17,17 @@ */ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { TimeSelectionId, TimeSettings } from '@streampipes/platform-services'; +import { + TimeSelectionConstants, + TimeSettings, +} from '@streampipes/platform-services'; import { DateRange, DefaultMatCalendarRangeStrategy, MatRangeDateSelectionModel, } from '@angular/material/datepicker'; +import { differenceInDays, endOfDay, startOfDay } from 'date-fns'; +import { TimeSelectorLabel } from '../../time-selector.model'; @Component({ selector: 'sp-custom-time-range-selection', @@ -31,6 +36,15 @@ import { }) export class CustomTimeRangeSelectionComponent implements OnInit { @Input() timeSettings: TimeSettings; + + @Input() labels: TimeSelectorLabel; + + @Input() + enableTimeChange: boolean; + + @Input() + maxDayRange: number; + @Output() timeSettingsEmitter = new EventEmitter(); currentStartDate: string; @@ -39,6 +53,9 @@ export class CustomTimeRangeSelectionComponent implements OnInit { currentEndTime: string; currentDateRange: DateRange; dateSelectionComplete = false; + dateRangeString: string; + + maxDateRangeError = false; constructor( private readonly selectionModel: MatRangeDateSelectionModel, @@ -75,13 +92,19 @@ export class CustomTimeRangeSelectionComponent implements OnInit { updateDateStrings(): void { this.currentStartDate = this.formatDate(this.currentDateRange.start); this.currentEndDate = this.formatDate(this.currentDateRange.end); + this.dateRangeString = `${this.currentStartDate} - ${this.currentEndDate}`; } formatDate(date: Date): string { - return date?.toLocaleDateString() || '-'; + if (this.enableTimeChange === true) { + return date?.toLocaleDateString() || '-'; + } else { + return date?.toLocaleDateString() || ' '; + } } onDateChange(selectedDate: Date): void { + this.maxDateRangeError = false; const newSelection = this.selectionStrategy.selectionFinished( selectedDate, this.selectionModel.selection, @@ -91,16 +114,40 @@ export class CustomTimeRangeSelectionComponent implements OnInit { newSelection.start, newSelection.end, ); - this.dateSelectionComplete = this.selectionModel.isComplete(); this.updateDateStrings(); + const daysDiff = differenceInDays(newSelection.end, newSelection.start); + if (this.selectionModel.isComplete()) { + if (this.maxDayRange === 0 || daysDiff + 1 <= this.maxDayRange) { + this.dateSelectionComplete = true; + if (!this.enableTimeChange) { + this.saveSelection(); + } + } else { + this.maxDateRangeError = true; + this.dateSelectionComplete = false; + } + } } 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; + if (this.enableTimeChange === true) { + 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(); + } else { + this.timeSettings.startTime = startOfDay( + this.currentDateRange.start, + ).getTime(); + this.timeSettings.endTime = endOfDay( + this.currentDateRange.end, + ).getTime(); + } + + this.timeSettings.timeSelectionId = TimeSelectionConstants.CUSTOM; this.timeSettingsEmitter.emit(this.timeSettings); } diff --git a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/time-selector-menu.component.html similarity index 84% rename from ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.html rename to ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/time-selector-menu.component.html index e53550aa43..2bfda05f10 100644 --- a/ui/src/app/data-explorer/components/time-selector/time-selector-menu/time-selector-menu.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/time-selector/time-selector-menu/time-selector-menu.component.html @@ -16,11 +16,13 @@ ~ --> -