From 22d93b845c3ff05265a99be3db5f8c307823a649 Mon Sep 17 00:00:00 2001 From: Ruslan Khayrullin Date: Wed, 12 Aug 2020 18:24:41 +0300 Subject: [PATCH] feat(ott-191): player page header (#76) --- src/config/lexics/homeLexics.tsx | 7 ++ .../{authenticated.tsx => indexLexics.tsx} | 3 +- src/config/lexics/playerLexics.tsx | 10 +++ src/config/procedures.tsx | 1 + src/features/App/AuthenticatedApp.tsx | 70 ++++++++--------- src/features/Common/StarIcon/index.tsx | 24 ++++++ src/features/Common/index.tsx | 1 + src/features/HomePage/index.tsx | 58 +++++++------- src/features/PlayerPage/hooks.tsx | 44 +++++++++++ src/features/PlayerPage/index.tsx | 45 ++++++++--- src/features/ProfileCard/hooks.tsx | 57 ++++++++++++++ src/features/ProfileCard/index.tsx | 63 ++++++++++++++++ src/features/ProfileCard/styled.tsx | 75 +++++++++++++++++++ src/features/ProfileCard/types.tsx | 8 ++ .../Search/hooks/useNormalizedItems.tsx | 19 ++++- src/features/UserFavorites/index.tsx | 4 +- src/features/UserFavorites/store/index.tsx | 27 +++++++ src/helpers/getProfileUrl/index.tsx | 2 +- src/hooks/index.tsx | 2 + src/hooks/useSportNameParam.tsx | 16 ++++ src/requests/getPlayerInfo.tsx | 48 ++++++++++++ 21 files changed, 503 insertions(+), 81 deletions(-) create mode 100644 src/config/lexics/homeLexics.tsx rename src/config/lexics/{authenticated.tsx => indexLexics.tsx} (89%) create mode 100644 src/config/lexics/playerLexics.tsx create mode 100644 src/features/Common/StarIcon/index.tsx create mode 100644 src/features/PlayerPage/hooks.tsx create mode 100644 src/features/ProfileCard/hooks.tsx create mode 100644 src/features/ProfileCard/index.tsx create mode 100644 src/features/ProfileCard/styled.tsx create mode 100644 src/features/ProfileCard/types.tsx create mode 100644 src/features/UserFavorites/store/index.tsx create mode 100644 src/hooks/useSportNameParam.tsx create mode 100644 src/requests/getPlayerInfo.tsx diff --git a/src/config/lexics/homeLexics.tsx b/src/config/lexics/homeLexics.tsx new file mode 100644 index 00000000..0b408a0c --- /dev/null +++ b/src/config/lexics/homeLexics.tsx @@ -0,0 +1,7 @@ +import { indexLexics } from './indexLexics' + +export const homeLexics = { + ...indexLexics, + + video_from_my_subscriptions: 13023, +} diff --git a/src/config/lexics/authenticated.tsx b/src/config/lexics/indexLexics.tsx similarity index 89% rename from src/config/lexics/authenticated.tsx rename to src/config/lexics/indexLexics.tsx index db3fd103..3bbbe6e5 100644 --- a/src/config/lexics/authenticated.tsx +++ b/src/config/lexics/indexLexics.tsx @@ -1,6 +1,6 @@ import { proceduresLexics } from './procedures' -export const authenticatedLexics = { +export const indexLexics = { basketball: 6960, football: 6958, full_game: 13028, @@ -24,7 +24,6 @@ export const authenticatedLexics = { team: 658, tournament: 1009, user_account: 12928, - video_from_my_subscriptions: 13023, watch_from_beginning: 13021, watch_from_last_pause: 13022, watch_now: 13020, diff --git a/src/config/lexics/playerLexics.tsx b/src/config/lexics/playerLexics.tsx new file mode 100644 index 00000000..eded7648 --- /dev/null +++ b/src/config/lexics/playerLexics.tsx @@ -0,0 +1,10 @@ +import { indexLexics } from './indexLexics' + +export const playerLexics = { + ...indexLexics, + + add_to_favorites: 1701, + added_to_favorites: 13048, + cm: 817, + kg: 652, +} diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx index b81749b8..d213e4be 100644 --- a/src/config/procedures.tsx +++ b/src/config/procedures.tsx @@ -3,6 +3,7 @@ export const PROCEDURES = { create_user: 'create_user', get_cities: 'get_cities', get_matches: 'get_matches_tmp', + get_player_info: 'get_player_info', get_players_teams_tournaments: 'get_players_teams_tournaments', get_sport_list: 'get_sport_list', get_tournament_list: 'get_tournament_list', diff --git a/src/features/App/AuthenticatedApp.tsx b/src/features/App/AuthenticatedApp.tsx index e21520c1..f447453c 100644 --- a/src/features/App/AuthenticatedApp.tsx +++ b/src/features/App/AuthenticatedApp.tsx @@ -7,8 +7,6 @@ import { import { PAGES } from 'config' -import { authenticatedLexics } from 'config/lexics/authenticated' -import { useLexicsConfig } from 'features/LexicsStore' import { HomePage } from 'features/HomePage' import { TeamPage } from 'features/TeamPage' import { MatchPage } from 'features/MatchPage' @@ -18,39 +16,35 @@ import { LanguageSelect } from 'features/LanguageSelect' import { UserAccount } from 'features/UserAccount' import { ScoreStore, ToggleScore } from 'features/ToggleScore' -export const AuthenticatedApp = () => { - useLexicsConfig(authenticatedLexics) - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} +export const AuthenticatedApp = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/src/features/Common/StarIcon/index.tsx b/src/features/Common/StarIcon/index.tsx new file mode 100644 index 00000000..cdd535d6 --- /dev/null +++ b/src/features/Common/StarIcon/index.tsx @@ -0,0 +1,24 @@ +import React from 'react' + +type StarProps = { + fill: string, +} + +export const StarIcon = ({ fill }: StarProps) => ( + + + + + + + + + + + + + + + + +) diff --git a/src/features/Common/index.tsx b/src/features/Common/index.tsx index 8f8984b6..b54bed51 100644 --- a/src/features/Common/index.tsx +++ b/src/features/Common/index.tsx @@ -5,5 +5,6 @@ export * from './Checkbox' export * from './Arrows' export * from './CloseButton' export * from './SportName' +export * from './StarIcon' export * from './customScrollbar' export * from './customStyles' diff --git a/src/features/HomePage/index.tsx b/src/features/HomePage/index.tsx index 93a16533..0c8833f6 100644 --- a/src/features/HomePage/index.tsx +++ b/src/features/HomePage/index.tsx @@ -1,5 +1,7 @@ import React from 'react' +import { homeLexics } from 'config/lexics/homeLexics' +import { useLexicsConfig } from 'features/LexicsStore' import { Header } from 'features/Header' import { MainWrapper } from 'features/MainWrapper' import { Search } from 'features/Search' @@ -20,32 +22,36 @@ import { Content, } from './styled' -export const HomePage = () => ( - - - -
- - - +export const HomePage = () => { + useLexicsConfig(homeLexics) - - - + return ( + + + +
+ + + - - - + + + - - - - -
- - <T9n t='video_from_my_subscriptions' /> - - -
-
-) + + + + + + + + +
+ + <T9n t='video_from_my_subscriptions' /> + + +
+
+ ) +} diff --git a/src/features/PlayerPage/hooks.tsx b/src/features/PlayerPage/hooks.tsx new file mode 100644 index 00000000..5ec36ee5 --- /dev/null +++ b/src/features/PlayerPage/hooks.tsx @@ -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(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, + } +} diff --git a/src/features/PlayerPage/index.tsx b/src/features/PlayerPage/index.tsx index aa4f9d17..8ad18e08 100644 --- a/src/features/PlayerPage/index.tsx +++ b/src/features/PlayerPage/index.tsx @@ -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 = () => ( - PLAYER PAGE -) +import { usePlayerPage } from './hooks' + +export const PlayerPage = () => { + const { + infoItems, + name, + sportType, + } = usePlayerPage() + useLexicsConfig(playerLexics) + + return ( + + + +
+ + +
+
+
+ ) +} diff --git a/src/features/ProfileCard/hooks.tsx b/src/features/ProfileCard/hooks.tsx new file mode 100644 index 00000000..1d3b0ae2 --- /dev/null +++ b/src/features/ProfileCard/hooks.tsx @@ -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, + } +} diff --git a/src/features/ProfileCard/index.tsx b/src/features/ProfileCard/index.tsx new file mode 100644 index 00000000..4a645656 --- /dev/null +++ b/src/features/ProfileCard/index.tsx @@ -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 ( + + +
+ {name} + + {isFavorite ? ( + + + + + ) : ( + + + + + )} + + {map(infoItems, (infoItem) => {infoItem})} + + +
+
+ ) +} diff --git a/src/features/ProfileCard/styled.tsx b/src/features/ProfileCard/styled.tsx new file mode 100644 index 00000000..a5ca4e98 --- /dev/null +++ b/src/features/ProfileCard/styled.tsx @@ -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); +` diff --git a/src/features/ProfileCard/types.tsx b/src/features/ProfileCard/types.tsx new file mode 100644 index 00000000..c2f8786c --- /dev/null +++ b/src/features/ProfileCard/types.tsx @@ -0,0 +1,8 @@ +import { ProfileTypes, SportTypes } from 'config' + +export type ProfileCardProps = { + infoItems: Array, + name: string, + profileType: ProfileTypes, + sportType: SportTypes, +} diff --git a/src/features/Search/hooks/useNormalizedItems.tsx b/src/features/Search/hooks/useNormalizedItems.tsx index 32a296ff..78bcac4c 100644 --- a/src/features/Search/hooks/useNormalizedItems.tsx +++ b/src/features/Search/hooks/useNormalizedItems.tsx @@ -7,6 +7,7 @@ import { getSportColor, getSportLexic, getProfileFallbackLogo, + getProfileUrl, } from 'helpers' import { useLexicsStore } from 'features/LexicsStore' @@ -45,7 +46,11 @@ export const useNormalizedItems = (searchItems: SearchItems) => { id, logo, name: `${firstName} ${lastName}`, - profileUrl: `/players/${id}`, + profileUrl: getProfileUrl({ + id, + profileType: ProfileTypes.PLAYERS, + sportType, + }), sportLexic, teamOrCountry: teamName, } @@ -76,7 +81,11 @@ export const useNormalizedItems = (searchItems: SearchItems) => { id, logo, name, - profileUrl: `/teams/${id}`, + profileUrl: getProfileUrl({ + id, + profileType: ProfileTypes.TEAMS, + sportType, + }), sportLexic, teamOrCountry: country, } @@ -107,7 +116,11 @@ export const useNormalizedItems = (searchItems: SearchItems) => { id, logo, name, - profileUrl: `/tournaments/${id}`, + profileUrl: getProfileUrl({ + id, + profileType: ProfileTypes.TOURNAMENTS, + sportType, + }), sportLexic, teamOrCountry: country, } diff --git a/src/features/UserFavorites/index.tsx b/src/features/UserFavorites/index.tsx index 3a27eacc..68a9ab89 100644 --- a/src/features/UserFavorites/index.tsx +++ b/src/features/UserFavorites/index.tsx @@ -4,7 +4,7 @@ import map from 'lodash/map' import { handleImageError } from 'helpers' -import { useUserFavorites } from './hooks' +import { useUserFavoritesStore } from './store' import { TooltipBlock } from './TooltipBlock' import { StyledLink, @@ -17,7 +17,7 @@ import { } from './styled' export const UserFavorites = () => { - const { addRemoveFavorite, userFavorites } = useUserFavorites() + const { addRemoveFavorite, userFavorites } = useUserFavoritesStore() return ( diff --git a/src/features/UserFavorites/store/index.tsx b/src/features/UserFavorites/store/index.tsx new file mode 100644 index 00000000..28c2a70c --- /dev/null +++ b/src/features/UserFavorites/store/index.tsx @@ -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 + +const UserFavoritesContext = createContext({} as UserFavoritesStore) + +type Props = { children: ReactNode } + +export const UserFavoritesStore = ({ children }: Props) => { + const userFavoritesStore = useUserFavorites() + + return ( + + {children} + + ) +} + +export const useUserFavoritesStore = () => useContext(UserFavoritesContext) diff --git a/src/helpers/getProfileUrl/index.tsx b/src/helpers/getProfileUrl/index.tsx index 3f8e282b..7b1a2209 100644 --- a/src/helpers/getProfileUrl/index.tsx +++ b/src/helpers/getProfileUrl/index.tsx @@ -16,5 +16,5 @@ export const getProfileUrl = ({ profileType, sportType, }: Args) => ( - `${SPORT_NAMES[sportType]}/${PROFILE_NAMES[profileType]}/${id}` + `/${SPORT_NAMES[sportType]}/${PROFILE_NAMES[profileType]}/${id}` ) diff --git a/src/hooks/index.tsx b/src/hooks/index.tsx index 44597d75..95d70492 100644 --- a/src/hooks/index.tsx +++ b/src/hooks/index.tsx @@ -1,2 +1,4 @@ export * from './usePageId' export * from './useToggle' +export * from './useRequest' +export * from './useSportNameParam' diff --git a/src/hooks/useSportNameParam.tsx b/src/hooks/useSportNameParam.tsx new file mode 100644 index 00000000..f13f35fe --- /dev/null +++ b/src/hooks/useSportNameParam.tsx @@ -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], + } +} diff --git a/src/requests/getPlayerInfo.tsx b/src/requests/getPlayerInfo.tsx new file mode 100644 index 00000000..1a72c3ce --- /dev/null +++ b/src/requests/getPlayerInfo.tsx @@ -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 => { + const config = { + body: { + params: { + _p_player_id: playerId, + _p_sport: sportType, + }, + proc, + }, + } + + return callApi({ + config, + url: DATA_URL, + }).then(getResponseData(proc)) +}