feat(mining-admin): implement complete system accounts feature

- Add system account types and display metadata
- Create API layer with getList and getSummary endpoints
- Add React Query hooks for data fetching
- Create AccountCard, AccountsTable, SummaryCards components
- Refactor page with tabs, refresh button, and error handling
- Add Alert UI component

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-13 20:27:59 -08:00
parent 471702d562
commit d43a70de93
10 changed files with 768 additions and 111 deletions

View File

@ -1,128 +1,137 @@
'use client';
import { PageHeader } from '@/components/layout/page-header';
import { useQuery } from '@tanstack/react-query';
import { apiClient } from '@/lib/api/client';
import { formatDecimal } from '@/lib/utils/format';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Skeleton } from '@/components/ui/skeleton';
import { Building2, Flame, ShoppingCart, Coins } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { AlertCircle, RefreshCw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useQueryClient } from '@tanstack/react-query';
interface SystemAccount {
id: string;
accountType: string;
accountName: string;
balance: string;
description: string;
}
const accountIcons: Record<string, typeof Building2> = {
DISTRIBUTION_POOL: Coins,
BLACK_HOLE: Flame,
CIRCULATION_POOL: ShoppingCart,
SYSTEM_OPERATION: Building2,
SYSTEM_PROVINCE: Building2,
SYSTEM_CITY: Building2,
};
const accountColors: Record<string, string> = {
DISTRIBUTION_POOL: 'text-green-500',
BLACK_HOLE: 'text-orange-500',
CIRCULATION_POOL: 'text-blue-500',
SYSTEM_OPERATION: 'text-purple-500',
SYSTEM_PROVINCE: 'text-indigo-500',
SYSTEM_CITY: 'text-pink-500',
};
import {
useSystemAccountsSummary,
useCategorizedAccounts,
AccountCard,
AccountsTable,
SummaryCards,
} from '@/features/system-accounts';
export default function SystemAccountsPage() {
const { data: accounts, isLoading } = useQuery({
queryKey: ['system-accounts'],
queryFn: async () => {
const response = await apiClient.get('/system-accounts');
return response.data.data as SystemAccount[];
},
});
const queryClient = useQueryClient();
const mainAccounts = accounts?.filter((a) =>
['DISTRIBUTION_POOL', 'BLACK_HOLE', 'CIRCULATION_POOL'].includes(a.accountType)
);
const systemAccounts = accounts?.filter((a) =>
['SYSTEM_OPERATION', 'SYSTEM_PROVINCE', 'SYSTEM_CITY'].includes(a.accountType)
);
const {
data: summary,
isLoading: summaryLoading,
error: summaryError,
} = useSystemAccountsSummary();
const {
data: categorized,
isLoading: accountsLoading,
error: accountsError,
total,
} = useCategorizedAccounts();
const isLoading = summaryLoading || accountsLoading;
const hasError = summaryError || accountsError;
const handleRefresh = () => {
queryClient.invalidateQueries({ queryKey: ['system-accounts'] });
};
return (
<div className="space-y-6">
<PageHeader title="系统账户" description="查看系统账户余额" />
<PageHeader
title="系统账户"
description="查看系统账户余额和算力分布"
actions={
<Button
variant="outline"
size="sm"
onClick={handleRefresh}
disabled={isLoading}
>
<RefreshCw className={`h-4 w-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
</Button>
}
/>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{isLoading
? [...Array(3)].map((_, i) => (
<Card key={i}>
<CardContent className="p-6">
<Skeleton className="h-20 w-full" />
</CardContent>
</Card>
))
: mainAccounts?.map((account) => {
const Icon = accountIcons[account.accountType] || Building2;
const color = accountColors[account.accountType] || 'text-gray-500';
return (
<Card key={account.id}>
<CardContent className="p-6">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-muted-foreground">{account.accountName}</p>
<p className="text-2xl font-bold font-mono mt-1">{formatDecimal(account.balance, 4)}</p>
<p className="text-xs text-muted-foreground mt-2">{account.description}</p>
</div>
<Icon className={`h-8 w-8 ${color}`} />
</div>
</CardContent>
</Card>
);
})}
</div>
{hasError && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
</AlertDescription>
</Alert>
)}
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{isLoading ? (
[...Array(3)].map((_, i) => (
<TableRow key={i}>
{[...Array(4)].map((_, j) => (
<TableCell key={j}>
<Skeleton className="h-4 w-full" />
</TableCell>
{/* Summary Cards */}
<SummaryCards summary={summary} isLoading={summaryLoading} />
{/* Main Content with Tabs */}
<Tabs defaultValue="overview" className="space-y-4">
<TabsList>
<TabsTrigger value="overview"></TabsTrigger>
<TabsTrigger value="all"></TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-6">
{/* Main Pools - Card Grid */}
{categorized.mainPools.length > 0 && (
<div>
<h3 className="text-lg font-semibold mb-4"></h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{accountsLoading
? [...Array(3)].map((_, i) => (
<Card key={i}>
<CardContent className="p-6">
<Skeleton className="h-20 w-full" />
</CardContent>
</Card>
))
: categorized.mainPools.map((account) => (
<AccountCard key={account.accountType} account={account} />
))}
</TableRow>
))
) : (
systemAccounts?.map((account) => (
<TableRow key={account.id}>
<TableCell className="font-mono">{account.accountType}</TableCell>
<TableCell>{account.accountName}</TableCell>
<TableCell className="text-right font-mono">{formatDecimal(account.balance, 4)}</TableCell>
<TableCell className="text-muted-foreground">{account.description}</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</CardContent>
</Card>
</div>
</div>
)}
{/* System Distribution Accounts - Table */}
<AccountsTable
title="系统分配账户"
accounts={categorized.systemAccounts}
isLoading={accountsLoading}
showSyncInfo
/>
{/* Fixed Accounts if any */}
{categorized.fixedAccounts.length > 0 && (
<AccountsTable
title="固定账户"
accounts={categorized.fixedAccounts}
isLoading={accountsLoading}
showSyncInfo
/>
)}
</TabsContent>
<TabsContent value="all" className="space-y-6">
{/* All Accounts in one table */}
<AccountsTable
title={`全部系统账户 (${total})`}
accounts={[
...categorized.mainPools,
...categorized.systemAccounts,
...categorized.fixedAccounts,
...categorized.otherAccounts,
]}
isLoading={accountsLoading}
showSyncInfo
/>
</TabsContent>
</Tabs>
</div>
);
}

View File

@ -0,0 +1,59 @@
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const alertVariants = cva(
'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
{
variants: {
variant: {
default: 'bg-background text-foreground',
destructive:
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
},
},
defaultVariants: {
variant: 'default',
},
}
);
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
));
Alert.displayName = 'Alert';
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn('mb-1 font-medium leading-none tracking-tight', className)}
{...props}
/>
));
AlertTitle.displayName = 'AlertTitle';
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('text-sm [&_p]:leading-relaxed', className)}
{...props}
/>
));
AlertDescription.displayName = 'AlertDescription';
export { Alert, AlertTitle, AlertDescription };

View File

@ -0,0 +1,43 @@
import { apiClient } from '@/lib/api/client';
import type {
SystemAccount,
SystemAccountsResponse,
SystemAccountsSummary,
} from '@/types/system-account';
export const systemAccountsApi = {
/**
* Get all system accounts (merged local + synced data)
*/
getList: async (): Promise<SystemAccountsResponse> => {
const response = await apiClient.get('/system-accounts');
return response.data.data;
},
/**
* Get system accounts summary with mining config and circulation pool
*/
getSummary: async (): Promise<SystemAccountsSummary> => {
const response = await apiClient.get('/system-accounts/summary');
return response.data.data;
},
};
// Helper to categorize accounts for display
export function categorizeAccounts(accounts: SystemAccount[]) {
const mainPools = ['DISTRIBUTION_POOL', 'BLACK_HOLE', 'CIRCULATION_POOL'];
const systemAccounts = ['SYSTEM_OPERATION', 'SYSTEM_PROVINCE', 'SYSTEM_CITY'];
const fixedAccounts = ['COST_ACCOUNT', 'OPERATION_ACCOUNT', 'HQ_COMMUNITY', 'RWAD_POOL_PENDING'];
return {
mainPools: accounts.filter((a) => mainPools.includes(a.accountType)),
systemAccounts: accounts.filter((a) => systemAccounts.includes(a.accountType)),
fixedAccounts: accounts.filter((a) => fixedAccounts.includes(a.accountType)),
otherAccounts: accounts.filter(
(a) =>
!mainPools.includes(a.accountType) &&
!systemAccounts.includes(a.accountType) &&
!fixedAccounts.includes(a.accountType)
),
};
}

View File

@ -0,0 +1,65 @@
'use client';
import { Card, CardContent } from '@/components/ui/card';
import { formatDecimal } from '@/lib/utils/format';
import { getAccountDisplayInfo, type SystemAccount } from '@/types/system-account';
import { Coins, Flame, ShoppingCart, Building2, Wallet, Landmark, Box } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
const iconMap = {
coins: Coins,
flame: Flame,
'shopping-cart': ShoppingCart,
building: Building2,
wallet: Wallet,
landmark: Landmark,
box: Box,
};
interface AccountCardProps {
account: SystemAccount;
}
export function AccountCard({ account }: AccountCardProps) {
const displayInfo = getAccountDisplayInfo(account.accountType);
const Icon = iconMap[displayInfo.icon] || Building2;
// Use contributionBalance if available (synced), otherwise totalContribution (local)
const balance = account.contributionBalance || account.totalContribution || '0';
const displayName = account.name || displayInfo.label;
return (
<Card className="hover:shadow-md transition-shadow">
<CardContent className="p-6">
<div className="flex items-start justify-between">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<p className="text-sm font-medium text-muted-foreground truncate">
{displayName}
</p>
{account.source === 'synced' && (
<Badge variant="outline" className="text-xs shrink-0">
</Badge>
)}
</div>
<p className="text-2xl font-bold font-mono mt-1">
{formatDecimal(balance, 4)}
</p>
<p className="text-xs text-muted-foreground mt-2 line-clamp-2">
{account.description || displayInfo.description}
</p>
{account.contributionNeverExpires && (
<Badge variant="secondary" className="mt-2 text-xs">
</Badge>
)}
</div>
<div className={`p-3 rounded-lg ${displayInfo.bgColor} shrink-0 ml-4`}>
<Icon className={`h-6 w-6 ${displayInfo.color}`} />
</div>
</div>
</CardContent>
</Card>
);
}

View File

@ -0,0 +1,134 @@
'use client';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import { formatDecimal } from '@/lib/utils/format';
import { getAccountDisplayInfo, type SystemAccount } from '@/types/system-account';
import { formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale';
interface AccountsTableProps {
title: string;
accounts: SystemAccount[];
isLoading?: boolean;
showSyncInfo?: boolean;
}
export function AccountsTable({
title,
accounts,
isLoading = false,
showSyncInfo = false,
}: AccountsTableProps) {
return (
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
{title}
{accounts.length > 0 && (
<Badge variant="secondary" className="text-xs">
{accounts.length}
</Badge>
)}
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[200px]"></TableHead>
<TableHead></TableHead>
<TableHead className="text-right">/</TableHead>
<TableHead></TableHead>
{showSyncInfo && <TableHead></TableHead>}
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{isLoading ? (
[...Array(3)].map((_, i) => (
<TableRow key={i}>
{[...Array(showSyncInfo ? 6 : 5)].map((_, j) => (
<TableCell key={j}>
<Skeleton className="h-4 w-full" />
</TableCell>
))}
</TableRow>
))
) : accounts.length === 0 ? (
<TableRow>
<TableCell
colSpan={showSyncInfo ? 6 : 5}
className="text-center text-muted-foreground py-8"
>
</TableCell>
</TableRow>
) : (
accounts.map((account) => {
const displayInfo = getAccountDisplayInfo(account.accountType);
const balance = account.contributionBalance || account.totalContribution || '0';
return (
<TableRow key={account.accountType}>
<TableCell>
<div className="flex items-center gap-2">
<span
className={`inline-block w-2 h-2 rounded-full ${displayInfo.color.replace('text-', 'bg-')}`}
/>
<code className="text-xs bg-muted px-1.5 py-0.5 rounded">
{account.accountType}
</code>
</div>
</TableCell>
<TableCell className="font-medium">
{account.name || displayInfo.label}
</TableCell>
<TableCell className="text-right font-mono">
{formatDecimal(balance, 4)}
{account.contributionNeverExpires && (
<Badge variant="outline" className="ml-2 text-xs">
</Badge>
)}
</TableCell>
<TableCell>
<Badge
variant={account.source === 'synced' ? 'default' : 'secondary'}
className="text-xs"
>
{account.source === 'synced' ? 'CDC同步' : '本地'}
</Badge>
</TableCell>
{showSyncInfo && (
<TableCell className="text-muted-foreground text-sm">
{account.syncedAt
? formatDistanceToNow(new Date(account.syncedAt), {
addSuffix: true,
locale: zhCN,
})
: '-'}
</TableCell>
)}
<TableCell className="text-muted-foreground text-sm max-w-[200px] truncate">
{account.description || displayInfo.description}
</TableCell>
</TableRow>
);
})
)}
</TableBody>
</Table>
</CardContent>
</Card>
);
}

View File

@ -0,0 +1,3 @@
export { AccountCard } from './account-card';
export { AccountsTable } from './accounts-table';
export { SummaryCards } from './summary-cards';

View File

@ -0,0 +1,132 @@
'use client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import { formatDecimal, formatNumber } from '@/lib/utils/format';
import type { SystemAccountsSummary } from '@/types/system-account';
import { Database, Coins, Activity, RefreshCcw } from 'lucide-react';
interface SummaryCardsProps {
summary: SystemAccountsSummary | undefined;
isLoading?: boolean;
}
export function SummaryCards({ summary, isLoading = false }: SummaryCardsProps) {
if (isLoading) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{[...Array(4)].map((_, i) => (
<Card key={i}>
<CardContent className="p-6">
<Skeleton className="h-20 w-full" />
</CardContent>
</Card>
))}
</div>
);
}
if (!summary) {
return null;
}
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{/* System Accounts Count */}
<Card>
<CardContent className="p-6">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold mt-1">{summary.systemAccounts.count}</p>
<p className="text-xs text-muted-foreground mt-2">
: {formatDecimal(summary.systemAccounts.totalContribution, 4)}
</p>
</div>
<div className="p-3 rounded-lg bg-blue-50">
<Database className="h-6 w-6 text-blue-600" />
</div>
</div>
</CardContent>
</Card>
{/* Synced Contributions */}
<Card>
<CardContent className="p-6">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold mt-1">{summary.syncedContributions.count}</p>
<p className="text-xs text-muted-foreground mt-2">
: {formatDecimal(summary.syncedContributions.totalBalance, 4)}
</p>
</div>
<div className="p-3 rounded-lg bg-green-50">
<RefreshCcw className="h-6 w-6 text-green-600" />
</div>
</div>
</CardContent>
</Card>
{/* Mining Config */}
<Card>
<CardContent className="p-6">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-muted-foreground flex items-center gap-2">
{summary.miningConfig?.isActive && (
<Badge variant="default" className="text-xs">
</Badge>
)}
</p>
{summary.miningConfig ? (
<>
<p className="text-2xl font-bold mt-1">
{summary.miningConfig.currentEra}
</p>
<p className="text-xs text-muted-foreground mt-2">
: {formatDecimal(summary.miningConfig.remainingDistribution, 2)}
</p>
</>
) : (
<p className="text-sm text-muted-foreground mt-2"></p>
)}
</div>
<div className="p-3 rounded-lg bg-purple-50">
<Activity className="h-6 w-6 text-purple-600" />
</div>
</div>
</CardContent>
</Card>
{/* Circulation Pool */}
<Card>
<CardContent className="p-6">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-muted-foreground"></p>
{summary.circulationPool ? (
<>
<p className="text-2xl font-bold mt-1">
{formatDecimal(summary.circulationPool.totalShares, 2)}
</p>
<p className="text-xs text-muted-foreground mt-2">
: {formatDecimal(summary.circulationPool.totalCash, 2)} USDT
</p>
</>
) : (
<p className="text-sm text-muted-foreground mt-2"></p>
)}
</div>
<div className="p-3 rounded-lg bg-amber-50">
<Coins className="h-6 w-6 text-amber-600" />
</div>
</div>
</CardContent>
</Card>
</div>
);
}

View File

@ -0,0 +1,48 @@
import { useQuery } from '@tanstack/react-query';
import { systemAccountsApi, categorizeAccounts } from '../api/system-accounts.api';
import { useMemo } from 'react';
/**
* Hook to fetch all system accounts
*/
export function useSystemAccounts() {
return useQuery({
queryKey: ['system-accounts'],
queryFn: systemAccountsApi.getList,
});
}
/**
* Hook to fetch system accounts summary (includes mining config and circulation pool)
*/
export function useSystemAccountsSummary() {
return useQuery({
queryKey: ['system-accounts', 'summary'],
queryFn: systemAccountsApi.getSummary,
});
}
/**
* Hook to get categorized system accounts for display
*/
export function useCategorizedAccounts() {
const { data, ...rest } = useSystemAccounts();
const categorized = useMemo(() => {
if (!data?.accounts) {
return {
mainPools: [],
systemAccounts: [],
fixedAccounts: [],
otherAccounts: [],
};
}
return categorizeAccounts(data.accounts);
}, [data?.accounts]);
return {
...rest,
data: categorized,
total: data?.total ?? 0,
};
}

View File

@ -0,0 +1,12 @@
// API
export { systemAccountsApi, categorizeAccounts } from './api/system-accounts.api';
// Hooks
export {
useSystemAccounts,
useSystemAccountsSummary,
useCategorizedAccounts,
} from './hooks/use-system-accounts';
// Components
export { AccountCard, AccountsTable, SummaryCards } from './components';

View File

@ -0,0 +1,152 @@
// System account types based on backend SystemAccountsService
export type SystemAccountType =
| 'DISTRIBUTION_POOL'
| 'BLACK_HOLE'
| 'CIRCULATION_POOL'
| 'SYSTEM_OPERATION'
| 'SYSTEM_PROVINCE'
| 'SYSTEM_CITY'
| 'COST_ACCOUNT'
| 'OPERATION_ACCOUNT'
| 'HQ_COMMUNITY'
| 'RWAD_POOL_PENDING';
export type AccountSource = 'local' | 'synced';
export interface SystemAccount {
accountType: SystemAccountType;
name?: string;
description?: string;
totalContribution?: string;
contributionBalance?: string;
contributionNeverExpires?: boolean;
syncedAt?: string;
createdAt?: string;
source: AccountSource;
}
export interface SystemAccountsResponse {
accounts: SystemAccount[];
total: number;
}
export interface MiningConfig {
totalShares: string;
distributionPool: string;
remainingDistribution: string;
currentEra: number;
isActive: boolean;
activatedAt: string | null;
}
export interface CirculationPool {
totalShares: string;
totalCash: string;
}
export interface SystemAccountsSummary {
systemAccounts: {
count: number;
totalContribution: string;
};
syncedContributions: {
count: number;
totalBalance: string;
};
miningConfig: MiningConfig | null;
circulationPool: CirculationPool | null;
}
// Account display metadata
export interface AccountDisplayInfo {
label: string;
description: string;
color: string;
bgColor: string;
icon: 'coins' | 'flame' | 'shopping-cart' | 'building' | 'wallet' | 'landmark' | 'box';
}
export const ACCOUNT_DISPLAY_INFO: Record<string, AccountDisplayInfo> = {
DISTRIBUTION_POOL: {
label: '分发池',
description: '用于挖矿分发的代币池',
color: 'text-green-600',
bgColor: 'bg-green-50',
icon: 'coins',
},
BLACK_HOLE: {
label: '黑洞账户',
description: '永久销毁的代币',
color: 'text-orange-600',
bgColor: 'bg-orange-50',
icon: 'flame',
},
CIRCULATION_POOL: {
label: '流通池',
description: '市场流通中的代币',
color: 'text-blue-600',
bgColor: 'bg-blue-50',
icon: 'shopping-cart',
},
SYSTEM_OPERATION: {
label: '系统运营账户',
description: '系统运营分配账户',
color: 'text-purple-600',
bgColor: 'bg-purple-50',
icon: 'building',
},
SYSTEM_PROVINCE: {
label: '省级系统账户',
description: '无授权商省级账户',
color: 'text-indigo-600',
bgColor: 'bg-indigo-50',
icon: 'landmark',
},
SYSTEM_CITY: {
label: '市级系统账户',
description: '无授权商市级账户',
color: 'text-pink-600',
bgColor: 'bg-pink-50',
icon: 'landmark',
},
COST_ACCOUNT: {
label: '成本账户',
description: '成本分配账户',
color: 'text-slate-600',
bgColor: 'bg-slate-50',
icon: 'wallet',
},
OPERATION_ACCOUNT: {
label: '运营账户',
description: '12% 运营收入账户',
color: 'text-teal-600',
bgColor: 'bg-teal-50',
icon: 'building',
},
HQ_COMMUNITY: {
label: '总部社区账户',
description: '总部社区分配账户',
color: 'text-cyan-600',
bgColor: 'bg-cyan-50',
icon: 'building',
},
RWAD_POOL_PENDING: {
label: 'RWAD 待注入池',
description: 'RWAD 代币待注入池',
color: 'text-amber-600',
bgColor: 'bg-amber-50',
icon: 'box',
},
};
// Helper function to get display info
export function getAccountDisplayInfo(accountType: string): AccountDisplayInfo {
return ACCOUNT_DISPLAY_INFO[accountType] || {
label: accountType,
description: '',
color: 'text-gray-600',
bgColor: 'bg-gray-50',
icon: 'building',
};
}