diff --git a/index.html b/index.html index 8441828..2c2fd14 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,7 @@
+ diff --git a/package-lock.json b/package-lock.json index 4b63b96..0935e2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "aurawebapp", "version": "0.0.0", "dependencies": { + "@chargebee/chargebee-js-react-wrapper": "^0.6.3", "@reduxjs/toolkit": "^1.9.5", "html-react-parser": "^3.0.16", "i18next": "^22.5.0", @@ -20,6 +21,7 @@ "react-router-dom": "^6.11.2" }, "devDependencies": { + "@chargebee/chargebee-js-types": "^1.0.1", "@types/react": "^18.2.6", "@types/react-dom": "^18.2.4", "@types/react-router-dom": "^5.3.3", @@ -404,6 +406,21 @@ "node": ">=6.9.0" } }, + "node_modules/@chargebee/chargebee-js-react-wrapper": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-react-wrapper/-/chargebee-js-react-wrapper-0.6.3.tgz", + "integrity": "sha512-U3KJLZZiUxxkW4HAkAZr2bs6r4HqL1S59zo3AAp4UYtJZbxmBpfX5e/MlsSENvsRxd2GvOpumcHr8rR4V0D5vw==", + "peerDependencies": { + "react": "^18.0.0 || ^17.0.0", + "react-dom": "^18.0.0 || ^17.0.0" + } + }, + "node_modules/@chargebee/chargebee-js-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-types/-/chargebee-js-types-1.0.1.tgz", + "integrity": "sha512-JsHlIAjZDwX2Q/vDGN4xzKRC8n1K4xCwzKl7wZOOwUH9ow030CRspVRkP3OWHrY5gLmpbmICc/iK2aptF3t/Ow==", + "dev": true + }, "node_modules/@esbuild/android-arm": { "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", @@ -3646,6 +3663,18 @@ "to-fast-properties": "^2.0.0" } }, + "@chargebee/chargebee-js-react-wrapper": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-react-wrapper/-/chargebee-js-react-wrapper-0.6.3.tgz", + "integrity": "sha512-U3KJLZZiUxxkW4HAkAZr2bs6r4HqL1S59zo3AAp4UYtJZbxmBpfX5e/MlsSENvsRxd2GvOpumcHr8rR4V0D5vw==", + "requires": {} + }, + "@chargebee/chargebee-js-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@chargebee/chargebee-js-types/-/chargebee-js-types-1.0.1.tgz", + "integrity": "sha512-JsHlIAjZDwX2Q/vDGN4xzKRC8n1K4xCwzKl7wZOOwUH9ow030CRspVRkP3OWHrY5gLmpbmICc/iK2aptF3t/Ow==", + "dev": true + }, "@esbuild/android-arm": { "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", diff --git a/package.json b/package.json index 8168c9d..889ad0c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@chargebee/chargebee-js-react-wrapper": "^0.6.3", "@reduxjs/toolkit": "^1.9.5", "html-react-parser": "^3.0.16", "i18next": "^22.5.0", @@ -22,6 +23,7 @@ "react-router-dom": "^6.11.2" }, "devDependencies": { + "@chargebee/chargebee-js-types": "^1.0.1", "@types/react": "^18.2.6", "@types/react-dom": "^18.2.4", "@types/react-router-dom": "^5.3.3", diff --git a/src/components/BackButton/styles.css b/src/components/Header/BackButton.css similarity index 100% rename from src/components/BackButton/styles.css rename to src/components/Header/BackButton.css diff --git a/src/components/BackButton/index.tsx b/src/components/Header/BackButton.tsx similarity index 96% rename from src/components/BackButton/index.tsx rename to src/components/Header/BackButton.tsx index 4e9f22e..e3ff535 100644 --- a/src/components/BackButton/index.tsx +++ b/src/components/Header/BackButton.tsx @@ -1,4 +1,4 @@ -import './styles.css' +import './BackButton.css' function BackButton({ className, ...props }: React.ButtonHTMLAttributes): JSX.Element { const combinedClassNames = ['back-btn', className].filter(Boolean).join(' ') diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index aa1273d..bd1a88c 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { useTranslation } from 'react-i18next' import routes, { hasNavigation, isNotEntrypoint } from '../../routes' -import BackButton from '../BackButton' +import BackButton from './BackButton' import iconUrl from './icon.png' import menuUrl from './menu.png' import './styles.css' diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx new file mode 100644 index 0000000..3f772ae --- /dev/null +++ b/src/components/Modal/index.tsx @@ -0,0 +1,30 @@ +import { ReactNode } from 'react' +import './styles.css' + +interface ModalProps { + children: ReactNode + open?: boolean + onClose?: () => void +} + +function Modal({ open, children, onClose }: ModalProps): JSX.Element { + const handleClose = () => { + onClose?.() + } + if (!open) return <> + return ( +
+
+ + {children} +
+
+ ) +} + +export default Modal diff --git a/src/components/Modal/styles.css b/src/components/Modal/styles.css new file mode 100644 index 0000000..496687c --- /dev/null +++ b/src/components/Modal/styles.css @@ -0,0 +1,65 @@ +.modal { + background: rgba(85,84,85,.8); + height: calc(100% + 76px); + left: 0; + margin-inline: 50%; + overflow: hidden; + position: fixed; + top: -76px; + -webkit-transform: translate(-50%); + transform: translate(-50%); + width: 100vw; + z-index: 2000; +} + +.modal-content { + position: absolute; + background: #fff; + width: 100%; + max-width: 375px; + top: 50%; + left: 50%; + z-index: 3; + border-radius: 16px; + padding: 64px 24px; + transform: translate(-50%,-50%); +} + +.modal-close-btn { + display: flex; + position: absolute; + justify-content: center; + align-items: center; + background: #eff2fd; + border: none; + border-radius: 20px; + height: 32px; + padding: 0; + right: 20px; + top: 16px; + width: 32px; + cursor: pointer; +} + +.modal .main-btn { + font-size: 14px; + font-weight: 400; + height: 48px; + line-height: 18px; + margin-top: 16px; + max-width: 100%; + min-height: 48px; + width: 100%; + color: #121620; +} + +.modal .main-btn:disabled { + background: #c7c7c7; + cursor: not-allowed; + opacity: 100%; +} + +.modal .main-btn svg { + margin-right: 12px; + fill: #121620; +} diff --git a/src/components/PaymentPage/index.tsx b/src/components/PaymentPage/index.tsx index ab90386..c6df6ea 100644 --- a/src/components/PaymentPage/index.tsx +++ b/src/components/PaymentPage/index.tsx @@ -1,66 +1,44 @@ -import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useNavigate } from 'react-router-dom' import { useSelector } from 'react-redux' import { selectors } from '../../store' -import { useAuth } from '../../auth' -import { useApi, useApiCall, SubscriptionCheckout } from '../../api' +import { usePayment } from '../../payment' +import { + ApplePayBanner, + ApplePayButton, + GooglePayBanner, + GooglePayButton, + CardButton, + CardModal +} from './methods' import UserHeader from '../UserHeader' import Title from '../Title' -import Loader from '../Loader' -import MainButton from '../MainButton' -import applePaySafeCheckout from './Apple-Pay.png' -import gPaySafeCheckout from './Google-Pay.png' -import secure from './Secure.png' -import ApplePay from './Apple-Pay.svg' -import GooglePay from './G-Pay.svg' -import card from './card.svg' -import routes from '../../routes' +import secure from './secure.png' import './styles.css' - -const isAndroid = () => /Android/i.test(navigator.userAgent) -const isApple = () => /Macintosh|iPhone|iPad|iPod/i.test(navigator.userAgent) +import { useState } from 'react' function PaymentPage(): JSX.Element { - const api = useApi() - const { token } = useAuth() - const { t, i18n } = useTranslation() - const locale = i18n.language - const navigate = useNavigate() + const { t } = useTranslation() + const { applePay } = usePayment() + const [open, setOpen] = useState(false) + const isApplePayAvailable = import.meta.env.PROD && applePay?.canMakePayments() const email = useSelector(selectors.selectEmail) - const itemPriceId = 'aura-membership-2-week-USD' - const handleClick = () => navigate(routes.client.wallpaper()) - const loadData = useCallback(() => { - return api.getSubscriptionCheckout({ locale, token, itemPriceId }) - }, [api, itemPriceId, locale, token]) - const { data, isPending } = useApiCall(loadData) - console.log(data, isPending) + return ( <>
- {isAndroid() && Guaranteed safe checkout} - {isApple() && Guaranteed safe checkout} + { isApplePayAvailable ? : } 100% Secure
{t('choose_payment')} - {isPending ? : ( - <> - - {isAndroid() && Google Pay} - {isApple() && Apple Pay} - -
{t('or').toUpperCase()}
- - Credit / Debit Card - {t('card')} - -

- {t('will_be_charged', { strongText: {t('trial_price')} })} -

- - )} + { isApplePayAvailable ? : } +
{t('or').toUpperCase()}
+ setOpen(true)} /> +

+ {t('will_be_charged', { strongText: {t('trial_price')} })} +

+ setOpen(false)} />
) diff --git a/src/components/PaymentPage/Apple-Pay.png b/src/components/PaymentPage/methods/ApplePay/Apple-Pay.png similarity index 100% rename from src/components/PaymentPage/Apple-Pay.png rename to src/components/PaymentPage/methods/ApplePay/Apple-Pay.png diff --git a/src/components/PaymentPage/Apple-Pay.svg b/src/components/PaymentPage/methods/ApplePay/Apple-Pay.svg similarity index 100% rename from src/components/PaymentPage/Apple-Pay.svg rename to src/components/PaymentPage/methods/ApplePay/Apple-Pay.svg diff --git a/src/components/PaymentPage/methods/ApplePay/Banner.tsx b/src/components/PaymentPage/methods/ApplePay/Banner.tsx new file mode 100644 index 0000000..20b889d --- /dev/null +++ b/src/components/PaymentPage/methods/ApplePay/Banner.tsx @@ -0,0 +1,5 @@ +import applePaySafeCheckout from './Apple-Pay.png' + +export function ApplePayBanner (): JSX.Element { + return Guaranteed safe checkout +} diff --git a/src/components/PaymentPage/methods/ApplePay/Button.tsx b/src/components/PaymentPage/methods/ApplePay/Button.tsx new file mode 100644 index 0000000..cadda22 --- /dev/null +++ b/src/components/PaymentPage/methods/ApplePay/Button.tsx @@ -0,0 +1,14 @@ +import { useNavigate } from 'react-router-dom' +import MainButton from '../../../MainButton' +import routes from '../../../../routes' +import ApplePay from './Apple-Pay.svg' + +export function ApplePayButton(): JSX.Element { + const navigate = useNavigate() + const handleClick = () => navigate(routes.client.wallpaper()) + return ( + + Apple Pay + + ) +} diff --git a/src/components/PaymentPage/methods/ApplePay/index.ts b/src/components/PaymentPage/methods/ApplePay/index.ts new file mode 100644 index 0000000..3818e10 --- /dev/null +++ b/src/components/PaymentPage/methods/ApplePay/index.ts @@ -0,0 +1,2 @@ +export * from './Banner' +export * from './Button' diff --git a/src/components/PaymentPage/methods/Card/Button.tsx b/src/components/PaymentPage/methods/Card/Button.tsx new file mode 100644 index 0000000..613efca --- /dev/null +++ b/src/components/PaymentPage/methods/Card/Button.tsx @@ -0,0 +1,18 @@ + +import { useTranslation } from 'react-i18next' +import MainButton from '../../../MainButton' +import card from './card.svg' + +interface CardButtonProps { + onClick: () => void +} + +export function CardButton({ onClick }: CardButtonProps): JSX.Element { + const { t } = useTranslation() + return ( + + Credit / Debit Card + {t('card')} + + ) +} diff --git a/src/components/PaymentPage/methods/Card/Modal.tsx b/src/components/PaymentPage/methods/Card/Modal.tsx new file mode 100644 index 0000000..b9af53c --- /dev/null +++ b/src/components/PaymentPage/methods/Card/Modal.tsx @@ -0,0 +1,58 @@ +import { useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { + CardCVV, CardComponent, CardExpiry, CardNumber, Provider +} from '@chargebee/chargebee-js-react-wrapper' +import { usePayment } from '../../../../payment' +import Modal from '../../../Modal' +import Title from '../../../Title' +import MainButton from '../../../MainButton' +import visa from './visa.svg' +import mastercard from './mastercard.svg' +import amex from './amex.svg' +import diners from './diners.svg' +import discover from './discover.svg' + +interface CardModalProps { + open: boolean + onClose: () => void +} + +export function CardModal({ open, onClose }: CardModalProps): JSX.Element { + const cardRef = useRef(null) + const { t, i18n } = useTranslation() + const locale = i18n.language + const { chargebee } = usePayment() + const payWithCard = () => { + // cardRef.current?.tokenize() + } + return ( + +
+ {t('card')} +
+ Visa Card + Mastercard Card + Amex Card + Diners Card + Discover Card +
+
+ + + + + + + +

{t('charged_only')}

+ + + + + + Start 7-Day Trial + +
+ ) +} diff --git a/src/components/PaymentPage/methods/Card/amex.svg b/src/components/PaymentPage/methods/Card/amex.svg new file mode 100644 index 0000000..1fa5c94 --- /dev/null +++ b/src/components/PaymentPage/methods/Card/amex.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/PaymentPage/card.svg b/src/components/PaymentPage/methods/Card/card.svg similarity index 100% rename from src/components/PaymentPage/card.svg rename to src/components/PaymentPage/methods/Card/card.svg diff --git a/src/components/PaymentPage/methods/Card/diners.svg b/src/components/PaymentPage/methods/Card/diners.svg new file mode 100644 index 0000000..6c050b6 --- /dev/null +++ b/src/components/PaymentPage/methods/Card/diners.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/components/PaymentPage/methods/Card/discover.svg b/src/components/PaymentPage/methods/Card/discover.svg new file mode 100644 index 0000000..b9884e8 --- /dev/null +++ b/src/components/PaymentPage/methods/Card/discover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/components/PaymentPage/methods/Card/index.ts b/src/components/PaymentPage/methods/Card/index.ts new file mode 100644 index 0000000..794d8c4 --- /dev/null +++ b/src/components/PaymentPage/methods/Card/index.ts @@ -0,0 +1,2 @@ +export * from './Button' +export * from './Modal' diff --git a/src/components/PaymentPage/methods/Card/mastercard.svg b/src/components/PaymentPage/methods/Card/mastercard.svg new file mode 100644 index 0000000..0f02395 --- /dev/null +++ b/src/components/PaymentPage/methods/Card/mastercard.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/PaymentPage/methods/Card/visa.svg b/src/components/PaymentPage/methods/Card/visa.svg new file mode 100644 index 0000000..3efb559 --- /dev/null +++ b/src/components/PaymentPage/methods/Card/visa.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/PaymentPage/methods/GooglePay/Banner.tsx b/src/components/PaymentPage/methods/GooglePay/Banner.tsx new file mode 100644 index 0000000..af82d77 --- /dev/null +++ b/src/components/PaymentPage/methods/GooglePay/Banner.tsx @@ -0,0 +1,5 @@ +import gPaySafeCheckout from './Google-Pay.png' + +export function GooglePayBanner(): JSX.Element { + return Guaranteed safe checkout +} diff --git a/src/components/PaymentPage/methods/GooglePay/Button.tsx b/src/components/PaymentPage/methods/GooglePay/Button.tsx new file mode 100644 index 0000000..2337f3e --- /dev/null +++ b/src/components/PaymentPage/methods/GooglePay/Button.tsx @@ -0,0 +1,14 @@ +import { useNavigate } from 'react-router-dom' +import routes from '../../../../routes' +import MainButton from '../../../MainButton' +import GooglePay from './G-Pay.svg' + +export function GooglePayButton(): JSX.Element { + const navigate = useNavigate() + const handleClick = () => navigate(routes.client.wallpaper()) + return ( + + Google Pay + + ) +} diff --git a/src/components/PaymentPage/G-Pay.svg b/src/components/PaymentPage/methods/GooglePay/G-Pay.svg similarity index 98% rename from src/components/PaymentPage/G-Pay.svg rename to src/components/PaymentPage/methods/GooglePay/G-Pay.svg index f01db7a..2bd000b 100644 --- a/src/components/PaymentPage/G-Pay.svg +++ b/src/components/PaymentPage/methods/GooglePay/G-Pay.svg @@ -2,7 +2,7 @@