Ott 799 buy match popup (#295)
* Ott 799 part 1 (#289) * refactor(799): moved reusable popup components into PopupComponents * feat(799): added subscriptions request * fix(799): fixed subscription types * Ott 799 part 2 (#293) * refactor(799): moved Price into features * refactor(799): moved popupScrollbarStyles into PopupComponents feature * refactor(799): created config of currency symbols * feat(799): added buy popup and its subscriptions selection step (#294) * feat(799): added buy popup and its subscriptions selection step * refactor(799): review comments fixkeep-around/af30b88d367751c9e05a735e4a0467a96238ef47
parent
9fcff60af8
commit
75f6911da1
@ -0,0 +1,4 @@ |
|||||||
|
export const currencySymbols = { |
||||||
|
dollar: '$', |
||||||
|
ruble: '₽', |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
export const MDASH = '\u2014' |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import isEmpty from 'lodash/isEmpty' |
||||||
|
|
||||||
|
import { MDASH } from 'config' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup' |
||||||
|
import { |
||||||
|
CloseButton, |
||||||
|
Header, |
||||||
|
HeaderActions, |
||||||
|
HeaderTitle, |
||||||
|
} from 'features/PopupComponents' |
||||||
|
import { Name } from 'features/Name' |
||||||
|
import { Steps } from 'features/BuyMatchPopup/types' |
||||||
|
|
||||||
|
import { SelectedCard } from '../SelectedCard' |
||||||
|
import { Subscriptions } from '../Subscriptions' |
||||||
|
import { Button } from '../../styled' |
||||||
|
|
||||||
|
const Wrapper = styled.div` |
||||||
|
width: 870px; |
||||||
|
height: 695px; |
||||||
|
` |
||||||
|
|
||||||
|
const Center = styled.div` |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
` |
||||||
|
|
||||||
|
export const MatchSubscriptionsStep = () => { |
||||||
|
const { |
||||||
|
close, |
||||||
|
goTo, |
||||||
|
match, |
||||||
|
selectedSubscriptions, |
||||||
|
} = useBuyMatchPopupStore() |
||||||
|
|
||||||
|
if (!match) return null |
||||||
|
|
||||||
|
return ( |
||||||
|
<Wrapper> |
||||||
|
<Header height={50}> |
||||||
|
<HeaderTitle> |
||||||
|
<Name nameObj={match.team1} /> |
||||||
|
{' '} {MDASH} {' '} |
||||||
|
<Name nameObj={match.team2} /> |
||||||
|
</HeaderTitle> |
||||||
|
<HeaderActions position='right'> |
||||||
|
<CloseButton onClick={close} /> |
||||||
|
</HeaderActions> |
||||||
|
</Header> |
||||||
|
<Subscriptions /> |
||||||
|
<SelectedCard /> |
||||||
|
<Center> |
||||||
|
<Button |
||||||
|
disabled={isEmpty(selectedSubscriptions)} |
||||||
|
onClick={(e) => goTo(e, Steps.Confirmation)} |
||||||
|
> |
||||||
|
<T9n t='buy_subscription' /> |
||||||
|
</Button> |
||||||
|
</Center> |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { SubscriptionType } from 'requests' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup/store' |
||||||
|
|
||||||
|
const List = styled.ul` |
||||||
|
display: flex; |
||||||
|
padding: 0 35px; |
||||||
|
margin-top: 9px; |
||||||
|
` |
||||||
|
|
||||||
|
type ItemProps = { |
||||||
|
active?: boolean, |
||||||
|
} |
||||||
|
|
||||||
|
const Item = styled.li.attrs(() => ({ |
||||||
|
tabIndex: 0, |
||||||
|
}))<ItemProps>` |
||||||
|
width: 50%; |
||||||
|
font-size: 20px; |
||||||
|
line-height: 42px; |
||||||
|
|
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
color: rgba(255, 255, 255, 0.5); |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
${({ active }) => ( |
||||||
|
active |
||||||
|
? css` |
||||||
|
border-bottom: 3px solid #fff; |
||||||
|
color: #fff; |
||||||
|
` |
||||||
|
: '' |
||||||
|
)} |
||||||
|
` |
||||||
|
|
||||||
|
export const PaymentPeriods = () => { |
||||||
|
const { onPeriodSelect, selectedPeriod } = useBuyMatchPopupStore() |
||||||
|
return ( |
||||||
|
<List> |
||||||
|
<Item |
||||||
|
active={selectedPeriod === SubscriptionType.Month} |
||||||
|
onClick={() => onPeriodSelect(SubscriptionType.Month)} |
||||||
|
> |
||||||
|
<T9n t='for_month' /> |
||||||
|
</Item> |
||||||
|
<Item |
||||||
|
active={selectedPeriod === SubscriptionType.Year} |
||||||
|
onClick={() => onPeriodSelect(SubscriptionType.Year)} |
||||||
|
> |
||||||
|
<T9n t='for_year' /> |
||||||
|
</Item> |
||||||
|
</List> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
import { ButtonOutline } from 'features/Common' |
||||||
|
|
||||||
|
const Wrapper = styled.div` |
||||||
|
display: flex; |
||||||
|
margin-top: 40px; |
||||||
|
margin-bottom: 30px; |
||||||
|
padding: 0 35px; |
||||||
|
` |
||||||
|
|
||||||
|
const CardInfo = styled.span` |
||||||
|
font-weight: 500; |
||||||
|
font-size: 18px; |
||||||
|
line-height: 20px; |
||||||
|
color: rgba(255, 255, 255, 0.7); |
||||||
|
` |
||||||
|
|
||||||
|
const ChangeCardButton = styled(ButtonOutline)` |
||||||
|
border: none; |
||||||
|
padding: 0; |
||||||
|
width: auto; |
||||||
|
height: auto; |
||||||
|
|
||||||
|
padding: 0 10px; |
||||||
|
margin-left: 10px; |
||||||
|
line-height: 20px; |
||||||
|
font-size: 14px; |
||||||
|
color: rgba(255, 255, 255, 0.5); |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
:hover { |
||||||
|
color: rgba(255, 255, 255); |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const SelectedCard = () => ( |
||||||
|
<Wrapper> |
||||||
|
<CardInfo>Mastercard •••• 4432</CardInfo> |
||||||
|
<ChangeCardButton> |
||||||
|
<T9n t='change_card' /> |
||||||
|
</ChangeCardButton> |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
|
||||||
|
import { PaymentPeriods } from '../PaymentPeriods' |
||||||
|
import { SubscriptionsList } from '../SubscriptionsList' |
||||||
|
|
||||||
|
const Wrapper = styled.div` |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
` |
||||||
|
|
||||||
|
const Title = styled.span` |
||||||
|
font-weight: normal; |
||||||
|
font-size: 20px; |
||||||
|
line-height: 21px; |
||||||
|
text-transform: uppercase; |
||||||
|
margin-top: 30px; |
||||||
|
padding: 0 35px; |
||||||
|
` |
||||||
|
|
||||||
|
export const Subscriptions = () => ( |
||||||
|
<Wrapper> |
||||||
|
<PaymentPeriods /> |
||||||
|
<Title> |
||||||
|
<T9n t='choose_subscription' /> |
||||||
|
</Title> |
||||||
|
<SubscriptionsList /> |
||||||
|
</Wrapper> |
||||||
|
) |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
import map from 'lodash/map' |
||||||
|
import includes from 'lodash/includes' |
||||||
|
|
||||||
|
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup/store' |
||||||
|
|
||||||
|
import { |
||||||
|
List, |
||||||
|
Item, |
||||||
|
InfoWrapper, |
||||||
|
Header, |
||||||
|
Description, |
||||||
|
Price, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
export const SubscriptionsList = () => { |
||||||
|
const { |
||||||
|
onSubscriptionSelect, |
||||||
|
selectedSubscriptions, |
||||||
|
subscriptions, |
||||||
|
} = useBuyMatchPopupStore() |
||||||
|
|
||||||
|
return ( |
||||||
|
<List> |
||||||
|
{ |
||||||
|
map(subscriptions, ({ |
||||||
|
description, |
||||||
|
header, |
||||||
|
price, |
||||||
|
subscription_id, |
||||||
|
type, |
||||||
|
}) => ( |
||||||
|
<Item |
||||||
|
key={subscription_id} |
||||||
|
onClick={() => onSubscriptionSelect(subscription_id)} |
||||||
|
active={includes(selectedSubscriptions, subscription_id)} |
||||||
|
> |
||||||
|
<InfoWrapper> |
||||||
|
<Header> |
||||||
|
{header} |
||||||
|
</Header> |
||||||
|
<Description> |
||||||
|
{description} |
||||||
|
</Description> |
||||||
|
</InfoWrapper> |
||||||
|
|
||||||
|
<Price amount={price} perPeriod={`per_${type}`} /> |
||||||
|
</Item> |
||||||
|
)) |
||||||
|
} |
||||||
|
</List> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,80 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { popupScrollbarStyles } from 'features/PopupComponents' |
||||||
|
import { Price as BasePrice } from 'features/Price' |
||||||
|
import { PriceAmount, PriceDetails } from 'features/Price/styled' |
||||||
|
|
||||||
|
export const List = styled.ul` |
||||||
|
height: 364px; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
overflow-y: auto; |
||||||
|
margin-top: 20px; |
||||||
|
padding: 0 35px; |
||||||
|
|
||||||
|
${popupScrollbarStyles} |
||||||
|
` |
||||||
|
|
||||||
|
type ItemProps = { |
||||||
|
active?: boolean, |
||||||
|
} |
||||||
|
|
||||||
|
export const Item = styled.li.attrs(() => ({ |
||||||
|
tabIndex: 0, |
||||||
|
}))<ItemProps>` |
||||||
|
width: 100%; |
||||||
|
min-height: 108px; |
||||||
|
padding: 20px; |
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 100%), #5C5C5C; |
||||||
|
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); |
||||||
|
border-radius: 2px; |
||||||
|
|
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
align-items: center; |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
:first-child { |
||||||
|
margin-top: 2px; |
||||||
|
} |
||||||
|
|
||||||
|
:not(:last-child) { |
||||||
|
margin-bottom: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
${({ active }) => ( |
||||||
|
active ? 'background-color: #294FC3' : '' |
||||||
|
)}; |
||||||
|
` |
||||||
|
|
||||||
|
export const InfoWrapper = styled.div` |
||||||
|
width: 80%; |
||||||
|
` |
||||||
|
|
||||||
|
export const Header = styled.span` |
||||||
|
font-weight: 600; |
||||||
|
font-size: 20px; |
||||||
|
line-height: 23px; |
||||||
|
letter-spacing: 0.03em; |
||||||
|
` |
||||||
|
|
||||||
|
export const Description = styled.p` |
||||||
|
margin-top: 10px; |
||||||
|
font-weight: 500; |
||||||
|
font-size: 17px; |
||||||
|
letter-spacing: 0.03em; |
||||||
|
` |
||||||
|
|
||||||
|
export const Price = styled(BasePrice)` |
||||||
|
${PriceAmount} { |
||||||
|
font-size: 24px; |
||||||
|
line-height: 24px; |
||||||
|
font-weight: normal; |
||||||
|
} |
||||||
|
|
||||||
|
${PriceDetails} { |
||||||
|
font-weight: 500; |
||||||
|
font-size: 12px; |
||||||
|
line-height: 18px; |
||||||
|
} |
||||||
|
` |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup' |
||||||
|
|
||||||
|
import { MatchSubscriptionsStep } from './components/MatchSubscriptions' |
||||||
|
import { Modal } from './styled' |
||||||
|
import { Steps } from './types' |
||||||
|
|
||||||
|
export * from './store' |
||||||
|
|
||||||
|
const Empty = () => null |
||||||
|
|
||||||
|
const components = { |
||||||
|
[Steps.Subscriptions]: MatchSubscriptionsStep, |
||||||
|
[Steps.CardSelection]: Empty, |
||||||
|
[Steps.Confirmation]: Empty, |
||||||
|
[Steps.Success]: Empty, |
||||||
|
[Steps.Error]: Empty, |
||||||
|
} |
||||||
|
|
||||||
|
export const BuyMatchPopup = () => { |
||||||
|
const { |
||||||
|
close, |
||||||
|
currentStep, |
||||||
|
match, |
||||||
|
} = useBuyMatchPopupStore() |
||||||
|
|
||||||
|
if (!match || !currentStep) return null |
||||||
|
|
||||||
|
const Step = components[currentStep] |
||||||
|
|
||||||
|
return ( |
||||||
|
<Modal |
||||||
|
isOpen |
||||||
|
close={close} |
||||||
|
withCloseButton={false} |
||||||
|
> |
||||||
|
<Step /> |
||||||
|
</Modal> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,71 @@ |
|||||||
|
import type { MouseEvent } from 'react' |
||||||
|
import { useCallback, useState } from 'react' |
||||||
|
|
||||||
|
import last from 'lodash/last' |
||||||
|
|
||||||
|
import type { Match } from 'features/Matches/hooks' |
||||||
|
import { useMatchPopupStore } from 'features/MatchPopup' |
||||||
|
import { Steps } from 'features/BuyMatchPopup/types' |
||||||
|
|
||||||
|
import { useSubscriptions } from './useSubscriptions' |
||||||
|
|
||||||
|
type MatchData = Pick<Match, ( |
||||||
|
'id' |
||||||
|
| 'team1' |
||||||
|
| 'team2' |
||||||
|
)> | null |
||||||
|
|
||||||
|
export const useBuyMatchPopup = () => { |
||||||
|
const { closePopup: closeMatchPopup } = useMatchPopupStore() |
||||||
|
const [steps, setSteps] = useState<Array<Steps>>([]) |
||||||
|
const [match, setMatch] = useState<MatchData>(null) |
||||||
|
const { |
||||||
|
onPeriodSelect, |
||||||
|
onSubscriptionSelect, |
||||||
|
resetSubscriptions, |
||||||
|
selectedPeriod, |
||||||
|
selectedSubscriptions, |
||||||
|
subscriptions, |
||||||
|
} = useSubscriptions() |
||||||
|
|
||||||
|
const goTo = useCallback( |
||||||
|
(e: MouseEvent<HTMLButtonElement>, step: Steps) => setSteps((state) => { |
||||||
|
e.stopPropagation() |
||||||
|
return [...state, step] |
||||||
|
}), |
||||||
|
[], |
||||||
|
) |
||||||
|
const goBack = useCallback(() => setSteps((state) => { |
||||||
|
const newState = [...state] |
||||||
|
newState.pop() |
||||||
|
return newState |
||||||
|
}), []) |
||||||
|
|
||||||
|
const openPopup = (e: MouseEvent<HTMLButtonElement>, matchData: MatchData) => { |
||||||
|
closeMatchPopup() |
||||||
|
e.stopPropagation() |
||||||
|
setMatch(matchData) |
||||||
|
setSteps([Steps.Subscriptions]) |
||||||
|
} |
||||||
|
|
||||||
|
const closePopup = () => { |
||||||
|
setMatch(null) |
||||||
|
setSteps([]) |
||||||
|
resetSubscriptions() |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
close: closePopup, |
||||||
|
currentStep: last(steps), |
||||||
|
goBack, |
||||||
|
goTo, |
||||||
|
match, |
||||||
|
onPeriodSelect, |
||||||
|
onSubscriptionSelect, |
||||||
|
open: openPopup, |
||||||
|
resetSubscriptions, |
||||||
|
selectedPeriod, |
||||||
|
selectedSubscriptions, |
||||||
|
subscriptions, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
import { |
||||||
|
useMemo, |
||||||
|
useState, |
||||||
|
useEffect, |
||||||
|
useCallback, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import filter from 'lodash/filter' |
||||||
|
import includes from 'lodash/includes' |
||||||
|
|
||||||
|
import type { MatchSubscriptions } from 'requests' |
||||||
|
import { SubscriptionType, getMatchSubscriptions } from 'requests' |
||||||
|
|
||||||
|
export const useSubscriptions = () => { |
||||||
|
const [selectedPeriod, setSelectedPeriod] = useState(SubscriptionType.Month) |
||||||
|
const [subscriptionsList, setSubscriptionsList] = useState<MatchSubscriptions>([]) |
||||||
|
const [selectedSubscriptions, setSelectedSubscriptions] = useState<Array<number>>([]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getMatchSubscriptions().then(setSubscriptionsList) |
||||||
|
}, []) |
||||||
|
|
||||||
|
const subscriptions = useMemo( |
||||||
|
() => filter(subscriptionsList, { type: selectedPeriod }), |
||||||
|
[selectedPeriod, subscriptionsList], |
||||||
|
) |
||||||
|
|
||||||
|
const onSubscriptionSelect = (id: number) => { |
||||||
|
if (includes(selectedSubscriptions, id)) { |
||||||
|
const newSubscriptions = filter( |
||||||
|
selectedSubscriptions, |
||||||
|
(subscriptionId) => subscriptionId !== id, |
||||||
|
) |
||||||
|
setSelectedSubscriptions(newSubscriptions) |
||||||
|
} else { |
||||||
|
setSelectedSubscriptions([...selectedSubscriptions, id]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const resetSubscriptions = useCallback(() => { |
||||||
|
setSelectedPeriod(SubscriptionType.Month) |
||||||
|
setSelectedSubscriptions([]) |
||||||
|
}, []) |
||||||
|
|
||||||
|
return { |
||||||
|
onPeriodSelect: setSelectedPeriod, |
||||||
|
onSubscriptionSelect, |
||||||
|
resetSubscriptions, |
||||||
|
selectedPeriod, |
||||||
|
selectedSubscriptions, |
||||||
|
subscriptions, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
import type { ReactNode } from 'react' |
||||||
|
import { createContext, useContext } from 'react' |
||||||
|
|
||||||
|
import { useBuyMatchPopup } from './hooks' |
||||||
|
|
||||||
|
type Context = ReturnType<typeof useBuyMatchPopup> |
||||||
|
type Props = { children: ReactNode } |
||||||
|
|
||||||
|
const BuyMatchPopupContext = createContext({} as Context) |
||||||
|
|
||||||
|
export const BuyMatchPopupStore = ({ children }: Props) => { |
||||||
|
const value = useBuyMatchPopup() |
||||||
|
return ( |
||||||
|
<BuyMatchPopupContext.Provider value={value}> |
||||||
|
{children} |
||||||
|
</BuyMatchPopupContext.Provider> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export const useBuyMatchPopupStore = () => useContext(BuyMatchPopupContext) |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { devices } from 'config' |
||||||
|
|
||||||
|
import { Modal as BaseModal } from 'features/Modal' |
||||||
|
import { ModalWindow } from 'features/Modal/styled' |
||||||
|
import { ButtonSolid } from 'features/Common' |
||||||
|
|
||||||
|
export const Modal = styled(BaseModal)` |
||||||
|
background-color: rgba(0, 0, 0, 0.7); |
||||||
|
|
||||||
|
${ModalWindow} { |
||||||
|
min-width: 517px; |
||||||
|
min-height: 310px; |
||||||
|
padding: 20px 0; |
||||||
|
background-color: #3F3F3F; |
||||||
|
border-radius: 5px; |
||||||
|
|
||||||
|
@media ${devices.mobile} { |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const Button = styled(ButtonSolid)` |
||||||
|
min-width: 142px; |
||||||
|
width: auto; |
||||||
|
height: 50px; |
||||||
|
padding: 0 20px; |
||||||
|
background-color: #294FC4; |
||||||
|
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); |
||||||
|
border-radius: 5px; |
||||||
|
|
||||||
|
:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
} |
||||||
|
` |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
export enum Steps { |
||||||
|
CardSelection = 'CardSelection', |
||||||
|
Confirmation = 'Confirmation', |
||||||
|
Error = 'Error', |
||||||
|
Subscriptions = 'Subscriptions', |
||||||
|
Success = 'Success', |
||||||
|
} |
||||||
@ -1,13 +0,0 @@ |
|||||||
import { Close } from 'features/Icons/Close' |
|
||||||
import { useMatchPopupStore } from 'features/MatchPopup/store' |
|
||||||
|
|
||||||
import { BaseButton } from '../../styled' |
|
||||||
|
|
||||||
export const CloseButton = () => { |
|
||||||
const { closePopup } = useMatchPopupStore() |
|
||||||
return ( |
|
||||||
<BaseButton onClick={closePopup}> |
|
||||||
<Close /> |
|
||||||
</BaseButton> |
|
||||||
) |
|
||||||
} |
|
||||||
@ -0,0 +1,29 @@ |
|||||||
|
import styled from 'styled-components/macro' |
||||||
|
|
||||||
|
import { devices } from 'config' |
||||||
|
|
||||||
|
export const BaseButton = styled.button` |
||||||
|
padding: 0; |
||||||
|
border: none; |
||||||
|
background: none; |
||||||
|
|
||||||
|
cursor: pointer; |
||||||
|
width: 34px; |
||||||
|
height: 34px; |
||||||
|
color: white; |
||||||
|
background-color: rgba(255, 255, 255, 0.12); |
||||||
|
background-position: center; |
||||||
|
background-repeat: no-repeat; |
||||||
|
border-radius: 50%; |
||||||
|
|
||||||
|
:hover { |
||||||
|
background-color: rgba(255, 255, 255, 0.22); |
||||||
|
} |
||||||
|
|
||||||
|
@media ${devices.mobile} { |
||||||
|
width: 24px; |
||||||
|
height: 24px; |
||||||
|
background-color: transparent; |
||||||
|
border-radius: 0; |
||||||
|
} |
||||||
|
` |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
import type { MouseEvent } from 'react' |
||||||
|
|
||||||
|
import { Close } from 'features/Icons/Close' |
||||||
|
|
||||||
|
import { BaseButton } from '../BaseButton' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
onClick: (e: MouseEvent<HTMLButtonElement>) => void, |
||||||
|
} |
||||||
|
|
||||||
|
export const CloseButton = ({ onClick }: Props) => ( |
||||||
|
<BaseButton onClick={onClick}> |
||||||
|
<Close /> |
||||||
|
</BaseButton> |
||||||
|
) |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { devices } from 'config' |
||||||
|
|
||||||
|
import { BaseButton } from '../BaseButton' |
||||||
|
|
||||||
|
type HeaderProps = { |
||||||
|
height?: number, |
||||||
|
} |
||||||
|
|
||||||
|
export const Header = styled.div<HeaderProps>` |
||||||
|
position: relative; |
||||||
|
height: ${({ height = 35 }) => height}px; |
||||||
|
display: flex; |
||||||
|
|
||||||
|
@media ${devices.mobile} { |
||||||
|
height: 52px; |
||||||
|
background-color: rgba(255, 255, 255, 0.1); |
||||||
|
padding: 0 12px; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
type HeaderActionsProps = { |
||||||
|
marginLeft?: number, |
||||||
|
position: 'left' | 'right', |
||||||
|
} |
||||||
|
|
||||||
|
export const HeaderActions = styled.div<HeaderActionsProps>` |
||||||
|
position: absolute; |
||||||
|
display: flex; |
||||||
|
|
||||||
|
${({ marginLeft = 0, position }) => css` |
||||||
|
${position}: 20px; |
||||||
|
margin-left: ${marginLeft}px; |
||||||
|
`}
|
||||||
|
|
||||||
|
@media ${devices.mobile} { |
||||||
|
${({ position }) => css` |
||||||
|
${position}: 12px; |
||||||
|
margin-left: 0; |
||||||
|
`}
|
||||||
|
} |
||||||
|
|
||||||
|
${BaseButton}:not(:last-child) { |
||||||
|
margin-right: 20px; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const HeaderTitle = styled.h2` |
||||||
|
position: absolute; |
||||||
|
width: 70%; |
||||||
|
left: 50%; |
||||||
|
transform: translateX(-50%); |
||||||
|
|
||||||
|
font-weight: 600; |
||||||
|
font-size: 24px; |
||||||
|
line-height: 42px; |
||||||
|
color: #FFFFFF; |
||||||
|
text-align: center; |
||||||
|
|
||||||
|
@media ${devices.mobile} { |
||||||
|
font-size: 19px; |
||||||
|
line-height: 28px; |
||||||
|
text-align: center; |
||||||
|
text-overflow: ellipsis; |
||||||
|
white-space: nowrap; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
` |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
export * from './BaseButton' |
||||||
|
export * from './CloseButton' |
||||||
|
export * from './Header' |
||||||
|
export * from './popupScrollbarStyles' |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
import { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { customScrollbar } from 'features/Common' |
||||||
|
|
||||||
|
export const popupScrollbarStyles = css` |
||||||
|
${customScrollbar} |
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb { |
||||||
|
border-radius: 3px; |
||||||
|
background: rgba(196, 196, 196, 0.3); |
||||||
|
} |
||||||
|
|
||||||
|
::-webkit-scrollbar-track, |
||||||
|
::-webkit-scrollbar-corner { |
||||||
|
border-radius: 3px; |
||||||
|
background: rgba(103, 103, 103, 0.3); |
||||||
|
} |
||||||
|
` |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
import { currencySymbols } from 'config' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
|
||||||
|
import { |
||||||
|
PriceWrapper, |
||||||
|
PriceAmount, |
||||||
|
PriceDetails, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
amount: number, |
||||||
|
className?: string, |
||||||
|
currency?: string, |
||||||
|
perPeriod?: string, |
||||||
|
} |
||||||
|
|
||||||
|
export const Price = ({ |
||||||
|
amount, |
||||||
|
className, |
||||||
|
currency = currencySymbols.ruble, |
||||||
|
perPeriod = 'month', |
||||||
|
}: Props) => ( |
||||||
|
<PriceWrapper className={className}> |
||||||
|
<PriceAmount>{amount}</PriceAmount> |
||||||
|
<PriceDetails> |
||||||
|
{currency} / <T9n t={perPeriod} /> |
||||||
|
</PriceDetails> |
||||||
|
</PriceWrapper> |
||||||
|
) |
||||||
@ -1,31 +0,0 @@ |
|||||||
import { useLexicsStore } from 'features/LexicsStore' |
|
||||||
|
|
||||||
import { |
|
||||||
PriceWrapper, |
|
||||||
PriceAmount, |
|
||||||
PriceDetails, |
|
||||||
} from './styled' |
|
||||||
|
|
||||||
type Props = { |
|
||||||
amount: number, |
|
||||||
currency?: string, |
|
||||||
perPeriod?: string, |
|
||||||
} |
|
||||||
|
|
||||||
export const Price = ({ |
|
||||||
amount, |
|
||||||
currency = '₽', |
|
||||||
perPeriod = 'month', |
|
||||||
}: Props) => { |
|
||||||
const { translate } = useLexicsStore() |
|
||||||
const perPeriodTranslated = translate(perPeriod) |
|
||||||
|
|
||||||
return ( |
|
||||||
<PriceWrapper> |
|
||||||
<PriceAmount>{amount}</PriceAmount> |
|
||||||
<PriceDetails> |
|
||||||
{currency} / {perPeriodTranslated} |
|
||||||
</PriceDetails> |
|
||||||
</PriceWrapper> |
|
||||||
) |
|
||||||
} |
|
||||||
@ -0,0 +1,28 @@ |
|||||||
|
import { API_ROOT } from 'config' |
||||||
|
import { callApi } from 'helpers' |
||||||
|
|
||||||
|
export enum SubscriptionType { |
||||||
|
Month = 'month', |
||||||
|
Year = 'year', |
||||||
|
} |
||||||
|
|
||||||
|
type MatchSubscription = { |
||||||
|
description: string, |
||||||
|
header: string, |
||||||
|
price: number, |
||||||
|
subscription_id: number, |
||||||
|
type: SubscriptionType, |
||||||
|
} |
||||||
|
|
||||||
|
export type MatchSubscriptions = Array<MatchSubscription> |
||||||
|
|
||||||
|
export const getMatchSubscriptions = (): Promise<MatchSubscriptions> => { |
||||||
|
const config = { |
||||||
|
method: 'GET', |
||||||
|
} |
||||||
|
|
||||||
|
return callApi({ |
||||||
|
config, |
||||||
|
url: `${API_ROOT}/account/get-subscriptions`, |
||||||
|
}) |
||||||
|
} |
||||||
Loading…
Reference in new issue