diff --git a/frontend/src/apollo.ts b/frontend/src/apollo.ts index e690230..501237f 100644 --- a/frontend/src/apollo.ts +++ b/frontend/src/apollo.ts @@ -4,8 +4,7 @@ import { setContext } from '@apollo/client/link/context'; const httpLink = createHttpLink({ uri: `http://localhost:3535/graphql/query${localStorage.getItem('csrf') ? '?csrf=' + localStorage.getItem('csrf') : ''}` }); - - + const authLink = setContext((_, { headers }) => { // get the authentication token from local storage if it exists const token = localStorage.getItem('jwt'); @@ -19,11 +18,10 @@ const authLink = setContext((_, { headers }) => { } return headers; }); - const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache() }); -export default client; \ No newline at end of file +export default client; diff --git a/frontend/src/components/BoardSelector.tsx b/frontend/src/components/BoardSelector.tsx index b33f760..73f0c94 100644 --- a/frontend/src/components/BoardSelector.tsx +++ b/frontend/src/components/BoardSelector.tsx @@ -1,9 +1,11 @@ import React, { useState } from 'react'; import styled from 'styled-components'; -import { shadow } from '../styles'; +import { shadow, colors } from '../styles'; import { AiOutlinePlus } from 'react-icons/ai'; +import { RANK_TO_CHAR, SUIT_TO_CHAR, TEXT_SUIT_TO_CHAR, suitColor } from '../redux/range/HandRange'; + import Modal from './Modal'; const BoardSelectorStyle = styled.div` @@ -50,8 +52,98 @@ const BoardSelectorStyle = styled.div` height: 30px; } } + .cards { + display: grid; + grid-template-rows: repeat(4, auto); + grid-template-columns: repeat(13, auto); + grid-template-areas: + 'twos threes fours fives sixs sevens eights nines tens jacks queens kings aces' + 'twoh threeh fourh fiveh sixh sevenh eighth nineh tenh jackh queenh kingh aceh' + 'twoc threec fourc fivec sixc sevenc eightc ninec tenc jackc queenc kingc acec' + 'twod threed fourd fived sixd sevend eightd nined tend jackd queend kingd aced'; + grid-gap: 1em; + .card { + width: 40px; + height: 40px; + background: rgba(0, 0, 0, 0.05); + line-height: 40px; + text-align: center; + border-radius: 2px; + cursor: pointer; + margin: 0 auto; + &:hover { + box-shadow: ${shadow[1]}; + } + } + .card-selected { + background: ${colors.primary}; + color: #fff; + } + } + @media only screen and (max-width: 800px) { + .cards { + grid-template-rows: repeat(13, auto); + grid-template-columns: repeat(4, auto); + grid-gap: .5em; + grid-template-areas: + 'twos twoh twoc twod' + 'threes threeh threec threed' + 'fours fourh fourc fourd' + 'fives fiveh fivec fived' + 'sixs sixh sixc sixd' + 'sevens sevenh sevenc sevend' + 'eights eighth eightc eightd' + 'nines nineh ninec nined' + 'tens tenh tenc tend' + 'jacks jackh jackc jackd' + 'queens queenh queenc queend' + 'kings kingh kingc kingd' + 'aces aceh acec aced'; + } + } `; +function rankToString(rank: number): string { + switch(rank) { + case 0: return 'two'; + case 1: return 'three'; + case 2: return 'four'; + case 3: return 'five'; + case 4: return 'six'; + case 5: return 'seven'; + case 6: return 'eight'; + case 7: return 'nine'; + case 8: return 'ten'; + case 9: return 'jack'; + case 10: return 'queen'; + case 11: return 'king'; + case 12: return 'ace'; + default: return ''; + } +} + +function Cards(): React.ReactElement { + return ( +
+ {new Array(52).fill(0).map((_, index) => { + const rank = index % 13; + const suit = Math.floor(index / 13); + const gridArea = `${rankToString(rank)}${TEXT_SUIT_TO_CHAR[suit]}`; + return ( +
+ + {RANK_TO_CHAR[rank]} + + + {SUIT_TO_CHAR[suit]} + +
+ ); + })} +
+ ); +} + type BoardSelectorProps = { className?: string; }; @@ -66,9 +158,7 @@ function BoardSelector(props: BoardSelectorProps): React.ReactElement { shown={modalShown} closeModal={() => setModalShown(false)} > -
- OK -
+
BOARD
diff --git a/frontend/src/components/Button.tsx b/frontend/src/components/Button.tsx index 5aae109..3c250ab 100644 --- a/frontend/src/components/Button.tsx +++ b/frontend/src/components/Button.tsx @@ -25,6 +25,7 @@ const ButtonStyle = styled.button<{ size: string; block: boolean, variant: strin display: flex; flex-direction: row; align-items: center; + justify-content: center; box-shadow: ${shadow[0]}; transition: transform .1s ease; width: ${props => props.block ? '100%': 'fit-content'}; diff --git a/frontend/src/components/Modal.tsx b/frontend/src/components/Modal.tsx index 2f5cee4..23b2fe0 100644 --- a/frontend/src/components/Modal.tsx +++ b/frontend/src/components/Modal.tsx @@ -3,6 +3,8 @@ import styled from 'styled-components'; import Button from './Button'; +import { FiX } from 'react-icons/fi'; + type ModalProps = { shown: boolean; closeModal: () => void; @@ -13,8 +15,6 @@ type ModalProps = { const ModalStyle = styled.div` position: fixed; - z-index: 999; - background: rgba(0, 0, 0, 0.65); width: 100%; height: 100%; left: 0; @@ -22,6 +22,16 @@ const ModalStyle = styled.div` display: flex; justify-content: center; align-items: center; + z-index: 999; + .modal-bg { + background: rgba(0, 0, 0, 0.65); + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: -1; + } .modal { background: #fff; border-radius: 2px; @@ -77,6 +87,7 @@ function Modal(props: ModalProps): React.ReactElement { } return ( +
@@ -87,7 +98,7 @@ function Modal(props: ModalProps): React.ReactElement { {children}
- + {actionButtons}
diff --git a/frontend/src/components/Nav.tsx b/frontend/src/components/Nav.tsx index e03a151..f7a5491 100644 --- a/frontend/src/components/Nav.tsx +++ b/frontend/src/components/Nav.tsx @@ -1,9 +1,13 @@ import React from 'react'; import styled from 'styled-components'; +import { connect, ConnectedProps } from 'react-redux'; import { Link, useHistory } from 'react-router-dom'; +import { RootState } from '../redux'; import { colors, shadow } from '../styles'; +import { logout } from '../redux/auth/actions'; + const NavStyle = styled.nav` height: 70px; width: 100%; @@ -51,12 +55,29 @@ const NavStyle = styled.nav` } `; -function Nav(): React.ReactElement { +function mapStateToProps(state: RootState) { + return { + isLoggedIn: state.auth.isLoggedIn + }; +} + +function mapDispatchToProps() { + return { + logout + }; +} + +const connector = connect(mapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +type NavProps = PropsFromRedux; + +function Nav(props: NavProps): React.ReactElement { + const { isLoggedIn, logout } = props; let history = useHistory(); - const isAuthenticated = localStorage.getItem('jwt') && localStorage.getItem('csrf'); - function logout() { - localStorage.removeItem('jwt'); - localStorage.removeItem('csrf'); + function onLogout() { + logout(); history.push('/'); } return ( @@ -65,8 +86,8 @@ function Nav(): React.ReactElement { Holdem Tools
- { isAuthenticated ? ( - + { isLoggedIn ? ( + ) : ( <> Sign In @@ -78,4 +99,4 @@ function Nav(): React.ReactElement { ); } -export default Nav; \ No newline at end of file +export default connector(Nav); diff --git a/frontend/src/components/RangeFilter.tsx b/frontend/src/components/RangeFilter.tsx index 47088a0..4f18210 100644 --- a/frontend/src/components/RangeFilter.tsx +++ b/frontend/src/components/RangeFilter.tsx @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; -import { colors, shadow } from '../styles'; +import { colors, shadow, makeOpaque } from '../styles'; const MADE_HAND_CATEGORIES = [ { @@ -15,8 +15,11 @@ const MADE_HAND_CATEGORIES = [ class: 'Flush', percent: '0.2%', }, + { class: 'Straight', + percent: '0.2%' + }, { - class: '3 of a kind', + class: 'Set', percent: '0.2%', }, { @@ -24,7 +27,27 @@ const MADE_HAND_CATEGORIES = [ percent: '0.1%', }, { - class: 'One pair', + class: 'Overpair', + percent: '0.01%', + }, + { + class: 'Top pair', + percent: '0.01%', + }, + { + class: 'PP below top', + percent: '0.01%', + }, + { + class: 'Middle pair', + percent: '0.01%', + }, + { + class: 'Weak pair', + percent: '0.01%', + }, + { + class: 'Ace high', percent: '0.01%', }, ]; @@ -34,13 +57,32 @@ const DRAW_HAND_CATEGORIES = [ class: '2 card fd', percent: '0.02%', }, + { + class: 'Nut fd (1 card)', + percent: '0.02%', + }, { class: 'OESD', percent: '0.02%', }, + { + class: 'Gutshot', + percent: '0.02%', + }, + { + class: 'Overcards', + percent: '0.02%', + }, ]; const RangeFilterStyle = styled.div` + .range-filter-header { + background: rgba(0, 0, 0, 0.05); + padding: 0.5em 1em; + font-size: 12px; + font-weight: 500; + color: rgba(0, 0, 0, 0.45); + } .range-filter-container { background: white; box-shadow: ${shadow[0]}; @@ -50,10 +92,14 @@ const RangeFilterStyle = styled.div` width: 100%; overflow-y: auto; .range-filter-table-item { + font-size: 12px; &:hover { - background: rgba(0, 0, 0, 0.05); + background: ${makeOpaque(colors.primary, 0.05)}; } } + .range-filter-table-body { + overflow-y: auto; + } .range-filter-select { > div { width: 1em; @@ -79,6 +125,9 @@ function RangeFilter(props: RangeFilterProps): React.ReactElement { return (
+
+ FILTERS +
@@ -87,7 +136,7 @@ function RangeFilter(props: RangeFilterProps): React.ReactElement { - +
% OF RANGE
MADE HANDS diff --git a/frontend/src/components/RangeTable.tsx b/frontend/src/components/RangeTable.tsx index 025ae3a..df16f35 100644 --- a/frontend/src/components/RangeTable.tsx +++ b/frontend/src/components/RangeTable.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import styled from 'styled-components'; -import { colors, shadow } from '../styles'; +import { colors, shadow, makeOpaque } from '../styles'; import { FiX, FiPlusCircle } from 'react-icons/fi'; import Modal from './Modal'; @@ -8,6 +8,13 @@ import Button from './Button'; import CreateRangeForm from './CreateRangeForm'; const RangeTableStyle = styled.div<{ className: string }>` + .range-table-header { + background: rgba(0, 0, 0, 0.05); + padding: 0.5em 1em; + font-size: 12px; + font-weight: 500; + color: rgba(0, 0, 0, 0.45); + } .range-table-controls { display: flex; justify-content: flex-end; @@ -38,7 +45,20 @@ const RangeTableStyle = styled.div<{ className: string }>` .range-table-item { border-bottom: 1px solid #eee; &:hover { - background: rgba(0, 0, 0, 0.05); + background: ${makeOpaque(colors.primary, 0.05)}; + } + } + .range-table-item-active { + position: relative; + background: ${makeOpaque(colors.primary, 0.05)}; + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 100%; + width: 3px; + background: ${colors.primary}; } } } @@ -83,13 +103,16 @@ function RangeTable(props: RangeTableProps): React.ReactElement { closeModal={() => setModalShown(false)} actionButtons={[ , ]} >
+
+ RANGES +
@@ -107,7 +130,7 @@ function RangeTable(props: RangeTableProps): React.ReactElement { - + diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 2304dc8..6b98f9d 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -48,6 +48,12 @@ const LoginStyle = styled.div` margin-bottom: 1.5em; } } + @media only screen and (max-width: 500px) { + .login-container { + width: 100%; + border-radius: 0; + } + } `; diff --git a/frontend/src/pages/Register.tsx b/frontend/src/pages/Register.tsx index 1adcaa4..15bbab7 100644 --- a/frontend/src/pages/Register.tsx +++ b/frontend/src/pages/Register.tsx @@ -47,6 +47,12 @@ const RegisterStyle = styled.div` margin-bottom: 1.5em; } } + @media only screen and (max-width: 500px) { + .register-container { + width: 100%; + border-radius: 0; + } + } `; type FieldErrors = { diff --git a/frontend/src/redux/auth/reducers.ts b/frontend/src/redux/auth/reducers.ts index f471ebc..7b47298 100644 --- a/frontend/src/redux/auth/reducers.ts +++ b/frontend/src/redux/auth/reducers.ts @@ -35,4 +35,4 @@ function authReducer(state = defaultState, action: AuthActionTypes): AuthState { } } -export default authReducer; \ No newline at end of file +export default authReducer; diff --git a/screenshots/1.png b/screenshots/1.png index cafe362..58bdf41 100644 Binary files a/screenshots/1.png and b/screenshots/1.png differ
UTG+1 Raise first in 56.64%