diff --git a/src/app/components/expense-list/expense-list.component.html b/src/app/components/expense-list/expense-list.component.html index ffd5211..18a7201 100644 --- a/src/app/components/expense-list/expense-list.component.html +++ b/src/app/components/expense-list/expense-list.component.html @@ -1,4 +1,5 @@
+

Expenses

@for (expense of expenses(); track expense.id) { } diff --git a/src/app/components/expense-list/expense-list.component.scss b/src/app/components/expense-list/expense-list.component.scss index e69de29..818821c 100644 --- a/src/app/components/expense-list/expense-list.component.scss +++ b/src/app/components/expense-list/expense-list.component.scss @@ -0,0 +1,4 @@ +.expense-list-container { + display: grid; + gap: 1rem; +} diff --git a/src/app/components/expense/expense-add/expense-add.component.html b/src/app/components/expense/expense-add/expense-add.component.html deleted file mode 100644 index a881b02..0000000 --- a/src/app/components/expense/expense-add/expense-add.component.html +++ /dev/null @@ -1 +0,0 @@ -

expense-add works!

diff --git a/src/app/components/expense/expense-add/expense-add.component.scss b/src/app/components/expense/expense-add/expense-add.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/components/expense/expense-add/expense-add.component.ts b/src/app/components/expense/expense-add/expense-add.component.ts deleted file mode 100644 index 5726791..0000000 --- a/src/app/components/expense/expense-add/expense-add.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-expense-add', - imports: [], - templateUrl: './expense-add.component.html', - styleUrl: './expense-add.component.scss', -}) -export class ExpenseAddComponent { - -} diff --git a/src/app/components/expense/expense-form/expense-form.component.html b/src/app/components/expense/expense-form/expense-form.component.html index 9e5b53c..4822a8b 100644 --- a/src/app/components/expense/expense-form/expense-form.component.html +++ b/src/app/components/expense/expense-form/expense-form.component.html @@ -1,24 +1,19 @@
Date - - - - - - + Cents - + Category - + @for (category of categories(); track category.id) { {{ category.name }} @@ -28,7 +23,7 @@ Merchant - + @for (merchant of merchants(); track merchant.id) { {{ merchant.name }} @@ -38,15 +33,15 @@ Note - + Tags - - - - - + + @for (tag of tags(); track tag.id) { + {{ tag.name }} + } +
diff --git a/src/app/components/expense/expense-form/expense-form.component.scss b/src/app/components/expense/expense-form/expense-form.component.scss index e69de29..2e8b1c2 100644 --- a/src/app/components/expense/expense-form/expense-form.component.scss +++ b/src/app/components/expense/expense-form/expense-form.component.scss @@ -0,0 +1,20 @@ +.expense-form-container { + display: grid; + + mat-form-field { + width: 100%; + } +} + +@media (min-width: 550px) { + .expense-form-container { + grid-template-columns: 1fr 1fr; + gap: 1rem; + } +} + +@media (min-width: 800px) { + .expense-form-container { + grid-template-columns: 1fr 1fr 1fr; + } +} 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 884758d..67c3119 100644 --- a/src/app/components/expense/expense-form/expense-form.component.ts +++ b/src/app/components/expense/expense-form/expense-form.component.ts @@ -1,4 +1,5 @@ -import {Component, computed, input, output, signal} from '@angular/core'; +import { Component, computed, input, OnInit, output, signal } from '@angular/core'; +import { min, required, disabled, form, FormField, Schema, schema } from '@angular/forms/signals'; import { Expense } from '../../../services/expense.service'; import { Category } from '../../../services/category.service'; import { Merchant } from '../../../services/merchant.service'; @@ -7,81 +8,76 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; -import {form, min, required} from '@angular/forms/signals'; +import { provideNativeDateAdapter } from '@angular/material/core'; interface ExpenseForm { - date: Date | string; + date: Date; cents: number; - category: Category | string; - merchant: Merchant | string; + category: Category; + merchant: Merchant; note: string; tags: Tag[]; } @Component({ selector: 'app-expense-form', - imports: [ - MatAutocompleteModule, - MatDatepickerModule, - MatInputModule, - MatSelectModule - ], + imports: [MatAutocompleteModule, MatDatepickerModule, MatInputModule, MatSelectModule, FormField], + providers: [provideNativeDateAdapter()], templateUrl: './expense-form.component.html', styleUrl: './expense-form.component.scss', }) -export class ExpenseFormComponent { - public disabled = input(); // TODO: should this even exist? - public expense = input(undefined); - public categories = input([]); - public merchants = input([]); - public tags = input([]); +export class ExpenseFormComponent implements OnInit { + public expense = input(); + public categories = input.required(); + public merchants = input.required(); + public tags = input.required(); + public disabled = input(false); public valid = output(); - public value = output(); // TODO: form value or expense value? - - - // protected enableSaveButton = computed(() => { - // const dateValid = this.expenseForm.date().valid(); - // const centsValid = this.expenseForm.cents().valid(); - // const categoryValid = this.expenseForm.category().valid(); - // const merchantValid = this.expenseForm.merchant().valid(); - // const noteValid = this.expenseForm.note().valid(); - // - // return dateValid && centsValid && categoryValid && merchantValid && noteValid && !this.saving(); - // }); + public value = output(); private lastSelectedDate = signal(undefined); + private formData = computed(() => this.buildForm(this.expense())); + private formModel = signal(this.formData()); - private expenseDate = computed(() => { - const lastDate = this.lastSelectedDate(); - const expense = this.expense(); - - if (expense) { - - } - return this.expense() - ? new Date() - : this.lastSelectedDate ?? ''; - }); - - private defaultForm: ExpenseForm = { - date: new Date(), - cents: this.expense()?.cents ?? NaN, - category: this.expense()?.category ?? '', - merchant: this.expense()?.merchant ?? '', - note: this.expense()?.note ?? '', - tags: this.expense()?.tags ?? [] - }; - private expenseModel = signal(this.defaultForm); - public expenseForm = form(this.expenseModel, (schema) => { - required(schema.date); - required(schema.cents); - min(schema.cents, 1); - required(schema.category); - }); + protected form = form(this.formModel, this.buildFormOptions()); + public ngOnInit(): void { + console.log({ expense: this.expense() }); + this.formModel.set(this.formData()); + } public autocompleteDisplay(value: Merchant | Category) { - return value.name ?? null; + return value?.name ?? null; + } + + private buildForm(expense?: Expense): ExpenseForm { + let formData = { date: this.lastSelectedDate() ?? '', cents: '', category: '', merchant: '', note: '', tags: ''} as unknown as ExpenseForm + if (expense) { + formData = { + date: new Date(expense.date.toString()), + cents: expense.cents, + category: expense.category, + merchant: expense.merchant, + note: expense.note, + tags: expense.tags ?? [] + } as ExpenseForm; + } + return formData; + } + + private buildFormOptions(): Schema { + return schema((schema) => { + required(schema.date); + required(schema.cents); + required(schema.category); + min(schema.cents, 1); + disabled(schema.date, () => this.disabled()); + disabled(schema.cents, () => this.disabled()); + disabled(schema.category, () => this.disabled()); + disabled(schema.merchant, () => this.disabled()); + disabled(schema.note, () => this.disabled()); + disabled(schema.tags, () => this.disabled()); + }); } } diff --git a/src/app/components/expense/expense-view/expense-view.component.html b/src/app/components/expense/expense-view/expense-view.component.html deleted file mode 100644 index fb4707d..0000000 --- a/src/app/components/expense/expense-view/expense-view.component.html +++ /dev/null @@ -1 +0,0 @@ -

expense-view works!

diff --git a/src/app/components/expense/expense-view/expense-view.component.scss b/src/app/components/expense/expense-view/expense-view.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/components/expense/expense-view/expense-view.component.ts b/src/app/components/expense/expense-view/expense-view.component.ts deleted file mode 100644 index 3b626fd..0000000 --- a/src/app/components/expense/expense-view/expense-view.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-expense-view', - imports: [], - templateUrl: './expense-view.component.html', - styleUrl: './expense-view.component.scss', -}) -export class ExpenseViewComponent { - -} diff --git a/src/app/components/expense/expense.component.html b/src/app/components/expense/expense.component.html index c16825a..93db7db 100644 --- a/src/app/components/expense/expense.component.html +++ b/src/app/components/expense/expense.component.html @@ -1,135 +1,31 @@
- @if (state() === 'add' || state() === 'edit') { -
- - @if (state() === 'add') { - Track new Expense - } @else { - Edit Expense - } - - -
- } - @if (state() === 'view') { -
- - {{ expense()?.date?.toString() | date }}: {{ expense()?.cents! / 100 | currency}} - +
+ @if (!expense()) { + Track new Expense + } @else { + @if (!editingExpense()) { + {{ expense()?.date?.toString() | date }}: {{ expense()?.cents! / 100 | currency }} + } @else { + Edit Expense + } + } - -
- } + @if (!expense() || editingExpense()) { + + } @else { + + } +
- -
- - Date - @if (state() === 'view') { - - - } @else { - - } - - - - - - Cents - - - - - Category - - - @for (category of categories(); track category.id) { - {{ category.name }} - } - - - - - Merchant - - - @for (merchant of merchants(); track merchant.id) { - {{ merchant.name }} - } - - - - - Note - - - - - Tags - - @for (tag of tags(); track tag.id) { - {{ tag.name }} - } - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
diff --git a/src/app/components/expense/expense.component.scss b/src/app/components/expense/expense.component.scss index 96c5a02..f488052 100644 --- a/src/app/components/expense/expense.component.scss +++ b/src/app/components/expense/expense.component.scss @@ -20,46 +20,3 @@ mat-card-header { justify-content: space-between; } } - -.expense-content { - display: grid; -} - -mat-form-field { - width: 100%; -} - -.view-tags { - display: flex; - gap: 0.5rem; - align-items: center; -} - -@media (min-width: 550px) { - .expense-content { - grid-template-columns: 1fr 1fr; - gap: 1rem; - } - - .view-note { - grid-column: 1 / span 2; - } - - .view-tags { - grid-column: 1 / span 2; - } -} - -@media (min-width: 800px) { - .expense-content { - grid-template-columns: 1fr 1fr 1fr; - } - - .view-note { - grid-column: 2 / span 2; - } - - .view-tags { - grid-column: 1 / span 3; - } -} diff --git a/src/app/components/expense/expense.component.ts b/src/app/components/expense/expense.component.ts index 0dc286b..0d9f7b9 100644 --- a/src/app/components/expense/expense.component.ts +++ b/src/app/components/expense/expense.component.ts @@ -1,98 +1,43 @@ import { Component, computed, input, signal } from '@angular/core'; -import { Category, CategoryService } from '../../services/category.service'; -import { Merchant, MerchantService } from '../../services/merchant.service'; -import { Tag, TagService } from '../../services/tag.service'; -import { form, FormField, min, required } from '@angular/forms/signals'; +import { CategoryService } from '../../services/category.service'; +import { MerchantService } from '../../services/merchant.service'; +import { TagService } from '../../services/tag.service'; import { MatCardModule } from '@angular/material/card'; -import { MatInputModule } from '@angular/material/input'; -import { MatDatepickerModule } from '@angular/material/datepicker'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatAutocompleteModule } from '@angular/material/autocomplete'; -import { MatSelectModule } from '@angular/material/select'; import { MatButtonModule } from '@angular/material/button'; -import { provideNativeDateAdapter } from '@angular/material/core'; -import { CreateExpense, Expense, ExpenseService } from '../../services/expense.service'; -import { CurrencyPipe, DatePipe } from '@angular/common'; +import { Expense, ExpenseService } from '../../services/expense.service'; import { MatIcon } from '@angular/material/icon'; -import { Temporal } from '@js-temporal/polyfill'; -// import { MatChip } from '@angular/material/chips'; - -interface ExpenseForm { - date: Date | string; - cents: number; - category: Category | string; - merchant: Merchant | string; - note: string; - tags: Tag[]; -} +import { ExpenseFormComponent } from './expense-form/expense-form.component'; +import { CurrencyPipe, DatePipe } from '@angular/common'; @Component({ selector: 'app-expense', - imports: [ - MatDatepickerModule, - MatFormFieldModule, - MatInputModule, - MatCardModule, - MatAutocompleteModule, - MatSelectModule, - MatButtonModule, - FormField, - DatePipe, - MatIcon, - CurrencyPipe, - // MatChip - ], - providers: [provideNativeDateAdapter(), DatePipe], + imports: [MatCardModule, MatButtonModule, MatIcon, ExpenseFormComponent, DatePipe, CurrencyPipe], + providers: [], templateUrl: './expense.component.html', styleUrl: './expense.component.scss', }) export class ExpenseComponent { public expense = input(); - protected expenseMode = signal<'view' | 'edit'>('view'); - protected state = computed<'add' | 'view' | 'edit'>(() => this.expense() ? this.expenseMode() : 'add'); - protected saving = signal(false); protected categories = computed(() => this.categoryService.categories()); protected merchants = computed(() => this.merchantService.merchants()); protected tags = computed(() => this.tagService.tags()); - protected enableSaveButton = computed(() => { - const dateValid = this.expenseForm.date().valid(); - const centsValid = this.expenseForm.cents().valid(); - const categoryValid = this.expenseForm.category().valid(); - const merchantValid = this.expenseForm.merchant().valid(); - const noteValid = this.expenseForm.note().valid(); - - return dateValid && centsValid && categoryValid && merchantValid && noteValid && !this.saving(); - }); - - // private lastSelectedDate: Date | undefined; - // private expenseDate = computed(() => { - // return this.expense() - // ? new Date(`${this.expense()?.year}-${this.expense()?.month}-${this.expense()?.day}`) - // : this.lastSelectedDate ?? ''; - // }); - private defaultForm: ExpenseForm = { - date: '', - cents: this.expense()?.cents ?? NaN, - category: this.expense()?.category ?? '', - merchant: this.expense()?.merchant ?? '', - note: this.expense()?.note ?? '', - tags: this.expense()?.tags ?? [] - }; - private expenseModel = signal(this.defaultForm); - public expenseForm = form(this.expenseModel, (schema) => { - required(schema.date); - required(schema.cents); - min(schema.cents, 1); - required(schema.category); - }); public constructor(private readonly categoryService: CategoryService, private readonly merchantService: MerchantService, private readonly tagService: TagService, - private readonly expenseService: ExpenseService, - private readonly datePipe: DatePipe) { } + private readonly expenseService: ExpenseService) {} + + + public editingExpense = signal(false); + public editClick(): void { + this.editingExpense.set(true); + } + + public saveButtonEnabled = signal(true); + public saveClick(): void { + console.log('Tracking new expense') + this.editingExpense.set(false); - public async saveClick(): Promise { // const saveExpenseModel = this.expenseModel(); // const date = this.datePipe.transform(saveExpenseModel.date, 'yyyy-MM-dd')?.split('-') ?? []; // const expense: CreateExpense = { @@ -120,12 +65,4 @@ export class ExpenseComponent { // this.saving.set(false); // } } - - public editClick(): void { - this.expenseMode.set('edit'); - } - - public autocompleteDisplay(value: Merchant | Category) { - return value.name ?? null; - } } diff --git a/src/app/pages/expenses/expense-page.component.html b/src/app/pages/expenses/expense-page.component.html index 4491c8b..2add09d 100644 --- a/src/app/pages/expenses/expense-page.component.html +++ b/src/app/pages/expenses/expense-page.component.html @@ -1,4 +1,4 @@
- +
diff --git a/src/app/services/category.service.ts b/src/app/services/category.service.ts index d79dc47..211e6e8 100644 --- a/src/app/services/category.service.ts +++ b/src/app/services/category.service.ts @@ -9,8 +9,6 @@ export class CategoryService { public readonly categories = this.internalCategories.asReadonly(); public readonly categoryPath = 'http://localhost:3000/common-cents/categories'; - // public categories = signal([]); - public constructor(private readonly http: HttpService) { void this.fetchCategories(); } diff --git a/src/app/services/expense.service.ts b/src/app/services/expense.service.ts index ac11b1e..0f2a81c 100644 --- a/src/app/services/expense.service.ts +++ b/src/app/services/expense.service.ts @@ -19,15 +19,15 @@ 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(expense: CreateExpense): Promise { - // const createdExpense = await this.http.post(this.expensePath, expense); - console.log(expense); + 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; - return { ...expense, id: '', category: { id: '', name: ''}, tags: [] }; + return createdExpense; } }