fix(#2525): add highlights page with form and matches

keep-around/f09ad060abadb54ab731d2923f633e5162e907d2
Andrei Dekterev 4 years ago
parent 72ef0c71c1
commit e8d7452d2e
  1. 38
      package-lock.json
  2. 1
      package.json
  3. BIN
      public/sounds/background_track.mp3
  4. 1
      src/config/lexics/highlightsPageLexic.tsx
  5. 2
      src/config/procedures.tsx
  6. 27
      src/features/AddCardForm/components/Form/hooks/index.tsx
  7. 6
      src/features/App/AuthenticatedApp.tsx
  8. 2
      src/features/BuyMatchPopup/index.tsx
  9. 12
      src/features/CardsStore/hooks/index.tsx
  10. 18
      src/features/Combobox/index.tsx
  11. 9
      src/features/Combobox/styled.tsx
  12. 3
      src/features/Combobox/types.tsx
  13. 1
      src/features/Common/Input/index.tsx
  14. 2
      src/features/Common/Input/styled.tsx
  15. 1
      src/features/FormStore/hooks/useFormState.tsx
  16. 10
      src/features/ProfileCard/index.tsx
  17. 8
      src/features/UserAccount/components/ChangeCardPopup/index.tsx
  18. 2
      src/features/UserAccount/styled.tsx
  19. 10
      src/features/UserFavorites/hooks/index.tsx
  20. 1
      src/libs/index.ts
  21. 17
      src/libs/objects/Sound.tsx
  22. 289
      src/pages/HighlightsPage/components/FormHighlights/hooks.tsx
  23. 95
      src/pages/HighlightsPage/components/FormHighlights/index.tsx
  24. 21
      src/pages/HighlightsPage/components/FormHighlights/styled.tsx
  25. 21
      src/pages/HighlightsPage/components/MatchesHighlights/hooks.tsx
  26. 80
      src/pages/HighlightsPage/components/MatchesHighlights/index.tsx
  27. 108
      src/pages/HighlightsPage/components/MatchesHighlights/styled.tsx
  28. 11
      src/pages/HighlightsPage/components/PriceInfo/index.tsx
  29. 1
      src/pages/HighlightsPage/components/PriceInfo/styled.tsx
  30. 48
      src/pages/HighlightsPage/index.tsx
  31. 30
      src/pages/HighlightsPage/storeHighlightsAtoms.tsx
  32. 20
      src/pages/HighlightsPage/styled.tsx
  33. 52
      src/requests/getSportTeams.tsx
  34. 41
      src/requests/getTeamPlayers.tsx
  35. 30
      src/requests/onePayment.tsx

38
package-lock.json generated

@ -28,6 +28,7 @@
"react-scripts": "^4.0.3", "react-scripts": "^4.0.3",
"react-window": "^1.8.6", "react-window": "^1.8.6",
"react-youtube": "^7.14.0", "react-youtube": "^7.14.0",
"recoil": "^0.7.4",
"screenfull": "^5.0.2", "screenfull": "^5.0.2",
"styled-components": "^5.3.3", "styled-components": "^5.3.3",
"workbox-core": "^5.1.4", "workbox-core": "^5.1.4",
@ -17283,6 +17284,11 @@
"uuid": "bin/uuid" "uuid": "bin/uuid"
} }
}, },
"node_modules/hamt_plus": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
},
"node_modules/handle-thing": { "node_modules/handle-thing": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@ -26950,6 +26956,25 @@
"node": ">=8.10.0" "node": ">=8.10.0"
} }
}, },
"node_modules/recoil": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.4.tgz",
"integrity": "sha512-sCXvQGMfSprkNU4ZRkJV4B0qFQSURJMgsICqY1952WRlg66NMwYqi6n67vhnhn0qw4zHU1gHXJuMvRDaiRNFZw==",
"dependencies": {
"hamt_plus": "1.0.2"
},
"peerDependencies": {
"react": ">=16.13.1"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/recompose": { "node_modules/recompose": {
"version": "0.27.1", "version": "0.27.1",
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.27.1.tgz", "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.27.1.tgz",
@ -46540,6 +46565,11 @@
} }
} }
}, },
"hamt_plus": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
},
"handle-thing": { "handle-thing": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@ -53976,6 +54006,14 @@
"picomatch": "^2.2.1" "picomatch": "^2.2.1"
} }
}, },
"recoil": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.4.tgz",
"integrity": "sha512-sCXvQGMfSprkNU4ZRkJV4B0qFQSURJMgsICqY1952WRlg66NMwYqi6n67vhnhn0qw4zHU1gHXJuMvRDaiRNFZw==",
"requires": {
"hamt_plus": "1.0.2"
}
},
"recompose": { "recompose": {
"version": "0.27.1", "version": "0.27.1",
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.27.1.tgz", "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.27.1.tgz",

@ -38,6 +38,7 @@
"react-scripts": "^4.0.3", "react-scripts": "^4.0.3",
"react-window": "^1.8.6", "react-window": "^1.8.6",
"react-youtube": "^7.14.0", "react-youtube": "^7.14.0",
"recoil": "^0.7.4",
"screenfull": "^5.0.2", "screenfull": "^5.0.2",
"styled-components": "^5.3.3", "styled-components": "^5.3.3",
"workbox-core": "^5.1.4", "workbox-core": "^5.1.4",

@ -6,6 +6,7 @@ export const highlightsPageLexic = {
highlight_will_be_generated: 18364, highlight_will_be_generated: 18364,
matches_highlight: 419, matches_highlight: 419,
maximal_duration: 18358, maximal_duration: 18358,
order_and_buy: 18361,
player_highlight: 630, player_highlight: 630,
price: 18356, price: 18356,
purchase_auto_generated: 18363, purchase_auto_generated: 18363,

@ -9,8 +9,10 @@ export const PROCEDURES = {
get_player_info: 'get_player_info', get_player_info: 'get_player_info',
get_player_matches: 'get_player_matches', get_player_matches: 'get_player_matches',
get_sport_list: 'get_sport_list', get_sport_list: 'get_sport_list',
get_sport_teams: 'get_sport_teams',
get_team_info: 'get_team_info', get_team_info: 'get_team_info',
get_team_matches: 'get_team_matches', get_team_matches: 'get_team_matches',
get_team_players: 'get_team_players',
get_teams: 'get_teams', get_teams: 'get_teams',
get_tournament_info: 'get_tournament_info', get_tournament_info: 'get_tournament_info',
get_tournament_list: 'get_tournament_list', get_tournament_list: 'get_tournament_list',

@ -16,6 +16,8 @@ import {
useElements, useElements,
} from '@stripe/react-stripe-js' } from '@stripe/react-stripe-js'
import { useRecoilValue } from 'recoil'
import toUpper from 'lodash/toUpper' import toUpper from 'lodash/toUpper'
import { useObjectState } from 'hooks' import { useObjectState } from 'hooks'
@ -23,6 +25,10 @@ import { useObjectState } from 'hooks'
import { useCardsStore } from 'features/CardsStore' import { useCardsStore } from 'features/CardsStore'
import { useLexicsStore } from 'features/LexicsStore' import { useLexicsStore } from 'features/LexicsStore'
import { dataForPayHighlights } from 'pages/HighlightsPage/storeHighlightsAtoms'
import { onePayment } from 'requests/onePayment'
import { SelectedCountry, useCountries } from './useCountries' import { SelectedCountry, useCountries } from './useCountries'
import { import {
isValidAddress, isValidAddress,
@ -67,13 +73,21 @@ export const useFormSubmit = ({ onAddSuccess }: Props) => {
const stripe = useStripe() const stripe = useStripe()
const elements = useElements() const elements = useElements()
const { translate } = useLexicsStore() const { translate } = useLexicsStore()
const { onAddCard, setError: setCardError } = useCardsStore() const {
onAddCard,
defaultCard,
isHighlightsPage,
setError: setCardError,
} = useCardsStore()
const [name, setName] = useState('') const [name, setName] = useState('')
const [city, setCity] = useState('') const [city, setCity] = useState('')
const [address, setAddress] = useState('') const [address, setAddress] = useState('')
const [inputStates, setInputStates] = useObjectState(initialState) const [inputStates, setInputStates] = useObjectState(initialState)
const [errorMessage, setErrorMessage] = useState('') const [errorMessage, setErrorMessage] = useState('')
const [loader, setLoader] = useState(false) const [loader, setLoader] = useState(false)
const dataFormHighlights = useRecoilValue(dataForPayHighlights)
console.log('inputStates', inputStates)
const { const {
countries, countries,
@ -163,6 +177,17 @@ export const useFormSubmit = ({ onAddSuccess }: Props) => {
return return
} }
const allInputEmpty = Object.values(inputStates).every((inputValue) => inputValue.empty)
if (isHighlightsPage && defaultCard && allInputEmpty) {
setErrorMessage('')
onePayment({
cardId: defaultCard?.id,
item: { ...dataFormHighlights },
})
return
}
const fieldError = validateFields({ const fieldError = validateFields({
address, address,
city, city,

@ -6,6 +6,8 @@ import {
Switch, Switch,
} from 'react-router-dom' } from 'react-router-dom'
import { RecoilRoot } from 'recoil'
import { indexLexics } from 'config/lexics/indexLexics' import { indexLexics } from 'config/lexics/indexLexics'
import { isProduction } from 'config/env' import { isProduction } from 'config/env'
import { PAGES } from 'config/pages' import { PAGES } from 'config/pages'
@ -75,7 +77,9 @@ export const AuthenticatedApp = () => {
<MatchPage /> <MatchPage />
</Route> </Route>
<Route path={`${PAGES.highlights}`}> <Route path={`${PAGES.highlights}`}>
<HighlightsPage /> <RecoilRoot>
<HighlightsPage />
</RecoilRoot>
</Route> </Route>
<Redirect to={PAGES.home} /> <Redirect to={PAGES.home} />
</Switch> </Switch>

@ -32,7 +32,7 @@ export const BuyMatchPopup = () => {
return ( return (
<Modal <Modal
isOpen isOpen={Boolean(1)}
close={close} close={close}
withCloseButton={false} withCloseButton={false}
> >

@ -2,6 +2,7 @@ import {
useCallback, useCallback,
useState, useState,
useMemo, useMemo,
useEffect,
} from 'react' } from 'react'
import find from 'lodash/find' import find from 'lodash/find'
@ -13,6 +14,7 @@ import { deleteCard } from 'requests/deleteCard'
import { setDefaultCard as setDefaultCardRequest } from 'requests/setDefaultCard' import { setDefaultCard as setDefaultCardRequest } from 'requests/setDefaultCard'
import { useRequest, useToggle } from 'hooks' import { useRequest, useToggle } from 'hooks'
import { PAGES } from 'config'
export const useBankCards = () => { export const useBankCards = () => {
const { const {
@ -20,6 +22,7 @@ export const useBankCards = () => {
toggle: toggleInfoModal, toggle: toggleInfoModal,
} = useToggle() } = useToggle()
const [isHighlightsPage, setIsHighlightsPage] = useState(false)
const [error, setError] = useState('') const [error, setError] = useState('')
const [cards, setCards] = useState<Cards | null>(null) const [cards, setCards] = useState<Cards | null>(null)
const defaultCard = useMemo( const defaultCard = useMemo(
@ -45,6 +48,14 @@ export const useBankCards = () => {
setDefaultCardRequest(cardId).then(fetchCards) setDefaultCardRequest(cardId).then(fetchCards)
} }
useEffect(() => {
if (window.location.pathname === PAGES.highlights) {
setIsHighlightsPage(true)
} else {
setIsHighlightsPage(false)
}
}, [])
return { return {
cards, cards,
defaultCard, defaultCard,
@ -52,6 +63,7 @@ export const useBankCards = () => {
fetchCards, fetchCards,
infoModalOpen, infoModalOpen,
isFetching, isFetching,
isHighlightsPage,
onAddCard, onAddCard,
onDeleteCard: handleCardDelete, onDeleteCard: handleCardDelete,
onSetDefaultCard, onSetDefaultCard,

@ -6,7 +6,9 @@ import map from 'lodash/map'
import { useLexicsStore } from 'features/LexicsStore' import { useLexicsStore } from 'features/LexicsStore'
import { PAGES } from 'config' import { PAGES } from 'config'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { Icon } from 'features/Icon'
import { OutsideClick } from 'features/OutsideClick' import { OutsideClick } from 'features/OutsideClick'
import { import {
Column, Column,
@ -15,6 +17,7 @@ import {
InputStyled, InputStyled,
Label, Label,
LabelTitle, LabelTitle,
LabelAfter,
} from 'features/Common/Input/styled' } from 'features/Common/Input/styled'
import { Props, Option } from './types' import { Props, Option } from './types'
@ -22,6 +25,7 @@ import { useCombobox } from './hooks'
import { import {
PopOver, PopOver,
ListOption, ListOption,
WrapperIcon,
} from './styled' } from './styled'
import { Arrow } from './components/Arrow' import { Arrow } from './components/Arrow'
@ -31,7 +35,9 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
className, className,
disabled, disabled,
error, error,
iconName,
label, label,
labelAfter,
labelLexic, labelLexic,
labelWidth, labelWidth,
maxLength, maxLength,
@ -87,11 +93,15 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
placeholder={translate(labelLexic || '')} placeholder={translate(labelLexic || '')}
isUserAccountPage={isUserAccountPage} isUserAccountPage={isUserAccountPage}
/> />
{labelAfter && query && <LabelAfter>{labelAfter}</LabelAfter>}
</Label> </Label>
<Arrow {iconName ? (
isExpanded={isOpen} <WrapperIcon>
toggle={toggle} <Icon refIcon={iconName} />
/> </WrapperIcon>
) : (
<Arrow isExpanded={isOpen} toggle={toggle} />
)}
{isOpen && !isEmpty(options) && ( {isOpen && !isEmpty(options) && (
<OutsideClick onClick={onOutsideClick}> <OutsideClick onClick={onOutsideClick}>
<PopOver ref={popoverRef}> <PopOver ref={popoverRef}>

@ -49,3 +49,12 @@ export const ListOption = styled.li.attrs(() => ({
color: #fff; color: #fff;
} }
` `
export const WrapperIcon = styled.div`
position: absolute;
right: 22px;
top: 50%;
width: 15px;
height: 15px;
transform: translateY(-50%);
`

@ -1,6 +1,7 @@
import type { import type {
InputHTMLAttributes, InputHTMLAttributes,
ChangeEvent, ChangeEvent,
ReactNode,
} from 'react' } from 'react'
import type { CustomStyles } from 'features/Common' import type { CustomStyles } from 'features/Common'
@ -19,7 +20,9 @@ export type Props<T> = Pick<InputHTMLAttributes<HTMLInputElement>, (
className?: string, className?: string,
customListStyles?: CustomStyles, customListStyles?: CustomStyles,
error?: string | null, error?: string | null,
iconName?: string,
label?: string, label?: string,
labelAfter?: string | ReactNode,
labelLexic?: string, labelLexic?: string,
labelWidth?: number, labelWidth?: number,
maxLength?: number, maxLength?: number,

@ -41,6 +41,7 @@ type Props = {
type?: string, type?: string,
value?: string, value?: string,
withError?: boolean, withError?: boolean,
wrapperHeight?: number,
} & WrapperProps } & WrapperProps
export const Input = ({ export const Input = ({

@ -69,6 +69,7 @@ export const LabelTitle = styled.p<TitleProps>`
` `
type InputProps = { type InputProps = {
disabled?: boolean,
inputWidth?: number, inputWidth?: number,
isUserAccountPage?: boolean, isUserAccountPage?: boolean,
} }
@ -115,6 +116,7 @@ const inputStyles = css<InputProps>`
export const InputStyled = styled.input<InputProps>` export const InputStyled = styled.input<InputProps>`
${inputStyles} ${inputStyles}
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
::placeholder { ::placeholder {
opacity: 0; opacity: 0;
} }

@ -14,6 +14,7 @@ export type FormState = {[formId: string]: FieldState}
export const useFormState = (initialState: FormState = {}) => { export const useFormState = (initialState: FormState = {}) => {
const [formState, setFormState] = useState<FormState>(initialState) const [formState, setFormState] = useState<FormState>(initialState)
const readFormValue = useCallback( const readFormValue = useCallback(
(fieldName: string) => formState[fieldName]?.value ?? '', (fieldName: string) => formState[fieldName]?.value ?? '',
[formState], [formState],

@ -76,7 +76,15 @@ export const ProfileCard = ({ profile }: ProfileType) => {
<T9n t='add_to_favorites' /> <T9n t='add_to_favorites' />
</FavoriteButton> </FavoriteButton>
<Link to={PAGES.highlights}> <Link to={PAGES.highlights}>
<SсGetHighlightBtn onClick={() => setPlayerHighlight(profile)}> <SсGetHighlightBtn
onClick={() => setPlayerHighlight({
profile: {
...profile,
id: profileId,
},
sportType,
})}
>
<T9n t='get_highlights' /> <T9n t='get_highlights' />
</SсGetHighlightBtn> </SсGetHighlightBtn>
</Link> </Link>

@ -6,13 +6,17 @@ import { CardStep } from 'features/BuyMatchPopup/components/CardStep'
import { Modal } from './styled' import { Modal } from './styled'
type Props = { type Props = {
btnName?: string,
changeCardPopupOpen: boolean, changeCardPopupOpen: boolean,
setChangeCardPopupOpen: (open: boolean) => void, setChangeCardPopupOpen: (open: boolean) => void,
title?: string,
} }
export const ChangeCardPopup = ({ export const ChangeCardPopup = ({
btnName,
changeCardPopupOpen, changeCardPopupOpen,
setChangeCardPopupOpen, setChangeCardPopupOpen,
title,
}: Props) => { }: Props) => {
const { fetchCards } = useCardsStore() const { fetchCards } = useCardsStore()
@ -28,8 +32,8 @@ export const ChangeCardPopup = ({
withCloseButton withCloseButton
> >
<CardStep <CardStep
title='change_card' title={title ?? 'change_card'}
btnName='change' btnName={btnName ?? 'change'}
/> />
</Modal> </Modal>
) )

@ -260,7 +260,7 @@ export const InlineButton = styled.button<InlineButtonProps>`
` `
export const ScButtonGetHighlight = styled(ButtonOutline)` export const ScButtonGetHighlight = styled(ButtonOutline)`
max-width: 130px; max-width: 200px;
max-height: 50px; max-height: 50px;
margin: 15px 0; margin: 15px 0;
border: 1px solid #FFFFFF; border: 1px solid #FFFFFF;

@ -10,11 +10,15 @@ import { modifyUserFavorites, getUserFavorites } from 'requests'
import { useToggle } from 'hooks/useToggle' import { useToggle } from 'hooks/useToggle'
import { ProfileTypes } from 'config' import { ProfileTypes } from 'config'
type ProfileType = ObjectWithName & { type ProfileType = {
additionalInfo: ObjectWithName & { profile: ObjectWithName & {
additionalInfo: ObjectWithName & {
id: number,
tournamentId?: number,
},
id: number, id: number,
tournamentId?: number,
}, },
sportType: number,
} }
type Args = Parameters<typeof modifyUserFavorites>[0] type Args = Parameters<typeof modifyUserFavorites>[0]

@ -8,6 +8,7 @@ export { Hockey } from './objects/Hockey'
export { Handball } from './objects/Handball' export { Handball } from './objects/Handball'
export { Volleyball } from './objects/Volleyball' export { Volleyball } from './objects/Volleyball'
export { Search } from './objects/Search' export { Search } from './objects/Search'
export { Sound } from './objects/Sound'
export { Star } from './objects/Star' export { Star } from './objects/Star'
export { Dollar } from './objects/Dollar' export { Dollar } from './objects/Dollar'
export { Close } from './objects/Close' export { Close } from './objects/Close'

@ -0,0 +1,17 @@
export const Sound = () => (
<svg
width='24'
height='15'
viewBox='0 0 24 15'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
opacity='0.5'
fillRule='evenodd'
clipRule='evenodd'
d='M0.5 3.85443H4.9571L10.6721 0.0834967C11.0045 -0.135849 11.4475 0.102562 11.4475 0.500835V13.7497C11.4475 14.148 11.0045 14.3864 10.6721 14.167L4.95671 10.3958H0.5C0.223858 10.3958 0 10.172 0 9.89584V4.35443C0 4.07828 0.223858 3.85443 0.5 3.85443ZM20.9665 14.7222L20.3088 13.9904C23.5785 10.3523 23.5785 4.42471 20.3088 0.786605L20.9665 0.054802C24.6026 4.11108 24.6026 10.6764 20.9665 14.7222ZM16.924 11.7055L17.8232 12.4657C21.1373 9.66374 21.1373 5.11329 17.8232 2.31133L16.924 3.07155C19.7371 5.44996 19.7371 9.32708 16.924 11.7055ZM15.6147 10.2092L14.6674 9.36539C15.8854 8.28052 15.8854 6.50857 14.6674 5.41165L15.6147 4.56786C17.3604 6.13489 17.3604 8.6542 15.6147 10.2092Z'
fill='currentColor'
/>
</svg>
)

@ -1,18 +1,293 @@
import { useEffect, useState } from 'react' import {
ChangeEvent,
FormEvent,
useEffect,
useRef,
useState,
useMemo,
} from 'react'
import debounce from 'lodash/debounce'
import { useRecoilState } from 'recoil'
import { useUserFavoritesStore } from 'features/UserFavorites/store'
import { getSportList } from 'requests/getSportList' import { getSportList } from 'requests/getSportList'
import {
getSportTeams,
Team,
SportTeamsType,
} from 'requests/getSportTeams'
import { getPlayerMatches } from 'requests/getMatches/getPlayerMatches'
import { getTeamPlayers, Player } from 'requests/getTeamPlayers'
import { playerMatchesState, dataForPayHighlights } from '../../storeHighlightsAtoms'
export type SportType = {
id: number,
lexic: string,
name: string,
name_eng: string,
name_rus?: string,
}
type SportTypeName = SportType& {
name: string,
}
type PlayerType = Player & {
name: string,
}
type TeamType = Team & {
name: string,
}
type FormType = {
duration: string,
playerValue: string,
players: Array<string>,
selectedPlayer: PlayerType | null,
selectedSound: any,
selectedTeam: Team | null,
sport: SportTypeName,
stats: {
id: number,
name: string,
},
teamValue: string,
}
const sounds = [
{
id: 0,
name: 'No',
},
{
id: 1,
name: 'Sound 1',
},
{
id: 2,
name: 'Sound 2',
},
{
id: 3,
name: 'Sound 3',
},
]
const summaryStats = [
{
id: 0,
name: 'No',
},
{
id: 1,
name: 'Yes',
},
]
export const useHighlightsForm = () => { export const useHighlightsForm = () => {
const [sports, setSports] = useState<any>() const { playerHighlight } = useUserFavoritesStore()
const players: any = []
const [sports, setSports] = useState<Array<SportTypeName>>([])
const [teams, setTeams] = useState<Array<TeamType>>([])
const [playersData, setPlayersData] = useState<Array<PlayerType>>([])
const [players, setPlayers] = useState<Array<PlayerType>>([])
const [formState, setFormState] = useState<FormType>({} as FormType)
const [playerMatches, setPlayerMatches] = useRecoilState(playerMatchesState)
const [dataHighlights, setDatahighlights] = useRecoilState(dataForPayHighlights)
const formRef = useRef<HTMLFormElement>(null)
const onSportSelect = (selectedSport: SportTypeName | null) => {
if (selectedSport) {
setFormState((state) => ({
...state,
playerValue: '',
selectedPlayer: null,
selectedTeam: null,
sport: selectedSport,
teamValue: '',
}))
setTeams([])
setPlayers([])
}
}
const onTeamSelect = (selectedTeam: TeamType | null) => {
if (selectedTeam) {
setFormState((state) => ({
...state,
selectedTeam,
}))
}
}
const onPlayerSelect = (selectedPlayer: PlayerType | null) => {
if (!selectedPlayer) return
setFormState((state) => ({
...state,
selectedPlayer,
}))
}
const onSoundSelect = (selectedSound: any) => {
if (!selectedSound) return
setFormState((state) => ({
...state,
selectedSound,
}))
}
const onStatsSelect = (stats: any) => {
if (!stats) return
setFormState((state) => ({
...state,
stats,
}))
}
const onChangeMaxDuration = (e: ChangeEvent<HTMLInputElement>) => {
if ((Number(e.target.value) > 0 && Number(e.target.value) <= 100) || e.target.value === '') {
setFormState((state) => ({
...state,
duration: e.target.value,
}))
}
}
const onChangeSummary = (e: ChangeEvent<HTMLInputElement>) => {
const regex = /[0-5]/.test(e.target.value)
if ((regex && e.target.value.length <= 1) || e.target.value === '') {
setFormState((state) => ({
...state,
summaryDuration: e.target.value,
}))
}
}
const onChangeTeam = (e: ChangeEvent<HTMLInputElement>) => {
setFormState((state: FormType) => ({
...state,
selectedTeam: null,
teamValue: e?.target?.value,
}))
}
const onChangePlayer = (e: ChangeEvent<HTMLInputElement>) => {
setFormState((state: FormType) => ({
...state,
playerValue: e?.target?.value,
selectedPlayer: null,
}))
setPlayers(
playersData?.filter(
(player: PlayerType) => player
&& player.name
?.toLowerCase()
.indexOf(e?.target?.value.toLowerCase()) >= 0,
),
)
}
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
console.log('sports', sports)
useEffect(() => { useEffect(() => {
getSportList().then(setSports) getSportList().then((state: any) => setSports(
// getTeams().then(teams) state?.map((sport: SportType) => ({
...sport,
name: sport?.name_eng,
})),
))
}, []) }, [])
useEffect(() => {
if (playerHighlight?.sportType) {
setFormState((prevState: any) => ({
...prevState,
sport: sports?.find((sportType) => sportType.id === playerHighlight.sportType),
teams: teams?.find(({ id }) => id === playerHighlight.profile.additionalInfo.id),
}))
}
}, [sports, playerHighlight, teams])
const fetchTeams = useMemo(
() => debounce(() => getSportTeams(formState?.sport?.id, -1, formState.teamValue).then(
({ data }: SportTeamsType) => setTeams(
data?.map((team: Team) => ({
...team,
name: team.name_eng,
})),
),
), 300),
[formState.teamValue],
)
useEffect(() => {
if (formState?.teamValue?.length >= 3 && formState?.sport) {
fetchTeams()
}
}, [formState.teamValue, formState?.sport, playerHighlight])
useEffect(() => {
if (formState?.selectedPlayer?.id) {
setDatahighlights({
duration: Number(formState?.duration),
lang: 'en',
matches: playerMatches?.map((match) => match.id),
player_id: formState?.selectedPlayer?.id,
sport_id: formState?.sport.id,
stats: Boolean(formState?.stats?.id),
})
}
}, [formState, playerMatches])
useEffect(() => {
formState?.selectedTeam
&& getTeamPlayers(formState?.sport?.id, formState?.selectedTeam?.id)
.then((state: any) => setPlayersData(state?.map((player: PlayerType) => ({
...player,
name: `${player?.firstname_eng} ${player?.lastname_eng}`,
}))))
}, [formState?.selectedTeam, playerHighlight])
useEffect(() => {
if (!formState?.selectedPlayer) return
formState?.selectedPlayer
&& getPlayerMatches({
limit: 1000,
offset: 0,
playerId: formState?.selectedPlayer?.id,
sportType: formState?.sport?.id,
})
.then(({ broadcast }) => setPlayerMatches(
broadcast.map((match: any) => ({ ...match, isChecked: false })),
))
}, [formState?.selectedPlayer, formState?.selectedTeam])
return { return {
formRef,
formState,
handleSubmit,
onChangeMaxDuration,
onChangePlayer,
onChangeTeam,
onPlayerSelect,
onSoundSelect,
onSportSelect,
onStatsSelect,
onTeamSelect,
playerMatches,
players, players,
sounds,
sports, sports,
// teams, summaryStats,
teams,
} }
} }

@ -1,9 +1,11 @@
import { Combobox } from 'features/Combobox' import { Combobox } from 'features/Combobox'
import { Input } from 'features/Common' import { Input } from 'features/Common'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { useUserFavoritesStore } from 'features/UserFavorites/store' import { Icon } from 'features/Icon'
import { useHighlightsForm } from './hooks' import {
useHighlightsForm,
} from './hooks'
import { import {
ScWrapper, ScWrapper,
@ -18,8 +20,33 @@ const labelWidth = 180
const wrapperHeight = 50 const wrapperHeight = 50
export const FormHighlights = () => { export const FormHighlights = () => {
const { sports } = useHighlightsForm() const {
const { playerHighlight } = useUserFavoritesStore() formRef,
formState: {
duration,
playerValue,
selectedPlayer,
selectedSound,
selectedTeam,
sport,
stats,
teamValue,
},
handleSubmit,
onChangeMaxDuration,
onChangePlayer,
onChangeTeam,
onPlayerSelect,
onSoundSelect,
onSportSelect,
onStatsSelect,
onTeamSelect,
players,
sounds,
sports,
summaryStats,
teams,
} = useHighlightsForm()
return ( return (
<ScWrapper> <ScWrapper>
@ -35,74 +62,88 @@ export const FormHighlights = () => {
/> />
</ScText> </ScText>
</ScInfoBlock> </ScInfoBlock>
<ScForm> <ScForm
ref={formRef}
>
<ScInputGroup> <ScInputGroup>
<Combobox <Combobox
noSearch noSearch
selected selected
labelLexic='sport_highlight' labelLexic='sport_highlight'
labelWidth={labelWidth} labelWidth={labelWidth}
value='123' value={sport?.name_eng || ''}
onSelect={() => console.log(123)} onSelect={onSportSelect}
options={sports} options={sports}
maxLength={500} maxLength={500}
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}
/> />
<Input <Combobox
value='Team' disabled={!sport}
selected={Boolean(selectedTeam?.name_eng)}
labelLexic='team_highlight' labelLexic='team_highlight'
autoComplete='family-name'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={() => console.log('lkdsmfkl')} value={selectedTeam?.name_eng || teamValue || ''}
iconName='Search' onSelect={onTeamSelect}
onChange={onChangeTeam}
options={teams}
maxLength={500} maxLength={500}
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}
iconName='Search'
className='FormHighlights__select__teams'
/> />
<Input <Combobox
value='Player' disabled={!sport || !selectedTeam}
selected
labelLexic='player_highlight' labelLexic='player_highlight'
autoComplete='family-name'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={() => console.log('lkdsmfkl')} value={selectedPlayer?.name || playerValue || ''}
iconName='Search' onSelect={onPlayerSelect}
onChange={onChangePlayer}
options={players}
maxLength={500} maxLength={500}
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}
iconName='Search'
className='FormHighlights__select__players'
/> />
<Input <Input
value='Player' disabled={!sport}
value={duration?.toString() || ''}
labelLexic='maximal_duration' labelLexic='maximal_duration'
autoComplete='family-name'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={() => console.log('lkdsmfkl')} onChange={onChangeMaxDuration}
maxLength={500} maxLength={500}
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}
// pattern={'1'}
labelAfter='min' labelAfter='min'
className='FormHighlights__input__duration'
/> />
<Combobox <Combobox
disabled={!sport}
noSearch noSearch
selected selected
labelLexic='background_music' labelLexic='background_music'
labelWidth={labelWidth} labelWidth={labelWidth}
value='123' value={selectedSound?.name || ''}
onSelect={() => console.log(123)} onSelect={onSoundSelect}
options={[]} options={sounds}
maxLength={500} maxLength={500}
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}
labelAfter={<Icon refIcon='Sound' />}
className='FormHighlights__input__sound'
/> />
<Combobox <Combobox
disabled={!sport}
noSearch noSearch
selected selected
labelLexic='add_summary' labelLexic='add_summary'
labelWidth={labelWidth} labelWidth={labelWidth}
value='123' value={stats?.name || ''}
onSelect={() => console.log(123)} onSelect={onStatsSelect}
options={[]} options={summaryStats}
maxLength={500} maxLength={500}
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}

@ -7,7 +7,7 @@ export const ScWrapper = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
color: #FFFFFF; color: #FFFFFF;
padding: 0 40px; padding: 0 80px 0 40px;
` `
export const ScInfoBlock = styled.div` export const ScInfoBlock = styled.div`
display: flex; display: flex;
@ -44,4 +44,23 @@ export const ScForm = styled.form`
? css` ? css`
` `
: ''}; : ''};
.FormHighlights__input__duration {
& input {
max-width: 40px;
}
}
.FormHighlights__input__sound {
& input {
max-width: 100px;
}
}
.FormHighlights__select__teams,
.FormHighlights__select__players {
& ul {
max-height: 200px;
}
}
` `

@ -0,0 +1,21 @@
import { ChangeEvent } from 'react'
import { useRecoilState } from 'recoil'
import { playerMatchesState } from '../../storeHighlightsAtoms'
export const useHighlighMatches = () => {
const [playerMatches, setPlayerMatches] = useRecoilState(playerMatchesState)
const onChangeSelectedMatches = ({ target }: ChangeEvent<HTMLInputElement>) => setPlayerMatches(
(prev) => prev.map(
(match) => (Number(match.id) === Number(target.id)
? { ...match, isChecked: !match.isChecked } : match),
),
)
return {
onChangeSelectedMatches,
playerMatches,
}
}

@ -0,0 +1,80 @@
import { T9n } from 'features/T9n'
import { Checkbox } from 'features/Common/Checkbox'
import { ArrowLoader } from 'features/ArrowLoader'
import { SportIcon } from 'features/SportIcon'
import { useHighlighMatches } from './hooks'
import {
ScTitle,
ScMatchesWrapper,
ScMatchesList,
ScLabel,
ScDate,
ScTournament,
ScTournamentName,
ScCountryFlag,
ScFakeDate,
ScFakeTournamentName,
ScFakeTournament,
ScFakeCheckbox,
ScFakeWrapper,
ScLoaderWrapper,
} from './styled'
export const MatchesHighlights = ({ matches }: any) => {
const { onChangeSelectedMatches, playerMatches } = useHighlighMatches()
return (
<ScMatchesWrapper>
<ScTitle>
<T9n t='matches_highlight' />
</ScTitle>
<ScMatchesList>
{playerMatches.length ? (playerMatches?.map(({
country_id,
date,
id,
isChecked,
sport,
team1,
team2,
tournament,
}: any) => (
<Checkbox
key={id}
id={id}
checked={isChecked}
onChange={onChangeSelectedMatches}
label={(
<ScLabel>
<ScDate>
{date}
</ScDate>&nbsp;
{team1.name_eng} - {team2.name_eng}
<ScTournament>
<SportIcon sport={sport} />
<ScCountryFlag
src={`https://instatscout.com/images/flags/48/${country_id}.png`}
/>
<ScTournamentName>{tournament.name_eng}</ScTournamentName>
</ScTournament>
</ScLabel>
)}
/>
))) : (Array.from(Array(12)).map(() => (
<ScFakeWrapper>
<ScFakeCheckbox />
<ScFakeTournament>
<ScFakeDate />
<ScFakeTournamentName />
</ScFakeTournament>
</ScFakeWrapper>
)))}
{/* <ScLoaderWrapper>
<ArrowLoader />
</ScLoaderWrapper> */}
</ScMatchesList>
</ScMatchesWrapper>
)
}

@ -0,0 +1,108 @@
import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent'
import { customScrollbar } from 'features/Common'
export const ScMatchesWrapper = styled.div`
display: flex;
flex-direction: column;
color: white;
min-width: 360px;
`
export const ScTitle = styled.span`
font-weight: 700;
font-size: 34px;
line-height: 40px;
margin-bottom: 75px;
`
export const ScMatchesList = styled.div`
max-height: 500px;
overflow-y: auto;
position: relative;
${customScrollbar}
`
export const ScLabel = styled.div`
font-weight: 600;
font-size: 14px;
line-height: 20px;
margin-bottom: 16px;
`
export const ScDate = styled.span`
font-weight: 400;
`
export const ScTournament = styled.div`
color: rgba(255, 255, 255, 0.7);
font-size: 10px;
font-weight: 400;
display: flex;
align-items: center;
height: 16px;
`
export const ScTournamentName = styled.span`
margin-left: 5px;
`
export const ScCountryFlag = styled.img`
width: 0.71rem;
height: 0.75rem;
margin-left: 0.567rem;
object-fit: contain;
object-position: bottom;
${isMobileDevice
? css`
width: 12px;
height: 8px;
margin-left: 3.5px;
`
: ''};
`
export const ScFakeDate = styled.div`
background: #4E4E4E;
border-radius: 4px;
width: 218px;
height: 10px;
margin-bottom: 7px;
`
export const ScFakeTournamentName = styled.div`
background: rgba(78, 78, 78, 0.6);
border-radius: 4px;
width: 92px;
height: 10px;
`
export const ScFakeTournament = styled.div`
display: flex;
flex-direction: column;
`
export const ScFakeCheckbox = styled.div`
width: 20px;
height: 20px;
background: #4E4E4E;
border-radius: 4px;
margin-right: 20px;
`
export const ScFakeWrapper = styled.div`
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 14px;
`
export const ScLoaderWrapper = styled.div`
position: absolute;
left: 30%;
top: 50%;
z-index: 1;
`

@ -1,5 +1,3 @@
import { ReactNode } from 'react'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { import {
@ -11,19 +9,16 @@ import {
} from './styled' } from './styled'
type PriceInfoType = { type PriceInfoType = {
currency?: number, price: number,
price?: number,
} }
export const PriceInfo = ({ currency, price }: PriceInfoType) => ( export const PriceInfo = ({ price }: PriceInfoType) => (
<ScPriceInfo> <ScPriceInfo>
<ScTitle> <ScTitle>
<T9n t='price' /> <T9n t='price' />
</ScTitle> </ScTitle>
<ScPrice> <ScPrice>
<ScCurrency> <ScCurrency>$</ScCurrency>
{currency || '$'}
</ScCurrency>
{price} {price}
</ScPrice> </ScPrice>
<ScWatchDemo> <ScWatchDemo>

@ -1,7 +1,6 @@
import styled, { css } from 'styled-components/macro' import styled, { css } from 'styled-components/macro'
export const ScPriceInfo = styled.div` export const ScPriceInfo = styled.div`
width: 188px;
height: 186px; height: 186px;
border: 1px solid #FFFFFF; border: 1px solid #FFFFFF;
border-radius: 34px; border-radius: 34px;

@ -1,27 +1,67 @@
import { Link } from 'react-router-dom'
import { PAGES } from 'config'
import { useRecoilValue, useRecoilState } from 'recoil'
import { T9n } from 'features/T9n'
import { CompanyInfo } from 'features/CompanyInfo'
import { ChangeCardPopup } from 'features/UserAccount/components/ChangeCardPopup'
import { MultiSourcePlayer } from 'features/MultiSourcePlayer'
import { PriceInfo } from './components/PriceInfo' import { PriceInfo } from './components/PriceInfo'
import { FormHighlights } from './components/FormHighlights' import { FormHighlights } from './components/FormHighlights'
import { MatchesHighlights } from './components/MatchesHighlights'
import {
playerMatchesState,
openPopupChangeCard,
} from './storeHighlightsAtoms'
import { import {
ScHeader, ScHeader,
ScHeaderLogo, ScHeaderLogo,
ScWrapper, ScWrapper,
ScWrapperContent, ScWrapperContent,
ScButton,
ScButtonWrap,
} from './styled' } from './styled'
const HighlightsPage = () => { const HighlightsPage = () => {
console.log(123) const playerMatches = useRecoilValue(playerMatchesState)
const [isOpenPopupChangeCard, setIsOpenPopupChangeCard] = useRecoilState(openPopupChangeCard)
const countIsCheckedMatches = playerMatches?.filter(
({ isChecked }: any) => isChecked,
).length
console.log(playerMatches)
return ( return (
<ScWrapper> <ScWrapper>
<ScHeader> <ScHeader>
<ScHeaderLogo /> <Link to={PAGES.home}>
<ScHeaderLogo />
</Link>
</ScHeader> </ScHeader>
<ScWrapperContent> <ScWrapperContent>
<PriceInfo price={0} /> <PriceInfo price={countIsCheckedMatches * 25} />
<FormHighlights /> <FormHighlights />
<MatchesHighlights />
</ScWrapperContent> </ScWrapperContent>
<ScButtonWrap onClick={() => setIsOpenPopupChangeCard(true)}>
<ScButton disabled={!!countIsCheckedMatches}>
<T9n t='order_and_buy' />
</ScButton>
</ScButtonWrap>
<CompanyInfo textAlign='center' />
<ChangeCardPopup
btnName='buy_subscription'
changeCardPopupOpen={isOpenPopupChangeCard}
setChangeCardPopupOpen={setIsOpenPopupChangeCard}
title='payment'
/>
</ScWrapper> </ScWrapper>
) )
} }
export default HighlightsPage export default HighlightsPage

@ -0,0 +1,30 @@
import { atom } from 'recoil'
import type { Match } from 'requests'
export type PlayerMatchesType = Array<Match & {isChecked: boolean}
>
type DataForm = {
duration: number,
lang: string,
matches: Array<number>,
player_id: number,
sport_id: number,
stats: boolean,
}
export const playerMatchesState = atom({
default: [] as PlayerMatchesType,
key: 'playerMatchesState',
})
export const openPopupChangeCard = atom({
default: false,
key: 'openPopupChangeCard',
})
export const dataForPayHighlights = atom({
default: {} as DataForm,
key: 'dataForPayHighlights',
})

@ -1,6 +1,7 @@
import styled, { css } from 'styled-components/macro' import styled, { css } from 'styled-components/macro'
import { Logo } from 'features/Logo' import { Logo } from 'features/Logo'
import { ButtonSolid } from 'features/Common/Button'
export const ScHeader = styled.div` export const ScHeader = styled.div`
width: 100%; width: 100%;
@ -14,7 +15,7 @@ export const ScHeaderLogo = styled(Logo)`
export const ScWrapper = styled.div` export const ScWrapper = styled.div`
width: 100%; width: 100%;
height: 100%; max-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -23,5 +24,20 @@ export const ScWrapper = styled.div`
export const ScWrapperContent = styled.div` export const ScWrapperContent = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding: 100px 170px 0px 170px; padding: 100px 170px 82px 170px;
`
export const ScButton = styled(ButtonSolid)`
width: 270px;
height: 50px;
background: #294FC4;
border-radius: 5px;
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)}
`
export const ScButtonWrap = styled.div`
display: flex;
justify-content: center;
margin-bottom: 100px;
` `

@ -0,0 +1,52 @@
import {
DATA_URL,
PROCEDURES,
} from 'config'
import { callApi } from 'helpers'
const proc = PROCEDURES.get_sport_teams
export type Team = {
c_country?: number,
c_gender?: number,
c_sport?: number,
c_team_type?: number,
country_en?: string,
country_iso?: string,
country_ru?: string,
dl?: boolean,
id: number,
name_eng: string,
name_national: string,
name_rus: string,
short_name_eng?: string,
short_name_rus?: string,
ts: string,
}
export type SportTeamsType = {
data: Array<Team>,
more: boolean,
}
export const getSportTeams = (
sport_id: number,
_p_limit: number,
_p_name: string,
)
: Promise<SportTeamsType> => {
const config = {
body: {
params: {
_p_limit,
_p_name,
sport_id,
},
proc,
},
}
return callApi({
config,
url: DATA_URL,
})
}

@ -0,0 +1,41 @@
import {
DATA_URL,
PROCEDURES,
} from 'config'
import { callApi } from 'helpers'
const proc = PROCEDURES.get_team_players
export type Player = {
birthday: string | null,
c_gender: number,
firstname_eng: string,
firstname_rus: string,
height: string | number,
id: number,
lastname_eng: string,
lastname_rus: string,
nickname_eng: string | number| null,
nickname_rus: string | number | null,
sport_id: number,
weight: string | number | null,
}
export const getTeamPlayers = (_p_sport_id: number,
_p_team_id: number)
: Promise<Array<Player>> => {
const config = {
body: {
params: {
_p_sport_id,
_p_team_id,
},
proc,
},
}
return callApi({
config,
url: DATA_URL,
})
}

@ -0,0 +1,30 @@
import { API_ROOT } from 'config'
import { callApi } from 'helpers'
export type Props = {
cardId: string,
item: {
duration: number,
lang: string,
matches: Array<number>,
player_id: number,
sport_id: number,
stats: boolean,
},
}
export const onePayment = async ({ cardId, item }: Props) => {
const config = {
body: {
action: 'one_payment',
card_id: cardId,
item,
service: 'stripe_ott',
},
}
return callApi({
config,
url: `${API_ROOT}/account/payments`,
})
}
Loading…
Cancel
Save