ISSUE 2: add Add Card Method modal UI, as well as form validation, and integration with addNewCard functionality
This commit is contained in:
@@ -1,22 +1,35 @@
|
|||||||
import { GlobalContext } from "@/globalContext";
|
import { GlobalContext } from "@/globalContext";
|
||||||
import MkdSDK from "@/utils/MkdSDK";
|
import MkdSDK from "@/utils/MkdSDK";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { CardCvcElement, CardExpiryElement, CardNumberElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
import {
|
||||||
|
CardCvcElement,
|
||||||
|
CardExpiryElement,
|
||||||
|
CardNumberElement,
|
||||||
|
useElements,
|
||||||
|
useStripe,
|
||||||
|
} from "@stripe/react-stripe-js";
|
||||||
import React, { Fragment, useState } from "react";
|
import React, { Fragment, useState } from "react";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { LoadingButton } from "../frontend";
|
import { LoadingButton } from "../frontend";
|
||||||
|
|
||||||
export default function AddCardMethodModal({ modalOpen, closeModal, onSuccess }) {
|
export default function AddCardMethodModal({
|
||||||
|
modalOpen,
|
||||||
|
closeModal,
|
||||||
|
onSuccess,
|
||||||
|
}) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const stripe = useStripe();
|
const stripe = useStripe();
|
||||||
const elements = useElements();
|
const elements = useElements();
|
||||||
const { dispatch: globalDispatch, state: globalState } = useContext(GlobalContext);
|
const { dispatch: globalDispatch, state: globalState } =
|
||||||
|
useContext(GlobalContext);
|
||||||
const sdk = new MkdSDK();
|
const sdk = new MkdSDK();
|
||||||
const [ctrl] = useState(new AbortController());
|
const [ctrl] = useState(new AbortController());
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
const addNewCard = async (e) => {
|
const addNewCard = async (e) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
setError("");
|
||||||
// create stripe token
|
// create stripe token
|
||||||
try {
|
try {
|
||||||
const cardNum = elements.getElement("cardNumber");
|
const cardNum = elements.getElement("cardNumber");
|
||||||
@@ -25,35 +38,123 @@ export default function AddCardMethodModal({ modalOpen, closeModal, onSuccess })
|
|||||||
globalDispatch({
|
globalDispatch({
|
||||||
type: "SHOW_ERROR",
|
type: "SHOW_ERROR",
|
||||||
payload: {
|
payload: {
|
||||||
message: r.error?.message ? r.error?.message : r?.trace?.raw?.message,
|
message: r.error?.message
|
||||||
|
? r.error?.message
|
||||||
|
: r?.trace?.raw?.message,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
setError(
|
||||||
|
r.error?.message ? r.error?.message : r?.trace?.raw?.message
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await sdk.createCustomerStripeCard({ sourceToken: r ? r.token.id : result.token.id }, ctrl.signal);
|
await sdk.createCustomerStripeCard(
|
||||||
|
{ sourceToken: r ? r.token.id : result.token.id },
|
||||||
|
ctrl.signal
|
||||||
|
);
|
||||||
closeModal();
|
closeModal();
|
||||||
onSuccess();
|
onSuccess();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name == "AbortError") {
|
if (error.name == "AbortError") {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(error)
|
console.log(error);
|
||||||
globalDispatch({
|
globalDispatch({
|
||||||
type: "SHOW_ERROR",
|
type: "SHOW_ERROR",
|
||||||
payload: {
|
payload: {
|
||||||
message: error?.message ? error?.message : "Declined",
|
message: error?.message ? error?.message : "Declined",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
setError(error?.message ? error?.message : "Declined");
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Transition appear show={modalOpen} as={Fragment}>
|
||||||
|
<Dialog as='div' className='relative z-50' onClose={closeModal}>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter='ease-out duration-300'
|
||||||
|
enterFrom='opacity-0'
|
||||||
|
enterTo='opacity-100'
|
||||||
|
leave='ease-in duration-200'
|
||||||
|
leaveFrom='opacity-100'
|
||||||
|
leaveTo='opacity-0'
|
||||||
|
>
|
||||||
|
<div className='fixed inset-0 bg-black bg-opacity-25' />
|
||||||
|
</Transition.Child>
|
||||||
|
<div className='fixed inset-0 overflow-y-auto'>
|
||||||
|
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter='ease-out duration-300'
|
||||||
|
enterFrom='opacity-0 scale-95'
|
||||||
|
enterTo='opacity-100 scale-100'
|
||||||
|
leave='ease-in duration-200'
|
||||||
|
leaveFrom='opacity-100 scale-100'
|
||||||
|
leaveTo='opacity-0 scale-95'
|
||||||
|
>
|
||||||
|
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all'>
|
||||||
|
<Dialog.Title
|
||||||
|
as='h3'
|
||||||
|
className='text-lg mb-4 font-medium leading-6 text-gray-900'
|
||||||
|
>
|
||||||
|
Add New Card
|
||||||
|
</Dialog.Title>
|
||||||
|
<form onSubmit={addNewCard} className='space-y-4'>
|
||||||
<div>
|
<div>
|
||||||
{/* Add Modal UI here to allow card to be created */}
|
<label className='mb-1 block text-sm font-medium text-gray-700'>
|
||||||
|
Card Number
|
||||||
|
</label>
|
||||||
|
<div className='rounded border px-2 py-2'>
|
||||||
|
<CardNumberElement options={{ showIcon: true }} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<div className='w-1/2'>
|
||||||
|
<label className='mb-1 block text-sm font-medium text-gray-700'>
|
||||||
|
Expiry
|
||||||
|
</label>
|
||||||
|
<div className='rounded border px-2 py-2'>
|
||||||
|
<CardExpiryElement />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='w-1/2'>
|
||||||
|
<label className='mb-1 block text-sm font-medium text-gray-700'>
|
||||||
|
CVC
|
||||||
|
</label>
|
||||||
|
<div className='rounded border px-2 py-2'>
|
||||||
|
<CardCvcElement />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{error && <div className='text-sm text-red-500'>{error}</div>}
|
||||||
|
<div className='mt-4 flex justify-end gap-2'>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='rounded bg-gray-200 px-4 py-2 text-gray-700 hover:bg-gray-300'
|
||||||
|
onClick={closeModal}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<LoadingButton
|
||||||
|
type='submit'
|
||||||
|
loading={loading}
|
||||||
|
className='rounded !bg-primary-dark px-4 py-2 text-white hover:!bg-primary'
|
||||||
|
>
|
||||||
|
Add Card
|
||||||
|
</LoadingButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Dialog.Panel>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user