feat(ott-144): added user fav sport items (#51)
parent
e29b1b3889
commit
455157c361
|
After Width: | Height: | Size: 696 B |
|
After Width: | Height: | Size: 240 B |
@ -0,0 +1,27 @@ |
|||||||
|
import { SPORT_COLORS } from 'config' |
||||||
|
|
||||||
|
export const getSportName = (sport: number, suffix: string): string => { |
||||||
|
switch (sport) { |
||||||
|
case 1: |
||||||
|
return suffix === 'eng' ? 'Football' : 'Футбол' |
||||||
|
case 2: |
||||||
|
return suffix === 'eng' ? 'Hockey' : 'Хоккей' |
||||||
|
case 3: |
||||||
|
return suffix === 'eng' ? 'Basketball' : 'Баскетбол' |
||||||
|
default: |
||||||
|
return '' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const getSportColor = (sport: number): string => { |
||||||
|
switch (sport) { |
||||||
|
case 1: |
||||||
|
return SPORT_COLORS.football |
||||||
|
case 2: |
||||||
|
return SPORT_COLORS.hockey |
||||||
|
case 3: |
||||||
|
return SPORT_COLORS.basketball |
||||||
|
default: |
||||||
|
return '' |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
import React from 'react' |
||||||
|
|
||||||
|
import { |
||||||
|
getSportName, |
||||||
|
getSportColor, |
||||||
|
} from './hooks' |
||||||
|
import { |
||||||
|
TooltipBlockWrapper, |
||||||
|
TooltipBlockItem, |
||||||
|
TooltipBlockItemThinUpperCase, |
||||||
|
TooltipBlockItemThin, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
type TTooltipBlock = { |
||||||
|
countryName?: string, |
||||||
|
date?: string, |
||||||
|
playerFirstName?: string, |
||||||
|
playerLastName?: string, |
||||||
|
playerTeamName?: string, |
||||||
|
sport: number, |
||||||
|
suffix: string, |
||||||
|
teamName?: string, |
||||||
|
} |
||||||
|
|
||||||
|
export const TooltipBlock = ({ |
||||||
|
countryName, |
||||||
|
playerFirstName, |
||||||
|
playerLastName, |
||||||
|
playerTeamName, |
||||||
|
sport, |
||||||
|
suffix, |
||||||
|
teamName, |
||||||
|
}: TTooltipBlock) => ( |
||||||
|
<TooltipBlockWrapper> |
||||||
|
<TooltipBlockItem> |
||||||
|
{playerFirstName && playerLastName && `${playerFirstName} ${playerLastName}`} |
||||||
|
</TooltipBlockItem> |
||||||
|
<TooltipBlockItem> |
||||||
|
{teamName} |
||||||
|
</TooltipBlockItem> |
||||||
|
<TooltipBlockItemThin> |
||||||
|
<TooltipBlockItemThinUpperCase color={getSportColor(sport)}> |
||||||
|
{getSportName(sport, suffix)} |
||||||
|
</TooltipBlockItemThinUpperCase>{' '} |
||||||
|
{playerTeamName || countryName} |
||||||
|
</TooltipBlockItemThin> |
||||||
|
</TooltipBlockWrapper> |
||||||
|
) |
||||||
@ -0,0 +1,47 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
export const TooltipBlockWrapper = styled.div` |
||||||
|
background-color: #fff; |
||||||
|
border-radius: 10px; |
||||||
|
padding: 12px; |
||||||
|
white-space: nowrap; |
||||||
|
&::before { |
||||||
|
position: absolute; |
||||||
|
top: -8px; |
||||||
|
content: ''; |
||||||
|
border-bottom: 8px solid #fff; |
||||||
|
border-left: 10px solid transparent; |
||||||
|
border-right: 10px solid transparent; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export type TTooltipProps = { |
||||||
|
color?: string, |
||||||
|
} |
||||||
|
|
||||||
|
export const TooltipStyles = css<TTooltipProps>` |
||||||
|
display: block; |
||||||
|
font-family: Montserrat, Tahoma, sans-serif; |
||||||
|
font-size: 14px; |
||||||
|
line-height: 18px; |
||||||
|
color: ${({ color }) => (color ? `${color}` : '#2c2d2e')}; |
||||||
|
font-weight: 600; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
background-color: rgba(255, 255, 255, 0.7); |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const TooltipBlockItem = styled.span` |
||||||
|
${TooltipStyles} |
||||||
|
` |
||||||
|
|
||||||
|
export const TooltipBlockItemThin = styled(TooltipBlockItem)` |
||||||
|
font-weight: 400; |
||||||
|
` |
||||||
|
|
||||||
|
export const TooltipBlockItemThinUpperCase = styled(TooltipBlockItem)` |
||||||
|
display: inline; |
||||||
|
text-transform: uppercase; |
||||||
|
font-weight: 400; |
||||||
|
` |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
import { |
||||||
|
useEffect, |
||||||
|
useState, |
||||||
|
useMemo, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import map from 'lodash/map' |
||||||
|
|
||||||
|
import { |
||||||
|
LOGOS_URLS, |
||||||
|
FAV_SPORT_URLS, |
||||||
|
} from 'config' |
||||||
|
import { |
||||||
|
SPORT_TYPES, |
||||||
|
DATA_TYPES, |
||||||
|
} from 'helpers' |
||||||
|
import { |
||||||
|
modifyUserSportFavs, |
||||||
|
ModifyUserSportFavsArgs, |
||||||
|
getUserSportFavs, |
||||||
|
UserSportFavItem, |
||||||
|
} from 'requests' |
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
|
||||||
|
type TNames = 'name_eng' | 'name_rus' |
||||||
|
type TShortNames = 'short_name_eng' | 'short_name_rus' |
||||||
|
type TFirstNames = 'firstname_eng' | 'firstname_rus' |
||||||
|
type TLastNames = 'lastname_eng' | 'firstname_rus' |
||||||
|
type TNickNames = 'nickname_eng' | 'nickname_rus' |
||||||
|
|
||||||
|
export type TMemoizedUserSportFavItem = { |
||||||
|
countryName?: string, |
||||||
|
firstname?: string, |
||||||
|
id: number, |
||||||
|
lastname?: string, |
||||||
|
name?: string, |
||||||
|
nickname?: string, |
||||||
|
shortName?: string, |
||||||
|
sport: number, |
||||||
|
teamName?: string, |
||||||
|
type: number, |
||||||
|
} |
||||||
|
|
||||||
|
type TMakeUrlArgs = { |
||||||
|
id: number, |
||||||
|
sport: number, |
||||||
|
type: number, |
||||||
|
} |
||||||
|
|
||||||
|
export const useUserSportFavs = () => { |
||||||
|
const { suffix } = useLexicsStore() |
||||||
|
const [userSportFavItems, setUserSportFavItems] = useState<Array<UserSportFavItem>>([]) |
||||||
|
|
||||||
|
const nameField = `name_${suffix}` as TNames |
||||||
|
const shortNameField = `short_name_${suffix}` as TShortNames |
||||||
|
const firtsNameField = `firstname_${suffix}` as TFirstNames |
||||||
|
const lastNameField = `lastname_${suffix}` as TLastNames |
||||||
|
const nickNameField = `nickname_${suffix}` as TNickNames |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getUserSportFavs().then(setUserSportFavItems) |
||||||
|
}, []) |
||||||
|
|
||||||
|
const addRemoveSportFav = ({ |
||||||
|
action, |
||||||
|
id, |
||||||
|
sport, |
||||||
|
type, |
||||||
|
}: ModifyUserSportFavsArgs) => { |
||||||
|
// при добавлении дубликата userSportFavItem back возвращает {}
|
||||||
|
modifyUserSportFavs({ |
||||||
|
action, |
||||||
|
id, |
||||||
|
sport, |
||||||
|
type, |
||||||
|
}).then((userSportFavs) => Array.isArray(userSportFavs) && setUserSportFavItems(userSportFavs)) |
||||||
|
} |
||||||
|
|
||||||
|
const memoizedUserSportFavItems = useMemo(() => map(userSportFavItems, (item) => ({ |
||||||
|
countryName: item.info.country?.[nameField], |
||||||
|
firstname: item.info[firtsNameField], |
||||||
|
id: item.id, |
||||||
|
lastname: item.info[lastNameField], |
||||||
|
name: item.info[nameField], |
||||||
|
nickname: item.info[nickNameField], |
||||||
|
shortName: item.info[shortNameField], |
||||||
|
sport: item.sport, |
||||||
|
teamName: item.info.team?.[nameField], |
||||||
|
type: item.type, |
||||||
|
})), |
||||||
|
[ |
||||||
|
firtsNameField, |
||||||
|
lastNameField, |
||||||
|
userSportFavItems, |
||||||
|
nameField, |
||||||
|
nickNameField, |
||||||
|
shortNameField, |
||||||
|
]) |
||||||
|
|
||||||
|
return { |
||||||
|
addRemoveSportFav, |
||||||
|
userSportFavItems: memoizedUserSportFavItems, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const makePicUrl = (arg: TMakeUrlArgs) => ( |
||||||
|
// @ts-expect-error
|
||||||
|
`${LOGOS_URLS[SPORT_TYPES[arg.sport]][DATA_TYPES[arg.type]]}/${arg.id}.png` |
||||||
|
) |
||||||
|
|
||||||
|
export const makeProfileUrl = (arg: TMakeUrlArgs) => ( |
||||||
|
// @ts-expect-error
|
||||||
|
`${FAV_SPORT_URLS[SPORT_TYPES[arg.sport]][DATA_TYPES[arg.type]]}/${arg.id}` |
||||||
|
) |
||||||
|
|
||||||
|
export const userSportFavs = ( |
||||||
|
userSportFavItems: Array<TMemoizedUserSportFavItem>, |
||||||
|
) => userSportFavItems?.length > 0 && map( |
||||||
|
userSportFavItems, (fav) => ({ |
||||||
|
...fav, |
||||||
|
pic_url: makePicUrl({ |
||||||
|
id: fav.id, |
||||||
|
sport: fav.sport, |
||||||
|
type: fav.type, |
||||||
|
}), |
||||||
|
profile_url: makeProfileUrl({ |
||||||
|
id: fav.id, |
||||||
|
sport: fav.sport, |
||||||
|
type: fav.type, |
||||||
|
}), |
||||||
|
}), |
||||||
|
) |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
import React from 'react' |
||||||
|
|
||||||
|
import map from 'lodash/map' |
||||||
|
|
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
import { handleImageError } from 'helpers' |
||||||
|
|
||||||
|
import { |
||||||
|
useUserSportFavs, |
||||||
|
userSportFavs, |
||||||
|
} from './hooks' |
||||||
|
import { TooltipBlock } from './TooltipBlock' |
||||||
|
import { |
||||||
|
StyledLink, |
||||||
|
UserSportFavItemLogoWrapper, |
||||||
|
UserSportFavXWrapper, |
||||||
|
UserSportFavImgWrapper, |
||||||
|
UserSportFavStar, |
||||||
|
UserSportFavLogoWrapper, |
||||||
|
UserSportFavWrapper, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
export const UserSportFav = () => { |
||||||
|
const { addRemoveSportFav, userSportFavItems } = useUserSportFavs() |
||||||
|
|
||||||
|
const { suffix } = useLexicsStore() |
||||||
|
|
||||||
|
const userSportFavList = userSportFavs(userSportFavItems) |
||||||
|
|
||||||
|
return ( |
||||||
|
<UserSportFavWrapper> |
||||||
|
<UserSportFavLogoWrapper height={12} width={52} /> |
||||||
|
<UserSportFavStar /> |
||||||
|
{userSportFavList && map(userSportFavList, (item) => ( |
||||||
|
<UserSportFavItemLogoWrapper key={`${item.type}_${item.sport}_${item.id}`}> |
||||||
|
<UserSportFavXWrapper |
||||||
|
onClick={() => addRemoveSportFav({ |
||||||
|
action: 2, |
||||||
|
id: item.id, |
||||||
|
sport: item.sport, |
||||||
|
type: item.type, |
||||||
|
})} |
||||||
|
/> |
||||||
|
<TooltipBlock |
||||||
|
countryName={item.countryName} |
||||||
|
teamName={item.name} |
||||||
|
playerTeamName={item.teamName} |
||||||
|
playerFirstName={item.firstname} |
||||||
|
playerLastName={item.lastname} |
||||||
|
sport={item.sport} |
||||||
|
suffix={suffix} |
||||||
|
/> |
||||||
|
<StyledLink to={item.profile_url} target='_blank'> |
||||||
|
<UserSportFavImgWrapper |
||||||
|
src={item.pic_url} |
||||||
|
alt={item.name} |
||||||
|
onError={(e) => handleImageError({ |
||||||
|
e, |
||||||
|
sport: item.sport, |
||||||
|
type: item.type, |
||||||
|
})} |
||||||
|
/> |
||||||
|
</StyledLink> |
||||||
|
</UserSportFavItemLogoWrapper> |
||||||
|
))} |
||||||
|
</UserSportFavWrapper> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,80 @@ |
|||||||
|
import { Link } from 'react-router-dom' |
||||||
|
|
||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { Logo } from 'features/Logo' |
||||||
|
import { TooltipBlockWrapper } from './TooltipBlock/styled' |
||||||
|
|
||||||
|
export const StyledLink = styled(Link)`` |
||||||
|
|
||||||
|
export const UserSportFavWrapper = styled.div` |
||||||
|
width: 80px; |
||||||
|
display: flex; |
||||||
|
flex: 0 0 auto; |
||||||
|
flex-direction:column; |
||||||
|
align-items: center; |
||||||
|
background: rgba(255, 255, 255, 0.1); |
||||||
|
` |
||||||
|
|
||||||
|
export const UserSportFavLogoWrapper = styled(Logo)` |
||||||
|
margin-top: 35px; |
||||||
|
margin-bottom: 120px; |
||||||
|
` |
||||||
|
|
||||||
|
export const UserSportFavXWrapper = styled.span` |
||||||
|
display: block; |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
right: 0; |
||||||
|
background: transparent url('/images/xIcon.png') no-repeat center; |
||||||
|
height: 11px; |
||||||
|
width: 11px; |
||||||
|
border: none; |
||||||
|
` |
||||||
|
|
||||||
|
export const UserSportFavItemLogoWrapper = styled.div` |
||||||
|
position: relative; |
||||||
|
width: 48px; |
||||||
|
height: 48px; |
||||||
|
border-radius: 50%; |
||||||
|
padding: 5px; |
||||||
|
background-color: #fff; |
||||||
|
margin-bottom: 16px; |
||||||
|
|
||||||
|
${UserSportFavXWrapper} { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
${TooltipBlockWrapper} { |
||||||
|
display: none; |
||||||
|
position: absolute; |
||||||
|
left: 40%; |
||||||
|
top: 120%; |
||||||
|
z-index: 2; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover { |
||||||
|
background-color: rgba(255, 255, 255, 0.7); |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
${UserSportFavXWrapper} { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
${TooltipBlockWrapper} { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const UserSportFavImgWrapper = styled.img` |
||||||
|
width: 100%; |
||||||
|
` |
||||||
|
|
||||||
|
export const UserSportFavStar = styled.div` |
||||||
|
width: 48px; |
||||||
|
height: 48px; |
||||||
|
border-radius: 50%; |
||||||
|
background: #3f3f3f url('/images/sportFavStar.png') no-repeat center; |
||||||
|
margin-bottom: 16px; |
||||||
|
` |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
import { BaseSyntheticEvent } from 'react' |
||||||
|
|
||||||
|
import { LOGOS_FALLBACKS } from 'config' |
||||||
|
|
||||||
|
export type imageErrorArgs = { |
||||||
|
e: BaseSyntheticEvent, |
||||||
|
sport: number, |
||||||
|
type: number, |
||||||
|
} |
||||||
|
|
||||||
|
export const SPORT_TYPES = { |
||||||
|
1: 'football', |
||||||
|
2: 'hockey', |
||||||
|
3: 'basketball', |
||||||
|
} as const |
||||||
|
|
||||||
|
export const DATA_TYPES = { |
||||||
|
1: 'tournaments', |
||||||
|
2: 'teams', |
||||||
|
3: 'players', |
||||||
|
} as const |
||||||
|
|
||||||
|
export const handleImageError = (arg: imageErrorArgs): void => { |
||||||
|
arg.e.target.onError = '' |
||||||
|
// @ts-expect-error
|
||||||
|
arg.e.target.src = LOGOS_FALLBACKS[SPORT_TYPES[arg.sport]][DATA_TYPES[arg.type]] |
||||||
|
} |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
import { DATA_URL, PROCEDURES } from 'config' |
||||||
|
import { callApi, getResponseData } from 'helpers' |
||||||
|
|
||||||
|
const proc = PROCEDURES.get_user_favorites |
||||||
|
|
||||||
|
export type UserSportFavItem = { |
||||||
|
id: number, |
||||||
|
info: { |
||||||
|
country?: { |
||||||
|
name_eng: string, |
||||||
|
name_rus: string, |
||||||
|
}, |
||||||
|
date?: string, |
||||||
|
firstname_eng?: string, |
||||||
|
firstname_rus?: string, |
||||||
|
lastname_eng?: string, |
||||||
|
lastname_rus?: string, |
||||||
|
name_eng?: string, |
||||||
|
name_rus?: string, |
||||||
|
nickname_eng?: string, |
||||||
|
nickname_rus?: string, |
||||||
|
short_name_eng?: string, |
||||||
|
short_name_rus?: string, |
||||||
|
team?: { |
||||||
|
name_eng: string, |
||||||
|
name_rus: string, |
||||||
|
}, |
||||||
|
team1?: { |
||||||
|
name_eng: string, |
||||||
|
name_rus: string, |
||||||
|
}, |
||||||
|
team2?: { |
||||||
|
name_eng: string, |
||||||
|
name_rus: string, |
||||||
|
}, |
||||||
|
}, |
||||||
|
sport: number, |
||||||
|
type: number, |
||||||
|
} |
||||||
|
|
||||||
|
export const getUserSportFavs = (): Promise<Array<UserSportFavItem>> => { |
||||||
|
const config = { |
||||||
|
body: { |
||||||
|
params: {}, |
||||||
|
proc, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
return callApi({ |
||||||
|
config, |
||||||
|
url: DATA_URL, |
||||||
|
}).then(getResponseData(proc)) |
||||||
|
} |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
import { DATA_URL, PROCEDURES } from 'config' |
||||||
|
import { callApi, getResponseData } from 'helpers' |
||||||
|
import { UserSportFavItem } from './getUserSportFavs' |
||||||
|
|
||||||
|
const proc = PROCEDURES.save_user_favorite |
||||||
|
|
||||||
|
export type ModifyUserSportFavsArgs = { |
||||||
|
action: number, |
||||||
|
id: number, |
||||||
|
sport: number, |
||||||
|
type: number, |
||||||
|
} |
||||||
|
|
||||||
|
// при добавлении дубликата userSportFavItem back возвращает {}
|
||||||
|
export const modifyUserSportFavs = ({ |
||||||
|
action, |
||||||
|
id, |
||||||
|
sport, |
||||||
|
type, |
||||||
|
}: ModifyUserSportFavsArgs): Promise<Array<UserSportFavItem> | {}> => { |
||||||
|
const config = { |
||||||
|
body: { |
||||||
|
params: { |
||||||
|
_p_action: action, |
||||||
|
_p_id: id, |
||||||
|
_p_sport: sport, |
||||||
|
_p_type: type, |
||||||
|
}, |
||||||
|
proc, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
return callApi({ |
||||||
|
config, |
||||||
|
url: DATA_URL, |
||||||
|
}).then(getResponseData(proc)) |
||||||
|
} |
||||||
Loading…
Reference in new issue