118 lines
4.5 KiB
TypeScript
118 lines
4.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { API_BASE, type NewsletterSectionPayload } from '../types';
|
|
|
|
interface DynamicNewsletterProps {
|
|
payload: NewsletterSectionPayload;
|
|
}
|
|
|
|
export default function DynamicNewsletter({ payload }: DynamicNewsletterProps) {
|
|
const {
|
|
title,
|
|
description,
|
|
buttonText = 'Subscribe',
|
|
placeholderText = 'Enter your email address',
|
|
showPrivacyCheckbox = true,
|
|
privacyText = 'I agree to the privacy policy.'
|
|
} = payload;
|
|
|
|
const [email, setEmail] = useState('');
|
|
const [agreed, setAgreed] = useState(!showPrivacyCheckbox);
|
|
const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
|
|
const [errorMessage, setErrorMessage] = useState('');
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (showPrivacyCheckbox && !agreed) {
|
|
alert('Please agree to the privacy policy.');
|
|
return;
|
|
}
|
|
|
|
setStatus('submitting');
|
|
try {
|
|
const res = await fetch(`${API_BASE}/pages/newsletter`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email })
|
|
});
|
|
|
|
if (res.ok) {
|
|
setStatus('success');
|
|
setEmail('');
|
|
} else {
|
|
const errData = await res.json();
|
|
setErrorMessage(errData.error || 'Failed to subscribe.');
|
|
setStatus('error');
|
|
}
|
|
} catch (err) {
|
|
console.error('Newsletter submission error:', err);
|
|
setErrorMessage('Network error. Please try again.');
|
|
setStatus('error');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section className="py-20 px-6 bg-spa-forest text-white relative overflow-hidden">
|
|
{/* Background decorations for luxury aesthetic */}
|
|
<div className="absolute inset-0 opacity-10 pointer-events-none">
|
|
<div className="absolute top-10 left-10 w-96 h-96 rounded-full bg-spa-gold filter blur-3xl"></div>
|
|
<div className="absolute bottom-10 right-10 w-96 h-96 rounded-full bg-spa-sage filter blur-3xl"></div>
|
|
</div>
|
|
|
|
<div className="max-w-3xl mx-auto text-center space-y-8 relative z-10">
|
|
<div className="space-y-3">
|
|
<h2 className="text-3xl md:text-5xl font-serif font-light tracking-wide text-spa-gold">
|
|
{title || "Begin Your Wellness Journey"}
|
|
</h2>
|
|
<p className="text-spa-light/80 text-sm md:text-base font-light max-w-xl mx-auto">
|
|
{description}
|
|
</p>
|
|
</div>
|
|
|
|
{status === 'success' ? (
|
|
<div className="p-6 bg-white/5 border border-spa-gold/30 rounded-2xl animate-pop-in space-y-2">
|
|
<h4 className="text-spa-gold font-semibold text-lg">Thank You for Subscribing</h4>
|
|
<p className="text-xs text-spa-light/75">You have successfully joined our newsletter list. We will send you exclusive sensory privileges soon.</p>
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="space-y-4 max-w-lg mx-auto">
|
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
<input
|
|
type="email"
|
|
required
|
|
disabled={status === 'submitting'}
|
|
placeholder={placeholderText}
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
className="flex-1 px-6 py-4 bg-white/5 border border-white/10 rounded-full text-white placeholder-white/40 focus:outline-none focus:border-spa-gold text-sm transition-all"
|
|
/>
|
|
<button
|
|
type="submit"
|
|
disabled={status === 'submitting'}
|
|
className="px-8 py-4 bg-white text-spa-forest hover:bg-spa-sage rounded-full font-bold text-sm tracking-widest uppercase transition-all disabled:opacity-50 cursor-pointer"
|
|
>
|
|
{status === 'submitting' ? 'Submitting...' : buttonText}
|
|
</button>
|
|
</div>
|
|
|
|
{status === 'error' && (
|
|
<p className="text-rose-400 text-xs text-left px-4">{errorMessage}</p>
|
|
)}
|
|
|
|
{showPrivacyCheckbox && (
|
|
<label className="flex items-start gap-3 text-[11px] text-spa-light/60 text-left px-4 cursor-pointer select-none">
|
|
<input
|
|
type="checkbox"
|
|
checked={agreed}
|
|
onChange={(e) => setAgreed(e.target.checked)}
|
|
className="mt-0.5 accent-spa-gold"
|
|
/>
|
|
<span>{privacyText}</span>
|
|
</label>
|
|
)}
|
|
</form>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|