Files
taiji-pda-v0/app/admin/dashboard/page.tsx
T

3004 lines
135 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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<string[]>([])
// const [monthlyQuota, setMonthlyQuota] = useState<string>(""); // REMOVED
const [creditLimit, setCreditLimit] = useState<string>("")
const [isSubscriptionDialogOpen, setIsSubscriptionDialogOpen] = useState(false)
const [selectedSubscriptionLevel, setSelectedSubscriptionLevel] = useState<string | null>(null)
const [commissionRate, setCommissionRate] = useState<string>("")
const [selectedTenant, setSelectedTenant] = useState<string | null>(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<any>(null)
const [providerForm, setProviderForm] = useState({
name: "",
url: "",
apiKey: "",
models: "",
rpm: "",
tpm: "",
})
const [selectedAgent, setSelectedAgent] = useState<any>(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<string[]>([])
const [agentQuantities, setAgentQuantities] = useState<Record<string, number>>({})
// 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<string | null>(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<any[]>([
{
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<any>(null)
const [isApprovalDialogOpen, setIsApprovalDialogOpen] = useState(false)
// Agent申请审批相关状态
const [agentApprovals, setAgentApprovals] = useState<any[]>([
{
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<any>(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 (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 border-b border-border bg-card/95 backdrop-blur-sm">
<div className="flex items-center justify-between px-6 py-4">
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<Shield className="h-6 w-6 text-primary" />
<div>
<h1 className="text-lg font-bold text-foreground">{text.title}</h1>
<p className="text-xs text-muted-foreground">{text.subtitle}</p>
</div>
</div>
</div>
<div className="flex items-center gap-3">
<Select value={language} onValueChange={(value: "en" | "zh") => setLanguage(value)}>
<SelectTrigger className="w-[120px] bg-background border-border">
<Globe className="mr-2 h-4 w-4" />
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="en">English</SelectItem>
<SelectItem value="zh">中文</SelectItem>
</SelectContent>
</Select>
<Button
variant="ghost"
size="sm"
onClick={handleLogout}
className="text-muted-foreground hover:text-foreground"
>
<LogOut className="h-4 w-4 mr-2" />
{text.logout}
</Button>
</div>
</div>
{/* Navigation tabs */}
<div className="px-6 flex gap-6 border-t border-border overflow-x-auto">
{[
{ 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) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex items-center gap-2 px-3 py-3 text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
activeTab === tab.id
? "border-primary text-foreground"
: "border-transparent text-muted-foreground hover:text-foreground"
}`}
>
<tab.icon className="h-4 w-4" />
{tab.label}
</button>
))}
</div>
</header>
{/* Main content */}
<main className="p-6 space-y-6">
{activeTab === "overview" && (
<>
{/* Stats grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{stats.map((stat, index) => (
<Card key={index} className="p-6 bg-card border-border">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-muted-foreground mb-1">{stat.title}</p>
<p className="text-3xl font-bold text-foreground mb-2">{stat.value}</p>
<div className="flex items-center gap-1">
<TrendingUp className="h-3 w-3 text-green-500" />
<span className="text-xs text-green-500">{stat.change}</span>
</div>
</div>
<div className="p-3 rounded-lg bg-primary/10">
<stat.icon className="h-5 w-5 text-primary" />
</div>
</div>
</Card>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* System Metrics */}
<Card className="lg:col-span-1 p-6 bg-card border-border">
<h3 className="text-lg font-semibold text-foreground mb-4">{text.systemMetrics}</h3>
<div className="space-y-4">
{systemMetrics.map((metric, index) => (
<div key={index}>
<div className="flex justify-between text-sm mb-2">
<span className="text-muted-foreground">{metric.label}</span>
<span className="text-foreground font-medium">
{typeof metric.value === "number" && metric.value < 200 ? `${metric.value}%` : metric.value}
</span>
</div>
<div className="h-2 bg-muted rounded-full overflow-hidden">
<div
className={`h-full ${metric.color} transition-all duration-300`}
style={{ width: `${(metric.value / metric.max) * 100}%` }}
/>
</div>
</div>
))}
</div>
</Card>
{/* Recent Tenants */}
<Card className="lg:col-span-2 p-6 bg-card border-border">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-foreground">{text.recentTenants}</h3>
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input placeholder={text.search} className="pl-9 w-[240px] bg-background border-border" />
</div>
</div>
<div className="space-y-3">
{tenants.map((tenant) => (
<div
key={tenant.id}
className="flex items-center justify-between p-4 rounded-lg bg-background border border-border hover:border-primary/50 transition-colors"
>
<div className="flex items-center gap-4 flex-1">
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
<Users className="h-5 w-5 text-primary" />
</div>
<div className="flex-1">
<p className="font-medium text-foreground">{tenant.name}</p>
<p className="text-xs text-muted-foreground">
{text[tenant.plan as keyof typeof text] || tenant.plan} · {tenant.revenue}/mo
</p>
</div>
<div className="text-right">
<div className="flex items-center gap-2 mb-1">
<div className="h-2 w-2 rounded-full bg-green-500" />
<span className="text-sm text-muted-foreground">{text.active}</span>
</div>
<p className="text-xs text-muted-foreground">
{text.usage}: {tenant.usage}%
</p>
</div>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm">
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>{text.viewDetails}</DropdownMenuItem>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive">{text.suspend}</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
))}
</div>
</Card>
</div>
{/* System alerts */}
<Card className="p-6 bg-card border-border border-l-4 border-l-yellow-500">
<div className="flex items-start gap-4">
<div className="p-2 rounded-lg bg-yellow-500/10">
<AlertTriangle className="h-5 w-5 text-yellow-500" />
</div>
<div className="flex-1">
<h4 className="font-semibold text-foreground mb-1">
{language === "zh" ? "系统警告" : "System Alert"}
</h4>
<p className="text-sm text-muted-foreground">
{language === "zh"
? "数据库存储使用率已达 78%。建议尽快扩容以避免性能下降。"
: "Database storage usage has reached 78%. Consider scaling up to avoid performance degradation."}
</p>
<div className="flex items-center gap-2 mt-3">
<Clock className="h-4 w-4 text-muted-foreground" />
<span className="text-xs text-muted-foreground">
2 {language === "zh" ? "小时前" : "hours ago"}
</span>
</div>
</div>
<Button variant="outline" size="sm">
{language === "zh" ? "查看详情" : "View Details"}
</Button>
</div>
</Card>
</>
)}
{activeTab === "channels" && (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-foreground">{text.channelManagement}</h2>
<p className="text-sm text-muted-foreground mt-1">{text.channelDescription}</p>
</div>
<Dialog open={isAddChannelOpen} onOpenChange={setIsAddChannelOpen}>
<DialogTrigger asChild>
<Button className="bg-primary text-primary-foreground">
<Plus className="h-4 w-4 mr-2" />
{text.addChannel}
</Button>
</DialogTrigger>
<DialogContent className="bg-card border-border">
<DialogHeader>
<DialogTitle>{text.createChannel}</DialogTitle>
<DialogDescription>
{language === "zh"
? "创建新的分销渠道账户,渠道可以管理自己的租户组合"
: "Create a new distribution channel account that can manage their own tenant portfolio"}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="channelName">{text.channelName}</Label>
<Input id="channelName" placeholder={text.channelNamePlaceholder} className="bg-background" />
</div>
<div className="space-y-2">
<Label htmlFor="contactPerson">{text.contactPerson}</Label>
<Input id="contactPerson" placeholder="John Doe" className="bg-background" />
</div>
<div className="space-y-2">
<Label htmlFor="contactEmail">{text.contactEmail}</Label>
<Input
id="contactEmail"
type="email"
placeholder="contact@example.com"
className="bg-background"
/>
</div>
<div className="space-y-2">
<Label htmlFor="contactPhone">{text.contactPhone}</Label>
<Input id="contactPhone" placeholder="+1 234 567 8900" className="bg-background" />
</div>
<div className="space-y-2">
<Label htmlFor="commission">{text.commissionRate}</Label>
<Input id="commission" type="number" placeholder="15" className="bg-background" />
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsAddChannelOpen(false)}>
{text.cancel}
</Button>
<Button className="bg-primary text-primary-foreground" onClick={() => setIsAddChannelOpen(false)}>
{text.create}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
<div className="relative mb-6">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input placeholder={text.searchChannels} className="pl-9 bg-card border-border" />
</div>
<div className="grid grid-cols-1 gap-4">
{channels.map((channel) => (
<Card key={channel.id} className="p-6 bg-card border-border hover:border-primary/50 transition-colors">
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="w-14 h-14 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0">
<Building2 className="h-7 w-7 text-primary" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg font-semibold text-foreground">{channel.name}</h3>
<div
className={`px-2 py-1 rounded text-xs font-medium ${
channel.status === "active"
? "bg-green-500/10 text-green-500"
: "bg-gray-500/10 text-gray-500"
}`}
>
{language === "zh" ? (channel.status === "active" ? "活跃" : "停用") : channel.status}
</div>
</div>
<div className="space-y-1 text-sm text-muted-foreground">
<p>
{text.contactPerson}: {channel.contact}
</p>
<p>{channel.email}</p>
</div>
</div>
</div>
<div className="flex items-start gap-8 ml-6">
<div className="text-center">
<p className="text-2xl font-bold text-foreground">{channel.tenantCount}</p>
<p className="text-xs text-muted-foreground mt-1">{text.tenantCount}</p>
</div>
<div className="text-center">
<p className="text-2xl font-bold text-foreground">{channel.revenue}</p>
<p className="text-xs text-muted-foreground mt-1">{language === "zh" ? "月收入" : "Monthly"}</p>
</div>
<div className="text-center">
<p className="text-2xl font-bold text-foreground">{channel.commission}%</p>
<p className="text-xs text-muted-foreground mt-1">{text.commission}</p>
</div>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="ml-4">
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="bg-card border-border">
<DropdownMenuItem>
<Eye className="h-4 w-4 mr-2" />
{text.viewDetails}
</DropdownMenuItem>
<DropdownMenuItem>
<Edit className="h-4 w-4 mr-2" />
{text.edit}
</DropdownMenuItem>
<DropdownMenuItem>
<Users className="h-4 w-4 mr-2" />
{language === "zh" ? "查看租户" : "View Tenants"}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleEditCommission(channel)}>
<DollarSign className="h-4 w-4 mr-2" />
{language === "zh" ? "修改佣金" : "Edit Commission"}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive">
<Trash2 className="h-4 w-4 mr-2" />
{language === "zh" ? "删除渠道" : "Delete Channel"}
</DropdownMenuItem>
{/* <DropdownMenuItem onClick={() => handleConfigGoods(channel)}> // REMOVED */}
{/* <Package className="h-4 w-4 mr-2" /> */}
{/* {language === "zh" ? "货源配置" : "Goods Configuration"} */}
{/* </DropdownMenuItem> */}
{/* <DropdownMenuItem onClick={() => handleAgentAllocation(channel)}> // REMOVED */}
{/* <Bot className="h-4 w-4 mr-2" /> */}
{/* {language === "zh" ? "Agent分配" : "Agent Allocation"} */}
{/* </DropdownMenuItem> */}
{/* CHANGED: Combine goods configuration and agent allocation into resource management */}
<DropdownMenuItem onClick={() => handleResourceManagement(channel)}>
<Settings className="h-4 w-4 mr-2" />
{language === "zh" ? "资源管理" : "Resource Management"}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</Card>
))}
</div>
{/* Commission Edit Dialog */}
<Dialog open={isCommissionDialogOpen} onOpenChange={setIsCommissionDialogOpen}>
<DialogContent className="bg-card border-border">
<DialogHeader>
<DialogTitle>{language === "zh" ? "修改佣金比例" : "Edit Commission Rate"}</DialogTitle>
<DialogDescription>
{language === "zh"
? `为渠道 "${selectedChannel?.name}" 设置新的佣金比例`
: `Set a new commission rate for channel "${selectedChannel?.name}"`}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="commission-rate">
{language === "zh" ? "佣金比例 (%)" : "Commission Rate (%)"}
</Label>
<Input
id="commission-rate"
type="number"
min="0"
max="100"
step="0.1"
placeholder={language === "zh" ? "例: 15" : "e.g., 15"}
value={commissionRate}
onChange={(e) => setCommissionRate(e.target.value)}
className="bg-background border-border"
/>
<p className="text-xs text-muted-foreground">
{language === "zh"
? "当前佣金比例: " + selectedChannel?.commission + "%"
: "Current rate: " + selectedChannel?.commission + "%"}
</p>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsCommissionDialogOpen(false)}>
{text.cancel}
</Button>
<Button className="bg-primary text-primary-foreground" onClick={handleSaveCommission}>
{language === "zh" ? "保存" : "Save"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* CHANGED: Combined Goods Configuration and Agent Allocation into a single Resource Management Dialog */}
{/* <Dialog open={isConfigGoodsOpen} onOpenChange={setIsConfigGoodsOpen}> // REMOVED */}
<Dialog open={isResourceManagementOpen} onOpenChange={setIsResourceManagementOpen}>
<DialogContent className="bg-card border-border max-w-3xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{language === "zh" ? "资源管理" : "Resource Management"}</DialogTitle>
<DialogDescription>
{language === "zh"
? `为渠道 "${selectedChannel?.name}" 配置可用的模型、Agent和配额`
: `Configure available models, agents and quota for channel "${selectedChannel?.name}"`}
</DialogDescription>
</DialogHeader>
<div className="space-y-6 py-4">
{/* Models Selection */}
<div className="space-y-3">
<Label className="text-base font-semibold">
{language === "zh" ? "模型供应商" : "Model Providers"}
</Label>
<div className="grid grid-cols-1 gap-2">
{availableModels.map((model) => (
<div key={model.id} className="flex items-center space-x-2">
<input
type="checkbox"
id={`model-${model.id}`}
checked={selectedModels.includes(model.id)}
onChange={(e) => {
if (e.target.checked) {
setSelectedModels([...selectedModels, model.id])
} else {
setSelectedModels(selectedModels.filter((m) => m !== model.id))
}
}}
className="w-4 h-4 rounded border-border"
/>
<label htmlFor={`model-${model.id}`} className="text-sm font-medium cursor-pointer flex-1">
{model.name}
<span className="text-xs text-muted-foreground ml-2">({model.provider})</span>
</label>
</div>
))}
</div>
</div>
{/* Agent Selection with Quantities */}
<div className="space-y-3">
<Label className="text-base font-semibold">
{language === "zh" ? "Agent分配" : "Agent Allocation"}
</Label>
<p className="text-sm text-muted-foreground">
{language === "zh"
? "选择该渠道可以分配给租户的Agent并设置可用数量"
: "Select agents that this channel can assign to tenants and set available quantities"}
</p>
<div className="grid grid-cols-1 gap-3 mt-4">
{availableAgents.map((agent) => (
<div
key={agent.id}
className={`border rounded-lg p-4 transition-all ${
selectedAgents.includes(agent.id) ? "border-primary bg-primary/5" : "border-border"
}`}
>
<div className="flex items-center justify-between gap-4">
<div
className="flex items-center gap-3 flex-1 cursor-pointer"
onClick={() => {
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 })
}
}}
>
<div
className={`w-10 h-10 rounded-lg flex items-center justify-center ${
selectedAgents.includes(agent.id) ? "bg-primary/20" : "bg-muted"
}`}
>
<Bot
className={`h-5 w-5 ${selectedAgents.includes(agent.id) ? "text-primary" : "text-muted-foreground"}`}
/>
</div>
<div>
<p className="font-medium text-foreground">{agent.name}</p>
<p className="text-xs text-muted-foreground">{agent.type}</p>
</div>
</div>
{selectedAgents.includes(agent.id) && (
<div className="flex items-center gap-2">
<Label className="text-sm text-muted-foreground whitespace-nowrap">
{language === "zh" ? "数量:" : "Quantity:"}
</Label>
<Input
type="number"
min="1"
max="100"
value={agentQuantities[agent.id] || 1}
onChange={(e) => {
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()}
/>
</div>
)}
<input
type="checkbox"
checked={selectedAgents.includes(agent.id)}
onChange={() => {}}
className="w-5 h-5 rounded border-border"
/>
</div>
</div>
))}
</div>
</div>
<div className="space-y-3 border-t border-border pt-4">
<Label className="text-base font-semibold">
{language === "zh" ? "自定义Agent资源配置" : "Custom Agent Resource Configuration"}
</Label>
<p className="text-sm text-muted-foreground">
{language === "zh"
? "为该渠道的租户创建的自定义Agent配置默认CPU和内存资源"
: "Configure default CPU and memory resources for custom agents created by this channel's tenants"}
</p>
<div className="grid grid-cols-2 gap-4 mt-4">
<div className="space-y-2">
<Label htmlFor="custom-cpu">{language === "zh" ? "CPU核数" : "CPU Cores"}</Label>
<Input
id="custom-cpu"
type="number"
min="0.5"
max="16"
step="0.5"
value={customAgentCpu}
onChange={(e) => setCustomAgentCpu(e.target.value)}
className="bg-background border-border"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "推荐范围: 0.5-16核" : "Recommended: 0.5-16 cores"}
</p>
</div>
<div className="space-y-2">
<Label htmlFor="custom-memory">
{language === "zh" ? "内存大小(GB)" : "Memory Size (GB)"}
</Label>
<Input
id="custom-memory"
type="number"
min="0.5"
max="64"
step="0.5"
value={customAgentMemory}
onChange={(e) => setCustomAgentMemory(e.target.value)}
className="bg-background border-border"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "推荐范围: 0.5-64GB" : "Recommended: 0.5-64GB"}
</p>
</div>
</div>
<div className="bg-blue-500/10 border border-blue-500/20 rounded-lg p-3 mt-2">
<p className="text-xs text-muted-foreground">
{language === "zh"
? "此配置将应用于该渠道的租户创建的所有自定义Agent,不影响平台原生Agent。"
: "This configuration will apply to all custom agents created by this channel's tenants, not affecting platform native agents."}
</p>
</div>
</div>
{/* REMOVED monthly quota field */}
<div className="space-y-2">
<Label htmlFor="credit-limit">{language === "zh" ? "渠道授信额度" : "Channel Credit Limit"}</Label>
<Input
id="credit-limit"
type="number"
placeholder={language === "zh" ? "例: 10000" : "e.g., 10000"}
value={creditLimit}
onChange={(e) => setCreditLimit(e.target.value)}
className="bg-background border-border"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "渠道可用授信上限 (USD)" : "Channel credit limit (USD)"}
</p>
</div>
<div className="bg-primary/5 border border-primary/20 rounded-lg p-4">
<p className="text-sm font-semibold text-foreground mb-2">
{language === "zh" ? "资源配置摘要" : "Resource Configuration Summary"}
</p>
<div className="space-y-1 text-xs text-muted-foreground">
<p>
{language === "zh" ? "已选择模型" : "Models selected"}: {selectedModels.length}
</p>
<p>
{language === "zh" ? "已选择Agent" : "Agents selected"}: {selectedAgents.length}
</p>
{selectedAgents.length > 0 && (
<p>
{language === "zh" ? "总Agent数量: " : "Total agents: "}
{Object.values(agentQuantities).reduce((sum, qty) => sum + qty, 0)}
</p>
)}
<p className="pt-2 border-t border-border/50">
{language === "zh" ? "自定义Agent CPU: " : "Custom Agent CPU: "}
{customAgentCpu} {language === "zh" ? "核" : "cores"}
</p>
<p>
{language === "zh" ? "自定义Agent 内存: " : "Custom Agent Memory: "}
{customAgentMemory} GB
</p>
{/* REMOVED monthly quota from summary */}
<p>
{language === "zh" ? "渠道授信额度" : "Channel Credit Limit"}: ${creditLimit || "0"}
</p>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsResourceManagementOpen(false)}>
{text.cancel}
</Button>
<Button className="bg-primary text-primary-foreground" onClick={handleSaveResourceManagement}>
{language === "zh" ? "保存配置" : "Save Configuration"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<div className="mt-8">
<h3 className="text-lg font-semibold mb-4">
{language === "zh" ? "渠道申请审批" : "Channel Application Approvals"}
</h3>
<Card className="bg-card border-border">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="border-b border-border">
<tr>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "渠道" : "Channel"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "供应商" : "Provider"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "期望模型" : "Models"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "提交时间" : "Submitted"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "状态" : "Status"}
</th>
<th className="text-right p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "操作" : "Actions"}
</th>
</tr>
</thead>
<tbody>
{providerApprovals.map((approval) => (
<tr key={approval.id} className="border-b border-border last:border-0 hover:bg-muted/50">
<td className="p-4 text-sm text-foreground font-medium">{approval.channelName}</td>
<td className="p-4 text-sm text-foreground">{approval.providerName}</td>
<td className="p-4 text-sm text-muted-foreground">{approval.expectedModels}</td>
<td className="p-4 text-sm text-muted-foreground">{approval.submittedAt}</td>
<td className="p-4">
<Badge
variant="secondary"
className="bg-yellow-500/10 text-yellow-500 border-yellow-500/20"
>
{language === "zh" ? "待审批" : "Pending"}
</Badge>
</td>
<td className="p-4 text-right">
<Button
size="sm"
variant="outline"
onClick={() => {
setSelectedApproval(approval)
setIsApprovalDialogOpen(true)
}}
>
{language === "zh" ? "审批" : "Review"}
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
{/* Agent申请审批 */}
<div className="space-y-4">
<h3 className="text-lg font-semibold text-foreground">
{language === "zh" ? "Agent申请审批" : "Agent Application Approvals"}
</h3>
<Card className="bg-card border-border">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="border-b border-border">
<tr>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "渠道" : "Channel"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "Agent类型" : "Agent Type"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "申请数量" : "Quantity"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "提交时间" : "Submitted"}
</th>
<th className="text-left p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "状态" : "Status"}
</th>
<th className="text-right p-4 text-sm font-medium text-muted-foreground">
{language === "zh" ? "操作" : "Actions"}
</th>
</tr>
</thead>
<tbody>
{agentApprovals.map((approval) => (
<tr key={approval.id} className="border-b border-border last:border-0 hover:bg-muted/50">
<td className="p-4 text-sm text-foreground font-medium">{approval.channelName}</td>
<td className="p-4 text-sm text-foreground">{approval.agentType}</td>
<td className="p-4 text-sm text-muted-foreground">{approval.requestedQuantity}</td>
<td className="p-4 text-sm text-muted-foreground">{approval.submittedAt}</td>
<td className="p-4">
<Badge
variant="secondary"
className="bg-yellow-500/10 text-yellow-500 border-yellow-500/20"
>
{language === "zh" ? "待审批" : "Pending"}
</Badge>
</td>
<td className="p-4 text-right">
<Button
size="sm"
variant="outline"
onClick={() => {
setSelectedAgentApproval(approval)
setIsAgentApprovalDialogOpen(true)
}}
>
{language === "zh" ? "审批" : "Review"}
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
<Dialog open={isAgentApprovalDialogOpen} onOpenChange={setIsAgentApprovalDialogOpen}>
<DialogContent className="bg-card border-border max-w-2xl">
<DialogHeader>
<DialogTitle>{language === "zh" ? "审批Agent申请" : "Review Agent Application"}</DialogTitle>
<DialogDescription>
{language === "zh"
? `审批渠道 "${selectedAgentApproval?.channelName}" 的Agent申请`
: `Review agent application from channel "${selectedAgentApproval?.channelName}"`}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-muted-foreground">
{language === "zh" ? "申请渠道" : "Channel"}
</label>
<p className="text-foreground mt-1">{selectedAgentApproval?.channelName}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground">
{language === "zh" ? "提交时间" : "Submitted At"}
</label>
<p className="text-foreground mt-1">{selectedAgentApproval?.submittedAt}</p>
</div>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground">
{language === "zh" ? "Agent类型" : "Agent Type"}
</label>
<p className="text-foreground mt-1">{selectedAgentApproval?.agentType}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground">
{language === "zh" ? "申请数量" : "Requested Quantity"}
</label>
<p className="text-foreground mt-1">{selectedAgentApproval?.requestedQuantity}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground">
{language === "zh" ? "申请理由" : "Reason"}
</label>
<p className="text-foreground mt-1">{selectedAgentApproval?.reason}</p>
</div>
</div>
<DialogFooter>
<Button
variant="outline"
className="border-destructive text-destructive hover:bg-destructive/10 bg-transparent"
onClick={() => {
setAgentApprovals(agentApprovals.filter((a) => a.id !== selectedAgentApproval?.id))
setIsAgentApprovalDialogOpen(false)
}}
>
{language === "zh" ? "拒绝" : "Reject"}
</Button>
<Button
className="bg-primary text-primary-foreground"
onClick={() => {
setAgentApprovals(agentApprovals.filter((a) => a.id !== selectedAgentApproval?.id))
setIsAgentApprovalDialogOpen(false)
}}
>
{language === "zh" ? "批准" : "Approve"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)}
{activeTab === "resources" && (
<div className="space-y-6">
<div>
<div className="flex items-center justify-between mb-4">
<div>
<h2 className="text-2xl font-bold text-foreground">
{language === "zh" ? "Agent计算资源分配" : "Agent Compute Resource Allocation"}
</h2>
<p className="text-sm text-muted-foreground mt-1">
{language === "zh"
? "为每个Agent单独配置CPU和内存资源"
: "Configure CPU and memory resources for each agent individually"}
</p>
</div>
</div>
{/* Agent Resource Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
{[
{
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) => (
<Card
key={agent.id}
className="p-4 bg-card border-border cursor-pointer hover:border-primary transition-colors"
onClick={() => {
setSelectedAgent(agent)
setComputeConfigOpen(true)
}}
>
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<h3 className="font-semibold text-foreground">{agent.name}</h3>
<p className="text-xs text-muted-foreground">{agent.id}</p>
</div>
<div
className={`px-2 py-1 rounded text-xs ${
agent.status === "active" ? "bg-green-500/20 text-green-500" : "bg-gray-500/20 text-gray-500"
}`}
>
{agent.status === "active"
? language === "zh"
? "活跃"
: "Active"
: language === "zh"
? "空闲"
: "Idle"}
</div>
</div>
<div className="space-y-3">
<div className="flex items-center justify-between text-sm">
<div className="flex items-center gap-2">
<Cpu className="h-4 w-4 text-blue-500" />
<span className="text-muted-foreground">CPU:</span>
</div>
<span className="font-medium text-foreground">
{agent.cpu} {language === "zh" ? "核" : "Cores"}
</span>
</div>
<div className="flex items-center justify-between text-sm">
<div className="flex items-center gap-2">
<Server className="h-4 w-4 text-green-500" />
<span className="text-muted-foreground">{language === "zh" ? "内存" : "Memory"}:</span>
</div>
<span className="font-medium text-foreground">{agent.memory} GB</span>
</div>
<div className="pt-2 border-t border-border">
<div className="space-y-2">
<div className="flex items-center justify-between text-xs">
<span className="text-muted-foreground">CPU {language === "zh" ? "使用率" : "Usage"}</span>
<span className="text-foreground">{agent.usage.cpu}%</span>
</div>
<div className="w-full bg-muted rounded-full h-1.5">
<div className="bg-blue-500 h-1.5 rounded-full" style={{ width: `${agent.usage.cpu}%` }} />
</div>
<div className="flex items-center justify-between text-xs">
<span className="text-muted-foreground">
{language === "zh" ? "内存使用率" : "Memory Usage"}
</span>
<span className="text-foreground">{agent.usage.memory}%</span>
</div>
<div className="w-full bg-muted rounded-full h-1.5">
<div
className="bg-green-500 h-1.5 rounded-full"
style={{ width: `${agent.usage.memory}%` }}
/>
</div>
</div>
</div>
</div>
<Button variant="ghost" size="sm" className="w-full mt-3">
<Settings className="h-4 w-4 mr-2" />
{language === "zh" ? "配置资源" : "Configure Resources"}
</Button>
</Card>
))}
</div>
</div>
{/* Agent Resource Configuration Dialog */}
<Dialog open={computeConfigOpen} onOpenChange={setComputeConfigOpen}>
<DialogContent className="bg-background border-border max-w-2xl">
<DialogHeader>
<DialogTitle className="text-foreground">
{language === "zh" ? "Agent资源配置" : "Agent Resource Configuration"}
</DialogTitle>
</DialogHeader>
<div className="space-y-6">
{selectedAgent && (
<>
<div className="p-4 bg-muted/50 rounded-lg">
<h4 className="font-semibold text-foreground mb-1">{selectedAgent.name}</h4>
<p className="text-sm text-muted-foreground">{selectedAgent.id}</p>
</div>
<div className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium text-foreground mb-1 block">
{language === "zh" ? "CPU核数" : "CPU Cores"}
</label>
<Input
type="number"
placeholder={language === "zh" ? "输入CPU核数" : "Enter CPU cores"}
defaultValue={selectedAgent.cpu}
min={1}
max={16}
className="bg-background border-border text-foreground"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "推荐范围: 1-16核" : "Recommended range: 1-16 cores"}
</p>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-foreground mb-1 block">
{language === "zh" ? "内存大小(GB)" : "Memory Size (GB)"}
</label>
<Input
type="number"
placeholder={language === "zh" ? "输入内存大小" : "Enter memory size"}
defaultValue={selectedAgent.memory}
min={1}
max={32}
className="bg-background border-border text-foreground"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "推荐范围: 1-32GB" : "Recommended range: 1-32GB"}
</p>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-foreground mb-1 block">
{language === "zh" ? "平台可用Agent个数" : "Available Agent Instances"}
</label>
<Input
type="number"
placeholder={language === "zh" ? "输入可用Agent个数" : "Enter available agents"}
defaultValue={10}
min={1}
max={100}
className="bg-background border-border text-foreground"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "推荐范围: 1-100个" : "Recommended range: 1-100 instances"}
</p>
</div>
<div className="p-3 bg-blue-500/10 border border-blue-500/20 rounded-lg">
<p className="text-sm text-foreground">
{language === "zh" ? "当前使用率: " : "Current Usage: "}
CPU {selectedAgent.usage.cpu}% · {language === "zh" ? "内存" : "Memory"}{" "}
{selectedAgent.usage.memory}%
</p>
</div>
</div>
</>
)}
<div className="flex justify-end gap-3">
<Button variant="outline" onClick={() => setComputeConfigOpen(false)}>
{language === "zh" ? "取消" : "Cancel"}
</Button>
<Button onClick={() => setComputeConfigOpen(false)}>
{language === "zh" ? "保存配置" : "Save Configuration"}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
<div className="flex items-center justify-between mb-4">
<div>
<h2 className="text-2xl font-bold text-foreground">
{language === "zh" ? "货源供应商管理" : "Goods Source Management"}
</h2>
<p className="text-sm text-muted-foreground mt-1">
{language === "zh" ? "管理平台的模型供应商配置" : "Manage platform model provider configurations"}
</p>
</div>
<Button className="bg-primary text-primary-foreground" onClick={() => handleAddProvider("model")}>
<Plus className="h-4 w-4 mr-2" />
{language === "zh" ? "添加模型供应商" : "Add Model Provider"}
</Button>
</div>
{/* Model Providers Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{[
{
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) => (
<Card
key={provider.name}
className="p-4 bg-card border-border hover:border-primary/50 transition-colors"
>
<div className="flex items-start justify-between mb-3">
<div>
<h4 className="font-semibold text-foreground">{provider.name}</h4>
<p className="text-xs text-muted-foreground">{provider.type}</p>
</div>
<Badge
variant={provider.status === "active" ? "default" : "secondary"}
className="bg-green-500/10 text-green-500 border-green-500/20"
>
{provider.status === "active"
? language === "zh"
? "活跃"
: "Active"
: language === "zh"
? "离线"
: "Offline"}
</Badge>
</div>
<div className="space-y-2 text-sm">
<div className="flex items-center justify-between">
<span className="text-muted-foreground">{language === "zh" ? "支持模型" : "Models"}</span>
<span className="text-foreground font-medium">{provider.models.length}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground">RPM:</span>
<span className="text-foreground font-medium">{provider.rpm.toLocaleString()}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground">TPM:</span>
<span className="text-foreground font-medium">{provider.tpm.toLocaleString()}</span>
</div>
</div>
<div className="flex gap-2 mt-4">
<Button
variant="outline"
size="sm"
className="flex-1 bg-transparent"
onClick={() => handleConfigProvider(provider, "model")}
>
<Settings className="h-3 w-3 mr-1" />
{language === "zh" ? "配置" : "Config"}
</Button>
<Button variant="outline" size="sm" className="flex-1 bg-transparent">
<Zap className="h-3 w-3 mr-1" />
{language === "zh" ? "测试延迟" : "Test Latency"}
</Button>
</div>
</Card>
))}
</div>
</div>
)}
{activeTab === "monitoring" && (
<div className="space-y-6">
{/* Agent health monitoring */}
<div className="mb-6">
<h2 className="text-2xl font-bold text-foreground">{text.agentHealthMonitoring}</h2>
<p className="text-sm text-muted-foreground mt-1">{text.agentHealthDesc}</p>
</div>
{/* Agent health cards grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{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 (
<Card key={agent.id} className="p-6 bg-card border-border hover:border-primary/50 transition-colors">
{/* Agent header */}
<div className="flex items-start justify-between mb-4">
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-primary/10">
<Bot className="h-5 w-5 text-primary" />
</div>
<div>
<h3 className="text-base font-semibold text-foreground">
{language === "zh" ? agent.nameCn : agent.name}
</h3>
<p className="text-xs text-muted-foreground">{agent.lastActive}</p>
</div>
</div>
<Badge className={statusColor}>{statusText}</Badge>
</div>
{/* Agent metrics */}
<div className="space-y-3">
{/* CPU Usage */}
<div>
<div className="flex justify-between text-xs mb-1">
<span className="text-muted-foreground">{text.cpuUsageLabel}</span>
<span className="text-foreground font-medium">{agent.cpuUsage}%</span>
</div>
<div className="w-full bg-muted rounded-full h-2">
<div
className={`h-2 rounded-full ${
agent.cpuUsage > 80
? "bg-red-500"
: agent.cpuUsage > 60
? "bg-yellow-500"
: "bg-green-500"
}`}
style={{ width: `${agent.cpuUsage}%` }}
/>
</div>
</div>
{/* Memory Usage */}
<div>
<div className="flex justify-between text-xs mb-1">
<span className="text-muted-foreground">{text.memoryUsageLabel}</span>
<span className="text-foreground font-medium">{agent.memoryUsage}%</span>
</div>
<div className="w-full bg-muted rounded-full h-2">
<div
className={`h-2 rounded-full ${
agent.memoryUsage > 80
? "bg-red-500"
: agent.memoryUsage > 60
? "bg-yellow-500"
: "bg-green-500"
}`}
style={{ width: `${agent.memoryUsage}%` }}
/>
</div>
</div>
{/* Stats grid */}
<div className="grid grid-cols-2 gap-3 pt-3 border-t border-border">
<div>
<p className="text-xs text-muted-foreground">{text.responseTime}</p>
<p className="text-sm font-semibold text-foreground">
{agent.responseTime}
{text.ms}
</p>
</div>
<div>
<p className="text-xs text-muted-foreground">{text.requestCount}</p>
<p className="text-sm font-semibold text-foreground">
{agent.requestCount.toLocaleString()} {text.requests}
</p>
</div>
<div>
<p className="text-xs text-muted-foreground">{text.errorRate}</p>
<p className="text-sm font-semibold text-foreground">{agent.errorRate}%</p>
</div>
<div>
<p className="text-xs text-muted-foreground">{text.uptime}</p>
<p className="text-sm font-semibold text-foreground">{agent.uptime}</p>
</div>
</div>
</div>
</Card>
)
})}
</div>
</div>
)}
{activeTab === "billing" && (
<div className="space-y-6">
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-bold text-foreground">{t("计费管理", "Billing Management")}</h2>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
className="gap-2 bg-transparent"
onClick={() => setShowDateDialog(true)}
>
<Calendar className="h-4 w-4" />
{t("时间查询", "Date Query")}
</Button>
<Button
variant="outline"
size="sm"
className="gap-2 bg-transparent"
onClick={() => setShowFilterDialog(true)}
>
<Filter className="h-4 w-4" />
{t("筛选", "Filter")}
</Button>
<Button
variant="outline"
size="sm"
className="gap-2 bg-transparent"
onClick={() => setShowExportDialog(true)}
>
<Download className="h-4 w-4" />
{t("导出", "Export")}
</Button>
</div>
</div>
{/* 计费维度切换 */}
<div className="flex gap-2 border-b border-border">
<button
onClick={() => setBillingView("channel")}
className={`px-4 py-2 font-medium transition-colors ${
billingView === "channel"
? "border-b-2 border-primary text-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("渠道维度", "Channel Dimension")}
</button>
<button
onClick={() => setBillingView("tenant")}
className={`px-4 py-2 font-medium transition-colors ${
billingView === "tenant"
? "border-b-2 border-primary text-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("租户维度", "Tenant Dimension")}
</button>
<button
onClick={() => setBillingView("calls")}
className={`px-4 py-2 font-medium transition-colors ${
billingView === "calls"
? "border-b-2 border-primary text-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t("调用记录", "Call Records")}
</button>
</div>
{/* 渠道维度计费 */}
{billingView === "channel" && (
<div className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card className="p-4 bg-card border-border">
<div className="text-sm text-muted-foreground">{t("渠道总数", "Total Channels")}</div>
<div className="text-2xl font-bold text-foreground mt-1">12</div>
</Card>
<Card className="p-4 bg-card border-border">
<div className="text-sm text-muted-foreground">{t("总计费额", "Total Billing")}</div>
<div className="text-2xl font-bold text-foreground mt-1">$52,840</div>
</Card>
<Card className="p-4 bg-card border-border">
<div className="text-sm text-muted-foreground">{t("总EU消耗", "Total EU Consumption")}</div>
<div className="text-2xl font-bold text-foreground mt-1">528,400 EU</div>
</Card>
</div>
<Card className="p-6 bg-card border-border">
<h3 className="text-lg font-semibold text-foreground mb-4">
{t("渠道计费详情", "Channel Billing Details")}
</h3>
<div className="overflow-x-auto">
<table className="w-full">
<thead className="border-b border-border">
<tr className="text-left text-sm text-muted-foreground">
<th className="pb-3 font-medium">{t("渠道名称", "Channel Name")}</th>
<th className="pb-3 font-medium">{t("调用次数", "Call Count")}</th>
<th className="pb-3 font-medium">{t("总EU", "Total EU")}</th>
<th className="pb-3 font-medium">{t("渠道总价", "Channel Total")}</th>
<th className="pb-3 font-medium">{t("计费周期", "Billing Period")}</th>
</tr>
</thead>
<tbody className="text-sm">
{[
{ 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) => (
<tr key={idx} className="border-b border-border/50">
<td className="py-3 text-foreground font-medium">{channel.name}</td>
<td className="py-3 text-foreground">{channel.calls.toLocaleString()}</td>
<td className="py-3 text-foreground">{channel.eu.toLocaleString()} EU</td>
<td className="py-3 text-foreground font-semibold">{channel.total}</td>
<td className="py-3 text-muted-foreground">{channel.period}</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
)}
{/* 租户维度计费 */}
{billingView === "tenant" && (
<div className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card className="p-4 bg-card border-border">
<div className="text-sm text-muted-foreground">{t("租户总数", "Total Tenants")}</div>
<div className="text-2xl font-bold text-foreground mt-1">156</div>
</Card>
<Card className="p-4 bg-card border-border">
<div className="text-sm text-muted-foreground">{t("用户总价", "User Total")}</div>
<div className="text-2xl font-bold text-foreground mt-1">$52,840</div>
</Card>
<Card className="p-4 bg-card border-border">
<div className="text-sm text-muted-foreground">{t("平均消费", "Average Spending")}</div>
<div className="text-2xl font-bold text-foreground mt-1">$339</div>
</Card>
</div>
<Card className="p-6 bg-card border-border">
<h3 className="text-lg font-semibold text-foreground mb-4">
{t("租户计费详情", "Tenant Billing Details")}
</h3>
<div className="overflow-x-auto">
<table className="w-full">
<thead className="border-b border-border">
<tr className="text-left text-sm text-muted-foreground">
<th className="pb-3 font-medium">{t("租户名称", "Tenant Name")}</th>
<th className="pb-3 font-medium">{t("所属渠道", "Channel")}</th>
<th className="pb-3 font-medium">{t("调用次数", "Call Count")}</th>
<th className="pb-3 font-medium">{t("总EU", "Total EU")}</th>
<th className="pb-3 font-medium">{t("用户总价", "User Total")}</th>
<th className="pb-3 font-medium">{t("计费周期", "Billing Period")}</th>
</tr>
</thead>
<tbody className="text-sm">
{[
{
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) => (
<tr key={idx} className="border-b border-border/50">
<td className="py-3 text-foreground font-medium">{tenant.name}</td>
<td className="py-3 text-muted-foreground">{tenant.channel}</td>
<td className="py-3 text-foreground">{tenant.calls.toLocaleString()}</td>
<td className="py-3 text-foreground">{tenant.eu.toLocaleString()} EU</td>
<td className="py-3 text-foreground font-semibold">{tenant.total}</td>
<td className="py-3 text-muted-foreground">{tenant.period}</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
)}
{/* 调用记录 */}
{billingView === "calls" && (
<div className="space-y-4">
<Card className="p-6 bg-card border-border">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-foreground">
{t("调用记录明细", "Call Records Detail")}
</h3>
<div className="text-sm text-muted-foreground">
{t("EU计算规则:1 EU = 10秒调用时间", "EU Calculation: 1 EU = 10 seconds call time")}
</div>
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead className="border-b border-border">
<tr className="text-left text-sm text-muted-foreground">
<th className="pb-3 font-medium">{t("调用ID", "Call ID")}</th>
<th className="pb-3 font-medium">{t("租户", "Tenant")}</th>
<th className="pb-3 font-medium">{t("渠道", "Channel")}</th>
<th className="pb-3 font-medium">{t("调用时间", "Call Time")}</th>
<th className="pb-3 font-medium">{t("时长(秒)", "Duration(s)")}</th>
<th className="pb-3 font-medium">{t("EU", "EU")}</th>
<th className="pb-3 font-medium">{t("单次调用总价", "Single Call Cost")}</th>
<th className="pb-3 font-medium">{t("时间戳", "Timestamp")}</th>
</tr>
</thead>
<tbody className="text-sm">
{[
{
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) => (
<tr key={idx} className="border-b border-border/50">
<td className="py-3 text-foreground font-mono text-xs">{call.id}</td>
<td className="py-3 text-foreground">{call.tenant}</td>
<td className="py-3 text-muted-foreground">{call.channel}</td>
<td className="py-3 text-foreground">{call.time}</td>
<td className="py-3 text-foreground">{call.duration}s</td>
<td className="py-3 text-foreground">{call.eu} EU</td>
<td className="py-3 text-foreground font-semibold">{call.cost}</td>
<td className="py-3 text-muted-foreground text-xs">{call.time}</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
)}
<Dialog open={showFilterDialog} onOpenChange={setShowFilterDialog}>
<DialogContent className="bg-background border-border">
<DialogHeader>
<DialogTitle>{t("筛选选项", "Filter Options")}</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div>
<Label>{t("客户名称", "Customer Name")}</Label>
<Input
placeholder={t("输入客户名称", "Enter customer name")}
className="bg-background border-border text-foreground"
/>
</div>
<div>
<Label>{t("最小调用次数", "Min Calls")}</Label>
<Input type="number" placeholder="0" className="bg-background border-border text-foreground" />
</div>
<div>
<Label>{t("最大调用次数", "Max Calls")}</Label>
<Input type="number" placeholder="10000" className="bg-background border-border text-foreground" />
</div>
<Button className="w-full">{t("应用筛选", "Apply Filter")}</Button>
</div>
</DialogContent>
</Dialog>
<Dialog open={showDateDialog} onOpenChange={setShowDateDialog}>
<DialogContent className="bg-background border-border">
<DialogHeader>
<DialogTitle>{t("时间范围", "Date & Time Range")}</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div>
<Label>{t("开始时间 (年-月-日 时:分)", "Start Date & Time (YYYY-MM-DD HH:MM)")}</Label>
<Input type="datetime-local" className="bg-background border-border text-foreground" />
</div>
<div>
<Label>{t("结束时间 (年-月-日 时:分)", "End Date & Time (YYYY-MM-DD HH:MM)")}</Label>
<Input type="datetime-local" className="bg-background border-border text-foreground" />
</div>
<Button className="w-full">{t("查询", "Query")}</Button>
</div>
</DialogContent>
</Dialog>
<Dialog open={showExportDialog} onOpenChange={setShowExportDialog}>
<DialogContent className="bg-background border-border">
<DialogHeader>
<DialogTitle>{t("导出格式", "Export Format")}</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<p className="text-sm text-muted-foreground">{t("选择导出文件格式", "Select export file format")}</p>
<div className="grid grid-cols-2 gap-3">
<Button variant="outline" className="h-20 flex-col gap-2 bg-transparent">
<Download className="h-5 w-5" />
<span>Excel (.xlsx)</span>
</Button>
<Button variant="outline" className="h-20 flex-col gap-2 bg-transparent">
<Download className="h-5 w-5" />
<span>CSV (.csv)</span>
</Button>
<Button variant="outline" className="h-20 flex-col gap-2 col-span-2 bg-transparent">
<Download className="h-5 w-5" />
<span>PDF (.pdf)</span>
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</div>
)}
{activeTab === "settings" && (
<div className="space-y-6">
<div>
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-bold text-foreground">{t("用户身份设置", "User Role Settings")}</h2>
<Button onClick={() => setShowAddAdminDialog(true)}>
<Plus className="w-4 h-4 mr-2" />
{t("添加管理员", "Add Administrator")}
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
{/* Billing Admin Role Card */}
<Card
className={`p-6 cursor-pointer transition-all ${selectedRole === "billing-admin" ? "border-primary bg-primary/5" : "bg-card border-border hover:border-primary/50"}`}
onClick={() => setSelectedRole("billing-admin")}
>
<div className="flex items-start gap-3">
<DollarSign className="w-8 h-8 text-green-500" />
<div className="flex-1">
<h3 className="text-lg font-semibold text-foreground">
{t("计费管理员", "Billing Administrator")}
</h3>
<p className="text-sm text-muted-foreground mt-1">
{t(
"负责平台计费、账单和财务管理",
"Responsible for billing, invoices, and financial management",
)}
</p>
<div className="mt-3 flex items-center gap-2">
<Badge variant="secondary" className="text-xs">
{rolePermissions["billing-admin"].length} {t("个权限", "permissions")}
</Badge>
</div>
</div>
</div>
</Card>
{/* Operations Admin Role Card */}
<Card
className={`p-6 cursor-pointer transition-all ${selectedRole === "operations-admin" ? "border-primary bg-primary/5" : "bg-card border-border hover:border-primary/50"}`}
onClick={() => setSelectedRole("operations-admin")}
>
<div className="flex items-start gap-3">
<Settings className="w-8 h-8 text-blue-500" />
<div className="flex-1">
<h3 className="text-lg font-semibold text-foreground">
{t("运营管理员", "Operations Administrator")}
</h3>
<p className="text-sm text-muted-foreground mt-1">
{t("负责渠道、资源和系统监控管理", "Responsible for channels, resources, and monitoring")}
</p>
<div className="mt-3 flex items-center gap-2">
<Badge variant="secondary" className="text-xs">
{rolePermissions["operations-admin"].length} {t("个权限", "permissions")}
</Badge>
</div>
</div>
</div>
</Card>
{/* Super Admin Role Card */}
<Card
className={`p-6 cursor-pointer transition-all ${selectedRole === "super-admin" ? "border-primary bg-primary/5" : "bg-card border-border hover:border-primary/50"}`}
onClick={() => setSelectedRole("super-admin")}
>
<div className="flex items-start gap-3">
<Shield className="w-8 h-8 text-purple-500" />
<div className="flex-1">
<h3 className="text-lg font-semibold text-foreground">
{t("超级管理员", "Super Administrator")}
</h3>
<p className="text-sm text-muted-foreground mt-1">
{t("拥有所有权限,管理整个平台", "Full access to all platform features")}
</p>
<div className="mt-3 flex items-center gap-2">
<Badge variant="secondary" className="text-xs">
{rolePermissions["super-admin"].length} {t("个权限", "permissions")}
</Badge>
</div>
</div>
</div>
</Card>
</div>
{selectedRole && (
<Card className="p-6 bg-card border-border">
<h3 className="text-lg font-semibold text-foreground mb-4">
{t("标签页访问权限", "Tab Access Permissions")}
</h3>
<p className="text-sm text-muted-foreground mb-6">
{t("选择该角色可以访问的标签页", "Select which tabs this role can access")}
</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ 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 (
<div
key={tab.id}
className={`p-4 rounded-lg border-2 cursor-pointer transition-all ${
hasPermission
? "border-primary bg-primary/10"
: "border-border bg-background hover:border-primary/30"
}`}
onClick={() => {
setRolePermissions((prev) => ({
...prev,
[selectedRole]: hasPermission
? prev[selectedRole].filter((p) => p !== tab.id)
: [...prev[selectedRole], tab.id],
}))
}}
>
<div className="flex items-center gap-2 mb-2">
<span className="text-xl">{tab.icon}</span>
{hasPermission && <Check className="w-4 h-4 text-primary ml-auto" />}
</div>
<p className="text-sm font-medium text-foreground">{tab.label}</p>
</div>
)
})}
</div>
<Button
className="mt-6"
onClick={() => {
console.log("[v0] Saving permissions for", selectedRole, rolePermissions[selectedRole])
}}
>
{t("保存权限配置", "Save Permissions")}
</Button>
</Card>
)}
</div>
</div>
)}
</main>
{/* Subscription level assignment dialog */}
<Dialog open={isSubscriptionDialogOpen} onOpenChange={setIsSubscriptionDialogOpen}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle>{text.assignSubscriptionLevel}</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<p className="text-sm text-muted-foreground">
{language === "zh" ? "为租户选择订阅级别:" : "Select a subscription level for the tenant:"}
</p>
<div className="space-y-3">
{subscriptionLevels.map((level) => (
<div
key={level.id}
onClick={() => 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"
}`}
>
<div className="flex items-center justify-between">
<div>
<h4 className="font-semibold text-foreground">{level.name}</h4>
<p className="text-sm text-muted-foreground">{level.description}</p>
</div>
<div
className={`w-5 h-5 rounded-full border-2 flex items-center justify-center ${
selectedSubscriptionLevel === level.id ? "border-primary bg-primary" : "border-border"
}`}
>
{selectedSubscriptionLevel === level.id && <div className="w-2 h-2 rounded-full bg-white" />}
</div>
</div>
</div>
))}
</div>
<div className="flex gap-2 justify-end pt-4">
<Button variant="outline" onClick={() => setIsSubscriptionDialogOpen(false)}>
{language === "zh" ? "取消" : "Cancel"}
</Button>
<Button
onClick={() => {
// Handle subscription level assignment
console.log(`Assigning subscription level ${selectedSubscriptionLevel} to tenant ${selectedTenant}`)
setIsSubscriptionDialogOpen(false)
}}
>
{language === "zh" ? "确认分配" : "Confirm Assignment"}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
{/* Dialogs for Add/Config Provider */}
<Dialog open={isAddProviderOpen} onOpenChange={setIsAddProviderOpen}>
<DialogContent className="bg-card border-border max-w-2xl">
<DialogHeader>
<DialogTitle>
{language === "zh"
? providerType === "model"
? "添加模型供应商"
: "添加数据供应商"
: providerType === "model"
? "Add Model Provider"
: "Add Data Provider"}
</DialogTitle>
<DialogDescription>
{language === "zh" ? "填写供应商信息以添加新的货源" : "Fill in provider information to add a new source"}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="provider-name">{language === "zh" ? "供应商名称" : "Provider Name"}</Label>
<Input
id="provider-name"
placeholder={language === "zh" ? "例: OpenAI" : "e.g., OpenAI"}
value={providerForm.name}
onChange={(e) => setProviderForm({ ...providerForm, name: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div className="space-y-2">
<Label htmlFor="provider-url">{language === "zh" ? "API URL / 服务端点" : "API URL / Endpoint"}</Label>
<Input
id="provider-url"
placeholder={language === "zh" ? "例: https://api.openai.com/v1" : "e.g., https://api.openai.com/v1"}
value={providerForm.url}
onChange={(e) => setProviderForm({ ...providerForm, url: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div className="space-y-2">
<Label htmlFor="provider-key">{language === "zh" ? "API 密钥" : "API Key"}</Label>
<Input
id="provider-key"
type="password"
placeholder={language === "zh" ? "输入 API 密钥" : "Enter API key"}
value={providerForm.apiKey}
onChange={(e) => setProviderForm({ ...providerForm, apiKey: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
{providerType === "model" && (
<>
<div className="space-y-2">
<Label htmlFor="provider-models">{language === "zh" ? "可调用模型" : "Available Models"}</Label>
<Input
id="provider-models"
placeholder={
language === "zh"
? "例: gpt-4, gpt-3.5-turbo (逗号分隔)"
: "e.g., gpt-4, gpt-3.5-turbo (comma separated)"
}
value={providerForm.models}
onChange={(e) => setProviderForm({ ...providerForm, models: e.target.value })}
className="bg-background border-border text-foreground"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "多个模型请用逗号分隔" : "Separate multiple models with commas"}
</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="provider-rpm">
{language === "zh" ? "RPM (每分钟请求数)" : "RPM (Requests Per Minute)"}
</Label>
<Input
id="provider-rpm"
type="number"
placeholder={language === "zh" ? "例: 10000" : "e.g., 10000"}
value={providerForm.rpm}
onChange={(e) => setProviderForm({ ...providerForm, rpm: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div className="space-y-2">
<Label htmlFor="provider-tpm">
{language === "zh" ? "TPM (每分钟令牌数)" : "TPM (Tokens Per Minute)"}
</Label>
<Input
id="provider-tpm"
type="number"
placeholder={language === "zh" ? "例: 500000" : "e.g., 500000"}
value={providerForm.tpm}
onChange={(e) => setProviderForm({ ...providerForm, tpm: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
</div>
</>
)}
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsAddProviderOpen(false)}>
{text.cancel}
</Button>
<Button className="bg-primary text-primary-foreground" onClick={handleSaveProvider}>
{language === "zh" ? "添加供应商" : "Add Provider"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog open={isConfigProviderOpen} onOpenChange={setIsConfigProviderOpen}>
<DialogContent className="bg-card border-border max-w-2xl">
<DialogHeader>
<DialogTitle>{language === "zh" ? "配置供应商" : "Configure Provider"}</DialogTitle>
<DialogDescription>
{language === "zh"
? `修改 "${selectedProvider?.name}" 的配置信息`
: `Modify configuration for "${selectedProvider?.name}"`}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="config-provider-name">{language === "zh" ? "供应商名称" : "Provider Name"}</Label>
<Input
id="config-provider-name"
value={providerForm.name}
onChange={(e) => setProviderForm({ ...providerForm, name: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div className="space-y-2">
<Label htmlFor="config-provider-url">
{language === "zh" ? "API URL / 服务端点" : "API URL / Endpoint"}
</Label>
<Input
id="config-provider-url"
value={providerForm.url}
onChange={(e) => setProviderForm({ ...providerForm, url: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div className="space-y-2">
<Label htmlFor="config-provider-key">{language === "zh" ? "API 密钥" : "API Key"}</Label>
<Input
id="config-provider-key"
type="password"
value={providerForm.apiKey}
onChange={(e) => setProviderForm({ ...providerForm, apiKey: e.target.value })}
className="bg-background border-border text-foreground"
/>
<p className="text-xs text-muted-foreground">
{language === "zh" ? "留空则不修改密钥" : "Leave blank to keep existing key"}
</p>
</div>
{providerType === "model" && (
<>
<div className="space-y-2">
<Label htmlFor="config-provider-models">
{language === "zh" ? "可调用模型" : "Available Models"}
</Label>
<Input
id="config-provider-models"
value={providerForm.models}
onChange={(e) => setProviderForm({ ...providerForm, models: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="config-provider-rpm">
{language === "zh" ? "RPM (每分钟请求数)" : "RPM (Requests Per Minute)"}
</Label>
<Input
id="config-provider-rpm"
type="number"
value={providerForm.rpm}
onChange={(e) => setProviderForm({ ...providerForm, rpm: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div className="space-y-2">
<Label htmlFor="config-provider-tpm">
{language === "zh" ? "TPM (每分钟令牌数)" : "TPM (Tokens Per Minute)"}
</Label>
<Input
id="config-provider-tpm"
type="number"
value={providerForm.tpm}
onChange={(e) => setProviderForm({ ...providerForm, tpm: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
</div>
</>
)}
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsConfigProviderOpen(false)}>
{text.cancel}
</Button>
<Button className="bg-primary text-primary-foreground" onClick={handleSaveProvider}>
{language === "zh" ? "保存修改" : "Save Changes"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Dialog for Add New Admin */}
<Dialog open={showAddAdminDialog} onOpenChange={setShowAddAdminDialog}>
<DialogContent className="max-w-md bg-background border-border">
<DialogHeader>
<DialogTitle className="text-foreground">{t("添加新管理员", "Add New Administrator")}</DialogTitle>
<DialogDescription className="text-muted-foreground">
{t("创建新的管理员账户并分配角色", "Create a new administrator account and assign a role")}
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div>
<label className="text-sm font-medium text-foreground mb-1 block">{t("姓名", "Name")}</label>
<Input
placeholder={t("输入管理员姓名", "Enter administrator name")}
value={newAdminForm.name}
onChange={(e) => setNewAdminForm({ ...newAdminForm, name: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">{t("邮箱", "Email")}</label>
<Input
type="email"
placeholder={t("输入邮箱地址", "Enter email address")}
value={newAdminForm.email}
onChange={(e) => setNewAdminForm({ ...newAdminForm, email: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">{t("密码", "Password")}</label>
<Input
type="password"
placeholder={t("设置登录密码", "Set login password")}
value={newAdminForm.password}
onChange={(e) => setNewAdminForm({ ...newAdminForm, password: e.target.value })}
className="bg-background border-border text-foreground"
/>
</div>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">{t("角色", "Role")}</label>
<Select
value={newAdminForm.role}
onValueChange={(value) => setNewAdminForm({ ...newAdminForm, role: value })}
>
<SelectTrigger className="bg-background border-border text-foreground">
<SelectValue />
</SelectTrigger>
<SelectContent className="bg-background border-border">
<SelectItem value="billing-admin" className="text-foreground">
{t("计费管理员", "Billing Administrator")}
</SelectItem>
<SelectItem value="operations-admin" className="text-foreground">
{t("运营管理员", "Operations Administrator")}
</SelectItem>
<SelectItem value="super-admin" className="text-foreground">
{t("超级管理员", "Super Administrator")}
</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground mt-1">
{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")}
</p>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowAddAdminDialog(false)}>
{t("取消", "Cancel")}
</Button>
<Button
onClick={() => {
console.log("[v0] Creating admin:", newAdminForm)
setShowAddAdminDialog(false)
setNewAdminForm({ name: "", email: "", password: "", role: "billing-admin" })
}}
disabled={!newAdminForm.name || !newAdminForm.email || !newAdminForm.password}
>
{t("创建管理员", "Create Administrator")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}