This commit is contained in:
Peter Rossa
2023-05-10 12:23:25 +02:00
commit af29efa0f3
131 changed files with 16889 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from 'src/app/components/dashboard/dashboard.component';
import { LoginComponent } from './components/login/login.component';
const routes: Routes = [
{ path: 'dashboard', component: DashboardComponent },
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: '**', redirectTo: '/dashboard', pathMatch: 'full' },
// { path: 'login', component: LoginComponent },
// { path: '', component: LoginComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

View File

@@ -0,0 +1,3 @@
<app-loader></app-loader>
<app-header></app-header>
<router-outlet></router-outlet>

View File

@@ -0,0 +1,35 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'rossa-tech-cli'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('rossa-tech-cli');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('rossa-tech-cli app is running!');
});
});

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'rossa-tech-cli';
}

View File

@@ -0,0 +1,91 @@
import { NgModule } from '@angular/core';
import { BrowserModule, DomSanitizer } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DashboardComponent } from './components/dashboard/dashboard.component';
import { MetersComponent } from './components/meters/meters.component';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { LoaderComponent } from './components/loader/loader.component';
import { HeaderComponent } from './components/header/header.component';
import { MeterDataListComponent } from './components/subcomponents/meter-data-list/meter-data-list.component';
import { MatGridListModule } from '@angular/material/grid-list';
import { ConsumptionLastYearComponent } from './components/subcomponents/consumption-last-year/consumption-last-year.component';
import { MeterDataWrapperComponent } from './components/subcomponents/meter-data-wrapper/meter-data-wrapper.component';
import { MeterDataAddDialogComponent } from './components/dialogs/meter-data-add-dialog/meter-data-add-dialog.component';
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
import { LoginComponent } from './components/login/login.component';
import { LogoutComponent } from './components/logout/logout.component';
// import { HttpInterceptorService } from './components/interceptors/httpInterceptor.service';
@NgModule({
declarations: [
AppComponent,
DashboardComponent,
MetersComponent,
LoaderComponent,
HeaderComponent,
MeterDataListComponent,
ConsumptionLastYearComponent,
MeterDataWrapperComponent,
MeterDataAddDialogComponent,
LoginComponent,
LogoutComponent,
],
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
FormsModule,
AppRoutingModule,
MatCardModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatTableModule,
MatProgressBarModule,
MatToolbarModule,
MatSidenavModule,
MatIconModule,
MatListModule,
MatGridListModule,
MatDialogModule,
MatButtonModule,
ReactiveFormsModule,
MatDatepickerModule,
MatNativeDateModule,
],
providers: [
MatDatepickerModule,
// {
// provide: HTTP_INTERCEPTORS,
// useClass: HttpInterceptorService,
// multi: true,
// },
],
bootstrap: [AppComponent],
})
export class AppModule {
constructor(matIconRegistry: MatIconRegistry, domSanitizer: DomSanitizer) {
matIconRegistry.addSvgIconSet(
domSanitizer.bypassSecurityTrustResourceUrl('./assets/mdi.svg')
);
}
}

View File

@@ -0,0 +1,21 @@
<div class="content">
<div class="content-header">
content header
<p>dashboard works!</p>
</div>
<button mat-raised-button color="primary" (click)="addMeterData()">addMeterData</button>
<mat-grid-list cols="2" rowHeight="400px">
<mat-grid-tile>
<app-meter-data-wrapper class="full-width" [meterData]="meterDataEnergyDTO"
[type]="usageTypes.ENERGY"></app-meter-data-wrapper>
</mat-grid-tile>
<mat-grid-tile>
<app-meter-data-wrapper class="full-width" [meterData]="meterDataWaterDTO"
[type]="usageTypes.WATER"></app-meter-data-wrapper>
</mat-grid-tile>
</mat-grid-list>
</div>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DashboardComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,113 @@
import { Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { LoaderService } from 'src/app/components/loader/loader.service';
import { MeterDataAddDialogComponent } from 'src/app/components/dialogs/meter-data-add-dialog/meter-data-add-dialog.component';
import { DatabaseService } from 'src/app/services/database.service';
import { GlobalService } from 'src/app/services/global.service';
import { UsageType } from 'src/app/models/UsageType';
import { MeterData } from 'src/app/models/Meterdata';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
})
export class DashboardComponent {
usageTypes = UsageType;
// meterDataEnergy1Hem: MeterData[] = [];
// meterDataEnergy1Log: MeterData[] = [];
meterDataEnergy: MeterData[] = [];
meterDataWater: MeterData[] = [];
// meterDataEnergyDTO1Log: MeterData[] = [];
// meterDataEnergyDTO1Hem: MeterData[] = [];
meterDataEnergyDTO: MeterData[] = [];
meterDataWaterDTO: MeterData[] = [];
energyAverageAmountLastYear1Log: number = 0;
energyAverageAmountLastYear1Hem: number = 0;
waterAverageAmountLastYear: number = 0;
displayedColumns: string[] = ['date', 'amount', 'meter'];
constructor(
private dataService: DatabaseService,
private loaderService: LoaderService,
private globalService: GlobalService,
private dialog: MatDialog
) {
this.loadMeterData();
}
loadMeterData(): void {
this.loaderService.show();
this.dataService.getMeterData().subscribe({
next: (data) => {
this.splitMeterData(data);
this.loaderService.hide();
console.log('Meter data:', data);
},
error: (err) => {
this.loaderService.hide();
console.error(err);
},
});
}
splitMeterData(meterData: MeterData[]): void {
if (meterData) {
meterData.forEach((data) => {
if (data.type === this.usageTypes.ENERGY) {
this.meterDataEnergy.push(data);
} else if (data.type === this.usageTypes.WATER) {
this.meterDataWater.push(data);
}
});
this.initEnergyMeterData();
this.initWaterMeterDate();
}
}
initEnergyMeterData(): void {
// this.meterDataEnergy1Log = this.globalService.sortMeterData(
// this.meterDataEnergy1Log
// );
// this.meterDataEnergy1Hem = this.globalService.sortMeterData(
// this.meterDataEnergy1Hem
// );
// this.meterDataEnergyDTO1Log = this.meterDataEnergy1Log;
// this.meterDataEnergyDTO1Hem = this.meterDataEnergy1Hem;
this.meterDataEnergy = this.globalService.sortMeterData(
this.meterDataEnergy
);
this.meterDataEnergyDTO = this.meterDataEnergy;
}
initWaterMeterDate(): void {
this.meterDataWater = this.globalService.sortMeterData(this.meterDataWater);
this.meterDataWaterDTO = this.meterDataWater;
}
addMeterData(): void {
const dialogRef = this.dialog.open(MeterDataAddDialogComponent);
dialogRef.afterClosed().subscribe((newMeterData: MeterData) => {
console.log({ newMeterData });
if (newMeterData) {
this.dataService.addMeterData(newMeterData).subscribe({
next: (meterData: MeterData) => {
console.log({ meterData });
},
error: (err) => {
console.log('add meterData error', { err });
},
});
}
});
}
}

View File

@@ -0,0 +1,25 @@
<h1 mat-dialog-title>Neuer Eintrag - Typ: {{usageType}}</h1>
<form [formGroup]="form">
<div mat-dialog-content>
<mat-form-field>
<input matInput [matDatepicker]="picker" formControlName="date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-form-field>
<mat-label>Meter</mat-label>
<mat-select formControlName="meter" (valueChange)="setUsageType($event.name)">
<mat-option *ngFor="let el of meters" [value]="el">{{el.name}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<input matInput type="number" formControlName="amount">
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-raised-button color="primary" (click)="submit()">Submit</button>
</div>
</form>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MeterDataAddDialogComponent } from './meter-data-add-dialog.component';
describe('MeterDataAddDialogComponent', () => {
let component: MeterDataAddDialogComponent;
let fixture: ComponentFixture<MeterDataAddDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MeterDataAddDialogComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(MeterDataAddDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,66 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialogRef } from '@angular/material/dialog';
import { Meter, Meters } from 'src/app/models/Meter';
import { MeterData } from 'src/app/models/Meterdata';
import { UsageType } from 'src/app/models/UsageType';
import { DatabaseService } from 'src/app/services/database.service';
@Component({
selector: 'app-meter-data-add-dialog',
templateUrl: './meter-data-add-dialog.component.html',
styleUrls: ['./meter-data-add-dialog.component.scss'],
})
export class MeterDataAddDialogComponent implements OnInit {
meters: Meter[];
usageTypes = UsageType;
usageType: UsageType;
form: FormGroup;
constructor(
private fb: FormBuilder,
private dialogRef: MatDialogRef<MeterDataAddDialogComponent>,
private dataService: DatabaseService
) {}
ngOnInit(): void {
this.form = this.fb.group({
date: [new Date(), Validators.required],
meter: ['', Validators.required],
amount: ['', Validators.required],
});
this.dataService.getMeters().subscribe({
next: (meters: Meter[]) => {
console.log({ meters });
this.meters = meters;
},
error: (err) => {
console.log('get meters error', { err });
},
});
}
submit() {
if (this.form.valid) {
const formValues = this.form.value;
const newMeterData: MeterData = {
amount: formValues.amount,
date: formValues.date,
meter: formValues.meter,
type: this.usageType,
};
this.dialogRef.close(newMeterData);
}
}
setUsageType(meterName: string): void {
if (meterName === Meters['1HEM'] || meterName === Meters['1LOG']) {
this.usageType = this.usageTypes.ENERGY;
} else if (meterName === Meters.ABWASSER || meterName === Meters.WASSER) {
this.usageType = this.usageTypes.WATER;
}
}
}

View File

@@ -0,0 +1,13 @@
<div class="header-wrapper">
<div class="logo">
rossa-tech
</div>
<div class="nav">
</div>
<div class="account">
<button mat-button>logout</button>
</div>
</div>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent {
}

View File

@@ -0,0 +1,49 @@
// import {
// HttpInterceptor,
// HttpRequest,
// HttpHandler,
// HttpEvent,
// HttpHeaders,
// } from '@angular/common/http';
// import { Injectable } from '@angular/core';
// import { Observable } from 'rxjs';
// import { AuthenticationService } from 'src/app/services/auth.service';
// @Injectable()
// export class HttpInterceptorService implements HttpInterceptor {
// constructor(private authenticationService: AuthenticationService) {}
// intercept(
// req: HttpRequest<any>,
// next: HttpHandler
// ): Observable<HttpEvent<any>> {
// console.log(
// this.authenticationService.username,
// this.authenticationService.password
// );
// if (
// this.authenticationService.isUserLoggedIn() &&
// req.url.indexOf('basicauth') === -1
// ) {
// console.log('if');
// const authReq = req.clone({
// headers: new HttpHeaders({
// 'Content-Type': 'application/json',
// Authorization: `Basic ${window.btoa('pezi:Password123!')}`,
// // headers: new HttpHeaders({
// // 'Content-Type': 'application/json',
// // Authorization: `Basic ${window.btoa(
// // this.authenticationService.username +
// // ':' +
// // this.authenticationService.password
// // )}`,
// }),
// });
// console.log(authReq);
// return next.handle(authReq);
// } else {
// console.log('else');
// return next.handle(req);
// }
// }
// }

View File

@@ -0,0 +1,3 @@
<div class="loading-indicator" *ngIf="loaderService.isLoading | async">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>

View File

@@ -0,0 +1,21 @@
/* Absolute Center Spinner */
.loading-indicator {
position: fixed;
z-index: 999;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* Transparent Overlay */
.loading-indicator:before {
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3);
}

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoaderComponent } from './loader.component';
describe('LoaderComponent', () => {
let component: LoaderComponent;
let fixture: ComponentFixture<LoaderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoaderComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(LoaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,13 @@
import { Component } from '@angular/core';
import { LoaderService } from './loader.service';
@Component({
selector: 'app-loader',
templateUrl: './loader.component.html',
styleUrls: ['./loader.component.scss']
})
export class LoaderComponent {
constructor(public loaderService: LoaderService) {
}
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LoaderService {
/* --- VARIABLEN --- */
public isLoading: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(
false
);
/* --- CONSTRUCTOR --- */
constructor() {
}
/* --- FUNCTIONS --- */
show(): void {
this.isLoading.next(true);
}
hide(): void {
this.isLoading.next(false);
}
}

View File

@@ -0,0 +1,22 @@
<div class="container col-lg-6">
<h1 class="text-center">Login</h1>
<div class="card">
<div class="card-body">
<form class="form-group">
<div class="alert alert-warning" *ngIf='invalidLogin'>{{errorMessage}}</div>
<div class="alert alert-success" *ngIf='loginSuccess'>{{successMessage}}</div>
<div class="form-group">
<label for="email">User Name :</label>
<input type="text" class="form-control" id="username" [(ngModel)]="username" placeholder="Enter User Name"
name="username">
</div>
<div class="form-group">
<label for="pwd">Password:</label>
<input type="password" class="form-control" [(ngModel)]="password" id="password" placeholder="Enter password"
name="password">
</div>
<button (click)=handleLogin() class="btn btn-success">Login</button>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,42 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AuthenticationService } from 'src/app/services/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit {
username: string;
password: string;
errorMessage = 'Invalid Credentials';
successMessage: string;
invalidLogin = false;
loginSuccess = false;
constructor(
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService
) {}
ngOnInit() {}
handleLogin() {
this.authenticationService
.authenticationService(this.username, this.password)
.subscribe(
(result) => {
this.invalidLogin = false;
this.loginSuccess = true;
this.successMessage = 'Login Successful.';
this.router.navigate(['/dashboard']);
},
() => {
this.invalidLogin = true;
this.loginSuccess = false;
}
);
}
}

View File

@@ -0,0 +1 @@
<p>logout works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LogoutComponent } from './logout.component';
describe('LogoutComponent', () => {
let component: LogoutComponent;
let fixture: ComponentFixture<LogoutComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LogoutComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(LogoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-logout',
templateUrl: './logout.component.html',
styleUrls: ['./logout.component.scss']
})
export class LogoutComponent {
}

View File

@@ -0,0 +1 @@
<p>meters works!</p>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MetersComponent } from './meters.component';
describe('MetersComponent', () => {
let component: MetersComponent;
let fixture: ComponentFixture<MetersComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MetersComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(MetersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-meters',
templateUrl: './meters.component.html',
styleUrls: ['./meters.component.scss']
})
export class MetersComponent {
}

View File

@@ -0,0 +1,20 @@
<ng-container *ngIf="type == usageTypes.ENERGY; else water">
<div style="display: flex; flex-direction: column">
<div>energy</div>
<div style="display: flex; flex-direction: row;">
<div>{{averageEnergy1Log}} kWh</div>
<div>{{averageEnergy1Hem}} kWh</div>
</div>
</div>
</ng-container>
<ng-template #water>
</ng-template>
<!-- <ng-container *ngIf="amount > 0; else noData">{{amount}} kWh</ng-container>
<ng-template #noData>keine Daten vorhanden</ng-template> -->

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ConsumptionLastYearComponent } from './consumption-last-year.component';
describe('ConsumptionLastYearComponent', () => {
let component: ConsumptionLastYearComponent;
let fixture: ComponentFixture<ConsumptionLastYearComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ConsumptionLastYearComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ConsumptionLastYearComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,126 @@
import { Component, Input, OnChanges } from '@angular/core';
import { Meters } from 'src/app/models/Meter';
import { MeterData } from 'src/app/models/Meterdata';
import { UsageType } from 'src/app/models/UsageType';
import { GlobalService } from 'src/app/services/global.service';
@Component({
selector: 'app-consumption-last-year',
templateUrl: './consumption-last-year.component.html',
styleUrls: ['./consumption-last-year.component.scss'],
})
export class ConsumptionLastYearComponent implements OnChanges {
@Input() meterData: MeterData[];
@Input() type: UsageType;
usageTypes = UsageType;
meters = Meters;
meterDataEnergy1Hem: MeterData[] = [];
averageEnergy1Hem: number;
meterDataEnergy1Log: MeterData[] = [];
averageEnergy1Log: number;
meterDataWasser: MeterData[] = [];
averageWasser: number;
meterDataAbwasser: MeterData[] = [];
averageDirtyAbwasser: number;
constructor(private globalService: GlobalService) {}
ngOnChanges(): void {
if (this.type && this.type === this.usageTypes.ENERGY) {
console.log('count energy');
if (this.meterData) {
this.meterData.forEach((data) => {
if (data.meter.name === this.meters['1LOG']) {
this.meterDataEnergy1Log.push(data);
} else if (data.meter.name === this.meters['1HEM']) {
this.meterDataEnergy1Hem.push(data);
}
});
const currentYear = new Date().getFullYear();
this.meterDataEnergy1Log = this.globalService.sortMeterData(
this.meterDataEnergy1Log
);
this.averageEnergy1Log = this.countAverageAmountForYear(
this.meterDataEnergy1Log,
currentYear - 1
);
this.meterDataEnergy1Hem = this.globalService.sortMeterData(
this.meterDataEnergy1Hem
);
this.averageEnergy1Hem = this.countAverageAmountForYear(
this.meterDataEnergy1Hem,
currentYear - 1
);
}
} else if (this.type && this.type === this.usageTypes.WATER) {
console.log('count water');
if (this.meterData) {
this.meterData.forEach((data) => {
if (data.meter.name === this.meters['WASSER']) {
this.meterDataWasser.push(data);
} else if (data.meter.name === this.meters['ABWASSER']) {
this.meterDataAbwasser.push(data);
}
});
}
}
}
// countAverageAmountForLastYear(meterData: MeterData[]): number {
// const currentYear = new Date().getFullYear();
// let lastYear = meterData.filter(
// (data) => new Date(data.date).getFullYear() === currentYear - 1
// );
// lastYear = this.globalService.sortMeterData(lastYear);
// if (lastYear.length > 0) {
// const end = lastYear[0];
// const startIdx =
// meterData.findIndex((el) => el === lastYear[lastYear.length - 1]) + 1;
// const start = meterData[startIdx];
// console.log({ end: end.amount, startIdx, startAm: start.amount, start });
// return end.amount - start.amount;
// }
// return 0;
// }
/* FOR TEST*/
countAverageAmountForYear(meterData: MeterData[], year: number): number {
// const currentYear = new Date().getFullYear();
let filteredData = meterData.filter(
(data) => new Date(data.date).getFullYear() === year
);
filteredData = this.globalService.sortMeterData(filteredData);
if (filteredData.length > 0) {
const end = filteredData[0];
const startIdx =
meterData.findIndex(
(el) => el === filteredData[filteredData.length - 1]
) + 1;
const start = meterData[startIdx];
console.log({ end: end.amount, startIdx, startAm: start.amount, start });
return end.amount - start.amount;
}
return 0;
}
}

View File

@@ -0,0 +1,31 @@
<h2>{{type}}</h2>
<div class="meter-data-list-wrapper">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef> id </th>
<td mat-cell *matCellDef="let element"> {{element.id}} </td>
</ng-container>
<ng-container matColumnDef="date">
<th mat-header-cell *matHeaderCellDef> date </th>
<td mat-cell *matCellDef="let element"> {{element.date | date:
"dd.MM.y"}} </td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef> amount </th>
<td mat-cell *matCellDef="let element"> {{element.amount}} </td>
</ng-container>
<ng-container matColumnDef="meter">
<th mat-header-cell *matHeaderCellDef> meter </th>
<td mat-cell *matCellDef="let element"> {{element.meter.name}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MeterDataListComponent } from './meter-data-list.component';
describe('MeterDataListComponent', () => {
let component: MeterDataListComponent;
let fixture: ComponentFixture<MeterDataListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MeterDataListComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(MeterDataListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,108 @@
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { UsageType } from 'src/app/models/UsageType';
import { MeterData } from 'src/app/models/Meterdata';
@Component({
selector: 'app-meter-data-list',
templateUrl: './meter-data-list.component.html',
styleUrls: ['./meter-data-list.component.scss'],
})
export class MeterDataListComponent implements OnInit, OnChanges {
@Input() meterData: MeterData[];
@Input() type: UsageType;
displayedColumns: string[] = ['id', 'date', 'amount', 'meter'];
dataSource = new MatTableDataSource<MeterData>();
// dataSource = [
// {
// id: 1,
// type: 'WATER',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// {
// id: 3,
// type: 'ENERGY',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// {
// id: 1,
// type: 'WATER',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// {
// id: 3,
// type: 'ENERGY',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// {
// id: 1,
// type: 'WATER',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// {
// id: 3,
// type: 'ENERGY',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// {
// id: 1,
// type: 'WATER',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// {
// id: 3,
// type: 'ENERGY',
// date: '2023-03-24T08:47:52.000+0000',
// amount: 2224,
// meter: {
// id: 1,
// name: '1LOG',
// },
// },
// ];
constructor() {}
ngOnInit(): void {}
ngOnChanges(): void {
this.dataSource = new MatTableDataSource(this.meterData);
}
}

View File

@@ -0,0 +1,3 @@
<app-consumption-last-year [meterData]="meterData" [type]="type"></app-consumption-last-year>
<app-meter-data-list [meterData]="meterData" [type]="type"></app-meter-data-list>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MeterDataWrapperComponent } from './meter-data-wrapper.component';
describe('MeterDataWrapperComponent', () => {
let component: MeterDataWrapperComponent;
let fixture: ComponentFixture<MeterDataWrapperComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MeterDataWrapperComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(MeterDataWrapperComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,25 @@
import { Component, Input, OnChanges } from '@angular/core';
import { MeterData } from 'src/app/models/Meterdata';
import { UsageType } from 'src/app/models/UsageType';
import { GlobalService } from 'src/app/services/global.service';
@Component({
selector: 'app-meter-data-wrapper',
templateUrl: './meter-data-wrapper.component.html',
styleUrls: ['./meter-data-wrapper.component.scss'],
})
export class MeterDataWrapperComponent /* implements OnChanges */ {
@Input() meterData: MeterData[];
@Input() type: UsageType;
// usageTypes = UsageType;
// averageConsumptionLastYear: number;
constructor() {}
// ngOnChanges(): void {
// this.averageConsumptionLastYear = this.countAverageAmountForLastYear(
// this.meterData
// );
// }
}

View File

@@ -0,0 +1 @@
export const METERS = ['1LOG', '1HEM', 'WASSER', 'ABWASSER'];

View File

@@ -0,0 +1,16 @@
export class Meter {
id: number;
name: '1LOG' | '1HEM' | 'WASSER' | 'ABWASSER';
constructor(id: number, name: '1LOG' | '1HEM' | 'WASSER' | 'ABWASSER') {
this.id = id;
this.name = name;
}
}
export enum Meters {
'1LOG' = '1LOG',
'1HEM' = '1HEM',
'WASSER' = 'WASSER',
'ABWASSER' = 'ABWASSER',
}

View File

@@ -0,0 +1,22 @@
import { Meter } from './Meter';
export class MeterData {
id?: number;
type: 'ENERGY' | 'WATER';
date: Date;
amount: number;
meter: Meter;
constructor(
type: 'ENERGY' | 'WATER',
date: Date,
amount: number,
meter: Meter,
id?: number
) {
this.id = id;
this.type = type;
this.date = date;
this.amount = amount;
this.meter = meter;
}
}

View File

@@ -0,0 +1,4 @@
export enum UsageType {
ENERGY = 'ENERGY',
WATER = 'WATER'
}

View File

@@ -0,0 +1,15 @@
CREATE TABLE meter (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE meterdata (
id INT NOT NULL AUTO_INCREMENT,
type ENUM('energy', 'water') NOT NULL,
date DATETIME NOT NULL,
amount FLOAT NOT NULL,
meter_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (meter_id) REFERENCES meter(id)
);

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,65 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class AuthenticationService {
// BASE_PATH: 'http://localhost:8080'
USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser';
public username: string | undefined;
public password: string | undefined;
constructor(private http: HttpClient) {}
authenticationService(username: string, password: string) {
// return this.http
// .get(`http://localhost:8080/authenticate`, {
// // .get(`http://localhost:8080/`, {
// headers: {
// 'Content-Type': 'application/json',
// Authorization: this.createBasicAuthToken(username, password),
// },
// })
return this.http
.post<any>('http://localhost:8080/hello', {
userName: username,
userPass: password,
})
.pipe(
map((res) => {
this.username = username;
this.password = password;
this.registerSuccessfulLogin(username, password);
})
);
}
createBasicAuthToken(username: string, password: string) {
return 'Basic ' + window.btoa(username + ':' + password);
}
registerSuccessfulLogin(username: string, password: string) {
sessionStorage.setItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME, username);
}
logout() {
sessionStorage.removeItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME);
this.username = undefined;
this.password = undefined;
}
isUserLoggedIn() {
let user = sessionStorage.getItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME);
if (user === undefined) return false;
return true;
}
getLoggedInUserName() {
let user = sessionStorage.getItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME);
if (user === undefined) return '';
return user;
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DatabaseService } from './database.service';
describe('DatabaseService', () => {
let service: DatabaseService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DatabaseService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,55 @@
import { MeterData } from './../models/Meterdata';
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Meter } from 'src/app/models/Meter';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root',
})
export class DatabaseService {
private baseUrl = environment.hostUrl + '/v1';
constructor(private http: HttpClient) {}
getMeterData(): Observable<MeterData[]> {
const url = `${this.baseUrl}/meter-data`;
return this.http.get<MeterData[]>(url).pipe(catchError(this.handleError));
}
getMeterDataByType(usageType: string): Observable<MeterData[]> {
const url = `${this.baseUrl}/meter-data?usageType=${usageType}`;
return this.http.get<MeterData[]>(url).pipe(catchError(this.handleError));
}
addMeterData(meterData: MeterData): Observable<MeterData> {
const url = `${this.baseUrl}/meter-data`;
return this.http
.post<MeterData>(url, meterData)
.pipe(catchError(this.handleError));
}
getMeters(): Observable<Meter[]> {
const url = `${this.baseUrl}/meters`;
return this.http.get<Meter[]>(url).pipe(catchError(this.handleError));
}
private handleError(error: HttpErrorResponse): Observable<never> {
// https://stackoverflow.com/questions/68655492/throwerrorerror-is-now-deprecated-but-there-is-no-new-errorhttperrorresponse
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong.
console.error(
`Backend returned code ${error.status}, ` + `body was: ${error.error}`
);
}
// Return an observable with a user-facing error message.
return throwError(() => error);
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { GlobalService } from './global.service';
describe('GlobalService', () => {
let service: GlobalService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(GlobalService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { MeterData } from '../models/Meterdata';
@Injectable({
providedIn: 'root',
})
export class GlobalService {
constructor() {}
sortMeterData(meterData: MeterData[]): MeterData[] {
if (meterData.length > 0) {
meterData.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
return meterData;
} else {
return [];
}
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { TokenStorageService } from './token-storage.service';
describe('TokenStorageService', () => {
let service: TokenStorageService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(TokenStorageService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TokenStorageService {
constructor() { }
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(UserService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor() { }
}