|
@@ -1,34 +1,63 @@
|
|
|
import React, { ChangeEvent } from "react";
|
|
import React, { ChangeEvent } from "react";
|
|
|
import "./App.css";
|
|
import "./App.css";
|
|
|
import Headerbox from "./components/Headerbox";
|
|
import Headerbox from "./components/Headerbox";
|
|
|
-
|
|
|
|
|
-import { Income } from './interfaces/incomes';
|
|
|
|
|
import { Person } from './interfaces/persons';
|
|
import { Person } from './interfaces/persons';
|
|
|
|
|
+import { Frequency } from "./interfaces/frequency";
|
|
|
|
|
+import { Type } from "./interfaces/type";
|
|
|
|
|
+import { WBS } from "./interfaces/wbs";
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+import { WbsService } from "./service/wbsService";
|
|
|
|
|
|
|
|
|
|
|
|
|
interface AppState {
|
|
interface AppState {
|
|
|
- personanzahl : number;
|
|
|
|
|
|
|
+ personanzahl: number;
|
|
|
persons: Person[];
|
|
persons: Person[];
|
|
|
|
|
+ wbs: WBS;
|
|
|
|
|
+ wbsResult: boolean;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const wbsService = new WbsService();
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
class App extends React.Component<{}, AppState> {
|
|
class App extends React.Component<{}, AppState> {
|
|
|
|
|
+ types: Array<Type> = [];
|
|
|
|
|
+ freq: Array<Frequency> = [];
|
|
|
|
|
+
|
|
|
constructor(props: {}) {
|
|
constructor(props: {}) {
|
|
|
super(props);
|
|
super(props);
|
|
|
this.state = {
|
|
this.state = {
|
|
|
|
|
+ wbsResult: false,
|
|
|
|
|
+ wbs: { wbsGranted: "Keinen", difference: 0, percent: 0, yearlyAmount: 0, yearlyAmountMax: 0 },
|
|
|
personanzahl: 1,
|
|
personanzahl: 1,
|
|
|
- persons: [{index: 1, incomes: [], totalincome: 0}]
|
|
|
|
|
|
|
+ persons: [{ index: 1, incomes: [], totalincome: 0 }]
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+ wbsService.getTypes().then(types => {
|
|
|
|
|
+ types.forEach(type => {
|
|
|
|
|
+ this.types.push(type);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ wbsService.getFrequencies().then(freqs => {
|
|
|
|
|
+ freqs.forEach(freq => {
|
|
|
|
|
+ this.freq.push(freq);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
render() {
|
|
render() {
|
|
|
return (
|
|
return (
|
|
|
- <div className="App">
|
|
|
|
|
- <h1>WBS Rechner</h1>
|
|
|
|
|
|
|
+ <div className="App" id="div_app">
|
|
|
|
|
+ <h1 id="ueberschrift">WBS Rechner</h1>
|
|
|
<Headerbox title="Haushaltsmitglieder">
|
|
<Headerbox title="Haushaltsmitglieder">
|
|
|
<div className="label-container">
|
|
<div className="label-container">
|
|
|
<label htmlFor="personenanzahl">Anzahl Personen im Haushalt</label>
|
|
<label htmlFor="personenanzahl">Anzahl Personen im Haushalt</label>
|
|
|
<input
|
|
<input
|
|
|
- value={this.state.personanzahl}
|
|
|
|
|
|
|
+ defaultValue={this.state.personanzahl}
|
|
|
id="personenanzahl"
|
|
id="personenanzahl"
|
|
|
type="number"
|
|
type="number"
|
|
|
min="1"
|
|
min="1"
|
|
@@ -37,36 +66,186 @@ class App extends React.Component<{}, AppState> {
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
</Headerbox>
|
|
</Headerbox>
|
|
|
- {this.state.persons.map( p =>
|
|
|
|
|
|
|
+ {this.state.persons.map(p =>
|
|
|
<Headerbox title={'Daten der ' + p.index + '. Person'}>
|
|
<Headerbox title={'Daten der ' + p.index + '. Person'}>
|
|
|
- <div></div>
|
|
|
|
|
|
|
+ <div>
|
|
|
|
|
+ {this.state.persons[p.index - 1].incomes.map(i =>
|
|
|
|
|
+ <div className="label_box">
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={'type' + p.index + '_' + i.index}>Typ</label>
|
|
|
|
|
+ <select id={'type_' + p.index + '_' + i.index} defaultValue={i.type} onChange={this.updateIncomeSelectValue}>
|
|
|
|
|
+ <option id={'type_' + p.index + '_' + i.index + '_0'} value={0} disabled>keine Auswahl</option>
|
|
|
|
|
+ {this.types.map(t =>
|
|
|
|
|
+ <option id={'type' + p.index + '_' + i.index + '_' + t.value} value={t.value}> {t.description}</option>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </select>
|
|
|
|
|
+
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={'frequency' + p.index + '_' + i.index}>Frequenz</label>
|
|
|
|
|
+ <select id={'frequency_' + p.index + '_' + i.index} onChange={this.updateIncomeSelectValue} defaultValue={i.frequency} >
|
|
|
|
|
+ <option id={'frequency_' + p.index + '_' + i.index + '_0'} value={0} disabled>keine Auswahl</option>
|
|
|
|
|
+ {this.freq.map(f =>
|
|
|
|
|
+ <option id={'frequency_' + p.index + '_' + i.index + '_' + f.value} value={f.value}> {f.description}</option>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={'amount_' + p.index + '_' + i.index}>Brutto Betrag</label>
|
|
|
|
|
+ <input id={'amount_' + p.index + '_' + i.index} type="number" defaultValue={i.amount} onChange={this.updateIncomeValue} min={0} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={'advertisingCosts_' + p.index + '_' + i.index}>jährliche Werbungskosten</label>
|
|
|
|
|
+ <input id={'advertisingCosts_' + p.index + '_' + i.index} type="number" defaultValue={i.advertisingCosts} onChange={this.updateIncomeValue} min={0} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={'delete_' + p.index + '_' + i.index}>Löschen</label>
|
|
|
|
|
+ <a className="delete-button button" id={'delete_' + p.index + '_' + i.index} onClick={() => this.deleteIncome(p.index, i.index)} title={ i.index + ' Einkommen der ' + p.index + ' Person löschen'} >×</a>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ <button onClick={() => this.addIncome(p.index)} className="calculateButton">Einkommen hinzufügen</button>
|
|
|
|
|
+ </div>
|
|
|
</Headerbox>
|
|
</Headerbox>
|
|
|
- )}
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ <Headerbox title="WBS berechnen">
|
|
|
|
|
+ <h2>Zusammenfassung Haushalt</h2>
|
|
|
|
|
+ {this.state.persons.map(p =>
|
|
|
|
|
+ <div className="label_box">
|
|
|
|
|
+ <div className='label-container'>
|
|
|
|
|
+ <label htmlFor={"nr" + p.index}>Nr.</label>
|
|
|
|
|
+ <span id={"nr" + p.index}>{p.index}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={"kindsalter" + p.index}>Kindsalter</label>
|
|
|
|
|
+ <span id={"kindsalter" + p.index}>N/A</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={"freibetrage" + p.index}>Freibeträge</label>
|
|
|
|
|
+ <span id={"freibetrage" + p.index}>N/A</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor={"gesamteinkommen" + p.index}>Gesamteinkommen</label>
|
|
|
|
|
+ <span id={"gesamteinkommen" + p.index}>{p.totalincome}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
|
|
+ <button onClick={this.calculateWBS.bind(this)} className="calculateButton">WBS berechnen</button>
|
|
|
|
|
+
|
|
|
|
|
+ {this.state.wbsResult ?
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor="yearlyAmount">anrechenbares jährliches Gesamteinkommen:</label>
|
|
|
|
|
+ <span id="yearlyAmount">{this.state.wbs.yearlyAmount} €</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor="yearlyAmountMax">Einkommensgrenze:</label>
|
|
|
|
|
+ <span id="yearlyAmountMax">{this.state.wbs.yearlyAmountMax} €</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor="difference">{this.state.wbs.difference > 0 ? 'Überschreitung' : 'Unterschreitung'}</label>
|
|
|
|
|
+ <span id="difference">{this.state.wbs.difference} €</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor="percent">Verhältnis Gesamteinkommen / Einkommensgrenze</label>
|
|
|
|
|
+ <span id="percent">{this.state.wbs.percent} %</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="label-container">
|
|
|
|
|
+ <label htmlFor="percent">Möglicher WBS:</label>
|
|
|
|
|
+ <span id="percent">{this.state.wbs.wbsGranted}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ :
|
|
|
|
|
+ <div></div>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ </Headerbox>
|
|
|
|
|
+ </div >
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- updateInputValue = (evt : ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
|
|
+ updateInputValue = (evt: ChangeEvent<HTMLInputElement>) => {
|
|
|
this.setState({ personanzahl: evt.target.valueAsNumber });
|
|
this.setState({ personanzahl: evt.target.valueAsNumber });
|
|
|
this.refreshPeople(evt.target.valueAsNumber);
|
|
this.refreshPeople(evt.target.valueAsNumber);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- refreshPeople(personenanzahl: number ) {
|
|
|
|
|
|
|
+ updateIncomeValue = (evt: ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
+ this.updateValueState(evt.target.id, evt.target.valueAsNumber);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ updateIncomeSelectValue = (evt: ChangeEvent<HTMLSelectElement>) => {
|
|
|
|
|
+ this.updateValueState(evt.target.id, evt.target.value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ updateValueState(id: string, value: any) {
|
|
|
|
|
+ var parts = id.split("_");
|
|
|
|
|
+ var field = parts[0];
|
|
|
|
|
+ var personid: number = parseInt(parts[1]);
|
|
|
|
|
+ var incomeId: number = parseInt(parts[2]);
|
|
|
|
|
+
|
|
|
|
|
+ this.setState(prevState => {
|
|
|
|
|
+ const persons = [...prevState.persons];
|
|
|
|
|
+ const person = persons[personid - 1];
|
|
|
|
|
+ const income = person.incomes[incomeId - 1];
|
|
|
|
|
+ if (field === "amount") {
|
|
|
|
|
+ income.amount = value;
|
|
|
|
|
+ person.totalincome = this.calculateTotalIncome(personid - 1);
|
|
|
|
|
+ } else if (field === "advertisingCosts") {
|
|
|
|
|
+ income.advertisingCosts = value;
|
|
|
|
|
+ person.totalincome = this.calculateTotalIncome(personid - 1);
|
|
|
|
|
+ } else if (field === "frequency") {
|
|
|
|
|
+ income.frequency = value;
|
|
|
|
|
+ person.totalincome = this.calculateTotalIncome(personid - 1);
|
|
|
|
|
+ } else if (field === "type") {
|
|
|
|
|
+ income.type = value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return { persons };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ calculateWBS() {
|
|
|
|
|
+ var persons: Array<Person> = this.state.persons;
|
|
|
|
|
+ wbsService.calculateWbs(persons).then(wbsErg => {
|
|
|
|
|
+ this.setState({ wbsResult: true });
|
|
|
|
|
+ this.setState({ wbs: wbsErg });
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ calculateTotalIncome(personindex: number): number {
|
|
|
|
|
+ let totalIncome = 0;
|
|
|
|
|
+ for (const income of this.state.persons[personindex].incomes) {
|
|
|
|
|
+ totalIncome += ((income.frequency * income.amount) - income.advertisingCosts);
|
|
|
|
|
+ }
|
|
|
|
|
+ return totalIncome;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ refreshPeople(personenanzahl: number) {
|
|
|
if (personenanzahl > this.state.persons.length) {
|
|
if (personenanzahl > this.state.persons.length) {
|
|
|
let anzahl = personenanzahl - this.state.persons.length;
|
|
let anzahl = personenanzahl - this.state.persons.length;
|
|
|
for (let i = 1; i <= anzahl; i++) {
|
|
for (let i = 1; i <= anzahl; i++) {
|
|
|
this.addPerson();
|
|
this.addPerson();
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (personenanzahl < this.state.persons.length) {
|
|
if (personenanzahl < this.state.persons.length) {
|
|
|
let anzahl = this.state.persons.length - personenanzahl;
|
|
let anzahl = this.state.persons.length - personenanzahl;
|
|
|
for (let i = 1; i <= anzahl; i++) {
|
|
for (let i = 1; i <= anzahl; i++) {
|
|
|
this.deletePerson(this.state.persons.length);
|
|
this.deletePerson(this.state.persons.length);
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
addPerson() {
|
|
addPerson() {
|
|
|
const newPerson: Person = {
|
|
const newPerson: Person = {
|
|
|
index: this.getHighestIndex(this.state.persons) + 1,
|
|
index: this.getHighestIndex(this.state.persons) + 1,
|
|
@@ -84,8 +263,41 @@ class App extends React.Component<{}, AppState> {
|
|
|
}));
|
|
}));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- getHighestIndex(arr: Person[]) {
|
|
|
|
|
|
|
+ deleteIncome = (pindex:number, iindex: number) => {
|
|
|
|
|
+ this.setState(prevState => {
|
|
|
|
|
+ const persons = [...prevState.persons];
|
|
|
|
|
+ const person = persons[pindex];
|
|
|
|
|
+ person.incomes = person.incomes.filter(income => income.index !== iindex);
|
|
|
|
|
+ return { persons };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ addIncome(index: number) {
|
|
|
|
|
+ const newincome = {
|
|
|
|
|
+ index: this.getHighestIndex(this.state.persons[index - 1].incomes) + 1,
|
|
|
|
|
+ amount: 0,
|
|
|
|
|
+ frequency: 0,
|
|
|
|
|
+ advertisingCosts: 0,
|
|
|
|
|
+ type: 0
|
|
|
|
|
+ };
|
|
|
|
|
+ this.setState(prevState => {
|
|
|
|
|
+ const persons = [...prevState.persons];
|
|
|
|
|
+ const person = persons[index - 1];
|
|
|
|
|
+ const isNewIncomeExist = person.incomes.find(income => income.index === newincome.index);
|
|
|
|
|
+ if (!isNewIncomeExist) {
|
|
|
|
|
+ person.incomes = [...person.incomes, newincome];
|
|
|
|
|
+ }
|
|
|
|
|
+ return { persons };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ getHighestIndex(arr: any[]) {
|
|
|
let highestIndex = 0;
|
|
let highestIndex = 0;
|
|
|
|
|
+ if (typeof arr === "undefined") {
|
|
|
|
|
+ return highestIndex;
|
|
|
|
|
+ }
|
|
|
for (const item of arr) {
|
|
for (const item of arr) {
|
|
|
if (item.index > highestIndex) {
|
|
if (item.index > highestIndex) {
|
|
|
highestIndex = item.index;
|
|
highestIndex = item.index;
|