import { useMemo, useState, type FormEvent, type ReactNode } from 'react'; import { Building2, CircleDollarSign, KeyRound, Pencil, Plus, RotateCcw, Trash2, UserRound, UsersRound } from 'lucide-react'; import type { GatewayTenant, GatewayTenantUpsertRequest, GatewayUser, GatewayUserUpsertRequest, GatewayWalletAccount, UserGroup, UserGroupUpsertRequest, WalletRechargeRequest, } from '@easyai-ai-gateway/contracts'; import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmDialog, FormDialog, Input, Label, ScreenMessage, Select, Table, TableCell, TableHead, TableRow, Textarea, } from '../../components/ui'; import type { ConsoleData } from '../../app-state'; import type { LoadState } from '../../types'; type TenantForm = { tenantKey: string; source: string; externalTenantId: string; name: string; description: string; defaultUserGroupId: string; planKey: string; billingProfileJson: string; rateLimitPolicyJson: string; authPolicyJson: string; metadataJson: string; status: string; }; type UserForm = { userKey: string; source: string; externalUserId: string; username: string; displayName: string; email: string; phone: string; avatarUrl: string; password: string; gatewayTenantId: string; tenantId: string; tenantKey: string; defaultUserGroupId: string; roles: string; authProfileJson: string; metadataJson: string; status: string; }; type WalletForm = { currency: string; amount: string; reason: string; }; type UserGroupForm = { groupKey: string; name: string; description: string; source: string; priority: string; rechargeDiscountFactor: string; rechargeDiscountPolicy: Record; billingDiscountFactor: string; billingDiscountPolicy: Record; rateLimitPolicyJson: string; quotaPolicyJson: string; metadataJson: string; status: string; }; const tenantStatuses = ['active', 'disabled', 'locked']; const userStatuses = ['active', 'disabled', 'locked']; const userGroupStatuses = ['active', 'disabled']; const sourceOptions = ['gateway', 'server-main', 'sync']; const roleOptions = [ { label: '普通用户', value: 'user' }, { label: '创作者', value: 'creator' }, { label: '管理', value: 'operator' }, { label: '超级管理员', value: 'manager' }, { label: '管理员', value: 'admin' }, ]; export function TenantsPanel(props: IdentityPanelProps) { const [dialogOpen, setDialogOpen] = useState(false); const [editingId, setEditingId] = useState(''); const [form, setForm] = useState(() => defaultTenantForm()); const [localError, setLocalError] = useState(''); const [pendingDeleteTenant, setPendingDeleteTenant] = useState(null); function openCreateDialog() { setEditingId(''); setLocalError(''); setForm(defaultTenantForm()); setDialogOpen(true); } function editTenant(tenant: GatewayTenant) { setEditingId(tenant.id); setLocalError(''); setForm(tenantToForm(tenant)); setDialogOpen(true); } function closeDialog() { setDialogOpen(false); setEditingId(''); setLocalError(''); } async function submit(event: FormEvent) { event.preventDefault(); setLocalError(''); try { await props.onSaveTenant(formToTenantPayload(form), editingId || undefined); closeDialog(); } catch (err) { setLocalError(err instanceof Error ? err.message : '保存租户失败'); } } async function deleteTenant(tenant: GatewayTenant) { try { await props.onDeleteTenant(tenant.id); setPendingDeleteTenant(null); } catch (err) { setLocalError(err instanceof Error ? err.message : '删除租户失败'); } } return (
setLocalError('')} /> } message={identityHeaderMessage(props)} title="租户管理" actionLabel="新增租户" onCreate={openCreateDialog} /> {props.data.tenants.length ? ( 租户 来源 默认用户组 套餐 状态 操作 {props.data.tenants.map((tenant) => ( {tenant.source} {groupName(props.data.userGroups, tenant.defaultUserGroupId)} {tenant.planKey || '未设置'} {tenant.status} editTenant(tenant)} onDelete={() => setPendingDeleteTenant(tenant)} /> ))}
) : ( )} } open={dialogOpen} title={editingId ? '编辑租户' : '新增租户'} onClose={closeDialog} onSubmit={submit} > setForm({ ...form, billingProfileJson: value })} /> setForm({ ...form, rateLimitPolicyJson: value })} /> setForm({ ...form, authPolicyJson: value })} /> setForm({ ...form, metadataJson: value })} /> setPendingDeleteTenant(null)} onConfirm={() => pendingDeleteTenant ? deleteTenant(pendingDeleteTenant) : undefined} />
); } export function UsersPanel(props: IdentityPanelProps) { const [dialogOpen, setDialogOpen] = useState(false); const [editingId, setEditingId] = useState(''); const [form, setForm] = useState(() => defaultUserForm(props.data.tenants[0])); const [localError, setLocalError] = useState(''); const [pendingDeleteUser, setPendingDeleteUser] = useState(null); const [walletUser, setWalletUser] = useState(null); const [walletForm, setWalletForm] = useState(() => defaultWalletForm()); const tenantById = useMemo(() => new Map(props.data.tenants.map((tenant) => [tenant.id, tenant])), [props.data.tenants]); function openCreateDialog() { setEditingId(''); setLocalError(''); setForm(defaultUserForm(props.data.tenants[0])); setDialogOpen(true); } function editUser(user: GatewayUser) { setEditingId(user.id); setLocalError(''); setForm(userToForm(user)); setDialogOpen(true); } function closeDialog() { setDialogOpen(false); setEditingId(''); setLocalError(''); } function openWalletDialog(user: GatewayUser) { const wallet = primaryWallet(user); setLocalError(''); setWalletUser(user); setWalletForm({ currency: wallet?.currency ?? 'resource', amount: '', reason: defaultRechargeReason, }); } function closeWalletDialog() { setWalletUser(null); setWalletForm(defaultWalletForm()); } async function submit(event: FormEvent) { event.preventDefault(); setLocalError(''); try { await props.onSaveUser(formToUserPayload(form), editingId || undefined); closeDialog(); } catch (err) { setLocalError(err instanceof Error ? err.message : '保存用户失败'); } } async function deleteUser(user: GatewayUser) { try { await props.onDeleteUser(user.id); setPendingDeleteUser(null); } catch (err) { setLocalError(err instanceof Error ? err.message : '删除用户失败'); } } async function submitWallet(event: FormEvent) { event.preventDefault(); setLocalError(''); if (!walletUser) return; const amount = Number(walletForm.amount); if (!Number.isFinite(amount) || amount <= 0) { setLocalError('充值金额必须大于 0'); return; } if (!walletForm.reason.trim()) { setLocalError('请填写调整原因'); return; } try { await props.onRechargeUserWalletBalance(walletUser.id, { currency: walletForm.currency, amount, reason: walletForm.reason.trim(), idempotencyKey: newIdempotencyKey(), }); closeWalletDialog(); } catch (err) { setLocalError(err instanceof Error ? err.message : '充值用户余额失败'); } } function selectTenant(gatewayTenantId: string) { const tenant = tenantById.get(gatewayTenantId); setForm({ ...form, gatewayTenantId, tenantKey: tenant?.tenantKey ?? form.tenantKey }); } return (
setLocalError('')} /> } message={identityHeaderMessage(props)} title="用户管理" actionLabel="新增用户" onCreate={openCreateDialog} /> {props.data.users.length ? ( 用户 角色 租户 用户组 余额 来源 状态 操作 {props.data.users.map((user) => ( {roleLabel(user.roles)} {tenantName(props.data.tenants, user.gatewayTenantId, user.tenantKey)} {groupName(props.data.userGroups, user.defaultUserGroupId)} {walletSummary(user)} {user.source} {user.status} editUser(user)} onDelete={() => setPendingDeleteUser(user)} onWallet={() => openWalletDialog(user)} /> ))}
) : ( )} } open={dialogOpen} title={editingId ? '编辑用户' : '新增用户'} onClose={closeDialog} onSubmit={submit} > setForm({ ...form, authProfileJson: value })} /> setForm({ ...form, metadataJson: value })} /> setPendingDeleteUser(null)} onConfirm={() => pendingDeleteUser ? deleteUser(pendingDeleteUser) : undefined} /> } open={Boolean(walletUser)} title={`充值余额${walletUser ? ` · ${walletUser.displayName || walletUser.username}` : ''}`} onClose={closeWalletDialog} onSubmit={submitWallet} >