Starting to see the light

This commit is contained in:
2018-09-09 21:06:34 -04:00
parent 911a8bac49
commit c931c03071
19 changed files with 442 additions and 70 deletions

47
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -47,6 +47,7 @@ export class AlertService {
}
alert(type: AlertType, message: string, keepAfterRouteChange = false) {
this.clear();
this.keepAfterRouteChange = keepAfterRouteChange;
this.subject.next(<Alert>{ type: type, message: message });
}

View File

@@ -3,10 +3,30 @@ import { Injectable, Output, EventEmitter } from '@angular/core';
@Injectable()
export class EmitcomService {
@Output() change: EventEmitter<number> = new EventEmitter();
@Output() change: EventEmitter<any> = 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() { }

View File

@@ -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"];
});
}
}

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -1,3 +1,11 @@
<app-search-view></app-search-view>
<app-stock-view></app-stock-view>
<div class="flexContainer">
<div class="subContainerS1">
<app-search-view></app-search-view>
</div>
<div class="subContainerS2">
<app-stock-view></app-stock-view>
</div>
<div class="watcherViewContainer" >
<app-watcher-view></app-watcher-view>
</div>
</div>

View File

@@ -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;
}

View File

@@ -1,5 +1,5 @@
<div class="lrContainer">
<mat-card class="lrCard">
<div class="cardContainer">
<mat-card id="searchCard">
<mat-card-header>
<mat-card-title>
<h2>Search IPO's</h2>
@@ -16,19 +16,16 @@
<mat-divider></mat-divider>
<div>
<mat-list>
<mat-list-item *ngFor="let company of searchResults" (click)="onSelect(company.Symbol)">
<div class="fullWidth">
<p class="alignleft">{{company.Name}}</p>
<img class="ipoMiniLogo alignright" src="{{searchResultLogos[company.Symbol].logo.url}}">
<div style="clear: both;"></div>
<mat-nav-list>
<mat-list-item *ngFor="let company of searchResults">
<div class="ipoContainer">
<img class="ipoMiniLogo" src="{{searchResultLogos[company.Symbol].logo.url}}" (error)="imgError($event)">
<span (click)="onSelect(company.Symbol)" >{{company.Name}}</span>
<div class="spacer"></div>
<mat-icon class="addWatchListIcon" fontSet="fa" fontIcon="fa-plus-circle" ></mat-icon>
</div>
</mat-list-item>
</mat-list>
</mat-nav-list>
</div>
</mat-card>
</div>

View File

@@ -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;
}
}

View File

@@ -0,0 +1,12 @@
.cardContainer{
margin-top: 5px;
height:450px;
}
#stockCard{
height:450px;
}
.chartContainer canvas{
width:800px ;
}

View File

@@ -1,3 +1,12 @@
<p (change)='onChange($event)' >
?
</p>
<div class="cardContainer">
<mat-card id="stockCard">
<mat-card-header>
<mat-card-title>
<h2>Graph View</h2>
</mat-card-title>
</mat-card-header>
<div class="chartContainer">
<canvas #chartView >{{chart}}</canvas>
</div>
</mat-card>
</div>

View File

@@ -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" );
});
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,46 @@
<div class="cardContainer">
<mat-card id="watcherCard">
<mat-card-header>
<mat-card-title>
<h2>Watch List</h2>
</mat-card-title>
</mat-card-header>
<div>
<!--
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef mat-sort-header> No. </th>
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Weight </th>
<td mat-cell *matCellDef="let element"> {{element.weight}} </td>
</ng-container>
<ng-container matColumnDef="symbol">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Symbol </th>
<td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
-->
</div>
</mat-card>
</div>

View File

@@ -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<WatcherViewComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ WatcherViewComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(WatcherViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -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() {
}
}

View File

@@ -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;
}