feat(#680): add statsview page
parent
b9b505d37e
commit
0e147688a5
@ -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