Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/frontend/update ui #9

Merged
merged 13 commits into from Oct 8, 2018
Binary file modified frontend/bitwatch-client/public/favicon.ico
Binary file not shown.
42 changes: 23 additions & 19 deletions frontend/bitwatch-client/public/index.html
@@ -1,17 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--

<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,700" rel="stylesheet">
<!--
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,700" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Expand All @@ -20,14 +22,15 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
<title>bitwatch - Cryptocurrency Tracker</title>
</head>

<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

Expand All @@ -37,5 +40,6 @@
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
</body>

</html>
Binary file added frontend/bitwatch-client/public/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 23 additions & 29 deletions frontend/bitwatch-client/src/components/App.js
Expand Up @@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import axios from 'axios';

import 'styles/App.css';
import { Welcome, CardList } from 'components';
import { Welcome, CardList, Search } from 'components';
import { setProductName, setProducts, savePrices } from 'actions';

class App extends Component {
Expand All @@ -13,43 +13,36 @@ class App extends Component {
product: '',
productsFetching: false,
pricesFetching: false,
showWelcome: true
showWelcome: false,
showCards: false,
}
}

handleInput = event => {
this.setState({
product: event.target.value
});
getValue = (valueFromFilter) => {
this.setState({product: valueFromFilter});
}

handleSubmit = async event => {
event.preventDefault();
this.setState ({showWelcome: false});
if (this.state.product.length) await this.props.setProductName(this.state.product);
this.setState({product: ''});
if (this.props.productName) await this.getPrices(this.props.productName);
if (this.state.product.length) {
this.setState ({showWelcome: false, showCards: true});
if (this.state.product.length) await this.props.setProductName(this.state.product.toUpperCase());
if (this.props.productName) await this.getPrices(this.props.productName);
}
}

getProducts = async () => {
this.setState({productsFetching: true});
const response = await axios.get(`${process.env.REACT_APP_BASE_URL}/products`);
this.props.setProducts(response.data);
this.setState({productsFetching: false});
this.setState({productsFetching: false, showWelcome: true});
}

getPrices = async (product) => {
this.setState({pricesFetching: true});
const response = await axios.get(`${process.env.REACT_APP_BASE_URL}/products/${product}/prices`);
this.props.savePrices(response.data);
if (response.data.prices.some(el => el.price>0)) await this.props.savePrices(response.data);
this.setState({pricesFetching: false});
console.log(this.props.prices);
}

renderDataListOptions = () => {
return this.props.products.map((product, i) => (
<option key = {i} value={product} />
))
}

componentDidMount() {this.getProducts()};
Expand All @@ -59,21 +52,22 @@ class App extends Component {
<div className='App'>
{!this.state.productsFetching ? (
<div className='Header'>
<h1>bitwatch</h1>
{/* <h3>The cryptocurrency tracker</h3> */}
<img src='logo.png' alt='logo' />
<form onSubmit={this.handleSubmit}>
<input type='text' onChange={this.handleInput} list='products' value={this.state.product} placeholder='Select product' />
<datalist id='products'>
{this.renderDataListOptions()}
</datalist>
<input type='submit' value='Get prices' />
<Search setValue={this.getValue} value={this.state.product} options={this.props.products} />
<input style={!this.state.product ? {
backgroundColor: '#C0C0C0',
backgroundImage: 'none',
boxShadow: 'none'}
: {}} type='submit' value='Get prices' />
</form>
{this.props.productName.length > 0 ?
{this.props.prices[this.props.productName] ?
(<h3>Current prices for</h3>) : ''}
<h1>{this.props.productName} </h1>
{this.props.prices[this.props.productName] ?
<h1 className='ProductName'>{this.props.productName} </h1> : <h1> </h1>}
</div>
) : ''}
{!this.state.pricesFetching ? <CardList prices = {this.props.prices} productName = {this.props.productName} /> : ''}
{!this.state.pricesFetching && this.props.productName ? <CardList prices = {this.props.prices} productName = {this.props.productName} /> : ''}
{this.state.showWelcome ? <Welcome /> : ''}
</div>
);
Expand Down
28 changes: 22 additions & 6 deletions frontend/bitwatch-client/src/components/CardList.js
@@ -1,17 +1,33 @@
import React from 'react';

import 'styles/CardList.css';

const CardList = ({productName, prices}) => {
const renderCardItems = (product) => {
return prices[product].map((el, i) => (
<div key={i}>
<strong>{el.exchange}</strong>
<h2>{el.price}</h2>
if (!prices[product]) return (
<div className='NotFound OneCard'>
<h3>Not Found: {product}</h3>
<p>Sorry, we couldn't find prices for this product. Perhaps it was mispelt. <br/><br/>Please try again.</p>
</div>
))
)
return prices[product].map((el, i) => {
if (el.price === -1) return (
<div className='NotFound' key={i}>
<strong>{el.exchange}</strong>
<h2>Sorry, no price found</h2>
</div>
);
return (
<div key={i}>
<strong>{el.exchange}</strong>
<h2>{el.price}</h2>
</div>
)})
};

return (
<div className='CardList'>
{ prices[productName] ? renderCardItems(productName): ''}
{renderCardItems(productName)}
</div>
);
}
Expand Down
105 changes: 105 additions & 0 deletions frontend/bitwatch-client/src/components/Search.js
@@ -0,0 +1,105 @@
import React, { Component } from 'react';

import 'styles/Search.css';

class Search extends Component {
constructor(props) {
super(props);
this.state = {
showDropdown: false,
showIcon: false
}
}

handleInput = event => {
const product = event.target.value.toUpperCase();
this.filterOptions(product);
this.props.setValue(event.target.value);
console.log(event.target.value);
}

saveInput = event => {
const product = event.target.innerHTML;
console.log(product);
this.props.setValue(product);
this.setState({showDropdown: false});
}

clearInput = event => {
if (event.keyCode === 8) {
this.props.setValue('');
this.filterOptions('');
this.setState({showDropdown: true})
}
};

filterOptions = (filter) => {
let ul = document.querySelector('ul');
let li = ul.getElementsByTagName('LI');
for (let i = 0; i < li.length; i++) {
if (li[i].innerHTML.toUpperCase().includes(filter)) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}

handleClick = event => {
console.log(this.state.showDropdown)
if (this.state.showDropdown) {
if (this.node.contains(event.target)) return;
else this.hideList();
}
}

showList = () => {
this.setState({showDropdown: true})
};

hideList = event => {
if (this.state.showDropdown) this.setState({showDropdown: false});
}

showIcon = () => {
this.setState({showIcon: true})
}

hideIcon = () => {
this.setState({showIcon: false})
}

renderOptions = () => {
if (this.props.options.length) {
console.log(this.props.options);
}
return this.props.options.map((product, i) => (
<li key = {i} onClick={this.saveInput}>{product}</li>
))
}

componentWillMount() {
document.addEventListener('click',this.handleClick, false);
}

componentWillUnMount() {
document.removeEventListener('click',this.handleClick, false);
}

render() {
return (
<div className='Search' ref={node => this.node=node}>
<input type='text' onClick={this.showList} onKeyUp={this.clearInput} onMouseEnter={this.showIcon} onMouseLeave={this.hideIcon} onChange={this.handleInput} value={this.props.value} placeholder='Select product' />
{this.state.showIcon || this.state.showDropdown ? (<i className="Icon material-icons" onClick={this.showList} onMouseEnter={this.showIcon} onMouseLeave={this.hideIcon}>expand_more</i>) : ''}
<ul style={this.state.showDropdown ? {} : {display: 'none'}}>
{this.renderOptions()}
</ul>
{/* <ul ref={node => this.node=node}>
{this.renderOptions()}
</ul> */}
</div>
)
}
}

export default Search;
20 changes: 13 additions & 7 deletions frontend/bitwatch-client/src/components/Welcome.js
@@ -1,12 +1,18 @@
import React from 'react';

const Welcome = ({somethinghere}) => (
<div className='Welcome'>
<p>
Welcome to <strong>bitwatch</strong>.
<br /><br />
Please select a product from the input above to see the current prices across the BTX, BNB and BFX exchanges.
</p>
import 'styles/Welcome.css';

const Welcome = () => (
<div className='Welcome animated animatedFadeInUp fadeInUp'>
<div className='Content'>
<p>
Welcome to
<br />
<span><strong>bitwatch</strong></span>.</p>
<p><strong>Bitwatch</strong> shows
the current prices for shared cryptocurrencies across the BTX, BNB and BFX exchanges.</p>
<p>Please select a product from the input above to get started.</p>
</div>
</div>
);

Expand Down
3 changes: 2 additions & 1 deletion frontend/bitwatch-client/src/components/index.js
@@ -1,3 +1,4 @@
export { default as App } from 'components/App';
export { default as Welcome } from 'components/Welcome';
export { default as CardList } from 'components/CardList';
export { default as CardList } from 'components/CardList';
export { default as Search } from 'components/Search';