Fix checkout page to hide login prompt when user is logged in
- Refactor checkout page into server component (page.tsx) + client component (client.tsx) - Server component checks getCurrentCustomer() and passes isLoggedIn prop - When logged in, show direct checkout button instead of login/guest options - When not logged in, show login/guest choice as before - Hides the 'Your cart will be saved...' message from logged-in users Fixes: Login/guest prompt appearing even when user is already authenticated Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
84454da5c9
commit
a2908e8ae1
@@ -0,0 +1,289 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useCart } from '@/lib/cartStore';
|
||||||
|
import { loginCustomer } from '@/app/account/actions';
|
||||||
|
import Price from '@/components/Price';
|
||||||
|
|
||||||
|
interface CheckoutPageClientProps {
|
||||||
|
isLoggedIn: boolean;
|
||||||
|
customer?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CheckoutPageClient({ isLoggedIn, customer }: CheckoutPageClientProps) {
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const [mode, setMode] = useState<'login' | 'guest' | null>(isLoggedIn ? 'logged-in' : null);
|
||||||
|
const [checkingOut, setCheckingOut] = useState(false);
|
||||||
|
const [checkoutError, setCheckoutError] = useState<string | null>(null);
|
||||||
|
const [guestData, setGuestData] = useState({
|
||||||
|
fullName: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
|
address: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = useCart((s) => s.items);
|
||||||
|
const total = useCart((s) => s.total());
|
||||||
|
|
||||||
|
useEffect(() => setMounted(true), []);
|
||||||
|
|
||||||
|
if (!mounted) return null;
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-md px-6 py-16 text-center">
|
||||||
|
<h1 className="font-display text-3xl">Checkout</h1>
|
||||||
|
<p className="mt-4 text-sm text-muted">Your bag is empty.</p>
|
||||||
|
<Link
|
||||||
|
href="/products"
|
||||||
|
className="mt-6 inline-block bg-clay px-6 py-3 text-sm font-medium text-paper hover:bg-clay-dark"
|
||||||
|
>
|
||||||
|
Continue shopping
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGuestCheckout = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!guestData.fullName || !guestData.email || !guestData.phone || !guestData.address) {
|
||||||
|
setCheckoutError('Please fill in all fields.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCheckingOut(true);
|
||||||
|
setCheckoutError(null);
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/checkout', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
items,
|
||||||
|
guest: {
|
||||||
|
name: guestData.fullName,
|
||||||
|
email: guestData.email,
|
||||||
|
phone: guestData.phone,
|
||||||
|
address: guestData.address,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok || !data.url) {
|
||||||
|
throw new Error(data.error ?? 'Something went wrong starting checkout.');
|
||||||
|
}
|
||||||
|
window.location.href = data.url;
|
||||||
|
} catch (err) {
|
||||||
|
setCheckoutError(err instanceof Error ? err.message : 'Something went wrong.');
|
||||||
|
setCheckingOut(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-2xl px-6 py-16">
|
||||||
|
<h1 className="font-display text-3xl">Checkout</h1>
|
||||||
|
|
||||||
|
<div className="mt-8 grid gap-8 md:grid-cols-2">
|
||||||
|
{/* Order summary */}
|
||||||
|
<div>
|
||||||
|
<h2 className="font-display text-lg">Order summary</h2>
|
||||||
|
<div className="mt-4 space-y-3 border-t border-line pt-4">
|
||||||
|
{items.map((item) => (
|
||||||
|
<div key={item.cartItemId} className="flex justify-between text-sm">
|
||||||
|
<div>
|
||||||
|
<p className="font-medium">{item.name}</p>
|
||||||
|
<p className="text-xs text-muted">Qty: {item.quantity}</p>
|
||||||
|
</div>
|
||||||
|
<Price cents={item.unitPrice * item.quantity} className="font-mono" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 border-t border-line pt-4">
|
||||||
|
<div className="flex justify-between font-display text-lg">
|
||||||
|
<span>Total</span>
|
||||||
|
<Price cents={total} className="font-mono" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Checkout options */}
|
||||||
|
<div>
|
||||||
|
{mode === 'logged-in' ? (
|
||||||
|
<div>
|
||||||
|
<h3 className="font-display text-lg">Ready to checkout</h3>
|
||||||
|
<p className="mt-2 text-sm text-muted">Logged in as {customer?.email}</p>
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
setCheckingOut(true);
|
||||||
|
setCheckoutError(null);
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/checkout', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ items, customerId: customer.id }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok || !data.url) {
|
||||||
|
throw new Error(data.error ?? 'Something went wrong starting checkout.');
|
||||||
|
}
|
||||||
|
window.location.href = data.url;
|
||||||
|
} catch (err) {
|
||||||
|
setCheckoutError(err instanceof Error ? err.message : 'Something went wrong.');
|
||||||
|
setCheckingOut(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={checkingOut}
|
||||||
|
className="mt-4 w-full bg-clay px-6 py-3 text-sm font-medium text-paper transition-colors hover:bg-clay-dark disabled:opacity-60"
|
||||||
|
>
|
||||||
|
{checkingOut ? 'Processing…' : 'Continue to payment'}
|
||||||
|
</button>
|
||||||
|
{checkoutError && (
|
||||||
|
<p className="mt-4 border border-splash-pink/40 bg-splash-pink/10 px-4 py-3 text-sm text-splash-pink">
|
||||||
|
{checkoutError}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : mode === null ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<p className="text-sm text-muted">How would you like to checkout?</p>
|
||||||
|
<button
|
||||||
|
onClick={() => setMode('login')}
|
||||||
|
className="w-full border-2 border-ink px-6 py-3 text-sm font-medium text-ink hover:border-clay hover:bg-clay hover:text-paper"
|
||||||
|
>
|
||||||
|
Log in to existing account
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setMode('guest')}
|
||||||
|
className="w-full bg-clay px-6 py-3 text-sm font-medium text-paper hover:bg-clay-dark"
|
||||||
|
>
|
||||||
|
Continue as guest
|
||||||
|
</button>
|
||||||
|
<p className="text-xs text-muted">
|
||||||
|
Don't have an account?{' '}
|
||||||
|
<Link href="/account/register" className="text-clay hover:text-clay-dark">
|
||||||
|
Create one now
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : mode === 'login' ? (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => setMode(null)}
|
||||||
|
className="text-sm text-clay hover:text-clay-dark mb-4"
|
||||||
|
>
|
||||||
|
← Back
|
||||||
|
</button>
|
||||||
|
<h3 className="font-display text-lg">Log in</h3>
|
||||||
|
<form action={loginCustomer} className="mt-4 space-y-4">
|
||||||
|
<input type="hidden" name="next" value="/checkout" />
|
||||||
|
<div>
|
||||||
|
<label htmlFor="email" className="tag-label mb-2 block">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
className="w-full border border-line px-3 py-2 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="password" className="tag-label mb-2 block">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
className="w-full border border-line px-3 py-2 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="w-full bg-clay px-6 py-3 text-sm font-medium text-paper hover:bg-clay-dark">
|
||||||
|
Log in
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => setMode(null)}
|
||||||
|
className="text-sm text-clay hover:text-clay-dark mb-4"
|
||||||
|
>
|
||||||
|
← Back
|
||||||
|
</button>
|
||||||
|
<h3 className="font-display text-lg">Guest checkout</h3>
|
||||||
|
{checkoutError && (
|
||||||
|
<p className="mt-4 border border-splash-pink/40 bg-splash-pink/10 px-4 py-3 text-sm text-splash-pink">
|
||||||
|
{checkoutError}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<form onSubmit={handleGuestCheckout} className="mt-4 space-y-4">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="fullName" className="tag-label mb-2 block">
|
||||||
|
Full name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="fullName"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={guestData.fullName}
|
||||||
|
onChange={(e) => setGuestData({ ...guestData, fullName: e.target.value })}
|
||||||
|
className="w-full border border-line px-3 py-2 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="email" className="tag-label mb-2 block">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
value={guestData.email}
|
||||||
|
onChange={(e) => setGuestData({ ...guestData, email: e.target.value })}
|
||||||
|
className="w-full border border-line px-3 py-2 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="phone" className="tag-label mb-2 block">
|
||||||
|
Phone number
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="phone"
|
||||||
|
type="tel"
|
||||||
|
required
|
||||||
|
value={guestData.phone}
|
||||||
|
onChange={(e) => setGuestData({ ...guestData, phone: e.target.value })}
|
||||||
|
className="w-full border border-line px-3 py-2 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="address" className="tag-label mb-2 block">
|
||||||
|
Address
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="address"
|
||||||
|
required
|
||||||
|
value={guestData.address}
|
||||||
|
onChange={(e) => setGuestData({ ...guestData, address: e.target.value })}
|
||||||
|
rows={3}
|
||||||
|
className="w-full border border-line px-3 py-2 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={checkingOut}
|
||||||
|
className="w-full bg-clay px-6 py-3 text-sm font-medium text-paper transition-colors hover:bg-clay-dark disabled:opacity-60"
|
||||||
|
>
|
||||||
|
{checkingOut ? 'Processing…' : 'Continue to payment'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+5
-246
@@ -1,249 +1,8 @@
|
|||||||
'use client';
|
import { getCurrentCustomer } from '@/lib/auth';
|
||||||
|
import CheckoutPageClient from './client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
export default async function CheckoutPage() {
|
||||||
import Link from 'next/link';
|
const customer = await getCurrentCustomer();
|
||||||
import { useCart } from '@/lib/cartStore';
|
|
||||||
import { loginCustomer } from '@/app/account/actions';
|
|
||||||
import Price from '@/components/Price';
|
|
||||||
|
|
||||||
export default function CheckoutPage() {
|
return <CheckoutPageClient isLoggedIn={!!customer} customer={customer} />;
|
||||||
const [mounted, setMounted] = useState(false);
|
|
||||||
const [mode, setMode] = useState<'login' | 'guest' | null>(null);
|
|
||||||
const [checkingOut, setCheckingOut] = useState(false);
|
|
||||||
const [checkoutError, setCheckoutError] = useState<string | null>(null);
|
|
||||||
const [guestData, setGuestData] = useState({
|
|
||||||
fullName: '',
|
|
||||||
email: '',
|
|
||||||
phone: '',
|
|
||||||
address: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const items = useCart((s) => s.items);
|
|
||||||
const total = useCart((s) => s.total());
|
|
||||||
|
|
||||||
useEffect(() => setMounted(true), []);
|
|
||||||
|
|
||||||
if (!mounted) return null;
|
|
||||||
|
|
||||||
if (items.length === 0) {
|
|
||||||
return (
|
|
||||||
<div className="mx-auto max-w-md px-6 py-16 text-center">
|
|
||||||
<h1 className="font-display text-3xl">Checkout</h1>
|
|
||||||
<p className="mt-4 text-sm text-muted">Your bag is empty.</p>
|
|
||||||
<Link
|
|
||||||
href="/products"
|
|
||||||
className="mt-6 inline-block bg-clay px-6 py-3 text-sm font-medium text-paper hover:bg-clay-dark"
|
|
||||||
>
|
|
||||||
Continue shopping
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleGuestCheckout = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!guestData.fullName || !guestData.email || !guestData.phone || !guestData.address) {
|
|
||||||
setCheckoutError('Please fill in all fields.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCheckingOut(true);
|
|
||||||
setCheckoutError(null);
|
|
||||||
try {
|
|
||||||
const res = await fetch('/api/checkout', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
items,
|
|
||||||
guest: {
|
|
||||||
name: guestData.fullName,
|
|
||||||
email: guestData.email,
|
|
||||||
phone: guestData.phone,
|
|
||||||
address: guestData.address,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
if (!res.ok || !data.url) {
|
|
||||||
throw new Error(data.error ?? 'Something went wrong starting checkout.');
|
|
||||||
}
|
|
||||||
window.location.href = data.url;
|
|
||||||
} catch (err) {
|
|
||||||
setCheckoutError(err instanceof Error ? err.message : 'Something went wrong.');
|
|
||||||
setCheckingOut(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mx-auto max-w-2xl px-6 py-16">
|
|
||||||
<h1 className="font-display text-3xl">Checkout</h1>
|
|
||||||
|
|
||||||
<div className="mt-8 grid gap-8 md:grid-cols-2">
|
|
||||||
{/* Order summary */}
|
|
||||||
<div>
|
|
||||||
<h2 className="font-display text-lg">Order summary</h2>
|
|
||||||
<div className="mt-4 space-y-3 border-t border-line pt-4">
|
|
||||||
{items.map((item) => (
|
|
||||||
<div key={item.cartItemId} className="flex justify-between text-sm">
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">{item.name}</p>
|
|
||||||
<p className="text-xs text-muted">Qty: {item.quantity}</p>
|
|
||||||
</div>
|
|
||||||
<Price cents={item.unitPrice * item.quantity} className="font-mono" />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 border-t border-line pt-4">
|
|
||||||
<div className="flex justify-between font-display text-lg">
|
|
||||||
<span>Total</span>
|
|
||||||
<Price cents={total} className="font-mono" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Checkout options */}
|
|
||||||
<div>
|
|
||||||
{mode === null ? (
|
|
||||||
<div className="space-y-3">
|
|
||||||
<p className="text-sm text-muted">How would you like to checkout?</p>
|
|
||||||
<button
|
|
||||||
onClick={() => setMode('login')}
|
|
||||||
className="w-full border-2 border-ink px-6 py-3 text-sm font-medium text-ink hover:border-clay hover:bg-clay hover:text-paper"
|
|
||||||
>
|
|
||||||
Log in to existing account
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setMode('guest')}
|
|
||||||
className="w-full bg-clay px-6 py-3 text-sm font-medium text-paper hover:bg-clay-dark"
|
|
||||||
>
|
|
||||||
Continue as guest
|
|
||||||
</button>
|
|
||||||
<p className="text-xs text-muted">
|
|
||||||
Don't have an account?{' '}
|
|
||||||
<Link href="/account/register" className="text-clay hover:text-clay-dark">
|
|
||||||
Create one now
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : mode === 'login' ? (
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={() => setMode(null)}
|
|
||||||
className="text-sm text-clay hover:text-clay-dark mb-4"
|
|
||||||
>
|
|
||||||
← Back
|
|
||||||
</button>
|
|
||||||
<h3 className="font-display text-lg">Log in</h3>
|
|
||||||
<form action={loginCustomer} className="mt-4 space-y-4">
|
|
||||||
<input type="hidden" name="next" value="/checkout" />
|
|
||||||
<div>
|
|
||||||
<label htmlFor="email" className="tag-label mb-2 block">
|
|
||||||
Email
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
type="email"
|
|
||||||
required
|
|
||||||
className="w-full border border-line px-3 py-2 text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="password" className="tag-label mb-2 block">
|
|
||||||
Password
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="password"
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
className="w-full border border-line px-3 py-2 text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button type="submit" className="w-full bg-clay px-6 py-3 text-sm font-medium text-paper hover:bg-clay-dark">
|
|
||||||
Log in
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={() => setMode(null)}
|
|
||||||
className="text-sm text-clay hover:text-clay-dark mb-4"
|
|
||||||
>
|
|
||||||
← Back
|
|
||||||
</button>
|
|
||||||
<h3 className="font-display text-lg">Guest checkout</h3>
|
|
||||||
{checkoutError && (
|
|
||||||
<p className="mt-4 border border-splash-pink/40 bg-splash-pink/10 px-4 py-3 text-sm text-splash-pink">
|
|
||||||
{checkoutError}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<form onSubmit={handleGuestCheckout} className="mt-4 space-y-4">
|
|
||||||
<div>
|
|
||||||
<label htmlFor="fullName" className="tag-label mb-2 block">
|
|
||||||
Full name
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="fullName"
|
|
||||||
type="text"
|
|
||||||
required
|
|
||||||
value={guestData.fullName}
|
|
||||||
onChange={(e) => setGuestData({ ...guestData, fullName: e.target.value })}
|
|
||||||
className="w-full border border-line px-3 py-2 text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="email" className="tag-label mb-2 block">
|
|
||||||
Email
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
required
|
|
||||||
value={guestData.email}
|
|
||||||
onChange={(e) => setGuestData({ ...guestData, email: e.target.value })}
|
|
||||||
className="w-full border border-line px-3 py-2 text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="phone" className="tag-label mb-2 block">
|
|
||||||
Phone number
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="phone"
|
|
||||||
type="tel"
|
|
||||||
required
|
|
||||||
value={guestData.phone}
|
|
||||||
onChange={(e) => setGuestData({ ...guestData, phone: e.target.value })}
|
|
||||||
className="w-full border border-line px-3 py-2 text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="address" className="tag-label mb-2 block">
|
|
||||||
Address
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="address"
|
|
||||||
required
|
|
||||||
value={guestData.address}
|
|
||||||
onChange={(e) => setGuestData({ ...guestData, address: e.target.value })}
|
|
||||||
rows={3}
|
|
||||||
className="w-full border border-line px-3 py-2 text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={checkingOut}
|
|
||||||
className="w-full bg-clay px-6 py-3 text-sm font-medium text-paper transition-colors hover:bg-clay-dark disabled:opacity-60"
|
|
||||||
>
|
|
||||||
{checkingOut ? 'Processing…' : 'Continue to payment'}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user