From fded7f7c0944360024c9efa5d8f02b9d6c18c0d1 Mon Sep 17 00:00:00 2001 From: Joe Arndt Date: Thu, 12 Feb 2026 23:41:54 -0600 Subject: [PATCH] scaffold expense list --- .../expense-list/expense-list.component.html | 7 +++ .../expense-list/expense-list.component.scss | 7 +++ .../expense-list/expense-list.component.ts | 46 +++++++++++++++++++ .../components/expense-list/expense-list.html | 3 -- .../components/expense-list/expense-list.scss | 0 .../components/expense-list/expense-list.ts | 18 -------- .../components/expense/expense.component.html | 37 +++++++++++++++ .../components/expense/expense.component.scss | 24 ++++++++++ .../components/expense/expense.component.ts | 16 +++++++ src/app/pages/expenses/expenses.ts | 4 +- src/app/pages/home/home.html | 2 +- src/app/services/categories.ts | 19 -------- src/app/services/category.service.ts | 20 ++++++++ src/app/services/expense.service.ts | 32 +++++++++++++ src/app/services/expenses.ts | 32 ------------- src/app/services/{http.ts => http.service.ts} | 2 +- src/app/services/merchant.service.ts | 20 ++++++++ src/app/services/merchants.ts | 19 -------- src/app/services/sub-categories.ts | 19 -------- src/app/services/sub-category.service.ts | 20 ++++++++ src/app/services/tag.service.ts | 20 ++++++++ src/app/services/tags.ts | 19 -------- 22 files changed, 253 insertions(+), 133 deletions(-) create mode 100644 src/app/components/expense-list/expense-list.component.html create mode 100644 src/app/components/expense-list/expense-list.component.scss create mode 100644 src/app/components/expense-list/expense-list.component.ts delete mode 100644 src/app/components/expense-list/expense-list.html delete mode 100644 src/app/components/expense-list/expense-list.scss delete mode 100644 src/app/components/expense-list/expense-list.ts create mode 100644 src/app/components/expense/expense.component.html create mode 100644 src/app/components/expense/expense.component.scss create mode 100644 src/app/components/expense/expense.component.ts delete mode 100644 src/app/services/categories.ts create mode 100644 src/app/services/category.service.ts create mode 100644 src/app/services/expense.service.ts delete mode 100644 src/app/services/expenses.ts rename src/app/services/{http.ts => http.service.ts} (97%) create mode 100644 src/app/services/merchant.service.ts delete mode 100644 src/app/services/merchants.ts delete mode 100644 src/app/services/sub-categories.ts create mode 100644 src/app/services/sub-category.service.ts create mode 100644 src/app/services/tag.service.ts delete mode 100644 src/app/services/tags.ts diff --git a/src/app/components/expense-list/expense-list.component.html b/src/app/components/expense-list/expense-list.component.html new file mode 100644 index 0000000..c5fb02f --- /dev/null +++ b/src/app/components/expense-list/expense-list.component.html @@ -0,0 +1,7 @@ +
+ @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 new file mode 100644 index 0000000..10dc123 --- /dev/null +++ b/src/app/components/expense-list/expense-list.component.scss @@ -0,0 +1,7 @@ +.expense-list-container { + padding: 1rem; +} + +.expense-item { + padding-bottom: 1rem; +} diff --git a/src/app/components/expense-list/expense-list.component.ts b/src/app/components/expense-list/expense-list.component.ts new file mode 100644 index 0000000..d13b24c --- /dev/null +++ b/src/app/components/expense-list/expense-list.component.ts @@ -0,0 +1,46 @@ +import { Component, OnInit, signal } from '@angular/core'; +import { Expense, ExpenseService } from '../../services/expense.service'; +import { ExpenseComponent } from '../expense/expense.component'; +import { Category, CategoryService } from '../../services/category.service'; +import { SubCategory, SubCategoryService } from '../../services/sub-category.service'; +import { Merchant, MerchantService } from '../../services/merchant.service'; +import { Tag, TagService } from '../../services/tag.service'; + +@Component({ + selector: 'app-expense-list', + imports: [ + ExpenseComponent + ], + templateUrl: './expense-list.component.html', + styleUrl: './expense-list.component.scss', +}) +export class ExpenseListComponent implements OnInit { + protected expenses = signal([]); + protected categories = signal([]); + protected subCategories = signal([]); + protected merchants = signal([]); + protected tags = signal([]); + + public constructor(private readonly expensesService: ExpenseService, + private readonly categoryService: CategoryService, + private readonly subCategoryService: SubCategoryService, + private readonly merchantService: MerchantService, + private readonly tagService: TagService) { } + + public ngOnInit() { + Promise.all([ + this.expensesService.getExpenses(), + this.categoryService.getCategories(), + this.subCategoryService.getSubCategories(), + this.merchantService.getMerchants(), + this.tagService.getTags() + ]).then(([expenses, categories, subCategories, merchants, tags]) => { + console.log({ expenses, categories, subCategories, merchants, tags }); // TODO: Remove me + this.expenses.set(expenses); + this.categories.set(categories); + this.subCategories.set(subCategories); + this.merchants.set(merchants); + this.tags.set(tags); + }) + } +} diff --git a/src/app/components/expense-list/expense-list.html b/src/app/components/expense-list/expense-list.html deleted file mode 100644 index d316863..0000000 --- a/src/app/components/expense-list/expense-list.html +++ /dev/null @@ -1,3 +0,0 @@ -
-

expense-list works!

-
diff --git a/src/app/components/expense-list/expense-list.scss b/src/app/components/expense-list/expense-list.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/components/expense-list/expense-list.ts b/src/app/components/expense-list/expense-list.ts deleted file mode 100644 index 1206cbb..0000000 --- a/src/app/components/expense-list/expense-list.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Expenses } from '../../services/expenses'; - -@Component({ - selector: 'app-expense-list', - imports: [], - templateUrl: './expense-list.html', - styleUrl: './expense-list.scss', -}) -export class ExpenseList implements OnInit { - public constructor(private readonly expenses: Expenses) { } - - public ngOnInit() { - this.expenses.getExpenses().then(expenses => { - console.log({ expenses }); - }); - } -} diff --git a/src/app/components/expense/expense.component.html b/src/app/components/expense/expense.component.html new file mode 100644 index 0000000..fee8c5b --- /dev/null +++ b/src/app/components/expense/expense.component.html @@ -0,0 +1,37 @@ +
+
+
+ {{ `${expense().year}/${expense().month}/${expense().day}` | date }} +
+ +
+ {{ (expense().cents / 100) | currency: 'USD' }} +
+ + @if (expense().merchant) { +
@ {{ expense().merchant?.name }}
+ } +
+ +
+
+ Category: {{ expense().category.name }} + @if (expense().subCategory) { + / {{ expense().subCategory?.name }} + } +
+ +
+ Description: {{ expense().description }} +
+
+ + +
diff --git a/src/app/components/expense/expense.component.scss b/src/app/components/expense/expense.component.scss new file mode 100644 index 0000000..a49c419 --- /dev/null +++ b/src/app/components/expense/expense.component.scss @@ -0,0 +1,24 @@ +.expense-container { + border-radius: 5px; + padding: 1rem; + box-shadow: rgba(99, 99, 99, 0.2) 0 2px 8px 0; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.expense-header { + display: flex; + gap: 0.5rem; +} + +.expense-body { + +} + +.expense-footer { + .expense-tags { + display: flex; + gap: 0.5rem; + } +} diff --git a/src/app/components/expense/expense.component.ts b/src/app/components/expense/expense.component.ts new file mode 100644 index 0000000..1b61a9a --- /dev/null +++ b/src/app/components/expense/expense.component.ts @@ -0,0 +1,16 @@ +import {Component, input} from '@angular/core'; +import { Expense } from '../../services/expense.service'; +import {CurrencyPipe, DatePipe} from '@angular/common'; + +@Component({ + selector: 'app-expense', + imports: [ + CurrencyPipe, + DatePipe + ], + templateUrl: './expense.component.html', + styleUrl: './expense.component.scss', +}) +export class ExpenseComponent { + public expense = input.required(); +} diff --git a/src/app/pages/expenses/expenses.ts b/src/app/pages/expenses/expenses.ts index 1fefb3e..44db40c 100644 --- a/src/app/pages/expenses/expenses.ts +++ b/src/app/pages/expenses/expenses.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; -import { ExpenseList } from '../../components/expense-list/expense-list'; +import { ExpenseListComponent } from '../../components/expense-list/expense-list.component'; @Component({ selector: 'app-expenses', imports: [ - ExpenseList + ExpenseListComponent ], templateUrl: './expenses.html', styleUrl: './expenses.scss' diff --git a/src/app/pages/home/home.html b/src/app/pages/home/home.html index e74d430..94c5c02 100644 --- a/src/app/pages/home/home.html +++ b/src/app/pages/home/home.html @@ -1,7 +1,7 @@ diff --git a/src/app/services/categories.ts b/src/app/services/categories.ts deleted file mode 100644 index 405b7e9..0000000 --- a/src/app/services/categories.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root', -}) -export class Categories { - public static readonly BASE_URL = 'http://localhost:3000/common-cents/categories'; - - public async getCategories(): Promise { - console.log('getCategories called'); - - return []; - } -} - -export interface Category { - id: string; - name: string; -} diff --git a/src/app/services/category.service.ts b/src/app/services/category.service.ts new file mode 100644 index 0000000..ee88ef3 --- /dev/null +++ b/src/app/services/category.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { HttpService } from './http.service'; + +@Injectable({ + providedIn: 'root', +}) +export class CategoryService { + public static readonly CATEGORY_PATH = 'http://localhost:3000/common-cents/categories'; + + public constructor(private readonly http: HttpService) { } + + public async getCategories(): Promise { + return this.http.get(CategoryService.CATEGORY_PATH); + } +} + +export interface Category { + id: string; + name: string; +} diff --git a/src/app/services/expense.service.ts b/src/app/services/expense.service.ts new file mode 100644 index 0000000..d795591 --- /dev/null +++ b/src/app/services/expense.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { SubCategory } from './sub-category.service'; +import { Category } from './category.service'; +import { Merchant } from './merchant.service'; +import { Tag } from './tag.service'; +import { HttpService } from './http.service'; + +@Injectable({ + providedIn: 'root', +}) +export class ExpenseService { + public static readonly EXPENSE_PATH = 'http://localhost:3000/common-cents/expenses'; + + public constructor(private readonly http: HttpService) { } + + public async getExpenses(): Promise { + return this.http.get(ExpenseService.EXPENSE_PATH); + } +} + +export interface Expense { + id: string; + year: string; + month: string; + day: string; + cents: number; + description?: string; + category: Category; + subCategory?: SubCategory; + merchant?: Merchant; + tags: Tag[]; +} diff --git a/src/app/services/expenses.ts b/src/app/services/expenses.ts deleted file mode 100644 index 7b8b65f..0000000 --- a/src/app/services/expenses.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Injectable } from '@angular/core'; -import { SubCategory } from './sub-categories'; -import { Category } from './categories'; -import { Merchant } from './merchants'; -import { Tag } from './tags'; -import { Http } from './http'; - -@Injectable({ - providedIn: 'root', -}) -export class Expenses { - public static readonly EXPENSES_URI = 'http://localhost:3000/common-cents/expenses'; - - public constructor(private readonly http: Http) { } - - public async getExpenses(): Promise { - return this.http.get(Expenses.EXPENSES_URI); - } -} - -export interface Expense { - id: string; - year: string; - month: string; - day: string; - cents: number; - description?: string; - category: Category; - subCategory?: SubCategory; - merchant?: Merchant; - tags: Tag[]; -} diff --git a/src/app/services/http.ts b/src/app/services/http.service.ts similarity index 97% rename from src/app/services/http.ts rename to src/app/services/http.service.ts index ccda4b4..d9f3212 100644 --- a/src/app/services/http.ts +++ b/src/app/services/http.service.ts @@ -5,7 +5,7 @@ import { firstValueFrom } from 'rxjs'; @Injectable({ providedIn: 'root', }) -export class Http { +export class HttpService { public constructor(private httpClient: HttpClient) { } public async get(url: string): Promise { diff --git a/src/app/services/merchant.service.ts b/src/app/services/merchant.service.ts new file mode 100644 index 0000000..20a9d68 --- /dev/null +++ b/src/app/services/merchant.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { HttpService } from './http.service'; + +@Injectable({ + providedIn: 'root', +}) +export class MerchantService { + public static readonly MERCHANT_PATH = 'http://localhost:3000/common-cents/merchants'; + + public constructor(private readonly http: HttpService) { } + + public async getMerchants(): Promise { + return this.http.get(MerchantService.MERCHANT_PATH); + } +} + +export interface Merchant { + id: string; + name: string; +} diff --git a/src/app/services/merchants.ts b/src/app/services/merchants.ts deleted file mode 100644 index 730c177..0000000 --- a/src/app/services/merchants.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root', -}) -export class Merchants { - public static readonly BASE_URL = 'http://localhost:3000/common-cents/merchants'; - - public async getMerchants(): Promise { - console.log('getMerchants called'); - - return []; - } -} - -export interface Merchant { - id: string; - name: string; -} diff --git a/src/app/services/sub-categories.ts b/src/app/services/sub-categories.ts deleted file mode 100644 index 8615603..0000000 --- a/src/app/services/sub-categories.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root', -}) -export class SubCategories { - public static readonly BASE_URL = 'http://localhost:3000/common-cents/sub-categories'; - - public async getSubCategories(): Promise { - console.log('getSubCategories called'); - - return []; - } -} - -export interface SubCategory { - id: string; - name: string; -} diff --git a/src/app/services/sub-category.service.ts b/src/app/services/sub-category.service.ts new file mode 100644 index 0000000..1b4e268 --- /dev/null +++ b/src/app/services/sub-category.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { HttpService } from './http.service'; + +@Injectable({ + providedIn: 'root', +}) +export class SubCategoryService { + public static readonly SUBCATEGORY_PATH = 'http://localhost:3000/common-cents/sub-categories'; + + public constructor(private readonly http: HttpService) { } + + public async getSubCategories(): Promise { + return this.http.get(SubCategoryService.SUBCATEGORY_PATH); + } +} + +export interface SubCategory { + id: string; + name: string; +} diff --git a/src/app/services/tag.service.ts b/src/app/services/tag.service.ts new file mode 100644 index 0000000..e5acad5 --- /dev/null +++ b/src/app/services/tag.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { HttpService } from './http.service'; + +@Injectable({ + providedIn: 'root', +}) +export class TagService { + public static readonly TAG_PATH = 'http://localhost:3000/common-cents/tags'; + + public constructor(private readonly http: HttpService) { } + + public async getTags(): Promise { + return this.http.get(TagService.TAG_PATH); + } +} + +export interface Tag { + id: string; + name: string; +} diff --git a/src/app/services/tags.ts b/src/app/services/tags.ts deleted file mode 100644 index 282b344..0000000 --- a/src/app/services/tags.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root', -}) -export class Tags { - public static readonly BASE_URL = 'http://localhost:3000/common-cents/tags'; - - public async getTags(): Promise { - console.log('getTags called'); - - return []; - } -} - -export interface Tag { - id: string; - name: string; -}