From e78b24e5217c04739d20da5314d9574e562f5c8e Mon Sep 17 00:00:00 2001 From: Joe Arndt Date: Sun, 22 Feb 2026 23:13:56 -0600 Subject: [PATCH] added in-place updating --- .../expense-list/expense-list.component.ts | 2 +- .../expense-form/expense-form.component.ts | 30 ++++++++++++++----- .../components/expense/expense.component.ts | 26 +++++++++------- src/app/services/expense.service.ts | 14 +++------ 4 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/app/components/expense-list/expense-list.component.ts b/src/app/components/expense-list/expense-list.component.ts index 88195b3..9846fb4 100644 --- a/src/app/components/expense-list/expense-list.component.ts +++ b/src/app/components/expense-list/expense-list.component.ts @@ -8,7 +8,7 @@ import { ExpenseComponent } from '../expense/expense.component'; selector: 'app-expense-list', imports: [MatTableModule, MatCardModule, ExpenseComponent], templateUrl: './expense-list.component.html', - styleUrl: './expense-list.component.scss', + styleUrl: './expense-list.component.scss' }) export class ExpenseListComponent { protected expenses = computed(() => this.expensesService.expenses()) diff --git a/src/app/components/expense/expense-form/expense-form.component.ts b/src/app/components/expense/expense-form/expense-form.component.ts index 07bc3a6..ceca49c 100644 --- a/src/app/components/expense/expense-form/expense-form.component.ts +++ b/src/app/components/expense/expense-form/expense-form.component.ts @@ -24,7 +24,7 @@ export interface ExpenseForm { imports: [MatAutocompleteModule, MatDatepickerModule, MatInputModule, MatSelectModule, FormField], providers: [provideNativeDateAdapter()], templateUrl: './expense-form.component.html', - styleUrl: './expense-form.component.scss', + styleUrl: './expense-form.component.scss' }) export class ExpenseFormComponent implements OnInit { public expense = input(); @@ -36,7 +36,7 @@ export class ExpenseFormComponent implements OnInit { private lastDate = signal(undefined); private formValid = computed(() => this.form().valid() && this.form().dirty()); private formDirty = computed(() => this.form().dirty() || this.form().touched()); - private formData = computed(() => this.buildForm(this.expense())); + private formData = signal(this.buildForm(this.expense())); private formModel = signal(this.formData()); protected categories = computed(() => this.categoryService.categories()); @@ -60,30 +60,46 @@ export class ExpenseFormComponent implements OnInit { } public ngOnInit(): void { - this.reset(); + this.initialize(); } public reset(clearDate = false): void { if (clearDate) { this.lastDate.set(undefined); - this.form().reset(); } + this.initialize(); + } + + public refresh(expense: Expense): void { + this.initialize(); + this.formData.set(this.buildForm(expense)); this.formModel.set(this.formData()); + this.form().reset(this.formModel()); } public autocompleteDisplay(value: Merchant | Category) { return value?.name ?? null; } + private initialize(): void { + this.formData.set(this.buildForm(this.expense())); + this.formModel.set(this.formData()); + this.form().reset(this.formModel()); + } + private buildForm(expense?: Expense): ExpenseForm { let formData = { date: this.lastDate() ?? '', cents: NaN, category: {}, merchant: {}, note: '', tags: [] } as unknown as ExpenseForm if (expense) { + const date = expense.date.toString().split('-'); + const day = Number(date[2]); + const month = Number(date[1]) - 1; + const year = Number(date[0]); formData = { - date: new Date(expense.date.toString()), + date: new Date(year, month, day), cents: expense.cents, category: expense.category, - merchant: expense.merchant, - note: expense.note, + merchant: expense.merchant ?? {}, + note: expense.note ?? '', tags: expense.tags ?? [] } as ExpenseForm; } diff --git a/src/app/components/expense/expense.component.ts b/src/app/components/expense/expense.component.ts index 32f28a8..4aa4ee8 100644 --- a/src/app/components/expense/expense.component.ts +++ b/src/app/components/expense/expense.component.ts @@ -1,4 +1,4 @@ -import { Component, input, model, signal, viewChild } from '@angular/core'; +import { Component, model, signal, viewChild } from '@angular/core'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { CreateExpense, Expense, ExpenseService, UpdateExpense } from '../../services/expense.service'; @@ -12,15 +12,15 @@ import { Temporal } from '@js-temporal/polyfill'; imports: [MatCardModule, MatButtonModule, MatIcon, ExpenseFormComponent, DatePipe, CurrencyPipe], providers: [], templateUrl: './expense.component.html', - styleUrl: './expense.component.scss', + styleUrl: './expense.component.scss' }) export class ExpenseComponent { private form = viewChild(ExpenseFormComponent); - public expense = input(); public editingExpense = signal(false); - public savingExpense = signal(false); + public savingExpense = signal(false); // TODO: implement UI for saving... + public expense = model(); public formValid = model(false); public formDirty = model(false); public formData = model(); @@ -34,7 +34,7 @@ export class ExpenseComponent { public async addClick(): Promise { const form = this.formData()!; const postExpense: CreateExpense = { - date: new Temporal.PlainDate(form.date.getFullYear(), form.date.getMonth(), form.date.getDate()), + date: this.dateToPlainDate(form.date), cents: form.cents, categoryId: form.category.id, note: !!form.note ? form.note : undefined, @@ -51,19 +51,19 @@ export class ExpenseComponent { public async updateClick(): Promise { const form = this.formData()!; - const updateExpense: UpdateExpense = { + const putExpense: UpdateExpense = { id: this.expense()!.id, - date: Temporal.PlainDate.from(form.date.toString()), + date: this.dateToPlainDate(form.date), cents: form.cents, categoryId: form.category.id, note: !!form.note ? form.note : undefined, merchantId: !!form.merchant ? form.merchant.id : undefined, tagIds: form.tags.map(tag => tag.id) }; - console.log('update:', updateExpense); - // post update dto - // set expense - this.resetForm(); + const expense = await this.expenseService.updateExpense(putExpense); + this.expense.set(expense); + this.form()?.refresh(expense); + this.editingExpense.set(false); } public cancelUpdateClick(): void { @@ -75,6 +75,10 @@ export class ExpenseComponent { this.editingExpense.set(false); } + private dateToPlainDate(date: Date): Temporal.PlainDate { + return new Temporal.PlainDate(date.getFullYear(), date.getMonth() + 1, date.getDate()); + } + // const saveExpenseModel = this.expenseModel(); // const date = this.datePipe.transform(saveExpenseModel.date, 'yyyy-MM-dd')?.split('-') ?? []; // const expense: CreateExpense = { diff --git a/src/app/services/expense.service.ts b/src/app/services/expense.service.ts index e782ae5..243c53b 100644 --- a/src/app/services/expense.service.ts +++ b/src/app/services/expense.service.ts @@ -6,12 +6,12 @@ import { HttpService } from './http.service'; import { Temporal } from '@js-temporal/polyfill'; @Injectable({ - providedIn: 'root', + providedIn: 'root' }) export class ExpenseService { private internalExpenses = signal([]); public readonly expenses = this.internalExpenses.asReadonly(); - public readonly expensePath = 'http://localhost:3000/common-cents/expenses'; + public readonly expensePath = 'http://localhost:3000/common-cents/expenses'; // TODO: refactor public constructor(private readonly http: HttpService) { void this.fetchExpenses(); @@ -19,29 +19,23 @@ export class ExpenseService { public async fetchExpenses(): Promise { this.internalExpenses.set(await this.http.get(this.expensePath)); - console.log({ expenses: this.internalExpenses() }); // TODO: Remove } public async postExpense(createExpense: CreateExpense): Promise { const createdExpense = await this.http.post(this.expensePath, createExpense); await this.fetchExpenses(); - console.log({ createExpense, createdExpense}); // TODO: Remove return createdExpense; } public async updateExpense(updateExpense: UpdateExpense): Promise { - const updatedExpense = await this.http.put(this.expensePath, updateExpense); - await this.fetchExpenses(); - console.log({ updateExpense, updatedExpense}); // TODO: Remove - - return updatedExpense; + return await this.http.put(this.expensePath, updateExpense); } } export interface Expense { id: string; - date: Temporal.PlainDate; + date: Date; cents: number; category: Category; note?: string;