/* eslint-disable react/jsx-no-bind */ import React, { Fragment, useCallback, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import Slider from 'rc-slider'; import Tooltip from 'rc-tooltip'; import { Alert, Button, Card, CardBody, CardTitle, Col, Form, FormGroup, Label, Modal, ModalBody, ModalFooter, ModalHeader, Row } from 'reactstrap'; import { licenses, mediaTypes, features, addonFeatures } from '../../../LoginRegister/Registration/PlanConstants'; import useForm from '../../../common/hooks/useForm'; import { debounce } from 'lodash'; import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js'; import useIsMounted from '../../../common/hooks/useIsMounted'; import reduxConnect from '../../../../redux/utils/connect'; import { getPlans, updatePrice } from '../../../../api/registration/registration'; import { updatePlanHubspot, updatePlanPayment } from '../../../../api/plans/userPlans'; import { planRoutes } from './UserPlans'; import BillingDetailsForm from './BillingDetailsForm'; import simpleNumberLocalizer from 'react-widgets-simple-number'; import NumberPicker from 'react-widgets/lib/NumberPicker'; import LoadersAdvanced from '../../../common/Loader/Loader'; import { IoIosWarning } from 'react-icons/io'; import { convertUTCtoLocal, setDocumentData } from '../../../../common/helper'; import { translate } from 'react-i18next'; simpleNumberLocalizer(); const Handle = Slider.Handle; const handle = (props) => { // eslint-disable-next-line react/prop-types const { value, dragging, index, ...restProps } = props; return ( ); }; const initialForm = { savedFeeds: 0, searchesPerDay: 0, webFeeds: 0, alerts: 0, news: 0, blog: 0, reddit: 0, instagram: 0, twitter: 0, analytics: 0, subscriberAccounts: 0, masterAccounts: 0 }; const initialPaymentForm = { name: '', line1: '', line2: '', city: '', state: '', postal_code: '', country: '', email: '', phone: '', errors: { name: null, line1: null, city: null, state: null, postal_code: null, country: null, email: null, phone: null } }; function UpdatePlan({ actions, restrictions, t }) { const stripe = useStripe(); const elements = useElements(); const isMounted = useIsMounted(); // first step const { form, handleChange, resetForm } = useForm(initialForm); const [updatingPrice, setUpdatingPrice] = useState(true); const [totalCost, setTotalCost] = useState(' - '); const [modal, setModal] = useState(false); const [loading, setLoading] = useState(false); const [planLoading, setPlanLoading] = useState(true); const [planError, setPlanError] = useState(false); const [planList, setPlanList] = useState([]); const [disableUpdate, setDisableUpdate] = useState(true); // second step const [nextStep, setNextStep] = useState(false); const { form: paymentForm, handleChange: handlePaymentForm, errors: paymentFormErrors, handleValidation: handlePaymentValidation, validateSubmit } = useForm(initialPaymentForm); const [paymentError, setPaymentError] = useState(false); const [paymentLoading, setPaymentLoading] = useState(false); // to update price when input changes useEffect(() => { if (planList.length > 0) { debouncePrice(form); } }, [...Object.values(form)]); const debouncePrice = useCallback( debounce((form) => { setUpdatingPrice(true); updatePrice(form).then((res) => { if (!isMounted.current) { return false; } if (res.error || isNaN(res.data.totalPrice)) { actions.addAlert(res.data); setUpdatingPrice(false); setTotalCost('Error'); return; } setTotalCost(res.data.totalPrice); setUpdatingPrice(false); }); }, 1000), [isMounted.current] ); useEffect(() => { if (!restrictions.isPlanCancelled && !restrictions.isPlanDowngrade) { setDisableUpdate(false); } else { setDisableUpdate(true); } }, [restrictions.isPlanCancelled, restrictions.isPlanDowngrade]); useEffect(() => { getBillingPlans(); setDocumentData('title', 'Update Plan'); return () => setDocumentData('title'); // default }, []); function getBillingPlans() { setPlanLoading(true); setPlanError(false); getPlans().then((res) => { if (!isMounted.current) { return false; } if (res.error || !res.data || !res.data.length) { setPlanError(true); setPlanLoading(false); res.data && res.data.length > 0 && actions.addAlert(res.data); return; } setPlanLoading(false); setPlanList(res.data); const modified = { ...initialForm }; let selectedPlan = {}; if (restrictions.plans.price > 0) { selectedPlan = { ...restrictions.plans }; Object.entries(restrictions.limits).map(([key, value]) => { selectedPlan[key] = value.limit; }); selectedPlan.blog = selectedPlan.blogs; delete selectedPlan.blogs; } else { selectedPlan = res.data[0]; } Object.keys(initialForm).map((key) => { modified[key] = selectedPlan[key] === undefined ? modified[key] : selectedPlan[key] === true ? 1 : selectedPlan[key] === false ? 0 : selectedPlan[key]; }); resetForm(modified); }); } function changePlan(id) { const selectedPlan = planList.find((plan) => plan.id === id); const modified = { ...initialForm }; Object.keys(initialForm).map((key) => { modified[key] = selectedPlan[key] === undefined ? modified[key] : selectedPlan[key] === true ? 1 : selectedPlan[key] === false ? 0 : selectedPlan[key]; }); resetForm(modified); } function handleSubmit() { if (restrictions.isPlanCancelled || restrictions.isPlanDowngrade) { return; } // move to payment page if new basic user // instruct according to upgrade and downgrade // if card already stored then only update the plan by showing modal or providing option to change card setLoading(true); if (restrictions.isPaymentId) { setModal(true); // show details of card } else { setNextStep(true); window.scrollTo(0, 0); } setLoading(false); } function toggle() { setModal((prev) => !prev); } function proceedToDetails() { toggle(); setNextStep(true); window.scrollTo(0, 0); } const submitPayment = async () => { if (!stripe || !elements) { // Stripe.js has not loaded yet. return; } if (restrictions.isPlanCancelled || restrictions.isPlanDowngrade) { return; } setPaymentError(false); setPaymentLoading(true); const obj = validateSubmit(); if (!obj) { setPaymentLoading(false); return actions.addAlert({ type: 'error', transKey: 'requiredInfo' }); } const cardElement = elements.getElement(CardElement); const { name, line1, line2, city, state, postal_code, country, email, phone } = obj; const { error, paymentMethod } = await stripe.createPaymentMethod({ type: 'card', card: cardElement, billing_details: { name, email, phone, address: { line1: line1, line2: line2, city: city, state: state, postal_code: postal_code, country: country } } }); if (error) { setPaymentError(error); setPaymentLoading(false); return; } const newObj = { ...form }; newObj.masterAccounts = '1'; newObj.paymentID = paymentMethod.id; //stripe card element ID const res = await updatePlanPayment(newObj); if (res.error) { res.data ? actions.addAlert(res.data) : actions.addAlert({ type: 'error', transKey: 'somethingWrong' }); setPaymentLoading(false); return; } window.gtag && window.gtag('event', 'purchase', { currency: 'USD', value: totalCost }); await updatePlanHubspot({ ...obj, ...form, totalCost }); actions.addAlert({ type: 'notice', transKey: 'planUpdated' }); // refresh page on success and move to active plan details setTimeout(() => { window.location.pathname = `/app/plans/${planRoutes.current}`; }, 1000); }; const proceedPayment = async () => { // payment with old card setLoading(true); const newObj = { ...form }; newObj.masterAccounts = '1'; const res = await updatePlanPayment(newObj); if (res.error) { res.data ? actions.addAlert(res.data) : actions.addAlert({ type: 'error', transKey: 'somethingWrong' }); setLoading(false); return; } window.gtag && window.gtag('event', 'purchase', { currency: 'USD', value: totalCost }); await updatePlanHubspot({ ...form, totalCost }); actions.addAlert({ type: 'notice', transKey: 'planUpdated' }); // refresh page on success and move to active plan details setTimeout(() => { window.location.pathname = `/app/plans/${planRoutes.current}`; }, 1000); }; function moveBack() { window.scrollTo(0, 0); setNextStep(false); } if (planError || planLoading) { return ( {t('plans.updatePlan.heading')} {planError && (
{t('plans.updatePlan.planLoadingFailed')}{' '}
)}
{planLoading && }
); } const isRTL = document.documentElement.dir === 'rtl'; return ( {!nextStep ? ( {t('plans.updatePlan.heading')}

{t('plans.updatePlan.subText')}{' '} {t('plans.updatePlan.learnMoreBtn')} .


{t('plans.updatePlan.prePlans')}
{planList.map((plan) => ( ))}

{t('plans.updatePlan.mediaTypes')}
{mediaTypes.map((type) => ( ))}

{t('plans.updatePlan.licenses')}
{licenses.map((license) => (
{form[license.name]}
handleChange(license.name, val) } />
))}

{t('plans.updatePlan.features')}
{features.map((type) => ( ))}
{features.map((type) => form[type.name] ? (

{type.desc}

) : null )}
{t('plans.updatePlan.addOns')}
{addonFeatures.map((type) => ( handleChange(type.name, val) } /> ))}
{t('plans.updatePlan.totalCost')}
{t('plans.updatePlan.monthly')}
{/* {updatingPrice && (
)} */}
${totalCost}

{restrictions.isPlanCancelled || restrictions.isPlanDowngrade ? (

{t('plans.updatePlan.cancelledWarning', { text: restrictions.isPlanCancelled ? 'cancelled' : 'downgraded' })}{' '} {restrictions.subStartDate && restrictions.subEndDate ? `(${convertUTCtoLocal( restrictions.subStartDate, 'MMM D, YYYY' )} - ${convertUTCtoLocal( restrictions.subEndDate, 'MMM D, YYYY' )})` : ''}

) : ( '' )}
) : ( {t('plans.updatePlan.billingHeading')} {paymentError && (

{t('plans.updatePlan.error')}

{paymentError.message}
)}
)}
{t('plans.updatePlan.confirmationHeading')}
{restrictions.plans && restrictions.plans.price > 0 ? ( restrictions.plans.price === totalCost ? null : restrictions.plans .price < totalCost ? (

{t('plans.updatePlan.upgradeNotice')}

) : (

{t('plans.updatePlan.downgradeNotice')}

) ) : null}

{t('plans.updatePlan.alreadyStoredCard')}

); } UpdatePlan.propTypes = { t: PropTypes.func.isRequired, actions: PropTypes.object, restrictions: PropTypes.object }; export default reduxConnect('restrictions', [ 'common', 'auth', 'user', 'restrictions' ])(translate(['tabsContent'], { wait: true })(UpdatePlan));