205 lines
8.8 KiB
TypeScript
205 lines
8.8 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useParams } from 'next/navigation';
|
|
import { Star, Send, Check } from 'lucide-react';
|
|
|
|
interface FeedbackData {
|
|
businessName: string;
|
|
googleReviewUrl?: string;
|
|
thankYouMessage?: string;
|
|
}
|
|
|
|
export default function FeedbackPage() {
|
|
const params = useParams();
|
|
const slug = params.slug as string;
|
|
const [feedback, setFeedback] = useState<FeedbackData | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [rating, setRating] = useState(0);
|
|
const [hoverRating, setHoverRating] = useState(0);
|
|
const [comment, setComment] = useState('');
|
|
const [submitted, setSubmitted] = useState(false);
|
|
const [submitting, setSubmitting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
async function fetchFeedback() {
|
|
try {
|
|
const res = await fetch(`/api/qrs/public/${slug}`);
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
if (data.contentType === 'FEEDBACK') {
|
|
setFeedback(data.content as FeedbackData);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching feedback data:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
fetchFeedback();
|
|
}, [slug]);
|
|
|
|
const handleSubmit = async () => {
|
|
if (rating === 0) return;
|
|
|
|
setSubmitting(true);
|
|
try {
|
|
await fetch('/api/feedback', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ slug, rating, comment }),
|
|
});
|
|
|
|
setSubmitted(true);
|
|
|
|
if (rating >= 4 && feedback?.googleReviewUrl) {
|
|
setTimeout(() => {
|
|
const url = ensureAbsoluteUrl(feedback.googleReviewUrl);
|
|
if (url) window.location.href = url;
|
|
}, 2000);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error submitting feedback:', error);
|
|
} finally {
|
|
setSubmitting(false);
|
|
}
|
|
};
|
|
|
|
// Loading
|
|
if (loading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#FAF8F5] via-[#C6C0B3] to-[#4C5F4E]">
|
|
<div className="w-10 h-10 border-3 border-indigo-200 border-t-indigo-600 rounded-full animate-spin"></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Not found
|
|
if (!feedback) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#FAF8F5] via-[#C6C0B3] to-[#4C5F4E] px-6">
|
|
<div className="text-center bg-white rounded-2xl p-8 shadow-lg">
|
|
<p className="text-gray-500 text-lg">This feedback form is not available.</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Success
|
|
if (submitted) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#FAF8F5] via-[#C6C0B3] to-[#4C5F4E] px-6">
|
|
<div className="max-w-sm w-full bg-white rounded-3xl shadow-xl p-10 text-center">
|
|
<div className="w-20 h-20 bg-gradient-to-br from-[#4C5F4E] to-[#FAF8F5] rounded-full flex items-center justify-center mx-auto mb-6 shadow-lg">
|
|
<Check className="w-10 h-10 text-white" strokeWidth={2.5} />
|
|
</div>
|
|
|
|
<h1 className="text-2xl font-bold text-gray-900 mb-2">
|
|
Thank you!
|
|
</h1>
|
|
|
|
<p className="text-gray-500">
|
|
{feedback.thankYouMessage || 'Your feedback has been submitted.'}
|
|
</p>
|
|
|
|
{rating >= 4 && feedback.googleReviewUrl && (
|
|
<p className="text-sm text-teal-600 mt-6 font-medium">
|
|
Redirecting to Google Reviews...
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Rating Form
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#FAF8F5] via-[#C6C0B3] to-[#4C5F4E] px-6 py-12">
|
|
<div className="max-w-md w-full">
|
|
{/* Card */}
|
|
<div className="bg-white rounded-3xl shadow-xl overflow-hidden">
|
|
{/* Colored Header */}
|
|
<div className="bg-gradient-to-r from-[#FAF8F5] via-[#C6C0B3] to-[#4C5F4E] p-8 text-center">
|
|
<div className="w-14 h-14 bg-[#4C5F4E]/10 rounded-2xl flex items-center justify-center mx-auto mb-4">
|
|
<Star className="w-7 h-7 text-[#4C5F4E]" />
|
|
</div>
|
|
<h1 className="text-2xl font-bold mb-1 text-gray-900">How was your experience?</h1>
|
|
<p className="text-gray-700">{feedback.businessName}</p>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="p-8">
|
|
{/* Stars */}
|
|
<div className="flex justify-center gap-2 mb-2">
|
|
{[1, 2, 3, 4, 5].map((star) => (
|
|
<button
|
|
key={star}
|
|
onClick={() => setRating(star)}
|
|
onMouseEnter={() => setHoverRating(star)}
|
|
onMouseLeave={() => setHoverRating(0)}
|
|
className="p-1 transition-transform hover:scale-110 focus:outline-none"
|
|
aria-label={`Rate ${star} stars`}
|
|
>
|
|
<Star
|
|
className={`w-11 h-11 transition-all ${star <= (hoverRating || rating)
|
|
? 'text-amber-400 fill-amber-400 drop-shadow-sm'
|
|
: 'text-gray-200'
|
|
}`}
|
|
/>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Rating text */}
|
|
<p className="text-center text-sm font-medium h-6 mb-6" style={{ color: rating > 0 ? '#6366f1' : 'transparent' }}>
|
|
{rating === 1 && 'Poor'}
|
|
{rating === 2 && 'Fair'}
|
|
{rating === 3 && 'Good'}
|
|
{rating === 4 && 'Great!'}
|
|
{rating === 5 && 'Excellent!'}
|
|
</p>
|
|
|
|
{/* Comment */}
|
|
<div className="mb-6">
|
|
<textarea
|
|
value={comment}
|
|
onChange={(e) => setComment(e.target.value)}
|
|
placeholder="Share your thoughts (optional)"
|
|
rows={3}
|
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-gray-900 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent resize-none"
|
|
/>
|
|
</div>
|
|
|
|
{/* Submit */}
|
|
<button
|
|
onClick={handleSubmit}
|
|
disabled={rating === 0 || submitting}
|
|
className={`w-full py-4 rounded-xl font-semibold flex items-center justify-center gap-2 transition-all ${rating === 0
|
|
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
|
: 'bg-gradient-to-r from-[#4C5F4E] to-[#0C342C] text-white hover:from-[#5a705c] hover:to-[#0E4036] shadow-lg shadow-emerald-200'
|
|
}`}
|
|
>
|
|
<Send className="w-4 h-4" />
|
|
{submitting ? 'Sending...' : 'Submit Feedback'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<p className="text-center text-sm text-white/60 mt-6">
|
|
Powered by QR Master
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ensureAbsoluteUrl(url: string | undefined): string | undefined {
|
|
if (!url) return undefined;
|
|
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) {
|
|
return url;
|
|
}
|
|
return `https://${url}`;
|
|
}
|