Skip to content

Commit

Permalink
Merge pull request #54 from LisaPMunich/#49_create-spend-tab_on_plan-…
Browse files Browse the repository at this point in the history
…page

#49 restore original balance after each calculation, create helper se…
  • Loading branch information
LisaPMunich authored Sep 7, 2023
2 parents 7aada29 + 0f31437 commit ed44e81
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 117 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# MoneySprouts
# Money Sprouts

## Description

Money-Sprouts is an application designed to teach children the value of saving and spending their pocket money sensibly. Through a fun and interactive interface, children can manage their savings, view transaction histories, plan purchases, and even earn extra by doing chores.
Money Sprouts is an application designed to teach children the value of saving and spending their pocket money sensibly. Through a fun and interactive interface, children can manage their savings, view transaction histories, plan purchases, and even earn extra by doing chores.

<img src="https://github.com/LisaPMunich/money-sprouts/assets/99111208/35fb3553-6460-4872-a945-28a471a1c23d" width="600" alt="Application mockups on different devices">

Expand Down Expand Up @@ -36,6 +36,7 @@ Money-Sprouts is an application designed to teach children the value of saving a
## Upcoming Features

- **Multi-Language Support**: Soon, Money-Sprouts will be available in both English and German.
- **Earn Extra Money**: Soon the kids will be able to earn extra money by choosing from voluntary chores.

## Start the app for local development

Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ registerLocaleData(localeDe);
PagesModule,
HttpClientModule,
CommonModule,
BrowserAnimationsModule
BrowserAnimationsModule,
],
providers: [
{ provide: LOCALE_ID, useValue: 'de' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
name="title"
ngModel
required
pattern="[A-Za-z\s]*"
title="Only letters and spaces are allowed"
pattern="^[a-zA-Z\s0-9-.]+$"
title="Only letters, numbers, spaces, hyphens, or dots are allowed"
#title="ngModel"
(blur)="formatNameInput($event)"
(blur)="formattingHelperService.formatNameInput($event)"
/>
</div>

Expand All @@ -25,8 +25,10 @@
ngModel
required
pattern="^\d{1,3}(?:\.\d{3})*(?:,\d{0,2})?$"
title="Only whole numbers or numbers with commas for decimal separation are
allowed."
#amount="ngModel"
(blur)="formatAmountInput($event)"
(blur)="formattingHelperService.formatAmountInput($event)"
(focus)="clearInput($event)"
/>
</div>
Expand All @@ -36,15 +38,16 @@
Calculate
</button>
<button class="button" type="submit" (click)="apply(spendingForm)">
Apply
Submit
</button>
</div>
</form>
<p *ngIf="title.invalid && title.dirty" class="error">
Only letters and spaces are allowed.
Only letters, numbers, spaces, hyphens, or dots are allowed.
</p>
<p *ngIf="amount.invalid && amount.dirty" class="error">
Only numbers are allowed.
Only whole numbers or numbers with commas for decimal separation are
allowed.
</p>
<p *ngIf="message" class="message">
<span class="message-icon" *ngIf="icon">{{ icon }}</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { NgForm } from '@angular/forms';
import { FormattingHelperService } from '../../../services/formatting-helper.service';

@Component({
selector: 'money-sprouts-plan-expenses',
Expand All @@ -11,16 +12,23 @@ export class PlanExpensesComponent {
icon: string | null = null;

@Output() calculateAmount = new EventEmitter<number>();
@Output() applyChanges = new EventEmitter<void>();
@Output() applyChanges = new EventEmitter<{
title: string;
amount: number;
}>();
@Output() resetBalance = new EventEmitter<void>();

constructor(public formattingHelperService: FormattingHelperService) {}

calculate(spendingForm: NgForm) {
if (this.fieldsAreEmpty(spendingForm)) {
return;
} else if (spendingForm.valid) {
const displayAmount = spendingForm.value.amount;
const enteredAmount =
this.germanFormatToNumber(displayAmount) * 100;
this.formattingHelperService.germanFormatToNumber(
displayAmount
) * 100;
console.log('enteredAmount: ', enteredAmount);
this.calculateAmount.emit(enteredAmount);
this.icon = 'ℹ';
Expand All @@ -36,7 +44,9 @@ export class PlanExpensesComponent {
if (this.fieldsAreEmpty(spendingForm)) {
return;
} else if (spendingForm.valid) {
this.applyChanges.emit();
const title = spendingForm.value.title;
const amount = spendingForm.value.amount * -100;
this.applyChanges.emit({ title, amount });
this.icon = '✔';
this.message =
'Success! Your sale will soon be approved by your parents and be applied to your total balance.';
Expand All @@ -46,33 +56,11 @@ export class PlanExpensesComponent {
}
}

formatNameInput(event: Event): void {
const input = event.target as HTMLInputElement;
let value = input.value;
value = value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
input.value = value;
}

formatAmountInput(event: Event): void {
clearInput(event: Event): void {
const input = event.target as HTMLInputElement;
const value = parseFloat(input.value.replace(',', '.'));
if (!isNaN(value)) {
input.value = this.convertToGermanFormat(value);
} else {
this.icon = '⚠';
this.message = 'Please enter a valid amount.';
}
}

convertToGermanFormat(amount: number): string {
let str = amount.toFixed(2);
str = str.replace('.', ',');
return str;
}

germanFormatToNumber(amount: string): number {
const numberAmount = amount.replace(',', '.');
return parseFloat(numberAmount);
input.value = '';
console.log('clearInput triggered');
this.resetBalance.emit();
}

fieldsAreEmpty(form: NgForm): boolean {
Expand All @@ -84,13 +72,6 @@ export class PlanExpensesComponent {
return false;
}

clearInput(event: Event): void {
const input = event.target as HTMLInputElement;
input.value = '';
console.log('clearInput triggered');
this.resetBalance.emit();
}

onSubmit() {
console.log('Form submitted!');
}
Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/app/pages/plan/plan.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div id="plan" class="custom-container">
<div class="custom-text-box" *ngIf="account$ | async as account">
<div class="balance-display">
{{ account.balance / 100 | currency : 'EUR' : 'symbol' : '1.2' }}
{{ balance / 100 | currency : 'EUR' : 'symbol' : '1.2' }}
</div>
<div class="tabs">
<div
Expand Down Expand Up @@ -36,7 +36,7 @@
<div *ngIf="activeTab === 'spend'">
<money-sprouts-plan-expenses
(calculateAmount)="onCalculateAmount($event)"
(applyChanges)="onApplyChanges()"
(applyChanges)="onApplyChanges($event.title, $event.amount)"
(resetBalance)="onResetBalance()"
></money-sprouts-plan-expenses>
</div>
Expand Down
20 changes: 15 additions & 5 deletions apps/client/src/app/pages/plan/plan.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Account } from '@money-sprouts/shared/domain';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { AccountService } from '../../services/account.service';
import { TransactionService } from '../../services/transaction.service';

@Component({
selector: 'money-sprouts-plan',
Expand All @@ -13,9 +14,11 @@ export class PlanComponent implements OnInit {
account$: Observable<Account | null>;
activeTab: 'spend' | 'earn' = 'spend';
originalBalance: number | null = null;
temporaryBalance: number | null = null;

constructor(
private accountService: AccountService,
private transactionService: TransactionService,
private http: HttpClient
) {}

Expand All @@ -24,20 +27,27 @@ export class PlanComponent implements OnInit {
this.originalBalance = this.accountService.getCurrentBalance();
}

get balance() {
return this.temporaryBalance ?? this.originalBalance;
}

switchTab(tab: 'spend' | 'earn'): void {
this.activeTab = tab;
}

onCalculateAmount(amount: number) {
this.accountService.updateBalanceTemporarily(amount);
this.temporaryBalance = this.originalBalance - amount;
}

onResetBalance(): void {
console.log('onResetBalance triggered');
this.accountService.resetBalanceToOriginal();
this.temporaryBalance = null;
}

onApplyChanges() {
this.accountService.applyBalanceChange();
onApplyChanges(title: string, value: number) {
this.transactionService.addTransaction(
title,
value,
this.accountService.getCurrentAccountId()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
}

.custom-table-button-grid {
@include create-text-box(70%, 0.9, 2rem, 5rem);
@include create-text-box(70%, 0.9, 2rem, 3rem);

display: grid;
grid-template-columns: 1fr 1fr;
Expand All @@ -52,7 +52,7 @@
}

@media (max-width: 1200px) {
@include create-text-box(80%, 0.9, 2rem, 4rem);
@include create-text-box(80%, 0.9, 2rem, 3rem);
}

@media (max-width: 1000px) {
Expand Down
65 changes: 5 additions & 60 deletions apps/client/src/app/services/account.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,68 +102,13 @@ export class AccountService extends Loggable {

// Method to get the current balance
getCurrentBalance(): number | null {
// TODO rename
const currentUser = this.currentAccountSubject.getValue();
return currentUser?.balance || null;
const currentAccount = this.currentAccountSubject.getValue();
return currentAccount?.balance || null;
}

// Method to update the balance temporarily
updateBalanceTemporarily(amount: number): void {
// TODO rename
const currentUser = this.currentAccountSubject.getValue();
if (currentUser && typeof currentUser.balance === 'number') {
currentUser.balance -= amount;
this.currentAccountSubject.next(currentUser);
}
}

// Method to reset the balance to the original value
resetBalanceToOriginal(): void {
console.log('resetBalanceToOriginal triggered');
// TODO rename
const currentUser = this.currentAccountSubject.getValue();
this.originalBalance = currentUser.balance;

// Log the values and conditions
console.log('currentUser:', currentUser);
console.log('typeof currentUser.balance:', typeof currentUser.balance);
console.log('this.originalBalance:', this.originalBalance);

if (
currentUser &&
typeof currentUser.balance === 'number' &&
this.originalBalance !== null
) {
currentUser.balance = this.originalBalance;
console.log('resetBalance: ', currentUser.balance);
this.currentAccountSubject.next(currentUser);
}
}

applyBalanceChange(): void {
const currentUserBalance = this.getCurrentBalance();
if (currentUserBalance !== null) {
this.http
.patch('/api/transactions/:userId', {
newBalance: currentUserBalance,
})
.subscribe({
next: () => {
this.balanceUpdateStatus.next('success');
this.originalBalance = this.currentBalance;
},
error: (error) => {
console.error('Error updating balance: ', error);
this.balanceUpdateStatus.next('error');
const currentBalance = this.getCurrentBalance();
if (currentBalance !== null) {
this.updateBalanceTemporarily(
this.originalBalance - currentBalance
);
}
},
});
}
getCurrentAccountId(): number | null {
const currentAccount = this.currentAccountSubject.getValue();
return currentAccount?.id;
}

logoutOrDeselectAccount() {
Expand Down
38 changes: 38 additions & 0 deletions apps/client/src/app/services/formatting-helper.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root',
})
export class FormattingHelperService {
message: string | null = '';
icon: string | null = null;

formatNameInput(event: Event): void {
const input = event.target as HTMLInputElement;
let value = input.value;
value = value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
input.value = value;
}

formatAmountInput(event: Event): void {
const input = event.target as HTMLInputElement;
const value = parseFloat(input.value.replace(',', '.'));
if (!isNaN(value)) {
input.value = this.convertToGermanFormat(value);
} else {
this.icon = '⚠';
this.message = 'Please enter a valid amount.';
}
}

convertToGermanFormat(amount: number): string {
let str = amount.toFixed(2);
str = str.replace('.', ',');
return str;
}

germanFormatToNumber(amount: string): number {
const numberAmount = amount.replace(',', '.');
return parseFloat(numberAmount);
}
}
Loading

0 comments on commit ed44e81

Please sign in to comment.