feat(#680): add statsview page
parent
17e8794399
commit
e1d82fc86e
@ -0,0 +1,3 @@ |
||||
export const statsviewLexics = { |
||||
powered_by: 20210, |
||||
} |
||||
@ -0,0 +1,90 @@ |
||||
import map from 'lodash/map' |
||||
|
||||
import { MatchInfo, Param } from 'requests' |
||||
|
||||
import { |
||||
CellContainer, |
||||
Container, |
||||
Header, |
||||
Row, |
||||
StatItemTitle, |
||||
Table, |
||||
TableWrapper, |
||||
TeamShortName, |
||||
} from 'features/MatchSidePlaylists/components/TeamsStatsTable/styled' |
||||
import { Cell } from 'features/MatchSidePlaylists/components/TeamsStatsTable/Cell' |
||||
|
||||
import { TeamsStats } from 'features/MatchPage/store/hooks/useTeamsStats' |
||||
|
||||
import { getStatItemById } from '../helpers' |
||||
|
||||
type StatsTableProps = { |
||||
firstClickableParam?: Param | null, |
||||
isOpen?: boolean, |
||||
profile: MatchInfo, |
||||
teamsStats: TeamsStats, |
||||
} |
||||
export const StatsTable = ({ |
||||
firstClickableParam = null, |
||||
isOpen = false, |
||||
profile, |
||||
teamsStats, |
||||
}: StatsTableProps) => { |
||||
if (!profile) return null |
||||
|
||||
return ( |
||||
<Container> |
||||
<TableWrapper isOpenTour={Boolean(isOpen)}> |
||||
<Table role='marquee' aria-live='off'> |
||||
<Header> |
||||
<Row> |
||||
<CellContainer as='th'> |
||||
<TeamShortName |
||||
nameObj={profile.team1} |
||||
prefix='abbrev_' |
||||
/> |
||||
</CellContainer> |
||||
<CellContainer as='th' /> |
||||
<CellContainer as='th'> |
||||
<TeamShortName |
||||
nameObj={profile.team2} |
||||
prefix='abbrev_' |
||||
/> |
||||
</CellContainer> |
||||
</Row> |
||||
</Header> |
||||
|
||||
<tbody> |
||||
{map(teamsStats[profile.team1.id], (team1StatItem) => { |
||||
const team2StatItem = getStatItemById({ |
||||
matchProfile: profile, |
||||
paramId: team1StatItem.param1.id, |
||||
stats: teamsStats, |
||||
}) |
||||
|
||||
return ( |
||||
<Row key={team1StatItem.param1.id}> |
||||
<Cell |
||||
teamStatItem={team1StatItem} |
||||
teamId={profile.team1.id} |
||||
firstClickableParam={firstClickableParam} |
||||
/> |
||||
|
||||
<CellContainer> |
||||
<StatItemTitle t={team1StatItem.lexic} /> |
||||
</CellContainer> |
||||
|
||||
<Cell |
||||
teamStatItem={team2StatItem} |
||||
teamId={profile.team2.id} |
||||
firstClickableParam={firstClickableParam} |
||||
/> |
||||
</Row> |
||||
) |
||||
})} |
||||
</tbody> |
||||
</Table> |
||||
</TableWrapper> |
||||
</Container> |
||||
) |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
import { TeamStatItem } from 'requests' |
||||
import { StatsLexicType } from './getStatsLexic' |
||||
|
||||
type StatsItemType = StatsLexicType & { |
||||
paramId: number, |
||||
} |
||||
export const getStatItemById = ({ |
||||
matchProfile, |
||||
paramId, |
||||
stats, |
||||
}: StatsItemType) => { |
||||
if (!matchProfile) return null |
||||
|
||||
return stats[matchProfile?.team2.id] |
||||
.find(({ param1 }: TeamStatItem) => param1.id === paramId) || null |
||||
} |
||||
@ -0,0 +1,22 @@ |
||||
import reduce from 'lodash/reduce' |
||||
import { MatchInfo, TeamStatItem } from 'requests' |
||||
import { TeamsStats } from 'features/MatchPage/store/hooks/useTeamsStats' |
||||
|
||||
export type StatsLexicType = { |
||||
matchProfile: MatchInfo, |
||||
stats: TeamsStats, |
||||
} |
||||
export const getStatsLexics = ({ matchProfile, stats }: StatsLexicType) => ( |
||||
matchProfile |
||||
? reduce<TeamStatItem, Array<number>>( |
||||
stats[matchProfile.team1.id], |
||||
(acc, curr) => { |
||||
!acc.includes(curr.lexic) && acc.push(curr.lexic) |
||||
!acc.includes(curr.param1.lexic) && acc.push(curr.param1.lexic) |
||||
curr.param2 && !acc.includes(curr.param2.lexic) && acc.push(curr.param2.lexic) |
||||
|
||||
return acc |
||||
}, |
||||
[], |
||||
) |
||||
: []) |
||||
@ -0,0 +1,2 @@ |
||||
export * from './getStatsLexic' |
||||
export * from './getStatsItem' |
||||
@ -0,0 +1,78 @@ |
||||
import { |
||||
useEffect, |
||||
useMemo, |
||||
useState, |
||||
} from 'react' |
||||
|
||||
import { |
||||
getMatchInfo, |
||||
getTeamsStats, |
||||
MatchInfo, |
||||
} from 'requests' |
||||
|
||||
import { useInterval, usePageParams } from 'hooks' |
||||
|
||||
import { TeamsStats } from 'features/MatchPage/store/hooks/useTeamsStats' |
||||
import { useLexicsConfig } from 'features/LexicsStore' |
||||
|
||||
import { getStatsLexics } from 'pages/StatsView/helpers' |
||||
|
||||
const INTERVAL_FETCH_STATS = 30 * 1000 |
||||
|
||||
export const useStatsView = () => { |
||||
const [matchProfile, setMatchProfile] = useState<MatchInfo>(null) |
||||
const [stats, setStats] = useState<TeamsStats>({}) |
||||
const { |
||||
profileId, |
||||
sportName, |
||||
sportType, |
||||
} = usePageParams() |
||||
|
||||
const fetchMatchInfo = async () => { |
||||
if (!sportName || !profileId) return |
||||
|
||||
const profile = await getMatchInfo(sportType, profileId) |
||||
setMatchProfile(profile) |
||||
} |
||||
|
||||
const fetchStats = async () => { |
||||
if (!sportName || !profileId) return |
||||
|
||||
const statistic = await getTeamsStats({ |
||||
matchId: profileId, |
||||
sportName, |
||||
}) |
||||
setStats(statistic) |
||||
} |
||||
|
||||
const statsLexicIds = useMemo( |
||||
() => getStatsLexics({ matchProfile, stats }), |
||||
[matchProfile, stats], |
||||
) |
||||
|
||||
useLexicsConfig(statsLexicIds) |
||||
|
||||
const { start, stop } = useInterval({ |
||||
callback: fetchStats, |
||||
intervalDuration: INTERVAL_FETCH_STATS, |
||||
startImmediate: true, |
||||
}) |
||||
|
||||
useEffect(() => { |
||||
if (!sportName || !profileId) return undefined |
||||
|
||||
start() |
||||
fetchMatchInfo() |
||||
|
||||
return () => stop() |
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ |
||||
profileId, |
||||
sportName, |
||||
]) |
||||
|
||||
return { |
||||
matchProfile, |
||||
teamsStats: stats, |
||||
} |
||||
} |
||||
@ -0,0 +1,35 @@ |
||||
import { Logo } from 'features/Logo' |
||||
import { T9n } from 'features/T9n' |
||||
|
||||
import { useStatsView } from './hooks' |
||||
import { StatsTable } from './components/StatsTable' |
||||
import { Powered, Container } from './styled' |
||||
|
||||
const StatsView = () => { |
||||
const { matchProfile, teamsStats } = useStatsView() |
||||
|
||||
return ( |
||||
<Container> |
||||
<header className='header__logo'> |
||||
<Logo /> |
||||
</header> |
||||
<main> |
||||
{matchProfile?.team1 && matchProfile?.team2 && ( |
||||
<StatsTable |
||||
profile={matchProfile} |
||||
teamsStats={teamsStats} |
||||
/> |
||||
)} |
||||
</main> |
||||
<footer className='footer__powered'> |
||||
<a target='_blank' href='https://india.insports.tv' rel='noreferrer'> |
||||
<Powered> |
||||
<T9n t='powered_by' /> |
||||
</Powered> |
||||
</a> |
||||
</footer> |
||||
</Container> |
||||
) |
||||
} |
||||
|
||||
export default StatsView |
||||
@ -0,0 +1,25 @@ |
||||
import styled from 'styled-components/macro' |
||||
|
||||
export const Powered = styled.span` |
||||
display: block; |
||||
text-decoration: none; |
||||
font-size: 12px; |
||||
line-height: 20px; |
||||
|
||||
text-align: center; |
||||
letter-spacing: -0.32px; |
||||
|
||||
color: #76ADFF; |
||||
` |
||||
|
||||
export const Container = styled.div` |
||||
display: flex; |
||||
flex-direction: column; |
||||
gap: 15px; |
||||
padding: 25px 5px 15px 5px; |
||||
|
||||
.header__logo{ |
||||
display: flex; |
||||
justify-content: center; |
||||
} |
||||
` |
||||
Loading…
Reference in new issue