181 lines
7.6 KiB
TypeScript
181 lines
7.6 KiB
TypeScript
import { useState, useEffect } from "react";
|
||
import Sidebar from "@/components/Sidebar";
|
||
import Tools from "@/components/Tools";
|
||
import Header from "@/components/Header";
|
||
import PythonRunner from "@/components/PythonRunner";
|
||
import Calculator from "@/components/Calculator";
|
||
import Browser from "@/components/Browser";
|
||
import WebDesign from "@/components/WebDesign";
|
||
import FileConverter from "@/components/FileConverter";
|
||
import LanguageTranslator from "@/components/LanguageTranslator";
|
||
import ApiIntegrator from "@/components/ApiIntegrator";
|
||
import { useAuth } from '@/hooks/useAuth';
|
||
import MCPImage from "@/components/Image/MCPImage";
|
||
|
||
type Props = {
|
||
children: React.ReactNode;
|
||
};
|
||
|
||
const MCPLayout = ({ children }: Props) => {
|
||
const [activeId, setActiveId] = useState<string | null>(null);
|
||
const [visibleTools, setVisibleTools] = useState(true);
|
||
const [visibleSidebar, setVisibleSidebar] = useState(false);
|
||
const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth();
|
||
|
||
console.log('[MCPLayout] Auth state:', {
|
||
user,
|
||
isAuthenticated,
|
||
canAccessChat,
|
||
authLoading
|
||
});
|
||
|
||
// 权限检查
|
||
if (authLoading) {
|
||
return (
|
||
<div className="flex items-center justify-center h-screen bg-gray-50">
|
||
<div className="text-center">
|
||
<div className="w-16 h-16 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||
<p className="text-lg text-gray-600">加载中...</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (!isAuthenticated) {
|
||
const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn';
|
||
return (
|
||
<div className="flex flex-col items-center justify-center h-screen bg-gray-50">
|
||
<div className="text-center p-8 bg-white rounded-lg shadow-lg">
|
||
<h2 className="text-2xl mb-4">请先登录</h2>
|
||
<p className="text-gray-600 mb-6">您需要登录才能使用 AI 助手</p>
|
||
<a
|
||
href={`${mainAppUrl}/auth/sign-in?redirect=/ai-chat`}
|
||
className="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
|
||
>
|
||
前往登录
|
||
</a>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (!canAccessChat) {
|
||
const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn';
|
||
return (
|
||
<div className="flex flex-col items-center justify-center h-screen bg-gray-50">
|
||
<div className="text-center p-8 bg-white rounded-lg shadow-lg">
|
||
<h2 className="text-2xl mb-4">需要订阅才能使用 AI 助手</h2>
|
||
<p className="text-gray-600 mb-2">升级到高级版解锁所有功能</p>
|
||
<p className="text-sm text-gray-500 mb-6">
|
||
当前订阅等级:{user?.subscription_type || '未订阅'}
|
||
</p>
|
||
<div className="space-y-3">
|
||
<a
|
||
href={`${mainAppUrl}/subscription`}
|
||
className="block bg-green-600 text-white px-6 py-3 rounded-lg hover:bg-green-700 transition-colors"
|
||
>
|
||
查看订阅方案
|
||
</a>
|
||
<button
|
||
onClick={() => window.location.reload()}
|
||
className="block w-full bg-gray-200 text-gray-700 px-6 py-3 rounded-lg hover:bg-gray-300 transition-colors"
|
||
>
|
||
刷新页面
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div
|
||
className={`pl-90 overflow-hidden transition-all max-3xl:pl-75 max-lg:pl-5 max-md:pl-4 ${
|
||
visibleTools
|
||
? "pr-90 max-3xl:pr-75 max-2xl:pr-5 max-md:pr-4"
|
||
: "pr-5 max-md:pr-4"
|
||
}`}
|
||
>
|
||
<Sidebar
|
||
visible={visibleSidebar}
|
||
onClose={() => setVisibleSidebar(false)}
|
||
onClickNewChat={() => setActiveId(null)}
|
||
/>
|
||
|
||
{/* 用户信息显示 */}
|
||
{user && (
|
||
<div className="fixed bottom-4 left-4 z-50 bg-white rounded-lg shadow-lg p-3 flex items-center space-x-3 max-w-xs">
|
||
{user.avatar_url ? (
|
||
<img
|
||
src={user.avatar_url}
|
||
alt={user.username || user.nickname}
|
||
className="w-10 h-10 rounded-full"
|
||
/>
|
||
) : (
|
||
<div className="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center text-white font-semibold">
|
||
{(user.username || user.nickname || 'U')[0].toUpperCase()}
|
||
</div>
|
||
)}
|
||
<div className="flex-1 min-w-0">
|
||
<p className="font-medium text-sm text-gray-900 truncate">
|
||
{user.username || user.nickname}
|
||
</p>
|
||
<p className="text-xs text-gray-500">
|
||
{user.subscription_type === 'max' ? '🌟 MAX会员' :
|
||
user.subscription_type === 'premium' ? '💎 高级会员' :
|
||
user.subscription_type === 'pro' ? '🚀 专业版' : '免费版'}
|
||
</p>
|
||
{user.subscription_days_left && user.subscription_days_left > 0 && (
|
||
<p className="text-xs text-green-600">
|
||
剩余 {user.subscription_days_left} 天
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="pt-9.5 pb-5 max-2xl:pt-5 max-md:pt-3 max-md:pb-4">
|
||
<Header
|
||
onOpenSidebar={() => setVisibleSidebar(true)}
|
||
onToggleTools={() => setVisibleTools(!visibleTools)}
|
||
/>
|
||
{activeId === "python" ? (
|
||
<PythonRunner />
|
||
) : activeId === "calculator" ? (
|
||
<Calculator />
|
||
) : activeId === "browser" ? (
|
||
<Browser />
|
||
) : activeId === "web-design" ? (
|
||
<WebDesign />
|
||
) : activeId === "exchange" ? (
|
||
<FileConverter />
|
||
) : activeId === "language" ? (
|
||
<LanguageTranslator />
|
||
) : activeId === "api" ? (
|
||
<ApiIntegrator />
|
||
) : (
|
||
children
|
||
)}
|
||
</div>
|
||
<Tools
|
||
activeId={activeId}
|
||
setActiveId={setActiveId}
|
||
visible={visibleTools}
|
||
onClose={() => setVisibleTools(!visibleTools)}
|
||
/>
|
||
<div
|
||
className={`fixed inset-0 z-10 hidden bg-overlay backdrop-blur-sm transition-all max-lg:block max-md:hidden ${
|
||
visibleSidebar || !visibleTools
|
||
? "visible opacity-100"
|
||
: "invisible opacity-0"
|
||
}`}
|
||
onClick={() => {
|
||
setVisibleSidebar(false);
|
||
setVisibleTools(true);
|
||
}}
|
||
></div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default MCPLayout; |