ai / components /FeedbackModal.tsx
Lianjx's picture
Upload 75 files
8fb4cca verified
import React, { useState } from 'react';
import { X, MessageSquare, AlertCircle, Lightbulb, HelpCircle, Star, Loader2 } from 'lucide-react';
import { Language, FeedbackItem, UserAccount } from '../types';
import { TRANSLATIONS } from '../constants/translations';
interface FeedbackModalProps {
isVisible: boolean;
onClose: () => void;
language: Language;
user?: UserAccount;
onSubmit: (data: Omit<FeedbackItem, 'id' | 'timestamp'>) => Promise<void>;
}
export const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, language, user, onSubmit }) => {
const t = TRANSLATIONS[language];
const [type, setType] = useState<FeedbackItem['type']>('suggestion');
const [content, setContent] = useState('');
const [rating, setRating] = useState(5);
const [isSubmitting, setIsSubmitting] = useState(false);
if (!isVisible) return null;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!content.trim()) return;
setIsSubmitting(true);
try {
await onSubmit({
userId: user?.id,
type,
rating,
content,
contact: user?.phone
});
// Reset form
setContent('');
setRating(5);
setType('suggestion');
} catch (error) {
console.error("Feedback error", error);
} finally {
setIsSubmitting(false);
}
};
const getIcon = () => {
switch (type) {
case 'bug': return <AlertCircle className="w-6 h-6 text-red-500" />;
case 'suggestion': return <Lightbulb className="w-6 h-6 text-amber-500" />;
default: return <HelpCircle className="w-6 h-6 text-blue-500" />;
}
};
return (
<div className="fixed inset-0 z-[110] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-fade-in">
<div className="bg-white rounded-2xl shadow-2xl w-full max-w-md overflow-hidden relative">
<button onClick={onClose} className="absolute top-4 right-4 p-1 rounded-full hover:bg-gray-100 transition-colors z-10">
<X className="w-5 h-5 text-gray-500" />
</button>
<div className="p-6">
<div className="text-center mb-6">
<div className="w-12 h-12 bg-rose-50 rounded-full flex items-center justify-center mx-auto mb-3 text-rose-500">
<MessageSquare className="w-6 h-6" />
</div>
<h2 className="text-xl font-bold text-gray-900">{t.feedbackTitle}</h2>
<p className="text-sm text-gray-500 mt-1">{t.feedbackDesc}</p>
</div>
<form onSubmit={handleSubmit} className="space-y-5">
{/* Rating */}
<div className="text-center">
<label className="block text-xs font-bold text-gray-500 mb-2">{t.feedbackRating}</label>
<div className="flex justify-center gap-2">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
type="button"
onClick={() => setRating(star)}
className="focus:outline-none transition-transform hover:scale-110"
>
<Star className={`w-8 h-8 ${star <= rating ? 'fill-yellow-400 text-yellow-400' : 'text-gray-300'}`} />
</button>
))}
</div>
</div>
{/* Type Selection */}
<div className="grid grid-cols-3 gap-2">
<button
type="button"
onClick={() => setType('bug')}
className={`p-3 rounded-xl border flex flex-col items-center gap-1 transition-all ${type === 'bug' ? 'bg-red-50 border-red-200 text-red-600' : 'bg-white border-gray-200 text-gray-500 hover:bg-gray-50'}`}
>
<AlertCircle className="w-5 h-5" />
<span className="text-xs font-bold">{t.feedbackTypeBug}</span>
</button>
<button
type="button"
onClick={() => setType('suggestion')}
className={`p-3 rounded-xl border flex flex-col items-center gap-1 transition-all ${type === 'suggestion' ? 'bg-amber-50 border-amber-200 text-amber-600' : 'bg-white border-gray-200 text-gray-500 hover:bg-gray-50'}`}
>
<Lightbulb className="w-5 h-5" />
<span className="text-xs font-bold">{t.feedbackTypeSugg}</span>
</button>
<button
type="button"
onClick={() => setType('other')}
className={`p-3 rounded-xl border flex flex-col items-center gap-1 transition-all ${type === 'other' ? 'bg-blue-50 border-blue-200 text-blue-600' : 'bg-white border-gray-200 text-gray-500 hover:bg-gray-50'}`}
>
<HelpCircle className="w-5 h-5" />
<span className="text-xs font-bold">{t.feedbackTypeOther}</span>
</button>
</div>
{/* Content */}
<div>
<div className="relative">
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder={t.feedbackContentPlace}
className="w-full p-4 rounded-xl border border-gray-200 focus:border-rose-500 focus:ring-1 focus:ring-rose-200 outline-none h-32 resize-none bg-gray-50 focus:bg-white transition-all text-sm"
required
/>
<div className="absolute top-3 right-3 opacity-50 pointer-events-none">
{getIcon()}
</div>
</div>
</div>
<button
type="submit"
disabled={isSubmitting || !content.trim()}
className="w-full py-3 bg-gray-900 text-white rounded-xl font-bold shadow-lg hover:bg-black transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting && <Loader2 className="w-4 h-4 animate-spin" />}
{t.feedbackSubmit}
</button>
</form>
</div>
</div>
</div>
);
};