diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4243a02..3bd1a70 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,6 +21,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-progress": "^1.1.8", "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", @@ -1530,6 +1531,30 @@ } } }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", + "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", diff --git a/frontend/package.json b/frontend/package.json index cb6db09..ed2b1f0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-progress": "^1.1.8", "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", diff --git a/frontend/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx new file mode 100644 index 0000000..105fb65 --- /dev/null +++ b/frontend/src/components/ui/progress.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)) +Progress.displayName = ProgressPrimitive.Root.displayName + +export { Progress } diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index ce62e89..30995dc 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -215,6 +215,14 @@ const en = { cacheTTLPlaceholder: 'Cache TTL in seconds, 0 = no cache', emptyTitle: 'Add your first upstream provider', emptyDescription: 'Providers supply the real nodes. Later, subscriptions can enable these sources and merge them into the final config.', + traffic: 'Traffic', + trafficUpload: 'Upload', + trafficDownload: 'Download', + trafficTotal: 'Total', + trafficExpires: 'Expires', + trafficExpired: 'Expired', + trafficExpiresToday: 'Expires today', + trafficDaysLeft: '{{count}} days left', }, userAgents: { diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index 10c7cba..2b46f6c 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -220,6 +220,14 @@ const zh = { cacheTTLPlaceholder: '缓存有效期(秒),0 表示不缓存', emptyTitle: '先添加一个上游订阅源', emptyDescription: '订阅源负责提供真实节点。后续创建订阅时,可以勾选这些来源并统一生成最终配置。', + traffic: '流量', + trafficUpload: '上传', + trafficDownload: '下载', + trafficTotal: '总量', + trafficExpires: '到期', + trafficExpired: '已过期', + trafficExpiresToday: '今日到期', + trafficDaysLeft: '{{count}} 天后到期', }, // UA 库 diff --git a/frontend/src/pages/Providers.tsx b/frontend/src/pages/Providers/index.tsx similarity index 96% rename from frontend/src/pages/Providers.tsx rename to frontend/src/pages/Providers/index.tsx index de94e7d..f39baa2 100644 --- a/frontend/src/pages/Providers.tsx +++ b/frontend/src/pages/Providers/index.tsx @@ -37,13 +37,8 @@ import { TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' - -interface ProviderFormData { - name: string - url: string - user_agent_id: string - cache_ttl: string -} +import type { ProviderFormData } from './shared/types' +import { TrafficCell } from './traffic/TrafficCell' export function Providers() { const { t } = useTranslation() @@ -177,6 +172,7 @@ export function Providers() { {t('providers.providerUrl')} {t('providers.userAgent')} {t('providers.lastFetched')} + {t('providers.traffic')} {t('common.actions')} @@ -184,14 +180,14 @@ export function Providers() { {isLoading ? ( Array.from({ length: 3 }).map((_, i) => ( - {Array.from({ length: 5 }).map((_, j) => ( + {Array.from({ length: 6 }).map((_, j) => ( ))} )) ) : providers.length === 0 ? ( - + )} + + +