diff --git a/src/app/components/expense/expense.component.ts b/src/app/components/expense/expense.component.ts index fe3c915..51cbdda 100644 --- a/src/app/components/expense/expense.component.ts +++ b/src/app/components/expense/expense.component.ts @@ -6,6 +6,7 @@ import { MatIcon } from '@angular/material/icon'; import { ExpenseForm, ExpenseFormComponent } from './expense-form/expense-form.component'; import { CurrencyPipe, DatePipe } from '@angular/common'; import { Temporal } from '@js-temporal/polyfill'; +import { SnackBarService } from '../../services/snack-bar'; @Component({ selector: 'app-expense', @@ -25,7 +26,8 @@ export class ExpenseComponent { public formDirty = model(false); public formData = model(); - public constructor(private readonly expenseService: ExpenseService) { } + public constructor(private readonly expenseService: ExpenseService, + private readonly snackBar: SnackBarService) { } public editClick(): void { this.editingExpense.set(true); @@ -42,12 +44,16 @@ export class ExpenseComponent { tagIds: form.tags.map(tag => tag.id) }; this.savingExpense.set(true); + const snackId = this.snackBar.staticBar('Tracking new expense...'); try { await this.expenseService.postExpense(postExpense); this.resetForm(); + this.snackBar.dismiss(snackId); + this.snackBar.autoBar('Expense tracked!'); } catch (error) { - console.error(error); // TODO: Add error processing + this.snackBar.dismiss(snackId); + this.snackBar.dismissableBar('Error: '); // TODO } finally { this.savingExpense.set(false); @@ -70,14 +76,18 @@ export class ExpenseComponent { tagIds: form.tags.map(tag => tag.id) }; this.savingExpense.set(true); + const snackId = this.snackBar.staticBar('Updating expense...'); try { const expense = await this.expenseService.updateExpense(putExpense); this.expense.set(expense); this.form()?.refresh(expense); this.editingExpense.set(false); + this.snackBar.dismiss(snackId); + this.snackBar.autoBar('Expense updated!'); } catch (error) { - console.error(error); // TODO: Add error processing + this.snackBar.dismiss(snackId); + this.snackBar.dismissableBar('Error: '); // TODO } finally { this.savingExpense.set(false); diff --git a/src/app/services/category.service.ts b/src/app/services/category.service.ts index 211e6e8..9c2da93 100644 --- a/src/app/services/category.service.ts +++ b/src/app/services/category.service.ts @@ -2,7 +2,7 @@ import { Injectable, signal } from '@angular/core'; import { HttpService } from './http.service'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class CategoryService { private internalCategories = signal([]); diff --git a/src/app/services/http.service.ts b/src/app/services/http.service.ts index d9f3212..4ec3f04 100644 --- a/src/app/services/http.service.ts +++ b/src/app/services/http.service.ts @@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { firstValueFrom } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class HttpService { public constructor(private httpClient: HttpClient) { } diff --git a/src/app/services/merchant.service.ts b/src/app/services/merchant.service.ts index 6589120..8cca35b 100644 --- a/src/app/services/merchant.service.ts +++ b/src/app/services/merchant.service.ts @@ -2,7 +2,7 @@ import { Injectable, signal } from '@angular/core'; import { HttpService } from './http.service'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class MerchantService { private internalMerchants = signal([]); diff --git a/src/app/services/snack-bar.ts b/src/app/services/snack-bar.ts new file mode 100644 index 0000000..56f66fe --- /dev/null +++ b/src/app/services/snack-bar.ts @@ -0,0 +1,44 @@ +import { Injectable, signal } from '@angular/core'; +import { MatSnackBar, MatSnackBarRef, TextOnlySnackBar } from '@angular/material/snack-bar'; + +interface SnackBar { + id: string; + ref: MatSnackBarRef; +} + +@Injectable({ + providedIn: 'root' +}) +export class SnackBarService { + protected TIMEOUT = 3000; + + private snackBars = signal([]); + + public constructor(private snackBar: MatSnackBar) { } + + public autoBar(msg: string, milliseconds = this.TIMEOUT): void { + const timeout = milliseconds ?? this.TIMEOUT; + this.snackBar.open(msg, undefined, { duration: timeout }); + } + public dismissableBar(msg: string, dismissMsg = 'Dismiss'): void { + this.snackBar.open(msg, dismissMsg); + } + + public staticBar(msg: string): string { + const snackBar: SnackBar = { + id: crypto.randomUUID().toString(), + ref: this.snackBar.open(msg) + }; + this.snackBars.update((bars) => [...bars, snackBar]); + + return snackBar.id; + } + + public dismiss(id: string): void { + const snackBar = this.snackBars().find((snackBar) => snackBar.id === id); + if (snackBar) { + snackBar.ref.dismiss(); + this.snackBars.update((bars) => bars.filter((bar) => bar.id !== snackBar.id)); + } + } +} diff --git a/src/app/services/tag.service.ts b/src/app/services/tag.service.ts index dd3cb2e..06a373b 100644 --- a/src/app/services/tag.service.ts +++ b/src/app/services/tag.service.ts @@ -2,7 +2,7 @@ import { Injectable, signal } from '@angular/core'; import { HttpService } from './http.service'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class TagService { private internalTags = signal([]);