├── a.txt ├── c.txt ├── .nvmrc ├── b.txt ├── postcss.config.js ├── jest ├── fileMock.js └── setup.js ├── src ├── components │ ├── League │ │ ├── League.scss │ │ └── League.js │ ├── Diamond │ │ ├── BaseballField.scss │ │ ├── BaseballField.js │ │ ├── props.js │ │ ├── BallStrikeOut.js │ │ ├── Diamond.scss │ │ ├── BallStrikeOut.scss │ │ └── Diamond.js │ ├── Test │ │ ├── Test.scss │ │ └── Test.js │ ├── About │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── About.test.js.snap │ │ │ └── About.test.js │ │ ├── About.js │ │ └── About.scss │ ├── Details │ │ ├── Details.scss │ │ └── Details.js │ ├── NotFound │ │ ├── NotFound.js │ │ └── NotFound.scss │ ├── UpdateTime │ │ ├── UpdateTime.scss │ │ └── UpdateTime.js │ ├── Logo │ │ ├── Logo.scss │ │ └── Logo.js │ ├── NoGames │ │ ├── NoGames.js │ │ └── NoGames.scss │ ├── Header │ │ ├── Footer.js │ │ ├── Trigger.js │ │ ├── Logo.js │ │ ├── Header.js │ │ ├── Extra.js │ │ ├── Item.js │ │ └── Menu.js │ ├── Footer │ │ ├── Footer.js │ │ └── Footer.scss │ ├── Event │ │ ├── Event.js │ │ └── Event.scss │ ├── PanelMenu │ │ ├── Panel.js │ │ ├── PanelMenu.scss │ │ ├── Panel.scss │ │ └── PanelMenu.js │ ├── GameState │ │ ├── GameState.scss │ │ └── GameState.js │ ├── Stats │ │ ├── Stats.scss │ │ ├── Stats.js │ │ └── props.js │ ├── Matchup │ │ ├── Matchup.scss │ │ ├── Matchup.js │ │ └── props.js │ ├── BoxScore │ │ ├── BoxScore.scss │ │ ├── BoxScore.js │ │ └── props.js │ ├── Expand │ │ ├── Expand.js │ │ └── Expand.scss │ ├── Scoreboard │ │ └── Scoreboard.scss │ ├── Social │ │ ├── Social.scss │ │ └── Social.js │ ├── Loading │ │ ├── Loading.js │ │ └── Loading.scss │ ├── Home │ │ ├── Home.js │ │ ├── Item.js │ │ └── Home.scss │ ├── Game │ │ └── Game.scss │ ├── Date │ │ ├── Date.scss │ │ └── Date.js │ ├── Team │ │ ├── Team.scss │ │ └── Team.js │ ├── index.js │ └── Global │ │ └── GlobalStyles.js ├── styles │ ├── _utils.scss │ ├── shared.css │ ├── _elements.scss │ └── _variables.scss ├── utils │ └── formatters.ts ├── config │ ├── metadata.js │ ├── analytics.js │ ├── velocity.js │ └── Routes.js ├── containers │ ├── Main │ │ ├── Goodbye.tsx │ │ ├── Main.scss │ │ └── Hello.tsx │ ├── About │ │ └── AboutContainer.js │ ├── NotFound │ │ └── NotFoundContainer.js │ ├── Logo │ │ └── LogoContainer.js │ ├── Test │ │ └── TestContainer.js │ ├── Home │ │ └── HomeContainer.js │ ├── Details │ │ └── DetailsContainer.js │ ├── index.js │ ├── Date │ │ └── DateContainer.js │ ├── Game │ │ └── GameContainer.js │ ├── Nfl │ │ └── NflContainer.js │ ├── Nhl │ │ └── NhlContainer.js │ ├── Nba │ │ └── NbaContainer.js │ └── Mlb │ │ └── MlbContainer.js ├── assets │ ├── icons │ │ ├── index.js │ │ ├── HockeyLight.js │ │ ├── SoccerLight.js │ │ ├── FootballLight.js │ │ ├── BasketballLight.js │ │ └── BaseballLight.js │ └── styles │ │ └── resets.css ├── data │ ├── stadiums.js │ └── app-pages.js ├── helpers │ ├── baseball.js │ ├── statsFns.js │ └── api.js ├── theme.js ├── index.html └── index.js ├── dist └── assets │ └── static │ ├── img │ ├── 404 │ │ └── mr_robot-404.jpg │ ├── mlb │ │ ├── teams │ │ │ ├── al.png │ │ │ ├── nl.png │ │ │ ├── oak.svg │ │ │ ├── ari.svg │ │ │ ├── tex.svg │ │ │ ├── sf.svg │ │ │ ├── sd.svg │ │ │ ├── nym.svg │ │ │ ├── was.svg │ │ │ ├── pit.svg │ │ │ ├── stl.svg │ │ │ └── chc.svg │ │ └── other │ │ │ ├── diamond_2b.svg │ │ │ ├── diamond_3b.svg │ │ │ ├── diamond.svg │ │ │ ├── diamond_1b.svg │ │ │ ├── diamond_1b_2b.svg │ │ │ ├── diamond_1b_2b_3b.svg │ │ │ ├── diamond_1b_3b.svg │ │ │ └── diamond_2b_3b.svg │ ├── nhl │ │ └── teams │ │ │ ├── tbl.svg │ │ │ ├── vgk.svg │ │ │ ├── phi.svg │ │ │ ├── ana.svg │ │ │ ├── mtl.svg │ │ │ ├── stl.svg │ │ │ ├── car.svg │ │ │ ├── dal.svg │ │ │ └── bos.svg │ └── nfl │ │ └── teams │ │ ├── dal.svg │ │ ├── gb.svg │ │ ├── hou.svg │ │ ├── nyg.svg │ │ ├── cin.svg │ │ ├── ne.svg │ │ ├── ari.svg │ │ ├── sea.svg │ │ └── atl.svg │ ├── other │ ├── favicon-dev.png │ └── favicon.ico │ ├── fonts │ ├── comfortaa-v28-latin-300.woff │ ├── comfortaa-v28-latin-500.woff │ ├── comfortaa-v28-latin-600.woff │ ├── comfortaa-v28-latin-700.woff │ ├── comfortaa-v28-latin-300.woff2 │ ├── comfortaa-v28-latin-500.woff2 │ ├── comfortaa-v28-latin-600.woff2 │ ├── comfortaa-v28-latin-700.woff2 │ ├── comfortaa-v28-latin-regular.woff │ └── comfortaa-v28-latin-regular.woff2 │ ├── icons │ ├── hockey.svg │ ├── hockey_light.svg │ ├── soccer.svg │ ├── soccer_light.svg │ ├── football.svg │ ├── football_light.svg │ ├── basketball.svg │ ├── basketball_light.svg │ ├── baseball.svg │ └── baseball_light.svg │ └── css │ └── fonts.css ├── .gitignore ├── commitlint.config.js ├── .prettierrc ├── tsconfig.js ├── jest.config.js ├── .versionrc ├── README.md ├── .babelrc ├── .eslintrc ├── LICENSE ├── .ssl ├── local.api.uxscoreboard.cert ├── local.uxscoreboard.cert ├── local.uxscoreboard.key └── local.api.uxscoreboard.key └── .circleci └── config.yml /a.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /c.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 11.0.0 2 | -------------------------------------------------------------------------------- /b.txt: -------------------------------------------------------------------------------- 1 | BBbbbbbbbbbbbbBBB 2 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | -------------------------------------------------------------------------------- /jest/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub' 2 | -------------------------------------------------------------------------------- /src/components/League/League.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- League ------- */ 3 | .container article { 4 | height:100%; 5 | } 6 | -------------------------------------------------------------------------------- /src/styles/_utils.scss: -------------------------------------------------------------------------------- 1 | 2 | .flexy { 3 | display:flex; 4 | align-items:center; 5 | justify-content:center; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Diamond/BaseballField.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- BaseballField ------- */ 3 | .baseballField { 4 | padding:4%; 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/formatters.ts: -------------------------------------------------------------------------------- 1 | export const capitalizeStr = (string: String): String => 2 | string[0].toUpperCase() + string.slice(1) 3 | -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/teams/al.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/img/mlb/teams/al.png -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/teams/nl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/img/mlb/teams/nl.png -------------------------------------------------------------------------------- /dist/assets/static/other/favicon-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/other/favicon-dev.png -------------------------------------------------------------------------------- /dist/assets/static/img/404/mr_robot-404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/img/404/mr_robot-404.jpg -------------------------------------------------------------------------------- /jest/setup.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme' 2 | import Adapter from 'enzyme-adapter-react-16' 3 | 4 | configure({ adapter: new Adapter() }) 5 | -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-300.woff -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-500.woff -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-600.woff -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-700.woff -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .DS_Store 3 | 4 | .vscode/ 5 | 6 | yarn-error.log 7 | node_modules/ 8 | 9 | dist/index.html 10 | dist/assets/build/ 11 | extra/ 12 | -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-300.woff2 -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-500.woff2 -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-600.woff2 -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-700.woff2 -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-regular.woff -------------------------------------------------------------------------------- /dist/assets/static/fonts/comfortaa-v28-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asapzacy/uxscoreboard/HEAD/dist/assets/static/fonts/comfortaa-v28-latin-regular.woff2 -------------------------------------------------------------------------------- /src/components/Test/Test.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Test ------- */ 3 | .container { 4 | composes:flexy from 'styles/_utils.scss' 5 | } 6 | .text { 7 | font-size:2.8em; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/About/__tests__/__snapshots__/About.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`About.js renders correctly 1`] = `ShallowWrapper {}`; 4 | -------------------------------------------------------------------------------- /src/styles/shared.css: -------------------------------------------------------------------------------- 1 | 2 | .standard { 3 | margin-top:24%; 4 | } 5 | @media only screen and (min-width:667px) { 6 | .standard { 7 | margin-top:12%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/config/metadata.js: -------------------------------------------------------------------------------- 1 | export const updatePageInfo = ({ title, desc }) => { 2 | document.title = title 3 | document.getElementsByTagName('meta')['description'].content = desc 4 | } 5 | -------------------------------------------------------------------------------- /src/components/Details/Details.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Details ------- */ 3 | .container { 4 | font-size:0.9em; 5 | padding:0.4em; 6 | flex-direction:column; 7 | text-align:center; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/NotFound/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './NotFound.scss' 3 | 4 | const NotFound = () =>
5 | 6 | export default NotFound 7 | -------------------------------------------------------------------------------- /src/config/analytics.js: -------------------------------------------------------------------------------- 1 | const ReactGA = require('react-ga') 2 | 3 | export const logPageView = () => { 4 | ReactGA.set({ page: window.location.pathname }) 5 | ReactGA.pageview(window.location.pathname) 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Test/Test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './Test.scss' 3 | 4 | const Test = () => ( 5 |
6 |

{'test'}

7 |
8 | ) 9 | 10 | export default Test 11 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'type-enum': [ 4 | 2, 5 | 'always', 6 | ['app', 'build', 'ci', 'dev', 'docs', 'feat', 'fix'] 7 | ] 8 | }, 9 | extends: ['@commitlint/config-conventional'] 10 | } 11 | -------------------------------------------------------------------------------- /src/components/UpdateTime/UpdateTime.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- UpdateTime ------- */ 3 | .container { 4 | text-align:right; 5 | font-size:0.66em; 6 | margin-top:2em; 7 | } 8 | .text strong { 9 | letter-spacing:0; 10 | margin-right:0.5em; 11 | } 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "arrowParens": "avoid" 11 | } 12 | -------------------------------------------------------------------------------- /src/components/About/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './About.scss' 3 | 4 | const About = () => ( 5 |
6 |

{'uxscoreboard'}

7 |
8 | ) 9 | 10 | export default About 11 | -------------------------------------------------------------------------------- /src/components/Logo/Logo.scss: -------------------------------------------------------------------------------- 1 | 2 | .container { 3 | height:75px; 4 | flex:1; 5 | opacity:0; 6 | overflow:hidden; 7 | white-space:nowrap; 8 | color:transparent; 9 | transition:opacity 0.77s 0.77s; 10 | } 11 | .container[src$='.png'] { 12 | width:75px; 13 | } -------------------------------------------------------------------------------- /src/components/NoGames/NoGames.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './NoGames.scss' 3 | 4 | const NoGames = ({ text }) => ( 5 |
  • 6 | {text} 7 |
  • 8 | ) 9 | 10 | export default NoGames 11 | -------------------------------------------------------------------------------- /tsconfig.js: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "es6", 7 | "target": "es6", 8 | "jsx": "react" 9 | }, 10 | "include": [ 11 | "./src/**/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Header/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Social } from 'components' 3 | import s from './Header.scss' 4 | 5 | const Footer = () => ( 6 | 9 | ) 10 | 11 | export default Footer 12 | -------------------------------------------------------------------------------- /src/containers/Main/Goodbye.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export interface GoodbyeProps { 4 | name: string 5 | } 6 | 7 | const Goodbye = (props: GoodbyeProps) => 8 | console.log(props) ||

    Goodbye {props.name}!

    9 | 10 | export default Goodbye 11 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Social } from 'components' 3 | import s from './Footer.scss' 4 | 5 | const Footer = () => ( 6 | 9 | ) 10 | 11 | export default Footer 12 | -------------------------------------------------------------------------------- /src/components/NotFound/NotFound.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- NotFound ------- */ 3 | .container { 4 | width: 100%; 5 | position: absolute; 6 | top: 0; right: 0; bottom: 0; left: 0; 7 | background: $real_black url('/assets/static/img/404/mr_robot-404.jpg') center center / contain no-repeat; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/About/__tests__/About.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { shallow } from 'enzyme' 3 | 4 | import About from '../About' 5 | 6 | describe('About.js', () => { 7 | it('renders correctly', () => { 8 | expect(shallow()).toMatchSnapshot() 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /src/components/Event/Event.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './Event.scss' 3 | 4 | const Event = ({ bgImg }) => ( 5 |
  • 9 | ) 10 | 11 | export default Event 12 | -------------------------------------------------------------------------------- /src/components/PanelMenu/Panel.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './Panel.scss' 3 | 4 | const Panel = ({ panel, isActive, fn }) => ( 5 |
  • fn(panel)}> 6 | {panel} 7 |
  • 8 | ) 9 | 10 | export default Panel 11 | -------------------------------------------------------------------------------- /src/assets/icons/index.js: -------------------------------------------------------------------------------- 1 | export { default as BasketballLight } from './BasketballLight' 2 | export { default as BaseballLight } from './BaseballLight' 3 | export { default as FootballLight } from './FootballLight' 4 | export { default as HockeyLight } from './HockeyLight' 5 | export { default as SoccerLight } from './SoccerLight' 6 | -------------------------------------------------------------------------------- /src/components/About/About.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- About ------- */ 3 | .container { 4 | width:100%; 5 | padding:0 2%; 6 | display:flex; 7 | flex-direction:column; 8 | composes:standard from 'styles/shared.css'; 9 | } 10 | .heading { 11 | font-size:2em; 12 | margin-bottom:0.5em; 13 | text-align:center; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/PanelMenu/PanelMenu.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- PanelMenu ------- */ 3 | .menu { 4 | height:1.95em; 5 | width:75%; 6 | overflow:hidden; 7 | border-radius:2px; 8 | border:1px solid $grey4; 9 | margin:0.75em auto 1.5em; 10 | } 11 | .list { 12 | display:flex; 13 | font-size:0.7em; 14 | height:100%; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/GameState/GameState.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- GameState ------- */ 3 | .container { 4 | display:flex; 5 | justify-content:space-between; 6 | font-size:0.8em; 7 | padding:7px 14px; 8 | letter-spacing:0.5px; 9 | line-height:1; 10 | } 11 | .inGame { 12 | flex-direction:row-reverse; 13 | composes:container; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Header/Trigger.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './Header.scss' 3 | 4 | const Trigger = ({ triggerMenu }) => ( 5 | 6 | 7 | 8 | 9 | ) 10 | 11 | export default Trigger 12 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Footer ------- */ 3 | .container { 4 | width:100%; 5 | flex:0 0 2.5em; 6 | font-size:2.4em; 7 | color:$black;; 8 | display:flex; 9 | align-items:center; 10 | justify-content:center; 11 | } 12 | @media only screen and (min-width:$large) { 13 | .container { 14 | font-size:3em; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Logo/Logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './Logo.scss' 3 | 4 | const Logo = ({ src, name, hasLoaded, fn }) => ( 5 | {`${name} 12 | ) 13 | 14 | export default Logo 15 | -------------------------------------------------------------------------------- /src/components/Stats/Stats.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Stats ------- */ 3 | .container { 4 | composes:tableContainer from 'styles/_elements.scss'; 5 | } 6 | .table { 7 | composes:table from 'styles/_elements.scss'; 8 | } 9 | .head { 10 | composes:tableHead from 'styles/_elements.scss' 11 | } 12 | .body { 13 | composes:tableBody from 'styles/_elements.scss' 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Matchup/Matchup.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Matchup ------- */ 3 | .container { 4 | font-size:0.9em; 5 | height:6em; 6 | display:flex; 7 | flex-direction:column; 8 | justify-content:center; 9 | } 10 | .teams { 11 | font-weight:700; 12 | font-size:105%; 13 | } 14 | .date { 15 | font-size:100%; 16 | } 17 | .location { 18 | font-size:95%; 19 | } 20 | -------------------------------------------------------------------------------- /src/components/BoxScore/BoxScore.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- BoxScore ------- */ 3 | .container { 4 | composes:tableContainer from 'styles/_elements.scss'; 5 | } 6 | .table { 7 | composes:table from 'styles/_elements.scss'; 8 | } 9 | .head { 10 | composes:tableHead from 'styles/_elements.scss'; 11 | } 12 | .body { 13 | composes:tableBody from 'styles/_elements.scss' 14 | } 15 | -------------------------------------------------------------------------------- /src/components/UpdateTime/UpdateTime.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import s from './UpdateTime.scss' 3 | 4 | const UpdateTime = ({ lastUpdatedStr }) => ( 5 |
    6 | 7 | {'last updated:'} 8 | {lastUpdatedStr} 9 | 10 |
    11 | ) 12 | 13 | export default UpdateTime 14 | -------------------------------------------------------------------------------- /src/components/Diamond/BaseballField.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createDiamond } from 'helpers/baseball' 3 | import s from './BaseballField.scss' 4 | 5 | const BaseballField = ({ offense }) => ( 6 | 10 | ) 11 | 12 | export default BaseballField 13 | -------------------------------------------------------------------------------- /src/containers/Main/Main.scss: -------------------------------------------------------------------------------- 1 | /* ------- MainContainer ------- */ 2 | .outerContainer { 3 | display: flex; 4 | flex-direction: column; 5 | transition: 0.44s height; 6 | } 7 | .innerContainer { 8 | width: 96%; 9 | display: flex; 10 | flex: 1 0 auto; 11 | min-height: 60%; 12 | max-width: 1600px; 13 | } 14 | .innerContainer > div:first-child { 15 | width: 100%; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Expand/Expand.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Arrow from 'react-icons/lib/io/ios-arrow-down' 3 | import s from './Expand.scss' 4 | 5 | const Expand = ({ isExpanded }) => ( 6 |
    7 | 8 | 9 | 10 |
    11 | ) 12 | 13 | export default Expand 14 | -------------------------------------------------------------------------------- /src/components/Header/Logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | import s from './Header.scss' 4 | 5 | const Logo = () => ( 6 | 7 | {'uxscoreboard'} 12 | 13 | ) 14 | 15 | export default Logo 16 | -------------------------------------------------------------------------------- /src/containers/Main/Hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import Goodbye from './Goodbye' 3 | 4 | export interface HelloProps { 5 | compiler: string 6 | framework: string 7 | } 8 | 9 | const Hello = (props: HelloProps) => 10 | console.log(props) || ( 11 |

    12 | 13 | Hello from {props.compiler} and {props.framework}! 14 |

    15 | ) 16 | 17 | export default Hello 18 | -------------------------------------------------------------------------------- /src/components/Diamond/props.js: -------------------------------------------------------------------------------- 1 | export const mlbDiamondProps = game => { 2 | const inGame = game.status.abstractGameCode === 'L' 3 | return { 4 | balls: inGame && Number(game.linescore.balls), 5 | strikes: inGame && Number(game.linescore.strikes), 6 | outs: inGame && Number(game.linescore.outs), 7 | inningState: inGame && game.linescore.inningState, 8 | offense: inGame && game.linescore.offense 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Expand/Expand.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Expand ------- */ 3 | .container { 4 | width:100%; 5 | height:0.9em; 6 | display:flex; 7 | align-items:center; 8 | justify-content:center; 9 | } 10 | .icon { 11 | backface-visibility:hidden; 12 | transform:rotate(0deg) translateZ(0); 13 | transition:transform 0.33s; 14 | } 15 | .iconExpanded { 16 | transform:rotate(180deg) translateZ(0); 17 | composes:icon; 18 | } 19 | -------------------------------------------------------------------------------- /src/components/Diamond/BallStrikeOut.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createBsoCount } from 'helpers/baseball' 3 | import s from './BallStrikeOut.scss' 4 | 5 | const BallStrikeOut = ({ label, filled, max, state }) => ( 6 |
    7 |

    {`${label}:`}

    8 | 9 |
    10 | ) 11 | 12 | export default BallStrikeOut 13 | -------------------------------------------------------------------------------- /src/components/PanelMenu/Panel.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Panel ------- */ 3 | .item { 4 | flex:1; 5 | height:100%; 6 | cursor:pointer; 7 | transition:background 0.44s; 8 | composes:flexy from 'styles/_utils.scss'; 9 | } 10 | .item:not(:last-child) { 11 | border-right:1px solid $grey4; 12 | } 13 | .itemActive { 14 | background:$white; 15 | composes:item; 16 | } 17 | 18 | @media (hover) { 19 | .item:hover { 20 | background:$white; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/containers/About/AboutContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { About } from 'components' 3 | import { updatePageInfo } from 'config/metadata' 4 | 5 | class AboutContainer extends Component { 6 | componentDidMount() { 7 | const pageInfo = { 8 | title: 'about · uxscoreboard' 9 | } 10 | updatePageInfo(pageInfo) 11 | } 12 | render() { 13 | return 14 | } 15 | } 16 | 17 | export default AboutContainer 18 | -------------------------------------------------------------------------------- /src/components/Scoreboard/Scoreboard.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Scoreboard ------- */ 3 | .container { 4 | height:100%; 5 | display:flex; 6 | flex-direction:column; 7 | padding-bottom:2em; 8 | } 9 | .list { 10 | display:flex; 11 | flex-wrap:wrap; 12 | justify-content:center; 13 | align-items:flex-start; 14 | align-content:flex-start; 15 | } 16 | .fade { 17 | width:100%; 18 | display:flex; 19 | position:relative; 20 | } 21 | .fade, .list { 22 | flex:1; 23 | } 24 | -------------------------------------------------------------------------------- /src/containers/NotFound/NotFoundContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { NotFound } from 'components' 3 | import { updatePageInfo } from 'config/metadata' 4 | 5 | class NotFoundContainer extends Component { 6 | componentDidMount() { 7 | const pageInfo = { 8 | title: `404 · uxscoreboard` 9 | } 10 | updatePageInfo(pageInfo) 11 | } 12 | render() { 13 | return 14 | } 15 | } 16 | 17 | export default NotFoundContainer 18 | -------------------------------------------------------------------------------- /src/containers/Logo/LogoContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Logo } from 'components' 3 | 4 | class LogoContainer extends Component { 5 | constructor() { 6 | super() 7 | this.state = {} 8 | this.onLoad = this.onLoad.bind(this) 9 | } 10 | onLoad() { 11 | this.props.logoHasLoaded() 12 | } 13 | render() { 14 | return 15 | } 16 | } 17 | 18 | export default LogoContainer 19 | -------------------------------------------------------------------------------- /src/containers/Test/TestContainer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import React, { Component } from 'react' 3 | import { Test } from 'components' 4 | 5 | class TestContainer extends Component { 6 | render() { 7 | const socket = new WebSocket('wss://clusterfuck.run:8443/chatsocket') 8 | console.log(socket) 9 | socket.addEventListener('message', event => { 10 | console.log('incoming: ', event.data) 11 | }) 12 | return 13 | } 14 | } 15 | 16 | export default TestContainer 17 | -------------------------------------------------------------------------------- /src/components/Matchup/Matchup.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { formatDateStr } from 'helpers/utils' 3 | import s from './Matchup.scss' 4 | 5 | const Matchup = ({ awayTeam, homeTeam, date, location, venue }) => ( 6 |
    7 |

    {`${awayTeam} v. ${homeTeam}`}

    8 |

    {formatDateStr(date)}

    9 |

    {`${venue} • ${location}`}

    10 |
    11 | ) 12 | 13 | export default Matchup 14 | -------------------------------------------------------------------------------- /src/components/Diamond/Diamond.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Diamond ------- */ 3 | .container { 4 | margin-top:1.5em; 5 | display:flex; 6 | justify-content:space-around; 7 | } 8 | .leftSide { 9 | width:48%; 10 | display:flex; 11 | align-items:center; 12 | flex-direction:column; 13 | } 14 | .rightSide { 15 | width:33%; 16 | display:flex; 17 | align-items:flex-end; 18 | } 19 | .bsoContainer { 20 | margin-bottom:25%; 21 | text-align:left; 22 | } 23 | .runnersContainer { } 24 | .matchupContainer { } 25 | -------------------------------------------------------------------------------- /src/assets/styles/resets.css: -------------------------------------------------------------------------------- 1 | * { margin: 0; padding: 0 } 2 | html, body { height: 100%; box-sizing: border-box } 3 | *, *::before, *::after { box-sizing: inherit } 4 | img { max-width: 100% } 5 | a { color: inherit; text-decoration: none } 6 | ul, menu { list-style-type: none } 7 | h1,h2,h3,h4,h5,h6 { font-weight: inherit } 8 | strong { font-weight: 700 } 9 | figure { text-align: center } 10 | figcaption { font-size: 0.9em } 11 | table, hr { border: 0 } 12 | nav, menu { letter-spacing: 0 } 13 | sup { font-size: 66% } 14 | -------------------------------------------------------------------------------- /src/components/Event/Event.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Event ------- */ 3 | .event { 4 | width:90%; 5 | height:8em; 6 | margin-top:6vh; 7 | } 8 | 9 | @media only screen and (min-width:$medium) { 10 | .event { 11 | height:9em; 12 | margin-top:9vh; 13 | } 14 | } 15 | 16 | @media only screen and (min-width:$large) { 17 | .event { 18 | height:10em; 19 | margin-top:12vh; 20 | } 21 | } 22 | @media only screen and (min-width:$xlarge) { 23 | .event { 24 | height:11em; 25 | margin-top:15vh; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Social/Social.scss: -------------------------------------------------------------------------------- 1 | 2 | /* ------- Social ------- */ 3 | .container { 4 | height:100%; 5 | display:flex; 6 | align-items:center; 7 | justify-content:center; 8 | } 9 | .link { 10 | line-height:1; 11 | margin:0 0.66em; 12 | padding:1%; 13 | position:relative; 14 | } 15 | .iconOutline { 16 | display:inline-block; 17 | } 18 | .iconFull { 19 | opacity:0; 20 | position:absolute; 21 | transition:opacity 0.44s; 22 | composes:iconOutline; 23 | } 24 | .link:hover .iconFull { 25 | opacity:1; 26 | } 27 | -------------------------------------------------------------------------------- /src/components/PanelMenu/PanelMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Panel from './Panel' 3 | import s from './PanelMenu.scss' 4 | 5 | const PanelMenu = ({ panels, activePanel, switchPanel }) => ( 6 | 7 |
      8 | {panels.map((el, i) => ( 9 | 15 | ))} 16 |
    17 |
    18 | ) 19 | 20 | export default PanelMenu 21 | -------------------------------------------------------------------------------- /src/containers/Home/HomeContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Home } from 'components' 3 | 4 | class HomeContainer extends Component { 5 | componentDidMount() { 6 | document.title = 'uxscoreboard' 7 | document.getElementsByTagName('meta')['description'].content = 8 | 'uxscoreboard · A sports scoreboard web app built on ES6, React, and Node.js—features MLB, NBA, NFL, NHL, and MLS (coming soon) games.' 9 | } 10 | render() { 11 | return 12 | } 13 | } 14 | 15 | export default HomeContainer 16 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | globals: {}, 4 | setupFiles: ['/jest/setup.js'], 5 | moduleFileExtensions: ['js'], 6 | moduleDirectories: ['/src', 'node_modules'], 7 | moduleNameMapper: { 8 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 9 | '/jest/fileMock.js', 10 | '\\.(scss|css)$': 'identity-obj-proxy', 11 | '^components$': '/src/components/index.js', 12 | '^containers$': '/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 |
    9 |
    10 | 11 | 12 |
    13 | 14 |
    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 | 8 | 17 | 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 |
    7 | 8 | 12 | 16 |
    17 |
    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) · [![GitHub License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/asapzacy/uxscoreboard/blob/master/LICENSE) [![CircleCI Status](https://img.shields.io/circleci/project/github/asapzacy/uxscoreboard.svg?style=flat-square&label=circleci)](https://circleci.com/gh/asapzacy/uxscoreboard) 2 | 3 |

    A real-time sports scoreboard web app built on ES6, React, and node.js.

    4 | 5 | ![uxscoreboard Preview Screenshot](https://zac.codes/assets/img/projects/uxscoreboard_preview-xlarge.jpg) 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 |
    13 | 14 | 20 | 21 |
    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 | tampa-bay-lightning -------------------------------------------------------------------------------- /.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 | oakland-athletics-2 -------------------------------------------------------------------------------- /dist/assets/static/img/nfl/teams/dal.svg: -------------------------------------------------------------------------------- 1 | dallas-cowboys -------------------------------------------------------------------------------- /dist/assets/static/img/nfl/teams/gb.svg: -------------------------------------------------------------------------------- 1 | green-bay-packers -------------------------------------------------------------------------------- /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 | houston-texans -------------------------------------------------------------------------------- /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 |
    10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
    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 | 11 | 15 | 16 | ) 17 | export default HockeyLight 18 | -------------------------------------------------------------------------------- /dist/assets/static/img/nfl/teams/nyg.svg: -------------------------------------------------------------------------------- 1 | new-york-giants -------------------------------------------------------------------------------- /.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 | arizona-diamondbacks -------------------------------------------------------------------------------- /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 | texas-rangers -------------------------------------------------------------------------------- /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 | san-francisco-giants-2 -------------------------------------------------------------------------------- /dist/assets/static/icons/football.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/assets/static/icons/football_light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/teams/sd.svg: -------------------------------------------------------------------------------- 1 | san-diego-padres-27f411c -------------------------------------------------------------------------------- /src/assets/icons/SoccerLight.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const SoccerLight = props => ( 4 | 11 | 15 | 16 | ) 17 | export default SoccerLight 18 | -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/teams/nym.svg: -------------------------------------------------------------------------------- 1 | new-york-mets-2 -------------------------------------------------------------------------------- /dist/assets/static/img/nfl/teams/cin.svg: -------------------------------------------------------------------------------- 1 | cincinnati-bengals -------------------------------------------------------------------------------- /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 | washington-nationals-2 -------------------------------------------------------------------------------- /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 | 11 | 15 | 19 | 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 | new-england-patriots -------------------------------------------------------------------------------- /dist/assets/static/img/nfl/teams/ari.svg: -------------------------------------------------------------------------------- 1 | arizona-cardinals -------------------------------------------------------------------------------- /dist/assets/static/img/nhl/teams/phi.svg: -------------------------------------------------------------------------------- 1 | philadelphia-flyers -------------------------------------------------------------------------------- /src/assets/icons/BasketballLight.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const BasketballLight = props => ( 4 | 11 | 15 | 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 | pittsburgh-pirates-2 -------------------------------------------------------------------------------- /dist/assets/static/img/nhl/teams/ana.svg: -------------------------------------------------------------------------------- 1 | anaheim-ducks -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/teams/stl.svg: -------------------------------------------------------------------------------- 1 | st-louis-cardinals-2 -------------------------------------------------------------------------------- /dist/assets/static/img/nhl/teams/mtl.svg: -------------------------------------------------------------------------------- 1 | montreal-canadiens -------------------------------------------------------------------------------- /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 | st-louis-blues -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond_2b.svg: -------------------------------------------------------------------------------- 1 | diamond_2b -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond_3b.svg: -------------------------------------------------------------------------------- 1 | diamond_3b -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond.svg: -------------------------------------------------------------------------------- 1 | baseball_diamond -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond_1b.svg: -------------------------------------------------------------------------------- 1 | baseball_diamond -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond_1b_2b.svg: -------------------------------------------------------------------------------- 1 | baseball_diamond -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond_1b_2b_3b.svg: -------------------------------------------------------------------------------- 1 | diamond_1b_2b -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond_1b_3b.svg: -------------------------------------------------------------------------------- 1 | diamond_1b_3b -------------------------------------------------------------------------------- /dist/assets/static/img/mlb/other/diamond_2b_3b.svg: -------------------------------------------------------------------------------- 1 | diamond_2b_3b -------------------------------------------------------------------------------- /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 | carolina-hurricanes -------------------------------------------------------------------------------- /dist/assets/static/img/nhl/teams/dal.svg: -------------------------------------------------------------------------------- 1 | dallas-stars -------------------------------------------------------------------------------- /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 | chicago-cubs -------------------------------------------------------------------------------- /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 |
    100 | 101 | 102 | 103 |
    104 | ) 105 | -------------------------------------------------------------------------------- /dist/assets/static/img/nfl/teams/sea.svg: -------------------------------------------------------------------------------- 1 | seattle-seahawks -------------------------------------------------------------------------------- /dist/assets/static/img/nfl/teams/atl.svg: -------------------------------------------------------------------------------- 1 | atlanta-falcons -------------------------------------------------------------------------------- /src/assets/icons/BaseballLight.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const BaseballLight = props => ( 4 | 11 | 15 | 16 | ) 17 | 18 | export default BaseballLight 19 | -------------------------------------------------------------------------------- /dist/assets/static/img/nhl/teams/bos.svg: -------------------------------------------------------------------------------- 1 | boston-bruins --------------------------------------------------------------------------------