feat(#2341): add new filters and tournaments

keep-around/26e96e2488f7881e90a1e330fecc8568ebc21871
Andrei Dekterev 4 years ago
parent 556811823d
commit 9c0e1adefb
  1. 16
      Makefile
  2. 1
      src/config/lexics/indexLexics.tsx
  3. 19
      src/features/HeaderFilters/components/DateFilter/hooks/index.tsx
  4. 21
      src/features/HeaderFilters/store/hooks/index.tsx
  5. 24
      src/features/HomePage/components/HeaderFilters/index.tsx
  6. 11
      src/features/HomePage/components/HeaderFilters/styled.tsx
  7. 22
      src/features/MatchesGrid/index.tsx
  8. 2
      src/features/SportsFilter/components/SelectSport/index.tsx
  9. 11
      src/features/SportsFilter/components/SelectSport/styled.tsx
  10. 81
      src/features/SportsFilter/components/SelectSportPopup/index.tsx
  11. 22
      src/features/SportsFilter/index.tsx
  12. 13
      src/features/TournamentList/components/CollapseTournament/index.tsx
  13. 1
      src/features/TournamentList/components/CollapseTournament/styled.tsx
  14. 26
      src/features/TournamentList/hooks.tsx
  15. 6
      src/features/TournamentList/index.tsx

@ -57,6 +57,13 @@ build-e: clean
REACT_APP_STAGE=e-staging \ REACT_APP_STAGE=e-staging \
npm run build npm run build
build-f: clean
REACT_APP_TYPE=ott \
REACT_APP_ENV=staging \
REACT_APP_CLIENT=instat \
REACT_APP_STAGE=f-staging \
npm run build
auth-build: auth-build:
rm -rf build_auth rm -rf build_auth
@ -96,7 +103,7 @@ prod: clean
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@de.instat.tv:/usr/local/www/ott/wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@de.instat.tv:/usr/local/www/ott/wwwroot/
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@fr.instat.tv:/usr/local/www/ott/wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@fr.instat.tv:/usr/local/www/ott/wwwroot/
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@bkup.instat.tv:/usr/local/www/ott/wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@10.0.3.8:/usr/local/www/ott/wwwroot/
preprod: clean preprod: clean
REACT_APP_TYPE=ott \ REACT_APP_TYPE=ott \
@ -105,7 +112,7 @@ preprod: clean
REACT_APP_STRIPE_PK=pk_live_ANI76cBhSo69DZUxPmyRVIZW \ REACT_APP_STRIPE_PK=pk_live_ANI76cBhSo69DZUxPmyRVIZW \
npm run build npm run build
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-test@test.instat.tv:/usr/local/www/ott-test/wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-test@10.0.3.8:/usr/local/www/ott-test/wwwroot/
facr-prod: clean facr-prod: clean
REACT_APP_TYPE=ott \ REACT_APP_TYPE=ott \
@ -116,7 +123,7 @@ facr-prod: clean
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@de.instat.tv:/usr/local/www/ott/facr-wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@de.instat.tv:/usr/local/www/ott/facr-wwwroot/
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@fr.instat.tv:/usr/local/www/ott/facr-wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@fr.instat.tv:/usr/local/www/ott/facr-wwwroot/
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@bkup.instat.tv:/usr/local/www/ott/facr-wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott@10.0.3.8:/usr/local/www/ott/facr-wwwroot/
deploy-all: prod preprod facr-prod deploy-all: prod preprod facr-prod
@ -138,6 +145,9 @@ d-stage: build-d
e-stage: build-e e-stage: build-e
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-staging@staging.instat.tv:/usr/local/www/ott-staging/e-wwwroot/ rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-staging@staging.instat.tv:/usr/local/www/ott-staging/e-wwwroot/
f-stage: build-f
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-staging@10.0.3.8:/usr/local/www/ott-staging/f-wwwroot/
test: test:
npm test npm test

@ -100,6 +100,7 @@ export const indexLexics = {
football: 6958, football: 6958,
full_game: 13028, full_game: 13028,
futsal: 17670, futsal: 17670,
game: 9680,
game_finished: 13026, game_finished: 13026,
game_time: 13029, game_time: 13029,
games: 15773, games: 15773,

@ -1,5 +1,8 @@
import type { MouseEvent } from 'react' import {
import { useMemo } from 'react' MouseEvent,
useEffect,
useMemo,
} from 'react'
import addDays from 'date-fns/addDays' import addDays from 'date-fns/addDays'
@ -11,7 +14,12 @@ import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { getDisplayDate, getWeeks } from '../helpers' import { getDisplayDate, getWeeks } from '../helpers'
export const useDateFilter = () => { export const useDateFilter = () => {
const { selectedDate, setSelectedDate } = useHeaderFiltersStore() const {
selectedDate,
setIsShowTournament,
setSelectedDate,
} = useHeaderFiltersStore()
const { lang } = useLexicsStore() const { lang } = useLexicsStore()
const { const {
close, close,
@ -28,6 +36,11 @@ export const useDateFilter = () => {
lang, lang,
}) })
useEffect(() => {
setIsShowTournament(true)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDate])
const onPreviousClick = () => { const onPreviousClick = () => {
setSelectedDate(addDays(selectedDate, -7)) setSelectedDate(addDays(selectedDate, -7))
} }

@ -22,16 +22,12 @@ export const useFilters = () => {
serialize: serializeDate, serialize: serializeDate,
validator: isValidDate, validator: isValidDate,
}) })
const [allFilters, setAllFilters] = useState({
selectedFilters: [],
selectedLeague: [],
selectedSports: [],
})
const [selectedSport, setSelectedSport] = useState(['all_sports']) const [selectedSport, setSelectedSport] = useState(['all_sports'])
const [selectedLeague, setSelectedLeague] = useState(['all_competitions']) const [selectedLeague, setSelectedLeague] = useState<Array<string | number>>(['all_competitions'])
const [selectedFilters, setSelectedFilters] = useState<Array<string>>([]) const [selectedFilters, setSelectedFilters] = useState<Array<string>>([])
const [isShowTournament, setIsShowTournament] = useState(true) const [isShowTournament, setIsShowTournament] = useState(true)
const [sportIds, setSportIds] = useState<any>()
const isTodaySelected = isToday(selectedDate) const isTodaySelected = isToday(selectedDate)
const resetFilters = useCallback(() => { const resetFilters = useCallback(() => {
@ -49,10 +45,8 @@ export const useFilters = () => {
]) ])
const store = useMemo(() => ({ const store = useMemo(() => ({
allFilters,
isShowTournament, isShowTournament,
isTodaySelected, isTodaySelected,
setAllFilters,
selectedDate, selectedDate,
selectedFilters, selectedFilters,
selectedLeague, selectedLeague,
@ -60,12 +54,13 @@ export const useFilters = () => {
setIsShowTournament, setIsShowTournament,
setSelectedDate, setSelectedDate,
setSelectedFilters, setSelectedFilters,
setSelectedLeague,
setSelectedSport, setSelectedSport,
setSportIds,
sportIds,
}), [ }), [
allFilters,
isShowTournament, isShowTournament,
isTodaySelected, isTodaySelected,
setAllFilters,
selectedDate, selectedDate,
selectedFilters, selectedFilters,
selectedLeague, selectedLeague,
@ -73,8 +68,10 @@ export const useFilters = () => {
setIsShowTournament, setIsShowTournament,
setSelectedDate, setSelectedDate,
setSelectedFilters, setSelectedFilters,
setSelectedLeague,
setSelectedSport, setSelectedSport,
setSportIds,
sportIds,
]) ])
return store return store

@ -1,14 +1,22 @@
import { SportsFilter } from 'features/SportsFilter' import { SportsFilter } from 'features/SportsFilter'
import { useHeaderFiltersStore } from 'features/HeaderFilters' import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { SelectFilter } from 'components/SelectFilter'
import { ScHeaderFilters, ScFilterItemsWrap, ScFilterItem } from './styled' import {
ScArrow,
ScHeaderFilters,
ScFilterItemsWrap,
ScFilterItem,
} from './styled'
export const HeaderFilters = () => { export const HeaderFilters = () => {
const { const {
isShowTournament,
selectedFilters, selectedFilters,
setIsShowTournament,
setSelectedFilters, setSelectedFilters,
setSelectedLeague,
setSelectedSport,
} = useHeaderFiltersStore() } = useHeaderFiltersStore()
const isActiveFilter = (filterItem: string) => selectedFilters.indexOf(filterItem) >= 0 const isActiveFilter = (filterItem: string) => selectedFilters.indexOf(filterItem) >= 0
@ -19,14 +27,22 @@ export const HeaderFilters = () => {
: setSelectedFilters([...selectedFilters, item]) : setSelectedFilters([...selectedFilters, item])
} }
const handleClickBack = () => {
setIsShowTournament(true)
setSelectedFilters([])
setSelectedSport(['all_sports'])
setSelectedLeague(['all_competitions'])
}
return ( return (
<ScHeaderFilters> <ScHeaderFilters>
{!isShowTournament && <ScArrow refIcon='Arrow' color='#ffffff' direction={90} onClick={() => handleClickBack()} />}
<SportsFilter /> <SportsFilter />
<SelectFilter {/* <SelectFilter
onModalOpen={() => console.log(1)} onModalOpen={() => console.log(1)}
open={false} open={false}
selectItem='all_competitions' selectItem='all_competitions'
/> /> */}
<ScFilterItemsWrap> <ScFilterItemsWrap>
<ScFilterItem <ScFilterItem
className={isActiveFilter('live') ? 'activeLive' : ''} className={isActiveFilter('live') ? 'activeLive' : ''}

@ -1,5 +1,7 @@
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { Icon } from 'features/Icon'
export const ScHeaderFilters = styled.div` export const ScHeaderFilters = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -39,3 +41,12 @@ export const ScFilterItem = styled.div<Props>`
text-align: center; text-align: center;
padding: 6px 16px; padding: 6px 16px;
` `
export const ScArrow = styled(Icon)`
margin-right: 15px;
cursor: pointer;
:hover {
/* background-color: rgba(255, 255, 255, 0.5);; */
}
`

@ -16,13 +16,21 @@ type MatchesGridProps = {
export const MatchesGrid = memo(({ matches }: MatchesGridProps) => { export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
const isHomePage = useRouteMatch(PAGES.home)?.isExact const isHomePage = useRouteMatch(PAGES.home)?.isExact
const { selectedFilters, isShowTournament } = useHeaderFiltersStore()
const filteredMatches = selectedFilters?.length const {
isShowTournament,
selectedFilters,
selectedLeague,
} = useHeaderFiltersStore()
const filteredMatches = isHomePage && selectedFilters?.length
? matches.filter((match) => ( ? matches.filter((match) => (
match.live && selectedFilters.indexOf('live') >= 0) ((match.live
|| (selectedFilters.indexOf('upcoming') >= 0 && match.date > new Date()) && selectedFilters.indexOf('live') >= 0)
|| (selectedFilters.indexOf('completed') >= 0 && match.is_finished)) || (selectedFilters.indexOf('upcoming') >= 0 && match.date > new Date())
|| (selectedFilters.indexOf('completed') >= 0 && match.is_finished))))
.filter((match) => ((selectedLeague.indexOf(match.tournament.id) >= 0
|| selectedLeague[0] === 'all_competitions')))
: matches : matches
return ( return (
@ -30,9 +38,7 @@ export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
{isHomePage && isShowTournament ? ( {isHomePage && isShowTournament ? (
<TournamentList matches={filteredMatches} /> <TournamentList matches={filteredMatches} />
) : ( ) : (
filteredMatches.map((match) => ( filteredMatches.map((match) => <MatchCard key={match.id} match={match} />)
<MatchCard key={match.id} match={match} />
))
)} )}
</Wrapper> </Wrapper>
) )

@ -13,7 +13,7 @@ export const SelectSport = ({
open, open,
sport, sport,
}: SportsFilterProps) => ( }: SportsFilterProps) => (
<ScSportsFilter onClick={onModalOpen}> <ScSportsFilter onClick={onModalOpen} opacity={sport[0] === 'all_sports' ? 0.5 : 1}>
<T9n t={sport[0]} /> <T9n t={sport[0]} />
&nbsp; &nbsp;
{sport.length > 1 ? `+ ${sport.length - 1}` : ''} {sport.length > 1 ? `+ ${sport.length - 1}` : ''}

@ -3,18 +3,23 @@ import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent' import { isMobileDevice } from 'config/userAgent'
import { Icon } from 'features/Icon' import { Icon } from 'features/Icon'
export const ScSportsFilter = styled.div` type PropsFilter = {
opacity: number,
}
export const ScSportsFilter = styled.div<PropsFilter>`
display: flex; display: flex;
color: white; color: #ffffff;
opacity: ${({ opacity }) => opacity};
font-size: 14px; font-size: 14px;
width: 30%;
text-transform: uppercase; text-transform: uppercase;
font-weight: 700; font-weight: 700;
cursor: pointer; cursor: pointer;
align-items: center; align-items: center;
width: 20%;
${isMobileDevice ${isMobileDevice
? css` ? css`
width: 30%;
letter-spacing: 0.15rem; letter-spacing: 0.15rem;
font-size: 10px; font-size: 10px;
` `

@ -1,4 +1,5 @@
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { getSportLexic } from 'helpers/getSportLexic'
import { import {
ScBody, ScBody,
@ -9,48 +10,62 @@ import {
ScSportName, ScSportName,
} from './styled' } from './styled'
const sports = [ const sports = {
'all_sports', all_sports: 'all_sports',
'basketball', basketball: 'basketball',
'football_popup', football: 'football_popup',
'handball', handball: 'handball',
'hockey_popup', hockey: 'hockey_popup',
'volleyball', volleyball: 'volleyball',
] }
type Props = { type Props = {
isOpen: boolean, isOpen: boolean,
onModalClose: () => void, onModalClose: () => void,
onSportClick: (sport: string) => void, onSportClick: (sport: string) => void,
selectedSport: Array<string>, selectedSport: Array<string>,
sportIds: Array<string>,
} }
export const SelectSportPopup = ({ export const SelectSportPopup = ({
isOpen, isOpen,
onModalClose, onModalClose,
onSportClick, onSportClick,
selectedSport, selectedSport,
}: Props) => ( sportIds,
<> }: Props) => {
<ScModal isOpen={isOpen} withCloseButton close={onModalClose} closeSize={9}> const sportNames = sportIds
<ScHeaderGroup> && Array.from(sportIds)
<ScHeaderTitle> .map((id) => String(getSportLexic(Number(id))))
<T9n t='choose_sport' /> sportNames?.unshift('all_sports')
</ScHeaderTitle>
</ScHeaderGroup> return (
<ScBody> <>
{sports.map((sport: string) => ( <ScModal
<ScSport isOpen={isOpen}
key={sport} withCloseButton
onClick={() => onSportClick(sport)} close={onModalClose}
className={selectedSport.indexOf(sport) >= 0 ? 'active' : ''} closeSize={9}
active={selectedSport.indexOf(sport) >= 0} >
> <ScHeaderGroup>
<ScSportName> <ScHeaderTitle>
<T9n t={sport} /> <T9n t='choose_sport' />
</ScSportName> </ScHeaderTitle>
</ScSport> </ScHeaderGroup>
))} <ScBody>
</ScBody> {sportNames?.map((sport: string) => (
</ScModal> <ScSport
</> key={sport}
) onClick={() => onSportClick(sport)}
className={selectedSport.indexOf(sport) >= 0 ? 'active' : ''}
active={selectedSport.indexOf(sport) >= 0}
>
<ScSportName>
<T9n t={sports[sport as keyof typeof sports]} />
</ScSportName>
</ScSport>
))}
</ScBody>
</ScModal>
</>
)
}

@ -6,18 +6,35 @@ import { SelectSport } from './components/SelectSport'
import { SelectSportPopup } from './components/SelectSportPopup' import { SelectSportPopup } from './components/SelectSportPopup'
export const SportsFilter = () => { export const SportsFilter = () => {
const { selectedSport, setSelectedSport, setIsShowTournament } = useHeaderFiltersStore() const {
isShowTournament,
selectedSport,
setIsShowTournament,
setSelectedLeague,
setSelectedSport,
sportIds,
} = useHeaderFiltersStore()
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const onSportClick = (sport: string) => { const onSportClick = (sport: string) => {
if (!isShowTournament) return
if (sport === 'all_sports') { if (sport === 'all_sports') {
setSelectedLeague(['all_competitions'])
setSelectedSport([sport]) setSelectedSport([sport])
} else { }
if (selectedSport.indexOf(sport) === -1) {
setSelectedSport((prev) => [ setSelectedSport((prev) => [
...prev.filter((item) => item !== 'all_sports'), ...prev.filter((item) => item !== 'all_sports'),
sport, sport,
]) ])
} else {
setSelectedSport((prev) => [
...prev.filter((item) => item !== 'all_sports' && item === sport),
])
} }
setSelectedLeague(['all_competitions'])
setIsShowTournament(true) setIsShowTournament(true)
setIsOpen(false) setIsOpen(false)
} }
@ -42,6 +59,7 @@ export const SportsFilter = () => {
isOpen={isOpen} isOpen={isOpen}
onModalClose={onModalClose} onModalClose={onModalClose}
selectedSport={selectedSport} selectedSport={selectedSport}
sportIds={sportIds}
/> />
</> </>
) )

@ -32,7 +32,11 @@ export const CollapseTournament = ({
tournament, tournament,
tournamentMatches, tournamentMatches,
}: TournamentProps) => { }: TournamentProps) => {
const { setSelectedSport, setIsShowTournament } = useHeaderFiltersStore() const {
setIsShowTournament,
setSelectedLeague,
setSelectedSport,
} = useHeaderFiltersStore()
const { isInFavorites } = useUserFavoritesStore() const { isInFavorites } = useUserFavoritesStore()
const { const {
countryId, countryId,
@ -49,6 +53,7 @@ export const CollapseTournament = ({
const sportName = getSportLexic(sportType) const sportName = getSportLexic(sportType)
setIsShowTournament(false) setIsShowTournament(false)
setSelectedSport([sportName]) setSelectedSport([sportName])
setSelectedLeague([tournament.id])
} }
return ( return (
@ -90,7 +95,11 @@ export const CollapseTournament = ({
<CountMatches> <CountMatches>
{tournamentMatches.length} {tournamentMatches.length}
&nbsp; &nbsp;
<T9n t='games' /> <T9n t={
tournamentMatches.length > 1
? 'games' : 'game'
}
/>
</CountMatches> </CountMatches>
</Info> </Info>
</CardWrapper> </CardWrapper>

@ -180,6 +180,7 @@ export const CountMatches = styled.span`
font-size: 10px; font-size: 10px;
line-height: 16px; line-height: 16px;
color: #FFFFFF; color: #FFFFFF;
text-transform: lowercase;
` `
export const TournamentName = styled(Name)` export const TournamentName = styled(Name)`

@ -1,3 +1,4 @@
import { useEffect } from 'react'
import { getSportLexic } from 'helpers/getSportLexic' import { getSportLexic } from 'helpers/getSportLexic'
import { TournamentListProps } from 'features/TournamentList' import { TournamentListProps } from 'features/TournamentList'
@ -5,7 +6,11 @@ import type { Match } from 'features/Matches'
import { useHeaderFiltersStore } from 'features/HeaderFilters' import { useHeaderFiltersStore } from 'features/HeaderFilters'
export const useTournaments = (matches: Array<Match>) => { export const useTournaments = (matches: Array<Match>) => {
const { selectedSport } = useHeaderFiltersStore() const {
selectedLeague,
selectedSport,
setSportIds,
} = useHeaderFiltersStore()
const compareSport = (match: Match, sportNames: Array<string>) => { const compareSport = (match: Match, sportNames: Array<string>) => {
if (sportNames[0] === 'all_sports') { if (sportNames[0] === 'all_sports') {
@ -15,11 +20,20 @@ export const useTournaments = (matches: Array<Match>) => {
return (sportNames.indexOf(sport) >= 0 || sportNames.indexOf(`${sport}_popup`) >= 0) return (sportNames.indexOf(sport) >= 0 || sportNames.indexOf(`${sport}_popup`) >= 0)
} }
const compareLeague = (id: number) => {
if (selectedLeague[0] === 'all_competitions') {
return true
}
return (selectedLeague.indexOf(id) >= 0)
}
const tournamentSort: Array<number> = [] const tournamentSort: Array<number> = []
const sportIds = new Set<number>([])
const tournaments = matches.reduce((acc: TournamentListProps, match: Match) => { const tournaments = matches.reduce((acc: TournamentListProps, match: Match) => {
if (matches.length === 0) return {} if (matches.length === 0) return {}
if (!acc[match.tournament.id] && compareSport(match, selectedSport)) { if (!acc[match.tournament.id] && compareSport(match, selectedSport)
&& compareLeague(match.tournament.id)) {
const tournament = { const tournament = {
...match.tournament, ...match.tournament,
countryId: match.countryId, countryId: match.countryId,
@ -34,7 +48,8 @@ export const useTournaments = (matches: Array<Match>) => {
tournamentMatches: [match], tournamentMatches: [match],
} }
tournamentSort.push(match.tournament.id) tournamentSort.push(match.tournament.id)
} else if (compareSport(match, selectedSport)) { sportIds.add(match.sportType)
} else if (compareSport(match, selectedSport) && compareLeague(match.tournament.id)) {
acc[match.tournament.id] = { acc[match.tournament.id] = {
...acc[match.tournament.id], ...acc[match.tournament.id],
tournament: { tournament: {
@ -49,6 +64,11 @@ export const useTournaments = (matches: Array<Match>) => {
return acc return acc
}, {}) }, {})
useEffect(() => {
sportIds && setSportIds(sportIds)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [matches])
return { return {
tournamentSort, tournamentSort,
tournaments, tournaments,

@ -22,9 +22,9 @@ export type TournamentListProps = {
countryId: number, countryId: number,
live: boolean, live: boolean,
sportType: number, sportType: number,
} },
tournamentMatches: Array<Match>, tournamentMatches: Array<Match>,
} },
} }
export const TournamentList = ({ matches }: TournamentTypeProps) => { export const TournamentList = ({ matches }: TournamentTypeProps) => {
@ -44,7 +44,7 @@ export const TournamentList = ({ matches }: TournamentTypeProps) => {
))} ))}
</> </>
) )
case isHomePage && matches.length <= 12: case isHomePage && matches.length >= 12:
return ( return (
<> <>
{tournamentSort?.map((id) => ( {tournamentSort?.map((id) => (

Loading…
Cancel
Save