diff --git a/CHANGELOG.md b/CHANGELOG.md index 729c5c9..abebf31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,87 @@ This document records all notable changes to ShadCN Admin. --- +## [1.3.6](https://github.com/perfect-panel/frontend/compare/v1.3.5...v1.3.6) (2025-12-30) + +### 🐛 Bug Fixes / 问题修复 + +* Update server and web version update descriptions to remove version prefix for clarity ([cbd6e29](https://github.com/perfect-panel/frontend/commit/cbd6e29deda1c7a913fae4edb169e0a0a43a3dd9)) + +## [1.3.5](https://github.com/perfect-panel/frontend/compare/v1.3.4...v1.3.5) (2025-12-30) + +### 🐛 Bug Fixes / 问题修复 + +* Update getUserSubscribe function to accept short and token parameters for improved URL generation ([39ebd09](https://github.com/perfect-panel/frontend/commit/39ebd09f0989b37308c1c803d8e380f967ab0db5)) + +## [1.3.4](https://github.com/perfect-panel/frontend/compare/v1.3.3...v1.3.4) (2025-12-29) + +### 🐛 Bug Fixes / 问题修复 + +* Only update internalValue in MonacoEditor if propValue has changed ([fe14002](https://github.com/perfect-panel/frontend/commit/fe1400235967f2ad0c1ffbc05d9e15bab370c664)) +* Set modal prop to false for DropdownMenu in multiple components for improved user experience ([0253b62](https://github.com/perfect-panel/frontend/commit/0253b62b6365476591121b6f18dcb3f3261cb4ff)) +* Update HTMLEditor onChange event and set DropdownMenu modal to false for better user experience ([ba65588](https://github.com/perfect-panel/frontend/commit/ba65588fdfcfba146cd76bfe7d14e053291f5926)) +* Update SendCode parameters to use form.watch for email and telephone fields ([51a98af](https://github.com/perfect-panel/frontend/commit/51a98afcae2c8b83c7176898ad8ac2c8b11ad3d9)) + +## [1.3.3](https://github.com/perfect-panel/frontend/compare/v1.3.2...v1.3.3) (2025-12-29) + +### 🐛 Bug Fixes / 问题修复 + +* Update user_subscribe_id reference in RowMoreActions to use row.id for correct token reset and status toggle ([2156a7f](https://github.com/perfect-panel/frontend/commit/2156a7f1df52970b5ab0816dd1da5dc7cefc1dae)) + +## [1.3.2](https://github.com/perfect-panel/frontend/compare/v1.3.1...v1.3.2) (2025-12-29) + +### 🐛 Bug Fixes / 问题修复 + +* Refactor update dialog logic in SystemVersionCard for better clarity and handling of update states ([0690deb](https://github.com/perfect-panel/frontend/commit/0690debf6c1bab7472468c742f9b15639f904c20)) +* Update default inventory value in SubscribeForm to -1 for better handling of inventory state ([2f50c6d](https://github.com/perfect-panel/frontend/commit/2f50c6df3345e26bc92f5730bc8b891d0515a367)) + +## [1.3.1](https://github.com/perfect-panel/frontend/compare/v1.3.0...v1.3.1) (2025-12-29) + +### 🐛 Bug Fixes / 问题修复 + +* : Update localization files and improve display logic for inventory handling ([83d821a](https://github.com/perfect-panel/frontend/commit/83d821a2dc24d97838af43ab610b65ff0d0d0d15)) +* Update quantity handling in Purchase and Renewal components based on showOriginalPrice prop ([a274607](https://github.com/perfect-panel/frontend/commit/a2746073a636706b0e214eca84055735417680f3)) +* Update SubscribeTable to display inventory using Display component for better clarity ([cc52e36](https://github.com/perfect-panel/frontend/commit/cc52e3614d9b6723f4ce8ab6385d626f0fcc54d7)) + +## [1.3.0](https://github.com/perfect-panel/frontend/compare/v1.2.4...v1.3.0) (2025-12-29) + +### ✨ Features / 新功能 + +* Add original price display option and enhance inventory messages in subscription components ([543a7b9](https://github.com/perfect-panel/frontend/commit/543a7b9eb9b2cd278a70f668b0a0e0e9e261fe57)) +* Added localized support for user subscription and deletion status, and optimized the subscription form and user interface. ([9f95cec](https://github.com/perfect-panel/frontend/commit/9f95cec876c9ac8bf00a6ca12a5c40243c7171af)) +* Added reset and pause subscription functionality, and updated the status display. ([bc451ee](https://github.com/perfect-panel/frontend/commit/bc451eea16b51f1ab81b6cacf65201886866e1cc)) +* Added the option to restore subscription, and updated the relevant description and confirmation information. ([5f5c339](https://github.com/perfect-panel/frontend/commit/5f5c33987e54533c5348223b3cebe31aef91aa25)) +* Enhance DatePicker component with clear button and improved value handling ([b27b928](https://github.com/perfect-panel/frontend/commit/b27b9287be0eb26fe5d5189b618a32db340c506c)) +* Refactor SubscriptionForm layout for improved readability and maintainability ([f432ba0](https://github.com/perfect-panel/frontend/commit/f432ba06f9d3a86ec00bfd316d1c020ddf779de6)) +* Update API proxy target to use environment variable for improved configurability ([8d514df](https://github.com/perfect-panel/frontend/commit/8d514dfd8f0a8fb9acc97a288addd1b1cbc882b8)) +* Update queryKey structure in Purchase and Renewal components for improved order creation ([9559e00](https://github.com/perfect-panel/frontend/commit/9559e003b7821303f7d0ac7f4da5fae4bf1ccad4)) +* Update Vite configuration to load environment variables for improved API proxy setup ([d36a2c9](https://github.com/perfect-panel/frontend/commit/d36a2c902b7943441282278009c5e5210d30c746)) + +### 🐛 Bug Fixes / 问题修复 + +* Replace tag removal icon with a button for better accessibility and event handling ([3751f64](https://github.com/perfect-panel/frontend/commit/3751f64f73cd70eaddd44cb650dd38949f0ca069)) +* Uncomment navigation to dashboard for authenticated users ([4d15b2b](https://github.com/perfect-panel/frontend/commit/4d15b2b6fc8f0d178c7c749aa9e1d9826bb706f8)) + +### 📚 Documentation / 文档更新 + +* Update default administrator account information and security recommendations in installation guides ([7279275](https://github.com/perfect-panel/frontend/commit/7279275532ad0b73994ffe5cf2e4c2f8dabc280b)) + +## [1.2.4](https://github.com/perfect-panel/frontend/compare/v1.2.3...v1.2.4) (2025-12-28) + +### 🐛 Bug Fixes / 问题修复 + +* Add onSuccess callback to Unsubscribe component and conditionally render Renewal component ([7b5367a](https://github.com/perfect-panel/frontend/commit/7b5367a9a99ac8ae608a765b79a66c1f7380dcd8)) +* Remove the system log dialog component from the system version card ([71cb827](https://github.com/perfect-panel/frontend/commit/71cb827918ee3250f0c9d06d46d876ce6799b8ac)) +* Update invite link format in auth forms and sidebar to include hash fragment for routing. ([7a8c010](https://github.com/perfect-panel/frontend/commit/7a8c0102958a859c9e7476810d5c9b822f882692)) + +### 📚 Documentation / 文档更新 + +* Add one-click installation script for PPanel with Docker support ([912c5c4](https://github.com/perfect-panel/frontend/commit/912c5c4cb63eeb0ecbc33bef6b31bd50d83d6491)) + +### 🔧 Chores / 其他变更 + +* **release:** Release 1.2.4-dev.1 / 发布版本 1.2.4-dev.1 [skip ci] ([62d45bb](https://github.com/perfect-panel/frontend/commit/62d45bbac17fab9656c1cce029a379ce8bb757d6)) + ## [1.2.4-dev.1](https://github.com/perfect-panel/frontend/compare/v1.2.3...v1.2.4-dev.1) (2025-12-22) ### 🐛 Bug Fixes / 问题修复 diff --git a/apps/admin/public/assets/locales/en-US/components.json b/apps/admin/public/assets/locales/en-US/components.json index dd65d5a..6c26754 100644 --- a/apps/admin/public/assets/locales/en-US/components.json +++ b/apps/admin/public/assets/locales/en-US/components.json @@ -44,6 +44,8 @@ "60003": "An existing subscription is detected. Please cancel it before proceeding.", "60004": "Unable to delete at the moment as the subscription has active users.", "60005": "Single subscription mode has exceeded user limit", + "60006": "User quota limit has been reached, unable to continue.", + "60007": "Insufficient inventory, please try again later or contact the administrator.", "70001": "Incorrect verification code, please re-enter.", "80001": "Task was not successfully queued, please try again later.", "90001": "Please disable DEBUG mode and try again.", @@ -61,5 +63,5 @@ "system": "System", "toggle": "Toggle theme" }, - "unlimited": "unlimited" + "unlimited": "Unlimited" } diff --git a/apps/admin/public/assets/locales/en-US/product.json b/apps/admin/public/assets/locales/en-US/product.json index f66faef..ca0c88d 100644 --- a/apps/admin/public/assets/locales/en-US/product.json +++ b/apps/admin/public/assets/locales/en-US/product.json @@ -30,6 +30,7 @@ "discountPercent": "Discount Percentage", "Hour": "Hour", "inventory": "Subscription Limit", + "unlimitedInventory": "Unlimited (enter -1)", "language": "Language", "languageDescription": "Leave empty for default without language restriction", "languagePlaceholder": "Language identifier for the subscription, e.g., en-US, zh-CN", @@ -56,6 +57,8 @@ "resetOn1st": "Reset on the 1st", "selectResetCycle": "Please select a reset cycle", "selectUnitTime": "Please select a unit of time", + "showOriginalPrice": "Show Original Price", + "showOriginalPriceDescription": "When enabled, the subscription card will display both the original price and the discounted price to help users understand the discount amount", "speedLimit": "Speed Limit ", "traffic": "Traffic", "unitPrice": "Unit Price", diff --git a/apps/admin/public/assets/locales/en-US/tool.json b/apps/admin/public/assets/locales/en-US/tool.json index 1503c5c..0bf5b5d 100644 --- a/apps/admin/public/assets/locales/en-US/tool.json +++ b/apps/admin/public/assets/locales/en-US/tool.json @@ -13,9 +13,9 @@ "systemServices": "System Services", "update": "Update", "updateFailed": "Update failed", - "updateServerDescription": "Are you sure you want to update the server version from V{{current}} to V{{latest}}?", + "updateServerDescription": "Are you sure you want to update the server version from {{current}} to {{latest}}?", "updateSuccess": "Update completed successfully", - "updateWebDescription": "Are you sure you want to update the web version from V{{current}} to V{{latest}}?", + "updateWebDescription": "Are you sure you want to update the web version from {{current}} to {{latest}}?", "userUpdateSuccess": "User updated successfully", "webVersion": "Web Version" } diff --git a/apps/admin/public/assets/locales/en-US/user.json b/apps/admin/public/assets/locales/en-US/user.json index 2e935ba..e1d6405 100644 --- a/apps/admin/public/assets/locales/en-US/user.json +++ b/apps/admin/public/assets/locales/en-US/user.json @@ -25,9 +25,11 @@ "createSuccess": "Created successfully", "createUser": "Create User", "delete": "Delete", + "deleted": "Deleted", "deleteDescription": "This action cannot be undone.", "deleteSubscriptionDescription": "This action cannot be undone.", "deleteSuccess": "Deleted successfully", + "isDeleted": "Status", "deviceLimit": "Device Limit", "download": "Download", "downloadTraffic": "Download Traffic", @@ -51,6 +53,7 @@ "loginStatus": "Login Status", "manager": "Administrator", "more": "More", + "normal": "Normal", "notifySettingsTitle": "Notify Settings", "offline": "Offline", "online": "Online", @@ -80,6 +83,25 @@ "toggleSubscriptionStatus": "Toggle Status", "toggleSubscriptionStatusDescription": "This will toggle the subscription status.", "resetTime": "Reset Time", + "resetToken": "Reset Subscription Address", + "resetTokenDescription": "This will reset the subscription address and regenerate a new token.", + "resetTokenSuccess": "Subscription address reset successfully", + "confirmResetToken": "Confirm Reset Subscription Address", + "stopSubscribe": "Stop Subscription", + "stopSubscribeDescription": "This will stop the subscription temporarily. User will not be able to use it.", + "stopSubscribeSuccess": "Subscription stopped successfully", + "confirmStopSubscribe": "Confirm Stop Subscription", + "resumeSubscribe": "Resume Subscription", + "resumeSubscribeDescription": "This will resume the subscription and allow the user to use it.", + "resumeSubscribeSuccess": "Subscription resumed successfully", + "confirmResumeSubscribe": "Confirm Resume Subscription", + "status": "Status", + "statusPending": "Pending", + "statusActive": "Active", + "statusFinished": "Finished", + "statusExpired": "Expired", + "statusDeducted": "Deducted", + "statusStopped": "Stopped", "save": "Save", "speedLimit": "Speed Limit", "startTime": "startTime", diff --git a/apps/admin/public/assets/locales/zh-CN/components.json b/apps/admin/public/assets/locales/zh-CN/components.json index 60fb6ff..b56ad5a 100644 --- a/apps/admin/public/assets/locales/zh-CN/components.json +++ b/apps/admin/public/assets/locales/zh-CN/components.json @@ -44,6 +44,8 @@ "60003": "检测到现有订阅,请先取消后再继续。", "60004": "由于订阅有活跃用户,暂时无法删除。", "60005": "单一订阅模式已超过用户限制", + "60006": "用户配额已达到限制,无法继续操作。", + "60007": "库存不足,请稍后再试或联系管理员。", "70001": "验证码不正确,请重新输入。", "80001": "任务未成功加入队列,请稍后再试。", "90001": "请禁用 DEBUG 模式后再试。", diff --git a/apps/admin/public/assets/locales/zh-CN/product.json b/apps/admin/public/assets/locales/zh-CN/product.json index dcd5e9c..2ee53e4 100644 --- a/apps/admin/public/assets/locales/zh-CN/product.json +++ b/apps/admin/public/assets/locales/zh-CN/product.json @@ -30,6 +30,7 @@ "discountPercent": "折扣百分比", "Hour": "小时", "inventory": "订阅库存", + "unlimitedInventory": "无限制(输入 -1)", "language": "语言", "languageDescription": "留空为默认无语言限制", "languagePlaceholder": "订阅的语言标识符,例如 en-US、zh-CN", @@ -56,6 +57,8 @@ "resetOn1st": "每月1日重置", "selectResetCycle": "请选择重置周期", "selectUnitTime": "请选择时间单位", + "showOriginalPrice": "显示原价", + "showOriginalPriceDescription": "开启后,在订阅卡片上将会显示原价和折后价,帮助用户了解优惠幅度", "speedLimit": "速度限制", "traffic": "流量", "unitPrice": "单价", diff --git a/apps/admin/public/assets/locales/zh-CN/tool.json b/apps/admin/public/assets/locales/zh-CN/tool.json index 478dbbc..32a6b2a 100644 --- a/apps/admin/public/assets/locales/zh-CN/tool.json +++ b/apps/admin/public/assets/locales/zh-CN/tool.json @@ -13,9 +13,9 @@ "systemServices": "系统服务", "update": "更新", "updateFailed": "更新失败", - "updateServerDescription": "确定要将服务器版本从 V{{current}} 更新到 V{{latest}} 吗?", + "updateServerDescription": "确定要将服务器版本从 {{current}} 更新到 {{latest}} 吗?", "updateSuccess": "更新成功", - "updateWebDescription": "确定要将前端版本从 V{{current}} 更新到 V{{latest}} 吗?", + "updateWebDescription": "确定要将前端版本从 {{current}} 更新到 {{latest}} 吗?", "userUpdateSuccess": "用户端更新成功", "webVersion": "前端版本" } diff --git a/apps/admin/public/assets/locales/zh-CN/user.json b/apps/admin/public/assets/locales/zh-CN/user.json index 80b8004..6e1aab1 100644 --- a/apps/admin/public/assets/locales/zh-CN/user.json +++ b/apps/admin/public/assets/locales/zh-CN/user.json @@ -25,9 +25,11 @@ "createSuccess": "创建成功", "createUser": "创建用户", "delete": "删除", + "deleted": "已删除", "deleteDescription": "此操作无法撤销。", "deleteSubscriptionDescription": "此操作无法撤销。", "deleteSuccess": "删除成功", + "isDeleted": "状态", "deviceLimit": "IP限制", "download": "下载", "downloadTraffic": "下载流量", @@ -51,6 +53,7 @@ "loginStatus": "登录状态", "manager": "管理员", "more": "更多", + "normal": "正常", "notifySettingsTitle": "通知设置", "offline": "离线", "online": "在线", @@ -80,6 +83,25 @@ "toggleSubscriptionStatus": "切换状态", "toggleSubscriptionStatusDescription": "将切换该订阅的启用/停用状态。", "resetTime": "重置时间", + "resetToken": "重置订阅地址", + "resetTokenDescription": "这将重置订阅地址并重新生成新的令牌。", + "resetTokenSuccess": "订阅地址重置成功", + "confirmResetToken": "确认重置订阅地址", + "stopSubscribe": "暂停订阅", + "stopSubscribeDescription": "这将暂时停止订阅。用户将无法使用。", + "stopSubscribeSuccess": "订阅已暂停", + "confirmStopSubscribe": "确认暂停订阅", + "resumeSubscribe": "恢复订阅", + "resumeSubscribeDescription": "这将恢复订阅,允许用户继续使用。", + "resumeSubscribeSuccess": "订阅已恢复", + "confirmResumeSubscribe": "确认恢复订阅", + "status": "状态", + "statusPending": "待处理", + "statusActive": "活跃", + "statusFinished": "已完成", + "statusExpired": "已过期", + "statusDeducted": "已扣除", + "statusStopped": "已停止", "save": "保存", "speedLimit": "速度限制", "startTime": "开始时间", diff --git a/apps/admin/src/components/display.tsx b/apps/admin/src/components/display.tsx index d7ec6bf..28181c9 100644 --- a/apps/admin/src/components/display.tsx +++ b/apps/admin/src/components/display.tsx @@ -28,7 +28,7 @@ export function Display({ if ( ["traffic", "trafficSpeed", "number"].includes(type) && unlimited && - !value + (value === 0 || value === null || value === undefined) ) { return t("unlimited"); } @@ -42,7 +42,7 @@ export function Display({ } if (type === "number") { - return value ? value.toString() : "0"; + return value !== null && value !== undefined ? value.toString() : "0"; } return "0"; diff --git a/apps/admin/src/sections/auth-control/forms/email-settings-form.tsx b/apps/admin/src/sections/auth-control/forms/email-settings-form.tsx index 060dda4..09a6cce 100644 --- a/apps/admin/src/sections/auth-control/forms/email-settings-form.tsx +++ b/apps/admin/src/sections/auth-control/forms/email-settings-form.tsx @@ -538,7 +538,7 @@ export default function EmailSettingsForm() { { if (user) { - // navigate({ to: "/dashboard" }); + navigate({ to: "/dashboard" }); } }, [navigate, user]); diff --git a/apps/admin/src/sections/dashboard/components/system-version-card.tsx b/apps/admin/src/sections/dashboard/components/system-version-card.tsx index 2fde9fa..daeda25 100644 --- a/apps/admin/src/sections/dashboard/components/system-version-card.tsx +++ b/apps/admin/src/sections/dashboard/components/system-version-card.tsx @@ -69,7 +69,7 @@ export default function SystemVersionCard() { queryFn: async () => { const { data } = await basicCheckServiceVersion( { - service_name: "admin", + service_name: "admin-web-with-api", secret: moduleConfig!.secret, }, { skipErrorHandler: true } @@ -105,13 +105,13 @@ export default function SystemVersionCard() { setIsUpdatingWeb(true); try { await basicUpdateService({ - service_name: "admin", + service_name: "admin-web-with-api", secret: moduleConfig.secret, }); toast.success(t("adminUpdateSuccess", "Admin updated successfully")); await basicUpdateService({ - service_name: "user", + service_name: "user-web-with-api", secret: moduleConfig.secret, }); toast.success(t("userUpdateSuccess", "User updated successfully")); @@ -188,52 +188,52 @@ export default function SystemVersionCard() {
V{packageJson.version} - {hasWebNewVersion && webVersionInfo && ( - - - + + + + + {t("confirmUpdate", "Confirm Update")} + + + {webVersionInfo + ? t( + "updateWebDescription", + "Are you sure you want to update the web version from {{current}} to {{latest}}?", + { + current: webVersionInfo.current_version, + latest: webVersionInfo.latest_version, + } + ) + : t( + "updateDescription", + "Are you sure you want to update?" + )} + + + + {t("cancel", "Cancel")} + - - - - - {t("confirmUpdate", "Confirm Update")} - - - {t( - "updateWebDescription", - "Are you sure you want to update the web version from V{{current}} to V{{latest}}?", - { - current: packageJson.version, - latest: webVersionInfo.latest_version, - } - )} - - - - - {t("cancel", "Cancel")} - - - - - - )} + + +
@@ -251,62 +251,63 @@ export default function SystemVersionCard() { serverVersionInfo?.current_version || "1.0.0"} - {hasServerNewVersion && serverVersionInfo && moduleConfig && ( - - + + + + + + + + {t("confirmUpdate", "Confirm Update")} + + + {serverVersionInfo && moduleConfig + ? t( + "updateServerDescription", + "Are you sure you want to update the server version from {{current}} to {{latest}}?", + { + current: + moduleConfig.service_version || + serverVersionInfo.current_version, + latest: serverVersionInfo.latest_version, + } + ) + : t( + "updateDescription", + "Are you sure you want to update?" + )} + + + + {t("cancel", "Cancel")} - - - - - {t("confirmUpdate", "Confirm Update")} - - - {t( - "updateServerDescription", - "Are you sure you want to update the server version from V{{current}} to V{{latest}}?", - { - current: - moduleConfig.service_version || - serverVersionInfo.current_version, - latest: serverVersionInfo.latest_version, - } - )} - - - - - {t("cancel", "Cancel")} - - - - - - )} + + + diff --git a/apps/admin/src/sections/product/subscribe-form.tsx b/apps/admin/src/sections/product/subscribe-form.tsx index 8aa0c4a..fefa2e8 100644 --- a/apps/admin/src/sections/product/subscribe-form.tsx +++ b/apps/admin/src/sections/product/subscribe-form.tsx @@ -63,7 +63,7 @@ interface SubscribeFormProps { } const defaultValues = { - inventory: 0, + inventory: -1, speed_limit: 0, device_limit: 0, traffic: 0, @@ -278,7 +278,7 @@ export default function SubscribeForm>({ {trigger} - + {title} @@ -446,11 +446,10 @@ export default function SubscribeForm>({ {t("form.inventory")} { form.setValue(field.name, value); }} - placeholder={t("form.noLimit")} + placeholder={t("form.unlimitedInventory")} step={1} type="number" value={field.value} @@ -901,6 +900,33 @@ export default function SubscribeForm>({ )} /> + ( + +
+
+ + {t("form.showOriginalPrice")} + + + {t("form.showOriginalPriceDescription")} + +
+ + { + form.setValue(field.name, value); + }} + /> + +
+ +
+ )} + /> diff --git a/apps/admin/src/sections/product/subscribe-table.tsx b/apps/admin/src/sections/product/subscribe-table.tsx index e9942ee..de846e8 100644 --- a/apps/admin/src/sections/product/subscribe-table.tsx +++ b/apps/admin/src/sections/product/subscribe-table.tsx @@ -208,15 +208,14 @@ export default function SubscribeTable() { { accessorKey: "inventory", header: t("inventory"), - cell: ({ row }) => ( - - ), + cell: ({ row }) => { + const inventory = row.getValue("inventory") as number; + return inventory === -1 ? ( + + ) : ( + + ); + }, }, { accessorKey: "quota", diff --git a/apps/admin/src/sections/servers/server-form.tsx b/apps/admin/src/sections/servers/server-form.tsx index 26f5678..46f11f5 100644 --- a/apps/admin/src/sections/servers/server-form.tsx +++ b/apps/admin/src/sections/servers/server-form.tsx @@ -98,7 +98,7 @@ function DynamicField({ field.generate ? ( field.generate.functions && field.generate.functions.length > 0 ? ( - + } />, - + @@ -176,6 +176,18 @@ export default function User() { accessorKey: "id", header: "ID", }, + { + accessorKey: "deleted_at", + header: t("isDeleted", "Deleted"), + cell: ({ row }) => { + const deletedAt = row.getValue("deleted_at") as number | undefined; + return deletedAt ? ( + {t("deleted", "Deleted")} + ) : ( + {t("normal", "Normal")} + ); + }, + }, { accessorKey: "auth_methods", header: t("userName", "Username"), @@ -371,16 +383,13 @@ function SubscriptionSheet({ userId }: { userId: number }) { - + {t("subscriptionList", "Subscription List")} · ID: {userId} -
+
diff --git a/apps/admin/src/sections/user/user-subscription/index.tsx b/apps/admin/src/sections/user/user-subscription/index.tsx index 171ff2f..840f789 100644 --- a/apps/admin/src/sections/user/user-subscription/index.tsx +++ b/apps/admin/src/sections/user/user-subscription/index.tsx @@ -1,4 +1,5 @@ import { Link } from "@tanstack/react-router"; +import { Badge } from "@workspace/ui/components/badge"; import { Button } from "@workspace/ui/components/button"; import { DropdownMenu, @@ -16,7 +17,6 @@ import { deleteUserSubscribe, getUserSubscribe, resetUserSubscribeToken, - resetUserSubscribeTraffic, toggleUserSubscribeStatus, updateUserSubscribe, } from "@workspace/ui/services/admin/user"; @@ -33,7 +33,6 @@ export default function UserSubscription({ userId }: { userId: number }) { const { t } = useTranslation("user"); const [loading, setLoading] = useState(false); const ref = useRef(null); - const { getUserSubscribe: getUserSubscribeUrls } = useGlobalStore(); return ( > @@ -59,100 +58,14 @@ export default function UserSubscription({ userId }: { userId: number }) { title={t("editSubscription", "Edit Subscription")} trigger={t("edit", "Edit")} />, - , - - { - await resetUserSubscribeToken({ user_subscribe_id: row.id }); - toast.success(t("resetSuccess", "Reset successfully")); - ref.current?.refresh(); - }} - title={t("resetSubscriptionToken", "Reset Token")} - trigger={ - - } + ref.current?.refresh()} + row={row} + token={row.token} + userId={userId} + /> />, - - { - await resetUserSubscribeTraffic({ user_subscribe_id: row.id }); - toast.success(t("resetSuccess", "Reset successfully")); - ref.current?.refresh(); - }} - title={t("resetSubscriptionTraffic", "Reset Traffic")} - trigger={ - - } - />, - - { - await toggleUserSubscribeStatus({ user_subscribe_id: row.id }); - toast.success(t("updateSuccess", "Updated successfully")); - ref.current?.refresh(); - }} - title={t("toggleSubscriptionStatus", "Toggle Status")} - trigger={ - - } - />, - { - await deleteUserSubscribe({ user_subscribe_id: row.id }); - toast.success(t("deleteSuccess", "Deleted successfully")); - ref.current?.refresh(); - }} - title={t("confirmDelete", "Confirm Delete")} - trigger={ - - } - />, - , ], }} columns={[ @@ -165,6 +78,51 @@ export default function UserSubscription({ userId }: { userId: number }) { header: t("subscriptionName", "Subscription Name"), cell: ({ row }) => row.original.subscribe.name, }, + { + accessorKey: "status", + header: t("status", "Status"), + cell: ({ row }) => { + const status = row.getValue("status") as number; + const expireTime = row.original.expire_time; + + // 如果过期时间为0,说明是永久订阅,应该显示为激活状态 + const displayStatus = status === 3 && expireTime === 0 ? 1 : status; + + const statusMap: Record< + number, + { + label: string; + variant: "default" | "secondary" | "destructive" | "outline"; + } + > = { + 0: { label: t("statusPending", "Pending"), variant: "outline" }, + 1: { label: t("statusActive", "Active"), variant: "default" }, + 2: { + label: t("statusFinished", "Finished"), + variant: "secondary", + }, + 3: { + label: t("statusExpired", "Expired"), + variant: "destructive", + }, + 4: { + label: t("statusDeducted", "Deducted"), + variant: "secondary", + }, + 5: { + label: t("statusStopped", "Stopped"), + variant: "destructive", + }, + }; + const statusInfo = statusMap[displayStatus] || { + label: "Unknown", + variant: "outline", + }; + return ( + {statusInfo.label} + ); + }, + }, { accessorKey: "upload", header: t("upload", "Upload"), @@ -216,10 +174,12 @@ export default function UserSubscription({ userId }: { userId: number }) { { accessorKey: "expire_time", header: t("expireTime", "Expire Time"), - cell: ({ row }) => - row.getValue("expire_time") - ? formatDate(row.getValue("expire_time")) - : t("permanent", "Permanent"), + cell: ({ row }) => { + const expireTime = row.getValue("expire_time") as number; + return expireTime && expireTime !== 0 + ? formatDate(expireTime) + : t("permanent", "Permanent"); + }, }, { accessorKey: "created_at", @@ -263,19 +223,72 @@ export default function UserSubscription({ userId }: { userId: number }) { ); } -function RowMoreActions({ userId, subId }: { userId: number; subId: number }) { +function RowMoreActions({ + userId, + row, + token, + refresh, +}: { + userId: number; + row: API.UserSubscribe; + token: string; + refresh: () => void; +}) { const triggerRef = useRef(null); + const resetTokenRef = useRef(null); + const toggleStatusRef = useRef(null); + const deleteRef = useRef(null); const { t } = useTranslation("user"); + const { getUserSubscribe: getUserSubscribeUrls } = useGlobalStore(); + return (
- + + { + e.preventDefault(); + await navigator.clipboard.writeText( + getUserSubscribeUrls(row.short, token)[0] || "" + ); + toast.success(t("copySuccess", "Copied successfully")); + }} + > + {t("copySubscription", "Copy Subscription")} + + { + e.preventDefault(); + resetTokenRef.current?.click(); + }} + > + {t("resetToken", "Reset Subscription Address")} + + { + e.preventDefault(); + toggleStatusRef.current?.click(); + }} + > + {row.status === 5 + ? t("resumeSubscribe", "Resume Subscription") + : t("stopSubscribe", "Stop Subscription")} + + { + e.preventDefault(); + deleteRef.current?.click(); + }} + > + {t("delete", "Delete")} + {t("subscriptionLogs", "Subscription Logs")} @@ -283,7 +296,7 @@ function RowMoreActions({ userId, subId }: { userId: number; subId: number }) { {t("resetLogs", "Reset Logs")} @@ -291,7 +304,7 @@ function RowMoreActions({ userId, subId }: { userId: number; subId: number }) { {t("trafficStats", "Traffic Stats")} @@ -299,7 +312,7 @@ function RowMoreActions({ userId, subId }: { userId: number; subId: number }) { {t("trafficDetails", "Traffic Details")} @@ -316,8 +329,78 @@ function RowMoreActions({ userId, subId }: { userId: number; subId: number }) { + {/* Hidden triggers for confirm dialogs */} + { + await resetUserSubscribeToken({ + user_subscribe_id: row.id, + }); + toast.success( + t("resetTokenSuccess", "Subscription address reset successfully") + ); + refresh(); + }} + title={t("confirmResetToken", "Confirm Reset Subscription Address")} + trigger={
- {userSubscribe.map((item) => ( -