4 neue dynamischen
This commit is contained in:
195
src/app/feedback/[slug]/page.tsx
Normal file
195
src/app/feedback/[slug]/page.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
'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(() => {
|
||||
window.location.href = feedback.googleReviewUrl!;
|
||||
}, 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-blue-50 to-indigo-100">
|
||||
<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-blue-50 to-indigo-100 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-emerald-50 to-teal-100 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-emerald-400 to-teal-500 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-blue-50 via-indigo-50 to-purple-50 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-indigo-500 to-purple-600 text-white p-8 text-center">
|
||||
<div className="w-14 h-14 bg-white/20 rounded-2xl flex items-center justify-center mx-auto mb-4">
|
||||
<Star className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold mb-1">How was your experience?</h1>
|
||||
<p className="text-white/80">{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-indigo-500 to-purple-600 text-white hover:from-indigo-600 hover:to-purple-700 shadow-lg shadow-indigo-200'
|
||||
}`}
|
||||
>
|
||||
<Send className="w-4 h-4" />
|
||||
{submitting ? 'Sending...' : 'Submit Feedback'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<p className="text-center text-sm text-indigo-300 mt-6">
|
||||
Powered by QR Master
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user