Fix club membership form in dark mode
All checks were successful
Build Production Image / Build Production Image (push) Successful in 53s
All checks were successful
Build Production Image / Build Production Image (push) Successful in 53s
This commit is contained in:
parent
7190b306e7
commit
f62e434dff
@ -2,12 +2,12 @@
|
|||||||
import { type Metadata } from 'next'
|
import { type Metadata } from 'next'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
import { Container } from '@/components/Container'
|
import { Container } from '@/components/Container'
|
||||||
import { UserPlusIcon, GiftIcon, EnvelopeIcon, UserGroupIcon } from '@heroicons/react/24/solid'
|
import { UserPlusIcon, GiftIcon, EnvelopeIcon, UserGroupIcon } from '@heroicons/react/24/solid'
|
||||||
import interiorEmptyImage from '@/images/photos/interior-empty.jpg'
|
import interiorEmptyImage from '@/images/photos/interior-empty.jpg'
|
||||||
import { ClubPayment } from './payment';
|
|
||||||
|
|
||||||
function SocialLink({
|
function SocialLink({
|
||||||
className,
|
className,
|
||||||
@ -43,6 +43,15 @@ export const metadata: Metadata = {
|
|||||||
// TODO: Replace interiorEmptyImage with a photo from a potluck
|
// TODO: Replace interiorEmptyImage with a photo from a potluck
|
||||||
|
|
||||||
export default function Club() {
|
export default function Club() {
|
||||||
|
|
||||||
|
// Dynamic import since ClubPayment uses `document`
|
||||||
|
const ClubPayment = dynamic(
|
||||||
|
() => {
|
||||||
|
return import("./payment");
|
||||||
|
},
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="mt-16 sm:mt-32">
|
<Container className="mt-16 sm:mt-32">
|
||||||
<div className="grid grid-cols-1 gap-y-16 lg:grid-cols-2 lg:grid-rows-[auto_1fr] lg:gap-y-12">
|
<div className="grid grid-cols-1 gap-y-16 lg:grid-cols-2 lg:grid-rows-[auto_1fr] lg:gap-y-12">
|
||||||
|
@ -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}
|
||||||
|
@ -2,3 +2,15 @@
|
|||||||
@import 'tailwindcss/components';
|
@import 'tailwindcss/components';
|
||||||
@import './prism.css';
|
@import './prism.css';
|
||||||
@import 'tailwindcss/utilities';
|
@import 'tailwindcss/utilities';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Your default theme */
|
||||||
|
|
||||||
|
--stripe-background: #FFFFFF;
|
||||||
|
--stripe-foreground: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--stripe-background: #3f3f46;
|
||||||
|
--stripe-foreground: #E4E4E7;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user