Skip to content

Commit

Permalink
SNRGY-3064 add stacked bar charts with custom sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
franzmueller committed Dec 10, 2024
1 parent d38c829 commit b00bb40
Show file tree
Hide file tree
Showing 13 changed files with 721 additions and 378 deletions.
19 changes: 18 additions & 1 deletion src/app/core/services/util.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class UtilService {
}


dateIsToday(dateTime: string | number): Boolean {
dateIsToday(dateTime: string | number): boolean {
const today = new Date();
today.setHours(0,0,0,0);

Expand All @@ -100,3 +100,20 @@ export class UtilService {
return date.getTime() === today.getTime();
}
}

export function hashCode(s: string): number {
let hash = 0;
let i = 0;
let chr = 0;
if (s.length === 0) {
return hash;
}
for (i = 0; i < s.length; i++) {
chr = s.charCodeAt(i);
// eslint-disable-next-line no-bitwise
hash = ((hash << 5) - hash) + chr;
// eslint-disable-next-line no-bitwise
hash |= 0; // Convert to 32bit integer
}
return hash;
}
48 changes: 46 additions & 2 deletions src/app/widgets/charts/export/charts-export.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ export class ChartsExportComponent implements OnInit, OnDestroy, AfterViewInit {
widget.properties.vAxes = this.modifiedVaxes;
}

this.chartsExportService.getChartData(widget, this.from?.toISOString(), this.to?.toISOString(), this.groupTime || undefined, this.hAxisFormat || undefined, lastOverride).subscribe((resp: ChartsModel | ErrorModel) => {
widget.properties.stacked = this.stacked;

this.chartsExportService.getChartData(widget, this.from?.toISOString(), this.to?.toISOString(), this.groupTime || undefined, this.hAxisFormat || undefined, lastOverride, this.chooseColors).subscribe((resp: ChartsModel | ErrorModel) => {
if (this.errorHandlerService.checkIfErrorExists(resp)) {
this.errorHasOccured = true;
this.errorMessage = 'No data';
Expand Down Expand Up @@ -361,11 +363,20 @@ export class ChartsExportComponent implements OnInit, OnDestroy, AfterViewInit {
return;
}
const axis = axes[$event.column - 1]; // time column
if (axis.deviceGroupMergingStrategy === ChartsExportDeviceGroupMergingStrategy.Sum && (axis.deviceGroupId !== undefined || axis.locationId !== undefined)) {
if (axis.subAxes !== undefined && axis.subAxes.length > 0) {
const cpy = JSON.parse(JSON.stringify(axis.subAxes)) as ChartsExportVAxesModel[];
cpy.forEach(sub => sub.displayOnSecondVAxis = axis.displayOnSecondVAxis);
this.modifiedVaxes = cpy;
this.stacked = true;
this.ready = false;
this.refresh();
} else if (axis.deviceGroupMergingStrategy === ChartsExportDeviceGroupMergingStrategy.Sum && (axis.deviceGroupId !== undefined || axis.locationId !== undefined)) {
// we can split this!
this.chooseColors = true;
const cpy = JSON.parse(JSON.stringify(axis)) as ChartsExportVAxesModel;
cpy.deviceGroupMergingStrategy = ChartsExportDeviceGroupMergingStrategy.Separate;
this.modifiedVaxes = [cpy];
this.stacked = true;
this.ready = false;
this.refresh();
}
Expand Down Expand Up @@ -509,6 +520,39 @@ export class ChartsExportComponent implements OnInit, OnDestroy, AfterViewInit {
}
}

private get stacked(): boolean {
const str = localStorage.getItem(this.widget.id + '_stacked');
if (str === null) {
return this.widget.properties.stacked || false;
}
return JSON.parse(str);
}

private set stacked(stacked: boolean | null) {
if (stacked === null) {
localStorage.removeItem(this.widget.id + '_stacked');
} else {
localStorage.setItem(this.widget.id + '_stacked', '' + stacked);
}
}

private get chooseColors(): boolean {
const str = localStorage.getItem(this.widget.id + '_chooseColors');
if (str === null) {
return false;
}
return JSON.parse(str);
}

private set chooseColors(chooseColors: boolean | null) {
if (chooseColors === null) {
localStorage.removeItem(this.widget.id + '_chooseColors');
} else {
localStorage.setItem(this.widget.id + '_chooseColors', '' + chooseColors);
}
}


getCustomIcons(header: boolean): { icons: string[]; disabled: boolean[]; tooltips: string[] } {
const res = { icons: [] as string[], disabled: [] as boolean[], tooltips: [] as string[] };

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2024 InfAI (CC SES)
*
* Licensed 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.
*/

.column {
display: flex;
flex-direction: column;
}

.row {
display: flex;
flex-direction: row;
}

.w-100 {
width: 100%;
}

.w-50 {
width: 50%;
}

.pb {
padding-bottom: 0.5em;
}

.mat-mdc-card-title {
font-size: 16px;
}

.mat-mdc-card-header {
display: flex;
justify-content: space-between;
}

.cpy-delete {
margin-top: -0.75rem;
}

.mat-mdc-form-field {
padding-right: 1rem;
}

.rules-btn-spacer {
padding-left: 2rem;
margin-top: -0.25rem;
}

.drop-zone {
height: 4em;
border-top: dotted;
text-align: center;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<li class="mat-tree-node" cdkDrag [cdkDragData]="element" (cdkDragStarted)="dragStart.emit(null)"
(cdkDragEnded)="dragEnd.emit(null)">
<button cdkDragHandle mat-icon-button color="accent" disableRipple class="tree-inline" [hidden]="!enableDragDrop">
<mat-icon>drag_handle</mat-icon>
</button>
<button mat-icon-button (click)="treeControl?.toggle(element)" class="nested-tree-inline">
<mat-icon class="mat-icon-rtl-mirror">
{{(treeControl !== undefined ? treeControl!.isExpanded(element) : false) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>

<div *ngIf="element !== undefined" class="column w-100">
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title [ngClass]="{'pb': !expanded}" (click)="treeControl?.toggle(element)">
{{element.exportName}} - {{element.valueName}} ({{element.valueType}})
</mat-card-title>
<div *ngIf="!subElement" class="cpy-delete">
<button mat-icon-button *ngIf="element.isDuplicate === false || element.isDuplicate === undefined"
(click)="copyClicked.emit(null)">
<mat-icon>content_copy</mat-icon>
</button>
<button mat-icon-button *ngIf="element.isDuplicate === true" (click)="deleteClicked.emit(null)">
<mat-icon>delete</mat-icon>
</button>
</div>
</mat-card-header>
<mat-card-content *ngIf="expanded">
<div class="row w-100">
<div>
<mat-form-field color="accent" appearance="outline">
<mat-label>Value Alias</mat-label>
<input type="text" matInput [(ngModel)]="element.valueAlias">
<mat-error senergyError label="Value Alias"></mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field color="accent" appearance="outline">
<mat-label>Color</mat-label>
<input type="text" matInput placeholder="e.g. red, #004411" [(ngModel)]="element.color">
<mat-error senergyError label="Color"></mat-error>
</mat-form-field>
</div>
<div [hidden]="groupTypeIsDifference">
<mat-form-field color="accent" appearance="outline">
<mat-label>Math</mat-label>
<input type="text" matInput placeholder="e.g. / 1000" [(ngModel)]="element.math"
[disabled]="groupTypeIsDifference">
<mat-error senergyError label="Math"></mat-error>
</mat-form-field>
</div>
</div>
<div class="row w-100">

<div>
<mat-form-field color="accent" appearance="outline">
<mat-label>Filter</mat-label>
<mat-select [compareWith]="compareFilterTypes" [(ngModel)]="element.filterType"
(valueChange)="filerTypeSelected(element)">
<mat-option [value]="undefined">None</mat-option>
<mat-option *ngFor="let option of [ '=', '!=', '>', '>=', '<' ,'<=']" [value]="option">
{{option}}</mat-option>
</mat-select>
<mat-error senergyError label="Filter"></mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field color="accent" appearance="outline">
<mat-label>Filter Value</mat-label>
<input [type]="element.valueType === 'string' ? 'text' : 'number'" matInput
placeholder="e.g. 1000, filter" [disabled]="element.filterType === undefined"
[(ngModel)]="element.filterValue">
<mat-error senergyError label="Filter Value"></mat-error>
</mat-form-field>
</div>
</div>
<div class="row w-100">
<div [hidden]="getTags(element).size === 0">
<mat-form-field color="accent" appearance="outline">
<mat-label>Tags</mat-label>
<senergy-select-search placeholder="Choose Tags" [multiple]="true"
[disabled]="getTags(element).size === 0" [options]="getTags(element)"
[getOptionDisabled]="getTagOptionDisabledFunction(element)"
[getOptionValue]="getTagValue" [(ngModel)]="element.tagSelection"
useOptionViewProperty="value">
</senergy-select-search>
<mat-error senergyError label="Tags"></mat-error>
</mat-form-field>
</div>
<div [hidden]="element.criteria === undefined">
<mat-form-field color="accent" appearance="outline">
<mat-label>Merging Strategy</mat-label>
<mat-select placeholder="Choose Tags" [disabled]="element.criteria === undefined"
[(ngModel)]="element.deviceGroupMergingStrategy">
<mat-option
[value]="chartsExportDeviceGroupMergingStrategy.Separate">Separate</mat-option>
<mat-option [value]="chartsExportDeviceGroupMergingStrategy.Merge">Merge</mat-option>
<mat-option *ngIf="groupType !== undefined"
[value]="chartsExportDeviceGroupMergingStrategy.Sum">Sum</mat-option>
</mat-select>
<mat-error senergyError label="Merging Strategy"></mat-error>
</mat-form-field>
</div>
</div>
<div class="row w-100">
<div [hidden]="subElement">
<mat-checkbox [(ngModel)]="element.displayOnSecondVAxis"> Use second Y-Axis
</mat-checkbox>
</div>
<div [ngClass]="{'rules-btn-spacer': !subElement}">
<button mat-icon-button (click)="listRules(element)">
<mat-icon color="accent">swap_horiz</mat-icon>
</button>
</div>
</div>
<div class="column w-100" cdkDropList (cdkDropListDropped)="dropped.emit({$event, target: element})"
[id]="getId()" [cdkDropListConnectedTo]="connectedNodes(element)">
<senergy-axis-config *ngFor="let child of element.subAxes" class="w-100" [element]="child"
[treeControl]="treeControl" [groupTypeIsDifference]="groupTypeIsDifference"
[userHasUpdatePropertiesAuthorization]="userHasUpdatePropertiesAuthorization"
[subElement]="true" [exportTags]="exportTags" [groupType]="groupType"
(dragStart)="dragStart.emit(null)" (dragEnd)="dragEnd.emit(null)"
[enableDragDrop]="enableDragDrop" (dropped)="dropped.emit($event)"
[connectedNodes]="connectedNodes" [dragging]="dragging">
</senergy-axis-config>
<div [hidden]="!dragging" style="height: 3rem">
</div>
</div>
</mat-card-content>
</mat-card>
</div>
</li>
Loading

0 comments on commit b00bb40

Please sign in to comment.