ADD: General Diagrams Component
This commit is contained in:
parent
af273889d8
commit
651aa1b023
@ -27,7 +27,7 @@
|
||||
</mat-drawer>
|
||||
<mat-drawer-content>
|
||||
<app-wettter-overview></app-wettter-overview>
|
||||
<app-temperature></app-temperature>
|
||||
<app-diagrams></app-diagrams>
|
||||
</mat-drawer-content>
|
||||
</mat-drawer-container>
|
||||
|
||||
|
||||
@ -7,10 +7,11 @@ import {MatDrawer, MatDrawerContainer, MatDrawerContent} from '@angular/material
|
||||
import {StationList} from './station-list/station-list';
|
||||
import {WettterOverview} from './components/wettter-overview/wettter-overview';
|
||||
import {TemperatureComponent} from './components/diagrams/temperature/temperature';
|
||||
import {Diagrams} from './components/diagrams/diagrams';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, MatToolbar, MatIcon, MatIconButton, RouterLink, MatButton, MatDrawerContainer, MatDrawer, MatDrawerContent, StationList, WettterOverview, TemperatureComponent],
|
||||
imports: [RouterOutlet, MatToolbar, MatIcon, MatIconButton, RouterLink, MatButton, MatDrawerContainer, MatDrawer, MatDrawerContent, StationList, WettterOverview, TemperatureComponent, Diagrams],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.css'
|
||||
})
|
||||
|
||||
3
src/app/components/diagrams/diagrams.css
Normal file
3
src/app/components/diagrams/diagrams.css
Normal file
@ -0,0 +1,3 @@
|
||||
.diagrams-container {
|
||||
margin: 2em;
|
||||
}
|
||||
3
src/app/components/diagrams/diagrams.html
Normal file
3
src/app/components/diagrams/diagrams.html
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="diagrams-container">
|
||||
<app-temperature #temperature [measurements]="measurements"></app-temperature>
|
||||
</div>
|
||||
22
src/app/components/diagrams/diagrams.spec.ts
Normal file
22
src/app/components/diagrams/diagrams.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Diagrams } from './diagrams';
|
||||
|
||||
describe('Diagrams', () => {
|
||||
let component: Diagrams;
|
||||
let fixture: ComponentFixture<Diagrams>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Diagrams],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Diagrams);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
65
src/app/components/diagrams/diagrams.ts
Normal file
65
src/app/components/diagrams/diagrams.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {ChangeDetectorRef, Component, Input, ViewChild} from '@angular/core';
|
||||
import {TemperatureComponent, TimeResolution} from './temperature/temperature';
|
||||
import {DefaultService, MeasurementListResponse, MeasurementResolution, StationListResponse} from '../../api';
|
||||
import {ApexOptions} from 'ng-apexcharts';
|
||||
import {addDays, addHours, addMonths, addYears, startOfDay} from 'date-fns';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diagrams',
|
||||
imports: [
|
||||
TemperatureComponent
|
||||
],
|
||||
templateUrl: './diagrams.html',
|
||||
styleUrl: './diagrams.css',
|
||||
})
|
||||
export class Diagrams {
|
||||
timeResolution: TimeResolution = '7d';
|
||||
@Input() activeStations: StationListResponse[] = [];
|
||||
@ViewChild('temperature') temperatureDiagram?: TemperatureComponent
|
||||
resolution: MeasurementResolution = MeasurementResolution.Daily;
|
||||
measurements: MeasurementListResponse | undefined;
|
||||
|
||||
constructor(private api: DefaultService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.activeStations.length === 0) {
|
||||
this.api.listStationsStationsListGet().subscribe(stations => {
|
||||
this.activeStations = stations;
|
||||
this.fetchMeasurements();
|
||||
});
|
||||
} else {
|
||||
this.fetchMeasurements();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
get timeIntervalStart(): string | undefined {
|
||||
const now = new Date();
|
||||
const map: Record<TimeResolution, Date | undefined> = {
|
||||
'24h': addHours(now, -24),
|
||||
'7d': startOfDay(addDays(now, -7)),
|
||||
'30d': startOfDay(addMonths(now, -1)),
|
||||
'1Y': startOfDay(addYears(now, -1)),
|
||||
'All': undefined
|
||||
};
|
||||
return map[this.timeResolution]?.toISOString();
|
||||
}
|
||||
|
||||
fetchMeasurements() {
|
||||
const stationIds = this.activeStations.map(s => s.id);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
this.api.getMeasurementsMeasurementsGet(
|
||||
stationIds, undefined, this.timeIntervalStart, now, this.resolution
|
||||
).subscribe(data => {
|
||||
this.measurements = data;
|
||||
this.buildCharts();
|
||||
});
|
||||
}
|
||||
|
||||
buildCharts() {
|
||||
this.temperatureDiagram?.buildChart(this.measurements!)
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,22 @@
|
||||
@if(measurements) {
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>Temperaturverlauf</mat-card-title>
|
||||
<mat-card-title>Temperaturverlauf
|
||||
<mat-button-toggle-group name="fontStyle" aria-label="Font Style" (valueChange)="switchChartType($event)" [value]="'line'">
|
||||
<mat-button-toggle value="line">
|
||||
<mat-icon>line_axis</mat-icon>
|
||||
Line
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle value="bar">
|
||||
<mat-icon>bar_chart</mat-icon>
|
||||
Balken
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle value="area">
|
||||
<mat-icon>area_chart</mat-icon>
|
||||
Fläche
|
||||
</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<apx-chart
|
||||
@ -11,6 +26,7 @@
|
||||
[yaxis]="chartOptions.yaxis!"
|
||||
[stroke]="chartOptions.stroke!"
|
||||
[grid]="chartOptions.grid!"
|
||||
[dataLabels]="chartOptions.dataLabels!"
|
||||
[tooltip]="chartOptions.tooltip!"
|
||||
[legend]="chartOptions.legend!"
|
||||
[theme]="chartOptions.theme!"
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
|
||||
import {ChangeDetectorRef, Component, Input, OnInit, ViewChild} from '@angular/core';
|
||||
import {DefaultService, MeasurementListResponse, MeasurementResolution, StationListResponse} from '../../../api';
|
||||
import {addDays, addHours, addMonths, addYears, startOfDay} from 'date-fns';
|
||||
import {ApexOptions, ChartComponent} from 'ng-apexcharts';
|
||||
import {ApexOptions, ChartComponent, ChartType} from 'ng-apexcharts';
|
||||
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
|
||||
import {MatProgressSpinner} from '@angular/material/progress-spinner';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
import {MatButtonToggle, MatButtonToggleGroup} from '@angular/material/button-toggle';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
|
||||
export type TimeResolution = '24h' | '7d' | '30d' | '1Y' | 'All'
|
||||
|
||||
@ -15,70 +18,64 @@ export type TimeResolution = '24h' | '7d' | '30d' | '1Y' | 'All'
|
||||
MatCardContent,
|
||||
MatCardHeader,
|
||||
MatCardTitle,
|
||||
MatButtonToggleGroup,
|
||||
MatButtonToggle,
|
||||
MatIcon,
|
||||
],
|
||||
templateUrl: './temperature.html',
|
||||
styleUrl: './temperature.css',
|
||||
})
|
||||
export class TemperatureComponent implements OnInit {
|
||||
@Input() timeResolution: TimeResolution = '7d';
|
||||
@Input() activeStations: StationListResponse[] = [];
|
||||
export class TemperatureComponent {
|
||||
|
||||
resolution: MeasurementResolution = MeasurementResolution.Daily;
|
||||
measurements: MeasurementListResponse | undefined;
|
||||
@Input() measurements: MeasurementListResponse | undefined;
|
||||
chartOptions: ApexOptions = {};
|
||||
chartType: ChartType = 'line';
|
||||
|
||||
constructor(private api: DefaultService, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.activeStations.length === 0) {
|
||||
this.api.listStationsStationsListGet().subscribe(stations => {
|
||||
this.activeStations = stations;
|
||||
this.fetchMeasurements();
|
||||
});
|
||||
} else {
|
||||
this.fetchMeasurements();
|
||||
switchChartType(type: 'bar' | 'area' | 'line') {
|
||||
this.chartType = type;
|
||||
this.buildChart(this.measurements!)
|
||||
}
|
||||
}
|
||||
|
||||
get timeIntervalStart(): string | undefined {
|
||||
const now = new Date();
|
||||
const map: Record<TimeResolution, Date | undefined> = {
|
||||
'24h': addHours(now, -24),
|
||||
'7d': startOfDay(addDays(now, -7)),
|
||||
'30d': startOfDay(addMonths(now, -1)),
|
||||
'1Y': startOfDay(addYears(now, -1)),
|
||||
'All': undefined
|
||||
};
|
||||
return map[this.timeResolution]?.toISOString();
|
||||
}
|
||||
|
||||
fetchMeasurements() {
|
||||
const stationIds = this.activeStations.map(s => s.id);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
this.api.getMeasurementsMeasurementsGet(
|
||||
stationIds, undefined, this.timeIntervalStart, now, this.resolution
|
||||
).subscribe(data => {
|
||||
this.measurements = data;
|
||||
this.buildChart();
|
||||
});
|
||||
}
|
||||
|
||||
buildChart() {
|
||||
public buildChart(measurements: MeasurementListResponse) {
|
||||
this.measurements = measurements;
|
||||
if (!this.measurements) return;
|
||||
|
||||
const isBar = this.chartType === 'bar';
|
||||
const isArea = this.chartType === 'area';
|
||||
|
||||
this.chartOptions = {
|
||||
chart: {
|
||||
type: 'line',
|
||||
type: this.chartType,
|
||||
height: 280,
|
||||
toolbar: { show: false },
|
||||
animations: { enabled: true },
|
||||
background: 'transparent',
|
||||
stacked: false,
|
||||
},
|
||||
theme: { mode: 'dark' },
|
||||
dataLabels: {
|
||||
enabled: false, // <- das hat gefehlt
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
width: 2,
|
||||
width: isBar ? 0 : 2,
|
||||
},
|
||||
fill: {
|
||||
type: isArea ? 'gradient' : 'solid',
|
||||
opacity: isArea ? 0.3 : (isBar ? 0.85 : 1),
|
||||
gradient: isArea ? {
|
||||
shadeIntensity: 1,
|
||||
opacityFrom: 0.4,
|
||||
opacityTo: 0.05,
|
||||
stops: [0, 100]
|
||||
} : undefined,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
columnWidth: '70%',
|
||||
borderRadius: 3,
|
||||
}
|
||||
},
|
||||
series: this.measurements.stations.map(s => ({
|
||||
name: `${s.station.name} ${s.indoor ? '(innen)' : '(außen)'}`,
|
||||
@ -108,13 +105,15 @@ export class TemperatureComponent implements OnInit {
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
shared: true,
|
||||
intersect: false,
|
||||
x: { format: 'dd.MM.yyyy HH:mm' },
|
||||
y: { formatter: (val: number) => `${val.toFixed(1)} °C` }
|
||||
y: { formatter: (val: number) => `${val?.toFixed(1)} °C` }
|
||||
},
|
||||
legend: {
|
||||
labels: { colors: '#9e9e9e' }
|
||||
},
|
||||
};
|
||||
this.cdr.detectChanges()
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user