├── .browserslistrc ├── .editorconfig ├── README.md ├── angular.json ├── config.json ├── karma.conf.js ├── license ├── package.json ├── src ├── app │ ├── about │ │ ├── about.component.html │ │ └── about.component.ts │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── app.router.ts │ ├── common │ │ ├── common.data.ts │ │ └── common.service.ts │ ├── dashboard │ │ ├── cards │ │ │ ├── cards.component.html │ │ │ └── cards.component.ts │ │ ├── column-chart │ │ │ ├── column-chart.component.html │ │ │ └── column-chart.component.ts │ │ ├── dashboard.component.html │ │ ├── dashboard.component.ts │ │ ├── dashboard.service.ts │ │ ├── line-chart │ │ │ ├── line-chart.component.html │ │ │ └── line-chart.component.ts │ │ ├── pie-chart │ │ │ ├── pie-chart.component.html │ │ │ └── pie-chart.component.ts │ │ └── recent-exp-grid │ │ │ ├── recent-exp-grid.component.html │ │ │ └── recent-exp-grid.component.ts │ ├── expense │ │ ├── content │ │ │ ├── content.component.html │ │ │ └── content.component.ts │ │ ├── dialogs │ │ │ ├── dialogs.component.html │ │ │ └── dialogs.component.ts │ │ ├── expense.component.html │ │ ├── expense.component.ts │ │ └── filter │ │ │ ├── filter.component.html │ │ │ ├── filter.component.ts │ │ │ └── filter.service.ts │ ├── main.ts │ └── menu │ │ ├── menu.component.html │ │ └── menu.component.ts ├── assets │ ├── definition │ │ └── material.scss │ ├── favicon.ico │ ├── images │ │ ├── About.svg │ │ ├── AvailableCash.svg │ │ ├── Home.svg │ │ ├── OverallTransactions.svg │ │ ├── Profile-img-Desktop │ │ ├── Profile-img-Mobile │ │ ├── Search.svg │ │ ├── TotalExpenses.svg │ │ ├── TotalIncome.svg │ │ ├── Transactions.svg │ │ ├── balance.svg │ │ ├── cash-wallet.svg │ │ ├── category │ │ │ ├── bills.png │ │ │ ├── business.png │ │ │ ├── clothing.png │ │ │ ├── education.png │ │ │ ├── entertainment.png │ │ │ ├── extra.png │ │ │ ├── food.png │ │ │ ├── health.png │ │ │ ├── house.png │ │ │ ├── insurance.png │ │ │ ├── interest.png │ │ │ ├── miscellaneous.png │ │ │ ├── personal.png │ │ │ ├── rent.png │ │ │ ├── salary.png │ │ │ ├── shopping.png │ │ │ ├── tax.png │ │ │ ├── transport.png │ │ │ └── utilities.png │ │ ├── exp-track.png │ │ ├── expense.svg │ │ ├── fonts │ │ │ ├── Expense-Analysis-Sample.eot │ │ │ ├── Expense-Analysis-Sample.svg │ │ │ ├── Expense-Analysis-Sample.ttf │ │ │ ├── Expense-Analysis-Sample.woff │ │ │ ├── controls.eot │ │ │ ├── controls.svg │ │ │ ├── controls.ttf │ │ │ ├── controls.woff │ │ │ ├── icons.eot │ │ │ ├── icons.svg │ │ │ ├── icons.ttf │ │ │ └── icons.woff │ │ ├── i.svg │ │ ├── income.svg │ │ ├── no-records.png │ │ ├── t.svg │ │ ├── title.svg │ │ ├── transaction.svg │ │ └── user.svg │ ├── index.css │ ├── index.scss │ ├── styles.css │ └── styles.scss ├── environments │ └── environment.ts ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major version 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 9-11 # For IE 9-11 support, remove 'not'. 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Essential JS 2 for Angular - Expense Tracker 2 | 3 | This expense tracker demo application showcases using several Essential JS 2 components together in 4 | a real-world application scenario. You can further explore the source code of this application and 5 | use it as a reference for integrating Essential JS 2 components into your applications. 6 | 7 | ## Deployment 8 | 9 | ### Install 10 | 11 | To install all dependent packages, use the below command 12 | 13 | ``` 14 | npm install 15 | ``` 16 | 17 | ### Run 18 | 19 | To run the sample, use the below command 20 | 21 | ``` 22 | ng serve 23 | ``` 24 | 25 | ## Demo 26 | 27 | #### https://ej2.syncfusion.com/showcase/angular/expensetracker/ 28 | 29 | Check all the showcase samples from here. -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ej2-ng-expense-tracker": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "output", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": true, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [] 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "extractLicenses": true, 45 | "vendorChunk": false, 46 | "buildOptimizer": true, 47 | "budgets": [ 48 | { 49 | "type": "initial", 50 | "maximumWarning": "2mb", 51 | "maximumError": "5mb" 52 | }, 53 | { 54 | "type": "anyComponentStyle", 55 | "maximumWarning": "6kb", 56 | "maximumError": "10kb" 57 | } 58 | ] 59 | } 60 | } 61 | }, 62 | "serve": { 63 | "builder": "@angular-devkit/build-angular:dev-server", 64 | "options": { 65 | "browserTarget": "ej2-ng-expense-tracker:build" 66 | }, 67 | "configurations": { 68 | "production": { 69 | "browserTarget": "ej2-ng-expense-tracker:build:production" 70 | } 71 | } 72 | }, 73 | "extract-i18n": { 74 | "builder": "@angular-devkit/build-angular:extract-i18n", 75 | "options": { 76 | "browserTarget": "ej2-ng-expense-tracker:build" 77 | } 78 | }, 79 | "test": { 80 | "builder": "@angular-devkit/build-angular:karma", 81 | "options": { 82 | "main": "src/test.ts", 83 | "polyfills": "src/polyfills.ts", 84 | "tsConfig": "tsconfig.spec.json", 85 | "karmaConfig": "karma.conf.js", 86 | "assets": [ 87 | "src/favicon.ico", 88 | "src/assets" 89 | ], 90 | "styles": [ 91 | "src/styles.css" 92 | ], 93 | "scripts": [] 94 | } 95 | }, 96 | "lint": { 97 | "builder": "@angular-devkit/build-angular:tslint", 98 | "options": { 99 | "tsConfig": [ 100 | "tsconfig.app.json", 101 | "tsconfig.spec.json", 102 | "e2e/tsconfig.json" 103 | ], 104 | "exclude": [ 105 | "**/node_modules/**" 106 | ] 107 | } 108 | }, 109 | "e2e": { 110 | "builder": "@angular-devkit/build-angular:protractor", 111 | "options": { 112 | "protractorConfig": "e2e/protractor.conf.js", 113 | "devServerTarget": "ej2-ng-expense-tracker:serve" 114 | }, 115 | "configurations": { 116 | "production": { 117 | "devServerTarget": "ej2-ng-expense-tracker:serve:production" 118 | } 119 | } 120 | } 121 | } 122 | }}, 123 | "defaultProject": "ej2-ng-expense-tracker" 124 | } 125 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "sasslint": ["./src/assets/**/*.scss"], 3 | "tslint": ["./src/app/**/*.ts", "!./src/app/**/*-model.d.ts", "!./src/app/**/**/*.d.ts", "!./src/app/**/**/*.ngfactory.ts"], 4 | "ts": ["./src/app/**/*.ts", "!./node_modules/**/*.ts"], 5 | "styleDependency":["ej2"], 6 | "isShowCase": true 7 | } -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/ej2-ng-expense-tracker'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | Essential JS 2 library is available under the Syncfusion Essential Studio program, and can be licensed either under the Syncfusion Community License Program or the Syncfusion commercial license. 2 | 3 | To be qualified for the Syncfusion Community License Program you must have a gross revenue of less than one (1) million U.S. dollars ($1,000,000.00 USD) per year and have less than five (5) developers in your organization, and agree to be bound by Syncfusion’s terms and conditions. 4 | 5 | Customers who do not qualify for the community license can contact sales@syncfusion.com for commercial licensing options. 6 | 7 | Under no circumstances can you use this product without (1) either a Community License or a commercial license and (2) without agreeing and abiding by Syncfusion’s license containing all terms and conditions. 8 | 9 | The Syncfusion license that contains the terms and conditions can be found at 10 | https://www.syncfusion.com/content/downloads/syncfusion_license.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@syncfusion/ej2-ng-expense-tracker", 3 | "version": "0.0.1", 4 | "description": "Essential JS 2 for Angular - Expense Tracker", 5 | "author": "Syncfusion Inc.", 6 | "license": "SEE LICENSE IN license", 7 | "scripts": { 8 | "ng": "ng", 9 | "start": "ng serve", 10 | "build": "ng build", 11 | "test": "ng test", 12 | "lint": "ng lint", 13 | "e2e": "ng e2e", 14 | "scss": "gulp styles", 15 | "ci-publish": "gulp publish-samples" 16 | }, 17 | "private": true, 18 | "dependencies": { 19 | "@angular/animations": "^15.0.0", 20 | "@angular/common": "^15.0.0", 21 | "@angular/compiler": "^15.0.0", 22 | "@angular/core": "^15.0.0", 23 | "@angular/forms": "^15.0.0", 24 | "@angular/platform-browser": "^15.0.0", 25 | "@angular/platform-browser-dynamic": "^15.0.0", 26 | "@angular/router": "^15.0.0", 27 | "@syncfusion/ej2-angular-base": "*", 28 | "@syncfusion/ej2-angular-buttons": "*", 29 | "@syncfusion/ej2-angular-calendars": "*", 30 | "@syncfusion/ej2-angular-charts": "*", 31 | "@syncfusion/ej2-angular-dropdowns": "*", 32 | "@syncfusion/ej2-angular-grids": "*", 33 | "@syncfusion/ej2-angular-inputs": "*", 34 | "@syncfusion/ej2-angular-navigations": "*", 35 | "@syncfusion/ej2-angular-popups": "*", 36 | "ajv": "^8.11.2", 37 | "gulp": "^3.9.0", 38 | "gulp-sass": "^3.1.0", 39 | "run-sequence": "2.2.0", 40 | "rxjs": "~7.5.0", 41 | "shelljs": "^0.7.0", 42 | "tslib": "^2.3.0", 43 | "zone.js": "~0.12.0" 44 | }, 45 | "devDependencies": { 46 | "@angular-devkit/build-angular": "^15.0.4", 47 | "@angular/cli": "~15.0.4", 48 | "@angular/compiler-cli": "^15.0.0", 49 | "@types/jasmine": "~4.3.0", 50 | "@types/jasminewd2": "~2.0.3", 51 | "@types/node": "^10.13.0", 52 | "codelyzer": "^6.0.0-next.1", 53 | "jasmine-core": "~4.5.0", 54 | "jasmine-spec-reporter": "~5.0.0", 55 | "karma": "~6.4.0", 56 | "karma-chrome-launcher": "~3.1.0", 57 | "karma-coverage-istanbul-reporter": "~3.0.2", 58 | "karma-jasmine": "~5.1.0", 59 | "karma-jasmine-html-reporter": "~2.0.0", 60 | "protractor": "~7.0.0", 61 | "ts-node": "~8.3.0", 62 | "tslint": "~6.1.0", 63 | "typescript": "~4.8.2" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/app/about/about.component.html: -------------------------------------------------------------------------------- 1 |
2 |
{{title}}
3 |
{{description}}
4 |
{{listTitle}}
5 |
6 |
7 | 8 | {{item.control}} 9 |
10 |
11 |
-------------------------------------------------------------------------------- /src/app/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { LowerCasePipe } from '@angular/common'; 2 | import { Component, ViewEncapsulation, OnInit } from '@angular/core'; 3 | 4 | import { MenuComponent } from '../menu/menu.component'; 5 | import { CardsComponent } from '../dashboard/cards/cards.component'; 6 | 7 | import { CommonService } from '../common/common.service'; 8 | 9 | @Component({ 10 | templateUrl: 'about.component.html', 11 | encapsulation: ViewEncapsulation.None, 12 | providers: [CommonService, CardsComponent] 13 | }) 14 | export class AboutComponent implements OnInit { 15 | public title: string; 16 | public listTitle: string; 17 | public description: string; 18 | public controlList: Object[]; 19 | 20 | /** Configurations for the About page */ 21 | constructor( 22 | public common: CommonService, 23 | public cards: CardsComponent, 24 | public menu: MenuComponent 25 | ) { 26 | this.common.removeRootClass(); 27 | this.common.addRootClass('about-page'); 28 | this.title = 'About this sample'; 29 | this.listTitle = 'List of EJ2 components used in this sample'; 30 | this.description = 'This expense tracker demo application showcases using several Essential JS 2 ' 31 | + 'components together in a real-world application scenario. You can further explore the source ' 32 | + 'code of this application and use it as a reference for integrating Essential JS 2 components ' 33 | + 'into your applications.'; 34 | 35 | this.controlList = [ 36 | { 'control': 'Button', 'link': 'http://ej2.syncfusion.com/angular/documentation/button/getting-started/' }, 37 | { 'control': 'Chart', 'link': 'http://ej2.syncfusion.com/angular/documentation/chart/getting-started/' }, 38 | { 'control': 'CheckBox', 'link': 'http://ej2.syncfusion.com/angular/documentation/check-box/getting-started/' }, 39 | { 'control': 'DatePicker', 'link': 'http://ej2.syncfusion.com/angular/documentation/datepicker/getting-started/' }, 40 | { 'control': 'DateRangePicker', 'link': 'http://ej2.syncfusion.com/angular/documentation/daterangepicker/getting-started/' }, 41 | { 'control': 'Dialog', 'link': 'http://ej2.syncfusion.com/angular/documentation/dialog/getting-started/' }, 42 | { 'control': 'DropDownList', 'link': 'http://ej2.syncfusion.com/angular/documentation/drop-down-list/getting-started/' }, 43 | { 'control': 'Grid', 'link': 'http://ej2.syncfusion.com/angular/documentation/grid/getting-started/' }, 44 | { 'control': 'MultiSelect', 'link': 'http://ej2.syncfusion.com/angular/documentation/multi-select/getting-started/' }, 45 | { 'control': 'NumericTextBox', 'link': 'http://ej2.syncfusion.com/angular/documentation/numerictextbox/getting-started/' }, 46 | { 'control': 'RadioButton' , 'link': 'http://ej2.syncfusion.com/angular/documentation/radio-button/getting-started/'}, 47 | { 'control': 'TextBoxes', 'link': 'http://ej2.syncfusion.com/angular/documentation/textbox/getting-started/' }, 48 | { 'control': 'TimePicker', 'link': 'http://ej2.syncfusion.com/angular/documentation/timepicker/getting-started/' } 49 | ]; 50 | } 51 | 52 | public ngOnInit(): void { 53 | this.cards.updateCardValues(); 54 | this.menu.removeToggleClass(); 55 | this.menu.disableOverlay(); 56 | } 57 | } -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Directive, HostListener } from '@angular/core'; 2 | 3 | import { expenseData, startDate, endDate } from './common/common.data'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: 'app.component.html' 8 | }) 9 | export class AppComponent { 10 | public dataSource: Object[]; 11 | public startDate: Date; 12 | public endDate: Date; 13 | 14 | constructor() { 15 | this.startDate = startDate; 16 | this.endDate = endDate; 17 | this.dataSource = expenseData; 18 | this.handleResize(); 19 | } 20 | 21 | public handleResize(): void { 22 | if (document.documentElement.offsetWidth > 1400) { 23 | document.body.style.minHeight = 'auto'; 24 | document.body.style.minHeight = document.documentElement.offsetHeight + 'px'; 25 | } 26 | } 27 | 28 | @HostListener('window:resize', ['$event']) 29 | onResize(event: any): void { 30 | /** Document height alignment corrections for high resoultion screens */ 31 | this.handleResize(); 32 | } 33 | } -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 3 | import { APP_BASE_HREF, HashLocationStrategy, Location, LocationStrategy} from '@angular/common'; 4 | 5 | import { GridModule, PagerModule } from '@syncfusion/ej2-angular-grids'; 6 | import { NumericTextBoxAllModule } from '@syncfusion/ej2-angular-inputs'; 7 | import { CheckBoxAllModule, RadioButtonAllModule } from '@syncfusion/ej2-angular-buttons'; 8 | import { ChartAllModule, AccumulationChartAllModule } from '@syncfusion/ej2-angular-charts'; 9 | import { MultiSelectAllModule, DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns'; 10 | import { DialogAllModule, TooltipAllModule } from '@syncfusion/ej2-angular-popups'; 11 | import { DateRangePickerModule, DateRangePickerAllModule, DatePickerAllModule, TimePickerAllModule } from '@syncfusion/ej2-angular-calendars'; 12 | 13 | import { routing } from './app.router'; 14 | 15 | import { AppComponent } from './app.component'; 16 | import { MenuComponent } from './menu/menu.component'; 17 | import { AboutComponent } from './about/about.component'; 18 | import { ExpenseComponent } from './expense/expense.component'; 19 | import { CardsComponent } from './dashboard/cards/cards.component'; 20 | import { FilterComponent } from './expense/filter/filter.component'; 21 | import { DashBoardComponent } from './dashboard/dashboard.component'; 22 | import { ContentComponent } from './expense/content/content.component'; 23 | import { DialogsComponent } from './expense/dialogs/dialogs.component'; 24 | import { PieChartComponent } from './dashboard/pie-chart/pie-chart.component'; 25 | import { LineChartComponent } from './dashboard/line-chart/line-chart.component'; 26 | import { ColumnChartComponent } from './dashboard/column-chart/column-chart.component'; 27 | import { RecentExpGridComponent } from './dashboard/recent-exp-grid/recent-exp-grid.component'; 28 | 29 | import { CommonService } from './common/common.service'; 30 | import { DashBoardService } from './dashboard/dashboard.service'; 31 | 32 | @NgModule({ 33 | imports: [ 34 | routing, 35 | GridModule, 36 | PagerModule, 37 | BrowserModule, 38 | ChartAllModule, 39 | DialogAllModule, 40 | CheckBoxAllModule, 41 | DatePickerAllModule, 42 | TimePickerAllModule, 43 | MultiSelectAllModule, 44 | RadioButtonAllModule, 45 | DateRangePickerModule, 46 | DropDownListAllModule, 47 | NumericTextBoxAllModule, 48 | DateRangePickerAllModule, 49 | AccumulationChartAllModule 50 | ], 51 | declarations: [ 52 | AppComponent, 53 | MenuComponent, 54 | CardsComponent, 55 | AboutComponent, 56 | FilterComponent, 57 | ExpenseComponent, 58 | ContentComponent, 59 | DialogsComponent, 60 | PieChartComponent, 61 | DashBoardComponent, 62 | LineChartComponent, 63 | ColumnChartComponent, 64 | RecentExpGridComponent, 65 | ], 66 | bootstrap: [AppComponent], 67 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 68 | providers: [ 69 | Location, 70 | CommonService, 71 | DashBoardService, 72 | ContentComponent, 73 | {provide: APP_BASE_HREF, useValue : '/' }, 74 | {provide: LocationStrategy, useClass: HashLocationStrategy} 75 | ] 76 | }) 77 | export class AppModule { 78 | private location: Location; 79 | constructor(location: Location) { 80 | this.location = location; 81 | } 82 | } -------------------------------------------------------------------------------- /src/app/app.router.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { DashBoardComponent } from './dashboard/dashboard.component'; 4 | import { ExpenseComponent } from './expense/expense.component'; 5 | import { AboutComponent } from './about/about.component'; 6 | 7 | // Route Configuration 8 | export const routes: Routes = [ 9 | { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, 10 | { path: 'dashboard', component: DashBoardComponent }, 11 | { path: 'expense', component: ExpenseComponent }, 12 | { path: 'about', component: AboutComponent }, 13 | { path: '**', redirectTo: 'home' } 14 | ]; 15 | 16 | export const routing: ModuleWithProviders = RouterModule.forRoot(routes); -------------------------------------------------------------------------------- /src/app/common/common.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, ViewChild } from '@angular/core'; 2 | 3 | import { extend, Internationalization } from '@syncfusion/ej2-base'; 4 | import { Predicate } from '@syncfusion/ej2-data'; 5 | 6 | import { expenseData } from '../common/common.data'; 7 | 8 | @Injectable() 9 | export class CommonService { 10 | public predicateStart: Predicate; 11 | public predicateEnd: Predicate; 12 | public predicate: Predicate; 13 | public intl: Internationalization = new Internationalization(); 14 | 15 | constructor() {} 16 | 17 | public getPredicate(start: Date, end: Date): Predicate { 18 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', start); 19 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', end); 20 | this.predicate = this.predicateStart.and(this.predicateEnd); 21 | return this.predicate; 22 | } 23 | 24 | public addRootClass(cls: string): void { 25 | let ele: HTMLElement = document.body; 26 | ele.classList.add(cls); 27 | } 28 | 29 | public removeRootClass(): void { 30 | let ele: HTMLElement = document.body; 31 | ele.classList.remove('dashboard-page'); 32 | ele.classList.remove('expense-page'); 33 | ele.classList.remove('about-page'); 34 | } 35 | 36 | public objectAssign(e: any): object[] { 37 | let result: Object[] = []; 38 | let obj: any; 39 | obj = extend(obj, e.result, {}, true); 40 | for (let data: number = 0; data < Object.keys(e.result).length; data++) { 41 | result.push(obj[data]); 42 | } 43 | return result; 44 | } 45 | 46 | public getDate(value: Date): string { 47 | return this.intl.formatDate(value, { skeleton: 'yMd', type: 'date' }); 48 | } 49 | 50 | public getCurrencyVal(value: number): string { 51 | return this.intl.formatNumber(value, { format: 'C0' }); 52 | } 53 | 54 | public getNumberVal(value: number): string { 55 | return this.intl.getNumberFormat({ skeleton: 'C0', currency: 'USD' })(value); 56 | } 57 | } -------------------------------------------------------------------------------- /src/app/dashboard/cards/cards.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |

{{totalIncome}}

9 | Income 10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |

{{totalExpense}}

23 | Expenses 24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |

{{totalBalance}}

37 | Balance 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |

{{totalTransactions}}

51 | Transactions 52 |
53 |
54 |
55 |
56 |
57 |
58 |
-------------------------------------------------------------------------------- /src/app/dashboard/cards/cards.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit } from '@angular/core'; 2 | 3 | import { Internationalization } from '@syncfusion/ej2-base'; 4 | import { Query, DataManager, Predicate } from '@syncfusion/ej2-data'; 5 | 6 | import { AppComponent } from '../../app.component'; 7 | 8 | import { CommonService } from '../../common/common.service'; 9 | 10 | @Component({ 11 | selector: 'cards', 12 | templateUrl: 'cards.component.html', 13 | encapsulation: ViewEncapsulation.None, 14 | providers: [CommonService] 15 | }) 16 | export class CardsComponent implements OnInit { 17 | public predicate: Predicate; 18 | public predicateEnd: Predicate; 19 | public predicateStart: Predicate; 20 | public totalIncome: string = '$0'; 21 | public totalExpense: string = '$0'; 22 | public totalBalance: string = '$0'; 23 | public totalTransactions: string = '0'; 24 | 25 | constructor(public common: CommonService, public app: AppComponent) {} 26 | 27 | public ngOnInit(): void { 28 | 29 | /** Updates each card values on the initial load */ 30 | this.updateCardValues(); 31 | } 32 | 33 | public updateCardValues(): void { 34 | let predicate: Predicate = this.common.getPredicate(this.app.startDate, this.app.endDate); 35 | let intl: Internationalization = new Internationalization(); 36 | let incomeRS: number = 0; 37 | let expenseRS: number = 0; 38 | let incomeD: any; 39 | let expenseD: any; 40 | 41 | /** Calulates total income and sets to the Income card */ 42 | new DataManager(this.app.dataSource).executeQuery((new Query() 43 | .where((predicate).and('TransactionType', 'equal', 'Income')))) 44 | .then((e: any) => { 45 | incomeD = this.common.objectAssign(e); 46 | for (let i: number = 0; i < incomeD.length; i++) { 47 | incomeRS += parseInt(incomeD[i].Amount, 0); 48 | } 49 | this.totalIncome = this.common.getCurrencyVal(incomeRS ? incomeRS : 0); 50 | }); 51 | 52 | /** Calulates total expenses and sets to the Expenses card */ 53 | new DataManager(this.app.dataSource).executeQuery(new Query() 54 | .where((predicate).and('TransactionType', 'equal', 'Expense'))) 55 | .then((e: any) => { 56 | expenseD = this.common.objectAssign(e); 57 | for (let i: number = 0; i < expenseD.length; i++) { 58 | expenseRS += parseInt(expenseD[i].Amount, 0); 59 | } 60 | this.totalExpense = this.common.getCurrencyVal(expenseRS ? expenseRS : 0); 61 | document.getElementById('current-balance').textContent = this.common.getCurrencyVal(incomeRS - expenseRS); 62 | 63 | /** Based on the Income and Expense, calulates the balance and sets to the Balance card */ 64 | this.totalBalance = this.common.getCurrencyVal(incomeRS - expenseRS); 65 | }); 66 | 67 | /** Calulates total transactions and sets to the Transactions card */ 68 | let transaction: any = new DataManager(this.app.dataSource) 69 | .executeLocal((new Query().where(predicate))); 70 | this.totalTransactions = this.common.getNumberVal(transaction.length); 71 | } 72 | } -------------------------------------------------------------------------------- /src/app/dashboard/column-chart/column-chart.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
-------------------------------------------------------------------------------- /src/app/dashboard/column-chart/column-chart.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core'; 2 | 3 | import { ChartComponent, ILoadedEventArgs} from '@syncfusion/ej2-angular-charts'; 4 | 5 | import { DashBoardComponent } from '../dashboard.component'; 6 | 7 | @Component({ 8 | selector: 'column-chart', 9 | templateUrl: 'column-chart.component.html', 10 | encapsulation: ViewEncapsulation.None 11 | }) 12 | export class ColumnChartComponent implements OnInit { 13 | @ViewChild('columnChart') colChart: ChartComponent; 14 | 15 | public lineObj: any; 16 | public marker: Object; 17 | public margin: Object; 18 | public cBorder: Object; 19 | public tooltip: Object; 20 | public titleStyle: Object; 21 | public incomeDS: any = []; 22 | public expenseDS: any = []; 23 | public primaryXAxis: Object; 24 | public primaryYAxis: Object; 25 | public legendSettings: Object; 26 | public initialRender: boolean = true; 27 | public animation: Object; 28 | 29 | constructor(public dashBoard: DashBoardComponent) {} 30 | 31 | public ngOnInit(): void { 32 | 33 | /** Configurations for the Column chart (Income vs Expense) component */ 34 | this.primaryXAxis = { 35 | labelFormat: 'MMM', 36 | valueType: 'DateTime', 37 | intervalType: 'Months', 38 | edgeLabelPlacement: 'Shift' 39 | }; 40 | this.primaryYAxis = { 41 | minimum: 3000, 42 | maximum: 9000, 43 | labelFormat: 'c0' 44 | }; 45 | this.titleStyle = { textAlignment: 'Near', fontWeight: '500', size: '16', color: '#000' }; 46 | this.legendSettings = { visible: true }; 47 | this.tooltip = { 48 | fill: '#707070', 49 | enable: true, 50 | shared: true, 51 | format: '${series.name} : ${point.y}', 52 | header: 'Month - ${point.x} ', 53 | }; 54 | this.marker = { visible: true, height: 10, width: 10 }; 55 | this.margin = { top: 90 }; 56 | this.cBorder = { width: 0.5, color: '#A16EE5' }; 57 | this.animation = { enable: false }; 58 | } 59 | 60 | public onChartLoaded(args: ILoadedEventArgs): void { 61 | if (this.initialRender) { 62 | this.initialRender = false; 63 | this.lineObj.line.refresh(); 64 | } else { 65 | this.initialRender = false; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Dashboard

4 |
5 | 6 |
7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 | 26 |
27 |
-------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core'; 2 | 3 | import { Query, DataManager, Predicate } from '@syncfusion/ej2-data'; 4 | import { DateRangePickerComponent, RangeEventArgs } from '@syncfusion/ej2-angular-calendars'; 5 | 6 | import { AppComponent } from '../app.component'; 7 | import { MenuComponent } from '../menu/menu.component'; 8 | import { CardsComponent } from './cards/cards.component'; 9 | import { PieChartComponent } from './pie-chart/pie-chart.component'; 10 | import { LineChartComponent } from './line-chart/line-chart.component'; 11 | import { ColumnChartComponent } from './column-chart/column-chart.component'; 12 | import { RecentExpGridComponent } from './recent-exp-grid/recent-exp-grid.component'; 13 | 14 | import { DashBoardService } from './dashboard.service'; 15 | import { CommonService } from '../common/common.service'; 16 | 17 | @Component({ 18 | templateUrl: 'dashboard.component.html', 19 | encapsulation: ViewEncapsulation.None, 20 | providers: [DashBoardService, CommonService] 21 | }) 22 | export class DashBoardComponent implements OnInit { 23 | @ViewChild('cards') cards: CardsComponent; 24 | @ViewChild('pieChart') pieChart: PieChartComponent; 25 | @ViewChild('lineChart') lineChart: LineChartComponent; 26 | @ViewChild('columnChart') columnChart: ColumnChartComponent; 27 | @ViewChild('recentGrid') recentGrid: RecentExpGridComponent; 28 | @ViewChild('dateRangePicker') dateRangePicker: DateRangePickerComponent; 29 | 30 | public predicate: Predicate; 31 | public datePresets: Object[]; 32 | public lineChartData: Object[]; 33 | public predicateEnd: Predicate; 34 | public predicateStart: Predicate; 35 | public colChartIncomeData: Object[]; 36 | public colChartExpenseData: Object[]; 37 | 38 | constructor( 39 | public app: AppComponent, 40 | public dashService: DashBoardService, 41 | public common: CommonService, 42 | public menu: MenuComponent 43 | ) { 44 | this.common.removeRootClass(); 45 | this.common.addRootClass('dashboard-page'); 46 | } 47 | 48 | public ngOnInit(): void { 49 | 50 | /** Configurations for the components in the DashBoard page */ 51 | this.menu.removeToggleClass(); 52 | this.menu.disableOverlay(); 53 | this.datePresets = [ 54 | { label: 'Last Month', start: new Date('10/1/2017'), end: new Date('10/31/2017') }, 55 | { label: 'Last 3 Months', start: new Date('9/1/2017'), end: new Date('11/30/2017') }, 56 | { label: 'All Time', start: new Date('6/1/2017'), end: new Date('11/30/2017') } 57 | ]; 58 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', this.app.startDate); 59 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', this.app.endDate); 60 | this.predicate = this.predicateStart.and(this.predicateEnd); 61 | this.updateChartData(); 62 | } 63 | 64 | public ngAfterViewInit(): void { 65 | this.columnChart.lineObj = this.lineChart; 66 | } 67 | 68 | /** Updates chart data during the DateRangePicker filtering operation */ 69 | public updateChartData(): void { 70 | new DataManager(this.app.dataSource).executeQuery(new Query() 71 | .where(this.predicate.and('TransactionType', 'equal', 'Expense'))) 72 | .then((e: any) => { 73 | this.colChartExpenseData = this.dashService.getColumnChartExpenseDS(e); 74 | }); 75 | new DataManager(this.app.dataSource).executeQuery(new Query() 76 | .where(this.predicate.and('TransactionType', 'equal', 'Income'))) 77 | .then((e: any) => { 78 | this.colChartIncomeData = this.dashService.getColumnChartIncomeDS(e); 79 | this.lineChartData = this.dashService.getLineChartDS(); 80 | }); 81 | } 82 | 83 | /** Performs fitlering and refreshes cards, chart and grid components based on the selected date ranges by using the DateRangePicker */ 84 | public onDateRangeChange(args: RangeEventArgs): void { 85 | this.app.startDate = args.startDate; 86 | this.app.endDate = args.endDate; 87 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', args.startDate); 88 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', args.endDate); 89 | this.predicate = this.predicateStart.and(this.predicateEnd); 90 | this.cards.updateCardValues(); 91 | this.pieChart.getTotalExpense(); 92 | this.updateChartData(); 93 | setTimeout(() => { 94 | this.pieChart.pie.refresh(); 95 | this.lineChart.line.refresh(); 96 | this.columnChart.colChart.refresh(); 97 | }, 400); 98 | setTimeout(() => { 99 | this.pieChart.refreshPieChart(); 100 | }, 1000); 101 | } 102 | } -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { extend } from '@syncfusion/ej2-base'; 4 | 5 | import { CommonService } from '../common/common.service'; 6 | import { userInfo } from '../common/common.data'; 7 | 8 | @Injectable() 9 | export class DashBoardService { 10 | public name: string; 11 | public lineD: any = []; 12 | public lineDS: any = []; 13 | public curDateTime: any; 14 | public tempLineDS: any = {}; 15 | public colIncomeDS: any = []; 16 | public colExpenseDS: any = []; 17 | public tempIncomeDS: any = {}; 18 | public tempExpenseDS: any = {}; 19 | 20 | constructor(public common: CommonService) { 21 | this.name = userInfo.FirstName; 22 | } 23 | 24 | public getName(): string { 25 | return this.name; 26 | } 27 | 28 | public getColumnChartIncomeDS(e: any): Object[] { 29 | this.colIncomeDS = []; 30 | this.tempIncomeDS = []; 31 | let result: Object[] = this.common.objectAssign(e); 32 | for (let i: number = 0; i < result.length; i++) { 33 | let cur: any = result[i]; 34 | if (cur.DateTime.getMonth() in this.tempIncomeDS) { 35 | this.curDateTime = this.tempIncomeDS[cur.DateTime.getMonth()]; 36 | this.tempIncomeDS[cur.DateTime.getMonth()].Amount = parseInt(this.curDateTime.Amount, 0) + parseInt(cur.Amount, 0); 37 | } else { 38 | this.tempIncomeDS[cur.DateTime.getMonth()] = cur; 39 | this.tempIncomeDS[cur.DateTime.getMonth()].DateTime = new Date(new Date(this.tempIncomeDS[cur.DateTime.getMonth()].DateTime.setHours(0, 0, 0, 0)).setDate(1)); 40 | } 41 | } 42 | for (let data in this.tempIncomeDS) { 43 | this.colIncomeDS.push(this.tempIncomeDS[data]); 44 | } 45 | return this.colIncomeDS; 46 | } 47 | 48 | public getColumnChartExpenseDS(e: any): Object[] { 49 | this.colExpenseDS = []; 50 | this.tempExpenseDS = []; 51 | let result: Object[] = this.common.objectAssign(e); 52 | for (let i: number = 0; i < result.length; i++) { 53 | let cur: any = result[i]; 54 | if (cur.DateTime.getMonth() in this.tempExpenseDS) { 55 | this.curDateTime = this.tempExpenseDS[cur.DateTime.getMonth()]; 56 | this.tempExpenseDS[cur.DateTime.getMonth()].Amount = parseInt(this.curDateTime.Amount, 0) + parseInt(cur.Amount, 0); 57 | } else { 58 | this.tempExpenseDS[cur.DateTime.getMonth()] = cur; 59 | this.tempExpenseDS[cur.DateTime.getMonth()].DateTime = new Date(new Date(this.tempExpenseDS[cur.DateTime.getMonth()].DateTime.setHours(0, 0, 0, 0)).setDate(1)); 60 | } 61 | } 62 | for (let data in this.tempExpenseDS) { 63 | this.colExpenseDS.push(this.tempExpenseDS[data]); 64 | } 65 | return this.colExpenseDS; 66 | } 67 | 68 | public getLineChartDS(): Object[] { 69 | this.lineD = []; 70 | this.lineDS = []; 71 | this.tempLineDS = []; 72 | let result: Object[] = []; 73 | let obj: any; 74 | obj = extend(obj, (this.colIncomeDS.concat(this.colExpenseDS)), {}, true); 75 | for (let data: number = 0; data < Object.keys((this.colIncomeDS.concat(this.colExpenseDS))).length; data++) { 76 | result.push(obj[data]); 77 | } 78 | this.tempLineDS = result; 79 | for (let i: number = 0; i < this.tempLineDS.length; i++) { 80 | let cur: any = this.tempLineDS[i]; 81 | if (cur.DateTime.getMonth() in this.lineD) { 82 | this.curDateTime = this.lineD[cur.DateTime.getMonth()]; 83 | this.lineD[cur.DateTime.getMonth()].Amount = Math.abs((parseInt(this.curDateTime.Amount, 0) - parseInt(cur.Amount, 0))); 84 | } else { 85 | this.lineD[cur.DateTime.getMonth()] = cur; 86 | } 87 | } 88 | for (let data: number = 0; data <= this.lineD.length; data++) { 89 | if (this.lineD[data]) { 90 | this.lineDS.push(this.lineD[data]); 91 | } 92 | } 93 | return this.lineDS; 94 | } 95 | } -------------------------------------------------------------------------------- /src/app/dashboard/line-chart/line-chart.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/app/dashboard/line-chart/line-chart.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core'; 2 | 3 | import { ChartComponent } from '@syncfusion/ej2-angular-charts'; 4 | 5 | import { DashBoardComponent } from '../dashboard.component'; 6 | 7 | @Component({ 8 | selector: 'line-chart', 9 | templateUrl: 'line-chart.component.html', 10 | encapsulation: ViewEncapsulation.None 11 | }) 12 | export class LineChartComponent implements OnInit { 13 | @ViewChild('lineChart') line: ChartComponent; 14 | 15 | public marker: Object; 16 | public margin: Object; 17 | public tooltip: Object; 18 | public lBorder: Object; 19 | public lineDS: any = []; 20 | public crossHair: Object; 21 | public chartArea: Object; 22 | public dataSource: Object; 23 | public primaryXAxis: Object; 24 | public primaryYAxis: Object; 25 | public animation: Object; 26 | 27 | constructor(public dashBoard: DashBoardComponent) {} 28 | 29 | public ngOnInit(): void { 30 | 31 | /** Configurations for the Line chart component */ 32 | this.primaryXAxis = { 33 | valueType: 'DateTime', 34 | labelFormat: 'MMM', 35 | majorGridLines: { width: 0 }, 36 | intervalType: 'Months' 37 | }; 38 | this.primaryYAxis = { 39 | maximum: 1800, 40 | interval: 300, 41 | labelFormat: 'c0' 42 | }; 43 | this.tooltip = { 44 | fill: '#707070', 45 | enable: true, 46 | shared: true, 47 | format: '${series.name} : ${point.y}', 48 | header: 'Month - ${point.x} ' 49 | }; 50 | this.chartArea = { 51 | border: { width: 0 } 52 | }; 53 | this.margin = { top: 90 }; 54 | this.lBorder = { width: 0.5, color: '#0470D8' }; 55 | this.marker = { 56 | visible: true, 57 | width: 10, 58 | height: 10, 59 | fill: 'white', 60 | border: { width: 2, color: '#0470D8' }, 61 | }; 62 | this.animation = { enable: false }; 63 | } 64 | } -------------------------------------------------------------------------------- /src/app/dashboard/pie-chart/pie-chart.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |

Total Expenses

7 |

8 |
9 |
10 |
11 | 14 | 15 | 17 | 18 | 19 | 20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | 44 | {{data.text}} 45 | {{data.y | currency:'USD'}} 46 | {{data.x}} 47 | 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
-------------------------------------------------------------------------------- /src/app/dashboard/pie-chart/pie-chart.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit, ViewChild, HostListener } from '@angular/core'; 2 | 3 | import { GridComponent } from '@syncfusion/ej2-angular-grids'; 4 | import { Query, DataManager } from '@syncfusion/ej2-data'; 5 | import { isNullOrUndefined as isNOU, Browser } from '@syncfusion/ej2-base'; 6 | import { IAccTextRenderEventArgs, IAccLoadedEventArgs, AccumulationChartComponent, 7 | IAccAnimationCompleteEventArgs } from '@syncfusion/ej2-angular-charts'; 8 | 9 | import { AppComponent } from '../../app.component'; 10 | 11 | @Component({ 12 | selector: 'pie-chart', 13 | templateUrl: 'pie-chart.component.html', 14 | encapsulation: ViewEncapsulation.None 15 | }) 16 | export class PieChartComponent { 17 | @ViewChild('pie') pie: AccumulationChartComponent; 18 | @ViewChild('legendGrid') lGrid: GridComponent; 19 | 20 | public dataLabel: Object; 21 | public groupValue: string; 22 | public hiGridData: Object[]; 23 | public expTotal: number = 0; 24 | public legendSettings: Object; 25 | public colorPalettes: Object[]; 26 | public category: string[] = []; 27 | public tempData: IExpenseData[]; 28 | public legendData: Object[] = []; 29 | public pieLegendData: Object[] = []; 30 | public pieRenderData: Object[] = []; 31 | public enableLegend: boolean = false; 32 | public pieRenderingData: Object[] = []; 33 | public animation: Object; 34 | public showWaitingPopup: boolean = false; 35 | 36 | constructor(public app: AppComponent) {} 37 | 38 | public ngOnInit(): void { 39 | 40 | /** Configurations for the Pie chart component */ 41 | this.legendSettings = { visible: true }; 42 | this.colorPalettes = ['#61EFCD', '#CDDE1F', '#FEC200', '#CA765A', '#2485FA', '#F57D7D', '#C152D2', '#8854D9', '#3D4EB8', 43 | '#00BCD7']; 44 | this.dataLabel = { 45 | name: 'x', visible: true, 46 | position: 'Outside', connectorStyle: { length: '10%' }, 47 | font: { color: 'Black', size: '14px', fontFamily: 'Roboto' } 48 | }; 49 | this.getTotalExpense(); 50 | this.animation = { enable: false }; 51 | } 52 | 53 | public ngAfterViewInit(): void { 54 | this.handleDataLabel(); 55 | } 56 | 57 | @HostListener('window:resize', ['$event']) 58 | onResize(event: any): void { 59 | /** Pie chart label disable at mobile mode handle at here */ 60 | this.handleDataLabel(); 61 | } 62 | 63 | public handleDataLabel(): void { 64 | if (Browser.isDevice || window.innerWidth < 400) { 65 | this.pie.series[0].dataLabel.visible = false; 66 | } else { 67 | this.pie.series[0].dataLabel.visible = true; 68 | } 69 | } 70 | 71 | /** Sets the pie chart's font size based on its size */ 72 | public getFontSize(width: number): string { 73 | if (width > 300) { 74 | return '13px'; 75 | } else if (width > 250) { 76 | return '8px'; 77 | } else { 78 | return '6px'; 79 | } 80 | } 81 | 82 | public onChartLoaded(args: IAccLoadedEventArgs): void { 83 | this.createLegendData('pie'); 84 | this.enableLegend = true; 85 | } 86 | 87 | public onTextRender(args: IAccTextRenderEventArgs): void { 88 | args.series.dataLabel.font.size = this.getFontSize(this.pie.initialClipRect.width); 89 | this.pie.animateSeries = true; 90 | if (args.text.indexOf('Others') > -1) { 91 | args.text = 'Others'; 92 | } 93 | } 94 | 95 | public onAnimateCompleted(args: IAccAnimationCompleteEventArgs): void { 96 | let element: HTMLElement = document.getElementById('total-expense_datalabel_Series_0'); 97 | if (!isNOU(element)) { element.style.visibility = 'visible'; } 98 | } 99 | 100 | public getTotalExpense(): void { 101 | this.tempData = this.app.dataSource; 102 | this.expTotal = 0; 103 | this.category = []; 104 | this.legendData = []; 105 | let renderingData: { x: string; y: number; text: string; }[] = []; 106 | 107 | /** Extracts the category based data from the whole expense data */ 108 | this.tempData.forEach((item: IExpenseData) => { 109 | if (item.TransactionType === 'Expense' && this.app.startDate.valueOf() <= item.DateTime.valueOf() 110 | && this.app.endDate.valueOf() >= item.DateTime.valueOf()) { 111 | this.expTotal += Number(item.Amount); 112 | this.legendData.push(item); 113 | if (this.category.indexOf(item.Category) < 0) { 114 | this.category.push(item.Category); 115 | } 116 | } 117 | }); 118 | 119 | /** From the category data, percentage calculation for legend grid */ 120 | this.category.forEach((str: string) => { 121 | let total: number = 0; 122 | this.legendData.forEach((item: IExpenseData) => { 123 | if (str === item.Category) { 124 | total += Number(item.Amount); 125 | } 126 | }); 127 | let percent: string = ((total / this.expTotal) * 100).toFixed(2) + '%'; 128 | renderingData.push({ x: str, y: total, text: percent }); 129 | }); 130 | 131 | /** Generates the pie chart data (pieRenderingData) */ 132 | this.pieRenderingData = new DataManager(JSON.parse(JSON.stringify(renderingData))) 133 | .executeLocal((new Query().sortByDesc('y'))); 134 | if (this.pieRenderingData.length > 10) { 135 | let temp: { x: string; y: number; text: string; } = <{ x: string; y: number; text: string; }> 136 | new DataManager(JSON.parse(JSON.stringify(renderingData))).executeLocal((new Query() 137 | .sortByDesc('y').range(0, 9)))[8]; 138 | this.groupValue = (temp.y - 1).toString(); 139 | this.hiGridData = new DataManager(JSON.parse(JSON.stringify(renderingData))) 140 | .executeLocal((new Query().sortByDesc('y').skip(9))); 141 | } else { 142 | this.groupValue = null; 143 | } 144 | } 145 | 146 | public createLegendData(initiate: string): void { 147 | if (initiate === 'pieUpdate' || this.pieLegendData.length === 0) { 148 | this.pieLegendData = []; 149 | this.pieLegendData = this.pie.visibleSeries[0].points; 150 | } 151 | this.pie.legendSettings.visible = false; 152 | /** Generates the legend grid data (pieRenderData) */ 153 | this.pieRenderData = []; 154 | for (let i: number = 0; i < this.pieLegendData.length; i++) { 155 | let data: { [k: string]: any } = this.pieLegendData[i]; 156 | if (data.text.indexOf('Others') > -1) { 157 | data.x = ((data.y / this.expTotal) * 100).toFixed(2).toString() + '%'; 158 | } 159 | this.pieRenderData.push(data); 160 | } 161 | } 162 | 163 | public onGridDataBound(args: Object): void { 164 | //this.pie.refresh(); 165 | //this.lineChart.refresh(); 166 | //this.columnChart.refresh(); 167 | this.showWaitingPopup = false; 168 | } 169 | 170 | public onGridLoad(args: any): void { 171 | /** While the legend grid loads, it gets the data from pie chart and process to this */ 172 | this.createLegendData('pie'); 173 | this.showWaitingPopup = true; 174 | } 175 | 176 | public refreshPieChart(): void { 177 | this.getTotalExpense(); 178 | this.createLegendData('pieUpdate'); 179 | this.lGrid.refresh(); 180 | } 181 | 182 | public updatePieChart(): void { 183 | let pieContainerObj: HTMLElement = document.getElementById('totalExpense'); 184 | if (!isNOU(pieContainerObj) && pieContainerObj.offsetWidth < 480) { 185 | this.disableChartLabel(); 186 | } else if (!isNOU(pieContainerObj) && pieContainerObj.offsetWidth > 480) { 187 | this.enableChartLabel(); 188 | } 189 | } 190 | public disableChartLabel(): void { 191 | this.pie.series[0].dataLabel.visible = false; 192 | this.pie.refresh(); 193 | } 194 | public enableChartLabel(): void { 195 | this.pie.series[0].dataLabel.visible = true; 196 | this.pie.refresh(); 197 | } 198 | } 199 | 200 | export interface IExpenseData { 201 | Amount: number; 202 | Category: string; 203 | DateTime: Date; 204 | Description: string; 205 | PaymentMode: string; 206 | TransactionType: string; 207 | UniqueId: string; 208 | } -------------------------------------------------------------------------------- /src/app/dashboard/recent-exp-grid/recent-exp-grid.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | {{data.DateTime | date:'M/d/yyyy'}} 8 | 9 | 10 | 11 | 12 |
13 |
14 |
{{data.Category}}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
{{data.Category}}
22 |
{{data.Description}}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
{{data.Category}}
31 |
{{data.Description}}
32 |
{{data.PaymentMode}}
33 |
34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 |
43 | {{data.Amount | currency:'USD'}} 44 |
45 |
46 |
47 |
48 | {{data.Amount | currency:'USD'}} 49 |
50 |
{{data.DateTime | date:'M/d/yyyy'}}
51 |
52 |
53 |
54 | {{data.Amount | currency:'USD'}} 55 |
56 |
{{data.DateTime | date:'M/d/yyyy'}}
57 |
58 |
59 |
60 |
61 |
62 |
63 |
-------------------------------------------------------------------------------- /src/app/dashboard/recent-exp-grid/recent-exp-grid.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit } from '@angular/core'; 2 | 3 | import { Query, Predicate } from '@syncfusion/ej2-data'; 4 | 5 | import { AppComponent } from '../../app.component'; 6 | 7 | @Component({ 8 | selector: 'recent-exp-grid', 9 | templateUrl: 'recent-exp-grid.component.html', 10 | encapsulation: ViewEncapsulation.None 11 | }) 12 | export class RecentExpGridComponent implements OnInit { 13 | public query: Query; 14 | public gridToolbar: Object[]; 15 | public predicate: Predicate; 16 | public predicateEnd: Predicate; 17 | public predicateStart: Predicate; 18 | 19 | constructor(public app: AppComponent) {} 20 | 21 | public ngOnInit(): void { 22 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', this.app.startDate); 23 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', this.app.endDate); 24 | this.predicate = this.predicateStart.and(this.predicateEnd); 25 | 26 | /** Query to takes last 5 lows to show the recent records only */ 27 | this.query = new Query().where(this.predicate).sortByDesc('DateTime').take(5); 28 | this.gridToolbar = [{ text: 'Recent Transactions' }]; 29 | } 30 | } -------------------------------------------------------------------------------- /src/app/expense/content/content.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{transactionTitle}}

4 |
5 |
6 |
7 |
8 |
9 |
10 | 14 |
15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
{{data.Category}}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
{{data.Category}}
38 |
{{data.Description}}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
{{data.Category}}
47 |
{{data.Description}}
48 |
{{data.PaymentMode}}
49 |
50 |
51 |
52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |
62 | {{data.Amount | currency:'USD'}} 63 |
64 |
65 |
66 |
67 |
68 |
69 | {{data.Amount | currency:'USD'}} 70 |
71 |
{{data.DateTime | date:'M/d/yyyy'}}
72 |
73 |
74 |
75 |
76 |
77 | {{data.Amount | currency:'USD'}} 78 |
79 |
{{data.DateTime | date:'M/d/yyyy'}}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
-------------------------------------------------------------------------------- /src/app/expense/content/content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit, ElementRef, ViewChild } from '@angular/core'; 2 | 3 | import { KeyboardEventArgs } from '@syncfusion/ej2-base'; 4 | import { Query, DataManager, Predicate } from '@syncfusion/ej2-data'; 5 | import { Input } from '@syncfusion/ej2-inputs'; 6 | import { GridComponent, RowSelectEventArgs, RowDeselectEventArgs, CheckBoxChangeEventArgs, 7 | PageService, EditService, CommandColumnService, ToolbarService, ContextMenuService, 8 | ResizeService } from '@syncfusion/ej2-angular-grids'; 9 | 10 | import { AppComponent } from '../../app.component'; 11 | import { MenuComponent } from '../../menu/menu.component'; 12 | import { DialogsComponent } from '../dialogs/dialogs.component'; 13 | 14 | import { expenseData, categoryIncomeData, categoryExpenseData } from '../../common/common.data'; 15 | 16 | @Component({ 17 | selector: 'content-section', 18 | templateUrl: 'content.component.html', 19 | encapsulation: ViewEncapsulation.None, 20 | providers: [DialogsComponent, AppComponent, PageService, EditService, CommandColumnService, ToolbarService, ContextMenuService, ResizeService], 21 | }) 22 | export class ContentComponent implements OnInit { 23 | @ViewChild('transactGrid') grid: GridComponent; 24 | 25 | public query: Query; 26 | public dlgCompObj: any; 27 | public filterCompObj: any; 28 | public predicate: Predicate; 29 | public dataSource: Object[]; 30 | public pageSettings: Object; 31 | public editSettings: Object; 32 | public validateRule: Object; 33 | public toolbarValue: Object[]; 34 | public predicateEnd: Predicate; 35 | public transactionTitle: string; 36 | public predicateStart: Predicate; 37 | public searchInput: HTMLInputElement; 38 | public clearIcon: HTMLElement; 39 | 40 | constructor( 41 | public app: AppComponent, 42 | public eleRef: ElementRef, 43 | public menu: MenuComponent) {} 44 | 45 | public ngOnInit(): void { 46 | this.transactionTitle = 'All Transactions'; 47 | 48 | /** Configurations for the Expense Grid component */ 49 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', this.app.startDate); 50 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', this.app.endDate); 51 | this.predicate = this.predicateStart.and(this.predicateEnd); 52 | this.toolbarValue = ['Edit', 'Delete']; 53 | this.query = new Query().where(this.predicate).sortByDesc('DateTime'); 54 | this.pageSettings = { pageSize: 11 }; 55 | this.validateRule = { required: true }; 56 | this.editSettings = { allowEditing: true }; 57 | } 58 | 59 | public ngAfterViewInit(): void { 60 | this.searchInput = this.eleRef.nativeElement.querySelector('#txt'); 61 | Input.createInput({ 62 | element: this.searchInput, 63 | properties: { 64 | showClearButton: true 65 | } 66 | }); 67 | this.clearIcon = this.eleRef.nativeElement.querySelector('.e-clear-icon'); 68 | this.clearIcon.onmousedown = () => { 69 | this.searchInput.value = ''; 70 | }; 71 | } 72 | 73 | public onGridCreated(): void { 74 | /** Edit and Delete toolbar items customization on the grid's created event */ 75 | let ele: any = this.eleRef.nativeElement.querySelector('#grid_edit'); 76 | let el: any = this.eleRef.nativeElement.querySelector('#grid_delete'); 77 | if (ele) { 78 | ele.addEventListener('click', this.showEditTransactDialog.bind(this)); 79 | } 80 | if (el) { 81 | el.addEventListener('click', this.showDeleteDialog.bind(this)); 82 | } 83 | } 84 | 85 | public onGridCellSaved(args: any): void { 86 | new DataManager( this.app.dataSource).update('UniqueId', args.rowData); 87 | } 88 | 89 | public onGridRowSelected(args: RowSelectEventArgs): void { 90 | this.handleToolbarVisibility(); 91 | } 92 | public onGridRowDeselected(args: RowDeselectEventArgs): void { 93 | this.handleToolbarVisibility(); 94 | } 95 | 96 | public showAddTransactDialog(): void { 97 | this.dlgCompObj.showAddDialog(); 98 | } 99 | 100 | public showDeleteDialog(): void { 101 | this.dlgCompObj.showAlertDialog(); 102 | } 103 | 104 | public showEditTransactDialog(): void { 105 | this.dlgCompObj.showEditDialog(); 106 | setTimeout(() => { 107 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_delete').parentElement, true); 108 | }, 0); 109 | } 110 | 111 | public showFilterNavigation(): void { 112 | this.menu.toggleFilterMenu(); 113 | } 114 | 115 | /** Performs search operation when press Enter key */ 116 | public onInputKeyUpSearch(args: KeyboardEventArgs): void { 117 | if (args.keyCode === 13) { 118 | this.grid.search(this.searchInput.value); 119 | } 120 | } 121 | 122 | /** Disables edit toolbar item in the Expense Grid on the initial load */ 123 | public onGridActionComplete(e: any): void { 124 | setTimeout(() => { 125 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_edit').parentElement, false); 126 | }, 0); 127 | } 128 | 129 | /** Prevents the edit operation of Grid, since we handled the custom dialog for the edit operation */ 130 | public onEditBegin(e: any): void { 131 | if (e.requestType === 'beginEdit') { 132 | e.cancel = true; 133 | } 134 | } 135 | 136 | /** Performs search operation in the Expense Grid */ 137 | public onSearchClicked(): void { 138 | this.grid.search(this.searchInput.value); 139 | } 140 | 141 | /** Based on the selected rows from the Grid, updates the visibility of the toolbar items (Edit, Delete) */ 142 | public handleToolbarVisibility(): void { 143 | if (this.grid.getSelectedRowIndexes().length > 1) { 144 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_edit').parentElement, false); 145 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_delete').parentElement, true); 146 | } else if (this.grid.getSelectedRowIndexes().length === 0) { 147 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_edit').parentElement, false); 148 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_delete').parentElement, false); 149 | } else if (this.grid.getSelectedRowIndexes().length === 1) { 150 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_edit').parentElement, true); 151 | this.grid.toolbarModule.toolbar.enableItems(document.getElementById('grid_delete').parentElement, true); 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /src/app/expense/dialogs/dialogs.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
Are you sure you want to delete the selected transaction(s)?
4 |
5 | 6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 | 45 | 46 |
47 |
48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 | 56 |
57 |
58 | 59 |
60 |
61 | 62 |
63 |
64 |
65 |
66 |
67 |
68 |
-------------------------------------------------------------------------------- /src/app/expense/dialogs/dialogs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, ViewChild, OnInit, ElementRef } from '@angular/core'; 2 | 3 | import { DialogComponent } from '@syncfusion/ej2-angular-popups'; 4 | import { isNullOrUndefined as isNOU } from '@syncfusion/ej2-base'; 5 | import { NumericTextBoxComponent } from '@syncfusion/ej2-angular-inputs'; 6 | import { Query, DataManager, Predicate } from '@syncfusion/ej2-data'; 7 | import { DropDownListComponent } from '@syncfusion/ej2-angular-dropdowns'; 8 | import { ChangeEventArgs, RadioButtonComponent } from '@syncfusion/ej2-angular-buttons'; 9 | import { DatePickerComponent, TimePickerComponent } from '@syncfusion/ej2-angular-calendars'; 10 | 11 | import { AppComponent } from '../../app.component'; 12 | import { CardsComponent } from '../../dashboard/cards/cards.component'; 13 | 14 | import { categoryIncomeData, categoryExpenseData } from '../../common/common.data'; 15 | 16 | @Component({ 17 | selector: 'dialog-section', 18 | templateUrl: 'dialogs.component.html', 19 | encapsulation: ViewEncapsulation.None, 20 | providers: [ CardsComponent] 21 | }) 22 | export class DialogsComponent implements OnInit { 23 | @ViewChild('dialog') dialog: DialogComponent; 24 | @ViewChild('confirmDialog') alertDialog: DialogComponent; 25 | @ViewChild('dlgAmount') dlgAmount: NumericTextBoxComponent; 26 | @ViewChild('dlgDropDown') dlgDropDown: DropDownListComponent; 27 | @ViewChild('dlgCashRadio') dlgCashRadio: RadioButtonComponent; 28 | @ViewChild('dlgDatePicker') dlgDatePicker: DatePickerComponent; 29 | @ViewChild('dlgTimePicker') dlgTimePicker: TimePickerComponent; 30 | @ViewChild('dlgDebitRadio') dlgDebitRadio: RadioButtonComponent; 31 | @ViewChild('dlgCreditRadio') dlgCreditRadio: RadioButtonComponent; 32 | @ViewChild('dlgIncomeRadio') dlgIncomeRadio: RadioButtonComponent; 33 | @ViewChild('dlgExpenseRadio') dlgExpenseRadio: RadioButtonComponent; 34 | 35 | public dateValue: Date; 36 | public cntCompObj: any; 37 | public isModal: boolean; 38 | public proxy: any = this; 39 | public predicate: Predicate; 40 | public dlgTarget: HTMLElement; 41 | public dropDownFields: Object; 42 | public predicateEnd: Predicate; 43 | public addDlgButtons: Object[]; 44 | public editDlgButtons: Object[]; 45 | public enableCloseIcon: boolean; 46 | public predicateStart: Predicate; 47 | public animationSettings: Object; 48 | public deleteDlgButtons: Object[]; 49 | public enableCloseOnEscape: boolean; 50 | public categoryDataSource: Object[]; 51 | public description: HTMLInputElement; 52 | 53 | constructor(public app: AppComponent, public eleRef: ElementRef, public cards: CardsComponent) { 54 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', this.app.startDate); 55 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', this.app.endDate); 56 | this.predicate = this.predicateStart.and(this.predicateEnd); 57 | this.isModal = true; 58 | this.dlgTarget = document.body; 59 | this.enableCloseIcon = true; 60 | this.enableCloseOnEscape = false; 61 | this.animationSettings = { effect: 'None' }; 62 | 63 | /** Functionalities for the buttons of "Add Dialog" window */ 64 | this.addDlgButtons = [{ 65 | click: (() => { 66 | let newExpense: IExpenseData = { 67 | 'UniqueId': 'T' + ('' + (+new Date())).substring(5, 10), 68 | 'DateTime': new Date(this.dlgDatePicker.value.setHours(this.dlgTimePicker.value.getHours())), 69 | 'Category': this.dlgDropDown.value, 70 | 'PaymentMode': (this.dlgCashRadio.checked && this.dlgCashRadio.label) || 71 | (this.dlgCreditRadio.checked && this.dlgCreditRadio.label) || 72 | (this.dlgDebitRadio.checked && this.dlgDebitRadio.label), 73 | 'TransactionType': (this.dlgIncomeRadio.checked && this.dlgIncomeRadio.label) || 74 | (this.dlgExpenseRadio.checked && this.dlgExpenseRadio.label), 75 | 'Description': this.description.value, 76 | 'Amount': this.dlgAmount.value 77 | }; 78 | new DataManager(this.app.dataSource).insert(newExpense); 79 | new DataManager(this.app.dataSource).update('UniqueId', { 80 | UniqueId: newExpense.UniqueId, 81 | 'DateTime': (this.dlgDatePicker.value), 82 | 'Category': newExpense.Category, 83 | 'PaymentMode': newExpense.PaymentMode, 84 | 'TransactionType': newExpense.TransactionType, 85 | 'Description': newExpense.Description, 86 | 'Amount': newExpense.Amount 87 | }); 88 | this.cntCompObj.grid.setProperties({ 89 | dataSource: this.app.dataSource, 90 | query: new Query().where(this.predicate).sortByDesc('DateTime') 91 | }); 92 | this.cntCompObj.grid.refresh(); 93 | this.cards.updateCardValues(); 94 | this.dialog.hide(); 95 | }), 96 | buttonModel: { content: 'Add', cssClass: 'e-info e-add', isPrimary: true } 97 | }, { 98 | click: (() => { 99 | this.dialog.hide(); 100 | }), 101 | buttonModel: { cssClass: 'e-outline e-cancel', content: 'Cancel' } 102 | }]; 103 | 104 | /** Functionalities for the buttons of "Delete Dialog" window */ 105 | this.deleteDlgButtons = [{ 106 | click: (() => { 107 | let selectedRecords: Object[] = this.cntCompObj.grid.getSelectedRecords(); 108 | for (let i: number = 0; i < selectedRecords.length; i++) { 109 | new DataManager(this.app.dataSource).remove('UniqueId', selectedRecords[i]); 110 | } 111 | this.cntCompObj.grid.refresh(); 112 | this.cards.updateCardValues(); 113 | this.alertDialog.hide(); 114 | }), buttonModel: { content: 'Yes', cssClass: 'e-ok e-flat', isPrimary: true } 115 | }, { 116 | click: (() => { 117 | this.alertDialog.hide(); 118 | }), buttonModel: { cssClass: 'e-no e-flat', content: 'No' } 119 | }]; 120 | 121 | /** Functionalities for the buttons of "Edit Dialog" window */ 122 | this.editDlgButtons = [{ 123 | click: (() => { 124 | let editRecord: IExpenseData = this.cntCompObj.grid.getSelectedRecords()[0]; 125 | let newExpense: IExpenseData = { 126 | 'UniqueId': editRecord.UniqueId, 127 | 'DateTime': new Date(this.dlgDatePicker.value.setHours(this.dlgTimePicker.value.getHours())), 128 | 'Category': this.dlgDropDown.value, 129 | 'PaymentMode': (this.dlgCashRadio.checked && this.dlgCashRadio.label) || 130 | (this.dlgCreditRadio.checked && this.dlgCreditRadio.label) || 131 | (this.dlgDebitRadio.checked && this.dlgDebitRadio.label), 132 | 'TransactionType': (this.dlgIncomeRadio.checked && this.dlgIncomeRadio.label) || 133 | (this.dlgExpenseRadio.checked && this.dlgExpenseRadio.label), 134 | 'Description': this.description.value, 135 | 'Amount': this.dlgAmount.value 136 | }; 137 | new DataManager(this.app.dataSource).update('UniqueId', newExpense); 138 | this.cntCompObj.grid.refresh(); 139 | this.cards.updateCardValues(); 140 | this.dialog.hide(); 141 | }), 142 | buttonModel: { content: 'Save', cssClass: 'e-info e-add', isPrimary: true } 143 | }, { 144 | click: (() => { 145 | this.dialog.hide(); 146 | }), buttonModel: { cssClass: 'e-outline e-cancel', content: 'Cancel' } 147 | }]; 148 | } 149 | 150 | public ngOnInit(): void { 151 | /** Configurations for the Category selection dropdown */ 152 | this.categoryDataSource = categoryExpenseData; 153 | this.dropDownFields = { text: 'Category', iconCss: 'Class', value: 'Category' }; 154 | } 155 | 156 | public ngAfterViewInit(): void { 157 | this.description = this.eleRef.nativeElement.querySelector('#description'); 158 | } 159 | 160 | /** Shows the "Edit Dialog" window with the corresponding selected row configuration */ 161 | public showEditDialog(): void { 162 | this.dialog.header = 'Edit Transaction'; 163 | this.dialog.buttons = this.editDlgButtons; 164 | this.dialog.dataBind(); 165 | let selectedRecord: IExpenseData = this.cntCompObj.grid.getSelectedRecords()[0]; 166 | if (!isNOU(selectedRecord)) { 167 | if (selectedRecord.TransactionType === 'Income') { 168 | this.dlgIncomeRadio.checked = true; 169 | this.dlgDropDown.dataSource = categoryIncomeData; 170 | } else if (selectedRecord.TransactionType === 'Expense') { 171 | this.dlgExpenseRadio.checked = true; 172 | this.dlgDropDown.dataSource = categoryExpenseData; 173 | } 174 | this.dlgDropDown.refresh(); 175 | this.dlgDatePicker.value = selectedRecord.DateTime; 176 | this.dlgTimePicker.value = selectedRecord.DateTime; 177 | if (selectedRecord.PaymentMode === 'Credit Card') { 178 | this.dlgCreditRadio.checked = true; 179 | } else if (selectedRecord.PaymentMode === 'Debit Card') { 180 | this.dlgDebitRadio.checked = true; 181 | } else if (selectedRecord.PaymentMode === 'Cash') { 182 | this.dlgCashRadio.checked = true; 183 | } 184 | this.description.value = selectedRecord.Description; 185 | this.dlgDropDown.text = selectedRecord.Category; 186 | this.dlgAmount.value = selectedRecord.Amount; 187 | this.dialog.show(); 188 | } 189 | this.dialog.show(); 190 | } 191 | 192 | public showAlertDialog(): void { 193 | this.alertDialog.buttons = this.deleteDlgButtons; 194 | this.alertDialog.dataBind(); 195 | this.alertDialog.show(); 196 | } 197 | 198 | public alertDialogOpen(): void { 199 | this.proxy.alertDialog.dlgContainer.style.zIndex = '1000000'; 200 | } 201 | 202 | /** Shows the "Add Dialog" window with the default configuration */ 203 | public showAddDialog(): void { 204 | this.dialog.header = 'New Transaction'; 205 | this.dialog.buttons = this.addDlgButtons; 206 | this.dialog.dataBind(); 207 | this.dlgAmount.value = 0; 208 | this.description.value = ''; 209 | this.dlgExpenseRadio.checked = true; 210 | this.dlgDropDown.dataSource = categoryExpenseData; 211 | this.dlgDropDown.dataBind(); 212 | this.dlgCashRadio.checked = true; 213 | this.dialog.show(); 214 | } 215 | 216 | /** Toggles the body scroll when the Dialog opens and close */ 217 | public dialogOpen(): void { 218 | this.proxy.dialog.dlgContainer.style.zIndex = '1000000'; 219 | document.body.style.overflowY = 'hidden'; 220 | } 221 | public dlgClose(): void { 222 | document.body.style.overflowY = 'auto'; 223 | } 224 | 225 | public dlgOverlayClicked(): void { 226 | this.proxy.alertDialog.hide(); 227 | this.proxy.dialog.hide(); 228 | } 229 | 230 | /** Update of the dropdown datasource based on the "Income" and "Expense" type */ 231 | public dlgTransactTypeChanged(args: ChangeEventArgs): void { 232 | let transactValue: any = (args.event.target).value; 233 | if (transactValue === 'Expense') { 234 | this.dlgDropDown.dataSource = categoryExpenseData; 235 | } else { 236 | this.dlgDropDown.dataSource = categoryIncomeData; 237 | } 238 | this.dlgDropDown.dataBind(); 239 | } 240 | } 241 | 242 | export interface IExpenseData { 243 | Amount: number; 244 | Category: string; 245 | DateTime: Date; 246 | Description: string; 247 | PaymentMode: string; 248 | TransactionType: string; 249 | UniqueId: string; 250 | } -------------------------------------------------------------------------------- /src/app/expense/expense.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
-------------------------------------------------------------------------------- /src/app/expense/expense.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, ViewChild, OnInit, HostListener } from '@angular/core'; 2 | 3 | import { Browser } from '@syncfusion/ej2-base'; 4 | 5 | import { MenuComponent } from '../menu/menu.component'; 6 | import { FilterComponent } from './filter/filter.component'; 7 | import { DialogsComponent } from './dialogs/dialogs.component'; 8 | import { ContentComponent } from './content/content.component'; 9 | import { CardsComponent } from '../dashboard/cards/cards.component'; 10 | 11 | import { CommonService } from '../common/common.service'; 12 | 13 | @Component({ 14 | templateUrl: 'expense.component.html', 15 | encapsulation: ViewEncapsulation.None, 16 | providers: [CommonService, CardsComponent] 17 | }) 18 | export class ExpenseComponent implements OnInit { 19 | @ViewChild('filterSection') filterObj: FilterComponent; 20 | @ViewChild('dialogSection') dialogObj: DialogsComponent; 21 | @ViewChild('contentSection') contentObj: ContentComponent; 22 | 23 | constructor( 24 | public common: CommonService, 25 | public cards: CardsComponent, 26 | public menu: MenuComponent 27 | ) { 28 | this.common.removeRootClass(); 29 | this.common.addRootClass('expense-page'); 30 | } 31 | 32 | public ngOnInit(): void { 33 | /** On initial load, update the sidebar selections and overlay */ 34 | this.menu.removeToggleClass(); 35 | this.menu.disableOverlay(); 36 | this.cards.updateCardValues(); 37 | } 38 | 39 | public ngAfterViewInit(): void { 40 | this.contentObj.filterCompObj = this.filterObj; 41 | this.filterObj.cntCompObj = this.contentObj; 42 | this.contentObj.dlgCompObj = this.dialogObj; 43 | this.dialogObj.cntCompObj = this.contentObj; 44 | } 45 | } -------------------------------------------------------------------------------- /src/app/expense/filter/filter.component.html: -------------------------------------------------------------------------------- 1 |
2 | 44 |
-------------------------------------------------------------------------------- /src/app/expense/filter/filter.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, ViewChild, OnInit, ChangeDetectorRef } from '@angular/core'; 2 | 3 | import { Query, Predicate } from '@syncfusion/ej2-data'; 4 | import { isNullOrUndefined as isNOU } from '@syncfusion/ej2-base'; 5 | import { CheckBoxComponent, ChangeEventArgs } from '@syncfusion/ej2-angular-buttons'; 6 | import { DateRangePickerComponent, RangeEventArgs } from '@syncfusion/ej2-angular-calendars'; 7 | import { MultiSelectComponent, SelectEventArgs, RemoveEventArgs } from '@syncfusion/ej2-angular-dropdowns'; 8 | 9 | import { AppComponent } from '../../app.component'; 10 | 11 | import { FilterService } from './filter.service'; 12 | 13 | @Component({ 14 | selector: 'filter-section', 15 | templateUrl: 'filter.component.html', 16 | encapsulation: ViewEncapsulation.None, 17 | providers: [FilterService, AppComponent] 18 | }) 19 | export class FilterComponent implements OnInit { 20 | @ViewChild('filterCash') cashFilter: CheckBoxComponent; 21 | @ViewChild('filterDebit') debitFilter: CheckBoxComponent; 22 | @ViewChild('filterCredit') creditFilter: CheckBoxComponent; 23 | @ViewChild('filterIncome') incomeFilter: CheckBoxComponent; 24 | @ViewChild('filterExpense') expenseFilter: CheckBoxComponent; 25 | @ViewChild('filterMinAmount') minAmtFilter: CheckBoxComponent; 26 | @ViewChild('filterMaxAmount') maxAmtFilter: CheckBoxComponent; 27 | @ViewChild('filterMultiSelect') multiSelectFilter: MultiSelectComponent; 28 | @ViewChild('filterDateRange') dateRangeFilter: DateRangePickerComponent; 29 | 30 | public cntCompObj: any; 31 | public minValue: number; 32 | public maxValue: number; 33 | public predicate: Predicate; 34 | public predicateEnd: Predicate; 35 | public tempData: IExpenseData[]; 36 | public filterCategory: string[]; 37 | public cashPredicate: Predicate; 38 | public predicateStart: Predicate; 39 | public debitPredicate: Predicate; 40 | public minAmtPredicate: Predicate; 41 | public maxAmtPredicate: Predicate; 42 | public incomePredicate: Predicate; 43 | public creditPredicate: Predicate; 44 | public expensePredicate: Predicate; 45 | public categoryPredicate: Predicate; 46 | public categoryPredicates: Predicate; 47 | 48 | constructor( 49 | public app: AppComponent, 50 | public filter: FilterService, 51 | private chgRef: ChangeDetectorRef 52 | ) { 53 | this.filterCategory = []; 54 | } 55 | 56 | public ngOnInit(): void { 57 | this.minValue = 0; 58 | this.maxValue = 0; 59 | this.tempData = this.app.dataSource; 60 | this.getCategory(this.app.startDate, this.app.endDate); 61 | } 62 | 63 | /** Gets the available category in-between the start and end date, for category filter dropdown */ 64 | public getCategory(start: Date, end: Date): void { 65 | this.filterCategory = []; 66 | this.tempData.forEach((item: any) => { 67 | if (start.valueOf() <= item.DateTime.valueOf() && end.valueOf() >= item.DateTime.valueOf()) { 68 | if (this.filterCategory.indexOf(item.Category) < 0) { 69 | this.filterCategory.push(item.Category); 70 | } 71 | } 72 | }); 73 | } 74 | 75 | public numericTextBoxCreated(): void { 76 | let val: IMinMax = this.filter.minMaxAmount(this.dateRangeFilter.startDate, this.dateRangeFilter.endDate); 77 | this.minValue = val.minValue; 78 | this.maxValue = val.maxValue; 79 | this.chgRef.detectChanges(); 80 | } 81 | 82 | /** Updates the Grid datasource based on the modified date range values */ 83 | public dateRangeChanged(args: RangeEventArgs): void { 84 | this.app.startDate = args.startDate; 85 | this.app.endDate = args.endDate; 86 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', this.app.startDate); 87 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', this.app.endDate); 88 | this.predicate = this.predicateStart.and(this.predicateEnd); 89 | this.updateGrid(this.app.startDate, this.app.endDate, 'dateChange'); 90 | } 91 | 92 | /** Filters the datasource based on amount */ 93 | public amountChanged(): void { 94 | this.updateGrid(this.dateRangeFilter.startDate, this.dateRangeFilter.endDate, ''); 95 | } 96 | 97 | /** Filters the datasource based on Cashflow and Payment modes */ 98 | public checkBoxStateChanged(args: ChangeEventArgs): void { 99 | this.updateGrid(this.dateRangeFilter.startDate, this.dateRangeFilter.endDate, ''); 100 | } 101 | 102 | /** Filters the datasource when category selected or deselected, from Multiselect component */ 103 | public categorySelected(args: SelectEventArgs): void { 104 | setTimeout(() => { 105 | this.updateGrid(this.dateRangeFilter.startDate, this.dateRangeFilter.endDate, ''); 106 | }, 10); 107 | } 108 | public categoryRemoved(args: RemoveEventArgs): void { 109 | this.updateGrid(this.dateRangeFilter.startDate, this.dateRangeFilter.endDate, ''); 110 | } 111 | 112 | /** Updates the Grid based on the filtered data source */ 113 | public updateGrid(start: Date, end: Date, updater: string): void { 114 | if (end instanceof Date) { 115 | end.setHours(23); 116 | end.setMinutes(59); 117 | } 118 | this.predicateStart = new Predicate('DateTime', 'greaterthanorequal', start); 119 | this.predicateEnd = new Predicate('DateTime', 'lessthanorequal', end); 120 | this.predicate = this.predicateStart.and(this.predicateEnd); 121 | let val: IMinMax = this.filter.minMaxAmount(start, end); 122 | this.minValue = val.minValue; 123 | this.maxValue = val.maxValue; 124 | this.minAmtPredicate = new Predicate('Amount', 'greaterthanorequal', this.minAmtFilter.value); 125 | this.maxAmtPredicate = new Predicate('Amount', 'lessthanorequal', this.maxAmtFilter.value); 126 | this.predicate = this.predicate.and(this.minAmtPredicate).and(this.maxAmtPredicate); 127 | if (this.incomeFilter.checked || this.expenseFilter.checked) { 128 | if (this.incomeFilter.checked) { 129 | this.incomePredicate = new Predicate('TransactionType', 'equal', 'Income'); 130 | } 131 | if (this.expenseFilter.checked) { 132 | this.expensePredicate = new Predicate('TransactionType', 'equal', 'Expense'); 133 | } 134 | if (this.expenseFilter.checked && this.incomeFilter.checked) { 135 | this.incomePredicate = this.incomePredicate.or(this.expensePredicate); 136 | this.predicate = this.predicate.and(this.incomePredicate); 137 | } else if (this.incomeFilter.checked) { 138 | this.predicate = this.predicate.and(this.incomePredicate); 139 | } else if (this.expenseFilter.checked) { 140 | this.predicate = this.predicate.and(this.expensePredicate); 141 | } 142 | } 143 | if (this.cashFilter.checked || this.debitFilter.checked || this.creditFilter.checked) { 144 | if (this.cashFilter.checked) { 145 | this.cashPredicate = new Predicate('PaymentMode', 'equal', 'Cash'); 146 | } 147 | if (this.creditFilter.checked) { 148 | this.creditPredicate = new Predicate('PaymentMode', 'equal', 'Credit Card'); 149 | } 150 | if (this.debitFilter.checked) { 151 | this.debitPredicate = new Predicate('PaymentMode', 'equal', 'Debit Card'); 152 | } 153 | if (this.cashFilter.checked && this.creditFilter.checked && this.debitFilter.checked) { 154 | this.incomePredicate = this.creditPredicate.or(this.debitPredicate).or(this.cashPredicate); 155 | this.predicate = this.predicate.and(this.incomePredicate); 156 | } else if (this.cashFilter.checked && this.creditFilter.checked) { 157 | this.incomePredicate = this.cashPredicate.or(this.creditPredicate); 158 | this.predicate = this.predicate.and(this.incomePredicate); 159 | } else if (this.cashFilter.checked && this.debitFilter.checked) { 160 | this.incomePredicate = this.cashPredicate.or(this.debitPredicate); 161 | this.predicate = this.predicate.and(this.incomePredicate); 162 | } else if (this.creditFilter.checked && this.debitFilter.checked) { 163 | this.incomePredicate = this.creditPredicate.or(this.debitPredicate); 164 | this.predicate = this.predicate.and(this.incomePredicate); 165 | } else if (this.cashFilter.checked) { 166 | this.predicate = this.predicate.and(this.cashPredicate); 167 | } else if (this.debitFilter.checked) { 168 | this.predicate = this.predicate.and(this.debitPredicate); 169 | } else if (this.creditFilter.checked) { 170 | this.predicate = this.predicate.and(this.creditPredicate); 171 | } 172 | } 173 | if (!isNOU(this.multiSelectFilter.value) && this.multiSelectFilter.value.length > 0) { 174 | let list: string[] = this.multiSelectFilter.value; 175 | for (let i: number = 0; i < list.length; i++) { 176 | this.categoryPredicate = new Predicate('Category', 'equal', list[i]); 177 | if (i === 0) { 178 | this.categoryPredicates = this.categoryPredicate; 179 | } else { 180 | this.categoryPredicates = this.categoryPredicates.or(this.categoryPredicate); 181 | } 182 | } 183 | this.predicate = this.predicate.and(this.categoryPredicates); 184 | } 185 | this.cntCompObj.grid.setProperties({ 186 | dataSource: this.app.dataSource, 187 | query: new Query().where(this.predicate).sortByDesc('DateTime') 188 | }); 189 | this.cntCompObj.grid.refresh(); 190 | this.getCategory(start, end); 191 | this.multiSelectFilter.dataSource = this.filterCategory; 192 | this.multiSelectFilter.dataBind(); 193 | } 194 | } 195 | 196 | export interface IMinMax { 197 | minValue: number; 198 | maxValue: number; 199 | } 200 | 201 | export interface IExpenseData { 202 | Amount: number; 203 | Category: string; 204 | DateTime: Date; 205 | Description: string; 206 | PaymentMode: string; 207 | TransactionType: string; 208 | UniqueId: string; 209 | } -------------------------------------------------------------------------------- /src/app/expense/filter/filter.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, ViewChild } from '@angular/core'; 2 | 3 | import { Query, DataManager, Predicate } from '@syncfusion/ej2-data'; 4 | 5 | import { AppComponent } from '../../app.component'; 6 | 7 | @Injectable() 8 | export class FilterService { 9 | public predicate: Predicate; 10 | public predicateEnd: Predicate; 11 | public predicateStart: Predicate; 12 | 13 | constructor(public app: AppComponent) {} 14 | 15 | /** Gets the minimum and maximum amount from the datasource */ 16 | public minMaxAmount(start: Date, end: Date): Object { 17 | let predicateStart: Predicate = new Predicate('DateTime', 'greaterthanorequal', start); 18 | let predicateEnd: Predicate = new Predicate('DateTime', 'lessthanorequal', end); 19 | let minAmount: any = new DataManager(this.app.dataSource).executeLocal((new Query() 20 | .where((predicateStart.and(predicateEnd)))) 21 | .requiresCount().aggregate('min', 'Amount')); 22 | let maxAmount: any = new DataManager(this.app.dataSource).executeLocal((new Query() 23 | .where((predicateStart.and(predicateEnd)))) 24 | .requiresCount().aggregate('max', 'Amount')); 25 | return { 26 | minValue: minAmount.aggregates['Amount - min'], 27 | maxValue: maxAmount.aggregates['Amount - max'] 28 | }; 29 | } 30 | } -------------------------------------------------------------------------------- /src/app/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowser } from '@angular/platform-browser'; 2 | import { AppModuleNgFactory } from './app.module.ngfactory'; 3 | platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); -------------------------------------------------------------------------------- /src/app/menu/menu.component.html: -------------------------------------------------------------------------------- 1 | 10 | 36 |
37 |
38 | 39 |
40 |
41 |
-------------------------------------------------------------------------------- /src/app/menu/menu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Directive, ElementRef } from '@angular/core'; 2 | 3 | import { Browser, rippleEffect, isNullOrUndefined as isNOU, enableRipple } from '@syncfusion/ej2-base'; 4 | 5 | import { userInfo } from '../common/common.data'; 6 | enableRipple(true); 7 | 8 | @Component({ 9 | selector: 'ng-app', 10 | templateUrl: 'menu.component.html' 11 | }) 12 | 13 | export class MenuComponent { 14 | public menu: HTMLElement; 15 | public userName: string; 16 | public filterMenu: HTMLElement; 17 | public overlay: HTMLElement; 18 | 19 | constructor(public eleRef: ElementRef) { 20 | /** Loads the user data in the profile from the sidebar */ 21 | this.userName = userInfo.FullName; 22 | rippleEffect(document.body, { selector: '.ripple-element', rippleFlag: true }); 23 | } 24 | 25 | public ngAfterViewInit(): void { 26 | /** Holds the sidebar elements for later use */ 27 | this.menu = this.eleRef.nativeElement.querySelector('#sidebar-wrapper'); 28 | this.overlay = this.eleRef.nativeElement.querySelector('#overlay'); 29 | } 30 | 31 | /** Toggles the sidebar open and close actions - for small resoultion */ 32 | public toggleMenu(): void { 33 | if (this.menu.classList.contains('open')) { 34 | this.removeToggleClass(); 35 | this.menu.classList.add('close'); 36 | this.disableOverlay(); 37 | } else if (this.menu.classList.contains('close')) { 38 | this.removeToggleClass(); 39 | this.menu.classList.add('open'); 40 | this.enableOverlay(); 41 | } else { 42 | this.menu.classList.add('open'); 43 | this.enableOverlay(); 44 | } 45 | } 46 | 47 | public removeToggleClass(): void { 48 | this.menu.classList.remove('open'); 49 | this.menu.classList.remove('close'); 50 | } 51 | 52 | public enableOverlay(): void { 53 | this.overlay.classList.add('dialog'); 54 | this.overlay.style.background = '#383838'; 55 | } 56 | 57 | public disableOverlay(): void { 58 | this.overlay.classList.remove('dialog'); 59 | this.overlay.style.background = 'none'; 60 | } 61 | 62 | public handleOverlay(): void { 63 | this.disableOverlay(); 64 | this.removeToggleClass(); 65 | this.removeFilterToggleClass(); 66 | } 67 | 68 | public removeFilterToggleClass(): void { 69 | this.menu.style.zIndex = '100001'; 70 | this.filterMenu = this.eleRef.nativeElement.querySelector('.sidebar-wrapper-filter'); 71 | if (!isNOU(this.filterMenu)) { 72 | this.filterMenu.classList.remove('filter-open'); 73 | this.filterMenu.classList.remove('filter-close'); 74 | } 75 | } 76 | 77 | /** Toggles the filter bar open and close actions */ 78 | public toggleFilterMenu(): void { 79 | this.menu.style.zIndex = '10000'; 80 | this.filterMenu = this.eleRef.nativeElement.querySelector('.sidebar-wrapper-filter'); 81 | if (this.filterMenu.classList.contains('filter-open')) { 82 | this.filterMenu.classList.remove('filter-open'); 83 | this.filterMenu.classList.add('filter-close'); 84 | this.disableOverlay(); 85 | } else if (this.filterMenu.classList.contains('filter-close')) { 86 | this.filterMenu.classList.remove('filter-close'); 87 | this.filterMenu.classList.add('filter-open'); 88 | this.enableOverlay(); 89 | } else { 90 | this.filterMenu.classList.add('filter-open'); 91 | this.enableOverlay(); 92 | } 93 | } 94 | 95 | public onNavigationClick(args: MouseEvent): void { 96 | if ((args.target as HTMLElement).nodeName === 'A') { 97 | this.handleOverlay(); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/assets/definition/material.scss: -------------------------------------------------------------------------------- 1 | $accent: #ff4081; 2 | $accent-font: #fff; 3 | 4 | $primary: #3f51b5; 5 | $primary-50: lighten($primary, 38%); 6 | $primary-100: lighten($primary, 31%); 7 | $primary-200: lighten($primary, 19%); 8 | $primary-300: lighten($primary, 8%); 9 | $primary-font: #fff; 10 | $primary-50-font: #000; 11 | $primary-100-font: #000; 12 | $primary-200-font: #000; 13 | $primary-300-font: #fff; 14 | $grey-white: #fff; 15 | $grey-black: #000; 16 | $grey-50: #fafafa; 17 | $grey-100: #f5f5f5; 18 | $grey-200: #eee; 19 | $grey-300: #e0e0e0; 20 | $grey-400: #bdbdbd; 21 | $grey-500: #9e9e9e; 22 | $grey-600: #757575; 23 | $grey-700: #616161; 24 | $grey-800: #424242; 25 | $grey-900: #212121; 26 | $grey-dark: #303030; 27 | $grey-light-font: #000; 28 | $grey-dark-font: #fff; 29 | $base-font: #000; 30 | $error-font: #f44336; 31 | -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/About.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 03 About 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/images/AvailableCash.svg: -------------------------------------------------------------------------------- 1 | Available Cash -------------------------------------------------------------------------------- /src/assets/images/Home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 01 Home 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/images/OverallTransactions.svg: -------------------------------------------------------------------------------- 1 | Overall Transactions -------------------------------------------------------------------------------- /src/assets/images/Profile-img-Desktop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/Profile-img-Desktop -------------------------------------------------------------------------------- /src/assets/images/Profile-img-Mobile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/Profile-img-Mobile -------------------------------------------------------------------------------- /src/assets/images/Search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | Search 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/images/TotalExpenses.svg: -------------------------------------------------------------------------------- 1 | Total Expenses -------------------------------------------------------------------------------- /src/assets/images/TotalIncome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | Total Income_1 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/images/Transactions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Overall Transactions_1 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/assets/images/balance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | Available Cash_1 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/assets/images/cash-wallet.svg: -------------------------------------------------------------------------------- 1 | 2 | Wallet 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/images/category/bills.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/bills.png -------------------------------------------------------------------------------- /src/assets/images/category/business.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/business.png -------------------------------------------------------------------------------- /src/assets/images/category/clothing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/clothing.png -------------------------------------------------------------------------------- /src/assets/images/category/education.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/education.png -------------------------------------------------------------------------------- /src/assets/images/category/entertainment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/entertainment.png -------------------------------------------------------------------------------- /src/assets/images/category/extra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/extra.png -------------------------------------------------------------------------------- /src/assets/images/category/food.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/food.png -------------------------------------------------------------------------------- /src/assets/images/category/health.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/health.png -------------------------------------------------------------------------------- /src/assets/images/category/house.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/house.png -------------------------------------------------------------------------------- /src/assets/images/category/insurance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/insurance.png -------------------------------------------------------------------------------- /src/assets/images/category/interest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/interest.png -------------------------------------------------------------------------------- /src/assets/images/category/miscellaneous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/miscellaneous.png -------------------------------------------------------------------------------- /src/assets/images/category/personal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/personal.png -------------------------------------------------------------------------------- /src/assets/images/category/rent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/rent.png -------------------------------------------------------------------------------- /src/assets/images/category/salary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/salary.png -------------------------------------------------------------------------------- /src/assets/images/category/shopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/shopping.png -------------------------------------------------------------------------------- /src/assets/images/category/tax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/tax.png -------------------------------------------------------------------------------- /src/assets/images/category/transport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/transport.png -------------------------------------------------------------------------------- /src/assets/images/category/utilities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/category/utilities.png -------------------------------------------------------------------------------- /src/assets/images/exp-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/exp-track.png -------------------------------------------------------------------------------- /src/assets/images/expense.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Total Expenses_1 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/assets/images/fonts/Expense-Analysis-Sample.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/Expense-Analysis-Sample.eot -------------------------------------------------------------------------------- /src/assets/images/fonts/Expense-Analysis-Sample.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/images/fonts/Expense-Analysis-Sample.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/Expense-Analysis-Sample.ttf -------------------------------------------------------------------------------- /src/assets/images/fonts/Expense-Analysis-Sample.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/Expense-Analysis-Sample.woff -------------------------------------------------------------------------------- /src/assets/images/fonts/controls.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/controls.eot -------------------------------------------------------------------------------- /src/assets/images/fonts/controls.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/assets/images/fonts/controls.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/controls.ttf -------------------------------------------------------------------------------- /src/assets/images/fonts/controls.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/controls.woff -------------------------------------------------------------------------------- /src/assets/images/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/icons.eot -------------------------------------------------------------------------------- /src/assets/images/fonts/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/images/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/icons.ttf -------------------------------------------------------------------------------- /src/assets/images/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/fonts/icons.woff -------------------------------------------------------------------------------- /src/assets/images/i.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | Total Income_1 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/images/income.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Total Income_1 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/assets/images/no-records.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-expensetracker/e83ec6b422b78b4af7141fee40b0105f57443e77/src/assets/images/no-records.png -------------------------------------------------------------------------------- /src/assets/images/t.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | Overall Transactions_1 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/images/title.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 14 | 15 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/assets/images/transaction.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 02 Overall Transactions 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/styles.scss: -------------------------------------------------------------------------------- 1 | //sass-lint:disable-all 2 | @import 'definition/material.scss'; 3 | @import 'ej2-base/styles/material-definition.scss'; 4 | @import 'ej2-base/styles/all.scss'; 5 | @import 'ej2-base/styles/definition/_material.scss'; 6 | @import 'ej2-icons/styles/material.scss'; 7 | $accent: #4273F9; 8 | $accent-font: #fff; 9 | @import 'ej2-buttons/styles/button/material-definition.scss'; 10 | @import 'ej2-buttons/styles/button/all.scss'; 11 | @import 'ej2-inputs/styles/input/material-definition.scss'; 12 | @import 'ej2-inputs/styles/input/icons/material.scss'; 13 | @import 'ej2-inputs/styles/input/all.scss'; 14 | @import 'ej2-inputs/styles/numerictextbox/material-definition.scss'; 15 | @import 'ej2-inputs/styles/numerictextbox/icons/material.scss'; 16 | @import 'ej2-inputs/styles/numerictextbox/all.scss'; 17 | @import 'ej2-calendars/styles/calendar/material-definition.scss'; 18 | @import 'ej2-calendars/styles/calendar/icons/material.scss'; 19 | @import 'ej2-calendars/styles/calendar/all.scss'; 20 | @import 'ej2-buttons/styles/check-box/material-definition.scss'; 21 | @import 'ej2-buttons/styles/check-box/icons/material.scss'; 22 | @import 'ej2-buttons/styles/check-box/all.scss'; 23 | @import 'ej2-navigations/styles/context-menu/material-definition.scss'; 24 | @import 'ej2-navigations/styles/context-menu/icons/material.scss'; 25 | @import 'ej2-navigations/styles/context-menu/all.scss'; 26 | @import 'ej2-calendars/styles/datepicker/material-definition.scss'; 27 | @import 'ej2-calendars/styles/datepicker/icons/material.scss'; 28 | @import 'ej2-calendars/styles/datepicker/all.scss'; 29 | @import 'ej2-calendars/styles/daterangepicker/material-definition.scss'; 30 | @import 'ej2-calendars/styles/daterangepicker/icons/material.scss'; 31 | @import 'ej2-calendars/styles/daterangepicker/all.scss'; 32 | @import 'ej2-popups/styles/dialog/material-definition.scss'; 33 | @import 'ej2-popups/styles/dialog/icons/material.scss'; 34 | @import 'ej2-popups/styles/dialog/all.scss'; 35 | @import 'ej2-dropdowns/styles/drop-down-base/material-definition.scss'; 36 | @import 'ej2-dropdowns/styles/drop-down-base/all.scss'; 37 | @import 'ej2-dropdowns/styles/drop-down-list/material-definition.scss'; 38 | @import 'ej2-dropdowns/styles/drop-down-list/icons/material.scss'; 39 | @import 'ej2-dropdowns/styles/drop-down-list/all.scss'; 40 | @import 'ej2-navigations/styles/h-scroll/material-definition.scss'; 41 | @import 'ej2-navigations/styles/h-scroll/icons/material.scss'; 42 | @import 'ej2-navigations/styles/h-scroll/all.scss'; 43 | @import 'ej2-lists/styles/list-view/material-definition.scss'; 44 | @import 'ej2-lists/styles/list-view/icons/material.scss'; 45 | @import 'ej2-lists/styles/list-view/all.scss'; 46 | @import 'ej2-dropdowns/styles/multi-select/material-definition.scss'; 47 | @import 'ej2-dropdowns/styles/multi-select/icons/material.scss'; 48 | @import 'ej2-dropdowns/styles/multi-select/all.scss'; 49 | @import 'ej2-popups/styles/popup/all.scss'; 50 | @import 'ej2-buttons/styles/radio-button/material-definition.scss'; 51 | @import 'ej2-buttons/styles/radio-button/all.scss'; 52 | @import 'ej2-popups/styles/spinner/material-definition.scss'; 53 | @import 'ej2-popups/styles/spinner/all.scss'; 54 | @import 'ej2-calendars/styles/timepicker/material-definition.scss'; 55 | @import 'ej2-calendars/styles/timepicker/icons/material.scss'; 56 | @import 'ej2-calendars/styles/timepicker/all.scss'; 57 | @import 'ej2-navigations/styles/toolbar/material-definition.scss'; 58 | @import 'ej2-navigations/styles/toolbar/icons/material.scss'; 59 | @import 'ej2-navigations/styles/toolbar/all.scss'; 60 | @import 'ej2-popups/styles/tooltip/material-definition.scss'; 61 | @import 'ej2-popups/styles/tooltip/icons/material.scss'; 62 | @import 'ej2-popups/styles/tooltip/all.scss'; 63 | @import 'ej2-grids/styles/grid/icons/material.scss'; 64 | @import 'ej2-grids/styles/grid/material-definition.scss'; 65 | @import 'ej2-grids/styles/grid/all.scss'; 66 | @import 'ej2-grids/styles/pager/material-definition.scss'; 67 | @import 'ej2-grids/styles/pager/icons/material.scss'; 68 | @import 'ej2-grids/styles/pager/all.scss'; 69 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | Essential JS 2 for Angular - Expense Tracker 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | //import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @import "./assets/styles.css"; 2 | @import "./assets/index.css"; 3 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.base.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "module": "es6", 15 | "lib": [ 16 | "es2015", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": false, 11 | "experimentalDecorators": true, 12 | "target": "es2015", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.base.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-return-shorthand": true, 12 | "curly": true, 13 | "deprecation": { 14 | "severity": "warning" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "directive-class-suffix": true, 19 | "directive-selector": [ 20 | true, 21 | "attribute", 22 | "app", 23 | "camelCase" 24 | ], 25 | "component-selector": [ 26 | true, 27 | "element", 28 | "app", 29 | "kebab-case" 30 | ], 31 | "eofline": true, 32 | "import-blacklist": [ 33 | true, 34 | "rxjs/Rx" 35 | ], 36 | "import-spacing": true, 37 | "indent": { 38 | "options": [ 39 | "spaces" 40 | ] 41 | }, 42 | "max-classes-per-file": false, 43 | "max-line-length": [ 44 | true, 45 | 140 46 | ], 47 | "member-ordering": [ 48 | true, 49 | { 50 | "order": [ 51 | "static-field", 52 | "instance-field", 53 | "static-method", 54 | "instance-method" 55 | ] 56 | } 57 | ], 58 | "no-console": [ 59 | true, 60 | "debug", 61 | "info", 62 | "time", 63 | "timeEnd", 64 | "trace" 65 | ], 66 | "no-empty": false, 67 | "no-inferrable-types": [ 68 | true, 69 | "ignore-params" 70 | ], 71 | "no-non-null-assertion": true, 72 | "no-redundant-jsdoc": true, 73 | "no-switch-case-fall-through": true, 74 | "no-var-requires": false, 75 | "object-literal-key-quotes": [ 76 | true, 77 | "as-needed" 78 | ], 79 | "quotemark": [ 80 | true, 81 | "single" 82 | ], 83 | "semicolon": { 84 | "options": [ 85 | "always" 86 | ] 87 | }, 88 | "space-before-function-paren": { 89 | "options": { 90 | "anonymous": "never", 91 | "asyncArrow": "always", 92 | "constructor": "never", 93 | "method": "never", 94 | "named": "never" 95 | } 96 | }, 97 | "typedef": [ 98 | true, 99 | "call-signature" 100 | ], 101 | "typedef-whitespace": { 102 | "options": [ 103 | { 104 | "call-signature": "nospace", 105 | "index-signature": "nospace", 106 | "parameter": "nospace", 107 | "property-declaration": "nospace", 108 | "variable-declaration": "nospace" 109 | }, 110 | { 111 | "call-signature": "onespace", 112 | "index-signature": "onespace", 113 | "parameter": "onespace", 114 | "property-declaration": "onespace", 115 | "variable-declaration": "onespace" 116 | } 117 | ] 118 | }, 119 | "variable-name": { 120 | "options": [ 121 | "ban-keywords", 122 | "check-format", 123 | "allow-pascal-case" 124 | ] 125 | }, 126 | "whitespace": { 127 | "options": [ 128 | "check-branch", 129 | "check-decl", 130 | "check-operator", 131 | "check-separator", 132 | "check-type", 133 | "check-typecast" 134 | ] 135 | }, 136 | "no-conflicting-lifecycle": true, 137 | "no-host-metadata-property": true, 138 | "no-input-rename": true, 139 | "no-inputs-metadata-property": true, 140 | "no-output-native": true, 141 | "no-output-on-prefix": true, 142 | "no-output-rename": true, 143 | "no-outputs-metadata-property": true, 144 | "template-banana-in-box": true, 145 | "template-no-negated-async": true, 146 | "use-lifecycle-interface": true, 147 | "use-pipe-transform-interface": true 148 | }, 149 | "rulesDirectory": [ 150 | "codelyzer" 151 | ] 152 | } --------------------------------------------------------------------------------