|
|
|
|
@ -1,7 +1,7 @@
|
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react';
|
|
|
|
|
import { motion } from 'framer-motion';
|
|
|
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
|
|
|
import Navigation from '@/components/Navigation';
|
|
|
|
|
import Footer from '@/components/Footer';
|
|
|
|
|
|
|
|
|
|
@ -12,25 +12,52 @@ export default function FeedbackPage() {
|
|
|
|
|
message: '',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
|
const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
|
|
|
|
|
const [errorMessage, setErrorMessage] = useState('');
|
|
|
|
|
|
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
|
|
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setIsSubmitting(true);
|
|
|
|
|
setStatus('submitting');
|
|
|
|
|
setErrorMessage('');
|
|
|
|
|
|
|
|
|
|
// Handle form submission here
|
|
|
|
|
console.log('Feedback submitted:', formData);
|
|
|
|
|
const form = e.currentTarget;
|
|
|
|
|
const data = new FormData(form);
|
|
|
|
|
|
|
|
|
|
// Simulate API call
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(form.action, {
|
|
|
|
|
method: form.method,
|
|
|
|
|
body: data,
|
|
|
|
|
headers: {
|
|
|
|
|
'Accept': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
alert('Thank you for your feedback! We appreciate your input.');
|
|
|
|
|
setFormData({
|
|
|
|
|
name: '',
|
|
|
|
|
email: '',
|
|
|
|
|
message: '',
|
|
|
|
|
});
|
|
|
|
|
setIsSubmitting(false);
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
setStatus('success');
|
|
|
|
|
setFormData({
|
|
|
|
|
name: '',
|
|
|
|
|
email: '',
|
|
|
|
|
message: '',
|
|
|
|
|
});
|
|
|
|
|
form.reset();
|
|
|
|
|
|
|
|
|
|
// Reset success message after 5 seconds
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
setStatus('idle');
|
|
|
|
|
}, 5000);
|
|
|
|
|
} else {
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
if (data.errors) {
|
|
|
|
|
setErrorMessage(data.errors.map((error: { message: string }) => error.message).join(', '));
|
|
|
|
|
} else {
|
|
|
|
|
setErrorMessage('Oops! There was a problem submitting your form.');
|
|
|
|
|
}
|
|
|
|
|
setStatus('error');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
setErrorMessage('Oops! There was a problem submitting your form.');
|
|
|
|
|
setStatus('error');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
|
|
|
@ -64,10 +91,76 @@ export default function FeedbackPage() {
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
transition={{ duration: 0.8, delay: 0.2, ease: [0.25, 0.1, 0.25, 1] }}
|
|
|
|
|
>
|
|
|
|
|
<form
|
|
|
|
|
onSubmit={handleSubmit}
|
|
|
|
|
className="space-y-6"
|
|
|
|
|
>
|
|
|
|
|
<AnimatePresence mode="wait">
|
|
|
|
|
{status === 'success' ? (
|
|
|
|
|
<motion.div
|
|
|
|
|
key="success"
|
|
|
|
|
initial={{ opacity: 0, scale: 0.8, y: 20 }}
|
|
|
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
|
|
|
exit={{ opacity: 0, scale: 0.8 }}
|
|
|
|
|
transition={{ duration: 0.5, ease: [0.25, 0.1, 0.25, 1] }}
|
|
|
|
|
className="text-center py-12 px-6 rounded-2xl"
|
|
|
|
|
style={{ backgroundColor: '#FBF7F4' }}
|
|
|
|
|
>
|
|
|
|
|
<motion.div
|
|
|
|
|
initial={{ scale: 0 }}
|
|
|
|
|
animate={{ scale: 1 }}
|
|
|
|
|
transition={{
|
|
|
|
|
type: 'spring',
|
|
|
|
|
stiffness: 200,
|
|
|
|
|
damping: 15,
|
|
|
|
|
delay: 0.2
|
|
|
|
|
}}
|
|
|
|
|
className="w-20 h-20 mx-auto mb-6 rounded-full flex items-center justify-center"
|
|
|
|
|
style={{ backgroundColor: '#EED2CC' }}
|
|
|
|
|
>
|
|
|
|
|
<motion.svg
|
|
|
|
|
initial={{ pathLength: 0 }}
|
|
|
|
|
animate={{ pathLength: 1 }}
|
|
|
|
|
transition={{ duration: 0.5, delay: 0.4 }}
|
|
|
|
|
className="w-10 h-10"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="#00171F"
|
|
|
|
|
strokeWidth="3"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<motion.path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
d="M5 13l4 4L19 7"
|
|
|
|
|
/>
|
|
|
|
|
</motion.svg>
|
|
|
|
|
</motion.div>
|
|
|
|
|
<motion.h3
|
|
|
|
|
initial={{ opacity: 0, y: 10 }}
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
transition={{ delay: 0.3 }}
|
|
|
|
|
style={{ color: '#00171F' }}
|
|
|
|
|
className="text-2xl md:text-3xl font-bold mb-3"
|
|
|
|
|
>
|
|
|
|
|
Thanks for your submission!
|
|
|
|
|
</motion.h3>
|
|
|
|
|
<motion.p
|
|
|
|
|
initial={{ opacity: 0, y: 10 }}
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
transition={{ delay: 0.4 }}
|
|
|
|
|
style={{ color: '#00171F' }}
|
|
|
|
|
className="text-lg opacity-90"
|
|
|
|
|
>
|
|
|
|
|
Your feedback helps me improve my services.
|
|
|
|
|
</motion.p>
|
|
|
|
|
</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>
|
|
|
|
|
@ -90,7 +183,7 @@ export default function FeedbackPage() {
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label htmlFor="email" className="block text-sm font-medium mb-2" style={{ color: '#00171F' }}>
|
|
|
|
|
Email (optional)
|
|
|
|
|
Email
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
|
|
|
|
@ -105,7 +198,9 @@ export default function FeedbackPage() {
|
|
|
|
|
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>
|
|
|
|
|
@ -128,15 +223,51 @@ export default function FeedbackPage() {
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={isSubmitting}
|
|
|
|
|
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"
|
|
|
|
|
>
|
|
|
|
|
{isSubmitting ? 'Sending...' : 'Send Feedback'}
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
<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>
|
|
|
|
|
|