Files
aiops/frontend/src/components/cloud/cloud-account/AccountCard.tsx
T

179 lines
6.4 KiB
TypeScript

import { useTranslation } from 'react-i18next';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
Trash2,
Pencil,
Key,
CheckCircle2,
XCircle,
Loader2,
MapPin,
Wallet,
CloudDownload,
} from 'lucide-react';
import { CloudBrandIcon, getProviderColor } from '@/components/icons/CloudBrandIcon';
import type { JsonData } from '@/types/common';
import type { ActionLoading } from './types';
export interface AccountCardProps {
account: JsonData;
providerLabels: Record<string, string>;
actionLoading: ActionLoading;
onEdit: (account: JsonData) => void;
onDelete: (id: string) => void;
onDetail: (account: JsonData) => void;
onSyncRegions: (account: JsonData) => void;
onSyncBilling: (account: JsonData) => void;
onImport: (account: JsonData) => void;
}
export function AccountCard({
account,
providerLabels,
actionLoading,
onEdit,
onDelete,
onDetail,
onSyncRegions,
onSyncBilling,
onImport,
}: AccountCardProps) {
const { t } = useTranslation();
const accountIdStr = String(account.id);
const accessKey = account.access_key || account.accessKey || '';
const serverCount = account.server_count ?? account.serverCount ?? 0;
const lastSync = account.last_sync || account.lastSync || account.created_at || '-';
const monthlyCost = account.monthly_cost ?? account.monthlyCost ?? 0;
return (
<Card key={account.id} className="dg-card dg-card-hover">
<CardContent className="p-4">
<div className="flex items-start justify-between">
<div className="flex items-start gap-3 min-w-0">
<div
className="w-10 h-10 rounded-lg bg-[var(--dg-bg-tertiary)] flex items-center justify-center shrink-0"
style={{ borderBottom: `2px solid ${getProviderColor(account.provider)}` }}
>
<CloudBrandIcon provider={account.provider} className="h-6 w-6" />
</div>
<div className="min-w-0">
<button
type="button"
className="font-medium text-[var(--dg-text-primary)] hover:text-[var(--dg-blue)] text-left truncate block max-w-full"
onClick={() => onDetail(account)}
>
{account.name}
</button>
<p className="text-sm dg-text-secondary mt-0.5">
{providerLabels[account.provider] || account.provider}
</p>
<div className="flex items-center gap-3 mt-2">
{accessKey && (
<Badge variant="secondary" className="dg-badge-muted">
<Key className="h-3 w-3 mr-1" />
{accessKey.slice(0, 8)}...
</Badge>
)}
{account.status === 'active' && (
<Badge className="dg-status-running border-0">
<CheckCircle2 className="h-3 w-3 mr-1" />
{t('cloud.normal')}
</Badge>
)}
{account.status === 'inactive' && (
<Badge className="dg-status-stopped border-0">
<XCircle className="h-3 w-3 mr-1" />
{t('cloud.stopped')}
</Badge>
)}
{account.status === 'error' && (
<Badge className="dg-status-error border-0">
<Key className="h-3 w-3 mr-1" />
{t('cloud.error')}
</Badge>
)}
</div>
</div>
</div>
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
className="h-8 w-8 dg-text-muted hover:text-[var(--dg-blue)]"
onClick={() => onEdit(account)}
>
<Pencil className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 dg-text-muted hover:text-[var(--dg-red)]"
onClick={() => onDelete(account.id)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
<div className="grid grid-cols-3 gap-4 mt-4 pt-4 border-t border-[var(--dg-border)]">
<div>
<p className="text-xs dg-text-secondary">{t('cloud.monthlyFee')}</p>
<p className="text-lg font-medium text-[var(--dg-green)]">
¥{Number(monthlyCost).toLocaleString()}
</p>
</div>
<div>
<p className="text-xs dg-text-secondary">{t('cloud.serverCount')}</p>
<p className="text-lg font-medium text-[var(--dg-text-primary)]">{serverCount}</p>
</div>
<div>
<p className="text-xs dg-text-secondary">{t('cloud.syncTime')}</p>
<p className="text-sm text-[var(--dg-text-secondary)]">
{lastSync !== '-' ? new Date(lastSync).toLocaleString() : '-'}
</p>
</div>
</div>
<div className="flex flex-wrap gap-2 mt-3 pt-3 border-t border-[var(--dg-border)]">
<Button
variant="outline"
size="sm"
className="h-7 text-xs dg-btn-secondary"
disabled={actionLoading[accountIdStr] === 'regions'}
onClick={() => onSyncRegions(account)}
>
{actionLoading[accountIdStr] === 'regions' ? (
<Loader2 className="h-3 w-3 mr-1 animate-spin" />
) : (
<MapPin className="h-3 w-3 mr-1" />
)}
{t('cloud.syncRegions')}
</Button>
<Button
variant="outline"
size="sm"
className="h-7 text-xs dg-btn-secondary"
disabled={actionLoading[accountIdStr] === 'billing'}
onClick={() => onSyncBilling(account)}
>
{actionLoading[accountIdStr] === 'billing' ? (
<Loader2 className="h-3 w-3 mr-1 animate-spin" />
) : (
<Wallet className="h-3 w-3 mr-1" />
)}
{t('cloud.syncBilling')}
</Button>
<Button
size="sm"
className="h-7 text-xs dg-btn-primary"
onClick={() => onImport(account)}
>
<CloudDownload className="h-3 w-3 mr-1" />
{t('cloud.importHosts')}
</Button>
</div>
</CardContent>
</Card>
);
}