Compare commits
18 Commits
angularMat
...
master
Author | SHA1 | Date | |
---|---|---|---|
9f0a7e9c1b
|
|||
eaabf522ee
|
|||
193142d6ec
|
|||
e644150be1
|
|||
fe0597376f
|
|||
05db934464
|
|||
3a713c929d
|
|||
7baee4d5f0
|
|||
555626936a
|
|||
19b9439891
|
|||
cbc7b52f96
|
|||
a0230690af
|
|||
aed0759eab
|
|||
ceb228e472
|
|||
fa1e64b93c
|
|||
9bfdb6b636
|
|||
ca206a86c4
|
|||
2f7e516273
|
@ -1,3 +1,4 @@
|
||||
FROM nginx:stable
|
||||
COPY default.conf /etc/nginx/conf.d/
|
||||
COPY dist/hv-ui/* /usr/share/nginx/html/
|
||||
|
||||
|
9
default.conf
Normal file
9
default.conf
Normal file
@ -0,0 +1,9 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name hv-ui;
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
@ -87,6 +88,7 @@
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
|
25
hv-ui/package-lock.json
generated
25
hv-ui/package-lock.json
generated
@ -155,6 +155,23 @@
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@angular/cdk": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-11.0.4.tgz",
|
||||
"integrity": "sha512-suhAhsZEv+lLwm8dc524cMvO7gHPi+z2+4tueNS+zDiIObdZc4fs+KoOlnRMdYwba++X/V8mHXuDEQetl3GFcw==",
|
||||
"requires": {
|
||||
"parse5": "^5.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"parse5": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular/cli": {
|
||||
"version": "11.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.7.tgz",
|
||||
@ -352,6 +369,14 @@
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@angular/material": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-11.0.4.tgz",
|
||||
"integrity": "sha512-yjJEl3UbJxyKZFAbhvyZGphDfhtR1vo0AnDEE3KBxAOp9oD4r7paL6ER9rSXFoNRjST56tIDbNtG/wZNtH0XDQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"version": "11.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.9.tgz",
|
||||
|
@ -12,10 +12,12 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~11.0.6",
|
||||
"@angular/cdk": "~11.0.3-sha-ce5d10a77",
|
||||
"@angular/common": "~11.0.6",
|
||||
"@angular/compiler": "~11.0.6",
|
||||
"@angular/core": "~11.0.6",
|
||||
"@angular/forms": "~11.0.6",
|
||||
"@angular/material": "~11.0.3-sha-ce5d10a77",
|
||||
"@angular/platform-browser": "~11.0.6",
|
||||
"@angular/platform-browser-dynamic": "~11.0.6",
|
||||
"@angular/router": "~11.0.6",
|
||||
|
@ -1,23 +0,0 @@
|
||||
/* AppComponent's private CSS styles */
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
nav a {
|
||||
padding: 5px 10px;
|
||||
text-decoration: none;
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
background-color: #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
nav a:visited, a:link {
|
||||
color: #334953;
|
||||
}
|
||||
nav a:hover {
|
||||
color: #039be5;
|
||||
background-color: #cfd8dc;
|
||||
}
|
||||
nav a.active {
|
||||
color: #039be5;
|
||||
}
|
@ -1,8 +1,3 @@
|
||||
<h1>{{title}}</h1>
|
||||
|
||||
<nav>
|
||||
<a routerLink="/objekte">Meine Objekte</a>
|
||||
<a routerLink="/mieters">Meine Mieter</a>
|
||||
</nav>
|
||||
<router-outlet></router-outlet>
|
||||
<app-messages></app-messages>
|
||||
<section class="mat-typography">
|
||||
<app-navigation></app-navigation>
|
||||
</section>
|
@ -10,6 +10,22 @@ import { MietersComponent } from './mieters/mieters.component';
|
||||
import { WohnungenComponent } from './wohnungen/wohnungen.component';
|
||||
import { MieterDetailComponent } from './mieter-detail/mieter-detail.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NavigationComponent } from './navigation/navigation.component';
|
||||
import { LayoutModule } from '@angular/cdk/layout';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatGridListModule } from '@angular/material/grid-list'
|
||||
import { MatFormFieldModule } from '@angular/material/form-field'
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker'
|
||||
import { MatInputModule } from '@angular/material/input'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -18,13 +34,29 @@ import { FormsModule } from '@angular/forms';
|
||||
ObjekteComponent,
|
||||
MietersComponent,
|
||||
WohnungenComponent,
|
||||
MieterDetailComponent
|
||||
MieterDetailComponent,
|
||||
NavigationComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
AppRoutingModule,
|
||||
HttpClientModule
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
LayoutModule,
|
||||
MatToolbarModule,
|
||||
MatButtonModule,
|
||||
MatSidenavModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatTableModule,
|
||||
MatPaginatorModule,
|
||||
MatSortModule,
|
||||
MatCardModule,
|
||||
MatGridListModule,
|
||||
MatFormFieldModule,
|
||||
MatDatepickerModule,
|
||||
MatInputModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
@ -1 +1 @@
|
||||
export const serviceBaseUrl = "http://172.16.3.185:5000";
|
||||
export const serviceBaseUrl = "http://172.16.10.29:5000";
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ForderungService } from './forderung.service';
|
||||
|
||||
describe('ForderungService', () => {
|
||||
let service: ForderungService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ForderungService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
|
||||
import { Forderung } from './forderung';
|
||||
import { MessageService } from './message.service';
|
||||
import { serviceBaseUrl } from './config';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ForderungService {
|
||||
constructor(private messageService: MessageService, private http: HttpClient) { }
|
||||
|
||||
getForderungenByMieter(mieterId: number): Promise<Forderung[]> {
|
||||
this.messageService.add(`ForderungService: fetched forderungen by mieter ${mieterId}`);
|
||||
return this.http.get<Forderung[]>(`${serviceBaseUrl}/hv/mieter/${mieterId}/forderungen`).toPromise()
|
||||
}
|
||||
|
||||
getForderung(id: number): Promise<Forderung> {
|
||||
this.messageService.add(`ForderungService: fetch forderung id=${id}`);
|
||||
return this.http.get<Forderung>(`${serviceBaseUrl}/hv/forderung/${id}`).toPromise()
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
export interface Forderung {
|
||||
id: number;
|
||||
mieter: number;
|
||||
ref_wohnung: number;
|
||||
datum: string;
|
||||
betrag: number;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
/* MessagesComponent's private CSS styles */
|
||||
h2 {
|
||||
color: red;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
button.clear {
|
||||
font-family: Arial, sans-serif;
|
||||
color: #333;
|
||||
background-color: #eee;
|
||||
margin-bottom: 12px;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
<div *ngIf="messageService.messages.length">
|
||||
|
||||
<h2>Messages</h2>
|
||||
<button class="clear"
|
||||
(click)="messageService.clear()">clear</button>
|
||||
<div *ngFor='let message of messageService.messages'> {{message}} </div>
|
||||
|
||||
</div>
|
||||
<section class="mat-typography">
|
||||
<mat-card class="defaultCard" *ngIf="messageService.messages.length">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Messages</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<button (click)="messageService.clear()">clear</button>
|
||||
<div *ngFor='let message of messageService.messages'> {{message}} </div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</section>
|
@ -0,0 +1,36 @@
|
||||
.label {
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: left;
|
||||
width: 50%;
|
||||
}
|
||||
.content::after {
|
||||
content: "\A";
|
||||
white-space: pre;
|
||||
}
|
||||
.content {
|
||||
text-align: right;
|
||||
width: 50%;
|
||||
}
|
||||
.details {
|
||||
width: 30%;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
#first {
|
||||
float: left;
|
||||
width: 35%;
|
||||
}
|
||||
#second {
|
||||
float: left;
|
||||
width: 20%;
|
||||
}
|
||||
#third {
|
||||
float: left;
|
||||
width: 35%
|
||||
}
|
||||
|
@ -1,38 +1,117 @@
|
||||
<h1>Mieter: {{mieter.vorname}} {{mieter.nachname}} ({{mieter.id}})</h1>
|
||||
<section class="mat-typography">
|
||||
<mat-card class="defaultCard">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
{{mieter?.vorname}} {{mieter?.nachname}}
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
ID: {{mieter?.id}}
|
||||
</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="details">
|
||||
<span class="label">Objekt</span><span class="content">{{mieter?.objekt_shortname}} ({{mieter?.objekt}})</span>
|
||||
<span class="label">Wohnung</span><span class="content">{{mieter?.wohnung_shortname}} ({{mieter?.wohnung}})</span>
|
||||
<span class="label">Anrede</span><span class="content">{{mieter?.anrede}}</span>
|
||||
<span class="label">Strasse</span><span class="content">{{mieter?.strasse}}</span>
|
||||
<span class="label">PLZ</span><span class="content">{{mieter?.plz}}</span>
|
||||
<span class="label">Ort</span><span class="content">{{mieter?.ort}}</span>
|
||||
<span class="label">Telefon</span><span class="content">{{mieter?.telefon}}</span>
|
||||
<span class="label">Einzug</span><span class="content">{{mieter?.einzug}}</span>
|
||||
<span class="label">Auszug</span><span class="content">{{mieter?.auszug}}</span>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<table>
|
||||
<tr><td>Objekt:</td><td>{{mieter.objekt_shortname}} ({{mieter.objekt}})</td></tr>
|
||||
<tr><td>Wohnung:</td><td>{{mieter.wohnung_shortname}} ({{mieter.wohnung}})</td></tr>
|
||||
<tr><td>Vorname:</td><td>{{mieter.vorname}}</td></tr>
|
||||
<tr><td>Nachname:</td><td>{{mieter.nachname}}</td></tr>
|
||||
<tr><td>Anrede:</td><td>{{mieter.anrede}}</td></tr>
|
||||
<tr><td>Strasse:</td><td>{{mieter.strasse}}</td></tr>
|
||||
<tr><td>PLZ:</td><td>{{mieter.plz}}</td></tr>
|
||||
<tr><td>Ort:</td><td>{{mieter.ort}}</td></tr>
|
||||
<tr><td>Telefon:</td><td>{{mieter.telefon}}</td></tr>
|
||||
<tr><td>Einzug:</td><td>{{mieter.einzug}}</td></tr>
|
||||
<tr><td>Auszug:</td><td>{{mieter.auszug}}</td></tr>
|
||||
</table>
|
||||
<mat-card class="defaultCard">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
Forderungen und Zahlungen
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div id="wrapper">
|
||||
<div id="first">
|
||||
<!-- <label style="text-align: left;">Jahr:
|
||||
<input [(ngModel)]="year" (input)="onYearInput()" placeholder="Jahr"/>
|
||||
</label>-->
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Jahr</mat-label>
|
||||
<!-- <input matInput [(ngModel)]="year"/> -->
|
||||
<input matInput [(ngModel)]="year"/>
|
||||
</mat-form-field>
|
||||
<br/>
|
||||
<button mat-raised-button (click)="loadForYear()" color="primary">Laden</button>
|
||||
</div>
|
||||
<div id="second">
|
||||
<div class="saldo">
|
||||
<span class="label">Forderungen</span><span class="content">{{saldo?.forderungen}}</span>
|
||||
<span class="label">Zahlungen</span><span class="content">{{saldo?.zahlungen}}</span>
|
||||
<span class="label">Saldo</span><span class="content">{{saldo?.saldo}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="third">
|
||||
<form (ngSubmit)="insertNewZahlung(zahlungForm.value)" #zahlungForm="ngForm">
|
||||
<mat-form-field appearance="outline">
|
||||
<!--
|
||||
<input matInput [matDatepicker]="picker"/>
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
-->
|
||||
<mat-label>Datum soll</mat-label> <input matInput name="datum_soll" ngModel/>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Datum ist</mat-label> <input matInput name="datum_ist" ngModel/>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Betrag Zahlung</mat-label> <input matInput name="betrag" ngModel/>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Kommentar</mat-label> <input matInput name="kommentar" ngModel/>
|
||||
</mat-form-field>
|
||||
<button type="submit" mat-raised-button color="primary">Erfassen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<div>
|
||||
<label>Jahr:
|
||||
<input [(ngModel)]="year" (input)="onYearInput()" placeholder="Jahr"/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Datum soll</th>
|
||||
<th>Datum ist</th>
|
||||
<th>Forderung</th>
|
||||
<th>Zahlung</th>
|
||||
<th>Kommentar</th>
|
||||
</tr>
|
||||
<tr *ngFor="let zahlungForderung of zahlungenForderungen">
|
||||
<td>{{zahlungForderung.datum_soll}}</td>
|
||||
<td>{{zahlungForderung.datum_ist}}</td>
|
||||
<td>{{zahlungForderung.betrag_forderung}}</td>
|
||||
<td>{{zahlungForderung.betrag_zahlung}}</td>
|
||||
<td>{{zahlungForderung.kommentar}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<mat-card class="defaultCard" *ngIf="dataSource">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
Forderungen und Zahlungen - Übersicht
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div>
|
||||
<table mat-table [dataSource]="dataSource" #zftable>
|
||||
<ng-container matColumnDef="datum_soll">
|
||||
<th mat-header-cell *matHeaderCellDef>Datum soll</th>
|
||||
<td mat-cell *matCellDef="let element">{{element.datum_soll}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="datum_ist">
|
||||
<th mat-header-cell *matHeaderCellDef>Datum ist</th>
|
||||
<td mat-cell *matCellDef="let element">{{element.datum_ist}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="betrag_forderung">
|
||||
<th mat-header-cell *matHeaderCellDef>Datum Forderung</th>
|
||||
<td mat-cell *matCellDef="let element">{{element.betrag_forderung}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="betrag_zahlung">
|
||||
<th mat-header-cell *matHeaderCellDef>Zahlung</th>
|
||||
<td mat-cell *matCellDef="let element">{{element.betrag_zahlung}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="kommentar">
|
||||
<th mat-header-cell *matHeaderCellDef>Kommentar</th>
|
||||
<td mat-cell *matCellDef="let element">{{element.kommentar}}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</section>
|
@ -1,12 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core'
|
||||
import { MatTable } from '@angular/material/table'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { Location } from '@angular/common'
|
||||
|
||||
import { Mieter } from '../mieter'
|
||||
import { ZahlungForderung } from '../zahlung-forderung'
|
||||
import { ZahlungForderung, Saldo, Zahlung, ZahlungRaw } from '../zahlung-forderung'
|
||||
|
||||
import { MieterService} from '../mieter.service'
|
||||
import { ZahlungForderungService} from '../zahlung-forderung.service'
|
||||
import { MessageService } from '../message.service'
|
||||
import { MatTableDataSource } from '@angular/material/table'
|
||||
|
||||
@Component({
|
||||
selector: 'app-mieter-detail',
|
||||
@ -18,7 +21,11 @@ export class MieterDetailComponent implements OnInit {
|
||||
mieter: Mieter
|
||||
year: string
|
||||
zahlungenForderungen : ZahlungForderung[]
|
||||
saldo: Saldo
|
||||
newZahlung: Zahlung
|
||||
|
||||
displayedColumns: string[] = ["datum_soll", "datum_ist", "betrag_forderung", "betrag_zahlung", "kommentar"]
|
||||
dataSource: MatTableDataSource<ZahlungForderung>
|
||||
|
||||
constructor(
|
||||
private mieterService: MieterService,
|
||||
@ -28,27 +35,48 @@ export class MieterDetailComponent implements OnInit {
|
||||
private location: Location ) {
|
||||
}
|
||||
|
||||
async getMieter() {
|
||||
async getMieter(): Promise<void> {
|
||||
const id = +this.route.snapshot.paramMap.get('id')
|
||||
try {
|
||||
this.mieter = await this.mieterService.getMieter(id)
|
||||
this.zahlungenForderungen = await this.zahlungForderungService.getZahlungenForderungenByMieterAndYear(id, +this.year)
|
||||
await this.getZahlungenForderungen()
|
||||
} catch (err) {
|
||||
this.messageService.add(JSON.stringify(err, undefined, 4))
|
||||
}
|
||||
}
|
||||
|
||||
async getZahlungenForderungen() {
|
||||
async getZahlungenForderungen(): Promise<void> {
|
||||
const id = this.mieter?.id ?? 0
|
||||
try {
|
||||
this.zahlungenForderungen = await this.zahlungForderungService.getZahlungenForderungenByMieterAndYear(id, +this.year)
|
||||
this.dataSource = new MatTableDataSource<ZahlungForderung>(this.zahlungenForderungen)
|
||||
this.saldo = await this.zahlungForderungService.getSaldoByMieterAndYear(id, +this.year)
|
||||
} catch (err) {
|
||||
this.messageService.add(JSON.stringify(err, undefined, 4))
|
||||
}
|
||||
}
|
||||
|
||||
onYearInput(): void {
|
||||
this.getZahlungenForderungen()
|
||||
async loadForYear(): Promise<void> {
|
||||
await this.getZahlungenForderungen()
|
||||
}
|
||||
|
||||
async insertNewZahlung(newZahlungRaw: ZahlungRaw): Promise<void> {
|
||||
try {
|
||||
this.messageService.add(`New ZahlungRaw: ${newZahlungRaw.datum_soll}, ${newZahlungRaw.datum_ist}, ${newZahlungRaw.betrag}, ${newZahlungRaw.kommentar}`)
|
||||
let newZahlung : Zahlung = {
|
||||
'id': 0,
|
||||
'mieter': this.mieter.id,
|
||||
'datum_soll': newZahlungRaw.datum_soll,
|
||||
'datum_ist': newZahlungRaw.datum_ist,
|
||||
'betrag': +(newZahlungRaw.betrag.replace(',','.')),
|
||||
'kommentar': newZahlungRaw.kommentar
|
||||
}
|
||||
this.messageService.add(`New Zahlung: ${newZahlung.datum_soll}, ${newZahlung.datum_ist}, ${newZahlung.betrag}, ${newZahlung.kommentar}, ${newZahlung.mieter}`)
|
||||
await this.zahlungForderungService.postZahlung(newZahlung)
|
||||
await this.getZahlungenForderungen()
|
||||
} catch (err) {
|
||||
this.messageService.add(`Error when inserted zahlung: ${JSON.stringify(err, undefined, 4)}`)
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
|
@ -13,4 +13,5 @@ export interface Mieter {
|
||||
telefon: string;
|
||||
einzug: string;
|
||||
auszug: string;
|
||||
saldo: number;
|
||||
}
|
||||
|
@ -1,57 +1,3 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.mieters {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 15em;
|
||||
}
|
||||
.mieters li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 0;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0;
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.mieters li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
.mieters li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.mieters li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
}
|
||||
.mieters .badge {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.8em 0.7em 0 0.7em;
|
||||
background-color:#405061;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
|
||||
.mieters a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.mieters a:hover {
|
||||
color:#607D8B;
|
||||
table {
|
||||
width: 75%;
|
||||
}
|
||||
|
@ -1,7 +1,36 @@
|
||||
<h2>Meine Mieter</h2>
|
||||
<ul class="mieters">
|
||||
<li *ngFor="let mieter of mieters"
|
||||
routerLink="/mieter/{{mieter.id}}">
|
||||
<span>{{mieter.vorname}} {{mieter.nachname}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<section class="mat-typography">
|
||||
|
||||
|
||||
<mat-card class="defaultCard">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
Meine Mieter
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div>
|
||||
<table mat-table [dataSource]="dataSource" #zftable>
|
||||
<ng-container matColumnDef="nachname">
|
||||
<th mat-header-cell *matHeaderCellDef>Nachname</th>
|
||||
<td mat-cell *matCellDef="let element" routerLink="/mieter/{{element.id}}">
|
||||
{{element.nachname}}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="vorname">
|
||||
<th mat-header-cell *matHeaderCellDef>Vorname</th>
|
||||
<td mat-cell *matCellDef="let element">{{element.vorname}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="saldo">
|
||||
<th mat-header-cell *matHeaderCellDef>Saldo</th>
|
||||
<td mat-cell *matCellDef="let element">{{element.saldo}}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
|
||||
</section>
|
@ -3,6 +3,11 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { Mieter } from '../mieter';
|
||||
import { MieterService } from '../mieter.service';
|
||||
import { MessageService } from '../message.service';
|
||||
import { ZahlungForderungService} from '../zahlung-forderung.service'
|
||||
|
||||
import { Saldo } from '../zahlung-forderung'
|
||||
|
||||
import { MatTableDataSource } from '@angular/material/table'
|
||||
|
||||
|
||||
@Component({
|
||||
@ -12,21 +17,48 @@ import { MessageService } from '../message.service';
|
||||
})
|
||||
export class MietersComponent implements OnInit {
|
||||
|
||||
mieters: Mieter[];
|
||||
mieters: Mieter[]
|
||||
year: string
|
||||
selectedMieter : Mieter
|
||||
|
||||
selectedMieter : Mieter;
|
||||
displayedColumns: string[] = ["nachname", "vorname", "saldo"]
|
||||
dataSource: MatTableDataSource<Mieter>
|
||||
|
||||
constructor(private mieterService: MieterService, private messageService: MessageService) { }
|
||||
|
||||
constructor(
|
||||
private mieterService: MieterService,
|
||||
private zahlungForderungService: ZahlungForderungService,
|
||||
private messageService: MessageService) {
|
||||
}
|
||||
|
||||
async getMieters() {
|
||||
try {
|
||||
this.mieters = await this.mieterService.getMieters();
|
||||
this.mieters = await this.mieterService.getMieters();
|
||||
this.mieters.sort((a, b) => (a.nachname < b.nachname ? -1 : 1))
|
||||
this.messageService.add("Mieters loaded")
|
||||
this.loadSaldos();
|
||||
this.messageService.add("Saldos loaded")
|
||||
|
||||
this.dataSource = new MatTableDataSource<Mieter>(this.mieters)
|
||||
} catch (err) {
|
||||
this.messageService.add(JSON.stringify(err, undefined, 4))
|
||||
}
|
||||
}
|
||||
|
||||
async loadSaldos() {
|
||||
try {
|
||||
this.mieters.forEach(async (m:Mieter) => {
|
||||
this.messageService.add(`Loading saldo for mieter ${m.id}`)
|
||||
let saldo: Saldo = await this.zahlungForderungService.getSaldoByMieterAndYear(m.id, +this.year)
|
||||
m.saldo = saldo.saldo
|
||||
})
|
||||
} catch (err) {
|
||||
this.messageService.add(JSON.stringify(err, undefined, 4))
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.year = new Date().getFullYear().toString()
|
||||
this.getMieters();
|
||||
}
|
||||
}
|
||||
|
17
hv-ui/src/app/navigation/navigation.component.css
Normal file
17
hv-ui/src/app/navigation/navigation.component.css
Normal file
@ -0,0 +1,17 @@
|
||||
.sidenav-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidenav {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.sidenav .mat-toolbar {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.mat-toolbar.mat-primary {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
30
hv-ui/src/app/navigation/navigation.component.html
Normal file
30
hv-ui/src/app/navigation/navigation.component.html
Normal file
@ -0,0 +1,30 @@
|
||||
<section class="mat-typography">
|
||||
<mat-sidenav-container class="sidenav-container">
|
||||
<mat-sidenav #drawer class="sidenav" fixedInViewport
|
||||
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
|
||||
[mode]="(isHandset$ | async) ? 'over' : 'side'"
|
||||
[opened]="(isHandset$ | async) === false">
|
||||
<mat-toolbar>Menu</mat-toolbar>
|
||||
<mat-nav-list>
|
||||
<a mat-list-item href="/objekte">Meine Objekte</a>
|
||||
<a mat-list-item href="/mieters">Meine Mieter</a>
|
||||
</mat-nav-list>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content>
|
||||
<mat-toolbar color="primary">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Toggle sidenav"
|
||||
mat-icon-button
|
||||
(click)="drawer.toggle()"
|
||||
*ngIf="isHandset$ | async">
|
||||
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
|
||||
</button>
|
||||
<span>Hausverwaltung</span>
|
||||
</mat-toolbar>
|
||||
<!-- Add Content Here -->
|
||||
<router-outlet></router-outlet>
|
||||
<app-messages></app-messages>
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
</section>
|
40
hv-ui/src/app/navigation/navigation.component.spec.ts
Normal file
40
hv-ui/src/app/navigation/navigation.component.spec.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { LayoutModule } from '@angular/cdk/layout';
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
|
||||
import { NavigationComponent } from './navigation.component';
|
||||
|
||||
describe('NavigationComponent', () => {
|
||||
let component: NavigationComponent;
|
||||
let fixture: ComponentFixture<NavigationComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [NavigationComponent],
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
LayoutModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatSidenavModule,
|
||||
MatToolbarModule,
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NavigationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should compile', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
21
hv-ui/src/app/navigation/navigation.component.ts
Normal file
21
hv-ui/src/app/navigation/navigation.component.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navigation',
|
||||
templateUrl: './navigation.component.html',
|
||||
styleUrls: ['./navigation.component.css']
|
||||
})
|
||||
export class NavigationComponent {
|
||||
|
||||
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
|
||||
.pipe(
|
||||
map(result => result.matches),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
constructor(private breakpointObserver: BreakpointObserver) {}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.objekte {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 15em;
|
||||
}
|
||||
.objekte li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 0;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0;
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.objekte li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
.objekte li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.objekte li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
}
|
||||
.objekte .badge {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.8em 0.7em 0 0.7em;
|
||||
background-color:#405061;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
|
||||
.objekte a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.objekte a:hover {
|
||||
color:#607D8B;
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
<section class="mat-typography">
|
||||
<h2>Meine Objekte</h2>
|
||||
<ul class="objekte">
|
||||
<ul>
|
||||
<li *ngFor="let objekt of objekte"
|
||||
routerLink="/wohnungen/{{objekt.id}}">
|
||||
<span>{{objekt.shortname}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
@ -1,57 +0,0 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.wohnungen {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 15em;
|
||||
}
|
||||
.wohnungen li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 0;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0;
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.wohnungen li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
.wohnungen li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.wohnungen li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
}
|
||||
.wohnungen .badge {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.8em 0.7em 0 0.7em;
|
||||
background-color:#405061;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
|
||||
.wohnungen a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.wohnungen a:hover {
|
||||
color:#607D8B;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
<section class="mat-typography">
|
||||
<h2>Meine Wohnungen</h2>
|
||||
<h3>Objekt: {{objekt.shortname}}</h3>
|
||||
<p>
|
||||
Objektfläche: {{objekt.flaeche}}
|
||||
</p>
|
||||
<ul class="wohnungen">
|
||||
<ul>
|
||||
<li *ngFor="let wohnung of wohnungen">
|
||||
<span>{{wohnung.shortname}}</span>: <span>{{wohnung.flaeche}}</span>m²
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
@ -2,8 +2,7 @@ import { Injectable } from '@angular/core'
|
||||
import { Observable, of } from 'rxjs'
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http'
|
||||
|
||||
import { ZahlungForderung } from './zahlung-forderung'
|
||||
import { Forderung } from './forderung'
|
||||
import { ZahlungForderung, Forderung, Zahlung, Saldo } from './zahlung-forderung'
|
||||
import { MessageService } from './message.service'
|
||||
import { serviceBaseUrl } from './config'
|
||||
|
||||
@ -22,4 +21,15 @@ export class ZahlungForderungService {
|
||||
this.messageService.add(`ZahlungForderungService: fetch forderung id=${id}`)
|
||||
return this.http.get<Forderung>(`${serviceBaseUrl}/hv/forderung/${id}`).toPromise()
|
||||
}
|
||||
|
||||
postZahlung(zahlung: Zahlung): Promise<string> {
|
||||
this.messageService.add(`ZahlungForderungService: insert zahlung ${JSON.stringify(zahlung, undefined, 4)}`)
|
||||
return this.http.post<string>(`${serviceBaseUrl}/hv/zahlung`, zahlung).toPromise()
|
||||
}
|
||||
|
||||
getSaldoByMieterAndYear(mieterId: number, year: number): Promise<Saldo> {
|
||||
this.messageService.add(`ZahlungForderungService: fetched saldo by mieter ${mieterId} and year ${year}`)
|
||||
return this.http.get<Saldo>(`${serviceBaseUrl}/hv/mieter/${mieterId}/saldo/${year}`).toPromise()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,4 +7,34 @@ export interface ZahlungForderung {
|
||||
betrag_zahlung: number
|
||||
betrag_forderung: number
|
||||
kommentar: string
|
||||
}
|
||||
|
||||
export interface Forderung {
|
||||
id: number
|
||||
mieter: number
|
||||
ref_wohnung: number
|
||||
datum: string
|
||||
betrag: number
|
||||
}
|
||||
|
||||
export interface Zahlung {
|
||||
id: number
|
||||
mieter: number
|
||||
datum_soll: string
|
||||
datum_ist: string
|
||||
betrag: number
|
||||
kommentar: string
|
||||
}
|
||||
|
||||
export interface ZahlungRaw {
|
||||
datum_soll: string
|
||||
datum_ist: string
|
||||
betrag: string
|
||||
kommentar: string
|
||||
}
|
||||
|
||||
export interface Saldo {
|
||||
forderungen: number
|
||||
zahlungen: number
|
||||
saldo: number
|
||||
}
|
@ -6,8 +6,10 @@
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<body class="mat-typography">
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,22 +1,8 @@
|
||||
/* Application-wide Styles */
|
||||
h1 {
|
||||
color: #369;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 250%;
|
||||
}
|
||||
h2, h3 {
|
||||
color: #444;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-weight: lighter;
|
||||
}
|
||||
body {
|
||||
margin: 2em;
|
||||
}
|
||||
body, input[type="text"], button {
|
||||
color: #333;
|
||||
font-family: Cambria, Georgia, serif;
|
||||
}
|
||||
/* everywhere else */
|
||||
* {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
/* @import '@angular/material/prebuilt-themes/deeppurple-amber.css'; */
|
||||
|
||||
html, body { height: 100%; }
|
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
|
||||
.defaultCard {
|
||||
margin: 5px;
|
||||
}
|
Reference in New Issue
Block a user