parent
6b19ad2ec3
commit
59766a9601
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@ |
||||
import { useState } from 'react' |
||||
|
||||
import styled from 'styled-components/macro' |
||||
|
||||
type LogoImgProps = { |
||||
isLogoError: boolean, |
||||
} |
||||
export const LogoImg = styled.img<LogoImgProps>` |
||||
height: 25px; |
||||
width: 25px; |
||||
margin-right: 15px; |
||||
display: ${({ isLogoError }) => (isLogoError ? 'none' : '')}; |
||||
filter: grayscale(1); |
||||
|
||||
:hover { |
||||
filter: none; |
||||
} |
||||
` |
||||
|
||||
type Props = { |
||||
src: string, |
||||
} |
||||
|
||||
export const TeamLogoImg = ({ |
||||
src, |
||||
}: Props) => { |
||||
const [isLogoError, setIsImgError] = useState(false) |
||||
|
||||
const onError = () => setIsImgError(true) |
||||
|
||||
return ( |
||||
<LogoImg |
||||
src={src} |
||||
onError={onError} |
||||
isLogoError={isLogoError} |
||||
/> |
||||
) |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
import { isPast } from 'date-fns' |
||||
|
||||
export const getLandingName = () => { |
||||
const splitPath = window.location.pathname.split('/') |
||||
return splitPath[2] |
||||
} |
||||
|
||||
const convertToDate = (date: string) => { |
||||
const d = date.split('.') |
||||
return new Date(`${d[2]}/${d[1]}/${d[0]}`) |
||||
} |
||||
|
||||
export const isPastLandingDate = (data: string) => { |
||||
const landingDate = convertToDate(data) |
||||
return isPast(landingDate) |
||||
} |
||||
@ -0,0 +1,95 @@ |
||||
import { |
||||
useEffect, |
||||
useState, |
||||
} from 'react' |
||||
|
||||
import size from 'lodash/size' |
||||
import includes from 'lodash/includes' |
||||
|
||||
import type { TournamentLanding } from 'requests/getTournamentLanding' |
||||
import { getTournamentLanding } from 'requests/getTournamentLanding' |
||||
|
||||
import { PAGES } from 'config/pages' |
||||
|
||||
import { redirectToUrl } from 'helpers/redirectToUrl' |
||||
|
||||
import { useLexicsStore } from 'features/LexicsStore' |
||||
import { useAuthStore } from 'features/AuthStore' |
||||
|
||||
import { getLandingName, isPastLandingDate } from './helpers' |
||||
|
||||
export const useTournamentLanding = () => { |
||||
const [tournamentInfo, setTournamentInfo] = useState<TournamentLanding | null>(null) |
||||
|
||||
const { addLexicsConfig } = useLexicsStore() |
||||
const { page, setIsFromLanding } = useAuthStore() |
||||
|
||||
const buttonLexic = tournamentInfo?.lexic_button || '' |
||||
const period = tournamentInfo?.lexic_period || '' |
||||
const title = tournamentInfo?.lexic_title || '' |
||||
const description = tournamentInfo?.lexic_description || '' |
||||
const gallery = tournamentInfo?.media.gallery |
||||
|
||||
useEffect(() => { |
||||
const lexics = [buttonLexic, period, title, description] |
||||
addLexicsConfig(lexics) |
||||
}, [ |
||||
addLexicsConfig, |
||||
buttonLexic, |
||||
description, |
||||
period, |
||||
title, |
||||
]) |
||||
|
||||
const redirectToHomePage = () => redirectToUrl(PAGES.home) |
||||
const onButtonClick = () => { |
||||
if (includes(page, 'matches')) { |
||||
setIsFromLanding(true) |
||||
redirectToUrl(page) |
||||
} else { |
||||
redirectToUrl(tournamentInfo?.url_button || '') |
||||
} |
||||
} |
||||
|
||||
useEffect(() => { |
||||
getTournamentLanding(getLandingName()) |
||||
.then((data) => ( |
||||
isPastLandingDate(data.date_to) |
||||
? redirectToHomePage() |
||||
: setTournamentInfo(data) |
||||
)) |
||||
.catch(redirectToHomePage) |
||||
}, []) |
||||
|
||||
const [sliderItemId, setSliderItemId] = useState(0) |
||||
|
||||
const onSliderSwitchClick = (itemId: number) => setSliderItemId(itemId) |
||||
|
||||
const imgCounter = size(gallery) |
||||
|
||||
useEffect(() => { |
||||
if (sliderItemId === imgCounter) { |
||||
setSliderItemId(0) |
||||
} |
||||
const getSliderInterval = setInterval(() => { |
||||
setSliderItemId(sliderItemId + 1) |
||||
}, 5000) |
||||
return () => clearInterval(getSliderInterval) |
||||
}, [imgCounter, sliderItemId]) |
||||
|
||||
return { |
||||
buttonColor: tournamentInfo?.button_color, |
||||
buttonLexic, |
||||
description, |
||||
gallery, |
||||
logo: tournamentInfo?.media.logo, |
||||
logoInsports: tournamentInfo?.logo_insports, |
||||
onButtonClick, |
||||
onSliderSwitchClick, |
||||
period, |
||||
redirectToHomePage, |
||||
sliderItemId, |
||||
teams: tournamentInfo?.teams, |
||||
title, |
||||
} |
||||
} |
||||
@ -0,0 +1,131 @@ |
||||
import format from 'date-fns/format' |
||||
|
||||
import map from 'lodash/map' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { T9n } from 'features/T9n' |
||||
|
||||
import { useTournamentLanding } from './hooks' |
||||
import { TeamLogoImg } from './TeamLogoImg' |
||||
|
||||
import { |
||||
Wrapper, |
||||
InsportsLogo, |
||||
HeaderWrapper, |
||||
Footer, |
||||
BlockWrapper, |
||||
TournamentInfo, |
||||
DateInfo, |
||||
InsportsImg, |
||||
TournamentMedia, |
||||
TournamentLogo, |
||||
TournamentTitle, |
||||
TournamentButton, |
||||
MainInfoContainer, |
||||
TournamentDescription, |
||||
TeamsLogo, |
||||
SliderContainer, |
||||
SliderWrapper, |
||||
MainLogoImg, |
||||
MainLogoWrapper, |
||||
SliderSwitch, |
||||
SliderSwitchItem, |
||||
SliderImg, |
||||
LogoBackground, |
||||
TournamentInfoContainer, |
||||
} from './styled' |
||||
|
||||
const TournamentLanding = () => { |
||||
const { |
||||
buttonColor, |
||||
buttonLexic, |
||||
description, |
||||
gallery, |
||||
logo, |
||||
logoInsports, |
||||
onButtonClick, |
||||
onSliderSwitchClick, |
||||
period, |
||||
redirectToHomePage, |
||||
sliderItemId, |
||||
teams, |
||||
title, |
||||
} = useTournamentLanding() |
||||
|
||||
const currentYear = format(new Date(), 'Y') |
||||
|
||||
return ( |
||||
<Wrapper> |
||||
<HeaderWrapper> |
||||
{isMobileDevice && <TournamentLogo src={logo} />} |
||||
<InsportsLogo onClick={redirectToHomePage} /> |
||||
</HeaderWrapper> |
||||
<MainInfoContainer> |
||||
<BlockWrapper> |
||||
{ |
||||
gallery |
||||
? ( |
||||
<SliderWrapper> |
||||
<SliderContainer> |
||||
{map(gallery, (img, itemId) => ( |
||||
<SliderImg |
||||
isAnimatedImg={itemId === sliderItemId} |
||||
key={img.id} |
||||
src={img.url} |
||||
/> |
||||
))} |
||||
</SliderContainer> |
||||
<SliderSwitch> |
||||
{map(gallery, (img, itemId) => ( |
||||
<SliderSwitchItem |
||||
onClick={() => onSliderSwitchClick(itemId)} |
||||
slideOpacity={itemId === sliderItemId} |
||||
key={img.id} |
||||
/> |
||||
))} |
||||
</SliderSwitch> |
||||
</SliderWrapper> |
||||
) |
||||
: ( |
||||
<MainLogoWrapper> |
||||
<LogoBackground /> |
||||
<MainLogoImg src={logo} /> |
||||
</MainLogoWrapper> |
||||
) |
||||
} |
||||
<TournamentInfoContainer> |
||||
<TournamentInfo> |
||||
<DateInfo t={period} /> |
||||
<TournamentTitle t={title} /> |
||||
<TournamentDescription t={description} /> |
||||
<TournamentButton |
||||
buttonColor={buttonColor} |
||||
onClick={onButtonClick} |
||||
> |
||||
<T9n t={buttonLexic} /> |
||||
</TournamentButton> |
||||
</TournamentInfo> |
||||
<TournamentMedia> |
||||
{gallery && <TournamentLogo src={logo} />} |
||||
{teams && ( |
||||
<TeamsLogo> |
||||
{map(teams, (item) => ( |
||||
<TeamLogoImg |
||||
key={item.id} |
||||
src={item.logo} |
||||
/> |
||||
))} |
||||
</TeamsLogo> |
||||
)} |
||||
{logoInsports && <InsportsImg src='/images/insports-logo.svg' />} |
||||
</TournamentMedia> |
||||
</TournamentInfoContainer> |
||||
</BlockWrapper> |
||||
</MainInfoContainer> |
||||
<Footer>©inSports.tv {currentYear}</Footer> |
||||
</Wrapper> |
||||
) |
||||
} |
||||
|
||||
export default TournamentLanding |
||||
@ -0,0 +1,362 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { ButtonSolid } from 'features/Common' |
||||
import { Logo } from 'features/Logo' |
||||
import { T9n } from 'features/T9n' |
||||
|
||||
type ButtonProps = { |
||||
buttonColor?: string, |
||||
} |
||||
|
||||
type SliderSwitchProps = { |
||||
slideOpacity: boolean, |
||||
} |
||||
|
||||
type SliderImgProps = { |
||||
isAnimatedImg: boolean, |
||||
} |
||||
|
||||
export const Wrapper = styled.div` |
||||
width: 100vw; |
||||
height: 100vh; |
||||
color: white; |
||||
display: flex; |
||||
flex-direction: column; |
||||
` |
||||
|
||||
export const HeaderWrapper = styled.div` |
||||
background-color: rgba(19, 21, 27, 0.7); |
||||
padding: 20px 0; |
||||
padding-left: 15%; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
background-color: black; |
||||
height: 40px; |
||||
padding-left: 25px; |
||||
display: flex; |
||||
align-items: center; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const InsportsLogo = styled(Logo)` |
||||
height: 26px; |
||||
width: 80px; |
||||
cursor: pointer; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
width: 57px; |
||||
height: 18px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const MainInfoContainer = styled.div` |
||||
height: 100%; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
overflow: scroll; |
||||
position: relative; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const BlockWrapper = styled.div` |
||||
height: 100%; |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
display: block; |
||||
@media screen and (orientation: landscape){ |
||||
height: auto; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const SliderWrapper = styled.div` |
||||
position: relative; |
||||
width: 50%; |
||||
margin-right: 1%; |
||||
height: 100%; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
height: 55%; |
||||
width: 100%; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const MainLogoWrapper = styled.div` |
||||
display: flex; |
||||
justify-content: center; |
||||
width: 50%; |
||||
position: relative; |
||||
align-items: center; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
height: 55%; |
||||
width: 100%; |
||||
align-items: flex-start; |
||||
padding-top: 25px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const MainLogoImg = styled.img` |
||||
width: 35%; |
||||
height: 35%; |
||||
position: relative; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
height: 160px; |
||||
width: 160px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const LogoBackground = styled.div` |
||||
background: #294FC4; |
||||
width: 65%; |
||||
opacity: 0.7; |
||||
filter: blur(104.135px); |
||||
height: 50%; |
||||
position: absolute; |
||||
left: 50%; |
||||
transform: translateX(-50%); |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
opacity: 0.8; |
||||
filter: blur(44.1346px); |
||||
width: 100%; |
||||
height: 35%; |
||||
top: 10%; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const SliderContainer = styled.div` |
||||
height: 100%; |
||||
position: relative; |
||||
overflow: hidden; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
:before { |
||||
content: ''; |
||||
z-index: 10; |
||||
width: 100%; |
||||
height: 100%; |
||||
position: absolute; |
||||
background: linear-gradient(0deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 30%); |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const SliderImg = styled.img<SliderImgProps>` |
||||
height: 100%; |
||||
width: 100%; |
||||
opacity: ${({ isAnimatedImg }) => (isAnimatedImg ? '1' : '0')}; |
||||
position: absolute; |
||||
animation-name: ${({ isAnimatedImg }) => (isAnimatedImg ? 'sliderAnimation' : '')}; |
||||
animation-iteration-count: 1; |
||||
animation-timing-function: ease-out; |
||||
animation-duration: 5s; |
||||
|
||||
@keyframes sliderAnimation { |
||||
0% { |
||||
transform: scale(1); |
||||
} |
||||
100% { |
||||
transform: scale(1.1); |
||||
} |
||||
` |
||||
|
||||
export const SliderSwitch = styled.div` |
||||
position: absolute; |
||||
display: flex; |
||||
justify-content: center; |
||||
left: 50%; |
||||
top: 90%; |
||||
width: 100%; |
||||
transform: translateX(-50%); |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
display: none; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const SliderSwitchItem = styled.div<SliderSwitchProps>` |
||||
width: 10%; |
||||
height: 4px; |
||||
border-radius: 2px; |
||||
background-color: white; |
||||
opacity: ${({ slideOpacity }) => (slideOpacity ? '1' : '.3')};; |
||||
margin-right: 10px; |
||||
cursor: pointer; |
||||
transition: .7s; |
||||
` |
||||
|
||||
export const TournamentInfoContainer = styled.div` |
||||
width: 50%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
position: absolute; |
||||
width: 100%; |
||||
top: 40%; |
||||
padding: 0 25px; |
||||
z-index: 100; |
||||
|
||||
@media screen and (orientation: landscape){ |
||||
padding-top: 0; |
||||
} |
||||
` |
||||
: css` |
||||
height: 100%; |
||||
`};
|
||||
` |
||||
|
||||
export const TournamentInfo = styled.div` |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
margin-top: ${(isMobileDevice ? 'none' : '90px')}; |
||||
` |
||||
|
||||
export const DateInfo = styled(T9n)` |
||||
text-transform: uppercase; |
||||
background-color: rgba(0, 0, 0, 0.4); |
||||
padding: 8px 25px; |
||||
color: #B9B9B9; |
||||
width: fit-content; |
||||
border-radius: 5px; |
||||
font-size: 13px; |
||||
font-weight: 600; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 10px; |
||||
border-radius: 3px; |
||||
background-color: rgba(0, 0, 0, 0.7); |
||||
padding: 0.7em 2.5rem;` |
||||
: ''}; |
||||
` |
||||
|
||||
export const TournamentTitle = styled(T9n)` |
||||
font-weight: 700; |
||||
font-size: 50px; |
||||
margin-top: 50px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 24px; |
||||
margin: 15px 0 0; |
||||
` |
||||
: css`
|
||||
width: 50%; |
||||
`};
|
||||
` |
||||
|
||||
export const TournamentButton = styled(ButtonSolid)<ButtonProps>` |
||||
width: 320px; |
||||
height: fit-content; |
||||
font-size: 24px; |
||||
font-weight: 600; |
||||
border-radius: 5px; |
||||
margin-bottom: 90px; |
||||
padding: 20px 0; |
||||
background-color: ${({ buttonColor }) => (buttonColor ? `${buttonColor}` : '#294FC3')}; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
width: 100%; |
||||
border-radius: 10px; |
||||
font-size: 17px; |
||||
padding: 20px 50px; |
||||
margin-bottom: 0; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const TournamentDescription = styled(T9n)` |
||||
max-width: 400px; |
||||
margin: 50px 0; |
||||
font-size: 17px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 12px; |
||||
margin: 30px 0; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const TournamentMedia = styled.div` |
||||
display: flex; |
||||
align-items: center; |
||||
height: 130px; |
||||
margin-bottom: 25px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
display: none; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const TournamentLogo = styled.img` |
||||
${isMobileDevice |
||||
? css` |
||||
margin-right: 10px; |
||||
height: 26px; |
||||
` |
||||
: css` |
||||
margin-right: 50px; |
||||
height: 100%; |
||||
`};
|
||||
` |
||||
|
||||
export const TeamsLogo = styled.div` |
||||
width: 400px; |
||||
height: 100%; |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
align-items: center; |
||||
` |
||||
|
||||
export const InsportsImg = styled.img` |
||||
height: 200px; |
||||
width: 200px; |
||||
` |
||||
|
||||
export const Footer = styled.div` |
||||
font-size: 14px; |
||||
background-color: black; |
||||
padding: 16px 0; |
||||
padding-left: 15%; |
||||
opacity: .5; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
display: none; |
||||
` |
||||
: ''}; |
||||
` |
||||
@ -0,0 +1,21 @@ |
||||
import { API_ROOT, SportTypes } from 'config' |
||||
import { callApi } from 'helpers' |
||||
|
||||
type Args = { |
||||
matchId: number, |
||||
sportType: SportTypes, |
||||
} |
||||
|
||||
export const getLandingStatus = async ({ |
||||
matchId, |
||||
sportType, |
||||
}: Args): Promise<{landing_id: number | null}> => { |
||||
const config = { |
||||
method: 'GET', |
||||
} |
||||
|
||||
return callApi({ |
||||
config, |
||||
url: `${API_ROOT}/v1/landings/${sportType}/${matchId}/status`, |
||||
}) |
||||
} |
||||
@ -0,0 +1,60 @@ |
||||
import { API_ROOT } from 'config' |
||||
import { callApi } from 'helpers' |
||||
|
||||
type Tournaments = { |
||||
season: string, |
||||
season_id: number, |
||||
sport_eng: string, |
||||
sport_id: number, |
||||
sport_rus: string, |
||||
tournament_eng: string, |
||||
tournament_id: number, |
||||
tournament_rus: string, |
||||
} |
||||
|
||||
type Teams = { |
||||
id: number, |
||||
logo: string, |
||||
name_eng: string, |
||||
name_rus: string, |
||||
sport_id: number, |
||||
} |
||||
|
||||
type Gallery = { |
||||
id: string, |
||||
url: string, |
||||
} |
||||
|
||||
export type TournamentLanding = { |
||||
button_color?: string, |
||||
date_from: string, |
||||
date_to: string, |
||||
id: number, |
||||
lexic_button?: number, |
||||
lexic_description?: number, |
||||
lexic_period?: number, |
||||
lexic_title?: number, |
||||
logo_insports: boolean, |
||||
logo_team: boolean, |
||||
media: { |
||||
gallery: Array<Gallery>, |
||||
logo: string, |
||||
}, |
||||
name: string, |
||||
teams: Array<Teams>, |
||||
tournaments: Array<Tournaments>, |
||||
url_button?: string, |
||||
} |
||||
|
||||
export const getTournamentLanding = async ( |
||||
landingName: number | string, |
||||
): Promise<TournamentLanding> => { |
||||
const config = { |
||||
method: 'GET', |
||||
} |
||||
|
||||
return callApi({ |
||||
config, |
||||
url: `${API_ROOT}/v1/landings/${landingName}`, |
||||
}) |
||||
} |
||||
Loading…
Reference in new issue