import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Plus, Search, Key, RefreshCw } from 'lucide-react'; import { toast } from 'sonner'; import { cloudApi } from '@/api'; import { CloudAccountDetailSheet } from './CloudAccountDetailSheet'; import { CloudServerImport } from '@/components/server/CloudServerImport'; import { PageShell } from '@/components/layout/PageShell'; import { PageLoading } from '@/components/layout/PageLoading'; import { PageEmpty } from '@/components/layout/PageEmpty'; import { StatStrip } from '@/components/ui/stat-strip'; import type { JsonData } from '@/types/common'; import { type AccountFormData, type ActionLoading, getInitialFormData, buildFormDataFromAccount, computeAccountStats, filterAccounts, getProviderLabels, } from './cloud-account/types'; import { AccountCard } from './cloud-account/AccountCard'; import { AccountFormDialog } from './cloud-account/AccountFormDialog'; export function CloudAccountManager() { const { t } = useTranslation(); const [accounts, setAccounts] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [selectedProvider, setSelectedProvider] = useState('all'); // 创建/编辑账号表单状态 const [formOpen, setFormOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [editingAccount, setEditingAccount] = useState(null); const [formData, setFormData] = useState(getInitialFormData()); const [detailAccount, setDetailAccount] = useState(null); const [detailOpen, setDetailOpen] = useState(false); const [importOpen, setImportOpen] = useState(false); const [importAccountId, setImportAccountId] = useState(); const [actionLoading, setActionLoading] = useState({}); const providerLabels = getProviderLabels(t); const setCardAction = (accountId: string | number, action: string | null) => { setActionLoading((prev) => { const next = { ...prev }; const key = String(accountId); if (action) next[key] = action; else delete next[key]; return next; }); }; const openDetail = (account: JsonData) => { setDetailAccount(account); setDetailOpen(true); }; const openImport = (account: JsonData) => { setImportAccountId(account.id); setImportOpen(true); }; const handleQuickSyncRegions = async (account: JsonData) => { setCardAction(account.id, 'regions'); try { await cloudApi.syncRegions(account.id); toast.success(t('cloud.syncRegionsSuccess')); loadData(); } catch (e) { toast.error(t('cloud.syncRegionsFailed'), { description: e instanceof Error ? e.message : String(e), }); } finally { setCardAction(account.id, null); } }; const handleQuickSyncBilling = async (account: JsonData) => { setCardAction(account.id, 'billing'); try { const res = await cloudApi.syncBilling(account.id); const errs = res?.errors || []; if (errs.length > 0) { toast.warning(t('cloud.syncBillingPartial'), { description: errs.join('\n') }); } else { toast.success(t('cloud.syncBillingSuccess'), { description: t('cloud.syncBillingResult', { date: res.billing_date, created: res.created, updated: res.updated, }), }); } loadData(); } catch (e) { toast.error(t('cloud.syncBillingFailed'), { description: e instanceof Error ? e.message : String(e), }); } finally { setCardAction(account.id, null); } }; const resetForm = () => { setEditingAccount(null); setFormData(getInitialFormData()); }; const handleOpenCreate = () => { resetForm(); setFormOpen(true); }; const handleOpenEdit = (account: JsonData) => { setEditingAccount(account); setFormData(buildFormDataFromAccount(account)); setFormOpen(true); }; const handleSubmit = async () => { if (!formData.name.trim() || !formData.provider.trim()) return; try { setSubmitting(true); const payload: JsonData = { name: formData.name.trim(), provider: formData.provider, access_key: formData.access_key.trim(), secret_key: formData.secret_key.trim(), monthly_cost: Number(formData.monthly_cost) || 0, status: formData.status, }; if (editingAccount) { await cloudApi.updateAccount(editingAccount.id, payload); } else { await cloudApi.createAccount(payload); } setFormOpen(false); loadData(); } catch (e) { console.error(editingAccount ? 'update account' : 'create account', e); } finally { setSubmitting(false); } }; const loadData = async () => { try { setLoading(true); const res: JsonData = await cloudApi.getAccounts(); setAccounts(res?.list || []); } catch (_e) { setAccounts([]); } finally { setLoading(false); } }; useEffect(() => { loadData(); }, []); const handleDelete = async (id: string) => { try { await cloudApi.deleteAccount(id); loadData(); } catch (_e) { // silently ignored } }; const filteredAccounts = filterAccounts(accounts, searchQuery, selectedProvider); const stats = computeAccountStats(accounts); const toolbar = (
setSearchQuery(e.target.value)} className="pl-10 h-9 dg-input" />
); return ( } metrics={ !loading ? ( 0 ? 'red' : 'muted', }, ]} /> ) : undefined } toolbar={toolbar} actions={ <> } > {loading ? ( ) : filteredAccounts.length === 0 ? ( } title={t('cloud.noAccountData')} action={ } /> ) : (
{filteredAccounts.map((account) => ( ))}
)} { if (detailAccount) { setDetailOpen(false); openImport(detailAccount); } }} /> { setImportOpen(false); setImportAccountId(undefined); loadData(); }} />
); }