Ott 496 get set delete subscriptions (#207)

* Ott 496 get set delete subscriptions 1 (#205)

* feat(ott-496): part of the code

* fix(ott-496): minor change in checkbo

* fix(ott-496): fixed get subscriptions types

* fix(ott-496): deleted unnecessary exports

* fix(ott-496): fixed types

* fix(ott-496): fixed types

* fix(ott-496): deleted fullwidth from click outside

* fix(ott-496): finish (#206)

* fix(ott-496): finish

* fix(ott-496): bug fix

* fix(ott-496): added switch

* fix(ott-496): bug fix according to Mirlan's comments

* fix(ott-496): finish
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Armen 5 years ago committed by GitHub
parent 26493b0c92
commit ee715e19e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/config/lexics/userAccount.tsx
  2. 1
      src/config/procedures.tsx
  3. 2
      src/features/Combobox/index.tsx
  4. 4
      src/features/Combobox/styled.tsx
  5. 10
      src/features/Common/Checkbox/index.tsx
  6. 1
      src/features/Common/Checkbox/styled.tsx
  7. 14
      src/features/Common/Radio/index.tsx
  8. 2
      src/features/Header/styled.tsx
  9. 4
      src/features/Login/index.tsx
  10. 3
      src/features/Login/styled.tsx
  11. 2
      src/features/MatchPage/MatchProfileCard/styled.tsx
  12. 6
      src/features/Modal/styled.tsx
  13. 8
      src/features/OutsideClick/index.tsx
  14. 5
      src/features/Register/components/CardStep/index.tsx
  15. 4
      src/features/Register/components/RegistrationStep/index.tsx
  16. 4
      src/features/Register/components/RegistrationSuccessful/index.tsx
  17. 2
      src/features/Register/components/SubscriptionsStep/styled.tsx
  18. 2
      src/features/UserAccount/components/CardNumber/index.tsx
  19. 2
      src/features/UserAccount/components/PageTitle/index.tsx
  20. 67
      src/features/UserAccount/components/Subscription/index.tsx
  21. 84
      src/features/UserAccount/components/Subscription/styled.tsx
  22. 98
      src/features/UserAccount/components/SubscriptionsForm/index.tsx
  23. 16
      src/features/UserAccount/components/SubscriptionsForm/styled.tsx
  24. 70
      src/features/UserAccount/components/SubscriptionsModal/index.tsx
  25. 46
      src/features/UserAccount/components/SubscriptionsModal/styled.tsx
  26. 4
      src/features/UserAccount/components/TextNoBorder/index.tsx
  27. 15
      src/features/UserAccount/components/UserAccountButton/index.tsx
  28. 29
      src/features/UserAccount/components/UserAccountSubscription/index.tsx
  29. 35
      src/features/UserAccount/components/UserAccountSubscription/styled.tsx
  30. 72
      src/features/UserAccount/components/UserAccountSubscriptionMatch/index.tsx
  31. 117
      src/features/UserAccount/components/UserAccountSubscriptionMatch/styled.tsx
  32. 31
      src/features/UserAccount/hooks/useSubscriptions.tsx
  33. 92
      src/features/UserAccount/hooks/useUserSubscriptions.tsx
  34. 88
      src/features/UserAccount/index.tsx
  35. 50
      src/features/UserAccount/styled.tsx
  36. 34
      src/requests/getSubscriptions.tsx
  37. 33
      src/requests/saveUserCustomSubscription.tsx
  38. 10
      src/requests/saveUserSubscription.tsx

@ -4,6 +4,7 @@ export const userAccountLexics = {
add_card: 8313, add_card: 8313,
change: 12614, change: 12614,
country: 835, country: 835,
delete: 848,
delete_card: 8692, delete_card: 8692,
lastname: 858, lastname: 858,
mail: 12912, mail: 12912,

@ -8,6 +8,7 @@ export const PROCEDURES = {
get_player_matches: 'get_player_matches', get_player_matches: 'get_player_matches',
get_players_teams_tournaments: 'get_players_teams_tournaments', get_players_teams_tournaments: 'get_players_teams_tournaments',
get_sport_list: 'get_sport_list', get_sport_list: 'get_sport_list',
get_subscriptions: 'get_subscriptions',
get_team_info: 'get_team_info', get_team_info: 'get_team_info',
get_team_matches: 'get_team_matches', get_team_matches: 'get_team_matches',
get_tournament_info: 'get_tournament_info', get_tournament_info: 'get_tournament_info',

@ -80,7 +80,7 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
/> />
)} )}
{isOpen && !isEmpty(options) && ( {isOpen && !isEmpty(options) && (
<OutsideClick fullWidth onClick={onOutsideClick}> <OutsideClick onClick={onOutsideClick}>
<PopOver <PopOver
ref={popoverRef} ref={popoverRef}
> >

@ -17,8 +17,8 @@ export const PopOver = styled.ul`
overflow: auto; overflow: auto;
z-index: 2; z-index: 2;
background: rgb(102, 102, 102); background: rgb(102, 102, 102);
${customScrollbar} ${customScrollbar};
${customStylesMixin} ${customStylesMixin};
` `
export const ListOption = styled.li<{isHighlighted?: boolean}>` export const ListOption = styled.li<{isHighlighted?: boolean}>`

@ -10,8 +10,7 @@ type Props = Pick<InputHTMLAttributes<HTMLInputElement>, (
| 'checked' | 'checked'
| 'id' | 'id'
| 'name' | 'name'
| 'value' | 'onClick'
| 'onChange'
)> & { )> & {
label?: string, label?: string,
} }
@ -21,16 +20,15 @@ export const Checkbox = ({
id, id,
label, label,
name, name,
onChange, onClick,
value,
}: Props) => ( }: Props) => (
<Wrapper> <Wrapper>
<Input <Input
id={id}
type='checkbox' type='checkbox'
name={name} name={name}
value={value}
checked={checked} checked={checked}
onChange={onChange} onClick={onClick}
/> />
<Label htmlFor={id}>{label}</Label> <Label htmlFor={id}>{label}</Label>
</Wrapper> </Wrapper>

@ -13,6 +13,7 @@ export const Wrapper = styled.div`
` `
export const Label = styled.label` export const Label = styled.label`
display: flex;
color: ${({ theme: { colors } }) => colors.text}; color: ${({ theme: { colors } }) => colors.text};
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;

@ -10,8 +10,7 @@ type Props = Pick<InputHTMLAttributes<HTMLInputElement>, (
| 'checked' | 'checked'
| 'id' | 'id'
| 'name' | 'name'
| 'value' | 'onClick'
| 'onChange'
)> & { )> & {
label?: string, label?: string,
} }
@ -21,17 +20,18 @@ export const Radio = ({
id, id,
label = '', label = '',
name, name,
onChange, onClick,
value,
}: Props) => ( }: Props) => (
<Wrapper> <Wrapper>
<Input <Input
id={id}
type='radio' type='radio'
name={name} name={name}
value={value}
checked={checked} checked={checked}
onChange={onChange} onClick={onClick}
/> />
<Label htmlFor={id}>{label}</Label> <Label htmlFor={id}>
{label}
</Label>
</Wrapper> </Wrapper>
) )

@ -10,7 +10,7 @@ export const HomeButtonLink = styled(Link)`
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
&:hover{ &:hover {
background-image: url('/images/home-btn-hover.svg'); background-image: url('/images/home-btn-hover.svg');
cursor:pointer; cursor:pointer;
} }

@ -35,9 +35,7 @@ const LoginForm = () => {
<CenterBlock> <CenterBlock>
<Logo /> <Logo />
<Form onSubmit={handleSubmit}> <Form onSubmit={handleSubmit}>
<BlockTitle> <BlockTitle t='step_title_login' />
<T9n t='step_title_login' />
</BlockTitle>
<Input <Input
type='email' type='email'

@ -4,6 +4,7 @@ import styled from 'styled-components/macro'
import { devices } from 'config/devices' import { devices } from 'config/devices'
import { outlineButtonStyles } from 'features/Common' import { outlineButtonStyles } from 'features/Common'
import { T9n } from 'features/T9n'
export const CenterBlock = styled.div` export const CenterBlock = styled.div`
display: flex; display: flex;
@ -40,7 +41,7 @@ export const Form = styled.form<{forRegPage?: boolean}>`
} }
` `
export const BlockTitle = styled.span` export const BlockTitle = styled(T9n)`
display: block; display: block;
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;

@ -27,7 +27,7 @@ export const Teams = styled.div`
export const StyledLink = styled(ProfileLink)` export const StyledLink = styled(ProfileLink)`
font-weight: bold; font-weight: bold;
color: white; color: white;
&:hover{ &:hover {
text-decoration: underline; text-decoration: underline;
} }
` `

@ -16,7 +16,7 @@ export const ModalContainer = styled.div`
font-weight: 600; font-weight: 600;
` `
export const ModalWindow = styled.div` export const ModalWindow = styled.div`
background: #3F3F3F; background-color: #313131;
position: relative; position: relative;
padding: 15px; padding: 15px;
box-shadow: 0px 5px 30px rgba(0, 0, 0, 0.7); box-shadow: 0px 5px 30px rgba(0, 0, 0, 0.7);
@ -26,7 +26,7 @@ export const ModalWindow = styled.div`
export const ModalCloseButton = styled(CloseButton)` export const ModalCloseButton = styled(CloseButton)`
margin-right: 19px; margin-right: 19px;
margin-top: 16px; margin-top: 16px;
width: 12.82px; width: 16px;
height: 12.82px; height: 16px;
cursor: pointer; cursor: pointer;
` `

@ -8,23 +8,19 @@ import { useOutsideClickEffect } from './hooks'
type Props = { type Props = {
/** элемент, которому необходим функционал `OutsideClick` */ /** элемент, которому необходим функционал `OutsideClick` */
children: ReactNode, children: ReactNode,
fullWidth?: boolean,
/** функция-коллбек, отрабатывающая по клику вне области элемента */ /** функция-коллбек, отрабатывающая по клику вне области элемента */
onClick: (event: MouseEvent) => void, onClick: (event: MouseEvent) => void,
} }
const OutsideClickWrapper = styled.div<{fullWidth: boolean}>` const OutsideClickWrapper = styled.div``
width: ${({ fullWidth }) => (fullWidth ? '100%' : '')};
`
export const OutsideClick = ({ export const OutsideClick = ({
children, children,
fullWidth = false,
onClick, onClick,
}: Props) => { }: Props) => {
const wrapperRef = useOutsideClickEffect({ onClick }) const wrapperRef = useOutsideClickEffect({ onClick })
return ( return (
<OutsideClickWrapper fullWidth={fullWidth} ref={wrapperRef}> <OutsideClickWrapper ref={wrapperRef}>
{children} {children}
</OutsideClickWrapper> </OutsideClickWrapper>
) )

@ -16,10 +16,7 @@ export const CardStep = () => {
const defaultMessage = translate('please_fill_out_this_field') const defaultMessage = translate('please_fill_out_this_field')
return ( return (
<Form> <Form>
<BlockTitle> <BlockTitle t='step_title_card' />
<T9n t='step_title_card' />
</BlockTitle>
<Card> <Card>
<Input <Input
labelLexic='form_card_number' labelLexic='form_card_number'

@ -27,9 +27,7 @@ const Registration = () => {
return ( return (
<Form onSubmit={handleSubmit} forRegPage> <Form onSubmit={handleSubmit} forRegPage>
<BlockTitle> <BlockTitle t='step_title_registration' />
<T9n t='step_title_registration' />
</BlockTitle>
<Input <Input
value={readFormValue(formIds.email)} value={readFormValue(formIds.email)}
error={readFormError(formIds.email)} error={readFormError(formIds.email)}

@ -13,9 +13,7 @@ import {
export const RegistrationSuccessful = () => ( export const RegistrationSuccessful = () => (
<Container> <Container>
<BlockTitle> <BlockTitle t='registration_successful' />
<T9n t='registration_successful' />
</BlockTitle>
<GreenTick /> <GreenTick />
<ButtonLink to={PAGES.login}> <ButtonLink to={PAGES.login}>
<T9n t='login' /> <T9n t='login' />

@ -41,8 +41,6 @@ export const SubscriptionsBlock = styled.div<SubscriptionsBlockProps>`
return '80px' return '80px'
}}; }};
} }
` `
export const BlockTitle = styled.span` export const BlockTitle = styled.span`

@ -21,7 +21,7 @@ export const CardNumber = ({
<Radio <Radio
label={label} label={label}
checked={checked} checked={checked}
onChange={() => {}} onClick={() => {}}
/> />
<VisaLogoWrapper visa={visa} /> <VisaLogoWrapper visa={visa} />
<CardNumberTextWrapper> <CardNumberTextWrapper>

@ -20,7 +20,7 @@ export const PageTitle = ({ titleText }: Props) => {
<Logo /> <Logo />
</Link> </Link>
<TitleTextWrapper> <TitleTextWrapper>
<BlockTitle>{titleText}</BlockTitle> <BlockTitle t={titleText} />
</TitleTextWrapper> </TitleTextWrapper>
</PageTitleWrapper> </PageTitleWrapper>
) )

@ -0,0 +1,67 @@
import React from 'react'
import { Price } from 'features/Register/components/Price'
import { Radio } from 'features/Common/Radio'
import { Checkbox } from 'features/Common/Checkbox'
import {
CheckboxWrapper,
SubscriptionWrapper,
BoldTextWrapper,
NormalTextWrapper,
} from './styled'
import { PriceWrapper } from '../CardNumber/styled'
type Props = {
amount: number,
checked?: boolean,
id: string,
inputType?: string,
label?: string,
noMarginBottom?: boolean,
noMarginTop?: boolean,
packageAction?: string,
packageName?: string,
selectSubscription: () => void,
}
export const Subscription = ({
amount,
checked,
id,
inputType,
label,
noMarginBottom,
noMarginTop,
packageAction,
packageName,
selectSubscription,
}: Props) => (
<SubscriptionWrapper
noMarginTop={noMarginTop}
noMarginBottom={noMarginBottom}
>
{inputType === 'radio' && (
<Radio
id={id}
checked={checked}
onClick={selectSubscription}
label={label}
/>
)}
{inputType === 'checkbox' && (
<CheckboxWrapper>
<Checkbox
checked={checked}
onClick={selectSubscription}
label={label}
/>
</CheckboxWrapper>
)}
{packageName && <BoldTextWrapper>{packageName}</BoldTextWrapper>}
{packageAction && <NormalTextWrapper>{packageAction}</NormalTextWrapper>}
<PriceWrapper>
<Price amount={amount} />
</PriceWrapper>
</SubscriptionWrapper>
)

@ -0,0 +1,84 @@
import styled, { css } from 'styled-components/macro'
import { Label as CheckboxLabel } from 'features/Common/Checkbox/styled'
import { Label as RadioLabel } from 'features/Common/Radio/styled'
import { TextWrapper } from '../CardNumber/styled'
type Props = {
noMarginBottom?: boolean,
noMarginTop?: boolean,
}
export const SubscriptionWrapperStyles = css<Props>`
display: flex;
align-items: center;
justify-content: flex-start;
height: 48px;
background-color: #3F3F3F;
border-top: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')};
border-bottom: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')};
border-radius: ${({ noMarginBottom, noMarginTop }) => {
switch (true) {
case noMarginTop && noMarginBottom:
return '0'
case noMarginTop:
return '0 0 2px 2px'
case noMarginBottom:
return '2px 2px 0 0'
default:
return '2px'
}
}};
margin-top: ${({ noMarginTop }) => (noMarginTop ? '0' : '20px')};
margin-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '0' : '20px')};
width: 100%;
${RadioLabel}::before {
margin-left: 22px;
}
`
export const SubscriptionWrapper = styled.div`
${SubscriptionWrapperStyles};
label{
font-size: 16px;
}
&:nth-child(n+1) {
border-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '1px solid #000' : '')};
border-top: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? 'none' : '')};
}
`
export const CheckboxWrapper = styled.div`
${CheckboxLabel} {
font-weight: bold;
font-size: 16px;
line-height: 24px;
&::before {
margin-left: 22px;
}
}
`
export const BoldTextWrapper = styled(TextWrapper)`
color: #fff;
font-weight: bold;
font-size: 20px;
margin-right: 24px;
&:hover {
cursor: default;
}
`
export const NormalTextWrapper = styled(TextWrapper)`
color: #fff;
font-weight: normal;
font-size: 16px;
margin-right: 24px;
`

@ -0,0 +1,98 @@
import React, { Fragment } from 'react'
import map from 'lodash/map'
import isNumber from 'lodash/isNumber'
import isArray from 'lodash/isArray'
import find from 'lodash/find'
import type { SportList } from 'requests/getSportList'
import { useLexicsStore } from 'features/LexicsStore'
import type { Subscription, UserSubscriptions } from '../../hooks/useUserSubscriptions'
import { UserAccountButton } from '../UserAccountButton'
import { UserAccountSubscription } from '../UserAccountSubscription'
import { UserAccountSubscriptionMatch } from '../UserAccountSubscriptionMatch'
import { TextNoBorder } from '../TextNoBorder'
import {
UserAccountBlockTitle,
SubscriptionTitle,
} from './styled'
type Props = {
deleteUserSubscription: (subscription: Subscription, sport: number) => void,
open: () => void,
selectedSubscription: number | null,
selectedSubscriptionLexic: string,
sportList: SportList,
userSubscriptions: any,
}
export const SubscriptionsForm = ({
deleteUserSubscription,
open,
selectedSubscription,
selectedSubscriptionLexic,
sportList,
userSubscriptions,
}: Props) => {
const { translate } = useLexicsStore()
return (
<Fragment>
<UserAccountBlockTitle t='subscriptions' />
{isNumber(selectedSubscription) && (
<UserAccountSubscription
amount={1999}
inputType='radio'
packageName={selectedSubscriptionLexic}
/>
)}
{map(userSubscriptions, (subscription) => {
const sportName = find(sportList, (item) => subscription.sport === item.id)
return (
<Fragment key={subscription.sport}>
{sportName && <SubscriptionTitle t={sportName.lexic} />}
{map(subscription, (item: UserSubscriptions) => {
if (isArray(item)) {
return map(item, (subscriptionObj) => {
if (subscriptionObj.date) {
return (
<UserAccountSubscriptionMatch
key={subscriptionObj.id}
noMarginBottom
amount={1999}
matchData={subscriptionObj}
deleteSubscription={
() => deleteUserSubscription(subscriptionObj, subscription.sport)
}
/>
)
}
return (
<UserAccountSubscription
key={subscriptionObj.id}
noMarginBottom
amount={1999}
label={subscriptionObj}
deleteSubscription={
() => deleteUserSubscription(subscriptionObj, subscription.sport)
}
/>
)
})
}
return null
})}
</Fragment>
)
})}
<UserAccountButton text='select_subscription' open={open} />
<TextNoBorder
text={`${translate('next_debit')} 31.02.2020`}
amount={4796}
/>
</Fragment>
)
}

@ -0,0 +1,16 @@
import styled from 'styled-components/macro'
import { BlockTitle } from 'features/Login/styled'
import { T9n } from 'features/T9n'
export const UserAccountBlockTitle = styled(BlockTitle)`
align-self: flex-start;
`
export const SubscriptionTitle = styled(T9n)`
color: white;
font-weight: 500;
font-size: 25px;
align-self: flex-start;
margin-top: 40px;
`

@ -0,0 +1,70 @@
import React from 'react'
import map from 'lodash/map'
import type { SubscriptionType } from 'requests/getSubscriptions'
import { Modal } from 'features/Modal'
import { useLexicsStore } from 'features/LexicsStore'
import { Subscription } from '../Subscription'
import {
AddSubscriptionModal,
Line,
ModalTitle,
SubscriptionsWrapper,
SaveButton,
} from './styled'
type Props = {
close: () => void,
isOpen: boolean,
saveSubscription: () => void,
selectedSubscription: number | null,
setSelectedSubscription: (arg: number | null) => void,
subscriptions: Array<SubscriptionType>,
}
export const SubscriptionModal = ({
close,
isOpen,
saveSubscription,
selectedSubscription,
setSelectedSubscription,
subscriptions,
}: Props) => {
const { translate } = useLexicsStore()
return (
<Modal
isOpen={isOpen}
close={close}
>
<AddSubscriptionModal>
<ModalTitle t='select_subscription' />
<Line />
<SubscriptionsWrapper>
{map(subscriptions, (subscription) => (
<Subscription
id={`${subscription.id}`}
checked={selectedSubscription === subscription.id}
key={`${subscription.id}`}
noMarginBottom
noMarginTop
amount={1999}
inputType='radio'
label={translate(`${subscription.lexic}`)}
selectSubscription={() => {
setSelectedSubscription(subscription.id)
}}
/>
))}
</SubscriptionsWrapper>
<SaveButton onClick={saveSubscription}>
Save
</SaveButton>
</AddSubscriptionModal>
</Modal>
)
}

@ -0,0 +1,46 @@
import styled from 'styled-components/macro'
import { customScrollbar, customStylesMixin } from 'features/Common'
import { T9n } from 'features/T9n'
import { OutlinedButton } from '../../styled'
export const AddSubscriptionModal = styled.div`
width: 538px;
height: 452px;
display: flex;
flex-direction: column;
align-items: center;
`
export const ModalTitle = styled(T9n)`
display: block;
font-size: 24px;
font-weight: normal;
`
export const Line = styled.hr`
position:absolute;
width: 100%;
top: 40px;
height: 1px;
border: 1px solid #3F3F3F;
`
export const SaveButton = styled(OutlinedButton)`
width: 192px;
height: 36px;
display: block;
font-weight: normal;
margin-left: auto;
margin-top: auto;
`
export const SubscriptionsWrapper = styled.div`
margin-top: 45px;
width: 100%;
height: 100%;
overflow: auto;
${customScrollbar};
${customStylesMixin};
`

@ -15,7 +15,9 @@ export const TextNoBorder = ({
text, text,
}: Props) => ( }: Props) => (
<TextNoBorderWrapper> <TextNoBorderWrapper>
<TextNoBorderTextWrapper>{text}</TextNoBorderTextWrapper> <TextNoBorderTextWrapper>
{text}
</TextNoBorderTextWrapper>
<PriceWrapper> <PriceWrapper>
<Price amount={amount} /> <Price amount={amount} />
</PriceWrapper> </PriceWrapper>

@ -1,11 +1,20 @@
import React from 'react' import React from 'react'
import { T9n } from 'features/T9n'
import { PlusIconWrapper } from '../PlusIcon' import { PlusIconWrapper } from '../PlusIcon'
import { UserAccountButtonWrapper, PlusIconTextWrapper } from './styled' import { UserAccountButtonWrapper, PlusIconTextWrapper } from './styled'
export const UserAccountButton = ({ text }: { text: string }) => ( type Props = {
<UserAccountButtonWrapper> open?: () => void,
text: string,
}
export const UserAccountButton = ({ open, text }: Props) => (
<UserAccountButtonWrapper onClick={open}>
<PlusIconWrapper /> <PlusIconWrapper />
<PlusIconTextWrapper>{text}</PlusIconTextWrapper> <PlusIconTextWrapper>
<T9n t={text} />
</PlusIconTextWrapper>
</UserAccountButtonWrapper> </UserAccountButtonWrapper>
) )

@ -1,20 +1,25 @@
import React from 'react' import React from 'react'
import { T9n } from 'features/T9n'
import type { ObjectWithName } from 'features/Name'
import { Name } from 'features/Name'
import { Price } from 'features/Register/components/Price' import { Price } from 'features/Register/components/Price'
import { import {
UserAccountSubscriptionWrapper, UserAccountSubscriptionWrapper,
UserAccountBoldTextWrapper, UserAccountBoldTextWrapper,
UserAccountNormalTextWrapper, UserAccountNormalTextWrapper,
Text, UserAccountText,
UserAccountDeleteButton,
} from './styled' } from './styled'
import { PriceWrapper } from '../CardNumber/styled' import { PriceWrapper } from '../CardNumber/styled'
type Props = { type Props = {
amount: number, amount: number,
checked?: boolean, checked?: boolean,
deleteSubscription?: () => void,
inputType?: string, inputType?: string,
label?: string, label?: ObjectWithName,
noMarginBottom?: boolean, noMarginBottom?: boolean,
noMarginTop?: boolean, noMarginTop?: boolean,
packageAction?: string, packageAction?: string,
@ -23,6 +28,7 @@ type Props = {
export const UserAccountSubscription = ({ export const UserAccountSubscription = ({
amount, amount,
deleteSubscription,
label, label,
noMarginBottom, noMarginBottom,
noMarginTop, noMarginTop,
@ -33,11 +39,24 @@ export const UserAccountSubscription = ({
noMarginTop={noMarginTop} noMarginTop={noMarginTop}
noMarginBottom={noMarginBottom} noMarginBottom={noMarginBottom}
> >
<Text>{label}</Text> <UserAccountText>
{packageName && <UserAccountBoldTextWrapper>{packageName}</UserAccountBoldTextWrapper>} <Name nameObj={label || {}} />
{packageAction && <UserAccountNormalTextWrapper>{packageAction}</UserAccountNormalTextWrapper>} </UserAccountText>
{packageName
&& (
<UserAccountBoldTextWrapper>
<T9n t={packageName} />
</UserAccountBoldTextWrapper>
)}
{packageAction
&& <UserAccountNormalTextWrapper>{packageAction}</UserAccountNormalTextWrapper>}
<PriceWrapper> <PriceWrapper>
<Price amount={amount} /> <Price amount={amount} />
</PriceWrapper> </PriceWrapper>
{deleteSubscription && (
<UserAccountDeleteButton type='button' onClick={deleteSubscription}>
<T9n t='delete' />
</UserAccountDeleteButton>
)}
</UserAccountSubscriptionWrapper> </UserAccountSubscriptionWrapper>
) )

@ -10,7 +10,7 @@ type Props = {
noMarginTop?: boolean, noMarginTop?: boolean,
} }
export const UserAccountSubscriptionWrapperStyles = css<Props>` export const SubscriptionWrapperStyles = css<Props>`
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
@ -19,16 +19,16 @@ export const UserAccountSubscriptionWrapperStyles = css<Props>`
border-top: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')}; border-top: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')};
border-bottom: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')}; border-bottom: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')};
border-radius: ${({ noMarginBottom, noMarginTop }) => { border-radius: ${({ noMarginBottom, noMarginTop }) => {
if (noMarginTop && noMarginBottom) { switch (true) {
case noMarginTop && noMarginBottom:
return '0' return '0'
} case noMarginTop:
if (noMarginTop) {
return '0 0 2px 2px' return '0 0 2px 2px'
} case noMarginBottom:
if (noMarginBottom) {
return '2px 2px 0 0' return '2px 2px 0 0'
} default:
return '2px' return '2px'
}
}}; }};
margin-top: ${({ noMarginTop }) => (noMarginTop ? '0' : '20px')}; margin-top: ${({ noMarginTop }) => (noMarginTop ? '0' : '20px')};
margin-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '0' : '20px')}; margin-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '0' : '20px')};
@ -39,8 +39,25 @@ export const UserAccountSubscriptionWrapperStyles = css<Props>`
} }
` `
export const UserAccountDeleteButton = styled.button`
width: 80px;
height: 100%;
background: red;
cursor: pointer;
border: none;
color: white;
font-size: 15px;
font-weight: 600;
display: none;
`
export const UserAccountSubscriptionWrapper = styled.div` export const UserAccountSubscriptionWrapper = styled.div`
${UserAccountSubscriptionWrapperStyles}; ${SubscriptionWrapperStyles};
&:hover {
${UserAccountDeleteButton} {
display: block;
}
}
&:nth-child(n+1) { &:nth-child(n+1) {
border-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '1px solid #000' : '')}; border-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '1px solid #000' : '')};
@ -60,7 +77,7 @@ export const CheckboxWrapper = styled.div`
} }
` `
export const Text = styled.p` export const UserAccountText = styled.p`
color: #fff; color: #fff;
font-size: 18px; font-size: 18px;
cursor: default; cursor: default;

@ -0,0 +1,72 @@
import React, { Fragment } from 'react'
import format from 'date-fns/format'
import type { Match } from 'features/UserAccount/hooks/useUserSubscriptions'
import { Name } from 'features/Name'
import { Price } from 'features/Register/components/Price'
import { T9n } from 'features/T9n'
import {
UserAccountSubscriptionWrapper,
UserAccountBoldTextWrapper,
UserAccountTextLeftPart,
UserAccountNormalTextWrapper,
UserAccountText,
UserAccountDate,
UserAccountDeleteButton,
UserAccountTeams,
} from './styled'
import { PriceWrapper } from '../CardNumber/styled'
type Props = {
amount: number,
deleteSubscription?: () => void,
inputType?: string,
matchData: Match,
noMarginBottom?: boolean,
noMarginTop?: boolean,
packageAction?: string,
packageName?: string,
}
export const UserAccountSubscriptionMatch = ({
amount,
deleteSubscription,
matchData,
noMarginBottom,
noMarginTop,
packageAction,
packageName,
}: Props) => (
<UserAccountSubscriptionWrapper
noMarginTop={noMarginTop}
noMarginBottom={noMarginBottom}
>
<UserAccountTextLeftPart>
<Fragment>
<UserAccountDate>
{format(new Date(matchData.date), 'dd.MM.yyyy')}
</UserAccountDate>
<UserAccountText>
<Name nameObj={matchData.tournament} />
</UserAccountText>
</Fragment>
<UserAccountTeams>
<Name nameObj={matchData.team1} /> - <Name nameObj={matchData.team2} />
</UserAccountTeams>
</UserAccountTextLeftPart>
{packageName
&& <UserAccountBoldTextWrapper>{packageName}</UserAccountBoldTextWrapper>}
{packageAction
&& <UserAccountNormalTextWrapper>{packageAction}</UserAccountNormalTextWrapper>}
<PriceWrapper>
<Price amount={amount} />
</PriceWrapper>
{deleteSubscription && (
<UserAccountDeleteButton type='button' onClick={deleteSubscription}>
<T9n t='delete' />
</UserAccountDeleteButton>
)}
</UserAccountSubscriptionWrapper>
)

@ -0,0 +1,117 @@
import styled, { css } from 'styled-components/macro'
import { Label as CheckboxLabel } from 'features/Common/Checkbox/styled'
import { Label as RadioLabel } from 'features/Common/Radio/styled'
import { TextWrapper } from '../CardNumber/styled'
type Props = {
noMarginBottom?: boolean,
noMarginTop?: boolean,
}
export const SubscriptionWrapperStyles = css<Props>`
display: flex;
align-items: center;
justify-content: flex-start;
height: 75px;
background-color: #3F3F3F;
border-top: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')};
border-bottom: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? '1px solid #000' : '')};
border-radius: ${({ noMarginBottom, noMarginTop }) => {
switch (true) {
case noMarginTop && noMarginBottom:
return '0'
case noMarginTop:
return '0 0 2px 2px'
case noMarginBottom:
return '2px 2px 0 0'
default:
return '2px'
}
}};
margin-top: ${({ noMarginTop }) => (noMarginTop ? '0' : '20px')};
margin-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '0' : '20px')};
padding-left: 20px;
width: 100%;
${RadioLabel}::before {
margin-left: 22px;
}
`
export const UserAccountDeleteButton = styled.button`
width: 80px;
height: 100%;
background: red;
cursor: pointer;
border: none;
color: white;
font-size: 15px;
font-weight: 600;
display: none;
`
export const UserAccountTextLeftPart = styled.div``
export const UserAccountSubscriptionWrapper = styled.div`
${SubscriptionWrapperStyles};
&:hover {
${UserAccountDeleteButton} {
display: block;
}
}
&:nth-child(n+1) {
border-bottom: ${({ noMarginBottom }) => (noMarginBottom ? '1px solid #000' : '')};
border-top: ${({ noMarginBottom, noMarginTop }) => (noMarginBottom && noMarginTop ? 'none' : '')};
}
`
export const CheckboxWrapper = styled.div`
${CheckboxLabel} {
font-weight: bold;
font-size: 16px;
line-height: 24px;
&::before {
margin-left: 22px;
}
}
`
export const UserAccountDate = styled.span`
color: #fff;
font-size: 17px;
cursor: default;
`
export const UserAccountText = styled.span`
color: #fff;
font-size: 18px;
cursor: default;
font-weight: 800;
margin-left: 20px;
`
export const UserAccountTeams = styled.p`
color: #fff;
font-size: 18px;
cursor: default;
margin-top: 15px;
`
export const UserAccountBoldTextWrapper = styled(TextWrapper)`
color: #fff;
font-weight: bold;
font-size: 20px;
margin-right: 24px;
&:hover {
cursor: default;
}
`
export const UserAccountNormalTextWrapper = styled(TextWrapper)`
color: #fff;
font-weight: normal;
font-size: 16px;
margin-right: 24px;
`

@ -0,0 +1,31 @@
import {
useEffect,
useState,
} from 'react'
import map from 'lodash/map'
import type { SubscriptionType } from 'requests/getSubscriptions'
import { getSubscriptions } from 'requests/getSubscriptions'
import { useLexicsStore } from 'features/LexicsStore'
export const useSubscriptions = () => {
const [subscriptions, setSubscriptions] = useState<Array<SubscriptionType>>([])
const { addLexicsConfig } = useLexicsStore()
useEffect(() => {
getSubscriptions().then((res) => {
const lexicsArray = map(res, (subscription) => (
String(subscription.lexic)
))
addLexicsConfig(lexicsArray)
setSubscriptions(res)
})
}, [addLexicsConfig])
return {
subscriptions,
}
}

@ -1,41 +1,99 @@
import { import {
useEffect, useEffect,
useState, useState,
useCallback,
} from 'react' } from 'react'
import map from 'lodash/map' import type { SportList } from 'requests/getSportList'
import {
SubscriptionAction,
saveUserCustomSubscription,
} from 'requests/saveUserCustomSubscription'
import { getSportList } from 'requests/getSportList'
import { getUserSubscriptions } from 'requests/getUserSubscriptions' import { getUserSubscriptions } from 'requests/getUserSubscriptions'
import { saveUserSubscription } from 'requests/saveUserSubscription'
import { useLexicsStore } from 'features/LexicsStore' import { useToggle } from 'hooks'
export type Team = { export type Subscription = {
id: number, id: number,
name_eng: string, name_eng: string,
name_rus: string, name_rus: string,
type?: number,
}
type Name = {
name_eng: string,
name_rus: string,
}
export type Match = {
date: Date,
id: number,
team1: Name,
team2: Name,
tournament: Name,
}
export type UserSubscriptions = {
matches?: Array<Match>,
sport: number,
teams?: Array<Subscription>,
tournaments?: Array<Subscription>,
} }
export const useUserSubscriptions = () => { export const useUserSubscriptions = () => {
const [subscriptions, setSubscriptions] = useState<Array<Team>>([]) const [userSubscriptions, setUserSubscriptions] = useState<Array<UserSubscriptions>>([])
const { suffix } = useLexicsStore() const [selectedSubscription, setSelectedSubscription] = useState<number | null>(null)
const [selectedSubscriptionLexic, setSelectedSubscriptionLexic] = useState<string>('')
const [sportList, setSportList] = useState<SportList>([])
type Names = 'name_eng' | 'name_rus' const {
close,
isOpen,
open,
} = useToggle()
useEffect(() => { useEffect(() => {
getUserSubscriptions().then((res) => { getUserSubscriptions().then((res) => {
setSubscriptions(res.custom?.[0].teams) setUserSubscriptions(res.custom)
setSelectedSubscription(res.id)
setSelectedSubscriptionLexic(`${res.lexic}`)
}) })
getSportList().then(setSportList)
}, []) }, [])
const normalizeSubscriptions = useCallback(() => { const saveSubscription = async () => {
const nameKey = `name_${suffix}` as Names await saveUserSubscription(selectedSubscription)
close()
await getUserSubscriptions().then((res) => {
setSelectedSubscription(res.id)
setSelectedSubscriptionLexic(`${res.lexic}`)
})
}
return map(subscriptions, (team) => ({ const deleteUserSubscription = async (subscription: Subscription, sport: number) => {
...team, await saveUserCustomSubscription({
name: team[nameKey], action: SubscriptionAction.REMOVE,
})) id: subscription.id,
}, [subscriptions, suffix]) sport,
type: subscription.type as number,
})
await getUserSubscriptions().then((res) => {
setUserSubscriptions(res.custom)
})
}
return normalizeSubscriptions() return {
close,
deleteUserSubscription,
isOpen,
open,
saveSubscription,
selectedSubscription,
selectedSubscriptionLexic,
setSelectedSubscription,
sportList,
userSubscriptions,
}
} }

@ -1,7 +1,5 @@
import React from 'react' import React from 'react'
import map from 'lodash/map'
import { userAccountLexics } from 'config/lexics/userAccount' import { userAccountLexics } from 'config/lexics/userAccount'
import { formIds } from 'config/form' import { formIds } from 'config/form'
@ -10,24 +8,26 @@ import { Input } from 'features/Common'
import { Form } from 'features/Login/styled' import { Form } from 'features/Login/styled'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { Error } from 'features/Common/Input/styled' import { Error } from 'features/Common/Input/styled'
import { useLexicsStore, useLexicsConfig } from 'features/LexicsStore' import { useLexicsConfig } from 'features/LexicsStore'
import { useUserInfo } from './hooks/useUserInfo'
import { useUserSubscriptions } from './hooks/useUserSubscriptions'
import { useSubscriptions } from './hooks/useSubscriptions'
import { CardNumber } from './components/CardNumber' import { CardNumber } from './components/CardNumber'
import { UserAccountButton } from './components/UserAccountButton' import { UserAccountButton } from './components/UserAccountButton'
import { PageTitle } from './components/PageTitle' import { PageTitle } from './components/PageTitle'
import { UserAccountSubscription } from './components/UserAccountSubscription'
import { TextNoBorder } from './components/TextNoBorder' import { SubscriptionModal } from './components/SubscriptionsModal'
import { useUserInfo } from './hooks/useUserInfo' import { SubscriptionsForm } from './components/SubscriptionsForm'
import { useUserSubscriptions } from './hooks/useUserSubscriptions'
import { import {
FormWrapper, FormWrapper,
OutlinedButton, OutlinedButton,
UserAccountWrapper, UserAccountWrapper,
UserAccountFormWrapper, UserAccountFormWrapper,
UserAccountBlockTitle,
UserAccountComponentWrapper, UserAccountComponentWrapper,
ButtonWrapper, ButtonWrapper,
UserAccountBlockTitle,
} from './styled' } from './styled'
const labelWidth = 110 const labelWidth = 110
@ -35,8 +35,6 @@ const labelWidth = 110
export const UserAccount = () => { export const UserAccount = () => {
useLexicsConfig(userAccountLexics) useLexicsConfig(userAccountLexics)
const { translate } = useLexicsStore()
const { const {
cities, cities,
countries, countries,
@ -51,18 +49,31 @@ export const UserAccount = () => {
updateFormValue, updateFormValue,
} = useUserInfo() } = useUserInfo()
const subscriptions = useUserSubscriptions() const {
close,
deleteUserSubscription,
isOpen,
open,
saveSubscription,
selectedSubscription,
selectedSubscriptionLexic,
setSelectedSubscription,
sportList,
userSubscriptions,
} = useUserSubscriptions()
const {
subscriptions,
} = useSubscriptions()
return ( return (
<UserAccountComponentWrapper> <UserAccountComponentWrapper>
<UserAccountWrapper> <UserAccountWrapper>
<PageTitle titleText={translate('user_account')} /> <PageTitle titleText='user_account' />
<UserAccountFormWrapper> <UserAccountFormWrapper>
<FormWrapper> <FormWrapper>
<Form> <Form>
<UserAccountBlockTitle> <UserAccountBlockTitle t='main' />
<T9n t='main' />
</UserAccountBlockTitle>
<Input <Input
value={readFormValue(formIds.firstname)} value={readFormValue(formIds.firstname)}
labelLexic='name' labelLexic='name'
@ -169,45 +180,38 @@ export const UserAccount = () => {
</FormWrapper> </FormWrapper>
<FormWrapper> <FormWrapper>
<Form> <Form>
<UserAccountBlockTitle> <UserAccountBlockTitle t='payment' />
<T9n t='payment' />
</UserAccountBlockTitle>
<CardNumber visa label='1234 1234 1234 1234' /> <CardNumber visa label='1234 1234 1234 1234' />
<CardNumber checked label='1234 1234 1234 1234' /> <CardNumber checked label='1234 1234 1234 1234' />
<CardNumber label='1234 1234 1234 1234' /> <CardNumber label='1234 1234 1234 1234' />
<UserAccountButton text={translate('add_card')} /> <UserAccountButton text='add_card' />
</Form> </Form>
</FormWrapper> </FormWrapper>
<FormWrapper> <FormWrapper>
<Form> <Form>
<UserAccountBlockTitle> <SubscriptionsForm
<T9n t='subscriptions' /> selectedSubscriptionLexic={selectedSubscriptionLexic}
</UserAccountBlockTitle> userSubscriptions={userSubscriptions}
<UserAccountSubscription sportList={sportList}
amount={1999} deleteUserSubscription={deleteUserSubscription}
inputType='radio' open={open}
packageName='Базовая' selectedSubscription={selectedSubscription}
// packageAction={translate('add_card')}
// Ещё не решили что делать с add card
/>
{map(subscriptions, (subscription) => (
<UserAccountSubscription
key={subscription.id}
noMarginBottom
amount={1999}
inputType='checkbox'
label={subscription.name}
/>
))}
<UserAccountButton text={translate('select_subscription')} />
<TextNoBorder
text={`${translate('next_debit')} 31.02.2020`}
amount={4796}
/> />
</Form> </Form>
</FormWrapper> </FormWrapper>
</UserAccountFormWrapper> </UserAccountFormWrapper>
</UserAccountWrapper> </UserAccountWrapper>
{/* Select subscription modal */}
<SubscriptionModal
close={close}
isOpen={isOpen}
saveSubscription={saveSubscription}
selectedSubscription={selectedSubscription}
setSelectedSubscription={setSelectedSubscription}
subscriptions={subscriptions}
/>
</UserAccountComponentWrapper> </UserAccountComponentWrapper>
) )
} }

@ -1,7 +1,8 @@
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { BlockTitle, Form } from 'features/Login/styled' import { Form, BlockTitle } from 'features/Login/styled'
import { outlineButtonStyles } from 'features/Common/Button' import { outlineButtonStyles } from 'features/Common/Button'
import { customScrollbar, customStylesMixin } from 'features/Common'
export const OutlinedButton = styled.button` export const OutlinedButton = styled.button`
${outlineButtonStyles}; ${outlineButtonStyles};
@ -18,6 +19,10 @@ export const OutlinedButton = styled.button`
} }
` `
export const UserAccountBlockTitle = styled(BlockTitle)`
align-self: flex-start;
`
export const UserAccountFormWrapper = styled.div` export const UserAccountFormWrapper = styled.div`
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -36,10 +41,6 @@ export const UserAccountWrapper = styled.div`
margin-top: 140px; margin-top: 140px;
` `
export const UserAccountBlockTitle = styled(BlockTitle)`
align-self: flex-start;
`
export const UserAccountComponentWrapper = styled.div`` export const UserAccountComponentWrapper = styled.div``
export const FormWrapper = styled.div` export const FormWrapper = styled.div`
@ -53,3 +54,42 @@ export const FormWrapper = styled.div`
margin-right: 0; margin-right: 0;
} }
` `
export const AddSubscriptionModal = styled.div`
width: 538px;
height: 452px;
display: flex;
flex-direction: column;
align-items: center;
`
export const ModalTitle = styled.p`
font-size: 24px;
font-weight: normal;
`
export const Line = styled.hr`
position:absolute;
width: 100%;
top: 40px;
height: 1px;
border: 1px solid #3F3F3F;
`
export const SaveButton = styled(OutlinedButton)`
width: 192px;
height: 36px;
display: block;
font-weight: normal;
margin-left: auto;
margin-top: auto;
`
export const SubscriptionsWrapper = styled.div`
margin-top: 45px;
width: 100%;
height: 100%;
overflow: auto;
${customScrollbar}
${customStylesMixin}
`

@ -0,0 +1,34 @@
import {
DATA_URL,
PROCEDURES,
} from 'config'
import { callApi } from 'helpers'
const proc = PROCEDURES.get_subscriptions
type Tournament = {
id: number,
name_eng: string,
name_rus: string,
sport: number,
}
export type SubscriptionType = {
id: number,
lexic: number,
tournaments: Array<Tournament>,
}
export const getSubscriptions = (): Promise<Array<SubscriptionType>> => {
const config = {
body: {
params: {},
proc,
},
}
return callApi({
config,
url: DATA_URL,
})
}

@ -1,31 +1,48 @@
import { import {
DATA_URL, DATA_URL,
PROCEDURES, PROCEDURES,
ProfileTypes,
SportTypes,
} from 'config' } from 'config'
import { callApi } from 'helpers' import { callApi } from 'helpers'
const proc = PROCEDURES.save_user_custom_subscription const proc = PROCEDURES.save_user_custom_subscription
export enum SubscriptionAction {
ADD = 1,
REMOVE = 2,
}
type Response = { type Response = {
_p_error: string | null, _p_error: string | null,
_p_status: 1 | 2, _p_status: 1 | 2,
} }
type Args = {
action: SubscriptionAction,
id: number,
sport: SportTypes,
type: ProfileTypes,
}
const responseStatus = { const responseStatus = {
FAILURE: 2, FAILURE: 2,
SUCCESS: 1, SUCCESS: 1,
} }
export const saveUserCustomSubscription = async () => { export const saveUserCustomSubscription = async ({
action,
id,
sport,
type,
} : Args) => {
const config = { const config = {
body: { body: {
params: { params: {
_p_action: 1, _p_action: action,
_p_id: 1, _p_id: id,
_p_sport: 1, _p_sport: sport,
_p_status: 1, _p_type: type,
_p_type: 1,
_p_user_id: 1,
}, },
proc, proc,
}, },
@ -37,7 +54,7 @@ export const saveUserCustomSubscription = async () => {
}) })
if (response._p_status === responseStatus.SUCCESS) { if (response._p_status === responseStatus.SUCCESS) {
return Promise.resolve(response) return Promise.resolve()
} }
return Promise.reject(response._p_error) return Promise.reject(response._p_error)
} }

@ -6,10 +6,6 @@ import { callApi } from 'helpers'
const proc = PROCEDURES.save_user_subscription const proc = PROCEDURES.save_user_subscription
type Type = {
subscriptionId: number,
}
type Response = { type Response = {
_p_error: string | null, _p_error: string | null,
_p_status: 1 | 2, _p_status: 1 | 2,
@ -20,11 +16,11 @@ const responseStatus = {
SUCCESS: 1, SUCCESS: 1,
} }
export const saveUserSubscription = async ({ subscriptionId }: Type) => { export const saveUserSubscription = async (subscriptionId : number | null) => {
const config = { const config = {
body: { body: {
params: { params: {
p_subscription_id: subscriptionId, _p_subscription_id: subscriptionId,
}, },
proc, proc,
}, },
@ -36,7 +32,7 @@ export const saveUserSubscription = async ({ subscriptionId }: Type) => {
}) })
if (response._p_status === responseStatus.SUCCESS) { if (response._p_status === responseStatus.SUCCESS) {
return Promise.resolve(response) return Promise.resolve()
} }
return Promise.reject(response._p_error) return Promise.reject(response._p_error)
} }

Loading…
Cancel
Save