'use client';
import { UserDetail } from '@/app/dashboard/user/user-detail';
import { IpLink } from '@/components/ip-link';
import { ProTable } from '@/components/pro-table';
import { getUserSubscribeById } from '@/services/admin/user';
import { useQuery } from '@tanstack/react-query';
import { Badge } from '@workspace/ui/components/badge';
import { Progress } from '@workspace/ui/components/progress';
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@workspace/ui/components/sheet';
import { formatBytes, formatDate } from '@workspace/ui/utils';
import { useTranslations } from 'next-intl';
import { useState } from 'react';
export function formatPercentage(value: number): string {
return `${value.toFixed(1)}%`;
}
// 统一的用户订阅信息组件
function UserSubscribeInfo({
userId,
type,
}: {
userId: number;
type: 'account' | 'subscribeName' | 'subscribeId' | 'trafficUsage' | 'expireTime';
}) {
const { data } = useQuery({
enabled: userId !== 0,
queryKey: ['getUserSubscribeById', userId],
queryFn: async () => {
const { data } = await getUserSubscribeById({ id: userId });
return data.data;
},
});
if (!data) return --;
switch (type) {
case 'account':
if (!data.user_id) return --;
return ;
case 'subscribeName':
if (!data.subscribe?.name) return --;
return {data.subscribe.name};
case 'subscribeId':
if (!data.id) return --;
return {data.id};
case 'trafficUsage': {
const usedTraffic = data.upload + data.download;
const totalTraffic = data.traffic || 0;
return (
{formatBytes(usedTraffic)} / {totalTraffic > 0 ? formatBytes(totalTraffic) : '无限制'}
);
}
case 'expireTime': {
if (!data.expire_time) return --;
const isExpired = data.expire_time < Date.now() / 1000;
return (
{formatDate(data.expire_time)}
{isExpired && (
过期
)}
);
}
default:
return --;
}
}
export function NodeStatusCell({ status, node }: { status: API.NodeStatus; node?: API.Server }) {
const t = useTranslations('server.node');
const [open, setOpen] = useState(false);
const { online, cpu, mem, disk, updated_at } = status || {
online: {},
cpu: 0,
mem: 0,
disk: 0,
updated_at: 0,
};
const isOnline = updated_at > 0;
const badgeVariant = isOnline ? 'default' : 'destructive';
const badgeText = isOnline ? t('normal') : t('abnormal');
const onlineCount = (online && Object.keys(online).length) || 0;
// 转换在线用户数据为ProTable需要的格式
const onlineUsersData = Object.entries(online || {}).map(([uid, ips]) => ({
uid,
ips: ips as string[],
primaryIp: ips[0] || '',
allIps: (ips as string[]).join(', '),
}));
return (
<>
{node && (
{t('nodeDetail')} - {node.name}
{t('nodeStatus')}
{isOnline ? t('normal') : t('abnormal')}
{t('onlineCount')}: {onlineCount}
{isOnline && (
{t('lastUpdated')}: {formatDate(updated_at)}
)}
{isOnline && (
{t('memory')}
{mem?.toFixed(1)}%
{t('disk')}
{disk?.toFixed(1)}%
)}
{isOnline && onlineCount > 0 && (
{t('onlineUsers')}
{
const ips = row.original.ips;
return (
{ips.map((ip: string, index: number) => (
{index === 0 ? (
) : (
)}
))}
);
},
},
{
accessorKey: 'user',
header: t('user'),
cell: ({ row }) => (
),
},
{
accessorKey: 'subscribeName',
header: t('subscribeName'),
cell: ({ row }) => (
),
},
{
accessorKey: 'subscribeId',
header: t('subscribeId'),
cell: ({ row }) => (
),
},
{
accessorKey: 'trafficUsage',
header: t('trafficUsage'),
cell: ({ row }) => (
),
},
{
accessorKey: 'expireTime',
header: t('expireTime'),
cell: ({ row }) => (
),
},
]}
request={async () => ({
list: onlineUsersData,
total: onlineUsersData.length,
})}
/>
)}
)}
>
);
}