commit f982a07b55eb380a7e6888f98fb6d5ee3d40ff7a Author: xiaohei Date: Thu Dec 25 07:58:59 2025 +0000 初始提交:完整的API客户端对接和前端项目 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f650315 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/app/admin/dashboard/loading.tsx b/app/admin/dashboard/loading.tsx new file mode 100644 index 0000000..f15322a --- /dev/null +++ b/app/admin/dashboard/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return null +} diff --git a/app/admin/dashboard/page.tsx b/app/admin/dashboard/page.tsx new file mode 100644 index 0000000..ae16e8d --- /dev/null +++ b/app/admin/dashboard/page.tsx @@ -0,0 +1,3003 @@ +"use client" + +import { useEffect, useState } from "react" +import { useRouter } from "next/navigation" +import { Card } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { + Shield, + Users, + DollarSign, + Activity, + Database, + TrendingUp, + AlertTriangle, + Search, + MoreVertical, + Globe, + LogOut, + Settings, + BarChart3, + Clock, + Building2, + Plus, + Eye, + Edit, + Trash2, + Zap, + Cpu, + Server, + Bot, + Check, + Calendar, + Filter, + Download, +} from "lucide-react" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { useLanguage } from "@/hooks/useLanguage" // Declare useLanguage import +import { Badge } from "@/components/ui/badge" + +export default function AdminDashboard() { + const router = useRouter() + const { language, setLanguage } = useLanguage() + const [activeTab, setActiveTab] = useState("overview") + const [isAddChannelOpen, setIsAddChannelOpen] = useState(false) + // const [isConfigGoodsOpen, setIsConfigGoodsOpen] = useState(false) // REMOVED + // const [isAgentAllocationOpen, setIsAgentAllocationOpen] = useState(false) // REMOVED + const [isResourceManagementOpen, setIsResourceManagementOpen] = useState(false) // ADDED + + const [isCommissionDialogOpen, setIsCommissionDialogOpen] = useState(false) + const [selectedChannel, setSelectedChannel] = useState<(typeof channels)[0] | null>(null) + const [selectedModels, setSelectedModels] = useState([]) + // const [monthlyQuota, setMonthlyQuota] = useState(""); // REMOVED + const [creditLimit, setCreditLimit] = useState("") + const [isSubscriptionDialogOpen, setIsSubscriptionDialogOpen] = useState(false) + const [selectedSubscriptionLevel, setSelectedSubscriptionLevel] = useState(null) + const [commissionRate, setCommissionRate] = useState("") + const [selectedTenant, setSelectedTenant] = useState(null) // Declare selectedTenant variable + const [computeConfigOpen, setComputeConfigOpen] = useState(false) // State for compute configuration dialog + + const [isAddProviderOpen, setIsAddProviderOpen] = useState(false) + const [isConfigProviderOpen, setIsConfigProviderOpen] = useState(false) + const [providerType, setProviderType] = useState<"model" | "data">("model") + const [selectedProvider, setSelectedProvider] = useState(null) + const [providerForm, setProviderForm] = useState({ + name: "", + url: "", + apiKey: "", + models: "", + rpm: "", + tpm: "", + }) + + const [selectedAgent, setSelectedAgent] = useState(null) // State for selected agent in resources tab + const [customAgentCpu, setCustomAgentCpu] = useState("2") + const [customAgentMemory, setCustomAgentMemory] = useState("4") + + // const [isAgentAllocationOpen, setIsAgentAllocationOpen] = useState(false) // REMOVED + const [selectedAgents, setSelectedAgents] = useState([]) + const [agentQuantities, setAgentQuantities] = useState>({}) + + // Billing state + const [billingView, setBillingView] = useState<"channel" | "tenant" | "calls">("channel") + const [showFilterDialog, setShowFilterDialog] = useState(false) + const [showDateDialog, setShowDateDialog] = useState(false) + const [showExportDialog, setShowExportDialog] = useState(false) + + const [selectedRole, setSelectedRole] = useState(null) + const [rolePermissions, setRolePermissions] = useState<{ [key: string]: string[] }>({ + "billing-admin": ["overview", "channels", "billing"], + "operations-admin": ["overview", "channels", "resources", "monitoring"], + "super-admin": [ + "overview", + "channels", + "resources", + "monitoring", + "billing", + "provider-backend", + "channel-backend", + "settings", + ], + }) + + const [showAddAdminDialog, setShowAddAdminDialog] = useState(false) + const [newAdminForm, setNewAdminForm] = useState({ + name: "", + email: "", + password: "", + role: "billing-admin", + }) + + const [providerApprovals, setProviderApprovals] = useState([ + { + id: 1, + channelName: "Enterprise Solutions Inc", + providerName: "Google AI", + expectedModels: "gemini-pro, palm-2", + reason: "Need Gemini Pro for advanced natural language processing tasks", + status: "pending", + submittedAt: "2024-01-15 14:30:22", + }, + { + id: 2, + channelName: "Cloud Partners Asia", + providerName: "Azure OpenAI", + expectedModels: "gpt-4, gpt-35-turbo", + reason: "Regional compliance requirements for Azure infrastructure", + status: "pending", + submittedAt: "2024-01-14 09:15:45", + }, + ]) + const [selectedApproval, setSelectedApproval] = useState(null) + const [isApprovalDialogOpen, setIsApprovalDialogOpen] = useState(false) + + // Agent申请审批相关状态 + const [agentApprovals, setAgentApprovals] = useState([ + { + id: 1, + channelName: "Enterprise Solutions Inc", + agentType: "Weather Query Agent", + requestedQuantity: 50, + reason: "Need weather agents for IoT monitoring platform", + status: "pending", + submittedAt: "2024-01-15 16:20:10", + }, + { + id: 2, + channelName: "Cloud Partners Asia", + agentType: "Data Analysis Agent", + requestedQuantity: 30, + reason: "Enterprise data analytics expansion", + status: "pending", + submittedAt: "2024-01-14 11:45:33", + }, + ]) + const [selectedAgentApproval, setSelectedAgentApproval] = useState(null) + const [isAgentApprovalDialogOpen, setIsAgentApprovalDialogOpen] = useState(false) + + // Simplified t function for demonstration + const t = (zh: string, en: string) => (language === "zh" ? zh : en) + + const translations = { + en: { + title: "Super Admin Console", + subtitle: "Platform Control Center", + overview: "Overview", + channels: "Channels", + resources: "Resources", + monitoring: "Monitoring", + billing: "Billing", + settings: "Settings", + goodsProviders: "Goods Providers", // renamed from separate model/data providers + logout: "Logout", + totalTenants: "Total Tenants", + totalChannels: "Total Channels", + activeTenants: "Active Tenants", + totalRevenue: "Total Revenue", + systemHealth: "System Health", + cpuUsage: "CPU Usage", + memoryUsage: "Memory Usage", + storageUsage: "Storage Usage", + apiRequests: "API Requests/min", + recentTenants: "Recent Tenants", + tenantName: "Tenant Name", + status: "Status", + plan: "Plan", + usage: "Usage", + actions: "Actions", + search: "Search tenants...", + searchChannels: "Search channels...", + active: "Active", + enterprise: "Enterprise", + professional: "Professional", + starter: "Starter", + viewDetails: "View Details", + suspend: "Suspend", + systemMetrics: "System Metrics", + healthy: "Healthy", + addChannel: "Add Channel", + channelName: "Channel Name", + channelManagement: "Channel Management", + channelDescription: "Manage distribution channels and their tenant portfolios", + tenantCount: "Tenants", + commission: "Commission Rate", + contactPerson: "Contact Person", + edit: "Edit", + delete: "Delete", + createChannel: "Create New Channel", + cancel: "Cancel", + create: "Create", + channelNamePlaceholder: "Enter channel name", + contactEmail: "Contact Email", + contactPhone: "Contact Phone", + commissionRate: "Commission Rate (%)", + assignSubscriptionLevel: "Assign Subscription Level", + selectedTenantPlaceholder: "Select a tenant", + agentHealthMonitoring: "Agent Health Monitoring", + agentHealthDesc: "Monitor performance and health status of all platform agents", + agentStatus: "Status", + cpuUsageLabel: "CPU Usage", + memoryUsageLabel: "Memory Usage", + responseTime: "Response Time", + requestCount: "Request Count", + errorRate: "Error Rate", + uptime: "Uptime", + lastActive: "Last Active", + ms: "ms", + requests: "requests", + warning: "Warning", + critical: "Critical", + // Added translations for role management + userRoleSettings: "User Role Settings", + billingAdministrator: "Billing Administrator", + responsibleForBilling: "Responsible for billing, invoices, and financial management", + operationsAdministrator: "Operations Administrator", + responsibleForChannels: "Responsible for channels, resources, and monitoring", + superAdministrator: "Super Administrator", + fullAccess: "Full access to all platform features", + permissions: "permissions", + tabAccessPermissions: "Tab Access Permissions", + selectTabs: "Select which tabs this role can access", + providerBackend: "Provider Backend", + channelBackend: "Channel Backend", + savePermissions: "Save Permissions", + // Added translations for add admin dialog + addAdministrator: "Add Administrator", + administratorName: "Administrator Name", + enterAdministratorName: "Enter administrator name", + email: "Email", + enterEmailAddress: "Enter email address", + password: "Password", + setLoginPassword: "Set login password", + role: "Role", + billingAdmin: "Billing Administrator", + operationsAdmin: "Operations Administrator", + superAdmin: "Super Administrator", + createAdministrator: "Create Administrator", + // ADDED translations for billing + billingManagement: "Billing Management", + dateQuery: "Date Query", + filter: "Filter", + export: "Export", + channelBillingDetails: "Channel Billing Details", + channelName: "Channel Name", + callCount: "Call Count", + totalEU: "Total EU", + channelTotal: "Channel Total", + billingPeriod: "Billing Period", + tenantBillingDetails: "Tenant Billing Details", + tenantName: "Tenant Name", + channel: "Channel", + userTotal: "User Total", + averageSpending: "Average Spending", + callRecordsDetail: "Call Records Detail", + callID: "Call ID", + tenant: "Tenant", + callTime: "Call Time", + durationSeconds: "Duration(s)", + singleCallCost: "Single Call Cost", + timestamp: "Timestamp", + EUCalculationRule: "EU Calculation: 1 EU = 10 seconds call time", + // ADDED translations for filter dialog + filterOptions: "Filter Options", + customerName: "Customer Name", + minCalls: "Min Calls", + maxCalls: "Max Calls", + applyFilter: "Apply Filter", + // ADDED translations for date dialog + dateTimeRange: "Date & Time Range", + startDate: "Start Date & Time (YYYY-MM-DD HH:MM)", + endDate: "End Date & Time (YYYY-MM-DD HH:MM)", + query: "Query", + // ADDED translations for export dialog + exportFormat: "Export Format", + selectExportFormat: "Select export file format", + }, + zh: { + title: "超级管理员控制台", + subtitle: "平台控制中心", + overview: "概览", + channels: "渠道管理", + resources: "资源管理", + monitoring: "监控", + billing: "计费", + settings: "设置", + goodsProviders: "货源供应商", // renamed from separate model/data providers + logout: "退出", + totalTenants: "总租户数", + totalChannels: "总渠道数", + activeTenants: "活跃租户", + totalRevenue: "总收入", + systemHealth: "系统健康", + cpuUsage: "CPU 使用率", + memoryUsage: "内存使用率", + storageUsage: "存储使用率", + apiRequests: "API 请求/分钟", + recentTenants: "最近租户", + tenantName: "租户名称", + status: "状态", + plan: "方案", + usage: "使用量", + actions: "操作", + search: "搜索租户...", + searchChannels: "搜索渠道...", + active: "活跃", + enterprise: "企业版", + professional: "专业版", + starter: "入门版", + viewDetails: "查看详情", + suspend: "暂停", + systemMetrics: "系统指标", + healthy: "健康", + addChannel: "添加渠道", + channelName: "渠道名称", + channelManagement: "渠道管理", + channelDescription: "管理分销渠道及其租户组合", + tenantCount: "租户数", + commission: "佣金比例", + contactPerson: "联系人", + edit: "编辑", + delete: "删除", + createChannel: "创建新渠道", + cancel: "取消", + create: "创建", + channelNamePlaceholder: "输入渠道名称", + contactEmail: "联系邮箱", + contactPhone: "联系电话", + commissionRate: "佣金比例 (%)", + assignSubscriptionLevel: "分配订阅级别", + selectedTenantPlaceholder: "选择一个租户", + agentHealthMonitoring: "Agent健康监控", + agentHealthDesc: "监控所有平台Agent的性能和健康状态", + agentStatus: "状态", + cpuUsageLabel: "CPU使用率", + memoryUsageLabel: "内存使用率", + responseTime: "响应时间", + requestCount: "请求数量", + errorRate: "错误率", + uptime: "运行时间", + lastActive: "最后活跃", + warning: "警告", + critical: "严重", + // Added translations for role management + userRoleSettings: "用户身份设置", + billingAdministrator: "计费管理员", + responsibleForBilling: "负责平台计费、账单和财务管理", + operationsAdministrator: "运营管理员", + responsibleForChannels: "负责渠道、资源和系统监控管理", + superAdministrator: "超级管理员", + fullAccess: "拥有所有权限,管理整个平台", + permissions: "个权限", + tabAccessPermissions: "标签页访问权限", + selectTabs: "选择该角色可以访问的标签页", + providerBackend: "供应商管理中心后台", + channelBackend: "渠道管理中心后台", + savePermissions: "保存权限配置", + // Added translations for add admin dialog + addAdministrator: "添加管理员", + administratorName: "管理员名称", + enterAdministratorName: "输入管理员名称", + email: "邮箱", + enterEmailAddress: "输入邮箱地址", + password: "密码", + setLoginPassword: "设置登录密码", + role: "角色", + billingAdmin: "计费管理员", + operationsAdmin: "运营管理员", + superAdmin: "超级管理员", + createAdministrator: "创建管理员", + // ADDED translations for billing + billingManagement: "计费管理", + dateQuery: "时间查询", + filter: "筛选", + export: "导出", + channelBillingDetails: "渠道计费详情", + channelName: "渠道名称", + callCount: "调用次数", + totalEU: "总EU", + channelTotal: "渠道总价", + billingPeriod: "计费周期", + tenantBillingDetails: "租户计费详情", + tenantName: "租户名称", + channel: "渠道", + userTotal: "用户总价", + averageSpending: "平均消费", + callRecordsDetail: "调用记录明细", + callID: "调用ID", + tenant: "租户", + callTime: "调用时间", + durationSeconds: "时长(秒)", + singleCallCost: "单次调用总价", + timestamp: "时间戳", + EUCalculationRule: "EU计算:1 EU = 10秒调用时间", + // ADDED translations for filter dialog + filterOptions: "筛选选项", + customerName: "客户名称", + minCalls: "最小调用次数", + maxCalls: "最大调用次数", + applyFilter: "应用筛选", + // ADDED translations for date dialog + dateTimeRange: "时间范围", + startDate: "开始时间 (年-月-日 时:分)", + endDate: "结束时间 (年-月-日 时:分)", + query: "查询", + // ADDED translations for export dialog + exportFormat: "导出格式", + selectExportFormat: "选择导出文件格式", + }, + } + + const text = translations[language] + + const agents = [ + { + id: "weather-agent", + name: "Weather Query Agent", + nameCn: "天气查询代理", + status: "healthy", + cpuUsage: 45, + memoryUsage: 62, + responseTime: 120, + requestCount: 1520, + errorRate: 0.2, + uptime: "99.8%", + lastActive: "2 mins ago", + }, + { + id: "data-agent", + name: "Data Analysis Agent", + nameCn: "数据分析代理", + status: "healthy", + cpuUsage: 68, + memoryUsage: 75, + responseTime: 250, + requestCount: 890, + errorRate: 0.5, + uptime: "99.5%", + lastActive: "1 min ago", + }, + { + id: "doc-agent", + name: "Document Processing Agent", + nameCn: "文档处理代理", + status: "warning", + cpuUsage: 82, + memoryUsage: 88, + responseTime: 450, + requestCount: 650, + errorRate: 1.2, + uptime: "98.9%", + lastActive: "30 secs ago", + }, + { + id: "email-agent", + name: "Email Management Agent", + nameCn: "邮件管理代理", + status: "healthy", + cpuUsage: 35, + memoryUsage: 48, + responseTime: 95, + requestCount: 2100, + errorRate: 0.1, + uptime: "99.9%", + lastActive: "5 mins ago", + }, + { + id: "api-agent", + name: "API Integration Agent", + nameCn: "API集成代理", + status: "critical", + cpuUsage: 95, + memoryUsage: 92, + responseTime: 850, + requestCount: 320, + errorRate: 3.5, + uptime: "96.2%", + lastActive: "10 mins ago", + }, + { + id: "db-agent", + name: "Database Operations Agent", + nameCn: "数据库操作代理", + status: "healthy", + cpuUsage: 52, + memoryUsage: 65, + responseTime: 180, + requestCount: 1100, + errorRate: 0.3, + uptime: "99.7%", + lastActive: "3 mins ago", + }, + ] + + useEffect(() => { + const token = localStorage.getItem("admin_token") + if (!token) { + router.push("/admin/login") + } + }, [router]) + + const handleLogout = () => { + localStorage.removeItem("admin_token") + router.push("/admin/login") + } + + const stats = [ + { + title: text.totalChannels, + value: "32", + change: "+4 this month", + icon: Building2, + trend: "up", + }, + { + title: text.totalTenants, + value: "247", + change: "+12.5%", + icon: Users, + trend: "up", + }, + { + title: text.activeTenants, + value: "189", + change: "+8.2%", + icon: Activity, + trend: "up", + }, + { + title: text.totalRevenue, + value: "$124.5K", + change: "+23.1%", + icon: DollarSign, + trend: "up", + }, + ] + + const systemMetrics = [ + { label: text.cpuUsage, value: 45, max: 100, color: "bg-blue-500" }, + { label: text.memoryUsage, value: 62, max: 100, color: "bg-green-500" }, + { label: text.storageUsage, value: 78, max: 100, color: "bg-yellow-500" }, + { label: text.apiRequests, value: 2847, max: 5000, color: "bg-purple-500" }, + ] + + const channels = [ + { + id: 1, + name: "Enterprise Solutions Inc", + tenantCount: 45, + revenue: "$45.2K", + commission: 15, + status: "active", + contact: "John Smith", + email: "john@enterprise-solutions.com", + }, + { + id: 2, + name: "Cloud Partners Asia", + tenantCount: 38, + revenue: "$38.9K", + commission: 12, + status: "active", + contact: "Li Wei", + email: "liwei@cloudpartners.cn", + }, + { + id: 3, + name: "Tech Distribution EU", + tenantCount: 52, + revenue: "$52.4K", + commission: 18, + status: "active", + contact: "Maria Garcia", + email: "maria@techdist.eu", + }, + { + id: 4, + name: "Digital Resellers Network", + tenantCount: 28, + revenue: "$28.1K", + commission: 10, + status: "active", + contact: "David Johnson", + email: "david@digitalresellers.com", + }, + { + id: 5, + name: "Global Systems Partners", + tenantCount: 34, + revenue: "$34.7K", + commission: 14, + status: "active", + contact: "Sarah Chen", + email: "sarah@globalsystems.com", + }, + { + id: 6, + name: "Innovation Hub Americas", + tenantCount: 22, + revenue: "$22.3K", + commission: 11, + status: "inactive", + contact: "Michael Brown", + email: "michael@innovationhub.us", + }, + ] + + const tenants = [ + { id: 1, name: "Acme Corp", status: "active", plan: "enterprise", usage: 87, revenue: "$2,450" }, + { id: 2, name: "TechStart Inc", status: "active", plan: "professional", usage: 64, revenue: "$890" }, + { id: 3, name: "Global Systems", status: "active", plan: "enterprise", usage: 92, revenue: "$3,200" }, + { id: 4, name: "Innovation Labs", status: "active", plan: "starter", usage: 34, revenue: "$290" }, + { id: 5, name: "DataFlow Co", status: "active", plan: "professional", usage: 71, revenue: "$1,100" }, + ] + + // available models and data sources for configuration + const availableModels = [ + { id: "gpt-4", name: "GPT-4", provider: "OpenAI" }, + { id: "gpt-3.5", name: "GPT-3.5 Turbo", provider: "OpenAI" }, + { id: "claude-3", name: "Claude 3", provider: "Anthropic" }, + { id: "claude-2", name: "Claude 2", provider: "Anthropic" }, + { id: "gemini-pro", name: "Gemini Pro", provider: "Google" }, + { id: "llama-2", name: "Llama 2", provider: "Meta" }, + ] + + const availableDataSources = [ + { id: "rapidapi", name: "RapidAPI", type: "API Hub" }, + { id: "apihub", name: "API Hub", type: "Data Platform" }, + { id: "kaggle", name: "Kaggle Datasets", type: "Data Portal" }, + { id: "huggingface", name: "Hugging Face", type: "ML Data" }, + ] + + const availableAgents = [ + { id: "weather-agent", name: "Weather Query Agent", type: "Weather" }, + { id: "data-analysis-agent", name: "Data Analysis Agent", type: "Analytics" }, + { id: "doc-processing-agent", name: "Document Processing Agent", type: "Document" }, + { id: "email-mgmt-agent", name: "Email Management Agent", type: "Email" }, + { id: "api-integration-agent", name: "API Integration Agent", type: "Integration" }, + { id: "db-operations-agent", name: "Database Operations Agent", type: "Database" }, + ] + + // handlers for goods configuration + const handleConfigGoods = (channel: (typeof channels)[0]) => { + setSelectedChannel(channel) + // setIsConfigGoodsOpen(true) // REMOVED + setIsResourceManagementOpen(true) // ADDED + } + + // const handleSaveGoodsConfig = () => { // REMOVED + // // Handle save logic + // console.log("Saving config for:", selectedChannel?.name) + // console.log("Selected models:", selectedModels) + // console.log("Selected data sources:", selectedDataSources) + // console.log("Monthly quota:", monthlyQuota) + // console.log("Monthly budget:", monthlyBudget) + + // setIsConfigGoodsOpen(false) + // setSelectedModels([]) + // setSelectedDataSources([]) + // setMonthlyQuota("") + // setMonthlyBudget("") + // } + + const handleEditCommission = (channel: (typeof channels)[0]) => { + setSelectedChannel(channel) + setCommissionRate(channel.commission.toString()) + setIsCommissionDialogOpen(true) + } + + const handleSaveCommission = () => { + console.log("Saving commission for:", selectedChannel?.name) + console.log("New commission rate:", commissionRate) + setIsCommissionDialogOpen(false) + setCommissionRate("") + } + + const handleAddProvider = (type: "model" | "data") => { + setProviderType(type) + setProviderForm({ + name: "", + url: "", + apiKey: "", + models: "", + rpm: "", + tpm: "", + }) + setIsAddProviderOpen(true) + } + + const handleConfigProvider = (provider: any, type: "model" | "data") => { + setProviderType(type) + setSelectedProvider(provider) + setProviderForm({ + name: provider.name, + url: provider.url || "", + apiKey: "••••••••", + models: provider.models?.join(", ") || "", + rpm: provider.rpm?.toString() || "", + tpm: provider.tpm?.toString() || "", + }) + setIsConfigProviderOpen(true) + } + + const handleSaveProvider = () => { + console.log("Saving provider:", providerForm) + setIsAddProviderOpen(false) + setIsConfigProviderOpen(false) + setProviderForm({ + name: "", + url: "", + apiKey: "", + models: "", + rpm: "", + tpm: "", + }) + } + + // const handleAgentAllocation = (channel: (typeof channels)[0]) => { // REMOVED + // setSelectedChannel(channel) + // setSelectedAgents([]) + // setAgentQuantities({}) + // setIsAgentAllocationOpen(true) + // } + + // const handleSaveAgentAllocation = () => { // REMOVED + // console.log("Saving agent allocation for:", selectedChannel?.name) + // console.log("Selected agents:", selectedAgents) + // console.log("Agent quantities:", agentQuantities) + // setIsAgentAllocationOpen(false) + // setSelectedAgents([]) + // setAgentQuantities({}) + // } + + // ADDED: Unified resource management handler + const handleResourceManagement = (channel: (typeof channels)[0]) => { + setSelectedChannel(channel) + setSelectedModels([]) + setSelectedAgents([]) + setAgentQuantities({}) + // setMonthlyQuota("") // REMOVED + setCreditLimit("") + setCustomAgentCpu("2") + setCustomAgentMemory("4") + setIsResourceManagementOpen(true) + } + + // ADDED: Unified resource management save handler + const handleSaveResourceManagement = () => { + console.log("Saving resource management for:", selectedChannel?.name) + console.log("Selected models:", selectedModels) + console.log("Selected agents:", selectedAgents) + console.log("Agent quantities:", agentQuantities) + // console.log("Monthly quota:", monthlyQuota) // REMOVED + console.log("Credit limit:", creditLimit) + console.log("Custom Agent CPU:", customAgentCpu) + console.log("Custom Agent Memory:", customAgentMemory) + + setIsResourceManagementOpen(false) + setSelectedModels([]) + setSelectedAgents([]) + setAgentQuantities({}) + // setMonthlyQuota("") // REMOVED + setCreditLimit("") + } + + const subscriptionLevels = [ + { + id: "free", + name: language === "zh" ? "免费版" : "Free", + description: language === "zh" ? "基础功能,有限额度" : "Basic features, limited quota", + }, + { + id: "pro", + name: language === "zh" ? "专业版" : "Pro", + description: language === "zh" ? "高级功能,标准额度" : "Advanced features, standard quota", + }, + { + id: "enterprise", + name: language === "zh" ? "企业版" : "Enterprise", + description: language === "zh" ? "完整功能,无限额度" : "Full features, unlimited quota", + }, + ] + + return ( +
+ {/* Header */} +
+
+
+
+ +
+

{text.title}

+

{text.subtitle}

+
+
+
+ +
+ + + +
+
+ + {/* Navigation tabs */} +
+ {[ + { id: "overview", label: text.overview, icon: BarChart3 }, + { id: "channels", label: text.channels, icon: Building2 }, + { id: "resources", label: text.resources, icon: Database }, + { id: "monitoring", label: text.monitoring, icon: Activity }, + { id: "billing", label: text.billing, icon: DollarSign }, + { id: "settings", label: text.settings, icon: Settings }, + ].map((tab) => ( + + ))} +
+
+ + {/* Main content */} +
+ {activeTab === "overview" && ( + <> + {/* Stats grid */} +
+ {stats.map((stat, index) => ( + +
+
+

{stat.title}

+

{stat.value}

+
+ + {stat.change} +
+
+
+ +
+
+
+ ))} +
+ +
+ {/* System Metrics */} + +

{text.systemMetrics}

+
+ {systemMetrics.map((metric, index) => ( +
+
+ {metric.label} + + {typeof metric.value === "number" && metric.value < 200 ? `${metric.value}%` : metric.value} + +
+
+
+
+
+ ))} +
+ + + {/* Recent Tenants */} + +
+

{text.recentTenants}

+
+ + +
+
+ +
+ {tenants.map((tenant) => ( +
+
+
+ +
+
+

{tenant.name}

+

+ {text[tenant.plan as keyof typeof text] || tenant.plan} · {tenant.revenue}/mo +

+
+
+
+
+ {text.active} +
+

+ {text.usage}: {tenant.usage}% +

+
+
+ + + + + + + {text.viewDetails} + Edit + + {text.suspend} + + +
+ ))} +
+ +
+ + {/* System alerts */} + +
+
+ +
+
+

+ {language === "zh" ? "系统警告" : "System Alert"} +

+

+ {language === "zh" + ? "数据库存储使用率已达 78%。建议尽快扩容以避免性能下降。" + : "Database storage usage has reached 78%. Consider scaling up to avoid performance degradation."} +

+
+ + + 2 {language === "zh" ? "小时前" : "hours ago"} + +
+
+ +
+
+ + )} + + {activeTab === "channels" && ( +
+
+
+

{text.channelManagement}

+

{text.channelDescription}

+
+ + + + + + + {text.createChannel} + + {language === "zh" + ? "创建新的分销渠道账户,渠道可以管理自己的租户组合" + : "Create a new distribution channel account that can manage their own tenant portfolio"} + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + +
+
+
+ +
+ + +
+ +
+ {channels.map((channel) => ( + +
+
+
+ +
+
+
+

{channel.name}

+
+ {language === "zh" ? (channel.status === "active" ? "活跃" : "停用") : channel.status} +
+
+
+

+ {text.contactPerson}: {channel.contact} +

+

{channel.email}

+
+
+
+ +
+
+

{channel.tenantCount}

+

{text.tenantCount}

+
+
+

{channel.revenue}

+

{language === "zh" ? "月收入" : "Monthly"}

+
+
+

{channel.commission}%

+

{text.commission}

+
+
+ + + + + + + + + {text.viewDetails} + + + + {text.edit} + + + + {language === "zh" ? "查看租户" : "View Tenants"} + + handleEditCommission(channel)}> + + {language === "zh" ? "修改佣金" : "Edit Commission"} + + + + + {language === "zh" ? "删除渠道" : "Delete Channel"} + + {/* handleConfigGoods(channel)}> // REMOVED */} + {/* */} + {/* {language === "zh" ? "货源配置" : "Goods Configuration"} */} + {/* */} + {/* handleAgentAllocation(channel)}> // REMOVED */} + {/* */} + {/* {language === "zh" ? "Agent分配" : "Agent Allocation"} */} + {/* */} + {/* CHANGED: Combine goods configuration and agent allocation into resource management */} + handleResourceManagement(channel)}> + + {language === "zh" ? "资源管理" : "Resource Management"} + + + +
+
+ ))} +
+ + {/* Commission Edit Dialog */} + + + + {language === "zh" ? "修改佣金比例" : "Edit Commission Rate"} + + {language === "zh" + ? `为渠道 "${selectedChannel?.name}" 设置新的佣金比例` + : `Set a new commission rate for channel "${selectedChannel?.name}"`} + + +
+
+ + setCommissionRate(e.target.value)} + className="bg-background border-border" + /> +

+ {language === "zh" + ? "当前佣金比例: " + selectedChannel?.commission + "%" + : "Current rate: " + selectedChannel?.commission + "%"} +

+
+
+ + + + +
+
+ + {/* CHANGED: Combined Goods Configuration and Agent Allocation into a single Resource Management Dialog */} + {/* // REMOVED */} + + + + {language === "zh" ? "资源管理" : "Resource Management"} + + {language === "zh" + ? `为渠道 "${selectedChannel?.name}" 配置可用的模型、Agent和配额` + : `Configure available models, agents and quota for channel "${selectedChannel?.name}"`} + + + +
+ {/* Models Selection */} +
+ +
+ {availableModels.map((model) => ( +
+ { + if (e.target.checked) { + setSelectedModels([...selectedModels, model.id]) + } else { + setSelectedModels(selectedModels.filter((m) => m !== model.id)) + } + }} + className="w-4 h-4 rounded border-border" + /> + +
+ ))} +
+
+ + {/* Agent Selection with Quantities */} +
+ +

+ {language === "zh" + ? "选择该渠道可以分配给租户的Agent并设置可用数量" + : "Select agents that this channel can assign to tenants and set available quantities"} +

+
+ {availableAgents.map((agent) => ( +
+
+
{ + if (selectedAgents.includes(agent.id)) { + setSelectedAgents(selectedAgents.filter((a) => a !== agent.id)) + const newQuantities = { ...agentQuantities } + delete newQuantities[agent.id] + setAgentQuantities(newQuantities) + } else { + setSelectedAgents([...selectedAgents, agent.id]) + setAgentQuantities({ ...agentQuantities, [agent.id]: 1 }) + } + }} + > +
+ +
+
+

{agent.name}

+

{agent.type}

+
+
+ + {selectedAgents.includes(agent.id) && ( +
+ + { + const value = Math.max(1, Math.min(100, Number.parseInt(e.target.value) || 1)) + setAgentQuantities({ ...agentQuantities, [agent.id]: value }) + }} + className="w-20 h-9" + onClick={(e) => e.stopPropagation()} + /> +
+ )} + + {}} + className="w-5 h-5 rounded border-border" + /> +
+
+ ))} +
+
+ +
+ +

+ {language === "zh" + ? "为该渠道的租户创建的自定义Agent配置默认CPU和内存资源" + : "Configure default CPU and memory resources for custom agents created by this channel's tenants"} +

+
+
+ + setCustomAgentCpu(e.target.value)} + className="bg-background border-border" + /> +

+ {language === "zh" ? "推荐范围: 0.5-16核" : "Recommended: 0.5-16 cores"} +

+
+
+ + setCustomAgentMemory(e.target.value)} + className="bg-background border-border" + /> +

+ {language === "zh" ? "推荐范围: 0.5-64GB" : "Recommended: 0.5-64GB"} +

+
+
+
+

+ {language === "zh" + ? "此配置将应用于该渠道的租户创建的所有自定义Agent,不影响平台原生Agent。" + : "This configuration will apply to all custom agents created by this channel's tenants, not affecting platform native agents."} +

+
+
+ + {/* REMOVED monthly quota field */} +
+ + setCreditLimit(e.target.value)} + className="bg-background border-border" + /> +

+ {language === "zh" ? "渠道可用授信上限 (USD)" : "Channel credit limit (USD)"} +

+
+ +
+

+ {language === "zh" ? "资源配置摘要" : "Resource Configuration Summary"} +

+
+

+ {language === "zh" ? "已选择模型" : "Models selected"}: {selectedModels.length} +

+

+ {language === "zh" ? "已选择Agent" : "Agents selected"}: {selectedAgents.length} +

+ {selectedAgents.length > 0 && ( +

+ {language === "zh" ? "总Agent数量: " : "Total agents: "} + {Object.values(agentQuantities).reduce((sum, qty) => sum + qty, 0)} +

+ )} +

+ {language === "zh" ? "自定义Agent CPU: " : "Custom Agent CPU: "} + {customAgentCpu} {language === "zh" ? "核" : "cores"} +

+

+ {language === "zh" ? "自定义Agent 内存: " : "Custom Agent Memory: "} + {customAgentMemory} GB +

+ {/* REMOVED monthly quota from summary */} +

+ {language === "zh" ? "渠道授信额度" : "Channel Credit Limit"}: ${creditLimit || "0"} +

+
+
+
+ + + + + +
+
+ +
+

+ {language === "zh" ? "渠道申请审批" : "Channel Application Approvals"} +

+ +
+ + + + + + + + + + + + + {providerApprovals.map((approval) => ( + + + + + + + + + ))} + +
+ {language === "zh" ? "渠道" : "Channel"} + + {language === "zh" ? "供应商" : "Provider"} + + {language === "zh" ? "期望模型" : "Models"} + + {language === "zh" ? "提交时间" : "Submitted"} + + {language === "zh" ? "状态" : "Status"} + + {language === "zh" ? "操作" : "Actions"} +
{approval.channelName}{approval.providerName}{approval.expectedModels}{approval.submittedAt} + + {language === "zh" ? "待审批" : "Pending"} + + + +
+
+
+
+ + {/* Agent申请审批 */} +
+

+ {language === "zh" ? "Agent申请审批" : "Agent Application Approvals"} +

+ +
+ + + + + + + + + + + + + {agentApprovals.map((approval) => ( + + + + + + + + + ))} + +
+ {language === "zh" ? "渠道" : "Channel"} + + {language === "zh" ? "Agent类型" : "Agent Type"} + + {language === "zh" ? "申请数量" : "Quantity"} + + {language === "zh" ? "提交时间" : "Submitted"} + + {language === "zh" ? "状态" : "Status"} + + {language === "zh" ? "操作" : "Actions"} +
{approval.channelName}{approval.agentType}{approval.requestedQuantity}{approval.submittedAt} + + {language === "zh" ? "待审批" : "Pending"} + + + +
+
+
+
+ + + + + {language === "zh" ? "审批Agent申请" : "Review Agent Application"} + + {language === "zh" + ? `审批渠道 "${selectedAgentApproval?.channelName}" 的Agent申请` + : `Review agent application from channel "${selectedAgentApproval?.channelName}"`} + + +
+
+
+ +

{selectedAgentApproval?.channelName}

+
+
+ +

{selectedAgentApproval?.submittedAt}

+
+
+
+ +

{selectedAgentApproval?.agentType}

+
+
+ +

{selectedAgentApproval?.requestedQuantity}

+
+
+ +

{selectedAgentApproval?.reason}

+
+
+ + + + +
+
+
+ )} + + {activeTab === "resources" && ( +
+
+
+
+

+ {language === "zh" ? "Agent计算资源分配" : "Agent Compute Resource Allocation"} +

+

+ {language === "zh" + ? "为每个Agent单独配置CPU和内存资源" + : "Configure CPU and memory resources for each agent individually"} +

+
+
+ + {/* Agent Resource Cards */} +
+ {[ + { + id: "agent-001", + name: "Weather Query Agent", + status: "active", + cpu: 2, + memory: 4, + usage: { cpu: 45, memory: 60 }, + }, + { + id: "agent-002", + name: "Data Analysis Agent", + status: "active", + cpu: 4, + memory: 8, + usage: { cpu: 78, memory: 85 }, + }, + { + id: "agent-003", + name: "Document Processing Agent", + status: "active", + cpu: 2, + memory: 4, + usage: { cpu: 32, memory: 48 }, + }, + { + id: "agent-004", + name: "Email Management Agent", + status: "idle", + cpu: 1, + memory: 2, + usage: { cpu: 5, memory: 15 }, + }, + { + id: "agent-005", + name: "API Integration Agent", + status: "active", + cpu: 3, + memory: 6, + usage: { cpu: 65, memory: 72 }, + }, + { + id: "agent-006", + name: "Database Operations Agent", + status: "active", + cpu: 4, + memory: 8, + usage: { cpu: 82, memory: 90 }, + }, + ].map((agent) => ( + { + setSelectedAgent(agent) + setComputeConfigOpen(true) + }} + > +
+
+

{agent.name}

+

{agent.id}

+
+
+ {agent.status === "active" + ? language === "zh" + ? "活跃" + : "Active" + : language === "zh" + ? "空闲" + : "Idle"} +
+
+
+
+
+ + CPU: +
+ + {agent.cpu} {language === "zh" ? "核" : "Cores"} + +
+
+
+ + {language === "zh" ? "内存" : "Memory"}: +
+ {agent.memory} GB +
+
+
+
+ CPU {language === "zh" ? "使用率" : "Usage"} + {agent.usage.cpu}% +
+
+
+
+
+ + {language === "zh" ? "内存使用率" : "Memory Usage"} + + {agent.usage.memory}% +
+
+
+
+
+
+
+ + + ))} +
+
+ + {/* Agent Resource Configuration Dialog */} + + + + + {language === "zh" ? "Agent资源配置" : "Agent Resource Configuration"} + + +
+ {selectedAgent && ( + <> +
+

{selectedAgent.name}

+

{selectedAgent.id}

+
+
+
+ + +

+ {language === "zh" ? "推荐范围: 1-16核" : "Recommended range: 1-16 cores"} +

+
+
+ + +

+ {language === "zh" ? "推荐范围: 1-32GB" : "Recommended range: 1-32GB"} +

+
+
+ + +

+ {language === "zh" ? "推荐范围: 1-100个" : "Recommended range: 1-100 instances"} +

+
+
+

+ {language === "zh" ? "当前使用率: " : "Current Usage: "} + CPU {selectedAgent.usage.cpu}% · {language === "zh" ? "内存" : "Memory"}{" "} + {selectedAgent.usage.memory}% +

+
+
+ + )} +
+ + +
+
+
+
+ +
+
+

+ {language === "zh" ? "货源供应商管理" : "Goods Source Management"} +

+

+ {language === "zh" ? "管理平台的模型供应商配置" : "Manage platform model provider configurations"} +

+
+ +
+ + {/* Model Providers Grid */} +
+ {[ + { + name: "OpenAI", + type: "Official", + models: ["gpt-4", "gpt-3.5-turbo"], + rpm: 10000, + tpm: 500000, + status: "active", + }, + { + name: "Anthropic", + type: "Official", + models: ["claude-3-opus", "claude-2"], + rpm: 5000, + tpm: 300000, + status: "active", + }, + { + name: "Google AI", + type: "GCP", + models: ["gemini-pro", "palm-2"], + rpm: 8000, + tpm: 400000, + status: "active", + }, + { + name: "Azure OpenAI", + type: "Azure", + models: ["gpt-4", "gpt-35-turbo"], + rpm: 12000, + tpm: 600000, + status: "active", + }, + ].map((provider) => ( + +
+
+

{provider.name}

+

{provider.type}

+
+ + {provider.status === "active" + ? language === "zh" + ? "活跃" + : "Active" + : language === "zh" + ? "离线" + : "Offline"} + +
+
+
+ {language === "zh" ? "支持模型" : "Models"} + {provider.models.length} +
+
+ RPM: + {provider.rpm.toLocaleString()} +
+
+ TPM: + {provider.tpm.toLocaleString()} +
+
+
+ + +
+
+ ))} +
+
+ )} + + {activeTab === "monitoring" && ( +
+ {/* Agent health monitoring */} +
+

{text.agentHealthMonitoring}

+

{text.agentHealthDesc}

+
+ + {/* Agent health cards grid */} +
+ {agents.map((agent) => { + const statusColor = + agent.status === "healthy" + ? "text-green-500 bg-green-500/10" + : agent.status === "warning" + ? "text-yellow-500 bg-yellow-500/10" + : "text-red-500 bg-red-500/10" + + const statusText = + agent.status === "healthy" ? text.healthy : agent.status === "warning" ? text.warning : text.critical + + return ( + + {/* Agent header */} +
+
+
+ +
+
+

+ {language === "zh" ? agent.nameCn : agent.name} +

+

{agent.lastActive}

+
+
+ {statusText} +
+ + {/* Agent metrics */} +
+ {/* CPU Usage */} +
+
+ {text.cpuUsageLabel} + {agent.cpuUsage}% +
+
+
80 + ? "bg-red-500" + : agent.cpuUsage > 60 + ? "bg-yellow-500" + : "bg-green-500" + }`} + style={{ width: `${agent.cpuUsage}%` }} + /> +
+
+ + {/* Memory Usage */} +
+
+ {text.memoryUsageLabel} + {agent.memoryUsage}% +
+
+
80 + ? "bg-red-500" + : agent.memoryUsage > 60 + ? "bg-yellow-500" + : "bg-green-500" + }`} + style={{ width: `${agent.memoryUsage}%` }} + /> +
+
+ + {/* Stats grid */} +
+
+

{text.responseTime}

+

+ {agent.responseTime} + {text.ms} +

+
+
+

{text.requestCount}

+

+ {agent.requestCount.toLocaleString()} {text.requests} +

+
+
+

{text.errorRate}

+

{agent.errorRate}%

+
+
+

{text.uptime}

+

{agent.uptime}

+
+
+
+ + ) + })} +
+
+ )} + + {activeTab === "billing" && ( +
+
+

{t("计费管理", "Billing Management")}

+
+ + + +
+
+ + {/* 计费维度切换 */} +
+ + + +
+ + {/* 渠道维度计费 */} + {billingView === "channel" && ( +
+
+ +
{t("渠道总数", "Total Channels")}
+
12
+
+ +
{t("总计费额", "Total Billing")}
+
$52,840
+
+ +
{t("总EU消耗", "Total EU Consumption")}
+
528,400 EU
+
+
+ + +

+ {t("渠道计费详情", "Channel Billing Details")} +

+
+ + + + + + + + + + + + {[ + { name: "Partner A", calls: 12500, eu: 125000, total: "$12,500", period: "2024-01" }, + { name: "Partner B", calls: 8300, eu: 83000, total: "$8,300", period: "2024-01" }, + { name: "Partner C", calls: 15200, eu: 152000, total: "$15,200", period: "2024-01" }, + ].map((channel, idx) => ( + + + + + + + + ))} + +
{t("渠道名称", "Channel Name")}{t("调用次数", "Call Count")}{t("总EU", "Total EU")}{t("渠道总价", "Channel Total")}{t("计费周期", "Billing Period")}
{channel.name}{channel.calls.toLocaleString()}{channel.eu.toLocaleString()} EU{channel.total}{channel.period}
+
+
+
+ )} + + {/* 租户维度计费 */} + {billingView === "tenant" && ( +
+
+ +
{t("租户总数", "Total Tenants")}
+
156
+
+ +
{t("用户总价", "User Total")}
+
$52,840
+
+ +
{t("平均消费", "Average Spending")}
+
$339
+
+
+ + +

+ {t("租户计费详情", "Tenant Billing Details")} +

+
+ + + + + + + + + + + + + {[ + { + name: "Tech Corp", + channel: "Partner A", + calls: 3200, + eu: 32000, + total: "$3,200", + period: "2024-01", + }, + { + name: "AI Solutions", + channel: "Partner A", + calls: 2800, + eu: 28000, + total: "$2,800", + period: "2024-01", + }, + { + name: "Data Inc", + channel: "Partner B", + calls: 4100, + eu: 41000, + total: "$4,100", + period: "2024-01", + }, + { + name: "Cloud Systems", + channel: "Partner C", + calls: 5500, + eu: 55000, + total: "$5,500", + period: "2024-01", + }, + ].map((tenant, idx) => ( + + + + + + + + + ))} + +
{t("租户名称", "Tenant Name")}{t("所属渠道", "Channel")}{t("调用次数", "Call Count")}{t("总EU", "Total EU")}{t("用户总价", "User Total")}{t("计费周期", "Billing Period")}
{tenant.name}{tenant.channel}{tenant.calls.toLocaleString()}{tenant.eu.toLocaleString()} EU{tenant.total}{tenant.period}
+
+
+
+ )} + + {/* 调用记录 */} + {billingView === "calls" && ( +
+ +
+

+ {t("调用记录明细", "Call Records Detail")} +

+
+ {t("EU计算规则:1 EU = 10秒调用时间", "EU Calculation: 1 EU = 10 seconds call time")} +
+
+
+ + + + + + + + + + + + + + + {[ + { + id: "CALL-2024-001", + tenant: "Tech Corp", + channel: "Partner A", + time: "2024-01-15 14:23:45", + duration: 45, + eu: 4.5, + cost: "$0.45", + }, + { + id: "CALL-2024-002", + tenant: "AI Solutions", + channel: "Partner A", + time: "2024-01-15 14:24:12", + duration: 30, + eu: 3.0, + cost: "$0.30", + }, + { + id: "CALL-2024-003", + tenant: "Data Inc", + channel: "Partner B", + time: "2024-01-15 14:25:03", + duration: 120, + eu: 12.0, + cost: "$1.20", + }, + { + id: "CALL-2024-004", + tenant: "Cloud Systems", + channel: "Partner C", + time: "2024-01-15 14:26:31", + duration: 65, + eu: 6.5, + cost: "$0.65", + }, + { + id: "CALL-2024-005", + tenant: "Tech Corp", + channel: "Partner A", + time: "2024-01-15 14:27:15", + duration: 90, + eu: 9.0, + cost: "$0.90", + }, + ].map((call, idx) => ( + + + + + + + + + + + ))} + +
{t("调用ID", "Call ID")}{t("租户", "Tenant")}{t("渠道", "Channel")}{t("调用时间", "Call Time")}{t("时长(秒)", "Duration(s)")}{t("EU", "EU")}{t("单次调用总价", "Single Call Cost")}{t("时间戳", "Timestamp")}
{call.id}{call.tenant}{call.channel}{call.time}{call.duration}s{call.eu} EU{call.cost}{call.time}
+
+
+
+ )} + + + + {t("筛选选项", "Filter Options")} + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ + + + + {t("时间范围", "Date & Time Range")} + +
+
+ + +
+
+ + +
+ +
+
+
+ + + + + {t("导出格式", "Export Format")} + +
+

{t("选择导出文件格式", "Select export file format")}

+
+ + + +
+
+
+
+
+ )} + + {activeTab === "settings" && ( +
+
+
+

{t("用户身份设置", "User Role Settings")}

+ +
+ +
+ {/* Billing Admin Role Card */} + setSelectedRole("billing-admin")} + > +
+ +
+

+ {t("计费管理员", "Billing Administrator")} +

+

+ {t( + "负责平台计费、账单和财务管理", + "Responsible for billing, invoices, and financial management", + )} +

+
+ + {rolePermissions["billing-admin"].length} {t("个权限", "permissions")} + +
+
+
+
+ + {/* Operations Admin Role Card */} + setSelectedRole("operations-admin")} + > +
+ +
+

+ {t("运营管理员", "Operations Administrator")} +

+

+ {t("负责渠道、资源和系统监控管理", "Responsible for channels, resources, and monitoring")} +

+
+ + {rolePermissions["operations-admin"].length} {t("个权限", "permissions")} + +
+
+
+
+ + {/* Super Admin Role Card */} + setSelectedRole("super-admin")} + > +
+ +
+

+ {t("超级管理员", "Super Administrator")} +

+

+ {t("拥有所有权限,管理整个平台", "Full access to all platform features")} +

+
+ + {rolePermissions["super-admin"].length} {t("个权限", "permissions")} + +
+
+
+
+
+ + {selectedRole && ( + +

+ {t("标签页访问权限", "Tab Access Permissions")} +

+

+ {t("选择该角色可以访问的标签页", "Select which tabs this role can access")} +

+ +
+ {[ + { id: "overview", label: t("概览", "Overview"), icon: "📊" }, + { id: "channels", label: t("渠道管理", "Channel Management"), icon: "🏢" }, + { id: "resources", label: t("资源管理", "Resource Management"), icon: "⚙️" }, + { id: "monitoring", label: t("监控", "Monitoring"), icon: "📈" }, + { id: "billing", label: t("计费", "Billing"), icon: "💰" }, + { id: "provider-backend", label: t("供应商管理中心后台", "Provider Backend"), icon: "🔧" }, + { id: "channel-backend", label: t("渠道管理中心后台", "Channel Backend"), icon: "🏪" }, + { id: "settings", label: t("设置", "Settings"), icon: "⚙️" }, + ].map((tab) => { + const hasPermission = rolePermissions[selectedRole]?.includes(tab.id) + return ( +
{ + setRolePermissions((prev) => ({ + ...prev, + [selectedRole]: hasPermission + ? prev[selectedRole].filter((p) => p !== tab.id) + : [...prev[selectedRole], tab.id], + })) + }} + > +
+ {tab.icon} + {hasPermission && } +
+

{tab.label}

+
+ ) + })} +
+ + +
+ )} +
+
+ )} +
+ + {/* Subscription level assignment dialog */} + + + + {text.assignSubscriptionLevel} + +
+

+ {language === "zh" ? "为租户选择订阅级别:" : "Select a subscription level for the tenant:"} +

+
+ {subscriptionLevels.map((level) => ( +
setSelectedSubscriptionLevel(level.id)} + className={`p-4 rounded-lg border-2 cursor-pointer transition-colors ${ + selectedSubscriptionLevel === level.id + ? "border-primary bg-primary/5" + : "border-border hover:border-primary/50" + }`} + > +
+
+

{level.name}

+

{level.description}

+
+
+ {selectedSubscriptionLevel === level.id &&
} +
+
+
+ ))} +
+
+ + +
+
+ +
+ + {/* Dialogs for Add/Config Provider */} + + + + + {language === "zh" + ? providerType === "model" + ? "添加模型供应商" + : "添加数据供应商" + : providerType === "model" + ? "Add Model Provider" + : "Add Data Provider"} + + + {language === "zh" ? "填写供应商信息以添加新的货源" : "Fill in provider information to add a new source"} + + + +
+
+ + setProviderForm({ ...providerForm, name: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setProviderForm({ ...providerForm, url: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setProviderForm({ ...providerForm, apiKey: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ + {providerType === "model" && ( + <> +
+ + setProviderForm({ ...providerForm, models: e.target.value })} + className="bg-background border-border text-foreground" + /> +

+ {language === "zh" ? "多个模型请用逗号分隔" : "Separate multiple models with commas"} +

+
+ +
+
+ + setProviderForm({ ...providerForm, rpm: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setProviderForm({ ...providerForm, tpm: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+
+ + )} +
+ + + + + +
+
+ + + + + {language === "zh" ? "配置供应商" : "Configure Provider"} + + {language === "zh" + ? `修改 "${selectedProvider?.name}" 的配置信息` + : `Modify configuration for "${selectedProvider?.name}"`} + + + +
+
+ + setProviderForm({ ...providerForm, name: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setProviderForm({ ...providerForm, url: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setProviderForm({ ...providerForm, apiKey: e.target.value })} + className="bg-background border-border text-foreground" + /> +

+ {language === "zh" ? "留空则不修改密钥" : "Leave blank to keep existing key"} +

+
+ + {providerType === "model" && ( + <> +
+ + setProviderForm({ ...providerForm, models: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+
+ + setProviderForm({ ...providerForm, rpm: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setProviderForm({ ...providerForm, tpm: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+
+ + )} +
+ + + + + +
+
+ + {/* Dialog for Add New Admin */} + + + + {t("添加新管理员", "Add New Administrator")} + + {t("创建新的管理员账户并分配角色", "Create a new administrator account and assign a role")} + + + +
+
+ + setNewAdminForm({ ...newAdminForm, name: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setNewAdminForm({ ...newAdminForm, email: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + setNewAdminForm({ ...newAdminForm, password: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+ +
+ + +

+ {newAdminForm.role === "billing-admin" && + t("负责平台计费、账单和财务管理", "Responsible for billing, invoices, and financial management")} + {newAdminForm.role === "operations-admin" && + t("负责渠道、资源和系统监控管理", "Responsible for channels, resources, and monitoring")} + {newAdminForm.role === "super-admin" && + t("拥有所有权限,管理整个平台", "Full access to all platform features")} +

+
+
+ + + + + +
+
+
+ ) +} diff --git a/app/admin/login/page.tsx b/app/admin/login/page.tsx new file mode 100644 index 0000000..89fef12 --- /dev/null +++ b/app/admin/login/page.tsx @@ -0,0 +1,156 @@ +"use client" + +import type React from "react" + +import { useState } from "react" +import { useRouter } from "next/navigation" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Card } from "@/components/ui/card" +import { Eye, EyeOff, Shield, Globe } from "lucide-react" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { useLanguage } from "@/contexts/language-context" + +export default function AdminLoginPage() { + const router = useRouter() + const { language, setLanguage, t } = useLanguage() + const [showPassword, setShowPassword] = useState(false) + const [isLoading, setIsLoading] = useState(false) + const [credentials, setCredentials] = useState({ + email: "", + password: "", + }) + + const translations = { + en: { + title: "Taiji Super Admin", + subtitle: "Platform Control Center", + email: "Admin Email", + emailPlaceholder: "admin@taiji.ai", + password: "Password", + passwordPlaceholder: "Enter admin password", + signIn: "Sign In", + signingIn: "Authenticating...", + footer: "Protected system access only", + securityNote: "All access attempts are logged and monitored", + }, + zh: { + title: "Taiji 超级管理员", + subtitle: "平台控制中心", + email: "管理员邮箱", + emailPlaceholder: "admin@taiji.ai", + password: "密码", + passwordPlaceholder: "输入管理员密码", + signIn: "登录", + signingIn: "正在验证...", + footer: "仅限授权系统访问", + securityNote: "所有访问尝试均被记录和监控", + }, + } + + const text = translations[language] + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setIsLoading(true) + + // Simulate admin authentication + await new Promise((resolve) => setTimeout(resolve, 1500)) + + // Store admin auth token + localStorage.setItem("admin_token", "admin_authenticated") + + router.push("/admin/dashboard") + } + + return ( +
+ {/* Background grid pattern */} +
+ + {/* Language selector - top right */} +
+ +
+ + +
+ {/* Logo and title */} +
+
+ +
+

{text.title}

+

{text.subtitle}

+
+ + {/* Login form */} +
+
+ + setCredentials({ ...credentials, email: e.target.value })} + required + className="bg-background border-border text-foreground" + /> +
+ +
+ +
+ setCredentials({ ...credentials, password: e.target.value })} + required + className="bg-background border-border text-foreground pr-10" + /> + +
+
+ + +
+ + {/* Footer */} +
+

{text.footer}

+

{text.securityNote}

+
+
+
+
+ ) +} diff --git a/app/agent-factory/loading.tsx b/app/agent-factory/loading.tsx new file mode 100644 index 0000000..f15322a --- /dev/null +++ b/app/agent-factory/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return null +} diff --git a/app/agent-factory/page.tsx b/app/agent-factory/page.tsx new file mode 100644 index 0000000..5d703ad --- /dev/null +++ b/app/agent-factory/page.tsx @@ -0,0 +1,458 @@ +"use client" + +import { useState, useEffect } from "react" +import { DashboardLayout } from "@/components/dashboard-layout" +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Bot, Cpu, MemoryStick, Zap } from "lucide-react" +import { useLanguage } from "@/hooks/useLanguage" +import { TaijiAPIClient } from "@/lib/api-client" +import { useToast } from "@/hooks/use-toast" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { Input } from "@/components/ui/input" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" + +export default function AgentFactoryPage() { + const { t } = useLanguage() + const { toast } = useToast() + const [selectedAgent, setSelectedAgent] = useState(null) + const [showDeployDialog, setShowDeployDialog] = useState(false) + const [loading, setLoading] = useState(true) + const [deployConfig, setDeployConfig] = useState({ + agentCount: 1, + model: "gpt-4o-mini", + serviceGateway: "", // 新增服务网关字段 + }) + const [platformAgents, setPlatformAgents] = useState([]) + const [deployedCount, setDeployedCount] = useState(0) + const [stats, setStats] = useState({ cpu: 0, memory: 0 }) + + useEffect(() => { + loadAgents() + }, []) + + const loadAgents = async () => { + try { + setLoading(true) + const result = await TaijiAPIClient.getPlatformAgents() + if (result?.success && result.data?.data) { + setPlatformAgents(result.data.data) + } + + // 加载已部署的Agent数量 + try { + const agents = await TaijiAPIClient.getAgents(0, 100) + if (Array.isArray(agents)) { + setDeployedCount(agents.filter((a: any) => a.status === "active" || a.status === "running").length) + } + } catch (error) { + console.error("Failed to load deployed agents:", error) + } + } catch (error) { + console.error("Failed to load platform agents:", error) + toast({ + title: t("加载失败", "Load Failed"), + description: t("无法加载Agent列表", "Failed to load agent list"), + variant: "destructive", + }) + } finally { + setLoading(false) + } + } + + const defaultPlatformAgents = [ + { + id: "weather-agent", + name: t("天气查询Agent", "Weather Query Agent"), + description: t("提供全球天气信息查询和预报服务", "Provide global weather information query and forecast service"), + capabilities: [ + t("实时天气", "Real-time weather"), + t("7天预报", "7-day forecast"), + t("气象警告", "Weather alerts"), + ], + status: "available", + icon: "🌤️", + }, + { + id: "data-analysis-agent", + name: t("数据分析Agent", "Data Analysis Agent"), + description: t("执行数据分析、可视化和报告生成", "Perform data analysis, visualization and report generation"), + capabilities: [ + t("数据清洗", "Data cleaning"), + t("统计分析", "Statistical analysis"), + t("图表生成", "Chart generation"), + ], + status: "available", + icon: "📊", + }, + { + id: "doc-processor-agent", + name: t("文档处理Agent", "Document Processing Agent"), + description: t("智能文档解析、提取和转换", "Intelligent document parsing, extraction and conversion"), + capabilities: [t("PDF解析", "PDF parsing"), t("文本提取", "Text extraction"), t("格式转换", "Format conversion")], + status: "available", + icon: "📄", + }, + { + id: "email-agent", + name: t("邮件管理Agent", "Email Management Agent"), + description: t("自动化邮件处理和智能回复", "Automated email processing and intelligent reply"), + capabilities: [ + t("邮件分类", "Email classification"), + t("自动回复", "Auto reply"), + t("内容总结", "Content summary"), + ], + status: "available", + icon: "📧", + }, + { + id: "api-integration-agent", + name: t("API集成Agent", "API Integration Agent"), + description: t("连接和编排第三方API服务", "Connect and orchestrate third-party API services"), + capabilities: [t("API调用", "API calls"), t("数据转换", "Data transformation"), t("错误处理", "Error handling")], + status: "available", + icon: "🔌", + }, + { + id: "database-agent", + name: t("数据库操作Agent", "Database Operations Agent"), + description: t("执行数据库查询和数据管理", "Execute database queries and data management"), + capabilities: [t("SQL查询", "SQL queries"), t("数据迁移", "Data migration"), t("备份恢复", "Backup & restore") ], + status: "available", + icon: "🗄️", + }, + ] + + const handleDeploy = (agent: any) => { + setSelectedAgent(agent) + setShowDeployDialog(true) + } + + const handleConfirmDeploy = async () => { + if (!selectedAgent || !deployConfig.serviceGateway) { + toast({ + title: t("错误", "Error"), + description: t("请选择服务网关", "Please select service gateway"), + variant: "destructive", + }) + return + } + + try { + const result = await TaijiAPIClient.deployAgent({ + agentId: selectedAgent.id, + instances: deployConfig.agentCount, + model: deployConfig.model, + gateway: deployConfig.serviceGateway as "MCP" | "A2A" | "API", + }) + + if (result?.success) { + toast({ + title: t("部署成功", "Deployment Successful"), + description: t("Agent部署已启动", "Agent deployment started"), + }) + setShowDeployDialog(false) + loadAgents() // 重新加载数据 + } else { + throw new Error(result?.message || "Deployment failed") + } + } catch (error: any) { + toast({ + title: t("部署失败", "Deployment Failed"), + description: error.message || t("无法部署Agent", "Failed to deploy agent"), + variant: "destructive", + }) + } + } + + return ( + +
+
+
+

{t("代理工厂", "Agent Factory")}

+

+ {t("平台原生Agent部署和资源管理", "Platform native agent deployment and resource management")} +

+
+
+ +
+ + + + {t("可用Agent", "Available Agents")} + + + +
+ {loading ? ( + + ) : ( + platformAgents.length || defaultPlatformAgents.length + )} +
+

{t("平台原生", "Platform native")}

+
+
+ + + + + {t("已部署Agent", "Deployed Agents")} + + + +
+ {loading ? ( + + ) : ( + deployedCount + )} +
+

{t("运行中", "Running")}

+
+
+ + + + + {t("总CPU使用", "Total CPU Usage")} + + + +
+ {loading ? ( + + ) : ( + stats.cpu || 0 + )} +
+

{t("核心", "Cores")}

+
+
+ + + + + {t("总内存使用", "Total Memory Usage")} + + + +
+ {loading ? ( + + ) : ( + `${stats.memory || 0}GB` + )} +
+

{t("已分配", "Allocated")}

+
+
+
+ + + + {t("平台原生Agent库", "Platform Native Agent Library")} + + {t("选择并部署预构建的Agent到您的资源", "Select and deploy pre-built agents to your resources")} + + + + {loading ? ( +
+ {[1, 2, 3].map((i) => ( + + +
+ + +
+ + + ))} +
+ ) : ( +
+ {(platformAgents.length > 0 ? platformAgents : defaultPlatformAgents).map((agent) => ( + + +
+
{agent.icon}
+ {agent.status} +
+ {agent.name} +
+ +

{agent.description}

+
+

{t("核心能力", "Core Capabilities")}:

+
+ {agent.capabilities.map((cap: string) => ( + + {cap} + + ))} +
+
+ +
+
+ ))} +
+ )} +
+ +
+ + {/* 部署Agent对话框 */} + + + + + {t("部署", "Deploy")} {selectedAgent?.name} + + + {t("配置Agent的运行环境和实例数量", "Configure runtime environment and instance count for agent")} + + + +
+
+
+
{selectedAgent?.icon}
+
+

{selectedAgent?.name}

+

{selectedAgent?.description}

+
+
+
+ +
+

+ + {t("资源配置", "Resource Configuration")} +

+ +
+
+ + setDeployConfig({ ...deployConfig, agentCount: Number.parseInt(e.target.value) })} + /> +

{t("建议范围: 1-10", "Recommended: 1-10")}

+
+ +
+ + +
+
+ +
+ + +

+ {t("选择Agent使用的服务网关协议", "Select the service gateway protocol for the agent")} +

+
+ +
+

+ + {t("部署摘要", "Deployment Summary")} +

+
+
+ + + {deployConfig.agentCount} {t("个实例", "instances")} + +
+
+ + {deployConfig.model} +
+
+ + + 2 {t("核/实例", "cores/instance")} ({t("固定", "Fixed")}) + +
+
+ + + 4GB/{t("实例", "instance")} ({t("固定", "Fixed")}) + +
+
+ {deployConfig.serviceGateway && ( +
+ {t("服务网关", "Service Gateway")}: + {deployConfig.serviceGateway} +
+ )} +
+

+ {t("总资源", "Total resources")}: {deployConfig.agentCount * 2} {t("核", "cores")},{" "} + {deployConfig.agentCount * 4}GB {t("内存", "memory")} +

+
+
+
+
+ + + + + +
+
+ + ) +} diff --git a/app/billing/page.tsx b/app/billing/page.tsx new file mode 100644 index 0000000..814f5b8 --- /dev/null +++ b/app/billing/page.tsx @@ -0,0 +1,522 @@ +"use client" + +import { DashboardLayout } from "@/components/dashboard-layout" +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Zap, Download, Filter, Calendar, Plus, Wallet } from "lucide-react" +import { Progress } from "@/components/ui/progress" +import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, LineChart, Line } from "recharts" +import { useLanguage } from "@/hooks/useLanguage" +import { useState, useEffect } from "react" +import { TaijiAPIClient } from "@/lib/api-client" +import { useToast } from "@/hooks/use-toast" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" + +export default function BillingPage() { + const { t } = useLanguage() + const { toast } = useToast() + const [loading, setLoading] = useState(true) + const [showFilterDialog, setShowFilterDialog] = useState(false) + const [showDateDialog, setShowDateDialog] = useState(false) + const [showExportDialog, setShowExportDialog] = useState(false) + const [showRechargeDialog, setShowRechargeDialog] = useState(false) + const [rechargeAmount, setRechargeAmount] = useState("") + const [balance, setBalance] = useState({ balance: 0, monthlySpent: 0, euBalance: 0 }) + const [euUsageData, setEuUsageData] = useState>([]) + const [costBreakdown, setCostBreakdown] = useState>([]) + const [resourceUsage, setResourceUsage] = useState({ + cpu: { used: 0, total: 100 }, + memory: { used: 0, total: 200 }, + storage: { used: 0, total: 1000 }, + apiCalls: { used: 0, total: 100000 }, + }) + + useEffect(() => { + loadBillingData() + }, []) + + const loadBillingData = async () => { + try { + setLoading(true) + + // 并行加载数据 + const [balanceResult, historyResult, costResult] = await Promise.allSettled([ + TaijiAPIClient.getBillingBalance(), + TaijiAPIClient.getBillingHistory({ + startTime: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), + endTime: new Date().toISOString(), + }), + TaijiAPIClient.getBillingHistory({ + startTime: new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString(), + endTime: new Date().toISOString(), + }), + ]) + + // 处理余额 + if (balanceResult.status === "fulfilled" && balanceResult.value?.success) { + const data = balanceResult.value.data + setBalance({ + balance: data.balance || 0, + monthlySpent: data.monthlySpent || 0, + euBalance: data.euBalance || 0, + }) + } + + // 处理历史数据(用于图表) + if (historyResult.status === "fulfilled" && historyResult.value?.success) { + const records = historyResult.value.data?.records || [] + // 按日期聚合EU消耗 + const dailyEu: Record = {} + records.forEach((record: any) => { + const date = new Date(record.timestamp).toLocaleDateString("en-US", { month: "short", day: "numeric" }) + dailyEu[date] = (dailyEu[date] || 0) + (record.eu || 0) + }) + setEuUsageData( + Object.entries(dailyEu).map(([date, eu]) => ({ date, eu })).slice(-8), + ) + } + + // 处理费用明细 + if (costResult.status === "fulfilled" && costResult.value?.success) { + const records = costResult.value.data?.records || [] + // 按类别聚合费用 + const categoryCosts: Record = {} + records.forEach((record: any) => { + const category = record.agentType || "Other" + if (!categoryCosts[category]) { + categoryCosts[category] = { cost: 0, eu: 0 } + } + categoryCosts[category].cost += record.cost || 0 + categoryCosts[category].eu += record.eu || 0 + }) + setCostBreakdown( + Object.entries(categoryCosts).map(([category, data]) => ({ + category, + ...data, + })), + ) + } + } catch (error) { + console.error("Failed to load billing data:", error) + } finally { + setLoading(false) + } + } + + const handleRecharge = async () => { + if (!rechargeAmount || Number.parseFloat(rechargeAmount) <= 0) { + toast({ + title: t("错误", "Error"), + description: t("请输入有效的充值金额", "Please enter a valid amount"), + variant: "destructive", + }) + return + } + + try { + const result = await TaijiAPIClient.rechargeBalance(Number.parseFloat(rechargeAmount)) + if (result?.success) { + toast({ + title: t("充值成功", "Recharge Successful"), + description: t("余额已更新", "Balance updated"), + }) + setShowRechargeDialog(false) + setRechargeAmount("") + loadBillingData() + } else { + throw new Error(result?.message || "Recharge failed") + } + } catch (error: any) { + toast({ + title: t("充值失败", "Recharge Failed"), + description: error.message || t("无法完成充值", "Failed to recharge"), + variant: "destructive", + }) + } + } + + return ( + +
+
+
+

{t("计费与资源平面", "Billing & Resources Plane")}

+

+ {t("即用即付,按实际消费计费", "Pay-as-you-go, billing based on actual consumption")} +

+
+
+ + + +
+
+ + + +
+
+ + + {t("账户余额", "Account Balance")} + + + {t("即用即付模式,按实际消费扣费", "Pay-as-you-go mode, deducted based on actual consumption")} + +
+ +
+
+ +
+ {loading ? ( + + ) : ( + `¥${balance.balance.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` + )} +
+

+ {loading ? ( + + ) : ( + t("本月已消费", "This month consumed") + `: ¥${balance.monthlySpent.toFixed(2)}` + )} +

+
+
+ +
+ + + {t("EU余额", "EU Balance")} + + +
+ {loading ? ( + + ) : ( + balance.euBalance.toLocaleString() + )} +
+

{t("可用单位", "Available units")}

+
+
+ + + + {t("本月", "This Month")} + + +
+ {loading ? ( + + ) : ( + `¥${balance.monthlySpent.toFixed(2)}` + )} +
+

+ {loading ? "" : `${Math.round(balance.monthlySpent * 100)} EU ${t("已消费", "consumed")}`} +

+
+
+ + + + + {t("平均每日费用", "Avg Daily Cost")} + + + +
$34
+

-5% {t("vs上月", "vs last month")}

+
+
+ + + + {t("预计", "Projected")} + + +
$1,050
+

{t("月底", "End of month")}

+
+
+
+ + + + {t("EU消费历史", "EU Consumption History")} + {t("执行单位使用情况随时间变化", "Execution Units usage over time")} + + + + 0 ? euUsageData : [{ date: "", eu: 0 }]}> + + + + + + + + + +
+ + + {t("费用明细", "Cost Breakdown")} + {t("当月支出(按类别)", "Current month spending by category")} + + + + 0 ? costBreakdown : [{ category: "", cost: 0, eu: 0 }]}> + + + + + + + + + + + + {t("资源使用", "Resource Usage")} + {t("当前分配和限制", "Current allocation and limits")} + + + {[ + { + name: "CPU", + used: resourceUsage.cpu.used, + total: resourceUsage.cpu.total, + unit: t("核", "cores"), + }, + { + name: t("内存", "Memory"), + used: resourceUsage.memory.used, + total: resourceUsage.memory.total, + unit: "GB", + }, + { + name: t("存储", "Storage"), + used: resourceUsage.storage.used, + total: resourceUsage.storage.total, + unit: "GB", + }, + { + name: t("API调用", "API Calls"), + used: resourceUsage.apiCalls.used, + total: resourceUsage.apiCalls.total, + unit: t("次", "calls"), + }, + ].map((resource) => ( +
+
+ {resource.name} + + {resource.used} / {resource.total} {resource.unit} + +
+ +
+ ))} +
+
+
+ + + + {t("EU定价详情", "EU Pricing Details")} + {t("了解执行单位", "Understanding Execution Units")} + + +
+ {[ + { + service: t("模型推理", "Model Inference"), + rate: "0.025 EU/call", + desc: t("LLM API调用", "LLM API calls"), + }, + { + service: t("API工具", "API Tools"), + rate: "0.001 EU/call", + desc: t("外部API使用", "External API usage"), + }, + { + service: t("VM计算", "VM Compute"), + rate: "0.5 EU/hour", + desc: t("Firecracker VM运行时", "Firecracker VM runtime"), + }, + { + service: t("数据存储", "Data Storage"), + rate: "0.01 EU/GB/day", + desc: t("持久存储", "Persistent storage"), + }, + ].map((pricing) => ( +
+
+ +

{pricing.service}

+
+

{pricing.rate}

+

{pricing.desc}

+
+ ))} +
+
+
+ + + + + {t("筛选选项", "Filter Options")} + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ + + + + {t("时间范围", "Date & Time Range")} + +
+
+ + +
+
+ + +
+ +
+
+
+ + + + + {t("导出格式", "Export Format")} + +
+

{t("选择导出文件格式", "Select export file format")}

+
+ + + +
+
+
+
+ + + + + {t("账户充值", "Account Recharge")} + +
+
+ + setRechargeAmount(e.target.value)} + /> +

{t("最低充值金额: $10", "Minimum recharge: $10")}

+
+
+ {[50, 100, 500, 1000].map((amount) => ( + + ))} +
+
+
+ {t("充值金额", "Recharge Amount")} + ${rechargeAmount || "0.00"} +
+
+ {t("当前余额", "Current Balance")} + $2,487.50 +
+
+ {t("充值后余额", "Balance After Recharge")} + ${(2487.5 + Number.parseFloat(rechargeAmount || "0")).toFixed(2)} +
+
+
+ + + + +
+
+
+
+ ) +} diff --git a/app/channel/dashboard/loading.tsx b/app/channel/dashboard/loading.tsx new file mode 100644 index 0000000..f15322a --- /dev/null +++ b/app/channel/dashboard/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return null +} diff --git a/app/channel/dashboard/page.tsx b/app/channel/dashboard/page.tsx new file mode 100644 index 0000000..487f656 --- /dev/null +++ b/app/channel/dashboard/page.tsx @@ -0,0 +1,2109 @@ +"use client" + +import { useEffect, useState } from "react" +import { useRouter } from "next/navigation" +import { Card } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { + X, + Database, + Calendar, + Filter, + Download, + Globe, + LogOut, + Settings, + Plus, + Eye, + Edit, + UserPlus, + Check, + Search, + MoreVertical, + Building2, + Users, + DollarSign, + Activity, + TrendingUp, + Wallet, + CreditCard, +} from "lucide-react" // Added X, Database, Globe, LogOut, Settings, Plus, Eye, Edit, UserPlus, Check, Search, MoreVertical, Building2, Users, DollarSign, Activity, TrendingUp, Wallet, CreditCard icons +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { useLanguage } from "@/contexts/language-context" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Badge } from "@/components/ui/badge" + +export default function ChannelDashboard() { + const router = useRouter() + const { language, setLanguage } = useLanguage() + const [isAddTenantOpen, setIsAddTenantOpen] = useState(false) + const [activeTab, setActiveTab] = useState("overview") + // ADD STATE VARIABLES FOR RESOURCES MANAGEMENT + const [computeConfigOpen, setComputeConfigOpen] = useState(false) + const [selectedAgent, setSelectedAgent] = useState(null) + const [isAddProviderOpen, setIsAddProviderOpen] = useState(false) + const [providerType, setProviderType] = useState<"model" | "data">("model") + const [providerForm, setProviderForm] = useState({ + name: "", + apiUrl: "", + apiKey: "", + models: "", + rpm: "", + tpm: "", + }) + const [providerApplications, setProviderApplications] = useState([]) + const [isApplicationDialogOpen, setIsApplicationDialogOpen] = useState(false) + const [applicationForm, setApplicationForm] = useState({ + providerName: "", + reason: "", + expectedModels: "", + type: "model", // Added for application type + agentType: "", // Added for agent application + agentQuantity: 0, // Added for agent application + rpm: 0, // Added for model application + tpm: 0, // Added for model application + }) + const [allocateAgentDialogOpen, setAllocateAgentDialogOpen] = useState(false) + const [selectedTenantForAgentAllocation, setSelectedTenantForAgentAllocation] = useState(null) + const [tenantAgentAllocations, setTenantAgentAllocations] = useState<{ + [key: string]: { [key: string]: number } + }>({}) + const [tenantModelAllocations, setTenantModelAllocations] = useState< + Record> + >({}) + + // Settings related states + const [isPermissionDialogOpen, setIsPermissionDialogOpen] = useState(false) + const [selectedRole, setSelectedRole] = useState("billing") + const [rolePermissions, setRolePermissions] = useState>({ + billing: ["overview", "tenants", "billing"], + operations: ["overview", "tenants", "resources"], + }) + const [isAddAdminOpen, setIsAddAdminOpen] = useState(false) + + // ADD STATE FOR BILLING MANAGEMENT DIALOG + const [billingManagementDialogOpen, setBillingManagementDialogOpen] = useState(false) + const [selectedTenantForBilling, setSelectedTenantForBilling] = useState(null) + const [tenantBillingSettings, setTenantBillingSettings] = useState<{ + [key: string]: { tier: string; discount: number } + }>({}) + + const [customAgentResources, setCustomAgentResources] = useState>({}) + + // CHANGE: Add state variables for dialogs + const [showFilterDialog, setShowFilterDialog] = useState(false) + const [showDateDialog, setShowDateDialog] = useState(false) + const [showExportDialog, setShowExportDialog] = useState(false) + + const [showRechargeDialog, setShowRechargeDialog] = useState(false) + const [showCreditDialog, setShowCreditDialog] = useState(false) + const [selectedTenantForRecharge, setSelectedTenantForRecharge] = useState(null) + const [rechargeAmount, setRechargeAmount] = useState("") + const [creditLimit, setCreditLimit] = useState("") + + const translations = { + en: { + title: "Channel Partner Portal", + subtitle: "Enterprise Solutions Inc", + dashboard: "Dashboard", + myTenants: "My Tenants", + providers: "Providers", + billing: "Billing", + settings: "Settings", + logout: "Logout", + totalTenants: "Total Tenants", + activeTenants: "Active Tenants", + monthlyRevenue: "Monthly Revenue", + commission: "Commission Earned", + searchTenants: "Search tenants...", + tenantName: "Tenant Name", + status: "Status", + plan: "Plan", + users: "Users", + revenue: "Revenue", + actions: "Actions", + active: "Active", + suspended: "Suspended", + enterprise: "Enterprise", + professional: "Professional", + starter: "Starter", + viewDetails: "View Details", + edit: "Edit", + manageBilling: "Manage Billing", + addTenant: "Add Tenant", + createTenant: "Create New Tenant", + createTenantDesc: "Add a new tenant to your portfolio", + companyName: "Company Name", + contactPerson: "Contact Person", + contactEmail: "Contact Email", + selectPlan: "Select Plan", + cancel: "Cancel", + create: "Create", + modelProviders: "Model Providers", + dataProviders: "Data Providers", + platformProviders: "Platform Providers", + customProviders: "Custom Providers", + addProvider: "Add Provider", + addModelProvider: "Add Model Provider", + addDataProvider: "Add Data Provider", + providerName: "Provider Name", + apiKey: "API Key", + apiEndpoint: "API Endpoint", + description: "Description", + enabled: "Enabled", + disabled: "Disabled", + totalRequests: "Total Requests", + avgLatency: "Avg Latency", + uptime: "Uptime", + capacity: "Capacity", + applyForModelProvider: "Apply for Model Provider", + applicationReason: "Application Reason", + expectedModels: "Expected Models", + submitApplication: "Submit Application", + approved: "Approved", + pending: "Pending", + allocateAgents: "Allocate Agents", + allocationSummary: "Allocation Summary", + availableAgents: "Available Agents", + }, + zh: { + title: "渠道合作伙伴平台", + subtitle: "企业解决方案公司", + dashboard: "仪表板", + myTenants: "我的租户", + providers: "供应商", + billing: "计费", + settings: "设置", + logout: "退出", + totalTenants: "总租户数", + activeTenants: "活跃租户", + monthlyRevenue: "月度收入", + commission: "已获佣金", + searchTenants: "搜索租户...", + tenantName: "租户名称", + status: "状态", + plan: "方案", + users: "用户数", + revenue: "收入", + actions: "操作", + active: "活跃", + suspended: "已暂停", + enterprise: "企业版", + professional: "专业版", + starter: "入门版", + viewDetails: "查看详情", + edit: "编辑", + manageBilling: "管理计费", + addTenant: "添加租户", + createTenant: "创建新租户", + createTenantDesc: "向您的租户组合添加新租户", + companyName: "公司名称", + contactPerson: "联系人", + contactEmail: "联系邮箱", + selectPlan: "选择方案", + cancel: "取消", + create: "创建", + modelProviders: "模型供应商", + dataProviders: "数据供应商", + platformProviders: "平台供应商", + customProviders: "自定义供应商", + addProvider: "添加供应商", + addModelProvider: "添加模型供应商", + addDataProvider: "添加数据供应商", + providerName: "供应商名称", + apiKey: "API密钥", + apiEndpoint: "API端点", + description: "描述", + enabled: "已启用", + disabled: "已禁用", + totalRequests: "总请求数", + avgLatency: "平均延迟", + uptime: "在线率", + capacity: "容量", + applyForModelProvider: "申请模型供应商", + applicationReason: "申请理由", + expectedModels: "期望使用的模型", + submitApplication: "提交申请", + approved: "已授权", + pending: "待审批", + allocateAgents: "分配Agent", + allocationSummary: "分配摘要", + availableAgents: "可用Agent", + }, + } + + const text = translations[language] + + useEffect(() => { + const token = localStorage.getItem("channel_token") + if (!token) { + router.push("/channel/login") + } + }, [router]) + + const handleLogout = () => { + localStorage.removeItem("channel_token") + router.push("/channel/login") + } + + const stats = [ + { + title: text.totalTenants, + value: "45", + change: "+3 this month", + icon: Users, + trend: "up", + }, + { + title: text.activeTenants, + value: "42", + change: "+5.2%", + icon: Activity, + trend: "up", + }, + { + title: text.monthlyRevenue, + value: "$45.2K", + change: "+12.8%", + icon: DollarSign, + trend: "up", + }, + { + title: text.commission, + value: "$6.78K", + change: "15% rate", + icon: TrendingUp, + trend: "stable", + }, + ] + + const tenants = [ + { + id: 1, + name: "Acme Corporation", + status: "active", + plan: "enterprise", + users: 45, + revenue: "$3,200", + balance: "500.00", + creditLimit: "2000.00", + }, + { + id: 2, + name: "TechStart Inc", + status: "active", + plan: "professional", + users: 28, + revenue: "$1,800", + balance: "150.00", + creditLimit: "1000.00", + }, + { + id: 3, + name: "Innovation Labs", + status: "active", + plan: "professional", + users: 32, + revenue: "$1,800", + balance: "200.00", + creditLimit: "1500.00", + }, + { + id: 4, + name: "Digital Solutions", + status: "active", + plan: "starter", + users: 12, + revenue: "$890", + balance: "50.00", + creditLimit: "500.00", + }, + { + id: 5, + name: "Global Systems", + status: "suspended", + plan: "enterprise", + users: 38, + revenue: "$3,200", + balance: "0.00", + creditLimit: "3000.00", + }, + { + id: 6, + name: "CloudTech Pro", + status: "active", + plan: "professional", + users: 25, + revenue: "$1,800", + balance: "300.00", + creditLimit: "1200.00", + }, + { + id: 7, + name: "Data Dynamics", + status: "active", + plan: "starter", + users: 8, + revenue: "$890", + balance: "25.00", + creditLimit: "750.00", + }, + { + id: 8, + name: "Smart Analytics", + status: "active", + plan: "professional", + users: 30, + revenue: "$1,800", + balance: "180.00", + creditLimit: "1100.00", + }, + ] + + const platformModelProviders = [ + { id: 1, name: "OpenAI", status: "active", requests: 52000, latency: 150, uptime: "99.9%", icon: "🤖" }, + { id: 2, name: "Anthropic", status: "active", requests: 38000, latency: 180, uptime: "99.8%", icon: "🧠" }, + { id: 3, name: "Google AI", status: "active", requests: 25000, latency: 120, uptime: "99.95%", icon: "🔍" }, + { id: 4, name: "Meta Llama", status: "active", requests: 15000, latency: 95, uptime: "99.7%", icon: "🦙" }, + ] + + const platformDataProviders = [ + { id: 1, name: "RapidAPI", status: "active", apis: 8000, calls: 2200000, capacity: "95%", icon: "⚡" }, + { id: 2, name: "API Hub", status: "active", apis: 5000, calls: 1500000, capacity: "87%", icon: "🔗" }, + { id: 3, name: "OpenData", status: "active", apis: 3000, calls: 800000, capacity: "92%", icon: "📊" }, + ] + + const customProviders = { + model: [ + { id: 101, name: "Internal AI Model", status: "active", requests: 5000, latency: 80, uptime: "99.5%" }, + { id: 102, name: "Custom LLM Endpoint", status: "active", requests: 3200, latency: 200, uptime: "98.9%" }, + ], + data: [ + { id: 201, name: "Internal Data API", status: "active", apis: 12, calls: 450000, capacity: "70%" }, + { id: 202, name: "Third-party Data Source", status: "active", apis: 8, calls: 320000, capacity: "65%" }, + ], + } + + const availableAgents = [ + { name: "Weather Query Agent", description: "用于查询天气信息", available: 100, color: "bg-blue-500", icon: "☁️" }, + { name: "Data Analysis Agent", description: "用于数据分析", available: 150, color: "bg-green-500", icon: "📊" }, + { + name: "Document Processing Agent", + description: "用于文档处理", + available: 80, + color: "bg-yellow-500", + icon: "📄", + }, + { name: "Email Management Agent", description: "用于邮件管理", available: 120, color: "bg-red-500", icon: "✉️" }, + { name: "API Integration Agent", description: "用于API集成", available: 200, color: "bg-purple-500", icon: "🔗" }, + { + name: "Database Operations Agent", + description: "用于数据库操作", + available: 50, + color: "bg-orange-500", + icon: "💾", + }, + ] + + return ( +
+ {/* Header */} +
+
+
+
+
+ +
+
+

{text.title}

+

{text.subtitle}

+
+
+
+ +
+ + + + + +
+
+ +
+ + + + {text.dashboard} + + + {text.myTenants} + + {/* UPDATE TABNAVIGATION: REPLACE "PROVIDERS" WITH "RESOURCES" */} + + {language === "zh" ? "资源管理" : "Resource Management"} + + {/* CHANGE: Add billing and settings tabs to navigation */} + + {language === "zh" ? "计费" : "Billing"} + + + {language === "zh" ? "设置" : "Settings"} + + + +
+
+ + {/* Main content */} +
+ {activeTab === "overview" && ( +
+ {/* Stats grid */} +
+ {stats.map((stat, index) => ( + +
+
+

{stat.title}

+

{stat.value}

+
+ + {stat.change} +
+
+
+ +
+
+
+ ))} +
+ +
+

+ {language === "zh" ? "可分配Agent概览" : "Assignable Agents Overview"} +

+
+ {[ + { name: "Weather Query Agent", nameZh: "天气查询代理", allocated: 10, icon: "☁️" }, + { name: "Data Analysis Agent", nameZh: "数据分析代理", allocated: 15, icon: "📊" }, + { name: "Document Processing Agent", nameZh: "文档处理代理", allocated: 8, icon: "📄" }, + { name: "Email Management Agent", nameZh: "邮件管理代理", allocated: 12, icon: "✉️" }, + { name: "API Integration Agent", nameZh: "API集成代理", allocated: 20, icon: "🔗" }, + { name: "Database Operations Agent", nameZh: "数据库操作代理", allocated: 5, icon: "💾" }, + ].map((agent) => ( + +
+
{agent.icon}
+
+

+ {language === "zh" ? agent.nameZh : agent.name} +

+
+ + {language === "zh" ? "可分配数量" : "Available Instances"} + + {agent.allocated} +
+
+
+
+ ))} +
+
+
+ )} + + {activeTab === "tenants" && ( + +
+

{text.myTenants}

+
+
+ + +
+ + + + + + + {text.createTenant} + {text.createTenantDesc} + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ {tenants.map((tenant) => ( +
+
+
+ +
+
+

{tenant.name}

+

+ {tenant.users} {text.users} · {text[tenant.plan as keyof typeof text] || tenant.plan} +

+
+
+
+ + {language === "zh" ? (tenant.status === "active" ? "活跃" : "已暂停") : tenant.status} + +
+

{tenant.revenue}/mo

+
+ + + + + + + + + {text.viewDetails} + + + + {text.edit} + + + + {language === "zh" ? "管理用户" : "Manage Users"} + + {/* + + {text.allocateAgents} + */} + {/* Update the tenant action menu item text from "分配Agent" to "分配资源" */} + { + setSelectedTenantForAgentAllocation(tenant) + setAllocateAgentDialogOpen(true) + }} + > + + {language === "zh" ? "分配资源" : "Allocate Resources"} + + { + setSelectedTenantForRecharge(tenant) + setShowRechargeDialog(true) + }} + > + + {language === "zh" ? "充值" : "Recharge"} + + { + setSelectedTenantForRecharge(tenant) + setShowCreditDialog(true) + }} + > + + {language === "zh" ? "授信额度" : "Credit Limit"} + + { + setSelectedTenantForBilling(tenant) + setBillingManagementDialogOpen(true) + }} + > + + {language === "zh" ? "管理计费" : "Manage Billing"} + + + +
+ ))} +
+ + )} + + {/* REPLACE PROVIDERS TAB CONTENT WITH RESOURCES MANAGEMENT (ALIGNED WITH ADMIN) */} + {activeTab === "resources" && ( +
+
+
+

+ {language === "zh" ? "资源管理" : "Resource Management"} +

+

+ {language === "zh" + ? "监控已分配资源并申请新的模型或Agent" + : "Monitor allocated resources and apply for new models or agents"} +

+
+ +
+ +
+

+ {language === "zh" ? "Agent 配额监控" : "Agent Quota Monitoring"} +

+
+ {availableAgents.map((agent) => ( + +
+
+
+ {agent.icon} +

{agent.name}

+
+

{agent.description}

+
+
+
+
+ + {language === "zh" ? "剩余配额" : "Remaining Quota"}: + + {agent.available} +
+
+
+
+
+ + ))} +
+
+ +
+

+ {language === "zh" ? "模型管理" : "Model Management"} +

+
+ {[ + { + name: "OpenAI", + type: "Official", + models: ["gpt-4", "gpt-3.5-turbo"], + rpm: 10000, + tpm: 500000, + status: "approved", + }, + { + name: "Anthropic", + type: "Official", + models: ["claude-3-opus", "claude-2"], + rpm: 5000, + tpm: 300000, + status: "approved", + }, + { + name: "Google AI", + type: "GCP", + models: ["gemini-pro", "palm-2"], + rpm: 8000, + tpm: 400000, + status: "pending", + }, + ].map((provider) => ( + +
+
+

{provider.name}

+

{provider.type}

+
+ + {provider.status === "approved" + ? language === "zh" + ? "已授权" + : "Approved" + : language === "zh" + ? "待审批" + : "Pending"} + +
+
+
+ {language === "zh" ? "支持模型" : "Models"}: + {provider.models.length} +
+
+ + {language === "zh" ? "授权 RPM" : "Approved RPM"}: + + {provider.rpm.toLocaleString()} +
+
+ + {language === "zh" ? "授权 TPM" : "Approved TPM"}: + + {provider.tpm.toLocaleString()} +
+
+
+ ))} +
+
+ + + + + + {language === "zh" ? "提交资源申请" : "Submit Resource Application"} + + + {language === "zh" + ? "您可以申请新的模型或Agent资源。超级管理员审核通过后,资源将分配给您的渠道。" + : "You can apply for new model or agent resources. After admin approval, resources will be allocated to your channel."} + + +
+
+ + +
+ + {/* Adding RPM and TPM fields to model application form */} + {applicationForm.type === "model" ? ( + <> +
+ + setApplicationForm({ ...applicationForm, providerName: e.target.value })} + className="bg-background border-border text-foreground" + /> +
+
+ + setApplicationForm({ ...applicationForm, expectedModels: e.target.value })} + className="bg-background border-border text-foreground" + /> +

+ {language === "zh" ? "请用逗号分隔多个模型" : "Separate multiple models with commas"} +

+
+
+
+ + + setApplicationForm({ ...applicationForm, rpm: Number.parseInt(e.target.value) }) + } + className="bg-background border-border text-foreground" + /> +
+
+ + + setApplicationForm({ ...applicationForm, tpm: Number.parseInt(e.target.value) }) + } + className="bg-background border-border text-foreground" + /> +
+
+ + ) : ( + <> +
+ + +
+
+ + + setApplicationForm({ ...applicationForm, agentQuantity: Number.parseInt(e.target.value) }) + } + className="bg-background border-border text-foreground" + /> +
+ + )} + +
+ +