Add additional chart to dashboard and prefill some transaction creation details
Signed-off-by: Billy Brawner <billy@wbrawner.com>
This commit is contained in:
parent
a3ea896232
commit
6968d9cfb8
11 changed files with 218 additions and 16 deletions
59
package-lock.json
generated
59
package-lock.json
generated
|
@ -1171,6 +1171,11 @@
|
||||||
"semver-intersect": "1.4.0"
|
"semver-intersect": "1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/chart.js": {
|
||||||
|
"version": "2.7.52",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.52.tgz",
|
||||||
|
"integrity": "sha512-h4Md9c0FYPoqHyPeo3sG+wBIDFGz6GubulKUopsmFkSSW2ieyI2phjlj+FjqzTwhrWwR9dbw/HlCW3axj+tWug=="
|
||||||
|
},
|
||||||
"@types/jasmine": {
|
"@types/jasmine": {
|
||||||
"version": "2.8.16",
|
"version": "2.8.16",
|
||||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz",
|
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz",
|
||||||
|
@ -2626,6 +2631,39 @@
|
||||||
"integrity": "sha1-5upnvSR+EHESmDt6sEee02KAAIE=",
|
"integrity": "sha1-5upnvSR+EHESmDt6sEee02KAAIE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"chart.js": {
|
||||||
|
"version": "2.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.8.0.tgz",
|
||||||
|
"integrity": "sha512-Di3wUL4BFvqI5FB5K26aQ+hvWh8wnP9A3DWGvXHVkO13D3DSnaSsdZx29cXlEsYKVkn1E2az+ZYFS4t0zi8x0w==",
|
||||||
|
"requires": {
|
||||||
|
"chartjs-color": "2.3.0",
|
||||||
|
"moment": "2.24.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chartjs-color": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g==",
|
||||||
|
"requires": {
|
||||||
|
"chartjs-color-string": "0.6.0",
|
||||||
|
"color-convert": "0.5.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
|
||||||
|
"integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chartjs-color-string": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
|
||||||
|
@ -2862,8 +2900,7 @@
|
||||||
"color-name": {
|
"color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
|
||||||
"integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=",
|
"integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
|
@ -7989,8 +8026,7 @@
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.11",
|
"version": "4.17.11",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lodash._isnative": {
|
"lodash._isnative": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
|
@ -8572,6 +8608,11 @@
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.24.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||||
|
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
||||||
|
},
|
||||||
"morgan": {
|
"morgan": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
||||||
|
@ -8687,6 +8728,16 @@
|
||||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ng2-charts": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-Kxj2bewn537xGFVkR7AgDmfqV+YH4hIL4R36EjlUI9WCWnphzY+VKZGX+D+usXd8e+znuqly+sbGHjddLxupUA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/chart.js": "2.7.52",
|
||||||
|
"lodash": "4.17.11",
|
||||||
|
"tslib": "1.9.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ng2-currency-mask": {
|
"ng2-currency-mask": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ng2-currency-mask/-/ng2-currency-mask-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ng2-currency-mask/-/ng2-currency-mask-5.3.1.tgz",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve --host '0.0.0.0'",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"publish": "ng build --prod --service-worker && firebase deploy",
|
"publish": "ng build --prod --service-worker && firebase deploy",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
|
@ -25,10 +25,12 @@
|
||||||
"@angular/pwa": "^0.7.5",
|
"@angular/pwa": "^0.7.5",
|
||||||
"@angular/router": "^7.2.14",
|
"@angular/router": "^7.2.14",
|
||||||
"@angular/service-worker": "^7.2.14",
|
"@angular/service-worker": "^7.2.14",
|
||||||
|
"chart.js": "^2.8.0",
|
||||||
"core-js": "^2.6.5",
|
"core-js": "^2.6.5",
|
||||||
"dexie": "^2.0.4",
|
"dexie": "^2.0.4",
|
||||||
"firebase": "^5.11.1",
|
"firebase": "^5.11.1",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
|
"ng2-charts": "^2.2.3",
|
||||||
"ng2-currency-mask": "^5.3.1",
|
"ng2-currency-mask": "^5.3.1",
|
||||||
"rxjs": "^6.5.1",
|
"rxjs": "^6.5.1",
|
||||||
"zone.js": "^0.8.29"
|
"zone.js": "^0.8.29"
|
||||||
|
|
|
@ -5,33 +5,41 @@
|
||||||
<span
|
<span
|
||||||
[ngClass]="{'income': getBalance() > 0, 'expense': getBalance() < 0}">{{ getBalance() / 100 | currency }}</span>
|
[ngClass]="{'income': getBalance() > 0, 'expense': getBalance() < 0}">{{ getBalance() / 100 | currency }}</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
<app-category-breakdown [barChartLabels]="barChartLabels" [barChartData]="barChartData">
|
||||||
|
</app-category-breakdown>
|
||||||
<div class="transaction-navigation">
|
<div class="transaction-navigation">
|
||||||
<a mat-button routerLink="/accounts/{{ account.id }}/transactions">View Transactions</a>
|
<a mat-button routerLink="/accounts/{{ account.id }}/transactions" *ngIf="account">View Transactions</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-categories" [hidden]="!account">
|
<div class="dashboard-categories" [hidden]="!account">
|
||||||
<h3 class="categories">Income</h3>
|
<h3 class="categories">Income</h3>
|
||||||
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new" class="view-all">Add Category</a>
|
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new" class="view-all" *ngIf="account">Add Category</a>
|
||||||
<div class="no-categories" *ngIf="!income || income.length === 0">
|
<div class="no-categories" *ngIf="!income || income.length === 0">
|
||||||
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new">
|
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new" *ngIf="account">
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
<p>Add categories to gain more insights into your income.</p>
|
<p>Add categories to gain more insights into your income.</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<app-category-list [accountId]="account.id" [categories]="income" [categoryBalances]="categoryBalances"></app-category-list>
|
<div class="category-info" *ngIf="income && income.length > 0">
|
||||||
|
<app-category-list [accountId]="account.id" [categories]="income" [categoryBalances]="categoryBalances">
|
||||||
|
</app-category-list>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-categories" [hidden]="!account">
|
<div class="dashboard-categories" [hidden]="!account">
|
||||||
<h3 class="categories">Expenses</h3>
|
<h3 class="categories">Expenses</h3>
|
||||||
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new" class="view-all">Add Category</a>
|
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new" class="view-all" *ngIf="account">Add Category</a>
|
||||||
<div class="no-categories" *ngIf="!expenses || expenses.length === 0">
|
<div class="no-categories" *ngIf="!expenses || expenses.length === 0">
|
||||||
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new">
|
<a mat-button routerLink="/accounts/{{ account.id }}/categories/new" *ngIf="account">
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
<p>Add categories to gain more insights into your expenses.</p>
|
<p>Add categories to gain more insights into your expenses.</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<app-category-list [accountId]="account.id" [categories]="expenses" [categoryBalances]="categoryBalances"></app-category-list>
|
<div class="category-info" *ngIf="expenses && expenses.length > 0">
|
||||||
|
<app-category-list [accountId]="account.id" [categories]="expenses" [categoryBalances]="categoryBalances">
|
||||||
|
</app-category-list>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a mat-fab routerLink="/accounts/{{ account.id }}/transactions/new" [hidden]="!account">
|
<a mat-fab routerLink="/accounts/{{ account.id }}/transactions/new" *ngIf="account">
|
||||||
<mat-icon aria-label="Add">add</mat-icon>
|
<mat-icon aria-label="Add">add</mat-icon>
|
||||||
</a>
|
</a>
|
|
@ -9,6 +9,8 @@ import { Observable } from 'rxjs';
|
||||||
import { TransactionType } from 'src/app/transactions/transaction.type';
|
import { TransactionType } from 'src/app/transactions/transaction.type';
|
||||||
import { TRANSACTION_SERVICE, TransactionService } from 'src/app/transactions/transaction.service';
|
import { TRANSACTION_SERVICE, TransactionService } from 'src/app/transactions/transaction.service';
|
||||||
import { CATEGORY_SERVICE, CategoryService } from 'src/app/categories/category.service';
|
import { CATEGORY_SERVICE, CategoryService } from 'src/app/categories/category.service';
|
||||||
|
import { Label } from 'ng2-charts';
|
||||||
|
import { ChartDataSets } from 'chart.js';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-account-details',
|
selector: 'app-account-details',
|
||||||
|
@ -22,6 +24,15 @@ export class AccountDetailsComponent implements OnInit {
|
||||||
public expenses: Category[] = [];
|
public expenses: Category[] = [];
|
||||||
public income: Category[] = [];
|
public income: Category[] = [];
|
||||||
categoryBalances: Map<string, number>;
|
categoryBalances: Map<string, number>;
|
||||||
|
expectedIncome = 0;
|
||||||
|
actualIncome = 0;
|
||||||
|
expectedExpenses = 0;
|
||||||
|
actualExpenses = 0;
|
||||||
|
barChartLabels: Label[] = ['Income', 'Expenses'];
|
||||||
|
barChartData: ChartDataSets[] = [
|
||||||
|
{ data: [0, 0], label: 'Expected' },
|
||||||
|
{ data: [0, 0], label: 'Actual' },
|
||||||
|
];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private app: AppComponent,
|
private app: AppComponent,
|
||||||
|
@ -49,6 +60,27 @@ export class AccountDetailsComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateBarChart() {
|
||||||
|
const color = [0, 188, 212];
|
||||||
|
this.barChartData = [
|
||||||
|
{
|
||||||
|
data: [this.expectedIncome / 100, this.expectedExpenses / 100],
|
||||||
|
label: 'Expected',
|
||||||
|
backgroundColor: 'rgba(241, 241, 241, 0.8)',
|
||||||
|
borderColor: 'rgba(241, 241, 241, 0.9)',
|
||||||
|
hoverBackgroundColor: 'rgba(241, 241, 241, 1)',
|
||||||
|
hoverBorderColor: 'rgba(241, 241, 241, 1)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: [this.actualIncome / 100, this.actualExpenses / 100],
|
||||||
|
label: 'Actual',
|
||||||
|
backgroundColor: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.8)`,
|
||||||
|
borderColor: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.9)`,
|
||||||
|
hoverBackgroundColor: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`,
|
||||||
|
hoverBorderColor: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
getBalance(): number {
|
getBalance(): number {
|
||||||
let totalBalance = 0;
|
let totalBalance = 0;
|
||||||
|
@ -68,13 +100,38 @@ export class AccountDetailsComponent implements OnInit {
|
||||||
|
|
||||||
getCategories(): void {
|
getCategories(): void {
|
||||||
this.categoryService.getCategories(this.account.id).subscribe(categories => {
|
this.categoryService.getCategories(this.account.id).subscribe(categories => {
|
||||||
|
const categoryBalances = new Map<string, number>();
|
||||||
|
let categoryBalancesCount = 0;
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
if (category.isExpense) {
|
if (category.isExpense) {
|
||||||
this.expenses.push(category);
|
this.expenses.push(category);
|
||||||
|
this.expectedExpenses += category.amount;
|
||||||
} else {
|
} else {
|
||||||
this.income.push(category);
|
this.income.push(category);
|
||||||
|
this.expectedIncome += category.amount;
|
||||||
}
|
}
|
||||||
this.getCategoryBalance(category.id).subscribe(balance => this.categoryBalances.set(category.id, balance));
|
this.getCategoryBalance(category.id).subscribe(
|
||||||
|
balance => {
|
||||||
|
console.log(balance);
|
||||||
|
if (category.isExpense) {
|
||||||
|
this.actualExpenses += balance * -1;
|
||||||
|
} else {
|
||||||
|
this.actualIncome += balance;
|
||||||
|
}
|
||||||
|
categoryBalances.set(category.id, balance);
|
||||||
|
categoryBalancesCount++;
|
||||||
|
},
|
||||||
|
error => { categoryBalancesCount++; },
|
||||||
|
() => {
|
||||||
|
// This weird workaround is to force the OnChanges callback to be fired.
|
||||||
|
// Angular needs the reference to the object to change in order for it to
|
||||||
|
// work.
|
||||||
|
if (categoryBalancesCount === categories.length) {
|
||||||
|
this.categoryBalances = categoryBalances;
|
||||||
|
this.updateBarChart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -91,6 +148,7 @@ export class AccountDetailsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subscriber.next(balance);
|
subscriber.next(balance);
|
||||||
|
subscriber.complete();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@ import { ACCOUNT_SERVICE } from './accounts/account.service';
|
||||||
import { FirestoreAccountService } from './accounts/account.service.firestore';
|
import { FirestoreAccountService } from './accounts/account.service.firestore';
|
||||||
import { USER_SERVICE } from './users/user.service';
|
import { USER_SERVICE } from './users/user.service';
|
||||||
import { FirestoreUserService } from './users/user.service.firestore';
|
import { FirestoreUserService } from './users/user.service.firestore';
|
||||||
|
import { CategoryBreakdownComponent } from './categories/category-breakdown/category-breakdown.component';
|
||||||
|
import { ChartsModule } from 'ng2-charts';
|
||||||
|
|
||||||
export const CustomCurrencyMaskConfig: CurrencyMaskConfig = {
|
export const CustomCurrencyMaskConfig: CurrencyMaskConfig = {
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -79,6 +81,7 @@ export const CustomCurrencyMaskConfig: CurrencyMaskConfig = {
|
||||||
NewAccountComponent,
|
NewAccountComponent,
|
||||||
AccountDetailsComponent,
|
AccountDetailsComponent,
|
||||||
AccountsComponent,
|
AccountsComponent,
|
||||||
|
CategoryBreakdownComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
@ -99,6 +102,7 @@ export const CustomCurrencyMaskConfig: CurrencyMaskConfig = {
|
||||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
CurrencyMaskModule,
|
CurrencyMaskModule,
|
||||||
|
ChartsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: CURRENCY_MASK_CONFIG, useValue: CustomCurrencyMaskConfig },
|
{ provide: CURRENCY_MASK_CONFIG, useValue: CustomCurrencyMaskConfig },
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div class="category-breakdown">
|
||||||
|
<canvas baseChart
|
||||||
|
[datasets]="barChartData"
|
||||||
|
[options]="barChartOptions"
|
||||||
|
[labels]="barChartLabels"
|
||||||
|
[legend]="barChartLegend"
|
||||||
|
[chartType]="barChartType">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CategoryBreakdownComponent } from './category-breakdown.component';
|
||||||
|
|
||||||
|
describe('CategoryBreakdownComponent', () => {
|
||||||
|
let component: CategoryBreakdownComponent;
|
||||||
|
let fixture: ComponentFixture<CategoryBreakdownComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CategoryBreakdownComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CategoryBreakdownComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { Component, OnInit, Input, OnChanges, SimpleChanges, SimpleChange, ViewChild } from '@angular/core';
|
||||||
|
import { Category } from '../category';
|
||||||
|
import { CategoriesComponent } from '../categories.component';
|
||||||
|
import { ChartOptions, ChartType, ChartDataSets } from 'chart.js';
|
||||||
|
import { BaseChartDirective, Label } from 'ng2-charts';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-category-breakdown',
|
||||||
|
templateUrl: './category-breakdown.component.html',
|
||||||
|
styleUrls: ['./category-breakdown.component.css']
|
||||||
|
})
|
||||||
|
export class CategoryBreakdownComponent implements OnInit, OnChanges {
|
||||||
|
barChartOptions: ChartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}], yAxes: [{}]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@Input() barChartLabels: Label[];
|
||||||
|
@Input() barChartData: ChartDataSets[] = [
|
||||||
|
{ data: [0, 0, 0, 0], label: '' },
|
||||||
|
];
|
||||||
|
barChartType: ChartType = 'horizontalBar';
|
||||||
|
barChartLegend = true;
|
||||||
|
@ViewChild(BaseChartDirective) chart: BaseChartDirective;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() { }
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
console.log(changes);
|
||||||
|
if (changes.barChartLabels) {
|
||||||
|
this.barChartLabels = changes.barChartLabels.currentValue;
|
||||||
|
}
|
||||||
|
if (changes.barChartData) {
|
||||||
|
this.barChartData = changes.barChartData.currentValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
<input matInput type="text" [(ngModel)]="currentTransaction.amount" placeholder="Amount" required currencyMask>
|
<input matInput type="text" [(ngModel)]="currentTransaction.amount" placeholder="Amount" required currencyMask>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput type="date" [(ngModel)]="currentTransaction.date" placeholder="Date" required>
|
<input matInput type="date" [ngModel]="currentTransaction.date | date:'yyyy-MM-dd'" (ngModelChange)="currentTransaction.date = $event" placeholder="Date" required>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select placeholder="Category" [(ngModel)]="currentTransaction.categoryId">
|
<mat-select placeholder="Category" [(ngModel)]="currentTransaction.categoryId">
|
||||||
|
|
|
@ -7,7 +7,7 @@ export class Transaction {
|
||||||
accountId: string;
|
accountId: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string = null;
|
description: string = null;
|
||||||
amount: number;
|
amount = 0;
|
||||||
date: Date = new Date();
|
date: Date = new Date();
|
||||||
categoryId: string;
|
categoryId: string;
|
||||||
type: TransactionType = TransactionType.EXPENSE;
|
type: TransactionType = TransactionType.EXPENSE;
|
||||||
|
|
Loading…
Reference in a new issue