refactored services and expense list
This commit is contained in:
parent
fded7f7c09
commit
6e6cced0c6
15 changed files with 125 additions and 91 deletions
19
src/app/components/add-expense/add-expense.component.html
Normal file
19
src/app/components/add-expense/add-expense.component.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<div class="add-expense-container">
|
||||
<app-card [header]="'Add Expense'">
|
||||
<div class="add-expense-body">
|
||||
<div>Date</div>
|
||||
<div>Amount</div>
|
||||
<div>Category</div>
|
||||
<div>Merchant</div>
|
||||
<div>Tags</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Note
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
Test
|
||||
</div>
|
||||
</app-card>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
.add-expense-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
14
src/app/components/add-expense/add-expense.component.ts
Normal file
14
src/app/components/add-expense/add-expense.component.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {CardComponent} from '../card/card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-expense',
|
||||
imports: [
|
||||
CardComponent
|
||||
],
|
||||
templateUrl: './add-expense.component.html',
|
||||
styleUrl: './add-expense.component.scss',
|
||||
})
|
||||
export class AddExpenseComponent {
|
||||
|
||||
}
|
||||
15
src/app/components/card/card.component.html
Normal file
15
src/app/components/card/card.component.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<div class="card-container">
|
||||
@if (header()) {
|
||||
<div class="card-header">{{ header() }}</div>
|
||||
} @else {
|
||||
<ng-content select="card-header" />
|
||||
}
|
||||
|
||||
<ng-content />
|
||||
|
||||
@if (footer()) {
|
||||
<div class="card-footer">{{ footer() }}</div>
|
||||
} @else {
|
||||
<ng-content select="card-footer" />
|
||||
}
|
||||
</div>
|
||||
8
src/app/components/card/card.component.scss
Normal file
8
src/app/components/card/card.component.scss
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.card-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;
|
||||
}
|
||||
12
src/app/components/card/card.component.ts
Normal file
12
src/app/components/card/card.component.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { Component, input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
imports: [],
|
||||
templateUrl: './card.component.html',
|
||||
styleUrl: './card.component.scss',
|
||||
})
|
||||
export class CardComponent {
|
||||
public header = input<string>('');
|
||||
public footer = input<string>('');
|
||||
}
|
||||
|
|
@ -1,10 +1,6 @@
|
|||
import { Component, OnInit, signal } from '@angular/core';
|
||||
import { Expense, ExpenseService } from '../../services/expense.service';
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { 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',
|
||||
|
|
@ -14,33 +10,8 @@ import { Tag, TagService } from '../../services/tag.service';
|
|||
templateUrl: './expense-list.component.html',
|
||||
styleUrl: './expense-list.component.scss',
|
||||
})
|
||||
export class ExpenseListComponent implements OnInit {
|
||||
protected expenses = signal<Expense[]>([]);
|
||||
protected categories = signal<Category[]>([]);
|
||||
protected subCategories = signal<SubCategory[]>([]);
|
||||
protected merchants = signal<Merchant[]>([]);
|
||||
protected tags = signal<Tag[]>([]);
|
||||
export class ExpenseListComponent {
|
||||
protected expenses = computed(() => this.expensesService.expenses())
|
||||
|
||||
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);
|
||||
})
|
||||
}
|
||||
public constructor(private readonly expensesService: ExpenseService) { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,25 @@
|
|||
<div class="expense-container">
|
||||
<div class="expense-header">
|
||||
<div class="expense-date">
|
||||
{{ `${expense().year}/${expense().month}/${expense().day}` | date }}
|
||||
</div>
|
||||
|
||||
<div class="expense-amount">
|
||||
{{ (expense().cents / 100) | currency: 'USD' }}
|
||||
</div>
|
||||
{{ `${expense().year}/${expense().month}/${expense().day}` | date }}: {{ (expense().cents / 100) | currency: 'USD' }}
|
||||
|
||||
@if (expense().merchant) {
|
||||
<div class="expense-merchant">@ {{ expense().merchant?.name }}</div>
|
||||
at {{ expense().merchant?.name }}
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="expense-body">
|
||||
<div>
|
||||
Category: {{ expense().category.name }}
|
||||
@if (expense().subCategory) {
|
||||
/ {{ expense().subCategory?.name }}
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Description: {{ expense().description }}
|
||||
Note: {{ expense().note ?? 'N/A' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="expense-footer">
|
||||
<div class="expense-tags">
|
||||
<div>Tags:</div>
|
||||
Tags:
|
||||
@for (tag of expense().tags; track tag.id) {
|
||||
<div>{{ tag.name }}</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<div class="expenses-container">
|
||||
<app-add-expense />
|
||||
<app-expense-list />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { ExpenseListComponent } from '../../components/expense-list/expense-list.component';
|
||||
import { AddExpenseComponent } from '../../components/add-expense/add-expense.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-expenses',
|
||||
imports: [
|
||||
ExpenseListComponent
|
||||
ExpenseListComponent,
|
||||
AddExpenseComponent
|
||||
],
|
||||
templateUrl: './expenses.html',
|
||||
styleUrl: './expenses.scss'
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, signal } 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';
|
||||
private internalCategories = signal<Category[]>([]);
|
||||
public readonly categories = this.internalCategories.asReadonly();
|
||||
public readonly categoryPath = 'http://localhost:3000/common-cents/categories';
|
||||
|
||||
public constructor(private readonly http: HttpService) { }
|
||||
// public categories = signal<Category[]>([]);
|
||||
|
||||
public async getCategories(): Promise<Category[]> {
|
||||
return this.http.get<Category[]>(CategoryService.CATEGORY_PATH);
|
||||
public constructor(private readonly http: HttpService) {
|
||||
void this.fetchCategories();
|
||||
}
|
||||
|
||||
public async fetchCategories(): Promise<void> {
|
||||
this.internalCategories.set(await this.http.get<Category[]>(this.categoryPath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { SubCategory } from './sub-category.service';
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { Category } from './category.service';
|
||||
import { Merchant } from './merchant.service';
|
||||
import { Tag } from './tag.service';
|
||||
|
|
@ -9,12 +8,16 @@ import { HttpService } from './http.service';
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class ExpenseService {
|
||||
public static readonly EXPENSE_PATH = 'http://localhost:3000/common-cents/expenses';
|
||||
private internalExpenses = signal<Expense[]>([]);
|
||||
public readonly expenses = this.internalExpenses.asReadonly();
|
||||
public readonly expensePath = 'http://localhost:3000/common-cents/expenses';
|
||||
|
||||
public constructor(private readonly http: HttpService) { }
|
||||
public constructor(private readonly http: HttpService) {
|
||||
void this.fetchExpenses();
|
||||
}
|
||||
|
||||
public async getExpenses(): Promise<Expense[]> {
|
||||
return this.http.get<Expense[]>(ExpenseService.EXPENSE_PATH);
|
||||
public async fetchExpenses(): Promise<void> {
|
||||
this.internalExpenses.set(await this.http.get(this.expensePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,9 +27,8 @@ export interface Expense {
|
|||
month: string;
|
||||
day: string;
|
||||
cents: number;
|
||||
description?: string;
|
||||
category: Category;
|
||||
subCategory?: SubCategory;
|
||||
note?: string;
|
||||
merchant?: Merchant;
|
||||
tags: Tag[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, signal } 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';
|
||||
private internalMerchants = signal<Merchant[]>([]);
|
||||
public readonly merchants = this.internalMerchants.asReadonly();
|
||||
public readonly merchantPath = 'http://localhost:3000/common-cents/merchants';
|
||||
|
||||
public constructor(private readonly http: HttpService) { }
|
||||
public constructor(private readonly http: HttpService) {
|
||||
void this.fetchMerchants();
|
||||
}
|
||||
|
||||
public async getMerchants(): Promise<Merchant[]> {
|
||||
return this.http.get<Merchant[]>(MerchantService.MERCHANT_PATH);
|
||||
public async fetchMerchants(): Promise<void> {
|
||||
this.internalMerchants.set(await this.http.get<Merchant[]>(this.merchantPath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
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<SubCategory[]> {
|
||||
return this.http.get<SubCategory[]>(SubCategoryService.SUBCATEGORY_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
export interface SubCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
|
@ -1,16 +1,20 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, signal } 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';
|
||||
private internalTags = signal<Tag[]>([]);
|
||||
public readonly tags = this.internalTags.asReadonly();
|
||||
public readonly tagPath = 'http://localhost:3000/common-cents/tags';
|
||||
|
||||
public constructor(private readonly http: HttpService) { }
|
||||
public constructor(private readonly http: HttpService) {
|
||||
void this.fetchTags();
|
||||
}
|
||||
|
||||
public async getTags(): Promise<Tag[]> {
|
||||
return this.http.get<Tag[]>(TagService.TAG_PATH);
|
||||
public async fetchTags(): Promise<void> {
|
||||
this.internalTags.set(await this.http.get<Tag[]>(this.tagPath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue