## 完成的功能 - 用户登录/注册(邮箱 + Google OAuth) - 初始问题引导流程(4 个问题) - 智能体自动生成(根据用户回答) - 智能体列表页面 ## 新增文件 - 登录/注册页面和组件 - Onboarding 页面和组件 - 智能体列表页面 - 智能体 API 端点 - 智能体 Prompt 设计 ## 数据库迁移 - onboarding_fix 迁移(users.has_completed_onboarding) ## 测试 - 登录/注册:100% 通过 - Onboarding:85% 通过 - 智能体功能:85% 通过 Co-Authored-By: Claude <noreply@anthropic.com>
192 lines
6.5 KiB
TypeScript
192 lines
6.5 KiB
TypeScript
'use client';
|
||
|
||
import { useState } from 'react';
|
||
import { Plus, X, Sparkles, Loader2 } from 'lucide-react';
|
||
import { AgentList } from '@/components/agents/AgentList';
|
||
import { Button } from '@/components/ui/Button';
|
||
import { Input } from '@/components/ui/Input';
|
||
import { Textarea } from '@/components/ui/Textarea';
|
||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/Card';
|
||
import type { Dictionary } from '@/get-dictionary';
|
||
|
||
interface AgentsPageClientProps {
|
||
locale: string;
|
||
isPremium: boolean;
|
||
dict: Dictionary;
|
||
}
|
||
|
||
export function AgentsPageClient({ locale, isPremium, dict }: AgentsPageClientProps) {
|
||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||
const [creating, setCreating] = useState(false);
|
||
const [formData, setFormData] = useState({
|
||
name: '',
|
||
personality: '',
|
||
background: '',
|
||
});
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
const handleCreateAgent = async () => {
|
||
if (!formData.name.trim()) {
|
||
setError('请输入智能体名称');
|
||
return;
|
||
}
|
||
|
||
setCreating(true);
|
||
setError(null);
|
||
|
||
try {
|
||
const response = await fetch('/api/agents', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
name: formData.name,
|
||
background: formData.background,
|
||
personality: {
|
||
traits: formData.personality.split(',').map((t) => t.trim()).filter(Boolean),
|
||
tone: 'warm',
|
||
},
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const data = await response.json();
|
||
throw new Error(data.error || '创建失败');
|
||
}
|
||
|
||
// Close modal and refresh
|
||
setShowCreateModal(false);
|
||
setFormData({ name: '', personality: '', background: '' });
|
||
// The AgentList will auto-refresh
|
||
} catch (err) {
|
||
setError(err instanceof Error ? err.message : '创建失败');
|
||
} finally {
|
||
setCreating(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<AgentList
|
||
locale={locale}
|
||
isPremium={isPremium}
|
||
onCreateAgent={() => setShowCreateModal(true)}
|
||
/>
|
||
|
||
{/* Create Modal */}
|
||
{showCreateModal && (
|
||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||
<div className="bg-white rounded-2xl w-full max-w-lg max-h-[90vh] overflow-y-auto">
|
||
{/* Modal header */}
|
||
<div className="flex items-center justify-between p-6 border-b border-gray-100">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-10 h-10 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 flex items-center justify-center">
|
||
<Sparkles className="w-5 h-5 text-white" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-lg font-semibold text-gray-900">
|
||
创建自定义智能体
|
||
</h3>
|
||
<p className="text-sm text-gray-500">
|
||
为你的新智能体起个名字吧
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={() => setShowCreateModal(false)}
|
||
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
||
>
|
||
<X className="w-5 h-5 text-gray-400" />
|
||
</button>
|
||
</div>
|
||
|
||
{/* Modal body */}
|
||
<div className="p-6 space-y-4">
|
||
{/* Name input */}
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
智能体名称 <span className="text-red-500">*</span>
|
||
</label>
|
||
<Input
|
||
value={formData.name}
|
||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||
placeholder="例如:十年后的自己"
|
||
maxLength={50}
|
||
/>
|
||
</div>
|
||
|
||
{/* Background textarea */}
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
背景故事
|
||
</label>
|
||
<Textarea
|
||
value={formData.background}
|
||
onChange={(e) => setFormData({ ...formData, background: e.target.value })}
|
||
placeholder="描述这个智能体的背景故事..."
|
||
rows={3}
|
||
maxLength={500}
|
||
/>
|
||
</div>
|
||
|
||
{/* Personality input */}
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
性格特点
|
||
</label>
|
||
<Input
|
||
value={formData.personality}
|
||
onChange={(e) => setFormData({ ...formData, personality: e.target.value })}
|
||
placeholder="例如:温柔、乐观、善解人意(用逗号分隔)"
|
||
maxLength={100}
|
||
/>
|
||
<p className="text-xs text-gray-400 mt-1">
|
||
多个特点请用逗号分隔
|
||
</p>
|
||
</div>
|
||
|
||
{/* Error message */}
|
||
{error && (
|
||
<div className="bg-red-50 border border-red-200 rounded-lg p-3 text-sm text-red-600">
|
||
{error}
|
||
</div>
|
||
)}
|
||
|
||
{/* Premium notice */}
|
||
{!isPremium && (
|
||
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4">
|
||
<p className="text-sm text-amber-800">
|
||
<strong>注意:</strong>
|
||
免费用户最多只能拥有 1 个自定义智能体。
|
||
升级到高级版可创建最多 3 个智能体。
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Modal footer */}
|
||
<div className="flex items-center justify-end gap-3 p-6 border-t border-gray-100">
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => setShowCreateModal(false)}
|
||
disabled={creating}
|
||
>
|
||
取消
|
||
</Button>
|
||
<Button
|
||
variant="primary"
|
||
onClick={handleCreateAgent}
|
||
isLoading={creating}
|
||
disabled={!formData.name.trim() || creating}
|
||
>
|
||
{creating ? '创建中...' : '创建智能体'}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|
||
}
|