Preprod (#495)
* feat(ott-1637): change in match popup and match events requests (#488) * Ott 1118 user match preferences (#492) * Ott 1118 part 1 (#469) * fix(1118): wip * feat(1118): tournaments block * feat(1118): resolved comments * feat(1118): show and search tournaments (#472) feat(1118): show loader on fetch * Ott 1118 part 3 (#478) * feat(1118): added lexics * feat(1118): wip, load and save prefs * feat(1118): virtualization using list * Ott 1118 part 4 (#487) * fix(1118): reverted useRequest hook change * fix(1118): px -> rem * fix(1118): request matches after apply * fix(1118): wip * refactor(1118): removed old filters from HeaderFiltersStore (#491) * fix(1118): disabled preferences (#493) * fix(ott-1662): add new langs (#494) Co-authored-by: PolyakovaM <55061222+PolyakovaM@users.noreply.github.com> Co-authored-by: Serg <936x936@gmail.com>keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
parent
e7f20c0ccb
commit
92599ec976
@ -1,6 +1,7 @@ |
|||||||
export type ArrowLoaderProps = { |
export type ArrowLoaderProps = { |
||||||
backgroundColor?: string, |
backgroundColor?: string, |
||||||
backgroundSize?: string, |
backgroundSize?: string, |
||||||
|
className?: string, |
||||||
disabled?: boolean, |
disabled?: boolean, |
||||||
width?:string, |
width?:string, |
||||||
} |
} |
||||||
|
|||||||
@ -1,15 +0,0 @@ |
|||||||
import { MatchStatuses } from 'features/HeaderFilters/store/hooks' |
|
||||||
|
|
||||||
import { isValidMatchStatus } from '..' |
|
||||||
|
|
||||||
it('returns true for valid match statuses', () => { |
|
||||||
expect(isValidMatchStatus(MatchStatuses.Finished)).toBe(true) |
|
||||||
expect(isValidMatchStatus(MatchStatuses.Live)).toBe(true) |
|
||||||
expect(isValidMatchStatus(MatchStatuses.Soon)).toBe(true) |
|
||||||
}) |
|
||||||
|
|
||||||
it('returns false for invalid match statuses', () => { |
|
||||||
expect(isValidMatchStatus(-1)).toBe(false) |
|
||||||
expect(isValidMatchStatus(0)).toBe(false) |
|
||||||
expect(isValidMatchStatus(4)).toBe(false) |
|
||||||
}) |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
import isNumber from 'lodash/isNumber' |
|
||||||
import includes from 'lodash/includes' |
|
||||||
import values from 'lodash/values' |
|
||||||
|
|
||||||
import { MatchStatuses } from '../../hooks' |
|
||||||
|
|
||||||
export const isValidMatchStatus = (value: number | null) => ( |
|
||||||
isNumber(value) && includes(values(MatchStatuses), value) |
|
||||||
) |
|
||||||
@ -1,15 +0,0 @@ |
|||||||
import { SportTypes } from 'config' |
|
||||||
|
|
||||||
import { isValidSportType } from '..' |
|
||||||
|
|
||||||
it('returns true for valid sport types', () => { |
|
||||||
expect(isValidSportType(SportTypes.BASKETBALL)).toBe(true) |
|
||||||
expect(isValidSportType(SportTypes.FOOTBALL)).toBe(true) |
|
||||||
expect(isValidSportType(SportTypes.HOCKEY)).toBe(true) |
|
||||||
}) |
|
||||||
|
|
||||||
it('returns false for invalid sport types', () => { |
|
||||||
expect(isValidSportType(-1)).toBe(false) |
|
||||||
expect(isValidSportType(0)).toBe(false) |
|
||||||
expect(isValidSportType(4)).toBe(false) |
|
||||||
}) |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
import isNumber from 'lodash/isNumber' |
|
||||||
import includes from 'lodash/includes' |
|
||||||
import values from 'lodash/values' |
|
||||||
|
|
||||||
import { SportTypes } from 'config' |
|
||||||
|
|
||||||
export const isValidSportType = (value: number | null) => ( |
|
||||||
isNumber(value) && includes(values(SportTypes), value) |
|
||||||
) |
|
||||||
@ -1,82 +1,19 @@ |
|||||||
import { useCallback } from 'react' |
import { useCallback } from 'react' |
||||||
|
|
||||||
import filter from 'lodash/filter' |
import { getHomeMatches } from 'requests' |
||||||
import isPast from 'date-fns/isPast' |
|
||||||
import differenceInMinutes from 'date-fns/differenceInMinutes' |
|
||||||
|
|
||||||
import { |
import { useHeaderFiltersStore } from 'features/HeaderFilters' |
||||||
getHomeMatches, |
|
||||||
Matches, |
|
||||||
MatchesBySection, |
|
||||||
} from 'requests' |
|
||||||
|
|
||||||
import { MatchStatuses, useHeaderFiltersStore } from 'features/HeaderFilters' |
|
||||||
import { useMatchSwitchesStore } from 'features/MatchSwitches' |
|
||||||
|
|
||||||
const matchesFilteredByStatus = (matches : Matches, status : MatchStatuses | null) => { |
|
||||||
if (!status) return matches |
|
||||||
const filteredMatches = filter(matches, (match) => { |
|
||||||
const matchDate = new Date(match.date) |
|
||||||
const matchIsStarted = isPast(matchDate) |
|
||||||
const difTime = differenceInMinutes(new Date(), matchDate) |
|
||||||
|
|
||||||
switch (status) { |
|
||||||
case MatchStatuses.Soon: |
|
||||||
return !matchIsStarted && (difTime > -60) |
|
||||||
case MatchStatuses.Live: |
|
||||||
return match.live && matchIsStarted |
|
||||||
case MatchStatuses.Finished: |
|
||||||
return matchIsStarted && (match.storage || match.has_video) |
|
||||||
default: return false |
|
||||||
} |
|
||||||
}) |
|
||||||
return filteredMatches |
|
||||||
} |
|
||||||
|
|
||||||
const setMatches = ( |
|
||||||
matches : MatchesBySection, |
|
||||||
status : MatchStatuses | null, |
|
||||||
): MatchesBySection => { |
|
||||||
if (matches.isVideoSections) { |
|
||||||
return { |
|
||||||
...matches, |
|
||||||
broadcast: matchesFilteredByStatus(matches.broadcast, status), |
|
||||||
features: matchesFilteredByStatus(matches.features, status), |
|
||||||
highlights: matchesFilteredByStatus(matches.highlights, status), |
|
||||||
} |
|
||||||
} return { |
|
||||||
...matches, |
|
||||||
broadcast: matchesFilteredByStatus(matches.broadcast, status), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export const useHomePage = () => { |
export const useHomePage = () => { |
||||||
const { |
const { selectedDateFormatted } = useHeaderFiltersStore() |
||||||
selectedDateFormatted, |
|
||||||
selectedMatchStatus, |
|
||||||
selectedSportTypeId, |
|
||||||
selectedTournamentId, |
|
||||||
} = useHeaderFiltersStore() |
|
||||||
const { availableMatchesOnly } = useMatchSwitchesStore() |
|
||||||
|
|
||||||
const fetchMatches = useCallback( |
const fetchMatches = useCallback( |
||||||
(limit: number, offset: number) => getHomeMatches({ |
(limit: number, offset: number) => getHomeMatches({ |
||||||
availableMatchesOnly, |
|
||||||
date: selectedDateFormatted, |
date: selectedDateFormatted, |
||||||
limit, |
limit, |
||||||
matchStatus: null, |
|
||||||
offset, |
offset, |
||||||
sportType: selectedSportTypeId, |
}), |
||||||
tournamentId: selectedTournamentId, |
[selectedDateFormatted], |
||||||
}) |
|
||||||
.then((matches) => setMatches(matches, selectedMatchStatus)), |
|
||||||
[ |
|
||||||
selectedDateFormatted, |
|
||||||
selectedMatchStatus, |
|
||||||
selectedSportTypeId, |
|
||||||
selectedTournamentId, |
|
||||||
availableMatchesOnly, |
|
||||||
], |
|
||||||
) |
) |
||||||
return { fetchMatches } |
return { fetchMatches } |
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,74 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { ClearButton } from 'features/Search/styled' |
||||||
|
import { usePreferencesStore } from 'features/PreferencesPopup/store' |
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
|
||||||
|
const Wrapper = styled.div` |
||||||
|
position: relative; |
||||||
|
width: 100%; |
||||||
|
height: 36px; |
||||||
|
margin-right: 5px; |
||||||
|
background: #292929; |
||||||
|
border-radius: 10px; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
|
||||||
|
::before { |
||||||
|
content: ''; |
||||||
|
display: block; |
||||||
|
position: absolute; |
||||||
|
top: 50%; |
||||||
|
margin-left: 7px; |
||||||
|
transform: translateY(-50%); |
||||||
|
width: 20px; |
||||||
|
height: 20px; |
||||||
|
background-image: url(/images/search.svg); |
||||||
|
background-size: 14px; |
||||||
|
background-repeat: no-repeat; |
||||||
|
background-position: center; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
const Input = styled.input` |
||||||
|
border: none; |
||||||
|
background: transparent; |
||||||
|
outline: none; |
||||||
|
position: relative; |
||||||
|
|
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
display: block; |
||||||
|
margin-bottom: 2px; |
||||||
|
padding-left: 34px; |
||||||
|
color: ${({ theme }) => theme.colors.text100}; |
||||||
|
caret-color: rgba(255, 255, 255, 0.5); |
||||||
|
font-weight: normal; |
||||||
|
font-size: 0.66rem; |
||||||
|
line-height: 1.04rem; |
||||||
|
letter-spacing: -0.4px; |
||||||
|
|
||||||
|
::placeholder { |
||||||
|
color: rgba(255, 255, 255, 0.5); |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const Search = () => { |
||||||
|
const { translate } = useLexicsStore() |
||||||
|
const { |
||||||
|
clearQuery, |
||||||
|
onQueryChange, |
||||||
|
query, |
||||||
|
} = usePreferencesStore() |
||||||
|
return ( |
||||||
|
<Wrapper> |
||||||
|
<Input |
||||||
|
placeholder={translate('search')} |
||||||
|
value={query} |
||||||
|
onChange={onQueryChange} |
||||||
|
/> |
||||||
|
{query && <ClearButton onClick={clearQuery} />} |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
import map from 'lodash/map' |
||||||
|
import includes from 'lodash/includes' |
||||||
|
|
||||||
|
import { usePreferencesStore } from 'features/PreferencesPopup/store' |
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
|
||||||
|
import { BlockTitle } from '../../styled' |
||||||
|
import { |
||||||
|
Wrapper, |
||||||
|
List, |
||||||
|
Item, |
||||||
|
Checkbox, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
export const SportsList = () => { |
||||||
|
const { |
||||||
|
onSportSelect, |
||||||
|
selectedSports, |
||||||
|
sports, |
||||||
|
} = usePreferencesStore() |
||||||
|
return ( |
||||||
|
<Wrapper> |
||||||
|
<BlockTitle> |
||||||
|
<T9n t='sport_types' /> |
||||||
|
</BlockTitle> |
||||||
|
<List> |
||||||
|
{map(sports, (sport) => ( |
||||||
|
<Item key={sport.id}> |
||||||
|
<Checkbox |
||||||
|
checked={includes(selectedSports, sport.id)} |
||||||
|
labelLexic={sport.lexic} |
||||||
|
onChange={() => onSportSelect(sport.id)} |
||||||
|
/> |
||||||
|
</Item> |
||||||
|
))} |
||||||
|
</List> |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { devices } from 'config' |
||||||
|
|
||||||
|
import { Checkbox as BaseCheckbox } from 'features/Common/Checkbox' |
||||||
|
import { Label } from 'features/Common/Checkbox/styled' |
||||||
|
import { CheckboxSvg } from 'features/Common/Checkbox/Icon' |
||||||
|
|
||||||
|
export const Wrapper = styled.div` |
||||||
|
min-width: 264px; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
|
||||||
|
@media ${devices.tablet} { |
||||||
|
min-width: 13rem; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const List = styled.ul` |
||||||
|
width: 100%; |
||||||
|
margin-top: 6px; |
||||||
|
` |
||||||
|
|
||||||
|
export const Item = styled.li` |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
padding: 6px 0; |
||||||
|
` |
||||||
|
|
||||||
|
export const Checkbox = styled(BaseCheckbox)` |
||||||
|
${Label} { |
||||||
|
font-weight: normal; |
||||||
|
font-size: 0.66rem; |
||||||
|
line-height: 1rem; |
||||||
|
} |
||||||
|
${CheckboxSvg} { |
||||||
|
margin-right: 12px; |
||||||
|
} |
||||||
|
` |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import type { Tournament } from 'requests/getSportTournaments' |
||||||
|
|
||||||
|
import { |
||||||
|
ItemInfo, |
||||||
|
Name, |
||||||
|
Flag, |
||||||
|
TeamOrCountry, |
||||||
|
} from 'features/ItemsList/styled' |
||||||
|
import { SportIcon } from 'features/SportIcon' |
||||||
|
|
||||||
|
const Wrapper = styled.div` |
||||||
|
width: 100%; |
||||||
|
max-width: 300px; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
justify-content: center; |
||||||
|
` |
||||||
|
|
||||||
|
type Props = { |
||||||
|
tournament: Tournament, |
||||||
|
} |
||||||
|
|
||||||
|
export const TournamentInfo = ({ tournament }: Props) => ( |
||||||
|
<Wrapper> |
||||||
|
<Name nameObj={tournament} /> |
||||||
|
<ItemInfo> |
||||||
|
<SportIcon sport={tournament.sport} /> |
||||||
|
<Flag src={`https://instatscout.com/images/flags/48/${tournament.country.id}.png`} /> |
||||||
|
<TeamOrCountry nameObj={tournament.country} /> |
||||||
|
</ItemInfo> |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
import type { CSSProperties } from 'react' |
||||||
|
import type { ListChildComponentProps } from 'react-window' |
||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import floor from 'lodash/floor' |
||||||
|
|
||||||
|
import type { Tournament, Tournaments } from 'requests' |
||||||
|
|
||||||
|
import { TournamentInfo } from '../TournamentInfo' |
||||||
|
import { Checkbox } from '../../styled' |
||||||
|
|
||||||
|
const Item = styled.li` |
||||||
|
width: 48.5%; |
||||||
|
padding: 6.5px 0; |
||||||
|
` |
||||||
|
|
||||||
|
const getTwoColumnListStyles = (index: number) => { |
||||||
|
const isEven = index % 2 === 0 |
||||||
|
const rowIndex = floor(index / 2) |
||||||
|
const style: CSSProperties = { |
||||||
|
position: 'absolute', |
||||||
|
top: 48 * rowIndex, |
||||||
|
} |
||||||
|
if (isEven) { |
||||||
|
style.left = 0 |
||||||
|
} else { |
||||||
|
style.right = 0 |
||||||
|
} |
||||||
|
return style |
||||||
|
} |
||||||
|
|
||||||
|
export type ItemData = { |
||||||
|
isTournamentSelected: (tournament: Tournament) => boolean, |
||||||
|
onTournamentSelect: (tournament: Tournament) => void, |
||||||
|
tournaments: Tournaments, |
||||||
|
} |
||||||
|
|
||||||
|
export const TournamentListItem = ({ |
||||||
|
data, |
||||||
|
index, |
||||||
|
}: ListChildComponentProps<ItemData>) => { |
||||||
|
const { |
||||||
|
isTournamentSelected, |
||||||
|
onTournamentSelect, |
||||||
|
tournaments, |
||||||
|
} = data |
||||||
|
const tournament = tournaments[index] |
||||||
|
if (!tournament) return null |
||||||
|
|
||||||
|
return ( |
||||||
|
<Item style={getTwoColumnListStyles(index)}> |
||||||
|
<Checkbox |
||||||
|
checked={isTournamentSelected(tournament)} |
||||||
|
label={<TournamentInfo tournament={tournament} />} |
||||||
|
onChange={() => onTournamentSelect(tournament)} |
||||||
|
/> |
||||||
|
</Item> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
|
||||||
|
import { usePreferencesStore } from '../../store' |
||||||
|
import { Search } from '../Search' |
||||||
|
import { TournamentsList } from '../TournamentsList' |
||||||
|
|
||||||
|
import { BlockTitle, Checkbox } from '../../styled' |
||||||
|
|
||||||
|
const Wrapper = styled.div` |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: flex-start; |
||||||
|
padding-left: 14px; |
||||||
|
flex-grow: 1; |
||||||
|
` |
||||||
|
|
||||||
|
const CheckboxWrapper = styled.div` |
||||||
|
margin: 20px 0 22px 2px; |
||||||
|
margin-top: 20px; |
||||||
|
margin-bottom: 22px; |
||||||
|
` |
||||||
|
|
||||||
|
export const TournamentsBlock = () => { |
||||||
|
const { |
||||||
|
allTournamentsSelected, |
||||||
|
isTournamentSelected, |
||||||
|
onSelectAllTournaments, |
||||||
|
onTournamentSelect, |
||||||
|
tournaments, |
||||||
|
} = usePreferencesStore() |
||||||
|
return ( |
||||||
|
<Wrapper> |
||||||
|
<BlockTitle> |
||||||
|
<T9n t='tournament' /> |
||||||
|
</BlockTitle> |
||||||
|
<CheckboxWrapper> |
||||||
|
<Checkbox |
||||||
|
labelLexic='all' |
||||||
|
checked={allTournamentsSelected} |
||||||
|
onChange={onSelectAllTournaments} |
||||||
|
/> |
||||||
|
</CheckboxWrapper> |
||||||
|
<Search /> |
||||||
|
<TournamentsList |
||||||
|
isTournamentSelected={isTournamentSelected} |
||||||
|
onTournamentSelect={onTournamentSelect} |
||||||
|
tournaments={tournaments} |
||||||
|
/> |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
import { memo } from 'react' |
||||||
|
import type { ListItemKeySelector } from 'react-window' |
||||||
|
import { FixedSizeList } from 'react-window' |
||||||
|
|
||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import size from 'lodash/size' |
||||||
|
|
||||||
|
import { customScrollbar } from 'features/Common' |
||||||
|
|
||||||
|
import type { ItemData } from '../TournamentListItem' |
||||||
|
import { TournamentListItem } from '../TournamentListItem' |
||||||
|
|
||||||
|
const getItemKey: ListItemKeySelector = (index, data) => { |
||||||
|
const tournament = data[index] |
||||||
|
|
||||||
|
return tournament |
||||||
|
? `${tournament.sport}_${tournament.id}` |
||||||
|
: index |
||||||
|
} |
||||||
|
|
||||||
|
type Props = ItemData & { |
||||||
|
className?: string, |
||||||
|
} |
||||||
|
|
||||||
|
const List = memo((props: Props) => { |
||||||
|
const { className, tournaments } = props |
||||||
|
return ( |
||||||
|
<FixedSizeList |
||||||
|
className={className} |
||||||
|
innerElementType='ul' |
||||||
|
width='100%' |
||||||
|
height={432} |
||||||
|
itemSize={24} |
||||||
|
itemCount={size(tournaments)} |
||||||
|
itemKey={getItemKey} |
||||||
|
itemData={props} |
||||||
|
> |
||||||
|
{TournamentListItem} |
||||||
|
</FixedSizeList> |
||||||
|
) |
||||||
|
}) |
||||||
|
|
||||||
|
export const TournamentsList = styled(List)` |
||||||
|
width: 100%; |
||||||
|
height: 432px; |
||||||
|
margin-top: 14px; |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: space-between; |
||||||
|
align-content: flex-start; |
||||||
|
overflow-y: auto; |
||||||
|
|
||||||
|
@media (max-width: 1370px) { |
||||||
|
height: 20rem !important; |
||||||
|
} |
||||||
|
|
||||||
|
${customScrollbar} |
||||||
|
|
||||||
|
::-webkit-scrollbar { |
||||||
|
width: 5px; |
||||||
|
height: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb { |
||||||
|
background: rgba(255, 255, 255, 0.2); |
||||||
|
} |
||||||
|
` |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
import { |
||||||
|
HeaderActions, |
||||||
|
CloseButton, |
||||||
|
} from 'features/PopupComponents' |
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
|
||||||
|
import { SportsList } from './components/SportList' |
||||||
|
import { TournamentsBlock } from './components/TournamentsBlock' |
||||||
|
import { usePreferencesStore } from './store' |
||||||
|
|
||||||
|
import { |
||||||
|
Modal, |
||||||
|
Wrapper, |
||||||
|
Header, |
||||||
|
HeaderTitle, |
||||||
|
Body, |
||||||
|
Footer, |
||||||
|
ApplyButton, |
||||||
|
Loader, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
export * from './store' |
||||||
|
|
||||||
|
export const PreferencesPopup = () => { |
||||||
|
const { |
||||||
|
closePopup, |
||||||
|
isApplyButtonDisabled, |
||||||
|
isFetching, |
||||||
|
isOpen, |
||||||
|
onApplyClick, |
||||||
|
} = usePreferencesStore() |
||||||
|
|
||||||
|
return ( |
||||||
|
<Modal |
||||||
|
isOpen={isOpen} |
||||||
|
close={closePopup} |
||||||
|
withCloseButton={false} |
||||||
|
> |
||||||
|
{isFetching && <Loader />} |
||||||
|
<Wrapper isFetching={isFetching}> |
||||||
|
<Header> |
||||||
|
<HeaderTitle> |
||||||
|
<T9n t='my_preferences' /> |
||||||
|
</HeaderTitle> |
||||||
|
<HeaderActions position='right'> |
||||||
|
<CloseButton onClick={closePopup} /> |
||||||
|
</HeaderActions> |
||||||
|
</Header> |
||||||
|
<Body> |
||||||
|
<SportsList /> |
||||||
|
<TournamentsBlock /> |
||||||
|
</Body> |
||||||
|
<Footer> |
||||||
|
<ApplyButton onClick={onApplyClick} disabled={isApplyButtonDisabled}> |
||||||
|
<T9n t='apply' /> |
||||||
|
</ApplyButton> |
||||||
|
</Footer> |
||||||
|
</Wrapper> |
||||||
|
</Modal> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
import includes from 'lodash/includes' |
||||||
|
import toLower from 'lodash/toLower' |
||||||
|
import reduce from 'lodash/reduce' |
||||||
|
import filter from 'lodash/filter' |
||||||
|
|
||||||
|
import type { SportTypes } from 'config' |
||||||
|
import type { TournamentsBySports, Tournaments } from 'requests/getSportTournaments' |
||||||
|
|
||||||
|
const getFlatTournaments = ( |
||||||
|
tournamentsBySports: TournamentsBySports, |
||||||
|
selectedSports: Array<SportTypes>, |
||||||
|
) => ( |
||||||
|
reduce( |
||||||
|
selectedSports, |
||||||
|
(acc, sport) => { |
||||||
|
const sportTournaments = tournamentsBySports[sport] |
||||||
|
if (sportTournaments) { |
||||||
|
return [...acc, ...sportTournaments] |
||||||
|
} |
||||||
|
return acc |
||||||
|
}, |
||||||
|
[] as Tournaments, |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
type NameKey = 'name_eng' | 'name_rus' |
||||||
|
|
||||||
|
type SearchProps = { |
||||||
|
query: string, |
||||||
|
selectedSports: Array<SportTypes>, |
||||||
|
suffix: string, |
||||||
|
tournamentsBySports: TournamentsBySports, |
||||||
|
} |
||||||
|
|
||||||
|
export const search = async ({ |
||||||
|
query, |
||||||
|
selectedSports, |
||||||
|
suffix, |
||||||
|
tournamentsBySports, |
||||||
|
}: SearchProps) => { |
||||||
|
const tournaments = getFlatTournaments(tournamentsBySports, selectedSports) |
||||||
|
const nameKey = `name_${suffix}` as NameKey |
||||||
|
return filter( |
||||||
|
tournaments, |
||||||
|
(tournament) => includes( |
||||||
|
toLower(tournament[nameKey]), |
||||||
|
toLower(query), |
||||||
|
), |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,124 @@ |
|||||||
|
import { useEffect, useCallback } from 'react' |
||||||
|
|
||||||
|
import isBoolean from 'lodash/isBoolean' |
||||||
|
import uniq from 'lodash/uniq' |
||||||
|
import map from 'lodash/map' |
||||||
|
|
||||||
|
import { useLocalStore, useToggle } from 'hooks' |
||||||
|
|
||||||
|
import { useTournamentsBySports } from './useTournamentsBySports' |
||||||
|
import { usePreferencesLoader } from './usePreferencesLoader' |
||||||
|
import { useSelectedTournaments } from './useTournaments' |
||||||
|
import { useSports } from './useSports' |
||||||
|
import { useSearch } from './useSearch' |
||||||
|
|
||||||
|
export const usePreferences = () => { |
||||||
|
const [firstLoad, setFirstLoad] = useLocalStore({ |
||||||
|
// defaultValue: true,
|
||||||
|
defaultValue: false, |
||||||
|
key: 'preferences_first_load', |
||||||
|
validator: isBoolean, |
||||||
|
}) |
||||||
|
const { |
||||||
|
close, |
||||||
|
isOpen, |
||||||
|
// open,
|
||||||
|
} = useToggle() |
||||||
|
|
||||||
|
const { |
||||||
|
// fetchInitialSports,
|
||||||
|
onSportSelect, |
||||||
|
selectedSports, |
||||||
|
setInitialSelectedSports, |
||||||
|
sports, |
||||||
|
} = useSports() |
||||||
|
|
||||||
|
const { |
||||||
|
isFetching: isFetchingTournaments, |
||||||
|
tournamentsBySports, |
||||||
|
} = useTournamentsBySports(selectedSports) |
||||||
|
|
||||||
|
const { |
||||||
|
clearQuery, |
||||||
|
onQueryChange, |
||||||
|
query, |
||||||
|
tournaments, |
||||||
|
} = useSearch(tournamentsBySports, selectedSports) |
||||||
|
|
||||||
|
const { |
||||||
|
allTournamentsSelected, |
||||||
|
isTournamentSelected, |
||||||
|
onSelectAllTournaments, |
||||||
|
onTournamentSelect, |
||||||
|
selectedTournaments, |
||||||
|
setInitialSelectedTournaments, |
||||||
|
} = useSelectedTournaments(selectedSports, tournaments) |
||||||
|
|
||||||
|
const { |
||||||
|
// fetchInitialPreferences,
|
||||||
|
isApplyButtonDisabled, |
||||||
|
isFetching: isFetchingPrefernces, |
||||||
|
isSaving, |
||||||
|
onApplyClick, |
||||||
|
userPreferences, |
||||||
|
} = usePreferencesLoader(selectedTournaments, close) |
||||||
|
|
||||||
|
const reset = useCallback(() => { |
||||||
|
if (!userPreferences) return |
||||||
|
|
||||||
|
const initialSelectedSports = uniq(map( |
||||||
|
userPreferences, |
||||||
|
({ sport }) => sport, |
||||||
|
)) |
||||||
|
setInitialSelectedTournaments(userPreferences) |
||||||
|
setInitialSelectedSports(initialSelectedSports) |
||||||
|
}, [ |
||||||
|
userPreferences, |
||||||
|
setInitialSelectedTournaments, |
||||||
|
setInitialSelectedSports, |
||||||
|
]) |
||||||
|
|
||||||
|
const openPopup = useCallback(() => { |
||||||
|
// fetchInitialPreferences()
|
||||||
|
// fetchInitialSports()
|
||||||
|
// open()
|
||||||
|
}, [/* fetchInitialPreferences, fetchInitialSports, open */]) |
||||||
|
|
||||||
|
const closePopup = () => { |
||||||
|
reset() |
||||||
|
close() |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(reset, [reset]) |
||||||
|
useEffect(() => { |
||||||
|
if (firstLoad) { |
||||||
|
openPopup() |
||||||
|
setFirstLoad(false) |
||||||
|
} |
||||||
|
}, [ |
||||||
|
firstLoad, |
||||||
|
openPopup, |
||||||
|
setFirstLoad, |
||||||
|
]) |
||||||
|
|
||||||
|
return { |
||||||
|
allTournamentsSelected, |
||||||
|
clearQuery, |
||||||
|
closePopup, |
||||||
|
isApplyButtonDisabled, |
||||||
|
isFetching: isFetchingPrefernces || isSaving || isFetchingTournaments, |
||||||
|
isOpen, |
||||||
|
isTournamentSelected, |
||||||
|
onApplyClick, |
||||||
|
onQueryChange, |
||||||
|
onSelectAllTournaments, |
||||||
|
onSportSelect, |
||||||
|
onTournamentSelect, |
||||||
|
openPopup, |
||||||
|
query, |
||||||
|
selectedSports, |
||||||
|
sports, |
||||||
|
tournaments, |
||||||
|
userPreferences, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
import { |
||||||
|
useCallback, |
||||||
|
useState, |
||||||
|
useMemo, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import isEmpty from 'lodash/isEmpty' |
||||||
|
import isNull from 'lodash/isNull' |
||||||
|
import size from 'lodash/size' |
||||||
|
import xorWith from 'lodash/xorWith' |
||||||
|
import isEqual from 'lodash/isEqual' |
||||||
|
|
||||||
|
import type{ UserPreferences } from 'requests/getUserPreferences' |
||||||
|
import { getUserPreferences as getUserPreferencesRequest } from 'requests/getUserPreferences' |
||||||
|
import { saveUserPreferences as saveUserPreferencesRequest } from 'requests/saveUserPreferences' |
||||||
|
|
||||||
|
import { useRequest } from 'hooks' |
||||||
|
|
||||||
|
export const usePreferencesLoader = ( |
||||||
|
selectedTournaments: UserPreferences, |
||||||
|
onApply: () => void, |
||||||
|
) => { |
||||||
|
const [userPreferences, setUserPreferences] = useState<UserPreferences | null>(null) |
||||||
|
|
||||||
|
const { |
||||||
|
isFetching, |
||||||
|
request: fetchUserPreferences, |
||||||
|
} = useRequest(getUserPreferencesRequest) |
||||||
|
const { |
||||||
|
isFetching: isSaving, |
||||||
|
request: saveUserPreferences, |
||||||
|
} = useRequest(saveUserPreferencesRequest) |
||||||
|
|
||||||
|
const fetchInitialPreferences = useCallback(() => { |
||||||
|
if (isNull(userPreferences)) { |
||||||
|
fetchUserPreferences().then(setUserPreferences) |
||||||
|
} |
||||||
|
}, [fetchUserPreferences, userPreferences]) |
||||||
|
|
||||||
|
const onApplyClick = () => { |
||||||
|
const data = isEmpty(selectedTournaments) ? null : selectedTournaments |
||||||
|
saveUserPreferences(data) |
||||||
|
.then(fetchUserPreferences) |
||||||
|
.then(setUserPreferences) |
||||||
|
.then(onApply) |
||||||
|
} |
||||||
|
|
||||||
|
const isApplyButtonDisabled = useMemo(() => { |
||||||
|
if (!userPreferences) return true |
||||||
|
|
||||||
|
if (size(userPreferences) !== size(selectedTournaments)) return false |
||||||
|
|
||||||
|
return isEmpty(xorWith( |
||||||
|
userPreferences, |
||||||
|
selectedTournaments, |
||||||
|
isEqual, |
||||||
|
)) |
||||||
|
}, [selectedTournaments, userPreferences]) |
||||||
|
|
||||||
|
return { |
||||||
|
fetchInitialPreferences, |
||||||
|
isApplyButtonDisabled, |
||||||
|
isFetching, |
||||||
|
isSaving, |
||||||
|
onApplyClick, |
||||||
|
userPreferences, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
import type { ChangeEvent, MouseEvent } from 'react' |
||||||
|
import { |
||||||
|
useState, |
||||||
|
useEffect, |
||||||
|
useMemo, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import debounce from 'lodash/debounce' |
||||||
|
|
||||||
|
import type { SportTypes } from 'config' |
||||||
|
import type { Tournaments, TournamentsBySports } from 'requests/getSportTournaments' |
||||||
|
|
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
|
||||||
|
import { search } from '../helpers' |
||||||
|
|
||||||
|
type SearchArgs = Parameters<typeof search>[0] |
||||||
|
|
||||||
|
export const useSearch = ( |
||||||
|
tournamentsBySports: TournamentsBySports, |
||||||
|
selectedSports: Array<SportTypes>, |
||||||
|
) => { |
||||||
|
const { suffix } = useLexicsStore() |
||||||
|
|
||||||
|
const [query, setQuery] = useState('') |
||||||
|
const [tournaments, setTournaments] = useState<Tournaments>([]) |
||||||
|
|
||||||
|
const onQueryChange = (e: ChangeEvent<HTMLInputElement>) => { |
||||||
|
setQuery(e.target.value) |
||||||
|
} |
||||||
|
|
||||||
|
const clearQuery = (e: MouseEvent) => { |
||||||
|
e.stopPropagation() |
||||||
|
setQuery('') |
||||||
|
} |
||||||
|
|
||||||
|
const debouncedSearch = useMemo(() => debounce((args: SearchArgs) => { |
||||||
|
search(args).then(setTournaments) |
||||||
|
}, 300), []) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
debouncedSearch({ |
||||||
|
query, |
||||||
|
selectedSports, |
||||||
|
suffix, |
||||||
|
tournamentsBySports, |
||||||
|
}) |
||||||
|
}, [ |
||||||
|
selectedSports, |
||||||
|
query, |
||||||
|
tournamentsBySports, |
||||||
|
suffix, |
||||||
|
debouncedSearch, |
||||||
|
]) |
||||||
|
|
||||||
|
return { |
||||||
|
clearQuery, |
||||||
|
onQueryChange, |
||||||
|
query, |
||||||
|
tournaments, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
import { useCallback, useState } from 'react' |
||||||
|
|
||||||
|
import includes from 'lodash/includes' |
||||||
|
import without from 'lodash/without' |
||||||
|
import isEmpty from 'lodash/isEmpty' |
||||||
|
|
||||||
|
import type { SportTypes } from 'config' |
||||||
|
import type { SportList } from 'requests/getSportList' |
||||||
|
import { getSportList } from 'requests/getSportList' |
||||||
|
|
||||||
|
export const useSports = () => { |
||||||
|
const [sports, setSports] = useState<SportList>([]) |
||||||
|
const [selectedSports, setSelectedSports] = useState<Array<SportTypes>>([]) |
||||||
|
|
||||||
|
const fetchInitialSports = useCallback(() => { |
||||||
|
if (isEmpty(sports)) { |
||||||
|
getSportList().then(setSports) |
||||||
|
} |
||||||
|
}, [sports]) |
||||||
|
|
||||||
|
const onSportSelect = (sport: SportTypes) => { |
||||||
|
const newSports = includes(selectedSports, sport) |
||||||
|
? without(selectedSports, sport) |
||||||
|
: [sport, ...selectedSports] |
||||||
|
setSelectedSports(newSports) |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
fetchInitialSports, |
||||||
|
onSportSelect, |
||||||
|
selectedSports, |
||||||
|
setInitialSelectedSports: setSelectedSports, |
||||||
|
sports, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
import { |
||||||
|
useMemo, |
||||||
|
useState, |
||||||
|
useCallback, |
||||||
|
useEffect, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import includes from 'lodash/includes' |
||||||
|
import isEmpty from 'lodash/isEmpty' |
||||||
|
import filter from 'lodash/filter' |
||||||
|
import remove from 'lodash/remove' |
||||||
|
import every from 'lodash/every' |
||||||
|
import find from 'lodash/find' |
||||||
|
import map from 'lodash/map' |
||||||
|
|
||||||
|
import type { SportTypes } from 'config' |
||||||
|
|
||||||
|
import type { Tournament, Tournaments } from 'requests/getSportTournaments' |
||||||
|
import type { UserPreferences } from 'requests/getUserPreferences' |
||||||
|
|
||||||
|
export const useSelectedTournaments = ( |
||||||
|
selectedSports: Array<SportTypes>, |
||||||
|
tournaments: Tournaments, |
||||||
|
) => { |
||||||
|
const [selectedTournaments, setSelectedTournaments] = useState<UserPreferences>([]) |
||||||
|
|
||||||
|
const isTournamentSelected = useCallback(({ id, sport }: Tournament) => ( |
||||||
|
Boolean(find( |
||||||
|
selectedTournaments, |
||||||
|
{ sport, tournament_id: id }, |
||||||
|
)) |
||||||
|
), [selectedTournaments]) |
||||||
|
|
||||||
|
const onTournamentSelect = useCallback((tournament: Tournament) => { |
||||||
|
const preference = { sport: tournament.sport, tournament_id: tournament.id } |
||||||
|
if (isTournamentSelected(tournament)) { |
||||||
|
remove(selectedTournaments, preference) |
||||||
|
setSelectedTournaments([...selectedTournaments]) |
||||||
|
} else { |
||||||
|
setSelectedTournaments([...selectedTournaments, preference]) |
||||||
|
} |
||||||
|
}, [isTournamentSelected, selectedTournaments]) |
||||||
|
|
||||||
|
const allTournamentsSelected = useMemo( |
||||||
|
() => { |
||||||
|
if (isEmpty(tournaments)) return false |
||||||
|
|
||||||
|
return every(tournaments, isTournamentSelected) |
||||||
|
}, |
||||||
|
[isTournamentSelected, tournaments], |
||||||
|
) |
||||||
|
|
||||||
|
const onSelectAllTournaments = () => { |
||||||
|
const newSelectedTournaments = allTournamentsSelected |
||||||
|
? [] |
||||||
|
: map( |
||||||
|
tournaments, |
||||||
|
({ id, sport }) => ({ sport, tournament_id: id }), |
||||||
|
) |
||||||
|
setSelectedTournaments(newSelectedTournaments) |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setSelectedTournaments((oldTournaments) => ( |
||||||
|
filter(oldTournaments, ({ sport }) => includes(selectedSports, sport)) |
||||||
|
)) |
||||||
|
}, [selectedSports]) |
||||||
|
|
||||||
|
return { |
||||||
|
allTournamentsSelected, |
||||||
|
isTournamentSelected, |
||||||
|
onSelectAllTournaments, |
||||||
|
onTournamentSelect, |
||||||
|
selectedTournaments, |
||||||
|
setInitialSelectedTournaments: setSelectedTournaments, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
import { useState, useEffect } from 'react' |
||||||
|
|
||||||
|
import isEmpty from 'lodash/isEmpty' |
||||||
|
import filter from 'lodash/filter' |
||||||
|
|
||||||
|
import type { SportTypes } from 'config' |
||||||
|
|
||||||
|
import type { TournamentsBySports } from 'requests/getSportTournaments' |
||||||
|
import { getTournamentsBySports } from 'requests/getSportTournaments' |
||||||
|
|
||||||
|
import { useRequest } from 'hooks' |
||||||
|
|
||||||
|
export const useTournamentsBySports = (selectedSports: Array<SportTypes>) => { |
||||||
|
const [tournamentsBySports, setTournamentsBySports] = useState<TournamentsBySports>({}) |
||||||
|
|
||||||
|
const { |
||||||
|
isFetching, |
||||||
|
request: fetchTournaments, |
||||||
|
} = useRequest(getTournamentsBySports) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
const missingTournaments = filter(selectedSports, (sport) => !tournamentsBySports[sport]) |
||||||
|
if (!isEmpty(missingTournaments)) { |
||||||
|
fetchTournaments(missingTournaments).then((newTournaments) => { |
||||||
|
setTournamentsBySports({ |
||||||
|
...tournamentsBySports, |
||||||
|
...newTournaments, |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
}, [selectedSports, tournamentsBySports, fetchTournaments]) |
||||||
|
|
||||||
|
return { |
||||||
|
isFetching, |
||||||
|
tournamentsBySports, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
import type { ReactNode } from 'react' |
||||||
|
import { createContext, useContext } from 'react' |
||||||
|
|
||||||
|
import { usePreferences } from './hooks' |
||||||
|
|
||||||
|
type Context = ReturnType<typeof usePreferences> |
||||||
|
type Props = { children: ReactNode } |
||||||
|
|
||||||
|
const PreferencesPopupContext = createContext({} as Context) |
||||||
|
|
||||||
|
export const PreferencesPopupStore = ({ children }: Props) => { |
||||||
|
const value = usePreferences() |
||||||
|
return ( |
||||||
|
<PreferencesPopupContext.Provider value={value}> |
||||||
|
{children} |
||||||
|
</PreferencesPopupContext.Provider> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export const usePreferencesStore = () => useContext(PreferencesPopupContext) |
||||||
@ -0,0 +1,106 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { ModalWindow } from 'features/Modal/styled' |
||||||
|
import { Modal as BaseModal } from 'features/Modal' |
||||||
|
import { Header as BaseHeader } from 'features/PopupComponents' |
||||||
|
|
||||||
|
import { ButtonSolid } from 'features/Common' |
||||||
|
import { Checkbox as BaseCheckbox } from 'features/Common/Checkbox' |
||||||
|
import { Label } from 'features/Common/Checkbox/styled' |
||||||
|
import { CheckboxSvg } from 'features/Common/Checkbox/Icon' |
||||||
|
import { ArrowLoader } from 'features/ArrowLoader' |
||||||
|
|
||||||
|
export const Modal = styled(BaseModal)` |
||||||
|
background-color: rgba(0, 0, 0, 0.7); |
||||||
|
padding: 0 60px; |
||||||
|
|
||||||
|
${ModalWindow} { |
||||||
|
width: 1066px; |
||||||
|
height: 781px; |
||||||
|
padding: 0; |
||||||
|
background-color: #333333; |
||||||
|
border-radius: 5px; |
||||||
|
|
||||||
|
@media (max-width: 1370px) { |
||||||
|
width: 70rem; |
||||||
|
height: auto; |
||||||
|
} |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
type WrapperProps = { |
||||||
|
isFetching?: boolean, |
||||||
|
} |
||||||
|
|
||||||
|
export const Wrapper = styled.div<WrapperProps>` |
||||||
|
${({ isFetching }) => ( |
||||||
|
isFetching |
||||||
|
? css`pointer-events: none;` |
||||||
|
: '' |
||||||
|
)} |
||||||
|
` |
||||||
|
|
||||||
|
export const Header = styled(BaseHeader)` |
||||||
|
height: auto; |
||||||
|
padding-top: 40px; |
||||||
|
justify-content: center; |
||||||
|
` |
||||||
|
|
||||||
|
export const HeaderTitle = styled.span` |
||||||
|
font-weight: 600; |
||||||
|
font-size: 1.13rem; |
||||||
|
line-height: 1.13rem; |
||||||
|
color: #FFFFFF; |
||||||
|
` |
||||||
|
|
||||||
|
export const BlockTitle = styled.span` |
||||||
|
font-weight: 600; |
||||||
|
font-size: 0.56rem; |
||||||
|
line-height: 0.85rem; |
||||||
|
text-transform: uppercase; |
||||||
|
color: rgba(255, 255, 255, 0.5); |
||||||
|
` |
||||||
|
|
||||||
|
export const Body = styled.div` |
||||||
|
padding: 25px 40px 0 42px; |
||||||
|
display: flex; |
||||||
|
` |
||||||
|
|
||||||
|
export const Footer = styled.div` |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
padding: 1.89rem; |
||||||
|
` |
||||||
|
|
||||||
|
export const Checkbox = styled(BaseCheckbox)` |
||||||
|
${Label} { |
||||||
|
font-weight: 600; |
||||||
|
font-size: 0.66rem; |
||||||
|
line-height: 0.94rem; |
||||||
|
letter-spacing: 0.1px; |
||||||
|
} |
||||||
|
${CheckboxSvg} { |
||||||
|
align-self: auto; |
||||||
|
margin-right: 18px; |
||||||
|
max-width: 20px; |
||||||
|
max-height: 20px; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const ApplyButton = styled(ButtonSolid)` |
||||||
|
width: 270px; |
||||||
|
border-radius: 5px; |
||||||
|
` |
||||||
|
|
||||||
|
export const Loader = styled(ArrowLoader)` |
||||||
|
position: absolute; |
||||||
|
z-index: 10; |
||||||
|
background-color: #3f3f3f60; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
pointer-events: none; |
||||||
|
` |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
import { |
||||||
|
DATA_URL, |
||||||
|
PROCEDURES, |
||||||
|
SportTypes, |
||||||
|
} from 'config' |
||||||
|
import { callApi } from 'helpers' |
||||||
|
|
||||||
|
const proc = PROCEDURES.get_user_preferences |
||||||
|
|
||||||
|
export type UserPreferences = Array<{ |
||||||
|
sport: SportTypes, |
||||||
|
tournament_id: number, |
||||||
|
}> |
||||||
|
|
||||||
|
export const getUserPreferences = async () => { |
||||||
|
const config = { |
||||||
|
body: { |
||||||
|
params: {}, |
||||||
|
proc, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
const response: UserPreferences | null = await callApi({ |
||||||
|
config, |
||||||
|
url: DATA_URL, |
||||||
|
}) |
||||||
|
|
||||||
|
return response || [] |
||||||
|
} |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
import { |
||||||
|
DATA_URL, |
||||||
|
PROCEDURES, |
||||||
|
} from 'config' |
||||||
|
import { callApi } from 'helpers' |
||||||
|
|
||||||
|
import type { UserPreferences } from './getUserPreferences' |
||||||
|
|
||||||
|
const proc = PROCEDURES.save_user_preferences |
||||||
|
|
||||||
|
export const saveUserPreferences = (data: UserPreferences | null) => { |
||||||
|
const config = { |
||||||
|
body: { |
||||||
|
params: { |
||||||
|
_p_data: data, |
||||||
|
}, |
||||||
|
proc, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
return callApi({ |
||||||
|
config, |
||||||
|
url: DATA_URL, |
||||||
|
}) |
||||||
|
} |
||||||
Loading…
Reference in new issue