feat(in-141): players stats

pull/23/head
Ruslan Khayrullin 3 years ago
parent ae3e29bcf4
commit 9ac84c779c
  1. 3
      public/images/sortUp.svg
  2. 7
      src/config/routes.tsx
  3. 12
      src/features/MatchSidePlaylists/components/PlayersTable/hooks/index.tsx
  4. 4
      src/features/MatchSidePlaylists/components/PlayersTable/types.tsx
  5. 22
      src/features/MatchSidePlaylists/components/TabPlayers/index.tsx
  6. 27
      src/features/MatchSidePlaylists/components/TabStats/hooks.tsx
  7. 108
      src/features/MatchSidePlaylists/components/TabStats/index.tsx
  8. 54
      src/features/MatchSidePlaylists/components/TabStats/styled.tsx
  9. 58
      src/features/MatchSidePlaylists/components/TeamsStatsTable/styled.tsx
  10. 6
      src/features/MatchSidePlaylists/components/TeamsStatsTable/types.tsx
  11. 10
      src/features/MatchSidePlaylists/hooks.tsx
  12. 11
      src/features/Name/index.tsx
  13. 3
      src/features/T9n/index.tsx
  14. 5
      src/features/Tooltip/index.tsx
  15. 2
      src/hooks/index.tsx
  16. 5
      src/requests/getMatchParticipants.tsx
  17. 4
      src/requests/getPlayersStats.tsx

@ -0,0 +1,3 @@
<svg width="7" height="6" viewBox="0 0 7 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.06699 0.75C3.25944 0.416667 3.74056 0.416667 3.93301 0.75L6.09808 4.5C6.29053 4.83333 6.04996 5.25 5.66506 5.25L1.33494 5.25C0.950036 5.25 0.709474 4.83333 0.901924 4.5L3.06699 0.75Z" fill="white" fill-opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

@ -23,9 +23,16 @@ const VIEWS_APIS = {
staging: 'https://views.test.insports.tv',
}
const STATS_APIS = {
preproduction: 'https://statistic.insports.tv',
production: 'https://statistic.insports.tv',
staging: 'https://statistic-stage.insports.tv',
}
const env = isProduction ? ENV : readSelectedApi() ?? ENV
export const VIEWS_API = VIEWS_APIS[env]
export const AUTH_SERVICE = APIS[env].auth
export const API_ROOT = APIS[env].api
export const DATA_URL = `${API_ROOT}/data`
export const STATS_API_URL = STATS_APIS[env]

@ -5,17 +5,20 @@ import { usePlayers } from './usePlayers'
import { useTable } from './useTable'
export const usePlayersTable = ({ teamId }: PlayersTableProps) => {
const [sortCondition, setSortCondition] = useState<SortCondition>({ dir: 'asc', paramId: null })
const [sortCondition, setSortCondition] = useState<SortCondition>({
clicksCount: 0,
dir: 'asc',
paramId: null,
})
const {
getFullName,
getPlayerName,
getPlayerParams,
players,
} = usePlayers({ sortCondition, teamId })
const {
containerRef,
firstColumnWidth,
getDisplayedValue,
handleScroll,
handleSortClick,
@ -36,9 +39,8 @@ export const usePlayersTable = ({ teamId }: PlayersTableProps) => {
return {
containerRef,
firstColumnWidth,
getDisplayedValue,
getFullName,
getPlayerName,
getPlayerParams,
handleScroll,
handleSortClick,

@ -1,8 +1,12 @@
import { TCircleAnimation } from 'features/CircleAnimationBar'
export type PlayersTableProps = {
circleAnimation?: TCircleAnimation,
teamId: number,
}
export type SortCondition = {
clicksCount: number,
dir: 'asc' | 'desc',
paramId: number | null,
}

@ -1,5 +1,3 @@
import isEmpty from 'lodash/isEmpty'
import type { Playlists, PlaylistOption } from 'features/MatchPage/types'
import type { MatchInfo } from 'requests'
@ -17,15 +15,11 @@ export const TabPlayers = ({
playlists,
profile,
selectedPlaylist,
}: Props) => {
if (isEmpty(playlists.players.team1)) return null
return (
<PlayersPlaylists
profile={profile}
players={playlists.players}
selectedMathPlaylist={selectedPlaylist}
onSelect={onSelect}
/>
)
}
}: Props) => (
<PlayersPlaylists
profile={profile}
players={playlists.players}
selectedMathPlaylist={selectedPlaylist}
onSelect={onSelect}
/>
)

@ -2,6 +2,8 @@ import { useEffect, useState } from 'react'
import isEmpty from 'lodash/isEmpty'
import { useTooltip } from 'hooks'
import { useMatchPageStore } from 'features/MatchPage/store'
import { StatsType, Tabs } from './config'
@ -12,15 +14,23 @@ export const useTabStats = () => {
const {
isEmptyPlayersStats,
profile: matchProfile,
setStatsType,
statsType,
teamsStats,
toggleStatsType,
} = useMatchPageStore()
const {
isTooltipShown,
onMouseLeave,
onMouseOver,
tooltipStyle,
tooltipText,
} = useTooltip()
const isFinalStatsType = statsType === StatsType.FINAL_STATS
const switchTitleLexic = isFinalStatsType ? 'final_stats' : 'current_stats'
const tooltipLexic = isFinalStatsType ? 'display_all_stats' : 'display_stats_according_to_video'
const switchButtonTooltipLexic = isFinalStatsType ? 'display_all_stats' : 'display_stats_according_to_video'
const isVisibleTeamsTab = !isEmpty(teamsStats)
const isVisibleTeam1PlayersTab = Boolean(
@ -30,12 +40,6 @@ export const useTabStats = () => {
matchProfile && !isEmptyPlayersStats(matchProfile.team2.id),
)
const toggleStatsType = () => {
const newStatsType = isFinalStatsType ? StatsType.CURRENT_STATS : StatsType.FINAL_STATS
setStatsType(newStatsType)
}
useEffect(() => {
switch (true) {
case isVisibleTeamsTab:
@ -56,13 +60,18 @@ export const useTabStats = () => {
return {
isFinalStatsType,
isTooltipShown,
isVisibleTeam1PlayersTab,
isVisibleTeam2PlayersTab,
isVisibleTeamsTab,
onMouseLeave,
onMouseOver,
selectedTab,
setSelectedTab,
switchButtonTooltipLexic,
switchTitleLexic,
toggleStatsType,
tooltipLexic,
tooltipStyle,
tooltipText,
}
}

@ -1,11 +1,17 @@
import { isMobileDevice } from 'config/userAgent'
import type { ComponentProps } from 'react'
import { createPortal } from 'react-dom'
import { isMobileDevice } from 'config'
import { getTeamAbbr } from 'helpers'
import { Tooltip } from 'features/Tooltip'
import { useModalRoot } from 'hooks'
import { T9n } from 'features/T9n'
import { useMatchPageStore } from 'features/MatchPage/store'
import { Name } from 'features/Name'
import { TCircleAnimation, TSetCircleAnimation } from 'features/CircleAnimationBar'
import { useLexicsStore } from 'features/LexicsStore'
import { Tabs } from './config'
import { useTabStats } from './hooks'
@ -20,27 +26,44 @@ import {
Switch,
SwitchTitle,
SwitchButton,
Tooltip,
TabTitle,
} from './styled'
const tabPanes = {
[Tabs.TEAMS]: TeamsStatsTable,
[Tabs.TEAM1]: PlayersTable,
[Tabs.TEAM2]: PlayersTable,
// eslint-disable-next-line react/jsx-props-no-spreading
[Tabs.TEAM1]: (props: ComponentProps<typeof PlayersTable>) => <PlayersTable {...props} />,
// eslint-disable-next-line react/jsx-props-no-spreading
[Tabs.TEAM2]: (props: ComponentProps<typeof PlayersTable>) => <PlayersTable {...props} />,
}
export const TabStats = () => {
type Props = {
circleAnimation?: TCircleAnimation,
setCircleAnimation?: TSetCircleAnimation,
}
export const TabStats = ({ circleAnimation, setCircleAnimation }: Props) => {
const {
isFinalStatsType,
isTooltipShown,
isVisibleTeam1PlayersTab,
isVisibleTeam2PlayersTab,
isVisibleTeamsTab,
onMouseLeave,
onMouseOver,
selectedTab,
setSelectedTab,
switchButtonTooltipLexic,
switchTitleLexic,
toggleStatsType,
tooltipLexic,
tooltipStyle,
tooltipText,
} = useTabStats()
const { profile: matchProfile } = useMatchPageStore()
const { suffix, translate } = useLexicsStore()
const modalRoot = useModalRoot()
const TabPane = tabPanes[selectedTab]
@ -57,7 +80,9 @@ export const TabStats = () => {
aria-pressed={selectedTab === Tabs.TEAMS}
onClick={() => setSelectedTab(Tabs.TEAMS)}
>
<T9n t='team' />
<TabTitle>
<T9n t='team' />
</TabTitle>
</Tab>
)}
{isVisibleTeam1PlayersTab && (
@ -65,11 +90,25 @@ export const TabStats = () => {
aria-pressed={selectedTab === Tabs.TEAM1}
onClick={() => setSelectedTab(Tabs.TEAM1)}
>
<Name nameObj={{
name_eng: team1.abbrev_eng || getTeamAbbr(team1.name_eng),
name_rus: team1.abbrev_rus || getTeamAbbr(team1.name_rus),
}}
/>
<TabTitle
onMouseOver={isMobileDevice
? undefined
: onMouseOver({
anchorId: 'team1Tab',
horizontalPosition: 'left',
indent: 25,
tooltipText: team1[`name_${suffix}`],
})}
onMouseLeave={isMobileDevice ? undefined : onMouseLeave}
>
<Name
id='team1Tab'
nameObj={{
name_eng: team1.abbrev_eng || getTeamAbbr(team1.name_eng),
name_rus: team1.abbrev_rus || getTeamAbbr(team1.name_rus),
}}
/>
</TabTitle>
</Tab>
)}
{isVisibleTeam2PlayersTab && (
@ -77,27 +116,56 @@ export const TabStats = () => {
aria-pressed={selectedTab === Tabs.TEAM2}
onClick={() => setSelectedTab(Tabs.TEAM2)}
>
<Name nameObj={{
name_eng: team2.abbrev_eng || getTeamAbbr(team2.name_eng),
name_rus: team2.abbrev_rus || getTeamAbbr(team2.name_rus),
}}
/>
<TabTitle
onMouseOver={isMobileDevice
? undefined
: onMouseOver({
anchorId: 'team2Tab',
horizontalPosition: 'left',
indent: 25,
tooltipText: team2[`name_${suffix}`],
})}
onMouseLeave={isMobileDevice ? undefined : onMouseLeave}
>
<Name
id='team2Tab'
nameObj={{
name_eng: team2.abbrev_eng || getTeamAbbr(team2.name_eng),
name_rus: team2.abbrev_rus || getTeamAbbr(team2.name_rus),
}}
/>
</TabTitle>
</Tab>
)}
</TabList>
<Switch>
<SwitchTitle t={switchTitleLexic} />
<SwitchButton
id='switchButton'
isFinalStatsType={isFinalStatsType}
onClick={toggleStatsType}
>
{!isMobileDevice && <Tooltip lexic={tooltipLexic} />}
</SwitchButton>
onMouseOver={isMobileDevice
? undefined
: onMouseOver({
anchorId: 'switchButton',
horizontalPosition: 'right',
tooltipText: translate(switchButtonTooltipLexic),
})}
onMouseLeave={isMobileDevice ? undefined : onMouseLeave}
/>
</Switch>
</Header>
<TabPane
teamId={selectedTab === Tabs.TEAM1 ? team1.id : team2.id}
circleAnimation={circleAnimation}
setCircleAnimation={setCircleAnimation}
/>
{isTooltipShown && modalRoot.current && createPortal(
<Tooltip style={tooltipStyle}>
{tooltipText}
</Tooltip>,
modalRoot.current,
)}
</Container>
)
}

@ -8,29 +8,51 @@ export const Container = styled.div``
export const Header = styled.div`
display: flex;
justify-content: space-between;
margin-bottom: 23px;
margin-bottom: 6px;
`
export const TabList = styled.div.attrs({ role: 'tablist' })`
display: flex;
`
export const Tooltip = styled(TooltipWrapper)`
display: block;
padding: 2px 10px;
border-radius: 6px;
transform: none;
font-size: 11px;
line-height: 1;
color: ${({ theme }) => theme.colors.black};
z-index: 999;
::before {
display: none;
}
`
export const TabTitle = styled.span`
color: ${({ theme }) => theme.colors.white};
opacity: 0.4;
`
export const Tab = styled.button.attrs({ role: 'tab' })`
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px 10px;
font-size: 12px;
color: ${({ theme }) => theme.colors.white};
opacity: 0.4;
cursor: pointer;
border: none;
background: none;
border-bottom: 2px solid transparent;
&[aria-pressed="true"] {
opacity: 1;
border-color: currentColor;
border-color: ${({ theme }) => theme.colors.white};
${TabTitle} {
opacity: 1;
}
}
`
@ -49,7 +71,6 @@ type SwitchButtonProps = {
}
export const SwitchButton = styled.button<SwitchButtonProps>`
position: relative;
width: 20px;
height: 7px;
margin-left: 5px;
@ -59,27 +80,6 @@ export const SwitchButton = styled.button<SwitchButtonProps>`
border: 1px solid ${({ theme }) => theme.colors.white};
cursor: pointer;
${TooltipWrapper} {
left: auto;
right: 0;
top: 15px;
padding: 2px 10px;
border-radius: 6px;
transform: none;
font-size: 11px;
line-height: 1;
::before {
display: none;
}
}
:hover {
${TooltipWrapper} {
display: block;
}
}
${({ isFinalStatsType, theme }) => (!isFinalStatsType
? css`
background-image: linear-gradient(

@ -1,13 +1,27 @@
import styled, { css } from 'styled-components/macro'
import { Name } from 'features/Name'
import { customScrollbar } from 'features/Common'
export const Container = styled.div`
export const Container = styled.div``
export const TableWrapper = styled.div`
width: 100%;
max-height: calc(100vh - 203px);
overflow: auto;
font-size: 11px;
overflow: hidden;
border-radius: 5px;
background-color: #333333;
${customScrollbar}
`
export const Table = styled.table`
width: 100%;
border-spacing: 0;
border-collapse: collapse;
letter-spacing: -0.078px;
table-layout: fixed;
`
export const TeamShortName = styled(Name)`
@ -18,16 +32,44 @@ export const TeamShortName = styled(Name)`
opacity: 0.5;
`
export const Row = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
export const Cell = styled.td`
height: 45px;
padding: 0 12px;
border-bottom: 0.5px solid rgba(255, 255, 255, 0.5);
background-color: #333333;
:nth-child(2) {
text-align: center;
}
:first-child, :last-child {
width: 32px;
}
:first-child {
padding-left: 12px;
}
:last-child {
border-bottom: none;
text-align: right;
padding-right: 12px;
}
`
export const Row = styled.tr`
:last-child:not(:first-child) {
${Cell} {
border-bottom: none;
}
}
`
export const Header = styled.thead`
position: sticky;
top: 0;
z-index: 1;
${Cell} {
background-color: #292929;
}
`

@ -0,0 +1,6 @@
import { TCircleAnimation, TSetCircleAnimation } from 'features/CircleAnimationBar'
export type Props = {
circleAnimation?: TCircleAnimation,
setCircleAnimation?: TSetCircleAnimation,
}

@ -61,7 +61,7 @@ export const useMatchSidePlaylists = () => {
isWatchTabVisible,
isEventTabVisible,
isPlayersTabVisible,
// isStatsTabVisible,
isStatsTabVisible,
]).length < 4
useEffect(() => {
@ -75,14 +75,14 @@ export const useMatchSidePlaylists = () => {
case isPlayersTabVisible:
setSelectedTab(Tabs.PLAYERS)
break
// case isStatsTabVisible:
// setSelectedTab(Tabs.STATS)
// break
case isStatsTabVisible:
setSelectedTab(Tabs.STATS)
break
}
}, [
isEventTabVisible,
isPlayersTabVisible,
// isStatsTabVisible,
isStatsTabVisible,
isWatchTabVisible,
])

@ -10,6 +10,7 @@ export type ObjectWithName = {
type Props = {
className?: string,
id?: string,
nameObj: ObjectWithName,
prefix?: string,
}
@ -42,9 +43,17 @@ export const useName = (
export const Name = ({
className,
id,
nameObj,
prefix,
}: Props) => {
const name = useName(nameObj, prefix)
return <NameStyled className={className}>{name}</NameStyled>
return (
<NameStyled
id={id}
className={className}
>
{name}
</NameStyled>
)
}

@ -7,6 +7,7 @@ const Text = styled.span``
type Props = {
className?: string,
id?: string,
onClick?: () => void,
t: LexicsId,
values?: Values,
@ -14,6 +15,7 @@ type Props = {
export const T9n = ({
className,
id,
onClick,
t,
values,
@ -22,6 +24,7 @@ export const T9n = ({
return (
<Text
id={id}
onClick={onClick}
className={className}
>

@ -34,7 +34,10 @@ export const Tooltip = ({
lexic,
}: Props) => (
<Fragment>
<TooltipWrapper top={0}>
<TooltipWrapper
role='tooltip'
top={0}
>
<T9n t={lexic} />
</TooltipWrapper>
{children}

@ -5,3 +5,5 @@ export * from './useInterval'
export * from './useEventListener'
export * from './useObjectState'
export * from './usePageParams'
export * from './useTooltip'
export * from './useModalRoot'

@ -1,6 +1,6 @@
import isUndefined from 'lodash/isUndefined'
import { SportTypes } from 'config'
import { SportTypes, STATS_API_URL } from 'config'
import { callApi } from 'helpers'
@ -23,6 +23,7 @@ export type Player = {
national_shirt_num: number,
nickname_eng: string | null,
nickname_rus: string | null,
num: number | null,
weight: number | null,
}
@ -56,7 +57,7 @@ export const getMatchParticipants = async ({
const response: Response = await callApi({
config,
url: `http://136.243.17.103:8888/ask/participants?sport_id=${sportType}&match_id=${matchId}${isUndefined(second) ? '' : `&second=${second}`}`,
url: `${STATS_API_URL}/ask/participants?sport_id=${sportType}&match_id=${matchId}${isUndefined(second) ? '' : `&second=${second}`}`,
})
if (response.error) Promise.reject(response)

@ -2,6 +2,8 @@ import isUndefined from 'lodash/isUndefined'
import { callApi } from 'helpers'
import { STATS_API_URL } from 'config'
export type PlayerParam = {
clickable: boolean,
data_type: string,
@ -45,7 +47,7 @@ export const getPlayersStats = async ({
const response: Response = await callApi({
config,
url: `http://136.243.17.103:8888/${sportName}/matches/${matchId}/teams/${teamId}/players/stats${isUndefined(second) ? '' : `?second=${second}`}`,
url: `${STATS_API_URL}/${sportName}/matches/${matchId}/teams/${teamId}/players/stats${isUndefined(second) ? '' : `?second=${second}`}`,
})
if (response.error) Promise.reject(response)

Loading…
Cancel
Save