Files
homaroma/apps/spa/src/components/DynamicNewsletter.tsx
T
2026-06-07 17:17:01 +07:00

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>
);
}