diff --git a/src/app/app.html b/src/app/app.html index 390d15d..05085a5 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -27,7 +27,7 @@ - + diff --git a/src/app/app.ts b/src/app/app.ts index f03e610..e82070b 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -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' }) diff --git a/src/app/components/diagrams/diagrams.css b/src/app/components/diagrams/diagrams.css new file mode 100644 index 0000000..37b3da8 --- /dev/null +++ b/src/app/components/diagrams/diagrams.css @@ -0,0 +1,3 @@ +.diagrams-container { + margin: 2em; +} diff --git a/src/app/components/diagrams/diagrams.html b/src/app/components/diagrams/diagrams.html new file mode 100644 index 0000000..6861fe7 --- /dev/null +++ b/src/app/components/diagrams/diagrams.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/components/diagrams/diagrams.spec.ts b/src/app/components/diagrams/diagrams.spec.ts new file mode 100644 index 0000000..1d2d162 --- /dev/null +++ b/src/app/components/diagrams/diagrams.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Diagrams } from './diagrams'; + +describe('Diagrams', () => { + let component: Diagrams; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Diagrams], + }).compileComponents(); + + fixture = TestBed.createComponent(Diagrams); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/diagrams/diagrams.ts b/src/app/components/diagrams/diagrams.ts new file mode 100644 index 0000000..33fb4b6 --- /dev/null +++ b/src/app/components/diagrams/diagrams.ts @@ -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 = { + '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!) + } +} diff --git a/src/app/components/diagrams/temperature/temperature.html b/src/app/components/diagrams/temperature/temperature.html index 6da1947..513bdae 100644 --- a/src/app/components/diagrams/temperature/temperature.html +++ b/src/app/components/diagrams/temperature/temperature.html @@ -1,7 +1,22 @@ @if(measurements) { - Temperaturverlauf + Temperaturverlauf + + + line_axis + Line + + + bar_chart + Balken + + + area_chart + Fläche + + + { - 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 = { - '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(); } }