Skip to content
Permalink
Browse files

feat(payments): add payment update form to subscriptions management

- Also refactor Subscrptions route into smaller sub-components

fixes #1086
  • Loading branch information...
lmorchard committed May 13, 2019
1 parent 047ab45 commit 5062bbaf031fc5c0ea4da2cff73044dc61edbef0

This file was deleted.

Oops, something went wrong.
@@ -0,0 +1,92 @@
import React, { useCallback, useEffect } from 'react';
import { useBooleanState } from '../../lib/hooks';
import { injectStripe, CardElement, ReactStripeElements } from 'react-stripe-elements';
import { UpdatePaymentFetchState } from '../../store/types';

type PaymentUpdateFormProps = {
accessToken: string,
resetUpdatePayment: Function,
updatePayment: Function,
updatePaymentStatus: UpdatePaymentFetchState
};
export const PaymentUpdateForm = ({
accessToken,
updatePayment,
updatePaymentStatus,
resetUpdatePayment,
stripe
}: PaymentUpdateFormProps & ReactStripeElements.InjectedStripeProps) => {
const [ updateRevealed, revealUpdate, hideUpdate ] = useBooleanState();

// Reset payment update status on initial render.
useEffect(() => {
resetUpdatePayment();
}, [ resetUpdatePayment ]);

const onSubmit = useCallback(ev => {
ev.preventDefault();

// TODO: use react state on form fields along with validation
const data = new FormData(ev.target);
const name = String(data.get('name'));

if (stripe) {
stripe
.createToken({ name })
.then((result) => {
updatePayment(accessToken, {
paymentToken: result && result.token && result.token.id,
});
hideUpdate(ev);
});
// TODO: error handling
}
}, [ accessToken, updatePayment, stripe ]);

if (updatePaymentStatus.loading) {
return (
<div>
<h3>Billing information</h3>
<p>Updating...</p>
</div>
);
}

if (updatePaymentStatus.error) {
return (
<div>
<h3>Billing information</h3>
<p>Updating... Error! {'' + updatePaymentStatus.error}</p>
</div>
);
}

return (
<div>
<h3>Billing information</h3>
{(!!updatePaymentStatus.result) &&
<p>Updating... Success! {'' + updatePaymentStatus.result}</p>}
{! updateRevealed ? <>
<button onClick={revealUpdate}>Change...</button>
</> : <>
<form onSubmit={onSubmit}>
<ul>
<li>
<input name="name" placeholder="Name" />
</li>
<li>
<p>Card details (e.g. 4242 4242 4242 4242)</p>
<CardElement style={{base: {fontSize: '18px'}}} />
</li>
<li>
<button onClick={hideUpdate}>Cancel</button>
<button>Update</button>
</li>
</ul>
</form>
</>}
</div>
);
};

export default injectStripe(PaymentUpdateForm);
@@ -0,0 +1,52 @@
import React, { useCallback } from 'react';
import { useBooleanState, useCheckboxState } from '../../lib/hooks';
import { Subscription as SubscriptionType } from '../../store/types';

type SubscriptionProps = {
accessToken: string,
subscription: SubscriptionType,
cancelSubscription: Function,
};
export const Subscription = ({
accessToken,
cancelSubscription,
subscription: {
subscriptionId,
productName,
createdAt
},
}: SubscriptionProps) => {
const [ cancelRevealed, revealCancel, hideCancel ] = useBooleanState();
const [ confirmationChecked, onConfirmationChanged ] = useCheckboxState();
const confirmCancellation = useCallback(
() => cancelSubscription(accessToken, subscriptionId),
[ accessToken, cancelSubscription, subscriptionId ]
);

return (
<div className="subscription">
<h3>{productName}</h3>
<p>{subscriptionId} - {productName} - {'' + new Date(createdAt)}</p>
<div>
{! cancelRevealed ? <>
<h3>Cancel subscription <button onClick={revealCancel}>Cancel...</button></h3>
</> : <>
<h3>Cancel subscription</h3>
<p>Cancelling means you&apos;ll no longer be able to access the product...</p>
<p>
<label>
<input type="checkbox" defaultChecked={confirmationChecked} onChange={onConfirmationChanged} />
Cancel my access and my saved information
</label>
</p>
<p>
<button onClick={hideCancel}>No, Stay Subscribed</button>
<button onClick={confirmCancellation} disabled={! confirmationChecked}>Yes, Cancel My Subscription</button>
</p>
</>}
</div>
</div>
);
};

export default Subscription;
@@ -0,0 +1,96 @@
import React, { useCallback, useEffect } from 'react';
import { connect, useDispatch } from 'react-redux';
import { selectorsFromState, actions } from '../../store';
import { Elements } from 'react-stripe-elements';
import { SubscriptionsFetchState, UpdatePaymentFetchState } from '../../store/types';
import LoadingSpinner from '../../components/LoadingSpinner';

import Subscription from './Subscription';
import PaymentUpdateForm from './PaymentUpdateForm';

type SubscriptionsProps = {
accessToken: string,
isLoading: boolean,
subscriptions: SubscriptionsFetchState,
cancelSubscription: Function,
resetUpdatePayment: Function,
updatePayment: Function,
updatePaymentStatus: UpdatePaymentFetchState,
};
export const Subscriptions = ({
accessToken,
isLoading,
subscriptions,
cancelSubscription,
updatePayment,
resetUpdatePayment,
updatePaymentStatus,
}: SubscriptionsProps) => {
const dispatch = useDispatch();

const resetCancelSubscription = useCallback(() => {
dispatch(actions.resetCancelSubscription());
}, [ dispatch ]);

// Reset subscription cancel status on initial render.
useEffect(() => {
resetCancelSubscription();
}, [ resetCancelSubscription ]);

// Fetch subscriptions on initial render or auth change.
useEffect(() => {
if (accessToken) {
dispatch(actions.fetchSubscriptions(accessToken));
}
}, [ dispatch, accessToken ]);

if (isLoading) {
return <LoadingSpinner />;
}

if (subscriptions.loading) {
return <div>(subscriptions loading...)</div>;
}

if (subscriptions.error) {
return <div>(subscriptions error! {'' + subscriptions.error})</div>;
}

if (subscriptions.result.length === 0) {
return (
<div>
<h2>Subscriptions</h2>
<div>No subscriptions yet.</div>
</div>
);
}

return (
<div>
<h2>Subscriptions</h2>

<Elements>
<PaymentUpdateForm {...{
accessToken,
updatePayment,
resetUpdatePayment,
updatePaymentStatus,
}} />
</Elements>

{subscriptions.result.map((subscription, idx) =>
<Subscription key={idx} {...{ accessToken, cancelSubscription, subscription }} />)}
</div>
);
};

export default connect(
// TODO: replace this with a useSelector hook
selectorsFromState('subscriptions', 'updatePaymentStatus'),
// TODO: replace this with a useDispatch hook
{
updatePayment: actions.updatePayment,
resetUpdatePayment: actions.resetUpdatePayment,
cancelSubscription: actions.cancelSubscriptionAndRefresh,
}
)(Subscriptions);
Oops, something went wrong.

0 comments on commit 5062bba

Please sign in to comment.
You can’t perform that action at this time.