feat(ott-191): player page header (#76)
parent
3e90223c5d
commit
22d93b845c
@ -0,0 +1,7 @@ |
||||
import { indexLexics } from './indexLexics' |
||||
|
||||
export const homeLexics = { |
||||
...indexLexics, |
||||
|
||||
video_from_my_subscriptions: 13023, |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
import { indexLexics } from './indexLexics' |
||||
|
||||
export const playerLexics = { |
||||
...indexLexics, |
||||
|
||||
add_to_favorites: 1701, |
||||
added_to_favorites: 13048, |
||||
cm: 817, |
||||
kg: 652, |
||||
} |
||||
@ -0,0 +1,24 @@ |
||||
import React from 'react' |
||||
|
||||
type StarProps = { |
||||
fill: string, |
||||
} |
||||
|
||||
export const StarIcon = ({ fill }: StarProps) => ( |
||||
<svg width='20' height='19' viewBox='0 0 20 19' xmlns='http://www.w3.org/2000/svg'> |
||||
<g filter='url(#filter0_d)'> |
||||
<path d='M10 0L12.5814 5.44704L18.5595 6.21885L14.1767 10.3571L15.2901 16.2812L10 13.3917L4.70993 16.2812L5.82325 10.3571L1.44049 6.21885L7.41863 5.44704L10 0Z' fill={fill} /> |
||||
</g> |
||||
<defs> |
||||
<filter id='filter0_d' x='0.44043' y='0' width='19.119' height='18.2812' filterUnits='userSpaceOnUse' colorInterpolationFilters='sRGB'> |
||||
<feFlood floodOpacity='0' result='BackgroundImageFix' /> |
||||
<feColorMatrix in='SourceAlpha' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' /> |
||||
<feOffset dy='1' /> |
||||
<feGaussianBlur stdDeviation='0.5' /> |
||||
<feColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0' /> |
||||
<feBlend mode='normal' in2='BackgroundImageFix' result='effect1_dropShadow' /> |
||||
<feBlend mode='normal' in='SourceGraphic' in2='effect1_dropShadow' result='shape' /> |
||||
</filter> |
||||
</defs> |
||||
</svg> |
||||
) |
||||
@ -0,0 +1,44 @@ |
||||
import { useEffect, useState } from 'react' |
||||
|
||||
import type { PlayerProfile } from 'requests/getPlayerInfo' |
||||
import { getPlayerInfo } from 'requests/getPlayerInfo' |
||||
import { useSportNameParam, usePageId } from 'hooks' |
||||
import { useLexicsStore } from 'features/LexicsStore' |
||||
|
||||
type Firstname = 'firstname_eng' | 'firstname_rus' |
||||
type Lastname = 'lastname_eng' | 'lastname_rus' |
||||
type Name = 'name_eng' | 'name_rus' |
||||
|
||||
export const usePlayerPage = () => { |
||||
const [playerProfile, setPlayerProfile] = useState<PlayerProfile>(null) |
||||
const { sportType } = useSportNameParam() |
||||
const pageId = usePageId() |
||||
const { suffix, translate } = useLexicsStore() |
||||
|
||||
const { |
||||
club_team, |
||||
height, |
||||
weight, |
||||
[`firstname_${suffix}` as Firstname]: firstName = '', |
||||
[`lastname_${suffix}` as Lastname]: lastName = '', |
||||
} = playerProfile || {} |
||||
|
||||
const fullName = `${firstName} ${lastName}` |
||||
const teamName = club_team?.[`name_${suffix}` as Name] || '' |
||||
|
||||
const infoItems = [ |
||||
`${height ? `${height} ${translate('cm')}` : ''}${ |
||||
weight ? `, ${weight} ${translate('kg')}` : ''}`,
|
||||
teamName, |
||||
] |
||||
|
||||
useEffect(() => { |
||||
getPlayerInfo(pageId, sportType).then(setPlayerProfile) |
||||
}, [pageId, sportType]) |
||||
|
||||
return { |
||||
infoItems, |
||||
name: fullName, |
||||
sportType, |
||||
} |
||||
} |
||||
@ -1,12 +1,39 @@ |
||||
import React from 'react' |
||||
import styled from 'styled-components' |
||||
|
||||
const TempPageTitle = styled.span` |
||||
padding: 20px; |
||||
font-size: 20px; |
||||
color: white; |
||||
` |
||||
import { ProfileTypes } from 'config' |
||||
import { playerLexics } from 'config/lexics/playerLexics' |
||||
import { useLexicsConfig } from 'features/LexicsStore' |
||||
import { Header } from 'features/Header' |
||||
import { MainWrapper } from 'features/MainWrapper' |
||||
import { Search } from 'features/Search' |
||||
import { UserFavorites } from 'features/UserFavorites' |
||||
import { ProfileCard } from 'features/ProfileCard' |
||||
import { UserFavoritesStore } from 'features/UserFavorites/store' |
||||
|
||||
export const PlayerPage = () => ( |
||||
<TempPageTitle>PLAYER PAGE</TempPageTitle> |
||||
) |
||||
import { usePlayerPage } from './hooks' |
||||
|
||||
export const PlayerPage = () => { |
||||
const { |
||||
infoItems, |
||||
name, |
||||
sportType, |
||||
} = usePlayerPage() |
||||
useLexicsConfig(playerLexics) |
||||
|
||||
return ( |
||||
<UserFavoritesStore> |
||||
<MainWrapper> |
||||
<UserFavorites /> |
||||
<Header> |
||||
<ProfileCard |
||||
sportType={sportType} |
||||
profileType={ProfileTypes.PLAYERS} |
||||
name={name} |
||||
infoItems={infoItems} |
||||
/> |
||||
<Search /> |
||||
</Header> |
||||
</MainWrapper> |
||||
</UserFavoritesStore> |
||||
) |
||||
} |
||||
|
||||
@ -0,0 +1,57 @@ |
||||
import type { BaseSyntheticEvent } from 'react' |
||||
import { useCallback } from 'react' |
||||
|
||||
import findIndex from 'lodash/findIndex' |
||||
|
||||
import { handleImageError, getProfileLogo } from 'helpers' |
||||
import { useUserFavoritesStore } from 'features/UserFavorites/store' |
||||
import { usePageId } from 'hooks' |
||||
|
||||
import type { ProfileCardProps } from './types' |
||||
|
||||
export const useProfileCard = ({ |
||||
profileType, |
||||
sportType, |
||||
}: ProfileCardProps) => { |
||||
const { addRemoveFavorite, userFavorites } = useUserFavoritesStore() |
||||
const profileId = usePageId() |
||||
|
||||
const isFavorite = findIndex(userFavorites, { |
||||
id: profileId, |
||||
sport: sportType, |
||||
type: profileType, |
||||
}) !== -1 |
||||
|
||||
const logo = getProfileLogo({ |
||||
id: profileId, |
||||
profileType, |
||||
sportType, |
||||
}) |
||||
|
||||
const addToFavorites = useCallback(() => { |
||||
addRemoveFavorite({ |
||||
action: 1, |
||||
id: profileId, |
||||
sport: sportType, |
||||
type: profileType, |
||||
}) |
||||
}, [ |
||||
addRemoveFavorite, |
||||
profileId, |
||||
profileType, |
||||
sportType, |
||||
]) |
||||
|
||||
const onError = useCallback((e: BaseSyntheticEvent) => handleImageError({ |
||||
e, |
||||
sport: sportType, |
||||
type: profileType, |
||||
}), [profileType, sportType]) |
||||
|
||||
return { |
||||
addToFavorites, |
||||
isFavorite, |
||||
logo, |
||||
onError, |
||||
} |
||||
} |
||||
@ -0,0 +1,63 @@ |
||||
import React from 'react' |
||||
|
||||
import map from 'lodash/map' |
||||
|
||||
import { T9n } from 'features/T9n' |
||||
import { StarIcon } from 'features/Common' |
||||
|
||||
import type { ProfileCardProps } from './types' |
||||
import { |
||||
Wrapper, |
||||
Details, |
||||
AddToFavButton, |
||||
Logo, |
||||
InfoItem, |
||||
InfoItems, |
||||
Name, |
||||
Bottom, |
||||
InFavorites, |
||||
} from './styled' |
||||
import { useProfileCard } from './hooks' |
||||
|
||||
export const ProfileCard = (props: ProfileCardProps) => { |
||||
const { |
||||
infoItems, |
||||
name, |
||||
} = props |
||||
|
||||
const { |
||||
addToFavorites, |
||||
isFavorite, |
||||
logo, |
||||
onError, |
||||
} = useProfileCard(props) |
||||
|
||||
return ( |
||||
<Wrapper> |
||||
<Logo |
||||
src={logo} |
||||
alt={name} |
||||
onError={onError} |
||||
/> |
||||
<Details> |
||||
<Name>{name}</Name> |
||||
<Bottom> |
||||
{isFavorite ? ( |
||||
<InFavorites as='div'> |
||||
<StarIcon fill='#eacb6f' /> |
||||
<T9n t='added_to_favorites' /> |
||||
</InFavorites> |
||||
) : ( |
||||
<AddToFavButton onClick={addToFavorites}> |
||||
<StarIcon fill='#fff' /> |
||||
<T9n t='add_to_favorites' /> |
||||
</AddToFavButton> |
||||
)} |
||||
<InfoItems> |
||||
{map(infoItems, (infoItem) => <InfoItem key={infoItem}>{infoItem}</InfoItem>)} |
||||
</InfoItems> |
||||
</Bottom> |
||||
</Details> |
||||
</Wrapper> |
||||
) |
||||
} |
||||
@ -0,0 +1,75 @@ |
||||
import styled from 'styled-components/macro' |
||||
|
||||
export const Wrapper = styled.div` |
||||
display: flex; |
||||
max-width: 815px; |
||||
margin-right: 100px; |
||||
` |
||||
|
||||
export const Name = styled.h1` |
||||
margin-bottom: 10px; |
||||
line-height: 40px; |
||||
font-size: 36px; |
||||
font-weight: bold; |
||||
letter-spacing: 0.02em; |
||||
color: #fff; |
||||
` |
||||
|
||||
export const Logo = styled.img` |
||||
width: 88px; |
||||
height: 88px; |
||||
margin-right: 15px; |
||||
border-radius: 50%; |
||||
background-color: #000; |
||||
` |
||||
|
||||
export const Details = styled.div`` |
||||
|
||||
export const Bottom = styled.div` |
||||
display: flex; |
||||
` |
||||
|
||||
export const AddToFavButton = styled.button` |
||||
display: flex; |
||||
align-items: center; |
||||
height: 32px; |
||||
padding: 0 16px; |
||||
border-radius: 2px; |
||||
font-size: 14px; |
||||
color: #fff; |
||||
background: |
||||
linear-gradient( |
||||
180deg, rgba(255, 255, 255, 0) 0%, |
||||
rgba(255, 255, 255, 0.1) 0.01%,
|
||||
rgba(0, 0, 0, 0.1) 99.99% |
||||
), |
||||
#0033CC; |
||||
box-shadow: 0px 1px 0px rgba(0, 4, 41, 0.3); |
||||
border-style: none; |
||||
outline: none; |
||||
cursor: pointer; |
||||
|
||||
svg { |
||||
margin-right: 10px; |
||||
width: 18px; |
||||
height: 18px; |
||||
} |
||||
` |
||||
|
||||
export const InFavorites = styled(AddToFavButton)` |
||||
color: #EACB6F; |
||||
border: 1px solid currentColor; |
||||
background-color: transparent; |
||||
cursor: default; |
||||
` |
||||
|
||||
export const InfoItems = styled.div` |
||||
margin-left: 20px; |
||||
` |
||||
|
||||
export const InfoItem = styled.div` |
||||
line-height: 18px; |
||||
font-size: 16px; |
||||
letter-spacing: 0.02em; |
||||
color: rgba(255, 255, 255, 0.5); |
||||
` |
||||
@ -0,0 +1,8 @@ |
||||
import { ProfileTypes, SportTypes } from 'config' |
||||
|
||||
export type ProfileCardProps = { |
||||
infoItems: Array<string>, |
||||
name: string, |
||||
profileType: ProfileTypes, |
||||
sportType: SportTypes, |
||||
} |
||||
@ -0,0 +1,27 @@ |
||||
import type { ReactNode } from 'react' |
||||
import React, { |
||||
useContext, |
||||
createContext, |
||||
} from 'react' |
||||
|
||||
import { useUserFavorites } from '../hooks' |
||||
|
||||
export * from '../hooks' |
||||
|
||||
type UserFavoritesStore = ReturnType<typeof useUserFavorites> |
||||
|
||||
const UserFavoritesContext = createContext({} as UserFavoritesStore) |
||||
|
||||
type Props = { children: ReactNode } |
||||
|
||||
export const UserFavoritesStore = ({ children }: Props) => { |
||||
const userFavoritesStore = useUserFavorites() |
||||
|
||||
return ( |
||||
<UserFavoritesContext.Provider value={userFavoritesStore}> |
||||
{children} |
||||
</UserFavoritesContext.Provider> |
||||
) |
||||
} |
||||
|
||||
export const useUserFavoritesStore = () => useContext(UserFavoritesContext) |
||||
@ -1,2 +1,4 @@ |
||||
export * from './usePageId' |
||||
export * from './useToggle' |
||||
export * from './useRequest' |
||||
export * from './useSportNameParam' |
||||
|
||||
@ -0,0 +1,16 @@ |
||||
import { useParams } from 'react-router' |
||||
|
||||
import toUpper from 'lodash/toUpper' |
||||
|
||||
import { SportTypes } from 'config' |
||||
|
||||
type SportName = 'FOOTBALL' | 'HOCKEY' | 'BASKETBALL' |
||||
|
||||
export const useSportNameParam = () => { |
||||
const { sportName } = useParams<{ sportName: string }>() |
||||
|
||||
return { |
||||
sportName, |
||||
sportType: SportTypes[toUpper(sportName) as SportName], |
||||
} |
||||
} |
||||
@ -0,0 +1,48 @@ |
||||
import { |
||||
DATA_URL, |
||||
PROCEDURES, |
||||
SportTypes, |
||||
} from 'config' |
||||
import { callApi, getResponseData } from 'helpers' |
||||
|
||||
const proc = PROCEDURES.get_player_info |
||||
|
||||
export type PlayerProfile = { |
||||
c_gender: 1 | 2 | null, |
||||
club_team: { |
||||
id: number, |
||||
name_eng: string, |
||||
name_rus: string, |
||||
} | null, |
||||
firstname_eng: string, |
||||
firstname_rus: string, |
||||
height: number | null, |
||||
id: number, |
||||
is_favorite: boolean, |
||||
is_gk: boolean | null, |
||||
lastname_eng: string, |
||||
lastname_rus: string, |
||||
nickname_eng: string | null, |
||||
nickname_rus: string | null, |
||||
weight: number | null, |
||||
} | null |
||||
|
||||
export const getPlayerInfo = ( |
||||
playerId: number, |
||||
sportType: SportTypes, |
||||
): Promise<PlayerProfile> => { |
||||
const config = { |
||||
body: { |
||||
params: { |
||||
_p_player_id: playerId, |
||||
_p_sport: sportType, |
||||
}, |
||||
proc, |
||||
}, |
||||
} |
||||
|
||||
return callApi({ |
||||
config, |
||||
url: DATA_URL, |
||||
}).then(getResponseData(proc)) |
||||
} |
||||
Loading…
Reference in new issue