/src/containers/index.js'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/styles/_elements.scss:
--------------------------------------------------------------------------------
1 |
2 | .tableContainer {
3 | letter-spacing:0;
4 | display:flex;
5 | align-items:flex-start;
6 | flex-direction:column;
7 | }
8 | .table {
9 | font-size:0.8em;
10 | width:100%;
11 | table-layout:fixed;
12 | border-collapse:collapse;
13 | }
14 | .table tr {
15 | height:3em;
16 | }
17 | .table tr th:first-child {
18 | text-align:left;
19 | width:40%;
20 | padding-left:2%;
21 | }
22 | .tableHead {
23 | border-bottom:1px solid #e0e0e0;
24 | }
25 | .tableBody tr:nth-of-type(even) {
26 | background:#f5f5f5;
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/NoGames/NoGames.scss:
--------------------------------------------------------------------------------
1 | /* ------- NoGames ------- */
2 | .container {
3 | margin: 1em;
4 | height: 205px;
5 | display: flex;
6 | align-items: center;
7 | font-size: 1.1em;
8 | }
9 | .text {
10 | letter-spacing: 0;
11 | text-align: center;
12 | padding: 0.8em 1.6em;
13 | border: 1px solid currentColor;
14 | border-radius: 2px;
15 | background: $white;
16 | text-transform: capitalize;
17 | font-weight: lighter;
18 | }
19 |
20 | @media only screen and (min-width: $medium) {
21 | .text {
22 | font-size: 1.2em;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Header/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Logo from './Logo'
3 | import Trigger from './Trigger'
4 | import Menu from './Menu'
5 | import s from './Header.scss'
6 |
7 | const Header = ({ isMenuOpen, menuHeight, screenWidth, triggerMenu }) => (
8 |
15 | )
16 |
17 | export default Header
18 |
--------------------------------------------------------------------------------
/src/components/Loading/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import s from './Loading.scss'
3 |
4 | const Loading = () => (
5 |
6 |
7 |
18 |
19 |
20 | )
21 |
22 | export default Loading
23 |
--------------------------------------------------------------------------------
/src/components/Stats/Stats.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { formatStatsTableHead, formatStatsTableBody } from 'helpers/statsFns'
3 | import s from './Stats.scss'
4 |
5 | const Stats = ({ teams, ...stats }) => (
6 |
18 | )
19 |
20 | export default Stats
21 |
--------------------------------------------------------------------------------
/src/styles/_variables.scss:
--------------------------------------------------------------------------------
1 | /* ------- colors ------- */
2 | /* ------------------------ */
3 | $real_white: #ffffff; // white
4 | $real_black: #000000; // black
5 | $red: #ba0021; // red
6 | $green: #00ba21; // green
7 | $white: #f5f5f5; // grey[0]
8 | $white2: #f0f0f0; // grey[1]
9 | $grey1: #e0e0e0; // grey[2]
10 | $grey2: #d3d3d3; // grey[3]
11 | $grey3: #9b9b9b; // grey[4]
12 | $grey4: #656565; // grey[5]
13 | $grey5: #3f3f3f; // grey[6]
14 | $grey6: #323232; // grey[7]
15 | $black: #070707; // grey[8]
16 |
17 | /* --- media queries ---- */
18 | /* ------------------------ */
19 | $medium: 667px;
20 | $large: 999px;
21 | $xlarge: 1331px;
22 | $xxlarge: 1663px;
23 |
--------------------------------------------------------------------------------
/src/components/Header/Extra.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { NavLink } from 'react-router-dom'
3 | import s from './Header.scss'
4 |
5 | const Extra = ({ name, url, isExternal }) => (
6 |
7 | {isExternal ? (
8 |
9 | {name}
10 |
11 | ) : (
12 |
17 | {name}
18 |
19 | )}
20 |
21 | )
22 |
23 | export default Extra
24 |
--------------------------------------------------------------------------------
/src/data/stadiums.js:
--------------------------------------------------------------------------------
1 | export const stadiums = {
2 | nba: {
3 | 'Amway Center': [-81.383611, 28.539167],
4 | 'BMO Harris Bradley Center': [-87.916944444444, 43.043611111111],
5 | 'Bankers Life Fieldhouse': [-86.155555555556, 39.763888888889],
6 | 'ORACLE Arena': [-122.20305555556, 37.750277777778],
7 | 'Spectrum Center': [-80.839166666667, 35.225],
8 | 'Staples Center': [-118.26706, 34.043102],
9 | 'TD Garden': [-71.062227777778, 42.366302777778],
10 | 'Toyota Center': [-95.362222222222, 29.750833333333],
11 | 'United Center': [-87.674166666667, 41.880555555556],
12 | 'Verizon Center': [-77.020833333333, 38.898055555556]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Header/Item.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { NavLink } from 'react-router-dom'
3 |
4 | import s from './Header.scss'
5 |
6 | const Item = ({ name, url, sport, screenWidth }) => (
7 |
8 |
9 |
10 | {screenWidth < 667 && (
11 |
17 | )}
18 | {name}
19 |
20 |
21 |
22 | )
23 |
24 | export default Item
25 |
--------------------------------------------------------------------------------
/src/containers/Details/DetailsContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Details } from 'components'
3 |
4 | class DetailsContainer extends Component {
5 | state = { panels: [], activePanel: '' }
6 | componentDidMount() {
7 | const defaultPanels = ['box score', 'team stats', 'leaders']
8 | this.setState({
9 | panels: defaultPanels,
10 | activePanel: defaultPanels[0]
11 | })
12 | }
13 | switchPanel = newPanel => {
14 | this.setState({ activePanel: newPanel })
15 | }
16 | render() {
17 | return (
18 |
19 | )
20 | }
21 | }
22 |
23 | export default DetailsContainer
24 |
--------------------------------------------------------------------------------
/src/components/Diamond/BallStrikeOut.scss:
--------------------------------------------------------------------------------
1 |
2 | /* ------- BSO ------- */
3 | .label {
4 | font:1.1em/100% monospace;
5 | display:inline-block;
6 | width:1.25em;
7 | letter-spacing:-1px;
8 | }
9 | :global(.circle) {
10 | width:11px;
11 | height:11px;
12 | display:inline-block;
13 | margin:0 2px;
14 | border-radius:50%;
15 | background:$grey2;
16 | }
17 | :global(.circle::after) {
18 | content:'';
19 | display:block;
20 | height:100%;
21 | width:100%;
22 | border-radius:50%;
23 | background:$red;
24 | opacity:0;
25 | transition:opacity 1.22s;
26 | }
27 | .bso:first-child :global(.circleFilled::after) {
28 | background:$green;
29 | }
30 | :global(.circleFilled::after) {
31 | opacity:1;
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Item from './Item'
4 |
5 | import { APP_PAGES } from 'data/app-pages'
6 | import s from './Home.scss'
7 |
8 | const Home = () => (
9 |
10 |
11 | {'uxscoreboard'}
12 |
13 | {'Real-time sports scoreboard built on ES6, React, and Node.js'}
14 |
15 |
16 |
23 |
24 | )
25 |
26 | export default Home
27 |
--------------------------------------------------------------------------------
/src/components/Header/Menu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Item from './Item'
4 | import Extra from './Extra'
5 | import Footer from './Footer'
6 |
7 | import { APP_PAGES } from 'data/app-pages'
8 | import s from './Header.scss'
9 |
10 | const Menu = ({ menuHeight, screenWidth }) => (
11 |
23 | )
24 |
25 | export default Menu
26 |
--------------------------------------------------------------------------------
/src/containers/index.js:
--------------------------------------------------------------------------------
1 | /* ------- base container ------- */
2 | import MainContainer from './Main/MainContainer'
3 |
4 | /* ------- page containers ------- */
5 |
6 | /* ------- league containers ------- */
7 |
8 | /* ------- scoreboard containers ------- */
9 | import ScoreboardContainer from './Scoreboard/ScoreboardContainer'
10 | import DateContainer from './Date/DateContainer'
11 |
12 | /* ------- game containers ------- */
13 | import GameContainer from './Game/GameContainer'
14 | import LogoContainer from './Logo/LogoContainer'
15 | import DetailsContainer from './Details/DetailsContainer'
16 |
17 | export {
18 | MainContainer,
19 | ScoreboardContainer,
20 | DateContainer,
21 | GameContainer,
22 | LogoContainer,
23 | DetailsContainer
24 | }
25 |
--------------------------------------------------------------------------------
/src/containers/Date/DateContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Date } from 'components'
3 |
4 | class DateContainer extends Component {
5 | constructor() {
6 | super()
7 | this.state = {
8 | width: 0
9 | }
10 | this.getScreenWidth = this.getScreenWidth.bind(this)
11 | }
12 | componentDidMount() {
13 | this.getScreenWidth()
14 | window.addEventListener('resize', this.getScreenWidth, false)
15 | }
16 | componentWillUnmount() {
17 | window.removeEventListener('resize', this.getScreenWidth, false)
18 | }
19 | getScreenWidth() {
20 | this.setState({
21 | width: window.innerWidth
22 | })
23 | }
24 | render() {
25 | return
26 | }
27 | }
28 |
29 | export default DateContainer
30 |
--------------------------------------------------------------------------------
/src/components/League/League.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Loading, NotFound } from 'components'
3 | import { ScoreboardContainer } from 'containers'
4 | import s from './League.scss'
5 |
6 | const League = ({
7 | isLoading,
8 | isValid,
9 | isError,
10 | scores,
11 | year,
12 | date,
13 | today,
14 | league,
15 | lastUpdated
16 | }) => (
17 |
18 | {isLoading ? (
19 |
20 | ) : isValid && scores ? (
21 |
30 | ) : (
31 |
32 | )}
33 |
34 | )
35 |
36 | export default League
37 |
--------------------------------------------------------------------------------
/.versionrc:
--------------------------------------------------------------------------------
1 | {
2 | "types": [
3 | {"type": "chore", "section":"Others", "hidden": false},
4 | {"type": "revert", "section":"Reverts", "hidden": false},
5 | {"type": "feat", "section": "Features", "hidden": false},
6 | {"type": "fix", "section": "Bug Fixes", "hidden": false},
7 | {"type": "improvement", "section": "Feature Improvements", "hidden": false},
8 | {"type": "docs", "section": "Docs", "hidden": false},
9 | {"type": "style", "section": "Styling", "hidden": false},
10 | {"type": "refactor", "section": "Code Refactoring", "hidden": false},
11 | {"type": "perf", "section": "Performance Improvements", "hidden": false},
12 | {"type": "test", "section": "Tests", "hidden": false},
13 | {"type": "build", "section": "Build System", "hidden": false},
14 | {"type": "ci", "section": "CI", "hidden":false}
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/src/helpers/baseball.js:
--------------------------------------------------------------------------------
1 | export const createDiamond = offense => {
2 | let file = 'diamond'
3 | if (!offense) return file
4 | if (offense.first) file += '_1b'
5 | if (offense.second) file += '_2b'
6 | if (offense.third) file += '_3b'
7 | return file
8 | }
9 |
10 | export const createBsoCount = (filled, max, state) => {
11 | if (!state || state === 'Middle' || state === 'End') {
12 | return fillCircles(0, max)
13 | } else {
14 | return fillCircles(filled, max)
15 | }
16 | }
17 |
18 | const fillCircles = (circlesFilled, maxCircles) => {
19 | let count = 0
20 | let result = ''
21 | while (count < circlesFilled) {
22 | result += ''
23 | count += 1
24 | }
25 | while (count < maxCircles) {
26 | result += ''
27 | count += 1
28 | }
29 | return { __html: result }
30 | }
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [uxscoreboard](https://old.uxscoreboard.com) · [](https://github.com/asapzacy/uxscoreboard/blob/master/LICENSE) [](https://circleci.com/gh/asapzacy/uxscoreboard)
2 |
3 | A real-time sports scoreboard web app built on ES6, React, and node.js.
4 |
5 | 
6 |
7 | uxscoreboard | NBA scores
8 |
9 | ## Usage
10 |
11 | ```sh
12 | git clone https://github.com/asapzacy/uxscoreboard.git
13 | cd uxscoreboard
14 | yarn dev:fresh
15 |
16 | # open https://local.uxsocreboard:8888 or https://localhost:8888
17 | ```
18 |
--------------------------------------------------------------------------------
/src/theme.js:
--------------------------------------------------------------------------------
1 | const bps = {
2 | small: 335,
3 | medium: 667,
4 | large: 999,
5 | xlarge: 1331,
6 | xxlarge: 1663
7 | }
8 |
9 | const theme = {
10 | colors: {
11 | white: '#ffffff', // real_white
12 | black: '#000000', // real_black
13 | red: '#ba0021', // red
14 | green: '#00ba21', // green
15 | grey: [
16 | '#f5f5f5', // 0
17 | '#f0f0f0', // 1
18 | '#e0e0e0', // 2
19 | '#d3d3d3', // 3
20 | '#9b9b9b', // 4
21 | '#656565', // 5
22 | '#3f3f3f', // 6
23 | '#323232', // 7
24 | '#070707' // 8
25 | ]
26 | },
27 | mq: size => {
28 | const bpTuples = Object.keys(bps).map(key => [key, bps[key]])
29 | const [result] = bpTuples.reduce((arr, [name, width]) => {
30 | if (name === size) {
31 | arr = [...arr, `@media (min-width: ${width}px)`]
32 | }
33 | return arr
34 | }, [])
35 |
36 | return result
37 | }
38 | }
39 |
40 | export default theme
41 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "browsers": ["ie >= 10"]
8 | },
9 | "modules": false,
10 | "debug": false
11 | }
12 | ],
13 | "@babel/react",
14 | "@babel/preset-typescript"
15 | ],
16 | "ignore": ["node_modules"],
17 | "plugins": [
18 | "emotion",
19 | "react-hot-loader/babel",
20 | "transform-react-stateless-component-name",
21 | ["@babel/plugin-proposal-decorators", { "legacy": true }],
22 | "@babel/plugin-proposal-class-properties",
23 | [
24 | "@babel/plugin-transform-runtime",
25 | {
26 | "helpers": false,
27 | "regenerator": true
28 | }
29 | ],
30 | "@babel/plugin-syntax-dynamic-import",
31 | "@babel/plugin-proposal-optional-chaining"
32 | ],
33 | "env": {
34 | "test": {
35 | "presets": [["@babel/preset-env"], "@babel/react"]
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/Matchup/props.js:
--------------------------------------------------------------------------------
1 | // mlb matchup props --> Matchup component
2 | export const mlbMatchupProps = (game, date) => ({
3 | awayTeam: game.teams.away.team.teamName,
4 | homeTeam: game.teams.home.team.teamName,
5 | location: game.teams.home.team.locationName,
6 | venue: game.venue.name,
7 | date
8 | })
9 |
10 | // nba matchup props --> Matchup component
11 | export const nbaMatchupProps = (game, date) => ({
12 | awayTeam: game.vTeam.nickname,
13 | homeTeam: game.hTeam.nickname,
14 | location: `${game.arena.city}, ${game.arena.stateAbbr}.`,
15 | venue: game.arena.name,
16 | date
17 | })
18 |
19 | // nhl matchup props --> Matchup component
20 | export const nhlMatchupProps = (game, date) => ({
21 | awayTeam: game.teams.away.team.teamName,
22 | homeTeam: game.teams.home.team.teamName,
23 | location:
24 | game.gameType === 'A' ? 'Los Angeles' : game.teams.home.team.venue.city,
25 | venue: game.teams.home.team.venue.name,
26 | date
27 | })
28 |
--------------------------------------------------------------------------------
/src/config/velocity.js:
--------------------------------------------------------------------------------
1 | export const velocityGame = {
2 | enter: {
3 | animation: 'slideDown',
4 | duration: 220,
5 | delay: 0
6 | },
7 | leave: {
8 | animation: 'slideUp',
9 | duration: 220,
10 | delay: 220
11 | }
12 | }
13 |
14 | export const velocityScoreboard = direction => ({
15 | enter: {
16 | animation: `transition.slide${direction.enter ? direction.enter : 'Up'}${
17 | direction.enter ? 'Big' : ''
18 | }In`,
19 | duration: direction.enter ? 480 : 1440,
20 | delay: direction.enter ? 315 : 0,
21 | display: 'flex',
22 | style: {
23 | position: 'relative',
24 | top: 0,
25 | left: 0,
26 | right: 0
27 | }
28 | },
29 | runOnMount: true,
30 | leave: {
31 | animation: `transition.slide${
32 | direction.leave ? direction.leave : 'Down'
33 | }BigOut`,
34 | duration: 480,
35 | delay: 0,
36 | display: 'none',
37 | style: {
38 | position: 'absolute'
39 | }
40 | }
41 | })
42 |
--------------------------------------------------------------------------------
/src/components/Game/Game.scss:
--------------------------------------------------------------------------------
1 |
2 | /* ------- Game ------- */
3 | .item {
4 | background:$white;
5 | font-size:1em;
6 | border:2px solid $white;
7 | margin:1em;
8 | color:rgba(7,7,7,0);
9 | flex:1 1 calc(33.3% - 2em);
10 | max-width:420px;
11 | min-width:100%;
12 | border-radius:2px;
13 | box-shadow:0 7.5px 17.5px rgba(50,50,93,.1), 0 2.5px 7.5px rgba(0,0,0,.07);
14 | transition:transform 0.44s,box-shadow 0.44s,color 0.77s 0.77s;
15 | }
16 | .topHalf {
17 | display:block;
18 | cursor:pointer;
19 | }
20 | .details {
21 | display:block;
22 | opacity:0;
23 | transition-property:opacity;
24 | transition-duration:0.22s;
25 | transition-delay:0s;
26 | }
27 | .detailsExpanded {
28 | display:block;
29 | opacity:1;
30 | transition-duration:0.66s;
31 | transition-delay:0.33s;
32 | background:$real_white;
33 | composes:details;
34 | }
35 |
36 | @media only screen and (min-width:375px) {
37 | .item {
38 | min-width:320px;
39 | font-size:1.1em;
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/src/components/Diamond/Diamond.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import BaseballField from './BaseballField'
3 | import BallStrikeOut from './BallStrikeOut'
4 | import s from './Diamond.scss'
5 |
6 | const Diamond = ({ balls, strikes, outs, inningState, offense }) => (
7 |
8 |
9 |
10 |
11 |
12 |
22 |
23 |
24 |
25 |
26 | )
27 |
28 | export default Diamond
29 |
--------------------------------------------------------------------------------
/src/data/app-pages.js:
--------------------------------------------------------------------------------
1 | import * as Icons from 'assets/icons'
2 |
3 | export const APP_PAGES = {
4 | base: [
5 | {
6 | name: 'MLB',
7 | url: 'mlb',
8 | sport: 'baseball',
9 | icon: Icons.BaseballLight
10 | },
11 | {
12 | name: 'NBA',
13 | url: 'nba',
14 | sport: 'basketball',
15 | icon: Icons.BasketballLight
16 | },
17 | {
18 | name: 'NFL',
19 | url: 'nfl',
20 | sport: 'football',
21 | icon: Icons.FootballLight
22 | },
23 | {
24 | name: 'NHL',
25 | url: 'nhl',
26 | sport: 'hockey',
27 | icon: Icons.HockeyLight
28 | },
29 | {
30 | name: 'MLS',
31 | url: 'mls',
32 | sport: 'soccer',
33 | icon: Icons.SoccerLight
34 | }
35 | ],
36 | other: [
37 | {
38 | name: 'about',
39 | url: 'about'
40 | },
41 | {
42 | name: 'source',
43 | url: 'https://github.com/asapzacy/uxscoreboard',
44 | isExternal: true
45 | }
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/tbl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "es6": true,
5 | "browser": true,
6 | "node": true,
7 | "commonjs": true,
8 | "jest": true
9 | },
10 | "parserOptions": {
11 | "ecmaVersion": 6,
12 | "sourceType": "module",
13 | "ecmaFeatures": {
14 | "jsx": true,
15 | "experimentalObjectRestSpread": true
16 | }
17 | },
18 | "plugins": ["react"],
19 | "extends": [
20 | "eslint:recommended",
21 | "plugin:react/recommended",
22 | "plugin:prettier/recommended"
23 | ],
24 | "rules": {
25 | "no-console": [2, { "allow": ["warn", "error"] }],
26 | "semi": [2, "never"],
27 | "no-extra-semi": 2,
28 | "react/jsx-boolean-value": [2, "always"],
29 | "react/jsx-curly-spacing": [2, "never", { "allowMultiline": false }],
30 | "react/jsx-no-literals": 2,
31 | "react/self-closing-comp": 2,
32 | "react/sort-comp": 2,
33 | "react/prop-types": 0
34 | },
35 | "settings": {
36 | "react": {
37 | "version": "detect"
38 | }
39 | },
40 | "globals": {
41 | "__DEV__": true
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/oak.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/dal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/gb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Zac Arellano
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/helpers/statsFns.js:
--------------------------------------------------------------------------------
1 | export const formatStatsTableHead = teams => {
2 | let result = ' | '
3 | for (let i = 0; i < teams.length; i++) {
4 | result += `${teams[i]} | `
5 | }
6 | result += '
'
7 | return { __html: result }
8 | }
9 |
10 | export const formatStatsTableBody = stats => {
11 | let result = ''
12 | for (let item in stats) {
13 | result += `| ${item} | `
14 | const awayTeam = stats[item][0]
15 | const homeTeam = stats[item][1]
16 | if (String(awayTeam).includes('/') || String(homeTeam).includes('/')) {
17 | result += `${awayTeam} | `
18 | result += `${homeTeam} | `
19 | } else {
20 | result +=
21 | parseInt(awayTeam) > parseInt(homeTeam)
22 | ? `${awayTeam} | `
23 | : `${awayTeam} | `
24 | result +=
25 | parseInt(awayTeam) < parseInt(homeTeam)
26 | ? `${homeTeam} | `
27 | : `${homeTeam} | `
28 | }
29 | result += '
'
30 | }
31 | result += ''
32 | return { __html: result }
33 | }
34 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/hou.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Social/Social.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import TwitterOutline from 'react-icons/lib/io/social-twitter-outline'
3 | import TwitterFull from 'react-icons/lib/io/social-twitter'
4 | import GithubOutline from 'react-icons/lib/io/social-github-outline'
5 | import GithubFull from 'react-icons/lib/io/social-github'
6 | import s from './Social.scss'
7 |
8 | const Social = () => (
9 |
35 | )
36 |
37 | export default Social
38 |
--------------------------------------------------------------------------------
/src/containers/Game/GameContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Game } from 'components'
3 |
4 | class GameContainer extends Component {
5 | state = {
6 | isExpanded: false,
7 | isHovered: false,
8 | isiOS: false,
9 | hasLoaded: false,
10 | details: {}
11 | }
12 | componentDidMount() {
13 | const testDevice =
14 | /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
15 | if (testDevice) {
16 | this.setState({ isiOS: true })
17 | }
18 | }
19 | showDetails = () => {
20 | this.setState(prevState => ({
21 | isExpanded: !prevState.isExpanded
22 | }))
23 | }
24 | scaleGame = () => {
25 | this.setState(prevState => ({
26 | isHovered: !this.state.isiOS && !prevState.isHovered
27 | }))
28 | }
29 | logoHasLoaded = () => {
30 | this.setState({ hasLoaded: true })
31 | }
32 | render() {
33 | return (
34 |
41 | )
42 | }
43 | }
44 |
45 | export default GameContainer
46 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/hockey.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/hockey_light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Loading/Loading.scss:
--------------------------------------------------------------------------------
1 |
2 | /* ------- Loading ------- */
3 | .container {
4 | composes:standard from 'styles/shared.css';
5 | }
6 | .svgContainer {
7 | position:relative;
8 | width:70px;
9 | margin:0 auto;
10 | }
11 | .svgContainer::before {
12 | content:'';
13 | display:block;
14 | padding-top:100%;
15 | }
16 | .svg {
17 | margin:auto;
18 | width:100%;height:100%;
19 | position:absolute;
20 | top:0;bottom:0;left:0;right:0;
21 | transform-origin:center center;
22 | animation:rotate 2s linear infinite;
23 | }
24 | .path {
25 | stroke:$grey5;
26 | stroke-dasharray:1,200;
27 | stroke-dashoffset:0;
28 | stroke-linecap:round;
29 | animation:dash 1.5s ease-in-out infinite;
30 | }
31 |
32 | @keyframes rotate {
33 | 100% { transform:rotate(360deg) }
34 | }
35 |
36 | @keyframes dash {
37 | 0% {
38 | stroke-dasharray:1,200;
39 | stroke-dashoffset:0;
40 | }
41 | 50% {
42 | stroke-dasharray:89,200;
43 | stroke-dashoffset:-35px;
44 | }
45 | 100% {
46 | stroke-dasharray:89,200;
47 | stroke-dashoffset:-124px;
48 | }
49 | }
50 |
51 |
52 | @media only screen and (min-width:$medium) {
53 | .svgContainer {
54 | width:90px
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | uxscoreboard
9 |
10 |
11 |
12 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { BrowserRouter as Router } from 'react-router-dom'
4 | import { AppContainer as ReactHotLoader } from 'react-hot-loader'
5 | import { ThemeProvider } from 'emotion-theming'
6 | import ReactGA from 'react-ga'
7 | import * as Sentry from '@sentry/browser'
8 |
9 | import { MainContainer as Root } from 'containers'
10 | import { GlobalStyles } from 'components'
11 | import theme from './theme'
12 |
13 | require('dotenv').config()
14 |
15 | if (process.env.NODE_ENV === 'production') {
16 | Sentry.init({
17 | dsn: 'https://66e8a28472ba439eabfa9cb013eaa1b4@sentry.io/1540454'
18 | })
19 | ReactGA.initialize('UA-86342987-2')
20 | }
21 |
22 | const renderApp = Root => {
23 | render(
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | ,
32 | document.getElementById('root')
33 | )
34 | }
35 |
36 | renderApp(Root)
37 |
38 | if (module.hot) {
39 | document.head.querySelector('link[rel=icon]').href =
40 | '/assets/static/other/favicon-dev.png'
41 | module.hot.accept('containers/Main/MainContainer', () => {
42 | const Root = require('containers/Main/MainContainer').default
43 | renderApp(Root)
44 | })
45 | }
46 |
--------------------------------------------------------------------------------
/dist/assets/static/other/favicon.ico:
--------------------------------------------------------------------------------
1 | h (
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/components/Date/Date.scss:
--------------------------------------------------------------------------------
1 | /* ------- Date ------- */
2 | .menu {
3 | height: 3.5em;
4 | margin: 1em 0 0.25em;
5 | background: $white;
6 | box-shadow: 0 7.5px 17.5px rgba(50, 50, 93, 0.1),
7 | 0 2.5px 7.5px rgba(0, 0, 0, 0.07);
8 | }
9 | .list {
10 | height: 100%;
11 | display: flex;
12 | justify-content: space-between;
13 | }
14 | .item {
15 | font-size: 1.06em;
16 | font-weight: 400;
17 | display: flex;
18 | align-items: stretch;
19 | }
20 | .arrow {
21 | font-size: 1.12em;
22 | padding-bottom: 1px;
23 | composes: item;
24 | }
25 | .link {
26 | display: flex;
27 | align-items: center;
28 | padding: 0 0.66em;
29 | span {
30 | padding-bottom: 2px;
31 | }
32 | }
33 | .mainLink {
34 | composes: link;
35 | }
36 | .text {
37 | white-space: nowrap;
38 | }
39 | .text::after {
40 | content: '';
41 | display: block;
42 | height: 1px;
43 | background: $grey3;
44 | }
45 |
46 | @media only screen and (min-width: $medium) {
47 | .link {
48 | opacity: 0.75;
49 | transition: opacity 0.22s;
50 | }
51 | .link:hover {
52 | opacity: 1;
53 | }
54 | .mainLink {
55 | opacity: 1;
56 | font-size: 110%;
57 | }
58 | .item {
59 | font-size: 1.12em;
60 | }
61 | .arrow {
62 | font-size: 1.18em;
63 | }
64 | }
65 |
66 | @media only screen and (min-width: $xlarge) {
67 | .item {
68 | font-size: 1.18em;
69 | }
70 | .arrow {
71 | font-size: 1.24em;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/helpers/api.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { dateObject } from './utils'
3 |
4 | export const getMlbScores = dt => {
5 | const api = `/api/mlb/scores/${dt}`
6 | return axios
7 | .get(api)
8 | .then(scores => scores.data)
9 | .catch(error => error.status)
10 | }
11 |
12 | export const getNbaScores = dt => {
13 | const api = `/api/nba/scores/${dt}`
14 | return axios
15 | .get(api)
16 | .then(scores => scores.data)
17 | .catch(error => error.status)
18 | }
19 |
20 | export const getNbaGameDetails = (dt, id) => {
21 | const api = `/api/nba/scores/${dt}/details/${id}`
22 | return axios
23 | .get(api)
24 | .then(details => details.data)
25 | .catch(error => error.status)
26 | }
27 |
28 | export const getNflScores = week => {
29 | const api = `/api/nfl/scores/week/${week}`
30 | return axios
31 | .get(api)
32 | .then(scores => scores.data)
33 | .catch(error => error.status)
34 | }
35 |
36 | // axios request - nhl scores
37 | export const getNhlScores = dt => {
38 | const { yyyy, mm, dd } = dateObject(dt)
39 | const api = `https://statsapi.web.nhl.com/api/v1/schedule?startDate=${yyyy}-${mm}-${dd}&endDate=${yyyy}-${mm}-${dd}&expand=schedule.teams,schedule.linescore,schedule.scoringplays,schedule.game.seriesSummary,seriesSummary.series`
40 | return axios
41 | .get(api)
42 | .then(currentScores => currentScores.data)
43 | .catch(currentScores => currentScores.status)
44 | }
45 |
--------------------------------------------------------------------------------
/src/assets/icons/HockeyLight.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HockeyLight = props => (
4 |
16 | )
17 | export default HockeyLight
18 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/nyg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ssl/local.api.uxscoreboard.cert:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDyzCCArOgAwIBAgIJAO/aHkUKHjPwMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
3 | BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5kaXNjbzENMAsG
4 | A1UECgwEYXNhcDESMBAGA1UEAwwJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZ6
5 | YWNkYXJlbGxhbm9AZ21haWwuY29tMB4XDTIwMDMwMTAwMzYzOFoXDTIwMDMzMTAw
6 | MzYzOFowfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4g
7 | RnJhbmRpc2NvMQ0wCwYDVQQKDARhc2FwMRIwEAYDVQQDDAlsb2NhbGhvc3QxJTAj
8 | BgkqhkiG9w0BCQEWFnphY2RhcmVsbGFub0BnbWFpbC5jb20wggEiMA0GCSqGSIb3
9 | DQEBAQUAA4IBDwAwggEKAoIBAQDCxJGktbSI0xcfuhjhjfTgd0jHPQaJYzHfEDke
10 | pqnDzVQxjB3I3hPsPqrPbr4eOeYM2OKYf+UjMEPi2imzVzSEbLRhRctugPc9Ns7f
11 | dx84sx7uZWKTNbYNkVcXWJ3QHsk6Mn4PopUGP5V78Yszw7zht6Rtn6/dWXxCs1on
12 | wr6fYT76Tw4OP4z1huPOEGMs240FXb6GA/Jv2RFNS9v0AOxYbJs+WNBtuMmViBZr
13 | 7f+pZPZjoGVL7ph0KNJFd4q8PwsEaUPF61eC+7WxugbCZZuRMhcF0jKkOs5qJeFY
14 | FVoWVd1IBPsJOrJbnRZA2L8Zs9WiZfn8Tv8aLbWTUIaNUElXAgMBAAGjUDBOMB0G
15 | A1UdDgQWBBRg3HDhwEH1MagRk/QhBL28IRVp5zAfBgNVHSMEGDAWgBRg3HDhwEH1
16 | MagRk/QhBL28IRVp5zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCh
17 | 6tzrU9jE5VPyU0NA7LCC8fci2Muuck5CcI9ykiKT7kqICu3KTyJCR5O1JnHA2kzZ
18 | C8S5+DuPUubexbtL6/DDde8sb6D2hz61L1gl4ykyOkCzPCMbrrL6GeFGn0Sh1Z3T
19 | FImxQfnla8hUPcwlBLV5iYUgr+m/f1BLao2m2B3q+n7nfvQSj9/w31yckTvqYDv8
20 | ZZW74ncud64quzEgM631mqp4mnG6pbsmuo2gN+pELHqQMefapOfk+wBHFKEj6fy/
21 | bRKa7otF25ouzwfT5rtd81TEQu3daWPXcbqH6my81k36FUB061B52omYKI75urbL
22 | ZBqUGnholNkdaMS7dIiF
23 | -----END CERTIFICATE-----
24 |
--------------------------------------------------------------------------------
/.ssl/local.uxscoreboard.cert:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDyzCCArOgAwIBAgIJAKMIDHP9cZVaMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
3 | BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5kaXNjbzENMAsG
4 | A1UECgwEYXNhcDESMBAGA1UEAwwJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZ6
5 | YWNkYXJlbGxhbm9AZ21haWwuY29tMB4XDTIwMDMwMTAwNTQ1NloXDTIwMDMzMTAw
6 | NTQ1NlowfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4g
7 | RnJhbmRpc2NvMQ0wCwYDVQQKDARhc2FwMRIwEAYDVQQDDAlsb2NhbGhvc3QxJTAj
8 | BgkqhkiG9w0BCQEWFnphY2RhcmVsbGFub0BnbWFpbC5jb20wggEiMA0GCSqGSIb3
9 | DQEBAQUAA4IBDwAwggEKAoIBAQCij34Qx9FsV9YF8R9QaAWQdGi0WFdEsudIc6TI
10 | sed7RISO97daZTd515OuTDzy+zb/3rG8uWysyEX2w/nVJ/ydLKKozqYjAK5wegCb
11 | mosRGFAkiA2IlrjjVs3NqiZkcXc2QOhE/UPD2ETX3vzJXVO7xJnEV21afW9PWcjF
12 | gI0Td796nEJB5ZZOYjxqBt2SYN3Df7sXaET/MX9nYTC2KOy9oPvAeZ9jWTf5Sk67
13 | lpDWT2fspVa5+91GE4uMuCzDWeNGp2HGh0fbw1+MacXRDbyNwBG88CaAvOp/cqO1
14 | JtVY3sSk3EJZmyIpiqvYxmQ2HvcW6FGgwiVNufbpddgssOwxAgMBAAGjUDBOMB0G
15 | A1UdDgQWBBRbebvhOiJ3iYZzbWjvaobelrvN1DAfBgNVHSMEGDAWgBRbebvhOiJ3
16 | iYZzbWjvaobelrvN1DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBm
17 | NDktXVDeRCdV+n981uL9+2j/u9HGcTrJIP6jFc7+LgDX6jB3KS0xNkcjKTLVJWFo
18 | 7DgI6C9GqkWbomvUMjBccMxCqYljoNo3rJgp+g0QnEuQjHQ9M7gg1ZpOfXn9aW8a
19 | 1hR+gDIyptZQ7EhCUat1spWGl0U6+G1ClRtYXwqhFmQ7xYZLy+MRGjhk06ixglWK
20 | Ery4cv55fO9TRyVQ+HyijJTFkQm/UT+wMNMTw3O7rjIj3aZ3gjbJf0JXzGvXAHBo
21 | g///yzmb5mkGmEp5uyzcSngKBqdFE2DPJB89Er6vJaWQNpO5pynWPLF60eDz0KTP
22 | uel8/NRk+TdV/BfBiPRL
23 | -----END CERTIFICATE-----
24 |
--------------------------------------------------------------------------------
/src/components/Team/Team.scss:
--------------------------------------------------------------------------------
1 |
2 | /* ------- Team ------- */
3 | .container {
4 | display:flex;
5 | align-items:center;
6 | padding:2px 0;
7 | }
8 | .info {
9 | display:flex;
10 | align-items:center;
11 | justify-content:space-between;
12 | flex:2;
13 | }
14 | .leftSide {
15 | display:flex;
16 | flex-direction:column;
17 | flex:2;
18 | }
19 | .rightSide {
20 | padding:0.25em 0;
21 | margin:0 12px;
22 | border-radius:1px;
23 | flex-basis:60px;
24 | }
25 |
26 | .name, .record {
27 | padding:2px 8px;
28 | background:$real_white;
29 | border:1px solid $grey3;
30 | border-radius:1px;
31 | }
32 | .name {
33 | margin-bottom:6px;
34 | font-size:110%;
35 | max-width:133px;
36 | }
37 | .name small {
38 | font-size:100%;
39 | letter-spacing:0px;
40 | }
41 | .record {
42 | display:table;
43 | margin-right:auto;
44 | font-size:66.6%;
45 | font-weight:700;
46 | padding-top:3px;
47 | font-weight:400;
48 | }
49 | .score {
50 | display:block;
51 | font-size:125%;
52 | text-align:center;
53 | font-weight:lighter;
54 | }
55 |
56 | // ..edge case for custom Yankees style..
57 | // ___go_Giants__¯\_(ツ)_/¯ ___
58 | .nyy {
59 | position:relative;
60 | composes:container;
61 | }
62 | .nyy * {
63 | z-index:1;
64 | }
65 | .nyy::before {
66 | content:'';
67 | position:absolute;
68 | left:0;top:0;
69 | width:40%;height:100%;
70 | background:linear-gradient(to right,#ffffff 86%,#142448 14%) -27.5% 100% / 15px;
71 | }
72 |
--------------------------------------------------------------------------------
/src/components/BoxScore/BoxScore.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | formatBoxScoreTableHead,
4 | formatBoxScoreTableBodyRow
5 | } from 'helpers/boxScoreFns'
6 | import s from './BoxScore.scss'
7 |
8 | const BoxScore = ({
9 | awayTeam,
10 | homeTeam,
11 | awayScore,
12 | homeScore,
13 | linescore,
14 | periods,
15 | totalPeriods,
16 | league,
17 | overtimes,
18 | children
19 | }) => (
20 |
21 |
22 |
31 |
32 |
43 |
54 |
55 |
56 | {children}
57 |
58 | )
59 |
60 | export default BoxScore
61 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/ari.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/soccer.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/soccer_light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/css/fonts.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Comfortaa';
3 | font-style: normal;
4 | font-weight: 300;
5 | font-display: swap;
6 | src: local('Comfortaa'),
7 | url('../fonts/comfortaa-v28-latin-300.woff2') format('woff2'),
8 | url('../fonts/comfortaa-v28-latin-300.woff') format('woff');
9 | }
10 | @font-face {
11 | font-family: 'Comfortaa';
12 | font-style: normal;
13 | font-weight: 400;
14 | font-display: swap;
15 | src: local('Comfortaa'),
16 | url('../fonts/comfortaa-v28-latin-regular.woff2') format('woff2'),
17 | url('../fonts/comfortaa-v28-latin-regular.woff') format('woff');
18 | }
19 | /* @font-face {
20 | font-family: 'Comfortaa';
21 | font-style: normal;
22 | font-weight: 500;
23 | font-display: swap;
24 | src: local('Comfortaa'), url('../fonts/comfortaa-v28-latin-500.woff2') format('woff2'),
25 | url('../fonts/comfortaa-v28-latin-500.woff') format('woff');
26 | }
27 | @font-face {
28 | font-family: 'Comfortaa';
29 | font-style: normal;
30 | font-weight: 600;
31 | font-display: swap;
32 | src: local('Comfortaa'), url('../fonts/comfortaa-v28-latin-600.woff2') format('woff2'),
33 | url('../fonts/comfortaa-v28-latin-600.woff') format('woff');
34 | } */
35 | @font-face {
36 | font-family: 'Comfortaa';
37 | font-style: normal;
38 | font-weight: 700;
39 | font-display: swap;
40 | src: local('Comfortaa'),
41 | url('../fonts/comfortaa-v28-latin-700.woff2') format('woff2'),
42 | url('../fonts/comfortaa-v28-latin-700.woff') format('woff');
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/Team/Team.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { LogoContainer } from 'containers'
3 | import { TEAM_COLORS } from 'data/teamColors'
4 | import s from './Team.scss'
5 |
6 | const makeBgImg = (code, league) => ({
7 | backgroundImage:
8 | code !== 'nyy' &&
9 | `linear-gradient(to right,${TEAM_COLORS[league][code]} 40%,transparent 0%)`
10 | })
11 |
12 | const Team = ({
13 | name,
14 | code,
15 | filetype = 'svg',
16 | ws,
17 | ls,
18 | ts,
19 | score,
20 | league,
21 | hasLoaded,
22 | logoHasLoaded
23 | }) => (
24 |
28 |
34 |
35 |
36 |
37 | {name.length >= 9 ? {name} : name}
38 |
39 | {ws && ls && (
40 | {`(${ws}-${ls}${
41 | ts ? `-${ts}` : ''
42 | })`}
43 | )}
44 |
45 |
49 | {score && {score}}
50 |
51 |
52 |
53 | )
54 |
55 | export default Team
56 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/tex.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Home/Item.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import styled from '@emotion/styled'
4 |
5 | const ListItem = styled.li(({ theme }) => ({
6 | flexBasis: '48%',
7 | maxWidth: 240,
8 | [theme.mq('medium')]: {
9 | flexBasis: '33.3%'
10 | },
11 | [theme.mq('xlarge')]: {
12 | flexBasis: '20%'
13 | }
14 | }))
15 |
16 | const LinkButton = styled(Link)(({ theme }) => ({
17 | display: 'block',
18 | padding: '7%',
19 | margin: '7%',
20 | background: theme.colors.white,
21 | border: `1px solid ${theme.colors.grey[6]}`,
22 | borderRadius: 4,
23 | position: 'relative',
24 | transition: 'color 0.11s, background-color 0.22s, box-shadow 0.22s',
25 | transitionDelay: '0.11s',
26 | '&:hover': {
27 | color: 'transparent',
28 | background: theme.colors.grey[6],
29 | boxShadow: `inset 0 0 0 5px ${theme.colors.white}`
30 | }
31 | }))
32 |
33 | const IconWrapper = styled.div({
34 | position: 'absolute',
35 | top: 0,
36 | bottom: 0,
37 | left: 0,
38 | right: 0,
39 | padding: '10px 0',
40 | display: 'flex',
41 | alignItems: 'center',
42 | justifyContent: 'center',
43 | opacity: '0',
44 | transition: 'opacity .22s',
45 | '&:hover': {
46 | opacity: '1',
47 | transitionDelay: '0.11s'
48 | }
49 | })
50 |
51 | const Item = ({ name, url, icon: Icon }) => (
52 |
53 |
54 | {name}
55 |
56 |
57 |
58 |
59 |
60 | )
61 |
62 | export default Item
63 |
--------------------------------------------------------------------------------
/src/components/Home/Home.scss:
--------------------------------------------------------------------------------
1 | /* ------- Home ------- */
2 | .container {
3 | text-align: center;
4 | flex-direction: column;
5 | composes: flexy from 'styles/_utils.scss';
6 | }
7 | .uxscoreboard {
8 | display: flex;
9 | flex: 1;
10 | flex-direction: column;
11 | justify-content: center;
12 | margin: 2.5% 0 -2.5%;
13 | width: 90%;
14 | letter-spacing: 0;
15 | }
16 | .name {
17 | font-size: 2.1em;
18 | text-shadow: 3px 0px 0px $real_white;
19 | }
20 | .description {
21 | font-size: 1.15em;
22 | line-height: 1.15em;
23 | color: $grey6;
24 | }
25 | .menu {
26 | width: 80%;
27 | flex: 1;
28 | font-size: 1.2em;
29 | }
30 | .list {
31 | display: flex;
32 | flex-flow: row wrap;
33 | justify-content: center;
34 | }
35 |
36 | @media only screen and (min-width: $medium) {
37 | /* ------- Home ------- */
38 | @media (orientation: portrait) {
39 | .uxscoreboard {
40 | font-size: 1.6em;
41 | }
42 | }
43 | @media (orientation: landscape) {
44 | .uxscoreboard {
45 | font-size: 1.16em;
46 | margin-bottom: 2.5%;
47 | }
48 | }
49 | .menu {
50 | width: 75%;
51 | }
52 | }
53 |
54 | @media only screen and (min-width: $large) {
55 | /* ------- Home ------- */
56 | .uxscoreboard {
57 | font-size: 1.8em;
58 | }
59 | }
60 |
61 | @media only screen and (min-width: $xlarge) {
62 | /* ------- Home ------- */
63 | .uxscoreboard {
64 | font-size: 2.2em;
65 | }
66 | }
67 |
68 | @media only screen and (min-width: $xxlarge) {
69 | /* ------- Home ------- */
70 | .uxscoreboard {
71 | font-size: 2.4em;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/sf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/football.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/football_light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/sd.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/SoccerLight.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const SoccerLight = props => (
4 |
16 | )
17 | export default SoccerLight
18 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/nym.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/cin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | /* ------- base components ------- */
2 | import GlobalStyles from './Global/GlobalStyles'
3 | import Header from './Header/Header'
4 | import Footer from './Footer/Footer'
5 | import Social from './Social/Social'
6 |
7 | /* ------- page components ------- */
8 | import Home from './Home/Home'
9 | import About from './About/About'
10 | import NotFound from './NotFound/NotFound'
11 | import Test from './Test/Test'
12 |
13 | /* ------- league components ------- */
14 | import League from './League/League'
15 | import Loading from './Loading/Loading'
16 | import NoGames from './NoGames/NoGames'
17 |
18 | /* ------- scoreboard components ------- */
19 | import Scoreboard from './Scoreboard/Scoreboard'
20 | import Date from './Date/Date'
21 | import Event from './Event/Event'
22 |
23 | /* ------- game components ------- */
24 | import Game from './Game/Game'
25 | import GameState from './GameState/GameState'
26 | import Team from './Team/Team'
27 | import Logo from './Logo/Logo'
28 | import Expand from './Expand/Expand'
29 | import Details from './Details/Details'
30 | import Matchup from './Matchup/Matchup'
31 | import PanelMenu from './PanelMenu/PanelMenu'
32 | import BoxScore from './BoxScore/BoxScore'
33 | import Stats from './Stats/Stats'
34 | import Diamond from './Diamond/Diamond'
35 | import UpdateTime from './UpdateTime/UpdateTime'
36 |
37 | export {
38 | GlobalStyles,
39 | Header,
40 | Footer,
41 | Social,
42 | Home,
43 | About,
44 | NotFound,
45 | Test,
46 | League,
47 | Loading,
48 | NoGames,
49 | Scoreboard,
50 | Date,
51 | Event,
52 | Game,
53 | GameState,
54 | Team,
55 | Logo,
56 | Expand,
57 | Details,
58 | Matchup,
59 | PanelMenu,
60 | BoxScore,
61 | Stats,
62 | Diamond,
63 | UpdateTime
64 | }
65 |
--------------------------------------------------------------------------------
/.ssl/local.uxscoreboard.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCij34Qx9FsV9YF
3 | 8R9QaAWQdGi0WFdEsudIc6TIsed7RISO97daZTd515OuTDzy+zb/3rG8uWysyEX2
4 | w/nVJ/ydLKKozqYjAK5wegCbmosRGFAkiA2IlrjjVs3NqiZkcXc2QOhE/UPD2ETX
5 | 3vzJXVO7xJnEV21afW9PWcjFgI0Td796nEJB5ZZOYjxqBt2SYN3Df7sXaET/MX9n
6 | YTC2KOy9oPvAeZ9jWTf5Sk67lpDWT2fspVa5+91GE4uMuCzDWeNGp2HGh0fbw1+M
7 | acXRDbyNwBG88CaAvOp/cqO1JtVY3sSk3EJZmyIpiqvYxmQ2HvcW6FGgwiVNufbp
8 | ddgssOwxAgMBAAECggEACKCA2jcggBVxQEN5m/TJRs9Gtztw9i5gY9HvTs9SL5KF
9 | TmkLC/sPJKIyLi/RlDuMwat510An6kt+mwfvfEZw+45g24CMBJOGrmVrHtbmxnN5
10 | 3VZzEWqeSUJUDbQX8YCsCO8M5IxkSkJx9Nrao30WcoKOfoL9RK4feCCVrRWTZxMA
11 | IUWsgAc3L2H5LUedxgq2TVg5n5fM8NeAfCsATxf3F00IY5KFHKLqGks10NPbNv6a
12 | zWU/hWhkfEsmgtoNjjHpQep1GhVP345TrrelHJt7NQqCE+gLgvca53ZZxhgd/FH0
13 | a0DJ7Xbqsf/UxjSg6fN53UAnPncG/oHlYETNdq68AQKBgQDXV+YvAJIIS9ZQNvCF
14 | NakTmH+qvuESPoHiZHJj3fN1qHX6E+nEIVYFw2zf0ZjEjSvmLZl3IqKgS7lZtDpi
15 | 8ZVQbadcoqNStgSk/j5+LSPh0HHD08t6ac4l+MgahMQjbK6WmBban0RDYoi6qGA6
16 | x+o8WLBQYS2jPE83TEUMbqBfkQKBgQDBQHZZfy0LGGVP6pnf0k3Fv1mk5Sg1NT6a
17 | 09zz2TgZ873yLCCDurk95freDjq9gIDag7d0OeA9ExxOjTmZhBRm/dLmCfNq/+Wm
18 | BCJ1suKnnmmabhDXcUZeLC+rwDf/eHwWHOAcVlIl0BiOQrJ7YmaAGX0CrMptWHRT
19 | ZttJNEKyoQKBgQC2WNkyvdHqsoUUwaDju++jB0Q8Cow9za5gTVxL0psg63iTPZ96
20 | lBvqn3QnBq3M6hndPImyRyLch6K5IhM6y4T7aQ8KMzIHjNfb+upiH8k9oytiforT
21 | 6ppEryiKxBfuQ06HeNwJXkoIoXxUStXJkJV5akRKnC9wBVay5aOmhlrI8QKBgBZw
22 | n82AoQBa0tjWywtcZj+CGqy1F1gmWKCt3UhLfuI0lWfbi+L/+aagX+MND/EVTOES
23 | zmiml80/vNe6OmgDGENlUen4WdWg5U/DxySXow4HrQeLpFUACEXDJ73v0hDOMsp2
24 | i+d+doeBhHhwcj01jeVuEEARRGgyDz/n/6qecOhBAoGBAJDE7zUSVPnh6UWoINI0
25 | zsDAwpoRiVWxdjqhHKAYBZwhqSMgA8PeqG4MCn9J06tD2uKJqpERmqjSVhoWNELA
26 | w/0DZzGp38OvSXKz/BEQubYMlGKUoKsIrGB3nQCo6PmsYVygoAtzKwoZ1G0FxQAo
27 | 97RYRCwfB9xN5g4QPL/X8ds2
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/src/components/Stats/props.js:
--------------------------------------------------------------------------------
1 | import { shortenTeamName } from 'helpers/utils'
2 |
3 | // nba stats props --> Stats component
4 | export const nbaStatsProps = game => {
5 | const awayStats = game.vTeam.stats
6 | const homeStats = game.hTeam.stats
7 | const inGame = game.statusNum === 2 || game.statusNum === 3
8 | const empty = ['', '']
9 |
10 | return {
11 | teams: [
12 | shortenTeamName(game.vTeam.nickname),
13 | shortenTeamName(game.hTeam.nickname)
14 | ],
15 | Points: inGame
16 | ? [awayStats?.totals?.points, homeStats?.totals?.points]
17 | : empty,
18 | 'Field-Goal %': inGame
19 | ? [`${awayStats?.totals?.fgp}%`, `${homeStats?.totals?.fgp}%`]
20 | : empty,
21 | '3-Point %': inGame
22 | ? [`${awayStats?.totals?.tpp}%`, `${homeStats?.totals?.tpp}%`]
23 | : empty,
24 | 'Free-Throw %': inGame
25 | ? [`${awayStats?.totals?.ftp}%`, `${homeStats?.totals?.ftp}%`]
26 | : empty,
27 | Rebounds: inGame
28 | ? [awayStats?.totals?.totReb, homeStats?.totals?.totReb]
29 | : empty,
30 | Assists: inGame
31 | ? [awayStats?.totals?.assists, homeStats?.totals?.assists]
32 | : empty,
33 | Blocks: inGame
34 | ? [awayStats?.totals?.blocks, homeStats?.totals?.blocks]
35 | : empty,
36 | Fouls: inGame
37 | ? [
38 | Number(awayStats?.totals?.pFouls) +
39 | Number(awayStats?.totals?.team_fouls),
40 | Number(homeStats?.totals?.pFouls) +
41 | Number(homeStats?.totals?.team_fouls)
42 | ]
43 | : empty,
44 | Steals: inGame
45 | ? [awayStats?.totals?.steals, homeStats?.totals?.steals]
46 | : empty,
47 | Turnovers: inGame
48 | ? [awayStats?.totals?.turnovers, homeStats?.totals?.turnovers]
49 | : empty
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/.ssl/local.api.uxscoreboard.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCxJGktbSI0xcf
3 | uhjhjfTgd0jHPQaJYzHfEDkepqnDzVQxjB3I3hPsPqrPbr4eOeYM2OKYf+UjMEPi
4 | 2imzVzSEbLRhRctugPc9Ns7fdx84sx7uZWKTNbYNkVcXWJ3QHsk6Mn4PopUGP5V7
5 | 8Yszw7zht6Rtn6/dWXxCs1onwr6fYT76Tw4OP4z1huPOEGMs240FXb6GA/Jv2RFN
6 | S9v0AOxYbJs+WNBtuMmViBZr7f+pZPZjoGVL7ph0KNJFd4q8PwsEaUPF61eC+7Wx
7 | ugbCZZuRMhcF0jKkOs5qJeFYFVoWVd1IBPsJOrJbnRZA2L8Zs9WiZfn8Tv8aLbWT
8 | UIaNUElXAgMBAAECggEAUzfuCvRgeP3IqZO4QdeHw8jz3b1jZ5xsmKSz4zbO3oyC
9 | 5tC5gTPyS0TTcuIcdYnDk8Wvu/0ZiJl8yBbzLl+aXuKbh9xseYC9IviiF7UsL+vI
10 | 0Y+cOM4ARXfOQHlqpJwi2qzsU/3hstPEKTshgTKnJ8JxjS1n1SoA//bmFIsPK8WA
11 | caO6apGyNesngP7GqZCHEZ6dbgWhY20OcfAUXFA4C6jzOaFEQQY/OG0uipMunTQ4
12 | Z5v3o+EmsllE/Jg0IqaFxSmnJCSgm1a/gBa/EGU+VfkXrRWBXao9lh6UPOgvTbiF
13 | 1dW668ULVQmTkQd9c/AvYHBT36/maMd+goig61gskQKBgQDhevWfXsyMFk8qvgVH
14 | 78NLymWqzePSmgfw4PMIIgVicafeEYPHn7JxcvKTkou7AGdoSUYeLRnCJI+fm3Cs
15 | y5aq02XQbCqliHwEUqwIbnGfb5sv73frPPQwN/FsavOtYgL5JJ0ATTdtgfMITk82
16 | fCzHqJvJaugk58K4YFPl2Xv99QKBgQDdIWcJE4Af3hb/xGFEAoAoyGIwrQkStzRp
17 | g+R7IyRKlWl93oP7DWAsX9VHPoR3q4rqWW4yRxFx17nlgVVIODLW+EhAhZUiiaKS
18 | Y85tI6k/9Iq6fXKX02nXL3FEdYLWBywKdYswM94M03nQj6U/l7dnowPDs9VYGOAg
19 | H0QNOH2umwKBgFB+eNkgGp2YCUMU+wWnTmzsIEdNoRs56cPQSnKI0qtGL8tTLVxC
20 | Yo2ln3uXQVLM3h2YqvNy3lM0giFUeHD3bR4mwZ4aP09A3iUc7lVEOEPWr+v47TKJ
21 | XBMqpYEy3TL0XmNa8sF8z2DEl2JLsnOc+M9+Hb9V7vHd2uG/YbQYY+9RAoGASz/4
22 | rjACo9WSAgu0Bc7MChTWxTW9Jq9NmLKXaAf3a6IYugqk4yQUHEWocIwnzAmGRmWU
23 | iGuC1jjRO/adLOxi8KChBcPvkkdAGYz5TE6cY/U12GykOGJrXBz68I8ErQzXwd9q
24 | NEcSQ08b3wti8pcQhKYGu+xQp6AfdQ7uu4kDzA0CgYEAn2XBlt4d2gJTYQ2N8wZx
25 | Im0Y7P+HWDjbcYOlFKWbJ9/PAERV2/7x1o3hgIuXhsUH8bu22QRI27fcDzLKO/Sb
26 | wDtLxbL+dG/WdzFCkH+bzGMf08WOiH4QjZlRbS9I6gPHQL34IarMQ+fOslh8c7Ih
27 | o8MbtI4LhsGkUxBown2vYWg=
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/src/containers/Nfl/NflContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { League } from 'components'
3 | import { getTodaysDate } from 'helpers/utils'
4 | import { updatePageInfo } from 'config/metadata'
5 | import { getNflScores } from 'helpers/api'
6 |
7 | class NflContainer extends Component {
8 | constructor() {
9 | super()
10 | this.state = {
11 | isLoading: true,
12 | isValid: true,
13 | isError: false,
14 | scores: {},
15 | year: '',
16 | today: '',
17 | week: 1
18 | }
19 | }
20 |
21 | componentDidMount() {
22 | const pageInfo = {
23 | title: `${this.props.league.toUpperCase()} scores · uxscoreboard`,
24 | desc: `live ${this.props.league.toUpperCase()} scores · uxscoreboard`
25 | }
26 | updatePageInfo(pageInfo)
27 | this.setState({ today: getTodaysDate() }, () => {
28 | this.makeRequest(this.props.match.params.week)
29 | })
30 | }
31 |
32 | componentDidUpdate(prevProps) {
33 | if (prevProps.match.params.date !== this.props.match.params.date) {
34 | clearTimeout(this.refreshId)
35 | this.makeRequest(this.props.match.params.date)
36 | }
37 | }
38 |
39 | makeRequest(week = this.state.week) {
40 | getNflScores(week)
41 | .then(data => {
42 | this.setState({
43 | isLoading: false,
44 | scores: data.games,
45 | year: data.year
46 | })
47 | })
48 | .catch(error => {
49 | this.setState({
50 | isLoading: false,
51 | isError: true
52 | })
53 | throw new Error(error)
54 | })
55 | }
56 |
57 | render() {
58 | return
59 | }
60 | }
61 |
62 | NflContainer.defaultProps = { league: 'nfl' }
63 |
64 | export default NflContainer
65 |
--------------------------------------------------------------------------------
/src/components/GameState/GameState.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { periodSuffix } from 'helpers/utils'
3 | import s from './GameState.scss'
4 |
5 | const GameState = props => {
6 | switch (props.gameState) {
7 | case 0:
8 | return
9 | case 1:
10 | return
11 | case 2:
12 | return
13 | }
14 | }
15 |
16 | export default GameState
17 |
18 | const PreGameState = ({
19 | time,
20 | isPlayoffs,
21 | playoffs,
22 | isDoubleHeader,
23 | doubleHeader
24 | }) => (
25 |
26 | {time}
27 | {isDoubleHeader && {`Game ${doubleHeader} of 2`}}
28 | {isPlayoffs && (
29 | {`Game ${playoffs.game} of ${playoffs.maxGames}`}
30 | )}
31 |
32 | )
33 |
34 | const InGameState = ({
35 | currentTime,
36 | currentPeriod,
37 | isHalfTime,
38 | status,
39 | inGameDelay
40 | }) => (
41 |
42 | {!isHalfTime && (
43 |
44 | {currentTime && `${currentTime} · `}
45 | {currentPeriod}
46 | {periodSuffix(currentPeriod)}
47 |
48 | )}
49 | {(isHalfTime || inGameDelay) && {status}}
50 |
51 | )
52 |
53 | const PostGameState = ({
54 | periods,
55 | totalPeriods,
56 | status,
57 | overtime,
58 | isDoubleHeader,
59 | doubleHeader,
60 | isPlayoffs,
61 | playoffs
62 | }) => (
63 |
64 | {totalPeriods > periods ? `${status}/${overtime}` : status}
65 | {isDoubleHeader && {`Game ${doubleHeader} of 2`}}
66 | {isPlayoffs && (
67 | {`Game ${playoffs.game} of ${playoffs.maxGames}`}
68 | )}
69 |
70 | )
71 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/basketball.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/basketball_light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/was.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Global/GlobalStyles.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Global, css } from '@emotion/core'
3 |
4 | const GlobalStyles = () => (
5 | css`
7 | * {
8 | margin: 0;
9 | padding: 0;
10 | }
11 | html,
12 | body {
13 | height: 100%;
14 | box-sizing: border-box;
15 | }
16 | *,
17 | ::before,
18 | *::after {
19 | box-sizing: inherit;
20 | }
21 | img {
22 | max-width: 100%;
23 | }
24 | a {
25 | color: inherit;
26 | text-decoration: none;
27 | }
28 | ul,
29 | menu {
30 | list-style-type: none;
31 | }
32 | h1,
33 | h2,
34 | h3,
35 | h4,
36 | h5,
37 | h6 {
38 | font-weight: inherit;
39 | }
40 | strong {
41 | font-weight: 700;
42 | }
43 | figure {
44 | text-align: center;
45 | }
46 | figcaption {
47 | font-size: 0.9em;
48 | }
49 | table,
50 | hr {
51 | border: 0;
52 | }
53 | nav,
54 | menu {
55 | letter-spacing: 0;
56 | }
57 | sup {
58 | font-size: 66%;
59 | }
60 |
61 | body {
62 | background: ${theme.colors.grey[3]};
63 | color: ${theme.colors.grey[8]};
64 | font: 400 16px/1.45 'Avenir Next', Avenir, 'Segoe UI', Roboto,
65 | 'Helvetica Neue', sans-serif;
66 | letter-spacing: 1px;
67 | transition: opacity 0.44s 0.66s;
68 | text-rendering: optimizeLegibility;
69 | -webkit-font-smoothing: antialiased;
70 | -webkit-tap-highlight-color: transparent;
71 | -moz-osx-font-smoothing: grayscale;
72 | }
73 | #root {
74 | height: 100%;
75 | font-family: 'Comfortaa', cursive;
76 | }
77 | `}
78 | />
79 | )
80 |
81 | export default GlobalStyles
82 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/vgk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/FootballLight.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const FootballLight = props => (
4 |
20 | )
21 | export default FootballLight
22 |
--------------------------------------------------------------------------------
/src/containers/Nhl/NhlContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { League } from 'components'
3 | import { getTodaysDate, isValidDate } from 'helpers/utils'
4 | import { updatePageInfo } from 'config/metadata'
5 | import { getNhlScores } from 'helpers/api'
6 |
7 | class NhlContainer extends Component {
8 | constructor() {
9 | super()
10 | this.state = {
11 | isLoading: true,
12 | isValid: false,
13 | isError: false,
14 | scores: {},
15 | year: '',
16 | date: '',
17 | today: ''
18 | }
19 | }
20 |
21 | componentDidMount() {
22 | const pageInfo = {
23 | title: `${this.props.league.toUpperCase()} scores · uxscoreboard`,
24 | desc: `live ${this.props.league.toUpperCase()} scores · uxscoreboard`
25 | }
26 | updatePageInfo(pageInfo)
27 | this.setState({ today: getTodaysDate() }, () => {
28 | this.makeRequest(this.props.match.params.date)
29 | })
30 | }
31 |
32 | componentDidUpdate(prevProps) {
33 | if (prevProps.match.params.date !== this.props.match.params.date) {
34 | clearTimeout(this.refreshId)
35 | this.makeRequest(this.props.match.params.date)
36 | }
37 | }
38 |
39 | makeRequest(dt = this.state.today) {
40 | if (isValidDate(dt)) {
41 | this.setState({ isValid: true })
42 | }
43 | getNhlScores(dt)
44 | .then(currentScores => {
45 | this.setState({
46 | isLoading: false,
47 | scores: currentScores,
48 | year: '20162017',
49 | date: dt
50 | })
51 | })
52 | .catch(error => {
53 | this.setState({
54 | isLoading: false,
55 | isError: true,
56 | date: dt
57 | })
58 | throw new Error(error)
59 | })
60 | }
61 |
62 | render() {
63 | return
64 | }
65 | }
66 |
67 | NhlContainer.defaultProps = { league: 'nhl' }
68 | export default NhlContainer
69 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/ne.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/ari.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/phi.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/BasketballLight.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const BasketballLight = props => (
4 |
16 | )
17 |
18 | export default BasketballLight
19 |
--------------------------------------------------------------------------------
/src/components/BoxScore/props.js:
--------------------------------------------------------------------------------
1 | import { shortenTeamName } from 'helpers/utils'
2 |
3 | // mlb box score props --> BoxScore component
4 | export const mlbBoxScoreProps = (game, league) => {
5 | const hasStarted = game.linescore
6 | const size =
7 | hasStarted && game.linescore.innings ? game.linescore.innings.length : 0
8 | return {
9 | awayTeam: shortenTeamName(game.teams.away.team.teamName),
10 | homeTeam: shortenTeamName(game.teams.home.team.teamName),
11 | awayScore:
12 | hasStarted && game.linescore.teams.away.runs !== undefined
13 | ? game.linescore.teams.away.runs
14 | : '',
15 | homeScore:
16 | hasStarted && game.linescore.teams.home.runs !== undefined
17 | ? game.linescore.teams.home.runs
18 | : '',
19 | linescore: game.linescore ? game.linescore : '',
20 | periods: 9,
21 | totalPeriods: size,
22 | overtimes: size > 9 ? size - 9 : 0,
23 | league
24 | }
25 | }
26 |
27 | // nba box score props --> BoxScore component
28 | export const nbaBoxScoreProps = (game, league) => {
29 | const inGame = game.period.current
30 | const size = Number(game.period.current)
31 | return {
32 | awayTeam: shortenTeamName(game.vTeam.nickname),
33 | homeTeam: shortenTeamName(game.hTeam.nickname),
34 | awayScore: inGame ? game.vTeam.score : '',
35 | homeScore: inGame ? game.hTeam.score : '',
36 | linescore: {
37 | away: game.vTeam.linescore,
38 | home: game.hTeam.linescore
39 | },
40 | periods: 4,
41 | totalPeriods: size,
42 | overtimes: game.period.current > 4 ? size - 4 : 0,
43 | league
44 | }
45 | }
46 |
47 | // nhl box score props --> BoxScore component
48 | export const nhlBoxScoreProps = (game, league) => {
49 | const inGame = game.status.codedGameState > '2'
50 | const isAllStar = game.gameType === 'A'
51 | return {
52 | awayTeam: shortenTeamName(game.teams.away.team.teamName),
53 | homeTeam: shortenTeamName(game.teams.home.team.teamName),
54 | awayScore: inGame ? game.linescore.teams.away.goals : '',
55 | homeScore: inGame ? game.linescore.teams.home.goals : '',
56 | linescore: game.linescore.periods,
57 | periods: isAllStar ? 2 : 3,
58 | totalPeriods: game.linescore.periods.length,
59 | league
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # https://circleci.com/docs/2.0/local-cli/
2 |
3 | defaults: &defaults
4 | docker:
5 | - image: circleci/node:11
6 | working_directory: ~/uxscoreboard
7 | environment:
8 | BUILD_DIR: ./dist
9 | NODE_APP: ./server.js
10 |
11 | version: 2
12 | jobs:
13 | build:
14 | <<: *defaults
15 | steps:
16 | - checkout
17 | - attach_workspace:
18 | at: ~/uxscoreboard
19 | - restore_cache:
20 | key: v1-yarn-{{ checksum "yarn.lock" }}s
21 | - run: yarn
22 | - save_cache:
23 | key: v1-yarn-{{ checksum "yarn.lock" }}
24 | paths:
25 | - ./node_modules
26 | - run: yarn test
27 | - run: yarn build
28 | - persist_to_workspace:
29 | root: .
30 | paths: .
31 |
32 | deploy:
33 | <<: *defaults
34 | steps:
35 | - checkout
36 | - attach_workspace:
37 | at: ~/uxscoreboard
38 | - restore_cache:
39 | key: v1-dist-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }}
40 | - run:
41 | name: Fix host authenticity for $DROPLET_IP
42 | command: |
43 | ssh-keyscan $DROPLET_IP >> ~/.ssh/known_hosts
44 | - run:
45 | name: Keyscan Github (HACK)
46 | command: ssh-keyscan -H github.com >> ~/.ssh/known_hosts # - add_ssh_keys:
47 | - add_ssh_keys
48 | - run:
49 | name: deploy
50 | command: |
51 | if [ "${CIRCLE_BRANCH}" = "master" ]; then
52 | sudo apt-get update && sudo apt-get install rsync
53 | rsync -avce ssh . $DROPLET_USER@$DROPLET_IP:/home/zac/apps/uxscoreboard
54 | elif [ "${CIRCLE_BRANCH}" = "staging" ]; then
55 | sudo apt-get update && sudo apt-get install rsync
56 | rsync -avce ssh . $DROPLET_USER@$DROPLET_IP:/home/zac/apps/uxscoreboard-staging
57 | else
58 | echo "Not master branch, dry run only"
59 | fi
60 |
61 | workflows:
62 | version: 2
63 |
64 | build-and-deploy:
65 | jobs:
66 | - build
67 | - deploy:
68 | requires:
69 | - build
70 | filters:
71 | branches:
72 | only:
73 | - master
74 | - staging
75 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/pit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/ana.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/stl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/mtl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/config/Routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Route, Switch } from 'react-router-dom'
3 |
4 | const MlbContainer = React.lazy(() =>
5 | import(/* webpackChunkName: "mlb" */ '../containers/Mlb/MlbContainer')
6 | )
7 | const NbaContainer = React.lazy(() =>
8 | import(/* webpackChunkName: "nba" */ '../containers/Nba/NbaContainer')
9 | )
10 | const NhlContainer = React.lazy(() =>
11 | import(/* webpackChunkName: "nhl" */ '../containers/Nhl/NhlContainer')
12 | )
13 | const NflContainer = React.lazy(() =>
14 | import(/* webpackChunkName: "nfl" */ '../containers/Nfl/NflContainer')
15 | )
16 | const AboutContainer = React.lazy(() =>
17 | import(/* webpackChunkName: "about" */ '../containers/About/AboutContainer')
18 | )
19 | const HomeContainer = React.lazy(() =>
20 | import(/* webpackChunkName: "home" */ '../containers/Home/HomeContainer')
21 | )
22 | const NotFoundContainer = React.lazy(() =>
23 | import(
24 | /* webpackChunkName: "404" */ '../containers/NotFound/NotFoundContainer'
25 | )
26 | )
27 | const TestContainer = React.lazy(() =>
28 | import(/* webpackChunkName: "test" */ '../containers/Test/TestContainer')
29 | )
30 |
31 | const Routes = () => (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | {__DEV__ && }
49 |
50 |
51 |
52 | )
53 |
54 | export default Routes
55 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/stl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond_2b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond_3b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond_1b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond_1b_2b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond_1b_2b_3b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond_1b_3b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/other/diamond_2b_3b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/containers/Nba/NbaContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { League } from 'components'
3 | import { getTodaysDate, isValidDate } from 'helpers/utils'
4 | import { getNbaScores } from 'helpers/api'
5 | import { updatePageInfo } from 'config/metadata'
6 |
7 | class NbaContainer extends Component {
8 | static defaultProps = { league: 'nba' }
9 | state = {
10 | isLoading: true,
11 | isValid: false,
12 | isError: false,
13 | scores: {},
14 | year: '',
15 | date: '',
16 | today: '',
17 | lastUpdated: null
18 | }
19 |
20 | componentDidMount() {
21 | const pageInfo = {
22 | title: `${this.props.league.toUpperCase()} scores · uxscoreboard`,
23 | desc: `live ${this.props.league.toUpperCase()} scores · uxscoreboard`
24 | }
25 | updatePageInfo(pageInfo)
26 | this.setState({ today: getTodaysDate() }, () => {
27 | this.makeRequest(this.props.match.params.date)
28 | })
29 | }
30 |
31 | componentDidUpdate(prevProps) {
32 | if (prevProps.match.params.date !== this.props.match.params.date) {
33 | clearTimeout(this.refreshId)
34 | this.makeRequest(this.props.match.params.date)
35 | }
36 | }
37 |
38 | componentWillUnmount() {
39 | clearTimeout(this.delayId)
40 | clearTimeout(this.refreshId)
41 | }
42 |
43 | makeRequest(dt = this.state.today) {
44 | if (isValidDate(dt)) {
45 | this.setState({ isValid: true })
46 | }
47 | getNbaScores(dt)
48 | .then(data => {
49 | this.setState(
50 | {
51 | isLoading: false,
52 | scores: data.games,
53 | year: data.year,
54 | date: dt,
55 | lastUpdated: Date.now()
56 | },
57 | () => this.delay()
58 | )
59 | })
60 | .catch(error => {
61 | this.setState({
62 | isLoading: false,
63 | isError: true,
64 | date: dt
65 | })
66 | throw new Error(error)
67 | })
68 | .then(() => {
69 | if (dt === this.state.today) {
70 | this.refreshScores(dt, 30)
71 | }
72 | })
73 | }
74 |
75 | delay() {
76 | if (this.state.isLoading) {
77 | this.delayId = setTimeout(() => {
78 | this.setState({ isLoading: false })
79 | }, 960)
80 | }
81 | }
82 |
83 | refreshScores(dt, seconds) {
84 | clearTimeout(this.refreshId)
85 | this.refreshId = setTimeout(() => this.makeRequest(dt), seconds * 1000)
86 | }
87 |
88 | render() {
89 | return
90 | }
91 | }
92 |
93 | export default NbaContainer
94 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/car.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/dal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/baseball.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Date/Date.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import moment from 'moment'
4 | import ArrowBack from 'react-icons/lib/io/ios-arrow-back'
5 | import ArrowForward from 'react-icons/lib/io/ios-arrow-forward'
6 | import s from './Date.scss'
7 |
8 | // TODO: implement a calendar
9 | const Date = ({ width, ...props }) => (
10 |
54 | )
55 |
56 | export default Date
57 |
58 | // TODO: remove the arrows at end of last season//start of next season
59 | function Day({ date, today, league, diff, isArrow }) {
60 | const day = moment(date).add(diff, 'days')
61 | const url = day.format('YYYYMMDD')
62 | const isMainLink = diff === 0
63 | const dayOfTheWeek =
64 | today === url
65 | ? 'today'
66 | : isMainLink
67 | ? day.format('dddd')
68 | : day.format('ddd')
69 | const formattedDate = `${dayOfTheWeek}, ${day.format('MMM D')}`.toLowerCase()
70 | const title = `${league.toUpperCase()} scores - ${day.format(
71 | 'MMMM D, YYYY'
72 | )} | uxscoreboard`
73 | return (
74 |
79 | {!isArrow ? (
80 | {formattedDate}
81 | ) : diff < 0 ? (
82 |
83 | ) : (
84 |
85 | )}
86 |
87 | )
88 | }
89 |
--------------------------------------------------------------------------------
/dist/assets/static/icons/baseball_light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/containers/Mlb/MlbContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { League } from 'components'
3 | import { getTodaysDate, isValidDate } from 'helpers/utils'
4 | import { getMlbScores } from 'helpers/api'
5 | import { updatePageInfo } from 'config/metadata'
6 |
7 | class MlbContainer extends Component {
8 | static defaultProps = { league: 'mlb' }
9 | state = {
10 | isLoading: true,
11 | isValid: false,
12 | isError: false,
13 | scores: {},
14 | year: '',
15 | date: '',
16 | today: '',
17 | lastUpdated: null
18 | }
19 |
20 | componentDidMount() {
21 | const pageInfo = {
22 | title: `${this.props.league.toUpperCase()} scores · uxscoreboard`,
23 | desc: `live ${this.props.league.toUpperCase()} scores · uxscoreboard`
24 | }
25 | updatePageInfo(pageInfo)
26 | this.setState({ today: getTodaysDate() }, () => {
27 | this.makeRequest(this.props.match.params.date)
28 | })
29 | }
30 |
31 | componentDidUpdate(prevProps) {
32 | if (prevProps.match.params.date !== this.props.match.params.date) {
33 | clearTimeout(this.refreshId)
34 | this.makeRequest(this.props.match.params.date)
35 | }
36 | }
37 |
38 | componentWillUnmount() {
39 | clearTimeout(this.delayId)
40 | clearTimeout(this.refreshId)
41 | }
42 |
43 | makeRequest(dt = this.state.today) {
44 | if (isValidDate(dt)) {
45 | this.setState({ isValid: true })
46 | }
47 |
48 | getMlbScores(dt)
49 | .then(currentScores => {
50 | const gamedayDetails = currentScores.dates[0]
51 | const scores = gamedayDetails !== undefined ? gamedayDetails.games : []
52 | const year = scores[0]
53 | ? scores[0].season
54 | : this.state.year || dt.slice(0, 4)
55 | this.setState(
56 | {
57 | date: dt,
58 | scores,
59 | year,
60 | lastUpdated: Date.now()
61 | },
62 | () => this.delay()
63 | )
64 | })
65 | .catch(error => {
66 | this.setState({
67 | isLoading: false,
68 | isError: true,
69 | date: dt
70 | })
71 | throw new Error(error)
72 | })
73 | .then(() => {
74 | if (dt === this.state.today) {
75 | this.refreshScores(dt, 30)
76 | }
77 | })
78 | }
79 |
80 | delay() {
81 | if (this.state.isLoading) {
82 | this.delayId = setTimeout(() => {
83 | this.setState({ isLoading: false })
84 | }, 960)
85 | }
86 | }
87 |
88 | refreshScores(dt, seconds) {
89 | clearTimeout(this.refreshId)
90 | this.refreshId = setTimeout(() => this.makeRequest(dt), seconds * 1000)
91 | }
92 |
93 | render() {
94 | return
95 | }
96 | }
97 |
98 | export default MlbContainer
99 |
--------------------------------------------------------------------------------
/dist/assets/static/img/mlb/teams/chc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/Details/Details.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Matchup,
4 | PanelMenu,
5 | BoxScore,
6 | Stats,
7 | Diamond,
8 | UpdateTime
9 | } from 'components'
10 | import {
11 | mlbMatchupProps,
12 | nbaMatchupProps,
13 | nhlMatchupProps
14 | } from '../Matchup/props'
15 | import {
16 | mlbBoxScoreProps,
17 | nbaBoxScoreProps,
18 | nhlBoxScoreProps
19 | } from '../BoxScore/props'
20 | import { mlbDiamondProps } from '../Diamond/props'
21 | import { nbaStatsProps } from '../Stats/props'
22 | import s from './Details.scss'
23 |
24 | const Details = props => {
25 | switch (props.league) {
26 | case 'mlb':
27 | return
28 | case 'nba':
29 | return
30 | case 'nfl':
31 | return
32 | case 'nhl':
33 | return
34 | }
35 | }
36 |
37 | export default Details
38 |
39 | const MlbDetails = ({
40 | game,
41 | date,
42 | league,
43 | panels,
44 | activePanel,
45 | switchPanel,
46 | lastUpdatedStr
47 | }) => (
48 |
49 |
50 |
55 | {activePanel === 'box score' && (
56 |
57 |
58 |
59 | )}
60 |
61 |
62 | )
63 |
64 | const NbaDetails = ({
65 | game,
66 | date,
67 | league,
68 | panels,
69 | activePanel,
70 | switchPanel,
71 | lastUpdatedStr
72 | }) => (
73 |
74 |
75 |
80 | {activePanel === 'box score' && (
81 |
82 | )}
83 | {activePanel === 'team stats' && }
84 |
85 |
86 | )
87 |
88 | const NflDetails = ({ lastUpdatedStr }) => (
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | )
97 |
98 | const NhlDetails = ({ game, date, league, lastUpdatedStr }) => (
99 |
104 | )
105 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/sea.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nfl/teams/atl.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/BaseballLight.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const BaseballLight = props => (
4 |
16 | )
17 |
18 | export default BaseballLight
19 |
--------------------------------------------------------------------------------
/dist/assets/static/img/nhl/teams/bos.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------