feat(client): refine payment handling and improve type safety in components
This commit is contained in:
@@ -7,7 +7,7 @@ import PaymentForm from './PaymentForm';
|
|||||||
import PaymentConfirmationCard from './PaymentConfirmationCard';
|
import PaymentConfirmationCard from './PaymentConfirmationCard';
|
||||||
import PreviousPaymentsList from './PreviousPaymentsList';
|
import PreviousPaymentsList from './PreviousPaymentsList';
|
||||||
|
|
||||||
const stripe = loadStripe(import.meta.env.VITE_STRIPE_API_KEY);
|
const stripe = loadStripe(import.meta.env.VITE_STRIPE_API_KEY as string);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const queryParams = new URLSearchParams(window.location.search);
|
const queryParams = new URLSearchParams(window.location.search);
|
||||||
@@ -44,7 +44,7 @@ function App() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json() as { clientSecret: string };
|
||||||
setSecret(data.clientSecret);
|
setSecret(data.clientSecret);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default function CheckoutForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={e => void handleSubmit(e)}>
|
||||||
<PaymentElement
|
<PaymentElement
|
||||||
id='payment-element'
|
id='payment-element'
|
||||||
options={{ layout: 'accordion' }}
|
options={{ layout: 'accordion' }}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default function PaymentConfirmationCard({ clientSecret }: PaymentConfirm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPaymentIntent();
|
void fetchPaymentIntent();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useRef } from 'react';
|
import { useState, useRef } from 'react';
|
||||||
|
|
||||||
type DonationFormProps = {
|
type DonationFormProps = {
|
||||||
onValidSubmit: (data: { name: string; amount: number; message?: string; email?: string }) => void;
|
onValidSubmit: (data: { name: string; amount: number; message?: string; email?: string }) => Promise<void> | void;
|
||||||
isSubmitting?: boolean;
|
isSubmitting?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,12 +98,12 @@ export default function PaymentForm({ onValidSubmit, isSubmitting }: DonationFor
|
|||||||
throw new Error('amount after validation should never be an empty string');
|
throw new Error('amount after validation should never be an empty string');
|
||||||
}
|
}
|
||||||
|
|
||||||
onValidSubmit({ name, amount, message, email });
|
await onValidSubmit({ name, amount, message, email });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={e => void handleSubmit(e)}
|
||||||
noValidate
|
noValidate
|
||||||
>
|
>
|
||||||
<div className='group'>
|
<div className='group'>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default function PreviousPaymentsList() {
|
|||||||
setPaymentData({ status: 'error', message: 'Failed to fetch payments' });
|
setPaymentData({ status: 'error', message: 'Failed to fetch payments' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json() as PaymentPage;
|
||||||
|
|
||||||
setPaymentData({ status: 'success', pages: [data] });
|
setPaymentData({ status: 'success', pages: [data] });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -58,7 +58,7 @@ export default function PreviousPaymentsList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPayments();
|
void fetchPayments();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
@@ -84,7 +84,7 @@ export default function PreviousPaymentsList() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json() as PaymentPage;
|
||||||
|
|
||||||
setPaymentData({
|
setPaymentData({
|
||||||
status: 'success',
|
status: 'success',
|
||||||
@@ -117,7 +117,7 @@ export default function PreviousPaymentsList() {
|
|||||||
<button
|
<button
|
||||||
className='load-more-button'
|
className='load-more-button'
|
||||||
type='button'
|
type='button'
|
||||||
onClick={handleLoadMoreButtonClick}
|
onClick={() => void handleLoadMoreButtonClick()}
|
||||||
disabled={currentPageNumber >= totalNumberOfPages || isLoadingMore}
|
disabled={currentPageNumber >= totalNumberOfPages || isLoadingMore}
|
||||||
>
|
>
|
||||||
Load More
|
Load More
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'CaskaydiaCove NFM';
|
font-family: 'CaskaydiaCove NFM';
|
||||||
src: url('/CaskaydiaCoveNFM-Regular.eot');
|
src: url('/CaskaydiaCoveNFM-Regular.eot');
|
||||||
src: url('/CaskaydiaCoveNFM-Regular.eot?#iefix') format('embedded-opentype'),
|
src:
|
||||||
|
url('/CaskaydiaCoveNFM-Regular.eot?#iefix') format('embedded-opentype'),
|
||||||
url('/CaskaydiaCoveNFM-Regular.woff2') format('woff2'),
|
url('/CaskaydiaCoveNFM-Regular.woff2') format('woff2'),
|
||||||
url('/CaskaydiaCoveNFM-Regular.woff') format('woff'),
|
url('/CaskaydiaCoveNFM-Regular.woff') format('woff'),
|
||||||
url('/CaskaydiaCoveNFM-Regular.svg#CaskaydiaCoveNFM-Regular') format('svg');
|
url('/CaskaydiaCoveNFM-Regular.svg#CaskaydiaCoveNFM-Regular') format('svg');
|
||||||
@@ -56,7 +57,7 @@ table {
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--bg-color: #323232;
|
--bg-color: #323232;
|
||||||
--text-color: #E4E4E4;
|
--text-color: #e4e4e4;
|
||||||
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-family: 'CaskaydiaCove NFM', monospace;
|
font-family: 'CaskaydiaCove NFM', monospace;
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ describe('form', () => {
|
|||||||
user = userEvent.setup();
|
user = userEvent.setup();
|
||||||
vi.stubGlobal('fetch', mockFetch);
|
vi.stubGlobal('fetch', mockFetch);
|
||||||
|
|
||||||
mockFetch.mockImplementation(async (url: URL) => {
|
mockFetch.mockImplementation((url: URL) => {
|
||||||
if (url.toString().endsWith('/payments/create-intent')) {
|
if (url.toString().endsWith('/payments/create-intent')) {
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
json: async () => ({
|
json: () => ({
|
||||||
clientSecret: 'test_client_secret',
|
clientSecret: 'test_client_secret',
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -44,7 +44,7 @@ describe('form', () => {
|
|||||||
if (url.toString().endsWith('/payments')) {
|
if (url.toString().endsWith('/payments')) {
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
json: async () => ({
|
json: () => ({
|
||||||
payments: [],
|
payments: [],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@@ -118,7 +118,6 @@ describe('form', () => {
|
|||||||
|
|
||||||
await user.type(amountInput, '10');
|
await user.type(amountInput, '10');
|
||||||
await user.click(submitButton);
|
await user.click(submitButton);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it should validate email if provided', async () => {
|
test('it should validate email if provided', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user