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