import {useEffect, useState, type FormEvent} from 'react';
import {useFetcher} from 'react-router';
import {motion} from 'motion/react';
import {ArrowRight, Sparkles} from 'lucide-react';
import {PAGE_TITLE_TYPOGRAPHY_CLASS} from '~/lib/pageTitle';
import type {NewsletterSectionCms} from '~/lib/newsletter';
import {MOCK_NEWSLETTER_SECTION_CMS} from '~/lib/newsletter';
/**
* GROQ: `home` singleton → Glow Club newsletter.
* Use with `context.sanity.loadQuery(NEWSLETTER_SECTION_GROQ)` or `loadNewsletterFromSanity`.
*
* Reference has no ``; decorative orbs are CSS-only (no Shopify `Image` needed).
*/
export const NEWSLETTER_SECTION_GROQ = `*[_type == "home" && _id == "home"][0].newsletterSection{
eyebrow,
headlinePlain,
headlineItalic,
body,
emailPlaceholder,
submitButtonLabel,
successTitle,
successSubtitle,
perks
}` as const;
export {MOCK_NEWSLETTER_SECTION_CMS};
export type {NewsletterSectionCms};
/**
* Design tokens match `React/src/app/components/Newsletter.tsx` (pixel-aligned Tailwind).
*/
const BURGUNDY = '#5d0b24';
const CREAM = '#faf8f3';
const CREAM_SOFT = '#f8f4ef';
const GOLD = '#b39763';
const CHARCOAL = '#1a1a1a';
const NEWSLETTER_SUBSCRIBE_ACTION = '/api/newsletter-subscribe';
type SubscribeResponse =
| {ok: true; alreadyExists?: boolean}
| {ok: false; error: string};
function subscribeErrorMessage(code: string): string {
switch (code) {
case 'invalid_email':
return 'Please enter a valid email address.';
case 'not_configured':
return 'Newsletter signup is temporarily unavailable.';
case 'service_unavailable':
case 'shopify_error':
default:
return 'Something went wrong. Please try again in a moment.';
}
}
export type NewsletterProps = {
cms: NewsletterSectionCms;
};
export function Newsletter({cms}: NewsletterProps) {
const fetcher = useFetcher();
const [email, setEmail] = useState('');
const [submitted, setSubmitted] = useState(false);
useEffect(() => {
if (fetcher.state !== 'idle' || !fetcher.data) return;
if (fetcher.data.ok) {
setSubmitted(true);
}
}, [fetcher.state, fetcher.data]);
function handleSubmit(e: FormEvent) {
e.preventDefault();
const trimmed = email.trim();
if (!trimmed) return;
fetcher.submit({email: trimmed}, {method: 'POST', action: NEWSLETTER_SUBSCRIBE_ACTION});
}
const submitError =
fetcher.state === 'idle' &&
fetcher.data &&
!fetcher.data.ok &&
'error' in fetcher.data ?
subscribeErrorMessage(fetcher.data.error)
: null;
const isSubmitting = fetcher.state !== 'idle';
return (
{cms.eyebrow}
{/*
Headline + body + form share one grid column. The form uses min-width in vw so the column
can grow up to 649px even when the headline is narrower (100% alone would stay headline-wide).
*/}