From 09e32d1cd1b4a2fe6a148478d9ad1e48342ce280 Mon Sep 17 00:00:00 2001 From: Billy Brawner Date: Sat, 15 Sep 2018 15:57:11 -0500 Subject: [PATCH] WIP: Integrate app with API --- .vscode/launch.json | 37 ++++++++ src/app/account.ts | 7 ++ src/app/accounts.service.spec.ts | 15 ++++ src/app/accounts.service.ts | 9 ++ src/app/actionable.ts | 4 + .../add-edit-account.component.css | 0 .../add-edit-account.component.html | 3 + .../add-edit-account.component.spec.ts | 25 ++++++ .../add-edit-account.component.ts | 15 ++++ .../add-edit-category.component.css | 13 --- .../add-edit-category.component.html | 15 +--- .../add-edit-category.component.ts | 25 ++++-- .../add-edit-transaction.component.html | 17 +--- .../add-edit-transaction.component.ts | 37 ++++---- src/app/api.service.spec.ts | 19 +++++ src/app/api.service.ts | 52 ++++++++++++ src/app/app-routing.module.ts | 6 +- src/app/app.component.html | 30 ++++++- src/app/app.component.ts | 20 ++++- src/app/app.module.ts | 30 +++++-- src/app/auth.service.spec.ts | 21 +++++ src/app/auth.service.ts | 55 ++++++++++++ src/app/budget-database.ts | 84 +++++++++++-------- src/app/categories/categories.component.html | 10 --- src/app/categories/categories.component.ts | 19 ++--- src/app/category.ts | 2 + src/app/dashboard/dashboard.component.html | 7 -- src/app/dashboard/dashboard.component.ts | 11 ++- .../edit-profile/edit-profile.component.css | 0 .../edit-profile/edit-profile.component.html | 3 + .../edit-profile.component.spec.ts | 25 ++++++ .../edit-profile/edit-profile.component.ts | 15 ++++ src/app/login/login.component.css | 0 src/app/login/login.component.html | 8 ++ src/app/login/login.component.spec.ts | 25 ++++++ src/app/login/login.component.ts | 38 +++++++++ src/app/register/register.component.css | 0 src/app/register/register.component.html | 14 ++++ src/app/register/register.component.spec.ts | 25 ++++++ src/app/register/register.component.ts | 38 +++++++++ src/app/transaction.ts | 2 + .../transactions/transactions.component.html | 10 --- .../transactions.component.spec.ts | 10 +-- .../transactions/transactions.component.ts | 14 ++-- src/app/user.ts | 6 ++ src/app/user/user.component.css | 0 src/app/user/user.component.html | 3 + src/app/user/user.component.spec.ts | 25 ++++++ src/app/user/user.component.ts | 15 ++++ src/styles.css | 47 +++++++++-- 50 files changed, 743 insertions(+), 168 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/app/account.ts create mode 100644 src/app/accounts.service.spec.ts create mode 100644 src/app/accounts.service.ts create mode 100644 src/app/actionable.ts create mode 100644 src/app/add-edit-account/add-edit-account.component.css create mode 100644 src/app/add-edit-account/add-edit-account.component.html create mode 100644 src/app/add-edit-account/add-edit-account.component.spec.ts create mode 100644 src/app/add-edit-account/add-edit-account.component.ts create mode 100644 src/app/api.service.spec.ts create mode 100644 src/app/api.service.ts create mode 100644 src/app/auth.service.spec.ts create mode 100644 src/app/auth.service.ts create mode 100644 src/app/edit-profile/edit-profile.component.css create mode 100644 src/app/edit-profile/edit-profile.component.html create mode 100644 src/app/edit-profile/edit-profile.component.spec.ts create mode 100644 src/app/edit-profile/edit-profile.component.ts create mode 100644 src/app/login/login.component.css create mode 100644 src/app/login/login.component.html create mode 100644 src/app/login/login.component.spec.ts create mode 100644 src/app/login/login.component.ts create mode 100644 src/app/register/register.component.css create mode 100644 src/app/register/register.component.html create mode 100644 src/app/register/register.component.spec.ts create mode 100644 src/app/register/register.component.ts create mode 100644 src/app/user.ts create mode 100644 src/app/user/user.component.css create mode 100644 src/app/user/user.component.html create mode 100644 src/app/user/user.component.spec.ts create mode 100644 src/app/user/user.component.ts diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5a476e3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,37 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Launch index.html", + "type": "firefox", + "request": "launch", + "reAttach": true, + "file": "${workspaceFolder}/index.html" + }, + { + "name": "Launch localhost", + "type": "firefox", + "request": "launch", + "reAttach": true, + "url": "http://localhost/index.html", + "webRoot": "${workspaceFolder}" + }, + { + "name": "Attach", + "type": "firefox", + "request": "attach" + }, + { + "name": "Launch addon", + "type": "firefox", + "request": "launch", + "reAttach": true, + "addonType": "webExtension", + "addonPath": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/src/app/account.ts b/src/app/account.ts new file mode 100644 index 0000000..6f7393c --- /dev/null +++ b/src/app/account.ts @@ -0,0 +1,7 @@ +import { IAccount } from './budget-database' + +export class Account implements IAccount { + id: number; + remoteId: number; + name: string; +} diff --git a/src/app/accounts.service.spec.ts b/src/app/accounts.service.spec.ts new file mode 100644 index 0000000..795c14d --- /dev/null +++ b/src/app/accounts.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AccountsService } from './accounts.service'; + +describe('AccountsService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AccountsService] + }); + }); + + it('should be created', inject([AccountsService], (service: AccountsService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/accounts.service.ts b/src/app/accounts.service.ts new file mode 100644 index 0000000..053723b --- /dev/null +++ b/src/app/accounts.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class AccountsService { + + constructor() { } +} diff --git a/src/app/actionable.ts b/src/app/actionable.ts new file mode 100644 index 0000000..de2a9f8 --- /dev/null +++ b/src/app/actionable.ts @@ -0,0 +1,4 @@ +export interface Actionable { + doAction(): void; + getActionLabel(): string; +} diff --git a/src/app/add-edit-account/add-edit-account.component.css b/src/app/add-edit-account/add-edit-account.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/add-edit-account/add-edit-account.component.html b/src/app/add-edit-account/add-edit-account.component.html new file mode 100644 index 0000000..a67e7a3 --- /dev/null +++ b/src/app/add-edit-account/add-edit-account.component.html @@ -0,0 +1,3 @@ +

+ add-edit-account works! +

diff --git a/src/app/add-edit-account/add-edit-account.component.spec.ts b/src/app/add-edit-account/add-edit-account.component.spec.ts new file mode 100644 index 0000000..ca90782 --- /dev/null +++ b/src/app/add-edit-account/add-edit-account.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddEditAccountComponent } from './add-edit-account.component'; + +describe('AddEditAccountComponent', () => { + let component: AddEditAccountComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AddEditAccountComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddEditAccountComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/add-edit-account/add-edit-account.component.ts b/src/app/add-edit-account/add-edit-account.component.ts new file mode 100644 index 0000000..6d0aac0 --- /dev/null +++ b/src/app/add-edit-account/add-edit-account.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-add-edit-account', + templateUrl: './add-edit-account.component.html', + styleUrls: ['./add-edit-account.component.css'] +}) +export class AddEditAccountComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/add-edit-category/add-edit-category.component.css b/src/app/add-edit-category/add-edit-category.component.css index 90e6614..92e1e78 100644 --- a/src/app/add-edit-category/add-edit-category.component.css +++ b/src/app/add-edit-category/add-edit-category.component.css @@ -1,16 +1,3 @@ -.category-form { - padding: 1em; - color: #F1F1F1; -} - -.category-form * { - display: block; -} - -mat-radio-button { - padding-bottom: 15px; -} - .button-delete { float: right; } diff --git a/src/app/add-edit-category/add-edit-category.component.html b/src/app/add-edit-category/add-edit-category.component.html index 6607491..cfd3361 100644 --- a/src/app/add-edit-category/add-edit-category.component.html +++ b/src/app/add-edit-category/add-edit-category.component.html @@ -1,20 +1,7 @@ - - - - arrow_back - - - - {{ title }} - - - Save - -

Select a category from the list to view details about it or edit it.

-
+
diff --git a/src/app/add-edit-category/add-edit-category.component.ts b/src/app/add-edit-category/add-edit-category.component.ts index 0e3b43a..c1f7230 100644 --- a/src/app/add-edit-category/add-edit-category.component.ts +++ b/src/app/add-edit-category/add-edit-category.component.ts @@ -1,31 +1,36 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit, Input, OnDestroy } from '@angular/core'; import { CategoryService } from '../category.service' import { Category } from '../category' import { Location } from '@angular/common'; +import { Actionable } from '../actionable'; +import { AppComponent } from '../app.component'; @Component({ selector: 'app-add-edit-category', templateUrl: './add-edit-category.component.html', styleUrls: ['./add-edit-category.component.css'] }) -export class AddEditCategoryComponent implements OnInit { +export class AddEditCategoryComponent implements OnInit, Actionable, OnDestroy { @Input() title: string; @Input() currentCategory: Category; constructor( + private app: AppComponent, private categoryService: CategoryService, - private location: Location ) { } ngOnInit() { + this.app.actionable = this; + this.app.backEnabled = true; + this.app.title = this.title; } - goBack(): void { - this.location.back() + ngOnDestroy() { + this.app.actionable = null; } - save(): void { + doAction(): void { if (this.currentCategory.id) { // This is an existing category, update it this.categoryService.updateCategory(this.currentCategory); @@ -33,11 +38,15 @@ export class AddEditCategoryComponent implements OnInit { // This is a new category, save it this.categoryService.saveCategory(this.currentCategory); } - this.goBack() + this.app.goBack(); + } + + getActionLabel(): string { + return 'Save'; } delete(): void { this.categoryService.deleteCategory(this.currentCategory); - this.goBack() + this.app.goBack(); } } diff --git a/src/app/add-edit-transaction/add-edit-transaction.component.html b/src/app/add-edit-transaction/add-edit-transaction.component.html index d7a9178..12d2c3b 100644 --- a/src/app/add-edit-transaction/add-edit-transaction.component.html +++ b/src/app/add-edit-transaction/add-edit-transaction.component.html @@ -1,20 +1,7 @@ - - - - arrow_back - - - - {{ title }} - - - Save - - -
+

Select a transaction from the list to view details about it or edit it.

-
+
diff --git a/src/app/add-edit-transaction/add-edit-transaction.component.ts b/src/app/add-edit-transaction/add-edit-transaction.component.ts index e23adda..fef3857 100644 --- a/src/app/add-edit-transaction/add-edit-transaction.component.ts +++ b/src/app/add-edit-transaction/add-edit-transaction.component.ts @@ -1,39 +1,42 @@ -import { Component, OnInit, Input } from '@angular/core'; -import { Transaction } from '../transaction' +import { Component, OnInit, Input, OnChanges, OnDestroy } from '@angular/core'; +import { Transaction } from '../transaction' import { TransactionType } from '../transaction.type' import { TransactionService } from '../transaction.service' -import { Location } from '@angular/common'; -import { Category } from '../category' +import { Category } from '../category' import { CategoryService } from '../category.service' +import { AppComponent } from '../app.component'; +import { Actionable } from '../actionable'; @Component({ selector: 'app-add-edit-transaction', templateUrl: './add-edit-transaction.component.html', styleUrls: ['./add-edit-transaction.component.css'] }) -export class AddEditTransactionComponent implements OnInit { - +export class AddEditTransactionComponent implements OnInit, OnDestroy, Actionable { @Input() title: string; @Input() currentTransaction: Transaction; public transactionType = TransactionType; public selectedCategory: Category; - public categories: Category[] + public categories: Category[]; constructor( + private app: AppComponent, private categoryService: CategoryService, private transactionService: TransactionService, - private location: Location ) { } ngOnInit() { - this.getCategories() + this.app.title = this.title; + this.app.backEnabled = true; + this.app.actionable = this; + this.getCategories(); } - goBack(): void { - this.location.back() + ngOnDestroy() { + this.app.actionable = null; } - save(): void { + doAction(): void { if (this.currentTransaction.id) { // This is an existing transaction, update it this.transactionService.updateTransaction(this.currentTransaction); @@ -41,15 +44,19 @@ export class AddEditTransactionComponent implements OnInit { // This is a new transaction, save it this.transactionService.saveTransaction(this.currentTransaction); } - this.goBack() + this.app.goBack(); + } + + getActionLabel(): string { + return 'Save'; } delete(): void { this.transactionService.deleteTransaction(this.currentTransaction); - this.goBack() + this.app.goBack(); } getCategories() { - this.categoryService.getCategories().subscribe(categories => this.categories = categories) + this.categoryService.getCategories().subscribe(categories => this.categories = categories); } } diff --git a/src/app/api.service.spec.ts b/src/app/api.service.spec.ts new file mode 100644 index 0000000..7fac290 --- /dev/null +++ b/src/app/api.service.spec.ts @@ -0,0 +1,19 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { ApiService } from './api.service'; +import { HttpClientModule } from '@angular/common/http'; + +describe('ApiService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientModule, + ], + providers: [ApiService] + }); + }); + + it('should be created', inject([ApiService], (service: ApiService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/api.service.ts b/src/app/api.service.ts new file mode 100644 index 0000000..21d052d --- /dev/null +++ b/src/app/api.service.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; + +const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }), + withCredentials: true, +}; + +const host = 'http://localhost:9090'; + +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + + constructor( + private http: HttpClient, + ) { } + + login(username: string, password: string): Observable { + return this.http.post( + host + '/login', + { + 'username': username, + 'password': password + }, + httpOptions + ); + } + + register(username: string, email: string, password: string): Observable { + return this.http.post( + host + '/register', + { + 'username': username, + 'email': email, + 'password': password + }, + httpOptions + ); + } + + logout(): Observable { + return this.http.get( + host + '/logout', + httpOptions + ); + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index b76ff9d..8db8896 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -7,16 +7,20 @@ import { NewTransactionComponent } from './new-transaction/new-transaction.compo import { CategoriesComponent } from './categories/categories.component'; import { CategoryDetailsComponent } from './category-details/category-details.component'; import { NewCategoryComponent } from './new-category/new-category.component'; +import { LoginComponent } from './login/login.component'; +import { RegisterComponent } from './register/register.component'; const routes: Routes = [ { path: '', component: DashboardComponent }, + { path: 'login', component: LoginComponent }, + { path: 'register', component: RegisterComponent }, { path: 'transactions', component: TransactionsComponent }, { path: 'transactions/new', component: NewTransactionComponent }, { path: 'transactions/:id', component: TransactionDetailsComponent }, { path: 'categories', component: CategoriesComponent }, { path: 'categories/new', component: NewCategoryComponent }, { path: 'categories/:id', component: CategoryDetailsComponent }, -] +]; @NgModule({ imports: [ diff --git a/src/app/app.component.html b/src/app/app.component.html index 0680b43..b79a93f 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1,29 @@ - + + + + Manage Accounts + Login + Register + Logout + + + + + + + arrow_back + + + menu + + + + {{ title }} + + + {{ actionable.getActionLabel() }} + + + + + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 562faa4..7e5483d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,7 @@ import { Component } from '@angular/core'; +import { Location } from '@angular/common'; +import { Actionable } from './actionable'; +import { AuthService } from './auth.service'; @Component({ selector: 'app-root', @@ -6,5 +9,20 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.css'] }) export class AppComponent { - title = 'budget'; + public title = 'Budget'; + public backEnabled = false; + public actionable: Actionable; + + constructor( + public authService: AuthService, + private location: Location + ) { } + + goBack(): void { + this.location.back(); + } + + logout(): void { + this.authService.logout(); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2d4196c..70e4c84 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -3,16 +3,17 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { - MatButtonModule, - MatDatepickerModule, - MatFormFieldModule, - MatIconModule, + MatButtonModule, + MatDatepickerModule, + MatFormFieldModule, + MatIconModule, MatInputModule, - MatListModule, + MatListModule, MatRadioModule, MatProgressBarModule, MatSelectModule, - MatToolbarModule, + MatToolbarModule, + MatSidenavModule, } from '@angular/material'; import { AppComponent } from './app.component'; @@ -29,6 +30,12 @@ import { NewCategoryComponent } from './new-category/new-category.component'; import { CategoryListComponent } from './category-list/category-list.component'; import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; +import { LoginComponent } from './login/login.component'; +import { RegisterComponent } from './register/register.component'; +import { AddEditAccountComponent } from './add-edit-account/add-edit-account.component'; +import { EditProfileComponent } from './edit-profile/edit-profile.component'; +import { UserComponent } from './user/user.component'; +import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ @@ -42,7 +49,12 @@ import { environment } from '../environments/environment'; CategoryDetailsComponent, AddEditCategoryComponent, NewCategoryComponent, - CategoryListComponent + CategoryListComponent, + LoginComponent, + RegisterComponent, + AddEditAccountComponent, + EditProfileComponent, + UserComponent, ], imports: [ BrowserModule, @@ -57,9 +69,11 @@ import { environment } from '../environments/environment'; MatProgressBarModule, MatSelectModule, MatToolbarModule, + MatSidenavModule, AppRoutingModule, FormsModule, - ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) + ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }), + HttpClientModule, ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/auth.service.spec.ts b/src/app/auth.service.spec.ts new file mode 100644 index 0000000..c4ea340 --- /dev/null +++ b/src/app/auth.service.spec.ts @@ -0,0 +1,21 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; +import { HttpClientModule } from '@angular/common/http'; +import { RouterTestingModule } from '@angular/router/testing'; + +describe('AuthService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientModule, + RouterTestingModule, + ], + providers: [AuthService] + }); + }); + + it('should be created', inject([AuthService], (service: AuthService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/auth.service.ts b/src/app/auth.service.ts new file mode 100644 index 0000000..15ba9f1 --- /dev/null +++ b/src/app/auth.service.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@angular/core'; +import { ApiService } from './api.service'; +import { User } from './user'; +import { Router } from '@angular/router'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + private currentUser: User; + + constructor( + private apiService: ApiService, + private router: Router, + ) { + console.log('AuthService constructed'); + } + + login(user: User) { + this.apiService.login(user.name, user.password).subscribe( + value => { + this.currentUser = value; + this.router.navigate(['/']); + }, + error => { + console.log('Login failed'); + console.log(error); + } + ); + } + + register(user: User) { + this.apiService.register(user.name, user.email, user.password).subscribe( + value => { + this.login(value); + }, + error => { + console.log('Registration failed'); + console.log(error); + } + ); + } + + logout() { + this.apiService.logout().subscribe( + value => { + window.location.reload(); + }, + error => { + console.log('Logout failed'); + console.log(error); + } + ); + } +} diff --git a/src/app/budget-database.ts b/src/app/budget-database.ts index 8c87ecd..e11cd20 100644 --- a/src/app/budget-database.ts +++ b/src/app/budget-database.ts @@ -1,49 +1,67 @@ import { Injectable } from '@angular/core'; import Dexie from 'dexie'; +import { Account } from './account' import { Category } from './category' import { Transaction } from './transaction' import { TransactionType } from './transaction.type'; @Injectable({ - providedIn: 'root' + providedIn: 'root' }) export class BudgetDatabase extends Dexie { - transactions: Dexie.Table; - categories: Dexie.Table; + transactions: Dexie.Table; + categories: Dexie.Table; + accounts: Dexie.Table; - constructor () { - super('BudgetDatabase') - this.version(1).stores({ - transactions: `++id, title, description, amount, date, category, type`, - categories: `++id, name, amount, repeat, color` - }) - this.version(2).stores({ - transactions: `++id, title, description, amount, date, category_id, type`, - categories: `++id, name, amount, repeat, color, type` - }) - this.version(3).stores({ - transactions: `++id, title, description, amount, date, category_id, type`, - categories: `++id, name, amount, repeat, color` - }) - this.transactions.mapToClass(Transaction) - this.categories.mapToClass(Category) - } + constructor() { + super('BudgetDatabase') + this.version(1).stores({ + transactions: `++id, title, description, amount, date, category, type`, + categories: `++id, name, amount, repeat, color` + }) + this.version(2).stores({ + transactions: `++id, title, description, amount, date, category_id, type`, + categories: `++id, name, amount, repeat, color, type` + }) + this.version(3).stores({ + transactions: `++id, title, description, amount, date, category_id, type`, + categories: `++id, name, amount, repeat, color` + }) + this.version(4).stores({ + transactions: `++id, remote_id, account_id, title, description, amount, date, category_id, type`, + categories: `++id, remote_id, account_id, name, amount, repeat, color`, + accounts: `++id, remote_id, name` + }) + this.transactions.mapToClass(Transaction) + this.categories.mapToClass(Category) + this.accounts.mapToClass(Account) + } } export interface ITransaction { - id: number; - title: string; - description: string; - amount: number; - date: Date; - categoryId: number; - type: TransactionType; + id: number; + accountId: number; + remoteId: number; + title: string; + description: string; + amount: number; + date: Date; + categoryId: number; + type: TransactionType; } -export interface ICategory{ - id: number; - name: string; - amount: number; - repeat: string; - color: string; +export interface ICategory { + id: number; + accountId: number; + remoteId: number; + name: string; + amount: number; + repeat: string; + color: string; +} + +export interface IAccount { + id: number; + remoteId: number; + name: string; } diff --git a/src/app/categories/categories.component.html b/src/app/categories/categories.component.html index e150b14..f699e64 100644 --- a/src/app/categories/categories.component.html +++ b/src/app/categories/categories.component.html @@ -1,13 +1,3 @@ - - - - arrow_back - - - Categories - - - add diff --git a/src/app/categories/categories.component.ts b/src/app/categories/categories.component.ts index 2ee26c9..970296f 100644 --- a/src/app/categories/categories.component.ts +++ b/src/app/categories/categories.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { CategoryService } from '../category.service' -import { Category } from '../category' -import { Location } from '@angular/common'; +import { CategoryService } from '../category.service'; +import { Category } from '../category'; +import { AppComponent } from '../app.component'; @Component({ selector: 'app-categories', @@ -14,25 +14,22 @@ export class CategoriesComponent implements OnInit { public categoryBalances: Map; constructor( + private app: AppComponent, private categoryService: CategoryService, - private location: Location ) { } ngOnInit() { + this.app.title = 'Categories'; this.getCategories(); this.categoryBalances = new Map(); } getCategories(): void { this.categoryService.getCategories().subscribe(categories => { - this.categories = categories - for (let category of this.categories) { + this.categories = categories; + for (const category of this.categories) { this.categoryService.getBalance(category).subscribe(balance => this.categoryBalances.set(category.id, balance)) } - }) - } - - goBack(): void { - this.location.back() + }); } } diff --git a/src/app/category.ts b/src/app/category.ts index e694475..5cb8aaa 100644 --- a/src/app/category.ts +++ b/src/app/category.ts @@ -2,6 +2,8 @@ import { ICategory } from './budget-database' export class Category implements ICategory { id: number; + accountId: number; + remoteId: number; name: string; amount: number; repeat: string; diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html index 7d56c72..26dcae2 100644 --- a/src/app/dashboard/dashboard.component.html +++ b/src/app/dashboard/dashboard.component.html @@ -1,10 +1,3 @@ - - - - My Finances - - -

diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts index 126aa6e..de8512d 100644 --- a/src/app/dashboard/dashboard.component.ts +++ b/src/app/dashboard/dashboard.component.ts @@ -3,6 +3,8 @@ import { Transaction } from '../transaction' import { TransactionService } from '../transaction.service' import { CategoryService } from '../category.service' import { Category } from '../category' +import { AppComponent } from '../app.component'; +import { AuthService } from '../auth.service'; @Component({ selector: 'app-dashboard', @@ -17,11 +19,14 @@ export class DashboardComponent implements OnInit { categoryBalances: Map; constructor( + private app: AppComponent, private transactionService: TransactionService, private categoryService: CategoryService ) { } ngOnInit() { + this.app.backEnabled = false; + this.app.title = 'My Finances'; this.balance = 0; this.getBalance(); this.getTransactions(); @@ -39,10 +44,10 @@ export class DashboardComponent implements OnInit { getCategories(): void { this.categoryService.getCategories(5).subscribe(categories => { - this.categories = categories - for (let category of this.categories) { + this.categories = categories; + for (const category of this.categories) { this.categoryService.getBalance(category).subscribe(balance => this.categoryBalances.set(category.id, balance)) } - }) + }); } } diff --git a/src/app/edit-profile/edit-profile.component.css b/src/app/edit-profile/edit-profile.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/edit-profile/edit-profile.component.html b/src/app/edit-profile/edit-profile.component.html new file mode 100644 index 0000000..e4ae426 --- /dev/null +++ b/src/app/edit-profile/edit-profile.component.html @@ -0,0 +1,3 @@ +

+ edit-profile works! +

diff --git a/src/app/edit-profile/edit-profile.component.spec.ts b/src/app/edit-profile/edit-profile.component.spec.ts new file mode 100644 index 0000000..5b24456 --- /dev/null +++ b/src/app/edit-profile/edit-profile.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditProfileComponent } from './edit-profile.component'; + +describe('EditProfileComponent', () => { + let component: EditProfileComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ EditProfileComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/edit-profile/edit-profile.component.ts b/src/app/edit-profile/edit-profile.component.ts new file mode 100644 index 0000000..11ee186 --- /dev/null +++ b/src/app/edit-profile/edit-profile.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-edit-profile', + templateUrl: './edit-profile.component.html', + styleUrls: ['./edit-profile.component.css'] +}) +export class EditProfileComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/login/login.component.css b/src/app/login/login.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html new file mode 100644 index 0000000..82671df --- /dev/null +++ b/src/app/login/login.component.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/src/app/login/login.component.spec.ts b/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..d6d85a8 --- /dev/null +++ b/src/app/login/login.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts new file mode 100644 index 0000000..722e592 --- /dev/null +++ b/src/app/login/login.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit, OnDestroy, OnChanges } from '@angular/core'; +import { AuthService } from '../auth.service'; +import { User } from '../user'; +import { Actionable } from '../actionable'; +import { AppComponent } from '../app.component'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.css'] +}) +export class LoginComponent implements OnInit, OnDestroy, Actionable { + + public user: User = new User(); + + constructor( + private app: AppComponent, + private authService: AuthService, + ) { } + + ngOnInit() { + this.app.title = 'Login'; + this.app.actionable = this; + this.app.backEnabled = true; + } + + ngOnDestroy() { + this.app.actionable = null; + } + + doAction(): void { + this.authService.login(this.user); + } + + getActionLabel() { + return 'Submit'; + } +} diff --git a/src/app/register/register.component.css b/src/app/register/register.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/register/register.component.html b/src/app/register/register.component.html new file mode 100644 index 0000000..76ab2ba --- /dev/null +++ b/src/app/register/register.component.html @@ -0,0 +1,14 @@ +
+ + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/app/register/register.component.spec.ts b/src/app/register/register.component.spec.ts new file mode 100644 index 0000000..6c19551 --- /dev/null +++ b/src/app/register/register.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ RegisterComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/register/register.component.ts b/src/app/register/register.component.ts new file mode 100644 index 0000000..1852edb --- /dev/null +++ b/src/app/register/register.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { User } from '../user'; +import { AppComponent } from '../app.component'; +import { AuthService } from '../auth.service'; +import { Actionable } from '../actionable'; + +@Component({ + selector: 'app-register', + templateUrl: './register.component.html', + styleUrls: ['./register.component.css'] +}) +export class RegisterComponent implements OnInit, OnDestroy, Actionable { + + public user: User = new User(); + + constructor( + private app: AppComponent, + private authService: AuthService, + ) { } + + ngOnInit() { + this.app.title = 'Login'; + this.app.actionable = this; + this.app.backEnabled = true; + } + + ngOnDestroy() { + this.app.actionable = null; + } + + doAction(): void { + this.authService.register(this.user); + } + + getActionLabel() { + return 'Submit'; + } +} diff --git a/src/app/transaction.ts b/src/app/transaction.ts index fe4949a..c8741ba 100644 --- a/src/app/transaction.ts +++ b/src/app/transaction.ts @@ -4,6 +4,8 @@ import { TransactionType } from './transaction.type'; export class Transaction implements ITransaction { id: number; + accountId: number; + remoteId: number; title: string; description: string; amount: number; diff --git a/src/app/transactions/transactions.component.html b/src/app/transactions/transactions.component.html index 1caadad..028a866 100644 --- a/src/app/transactions/transactions.component.html +++ b/src/app/transactions/transactions.component.html @@ -1,13 +1,3 @@ - - -
- arrow_back - - - Transactions - - -
diff --git a/src/app/transactions/transactions.component.spec.ts b/src/app/transactions/transactions.component.spec.ts index 0e25803..6db67b9 100644 --- a/src/app/transactions/transactions.component.spec.ts +++ b/src/app/transactions/transactions.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { ExpensesComponent } from './transactions.component'; +import { TransactionsComponent } from './transactions.component'; describe('ExpensesComponent', () => { - let component: ExpensesComponent; - let fixture: ComponentFixture; + let component: TransactionsComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ExpensesComponent ] + declarations: [ TransactionsComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(ExpensesComponent); + fixture = TestBed.createComponent(TransactionsComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/transactions/transactions.component.ts b/src/app/transactions/transactions.component.ts index 28cd5a3..8018a0b 100644 --- a/src/app/transactions/transactions.component.ts +++ b/src/app/transactions/transactions.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { Transaction } from '../transaction'; import { TransactionType } from '../transaction.type'; import { TransactionService } from '../transaction.service'; -import { Location } from '@angular/common'; +import { AppComponent } from '../app.component'; @Component({ selector: 'app-transactions', @@ -16,21 +16,19 @@ export class TransactionsComponent implements OnInit { public transactions: Transaction[] constructor( + private app: AppComponent, private transactionService: TransactionService, - private location: Location ) { } ngOnInit() { - this.getTransactions() - } - - goBack(): void { - this.location.back() + this.app.backEnabled = true; + this.app.title = 'Transactions'; + this.getTransactions(); } getTransactions(): void { this.transactionService.getTransactions().subscribe(transactions => { this.transactions = transactions; - }) + }); } } diff --git a/src/app/user.ts b/src/app/user.ts new file mode 100644 index 0000000..0bd6f04 --- /dev/null +++ b/src/app/user.ts @@ -0,0 +1,6 @@ +export class User { + id: number; + name: string; + password: string; + email: string; +} diff --git a/src/app/user/user.component.css b/src/app/user/user.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/user/user.component.html b/src/app/user/user.component.html new file mode 100644 index 0000000..5e7fd58 --- /dev/null +++ b/src/app/user/user.component.html @@ -0,0 +1,3 @@ +

+ user works! +

diff --git a/src/app/user/user.component.spec.ts b/src/app/user/user.component.spec.ts new file mode 100644 index 0000000..dd3b1d7 --- /dev/null +++ b/src/app/user/user.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserComponent } from './user.component'; + +describe('UserComponent', () => { + let component: UserComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ UserComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/user/user.component.ts b/src/app/user/user.component.ts new file mode 100644 index 0000000..204ccd2 --- /dev/null +++ b/src/app/user/user.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-user', + templateUrl: './user.component.html', + styleUrls: ['./user.component.css'] +}) +export class UserComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/styles.css b/src/styles.css index f5d2b41..a658861 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,5 +1,7 @@ /* You can add global styles to this file, and also import other style files */ -html, body { + +html, +body { background: #333333; font-family: Roboto, "Helvetica Neue", sans-serif; margin: 0; @@ -11,6 +13,10 @@ p { margin: 0; } +a.mat-button[hidden] { + display: none; +} + a.mat-fab { position: fixed; right: 2em; @@ -26,13 +32,15 @@ mat-toolbar { box-shadow: 0px 3px 3px 1px #212121; } -mat-toolbar.mat-toolbar-row, mat-toolbar.mat-toolbar-single-row { +mat-toolbar.mat-toolbar-row, +mat-toolbar.mat-toolbar-single-row { padding: 0; } mat-toolbar span { display: flex; - width: 33%;; + width: 33%; + ; } mat-toolbar span:nth-child(1) { @@ -70,11 +78,40 @@ mat-toolbar .action-item a { vertical-align: middle; } +mat-sidenav-container.mat-drawer-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +mat-sidenav-container .mat-drawer-backdrop.mat-drawer-shown { + background-color: rgba(0, 0, 0, 0.8); +} + .income { - color: #81C784; + color: #81C784; } .expense { - color: #E57373; + color: #E57373; } +/** + * Forms + */ + +.form { + padding: 1em; + color: #F1F1F1; +} + +.form .mat-form-field, +.form .button { + display: block; +} + +.form mat-radio-button { + padding-bottom: 15px; +} \ No newline at end of file