175 lines
6.4 KiB
TypeScript
175 lines
6.4 KiB
TypeScript
|
|
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>
|
|||
|
|
);
|
|||
|
|
}
|