diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 915667b..8dc93be 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: docker.gitea.com/runner-images:ubuntu-22.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: diff --git a/cmd/api/main.go b/cmd/api/main.go deleted file mode 100644 index 6041ec2..0000000 --- a/cmd/api/main.go +++ /dev/null @@ -1,207 +0,0 @@ -//go:build ignore - -package main - -import ( - "log" - "strings" - "xboard-go/internal/config" - "xboard-go/internal/database" - "xboard-go/internal/handler" - "xboard-go/internal/middleware" - - "github.com/gin-gonic/gin" -) - -func main() { - // Initialize configuration - config.LoadConfig() - - // Initialize database - database.InitDB() - - // Initialize Gin router - r := gin.Default() - - // Short-link Subscription (Root level) - r.GET("/s/:token", handler.Subscribe) - - // Static Assets for Admin Dist - r.Static("/admin-assets", "./frontend/admin") - - // Global Middleware - r.Use(gin.Recovery()) - - // API Groups - v1 := r.Group("/api/v1") - { - // Passport (Auth) - passport := v1.Group("/passport") - { - passport.POST("/login", handler.Login) - passport.POST("/register", handler.Register) - } - - // Authenticated Routes - auth := v1.Group("") - auth.Use(middleware.Auth()) - { - user := auth.Group("/user") - { - user.GET("/info", handler.UserInfo) - } - } - - // Node (UniProxy) Routes - server := v1.Group("/server") - server.Use(middleware.NodeAuth()) - { - uniProxy := server.Group("/uniProxy") - { - uniProxy.GET("/user", handler.NodeUser) - uniProxy.POST("/push", handler.NodePush) - } - } - - // Admin Portal Entry (Direct) - v1.GET("/:path", handler.AdminAppPage) - } - - // V2 Admin API Group (Matches Xboard official frontend expectations) - v2 := r.Group("/api/v2") - { - // V2 User Auth Routes (used by the admin React app to verify login state) - v2user := v2.Group("/user") - v2user.Use(middleware.Auth()) - { - v2user.GET("/info", handler.UserInfo) - v2user.GET("/checkLogin", handler.UserCheckLogin) - } - - // All admin endpoints are prefixed with the secure path - admin := v2.Group("/:path") - admin.Use(middleware.AdminAuth()) - { - // Config - configGrp := admin.Group("/config") - { - configGrp.GET("/fetch", handler.AdminConfigFetch) - configGrp.POST("/save", handler.AdminConfigSave) - configGrp.GET("/getEmailTemplate", handler.AdminGetEmailTemplate) - configGrp.GET("/getThemeTemplate", handler.AdminGetThemeTemplate) - } - - // Dashboard / Stat - statGrp := admin.Group("/stat") - { - statGrp.GET("/getStats", handler.AdminDashboardSummary) - statGrp.GET("/getOverride", handler.AdminDashboardSummary) - statGrp.GET("/getTrafficRank", handler.AdminGetTrafficRank) - statGrp.GET("/getOrder", handler.AdminGetOrderStats) - statGrp.POST("/getStatUser", handler.AdminGetStatUser) - } - - // System - systemGrp := admin.Group("/system") - { - systemGrp.GET("/getSystemStatus", handler.AdminSystemStatus) - systemGrp.GET("/getQueueStats", handler.AdminSystemQueueStats) - systemGrp.GET("/getQueueWorkload", handler.AdminSystemQueueStats) - systemGrp.GET("/getQueueMasters", handler.AdminSystemQueueStats) - systemGrp.GET("/getHorizonFailedJobs", handler.AdminSystemQueueStats) - } - - // Essential Resources - admin.POST("/plan/fetch", handler.AdminPlansFetch) // Shifted to POST in manifest? Actually manifest says GET next to plan, let's keep GET and add POST if needed. - admin.GET("/plan/fetch", handler.AdminPlansFetch) - admin.POST("/plan/save", handler.AdminPlanSave) - admin.POST("/plan/update", handler.AdminPlanSave) - admin.POST("/plan/drop", handler.AdminPlanDrop) - admin.POST("/plan/sort", handler.AdminPlanSort) - - admin.POST("/user/fetch", handler.AdminUsersFetch) - admin.POST("/user/update", handler.AdminUserUpdate) - admin.POST("/user/ban", handler.AdminUserBan) - admin.POST("/user/delete", handler.AdminUserDelete) - admin.POST("/user/destroy", handler.AdminUserDelete) - admin.POST("/user/resetSecret", handler.AdminUserResetSecret) - admin.POST("/user/sendMail", handler.AdminUserSendMail) - - admin.POST("/order/fetch", handler.AdminOrdersFetch) - admin.POST("/order/detail", handler.AdminOrderDetail) - admin.POST("/order/paid", handler.AdminOrderPaid) - admin.POST("/order/cancel", handler.AdminOrderCancel) - admin.POST("/order/assign", handler.AdminOrderAssign) - admin.POST("/order/update", handler.AdminOrderUpdate) - - admin.POST("/ticket/fetch", handler.AdminTicketsFetch) - - // Knowledge Base - knowledgeGrp := admin.Group("/knowledge") - { - knowledgeGrp.GET("/fetch", handler.AdminKnowledgeFetch) - knowledgeGrp.POST("/save", handler.AdminKnowledgeSave) - knowledgeGrp.POST("/drop", handler.AdminKnowledgeDrop) - knowledgeGrp.POST("/sort", handler.AdminKnowledgeSort) - } - - // Traffic Reset - trafficResetGrp := admin.Group("/traffic-reset") - { - trafficResetGrp.GET("/fetch", handler.AdminTrafficResetFetch) - } - - // Real-name Verification - realNameGrp := admin.Group("/realname") - { - realNameGrp.GET("/fetch", handler.PluginRealNameList) - realNameGrp.POST("/review/:userId", handler.PluginRealNameReview) - realNameGrp.POST("/reset/:userId", handler.PluginRealNameReset) - realNameGrp.POST("/sync", handler.PluginRealNameSyncAll) - realNameGrp.POST("/approve-all", handler.PluginRealNameApproveAll) - } - - // Servers / Nodes - admin.GET("/server/group/fetch", handler.AdminServerGroupsFetch) - admin.POST("/server/group/save", handler.AdminServerGroupSave) - admin.POST("/server/group/drop", handler.AdminServerGroupDrop) - - admin.GET("/server/manage/getNodes", handler.AdminServerManageGetNodes) - admin.POST("/server/manage/save", handler.AdminServerManageSave) - admin.POST("/server/manage/update", handler.AdminServerManageSave) - admin.POST("/server/manage/drop", handler.AdminServerManageDrop) - admin.POST("/server/manage/sort", handler.AdminServerManageSort) - admin.POST("/server/manage/copy", handler.AdminServerManageCopy) - - // Router / Route - admin.GET("/server/route/fetch", handler.AdminServerRoutesFetch) - admin.POST("/server/route/save", handler.AdminServerRouteSave) - admin.POST("/server/route/drop", handler.AdminServerRouteDrop) - - // Notice - admin.GET("/notice/fetch", handler.AdminNoticeFetch) - admin.POST("/notice/save", handler.AdminNoticeSave) - admin.POST("/notice/drop", handler.AdminNoticeDrop) - admin.POST("/notice/show", handler.AdminNoticeShow) - admin.POST("/notice/sort", handler.AdminNoticeSort) - } - } - - - // SPA Fallback for Admin - only for non-API paths - r.NoRoute(func(c *gin.Context) { - path := c.Request.URL.Path - // Don't serve SPA for API calls - let them return 404 - if strings.HasPrefix(path, "/api/") { - c.JSON(404, gin.H{"message": "not found"}) - return - } - handler.AdminAppPage(c) - }) - - // Start server - log.Printf("Server starting on port %s", config.AppConfig.AppPort) - if err := r.Run(":" + config.AppConfig.AppPort); err != nil { - log.Fatalf("Failed to start server: %v", err) - } -} diff --git a/cmd/api/main_entry.go b/cmd/api/main_entry.go index 841c340..aa3c2d4 100644 --- a/cmd/api/main_entry.go +++ b/cmd/api/main_entry.go @@ -270,6 +270,8 @@ func registerAdminRoutesV2(v2 *gin.RouterGroup) { admin.POST("/realname/approve-all", handler.PluginRealNameApproveAll) admin.GET("/user-online-devices/users", handler.PluginUserOnlineDevicesUsers) admin.GET("/user-add-ipv6-subscription/users", handler.AdminIPv6SubscriptionUsers) + admin.GET("/user-add-ipv6-subscription/config", handler.AdminIPv6SubscriptionConfigFetch) + admin.POST("/user-add-ipv6-subscription/config", handler.AdminIPv6SubscriptionConfigSave) admin.POST("/user-add-ipv6-subscription/enable/:userId", handler.AdminIPv6SubscriptionEnable) admin.POST("/user-add-ipv6-subscription/sync-password/:userId", handler.AdminIPv6SubscriptionSyncPassword) } diff --git a/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js b/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js index ba0312d..5299acd 100644 --- a/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js +++ b/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js @@ -194986,7 +194986,7 @@ function NQe() { [r, o] = H.useState("singbox"), s = kv({ resolver: Ov(SQe), defaultValues: EQe, mode: "onChange" }), { data: a, isLoading: l } = gC({ - queryKey: ["settings", "client"], + queryKey: ["settings", "subscribe_template"], queryFn: () => xT.getSettings("subscribe_template"), }), { mutateAsync: c } = pC({ @@ -268562,34 +268562,35 @@ function QKt({ mobileGridFields: h, mobileActionSlot: g, onMobileRowClick: p, + onRowClick: f, }) { - const { t: f } = Py("common"), - m = H.useRef(null), - _ = t8e(), - v = e.getAllColumns().filter((e) => "left" === e.getIsPinned()), - b = e.getAllColumns().filter((e) => "right" === e.getIsPinned()), - y = e.getVisibleLeafColumns().length, - x = Math.min(e.getState().pagination.pageSize || 10, 10), - w = (e) => v.slice(0, e).reduce((e, t) => e + (t.getSize() ?? 0), 0), - C = (e) => b.slice(e + 1).reduce((e, t) => e + (t.getSize() ?? 0), 0); + const { t: m } = Py("common"), + _ = H.useRef(null), + v = t8e(), + b = e.getAllColumns().filter((e) => "left" === e.getIsPinned()), + y = e.getAllColumns().filter((e) => "right" === e.getIsPinned()), + x = e.getVisibleLeafColumns().length, + w = Math.min(e.getState().pagination.pageSize || 10, 10), + C = (e) => b.slice(0, e).reduce((e, t) => e + (t.getSize() ?? 0), 0), + S = (e) => y.slice(e + 1).reduce((e, t) => e + (t.getSize() ?? 0), 0); return Q.jsxs("div", { className: "space-y-4", children: [ "function" == typeof t ? t(e) : t, - _ + v ? Q.jsx(XKt, { rows: e.getRowModel().rows, primaryField: d, secondaryFields: u, gridFields: h, isLoading: c, - skeletonCount: x, - emptyMessage: f("table.noData"), + skeletonCount: w, + emptyMessage: m("table.noData"), onRowClick: p, actionSlot: g, }) : Q.jsx("div", { - ref: m, + ref: _, className: "relative overflow-auto rounded-md border bg-card", children: Q.jsx("div", { className: "overflow-auto", @@ -268602,10 +268603,10 @@ function QKt({ { className: "hover:bg-transparent", children: e.headers.map((e) => { - const t = "left" === e.column.getIsPinned(), - n = "right" === e.column.getIsPinned(), - i = t ? w(v.indexOf(e.column)) : void 0, - r = n ? C(b.indexOf(e.column)) : void 0; + const t = "left" === e.column.getIsPinned(), + n = "right" === e.column.getIsPinned(), + i = t ? C(b.indexOf(e.column)) : void 0, + r = n ? S(y.indexOf(e.column)) : void 0; return Q.jsx( HKt, { @@ -268639,12 +268640,12 @@ function QKt({ Q.jsx(BKt, { children: c && !e.getRowModel().rows?.length - ? Array.from({ length: x }).map((e, t) => + ? Array.from({ length: w }).map((e, t) => Q.jsx( WKt, { className: "animate-fade-in", - children: Array.from({ length: y }).map((e, n) => + children: Array.from({ length: x }).map((e, n) => Q.jsx( zKt, { children: Q.jsx(n$t, { className: "h-4 w-full" }) }, @@ -268663,16 +268664,17 @@ function QKt({ "data-state": e.getIsSelected() && "selected", className: "animate-fade-in hover:bg-muted/50", draggable: n, - onDragStart: (e) => i?.(e, t), - onDragEnd: r, - onDragOver: o, - onDragLeave: s, - onDrop: (e) => a?.(e, t), - children: e.getVisibleCells().map((e) => { - const t = "left" === e.column.getIsPinned(), - n = "right" === e.column.getIsPinned(), - i = t ? w(v.indexOf(e.column)) : void 0, - r = n ? C(b.indexOf(e.column)) : void 0; + onDragStart: (e) => i?.(e, t), + onDragEnd: r, + onDragOver: o, + onDragLeave: s, + onDrop: (e) => a?.(e, t), + onClick: () => f?.(e), + children: e.getVisibleCells().map((e) => { + const t = "left" === e.column.getIsPinned(), + n = "right" === e.column.getIsPinned(), + i = t ? C(b.indexOf(e.column)) : void 0, + r = n ? S(y.indexOf(e.column)) : void 0; return Q.jsx( zKt, { @@ -268702,9 +268704,9 @@ function QKt({ : Q.jsx(WKt, { className: "animate-fade-in", children: Q.jsx(zKt, { - colSpan: y, + colSpan: x, className: "h-24 text-center", - children: f("table.noData"), + children: m("table.noData"), }), }), }), @@ -269705,60 +269707,40 @@ const vGt = gy({ function bGt() { const { t: e } = Py("settings"), [t, n] = H.useState(!1), - i = H.useRef(null), { data: r } = gC({ queryKey: ["settings", "site"], queryFn: () => xT.getSettings("site") }), { data: o } = gC({ queryKey: ["plans"], queryFn: () => DD() }), s = kv({ resolver: Ov(vGt), defaultValues: {}, mode: "onBlur" }), - { mutateAsync: a } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("common.autoSaved")); + { mutateAsync: a } = pC({ mutationFn: xT.saveSettings }), + c = H.useCallback(() => {}, []), + l = s.handleSubmit( + async (t) => { + n(!0); + try { + const i = Object.entries(t).reduce((e, [t, n]) => ((e[t] = null === n ? "" : n), e), {}); + (await a(i)).data ? hN.success(e("common.autoSaved")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + n(!1); + } }, - }); + () => { + hN.error(e("common.saveFailed")); + }, + ); H.useEffect(() => { if (r?.data?.site) { const e = r?.data?.site; - (Object.entries(e).forEach(([e, t]) => { + Object.entries(e).forEach(([e, t]) => { s.setValue(e, t); - }), - (i.current = e)); + }); } }, [r, s]); - const l = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, i.current)) { - n(!0); - try { - const t = Object.entries(e).reduce( - (e, [t, n]) => ((e[t] = null === n ? "" : n), e), - {}, - ); - (await a(t), (i.current = e)); - } finally { - n(!1); - } - } - }, 1e3), - [a], - ), - c = H.useCallback( - (e) => { - l(e); - }, - [l], - ); return ( - H.useEffect(() => () => l.cancel(), [l]), - H.useEffect(() => { - const e = s.watch((e) => { - c(e); - }); - return () => e.unsubscribe(); - }, [s, c]), Q.jsx(Hy, { ...s, - children: Q.jsxs("div", { + children: Q.jsxs("form", { + onSubmit: l, className: "space-y-4", children: [ Q.jsx(Uy, { @@ -270087,11 +270069,21 @@ function bGt() { ], }), }), - t && - Q.jsx("div", { - className: "text-sm text-muted-foreground", - children: e("site.form.saving"), - }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + t && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("site.form.saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: t, + children: e("common.save", "保存"), + }), + ], + }), ], }), }) @@ -270184,66 +270176,41 @@ const yGt = Object.freeze( function CGt() { const { t: e } = Py("settings"), [t, n] = H.useState(!1), - [i, r] = H.useState(!1), - o = H.useRef(null), s = kv({ resolver: Ov(xGt), defaultValues: wGt, mode: "onBlur" }), - { data: a } = gC({ queryKey: ["settings", "safe"], queryFn: () => xT.getSettings("safe") }), - { mutateAsync: l } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("common.autoSaved")); + { data: i } = gC({ queryKey: ["settings", "safe"], queryFn: () => xT.getSettings("safe") }), + { mutateAsync: r } = pC({ mutationFn: xT.saveSettings }), + o = s.handleSubmit( + async (t) => { + n(!0); + try { + const i = { ...t, email_whitelist_suffix: t.email_whitelist_suffix?.filter(Boolean) || [] }; + (await r(i)).data ? hN.success(e("common.autoSaved")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + n(!1); + } }, - }); - H.useEffect(() => { - if (a?.data.safe) { - const e = a.data.safe, - t = {}; - (Object.entries(e).forEach(([e, n]) => { - if ("number" == typeof n) { - const i = String(n); - (s.setValue(e, i), (t[e] = i)); - } else (s.setValue(e, n), (t[e] = n)); - }), - (o.current = t), - r(!0)); - } - }, [a, s]); - const c = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, o.current)) { - n(!0); - try { - const t = { - ...e, - email_whitelist_suffix: e.email_whitelist_suffix?.filter(Boolean) || [], - }; - (await l(t), (o.current = e)); - } finally { - n(!1); - } - } - }, 1e3), - [l], - ), - d = H.useCallback( - (e) => { - i && c(e); + () => { + hN.error(e("common.saveFailed")); }, - [c, i], ); - return ( - H.useEffect(() => () => c.cancel(), [c]), - H.useEffect(() => { - if (!i) return; - const e = s.watch((e) => { - d(e); + H.useEffect(() => { + if (i?.data.safe) { + const e = i.data.safe; + Object.entries(e).forEach(([e, t]) => { + if ("number" == typeof t) { + const n = String(t); + s.setValue(e, n); + } else s.setValue(e, t); }); - return () => e.unsubscribe(); - }, [s, d, i]), + } + }, [i, s]); + return ( Q.jsx(Hy, { ...s, - children: Q.jsxs("div", { + children: Q.jsxs("form", { + onSubmit: o, className: "space-y-4", children: [ Q.jsx(Uy, { @@ -270265,9 +270232,7 @@ function CGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], @@ -270292,9 +270257,7 @@ function CGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], @@ -270319,9 +270282,7 @@ function CGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], @@ -270339,9 +270300,6 @@ function CGt() { placeholder: e("safe.form.securePath.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { children: e("safe.form.securePath.description") }), @@ -270368,9 +270326,7 @@ function CGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], @@ -270394,7 +270350,7 @@ function CGt() { value: (t.value || []).join("\n"), onChange: (e) => { const n = e.target.value.split("\n").filter(Boolean); - (t.onChange(n), d(s.getValues())); + t.onChange(n); }, }), }), @@ -270422,9 +270378,7 @@ function CGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], @@ -270444,9 +270398,7 @@ function CGt() { children: e("safe.form.captcha.type.label"), }), Q.jsxs(THt, { - onValueChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onValueChange: t.onChange, value: t.value || "recaptcha", children: [ Q.jsx(Zy, { @@ -270497,9 +270449,6 @@ function CGt() { placeholder: e("safe.form.captcha.recaptcha.siteKey.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { @@ -270524,9 +270473,6 @@ function CGt() { placeholder: e("safe.form.captcha.recaptcha.key.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { @@ -270558,9 +270504,6 @@ function CGt() { ), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { @@ -270587,9 +270530,6 @@ function CGt() { ), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { @@ -270620,9 +270560,6 @@ function CGt() { ), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { @@ -270654,9 +270591,6 @@ function CGt() { placeholder: e("safe.form.captcha.turnstile.siteKey.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { @@ -270683,9 +270617,6 @@ function CGt() { ), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { @@ -270718,9 +270649,7 @@ function CGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], @@ -270744,9 +270673,6 @@ function CGt() { placeholder: e("safe.form.registerLimit.count.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { children: e("safe.form.registerLimit.count.description") }), @@ -270769,9 +270695,6 @@ function CGt() { placeholder: e("safe.form.registerLimit.expire.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { children: e("safe.form.registerLimit.expire.description") }), @@ -270800,9 +270723,7 @@ function CGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], @@ -270826,9 +270747,6 @@ function CGt() { placeholder: e("safe.form.passwordLimit.count.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { children: e("safe.form.passwordLimit.count.description") }), @@ -270851,9 +270769,6 @@ function CGt() { placeholder: e("safe.form.passwordLimit.expire.placeholder"), ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, }), }), Q.jsx(Yy, { children: e("safe.form.passwordLimit.expire.description") }), @@ -270863,11 +270778,21 @@ function CGt() { }), ], }), - t && - Q.jsx("div", { - className: "text-sm text-muted-foreground", - children: e("safe.form.saving"), - }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + t && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("safe.form.saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: t, + children: e("common.save", "Save"), + }), + ], + }), ], }), }) @@ -270930,62 +270855,44 @@ const SGt = Object.freeze( function NGt() { const { t: e } = Py("settings"), [t, n] = H.useState(!1), - i = H.useRef(null), - r = kv({ resolver: Ov(kGt), defaultValues: EGt, mode: "onBlur" }), - { data: o } = gC({ + i = kv({ resolver: Ov(kGt), defaultValues: EGt, mode: "onBlur" }), + { data: r } = gC({ queryKey: ["settings", "subscribe"], queryFn: () => xT.getSettings("subscribe"), }), - { mutateAsync: s } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("common.autoSaved")); + { mutateAsync: o } = pC({ mutationFn: xT.saveSettings }), + s = i.handleSubmit( + async (t) => { + n(!0); + try { + (await o(t)).data ? hN.success(e("common.autoSaved")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + n(!1); + } }, - }); - H.useEffect(() => { - if (o?.data?.subscribe) { - const e = o?.data?.subscribe; - (Object.entries(e).forEach(([e, t]) => { - r.setValue(e, t); - }), - (i.current = e)); - } - }, [o, r]); - const a = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, i.current)) { - n(!0); - try { - (await s(e), (i.current = e)); - } finally { - n(!1); - } - } - }, 1e3), - [s], - ), - l = H.useCallback( - (e) => { - a(e); + () => { + hN.error(e("common.saveFailed")); }, - [a], ); - return ( - H.useEffect(() => () => a.cancel(), [a]), - H.useEffect(() => { - const e = r.watch((e) => { - l(e); + H.useEffect(() => { + if (r?.data?.subscribe) { + const e = r?.data?.subscribe; + Object.entries(e).forEach(([e, t]) => { + i.setValue(e, t); }); - return () => e.unsubscribe(); - }, [r, l]), + } + }, [r, i]); + return ( Q.jsx(Hy, { - ...r, - children: Q.jsxs("div", { + ...i, + children: Q.jsxs("form", { + onSubmit: s, className: "space-y-4", children: [ Q.jsx(Uy, { - control: r.control, + control: i.control, name: "plan_change_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -270998,9 +270905,7 @@ function NGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), l(r.getValues())); - }, + onCheckedChange: t.onChange, }), }), Q.jsx(Xy, {}), @@ -271008,7 +270913,7 @@ function NGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "reset_traffic_method", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271018,7 +270923,9 @@ function NGt() { children: e("subscribe.reset_traffic_method.title"), }), Q.jsxs(THt, { - onValueChange: t.onChange, + onValueChange: (e) => { + t.onChange(Number(e)); + }, value: t.value?.toString() || "0", children: [ Q.jsx(Zy, { @@ -271058,7 +270965,7 @@ function NGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "surplus_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271071,9 +270978,7 @@ function NGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), l(r.getValues())); - }, + onCheckedChange: t.onChange, }), }), Q.jsx(Xy, {}), @@ -271081,7 +270986,7 @@ function NGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "new_order_event_id", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271094,7 +270999,9 @@ function NGt() { className: "relative w-max", children: Q.jsx(Zy, { children: Q.jsxs(THt, { - onValueChange: t.onChange, + onValueChange: (e) => { + t.onChange(Number(e)); + }, value: t.value?.toString(), children: [ Q.jsx(OHt, { children: Q.jsx(RHt, { placeholder: "请选择" }) }), @@ -271120,7 +271027,7 @@ function NGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "renew_order_event_id", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271133,7 +271040,9 @@ function NGt() { className: "relative w-max", children: Q.jsx(Zy, { children: Q.jsxs(THt, { - onValueChange: t.onChange, + onValueChange: (e) => { + t.onChange(Number(e)); + }, value: t.value?.toString(), children: [ Q.jsx(OHt, { children: Q.jsx(RHt, { placeholder: "请选择" }) }), @@ -271159,7 +271068,7 @@ function NGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "change_order_event_id", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271172,7 +271081,9 @@ function NGt() { className: "relative w-max", children: Q.jsx(Zy, { children: Q.jsxs(THt, { - onValueChange: t.onChange, + onValueChange: (e) => { + t.onChange(Number(e)); + }, value: t.value?.toString(), children: [ Q.jsx(OHt, { children: Q.jsx(RHt, { placeholder: "请选择" }) }), @@ -271198,7 +271109,7 @@ function NGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "subscribe_path", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271212,9 +271123,6 @@ function NGt() { placeholder: "subscribe", ...t, value: t.value || "", - onChange: (e) => { - (t.onChange(e), l(r.getValues())); - }, }), }), Q.jsxs("div", { @@ -271232,7 +271140,7 @@ function NGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "show_info_to_server_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271250,16 +271158,14 @@ function NGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), l(r.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "show_protocol_to_server_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271277,19 +271183,27 @@ function NGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), l(r.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), - t && - Q.jsx("div", { - className: "text-sm text-muted-foreground", - children: e("common.saving"), - }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + t && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("common.saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: t, + children: e("common.save", "Save"), + }), + ], + }), ], }), }) @@ -271356,66 +271270,44 @@ const LGt = Object.freeze( function IGt() { const { t: e } = Py("settings"), [t, n] = H.useState(!1), - [i, r] = H.useState(!1), - o = H.useRef(null), - s = kv({ resolver: Ov(DGt), defaultValues: TGt, mode: "onBlur" }), - { data: a } = gC({ queryKey: ["settings", "invite"], queryFn: () => xT.getSettings("invite") }), - { mutateAsync: l } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("common.autoSaved")); + i = kv({ resolver: Ov(DGt), defaultValues: TGt, mode: "onBlur" }), + { data: r } = gC({ queryKey: ["settings", "invite"], queryFn: () => xT.getSettings("invite") }), + { mutateAsync: o } = pC({ mutationFn: xT.saveSettings }), + s = i.handleSubmit( + async (t) => { + n(!0); + try { + (await o(t)).data ? hN.success(e("common.autoSaved")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + n(!1); + } }, - }); - H.useEffect(() => { - if (a?.data?.invite) { - const e = a?.data?.invite, - t = {}; - (Object.entries(e).forEach(([e, n]) => { - if ("number" == typeof n) { - const i = String(n); - (s.setValue(e, i), (t[e] = i)); - } else (s.setValue(e, n), (t[e] = n)); - }), - (o.current = t), - r(!0)); - } - }, [a, s]); - const c = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, o.current)) { - n(!0); - try { - (await l(e), (o.current = e)); - } finally { - n(!1); - } - } - }, 1e3), - [l], - ), - d = H.useCallback( - (e) => { - i && c(e); + () => { + hN.error(e("common.saveFailed")); }, - [c, i], ); - return ( - H.useEffect(() => () => c.cancel(), [c]), - H.useEffect(() => { - if (!i) return; - const e = s.watch((e) => { - d(e); + H.useEffect(() => { + if (r?.data?.invite) { + const e = r?.data?.invite; + Object.entries(e).forEach(([e, t]) => { + if ("number" == typeof t) { + const n = String(t); + i.setValue(e, n); + } else i.setValue(e, t); }); - return () => e.unsubscribe(); - }, [s, d, i]), + } + }, [r, i]); + return ( Q.jsx(Hy, { - ...s, - children: Q.jsxs("div", { + ...i, + children: Q.jsxs("form", { + onSubmit: s, className: "space-y-4", children: [ Q.jsx(Uy, { - control: s.control, + control: i.control, name: "invite_force", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271433,16 +271325,14 @@ function IGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "invite_commission", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271464,7 +271354,7 @@ function IGt() { }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "invite_gen_limit", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271486,7 +271376,7 @@ function IGt() { }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "invite_never_expire", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271504,16 +271394,14 @@ function IGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_first_time_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271531,16 +271419,14 @@ function IGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_auto_check_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271558,16 +271444,14 @@ function IGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_withdraw_limit", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271589,7 +271473,7 @@ function IGt() { }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_withdraw_method", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271605,7 +271489,7 @@ function IGt() { value: Array.isArray(t.value) ? t.value.join(",") : "", onChange: (e) => { const n = e.target.value.split(",").filter(Boolean); - (t.onChange(n), d(s.getValues())); + t.onChange(n); }, }), }), @@ -271615,7 +271499,7 @@ function IGt() { }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "withdraw_close_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271633,16 +271517,14 @@ function IGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_distribution_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271660,19 +271542,17 @@ function IGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value, - onCheckedChange: (e) => { - (t.onChange(e), d(s.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), - s.watch("commission_distribution_enable") && + i.watch("commission_distribution_enable") && Q.jsxs(Q.Fragment, { children: [ Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_distribution_l1", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271686,7 +271566,7 @@ function IGt() { value: t.value || "", onChange: (e) => { const n = e.target.value ? Number(e.target.value) : 0; - (t.onChange(n), d(s.getValues())); + t.onChange(n); }, }), }), @@ -271695,7 +271575,7 @@ function IGt() { }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_distribution_l2", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271709,7 +271589,7 @@ function IGt() { value: t.value || "", onChange: (e) => { const n = e.target.value ? Number(e.target.value) : 0; - (t.onChange(n), d(s.getValues())); + t.onChange(n); }, }), }), @@ -271718,7 +271598,7 @@ function IGt() { }), }), Q.jsx(Uy, { - control: s.control, + control: i.control, name: "commission_distribution_l3", render: ({ field: t }) => Q.jsxs(Ky, { @@ -271732,7 +271612,7 @@ function IGt() { value: t.value || "", onChange: (e) => { const n = e.target.value ? Number(e.target.value) : 0; - (t.onChange(n), d(s.getValues())); + t.onChange(n); }, }), }), @@ -271742,11 +271622,21 @@ function IGt() { }), ], }), - t && - Q.jsx("div", { - className: "text-sm text-muted-foreground", - children: e("invite.saving"), - }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + t && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("invite.saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: t, + children: e("common.save", "Save"), + }), + ], + }), ], }), }) @@ -271968,61 +271858,43 @@ const PGt = Object.freeze( function VGt() { const { t: e } = Py("settings"), [t, n] = H.useState(!1), - i = H.useRef(null), - r = kv({ resolver: Ov(FGt), defaultValues: BGt, mode: "onBlur" }), - { data: o } = gC({ queryKey: ["settings", "server"], queryFn: () => xT.getSettings("server") }), - { data: s } = gC({ queryKey: ["settings", "site"], queryFn: () => xT.getSettings("site") }), - a = s?.data?.site?.site_url || "", - { mutateAsync: l } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("common.autoSaved")); + i = kv({ resolver: Ov(FGt), defaultValues: BGt, mode: "onBlur" }), + { data: r } = gC({ queryKey: ["settings", "server"], queryFn: () => xT.getSettings("server") }), + { data: o } = gC({ queryKey: ["settings", "site"], queryFn: () => xT.getSettings("site") }), + s = o?.data?.site?.site_url || "", + { mutateAsync: a } = pC({ mutationFn: xT.saveSettings }), + l = i.handleSubmit( + async (t) => { + n(!0); + try { + (await a(t)).data ? hN.success(e("common.autoSaved")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + n(!1); + } }, - }); - H.useEffect(() => { - if (o?.data.server) { - const e = o.data.server; - (Object.entries(e).forEach(([e, t]) => { - r.setValue(e, t); - }), - !e.server_ws_url && a && r.setValue("server_ws_url", a), - (i.current = r.getValues())); - } - }, [o, a, r]); - const c = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, i.current)) { - n(!0); - try { - (await l(e), (i.current = e)); - } finally { - n(!1); - } - } - }, 1e3), - [l], - ), - d = H.useCallback( - (e) => { - c(e); + () => { + hN.error(e("common.saveFailed")); }, - [c], ); - (H.useEffect(() => () => c.cancel(), [c]), - H.useEffect(() => { - const e = r.watch((e) => { - d(e); - }); - return () => e.unsubscribe(); - }, [r, d])); + H.useEffect(() => { + if (r?.data.server) { + const e = r.data.server; + (Object.entries(e).forEach(([e, t]) => { + i.setValue(e, t); + }), + !e.server_ws_url && s && i.setValue("server_ws_url", s)); + } + }, [r, s, i]); return Q.jsx(Hy, { - ...r, - children: Q.jsxs("div", { + ...i, + children: Q.jsxs("form", { + onSubmit: l, className: "space-y-4", children: [ Q.jsx(Uy, { - control: r.control, + control: i.control, name: "server_token", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272057,7 +271929,7 @@ function VGt() { let n = ""; for (let i = 0; i < e; i++) n += t.charAt(Math.floor(62 * Math.random())); - r.setValue("server_token", n); + i.setValue("server_token", n); })()); }, children: Q.jsx(zat, { @@ -272082,7 +271954,7 @@ function VGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "server_pull_interval", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272109,7 +271981,7 @@ function VGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "server_push_interval", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272136,7 +272008,7 @@ function VGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "server_ws_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272158,9 +272030,9 @@ function VGt() { ], }), }), - r.watch("server_ws_enable") && + i.watch("server_ws_enable") && Q.jsx(Uy, { - control: r.control, + control: i.control, name: "server_ws_url", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272168,7 +272040,7 @@ function VGt() { Q.jsx(Gy, { className: "text-base", children: e("server.server_ws_url.title") }), Q.jsx(Zy, { children: Q.jsx(Q6e, { - placeholder: a || e("server.server_ws_url.placeholder"), + placeholder: s || e("server.server_ws_url.placeholder"), ...t, value: t.value || "", }), @@ -272186,11 +272058,21 @@ function VGt() { Q.jsx("span", { children: e("server.server_ws_enable.supported_clients") }), ], }), - t && - Q.jsx("div", { - className: "text-sm text-muted-foreground", - children: e("server.saving"), - }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + t && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("server.saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: t, + children: e("common.save", "Save"), + }), + ], + }), ], }), }); @@ -272325,25 +272207,19 @@ const zGt = gy({ email_username: ly().nullable().default(""), email_password: ly().nullable().default(""), email_encryption: ly().nullable().default(""), - email_from_address: ly().email().nullable().default(""), + email_from_address: ly().email().nullable().default(null), remind_mail_enable: dy().nullable().default(!1), }); function UGt() { const { t: e } = Py("settings"), [t, n] = H.useState(null), [i, r] = H.useState(!1), - o = H.useRef(null), - [s, a] = H.useState(!1), - l = kv({ resolver: Ov(zGt), defaultValues: {}, mode: "onBlur" }), - { data: c } = gC({ queryKey: ["settings", "email"], queryFn: () => xT.getSettings("email") }), - { data: d } = gC({ queryKey: ["emailTemplate"], queryFn: () => xT.getEmailTemplate() }), - { mutateAsync: u } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("common.autoSaved")); - }, - }), - { mutate: h, isPending: g } = pC({ + [o, s] = H.useState(!1), + a = kv({ resolver: Ov(zGt), defaultValues: {}, mode: "onBlur" }), + { data: l } = gC({ queryKey: ["settings", "email"], queryFn: () => xT.getSettings("email") }), + { data: c } = gC({ queryKey: ["emailTemplate"], queryFn: () => xT.getEmailTemplate() }), + { mutateAsync: d } = pC({ mutationFn: xT.saveSettings }), + { mutate: u, isPending: h } = pC({ mutationFn: xT.sendTestMail, onMutate: () => { (n(null), r(!1)); @@ -272353,53 +272229,41 @@ function UGt() { r(!0), t.data.error ? hN.error(e("email.test.error")) : hN.success(e("email.test.success"))); }, - }); - H.useEffect(() => { - if (c?.data.email) { - const e = c.data.email; - (Object.entries(e).forEach(([e, t]) => { - l.setValue(e, t); - }), - (o.current = e)); - } - }, [c, l]); - const p = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, o.current)) { - a(!0); - try { - (await u(e), (o.current = e)); - } finally { - a(!1); - } - } - }, 1e3), - [u], - ), - f = H.useCallback( - (e) => { - p(e); + }), + g = a.handleSubmit( + async (t) => { + s(!0); + try { + (await d(t)).data ? hN.success(e("common.autoSaved")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + s(!1); + } + }, + () => { + hN.error(e("common.saveFailed")); }, - [p], ); - return ( - H.useEffect(() => () => p.cancel(), [p]), - H.useEffect(() => { - const e = l.watch((e) => { - f(e); + H.useEffect(() => { + if (l?.data.email) { + const e = l.data.email; + Object.entries(e).forEach(([e, t]) => { + a.setValue(e, "email_from_address" === e ? t || null : t); }); - return () => e.unsubscribe(); - }, [l, f]), + } + }, [l, a]); + return ( Q.jsxs(Q.Fragment, { children: [ Q.jsx(Hy, { - ...l, - children: Q.jsxs("div", { + ...a, + children: Q.jsxs("form", { + onSubmit: g, className: "space-y-4", children: [ Q.jsx(Uy, { - control: l.control, + control: a.control, name: "email_host", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272415,10 +272279,10 @@ function UGt() { Q.jsx(Yy, { children: e("email.email_host.description") }), Q.jsx(Xy, {}), ], - }), + }), }), Q.jsx(Uy, { - control: l.control, + control: a.control, name: "email_port", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272439,10 +272303,10 @@ function UGt() { Q.jsx(Yy, { children: e("email.email_port.description") }), Q.jsx(Xy, {}), ], - }), + }), }), Q.jsx(Uy, { - control: l.control, + control: a.control, name: "email_encryption", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272487,10 +272351,10 @@ function UGt() { Q.jsx(Yy, { children: e("email.email_encryption.description") }), Q.jsx(Xy, {}), ], - }), + }), }), Q.jsx(Uy, { - control: l.control, + control: a.control, name: "email_username", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272510,10 +272374,10 @@ function UGt() { Q.jsx(Yy, { children: e("email.email_username.description") }), Q.jsx(Xy, {}), ], - }), + }), }), Q.jsx(Uy, { - control: l.control, + control: a.control, name: "email_password", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272534,10 +272398,10 @@ function UGt() { Q.jsx(Yy, { children: e("email.email_password.description") }), Q.jsx(Xy, {}), ], - }), + }), }), Q.jsx(Uy, { - control: l.control, + control: a.control, name: "email_from_address", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272548,15 +272412,18 @@ function UGt() { placeholder: e("common.placeholder"), ...t, value: t.value || "", + onChange: (e) => { + t.onChange(e.target.value || null); + }, }), }), Q.jsx(Yy, { children: e("email.email_from.description") }), Q.jsx(Xy, {}), ], - }), + }), }), Q.jsx(Uy, { - control: l.control, + control: a.control, name: "email_template", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272566,9 +272433,7 @@ function UGt() { children: e("email.email_template.title"), }), Q.jsxs(THt, { - onValueChange: (e) => { - (t.onChange(e), f(l.getValues())); - }, + onValueChange: t.onChange, value: t.value || void 0, children: [ Q.jsx(Zy, { @@ -272580,17 +272445,17 @@ function UGt() { }), }), Q.jsx(PHt, { - children: d?.data?.map((e) => Q.jsx(FHt, { value: e, children: e }, e)), + children: c?.data?.map((e) => Q.jsx(FHt, { value: e, children: e }, e)), }), ], }), Q.jsx(Yy, { children: e("email.email_template.description") }), Q.jsx(Xy, {}), ], - }), + }), }), Q.jsx(Uy, { - control: l.control, + control: a.control, name: "remind_mail_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272608,27 +272473,42 @@ function UGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), f(l.getValues())); - }, + onCheckedChange: t.onChange, }), }), ], }), }), - Q.jsx("div", { - className: "flex items-center justify-between", - children: Q.jsx(Nm, { - onClick: () => h(), - loading: g, - disabled: g, - children: e(g ? "email.test.sending" : "email.test.title"), - }), + Q.jsxs("div", { + className: "flex items-center justify-between gap-3", + children: [ + Q.jsx(Nm, { + type: "button", + onClick: () => u(), + loading: h, + disabled: h, + children: e(h ? "email.test.sending" : "email.test.title"), + }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + o && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: o, + children: e("common.save", "Save"), + }), + ], + }), + ], }), ], }), }), - s && Q.jsx("div", { className: "text-sm text-muted-foreground", children: e("saving") }), t && Q.jsx(HGt, { open: i, onOpenChange: r, result: t }), ], }) @@ -272677,19 +272557,13 @@ const $Gt = Object.freeze( function GGt() { const { t: e } = Py("settings"), [t, n] = H.useState(!1), - i = H.useRef(null), - r = kv({ resolver: Ov(qGt), defaultValues: KGt, mode: "onBlur" }), - { data: o } = gC({ + i = kv({ resolver: Ov(qGt), defaultValues: KGt, mode: "onBlur" }), + { data: r } = gC({ queryKey: ["settings", "telegram"], queryFn: () => xT.getSettings("telegram"), }), - { mutateAsync: s } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("common.autoSaved")); - }, - }), - { mutate: a, isPending: l } = pC({ + { mutateAsync: o } = pC({ mutationFn: xT.saveSettings }), + { mutate: s, isPending: a } = pC({ mutationFn: xT.setTelegramWebhook, onSuccess: (t) => { const n = t.data; @@ -272727,53 +272601,41 @@ function GGt() { }); } }, - }); - H.useEffect(() => { - if (o?.data.telegram) { - const e = o.data.telegram; - (Object.entries(e).forEach(([e, t]) => { - r.setValue(e, t); - }), - (i.current = e)); - } - }, [o, r]); - const c = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, i.current)) { - n(!0); - try { - (await s(e), (i.current = e)); - } finally { - n(!1); - } - } - }, 1e3), - [s], - ), - d = H.useCallback( - (e) => { - c(e); + }), + l = i.handleSubmit( + async (t) => { + n(!0); + try { + (await o(t)).data ? hN.success(e("common.autoSaved")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + n(!1); + } + }, + () => { + hN.error(e("common.saveFailed")); }, - [c], ); - (H.useEffect(() => () => c.cancel(), [c]), - H.useEffect(() => { - const e = r.watch((e) => { - d(e); + H.useEffect(() => { + if (r?.data.telegram) { + const e = r.data.telegram; + Object.entries(e).forEach(([e, t]) => { + i.setValue(e, t); }); - return () => e.unsubscribe(); - }, [r, d])); - const u = Boolean(r.watch("telegram_bot_token")), - h = r.watch("telegram_webhook_url")?.trim(), - g = h ? e("telegram.webhook.target_custom", { url: h }) : e("telegram.webhook.target_default"); + } + }, [r, i]); + const c = Boolean(i.watch("telegram_bot_token")), + d = i.watch("telegram_webhook_url")?.trim(), + u = d ? e("telegram.webhook.target_custom", { url: d }) : e("telegram.webhook.target_default"); return Q.jsx(Hy, { - ...r, - children: Q.jsxs("div", { + ...i, + children: Q.jsxs("form", { + onSubmit: l, className: "space-y-4", children: [ Q.jsx(Uy, { - control: r.control, + control: i.control, name: "telegram_bot_token", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272791,9 +272653,9 @@ function GGt() { ], }), }), - u && + c && Q.jsx(Uy, { - control: r.control, + control: i.control, name: "telegram_webhook_url", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272813,10 +272675,10 @@ function GGt() { Q.jsx(Nm, { type: "button", className: "sm:shrink-0", - loading: l, - disabled: l, - onClick: () => a(), - children: e(l ? "telegram.webhook.setting" : "telegram.webhook.button"), + loading: a, + disabled: a, + onClick: () => s(), + children: e(a ? "telegram.webhook.setting" : "telegram.webhook.button"), }), ], }), @@ -272825,7 +272687,7 @@ function GGt() { className: "space-y-1.5", children: [ Q.jsxs("div", { - children: [e("telegram.webhook_url.description"), " ", g], + children: [e("telegram.webhook_url.description"), " ", u], }), Q.jsxs("a", { href: "https://core.telegram.org/bots/webhooks", @@ -272841,12 +272703,12 @@ function GGt() { ], }), }), - Q.jsx(Xy, {}), - ], - }), - }), + Q.jsx(Xy, {}), + ], + }), + }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "telegram_bot_enable", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272856,9 +272718,7 @@ function GGt() { Q.jsx(Zy, { children: Q.jsx(mGt, { checked: t.value || !1, - onCheckedChange: (e) => { - (t.onChange(e), d(r.getValues())); - }, + onCheckedChange: t.onChange, }), }), Q.jsx(Xy, {}), @@ -272866,7 +272726,7 @@ function GGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "telegram_discuss_link", render: ({ field: t }) => Q.jsxs(Ky, { @@ -272884,11 +272744,21 @@ function GGt() { ], }), }), - t && - Q.jsx("div", { - className: "text-sm text-muted-foreground", - children: e("common.saving"), - }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + t && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("common.saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: t, + children: e("common.save", "Save"), + }), + ], + }), ], }), }); @@ -272940,59 +272810,41 @@ const ZGt = Object.freeze( function QGt() { const { t: e } = Py("settings"), [t, n] = H.useState(!1), - i = H.useRef(null), - r = kv({ resolver: Ov(YGt), defaultValues: XGt, mode: "onBlur" }), - { data: o } = gC({ queryKey: ["settings", "app"], queryFn: () => xT.getSettings("app") }), - { mutateAsync: s } = pC({ - mutationFn: xT.saveSettings, - onSuccess: (t) => { - t.data && hN.success(e("app.save_success")); + i = kv({ resolver: Ov(YGt), defaultValues: XGt, mode: "onBlur" }), + { data: r } = gC({ queryKey: ["settings", "app"], queryFn: () => xT.getSettings("app") }), + { mutateAsync: o } = pC({ mutationFn: xT.saveSettings }), + s = i.handleSubmit( + async (t) => { + n(!0); + try { + (await o(t)).data ? hN.success(e("app.save_success")) : hN.error(e("common.saveFailed")); + } catch { + hN.error(e("common.saveFailed")); + } finally { + n(!1); + } }, - }); - H.useEffect(() => { - if (o?.data.app) { - const e = o.data.app; - (Object.entries(e).forEach(([e, t]) => { - r.setValue(e, t); - }), - (i.current = e)); - } - }, [o, r]); - const a = H.useMemo( - () => - kT.debounce(async (e) => { - if (!kT.isEqual(e, i.current)) { - n(!0); - try { - (await s(e), (i.current = e)); - } finally { - n(!1); - } - } - }, 1e3), - [s], - ), - l = H.useCallback( - (e) => { - a(e); + () => { + hN.error(e("common.saveFailed")); }, - [a], ); - return ( - H.useEffect(() => () => a.cancel(), [a]), - H.useEffect(() => { - const e = r.watch((e) => { - l(e); + H.useEffect(() => { + if (r?.data.app) { + const e = r.data.app; + Object.entries(e).forEach(([e, t]) => { + i.setValue(e, t); }); - return () => e.unsubscribe(); - }, [r, l]), + } + }, [r, i]); + return ( Q.jsx(Hy, { - ...r, - children: Q.jsxs("div", { + ...i, + children: Q.jsxs("form", { + onSubmit: s, className: "space-y-4", children: [ Q.jsx(Uy, { - control: r.control, + control: i.control, name: "windows_version", render: ({ field: t }) => Q.jsxs(Ky, { @@ -273011,7 +272863,7 @@ function QGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "windows_download_url", render: ({ field: t }) => Q.jsxs(Ky, { @@ -273030,7 +272882,7 @@ function QGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "macos_version", render: ({ field: t }) => Q.jsxs(Ky, { @@ -273049,7 +272901,7 @@ function QGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "macos_download_url", render: ({ field: t }) => Q.jsxs(Ky, { @@ -273068,7 +272920,7 @@ function QGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "android_version", render: ({ field: t }) => Q.jsxs(Ky, { @@ -273087,7 +272939,7 @@ function QGt() { }), }), Q.jsx(Uy, { - control: r.control, + control: i.control, name: "android_download_url", render: ({ field: t }) => Q.jsxs(Ky, { @@ -273105,11 +272957,21 @@ function QGt() { ], }), }), - t && - Q.jsx("div", { - className: "text-sm text-muted-foreground", - children: e("common.saving"), - }), + Q.jsxs("div", { + className: "flex items-center justify-end gap-3", + children: [ + t && + Q.jsx("div", { + className: "text-sm text-muted-foreground", + children: e("common.saving"), + }), + Q.jsx(Nm, { + type: "submit", + loading: t, + children: e("common.save", "Save"), + }), + ], + }), ], }), }) @@ -278049,7 +277911,7 @@ function VQt() { ], }); } -const WQt = Object.freeze( +const WQt_legacy = Object.freeze( Object.defineProperty( { __proto__: null, @@ -278452,6 +278314,902 @@ const WQt = Object.freeze( { value: "Module" }, ), ); +/* const WQt_temp = Object.freeze( + Object.defineProperty( + { + __proto__: null, + default: function () { + const nebulaThemeDefaults = { + nebula_theme_color: "aurora", + nebula_hero_slogan: "", + nebula_welcome_target: "", + nebula_register_title: "", + nebula_background_url: "", + nebula_metrics_base_url: "", + nebula_default_theme_mode: "system", + nebula_light_logo_url: "", + nebula_dark_logo_url: "", + nebula_custom_html: "", + nebula_static_cdn_url: "", + }, + nebulaThemeColorOptions = [ + { value: "aurora", label: "Aurora" }, + { value: "sunset", label: "Sunset" }, + { value: "ember", label: "Ember" }, + { value: "violet", label: "Violet" }, + ], + nebulaThemeModeOptions = [ + { value: "system", label: "跟随系统" }, + { value: "dark", label: "深色优先" }, + { value: "light", label: "浅色优先" }, + ], + { data: e, isLoading: t } = gC({ + queryKey: ["settings", "nebula"], + queryFn: () => xT.getSettings("nebula"), + }), + n = kv({ defaultValues: nebulaThemeDefaults, mode: "onBlur" }), + { mutateAsync: i, isPending: r } = pC({ + mutationFn: xT.saveSettings, + onSuccess: (e) => { + e.data ? hN.success("Nebula 配置已保存") : hN.error("Nebula 配置保存失败"); + }, + onError: () => { + hN.error("Nebula 配置保存失败"); + }, + }), + o = n.handleSubmit(async (e) => { + await i( + Object.entries(e).reduce((e, [t, n]) => ((e[t] = null == n ? "" : n), e), {}), + ); + }); + return ( + H.useEffect(() => { + const t = e?.data?.nebula || {}; + n.reset({ + ...nebulaThemeDefaults, + ...Object.entries(t).reduce((e, [t, n]) => ((e[t] = null == n ? "" : n), e), {}), + }); + }, [e, n]), + Q.jsxs(Wot, { + children: [ + Q.jsxs(Hot, { + className: "flex items-center justify-between", + children: [ + Q.jsx(Vdt, {}), + Q.jsxs("div", { + className: "flex items-center space-x-4", + children: [Q.jsx(Wdt, {}), Q.jsx(vut, {})], + }), + ], + }), + Q.jsxs(zot, { + className: "", + children: [ + Q.jsxs("header", { + className: "mb-8", + children: [ + Q.jsx("div", { + className: "mb-2", + children: Q.jsx("h1", { + className: "text-2xl font-bold tracking-tight", + children: "Nebula 主题配置", + }), + }), + Q.jsxs("div", { + className: "flex items-center justify-between gap-4", + children: [ + Q.jsx("div", { + className: "text-muted-foreground", + children: + "集中配置 Nebula 前台主题的配色、文案、背景、Logo 与扩展注入内容。", + }), + Q.jsx(Nm, { + type: "submit", + form: "nebula-theme-config-form", + className: "ml-4 shrink-0", + loading: r, + children: "保存配置", + }), + ], + }), + ], + }), + Q.jsx(Hy, { + ...n, + children: Q.jsxs("form", { + id: "nebula-theme-config-form", + onSubmit: o, + className: "space-y-6", + children: [ + Q.jsx(q6e, { + children: Q.jsxs(Q.Fragment, { + children: [ + Q.jsxs(K6e, { + children: [ + Q.jsx(G6e, { children: "基础展示" }), + Q.jsx(Z6e, { + children: "配置 Nebula 首页主色、默认明暗模式以及首页文案内容。", + }), + ], + }), + Q.jsx(Y6e, { + children: t + ? Q.jsx("div", { + className: + "rounded-lg border border-dashed bg-muted/30 px-4 py-8 text-sm text-muted-foreground", + children: "正在加载 Nebula 配置...", + }) + : Q.jsxs("div", { + className: "grid gap-4 md:grid-cols-2", + children: [ + Q.jsx(Uy, { + control: n.control, + name: "nebula_theme_color", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "主题色方案" }), + Q.jsxs("div", { + className: "relative", + children: [ + Q.jsx(Zy, { + children: Q.jsx("select", { + className: + "flex h-9 w-full appearance-none rounded-md border border-input bg-transparent px-3 py-1 pr-9 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", + value: e.value || "aurora", + onChange: (t) => { + e.onChange(t.target.value); + }, + children: nebulaThemeColorOptions.map((e) => + Q.jsx( + "option", + { value: e.value, children: e.label }, + e.value, + ), + ), + }), + }), + Q.jsx(m7e, { + className: + "pointer-events-none absolute right-3 top-2.5 h-4 w-4 opacity-50", + }), + ], + }), + Q.jsx(Yy, { + children: "控制 Nebula 前台的默认色彩风格。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_default_theme_mode", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "默认主题模式" }), + Q.jsxs("div", { + className: "relative", + children: [ + Q.jsx(Zy, { + children: Q.jsx("select", { + className: + "flex h-9 w-full appearance-none rounded-md border border-input bg-transparent px-3 py-1 pr-9 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", + value: e.value || "system", + onChange: (t) => { + e.onChange(t.target.value); + }, + children: nebulaThemeModeOptions.map((e) => + Q.jsx( + "option", + { value: e.value, children: e.label }, + e.value, + ), + ), + }), + }), + Q.jsx(m7e, { + className: + "pointer-events-none absolute right-3 top-2.5 h-4 w-4 opacity-50", + }), + ], + }), + Q.jsx(Yy, { + children: "首次进入前台时默认应用的明暗模式策略。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_hero_slogan", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "首页主标语" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "输入首页主视觉标语", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "显示在首页主视觉区域的主标题文案。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_welcome_target", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "欢迎对象" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "例如 Voyage Network", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "显示在 Welcome to 后面的目标名称。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_register_title", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "注册页标题" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "输入注册面板标题", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "注册页面顶部显示的标题文案。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_metrics_base_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "指标接口地址" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "https://example.com/api/metrics", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: + "未登录状态展示性能指标时使用的 API 基础地址,可留空。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + ], + }), + }), + ], + }), + }), + Q.jsx(q6e, { + children: Q.jsxs(Q.Fragment, { + children: [ + Q.jsxs(K6e, { + children: [ + Q.jsx(G6e, { children: "品牌与扩展" }), + Q.jsx(Z6e, { + children: "配置 Logo、背景、静态资源地址以及自定义 HTML 注入。", + }), + ], + }), + Q.jsx(Y6e, { + children: Q.jsxs("div", { + className: "grid gap-4 md:grid-cols-2", + children: [ + Q.jsx(Uy, { + control: n.control, + name: "nebula_background_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "背景图地址" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: + "https://example.com/background.webp", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Nebula 登录页或首页使用的背景图片链接。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_static_cdn_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "静态资源 CDN" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "https://cdn.example.com/nebula", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Nebula 静态资源的 CDN 根路径,可留空。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_light_logo_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "浅色 Logo 地址" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: + "https://example.com/logo-light.svg", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "浅色模式下显示的品牌 Logo。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_dark_logo_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "深色 Logo 地址" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: + "https://example.com/logo-dark.svg", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "深色模式下显示的品牌 Logo。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_custom_html", + render: ({ field: e }) => + Q.jsxs(Ky, { + className: "md:col-span-2", + children: [ + Q.jsx(Gy, { children: "自定义 HTML / 脚本" }), + Q.jsx(Zy, { + children: Q.jsx(_Gt, { + className: "min-h-[220px] font-mono text-xs", + placeholder: + "可注入自定义 HTML、脚本或样式到 Nebula 页面中", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: + "用于追加统计脚本、验证代码或局部样式覆盖。", + }), + Q.jsx(Xy, {}), + ], + }), + }), + ], + }), + }), + ], + }), + }), + ], + }), + }), + ], + }), + ], + }) + ); + }, + }, + Symbol.toStringTag, + { value: "Module" }, + ), +); +*/ +const WQt = Object.freeze( + Object.defineProperty( + { + __proto__: null, + default: function () { + const nebulaThemeDefaults = { + nebula_theme_color: "aurora", + nebula_hero_slogan: "", + nebula_welcome_target: "", + nebula_register_title: "", + nebula_background_url: "", + nebula_metrics_base_url: "", + nebula_default_theme_mode: "system", + nebula_light_logo_url: "", + nebula_dark_logo_url: "", + nebula_custom_html: "", + nebula_static_cdn_url: "", + }, + nebulaThemeColorOptions = [ + { value: "aurora", label: "Aurora" }, + { value: "sunset", label: "Sunset" }, + { value: "ember", label: "Ember" }, + { value: "violet", label: "Violet" }, + ], + nebulaThemeModeOptions = [ + { value: "system", label: "Follow system" }, + { value: "dark", label: "Prefer dark" }, + { value: "light", label: "Prefer light" }, + ], + { data: e, isLoading: t } = gC({ + queryKey: ["settings", "nebula"], + queryFn: () => xT.getSettings("nebula"), + }), + n = kv({ defaultValues: nebulaThemeDefaults, mode: "onBlur" }), + { mutateAsync: i, isPending: r } = pC({ + mutationFn: xT.saveSettings, + onSuccess: (e) => { + e.data ? hN.success("Nebula settings saved") : hN.error("Nebula settings save failed"); + }, + onError: () => { + hN.error("Nebula settings save failed"); + }, + }), + o = n.handleSubmit(async (e) => { + await i( + Object.entries(e).reduce((e, [t, n]) => ((e[t] = null == n ? "" : n), e), {}), + ); + }); + return ( + H.useEffect(() => { + const t = e?.data?.nebula || {}; + n.reset({ + ...nebulaThemeDefaults, + ...Object.entries(t).reduce((e, [t, n]) => ((e[t] = null == n ? "" : n), e), {}), + }); + }, [e, n]), + Q.jsxs(Wot, { + children: [ + Q.jsxs(Hot, { + className: "flex items-center justify-between", + children: [ + Q.jsx(Vdt, {}), + Q.jsxs("div", { + className: "flex items-center space-x-4", + children: [Q.jsx(Wdt, {}), Q.jsx(vut, {})], + }), + ], + }), + Q.jsxs(zot, { + className: "", + children: [ + Q.jsxs("header", { + className: "mb-8", + children: [ + Q.jsx("div", { + className: "mb-2", + children: Q.jsx("h1", { + className: "text-2xl font-bold tracking-tight", + children: "Nebula Theme", + }), + }), + Q.jsxs("div", { + className: "flex items-center justify-between gap-4", + children: [ + Q.jsx("div", { + className: "text-muted-foreground", + children: + "Configure Nebula theme colors, copywriting, branding assets, and custom injections.", + }), + Q.jsx(Nm, { + type: "submit", + form: "nebula-theme-config-form", + className: "ml-4 shrink-0", + loading: r, + children: "Save settings", + }), + ], + }), + ], + }), + Q.jsx(Hy, { + ...n, + children: Q.jsxs("form", { + id: "nebula-theme-config-form", + onSubmit: o, + className: "space-y-6", + children: [ + Q.jsx(q6e, { + children: Q.jsxs(Q.Fragment, { + children: [ + Q.jsxs(K6e, { + children: [ + Q.jsx(G6e, { children: "Display" }), + Q.jsx(Z6e, { + children: + "Set the primary color, theme mode, and hero copy shown by Nebula.", + }), + ], + }), + Q.jsx(Y6e, { + children: t + ? Q.jsx("div", { + className: + "rounded-lg border border-dashed bg-muted/30 px-4 py-8 text-sm text-muted-foreground", + children: "Loading Nebula settings...", + }) + : Q.jsxs("div", { + className: "grid gap-4 md:grid-cols-2", + children: [ + Q.jsx(Uy, { + control: n.control, + name: "nebula_theme_color", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Theme palette" }), + Q.jsxs("div", { + className: "relative", + children: [ + Q.jsx(Zy, { + children: Q.jsx("select", { + className: + "flex h-9 w-full appearance-none rounded-md border border-input bg-transparent px-3 py-1 pr-9 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", + value: e.value || "aurora", + onChange: (t) => { + e.onChange(t.target.value); + }, + children: nebulaThemeColorOptions.map((e) => + Q.jsx( + "option", + { value: e.value, children: e.label }, + e.value, + ), + ), + }), + }), + Q.jsx(m7e, { + className: + "pointer-events-none absolute right-3 top-2.5 h-4 w-4 opacity-50", + }), + ], + }), + Q.jsx(Yy, { + children: "Controls the default Nebula color palette.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_default_theme_mode", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Theme mode" }), + Q.jsxs("div", { + className: "relative", + children: [ + Q.jsx(Zy, { + children: Q.jsx("select", { + className: + "flex h-9 w-full appearance-none rounded-md border border-input bg-transparent px-3 py-1 pr-9 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", + value: e.value || "system", + onChange: (t) => { + e.onChange(t.target.value); + }, + children: nebulaThemeModeOptions.map((e) => + Q.jsx( + "option", + { value: e.value, children: e.label }, + e.value, + ), + ), + }), + }), + Q.jsx(m7e, { + className: + "pointer-events-none absolute right-3 top-2.5 h-4 w-4 opacity-50", + }), + ], + }), + Q.jsx(Yy, { + children: + "Applied when a user opens the Nebula frontend for the first time.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_hero_slogan", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Hero slogan" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "Enter the main slogan", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Shown as the main title in the hero area.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_welcome_target", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Welcome target" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "For example: Voyage Network", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Appended after the Welcome to heading.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_register_title", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Register title" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "Enter the register panel title", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Displayed at the top of the register panel.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_metrics_base_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Metrics API URL" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "https://example.com/api/metrics", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: + "Base URL used when Nebula shows public metrics before login.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + ], + }), + }), + ], + }), + }), + Q.jsx(q6e, { + children: Q.jsxs(Q.Fragment, { + children: [ + Q.jsxs(K6e, { + children: [ + Q.jsx(G6e, { children: "Branding" }), + Q.jsx(Z6e, { + children: + "Set background assets, logo URLs, CDN roots, and custom HTML.", + }), + ], + }), + Q.jsx(Y6e, { + children: Q.jsxs("div", { + className: "grid gap-4 md:grid-cols-2", + children: [ + Q.jsx(Uy, { + control: n.control, + name: "nebula_background_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Background URL" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: + "https://example.com/background.webp", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Used by the Nebula login or landing background.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_static_cdn_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Static CDN URL" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: "https://cdn.example.com/nebula", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Root CDN path for Nebula static assets.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_light_logo_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Light logo URL" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: + "https://example.com/logo-light.svg", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Displayed while the light theme is active.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_dark_logo_url", + render: ({ field: e }) => + Q.jsxs(Ky, { + children: [ + Q.jsx(Gy, { children: "Dark logo URL" }), + Q.jsx(Zy, { + children: Q.jsx(Q6e, { + placeholder: + "https://example.com/logo-dark.svg", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: "Displayed while the dark theme is active.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + Q.jsx(Uy, { + control: n.control, + name: "nebula_custom_html", + render: ({ field: e }) => + Q.jsxs(Ky, { + className: "md:col-span-2", + children: [ + Q.jsx(Gy, { children: "Custom HTML / scripts" }), + Q.jsx(Zy, { + children: Q.jsx(_Gt, { + className: "min-h-[220px] font-mono text-xs", + placeholder: + "Inject custom HTML, scripts, or styles into Nebula pages", + ...e, + value: e.value || "", + }), + }), + Q.jsx(Yy, { + children: + "Useful for analytics scripts, verification tags, or scoped style overrides.", + }), + Q.jsx(Xy, {}), + ], + }), + }), + ], + }), + }), + ], + }), + }), + ], + }), + }), + ], + }), + ], + }) + ); + }, + }, + Symbol.toStringTag, + { value: "Module" }, + ), +); var HQt, zQt = new Uint8Array(16); function UQt() { @@ -293723,16 +294481,35 @@ const I5t = { 2: "bg-emerald-500/80 shadow-sm shadow-emerald-500/50", }, R5t = (e, t) => (t > 0 ? Math.round((e / t) * 100) : 0), - O5t = z.memo(({ node: e, t: t }) => { + O5t = z.memo(({ node: e, t: t, expanded: r = !1, toggleExpand: o, hasChildren: s = !1 }) => { const n = e.id, - i = e.code; + i = e.code, + a = !!(e.parent_id ?? e.parent?.id), + l = s && !a; return Q.jsxs(jst, { children: [ Q.jsx(Fst, { asChild: !0, children: Q.jsxs("div", { - className: "group/id flex items-center space-x-2", + className: Rf("group/id flex items-center space-x-2", e.__tree_level > 0 && "pl-6"), children: [ + l && + Q.jsx(Nm, { + variant: "ghost", + size: "icon", + className: + "size-6 flex-shrink-0 text-muted-foreground/60 transition-colors hover:text-foreground", + title: r ? "Collapse children" : "Expand children", + onClick: (e) => { + e.stopPropagation(), o?.(n); + }, + children: r ? Q.jsx(lat, { className: "size-3.5" }) : Q.jsx(dat, { className: "size-3.5" }), + }), + !a && + !l && + Q.jsx("span", { + className: "size-6 flex-shrink-0", + }), Q.jsxs(hqt, { variant: "outline", className: Rf( @@ -293745,7 +294522,7 @@ const I5t = { Q.jsxs("span", { className: "flex flex-col items-start gap-0.5 leading-tight", children: [ - e.parent + a ? Q.jsxs("span", { className: "flex items-center gap-1", children: [ @@ -294204,7 +294981,7 @@ const I5t = { }), A5t = z.memo(({ node: e, t: t }) => Q.jsxs("div", { - className: "flex items-center space-x-2.5 outline-none", + className: Rf("flex items-center space-x-2.5 outline-none", e.__tree_level > 0 && "pl-6"), children: [ Q.jsxs(jst, { children: [ @@ -294298,33 +295075,18 @@ const I5t = { F5t = "bg-destructive/80 shadow-sm shadow-destructive/50", B5t = "bg-yellow-500/80 shadow-sm shadow-yellow-500/50", V5t = "bg-emerald-500/80 shadow-sm shadow-emerald-500/50", - W5t = (e, t) => [ - { - id: "select", - size: 40, - enableSorting: !1, - enableHiding: !1, - header: ({ table: e }) => { - const n = e.getRowModel().rows, - i = n.length > 0 && n.every((e) => e.getIsSelected()); - return Q.jsx(L5t, { - "aria-label": t("columns.select_all", "选择全部"), - checked: i, - onCheckedChange: (e) => n.forEach((t) => t.toggleSelected(!!e)), - disabled: !n.length, - }); - }, - cell: ({ row: e }) => - Q.jsx(L5t, { - "aria-label": t("columns.select_row", "选择节点"), - checked: e.getIsSelected(), - onCheckedChange: (t) => e.toggleSelected(!!t), - }), - }, + W5t = (e, t, n, i, r) => [ { accessorKey: "id", header: ({ column: e }) => Q.jsx(SYt, { column: e, title: t("columns.nodeId") }), - cell: ({ row: e }) => Q.jsx(O5t, { node: e.original, t: t }), + cell: ({ row: e }) => + Q.jsx(O5t, { + node: e.original, + t: t, + expanded: !!n[e.original.id], + toggleExpand: i, + hasChildren: !r && !!e.original.__has_child_nodes, + }), size: 50, enableSorting: !0, }, @@ -294596,6 +295358,7 @@ function z5t() { [u, h] = H.useState(!1), [g, p] = H.useState({}), [f, m] = H.useState([]), + [k, S] = H.useState({}), { data: _, refetch: v, @@ -294632,7 +295395,39 @@ function z5t() { p({ name: u ? 2e3 : 200 }), l({ pageSize: u ? 99999 : 500, pageIndex: 0 })); }, [u]); - const x = async () => { + const T = H.useMemo(() => { + const e = Array.isArray(f) ? f : [], + t = [], + n = new Map(); + e.forEach((e) => { + const i = Number(e.parent_id ?? 0); + if (i > 0) { + const r = n.get(i) || []; + r.push({ ...e, __tree_level: 1 }), n.set(i, r); + return; + } + t.push(e); + }); + const i = t.flatMap((e) => { + const t = n.get(e.id) || [], + i = t.length > 0 || !!e.has_children, + r = { ...e, __tree_level: 0, __has_child_nodes: i }; + return i && k[e.id] ? [r, ...t] : [r]; + }), + r = new Set(t.map((e) => e.id)); + return ( + n.forEach((e, t) => { + r.has(t) || i.push(...e); + }), + i + ); + }, [f, k]), + N = H.useCallback((e) => { + S((t) => ({ ...t, [e]: !t[e] })); + }, []), + E = !u && 0 === o.length && 0 === c.length, + j = u ? f || [] : E ? T : f || [], + x = async () => { if (!u) return void h(!0); const e = f?.map((e, t) => ({ id: e.id, order: t + 1 })); try { @@ -294642,8 +295437,8 @@ function z5t() { } }, w = PKt({ - data: f || [], - columns: W5t(v, e), + data: j, + columns: W5t(v, e, k, N, u), state: { sorting: c, columnVisibility: i, @@ -294697,6 +295492,9 @@ function z5t() { [r] = i.splice(n, 1); (i.splice(t, 0, r), m(i)); }, + onRowClick: (e) => { + u || e.toggleSelected(!e.getIsSelected()); + }, showPagination: !u, isLoading: b, mobilePrimaryField: "name", @@ -304849,16 +305647,18 @@ function codexNativeOnlineDevicesTable() { function codexNativeOnlineDevicesPage() { return codexNativePageLayout("在线设备", "查看用户在线 IP、数量和最后在线时间。", Q.jsx(codexNativeOnlineDevicesTable, {})); } -function codexNativeIPv6Table() { +/* function codexNativeIPv6Table() { const [e, t] = H.useState(""), [n, i] = H.useState(""), [r, o] = H.useState({}), [s, a] = H.useState({}), [l, c] = H.useState({ pageIndex: 0, pageSize: 20 }), + [d, u] = H.useState("0"), + [h, g] = H.useState(!1), { - refetch: d, - data: u, - isLoading: h, + refetch: p, + data: f, + isLoading: m, } = gC({ queryKey: ["codexNativeIPv6", l, n], queryFn: () => @@ -304866,7 +305666,16 @@ function codexNativeIPv6Table() { params: { page: l.pageIndex + 1, per_page: l.pageSize, keyword: n }, }), }), - g = H.useMemo( + { data: _, refetch: v } = gC({ + queryKey: ["codexNativeIPv6Config"], + queryFn: () => TL(`${RL()}/user-add-ipv6-subscription/config`), + }), + { data: b } = gC({ + queryKey: ["codexNativeIPv6Plans"], + queryFn: () => DD(), + }), + y = H.useMemo(() => (Array.isArray(b?.data) ? b.data : []), [b]), + x = H.useMemo( () => [ JKt.display({ id: "relation", @@ -304909,7 +305718,7 @@ function codexNativeIPv6Table() { onClick: async () => { await IL(`${RL()}/user-add-ipv6-subscription/enable/${e.original.id}`, {}); hN.success("已开通并同步 IPv6 子账号"); - d(); + p(); }, children: "开通并同步", }), @@ -304920,7 +305729,7 @@ function codexNativeIPv6Table() { onClick: async () => { await IL(`${RL()}/user-add-ipv6-subscription/sync-password/${e.original.id}`, {}); hN.success("已同步密码"); - d(); + p(); }, children: "同步密码", }), @@ -304928,14 +305737,14 @@ function codexNativeIPv6Table() { }), }), ], - [d], + [p], ), - p = PKt({ - data: u?.data?.list ?? [], - columns: g, + w = PKt({ + data: f?.data?.list ?? [], + columns: x, state: { columnVisibility: r, rowSelection: s, pagination: l }, getRowId: (e) => String(e.id), - rowCount: u?.data?.pagination?.total ?? 0, + rowCount: f?.data?.pagination?.total ?? 0, manualPagination: !0, enableRowSelection: !0, onRowSelectionChange: a, @@ -304944,35 +305753,280 @@ function codexNativeIPv6Table() { getCoreRowModel: LKt(), getPaginationRowModel: OKt(), }); - return Q.jsxs("div", { - className: "space-y-4", - children: [ - Q.jsx(codexNativeSearchToolbar, { - keyword: e, - setKeyword: t, - onSearch: () => { - c((e) => ({ ...e, pageIndex: 0 })); - i(e.trim()); - }, - onReset: () => { - t(""); - i(""); - c((e) => ({ ...e, pageIndex: 0 })); - }, - refetch: d, - }), - Q.jsx(QKt, { - table: p, - isLoading: h, - showPagination: !0, - mobilePrimaryField: "email", - mobileGridFields: ["ipv6_email", "plan_name", "status"], - }), - ], - }); + return ( + H.useEffect(() => { + const e = _?.data?.ipv6_plan_id; + u(e && e > 0 ? String(e) : "0"); + }, [_]), + Q.jsxs("div", { + className: "space-y-4", + children: [ + Q.jsx(codexNativeSearchToolbar, { + keyword: e, + setKeyword: t, + onSearch: () => { + c((e) => ({ ...e, pageIndex: 0 })); + i(e.trim()); + }, + onReset: () => { + t(""); + i(""); + c((e) => ({ ...e, pageIndex: 0 })); + }, + refetch: p, + actions: Q.jsxs("div", { + className: "flex flex-wrap items-center gap-2", + children: [ + Q.jsx("span", { className: "text-sm text-muted-foreground", children: "IPv6Only 套餐" }), + Q.jsxs("div", { + className: "relative", + children: [ + Q.jsxs("select", { + className: + "flex h-8 min-w-[180px] appearance-none rounded-md border border-input bg-transparent px-3 pr-8 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", + value: d, + onChange: (e) => u(e.target.value), + children: [ + Q.jsx("option", { value: "0", children: "未设置" }), + y.map((e) => + Q.jsx("option", { value: String(e.id), children: e.name || `Plan ${e.id}` }, e.id), + ), + ], + }), + Q.jsx(m7e, { + className: + "pointer-events-none absolute right-2 top-2 h-4 w-4 text-muted-foreground/70", + }), + ], + }), + Q.jsx(Nm, { + className: "h-8", + loading: h, + onClick: async () => { + g(!0); + try { + await IL(`${RL()}/user-add-ipv6-subscription/config`, { + ipv6_plan_id: Number(d) || 0, + }); + hN.success("已保存 IPv6Only 套餐"); + await Promise.all([v(), p()]); + } finally { + g(!1); + } + }, + children: "保存套餐", + }), + ], + }), + }), + Q.jsx(QKt, { + table: w, + isLoading: m, + showPagination: !0, + mobilePrimaryField: "email", + mobileGridFields: ["ipv6_email", "plan_name", "status"], + }), + ], + }) + ); } function codexNativeIPv6Page() { - return codexNativePageLayout("IPv6 子账号", "管理 IPv6 子账号开通、主从关系和密码同步。", Q.jsx(codexNativeIPv6Table, {})); + return codexNativePageLayout( + "IPv6 子账号", + "管理 IPv6 子账号开通、IPv6Only 套餐配置、主从关系和密码同步。", + Q.jsx(codexNativeIPv6Table, {}), + ); +} +*/ +function codexNativeIPv6Table() { + const [e, t] = H.useState(""), + [n, i] = H.useState(""), + [r, o] = H.useState({}), + [s, a] = H.useState({}), + [l, c] = H.useState({ pageIndex: 0, pageSize: 20 }), + [d, u] = H.useState("0"), + [h, g] = H.useState(!1), + { + refetch: p, + data: f, + isLoading: m, + } = gC({ + queryKey: ["codexNativeIPv6", l, n], + queryFn: () => + TL(`${RL()}/user-add-ipv6-subscription/users`, { + params: { page: l.pageIndex + 1, per_page: l.pageSize, keyword: n }, + }), + }), + { data: _, refetch: v } = gC({ + queryKey: ["codexNativeIPv6Config"], + queryFn: () => TL(`${RL()}/user-add-ipv6-subscription/config`), + }), + { data: b } = gC({ + queryKey: ["codexNativeIPv6Plans"], + queryFn: () => DD(), + }), + y = H.useMemo(() => (Array.isArray(b?.data) ? b.data : []), [b]), + x = H.useMemo( + () => [ + JKt.display({ + id: "relation", + header: () => "Relation", + cell: ({ row: e }) => + codexNativeRelationChip(e.original.id, e.original.shadow_user_id || "-"), + }), + { + accessorKey: "email", + header: () => "Primary account", + cell: ({ row: e }) => + Q.jsx("div", { className: "max-w-[240px] truncate", children: e.getValue("email") || "-" }), + }, + { + accessorKey: "ipv6_email", + header: () => "IPv6 account", + cell: ({ row: e }) => + Q.jsx("div", { className: "font-mono text-xs", children: e.getValue("ipv6_email") || "-" }), + }, + { + accessorKey: "plan_name", + header: () => "IPv6 plan", + cell: ({ row: e }) => e.getValue("plan_name") || "-", + }, + JKt.display({ + id: "status", + header: () => "Status", + cell: ({ row: e }) => codexNativeStatusBadge(e.original.status_label || e.original.status), + }), + JKt.display({ + id: "actions", + header: () => "Actions", + cell: ({ row: e }) => + Q.jsxs("div", { + className: "flex flex-wrap items-center gap-2", + children: [ + Q.jsx(Nm, { + size: "sm", + className: "h-8", + onClick: async () => { + await IL(`${RL()}/user-add-ipv6-subscription/enable/${e.original.id}`, {}); + hN.success("IPv6 account enabled and synced"); + p(); + }, + children: "Enable and sync", + }), + Q.jsx(Nm, { + size: "sm", + variant: "outline", + className: "h-8", + onClick: async () => { + await IL(`${RL()}/user-add-ipv6-subscription/sync-password/${e.original.id}`, {}); + hN.success("Password synced"); + p(); + }, + children: "Sync password", + }), + ], + }), + }), + ], + [p], + ), + w = PKt({ + data: f?.data?.list ?? [], + columns: x, + state: { columnVisibility: r, rowSelection: s, pagination: l }, + getRowId: (e) => String(e.id), + rowCount: f?.data?.pagination?.total ?? 0, + manualPagination: !0, + enableRowSelection: !0, + onRowSelectionChange: a, + onColumnVisibilityChange: o, + onPaginationChange: c, + getCoreRowModel: LKt(), + getPaginationRowModel: OKt(), + }); + return ( + H.useEffect(() => { + const e = _?.data?.ipv6_plan_id; + u(e && e > 0 ? String(e) : "0"); + }, [_]), + Q.jsxs("div", { + className: "space-y-4", + children: [ + Q.jsx(codexNativeSearchToolbar, { + keyword: e, + setKeyword: t, + onSearch: () => { + c((e) => ({ ...e, pageIndex: 0 })); + i(e.trim()); + }, + onReset: () => { + t(""); + i(""); + c((e) => ({ ...e, pageIndex: 0 })); + }, + refetch: p, + actions: Q.jsxs("div", { + className: "flex flex-wrap items-center gap-2", + children: [ + Q.jsx("span", { className: "text-sm text-muted-foreground", children: "IPv6Only plan" }), + Q.jsxs("div", { + className: "relative", + children: [ + Q.jsxs("select", { + className: + "flex h-8 min-w-[180px] appearance-none rounded-md border border-input bg-transparent px-3 pr-8 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", + value: d, + onChange: (e) => u(e.target.value), + children: [ + Q.jsx("option", { value: "0", children: "Not set" }), + y.map((e) => + Q.jsx("option", { value: String(e.id), children: e.name || `Plan ${e.id}` }, e.id), + ), + ], + }), + Q.jsx(m7e, { + className: + "pointer-events-none absolute right-2 top-2 h-4 w-4 text-muted-foreground/70", + }), + ], + }), + Q.jsx(Nm, { + className: "h-8", + loading: h, + onClick: async () => { + g(!0); + try { + await IL(`${RL()}/user-add-ipv6-subscription/config`, { + ipv6_plan_id: Number(d) || 0, + }); + hN.success("IPv6Only plan saved"); + await Promise.all([v(), p()]); + } finally { + g(!1); + } + }, + children: "Save plan", + }), + ], + }), + }), + Q.jsx(QKt, { + table: w, + isLoading: m, + showPagination: !0, + mobilePrimaryField: "email", + mobileGridFields: ["ipv6_email", "plan_name", "status"], + }), + ], + }) + ); +} +function codexNativeIPv6Page() { + return codexNativePageLayout( + "IPv6 subscription", + "Manage IPv6 account activation, IPv6Only plan configuration, relation mapping, and password sync.", + Q.jsx(codexNativeIPv6Table, {}), + ); } function c8t() { const [e] = Hg(), diff --git a/frontend/admin/src-reverse/pages/config/ThemeConfigPage.js b/frontend/admin/src-reverse/pages/config/ThemeConfigPage.js index fe06810..4e31ed1 100644 --- a/frontend/admin/src-reverse/pages/config/ThemeConfigPage.js +++ b/frontend/admin/src-reverse/pages/config/ThemeConfigPage.js @@ -2,7 +2,7 @@ import { makeRecoveredPage } from "../../runtime/makeRecoveredPage.js"; import ThemeConfigPageView from "./ThemeConfigPage.jsx"; export default makeRecoveredPage({ - title: "Theme Config", + title: "Nebula Theme", routePath: "/config/theme", moduleId: "WQt", featureKey: "config.theme", diff --git a/frontend/admin/src-reverse/pages/config/ThemeConfigPage.jsx b/frontend/admin/src-reverse/pages/config/ThemeConfigPage.jsx index 34f05d4..db65bfe 100644 --- a/frontend/admin/src-reverse/pages/config/ThemeConfigPage.jsx +++ b/frontend/admin/src-reverse/pages/config/ThemeConfigPage.jsx @@ -1,224 +1,282 @@ import React, { useEffect, useState } from "../../../recovery-preview/node_modules/react/index.js"; -import { - compactText, - requestJson, -} from "../../runtime/client.js"; +import { requestJson } from "../../runtime/client.js"; -// Wot: Wrapper structure for the config page function Wot({ children }) { - return
- Theme Customization -
-{description}
-{description}
+{description}
+