Files
craft2prints/src/app/contact/page.tsx
T
AndymickandClaude Sonnet 5 396347b982 Move uploaded images to disk storage, fix Next 16 async params/searchParams
Homepage was rendering 71MB of HTML because every uploaded image (gallery,
products, proofs, custom requests, design previews, invoices, receipts) was
stored as base64 in Postgres and double-embedded via RSC hydration payloads.
Adds src/lib/storage.ts (sharp-based compression, disk storage under
public/uploads/) and a new /api/design-uploads endpoint for images added
inside the design tool, migrates existing rows, and drops the now-unused
base64 columns and the dead ProductImage table.

Also fixes a systemic bug from the Next.js 16 upgrade where dynamic pages
across the app still destructured params/searchParams synchronously instead
of awaiting them as Promises, which was silently breaking filters/params and
crashing /products/[slug] outright.

Updates README.md and SETUP_AND_BUILD.md to reflect Postgres + Next.js 16 and
document the new image storage architecture and backup requirements.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 20:56:40 +01:00

92 lines
2.9 KiB
TypeScript

import { getCurrentCustomer } from '@/lib/auth';
import { sendContactMessage } from './actions';
import Turnstile from '@/components/Turnstile';
const ERROR_MESSAGES: Record<string, string> = {
missing: 'Name, email, and a message are required.',
captcha: 'CAPTCHA verification failed — please try again.',
};
export default async function ContactPage({
searchParams,
}: {
searchParams: Promise<{ sent?: string; error?: string }>;
}) {
const params = await searchParams;
const customer = await getCurrentCustomer();
const error = params.error ? (ERROR_MESSAGES[params.error] ?? 'Something went wrong.') : null;
return (
<div className="mx-auto max-w-2xl px-6 py-12">
<p className="tag-label text-splash-pink">Contact</p>
<h1 className="mt-2 font-display text-3xl">Get in touch</h1>
<p className="mt-2 text-sm text-muted">
Question about an order, a template, or anything else? Send us a message and we&apos;ll get
back to you.
</p>
{params.sent === '1' ? (
<div className="mt-10 border border-splash-blue bg-splash-blue/5 p-6 text-sm">
Thanks we&apos;ve got your message and will be in touch soon.
</div>
) : (
<form action={sendContactMessage} className="mt-10 space-y-6">
{error && (
<p className="border border-splash-pink/40 bg-splash-pink/10 px-4 py-3 text-sm text-splash-pink">
{error}
</p>
)}
<div className="grid grid-cols-2 gap-4">
<div>
<label htmlFor="name" className="tag-label mb-2 block">
Your name
</label>
<input
id="name"
name="name"
required
defaultValue={customer?.name ?? ''}
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"
name="email"
type="email"
required
defaultValue={customer?.email ?? ''}
className="w-full border border-line px-3 py-2 text-sm"
/>
</div>
</div>
<div>
<label htmlFor="message" className="tag-label mb-2 block">
Message
</label>
<textarea
id="message"
name="message"
required
rows={5}
className="w-full border border-line px-3 py-2 text-sm"
placeholder="How can we help?"
/>
</div>
<Turnstile />
<button type="submit" className="bg-clay px-6 py-3 text-sm font-medium text-paper hover:bg-clay-dark">
Send message
</button>
</form>
)}
</div>
);
}