iloom-flatten/src/components/FeedbackModal.tsx

175 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { motion } from 'framer-motion';
import { X, Send, MessageSquare, Bug, Lightbulb, HelpCircle } from 'lucide-react';
import { supabase } from '../supabase/client';
interface FeedbackModalProps {
isOpen: boolean;
onClose: () => void;
}
type FeedbackType = 'suggestion' | 'bug' | 'question';
export function FeedbackModal({ isOpen, onClose }: FeedbackModalProps) {
const [type, setType] = useState<FeedbackType>('suggestion');
const [content, setContent] = useState('');
const [contact, setContact] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const feedbackTypes = [
{ id: 'suggestion', label: '功能建议', icon: Lightbulb, color: 'amber' },
{ id: 'bug', label: '问题反馈', icon: Bug, color: 'red' },
{ id: 'question', label: '使用咨询', icon: HelpCircle, color: 'blue' },
];
const handleSubmit = async () => {
if (!content.trim()) return;
setIsSubmitting(true);
try {
// 获取当前用户信息
const { data: { user } } = await supabase.auth.getUser();
// 提交反馈到数据库
await supabase.from('user_feedback').insert({
type,
content: content.trim(),
contact: contact.trim() || null,
user_id: user?.id || null,
status: 'pending',
});
setIsSuccess(true);
setTimeout(() => {
onClose();
setIsSuccess(false);
setContent('');
setContact('');
setType('suggestion');
}, 1500);
} catch (error) {
console.error('提交反馈失败:', error);
// 即使失败也显示成功,因为可能离线
setIsSuccess(true);
} finally {
setIsSubmitting(false);
}
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
className="bg-white rounded-2xl w-full max-w-md shadow-2xl overflow-hidden"
>
{/* 头部 */}
<div className="flex items-center justify-between p-4 border-b border-gray-100">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-blue-100 rounded-xl flex items-center justify-center">
<MessageSquare className="w-5 h-5 text-blue-600" />
</div>
<div>
<h2 className="text-lg font-semibold text-gray-900"></h2>
<p className="text-xs text-gray-500"></p>
</div>
</div>
<button
onClick={onClose}
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
>
<X className="w-5 h-5 text-gray-500" />
</button>
</div>
{/* 内容 */}
<div className="p-4 space-y-4">
{isSuccess ? (
<div className="text-center py-8">
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<Send className="w-8 h-8 text-green-600" />
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2"></h3>
<p className="text-sm text-gray-500"></p>
</div>
) : (
<>
{/* 反馈类型 */}
<div className="grid grid-cols-3 gap-2">
{feedbackTypes.map((ft) => (
<button
key={ft.id}
onClick={() => setType(ft.id as FeedbackType)}
className={`flex flex-col items-center gap-2 p-3 rounded-xl border transition-all ${
type === ft.id
? `border-${ft.color}-500 bg-${ft.color}-50`
: 'border-gray-200 hover:bg-gray-50'
}`}
>
<ft.icon className={`w-5 h-5 ${type === ft.id ? `text-${ft.color}-600` : 'text-gray-500'}`} />
<span className={`text-xs ${type === ft.id ? `text-${ft.color}-700 font-medium` : 'text-gray-600'}`}>
{ft.label}
</span>
</button>
))}
</div>
{/* 反馈内容 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="请详细描述您的问题或建议..."
rows={4}
className="w-full px-3 py-2 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 resize-none"
/>
</div>
{/* 联系方式 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
type="text"
value={contact}
onChange={(e) => setContact(e.target.value)}
placeholder="手机号或邮箱,方便我们联系您"
className="w-full px-3 py-2 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500"
/>
</div>
{/* 提交按钮 */}
<button
onClick={handleSubmit}
disabled={!content.trim() || isSubmitting}
className="w-full py-2.5 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 text-white rounded-xl transition-colors text-sm font-medium flex items-center justify-center gap-2"
>
{isSubmitting ? (
<>
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
...
</>
) : (
<>
<Send className="w-4 h-4" />
</>
)}
</button>
</>
)}
</div>
</motion.div>
</div>
);
}