x
This commit is contained in:
1 parent
d325cd8763
commit
c6bf2f70ac
45 files changed
+5195
-338
No files matched your search
@@ -0,0 +1,117 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user