({ data, limit = 5, onViewAll }: ProxyTopListProps)
| 47 | } |
| 48 | |
| 49 | export function ProxyTopList({ data, limit = 5, onViewAll }: ProxyTopListProps) { |
| 50 | const [sortBy, setSortBy] = useState<SortBy>("traffic"); |
| 51 | const t = useTranslations("topProxies"); |
| 52 | const proxiesT = useTranslations("proxies"); |
| 53 | const isWindows = useIsWindows(); |
| 54 | |
| 55 | const { proxies, totalTraffic, totalConnections } = useMemo(() => { |
| 56 | if (!data) return { proxies: [], totalTraffic: 0, totalConnections: 0 }; |
| 57 | |
| 58 | const sorted = [...data].sort((a, b) => { |
| 59 | if (sortBy === "traffic") { |
| 60 | const totalA = a.totalDownload + a.totalUpload; |
| 61 | const totalB = b.totalDownload + b.totalUpload; |
| 62 | return totalB - totalA; |
| 63 | } |
| 64 | return b.totalConnections - a.totalConnections; |
| 65 | }); |
| 66 | |
| 67 | const list = sorted.slice(0, limit).map((p, i) => ({ |
| 68 | ...p, |
| 69 | total: p.totalDownload + p.totalUpload, |
| 70 | color: COLORS[i % COLORS.length], |
| 71 | displayName: formatProxyName(p.chain), |
| 72 | countryCode: getProxyCountryCode(p.chain), |
| 73 | })); |
| 74 | |
| 75 | const totalT = list.reduce((sum, p) => sum + p.total, 0); |
| 76 | const totalC = list.reduce((sum, p) => sum + p.totalConnections, 0); |
| 77 | |
| 78 | return { proxies: list, totalTraffic: totalT, totalConnections: totalC }; |
| 79 | }, [data, limit, sortBy]); |
| 80 | |
| 81 | const toggleSort = () => { |
| 82 | setSortBy(prev => prev === "traffic" ? "connections" : "traffic"); |
| 83 | }; |
| 84 | |
| 85 | if (proxies.length === 0) { |
| 86 | return ( |
| 87 | <OverviewCard title={t("title")} icon={<Server className="w-4 h-4" />}> |
| 88 | <div className="py-8 text-center text-sm text-muted-foreground"> |
| 89 | {proxiesT("noData")} |
| 90 | </div> |
| 91 | </OverviewCard> |
| 92 | ); |
| 93 | } |
| 94 | |
| 95 | return ( |
| 96 | <OverviewCard |
| 97 | title={t("title")} |
| 98 | icon={<Server className="w-4 h-4" />} |
| 99 | action={ |
| 100 | <Button |
| 101 | variant="ghost" |
| 102 | size="sm" |
| 103 | className="h-7 px-2 text-xs" |
| 104 | onClick={toggleSort} |
| 105 | > |
| 106 | {sortBy === "traffic" ? ( |
nothing calls this directly
no test coverage detected