You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
14 KiB
369 lines
14 KiB
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import Navigation from '@/components/Navigation';
|
|
import Footer from '@/components/Footer';
|
|
|
|
export default function FeedbackPage() {
|
|
const [formData, setFormData] = useState({
|
|
name: '',
|
|
email: '',
|
|
message: '',
|
|
});
|
|
|
|
const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
|
|
const [errorMessage, setErrorMessage] = useState('');
|
|
|
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
setStatus('submitting');
|
|
setErrorMessage('');
|
|
|
|
const form = e.currentTarget;
|
|
const formDataObj = new FormData(form);
|
|
|
|
try {
|
|
const response = await fetch(form.action, {
|
|
method: form.method,
|
|
body: formDataObj,
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
},
|
|
});
|
|
|
|
if (response.ok) {
|
|
setStatus('success');
|
|
setFormData({
|
|
name: '',
|
|
email: '',
|
|
message: '',
|
|
});
|
|
form.reset();
|
|
|
|
// Reset success message after 6 seconds
|
|
setTimeout(() => {
|
|
setStatus('idle');
|
|
}, 6000);
|
|
} else {
|
|
const responseData = await response.json();
|
|
if (responseData.errors) {
|
|
setErrorMessage(responseData.errors.map((error: { message: string }) => error.message).join(', '));
|
|
} else {
|
|
setErrorMessage('Oops! There was a problem submitting your form. Please try again.');
|
|
}
|
|
setStatus('error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Form submission error:', error);
|
|
setErrorMessage('Oops! There was a problem submitting your form. Please check your connection and try again.');
|
|
setStatus('error');
|
|
}
|
|
};
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
setFormData({
|
|
...formData,
|
|
[e.target.name]: e.target.value,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<main className="min-h-screen">
|
|
<Navigation />
|
|
<section className="py-20 md:py-32" style={{ backgroundColor: '#FBF7F4' }}>
|
|
<div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.8, ease: [0.25, 0.1, 0.25, 1] }}
|
|
className="text-center mb-12"
|
|
>
|
|
<h1 style={{ color: '#00171F' }} className="text-3xl md:text-4xl lg:text-5xl font-bold mb-4">
|
|
Share Your Feedback
|
|
</h1>
|
|
<p style={{ color: '#00171F' }} className="text-lg md:text-xl opacity-90">
|
|
I value your thoughts and suggestions. Your feedback helps me improve my work and services.
|
|
</p>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.8, delay: 0.2, ease: [0.25, 0.1, 0.25, 1] }}
|
|
>
|
|
<AnimatePresence mode="wait">
|
|
{status === 'success' ? (
|
|
<motion.div
|
|
key="success"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
className="relative overflow-hidden"
|
|
>
|
|
{/* Animated background circles */}
|
|
<motion.div
|
|
initial={{ scale: 0, opacity: 0 }}
|
|
animate={{ scale: 1, opacity: 0.1 }}
|
|
transition={{ duration: 0.8, ease: 'easeOut' }}
|
|
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 rounded-full"
|
|
style={{ backgroundColor: '#EED2CC' }}
|
|
/>
|
|
<motion.div
|
|
initial={{ scale: 0, opacity: 0 }}
|
|
animate={{ scale: 1, opacity: 0.15 }}
|
|
transition={{ duration: 0.8, delay: 0.2, ease: 'easeOut' }}
|
|
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-64 h-64 rounded-full"
|
|
style={{ backgroundColor: '#7D6E5E' }}
|
|
/>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.5, y: 50 }}
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
exit={{ opacity: 0, scale: 0.8 }}
|
|
transition={{
|
|
type: 'spring',
|
|
stiffness: 200,
|
|
damping: 20,
|
|
duration: 0.6
|
|
}}
|
|
className="relative text-center py-16 px-6 rounded-2xl"
|
|
style={{ backgroundColor: '#FBF7F4' }}
|
|
>
|
|
{/* Success icon with multiple animations */}
|
|
<motion.div
|
|
initial={{ scale: 0, rotate: -180 }}
|
|
animate={{ scale: 1, rotate: 0 }}
|
|
transition={{
|
|
type: 'spring',
|
|
stiffness: 200,
|
|
damping: 15,
|
|
delay: 0.2
|
|
}}
|
|
className="relative w-24 h-24 mx-auto mb-8"
|
|
>
|
|
<motion.div
|
|
initial={{ scale: 0 }}
|
|
animate={{ scale: [0, 1.2, 1] }}
|
|
transition={{ duration: 0.6, delay: 0.3 }}
|
|
className="absolute inset-0 rounded-full"
|
|
style={{ backgroundColor: '#EED2CC' }}
|
|
/>
|
|
<motion.div
|
|
initial={{ scale: 0 }}
|
|
animate={{ scale: 1 }}
|
|
transition={{ duration: 0.4, delay: 0.5 }}
|
|
className="absolute inset-0 flex items-center justify-center"
|
|
>
|
|
<motion.svg
|
|
initial={{ pathLength: 0, opacity: 0 }}
|
|
animate={{ pathLength: 1, opacity: 1 }}
|
|
transition={{ duration: 0.6, delay: 0.6 }}
|
|
className="w-12 h-12"
|
|
fill="none"
|
|
stroke="#00171F"
|
|
strokeWidth="3"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<motion.path
|
|
d="M5 13l4 4L19 7"
|
|
initial={{ pathLength: 0 }}
|
|
animate={{ pathLength: 1 }}
|
|
transition={{ duration: 0.5, delay: 0.7 }}
|
|
/>
|
|
</motion.svg>
|
|
</motion.div>
|
|
</motion.div>
|
|
|
|
{/* Confetti effect using dots */}
|
|
{[...Array(12)].map((_, i) => (
|
|
<motion.div
|
|
key={i}
|
|
initial={{
|
|
opacity: 0,
|
|
scale: 0,
|
|
x: 0,
|
|
y: 0,
|
|
}}
|
|
animate={{
|
|
opacity: [0, 1, 0],
|
|
scale: [0, 1, 0],
|
|
x: Math.cos((i * Math.PI * 2) / 12) * 100,
|
|
y: Math.sin((i * Math.PI * 2) / 12) * 100,
|
|
}}
|
|
transition={{
|
|
duration: 1.5,
|
|
delay: 0.8 + i * 0.05,
|
|
ease: 'easeOut',
|
|
}}
|
|
className="absolute top-1/2 left-1/2 w-2 h-2 rounded-full"
|
|
style={{
|
|
backgroundColor: ['#EED2CC', '#7D6E5E', '#00171F'][i % 3]
|
|
}}
|
|
/>
|
|
))}
|
|
|
|
<motion.h3
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.9, type: 'spring', stiffness: 100 }}
|
|
style={{ color: '#00171F' }}
|
|
className="text-3xl md:text-4xl font-bold mb-4 relative z-10"
|
|
>
|
|
Thanks for your submission!
|
|
</motion.h3>
|
|
<motion.p
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 1.0 }}
|
|
style={{ color: '#00171F' }}
|
|
className="text-lg md:text-xl opacity-90 relative z-10"
|
|
>
|
|
Your feedback helps me improve my services.
|
|
</motion.p>
|
|
|
|
{/* Subtle pulse animation */}
|
|
<motion.div
|
|
animate={{
|
|
scale: [1, 1.05, 1],
|
|
opacity: [0.3, 0.5, 0.3]
|
|
}}
|
|
transition={{
|
|
duration: 2,
|
|
repeat: Infinity,
|
|
ease: 'easeInOut'
|
|
}}
|
|
className="absolute inset-0 rounded-2xl pointer-events-none"
|
|
style={{ backgroundColor: '#EED2CC' }}
|
|
/>
|
|
</motion.div>
|
|
</motion.div>
|
|
) : (
|
|
<motion.form
|
|
key="form"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
onSubmit={handleSubmit}
|
|
action="https://formspree.io/f/mkgyzzqw"
|
|
method="POST"
|
|
className="space-y-6"
|
|
>
|
|
<div>
|
|
<label htmlFor="name" className="block text-sm font-medium mb-2" style={{ color: '#00171F' }}>
|
|
Name <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="name"
|
|
name="name"
|
|
required
|
|
value={formData.name}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 border rounded-lg outline-none transition-all focus:ring-2 focus:ring-opacity-50"
|
|
style={{
|
|
borderColor: '#EED2CC',
|
|
color: '#00171F',
|
|
backgroundColor: '#FBF7F4'
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium mb-2" style={{ color: '#00171F' }}>
|
|
Email
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 border rounded-lg outline-none transition-all focus:ring-2 focus:ring-opacity-50"
|
|
style={{
|
|
borderColor: '#EED2CC',
|
|
color: '#00171F',
|
|
backgroundColor: '#FBF7F4'
|
|
}}
|
|
/>
|
|
<p className="text-sm mt-1 opacity-70" style={{ color: '#00171F' }}>
|
|
Optional - but helpful if we need to follow up
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="message" className="block text-sm font-medium mb-2" style={{ color: '#00171F' }}>
|
|
Message <span className="text-red-500">*</span>
|
|
</label>
|
|
<textarea
|
|
id="message"
|
|
name="message"
|
|
rows={6}
|
|
required
|
|
value={formData.message}
|
|
onChange={handleChange}
|
|
className="w-full px-4 py-3 border rounded-lg outline-none transition-all resize-none focus:ring-2 focus:ring-opacity-50"
|
|
style={{
|
|
borderColor: '#EED2CC',
|
|
color: '#00171F',
|
|
backgroundColor: '#FBF7F4'
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<AnimatePresence>
|
|
{errorMessage && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -10 }}
|
|
className="p-4 rounded-lg border"
|
|
style={{
|
|
backgroundColor: '#FBF7F4',
|
|
borderColor: '#EED2CC',
|
|
color: '#00171F'
|
|
}}
|
|
>
|
|
<p className="text-sm">{errorMessage}</p>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={status === 'submitting'}
|
|
style={{ backgroundColor: '#00171F' }}
|
|
className="w-full text-white px-8 py-4 rounded-full text-lg font-semibold hover:opacity-90 transition-all transform hover:scale-105 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none"
|
|
>
|
|
{status === 'submitting' ? (
|
|
<span className="flex items-center justify-center">
|
|
<motion.svg
|
|
animate={{ rotate: 360 }}
|
|
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
|
|
className="w-5 h-5 mr-2"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
</motion.svg>
|
|
Sending...
|
|
</span>
|
|
) : (
|
|
'Send Feedback'
|
|
)}
|
|
</button>
|
|
</motion.form>
|
|
)}
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
<Footer />
|
|
</main>
|
|
);
|
|
}
|
|
|