feat(#2428): Edit page of subsribtions
parent
423dc333a2
commit
e74c2f5303
@ -0,0 +1,100 @@ |
||||
import { ArrowLoader } from 'features/ArrowLoader' |
||||
import { T9n } from 'features/T9n' |
||||
|
||||
import type { Subscribe } from 'requests/getUserSubscribes' |
||||
|
||||
import { format } from 'date-fns' |
||||
|
||||
import { |
||||
Modal, |
||||
Wrapper, |
||||
Header, |
||||
HeaderTitle, |
||||
Body, |
||||
Footer, |
||||
ScSaveSubBtn, |
||||
Text, |
||||
ScCancelSub, |
||||
ScStillCancelBtn, |
||||
ScNotificationPopup, |
||||
ScOkBtn, |
||||
} from './styled' |
||||
|
||||
type Props = { |
||||
cancelSub: () => void, |
||||
error: string, |
||||
handleModalClose: () => void, |
||||
isFetching: boolean, |
||||
isModalOpen: boolean, |
||||
isSubCanceled: boolean, |
||||
subscribe: Subscribe, |
||||
} |
||||
|
||||
export const CancelSubPopup = (props: Props) => { |
||||
const { |
||||
cancelSub, |
||||
error, |
||||
handleModalClose, |
||||
isFetching, |
||||
isModalOpen, |
||||
isSubCanceled, |
||||
subscribe, |
||||
} = props |
||||
|
||||
const { |
||||
access_to, |
||||
name, |
||||
option_name, |
||||
} = subscribe |
||||
|
||||
return ( |
||||
<Modal isOpen={isModalOpen} withCloseButton={false}> |
||||
<Wrapper> |
||||
{isSubCanceled || error ? ( |
||||
<ScNotificationPopup> |
||||
<Text> |
||||
<T9n t={error || 'sub_not_renewed'} /> |
||||
</Text> |
||||
<ScOkBtn onClick={() => handleModalClose()}>Ok</ScOkBtn> |
||||
</ScNotificationPopup> |
||||
) : ( |
||||
<> |
||||
<Header> |
||||
<HeaderTitle> |
||||
<T9n t='unsubscribe' /> |
||||
</HeaderTitle> |
||||
</Header> |
||||
<Body> |
||||
<Text> |
||||
<T9n t='if_you_cancel' /> |
||||
</Text> |
||||
<ScCancelSub> |
||||
{option_name} — {name} |
||||
</ScCancelSub> |
||||
<Text> |
||||
<T9n t='after_canceling' /> |
||||
|
||||
{access_to && format(new Date(access_to), 'dd.MM.yyyy')} |
||||
</Text> |
||||
<Footer> |
||||
<ScStillCancelBtn |
||||
onClick={() => cancelSub()} |
||||
isFetching={isFetching} |
||||
> |
||||
{isFetching ? ( |
||||
<ArrowLoader /> |
||||
) : ( |
||||
<T9n t='still_cancel' /> |
||||
)} |
||||
</ScStillCancelBtn> |
||||
<ScSaveSubBtn onClick={() => handleModalClose()}> |
||||
<T9n t='save_sub' /> |
||||
</ScSaveSubBtn> |
||||
</Footer> |
||||
</Body> |
||||
</> |
||||
)} |
||||
</Wrapper> |
||||
</Modal> |
||||
) |
||||
} |
||||
@ -0,0 +1,195 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
import { devices } from 'config/devices' |
||||
|
||||
import { ModalWindow } from 'features/Modal/styled' |
||||
import { Modal as BaseModal } from 'features/Modal' |
||||
import { Header as BaseHeader } from 'features/PopupComponents' |
||||
|
||||
import { ButtonSolid, ButtonOutline } from 'features/Common' |
||||
|
||||
export const Modal = styled(BaseModal)` |
||||
background-color: rgba(0, 0, 0, 0.7); |
||||
|
||||
${ModalWindow} { |
||||
max-width: 642px; |
||||
max-height: 340px; |
||||
padding-top: 40px; |
||||
background-color: #333333; |
||||
border-radius: 5px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
height: auto; |
||||
top: -7vh; |
||||
width: 98%; |
||||
max-height: none; |
||||
padding: 0; |
||||
` |
||||
: ''}; |
||||
} |
||||
` |
||||
|
||||
type WrapperProps = { |
||||
isFetching?: boolean, |
||||
} |
||||
|
||||
export const Wrapper = styled.div<WrapperProps>` |
||||
${({ isFetching }) => ( |
||||
isFetching |
||||
? css`pointer-events: none;` |
||||
: '' |
||||
)} |
||||
` |
||||
|
||||
export const Header = styled(BaseHeader)` |
||||
height: auto; |
||||
justify-content: center; |
||||
${isMobileDevice |
||||
? css` |
||||
@media ${devices.mobile}{ |
||||
padding-top: 33px; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const HeaderTitle = styled.span` |
||||
font-weight: 700; |
||||
font-size: 24px; |
||||
line-height: 24px; |
||||
color: #FFFFFF; |
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 14px; |
||||
line-height: 20px; |
||||
|
||||
@media (orientation: landscape) { |
||||
font-size: 20px; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const Body = styled.div` |
||||
padding: 20px 40px; |
||||
display: flex; |
||||
flex-direction: column; |
||||
font-weight: normal; |
||||
font-size: 20px; |
||||
line-height: 27px; |
||||
${isMobileDevice |
||||
? css` |
||||
padding: 13px 25px 0; |
||||
flex-direction: column; |
||||
|
||||
@media (orientation: landscape){ |
||||
padding: 22px 23px 0 29px; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const Footer = styled.div` |
||||
width: 100%; |
||||
display: flex; |
||||
justify-content: center; |
||||
margin-top: 25px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
flex-direction: column; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const ScSaveSubBtn = styled(ButtonSolid)` |
||||
max-width: 270px; |
||||
border-radius: 5px; |
||||
font-weight: 600; |
||||
height: 50px; |
||||
font-size: 20px; |
||||
${isMobileDevice |
||||
? css` |
||||
max-width: none; |
||||
width: 100%; |
||||
min-height: 42px; |
||||
margin-bottom: 20px; |
||||
|
||||
@media (orientation: landscape){ |
||||
width: 290px; |
||||
min-height: 42px; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const ScStillCancelBtn = styled(ButtonOutline)<{isFetching: boolean}>` |
||||
max-width: 270px; |
||||
height: 50px; |
||||
border-radius: 5px; |
||||
font-weight: 500; |
||||
font-size: 20px; |
||||
margin-right: 24px; |
||||
|
||||
${({ isFetching }) => (isFetching ? 'border: none' : '')}; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
max-width: none; |
||||
margin-bottom: 20px; |
||||
width: 100%; |
||||
|
||||
@media (orientation: landscape){ |
||||
width: 290px; |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const Text = styled.span` |
||||
display: block; |
||||
color: rgba(255, 255, 255, 0.7); |
||||
font-weight: 500; |
||||
font-size: 16px; |
||||
line-height: 20px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
font-size: 13px; |
||||
` |
||||
: ''}; |
||||
` |
||||
|
||||
export const ScCancelSub = styled.div` |
||||
max-width: 562px; |
||||
height: 59px; |
||||
background: linear-gradient( |
||||
180deg, |
||||
rgba(255, 255, 255, 0.1) 0%, |
||||
rgba(255, 255, 255, 0) 100% |
||||
), |
||||
#3f3f3f; |
||||
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); |
||||
border-radius: 2px; |
||||
font-weight: 500; |
||||
font-size: 18px; |
||||
line-height: 22px; |
||||
padding: 20px; |
||||
margin: 20px 0; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
white-space: nowrap; |
||||
` |
||||
|
||||
export const ScNotificationPopup = styled.div` |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
` |
||||
|
||||
export const ScOkBtn = styled(ScSaveSubBtn)` |
||||
max-width: 70px; |
||||
margin-top: 40px; |
||||
` |
||||
@ -0,0 +1,36 @@ |
||||
import { useEffect } from 'react' |
||||
|
||||
import { useCardsStore } from 'features/CardsStore' |
||||
import { CardStep } from 'features/BuyMatchPopup/components/CardStep' |
||||
|
||||
import { Modal } from './styled' |
||||
|
||||
type Props = { |
||||
changeCardPopupOpen: boolean, |
||||
setChangeCardPopupOpen: (open: boolean) => void, |
||||
} |
||||
|
||||
export const ChangeCardPopup = ({ |
||||
changeCardPopupOpen, |
||||
setChangeCardPopupOpen, |
||||
}: Props) => { |
||||
const { fetchCards } = useCardsStore() |
||||
|
||||
useEffect(() => { |
||||
fetchCards() |
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []) |
||||
|
||||
return ( |
||||
<Modal |
||||
isOpen={changeCardPopupOpen} |
||||
close={() => setChangeCardPopupOpen(false)} |
||||
withCloseButton |
||||
> |
||||
<CardStep |
||||
title='change_card' |
||||
btnName='change' |
||||
/> |
||||
</Modal> |
||||
) |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
import { ModalWindow } from 'features/Modal/styled' |
||||
import { Modal as BaseModal } from 'features/Modal' |
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
export const Modal = styled(BaseModal)` |
||||
background-color: rgba(0, 0, 0, 0.7); |
||||
|
||||
${ModalWindow} { |
||||
padding: 0; |
||||
background-color: #333333; |
||||
border-radius: 5px; |
||||
|
||||
${isMobileDevice |
||||
? css` |
||||
width: calc(100vw - 60px); |
||||
@media screen and (orientation: landscape){ |
||||
max-width: calc(100vw - 80px); |
||||
height: calc(100vh - 20px); |
||||
overflow: auto; |
||||
} |
||||
` |
||||
: ''}; |
||||
} |
||||
` |
||||
@ -0,0 +1,62 @@ |
||||
import { useEffect, useState } from 'react' |
||||
|
||||
import { getUserSubscribes, Subscribe } from 'requests/getUserSubscribes' |
||||
import { cancelSubscribe } from 'requests/cancelSubscribe' |
||||
|
||||
import { useAuthStore } from 'features/AuthStore' |
||||
|
||||
export const useUserSubscribes = () => { |
||||
const [selectedSubscribe, setSelectedSubscribe] = useState<Subscribe>({} as Subscribe) |
||||
const [subscribes, setSubscribes] = useState<any>([]) |
||||
const [isCancelPopupOpen, setIsCancelPopupOpen] = useState(false) |
||||
const [changeCardPopupOpen, setChangeCardPopupOpen] = useState(false) |
||||
const [isSubCanceled, setSubCanceled] = useState(false) |
||||
const [isFetching, setIsFetching] = useState(false) |
||||
const [error, setError] = useState('') |
||||
const { user } = useAuthStore() |
||||
|
||||
const handleClose = () => { |
||||
setIsCancelPopupOpen(false) |
||||
setChangeCardPopupOpen(false) |
||||
setError('') |
||||
} |
||||
|
||||
const cancelSub = async () => { |
||||
try { |
||||
setIsFetching(true) |
||||
if (selectedSubscribe?.sub_id) { |
||||
await cancelSubscribe(selectedSubscribe?.sub_id) |
||||
setSubCanceled(true) |
||||
} |
||||
} catch { |
||||
setError('error_payment_unsuccessful') |
||||
} |
||||
setIsFetching(false) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
(async () => { |
||||
setIsFetching(true) |
||||
const allSubscribes = (await (getUserSubscribes(user?.profile?.email || ''))) |
||||
.sort((a, b) => Date.parse(b.access_to) - Date.parse(a.access_to)) |
||||
setSubscribes(allSubscribes) |
||||
setIsFetching(false) |
||||
})() |
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []) |
||||
|
||||
return { |
||||
cancelSub, |
||||
changeCardPopupOpen, |
||||
error, |
||||
handleClose, |
||||
isCancelPopupOpen, |
||||
isFetching, |
||||
isSubCanceled, |
||||
selectedSubscribe, |
||||
setChangeCardPopupOpen, |
||||
setIsCancelPopupOpen, |
||||
setSelectedSubscribe, |
||||
subscribes, |
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
export const Edit = (): JSX.Element => ( |
||||
<svg |
||||
xmlns='http://www.w3.org/2000/svg' |
||||
width='22' |
||||
height='23' |
||||
fill='true' |
||||
viewBox='0 0 22 23' |
||||
> |
||||
<path |
||||
fill='true' |
||||
fillOpacity='0.3' |
||||
d='M13.705 5.002L17.27 8.52 5.799 20.147l-4.017.507.452-4.023L13.705 5.002zM16.183 2.49a2.504 2.504 0 013.565 3.516L18.53 7.24l-3.565-3.517 1.217-1.234z' |
||||
/> |
||||
</svg> |
||||
) |
||||
@ -0,0 +1,15 @@ |
||||
import { API_ROOT } from 'config' |
||||
import { callApi } from 'helpers' |
||||
|
||||
export const cancelSubscribe = (id: number): Promise<unknown> => { |
||||
const config = { |
||||
body: { |
||||
id, |
||||
}, |
||||
} |
||||
|
||||
return callApi({ |
||||
config, |
||||
url: `${API_ROOT}/account/subscription/cancel`, |
||||
}) |
||||
} |
||||
@ -0,0 +1,53 @@ |
||||
import { DATA_URL, PROCEDURES } from 'config' |
||||
import { callApi } from 'helpers' |
||||
|
||||
const proc = PROCEDURES.get_user_subscribes |
||||
|
||||
export type Subscribe = { |
||||
access_to: string, |
||||
c_payment_service: number, |
||||
c_subscription_option: number, |
||||
card: string, |
||||
currency_id: number, |
||||
dt_next_billing: string | null, |
||||
is_access: boolean, |
||||
is_active: boolean, |
||||
iso: string, |
||||
name: string, |
||||
option_name: string, |
||||
option_sys_name: string, |
||||
payment_type: string, |
||||
price: number, |
||||
purchase_type: string, |
||||
sub_external_id: number | null, |
||||
sub_id: number | null, |
||||
sub_info: Array<Team>, |
||||
ts_payment: string, |
||||
} |
||||
|
||||
type Team = { |
||||
id: number, |
||||
name_en: string, |
||||
name_ru: string, |
||||
} |
||||
|
||||
type Subscribes = Array<Subscribe> |
||||
|
||||
export const getUserSubscribes = ( |
||||
_p_email: string, |
||||
) |
||||
: Promise<Subscribes> => { |
||||
const config = { |
||||
body: { |
||||
params: { |
||||
_p_email, |
||||
}, |
||||
proc, |
||||
}, |
||||
} |
||||
|
||||
return callApi({ |
||||
config, |
||||
url: DATA_URL, |
||||
}) |
||||
} |
||||
Loading…
Reference in new issue