Compare commits
No commits in common. "0bf8bd6c9ce6fb515b65a57fbbac478a256593d2" and "bd18fecdff547ad62b0315f7ffe511055a65dfa5" have entirely different histories.
0bf8bd6c9c
...
bd18fecdff
19 changed files with 35 additions and 297 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
|
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<div class="expense-list-container">
|
|
||||||
@for (expense of expenses(); track expense.id) {
|
|
||||||
<div class="expense-item">
|
|
||||||
<app-expense [expense]="expense" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
.expense-list-container {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expense-item {
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
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<Expense[]>([]);
|
|
||||||
protected categories = signal<Category[]>([]);
|
|
||||||
protected subCategories = signal<SubCategory[]>([]);
|
|
||||||
protected merchants = signal<Merchant[]>([]);
|
|
||||||
protected tags = signal<Tag[]>([]);
|
|
||||||
|
|
||||||
public constructor(private readonly expensesService: ExpenseService,
|
|
||||||
private readonly categoryService: CategoryService,
|
|
||||||
private readonly subCategoryService: SubCategoryService,
|
|
||||||
private readonly merchantService: MerchantService,
|
|
||||||
private readonly tagService: TagService) { }
|
|
||||||
|
|
||||||
public ngOnInit() {
|
|
||||||
// this.expensesService.getExpenses().then(expenses => {
|
|
||||||
// console.log({ expenses }); // TODO: remove me
|
|
||||||
// this.expenses.set(expenses);
|
|
||||||
// });
|
|
||||||
|
|
||||||
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);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
src/app/components/expense-list/expense-list.html
Normal file
3
src/app/components/expense-list/expense-list.html
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="expense-list-container">
|
||||||
|
<p>expense-list works!</p>
|
||||||
|
</div>
|
||||||
0
src/app/components/expense-list/expense-list.scss
Normal file
0
src/app/components/expense-list/expense-list.scss
Normal file
16
src/app/components/expense-list/expense-list.ts
Normal file
16
src/app/components/expense-list/expense-list.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
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() {
|
||||||
|
void this.expenses.getExpenses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
<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>
|
|
||||||
|
|
||||||
@if (expense().merchant) {
|
|
||||||
<div class="expense-merchant">@ {{ expense().merchant?.name }}</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="expense-body">
|
|
||||||
<div>
|
|
||||||
Category: {{ expense().category.name }}
|
|
||||||
@if (expense().subCategory) {
|
|
||||||
/ {{ expense().subCategory?.name }}
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Description: {{ expense().description }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="expense-footer">
|
|
||||||
<div class="expense-tags">
|
|
||||||
<div>Tags:</div>
|
|
||||||
@for (tag of expense().tags; track tag.id) {
|
|
||||||
<div>{{ tag.name }}</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
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<Expense>();
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ExpenseListComponent } from '../../components/expense-list/expense-list.component';
|
import { ExpenseList } from '../../components/expense-list/expense-list';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-expenses',
|
selector: 'app-expenses',
|
||||||
imports: [
|
imports: [
|
||||||
ExpenseListComponent
|
ExpenseList
|
||||||
],
|
],
|
||||||
templateUrl: './expenses.html',
|
templateUrl: './expenses.html',
|
||||||
styleUrl: './expenses.scss'
|
styleUrl: './expenses.scss'
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="home-container">
|
<div class="home-container">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="expenses">ExpenseService</a>
|
<a routerLink="expenses">Expenses</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
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<Category[]> {
|
|
||||||
return this.http.get<Category[]>(CategoryService.CATEGORY_PATH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Category {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
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<Expense[]> {
|
|
||||||
return this.http.get<Expense[]>(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[];
|
|
||||||
}
|
|
||||||
12
src/app/services/expenses.ts
Normal file
12
src/app/services/expenses.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class Expenses {
|
||||||
|
public static readonly BASE_URL = 'http://localhost:3000/common-cents/expenses';
|
||||||
|
|
||||||
|
public async getExpenses(): Promise<void> {
|
||||||
|
console.log('getExpenses called');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class HttpService {
|
|
||||||
public constructor(private httpClient: HttpClient) { }
|
|
||||||
|
|
||||||
public async get<T>(url: string): Promise<T> {
|
|
||||||
return this.request<T>(url, 'get');
|
|
||||||
}
|
|
||||||
|
|
||||||
public async post<T>(url: string, body?: any): Promise<T> {
|
|
||||||
return this.request<T>(url, 'post', body);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async put<T>(url: string, body?: any): Promise<T> {
|
|
||||||
return this.request<T>(url, 'put', body);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async delete<T>(url: string): Promise<T> {
|
|
||||||
return this.request<T>(url, 'delete');
|
|
||||||
}
|
|
||||||
|
|
||||||
private async request<T>(url: string, method: 'get' | 'post' | 'put' | 'delete', body?: any): Promise<T> {
|
|
||||||
const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' };
|
|
||||||
switch (method) {
|
|
||||||
case 'post':
|
|
||||||
return firstValueFrom(this.httpClient.post<T>(url, body, { headers }));
|
|
||||||
case 'put':
|
|
||||||
return firstValueFrom(this.httpClient.put<T>(url, body, { headers }));
|
|
||||||
case 'delete':
|
|
||||||
return firstValueFrom(this.httpClient.delete<T>(url, { headers }));
|
|
||||||
default:
|
|
||||||
return firstValueFrom(this.httpClient.get<T>(url, { headers }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
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<Merchant[]> {
|
|
||||||
return this.http.get<Merchant[]>(MerchantService.MERCHANT_PATH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Merchant {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
@ -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,20 +0,0 @@
|
||||||
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<Tag[]> {
|
|
||||||
return this.http.get<Tag[]>(TagService.TAG_PATH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Tag {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue