├── .gitignore ├── README.md ├── deploy.sh ├── package.json ├── public ├── favicon.ico └── index.html └── src ├── App.css ├── App.js ├── App.test.js ├── DraftBoard.js ├── Drafted.js ├── Footer.js ├── Header.js ├── PlayerTable.js ├── Undrafted.js ├── UndraftedAll.js ├── UndraftedPositions.js ├── index.css └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-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 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is a direct port of [Fantasy Football Rankings](https://github.com/jayjzheng/ff_rankings). 2 | 3 | It uses ranking and tiering data [Boris Chen](http://www.borischen.co/) and makes an interactive draftboard to help you during your fantasy football draft. 4 | 5 | it was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). 6 | 7 | ## Commands 8 | - Run: `npm start` 9 | - Build: `npm run build` 10 | - Test: `npm test` 11 | 12 | ## TODOs 13 | - Local Storage 14 | - Tests 15 | - Better CSS -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | aws s3 sync ./build s3://jayzheng.com/ff --exclude 'deploy.sh' --exclude '.git/*' 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "draftaid", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://jayzheng.com/ff", 6 | "devDependencies": { 7 | "react-scripts": "0.9.0" 8 | }, 9 | "dependencies": { 10 | "bootstrap": "^3.3.7", 11 | "react": "^15.4.2", 12 | "react-dom": "^15.4.2" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jayjzheng/draftaid-react/171ffa06c9130b8d8d88ad612ee7dab0d9bbd72b/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | Draft Aid 19 | 20 | 21 |
22 | 32 | 33 | 34 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .container {padding-left:0;padding-right:5px;} 2 | .row {margin-left:0;margin-right:-5px;} 3 | .row > div {padding-left:0;padding-right:5px;} 4 | 5 | .scrollable { 6 | overflow-y: auto; 7 | } 8 | 9 | .table-condensed > tbody > tr > td { 10 | padding: 2px; 11 | font-size: 12px; 12 | } 13 | 14 | .aid-title { 15 | text-align: center; 16 | font-weight: bold; 17 | background-color: #eee; 18 | padding: 5px; 19 | margin-bottom: 5px; 20 | } 21 | 22 | .position-title { 23 | text-align: center; 24 | font-weight: bold; 25 | margin-bottom: 5px; 26 | } 27 | 28 | html, body { 29 | height: 100%; 30 | } 31 | 32 | #wrap { 33 | min-height: 100%; 34 | } 35 | 36 | #main { 37 | overflow:auto; 38 | padding-bottom:60px; /* this needs to be bigger than footer height*/ 39 | } 40 | 41 | .footer { 42 | position: relative; 43 | margin-top: -50px; /* negative value of footer height */ 44 | height: 50px; 45 | clear:both; 46 | padding:10px; 47 | } 48 | 49 | .overall-rankings { 50 | height: 250px; 51 | margin-bottom: 10px; 52 | } 53 | 54 | .btn-responsive { 55 | padding: 4px 8px; 56 | font-size: 90%; 57 | } 58 | 59 | .pointer { 60 | cursor: pointer; 61 | } 62 | 63 | @media(min-width: 768px){ 64 | .btn-responsive { 65 | font-size: 100%; 66 | padding: 5px 10px; 67 | } 68 | 69 | .overall-rankings { 70 | height: 710px; 71 | margin-bottom: 10px; 72 | } 73 | 74 | .draft-history { 75 | height: 710px; 76 | } 77 | 78 | .table-condensed > tbody > tr > td { 79 | padding: 2px; 80 | font-size: 13px; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import './App.css'; 4 | 5 | import Header from './Header' 6 | import Footer from './Footer' 7 | import DraftBoard from './DraftBoard' 8 | 9 | class App extends Component { 10 | render() { 11 | return ( 12 |
13 |
14 | 15 |
16 | ); 17 | } 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/DraftBoard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import UndraftedAll from './UndraftedAll' 4 | import UndraftedPositions from './UndraftedPositions' 5 | import Drafted from './Drafted' 6 | 7 | class DraftBoard extends Component { 8 | constructor() { 9 | super(); 10 | 11 | this.state = { 12 | players: [], 13 | filteredPlayers: [], 14 | isLoading: true, 15 | currentDraft: 0, 16 | fetchError: null, 17 | format: 'standard', 18 | query: '', 19 | }; 20 | } 21 | 22 | componentDidMount() { 23 | this.fetchPlayers(this.state.format); 24 | } 25 | 26 | fetchPlayers(format) { 27 | // const url = 'https://draftaid-api.herokuapp.com/rankings'; 28 | const url = 'https://jayzheng-ff-api.herokuapp.com/rankings'; 29 | const self = this; 30 | 31 | fetch(url+'?format='+format, { 32 | method: 'get' 33 | }).then(function(response) { 34 | response.json().then(function(res){ 35 | self.setState({ 36 | players: res.rankings, 37 | filteredPlayers: res.rankings, 38 | isLoading: false, 39 | format: format, 40 | query: '', 41 | }); 42 | }); 43 | }).catch(function(err) { 44 | self.setState({ 45 | fetchError: err, 46 | isLoading: false, 47 | }); 48 | }); 49 | } 50 | 51 | searchPlayers(query) { 52 | let players = this.state.players.filter(player => 53 | player.name.toUpperCase().includes(query.toUpperCase()) 54 | ); 55 | 56 | this.setState({ 57 | filteredPlayers: players, 58 | query: query, 59 | }); 60 | } 61 | 62 | draft(player) { 63 | const players = this.state.players.slice(); 64 | const index = players.indexOf(player); 65 | if (~index) { 66 | players[index].drafted = this.state.currentDraft + 1; 67 | } 68 | 69 | this.setState({ 70 | currentDraft: this.state.currentDraft + 1, 71 | players: players, 72 | filteredPlayers: players, 73 | query: '', 74 | }); 75 | } 76 | 77 | undo(currentDraft) { 78 | if(currentDraft === 0) { 79 | return 80 | } 81 | 82 | const players = this.state.players.slice(); 83 | const index = players.findIndex(p => p.drafted === currentDraft); 84 | if (~index) { 85 | players[index].drafted = null; 86 | } 87 | 88 | this.setState({ 89 | currentDraft: this.state.currentDraft - 1, 90 | players: players, 91 | }); 92 | } 93 | 94 | reset() { 95 | const players = this.state.players.slice(); 96 | players.map((player, i) => { 97 | return player.drafted = null; 98 | }); 99 | 100 | this.setState({ 101 | currentDraft: 0, 102 | players: players, 103 | }); 104 | } 105 | 106 | render() { 107 | if (this.state.isLoading) { 108 | return (
Loading...
) 109 | } 110 | 111 | if (this.state.fetchError) { 112 | return (
error fetching rankings...
) 113 | } 114 | 115 | return ( 116 |
117 | this.draft(p) } 120 | fetch={ (e) => this.fetchPlayers(e.target.value) } 121 | search={ (e) => this.searchPlayers(e.target.value) } 122 | format={ this.state.format } 123 | query={ this.state.query } 124 | /> 125 | 126 | this.draft(p)} 129 | /> 130 | 131 | this.undo(c) } 135 | reset={ () => this.reset() } 136 | /> 137 |
138 | ); 139 | } 140 | } 141 | 142 | export default DraftBoard; 143 | -------------------------------------------------------------------------------- /src/Drafted.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import PlayerTable from './PlayerTable' 4 | 5 | function Drafted(props) { 6 | let players = props.players.slice().filter(p => p.drafted); 7 | players = players.sort((a, b) => b.drafted - a.drafted); 8 | 9 | return ( 10 |
11 |
12 | Draft History 13 |
14 | 15 |
16 |
17 | 22 | 23 | 28 |
29 |
30 | 31 | 36 |
37 | ); 38 | } 39 | 40 | Drafted.propTypes = { 41 | currentDraft: React.PropTypes.number.isRequired, 42 | reset: React.PropTypes.func.isRequired, 43 | undo: React.PropTypes.func.isRequired, 44 | players: React.PropTypes.array.isRequired, 45 | }; 46 | 47 | export default Drafted 48 | -------------------------------------------------------------------------------- /src/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Footer() { 4 | return ( 5 |
6 | footer 7 |
8 | ); 9 | } 10 | 11 | export default Footer -------------------------------------------------------------------------------- /src/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Header() { 4 | return ( 5 |
6 |

7 | Draft Aid  8 | BorisChen.co 9 |

10 |
11 | ); 12 | } 13 | 14 | export default Header -------------------------------------------------------------------------------- /src/PlayerTable.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | class PlayerTable extends PureComponent { 4 | rows() { 5 | let players = this.props.players.slice(); 6 | 7 | if (this.props.size) { 8 | players = players.slice(0, this.props.size); 9 | } 10 | 11 | return players.map((player, i) => { 12 | return ( 13 | this.onClick(player)}> 16 | {this.columns(player)} 17 | 18 | ) 19 | }); 20 | } 21 | 22 | onClick(player) { 23 | if (this.props.onClick) { 24 | return this.props.onClick(player); 25 | } 26 | } 27 | 28 | trClassName(tier, disable) { 29 | if (disable) { 30 | return 'pointer' 31 | } 32 | if (tier % 4 === 0) { 33 | return 'active pointer' 34 | } 35 | if (tier % 4 === 1) { 36 | return 'success pointer' 37 | } 38 | if (tier % 4 === 2) { 39 | return 'warning pointer' 40 | } 41 | if (tier % 4 === 3) { 42 | return 'info pointer' 43 | } 44 | return 'danger pointer' 45 | } 46 | 47 | columns(player) { 48 | return this.props.fields.map((f, i) => { 49 | if (f === 'tier') { 50 | return Tier {player[f]} 51 | } else { 52 | return {player[f]} 53 | } 54 | }); 55 | } 56 | 57 | render() { 58 | return ( 59 | 60 | {this.rows()} 61 |
62 | ); 63 | } 64 | } 65 | 66 | PlayerTable.propTypes = { 67 | players: React.PropTypes.array.isRequired, 68 | fields: React.PropTypes.array.isRequired, 69 | 70 | onClick: React.PropTypes.func, 71 | size: React.PropTypes.number, 72 | disableColor: React.PropTypes.bool, 73 | }; 74 | 75 | export default PlayerTable 76 | -------------------------------------------------------------------------------- /src/Undrafted.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import PlayerTable from './PlayerTable' 4 | 5 | function Undrafted(props) { 6 | let players = props.players.slice().filter(p => !p.drafted); 7 | 8 | if (props.position) { 9 | players = players.filter(p => p.position.includes(props.position)); 10 | } 11 | 12 | players = players.sort((a, b) => a.rank - b.rank); 13 | 14 | return ( 15 | props.draft(p)} 20 | /> 21 | ); 22 | } 23 | 24 | 25 | Undrafted.propTypes = { 26 | draft: React.PropTypes.func.isRequired, 27 | players: React.PropTypes.array.isRequired, 28 | fields: React.PropTypes.array.isRequired, 29 | 30 | size: React.PropTypes.number, 31 | position: React.PropTypes.string, 32 | }; 33 | 34 | export default Undrafted 35 | -------------------------------------------------------------------------------- /src/UndraftedAll.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Undrafted from './Undrafted' 4 | 5 | function UndraftedAll(props) { 6 | return ( 7 |
8 |
9 | Overall Rankings 10 |
11 | 12 |
13 |
14 | 19 |
20 | 21 |
22 | 29 |
30 |
31 | 32 |
33 | props.draft(p)} 37 | /> 38 |
39 |
40 | ) 41 | } 42 | 43 | UndraftedAll.propTypes = { 44 | players: React.PropTypes.array.isRequired, 45 | format: React.PropTypes.string.isRequired, 46 | query: React.PropTypes.string.isRequired, 47 | search: React.PropTypes.func.isRequired, 48 | fetch: React.PropTypes.func.isRequired, 49 | }; 50 | 51 | export default UndraftedAll 52 | -------------------------------------------------------------------------------- /src/UndraftedPositions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Undrafted from './Undrafted' 4 | 5 | function UndraftedPositions(props) { 6 | const fields = ['tier', 'name', 'team']; 7 | 8 | return ( 9 |
10 |
11 | Top Picks By Position 12 |
13 | 14 |
15 | Runningbacks 16 | props.draft(p)} 20 | size={15} 21 | position='RB' 22 | /> 23 |
24 | 25 |
26 | Wide Receivers 27 | props.draft(p)} 31 | size={15} 32 | position='WR' 33 | /> 34 |
35 | 36 |
37 | Quarterbacks 38 | props.draft(p)} 42 | size={15} 43 | position='QB' 44 | /> 45 |
46 | 47 |
48 | Tightends 49 | props.draft(p)} 53 | size={15} 54 | position='TE' 55 | /> 56 |
57 |
58 | ) 59 | } 60 | 61 | UndraftedPositions.propTypes = { 62 | draft: React.PropTypes.func.isRequired, 63 | players: React.PropTypes.array.isRequired, 64 | }; 65 | 66 | export default UndraftedPositions 67 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import 'bootstrap/dist/css/bootstrap.css'; 5 | import App from './App'; 6 | 7 | import './index.css'; 8 | 9 | ReactDOM.render( 10 | , 11 | document.getElementById('root') 12 | ); 13 | --------------------------------------------------------------------------------