Added the sample into the repository

This commit is contained in:
ROOT\soundarapandianr 2018-03-09 19:21:51 +05:30
Коммит af5eb5ef8f
34 изменённых файлов: 2369 добавлений и 0 удалений

60
.angular-cli.json Normal file
Просмотреть файл

@ -0,0 +1,60 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "ej2-ng-loan-calculator"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}

15
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,15 @@
dist/
*d.ts
*.map
.vscode/
src/**/*.js
*.ngstyle.ts
src/**/*.map
node_modules/
npm-debug.log
*ngfactory.ts
*metadata.json
*ngsummary.json
src/assets/index.css
src/assets/*.css
*.css

27
README.md Normal file
Просмотреть файл

@ -0,0 +1,27 @@
# Essential JS 2 for Angular - Loan Calculator
This Loan calculator demo application showcases the usage of several Essential JS 2 components in a real world application scenario. You can use this application to Calculates your loan payment based on your loan amount, interest and term.
## Deployment
### Install
To install all dependent packages, use the below command
```
npm install
```
### Run
To run the sample, use the below command
```
ng serve
```
## Demo
#### <a href="https://ej2.syncfusion.com/showcase/angular/loancalculator/" target="_blank">https://ej2.syncfusion.com/showcase/angular/loancalculator/</a>
Check all the showcase samples from <a href="https://ej2.syncfusion.com/home/angular.html" target="_blank">here</a>.

10
license Normal file
Просмотреть файл

@ -0,0 +1,10 @@
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.
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 Syncfusions terms and conditions.
Customers who do not qualify for the community license can contact sales@syncfusion.com for commercial licensing options.
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 Syncfusions license containing all terms and conditions.
The Syncfusion license that contains the terms and conditions can be found at
https://www.syncfusion.com/content/downloads/syncfusion_license.pdf

73
package.json Normal file
Просмотреть файл

@ -0,0 +1,73 @@
{
"name": "@syncfusion/ej2-ng-loan-calculator",
"version": "0.0.1",
"description": "Essential JS 2 for Angular - Loan Calculator application",
"author": "Syncfusion Inc.",
"license": "SEE LICENSE IN license",
"dependencies": {
"rxjs": "^5.0.1",
"tslint": "4.0.2",
"zone.js": "^0.8.4",
"core-js": "^2.4.1",
"systemjs": "^0.19.40",
"gulp-print": "^2.0.1",
"gulp-tslint": "^7.0.1",
"browser-sync": "2.11.2",
"gulp-sass-lint": "^1.1.1",
"reflect-metadata": "^0.1.9",
"tslint-microsoft-contrib": "^4.0.0",
"@angular/core": "~5.0.0",
"@angular/http": "~5.0.0",
"@angular/forms": "~5.0.0",
"@angular/router": "^5.0.0",
"@angular/common": "~5.0.0",
"@angular-devkit/core": "^0.4.5",
"@angular/upgrade": "^5.0.0",
"@angular/compiler": "~5.0.0",
"@angular/platform-browser": "~5.0.0",
"@angular/platform-browser-dynamic": "~5.0.0",
"@syncfusion/ej2-ng-base": "*",
"@syncfusion/ej2-ng-buttons": "*",
"@syncfusion/ej2-ng-grids": "*",
"@syncfusion/ej2-ng-inputs": "*",
"@syncfusion/ej2-ng-charts": "*",
"@syncfusion/ej2-ng-calendars": "*"
},
"devDependencies": {
"@angular/cli": "1.5.6",
"@angular/compiler-cli": "5.0.0",
"@angular/platform-server": "5.0.0",
"@types/jasmine": "^2.2.29",
"@types/requirejs": "^2.1.26",
"@types/node": "^6.0.46",
"gulp": "^3.9.0",
"webpack": "2.5.1",
"shelljs": "^0.7.0",
"typescript": "~2.4.2",
"gulp-sass": "^3.1.0",
"gulp-clean": "^0.3.2",
"run-sequence": "2.2.0",
"webpack-stream": "^3.2.0",
"karma-systemjs": "^0.16.0",
"gulp-typescript": "^2.13.0",
"es6-module-loader": "^0.17.11",
"systemjs-plugin-babel": "0.0.17"
},
"keywords": [
"ej2",
"samples",
"loan-calculator",
"syncfusion",
"ej2-samples",
"ej2-ng-loan-calculator",
"ej2-showcase-samples",
"syncfusion-samples",
"syncfusion-loan-calculator"
],
"scripts": {
"build": "npm run scripts && gulp styles && gulp bundle",
"scripts": "ngc -p tsconfig-aot.json",
"bundle": "gulp bundle",
"ci-publish": "gulp publish-samples"
}
}

Просмотреть файл

@ -0,0 +1,7 @@
<h2 class="header-style">Loan Calculator</h2>
<div class="container main-content" id="content">
<home-section #homeSection></home-section>
<statement-section #statementSection></statement-section>
<chart-section #chartSection></chart-section>
<grid-section #gridSection></grid-section>
</div>

29
src/app/app.component.ts Normal file
Просмотреть файл

@ -0,0 +1,29 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { GridAppComponent } from './grid-app/grid-app.component';
import { BarChartComponent } from './bar-chart/bar-chart.component';
import { DataService } from './data-service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
/** Configurations for the Home page */
constructor(private data: DataService) {
}
@ViewChild('chartSection')
public chart: BarChartComponent;
@ViewChild('gridSection')
public grid: GridAppComponent;
public ngOnInit(): void {
}
public ngAfterViewInit(): void {
this.data.grid = this.grid;
this.data.chart = this.chart;
}
}

44
src/app/app.module.ts Normal file
Просмотреть файл

@ -0,0 +1,44 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { NumericTextBoxModule, SliderModule } from '@syncfusion/ej2-ng-inputs';
import { RadioButtonModule } from '@syncfusion/ej2-ng-buttons';
import { AccumulationChartModule, ChartModule } from '@syncfusion/ej2-ng-charts';
import { DatePickerModule } from '@syncfusion/ej2-ng-calendars';
import { GridModule } from '@syncfusion/ej2-ng-grids';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { InputComponent } from './home/input/input.component';
import { DashboardComponent } from './home/dashboard/dashboard.component';
import { StatementComponent } from './statement/statement.component';
import { BarChartComponent } from './bar-chart/bar-chart.component';
import { GridAppComponent } from './grid-app/grid-app.component';
import { DataService } from './data-service';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
InputComponent,
DashboardComponent,
StatementComponent,
BarChartComponent,
GridAppComponent
],
imports: [
BrowserModule,
NumericTextBoxModule,
SliderModule,
RadioButtonModule,
AccumulationChartModule,
DatePickerModule,
ChartModule,
GridModule
],
providers: [DataService],
bootstrap: [AppComponent]
})
export class AppModule { }

Просмотреть файл

@ -0,0 +1,8 @@
<div class="row top-space loan-content max-content">
<h6 class="center-heading">Amortization Chart</h6>
<div class="col-lg-12 graph-container">
<ejs-chart #paymentGraph style='display:block; height: 100%' id='paymentGraph' [primaryXAxis]='primaryXAxis' [primaryYAxis]='primaryYAxis' [axes]='axes' [tooltip]='tooltip' [chartArea]="chartArea" enableSideBySidePlacement='false'
[height]='height' useGroupingSeparator='true' [palettes]='palettes' [legendSettings]='legendSettings' border='#27304c' background="#27304c" [series]="barSeries" (chartMouseUp)='onChartMouseUp($event)' (axisLabelRender)='axisLabelRender($event)'>
</ejs-chart>
</div>
</div>

Просмотреть файл

@ -0,0 +1,216 @@
import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
import { AccumulationChart, AccumulationLegend, PieSeries, AccumulationTooltip,
AccumulationDataLabel,  IAxisLabelRenderEventArgs,
Chart, LineSeries, DateTime, Legend, Tooltip, IAccLoadedEventArgs, AccumulationTheme, IAccPointRenderEventArgs,
StackingColumnSeries, DataLabel, ColumnSeries, IMouseEventArgs, Series
} from '@syncfusion/ej2-charts';
import { DataService } from '../data-service';
Chart.Inject(LineSeries, StackingColumnSeries, DataLabel, ColumnSeries, DateTime, Legend, Tooltip);
@Component({
selector: 'chart-section',
templateUrl: './bar-chart.component.html',
encapsulation: ViewEncapsulation.None
})
export class BarChartComponent implements OnInit {
/** Configurations for the Chart page */
constructor(private data: DataService) {
}
@ViewChild('paymentGraph')
public chartObj: Chart;
// Chart component binding properties
public primaryXAxis: Object = {
title: 'Years',
valueType: 'DateTime',
labelFormat: 'y',
intervalType: 'Years',
majorGridLines: { width: 0 },
minorGridLines: { width: 0 },
majorTickLines: { width: 0 },
minorTickLines: { width: 0 },
lineStyle: { width: 1, dashArray: '2', color: 'rgba(255,255,255,0.2)' },
labelStyle: {
color: '#989CA9',
fontFamily: 'Roboto',
fontWeight: '400',
size: '12px',
},
titleStyle: {
color: '#FFFFFF',
fontFamily: 'Raleway, sans-serif',
fontWeight: '600',
opacity: 0.62,
size: '16px',
}
};
public primaryYAxis: Object = {
title: 'Balance',
labelFormat: 'c0',
rangePadding: 'None',
lineStyle: { width: 0 },
majorTickLines: { width: 0 },
majorGridLines: { width: 1, dashArray: '2', color: 'rgba(255,255,255,0.2)' },
minorGridLines: { width: 0 },
minorTickLines: { width: 0 },
labelStyle: {
color: '#989CA9',
fontFamily: 'Roboto',
fontWeight: '400',
size: '16px',
},
titleStyle: {
color: '#FFFFFF',
fontFamily: 'Raleway, sans-serif',
fontWeight: '600',
opacity: 0.62,
size: '16px',
}
};
public axes: Object[] = [
{
majorGridLines: { width: 0 },
minorGridLines: { width: 0 },
majorTickLines: { width: 0 },
minorTickLines: { width: 0 },
rowIndex: 0, opposedPosition: true,
lineStyle: { width: 0 },
name: 'yAxis',
title: 'Payment',
labelFormat: 'c0',
labelStyle: {
color: '#989CA9',
fontFamily: 'Roboto',
fontWeight: '400',
size: '16px',
},
titleStyle: {
color: '#FFFFFF',
fontFamily: 'Raleway, sans-serif',
fontWeight: '600',
opacity: 0.62,
size: '16px',
}
}
];
public tooltip: Object = {
enable: true, shared: true,
format: '${series.name} : ${point.y}',
header: '<b>${point.x}<b>',
fill: '#FFFFFF',
opacity: 1,
textStyle: {
color: '#555555',
fontFamily: 'Roboto',
size: '12px',
fontWeight: '400',
},
};
public palettes: string[] = ['#FB6589', '#3AC8DC', '#FFFFFF'];
public legendSettings: Object = {
textStyle: {
color: '#FFFFFF',
fontFamily: 'Raleway, sans-serif',
fontWeight: '600',
opacity: 0.62,
size: '16px',
}
};
public chartArea: Object = {
border: {
width: 0
}
};
public height: string = '500px';
public yearWiseData: Object[] = this.data.yearWiseData;
public barSeries: Object[] = [
// {
// type: 'Column',
// columnWidth: 0.7,
// dataSource: this.yearWiseData,
// xName: 'yearN', width: 2, marker: {
// visible: true,
// width: 10,
// height: 10,
// },
// yName: 'yearTotal', name: 'Total Amount Paid', yAxisName: 'yAxis', visible: false
// },
{
type: 'StackingColumn',
columnWidth: 0.425,
dataSource: this.yearWiseData,
xName: 'yearN', width: 2, marker: {
visible: true,
width: 10,
height: 10
},
yName: 'yearPrincipal', name: 'Principal Paid', yAxisName: 'yAxis'
},
{
type: 'StackingColumn',
columnWidth: 0.425,
dataSource: this.yearWiseData,
xName: 'yearN', width: 2, marker: {
visible: true,
width: 10,
height: 10
},
yName: 'yearInterest', name: 'Interest Paid', yAxisName: 'yAxis'
},
{
type: 'Line',
dataSource: this.yearWiseData,
xName: 'yearN', width: 2, marker: {
visible: true,
width: 10,
height: 10,
fill: '#60448D',
},
yName: 'endingBalance', name: 'Balance',
}
];
public onChartMouseUp(args: IMouseEventArgs): void {
if (args.target.indexOf('_chart_legend_') > -1 && (args.target.indexOf('shape') > -1 || args.target.indexOf('text') > -1)) {
let id: string[] = [args.target];
id = (args.target.indexOf('shape') > -1) ? id[0].split('chart_legend_shape_') : id[0].split('chart_legend_text_');
let index: number = parseInt(id[1], 10);
let series: Series = this.chartObj.visibleSeries[index];
let yName: string = series.yAxisName;
let ySName: string;
let visibility: boolean = false;
if (series.visible) {
for (let i: number = 0, len: number = this.chartObj.series.length; i < len; i++) {
ySName = this.chartObj.series[i].yAxisName;
if (len === 1 || (this.chartObj.series[i].visible &&
(<Series>this.chartObj.series[i]).index !== series.index && yName === ySName)) {
visibility = true;
}
}
series.yAxis.visible = visibility;
} else {
series.yAxis.visible = true;
}
}
}
public axisLabelRender(args: IAxisLabelRenderEventArgs): void {
if (window.innerWidth < 576) {
if (args.axis.name === 'primaryYAxis' || args.axis.name === 'yAxis') {
let value: number = Number(args.value) / 1000;
args.text = value === 0 ? String(value) : (String(value) + 'K');
}
}
}
public ngOnInit(): void {
}
public ngAfterViewInit(): void {
}
}

253
src/app/data-service.ts Normal file
Просмотреть файл

@ -0,0 +1,253 @@
import { Injectable } from '@angular/core';
import { Internationalization } from '@syncfusion/ej2-base';
import {
AccumulationChart, AccumulationLegend, PieSeries, AccumulationTooltip,
AccumulationDataLabel, SeriesModel, Chart, LineSeries, DateTime, Legend, Tooltip, IAccLoadedEventArgs, AccumulationTheme, IAccPointRenderEventArgs,
StackingColumnSeries, Crosshair, DataLabel, ColumnSeries, IMouseEventArgs, Series
} from '@syncfusion/ej2-charts';
import { Grid, DetailRow } from '@syncfusion/ej2-grids';
import { GridAppComponent } from './grid-app/grid-app.component';
import { BarChartComponent } from './bar-chart/bar-chart.component';
import { DashboardComponent } from './home/dashboard/dashboard.component';
@Injectable()
export class DataService {
constructor() {
this.initialize();
}
public emi: number = 0;
public princ: number = 0;
public tent: number = 0;
public principalValue: number = 300000;
public interestValue: number = 5.5;
public loanValue: number = 15;
public dateValue: Date = new Date();
public yearWiseData: Object[] = [];
public emiAmt: string = '';
public principalAmt: string = '';
public interestAmt: string = '';
public totalAmt: string = '';
public dashboard: DashboardComponent;
public yearTenure: boolean = true;
public chart: BarChartComponent;
public grid: GridAppComponent;
public totalPrincipalYear: number = 0;
public totalInterestYear: number = 0;
public inter: number;
public dataUnits: [Object] = <[Object]>[];
public dateObj: Date = new Date();
public totalInterest: number = 0;
public totalAmount: number = 0;
public totalPrincipal: number = 0;
public endBalance: number;
public beginBalance: number;
public yearTotal: number = 0;
public monthNames: [string] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
public intl: Internationalization = new Internationalization();
public getCurrencyVal(value: number): string {
return this.intl.formatNumber(value, { format: 'C0' });
}
public refreshUI1(): void {
this.setInitValues();
let interestPercent: number = parseFloat((Math.round((this.emi * this.tent) - this.princ) / Math.round((this.emi * this.tent)) * 100).toFixed(2));
this.dashboard.pieChart.series = [
{
dataSource: [
{
'x': 'Principal Amount',
y: this.princ,
text: parseFloat(((this.princ) / Math.round((this.emi * this.tent)) * 100).toFixed(2)) + '%'
},
{
'x': 'Interest Amount',
y: (this.tent ? Math.round((this.emi * this.tent) - this.princ) : 0),
text: interestPercent ? interestPercent + '%' : ' '
}
],
radius: '80%', xName: 'x',
animation: { enable: true },
yName: 'y',
startAngle: 290,
endAngle: 290, innerRadius: '60%',
explode: true, explodeOffset: '10%', explodeIndex: 3
}
];
this.dashboard.pieChart.refresh();
this.updateTotalAmt();
}
public refreshUI(): void {
this.refreshUI1();
this.calRangeValues();
this.renderControls();
this.chart.chartObj.refresh();
}
public updateTotalAmt(): void {
this.dashboard.emiAmt = this.emiAmt;
this.dashboard.interestAmt = this.interestAmt;
this.dashboard.totalAmt = this.totalAmt;
this.dashboard.principalAmt = this.principalAmt;
}
public renderControls(): void {
this.grid.yearWiseData = this.yearWiseData;
this.grid.childGrid = {
created: this.grid.childCreated,
dataBound: this.grid.childDataBound,
queryString: 'year',
columns: [
{ field: 'month', headerText: 'Month', textAlign: 'center', minWidth: '80px' },
{
field: 'emi', format: 'C0',
hideAtMedia: '(min-width: 480px)', headerText: 'Payment', minWidth: '80px', textAlign: 'center'
},
{ field: 'pricipalPaid', format: 'C0', headerText: 'Pricipal Paid', minWidth: '80px', textAlign: 'center' },
{ field: 'interest', format: 'C0', headerText: 'Interest Paid', minWidth: '80px', textAlign: 'center' },
{ field: 'endingBalance', format: 'C0', headerText: 'Balance', minWidth: '80px', textAlign: 'center' }
],
dataSource: this.dataUnits
};
this.chart.chartObj.series = [
// {
// type: 'Column',
// columnWidth: 0.7,
// dataSource: this.yearWiseData,
// xName: 'yearN', width: 2, marker: {
// visible: true,
// width: 10,
// height: 10,
// },
// yName: 'yearTotal', name: 'Total Amount Paid', yAxisName: 'yAxis',
// },
{
type: 'StackingColumn',
columnWidth: 0.425,
dataSource: this.yearWiseData,
xName: 'yearN', width: 2, marker: {
visible: true,
width: 10,
height: 10
},
yName: 'yearPrincipal', name: 'Principal Paid', yAxisName: 'yAxis'
},
{
type: 'StackingColumn',
columnWidth: 0.425,
dataSource: this.yearWiseData,
xName: 'yearN', width: 2, marker: {
visible: true,
width: 10,
height: 10
},
yName: 'yearInterest', name: 'Interest Paid', yAxisName: 'yAxis'
},
{
type: 'Line',
dataSource: this.yearWiseData,
xName: 'yearN', width: 2, marker: {
visible: true,
width: 10,
height: 10,
fill: '#60448D',
},
yName: 'endingBalance', name: 'Balance',
},
];
}
public getInterest(): number {
return this.interestValue ? parseFloat('' + this.interestValue / 12 / 100) : 0;
}
public calculateEMI(): number {
let interestValue: number = this.getInterest();
let tent: number = this.yearTenure ? (this.loanValue * 12) : this.loanValue;
if (interestValue) {
return this.principalValue * interestValue *
(Math.pow((1 + interestValue), tent)) / ((Math.pow((1 + interestValue), tent)) - 1);
}
return this.principalValue / tent;
}
public setInitValues(): void {
this.emi = this.calculateEMI();
this.princ = this.principalValue;
this.tent = this.yearTenure ? (this.loanValue * 12) : this.loanValue;
this.dataUnits = <[Object]>[];
this.yearWiseData = <[Object]>[];
this.dateObj = new Date(this.dateValue.getTime());
this.totalInterest = 0;
this.totalAmount = 0;
this.totalPrincipal = 0;
this.totalPrincipalYear = 0;
this.totalInterestYear = 0;
this.emiAmt = this.getCurrencyVal(this.tent ? Math.round(this.emi) : 0);
this.interestAmt = this.getCurrencyVal(this.tent ? Math.round((this.emi * this.tent) - this.princ) : 0);
this.totalAmt = this.getCurrencyVal(this.tent ? Math.round((this.emi * this.tent)) : 0);
this.principalAmt = this.getCurrencyVal(this.princ);
}
public calRangeValues(): void {
for (let i: number = 0; i < this.tent; i++) {
this.inter = this.getInterest ? (this.princ * this.getInterest()) : this.princ;
this.totalInterest += this.inter;
this.totalAmount += this.emi;
this.totalPrincipal += parseFloat((this.emi - this.inter).toFixed(2));
this.endBalance = this.princ - (this.emi - this.inter);
this.yearTotal += this.emi;
this.totalPrincipalYear += parseFloat((this.emi - this.inter).toFixed(2));
this.totalInterestYear += this.inter;
this.dataUnits.push({
month: this.monthNames[this.dateObj.getMonth()],
index: (i + 1),
totalInterest: Math.round(this.totalInterest),
totalAmount: this.totalAmount,
emi: Math.round(this.emi),
year: this.dateObj.getFullYear(),
beginningBalance: Math.round(this.princ),
interest: Math.round(this.inter),
pricipalPaid: Math.round((this.emi - this.inter)),
endingBalance: Math.round(this.endBalance)
});
if (i === 0 || this.dateObj.getMonth() === 0) {
this.beginBalance = this.princ;
}
if (this.dateObj.getMonth() === 11 || (i === this.tent - 1)) {
this.yearWiseData.push({
beginningBalance: Math.round(this.beginBalance),
totalInterest: Math.round(this.totalInterest),
totalPrincipal: Math.round(this.totalPrincipal),
totalAmount: Math.round(this.totalAmount),
yearTotal: Math.round(this.yearTotal),
endingBalance: Math.round(this.endBalance),
yearN: new Date(this.dateObj.getFullYear(), 0, 1),
year: this.dateObj.getFullYear(),
yearPrincipal: this.totalPrincipalYear,
yearInterest: this.totalInterestYear
});
this.yearTotal = 0;
this.totalPrincipalYear = 0;
this.totalInterestYear = 0;
}
this.princ = this.endBalance;
if (i < this.tent - 1) {
this.dateObj.setMonth(this.dateObj.getMonth() + 1);
}
}
}
public initialize(): void {
this.setInitValues();
this.calRangeValues();
}
}

Просмотреть файл

@ -0,0 +1,17 @@
<div class="row top-space loan-content max-content">
<h6 class="center-heading">Amortization Schedule</h6>
<ejs-grid #scheduleGrid id='scheduleGrid' [dataSource]='yearWiseData' [childGrid]='childGrid' width="100%" (click)='onClick($event)'>
<e-columns>
<e-column field='year' headerText='Year' minWidth='80px' textAlign="center">
<ng-template #template let-data>
<div class="e-icons e-icon-grightarrow e-row-toggle"></div>
<span style="padding-left: 12px;">{{data.year}}</span>
</ng-template>
</e-column>
<e-column field='yearTotal' headerText='Payment' minWidth='80px' textAlign="center" [format]="format" [hideAtMedia]="paymentHideAtMedia"></e-column>
<e-column field='yearPrincipal' headerText='Principal Paid' minWidth='80px' textAlign="center" [format]="format"></e-column>
<e-column field='yearInterest' headerText='Interest Paid' minWidth='80px' textAlign="center" [format]="format"></e-column>
<e-column field='endingBalance' headerText='Balance' minWidth='80px' textAlign="center" [format]="format"></e-column>
</e-columns>
</ejs-grid>
</div>

Просмотреть файл

@ -0,0 +1,74 @@
import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
import { closest } from '@syncfusion/ej2-base';
import { GridComponent } from '@syncfusion/ej2-ng-grids';
import { Grid, DetailRow, DetailDataBoundEventArgs } from '@syncfusion/ej2-grids';
import { DataService } from '../data-service';
Grid.Inject(DetailRow);
@Component({
selector: 'grid-section',
templateUrl: './grid-app.component.html',
encapsulation: ViewEncapsulation.None
})
export class GridAppComponent implements OnInit {
/** Configurations for the Grid page */
constructor(private data: DataService) {
}
@ViewChild('scheduleGrid')
public grid: Grid;
public yearWiseData: Object[] = this.data.yearWiseData;
public childGrid: Object = {
created: this.childCreated,
dataBound: this.childDataBound,
queryString: 'year',
columns: [
{ field: 'month', headerText: 'Month', textAlign: 'center', minWidth: '80px' },
{
field: 'emi', format: 'C0',
hideAtMedia: '(min-width: 480px)', headerText: 'Payment', minWidth: '80px', textAlign: 'center'
},
{ field: 'pricipalPaid', format: 'C0', headerText: 'Pricipal Paid', minWidth: '80px', textAlign: 'center' },
{ field: 'interest', format: 'C0', headerText: 'Interest Paid', minWidth: '80px', textAlign: 'center' },
{ field: 'endingBalance', format: 'C0', headerText: 'Balance', minWidth: '80px', textAlign: 'center' }
],
dataSource: this.data.dataUnits
};
public format: string = 'c0';
public balanceHideAtMedia: string = '(min-width: 750px)';
public paymentHideAtMedia: string = '(min-width: 480px)';
/* tslint:disable */
public childCreated(args: Object): void {
// (this.getHeaderContent() as HTMLElement).style.display = 'none';
// this.element.style.display = 'none';
}
public childDataBound(args: Object): void {
// this.element.style.display = '';
}
/* tslint:enable */
public onClick(args: MouseEvent): void {
let target: Element = args.target as Element;
if (target.classList.contains('e-row-toggle') || target.parentElement.querySelector('.e-row-toggle')) {
target = target.parentElement.querySelector('.e-row-toggle') ? target.parentElement.querySelector('.e-row-toggle') : target;
if (target.classList.contains('e-icon-gdownarrow')) {
target.classList.remove('e-icon-gdownarrow');
target.classList.add('e-icon-grightarrow');
this.grid.detailRowModule.collapse(parseInt((closest(target, 'tr') as HTMLElement).getAttribute('aria-rowindex'), 10));
} else {
target.classList.remove('e-icon-grightarrow');
target.classList.add('e-icon-gdownarrow');
this.grid.detailRowModule.expand(parseInt((closest(target, 'tr') as HTMLElement).getAttribute('aria-rowindex'), 10));
}
}
}
public ngOnInit(): void {
}
public ngAfterViewInit(): void {
}
}

Просмотреть файл

@ -0,0 +1,44 @@
<div class="col-lg-12 emi-content">
<div>
<h6 class="emi-header">Break-up of Total Payment</h6>
</div>
<div class="row">
<div class="col-lg-7">
<ejs-accumulationchart style="display: block;" #pieChart id="payment_pieChart" enableSmartLabels="true" [legendSettings]="legendSettings" height="365px" [width]="width" [tooltip]="tooltip" border="#27304c" background="#27304c" [series]='pieSeries' (load)='onLoad($event)' (pointRender)="pointRender($event)"></ejs-accumulationchart>
</div>
<div class="col-lg-5 pie-content" id="pieContent">
<div>
<p><span class="pie-icon pie-principal"></span>Principal Amount</p>
<h5 id="loan_principal">{{principalAmt}}</h5>
</div>
<div>
<p><span class="pie-icon pie-interest"></span>Total Interest</p>
<h5 id="loan_interest">{{interestAmt}}</h5>
</div>
<div class="pie-total">
<span>
<p>Total Payment</p>
<p>(Principal + Interest)</p>
</span>
<h5 id="loan_total_payment">{{totalAmt}}</h5>
</div>
</div>
</div>
<div>
<h6 class="emi-footer">Your Monthly Payment</h6>
<h1 id="loan_emi">{{emiAmt}}</h1>
</div>
</div>
<svg style="height: 0; display: block; width: 0;">
<defs>
<linearGradient id="principal_svg" x1="0" x2="0" y1="0" y2="1">
<stop offset="0"></stop>
<stop offset="1"></stop>
</linearGradient>
<linearGradient id="interest_svg" x1="0" x2="0" y1="0" y2="1">
<stop offset="0"></stop>
<stop offset="1"></stop>
</linearGradient>
</defs>
</svg>

Просмотреть файл

@ -0,0 +1,71 @@
import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
import { AccumulationChart, AccumulationLegend, PieSeries, AccumulationTooltip,
AccumulationDataLabel,
Chart, LineSeries, DateTime, Legend, Tooltip, IAccLoadedEventArgs, AccumulationTheme, IAccPointRenderEventArgs,
StackingColumnSeries, Crosshair, DataLabel, ColumnSeries, IMouseEventArgs, Series
} from '@syncfusion/ej2-charts';
import { AccumulationChartComponent } from '@syncfusion/ej2-ng-charts';
import { DataService } from '../../data-service';
AccumulationChart.Inject(AccumulationLegend, PieSeries, AccumulationTooltip, AccumulationDataLabel);
@Component({
selector: 'dashboard-section',
templateUrl: './dashboard.component.html',
encapsulation: ViewEncapsulation.None
})
export class DashboardComponent implements OnInit {
/** Configurations for the Dashboard page */
constructor(private data: DataService) {
}
@ViewChild('pieChart')
public pieChart: AccumulationChartComponent;
// chart binding properties
public legendSettings: Object = { visible: false };
public width: string = '100%';
public tooltip: Object = { enable: false };
public pieSeries: Object[] = [
{
dataSource: [
{ 'x': 'Principal Amount', y: this.data.principalValue },
{ 'x': 'Interest Amount', y: ((this.data.emi * this.data.tent) - this.data.principalValue) }
],
radius: '80%', xName: 'x',
animation: { enable: true },
yName: 'y',
startAngle: 290,
endAngle: 290, innerRadius: '60%',
explode: true, explodeOffset: '10%', explodeIndex: 3
}
];
public emiAmt: string = this.data.emiAmt;
public principalAmt: string = this.data.principalAmt;
public interestAmt: string = this.data.interestAmt;
public totalAmt: string = this.data.totalAmt;
public onLoad(args: IAccLoadedEventArgs): void {
let selectedTheme: string = location.hash.split('/')[1];
selectedTheme = selectedTheme ? selectedTheme : 'Material';
args.accumulation.theme = <AccumulationTheme>(selectedTheme.charAt(0).toUpperCase() + selectedTheme.slice(1));
}
public pointRender(args: IAccPointRenderEventArgs): void {
if (args.point.index) {
args.border.width = 7;
args.fill = 'url(#interest_svg)';
} else {
args.border.width = 7;
args.border.color = '#162036';
args.fill = 'url(#principal_svg)';
}
}
public ngOnInit(): void {
}
public ngAfterViewInit(): void {
}
}

Просмотреть файл

@ -0,0 +1,8 @@
<div class="row left-content-wrap">
<div class="row loan-content">
<input-section #inputSection></input-section>
</div>
<div class="row loan-content">
<dashboard-section #dashboardSection></dashboard-section>
</div>
</div>

Просмотреть файл

@ -0,0 +1,26 @@
import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
import { DashboardComponent } from './dashboard/dashboard.component';
import { DataService } from '../data-service';
@Component({
selector: 'home-section',
templateUrl: './home.component.html',
encapsulation: ViewEncapsulation.None
})
export class HomeComponent implements OnInit {
/** Configurations for the Home page */
constructor(private data: DataService) {
}
@ViewChild('dashboardSection')
public dashboard: DashboardComponent;
public ngOnInit(): void {
}
public ngAfterViewInit(): void {
this.data.dashboard = this.dashboard;
}
}

Просмотреть файл

@ -0,0 +1,67 @@
<div class="left-content col-lg-12">
<div class="row form-space">
<div class="col-lg-12">
<div class="content-space">
<table>
<tr>
<td>
<label class="pricipal">Loan Amount</label>
</td>
<td>
<div class="editor-space">
<ejs-numerictextbox id="principal_txt" [(value)]="principalValue" [min]="principalNumMinValue" [max]="principalNumMaxValue" [step]="principalNumStep" [format]="principalNumFormat" [width]="principalNumWidth" (change)="principalNumChange($event)"></ejs-numerictextbox>
</div>
</td>
</tr>
</table>
</div>
<ejs-slider id='pricipal' [(value)]="principalValue" [min]="principalMinValue" [max]="principalMaxValue" [step]="principalStep" [type]="principalType" [ticks]="principalTicks" (change)="principalChange($event)" (changed)="principalChanged($event)" (renderedTicks)="principalRenderedTicks($event)"></ejs-slider>
</div>
</div>
<div class="row form-space">
<div class="col-lg-12">
<div class="content-space">
<table>
<tr>
<td>
<label class="interestrate">Interest Rate</label>
</td>
<td>
<div class="editor-space">
<ejs-numerictextbox id="interest_txt" [(value)]="interestValue" [min]="interestNumMinValue" [max]="interestNumMaxValue" [step]="interestNumStep" [format]="interestNumFormat" [width]="interestNumWidth" (change)="interestNumChange($event)"></ejs-numerictextbox>
</div>
</td>
</tr>
</table>
</div>
<ejs-slider id='interestrate' [(value)]="interestValue" [min]="interestMinValue" [max]="interestMaxValue" [step]="interestStep" [type]="interestType" [ticks]="interestTicks" (change)="interestChange($event)" (changed)="interestChanged($event)"></ejs-slider>
</div>
</div>
<div class="row form-space">
<div class="col-lg-12">
<div class="content-space">
<table>
<tr>
<td>
<label class="loantenure">Loan Term</label>
<ul class="tenure-value" style="float: left;">
<li>
<ejs-radiobutton id="radio1" label="Month" name="tenure" value="month" (change)="monthChanged()"></ejs-radiobutton>
</li>
<li>
<ejs-radiobutton id="radio2" label="Year" name="tenure" value="year" checked="true" [checked]="yearTenure" (change)="yearChanged()"></ejs-radiobutton>
</li>
</ul>
</td>
<td>
<div class="editor-space">
<ejs-numerictextbox id="loan_txt" [(value)]="loanValue" [min]="loanNumMinValue" [max]="loanNumMaxValue" [step]="loanNumStep" [format]="loanNumFormat" [width]="loanNumWidth" (change)="loanNumChange($event)"></ejs-numerictextbox>
</div>
</td>
</tr>
</table>
</div>
<ejs-slider id='loantenure' [(value)]="loanValue" [min]="loanMinValue" [max]="loanMaxValue" [step]="loanStep" [type]="loanType" [ticks]="loanTicks" (change)="loanChange($event)" (changed)="loanChanged($event)"></ejs-slider>
</div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,156 @@
import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
import { NumericTextBoxComponent, SliderTickRenderedEventArgs } from '@syncfusion/ej2-ng-inputs';
import { ChangeEventArgs } from '@syncfusion/ej2-inputs';
import { DataService } from '../../data-service';
@Component({
selector: 'input-section',
templateUrl: './input.component.html',
encapsulation: ViewEncapsulation.None
})
export class InputComponent implements OnInit {
/** Configurations for the Input page */
constructor(private data: DataService) {
this.principalValue = this.data.principalValue;
this.interestValue = this.data.interestValue;
this.loanValue = this.data.loanValue;
}
// The principal NumericTextBox binding properties
public principalNumMinValue: number = 1000;
public principalNumMaxValue: number = 5000000;
public principalNumStep: number = 10000;
public principalNumFormat: string = 'c0';
public principalNumWidth: string = '200px';
public principalNumChange(args: ChangeEventArgs): void {
this.data.principalValue = this.principalValue;
if (args.isInteraction) {
this.data.refreshUI();
}
}
// The interest NumericTextBox binding properties
public interestNumMinValue: number = 0;
public interestNumMaxValue: number = 20;
public interestNumStep: number = .25;
public interestNumFormat: string = '#.##\' %\'';
public interestNumWidth: string = '165px';
public interestNumChange(args: ChangeEventArgs): void {
this.data.interestValue = this.interestValue;
if (args.isInteraction) {
this.data.refreshUI();
}
}
// The loan NumericTextBox binding properties
public loanNumMinValue: number = 1;
public loanNumMaxValue: number = 40;
public loanNumStep: number = 1;
public loanNumFormat: string = '#.##';
public loanNumWidth: string = '150px';
public loanNumChange(args: ChangeEventArgs): void {
this.data.loanValue = this.loanValue;
if (args.isInteraction) {
this.data.refreshUI();
}
}
// The principal Slider binding properties
public principalValue: number = 0;
public principalMinValue: number = 0;
public principalMaxValue: number = 500000;
public principalStep: number = 10000;
public principalType: string = 'MinRange';
public principalTicks: Object = { placement: 'After', largeStep: 100000, smallStep: 10000, showSmallTicks: false, format: 'c0' };
public principalChange(args: ChangeEventArgs): void {
this.data.principalValue = this.principalValue;
this.data.setInitValues();
this.data.updateTotalAmt();
}
public principalChanged(args: ChangeEventArgs): void {
this.data.refreshUI();
}
public principalRenderedTicks(args: SliderTickRenderedEventArgs): void {
let li: NodeListOf<Element> = args.ticksWrapper.getElementsByClassName('e-large');
for (let i: number = 0; i < li.length; ++i) {
let ele: HTMLElement = (li[i].querySelectorAll('.e-tick-value')[0] as HTMLElement);
let num: number = parseInt(ele.innerText.substring(1).replace(/,/g , ''), 10) / 1000;
ele.innerText = num === 0 ? ('' + num) : (num + 'K');
}
}
// The interest Slider binding properties
public interestValue: number = 0;
public interestMinValue: number = 0;
public interestMaxValue: number = 20;
public interestStep: number = .25;
public interestType: string = 'MinRange';
public interestTicks: Object = { placement: 'After', largeStep: 5, smallStep: 1, showSmallTicks: false };
public interestChange(args: ChangeEventArgs): void {
this.data.interestValue = this.interestValue;
this.data.setInitValues();
this.data.updateTotalAmt();
}
public interestChanged(args: ChangeEventArgs): void {
this.data.refreshUI();
}
// The loan Slider binding properties
public loanValue: number = 0;
public loanMinValue: number = 0;
public loanMaxValue: number = 40;
public loanStep: number = 1;
public loanType: string = 'MinRange';
public loanTicks: Object = { placement: 'After', largeStep: 10, smallStep: 1, showSmallTicks: false };
public loanChange(args: ChangeEventArgs): void {
this.data.loanValue = this.loanValue;
this.data.setInitValues();
this.data.updateTotalAmt();
}
public loanChanged(args: ChangeEventArgs): void {
this.data.refreshUI();
}
// Radio button binding properties
public yearTenure: boolean = this.data.yearTenure;
public monthChanged(): void {
this.data.yearTenure = this.yearTenure = false;
this.loanNumMinValue = 12;
this.loanNumMaxValue = 480;
this.loanNumStep = 12;
this.loanMaxValue = 480;
this.loanValue = (this.loanValue * 12);
this.loanStep = 12;
this.loanTicks = { placement: 'After', largeStep: 120, smallStep: 12, showSmallTicks: false };
}
public yearChanged(): void {
this.data.yearTenure = this.yearTenure = true;
this.loanNumMinValue = 1;
this.loanNumMaxValue = 40;
this.loanNumStep = 1;
this.loanMaxValue = 40;
this.loanValue = (this.loanValue / 12);
this.loanStep = 1;
this.loanTicks = { largeStep: 10, smallStep: 1, showSmallTicks: false };
}
public ngOnInit(): void {
}
public ngAfterViewInit(): void {
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<div class="row top-space loan-content" style="text-align: center">
<div class="graph-text">Monthly payments starting from</div>
<div class="graph-input">
<ejs-datepicker id="monthStarter" placeholder='Enter date' start="Year" [showClearButton]="false" [strictMode]="true" [showTodayButton]="false" depth="Year" [format]="format" [value]="dateValue" [width]="width" (change)="onChange($event)"></ejs-datepicker>
</div>
</div>

Просмотреть файл

@ -0,0 +1,33 @@
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { ChangedEventArgs } from '@syncfusion/ej2-ng-calendars';
import { isNullOrUndefined as isNOU } from '@syncfusion/ej2-base';
import { DataService } from '../data-service';
@Component({
selector: 'statement-section',
templateUrl: './statement.component.html',
encapsulation: ViewEncapsulation.None
})
export class StatementComponent implements OnInit {
/** Configurations for the Statement page */
constructor(private data: DataService) {
}
public format: string = 'MMM yyy';
public dateValue: Date = new Date();
public width: string = '250px';
public onChange(args: ChangedEventArgs): void {
if (isNOU(args.value)) {
this.dateValue = new Date();
} else {
this.data.dateValue = this.dateValue = args.value;
this.data.refreshUI();
}
}
public ngOnInit(): void {
}
}

0
src/assets/.gitkeep Normal file
Просмотреть файл

901
src/assets/index.scss Normal file
Просмотреть файл

@ -0,0 +1,901 @@
//sass-lint:disable-all
$border-color: #e4e2e2;
$bg-color: #fbfbfb;
$body-bg-color: rgb(22, 32, 54);
$header-bg-color: #27304c;
$header-font-color: rgb(255, 255, 255);
$input-bg-color: #3A4360;
$input-font-color: #fff;
$input-icon-color: #95979c;
$input-border-color: #475274;
$main-bg-color: #27304c;
$text-color: #E7E7E7;
$text-color1: #989CA9;
$text-color2: #F5F5F5;
$header-color: #4a4a4a 100%;
$range-color: #3B9ED4;
$slider-color: #20273E;
$row-bg-color: #27304c;
$altrow-bg-color: #313B5A;
$row-font-color: #fff;
$hover-color: #404D75;
$active-color: #5B6BC0;
$principal-color: linear-gradient(#3AC8DC, #5B6BC0);
$interest-color: linear-gradient(#F3A55B, #FB6589);
$tick-color: #ABABAB;
@mixin Raleway-600 {
font-family: 'Raleway', sans-serif;
font-weight: 600;
}
@mixin Raleway-500 {
font-family: 'Raleway', sans-serif;
font-weight: 500;
}
@mixin Raleway-400 {
font-family: 'Raleway', sans-serif;
font-weight: 500;
}
@mixin Roboto-400 {
font-family: "Roboto";
font-weight: 400;
}
@mixin Roboto-500 {
font-family: "Roboto";
font-weight: 500;
}
.content-space {
display: block;
margin-top: 25px;
margin-bottom: 25px;
width: 100%;
table {
width: 100%;
}
label {
float: left;
margin-top: 6px;
color: $text-color;
font-size: 16px;
@include Raleway-400();
}
}
.editor-space {
display: flex;
justify-content: left;
float: right;
}
.form-space {
margin-bottom: 25px;
&:not(:first-child) {
margin-top: 25px;
}
}
.navbar {
border-radius: 0;
}
.component-content {
border-radius: 3px;
height: 300px;
}
.parent {
min-height: 85px;
position: relative;
text-align: center;
}
.child {
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}
.top-space {
margin-top: 30px;
margin-bottom: 15px;
h6 {
position: relative;
}
}
// .emi-content > div {
// background-color: $bg-color;
// }
.emi-content {
max-width: 634px;
max-height: 660px;
margin: 30px 0 25px 0;
padding: 0 44px;
h6 {
@include Raleway-500();
text-align: center;
}
.emi-header {
font-size: 20px;
color: $text-color;
letter-spacing: 0.7px;
line-height: 29px;
}
.emi-footer {
font-size: 18px;
color: $text-color1;
margin-top: 5px;
text-align: center;
}
h1 {
color: $text-color2;
font-size: 32px;
@include Roboto-500();
text-align: center;
}
> div {
> h6 {
text-align: left;
}
}
}
.top-margin {
margin-top: 10px;
}
.bottom-margin {
margin-bottom: 35px;
}
.pie-content {
height: 100%;
padding: 0;
p {
text-align: center;
font-size: 15px;
@include Raleway-400();
color: $text-color1;
}
.pie-icon {
height: 21px;
width: 21px;
display: inline-block;
border-radius: 10px;
margin: 0 14px 0 0;
vertical-align: text-top;
&.pie-principal {
background: $principal-color;
}
&.pie-interest {
background: $interest-color;
}
}
h5 {
font-size: 18px;
@include Roboto-500();
color: $text-color2;
padding: 5px 0 10px;
text-align: center;
}
> div {
margin: 35px 0;
&:first-child {
margin-top: 43px;
}
&.pie-total {
p:first-child {
margin-bottom: 0;
}
p + p {
font-size: 14px;
letter-spacing: 1px;
}
}
}
}
.center-heading {
padding: 10px;
font-size: 20px;
text-align: center;
font-weight: 500;
}
.border-set {
border: 1px solid $border-color;
}
.tenure-value {
display: flex;
padding: 4px 5px 4px 10px;
position: relative;
li {
float: left;
}
.e-radio {
& + label {
&::before {
height: 20px;
width: 20px;
border-width: 2px;
}
&::before, &:hover::before {
border-color: $range-color;
background-color: $input-bg-color;
}
&::after, &:hover::after {
background-color: $range-color;
}
.e-label {
color: $text-color;
font-size: 16px;
@include Raleway-500()
}
}
&:focus + label {
&::before {
border-color: $range-color;
background-color: $input-bg-color;
}
&::after{
background-color: $range-color;
}
}
}
>li {
padding-left: 10px;
}
}
.graph-container {
height: 520px;
padding: 0 33px;
}
li {
list-style: none;
}
.border-unset-left {
border-left: 0;
}
.border-unset-right {
border-right: 0;
}
.main-content {
margin-top: 40px;
max-width: 1300px;
.loan-content {
background-color: $main-bg-color;
color: $text-color;
}
.e-slider-container {
.e-scale {
color: $tick-color;
.e-tick {
&.e-large {
top: 14px;
}
.e-tick-value {
@include Roboto-400();
font-size: 13px;
}
}
}
.e-slider {
.e-slider-track {
background: $slider-color;
border-color: $slider-color;
}
.e-handle {
height: 26px;
width: 26px;
top: calc(50% - 13px);
margin-left: -13px;
&::before {
content: '';
background: $range-color;
width: 14px;
height: 14px;
position: absolute;
border-radius: 16px;
top: 5px;
left: 5px;
}
}
.e-range {
background-color: $range-color;
top: calc(50% - 5px);
}
& .e-slider-track, & .e-range {
height: 12px;
}
& .e-handle, & .e-slider-track, & .e-range {
border-radius: 15px;
}
}
}
.e-input-group.e-control-wrapper {
&:not(.e-success):not(.e-warning):not(.e-error) {
border-color: $input-border-color;
background: $input-bg-color;
}
&:not(.e-disabled):active:not(.e-success):not(.e-warning):not(.e-error):not(.e-input-focus) {
border-color: $hover-color;
}
.e-input-group-icon {
background: $input-bg-color;
color: $input-icon-color;
border-color: $input-border-color;
&:hover {
background: $hover-color;
color: $input-icon-color;
}
}
.e-spin-down {
border-right-width: 0;
}
input.e-input {
@include Roboto-400();
font-size: 16px;
height: 38px;
background-color: $input-bg-color;
color: $input-font-color;
}
}
.graph-text {
color: $text-color;
font-size: 20px;
@include Raleway-500();
letter-spacing: 0.7px;
margin: 0 22px;
display: inline-block;
vertical-align: middle;
}
.graph-input {
display: inline-block;
margin: 34px 22px;
}
.e-grid {
border-color: $main-bg-color;
border-radius: 0;
.e-row {
cursor: pointer;
}
.e-gridheader {
border-width: 0;
}
.e-icon-grightarrow, .e-icon-gdownarrow {
width: 11px;
height: 11px;
border: 1px solid #fff;
margin: auto;
text-indent: 0;
&::before {
position: absolute;
font-size: 13px;
}
}
.e-icon-grightarrow::before {
content: '+';
margin: -4px 0 0 -4px;
}
.e-icon-gdownarrow::before {
content: '-';
margin: -6px 0 0 -2px;
}
.e-headercelldiv {
font-size: 14px;
height: 55px;
line-height: 55px;
@include Raleway-400();
white-space: normal;
}
& .e-columnheader {
background: linear-gradient(-90deg, #5B6BC0, #3AC8DC);
}
& .e-headercell, & .e-detailheadercell {
background: transparent;
color: #fff;
}
& .e-rowcell, & .e-detailrowcollapse, & .e-detailrowexpand, & .e-detailindentcell, & .e-detailrow .e-altrow .e-rowcell {
background: $row-bg-color;
border-width: 0;
font-size: 13px;
@include Roboto-400();
}
.e-altrow {
& .e-rowcell, & .e-detailrowcollapse, & .e-detailrowexpand {
background: $altrow-bg-color;
}
}
& .e-icons, & .e-rowcell, & .e-detailrowcollapse, & .e-detailrowexpand {
color: $row-font-color;
}
.e-detailcell {
padding: 0;
border-width: 0;
}
&.e-gridhover tr[role='row'] {
&:not(.e-editedrow):hover .e-rowcell:not(.e-cellselectionbackground), &:hover .e-detailrowcollapse:not(.e-cellselectionbackground), &:hover .e-detailrowexpand:not(.e-cellselectionbackground) {
&:not(.e-active):not(.e-updatedtd):not(.e-indentcell) {
background: $hover-color;
}
}
}
.e-grid {
& .e-headercontent, & .e-gridcontent .e-emptyrow, & .e-spinner-pane {
display: none;
}
}
& [aria-selected] + tr .e-detailindentcell {
border-width: 0;
}
}
}
#control-container {
padding: 0px !important;
}
#principal_svg stop {
stop-color: #3AC8DC;
}
#principal_svg stop[offset="0"] {
stop-color: #3AC8DC;
}
#principal_svg stop[offset="1"] {
stop-color: #5B6BC0;
}
#interest_svg stop {
stop-color: #F3A55B;
}
#interest_svg stop[offset="0"] {
stop-color: #e97c11;
}
#interest_svg stop[offset="1"] {
stop-color: #FB6589;
}
.navbar-right {
float: right;
}
.navbar-header {
float: left;
}
.flex-container {
display: flex;
flex-wrap: wrap;
.col-lg-4 {
width: 33.33333333%;
}
@media (max-width: 500px) {
.col-lg-4 {
width: 100%;
}
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 600;
}
body {
background-color: $body-bg-color;
color: $text-color;
}
.left-content {
background-color: $main-bg-color;
max-height: 660px;
margin: 25px 0;
padding: 0 44px;
border-right: 1px solid $input-border-color;
}
.header-style {
background: $header-bg-color;
color: $header-font-color;
margin: 0;
padding: 15px;
text-align: center;
text-transform: uppercase;
font-size: 26px;
@include Raleway-500();
}
.margin-setter {
margin: 35px;
}
.start-setter {
margin: 17px 0;
}
.e-datepicker.e-popup-wrapper {
border-color: $input-border-color;
}
.e-datepicker .e-calendar {
background-color: $input-bg-color;
}
.e-calendar .e-header .e-title, .e-calendar .e-content span, .e-calendar .e-header.e-decade .e-title,
.e-calendar .e-header .e-title:hover {
color: $input-font-color;
}
.e-calendar .e-header .e-icon-container span, .e-calendar .e-header .e-prev:hover > span, .e-calendar .e-header .e-next:hover > span,
.e-calendar .e-header button.e-prev:active span, .e-calendar .e-header button.e-next:active span {
color: $input-icon-color;
}
.e-calendar .e-content td.e-selected span.e-day, .e-calendar .e-header .e-prev:active, .e-calendar .e-header .e-next:active {
background: $active-color;
}
.e-calendar .e-content.e-decade tr:first-child .e-cell:first-child span.e-day, .e-calendar .e-content.e-decade tr:last-child .e-cell:last-child span.e-day {
color: $tick-color;
}
.e-calendar .e-header .e-prev:hover, .e-calendar .e-header .e-next:hover, .e-calendar .e-content td.e-focused-date span.e-day,
.e-calendar .e-content.e-year td:hover span.e-day, .e-calendar .e-content.e-decade td:hover span.e-day,
.e-calendar .e-content.e-year td.e-selected:hover span.e-day, .e-calendar .e-content.e-decade td.e-selected:hover span.e-day {
background: $hover-color;
color: $input-font-color;
}
@media (max-width: 319px) {
.container {
min-width: 300px;
}
}
@media (max-width: 576px) {
.header-style {
font-size: 20px;
padding: 13px 0;
}
.container {
padding: 10px;
margin: 0;
.e-slider-container .e-scale .e-tick .e-tick-value {
font-size: 12px;
}
}
.top-space {
margin: 10px;
}
.row {
margin-left: 0;
margin-right: 0;
}
.left-content-wrap .loan-content:first-child {
margin-bottom: 10px;
}
.left-content {
padding: 0;
margin: 0;
border-right-width: 0;
.col-lg-12 {
padding: 12px 12px 18px 12px;
}
.content-space {
margin: 0 0 12px 0;
td {
display: block;
}
label {
font-size: 15px;
margin: 0 0 12px;
}
.tenure-value .e-radio + label {
margin: 0;
.e-label {
font-size: 13px;
}
}
}
.editor-space {
float: left;
width: 100%;
}
.form-space {
margin-bottom: 23px;
&:last-child {
margin-bottom: 0;
}
}
.e-input-group {
width: 100% !important;
}
}
.emi-content {
padding: 0;
margin-top: 0;
.emi-header {
text-align: center;
font-size: 18px;
}
}
.tenure-value {
padding: 0;
float: none;
}
.emi-content {
h6 {
font-size: 23px;
}
h1 {
font-size: 30px;
}
.col-lg-7 {
width: 100%;
float: left;
}
.col-lg-5 {
width: 100%;
float: left;
}
}
.pie-content {
div {
margin: 10px 0;
text-align: center;
&:first-child {
margin-top: 0;
}
}
p {
display: inline-block;
margin: 0;
}
h5 {
display: inline-block;
margin: 0;
padding: 5px;
}
.pie-total {
span {
display: inline-block;
}
p {
display: block;
}
}
}
.main-content .loan-content {
.graph-text {
margin: 12px;
font-size: 18px;
}
.graph-input {
padding: 0 12px 12px;
width: 100%;
margin: 0;
}
}
.center-heading {
font-size: 18px;
}
.graph-container {
padding: 0 2px;
}
}
@media (min-width: 576px) and (max-width: 991px) {
.container {
padding: 0 30px;
width: 100%;
}
.left-content-wrap {
.loan-content {
width: 100%;
float: left;
margin: 0;
padding-bottom: 25px;
& + .loan-content {
margin-top: 30px;
padding-top: 25px;
padding-bottom: 0px;
}
}
}
.left-content {
border-right-width: 0;
}
.emi-content {
max-width: 100%;
.col-lg-7 {
width: 60%;
float: left;
}
.col-lg-5 {
width: 40%;
float: left;
}
}
}
@media (max-width: 700px) {
.main-content .graph-text {
margin-top: 15px;
}
.main-content .graph-input {
margin: 21px;
}
}
@media (min-width: 992px) {
.container {
padding: 0 35px;
width: 100%;
}
.left-content-wrap {
.loan-content {
width: 50%;
float: left;
margin: 0;
& + .loan-content {
margin: 0;
}
}
}
.left-content {
border-right-width: 1px;
}
}
@media (min-width: 992px) and (max-width: 1200px) {
.left-content {
min-width: 450px;
padding: 0 20px;
margin-bottom: 40px;
}
.emi-content {
max-width: 100%;
padding: 0 20px;
.col-lg-7 {
width: 60%;
float: left;
}
.col-lg-5 {
width: 40%;
float: left;
}
.emi-footer {
margin-top: 15px;
}
}
.graph-container {
padding: 0 10px;
}
}
@media (min-width: 1200px) and (max-width: 1329px) {
.container {
padding: 0 35px;
}
}
@media (min-width: 1330px) {
.container {
width: 1300px;
}
}
.e-grid#scheduleGrid > .e-gridheader .e-headercontent .e-table colgroup col:first-child,
.e-grid .e-content .e-table#scheduleGrid_content_table > colgroup col:first-child {
width: 0px !important;
}
.e-grid .e-detailrowexpand > div,
.e-grid .e-detailrowcollapse > div {
display: none;
}
.e-grid .e-row .e-row-toggle {
display: inline-block;
}

3
src/assets/styles.scss Normal file
Просмотреть файл

@ -0,0 +1,3 @@
//sass-lint:disable-all
@import 'ej2-inputs/styles/bootstrap.scss';
@import 'ej2-grids/styles/bootstrap.scss';

Просмотреть файл

@ -0,0 +1,8 @@
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
production: false
};

Двоичные данные
src/favicon.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.4 KiB

29
src/index.html Normal file
Просмотреть файл

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Essential JS 2 for Angular - Loan Calculator</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="Essential JS 2 for Angular - Loan Calculator">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="icon" type="image/x-icon" href="./favicon.ico">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Raleway:500,600" rel="stylesheet">
<link href="./styles.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reflect-metadata/0.1.10/Reflect.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.8.18/zone.min.js"></script>
<script type="text/javascript">
if (/MSIE \d|Trident.*rv:/.test(navigator.userAgent)) {
document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.5/bluebird.min.js"><\/script>');
}
</script>
</head>
<body>
<app-root></app-root>
</body>
<!--<script src="./dist/common.js"></script>-->
</html>

13
src/main.ts Normal file
Просмотреть файл

@ -0,0 +1,13 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));

66
src/polyfills.ts Normal file
Просмотреть файл

@ -0,0 +1,66 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';
/**
* Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

20
src/test.ts Normal file
Просмотреть файл

@ -0,0 +1,20 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

13
src/tsconfig.app.json Normal file
Просмотреть файл

@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "es2015",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}

19
src/tsconfig.spec.json Normal file
Просмотреть файл

@ -0,0 +1,19 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"baseUrl": "./",
"module": "commonjs",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

25
tsconfig-aot.json Normal file
Просмотреть файл

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"declaration": true,
"lib": ["es2015", "dom"],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"types": ["requirejs"]
},
"exclude": [
"node_modules"
],
"files": [
"src/app/app.module.ts",
"src/app/main.ts"
],
"angularCompilerOptions": {
"skipMetadataEmit": true
}
}

28
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "es5",
"module": "amd",
"declaration": true,
"removeComments": true,
"noLib": false,
"lib": ["es2015", "dom"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"sourceMap": true,
"pretty": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitUseStrict": false,
"noFallthroughCasesInSwitch": true,
"allowJs": false,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"types": ["requirejs"]
},
"exclude": [
"node_modules"
],
"compileOnSave": false
}