├── .gitignore ├── README.md ├── images ├── Dashboard.png ├── Full_coin_list.png └── setting.png ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── App.css ├── App.js ├── Button.js ├── CoinList.js ├── Dashboard.js ├── HighchartsConfig.js ├── HighchartsTheme.js ├── NavBar.js ├── Search.js ├── Style.js ├── Text.js ├── index.css ├── index.js └── serviceWorker.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ### Benefits of choosing create-react-app 4 | * Official React.js starter project by Facebook 5 | * "Create React apps with no build configuration" 6 | * Only 3 commands to an enterprise-grade web application 7 | 8 | - npm create-react-app react-dashboard 9 | - cd react-dashboard 10 | - npm start 11 | 12 | * hot reloading by default 13 | * immediately start coding 14 | * command for optimized production build 15 | 16 | ### Below are the technologies we have used for this Project
17 | 18 | * React - It is an open-source JavaScript library which is used for building user interfaces specifically for single page applications. 19 | * Styled-components - It is a library that gives you the way to handling styling using CSS-in-JS. 20 | * CSS Grid - The CSS Grid Layout Module offers a grid-based layout system, with rows and columns, making it easier to design web pages without having to use floats and positioning. 21 | * Highcharts - Interactive JavaScript charts for your web pages. 22 | * cryptocompare API - This is the best way to get intraday trading data for cryptocurrencies. 23 | * Async/await - There's a special syntax to work with promises in a more comfortable fashion 24 | * Fuzzy Search - A fuzzy search is a process that locates Web pages that are likely to be relevant to a search argument even when the argument does not exactly correspond to the desired information. 25 | * Lodash - for functional programming 26 | * Webkit inspector - For debugging 27 | * localstorage - to set and retrieve data from web browser 28 | * Google fonts - fonts 29 | 30 | ### UI screen for reference 31 | full view 32 | setting 33 | dashboard 34 | 35 | ### Thanks & references 36 | * Thanks to Udemy & Digital Hermits -------------------------------------------------------------------------------- /images/Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karkranikhil/react-dashboard/f122e6b60dc319a1c096b0328010aeb1edd85f58/images/Dashboard.png -------------------------------------------------------------------------------- /images/Full_coin_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karkranikhil/react-dashboard/f122e6b60dc319a1c096b0328010aeb1edd85f58/images/Full_coin_list.png -------------------------------------------------------------------------------- /images/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karkranikhil/react-dashboard/f122e6b60dc319a1c096b0328010aeb1edd85f58/images/setting.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-dashboard", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "cryptocompare": "^0.5.0", 7 | "fuzzy": "^0.1.3", 8 | "lodash": "^4.17.11", 9 | "moment": "^2.22.2", 10 | "react": "^16.6.0", 11 | "react-dom": "^16.6.0", 12 | "react-highcharts": "^16.0.2", 13 | "react-scripts": "2.0.5", 14 | "styled-components": "^4.0.2" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": [ 26 | ">0.2%", 27 | "not dead", 28 | "not ie <= 11", 29 | "not op_mini all" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karkranikhil/react-dashboard/f122e6b60dc319a1c096b0328010aeb1edd85f58/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 23 | React App 24 | 25 | 26 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background-color:#010e2c; 3 | color:white; 4 | font-family: 'Do Hyeon', sans-serif; 5 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import styled from 'styled-components' 4 | import NavBar from './NavBar' 5 | import CoinList from './CoinList' 6 | import cc from 'cryptocompare' 7 | import _ from 'lodash' 8 | import Search from './Search' 9 | import {ConfirmButton} from './Button' 10 | import fuzzy from 'fuzzy' 11 | import Dashboard from './Dashboard' 12 | import moment from 'moment' 13 | const Content = styled.div`` 14 | const AppLayout = styled.div` 15 | padding:40px; 16 | ` 17 | export const CenterDiv = styled.div` 18 | display:grid; 19 | justify-content:center; 20 | ` 21 | export const SelectedFav = styled.div` 22 | margin-top: 40px; 23 | ` 24 | export const NotFound = styled.div` 25 | padding-left: 140px; 26 | color: red; 27 | ` 28 | const checkFirstVisit=()=>{ 29 | let cryptoDashData = JSON.parse(localStorage.getItem('cryptoDash')) 30 | if(!cryptoDashData){ 31 | return { 32 | firstVisit:true, 33 | page:'settings' 34 | } 35 | } 36 | let {favorites, currentFavorite} = cryptoDashData; 37 | return { 38 | favorites, 39 | currentFavorite 40 | } 41 | } 42 | const MAX_FAVORITES = 10 43 | const TIME_UNITS =10 44 | class App extends Component { 45 | state = { 46 | page: 'dashboard', 47 | notFound:false, 48 | favorites: ['ETH', 'BTC', 'XMR', 'DOGE', 'EOS'], 49 | timeInterval: 'months', 50 | ...checkFirstVisit() 51 | }; 52 | componentDidMount=()=>{ 53 | this.fetchHistorical() 54 | this.fetchCoins() 55 | this.fetchPrice() 56 | } 57 | validateFavorites=(coinList)=>{ 58 | let validateFavorites=[] 59 | this.state.favorites.forEach(favorite=>{ 60 | if(coinList[favorite]){ 61 | validateFavorites.push(favorite) 62 | } 63 | }) 64 | return validateFavorites 65 | } 66 | fetchPrice=async()=>{ 67 | if(this.state.firstVisit) return 68 | let prices 69 | try{ 70 | prices= await this.prices() 71 | //prices = prices.filter(item=>Object.keys(item).length) 72 | } catch(e){ 73 | this.setState({error:true}) 74 | } 75 | this.setState({prices}) 76 | } 77 | fetchHistorical=async()=>{ 78 | if(this.state.firstVisit) return 79 | let result = await this.historical() 80 | let historical=[{ 81 | name:this.state.currentFavorite, 82 | data:result.map((ticker,index)=>[moment().subtract({[this.state.timeInterval]:TIME_UNITS - index}).valueOf(), ticker.USD]) 83 | }] 84 | this.setState({historical}) 85 | } 86 | historical=()=>{ 87 | let promises =[] 88 | for(let units = TIME_UNITS; units>0; units--){ 89 | promises.push(cc.priceHistorical(this.state.currentFavorite,['USD'], moment().subtract({[this.state.timeInterval]:units}).toDate())) 90 | } 91 | return Promise.all(promises) 92 | } 93 | prices=async ()=>{ 94 | // let promises=[]; 95 | // this.state.favorites.forEach(sym=>{ 96 | // promises.push(cc.priceFull(sym,'USD') 97 | // ) 98 | // }) 99 | // return Promise.all(promises) 100 | let returnData =[]; 101 | for(let i=0;i{ 112 | let coinList = (await cc.coinList()).Data 113 | this.setState({coinList, favorites:this.validateFavorites(coinList)}) 114 | } 115 | 116 | displayingDashboard = () =>this.state.page === 'dashboard' 117 | displayingSettings = () =>this.state.page === 'settings' 118 | firstVisitMessage=()=>{ 119 | if(this.state.firstVisit){ 120 | return
Welcome to CryptoDash, please select your favorite coins to begin.
121 | } 122 | } 123 | confirmFavorites=()=>{ 124 | let currentFavorite = this.state.favorites[0] 125 | this.setState({ 126 | firstVisit:false, 127 | page:'dashboard', 128 | prices:null, 129 | currentFavorite, 130 | historical:null 131 | },()=>{ 132 | this.fetchPrice() 133 | this.fetchHistorical() 134 | }) 135 | localStorage.setItem('cryptoDash', JSON.stringify({ 136 | favorites:this.state.favorites, 137 | currentFavorite 138 | })); 139 | } 140 | settingsContent =()=>{ 141 | return
142 | {this.firstVisitMessage()} 143 |
144 | Selected Favorites 145 | {CoinList.call(this, true)} 146 | 147 | {this.state.favorites && this.state.favorites.length >0 && 148 | Confirm Favorites 149 | } 150 | 151 | {Search.call(this)} 152 | {this.state.notFound && No coin found} 153 | {CoinList.call(this)} 154 |
155 |
156 | } 157 | loadingContent=()=>{ 158 | if(!this.state.coinList){ 159 | return
Loading coin...
160 | } 161 | if(!this.state.firstVisit && !this.state.prices){ 162 | return
Loading prices...
163 | } 164 | } 165 | addCointToFavorites =(key)=>{ 166 | let favorites = [...this.state.favorites] 167 | if(favorites.length < MAX_FAVORITES){ 168 | favorites.push(key) 169 | this.setState({favorites}) 170 | } 171 | } 172 | removeCoinFromFavorites=(key)=>{ 173 | if(!this.state.favorites){ 174 | this.setState({firstVisit:true}) 175 | } else { 176 | let favorites = [...this.state.favorites] 177 | this.setState({favorites:_.pull(favorites, key)}) 178 | } 179 | 180 | } 181 | isInFavorites =(key)=> _.includes(this.state.favorites,key) 182 | handleFilter=_.debounce((inputValue)=>{ 183 | let coinSymbols = Object.keys(this.state.coinList) 184 | let coinNames = coinSymbols.map(sym=>this.state.coinList[sym].CoinName) 185 | let allStringsToSearch = coinSymbols.concat(coinNames) 186 | let fuzzyResults = fuzzy.filter(inputValue, allStringsToSearch, {}).map(result =>result.string) 187 | let filteredCoins = _.pickBy(this.state.coinList, (result, symkey)=>{ 188 | let coinName = result.CoinName 189 | return _.includes(fuzzyResults, symkey) || _.includes(fuzzyResults, coinName) 190 | }) 191 | if(filteredCoins && Object.keys(filteredCoins).length === 0 ){ 192 | this.setState({notFound:true}) 193 | } else { 194 | this.setState({notFound:false}) 195 | } 196 | this.setState({filteredCoins}) 197 | }, 100) 198 | filterCoins=(e)=>{ 199 | let inputValue=_.get(e, 'target.value') 200 | if(!inputValue){ 201 | this.setState({ 202 | filteredCoins:null 203 | }) 204 | return; 205 | } 206 | this.handleFilter(inputValue) 207 | } 208 | render() { 209 | return ( 210 | 211 | {NavBar.call(this)} 212 | {this.loadingContent() || 213 | {this.displayingSettings() && this.settingsContent()} 214 | {this.displayingDashboard() && Dashboard.call(this)} 215 | } 216 | 217 | ); 218 | } 219 | } 220 | 221 | export default App; 222 | -------------------------------------------------------------------------------- /src/Button.js: -------------------------------------------------------------------------------- 1 | import {fontSize1, greenBoxShadow} from './Style' 2 | import styled from 'styled-components' 3 | 4 | export const ConfirmButton = styled.div` 5 | margin-top:10px; 6 | color:#1163c9; 7 | ${fontSize1} 8 | font-family: Exo 2, sans-serif; 9 | color:#42ff3a; 10 | padding:5px; 11 | ${greenBoxShadow} 12 | cursor:pointer; 13 | 14 | ` -------------------------------------------------------------------------------- /src/CoinList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, {css} from 'styled-components' 3 | import { subtleBoxShadow, greenBoxShadow,redBoxShadow, lightBlueBackground } from './Style'; 4 | 5 | export const CoinGrid = styled.div` 6 | display:grid; 7 | grid-template-columns:1fr 1fr 1fr 1fr 1fr; 8 | grid-gap:15px; 9 | ${props=>props.count && css` 10 | grid-template-columns:repeat(${props.count >5 ? props.count:5}, 1fr); 11 | `} 12 | margin-top:15px; 13 | ` 14 | export const CoinTile = styled.div` 15 | ${subtleBoxShadow} 16 | ${lightBlueBackground} 17 | padding:10px; 18 | ${props=>!props.favorite && css` 19 | &:hover{ 20 | cursor:pointer; 21 | ${greenBoxShadow} 22 | } 23 | `} 24 | ${props=>props.dashboardFavorite && css` 25 | ${greenBoxShadow} 26 | &:hover{ 27 | pointer-events:none; 28 | } 29 | `} 30 | 31 | ${props=>props.favorite && css` 32 | &:hover{ 33 | cursor:pointer; 34 | ${redBoxShadow} !important; 35 | } 36 | `} 37 | ${props=>props.choosen && !props.favorite && css` 38 | pointer-events:none; 39 | opacity:0.4; 40 | `} 41 | ` 42 | 43 | export const CoinHeaderGrid = styled.div` 44 | display:grid; 45 | grid-template-columns:1fr 1fr; 46 | ` 47 | export const CoinSymbol = styled.div` 48 | justify-self: right; 49 | ` 50 | const DeleteIcon = styled.div` 51 | justify-self: right; 52 | display:none; 53 | ${CoinTile}:hover & { 54 | display:block; 55 | color:red; 56 | } 57 | ` 58 | 59 | 60 | export default function(favorites=false){ 61 | console.log(this.state.favorites) 62 | let coinKeys = favorites ? this.state.favorites: 63 | ((this.state.filteredCoins && Object.keys(this.state.filteredCoins)) || (Object.keys(this.state.coinList).slice(0,100))) 64 | return 65 | {coinKeys ? coinKeys.map((coinKey,index)=> 66 | {this.removeCoinFromFavorites(coinKey)}:()=>{this.addCointToFavorites(coinKey)}}> 67 | 68 |
{this.state.coinList[coinKey].CoinName}
69 | {favorites ? 70 | X: 71 | {this.state.coinList[coinKey].Symbol}} 72 |
73 | coin icon 74 |
75 | ):null} 76 |
77 | } -------------------------------------------------------------------------------- /src/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {CoinGrid, CoinTile, CoinHeaderGrid, CoinSymbol} from './CoinList' 3 | import styled, {css} from 'styled-components' 4 | import {fontSize2, fontSize1, subtleBoxShadow, lightBlueBackground, backgroundColor2} from './Style' 5 | import highchartsConfig from './HighchartsConfig'; 6 | import theme from './HighchartsTheme' 7 | 8 | const ReactHighcharts = require('react-highcharts') 9 | ReactHighcharts.Highcharts.setOptions(theme); 10 | 11 | const numberFormat =(number) =>{ 12 | return +(number+'').slice(0,7); 13 | } 14 | const ChangePct = styled.div` 15 | color:green; 16 | ${props=>props.red && css` 17 | color:red; 18 | `} 19 | ` 20 | const TicketPrice=styled.div` 21 | ${fontSize2} 22 | ` 23 | const CoinTileCompact=styled(CoinTile)` 24 | ${fontSize1} 25 | display:grid; 26 | grid-gap:5px; 27 | grid-template-columns:repeat(3,1fr); 28 | // justify-items:right; 29 | ` 30 | const PaddingBlue = styled.div` 31 | ${subtleBoxShadow} 32 | ${lightBlueBackground} 33 | padding:5px; 34 | ` 35 | 36 | const ChartGrid = styled.div` 37 | display:grid; 38 | margin-top:14px; 39 | grid-gap:15px; 40 | grid-template-columns:1fr 3fr; 41 | ` 42 | const ChartSelect = styled.select` 43 | float:right; 44 | ${backgroundColor2} 45 | color:#1163c9; 46 | border:1px solid; 47 | ${fontSize1} 48 | ` 49 | 50 | export default function(){ 51 | return
52 | 53 | {this.state.prices.map((price,index)=>{ 54 | let sym = Object.keys(price)[0] 55 | let data = price[sym]['USD'] 56 | let tileProps={ 57 | dashboardFavorite:sym=== this.state.currentFavorite, 58 | onClick:()=>{ 59 | this.setState({currentFavorite:sym,historical:null}, this.fetchHistorical); 60 | localStorage.setItem('cryptoDash', JSON.stringify({ 61 | ...JSON.parse(localStorage.getItem('cryptoDash')), 62 | currentFavorite:sym, 63 | })); 64 | } 65 | } 66 | return index<5 ? 67 | 68 |
{sym}
69 | 70 | 71 | {numberFormat(data.CHANGEPCT24HOUR)}% 72 | 73 | 74 |
75 | ${numberFormat(data.PRICE)} 76 |
: 77 | 78 |
{sym}
79 | 80 | 81 | {numberFormat(data.CHANGEPCT24HOUR)}% 82 | 83 | 84 |
${numberFormat(data.PRICE)}
85 |
86 | })} 87 |
88 | 89 | 90 | {this.state.coinList[this.state.currentFavorite] &&

{this.state.coinList[this.state.currentFavorite].CoinName}

} 91 | {this.state.currentFavorite} 92 |
93 | 94 | { 95 | this.setState({timeInterval:e.target.value, historical:null},this.fetchHistorical); 96 | }}> 97 | 98 | 99 | 100 | 101 | {this.state.historical ? 102 | 103 | :
Loading historical data...
} 104 |
105 |
106 |
107 | } -------------------------------------------------------------------------------- /src/HighchartsConfig.js: -------------------------------------------------------------------------------- 1 | export default function(){ 2 | return { 3 | title: { 4 | text: '' 5 | }, 6 | chart:{ 7 | height:'400px' 8 | }, 9 | subtitle: { 10 | text: '' 11 | }, 12 | 13 | yAxis: { 14 | title: { 15 | text: 'Price' 16 | } 17 | }, 18 | xAxis:{type:'datetime'}, 19 | legend: { 20 | layout: 'vertical', 21 | align: 'right', 22 | verticalAlign: 'middle' 23 | }, 24 | 25 | plotOptions: { 26 | series: { 27 | label: { 28 | connectorAllowed: false 29 | }, 30 | pointStart: 2010 31 | } 32 | }, 33 | 34 | series: this.state.historical, 35 | 36 | responsive: { 37 | rules: [{ 38 | condition: { 39 | maxWidth: 500 40 | }, 41 | chartOptions: { 42 | legend: { 43 | layout: 'horizontal', 44 | align: 'center', 45 | verticalAlign: 'bottom' 46 | } 47 | } 48 | }] 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/HighchartsTheme.js: -------------------------------------------------------------------------------- 1 | import {lightTheme} from './Style'; 2 | export default { 3 | lang: { 4 | thousandsSep: ',' 5 | }, 6 | colors: [ 7 | '#61d936', 8 | '#552ccb', 9 | '#1163c9', 10 | '#04A1EE', 11 | '#08C6E0', 12 | '#146B9E', 13 | '#F376C1', 14 | '#1B2839' 15 | ], 16 | chart: { 17 | backgroundColor: lightTheme ? 'white': '#061a44', 18 | borderColor: '#000000', 19 | borderWidth: 0, 20 | className: 'dark-container', 21 | plotBackgroundColor: lightTheme ? 'white': '#061a44', 22 | plotBorderWidth: 0 23 | }, 24 | title: { 25 | style: { 26 | color: '#C0C0C0', 27 | font: 'bold 16px "Trebuchet MS", Verdana, sans-serif' 28 | } 29 | }, 30 | subtitle: { 31 | style: { 32 | color: '#666666', 33 | font: 'bold 12px "Trebuchet MS", Verdana, sans-serif' 34 | } 35 | }, 36 | xAxis: { 37 | gridLineColor: '#333333', 38 | gridLineWidth: 0, 39 | labels: { 40 | style: { 41 | color: '#A0A0A0' 42 | } 43 | }, 44 | lineColor: '#A0A0A0', 45 | tickColor: '#A0A0A0', 46 | title: { 47 | style: { 48 | color: '#CCC', 49 | fontWeight: 'bold', 50 | fontSize: '12px', 51 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 52 | } 53 | } 54 | }, 55 | yAxis: { 56 | gridLineWidth: 0, 57 | gridLineColor: '#333333', 58 | labels: { 59 | style: { 60 | color: '#A0A0A0' 61 | } 62 | }, 63 | lineColor: '#A0A0A0', 64 | minorTickInterval: null, 65 | tickColor: '#A0A0A0', 66 | tickWidth: 1, 67 | title: { 68 | style: { 69 | color: '#CCC', 70 | fontWeight: 'bold', 71 | fontSize: '12px', 72 | fontFamily: 'Trebuchet MS, Verdana, sans-serif' 73 | } 74 | } 75 | }, 76 | tooltip: { 77 | backgroundColor: 'rgba(0, 0, 0, 0.75)', 78 | style: { 79 | color: '#F0F0F0' 80 | } 81 | }, 82 | toolbar: { 83 | itemStyle: { 84 | color: 'silver' 85 | } 86 | }, 87 | plotOptions: { 88 | line: { 89 | dataLabels: { 90 | color: '#CCC' 91 | }, 92 | marker: { 93 | lineColor: '#333' 94 | } 95 | }, 96 | spline: { 97 | marker: { 98 | lineColor: '#333' 99 | } 100 | }, 101 | scatter: { 102 | marker: { 103 | lineColor: '#333' 104 | } 105 | }, 106 | candlestick: { 107 | lineColor: 'white' 108 | } 109 | }, 110 | legend: { 111 | itemStyle: { 112 | font: '9pt Trebuchet MS, Verdana, sans-serif', 113 | color: '#A0A0A0' 114 | }, 115 | itemHoverStyle: { 116 | color: '#FFF' 117 | }, 118 | itemHiddenStyle: { 119 | color: '#444' 120 | } 121 | }, 122 | credits: { 123 | style: { 124 | color: '#666' 125 | } 126 | }, 127 | labels: { 128 | style: { 129 | color: '#CCC' 130 | } 131 | }, 132 | 133 | navigation: { 134 | buttonOptions: { 135 | symbolStroke: '#DDDDDD', 136 | hoverSymbolStroke: '#FFFFFF', 137 | theme: { 138 | fill: { 139 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 140 | stops: [[0.4, '#606060'], [0.6, '#333333']] 141 | }, 142 | stroke: '#000000' 143 | } 144 | } 145 | }, 146 | 147 | // scroll charts 148 | rangeSelector: { 149 | buttonTheme: { 150 | fill: { 151 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 152 | stops: [[0.4, '#888'], [0.6, '#555']] 153 | }, 154 | stroke: '#000000', 155 | style: { 156 | color: '#CCC', 157 | fontWeight: 'bold' 158 | }, 159 | states: { 160 | hover: { 161 | fill: { 162 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 163 | stops: [[0.4, '#BBB'], [0.6, '#888']] 164 | }, 165 | stroke: '#000000', 166 | style: { 167 | color: 'white' 168 | } 169 | }, 170 | select: { 171 | fill: { 172 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 173 | stops: [[0.1, '#000'], [0.3, '#333']] 174 | }, 175 | stroke: '#000000', 176 | style: { 177 | color: 'yellow' 178 | } 179 | } 180 | } 181 | }, 182 | inputStyle: { 183 | backgroundColor: '#333', 184 | color: 'silver' 185 | }, 186 | labelStyle: { 187 | color: 'silver' 188 | } 189 | }, 190 | 191 | navigator: { 192 | handles: { 193 | backgroundColor: '#666', 194 | borderColor: '#AAA' 195 | }, 196 | outlineColor: '#CCC', 197 | maskFill: 'rgba(16, 16, 16, 0.5)', 198 | series: { 199 | color: '#7798BF', 200 | lineColor: '#A6C7ED' 201 | } 202 | }, 203 | 204 | scrollbar: { 205 | barBackgroundColor: { 206 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 207 | stops: [[0.4, '#888'], [0.6, '#555']] 208 | }, 209 | barBorderColor: '#CCC', 210 | buttonArrowColor: '#CCC', 211 | buttonBackgroundColor: { 212 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 213 | stops: [[0.4, '#888'], [0.6, '#555']] 214 | }, 215 | buttonBorderColor: '#CCC', 216 | rifleColor: '#FFF', 217 | trackBackgroundColor: { 218 | linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, 219 | stops: [[0, '#000'], [1, '#333']] 220 | }, 221 | trackBorderColor: '#666' 222 | }, 223 | 224 | // special colors for some of the 225 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 226 | background2: 'rgb(35, 35, 70)', 227 | dataLabelsColor: '#444', 228 | textColor: '#C0C0C0', 229 | maskColor: 'rgba(255,255,255,0.3)' 230 | }; 231 | -------------------------------------------------------------------------------- /src/NavBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled, {css} from 'styled-components' 3 | const Logo = styled.div` 4 | font-size:1.5em; 5 | ` 6 | const ControlButton = styled.div` 7 | cursor: pointer; 8 | line-height: 30px; 9 | ${props=>props.active && css` 10 | text-shadow: 0px 0px 60px #03ff03; 11 | `} 12 | ` 13 | const NavBar = styled.div` 14 | margin-bottom:40px; 15 | display:grid; 16 | grid-template-columns:180px auto 100px 100px; 17 | ` 18 | export default function(){ 19 | return 20 | 21 | CryptoDash 22 | 23 |
24 | {!this.state.firstVisit && this.state.favorites && this.state.favorites.length >0 &&{this.setState({page:'dashboard'})}} active={this.displayingDashboard()}> 25 | Dashboard 26 | } 27 | {this.setState({page:'settings'})}} active={this.displayingSettings()}> 28 | Settings 29 | 30 |
31 | } -------------------------------------------------------------------------------- /src/Search.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import {backgroundColor2, fontSize2} from './Style' 4 | import {WhiteText} from './Text' 5 | 6 | 7 | const SearchContainer = styled.div` 8 | margin-top:20px; 9 | display:grid; 10 | grid-template-columns:110px 1fr; 11 | grid-gap:20px; 12 | ` 13 | const SearchInput = styled.input` 14 | place-self:center left; 15 | ${backgroundColor2} 16 | color:#1163c9; 17 | border:1px solid; 18 | ${fontSize2} 19 | margin:5px; 20 | height: 40px; 21 | line-height: 40px; 22 | width: 80%; 23 | ` 24 | export default function(){ 25 | return 26 | Search all coin 27 | 28 | 29 | } -------------------------------------------------------------------------------- /src/Style.js: -------------------------------------------------------------------------------- 1 | const theme = 'dark'; 2 | //const theme = 'light'; 3 | export const lightTheme = theme === 'light'; 4 | 5 | export const color = lightTheme ? 'white' : '#061a44'; 6 | export const color2 = lightTheme ? 'white': '#010e2c'; 7 | export const color3 = lightTheme ? '#09f010' : '#42ff3a'; 8 | 9 | if(lightTheme){ 10 | document.body.style.background = '#e1eaee'; 11 | document.body.style.color = '#061a44'; 12 | } 13 | 14 | export const lightBlueBackground = `background-color: ${color}`; 15 | export const backgroundColor2 = `background-color: ${color2};`; 16 | export const greenBackgroundColor = `background-color: ${color3};`; 17 | 18 | export const fontColorGreen = `color: #03A9F4`; 19 | export const fontColorWhite = `color: white`; 20 | export const subtleBoxShadow = `box-shadow: 0px 0px 5px 1px ${lightTheme ? '#a9b6ff' : '#121d5b'}`; 21 | export const greenBoxShadow = `box-shadow: 0px 0px 4px 2px #5fff17`; 22 | export const redBoxShadow = `box-shadow: 0px 0px 2px 2px #e41111`; 23 | 24 | // export const fontSizeBig = 'font-size: 2em'; 25 | // export const fontSize1 = 'font-size: 1.5em;'; 26 | // export const fontSize2 = 'font-size: 1.0em'; 27 | // export const fontSize3 = 'font-size: .75em'; 28 | 29 | export const fontSize2 = `font-size:2.0em` 30 | export const fontSize1 = `font-size:1.0em` 31 | export const fontSizeBig = `font-size:5.0em` 32 | 33 | export const textAlignCenter = 'text-align: center;'; 34 | -------------------------------------------------------------------------------- /src/Text.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import {textAlignCenter, fontSize1, fontColorWhite } from './Style' 3 | export const WhiteText = styled.h2` 4 | ${textAlignCenter} 5 | ${fontSize1} 6 | ${fontColorWhite} 7 | 8 | ` -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karkranikhil/react-dashboard/f122e6b60dc319a1c096b0328010aeb1edd85f58/src/index.css -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read http://bit.ly/CRA-PWA. 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | installingWorker.onstatechange = () => { 64 | if (installingWorker.state === 'installed') { 65 | if (navigator.serviceWorker.controller) { 66 | // At this point, the updated precached content has been fetched, 67 | // but the previous service worker will still serve the older 68 | // content until all client tabs are closed. 69 | console.log( 70 | 'New content is available and will be used when all ' + 71 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' 72 | ); 73 | 74 | // Execute callback 75 | if (config && config.onUpdate) { 76 | config.onUpdate(registration); 77 | } 78 | } else { 79 | // At this point, everything has been precached. 80 | // It's the perfect time to display a 81 | // "Content is cached for offline use." message. 82 | console.log('Content is cached for offline use.'); 83 | 84 | // Execute callback 85 | if (config && config.onSuccess) { 86 | config.onSuccess(registration); 87 | } 88 | } 89 | } 90 | }; 91 | }; 92 | }) 93 | .catch(error => { 94 | console.error('Error during service worker registration:', error); 95 | }); 96 | } 97 | 98 | function checkValidServiceWorker(swUrl, config) { 99 | // Check if the service worker can be found. If it can't reload the page. 100 | fetch(swUrl) 101 | .then(response => { 102 | // Ensure service worker exists, and that we really are getting a JS file. 103 | if ( 104 | response.status === 404 || 105 | response.headers.get('content-type').indexOf('javascript') === -1 106 | ) { 107 | // No service worker found. Probably a different app. Reload the page. 108 | navigator.serviceWorker.ready.then(registration => { 109 | registration.unregister().then(() => { 110 | window.location.reload(); 111 | }); 112 | }); 113 | } else { 114 | // Service worker found. Proceed as normal. 115 | registerValidSW(swUrl, config); 116 | } 117 | }) 118 | .catch(() => { 119 | console.log( 120 | 'No internet connection found. App is running in offline mode.' 121 | ); 122 | }); 123 | } 124 | 125 | export function unregister() { 126 | if ('serviceWorker' in navigator) { 127 | navigator.serviceWorker.ready.then(registration => { 128 | registration.unregister(); 129 | }); 130 | } 131 | } 132 | --------------------------------------------------------------------------------