diff --git a/package-lock.json b/package-lock.json index 2cc441d..34d09d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -592,6 +592,12 @@ "semver-intersect": "^1.1.2" } }, + "@types/chart.js": { + "version": "2.7.32", + "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.32.tgz", + "integrity": "sha512-e5NsIt0n794piE2y1p4r0nIN0z4AEoKVhkam1UFZHD0qkgNag3pt8/eSq5q2kZbZzXL5K06hKKIdJlGce46mOQ==", + "dev": true + }, "@types/jasmine": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.8.tgz", @@ -1832,6 +1838,39 @@ "supports-color": "^5.3.0" } }, + "chart.js": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.2.tgz", + "integrity": "sha512-90wl3V9xRZ8tnMvMlpcW+0Yg13BelsGS9P9t0ClaDxv/hdypHDr/YAGf+728m11P5ljwyB0ZHfPKCapZFqSqYA==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", + "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", + "requires": { + "chartjs-color-string": "^0.5.0", + "color-convert": "^0.5.3" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + } + } + }, + "chartjs-color-string": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", + "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", + "requires": { + "color-name": "^1.0.0" + } + }, "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", @@ -1999,8 +2038,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.1.2", @@ -6470,6 +6508,11 @@ "minimist": "0.0.8" } }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", diff --git a/package.json b/package.json index b0ffe2f..7aff0f1 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@angular/platform-browser-dynamic": "^6.1.0", "@angular/router": "^6.1.0", "angular-in-memory-web-api": "^0.6.1", + "chart.js": "^2.7.2", "core-js": "^2.5.4", "font-awesome": "^4.7.0", "rxjs": "^6.0.0", @@ -34,6 +35,7 @@ "@angular/cli": "~6.1.5", "@angular/compiler-cli": "^6.1.0", "@angular/language-service": "^6.1.0", + "@types/chart.js": "^2.7.32", "@types/jasmine": "~2.8.6", "@types/jasminewd2": "~2.0.3", "@types/node": "~8.9.4", diff --git a/src/app/_services/alert.service.ts b/src/app/_services/alert.service.ts index 29edfa5..72d8eb2 100644 --- a/src/app/_services/alert.service.ts +++ b/src/app/_services/alert.service.ts @@ -47,6 +47,7 @@ export class AlertService { } alert(type: AlertType, message: string, keepAfterRouteChange = false) { + this.clear(); this.keepAfterRouteChange = keepAfterRouteChange; this.subject.next({ type: type, message: message }); } diff --git a/src/app/_services/emitcom.service.ts b/src/app/_services/emitcom.service.ts index b4d2608..93632bb 100644 --- a/src/app/_services/emitcom.service.ts +++ b/src/app/_services/emitcom.service.ts @@ -3,10 +3,30 @@ import { Injectable, Output, EventEmitter } from '@angular/core'; @Injectable() export class EmitcomService { - @Output() change: EventEmitter = new EventEmitter(); + @Output() change: EventEmitter = new EventEmitter(); - sendData( data: number ) { - this.change.emit( data ); + sendData( data: string ) { + let sendData ={ + type: "ipo", + data: data + }; + this.change.emit( sendData ); + } + + destroyChart() { + let sendData ={ + type: "action", + data: "destroyChart" + }; + this.change.emit( sendData ); + } + + addToWatcher( data: string ){ + let sendData ={ + type: "watcher", + data: data + }; + this.change.emit( sendData ); } constructor() { } diff --git a/src/app/_services/stock.service.ts b/src/app/_services/stock.service.ts index 27dccdd..0ea1ec3 100644 --- a/src/app/_services/stock.service.ts +++ b/src/app/_services/stock.service.ts @@ -41,4 +41,12 @@ export class StockService { }); } + + getCharByTime( symbol, timeFrame ){ + const url2 = "https://api.iextrading.com/1.0/stock/" + symbol + "/chart/1m?callback=JSONP_CALLBACK"; + return this.jsonp.request(url2) + .map(chartData => { + return chartData["_body"]; + }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index fa3c899..afc94cf 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -26,8 +26,9 @@ import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatIconRegistry } from "@angular/material"; -import {MatDividerModule} from '@angular/material/divider'; -import {MatListModule} from '@angular/material/list'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatListModule } from '@angular/material/list'; +import { WatcherViewComponent } from './watcher-view/watcher-view.component'; @NgModule({ @@ -39,6 +40,7 @@ import {MatListModule} from '@angular/material/list'; AlertComponent, SearchViewComponent, StockViewComponent, + WatcherViewComponent, ], imports: [ BrowserModule, @@ -56,7 +58,7 @@ import {MatListModule} from '@angular/material/list'; MatIconModule, MatDividerModule, MatListModule, - JsonpModule + JsonpModule ], providers: [ AlertService, diff --git a/src/app/home/home.component.css b/src/app/home/home.component.css index e69de29..0085b25 100644 --- a/src/app/home/home.component.css +++ b/src/app/home/home.component.css @@ -0,0 +1,30 @@ +.flexContainer{ + width:100%; + height:550px; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; +} + +.subContainerS1{ + height:500px; + width:450px; + margin: 5px 10px; +} + +.subContainerS2{ + height:500px; + width:800px; + margin: 5px 10px; +} + +.watcherViewContainer{ + margin-top:15px; + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; +} \ No newline at end of file diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index f7356af..04a0b09 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,3 +1,11 @@ - - - \ No newline at end of file +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/src/app/search-view/search-view.component.css b/src/app/search-view/search-view.component.css index e69de29..8847745 100644 --- a/src/app/search-view/search-view.component.css +++ b/src/app/search-view/search-view.component.css @@ -0,0 +1,34 @@ +.cardContainer{ + margin-top: 5px; + height:450px; +} + +#searchCard{ + height:450px; +} + +.ipoContainer{ + width:100%; + display: flex; + align-items:center; +} + +.ipoMiniLogo{ + width:35px; + height: auto; + border-radius: 50%; + text-align: right; + margin-right: 5px; +} + +.spacer{ + flex: 1 1 auto; +} + +.addWatchListIcon{ + width:auto; + font-size: 25px; + display: flex; + align-items:center; + color: #018786; +} \ No newline at end of file diff --git a/src/app/search-view/search-view.component.html b/src/app/search-view/search-view.component.html index 796984a..5104bcf 100644 --- a/src/app/search-view/search-view.component.html +++ b/src/app/search-view/search-view.component.html @@ -1,5 +1,5 @@ -
- +
+

Search IPO's

@@ -16,19 +16,16 @@
- - -
-

{{company.Name}}

- -
+ + +
+ + {{company.Name}} +
+
- +
- - - -
\ No newline at end of file diff --git a/src/app/search-view/search-view.component.ts b/src/app/search-view/search-view.component.ts index 92bb9b1..952fe79 100644 --- a/src/app/search-view/search-view.component.ts +++ b/src/app/search-view/search-view.component.ts @@ -3,6 +3,7 @@ import { Component, OnInit, HostListener } from '@angular/core'; import { EmitcomService } from '../_services/emitcom.service'; import { NasdaqSearchService } from '../_services/nasdaq-search.service'; import { StockService } from '../_services/stock.service'; +import { AlertService } from '../_services/alert.service'; @Component({ selector: 'app-search-view', @@ -17,7 +18,8 @@ export class SearchViewComponent implements OnInit { constructor( private emitcomService: EmitcomService, private nasdaqSearchService: NasdaqSearchService, - private stockService: StockService + private stockService: StockService, + private alertService: AlertService ) { } ngOnInit() { @@ -25,28 +27,30 @@ export class SearchViewComponent implements OnInit { } searchCompany( searchData ){ - if( searchData.length > 3 ){ + /* clear out IPO list at each key press */ + this.clearIpoList(); + this.alertService.clear(); + + if( searchData.length >= 3 ){ this.nasdaqSearchService.query( searchData ) .subscribe( data => { if( Object.keys(data).length === 0 ){ - //this.alertService.error( "Bad username or password" ); + /* We only need this here becasue i'm not great at regular expressions */ + this.alertService.error( "No IPO's Found" ); }else{ - //console.log(data[0].userName); - //localStorage.setItem('currentUser', JSON.stringify(data[0])); - //this.router.navigate(["home"]); - - //console.log( data ); - //this.searchResults = data; if( data.length > 0 ){ - this.getLogos( data ); + /* Data found in mock DB slice out the first six results and call method for logos. */ + this.getLogos( data.slice(0, 6) ); } } }, error => { - //console.log(error) - //this.alertService.error( "Bad username or password" ); + /* 404 not found in mock DB send alert to user. */ + this.alertService.error( "No IPO's Found" ); }); + }else if( searchData.length === 0 ){ + this.emitcomService.destroyChart(); } } @@ -56,38 +60,31 @@ export class SearchViewComponent implements OnInit { .subscribe( data => { if( Object.keys(data).length === 0 ){ - //this.alertService.error( "Bad username or password" ); - console.log("FAIL1"); + /* We only have to check for the object key becasue i'm not great with regex... */ + /* If nothing is found do nothing */ }else{ - //console.log(data[0].userName); - //localStorage.setItem('currentUser', JSON.stringify(data[0])); - //this.router.navigate(["home"]); - console.log("good?"); - console.log( data ); - //this.searchResults = data; - //this.companySearchResults( data ); + /* Now that we have the search results and company IPO logos to match we can set the data and let the template take over. */ this.searchResults = companySearchResults; this.searchResultLogos = data; } }, error => { - console.log("FAIL2"); - console.log(error) - //this.alertService.error( "Bad username or password" ); + /* in this circumstance we don't care about error as the logo return is static from the service. We will find another way to validate images. */ }); } + imgError( event ){ + event.target.src = "http://www.lazypug.net/img/pug.jpg"; + } + onSelect( selectedSymbol ){ + /* On user click call sendData method on the service to emit an event to be picked up on the stock-view componet */ this.emitcomService.sendData( selectedSymbol ); } - - /* - @HostListener('click') - click() { - this.emitcomService.sendData( 42 ); - } - */ + clearIpoList(){ + this.searchResults = null; + } } diff --git a/src/app/stock-view/stock-view.component.css b/src/app/stock-view/stock-view.component.css index e69de29..5cce6f2 100644 --- a/src/app/stock-view/stock-view.component.css +++ b/src/app/stock-view/stock-view.component.css @@ -0,0 +1,12 @@ +.cardContainer{ + margin-top: 5px; + height:450px; +} + +#stockCard{ + height:450px; +} + +.chartContainer canvas{ + width:800px ; +} diff --git a/src/app/stock-view/stock-view.component.html b/src/app/stock-view/stock-view.component.html index a9d60a9..7455594 100644 --- a/src/app/stock-view/stock-view.component.html +++ b/src/app/stock-view/stock-view.component.html @@ -1,3 +1,12 @@ -

-? -

+
+ + + +

Graph View

+
+
+
+ {{chart}} +
+
+
\ No newline at end of file diff --git a/src/app/stock-view/stock-view.component.ts b/src/app/stock-view/stock-view.component.ts index a7f4c0c..265a967 100644 --- a/src/app/stock-view/stock-view.component.ts +++ b/src/app/stock-view/stock-view.component.ts @@ -1,5 +1,7 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { EmitcomService } from '../_services/emitcom.service'; +import { StockService } from '../_services/stock.service'; +import * as Chart from 'chart.js' @Component({ selector: 'app-stock-view', @@ -8,15 +10,110 @@ import { EmitcomService } from '../_services/emitcom.service'; }) export class StockViewComponent implements OnInit { + @ViewChild('chartView') private chartRef; + chart: any; + constructor( - private emitcomService: EmitcomService + private emitcomService: EmitcomService, + private stockService: StockService ) { } ngOnInit() { - this.emitcomService.change.subscribe(data => { - console.log( data ) + + this.emitcomService.change.subscribe(data => { + if( data.type == "action" && data.data == "destroyChart" ){ + if( this.chart !== undefined ){ + this.chart.destroy(); + } + }else if( data.type == "ipo" ){ + this.getStockByChart( data.data, "1m" ); + } + }); + + + } + + + getStockByChart( symbol, timeFrame ){ + + this.stockService.getCharByTime( symbol, timeFrame ) + .subscribe( + data => { + if( Object.keys(data).length === 0 ){ + //this.alertService.error( "Bad username or password" ); + }else{ + //console.log(data[0].userName); + //localStorage.setItem('currentUser', JSON.stringify(data[0])); + //this.router.navigate(["home"]); + + //console.log( data ); + //this.searchResults = data; + if( data.length > 0 ){ + console.log( data ); + //[n].close + //[n].date + let date = data.map(data => data.date); + let close = data.map(data => data.close); + + // console.log(date); + // console.log(close); + + if( this.chart !== undefined ){ + this.chart.destroy(); + } + + + this.chart = new Chart(this.chartRef.nativeElement, { + type: 'line', + data:{ + labels: date, + datasets: [{ + data: close, + borderColor: '#0097A7', + fill: false + }] + }, + options:{ + legend:{ + display: false + }, + scales:{ + xAxes:[{ + display: true + }], + yAxes:[{ + display: true + }] + } + } + }); + + + /* + let dates = []; + dates.forEach((res) =>{ + let jsdate = new Date(res * 1000) + dates.push( jsdate.toLocalTimeString('en') ) + }) + */ + + + + } + } + }, + error => { + //console.log(error) + //this.alertService.error( "Bad username or password" ); + }); + + } + + + } + diff --git a/src/app/watcher-view/watcher-view.component.css b/src/app/watcher-view/watcher-view.component.css new file mode 100644 index 0000000..55269c4 --- /dev/null +++ b/src/app/watcher-view/watcher-view.component.css @@ -0,0 +1,29 @@ +@media only screen and (min-width : 600px){ + #watcherCard{ + width: 400px; + } +} + +@media only screen and (min-width : 800px){ + #watcherCard{ + width: 600px; + } +} + +@media only screen and (min-width : 1000px) { + #watcherCard{ + width: 900px; + } +} + +@media only screen and (min-width : 1200px) { + #watcherCard{ + width: 1100px; + } +} + +@media only screen and (min-width : 1300px) { + #watcherCard{ + width: 1200px; + } +} \ No newline at end of file diff --git a/src/app/watcher-view/watcher-view.component.html b/src/app/watcher-view/watcher-view.component.html new file mode 100644 index 0000000..444fde3 --- /dev/null +++ b/src/app/watcher-view/watcher-view.component.html @@ -0,0 +1,46 @@ +
+ + + +

Watch List

+
+
+
+ + + +
+ + +
+
\ No newline at end of file diff --git a/src/app/watcher-view/watcher-view.component.spec.ts b/src/app/watcher-view/watcher-view.component.spec.ts new file mode 100644 index 0000000..1985c75 --- /dev/null +++ b/src/app/watcher-view/watcher-view.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WatcherViewComponent } from './watcher-view.component'; + +describe('WatcherViewComponent', () => { + let component: WatcherViewComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ WatcherViewComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WatcherViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/watcher-view/watcher-view.component.ts b/src/app/watcher-view/watcher-view.component.ts new file mode 100644 index 0000000..4147e57 --- /dev/null +++ b/src/app/watcher-view/watcher-view.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-watcher-view', + templateUrl: './watcher-view.component.html', + styleUrls: ['./watcher-view.component.css'] +}) +export class WatcherViewComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/styles.css b/src/styles.css index 12ac91d..aa8ce0e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,7 +1,7 @@ @import "~@angular/material/prebuilt-themes/indigo-pink.css"; .lrCard{ - min-width: 300px; + min-width: 400px; width:20%; margin:0 auto; } @@ -29,12 +29,9 @@ } .alert-danger mat-card{ - background-color: pink; + background-color: #C51162; +} +.alert-danger p{ + color: #FFF } -.ipoMiniLogo{ - width:35px; - height: auto; - border-radius: 50%; - text-align: right; -} \ No newline at end of file