|
|
@ -1,6 +1,6 @@
|
|
|
|
"use client"
|
|
|
|
"use client"
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useEffect, useState, FormEvent } from 'react';
|
|
|
|
import React, { useEffect, useState, FormEvent, useCallback } from 'react';
|
|
|
|
import { AddressElement, Elements } from '@stripe/react-stripe-js';
|
|
|
|
import { AddressElement, Elements } from '@stripe/react-stripe-js';
|
|
|
|
import { Appearance, StripeAddressElementOptions, StripeElementsOptions, loadStripe } from '@stripe/stripe-js';
|
|
|
|
import { Appearance, StripeAddressElementOptions, StripeElementsOptions, loadStripe } from '@stripe/stripe-js';
|
|
|
|
import {
|
|
|
|
import {
|
|
|
@ -166,7 +166,7 @@ function CheckoutForm({
|
|
|
|
|
|
|
|
|
|
|
|
{/* Membership Type */}
|
|
|
|
{/* Membership Type */}
|
|
|
|
<RadioGroup value={selectedMembershipLevel} onChange={setSelectedMembershipLevel} className="space-y-3">
|
|
|
|
<RadioGroup value={selectedMembershipLevel} onChange={setSelectedMembershipLevel} className="space-y-3">
|
|
|
|
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900">
|
|
|
|
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
|
|
|
Select a membership type
|
|
|
|
Select a membership type
|
|
|
|
</RadioGroup.Label>
|
|
|
|
</RadioGroup.Label>
|
|
|
|
|
|
|
|
|
|
|
@ -177,8 +177,8 @@ function CheckoutForm({
|
|
|
|
value={membership}
|
|
|
|
value={membership}
|
|
|
|
className={({ active }) =>
|
|
|
|
className={({ active }) =>
|
|
|
|
classNames(
|
|
|
|
classNames(
|
|
|
|
active ? 'border-indigo-600 ring-2 ring-indigo-600' : 'border-gray-300',
|
|
|
|
active ? 'border-indigo-600 ring-2 ring-indigo-600' : 'border-gray-200 dark:border-gray-500',
|
|
|
|
'relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none'
|
|
|
|
'relative flex cursor-pointer rounded-lg border bg-white hover:bg-gray-50 hover:dark:bg-zinc-600 dark:bg-zinc-700 p-4 shadow-sm focus:outline-none'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
>
|
|
|
|
>
|
|
|
@ -186,7 +186,7 @@ function CheckoutForm({
|
|
|
|
<>
|
|
|
|
<>
|
|
|
|
<span className="flex flex-1 items-center justify-between ">
|
|
|
|
<span className="flex flex-1 items-center justify-between ">
|
|
|
|
<span className="flex flex-col">
|
|
|
|
<span className="flex flex-col">
|
|
|
|
<RadioGroup.Label as="span" className="block text-sm font-medium text-gray-900">
|
|
|
|
<RadioGroup.Label as="span" className="block text-sm font-medium text-gray-900 dark:text-white">
|
|
|
|
{membership.title}
|
|
|
|
{membership.title}
|
|
|
|
</RadioGroup.Label>
|
|
|
|
</RadioGroup.Label>
|
|
|
|
<RadioGroup.Description as="span" className="mt-1 flex items-center text-sm text-gray-500">
|
|
|
|
<RadioGroup.Description as="span" className="mt-1 flex items-center text-sm text-gray-500">
|
|
|
@ -194,7 +194,7 @@ function CheckoutForm({
|
|
|
|
</RadioGroup.Description>
|
|
|
|
</RadioGroup.Description>
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
<RadioGroup.Description as="span" className="ml-8 text-sm font-medium">
|
|
|
|
<RadioGroup.Description as="span" className="ml-8 text-sm font-medium">
|
|
|
|
<span className="text-gray-900">${membership.price}</span>
|
|
|
|
<span className="text-gray-900 dark:text-white">${membership.price}</span>
|
|
|
|
<span className="text-gray-500">/yr</span>
|
|
|
|
<span className="text-gray-500">/yr</span>
|
|
|
|
</RadioGroup.Description>
|
|
|
|
</RadioGroup.Description>
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
@ -215,7 +215,7 @@ function CheckoutForm({
|
|
|
|
|
|
|
|
|
|
|
|
{/* Additional donation */}
|
|
|
|
{/* Additional donation */}
|
|
|
|
<RadioGroup value={selectedAdditionalDonation} onChange={setSelectedAdditionalDonation} className="space-y-3">
|
|
|
|
<RadioGroup value={selectedAdditionalDonation} onChange={setSelectedAdditionalDonation} className="space-y-3">
|
|
|
|
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900">
|
|
|
|
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
|
|
|
Additional donation
|
|
|
|
Additional donation
|
|
|
|
</RadioGroup.Label>
|
|
|
|
</RadioGroup.Label>
|
|
|
|
<div className="grid grid-cols-3 gap-3 sm:grid-cols-5">
|
|
|
|
<div className="grid grid-cols-3 gap-3 sm:grid-cols-5">
|
|
|
@ -228,9 +228,9 @@ function CheckoutForm({
|
|
|
|
'cursor-pointer focus:outline-none',
|
|
|
|
'cursor-pointer focus:outline-none',
|
|
|
|
option === -1 ? 'col-span-2' : '',
|
|
|
|
option === -1 ? 'col-span-2' : '',
|
|
|
|
checked
|
|
|
|
checked
|
|
|
|
? 'ring-2 ring-indigo-600 ring-offset-2'
|
|
|
|
? 'ring-2 ring-indigo-600'
|
|
|
|
: 'ring-1 ring-inset ring-gray-300 bg-white text-gray-900 hover:bg-gray-50',
|
|
|
|
: 'ring-1 ring-inset ring-gray-200 dark:ring-gray-500 text-gray-900 dark:text-white hover:bg-gray-50 hover:dark:bg-zinc-600',
|
|
|
|
'flex items-center justify-center rounded-md py-3 px-3 text-sm font-semibold sm:flex-1'
|
|
|
|
'flex items-center justify-center rounded-md py-3 px-3 text-sm font-semibold sm:flex-1 bg-white dark:bg-zinc-700'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
>
|
|
|
|
>
|
|
|
@ -250,7 +250,7 @@ function CheckoutForm({
|
|
|
|
min="0"
|
|
|
|
min="0"
|
|
|
|
step="1"
|
|
|
|
step="1"
|
|
|
|
onChange={(e) => setCustomAmount(e.target.value)}
|
|
|
|
onChange={(e) => setCustomAmount(e.target.value)}
|
|
|
|
className="block w-full rounded-md border-0 py-1.5 pl-7 pr-12 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-500 sm:text-sm sm:leading-6"
|
|
|
|
className="block w-full rounded-md border-0 py-1.5 pl-7 pr-12 text-gray-900 dark:text-white dark:bg-zinc-700 ring-1 ring-inset ring-gray-200 dark:ring-gray-500 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-500 sm:text-sm sm:leading-6"
|
|
|
|
placeholder="Custom"
|
|
|
|
placeholder="Custom"
|
|
|
|
aria-describedby="price-currency"
|
|
|
|
aria-describedby="price-currency"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
@ -274,12 +274,12 @@ function CheckoutForm({
|
|
|
|
</RadioGroup>
|
|
|
|
</RadioGroup>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
<div className="space-y-3">
|
|
|
|
<h2 className="text-base font-semibold leading-6 text-gray-900">
|
|
|
|
<h2 className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
|
|
|
About you
|
|
|
|
About you
|
|
|
|
</h2>
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="rounded-md mb-3 px-3 pb-1.5 pt-2.5 shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-indigo-600">
|
|
|
|
<div className="rounded-md mb-3 px-3 pb-1.5 pt-2.5 shadow-sm ring-1 ring-inset ring-gray-200 dark:ring-gray-500 focus-within:ring-2 focus-within:ring-indigo-600 dark:bg-zinc-700 dark:text-white">
|
|
|
|
<label htmlFor="name" className="block text-xs font-medium text-gray-900">
|
|
|
|
<label htmlFor="name" className="block text-xs font-medium text-gray-900 dark:text-zinc-400">
|
|
|
|
Email
|
|
|
|
Email
|
|
|
|
</label>
|
|
|
|
</label>
|
|
|
|
<input
|
|
|
|
<input
|
|
|
@ -288,7 +288,7 @@ function CheckoutForm({
|
|
|
|
id="email"
|
|
|
|
id="email"
|
|
|
|
value={email}
|
|
|
|
value={email}
|
|
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
|
|
className="block w-full border-0 p-0 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
|
|
|
|
className="block w-full border-0 p-0 dark:bg-zinc-700 text-gray-900 dark:text-white placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
|
|
|
|
placeholder="you@example.com"
|
|
|
|
placeholder="you@example.com"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
@ -296,7 +296,7 @@ function CheckoutForm({
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
<div className="space-y-3">
|
|
|
|
<h2 className="text-base font-semibold leading-6 text-gray-900">
|
|
|
|
<h2 className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
|
|
|
Payment
|
|
|
|
Payment
|
|
|
|
</h2>
|
|
|
|
</h2>
|
|
|
|
<div className="mt-1 text-sm text-gray-500">
|
|
|
|
<div className="mt-1 text-sm text-gray-500">
|
|
|
@ -329,9 +329,37 @@ function CheckoutForm({
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function ClubPayment() {
|
|
|
|
const DEFAULT_OPTIONS = {
|
|
|
|
|
|
|
|
config: { attributes: true, childList: true, subtree: true },
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function useMutationObservable(targetEl: Node, cb: MutationCallback, options = DEFAULT_OPTIONS) {
|
|
|
|
|
|
|
|
const [observer, setObserver] = useState<MutationObserver | null>(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
const obs = new MutationObserver(cb);
|
|
|
|
|
|
|
|
setObserver(obs);
|
|
|
|
|
|
|
|
}, [cb, options, setObserver]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
if (!observer) return;
|
|
|
|
|
|
|
|
const { config } = options;
|
|
|
|
|
|
|
|
observer.observe(targetEl, config);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
|
|
if (observer) {
|
|
|
|
|
|
|
|
observer.disconnect();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}, [observer, targetEl, options]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default function ClubPayment() {
|
|
|
|
const [clientSecret, setClientSecret] = useState('');
|
|
|
|
const [clientSecret, setClientSecret] = useState('');
|
|
|
|
const [paymentIntent, setPaymentIntent] = useState('');
|
|
|
|
const [paymentIntent, setPaymentIntent] = useState('');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const htmlEl = document.getElementsByTagName('html')[0];
|
|
|
|
|
|
|
|
const darkTheme = htmlEl.classList.contains("dark");
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
// Create PaymentIntent as soon as the page loads using our local API
|
|
|
|
// Create PaymentIntent as soon as the page loads using our local API
|
|
|
|
fetch('api/stripe_intent', {
|
|
|
|
fetch('api/stripe_intent', {
|
|
|
@ -349,15 +377,36 @@ export function ClubPayment() {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const styles = getComputedStyle(htmlEl);
|
|
|
|
|
|
|
|
|
|
|
|
const appearance: Appearance = {
|
|
|
|
const appearance: Appearance = {
|
|
|
|
theme: 'stripe',
|
|
|
|
theme: 'stripe',
|
|
|
|
|
|
|
|
variables: {
|
|
|
|
|
|
|
|
colorBackground: styles.getPropertyValue('--stripe-background'),
|
|
|
|
|
|
|
|
colorText: styles.getPropertyValue('--stripe-foreground'),
|
|
|
|
|
|
|
|
},
|
|
|
|
labels: 'floating',
|
|
|
|
labels: 'floating',
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cb = useCallback(
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
const updatedHtmlEl = document.getElementsByTagName('html')[0];
|
|
|
|
|
|
|
|
const updatedDarkTheme = updatedHtmlEl.classList.contains("dark");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (updatedDarkTheme !== darkTheme) {
|
|
|
|
|
|
|
|
location.reload();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
[darkTheme]
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useMutationObservable(htmlEl, cb);
|
|
|
|
|
|
|
|
|
|
|
|
const options: StripeElementsOptions = {
|
|
|
|
const options: StripeElementsOptions = {
|
|
|
|
clientSecret,
|
|
|
|
clientSecret,
|
|
|
|
appearance
|
|
|
|
appearance,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return <>
|
|
|
|
return <>
|
|
|
|
{clientSecret && (
|
|
|
|
{clientSecret && (
|
|
|
|
<Elements options={options}
|
|
|
|
<Elements options={options}
|
|
|
|