## 完成的功能 - 用户登录/注册(邮箱 + Google OAuth) - 初始问题引导流程(4 个问题) - 智能体自动生成(根据用户回答) - 智能体列表页面 ## 新增文件 - 登录/注册页面和组件 - Onboarding 页面和组件 - 智能体列表页面 - 智能体 API 端点 - 智能体 Prompt 设计 ## 数据库迁移 - onboarding_fix 迁移(users.has_completed_onboarding) ## 测试 - 登录/注册:100% 通过 - Onboarding:85% 通过 - 智能体功能:85% 通过 Co-Authored-By: Claude <noreply@anthropic.com>
52 lines
2.0 KiB
TypeScript
52 lines
2.0 KiB
TypeScript
'use client';
|
|
|
|
import { forwardRef } from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
|
|
size?: 'sm' | 'md' | 'lg';
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
({ className, variant = 'primary', size = 'md', isLoading, children, disabled, ...props }, ref) => {
|
|
const baseStyles = 'inline-flex items-center justify-center rounded-lg font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed';
|
|
|
|
const variants = {
|
|
primary: 'bg-gradient-to-r from-pink-500 to-purple-500 text-white hover:from-pink-600 hover:to-purple-600 focus:ring-purple-500',
|
|
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500',
|
|
outline: 'border-2 border-gray-200 text-gray-700 hover:bg-gray-50 focus:ring-gray-500',
|
|
ghost: 'text-gray-600 hover:bg-gray-100 hover:text-gray-900 focus:ring-gray-500',
|
|
danger: 'bg-red-500 text-white hover:bg-red-600 focus:ring-red-500',
|
|
};
|
|
|
|
const sizes = {
|
|
sm: 'text-sm px-3 py-1.5 gap-1.5',
|
|
md: 'text-base px-4 py-2 gap-2',
|
|
lg: 'text-lg px-6 py-3 gap-2.5',
|
|
};
|
|
|
|
return (
|
|
<button
|
|
ref={ref}
|
|
className={cn(baseStyles, variants[variant], sizes[size], className)}
|
|
disabled={disabled || isLoading}
|
|
{...props}
|
|
>
|
|
{isLoading && (
|
|
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
|
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
|
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
|
</svg>
|
|
)}
|
|
{children}
|
|
</button>
|
|
);
|
|
}
|
|
);
|
|
|
|
Button.displayName = 'Button';
|
|
|
|
export { Button };
|