update plan selector to use mui dialog
This commit is contained in:
parent
03228664f6
commit
1abd62964f
|
@ -38,10 +38,7 @@ const LinkButton: FC<LinkProps<'button', { color?: ButtonProps['color'] }>> = ({
|
|||
<Link
|
||||
component="button"
|
||||
sx={{
|
||||
color:
|
||||
props.color && typeof props.color === 'object'
|
||||
? `${props.color}.main`
|
||||
: props.color,
|
||||
color: props.color && `${props.color}.main`,
|
||||
...sx,
|
||||
}}
|
||||
{...props}>
|
||||
|
|
|
@ -1,417 +0,0 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Form, Modal, Button } from 'react-bootstrap';
|
||||
import constants from 'utils/strings/constants';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { Plan, Subscription } from 'types/billing';
|
||||
import {
|
||||
convertBytesToGBs,
|
||||
getUserSubscription,
|
||||
isUserSubscribedPlan,
|
||||
isSubscriptionCancelled,
|
||||
updatePaymentMethod,
|
||||
updateSubscription,
|
||||
activateSubscription,
|
||||
cancelSubscription,
|
||||
hasStripeSubscription,
|
||||
hasPaidSubscription,
|
||||
isOnFreePlan,
|
||||
planForSubscription,
|
||||
hasMobileSubscription,
|
||||
hasPaypalSubscription,
|
||||
manageFamilyMethod,
|
||||
} from 'utils/billing';
|
||||
import { reverseString } from 'utils/common';
|
||||
import ArrowEast from 'components/icons/ArrowEast';
|
||||
import LinkButton from './LinkButton';
|
||||
import { DeadCenter, GalleryContext } from 'pages/gallery';
|
||||
import billingService from 'services/billingService';
|
||||
import { SetLoading } from 'types/gallery';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { AppContext } from 'pages/_app';
|
||||
|
||||
export const PlanIcon = styled.div<{ currentlySubscribed: boolean }>`
|
||||
border-radius: 20px;
|
||||
width: 220px;
|
||||
border: 2px solid #333;
|
||||
padding: 30px;
|
||||
margin: 10px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
background-color: #ffffff00;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
cursor: ${(props) =>
|
||||
props.currentlySubscribed ? 'not-allowed' : 'pointer'};
|
||||
border-color: ${(props) => props.currentlySubscribed && '#56e066'};
|
||||
transition: all 0.3s ease-out;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
& > div:first-child::before {
|
||||
content: ' ';
|
||||
height: 600px;
|
||||
width: 50px;
|
||||
background-color: #444;
|
||||
left: 0;
|
||||
top: -50%;
|
||||
position: absolute;
|
||||
transform: rotate(45deg) translateX(-200px);
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
&:hover
|
||||
${(props) =>
|
||||
!props.currentlySubscribed &&
|
||||
css`
|
||||
{
|
||||
transform: scale(1.1);
|
||||
background-color: #ffffff11;
|
||||
}
|
||||
`}
|
||||
&:hover
|
||||
> div:first-child::before {
|
||||
transform: rotate(45deg) translateX(300px);
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
modalView: boolean;
|
||||
closeModal: any;
|
||||
|
||||
setLoading: SetLoading;
|
||||
}
|
||||
enum PLAN_PERIOD {
|
||||
MONTH = 'month',
|
||||
YEAR = 'year',
|
||||
}
|
||||
function PlanSelector(props: Props) {
|
||||
const subscription: Subscription = getUserSubscription();
|
||||
const [plans, setPlans] = useState<Plan[]>(null);
|
||||
const [planPeriod, setPlanPeriod] = useState<PLAN_PERIOD>(PLAN_PERIOD.YEAR);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const appContext = useContext(AppContext);
|
||||
|
||||
const togglePeriod = () => {
|
||||
setPlanPeriod((prevPeriod) =>
|
||||
prevPeriod === PLAN_PERIOD.MONTH
|
||||
? PLAN_PERIOD.YEAR
|
||||
: PLAN_PERIOD.MONTH
|
||||
);
|
||||
};
|
||||
function onReopenClick() {
|
||||
appContext.closeMessageDialog();
|
||||
galleryContext.showPlanSelectorModal();
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!props.modalView) {
|
||||
return;
|
||||
}
|
||||
const main = async () => {
|
||||
try {
|
||||
props.setLoading(true);
|
||||
let plans = await billingService.getPlans();
|
||||
|
||||
const planNotListed =
|
||||
plans.filter((plan) =>
|
||||
isUserSubscribedPlan(plan, subscription)
|
||||
).length === 0;
|
||||
if (
|
||||
subscription &&
|
||||
!isOnFreePlan(subscription) &&
|
||||
planNotListed
|
||||
) {
|
||||
plans = [planForSubscription(subscription), ...plans];
|
||||
}
|
||||
setPlans(plans);
|
||||
} catch (e) {
|
||||
logError(e, 'plan selector modal open failed');
|
||||
props.closeModal();
|
||||
appContext.setDialogMessage({
|
||||
title: constants.OPEN_PLAN_SELECTOR_MODAL_FAILED,
|
||||
content: constants.UNKNOWN_ERROR,
|
||||
close: { text: 'close', variant: 'danger' },
|
||||
proceed: {
|
||||
text: constants.REOPEN_PLAN_SELECTOR_MODAL,
|
||||
variant: 'success',
|
||||
action: onReopenClick,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
props.setLoading(false);
|
||||
}
|
||||
};
|
||||
main();
|
||||
}, [props.modalView]);
|
||||
|
||||
async function onPlanSelect(plan: Plan) {
|
||||
if (
|
||||
hasMobileSubscription(subscription) &&
|
||||
!isSubscriptionCancelled(subscription)
|
||||
) {
|
||||
appContext.setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE,
|
||||
close: { variant: 'danger' },
|
||||
});
|
||||
} else if (
|
||||
hasPaypalSubscription(subscription) &&
|
||||
!isSubscriptionCancelled(subscription)
|
||||
) {
|
||||
appContext.setDialogMessage({
|
||||
title: constants.MANAGE_PLAN,
|
||||
content: constants.PAYPAL_MANAGE_NOT_SUPPORTED_MESSAGE(),
|
||||
close: { variant: 'danger' },
|
||||
});
|
||||
} else if (hasStripeSubscription(subscription)) {
|
||||
appContext.setDialogMessage({
|
||||
title: `${constants.CONFIRM} ${reverseString(
|
||||
constants.UPDATE_SUBSCRIPTION
|
||||
)}`,
|
||||
content: constants.UPDATE_SUBSCRIPTION_MESSAGE,
|
||||
proceed: {
|
||||
text: constants.UPDATE_SUBSCRIPTION,
|
||||
action: updateSubscription.bind(
|
||||
null,
|
||||
plan,
|
||||
appContext.setDialogMessage,
|
||||
props.setLoading,
|
||||
props.closeModal
|
||||
),
|
||||
variant: 'success',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
props.setLoading(true);
|
||||
await billingService.buySubscription(plan.stripeID);
|
||||
} catch (e) {
|
||||
props.setLoading(false);
|
||||
appContext.setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
content: constants.SUBSCRIPTION_PURCHASE_FAILED,
|
||||
close: { variant: 'danger' },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PlanIcons: JSX.Element[] = plans
|
||||
?.filter((plan) => plan.period === planPeriod)
|
||||
?.map((plan) => (
|
||||
<PlanIcon
|
||||
key={plan.stripeID}
|
||||
className="subscription-plan-selector"
|
||||
currentlySubscribed={isUserSubscribedPlan(plan, subscription)}
|
||||
onClick={
|
||||
isUserSubscribedPlan(plan, subscription)
|
||||
? () => {}
|
||||
: async () => await onPlanSelect(plan)
|
||||
}>
|
||||
<div>
|
||||
<span
|
||||
style={{
|
||||
color: '#ECECEC',
|
||||
fontWeight: 900,
|
||||
fontSize: '40px',
|
||||
lineHeight: '40px',
|
||||
}}>
|
||||
{convertBytesToGBs(plan.storage, 0)}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
color: '#858585',
|
||||
fontSize: '24px',
|
||||
fontWeight: 900,
|
||||
}}>
|
||||
{' '}
|
||||
GB
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="bold-text"
|
||||
style={{
|
||||
color: '#aaa',
|
||||
lineHeight: '36px',
|
||||
fontSize: '20px',
|
||||
}}>
|
||||
{`${plan.price} / ${plan.period}`}
|
||||
</div>
|
||||
<Button
|
||||
variant="outline-success"
|
||||
block
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
fontSize: '14px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
disabled={isUserSubscribedPlan(plan, subscription)}>
|
||||
{constants.CHOOSE_PLAN_BTN}
|
||||
<ArrowEast style={{ marginLeft: '5px' }} />
|
||||
</Button>
|
||||
</PlanIcon>
|
||||
));
|
||||
return (
|
||||
<Modal
|
||||
show={props.modalView}
|
||||
onHide={props.closeModal}
|
||||
size="xl"
|
||||
centered
|
||||
backdrop={hasPaidSubscription(subscription) ? true : 'static'}
|
||||
contentClassName="plan-selector-modal-content">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title
|
||||
style={{
|
||||
marginLeft: '12px',
|
||||
width: '100%',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
<span>
|
||||
{hasPaidSubscription(subscription)
|
||||
? constants.MANAGE_PLAN
|
||||
: constants.CHOOSE_PLAN}
|
||||
</span>
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body style={{ marginTop: '20px' }}>
|
||||
<DeadCenter>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<span
|
||||
className="bold-text"
|
||||
style={{ fontSize: '16px' }}>
|
||||
{constants.MONTHLY}
|
||||
</span>
|
||||
|
||||
<Form.Switch
|
||||
checked={planPeriod === PLAN_PERIOD.YEAR}
|
||||
id="plan-period-toggler"
|
||||
style={{
|
||||
margin: '-4px 0 20px 15px',
|
||||
fontSize: '10px',
|
||||
}}
|
||||
className="custom-switch-md"
|
||||
onChange={togglePeriod}
|
||||
/>
|
||||
<span
|
||||
className="bold-text"
|
||||
style={{ fontSize: '16px' }}>
|
||||
{constants.YEARLY}
|
||||
</span>
|
||||
</div>
|
||||
</DeadCenter>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-around',
|
||||
flexWrap: 'wrap',
|
||||
minHeight: '212px',
|
||||
margin: '5px 0',
|
||||
}}>
|
||||
{plans && PlanIcons}
|
||||
</div>
|
||||
<DeadCenter style={{ marginBottom: '30px' }}>
|
||||
{hasPaidSubscription(subscription) ? (
|
||||
<>
|
||||
{hasStripeSubscription(subscription) && (
|
||||
<>
|
||||
{isSubscriptionCancelled(subscription) ? (
|
||||
<LinkButton
|
||||
color="success"
|
||||
onClick={() =>
|
||||
appContext.setDialogMessage({
|
||||
title: constants.CONFIRM_ACTIVATE_SUBSCRIPTION,
|
||||
content:
|
||||
constants.ACTIVATE_SUBSCRIPTION_MESSAGE(
|
||||
subscription.expiryTime
|
||||
),
|
||||
|
||||
proceed: {
|
||||
text: constants.ACTIVATE_SUBSCRIPTION,
|
||||
action: activateSubscription.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.closeModal,
|
||||
props.setLoading
|
||||
),
|
||||
variant: 'success',
|
||||
},
|
||||
close: {
|
||||
text: constants.CANCEL,
|
||||
},
|
||||
})
|
||||
}>
|
||||
{constants.ACTIVATE_SUBSCRIPTION}
|
||||
</LinkButton>
|
||||
) : (
|
||||
<LinkButton
|
||||
color="danger"
|
||||
onClick={() =>
|
||||
appContext.setDialogMessage({
|
||||
title: constants.CONFIRM_CANCEL_SUBSCRIPTION,
|
||||
content:
|
||||
constants.CANCEL_SUBSCRIPTION_MESSAGE(),
|
||||
|
||||
proceed: {
|
||||
text: constants.CANCEL_SUBSCRIPTION,
|
||||
action: cancelSubscription.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.closeModal,
|
||||
props.setLoading
|
||||
),
|
||||
variant: 'danger',
|
||||
},
|
||||
close: {
|
||||
text: constants.CANCEL,
|
||||
},
|
||||
})
|
||||
}>
|
||||
{constants.CANCEL_SUBSCRIPTION}
|
||||
</LinkButton>
|
||||
)}
|
||||
<LinkButton
|
||||
color="primary"
|
||||
onClick={updatePaymentMethod.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.setLoading
|
||||
)}
|
||||
style={{ marginTop: '20px' }}>
|
||||
{constants.MANAGEMENT_PORTAL}
|
||||
</LinkButton>
|
||||
</>
|
||||
)}
|
||||
<LinkButton
|
||||
color="primary"
|
||||
onClick={manageFamilyMethod.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.setLoading
|
||||
)}
|
||||
style={{ marginTop: '20px' }}>
|
||||
{constants.MANAGE_FAMILY_PORTAL}
|
||||
</LinkButton>
|
||||
</>
|
||||
) : (
|
||||
<LinkButton
|
||||
color="primary"
|
||||
onClick={props.closeModal}
|
||||
style={{
|
||||
color: 'rgb(121, 121, 121)',
|
||||
marginTop: '20px',
|
||||
}}>
|
||||
{isOnFreePlan(subscription)
|
||||
? constants.SKIP
|
||||
: constants.CLOSE}
|
||||
</LinkButton>
|
||||
)}
|
||||
</DeadCenter>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlanSelector;
|
184
src/components/pages/gallery/PlanSelector/index.tsx
Normal file
184
src/components/pages/gallery/PlanSelector/index.tsx
Normal file
|
@ -0,0 +1,184 @@
|
|||
import { PeriodToggler } from './periodToggler';
|
||||
import { ManageSubscription } from './manageSubscription';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { Plan, Subscription } from 'types/billing';
|
||||
import {
|
||||
getUserSubscription,
|
||||
isUserSubscribedPlan,
|
||||
isSubscriptionCancelled,
|
||||
updateSubscription,
|
||||
hasStripeSubscription,
|
||||
hasPaidSubscription,
|
||||
isOnFreePlan,
|
||||
planForSubscription,
|
||||
hasMobileSubscription,
|
||||
hasPaypalSubscription,
|
||||
} from 'utils/billing';
|
||||
import { reverseString } from 'utils/common';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import billingService from 'services/billingService';
|
||||
import { SetLoading } from 'types/gallery';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import DialogBox from 'components/DialogBox';
|
||||
import Plans from './plans';
|
||||
import { DialogBoxAttributes } from 'types/dialogBox';
|
||||
|
||||
interface Props {
|
||||
modalView: boolean;
|
||||
closeModal: any;
|
||||
|
||||
setLoading: SetLoading;
|
||||
}
|
||||
export enum PLAN_PERIOD {
|
||||
MONTH = 'month',
|
||||
YEAR = 'year',
|
||||
}
|
||||
function PlanSelector(props: Props) {
|
||||
const subscription: Subscription = getUserSubscription();
|
||||
const [plans, setPlans] = useState<Plan[]>(null);
|
||||
const [planPeriod, setPlanPeriod] = useState<PLAN_PERIOD>(PLAN_PERIOD.YEAR);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const appContext = useContext(AppContext);
|
||||
|
||||
const togglePeriod = () => {
|
||||
setPlanPeriod((prevPeriod) =>
|
||||
prevPeriod === PLAN_PERIOD.MONTH
|
||||
? PLAN_PERIOD.YEAR
|
||||
: PLAN_PERIOD.MONTH
|
||||
);
|
||||
};
|
||||
function onReopenClick() {
|
||||
appContext.closeMessageDialog();
|
||||
galleryContext.showPlanSelectorModal();
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!props.modalView) {
|
||||
return;
|
||||
}
|
||||
const main = async () => {
|
||||
try {
|
||||
props.setLoading(true);
|
||||
let plans = await billingService.getPlans();
|
||||
|
||||
const planNotListed =
|
||||
plans.filter((plan) =>
|
||||
isUserSubscribedPlan(plan, subscription)
|
||||
).length === 0;
|
||||
if (
|
||||
subscription &&
|
||||
!isOnFreePlan(subscription) &&
|
||||
planNotListed
|
||||
) {
|
||||
plans = [planForSubscription(subscription), ...plans];
|
||||
}
|
||||
setPlans(plans);
|
||||
} catch (e) {
|
||||
logError(e, 'plan selector modal open failed');
|
||||
props.closeModal();
|
||||
appContext.setDialogMessage({
|
||||
title: constants.OPEN_PLAN_SELECTOR_MODAL_FAILED,
|
||||
content: constants.UNKNOWN_ERROR,
|
||||
close: { text: 'close', variant: 'danger' },
|
||||
proceed: {
|
||||
text: constants.REOPEN_PLAN_SELECTOR_MODAL,
|
||||
variant: 'success',
|
||||
action: onReopenClick,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
props.setLoading(false);
|
||||
}
|
||||
};
|
||||
main();
|
||||
}, [props.modalView]);
|
||||
|
||||
async function onPlanSelect(plan: Plan) {
|
||||
if (
|
||||
hasMobileSubscription(subscription) &&
|
||||
!isSubscriptionCancelled(subscription)
|
||||
) {
|
||||
appContext.setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE,
|
||||
close: { variant: 'danger' },
|
||||
});
|
||||
} else if (
|
||||
hasPaypalSubscription(subscription) &&
|
||||
!isSubscriptionCancelled(subscription)
|
||||
) {
|
||||
appContext.setDialogMessage({
|
||||
title: constants.MANAGE_PLAN,
|
||||
content: constants.PAYPAL_MANAGE_NOT_SUPPORTED_MESSAGE(),
|
||||
close: { variant: 'danger' },
|
||||
});
|
||||
} else if (hasStripeSubscription(subscription)) {
|
||||
appContext.setDialogMessage({
|
||||
title: `${constants.CONFIRM} ${reverseString(
|
||||
constants.UPDATE_SUBSCRIPTION
|
||||
)}`,
|
||||
content: constants.UPDATE_SUBSCRIPTION_MESSAGE,
|
||||
proceed: {
|
||||
text: constants.UPDATE_SUBSCRIPTION,
|
||||
action: updateSubscription.bind(
|
||||
null,
|
||||
plan,
|
||||
appContext.setDialogMessage,
|
||||
props.setLoading,
|
||||
props.closeModal
|
||||
),
|
||||
variant: 'success',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
props.setLoading(true);
|
||||
await billingService.buySubscription(plan.stripeID);
|
||||
} catch (e) {
|
||||
props.setLoading(false);
|
||||
appContext.setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
content: constants.SUBSCRIPTION_PURCHASE_FAILED,
|
||||
close: { variant: 'danger' },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const planSelectorAttributes: DialogBoxAttributes = {
|
||||
closeOnBackdropClick: hasPaidSubscription(subscription) ? true : false,
|
||||
title: hasPaidSubscription(subscription)
|
||||
? constants.MANAGE_PLAN
|
||||
: constants.CHOOSE_PLAN,
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogBox
|
||||
open={props.modalView}
|
||||
titleCloseButton
|
||||
onClose={props.closeModal}
|
||||
size={'xl'}
|
||||
attributes={planSelectorAttributes}
|
||||
fullWidth={false}>
|
||||
<PeriodToggler
|
||||
planPeriod={planPeriod}
|
||||
togglePeriod={togglePeriod}
|
||||
/>
|
||||
<Plans
|
||||
plans={plans}
|
||||
planPeriod={planPeriod}
|
||||
onPlanSelect={onPlanSelect}
|
||||
subscription={subscription}
|
||||
/>
|
||||
<ManageSubscription
|
||||
subscription={subscription}
|
||||
closeModal={props.closeModal}
|
||||
setLoading={props.setLoading}
|
||||
/>
|
||||
</DialogBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlanSelector;
|
122
src/components/pages/gallery/PlanSelector/manageSubscription.tsx
Normal file
122
src/components/pages/gallery/PlanSelector/manageSubscription.tsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
import { DeadCenter } from 'pages/gallery';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
activateSubscription,
|
||||
cancelSubscription,
|
||||
updatePaymentMethod,
|
||||
manageFamilyMethod,
|
||||
hasPaidSubscription,
|
||||
hasStripeSubscription,
|
||||
isOnFreePlan,
|
||||
isSubscriptionCancelled,
|
||||
} from 'utils/billing';
|
||||
import constants from 'utils/strings/constants';
|
||||
import LinkButton from '../LinkButton';
|
||||
export function ManageSubscription({ subscription, ...props }) {
|
||||
const appContext = useContext(AppContext);
|
||||
return (
|
||||
<DeadCenter
|
||||
style={{
|
||||
marginBottom: '30px',
|
||||
}}>
|
||||
{hasPaidSubscription(subscription) ? (
|
||||
<>
|
||||
{hasStripeSubscription(subscription) && (
|
||||
<>
|
||||
{isSubscriptionCancelled(subscription) ? (
|
||||
<LinkButton
|
||||
color="accent"
|
||||
onClick={() =>
|
||||
appContext.setDialogMessage({
|
||||
title: constants.CONFIRM_ACTIVATE_SUBSCRIPTION,
|
||||
content:
|
||||
constants.ACTIVATE_SUBSCRIPTION_MESSAGE(
|
||||
subscription.expiryTime
|
||||
),
|
||||
proceed: {
|
||||
text: constants.ACTIVATE_SUBSCRIPTION,
|
||||
action: activateSubscription.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.closeModal,
|
||||
props.setLoading
|
||||
),
|
||||
variant: 'success',
|
||||
},
|
||||
close: {
|
||||
text: constants.CANCEL,
|
||||
},
|
||||
})
|
||||
}>
|
||||
{constants.ACTIVATE_SUBSCRIPTION}
|
||||
</LinkButton>
|
||||
) : (
|
||||
<LinkButton
|
||||
color="danger"
|
||||
onClick={() =>
|
||||
appContext.setDialogMessage({
|
||||
title: constants.CONFIRM_CANCEL_SUBSCRIPTION,
|
||||
content:
|
||||
constants.CANCEL_SUBSCRIPTION_MESSAGE(),
|
||||
proceed: {
|
||||
text: constants.CANCEL_SUBSCRIPTION,
|
||||
action: cancelSubscription.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.closeModal,
|
||||
props.setLoading
|
||||
),
|
||||
variant: 'danger',
|
||||
},
|
||||
close: {
|
||||
text: constants.CANCEL,
|
||||
},
|
||||
})
|
||||
}>
|
||||
{constants.CANCEL_SUBSCRIPTION}
|
||||
</LinkButton>
|
||||
)}
|
||||
<LinkButton
|
||||
color="primary"
|
||||
onClick={updatePaymentMethod.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.setLoading
|
||||
)}
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
}}>
|
||||
{constants.MANAGEMENT_PORTAL}
|
||||
</LinkButton>
|
||||
</>
|
||||
)}
|
||||
<LinkButton
|
||||
color="primary"
|
||||
onClick={manageFamilyMethod.bind(
|
||||
null,
|
||||
appContext.setDialogMessage,
|
||||
props.setLoading
|
||||
)}
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
}}>
|
||||
{constants.MANAGE_FAMILY_PORTAL}
|
||||
</LinkButton>
|
||||
</>
|
||||
) : (
|
||||
<LinkButton
|
||||
color="primary"
|
||||
onClick={props.closeModal}
|
||||
style={{
|
||||
color: 'rgb(121, 121, 121)',
|
||||
marginTop: '20px',
|
||||
}}>
|
||||
{isOnFreePlan(subscription)
|
||||
? constants.SKIP
|
||||
: constants.CLOSE}
|
||||
</LinkButton>
|
||||
)}
|
||||
</DeadCenter>
|
||||
);
|
||||
}
|
41
src/components/pages/gallery/PlanSelector/periodToggler.tsx
Normal file
41
src/components/pages/gallery/PlanSelector/periodToggler.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { DeadCenter } from 'pages/gallery';
|
||||
import React from 'react';
|
||||
import { Form } from 'react-bootstrap';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { PLAN_PERIOD } from '.';
|
||||
export function PeriodToggler({ planPeriod, togglePeriod }) {
|
||||
return (
|
||||
<DeadCenter>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
}}>
|
||||
<span
|
||||
className="bold-text"
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
}}>
|
||||
{constants.MONTHLY}
|
||||
</span>
|
||||
|
||||
<Form.Switch
|
||||
checked={planPeriod === PLAN_PERIOD.YEAR}
|
||||
id="plan-period-toggler"
|
||||
style={{
|
||||
margin: '-4px 0 20px 15px',
|
||||
fontSize: '10px',
|
||||
}}
|
||||
className="custom-switch-md"
|
||||
onChange={togglePeriod}
|
||||
/>
|
||||
<span
|
||||
className="bold-text"
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
}}>
|
||||
{constants.YEARLY}
|
||||
</span>
|
||||
</div>
|
||||
</DeadCenter>
|
||||
);
|
||||
}
|
29
src/components/pages/gallery/PlanSelector/plans/index.tsx
Normal file
29
src/components/pages/gallery/PlanSelector/plans/index.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import { isUserSubscribedPlan, convertBytesToGBs } from 'utils/billing';
|
||||
import { PlanCard } from './planCard';
|
||||
|
||||
const Plans = ({ plans, planPeriod, subscription, onPlanSelect }) => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-around',
|
||||
flexWrap: 'wrap',
|
||||
minHeight: '212px',
|
||||
margin: '5px 0',
|
||||
}}>
|
||||
{plans
|
||||
?.filter((plan) => plan.period === planPeriod)
|
||||
?.map((plan) => (
|
||||
<PlanCard
|
||||
key={plan.stripeID}
|
||||
isUserSubscribedPlan={isUserSubscribedPlan}
|
||||
plan={plan}
|
||||
subscription={subscription}
|
||||
onPlanSelect={onPlanSelect}
|
||||
convertBytesToGBs={convertBytesToGBs}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Plans;
|
72
src/components/pages/gallery/PlanSelector/plans/planCard.tsx
Normal file
72
src/components/pages/gallery/PlanSelector/plans/planCard.tsx
Normal file
|
@ -0,0 +1,72 @@
|
|||
import ArrowEast from 'components/icons/ArrowEast';
|
||||
import React from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { PlanTile } from './planTile';
|
||||
|
||||
export function PlanCard({
|
||||
isUserSubscribedPlan,
|
||||
plan,
|
||||
subscription,
|
||||
onPlanSelect,
|
||||
convertBytesToGBs,
|
||||
}) {
|
||||
return (
|
||||
<PlanTile
|
||||
key={plan.stripeID}
|
||||
className="subscription-plan-selector"
|
||||
currentlySubscribed={isUserSubscribedPlan(plan, subscription)}
|
||||
onClick={
|
||||
isUserSubscribedPlan(plan, subscription)
|
||||
? () => {}
|
||||
: async () => await onPlanSelect(plan)
|
||||
}>
|
||||
<div>
|
||||
<span
|
||||
style={{
|
||||
color: '#ECECEC',
|
||||
fontWeight: 900,
|
||||
fontSize: '40px',
|
||||
lineHeight: '40px',
|
||||
}}>
|
||||
{convertBytesToGBs(plan.storage, 0)}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
color: '#858585',
|
||||
fontSize: '24px',
|
||||
fontWeight: 900,
|
||||
}}>
|
||||
{' '}
|
||||
GB
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="bold-text"
|
||||
style={{
|
||||
color: '#aaa',
|
||||
lineHeight: '36px',
|
||||
fontSize: '20px',
|
||||
}}>
|
||||
{`${plan.price} / ${plan.period}`}
|
||||
</div>
|
||||
<Button
|
||||
variant="outline-success"
|
||||
block
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
fontSize: '14px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
disabled={isUserSubscribedPlan(plan, subscription)}>
|
||||
{constants.CHOOSE_PLAN_BTN}
|
||||
<ArrowEast
|
||||
style={{
|
||||
marginLeft: '5px',
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
</PlanTile>
|
||||
);
|
||||
}
|
48
src/components/pages/gallery/PlanSelector/plans/planTile.tsx
Normal file
48
src/components/pages/gallery/PlanSelector/plans/planTile.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import styled, { css } from 'styled-components';
|
||||
|
||||
export const PlanTile = styled.div<{ currentlySubscribed: boolean }>`
|
||||
border-radius: 20px;
|
||||
width: 220px;
|
||||
border: 2px solid #333;
|
||||
padding: 30px;
|
||||
margin: 10px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
background-color: #ffffff00;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
cursor: ${(props) =>
|
||||
props.currentlySubscribed ? 'not-allowed' : 'pointer'};
|
||||
border-color: ${(props) => props.currentlySubscribed && '#56e066'};
|
||||
transition: all 0.3s ease-out;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
& > div:first-child::before {
|
||||
content: ' ';
|
||||
height: 600px;
|
||||
width: 50px;
|
||||
background-color: #444;
|
||||
left: 0;
|
||||
top: -50%;
|
||||
position: absolute;
|
||||
transform: rotate(45deg) translateX(-200px);
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
&:hover
|
||||
${(props) =>
|
||||
!props.currentlySubscribed &&
|
||||
css`
|
||||
{
|
||||
transform: scale(1.1);
|
||||
background-color: #ffffff11;
|
||||
}
|
||||
`}
|
||||
&:hover
|
||||
> div:first-child::before {
|
||||
transform: rotate(45deg) translateX(300px);
|
||||
}
|
||||
`;
|
|
@ -149,7 +149,7 @@ export default function Gallery() {
|
|||
count: 0,
|
||||
collectionID: 0,
|
||||
});
|
||||
const [planModalView, setPlanModalView] = useState(false);
|
||||
const [planModalView, setPlanModalView] = useState(true);
|
||||
const [blockingLoad, setBlockingLoad] = useState(false);
|
||||
const [collectionSelectorAttributes, setCollectionSelectorAttributes] =
|
||||
useState<CollectionSelectorAttributes>(null);
|
||||
|
|
Loading…
Reference in a new issue