added expense view mode

This commit is contained in:
Joe Arndt 2026-02-16 15:41:15 -06:00
parent fed0f7908a
commit f084c10de9
11 changed files with 121 additions and 43 deletions

View file

@ -1,14 +1,14 @@
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { Expenses } from './pages/expenses/expenses'; import { ExpensePage } from './pages/expenses/expense-page.component';
import { Home } from './pages/home/home'; import { HomePage } from './pages/home/home-page.component';
export const routes: Routes = [ export const routes: Routes = [
{ {
path: '', path: '',
component: Home component: HomePage
}, },
{ {
path: 'expenses', path: 'expenses',
component: Expenses component: ExpensePage
} }
]; ];

View file

@ -1,13 +1,12 @@
import { Component, computed } from '@angular/core'; import { Component, computed } from '@angular/core';
import { ExpenseService } from '../../services/expense.service'; import { ExpenseService } from '../../services/expense.service';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { CurrencyPipe, DatePipe } from '@angular/common';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import {ExpenseComponent} from '../expense/expense.component'; import { ExpenseComponent } from '../expense/expense.component';
@Component({ @Component({
selector: 'app-expense-list', selector: 'app-expense-list',
imports: [MatTableModule, MatCardModule, DatePipe, CurrencyPipe, ExpenseComponent], imports: [MatTableModule, MatCardModule, ExpenseComponent],
templateUrl: './expense-list.component.html', templateUrl: './expense-list.component.html',
styleUrl: './expense-list.component.scss', styleUrl: './expense-list.component.scss',
}) })

View file

@ -1,17 +1,25 @@
<div class="expense-container"> <div class="expense-container">
<mat-card appearance="outlined"> <mat-card appearance="outlined">
<mat-card-header> <mat-card-header>
@if (state() === 'add') { @if (state() === 'add' || state() === 'edit') {
<div class="expense-header"> <div class="expense-header">
<mat-card-title>Track new Expense</mat-card-title> <mat-card-title>
@if (state() === 'add') {
Track new Expense
} @else {
Edit Expense
}
</mat-card-title>
<button matButton="tonal" [disabled]="!enableSaveButton()" (click)="saveClick()">Save</button> <button matButton="tonal" [disabled]="!enableSaveButton()" (click)="saveClick()">Save</button>
</div> </div>
} }
@if (state() === 'view') { @if (state() === 'view') {
<div class="expense-header"> <div class="expense-header">
<mat-card-title>{{ `${expense()?.year}-${expense()?.month}-${expense()?.day}` | date }}</mat-card-title> <mat-card-title>
{{ `${expense()?.year}-${expense()?.month}-${expense()?.day}` | date }}: {{ expense()?.cents! / 100 | currency}}
</mat-card-title>
<button matIconButton> <button matIconButton (click)="editClick()">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
</div> </div>
@ -19,11 +27,16 @@
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
@if (state() === 'add') { <!-- @if (state() === 'add' || state() === 'edit') {-->
<div class="expense-content"> <div class="expense-content">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Date</mat-label> <mat-label>Date</mat-label>
@if (state() === 'view') {
<!-- <input matInput disabled="true" [matDatepicker]="expenseDatePicker" [placeholder]="expense()?.year! + '-' + expense()?.month! + '-' + expense()?.day!">-->
<input matInput disabled="true" [matDatepicker]="expenseDatePicker" [placeholder]="'2026-12-03'" [value]="expenseForm.date().value()">
} @else {
<input matInput [matDatepicker]="expenseDatePicker" [formField]="expenseForm.date"> <input matInput [matDatepicker]="expenseDatePicker" [formField]="expenseForm.date">
}
<mat-datepicker-toggle matIconSuffix [for]="expenseDatePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matIconSuffix [for]="expenseDatePicker"></mat-datepicker-toggle>
<mat-datepicker #expenseDatePicker></mat-datepicker> <mat-datepicker #expenseDatePicker></mat-datepicker>
</mat-form-field> </mat-form-field>
@ -67,30 +80,56 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
} <!-- }-->
@if (state() === 'view') { <!-- @if (state() === 'view') {-->
<div class="expense-content"> <!-- <div class="expense-content">-->
<div> <!-- <mat-form-field appearance="outline">-->
Amount: {{ expense()?.cents! / 100 | currency}} <!-- <input matInput disabled="true" [matDatepicker]="expenseDatePicker" [placeholder]="expense()?.year! + '-' + expense()?.month! + '-' + expense()?.day!">-->
</div> <!-- <mat-datepicker-toggle matIconSuffix [for]="expenseDatePicker"></mat-datepicker-toggle>-->
<!-- <mat-datepicker #expenseDatePicker></mat-datepicker>-->
<!-- </mat-form-field>-->
<div> <!-- <mat-form-field appearance="outline">-->
Category: {{ expense()?.category!.name }} <!-- <mat-label>Cents</mat-label>-->
</div> <!-- <input type="number" matInput disabled="true" [value]="expense()?.cents">-->
<!-- </mat-form-field>-->
<div> <!-- <mat-form-field appearance="outline">-->
Merchant: {{ expense()?.merchant?.name ?? '--' }} <!-- <mat-label>Category</mat-label>-->
</div> <!-- <input type="text" matInput [matAutocomplete]="categoryAuto" [value]="expense()?.category!.name" disabled="true">-->
</div> <!-- <mat-autocomplete [displayWith]="autocompleteDisplay" autoActiveFirstOption #categoryAuto="matAutocomplete">-->
<!-- @for (category of categories(); track category.id) {-->
<!-- <mat-option [value]="category">{{ category.name }}</mat-option>-->
<!-- }-->
<!-- </mat-autocomplete>-->
<!-- </mat-form-field>-->
<div> <!-- <mat-form-field appearance="outline">-->
Note: {{ expense()?.note ?? '--' }} <!-- <mat-label>Merchant</mat-label>-->
</div> <!-- <input type="text" matInput [matAutocomplete]="merchantAuto" [value]="expense()?.merchant?.name ?? 'N/A'" disabled="true">-->
<!-- <mat-autocomplete [displayWith]="autocompleteDisplay" autoActiveFirstOption #merchantAuto="matAutocomplete">-->
<!-- @for (merchant of merchants(); track merchant.id) {-->
<!-- <mat-option [value]="merchant">{{ merchant.name }}</mat-option>-->
<!-- }-->
<!-- </mat-autocomplete>-->
<!-- </mat-form-field>-->
<div> <!-- <mat-form-field appearance="outline" class="view-note">-->
Tags: <!-- <mat-label>Note</mat-label>-->
</div> <!-- <textarea rows="1" matInput disabled="true" [value]="expense()?.note ?? 'N/A'"></textarea>-->
} <!-- </mat-form-field>-->
<!-- <div class="view-tags">-->
<!-- <mat-card-subtitle>Tags:</mat-card-subtitle>-->
<!-- @if (!expense()?.tags?.length) {-->
<!-- N/A-->
<!-- }-->
<!-- @for (tag of expense()?.tags; track tag.id) {-->
<!-- <mat-chip>{{ tag.name }}</mat-chip>-->
<!-- }-->
<!-- </div>-->
<!-- </div>-->
<!-- }-->
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</div> </div>

View file

@ -29,15 +29,37 @@ mat-form-field {
width: 100%; width: 100%;
} }
.view-tags {
display: flex;
gap: 0.5rem;
align-items: center;
}
@media (min-width: 550px) { @media (min-width: 550px) {
.expense-content { .expense-content {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 1rem; gap: 1rem;
} }
.view-note {
grid-column: 1 / span 2;
}
.view-tags {
grid-column: 1 / span 2;
}
} }
@media (min-width: 800px) { @media (min-width: 800px) {
.expense-content { .expense-content {
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
} }
.view-note {
grid-column: 2 / span 2;
}
.view-tags {
grid-column: 1 / span 3;
}
} }

View file

@ -12,8 +12,9 @@ import { MatSelectModule } from '@angular/material/select';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { provideNativeDateAdapter } from '@angular/material/core'; import { provideNativeDateAdapter } from '@angular/material/core';
import { CreateExpense, Expense, ExpenseService } from '../../services/expense.service'; import { CreateExpense, Expense, ExpenseService } from '../../services/expense.service';
import {CurrencyPipe, DatePipe} from '@angular/common'; import { CurrencyPipe, DatePipe } from '@angular/common';
import {MatIcon} from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
// import { MatChip } from '@angular/material/chips';
interface ExpenseForm { interface ExpenseForm {
date: Date | string; date: Date | string;
@ -26,7 +27,20 @@ interface ExpenseForm {
@Component({ @Component({
selector: 'app-expense', selector: 'app-expense',
imports: [MatDatepickerModule, MatFormFieldModule, MatInputModule, MatCardModule, MatAutocompleteModule, MatSelectModule, MatButtonModule, FormField, DatePipe, MatIcon, CurrencyPipe], imports: [
MatDatepickerModule,
MatFormFieldModule,
MatInputModule,
MatCardModule,
MatAutocompleteModule,
MatSelectModule,
MatButtonModule,
FormField,
DatePipe,
MatIcon,
CurrencyPipe,
// MatChip
],
providers: [provideNativeDateAdapter(), DatePipe], providers: [provideNativeDateAdapter(), DatePipe],
templateUrl: './expense.component.html', templateUrl: './expense.component.html',
styleUrl: './expense.component.scss', styleUrl: './expense.component.scss',
@ -106,6 +120,10 @@ export class ExpenseComponent {
} }
} }
public editClick(): void {
this.expenseMode.set('edit');
}
public autocompleteDisplay(value: Merchant | Category) { public autocompleteDisplay(value: Merchant | Category) {
return value.name ?? null; return value.name ?? null;
} }

View file

@ -8,7 +8,7 @@ import { ExpenseComponent } from '../../components/expense/expense.component';
ExpenseListComponent, ExpenseListComponent,
ExpenseComponent ExpenseComponent
], ],
templateUrl: './expenses.html', templateUrl: './expense-page.component.html',
styleUrl: './expenses.scss' styleUrl: './expense-page.component.scss'
}) })
export class Expenses { } export class ExpensePage { }

View file

@ -6,7 +6,7 @@ import { RouterLink } from '@angular/router';
imports: [ imports: [
RouterLink RouterLink
], ],
templateUrl: './home.html', templateUrl: './home-page.component.html',
styleUrl: './home.scss', styleUrl: './home-page.component.scss',
}) })
export class Home { } export class HomePage { }