From d2732e650b64d288850f91108edbde4002813e39 Mon Sep 17 00:00:00 2001 From: turbolnk Date: Thu, 6 Mar 2025 09:34:39 +0800 Subject: [PATCH 01/17] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20Enhance?= =?UTF-8?q?=20user=20navigation=20dropdown=20ui=20and=20styling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/user/components/user-nav.tsx | 57 ++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/apps/user/components/user-nav.tsx b/apps/user/components/user-nav.tsx index b1451b0..8472728 100644 --- a/apps/user/components/user-nav.tsx +++ b/apps/user/components/user-nav.tsx @@ -4,13 +4,11 @@ import { navs } from '@/config/navs'; import useGlobalStore from '@/config/use-global'; import { Logout } from '@/utils/common'; import { Avatar, AvatarFallback, AvatarImage } from '@workspace/ui/components/avatar'; -import { Button } from '@workspace/ui/components/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, - DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '@workspace/ui/components/dropdown-menu'; @@ -27,37 +25,58 @@ export function UserNav() { return ( - + + {user?.auth_methods?.[0]?.auth_identifier.split('@')[0]} + + + - - -
-

ID: {user?.id}

-

+ +

+ + + + {user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)} + + +
+

+ {user?.auth_methods?.[0]?.auth_identifier.split('@')[0]} +

+

{user?.auth_methods?.[0]?.auth_identifier}

- +
{navs.map((nav) => ( - {/* {nav.items && {t(nav.title)}} */} {(nav.items || [nav]).map((item) => ( { router.push(`${item.url}`); }} + className="flex items-center gap-2 py-2 cursor-pointer" > - - {t(item.title)} + + {t(item.title)} + ))} @@ -68,10 +87,10 @@ export function UserNav() { Logout(); setUser(); }} + className="flex items-center gap-2 py-2 text-destructive focus:text-destructive cursor-pointer" > - - - {t('logout')} + + {t('logout')} From 32220167997e524536598095e1db82e0ce726eca Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 2 Mar 2025 16:40:10 +0000 Subject: [PATCH 02/17] =?UTF-8?q?=F0=9F=94=A7=20chore(merge):=20Bump=20ver?= =?UTF-8?q?sion=20to=201.0.0-beta.26=20and=20update=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb7dcf4..188dd07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # Changelog +# [1.0.0-beta.26](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2025-03-02) + + +### 🐛 Bug Fixes + +* **icon**: Comment out unused icon collection imports ([f17bf8d](https://github.com/perfect-panel/ppanel-web/commit/f17bf8d)) + # [1.0.0-beta.25](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2025-03-01) diff --git a/package.json b/package.json index 8afa2a1..98c5c80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ppanel-web", - "version": "1.0.0-beta.25", + "version": "1.0.0-beta.26", "private": true, "homepage": "https://github.com/perfect-panel/ppanel-web", "bugs": { From 2215c7f2b98b7d9447ad2eb26249940d3cdd1002 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Sat, 8 Mar 2025 12:34:23 +0700 Subject: [PATCH 03/17] =?UTF-8?q?=E2=9C=A8=20feat(subscription):=20Refacto?= =?UTF-8?q?r=20subscription=20handling=20and=20update=20imports=20for=20be?= =?UTF-8?q?tter=20organization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 28 +- .../user-subscription/subscription-detail.tsx | 2 +- apps/admin/services/admin/typings.d.ts | 42 ++- apps/admin/services/common/common.ts | 11 - apps/admin/services/common/typings.d.ts | 56 ++-- apps/user/app/(main)/(user)/payment/page.tsx | 8 +- apps/user/app/(main)/page.tsx | 2 +- apps/user/app/(main)/purchasing/content.tsx | 221 +++++++++++++++ .../user/app/(main)/purchasing/order/page.tsx | 266 ++++++++++++++++++ .../app/(main)/purchasing/order/stripe.tsx | 109 +++++++ apps/user/app/(main)/purchasing/page.tsx | 23 ++ apps/user/app/auth/page.tsx | 51 +--- apps/user/components/auth/oauth-methods.tsx | 56 ++++ .../content.tsx} | 27 +- .../main/product-showcase/index.tsx | 17 ++ .../components/subscribe/payment-methods.tsx | 20 +- apps/user/components/subscribe/purchase.tsx | 5 +- apps/user/components/subscribe/recharge.tsx | 5 +- apps/user/components/subscribe/renewal.tsx | 5 +- .../components/subscribe/reset-traffic.tsx | 5 +- apps/user/services/common/common.ts | 11 - apps/user/services/common/typings.d.ts | 56 ++-- apps/user/services/user/index.ts | 4 +- apps/user/services/user/order.ts | 15 - apps/user/services/user/portal.ts | 91 ++++++ apps/user/services/user/typings.d.ts | 85 +++++- apps/user/utils/common.ts | 1 + 27 files changed, 1042 insertions(+), 180 deletions(-) create mode 100644 apps/user/app/(main)/purchasing/content.tsx create mode 100644 apps/user/app/(main)/purchasing/order/page.tsx create mode 100644 apps/user/app/(main)/purchasing/order/stripe.tsx create mode 100644 apps/user/app/(main)/purchasing/page.tsx create mode 100644 apps/user/components/auth/oauth-methods.tsx rename apps/user/components/main/{product-showcase.tsx => product-showcase/content.tsx} (90%) create mode 100644 apps/user/components/main/product-showcase/index.tsx create mode 100644 apps/user/services/user/portal.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 188dd07..c4ec8da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,49 +1,43 @@ + # Changelog # [1.0.0-beta.26](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2025-03-02) - ### 🐛 Bug Fixes -* **icon**: Comment out unused icon collection imports ([f17bf8d](https://github.com/perfect-panel/ppanel-web/commit/f17bf8d)) +- **icon**: Comment out unused icon collection imports ([f17bf8d](https://github.com/perfect-panel/ppanel-web/commit/f17bf8d)) # [1.0.0-beta.25](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2025-03-01) - ### ✨ Features -* **auth**: Add privacy policy link to the footer ([8e16ef1](https://github.com/perfect-panel/ppanel-web/commit/8e16ef1)) - +- **auth**: Add privacy policy link to the footer ([8e16ef1](https://github.com/perfect-panel/ppanel-web/commit/8e16ef1)) ### 🐛 Bug Fixes -* **dashboard**: Display subscription creation date in user dashboard ([d0e6df0](https://github.com/perfect-panel/ppanel-web/commit/d0e6df0)) -* **request**: Add error code 40005 to trigger logout ([71bf002](https://github.com/perfect-panel/ppanel-web/commit/71bf002)) -* **subscribe**: Update payment return URL ([2b80496](https://github.com/perfect-panel/ppanel-web/commit/2b80496)) +- **dashboard**: Display subscription creation date in user dashboard ([d0e6df0](https://github.com/perfect-panel/ppanel-web/commit/d0e6df0)) +- **request**: Add error code 40005 to trigger logout ([71bf002](https://github.com/perfect-panel/ppanel-web/commit/71bf002)) +- **subscribe**: Update payment return URL ([2b80496](https://github.com/perfect-panel/ppanel-web/commit/2b80496)) # [1.0.0-beta.24](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2025-02-27) - ### ♻ Code Refactoring -* **ui**: Optimize document display ([2ca2992](https://github.com/perfect-panel/ppanel-web/commit/2ca2992)) -* Reduce code complexity and improve readability ([e11f18c](https://github.com/perfect-panel/ppanel-web/commit/e11f18c)) - +- **ui**: Optimize document display ([2ca2992](https://github.com/perfect-panel/ppanel-web/commit/2ca2992)) +- Reduce code complexity and improve readability ([e11f18c](https://github.com/perfect-panel/ppanel-web/commit/e11f18c)) ### ✨ Features -* **loading**: Add loading components and integrate them in Providers ([d5847fa](https://github.com/perfect-panel/ppanel-web/commit/d5847fa)) - +- **loading**: Add loading components and integrate them in Providers ([d5847fa](https://github.com/perfect-panel/ppanel-web/commit/d5847fa)) ### 🎫 Chores -* **merge**: Add advertising module and device settings ([0130e02](https://github.com/perfect-panel/ppanel-web/commit/0130e02)) - +- **merge**: Add advertising module and device settings ([0130e02](https://github.com/perfect-panel/ppanel-web/commit/0130e02)) ### 🐛 Bug Fixes -* **locales**: Order recharge related fields ([35210fe](https://github.com/perfect-panel/ppanel-web/commit/35210fe)) +- **locales**: Order recharge related fields ([35210fe](https://github.com/perfect-panel/ppanel-web/commit/35210fe)) diff --git a/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx b/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx index b893d21..012c057 100644 --- a/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx +++ b/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx @@ -115,7 +115,7 @@ export function SubscriptionDetail({ user_id: userId, subscribe_id: subscriptionId, ...pagination, - }); + } as API.GetUserSubscribeTrafficLogsParams); return { list: data.data?.list || [], total: data.data?.total || 0, diff --git a/apps/admin/services/admin/typings.d.ts b/apps/admin/services/admin/typings.d.ts index 565e79d..da8b2f7 100644 --- a/apps/admin/services/admin/typings.d.ts +++ b/apps/admin/services/admin/typings.d.ts @@ -120,17 +120,6 @@ declare namespace API { ids: number[]; }; - type CheckoutOrderRequest = { - orderNo: string; - returnUrl?: string; - }; - - type CheckoutOrderResponse = { - type: string; - checkout_url?: string; - stripe?: StripePayment; - }; - type CloseOrderRequest = { orderNo: string; }; @@ -488,7 +477,7 @@ declare namespace API { }; type GetAvailablePaymentMethodsResponse = { - list: PaymentConfig[]; + list: PaymenMethod[]; }; type GetCouponListParams = { @@ -797,6 +786,8 @@ declare namespace API { size: number; user_id: number; subscribe_id: number; + start_time: number; + end_time: number; }; type GetUserSubscribeTrafficLogsRequest = { @@ -804,6 +795,8 @@ declare namespace API { size: number; user_id: number; subscribe_id: number; + start_time: number; + end_time: number; }; type GetUserSubscribeTrafficLogsResponse = { @@ -931,6 +924,16 @@ declare namespace API { list?: OrdersStatistics[]; }; + type PaymenMethod = { + id: number; + name: string; + mark: string; + icon: string; + fee_mode: number; + fee_percent: number; + fee_amount: number; + }; + type PaymentConfig = { id: number; name: string; @@ -1029,6 +1032,21 @@ declare namespace API { list: OrderDetail[]; }; + type QuerySubscribeGroupListResponse = { + list: SubscribeGroup[]; + total: number; + }; + + type QuerySubscribeListResponse = { + list: Subscribe[]; + total: number; + }; + + type QueryUserAffiliateListResponse = { + list: UserAffiliate[]; + total: number; + }; + type RechargeOrderRequest = { amount: number; payment: string; diff --git a/apps/admin/services/common/common.ts b/apps/admin/services/common/common.ts index 685ef9c..11fa9a5 100644 --- a/apps/admin/services/common/common.ts +++ b/apps/admin/services/common/common.ts @@ -58,17 +58,6 @@ export async function getStat(options?: { [key: string]: any }) { }); } -/** Get Subscription GET /v1/common/site/subscribe */ -export async function getSubscription(options?: { [key: string]: any }) { - return request( - '/v1/common/site/subscribe', - { - method: 'GET', - ...(options || {}), - }, - ); -} - /** Get Tos Content GET /v1/common/site/tos */ export async function getTos(options?: { [key: string]: any }) { return request('/v1/common/site/tos', { diff --git a/apps/admin/services/common/typings.d.ts b/apps/admin/services/common/typings.d.ts index 566eeb9..878dfce 100644 --- a/apps/admin/services/common/typings.d.ts +++ b/apps/admin/services/common/typings.d.ts @@ -90,17 +90,6 @@ declare namespace API { enabled: boolean; }; - type CheckoutOrderRequest = { - orderNo: string; - returnUrl?: string; - }; - - type CheckoutOrderResponse = { - type: string; - checkout_url?: string; - stripe?: StripePayment; - }; - type CheckUserParams = { email: string; }; @@ -175,7 +164,7 @@ declare namespace API { }; type GetAvailablePaymentMethodsResponse = { - list: PaymentConfig[]; + list: PaymenMethod[]; }; type GetGlobalConfigResponse = { @@ -196,14 +185,24 @@ declare namespace API { protocol: string[]; }; - type GetSubscriptionResponse = { - list: Subscribe[]; - }; - type GetTosResponse = { tos_content: string; }; + type GetUserSubscribeTrafficLogsRequest = { + page: number; + size: number; + user_id: number; + subscribe_id: number; + start_time: number; + end_time: number; + }; + + type GetUserSubscribeTrafficLogsResponse = { + list: TrafficLog[]; + total: number; + }; + type GoogleLoginCallbackRequest = { code: string; state: string; @@ -329,6 +328,16 @@ declare namespace API { updated_at: number; }; + type PaymenMethod = { + id: number; + name: string; + mark: string; + icon: string; + fee_mode: number; + fee_percent: number; + fee_amount: number; + }; + type PaymentConfig = { id: number; name: string; @@ -417,6 +426,21 @@ declare namespace API { list: OrderDetail[]; }; + type QuerySubscribeGroupListResponse = { + list: SubscribeGroup[]; + total: number; + }; + + type QuerySubscribeListResponse = { + list: Subscribe[]; + total: number; + }; + + type QueryUserAffiliateListResponse = { + list: UserAffiliate[]; + total: number; + }; + type RechargeOrderRequest = { amount: number; payment: string; diff --git a/apps/user/app/(main)/(user)/payment/page.tsx b/apps/user/app/(main)/(user)/payment/page.tsx index 8449812..4226f1d 100644 --- a/apps/user/app/(main)/(user)/payment/page.tsx +++ b/apps/user/app/(main)/(user)/payment/page.tsx @@ -4,7 +4,8 @@ import { Display } from '@/components/display'; import { SubscribeBilling } from '@/components/subscribe/billing'; import { SubscribeDetail } from '@/components/subscribe/detail'; import useGlobalStore from '@/config/use-global'; -import { checkoutOrder, queryOrderDetail } from '@/services/user/order'; +import { queryOrderDetail } from '@/services/user/order'; +import { purchaseCheckout } from '@/services/user/portal'; import { useQuery } from '@tanstack/react-query'; import { Badge } from '@workspace/ui/components/badge'; import { Button } from '@workspace/ui/components/button'; @@ -50,7 +51,10 @@ export default function Page() { enabled: !!orderNo && data?.status === 1, queryKey: ['checkoutOrder', orderNo], queryFn: async () => { - const { data } = await checkoutOrder({ orderNo: orderNo!, returnUrl: window.location.href }); + const { data } = await purchaseCheckout({ + orderNo: orderNo!, + returnUrl: window.location.href, + }); return data?.data; }, }); diff --git a/apps/user/app/(main)/page.tsx b/apps/user/app/(main)/page.tsx index a62603b..3d540bb 100644 --- a/apps/user/app/(main)/page.tsx +++ b/apps/user/app/(main)/page.tsx @@ -1,6 +1,6 @@ import { GlobalMap } from '@/components/main/global-map'; import { Hero } from '@/components/main/hero'; -import { ProductShowcase } from '@/components/main/product-showcase'; +import { ProductShowcase } from '@/components/main/product-showcase/index'; import { Stats } from '@/components/main/stats'; export default function Home() { diff --git a/apps/user/app/(main)/purchasing/content.tsx b/apps/user/app/(main)/purchasing/content.tsx new file mode 100644 index 0000000..39a44a5 --- /dev/null +++ b/apps/user/app/(main)/purchasing/content.tsx @@ -0,0 +1,221 @@ +'use client'; + +import { SubscribeBilling } from '@/components/subscribe/billing'; +import CouponInput from '@/components/subscribe/coupon-input'; +import { SubscribeDetail } from '@/components/subscribe/detail'; +import DurationSelector from '@/components/subscribe/duration-selector'; +import PaymentMethods from '@/components/subscribe/payment-methods'; +import useGlobalStore from '@/config/use-global'; +import { prePurchaseOrder, purchase } from '@/services/user/portal'; +import { useQuery } from '@tanstack/react-query'; +import { Button } from '@workspace/ui/components/button'; +import { Card, CardContent, CardHeader } from '@workspace/ui/components/card'; +import { Separator } from '@workspace/ui/components/separator'; +import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input'; +import { cn } from '@workspace/ui/lib/utils'; +import { LoaderCircle } from 'lucide-react'; +import { useTranslations } from 'next-intl'; +import { useRouter } from 'next/navigation'; +import { useCallback, useEffect, useState, useTransition } from 'react'; + +export default function Content({ subscription }: { subscription?: API.Subscribe }) { + const t = useTranslations('subscribe'); + const { common } = useGlobalStore(); + const router = useRouter(); + const [params, setParams] = useState({ + quantity: 1, + subscribe_id: 0, + payment: '', + coupon: '', + platform: 'email', + identifier: '', + password: '', + }); + const [loading, startTransition] = useTransition(); + const [isEmailValid, setIsEmailValid] = useState({ + valid: false, + message: '', + }); + + const { data: order } = useQuery({ + enabled: !!subscription?.id && !!params.payment, + queryKey: ['preCreateOrder', params.coupon, params.quantity, params.payment], + queryFn: async () => { + const { data } = await prePurchaseOrder({ + ...params, + subscribe_id: subscription?.id as number, + } as API.PrePurchaseOrderRequest); + return data.data; + }, + }); + + useEffect(() => { + if (subscription) { + setParams((prev) => ({ + ...prev, + quantity: 1, + subscribe_id: subscription?.id, + })); + } + }, [subscription]); + + const handleChange = useCallback((field: keyof typeof params, value: string | number) => { + setParams((prev) => ({ + ...prev, + [field]: value, + })); + }, []); + + const handleSubmit = useCallback(async () => { + startTransition(async () => { + try { + const { data } = await purchase(params); + console.log(data); + const { order_no, check_url, type } = data.data!; + if (order_no) { + if (type === 'link') { + window.location.href = check_url!; + } + router.push(`/purchasing/order?order_no=${order_no}`); + } + } catch (error) { + console.log(error); + } + }); + }, [params, router, subscription?.id]); + + if (!subscription) { + return
{t('subscriptionNotFound')}
; + } + + return ( +
+
+ + 输入要用于 {common.site.site_name} 账户的电子邮件地址 + +
+ { + const email = value as string; + setParams((prev) => ({ + ...prev, + identifier: email, + })); + const reg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!reg.test(email)) { + setIsEmailValid({ + valid: false, + message: '请输入有效的邮箱地址', + }); + } else if (common.auth.email.enable_domain_suffix) { + const domain = email.split('@')[1]; + const isValid = common.auth.email?.domain_suffix_list + .split('\n') + .includes(domain || ''); + if (!isValid) { + setIsEmailValid({ + valid: false, + message: '邮箱域名不在白名单中', + }); + return; + } + } else { + setIsEmailValid({ + valid: true, + message: '', + }); + } + }} + required + /> +

+ {isEmailValid.message || '请填写您的电子邮件地址。'} +

+
+ {params.identifier && isEmailValid.valid && ( +
+ handleChange('password', value)} + /> +

+ 如果您不填写密码,我们将会自动生成密码并发送到您的邮箱。 +

+
+ )} + {/*
+ +
*/} +
+
+ + +

{subscription.name}

+

{subscription.description}

+ + + +
+
+
+ +
+ + +
+ handleChange('quantity', value)} + /> + handleChange('coupon', value)} + /> + handleChange('payment', value)} + /> +
+
+
+ + +
+
+ ); +} diff --git a/apps/user/app/(main)/purchasing/order/page.tsx b/apps/user/app/(main)/purchasing/order/page.tsx new file mode 100644 index 0000000..39c2d7c --- /dev/null +++ b/apps/user/app/(main)/purchasing/order/page.tsx @@ -0,0 +1,266 @@ +'use client'; + +import { Display } from '@/components/display'; +import { SubscribeBilling } from '@/components/subscribe/billing'; +import { SubscribeDetail } from '@/components/subscribe/detail'; +import useGlobalStore from '@/config/use-global'; +import { purchaseCheckout, queryPurchaseOrder } from '@/services/user/portal'; +import { setAuthorization } from '@/utils/common'; +import { useQuery } from '@tanstack/react-query'; +import { Badge } from '@workspace/ui/components/badge'; +import { Button } from '@workspace/ui/components/button'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@workspace/ui/components/card'; +import { Separator } from '@workspace/ui/components/separator'; +import { Icon } from '@workspace/ui/custom-components/icon'; +import { formatDate } from '@workspace/ui/utils'; +import { useCountDown } from 'ahooks'; +import { addMinutes, format } from 'date-fns'; +import { useTranslations } from 'next-intl'; +import Link from 'next/link'; +import { QRCodeCanvas } from 'qrcode.react'; +import { useEffect, useState } from 'react'; +import StripePayment from './stripe'; + +export default function Page() { + const t = useTranslations('order'); + const { getUserInfo } = useGlobalStore(); + const [orderNo, setOrderNo] = useState(); + const [enabled, setEnabled] = useState(false); + + const { data } = useQuery({ + enabled: enabled, + queryKey: ['queryPurchaseOrder', orderNo], + queryFn: async () => { + const { data } = await queryPurchaseOrder({ order_no: orderNo! }); + if (data?.data?.status !== 1) { + setEnabled(false); + if (data?.data?.token) { + setAuthorization(data?.data?.token); + await new Promise((resolve) => setTimeout(resolve, 100)); + await getUserInfo(); + } + } + return data?.data; + }, + refetchInterval: 3000, + }); + + const { data: payment } = useQuery({ + enabled: !!orderNo && data?.status === 1, + queryKey: ['purchaseCheckout', orderNo], + queryFn: async () => { + const { data } = await purchaseCheckout({ + orderNo: orderNo!, + returnUrl: window.location.href, + }); + if (data.data?.type === 'url' && data.data?.checkout_url) { + window.location.href = data.data?.checkout_url; + } + return data?.data; + }, + }); + + useEffect(() => { + const searchParams = new URLSearchParams(location.search); + if (searchParams.get('order_no')) { + setOrderNo(searchParams.get('order_no')!); + setEnabled(true); + } + }, []); + + const [countDown, formattedRes] = useCountDown({ + targetDate: data && format(addMinutes(data?.created_at, 15), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"), + }); + + const { hours, minutes, seconds } = formattedRes; + + const countdownDisplay = + countDown > 0 ? ( + <> + {hours.toString().length === 1 ? `0${hours}` : hours} :{' '} + {minutes.toString().length === 1 ? `0${minutes}` : minutes} :{' '} + {seconds.toString().length === 1 ? `0${seconds}` : seconds} + + ) : ( + <>{t('timeExpired')} + ); + + return ( +
+
+ + +
+ + {t('orderNumber')} + {data?.order_no} + + + {t('createdAt')}: {formatDate(data?.created_at)} + +
+
+ +
{t('paymentMethod')}
+
+
+
+ {data?.payment && {t(`methods.${data?.payment}`)}} +
+
+
+ + + {data?.status && [1, 2].includes(data.status) && ( + + )} + {data?.status === 3 && ( + <> +
{t('resetTraffic')}
+
    +
  • + + {t('resetPrice')} + + + + +
  • +
+ + )} + + {data?.status === 4 && ( + <> +
{t('balanceRecharge')}
+
    +
  • + + {t('rechargeAmount')} + + + + +
  • +
+ + )} + + +
+
+ + + {data?.status && [2, 5].includes(data?.status) && ( +
+

{t('paymentSuccess')}

+ +
+ + +
+
+ )} + {data?.status === 1 && payment?.type === 'url' && ( +
+

{t('waitingForPayment')}

+

{countdownDisplay}

+ +
+ + +
+
+ )} + + {data?.status === 1 && payment?.type === 'qr' && ( +
+

{t('scanToPay')}

+

{countdownDisplay}

+ +
+ + +
+
+ )} + + {data?.status === 1 && payment?.type === 'stripe' && ( +
+

{t('scanToPay')}

+

{countdownDisplay}

+ {payment.stripe && } +
+ + +
+
+ )} + + {data?.status && [3, 4].includes(data?.status) && ( +
+

{t('orderClosed')}

+ +
+ + +
+
+ )} +
+
+
+
+ ); +} diff --git a/apps/user/app/(main)/purchasing/order/stripe.tsx b/apps/user/app/(main)/purchasing/order/stripe.tsx new file mode 100644 index 0000000..62f5741 --- /dev/null +++ b/apps/user/app/(main)/purchasing/order/stripe.tsx @@ -0,0 +1,109 @@ +import { Elements, useStripe } from '@stripe/react-stripe-js'; +import { loadStripe, PaymentIntentResult } from '@stripe/stripe-js'; +import { QRCodeCanvas } from 'qrcode.react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; + +interface StripePaymentProps { + method: string; + client_secret: string; + publishable_key: string; +} + +const StripePayment: React.FC = ({ + method, + client_secret, + publishable_key, +}) => { + const stripePromise = useMemo(() => loadStripe(publishable_key), [publishable_key]); + + return ( + + + + ); +}; + +const CheckoutForm: React.FC> = ({ + client_secret, + method, +}) => { + const stripe = useStripe(); + const [errorMessage, setErrorMessage] = useState(null); + const [qrCodeUrl, setQrCodeUrl] = useState(null); + const [isSubmitted, setIsSubmitted] = useState(false); + + const handleError = useCallback((message: string) => { + setErrorMessage(message); + setIsSubmitted(false); + }, []); + + const confirmPayment = useCallback(async (): Promise => { + if (!stripe) { + handleError('Stripe.js is not loaded.'); + return null; + } + + if (method === 'alipay') { + return await stripe.confirmAlipayPayment( + client_secret, + { return_url: window.location.href }, + { handleActions: false }, + ); + } + + return await stripe.confirmWechatPayPayment( + client_secret, + { + payment_method_options: { wechat_pay: { client: 'web' } }, + }, + { handleActions: false }, + ); + }, [client_secret, method, stripe, handleError]); + + const autoSubmit = useCallback(async () => { + if (isSubmitted) return; + + setIsSubmitted(true); + + try { + const result = await confirmPayment(); + if (!result) return; + + const { error, paymentIntent } = result; + if (error) return handleError(error.message!); + + if (paymentIntent?.status === 'requires_action') { + const nextAction = paymentIntent.next_action as any; + const qrUrl = + method === 'alipay' + ? nextAction?.alipay_handle_redirect?.url + : nextAction?.wechat_pay_display_qr_code?.image_url_svg; + + setQrCodeUrl(qrUrl || null); + } + } catch (error) { + handleError('An unexpected error occurred'); + } + }, [confirmPayment, isSubmitted, handleError, method]); + + useEffect(() => { + autoSubmit(); + }, [autoSubmit]); + + return qrCodeUrl ? ( + + ) : ( + errorMessage + ); +}; + +export default StripePayment; diff --git a/apps/user/app/(main)/purchasing/page.tsx b/apps/user/app/(main)/purchasing/page.tsx new file mode 100644 index 0000000..dd0e9be --- /dev/null +++ b/apps/user/app/(main)/purchasing/page.tsx @@ -0,0 +1,23 @@ +import { getSubscription } from '@/services/user/portal'; +import Content from './content'; + +export default async function Page({ + searchParams, +}: { + searchParams: Promise<{ + id: string; + }>; +}) { + const { id } = await searchParams; + const { data } = await getSubscription({ + skipErrorHandler: true, + }); + const subscriptionList = data.data?.list || []; + const subscription = subscriptionList.find((item) => item.id === Number(id)); + + return ( +
+ +
+ ); +} diff --git a/apps/user/app/auth/page.tsx b/apps/user/app/auth/page.tsx index 6fa4e43..5864082 100644 --- a/apps/user/app/auth/page.tsx +++ b/apps/user/app/auth/page.tsx @@ -1,13 +1,11 @@ 'use client'; +import { OAuthMethods } from '@/components/auth/oauth-methods'; import LanguageSwitch from '@/components/language-switch'; import ThemeSwitch from '@/components/theme-switch'; import useGlobalStore from '@/config/use-global'; -import { oAuthLogin } from '@/services/common/oauth'; import { DotLottieReact } from '@lottiefiles/dotlottie-react'; -import { Button } from '@workspace/ui/components/button'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs'; -import { Icon } from '@workspace/ui/custom-components/icon'; import LoginLottie from '@workspace/ui/lotties/login.json'; import { useTranslations } from 'next-intl'; import Image from 'next/legacy/image'; @@ -15,18 +13,10 @@ import Link from 'next/link'; import EmailAuthForm from './email/auth-form'; import PhoneAuthForm from './phone/auth-form'; -const icons = { - apple: 'uil:apple', - google: 'logos:google-icon', - facebook: 'logos:facebook', - github: 'uil:github', - telegram: 'logos:telegram', -}; - export default function Page() { const t = useTranslations('auth'); const { common } = useGlobalStore(); - const { site, auth, oauth_methods } = common; + const { site, auth } = common; const AUTH_METHODS = [ { @@ -41,10 +31,6 @@ export default function Page() { }, ].filter((method) => method.enabled); - const OAUTH_METHODS = oauth_methods?.filter( - (method) => !['mobile', 'email', 'device'].includes(method), - ); - return (
@@ -95,38 +81,7 @@ export default function Page() { )}
- {OAUTH_METHODS?.length > 0 && ( - <> -
- - Or continue with - -
-
- {OAUTH_METHODS?.map((method: any) => { - return ( - - ); - })} -
- - )} +
diff --git a/apps/user/components/auth/oauth-methods.tsx b/apps/user/components/auth/oauth-methods.tsx new file mode 100644 index 0000000..c805e65 --- /dev/null +++ b/apps/user/components/auth/oauth-methods.tsx @@ -0,0 +1,56 @@ +'use client'; + +import useGlobalStore from '@/config/use-global'; +import { oAuthLogin } from '@/services/common/oauth'; +import { Button } from '@workspace/ui/components/button'; +import { Icon } from '@workspace/ui/custom-components/icon'; + +const icons = { + apple: 'uil:apple', + google: 'logos:google-icon', + facebook: 'logos:facebook', + github: 'uil:github', + telegram: 'logos:telegram', +}; + +export function OAuthMethods() { + const { common } = useGlobalStore(); + const { oauth_methods } = common; + const OAUTH_METHODS = oauth_methods?.filter( + (method) => !['mobile', 'email', 'device'].includes(method), + ); + return ( + OAUTH_METHODS?.length > 0 && ( + <> +
+ + Or continue with + +
+
+ {OAUTH_METHODS?.map((method: any) => { + return ( + + ); + })} +
+ + ) + ); +} diff --git a/apps/user/components/main/product-showcase.tsx b/apps/user/components/main/product-showcase/content.tsx similarity index 90% rename from apps/user/components/main/product-showcase.tsx rename to apps/user/components/main/product-showcase/content.tsx index 4083e63..8f859a7 100644 --- a/apps/user/components/main/product-showcase.tsx +++ b/apps/user/components/main/product-showcase/content.tsx @@ -2,8 +2,7 @@ import { Display } from '@/components/display'; import { SubscribeDetail } from '@/components/subscribe/detail'; -import { getSubscription } from '@/services/common/common'; -import { useQuery } from '@tanstack/react-query'; +import useGlobalStore from '@/config/use-global'; import { Button } from '@workspace/ui/components/button'; import { Card, CardContent, CardFooter, CardHeader } from '@workspace/ui/components/card'; import { Separator } from '@workspace/ui/components/separator'; @@ -14,19 +13,15 @@ import { useTranslations } from 'next-intl'; import Link from 'next/link'; import { Key, ReactNode } from 'react'; -export function ProductShowcase() { +interface ProductShowcaseProps { + subscriptionData: API.Subscribe[]; +} + +export function Content({ subscriptionData }: ProductShowcaseProps) { const t = useTranslations('index'); - const { data } = useQuery({ - queryKey: ['getSubscription'], - queryFn: async () => { - const { data } = await getSubscription({ - skipErrorHandler: true, - }); - return data.data?.list || []; - }, - }); - if (data?.length === 0) return null; + const { user } = useGlobalStore(); + return (
- {data?.map((item, index) => ( + {subscriptionData?.map((item, index) => ( - {t('subscribe')} + + {t('subscribe')} + diff --git a/apps/user/components/main/product-showcase/index.tsx b/apps/user/components/main/product-showcase/index.tsx new file mode 100644 index 0000000..27046de --- /dev/null +++ b/apps/user/components/main/product-showcase/index.tsx @@ -0,0 +1,17 @@ +import { getSubscription } from '@/services/user/portal'; +import { Content } from './content'; + +export async function ProductShowcase() { + try { + const { data } = await getSubscription({ + skipErrorHandler: true, + }); + const subscriptionList = data.data?.list || []; + + if (subscriptionList.length === 0) return null; + + return ; + } catch (error) { + return null; + } +} diff --git a/apps/user/components/subscribe/payment-methods.tsx b/apps/user/components/subscribe/payment-methods.tsx index d440fe9..c768ce1 100644 --- a/apps/user/components/subscribe/payment-methods.tsx +++ b/apps/user/components/subscribe/payment-methods.tsx @@ -1,6 +1,8 @@ 'use client'; -import { getAvailablePaymentMethods } from '@/services/user/payment'; +import { getAvailablePaymentMethods } from '@/services/user/portal'; +// import { getAvailablePaymentMethods } from '@/services/user/payment'; +// import { getAvailablePaymentMethods } from '@/services/user/payment'; import { useQuery } from '@tanstack/react-query'; import { Label } from '@workspace/ui/components/label'; import { RadioGroup, RadioGroupItem } from '@workspace/ui/components/radio-group'; @@ -11,22 +13,30 @@ import React, { memo } from 'react'; interface PaymentMethodsProps { value: string; onChange: (value: string) => void; + balance?: boolean; } -const PaymentMethods: React.FC = ({ value, onChange }) => { +const PaymentMethods: React.FC = ({ value, onChange, balance = true }) => { const t = useTranslations('subscribe'); const { data } = useQuery({ - queryKey: ['getAvailablePaymentMethods'], + queryKey: ['getAvailablePaymentMethods', { balance }], queryFn: async () => { const { data } = await getAvailablePaymentMethods(); - return data.data?.list || []; + const methods = data.data?.list || []; + if (!value && methods[0]?.mark) onChange(methods[0]?.mark); + if (balance) return methods; + return methods.filter((item) => item.mark !== 'balance'); }, }); return ( <>
{t('paymentMethod')}
- + {data?.map((item) => (
diff --git a/apps/user/components/subscribe/purchase.tsx b/apps/user/components/subscribe/purchase.tsx index 88e1ce7..4ec165f 100644 --- a/apps/user/components/subscribe/purchase.tsx +++ b/apps/user/components/subscribe/purchase.tsx @@ -4,7 +4,8 @@ import CouponInput from '@/components/subscribe/coupon-input'; import DurationSelector from '@/components/subscribe/duration-selector'; import PaymentMethods from '@/components/subscribe/payment-methods'; import useGlobalStore from '@/config/use-global'; -import { checkoutOrder, preCreateOrder, purchase } from '@/services/user/order'; +import { preCreateOrder, purchase } from '@/services/user/order'; +import { purchaseCheckout } from '@/services/user/portal'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@workspace/ui/components/button'; import { Card, CardContent } from '@workspace/ui/components/card'; @@ -69,7 +70,7 @@ export default function Purchase({ subscribe, setSubscribe }: Readonly) { const response = await recharge(params); const orderNo = response.data.data?.order_no; if (orderNo) { - const { data } = await checkoutOrder({ + const { data } = await purchaseCheckout({ orderNo, returnUrl: `${window.location.origin}/payment?order_no=${orderNo}`, }); diff --git a/apps/user/components/subscribe/renewal.tsx b/apps/user/components/subscribe/renewal.tsx index f5ebcf8..c8faf58 100644 --- a/apps/user/components/subscribe/renewal.tsx +++ b/apps/user/components/subscribe/renewal.tsx @@ -4,7 +4,8 @@ import CouponInput from '@/components/subscribe/coupon-input'; import DurationSelector from '@/components/subscribe/duration-selector'; import PaymentMethods from '@/components/subscribe/payment-methods'; import useGlobalStore from '@/config/use-global'; -import { checkoutOrder, preCreateOrder, renewal } from '@/services/user/order'; +import { preCreateOrder, renewal } from '@/services/user/order'; +import { purchaseCheckout } from '@/services/user/portal'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@workspace/ui/components/button'; import { Card, CardContent } from '@workspace/ui/components/card'; @@ -77,7 +78,7 @@ export default function Renewal({ id, subscribe }: Readonly) { const response = await renewal(params as API.RenewalOrderRequest); const orderNo = response.data.data?.order_no; if (orderNo) { - const { data } = await checkoutOrder({ + const { data } = await purchaseCheckout({ orderNo, returnUrl: `${window.location.origin}/payment?order_no=${orderNo}`, }); diff --git a/apps/user/components/subscribe/reset-traffic.tsx b/apps/user/components/subscribe/reset-traffic.tsx index 42d8413..33640a8 100644 --- a/apps/user/components/subscribe/reset-traffic.tsx +++ b/apps/user/components/subscribe/reset-traffic.tsx @@ -2,7 +2,8 @@ import { Display } from '@/components/display'; import useGlobalStore from '@/config/use-global'; -import { checkoutOrder, resetTraffic } from '@/services/user/order'; +import { resetTraffic } from '@/services/user/order'; +import { purchaseCheckout } from '@/services/user/portal'; import { Button } from '@workspace/ui/components/button'; import { Dialog, @@ -84,7 +85,7 @@ export default function ResetTraffic({ id, replacement }: Readonly( - '/v1/common/site/subscribe', - { - method: 'GET', - ...(options || {}), - }, - ); -} - /** Get Tos Content GET /v1/common/site/tos */ export async function getTos(options?: { [key: string]: any }) { return request('/v1/common/site/tos', { diff --git a/apps/user/services/common/typings.d.ts b/apps/user/services/common/typings.d.ts index 566eeb9..878dfce 100644 --- a/apps/user/services/common/typings.d.ts +++ b/apps/user/services/common/typings.d.ts @@ -90,17 +90,6 @@ declare namespace API { enabled: boolean; }; - type CheckoutOrderRequest = { - orderNo: string; - returnUrl?: string; - }; - - type CheckoutOrderResponse = { - type: string; - checkout_url?: string; - stripe?: StripePayment; - }; - type CheckUserParams = { email: string; }; @@ -175,7 +164,7 @@ declare namespace API { }; type GetAvailablePaymentMethodsResponse = { - list: PaymentConfig[]; + list: PaymenMethod[]; }; type GetGlobalConfigResponse = { @@ -196,14 +185,24 @@ declare namespace API { protocol: string[]; }; - type GetSubscriptionResponse = { - list: Subscribe[]; - }; - type GetTosResponse = { tos_content: string; }; + type GetUserSubscribeTrafficLogsRequest = { + page: number; + size: number; + user_id: number; + subscribe_id: number; + start_time: number; + end_time: number; + }; + + type GetUserSubscribeTrafficLogsResponse = { + list: TrafficLog[]; + total: number; + }; + type GoogleLoginCallbackRequest = { code: string; state: string; @@ -329,6 +328,16 @@ declare namespace API { updated_at: number; }; + type PaymenMethod = { + id: number; + name: string; + mark: string; + icon: string; + fee_mode: number; + fee_percent: number; + fee_amount: number; + }; + type PaymentConfig = { id: number; name: string; @@ -417,6 +426,21 @@ declare namespace API { list: OrderDetail[]; }; + type QuerySubscribeGroupListResponse = { + list: SubscribeGroup[]; + total: number; + }; + + type QuerySubscribeListResponse = { + list: Subscribe[]; + total: number; + }; + + type QueryUserAffiliateListResponse = { + list: UserAffiliate[]; + total: number; + }; + type RechargeOrderRequest = { amount: number; payment: string; diff --git a/apps/user/services/user/index.ts b/apps/user/services/user/index.ts index a3487d7..f988131 100644 --- a/apps/user/services/user/index.ts +++ b/apps/user/services/user/index.ts @@ -1,11 +1,12 @@ // @ts-ignore -/* eslint-disable */ + // API 更新时间: // API 唯一标识: import * as announcement from './announcement'; import * as document from './document'; import * as order from './order'; import * as payment from './payment'; +import * as portal from './portal'; import * as subscribe from './subscribe'; import * as ticket from './ticket'; import * as user from './user'; @@ -14,6 +15,7 @@ export default { document, order, payment, + portal, subscribe, ticket, user, diff --git a/apps/user/services/user/order.ts b/apps/user/services/user/order.ts index 59db689..aeebc8d 100644 --- a/apps/user/services/user/order.ts +++ b/apps/user/services/user/order.ts @@ -2,21 +2,6 @@ /* eslint-disable */ import request from '@/utils/request'; -/** Checkout order POST /v1/public/order/checkout */ -export async function checkoutOrder( - body: API.CheckoutOrderRequest, - options?: { [key: string]: any }, -) { - return request('/v1/public/order/checkout', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - data: body, - ...(options || {}), - }); -} - /** Close order POST /v1/public/order/close */ export async function closeOrder(body: API.CloseOrderRequest, options?: { [key: string]: any }) { return request('/v1/public/order/close', { diff --git a/apps/user/services/user/portal.ts b/apps/user/services/user/portal.ts new file mode 100644 index 0000000..abac9f9 --- /dev/null +++ b/apps/user/services/user/portal.ts @@ -0,0 +1,91 @@ +// @ts-ignore +/* eslint-disable */ +import request from '@/utils/request'; + +/** Purchase Checkout POST /v1/public/portal/order/checkout */ +export async function purchaseCheckout( + body: API.CheckoutOrderRequest, + options?: { [key: string]: any }, +) { + return request( + '/v1/public/portal/order/checkout', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }, + ); +} + +/** Query Purchase Order GET /v1/public/portal/order/status */ +export async function queryPurchaseOrder( + // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) + params: API.QueryPurchaseOrderParams, + options?: { [key: string]: any }, +) { + return request( + '/v1/public/portal/order/status', + { + method: 'GET', + params: { + ...params, + }, + ...(options || {}), + }, + ); +} + +/** Get available payment methods GET /v1/public/portal/payment-method */ +export async function getAvailablePaymentMethods(options?: { [key: string]: any }) { + return request( + '/v1/public/portal/payment-method', + { + method: 'GET', + ...(options || {}), + }, + ); +} + +/** Pre Purchase Order POST /v1/public/portal/pre */ +export async function prePurchaseOrder( + body: API.PrePurchaseOrderRequest, + options?: { [key: string]: any }, +) { + return request('/v1/public/portal/pre', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }); +} + +/** Purchase subscription POST /v1/public/portal/purchase */ +export async function purchase(body: API.PortalPurchaseRequest, options?: { [key: string]: any }) { + return request( + '/v1/public/portal/purchase', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }, + ); +} + +/** Get Subscription GET /v1/public/portal/subscribe */ +export async function getSubscription(options?: { [key: string]: any }) { + return request( + '/v1/public/portal/subscribe', + { + method: 'GET', + ...(options || {}), + }, + ); +} diff --git a/apps/user/services/user/typings.d.ts b/apps/user/services/user/typings.d.ts index 06e50d6..4e4b8c8 100644 --- a/apps/user/services/user/typings.d.ts +++ b/apps/user/services/user/typings.d.ts @@ -187,7 +187,7 @@ declare namespace API { }; type GetAvailablePaymentMethodsResponse = { - list: PaymentConfig[]; + list: PaymenMethod[]; }; type GetLoginLogParams = { @@ -224,6 +224,24 @@ declare namespace API { total: number; }; + type GetSubscriptionResponse = { + list: Subscribe[]; + }; + + type GetUserSubscribeTrafficLogsRequest = { + page: number; + size: number; + user_id: number; + subscribe_id: number; + start_time: number; + end_time: number; + }; + + type GetUserSubscribeTrafficLogsResponse = { + list: TrafficLog[]; + total: number; + }; + type GetUserTicketDetailRequest = { id: number; }; @@ -351,6 +369,16 @@ declare namespace API { updated_at: number; }; + type PaymenMethod = { + id: number; + name: string; + mark: string; + icon: string; + fee_mode: number; + fee_percent: number; + fee_amount: number; + }; + type PaymentConfig = { id: number; name: string; @@ -364,6 +392,21 @@ declare namespace API { enable: boolean; }; + type PortalPurchaseRequest = { + identifier: string; + platform: string; + password?: string; + payment: string; + subscribe_id: number; + quantity: number; + coupon?: string; + turnstile_token?: string; + }; + + type PortalPurchaseResponse = { + order_no: string; + }; + type PreOrderResponse = { price: number; amount: number; @@ -374,6 +417,22 @@ declare namespace API { fee_amount: number; }; + type PrePurchaseOrderRequest = { + payment: string; + subscribe_id: number; + quantity: number; + coupon?: string; + }; + + type PrePurchaseOrderResponse = { + price: number; + amount: number; + discount: number; + coupon: string; + coupon_discount: number; + fee_amount: number; + }; + type PreRenewalOrderResponse = { orderNo: string; }; @@ -467,6 +526,30 @@ declare namespace API { list: OrderDetail[]; }; + type QueryPurchaseOrderParams = { + order_no: string; + }; + + type QueryPurchaseOrderRequest = { + order_no: string; + }; + + type QueryPurchaseOrderResponse = { + order_no: string; + subscribe: Subscribe; + quantity: number; + price: number; + amount: number; + discount: number; + coupon: string; + coupon_discount: number; + fee_amount: number; + payment: string; + status: number; + created_at: number; + token?: string; + }; + type QuerySubscribeGroupListResponse = { list: SubscribeGroup[]; total: number; diff --git a/apps/user/utils/common.ts b/apps/user/utils/common.ts index 28ac162..932315f 100644 --- a/apps/user/utils/common.ts +++ b/apps/user/utils/common.ts @@ -52,6 +52,7 @@ export function Logout() { const pathname = location.pathname; if ( !['', '/', '/auth', '/tos', '/privacy-policy'].includes(pathname) && + !pathname.startsWith('/purchasing') && !pathname.startsWith('/oauth/') ) { setRedirectUrl(location.pathname); From e4630f8ca980e2f8fd160744c870af734e1966c4 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Sat, 8 Mar 2025 12:43:57 +0700 Subject: [PATCH 04/17] =?UTF-8?q?=E2=9C=A8=20feat(subscription):=20=20Impr?= =?UTF-8?q?ove=20layout=20and=20organization=20of=20subscription=20detail?= =?UTF-8?q?=20tabs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user-subscription/subscription-detail.tsx | 292 +++++++++--------- 1 file changed, 148 insertions(+), 144 deletions(-) diff --git a/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx b/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx index 012c057..06f112a 100644 --- a/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx +++ b/apps/admin/app/dashboard/user/[id]/user-subscription/subscription-detail.tsx @@ -57,150 +57,154 @@ export function SubscriptionDetail({ {t('onlineDevices')} - - > - columns={[ - { - accessorKey: 'ip', - header: 'IP', - }, - { - accessorKey: 'user_agent', - header: t('userAgent'), - }, - { - accessorKey: 'token', - header: t('token'), - }, - { - accessorKey: 'created_at', - header: t('time'), - cell: ({ row }) => formatDate(row.getValue('created_at')), - }, - ]} - request={async (pagination) => { - const { data } = await getUserSubscribeLogs({ - user_id: userId, - subscribe_id: subscriptionId, - ...pagination, - }); - return { - list: data.data?.list || [], - total: data.data?.total || 0, - }; - }} - /> - - - > - columns={[ - { - accessorKey: 'download', - header: t('download'), - cell: ({ row }) => , - }, - { - accessorKey: 'upload', - header: t('upload'), - cell: ({ row }) => , - }, - { - accessorKey: 'timestamp', - header: t('time'), - cell: ({ row }) => formatDate(row.getValue('timestamp')), - }, - ]} - request={async (pagination) => { - const { data } = await getUserSubscribeTrafficLogs({ - user_id: userId, - subscribe_id: subscriptionId, - ...pagination, - } as API.GetUserSubscribeTrafficLogsParams); - return { - list: data.data?.list || [], - total: data.data?.total || 0, - }; - }} - /> - - - > - columns={[ - { - accessorKey: 'enabled', - header: t('enable'), - cell: ({ row }) => ( - { - console.log('Switch:', checked); - }} - /> - ), - }, - { - accessorKey: 'id', - header: 'ID', - }, - { - accessorKey: 'identifier', - header: 'IMEI', - }, - { - accessorKey: 'user_agent', - header: t('userAgent'), - }, - { - accessorKey: 'ip', - header: 'IP', - }, - { - accessorKey: 'online', - header: t('loginStatus'), - cell: ({ row }) => ( - - {row.getValue('online') ? t('online') : t('offline')} - - ), - }, - { - accessorKey: 'updated_at', - header: t('lastSeen'), - cell: ({ row }) => formatDate(row.getValue('updated_at')), - }, - ]} - request={async (pagination) => { - const { data } = await getUserSubscribeDevices({ - user_id: userId, - subscribe_id: subscriptionId, - ...pagination, - }); - return { - list: data.data?.list || [], - total: data.data?.total || 0, - }; - }} - actions={{ - render: (row) => { - if (!row.identifier) return []; - return [ - {t('confirmOffline')}} - title={t('confirmOffline')} - description={t('kickOfflineConfirm', { ip: row.ip })} - onConfirm={async () => { - await kickOfflineByUserDevice({ id: row.id }); - toast.success(t('kickOfflineSuccess')); - }} - cancelText={t('cancel')} - confirmText={t('confirm')} - />, - ]; - }, - }} - /> - +
+ + > + columns={[ + { + accessorKey: 'ip', + header: 'IP', + }, + { + accessorKey: 'user_agent', + header: t('userAgent'), + }, + { + accessorKey: 'token', + header: t('token'), + }, + { + accessorKey: 'created_at', + header: t('time'), + cell: ({ row }) => formatDate(row.getValue('created_at')), + }, + ]} + request={async (pagination) => { + const { data } = await getUserSubscribeLogs({ + user_id: userId, + subscribe_id: subscriptionId, + ...pagination, + }); + return { + list: data.data?.list || [], + total: data.data?.total || 0, + }; + }} + /> + + + > + columns={[ + { + accessorKey: 'download', + header: t('download'), + cell: ({ row }) => ( + + ), + }, + { + accessorKey: 'upload', + header: t('upload'), + cell: ({ row }) => , + }, + { + accessorKey: 'timestamp', + header: t('time'), + cell: ({ row }) => formatDate(row.getValue('timestamp')), + }, + ]} + request={async (pagination) => { + const { data } = await getUserSubscribeTrafficLogs({ + user_id: userId, + subscribe_id: subscriptionId, + ...pagination, + } as API.GetUserSubscribeTrafficLogsParams); + return { + list: data.data?.list || [], + total: data.data?.total || 0, + }; + }} + /> + + + > + columns={[ + { + accessorKey: 'enabled', + header: t('enable'), + cell: ({ row }) => ( + { + console.log('Switch:', checked); + }} + /> + ), + }, + { + accessorKey: 'id', + header: 'ID', + }, + { + accessorKey: 'identifier', + header: 'IMEI', + }, + { + accessorKey: 'user_agent', + header: t('userAgent'), + }, + { + accessorKey: 'ip', + header: 'IP', + }, + { + accessorKey: 'online', + header: t('loginStatus'), + cell: ({ row }) => ( + + {row.getValue('online') ? t('online') : t('offline')} + + ), + }, + { + accessorKey: 'updated_at', + header: t('lastSeen'), + cell: ({ row }) => formatDate(row.getValue('updated_at')), + }, + ]} + request={async (pagination) => { + const { data } = await getUserSubscribeDevices({ + user_id: userId, + subscribe_id: subscriptionId, + ...pagination, + }); + return { + list: data.data?.list || [], + total: data.data?.total || 0, + }; + }} + actions={{ + render: (row) => { + if (!row.identifier) return []; + return [ + {t('confirmOffline')}} + title={t('confirmOffline')} + description={t('kickOfflineConfirm', { ip: row.ip })} + onConfirm={async () => { + await kickOfflineByUserDevice({ id: row.id }); + toast.success(t('kickOfflineSuccess')); + }} + cancelText={t('cancel')} + confirmText={t('confirm')} + />, + ]; + }, + }} + /> + +
From 0c907337e10269f27911961cf32767ac894c9a7d Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Sat, 8 Mar 2025 12:54:58 +0700 Subject: [PATCH 05/17] =?UTF-8?q?=E2=9C=A8=20feat(cdn):=20Add=20CDN=20URL?= =?UTF-8?q?=20configuration=20and=20update=20related=20references?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/user/.env.template | 1 + apps/user/config/constants.ts | 2 ++ apps/user/utils/tutorial.ts | 7 ++++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/user/.env.template b/apps/user/.env.template index 220084d..8b81c94 100644 --- a/apps/user/.env.template +++ b/apps/user/.env.template @@ -4,6 +4,7 @@ NEXT_PUBLIC_DEFAULT_LANGUAGE=en-US # Site URL and API URL NEXT_PUBLIC_SITE_URL=https://user.ppanel.dev NEXT_PUBLIC_API_URL=https://api.ppanel.dev +NEXT_PUBLIC_CDN_URL=https://cdn.jsdelivr.net # Home Page Settings NEXT_PUBLIC_HOME_USER_COUNT=999 diff --git a/apps/user/config/constants.ts b/apps/user/config/constants.ts index ecd6b76..7211d89 100644 --- a/apps/user/config/constants.ts +++ b/apps/user/config/constants.ts @@ -9,6 +9,8 @@ export const NEXT_PUBLIC_DEFAULT_LANGUAGE = export const NEXT_PUBLIC_SITE_URL = env('NEXT_PUBLIC_SITE_URL') ?? process.env.NEXT_PUBLIC_SITE_URL; export const NEXT_PUBLIC_API_URL = env('NEXT_PUBLIC_API_URL') ?? process.env.NEXT_PUBLIC_API_URL; +export const NEXT_PUBLIC_CDN_URL = + env('NEXT_PUBLIC_CDN_URL') || process.env.NEXT_PUBLIC_CDN_URL || 'https://fastly.jsdelivr.net'; export const NEXT_PUBLIC_DEFAULT_USER_EMAIL = env('NEXT_PUBLIC_DEFAULT_USER_EMAIL') ?? process.env.NEXT_PUBLIC_DEFAULT_USER_EMAIL; diff --git a/apps/user/utils/tutorial.ts b/apps/user/utils/tutorial.ts index a635c03..c6dcb4c 100644 --- a/apps/user/utils/tutorial.ts +++ b/apps/user/utils/tutorial.ts @@ -1,14 +1,15 @@ +import { NEXT_PUBLIC_CDN_URL } from '@/config/constants'; import matter from 'gray-matter'; -const BASE_URL = 'https://cdn.jsdelivr.net/gh/perfect-panel/ppanel-tutorial'; +const BASE_URL = `${NEXT_PUBLIC_CDN_URL}/gh/perfect-panel/ppanel-tutorial`; async function getVersion() { // API rate limit: 60 requests per hour const response = await fetch( - 'https://api.github.com/repos/perfect-panel/ppanel-tutorial/commits', + 'https://data.jsdelivr.com/v1/stats/packages/gh/perfect-panel/ppanel-tutorial/versions', ); const json = await response.json(); - return json[0].sha; + return json[0].version; } async function getVersionPath() { From f5d8fd3b65b204313b3daff9e0d10863584b34c3 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Sat, 8 Mar 2025 14:46:20 +0700 Subject: [PATCH 06/17] =?UTF-8?q?=F0=9F=90=9B=20fix(ui):=20Multiple=20disp?= =?UTF-8?q?lay=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(main)/(user)/dashboard/content.tsx | 6 +-- apps/user/app/(main)/(user)/wallet/page.tsx | 4 +- apps/user/components/user-nav.tsx | 45 ++++++++++--------- apps/user/locales/cs-CZ/dashboard.json | 1 + apps/user/locales/cs-CZ/wallet.json | 1 + apps/user/locales/de-DE/dashboard.json | 1 + apps/user/locales/de-DE/wallet.json | 1 + apps/user/locales/en-US/dashboard.json | 1 + apps/user/locales/en-US/wallet.json | 3 +- apps/user/locales/es-ES/dashboard.json | 1 + apps/user/locales/es-ES/wallet.json | 1 + apps/user/locales/es-MX/dashboard.json | 1 + apps/user/locales/es-MX/wallet.json | 1 + apps/user/locales/fa-IR/dashboard.json | 1 + apps/user/locales/fa-IR/wallet.json | 1 + apps/user/locales/fi-FI/dashboard.json | 1 + apps/user/locales/fi-FI/wallet.json | 1 + apps/user/locales/fr-FR/dashboard.json | 1 + apps/user/locales/fr-FR/wallet.json | 1 + apps/user/locales/hi-IN/dashboard.json | 1 + apps/user/locales/hi-IN/wallet.json | 1 + apps/user/locales/hu-HU/dashboard.json | 1 + apps/user/locales/hu-HU/wallet.json | 1 + apps/user/locales/ja-JP/dashboard.json | 1 + apps/user/locales/ja-JP/wallet.json | 1 + apps/user/locales/ko-KR/dashboard.json | 1 + apps/user/locales/ko-KR/wallet.json | 1 + apps/user/locales/no-NO/dashboard.json | 1 + apps/user/locales/no-NO/wallet.json | 1 + apps/user/locales/pl-PL/dashboard.json | 1 + apps/user/locales/pl-PL/wallet.json | 1 + apps/user/locales/pt-BR/dashboard.json | 1 + apps/user/locales/pt-BR/wallet.json | 1 + apps/user/locales/ro-RO/dashboard.json | 1 + apps/user/locales/ro-RO/wallet.json | 1 + apps/user/locales/ru-RU/dashboard.json | 1 + apps/user/locales/ru-RU/wallet.json | 1 + apps/user/locales/th-TH/dashboard.json | 1 + apps/user/locales/th-TH/wallet.json | 1 + apps/user/locales/tr-TR/dashboard.json | 1 + apps/user/locales/tr-TR/wallet.json | 1 + apps/user/locales/uk-UA/dashboard.json | 1 + apps/user/locales/uk-UA/wallet.json | 1 + apps/user/locales/vi-VN/dashboard.json | 1 + apps/user/locales/vi-VN/wallet.json | 1 + apps/user/locales/zh-CN/dashboard.json | 1 + apps/user/locales/zh-CN/wallet.json | 1 + apps/user/locales/zh-HK/dashboard.json | 1 + apps/user/locales/zh-HK/wallet.json | 1 + packages/ui/src/utils/formatting.ts | 9 +++- 50 files changed, 84 insertions(+), 28 deletions(-) diff --git a/apps/user/app/(main)/(user)/dashboard/content.tsx b/apps/user/app/(main)/(user)/dashboard/content.tsx index f2b7c07..315a262 100644 --- a/apps/user/app/(main)/(user)/dashboard/content.tsx +++ b/apps/user/app/(main)/(user)/dashboard/content.tsx @@ -32,8 +32,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/componen import { Separator } from '@workspace/ui/components/separator'; import { Tabs, TabsList, TabsTrigger } from '@workspace/ui/components/tabs'; import { Icon } from '@workspace/ui/custom-components/icon'; -import { formatDate, isBrowser } from '@workspace/ui/utils'; -import { differenceInDays } from 'date-fns'; +import { differenceInDays, formatDate, isBrowser } from '@workspace/ui/utils'; import { useTranslations } from 'next-intl'; import Image from 'next/image'; import Link from 'next/link'; @@ -206,12 +205,13 @@ export default function Content() { {item.reset_time ? differenceInDays(new Date(item.reset_time), new Date()) - : t('unknown')} + : t('noReset')}
  • {t('expirationDays')} + {} {item.expire_time ? differenceInDays(new Date(item.expire_time), new Date()) || t('unknown') : t('noLimit')} diff --git a/apps/user/app/(main)/(user)/wallet/page.tsx b/apps/user/app/(main)/(user)/wallet/page.tsx index 6787e07..fff3e06 100644 --- a/apps/user/app/(main)/(user)/wallet/page.tsx +++ b/apps/user/app/(main)/(user)/wallet/page.tsx @@ -21,11 +21,11 @@ export default function Page() { <> -

    {t('totalAssets')}

    +

    {t('assetOverview')}

    -

    总资产

    +

    {t('totalAssets')}

    diff --git a/apps/user/components/user-nav.tsx b/apps/user/components/user-nav.tsx index 8472728..b2f7cd0 100644 --- a/apps/user/components/user-nav.tsx +++ b/apps/user/components/user-nav.tsx @@ -25,40 +25,40 @@ export function UserNav() { return ( -
    - +
    + - + {user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)} - + {user?.auth_methods?.[0]?.auth_identifier.split('@')[0]} - +
    - -
    - + +
    + - + {user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)} -
    -

    +

    +

    {user?.auth_methods?.[0]?.auth_identifier.split('@')[0]}

    -

    +

    {user?.auth_methods?.[0]?.auth_identifier}

    @@ -72,11 +72,14 @@ export function UserNav() { onClick={() => { router.push(`${item.url}`); }} - className="flex items-center gap-2 py-2 cursor-pointer" + className='flex cursor-pointer items-center gap-2 py-2' > - - {t(item.title)} - + + {t(item.title)} + ))} @@ -87,10 +90,10 @@ export function UserNav() { Logout(); setUser(); }} - className="flex items-center gap-2 py-2 text-destructive focus:text-destructive cursor-pointer" + className='text-destructive focus:text-destructive flex cursor-pointer items-center gap-2 py-2' > - - {t('logout')} + + {t('logout')} diff --git a/apps/user/locales/cs-CZ/dashboard.json b/apps/user/locales/cs-CZ/dashboard.json index 5e0df04..5fec8a1 100644 --- a/apps/user/locales/cs-CZ/dashboard.json +++ b/apps/user/locales/cs-CZ/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Moje předplatné", "nextResetDays": "Příští reset/den", "noLimit": "Bez omezení", + "noReset": "Žádné obnovení", "prompt": "Výzva", "purchaseSubscription": "Zakoupit předplatné", "qrCode": "QR kód", diff --git a/apps/user/locales/cs-CZ/wallet.json b/apps/user/locales/cs-CZ/wallet.json index 510b20a..9c7f261 100644 --- a/apps/user/locales/cs-CZ/wallet.json +++ b/apps/user/locales/cs-CZ/wallet.json @@ -1,5 +1,6 @@ { "amount": "Částka", + "assetOverview": "Přehled aktiv", "balance": "zůstatek", "commission": "Provize", "createdAt": "čas", diff --git a/apps/user/locales/de-DE/dashboard.json b/apps/user/locales/de-DE/dashboard.json index 8a22033..bd31574 100644 --- a/apps/user/locales/de-DE/dashboard.json +++ b/apps/user/locales/de-DE/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Meine Abonnements", "nextResetDays": "Nächster Reset/Tage", "noLimit": "Kein Limit", + "noReset": "Kein Zurücksetzen", "prompt": "Aufforderung", "purchaseSubscription": "Abonnement kaufen", "qrCode": "QR-Code", diff --git a/apps/user/locales/de-DE/wallet.json b/apps/user/locales/de-DE/wallet.json index 6da3c0d..e5fa87e 100644 --- a/apps/user/locales/de-DE/wallet.json +++ b/apps/user/locales/de-DE/wallet.json @@ -1,5 +1,6 @@ { "amount": "Betrag", + "assetOverview": "Vermögensübersicht", "balance": "Kontostand", "commission": "Provision", "createdAt": "Zeit", diff --git a/apps/user/locales/en-US/dashboard.json b/apps/user/locales/en-US/dashboard.json index c535a6a..4b60c3b 100644 --- a/apps/user/locales/en-US/dashboard.json +++ b/apps/user/locales/en-US/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "My Subscriptions", "nextResetDays": "Next Reset in Days", "noLimit": "No Limit", + "noReset": "No Reset", "prompt": "Prompt", "purchaseSubscription": "Purchase Subscription", "qrCode": "QR Code", diff --git a/apps/user/locales/en-US/wallet.json b/apps/user/locales/en-US/wallet.json index 4934770..a3550fd 100644 --- a/apps/user/locales/en-US/wallet.json +++ b/apps/user/locales/en-US/wallet.json @@ -1,10 +1,11 @@ { "amount": "Amount", + "assetOverview": "Asset Overview", "balance": "Balance", "commission": "Commission", "createdAt": "Time", "giftAmount": "Girt Amount", - "totalAssets": "Asset overview", + "totalAssets": "Total Assets", "type": { "0": "Type", "1": "Recharge", diff --git a/apps/user/locales/es-ES/dashboard.json b/apps/user/locales/es-ES/dashboard.json index 8c28ae7..57254e1 100644 --- a/apps/user/locales/es-ES/dashboard.json +++ b/apps/user/locales/es-ES/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Mis suscripciones", "nextResetDays": "Próximo reinicio/días", "noLimit": "Sin límite", + "noReset": "Sin Reinicio", "prompt": "sugerencia", "purchaseSubscription": "Comprar suscripción", "qrCode": "Código QR", diff --git a/apps/user/locales/es-ES/wallet.json b/apps/user/locales/es-ES/wallet.json index 575687f..f51d71b 100644 --- a/apps/user/locales/es-ES/wallet.json +++ b/apps/user/locales/es-ES/wallet.json @@ -1,5 +1,6 @@ { "amount": "Cantidad", + "assetOverview": "Descripción del activo", "balance": "saldo", "commission": "Comisión", "createdAt": "hora", diff --git a/apps/user/locales/es-MX/dashboard.json b/apps/user/locales/es-MX/dashboard.json index 8292a7c..f6602fd 100644 --- a/apps/user/locales/es-MX/dashboard.json +++ b/apps/user/locales/es-MX/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Mis suscripciones", "nextResetDays": "Próximo reinicio/días", "noLimit": "Sin Límite", + "noReset": "Sin Reinicio", "prompt": "Sugerencia", "purchaseSubscription": "Comprar suscripción", "qrCode": "Código QR", diff --git a/apps/user/locales/es-MX/wallet.json b/apps/user/locales/es-MX/wallet.json index 5faed0c..1531d49 100644 --- a/apps/user/locales/es-MX/wallet.json +++ b/apps/user/locales/es-MX/wallet.json @@ -1,5 +1,6 @@ { "amount": "Monto", + "assetOverview": "Descripción del Activo", "balance": "saldo", "commission": "Comisión", "createdAt": "Hora", diff --git a/apps/user/locales/fa-IR/dashboard.json b/apps/user/locales/fa-IR/dashboard.json index 15bff60..4f39cca 100644 --- a/apps/user/locales/fa-IR/dashboard.json +++ b/apps/user/locales/fa-IR/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "اشتراک‌های من", "nextResetDays": "روزهای باقی‌مانده تا بازنشانی بعدی", "noLimit": "بدون محدودیت", + "noReset": "عدم بازنشانی", "prompt": "پیشنهاد", "purchaseSubscription": "خرید اشتراک", "qrCode": "کد QR", diff --git a/apps/user/locales/fa-IR/wallet.json b/apps/user/locales/fa-IR/wallet.json index 1ae61f2..1dcec66 100644 --- a/apps/user/locales/fa-IR/wallet.json +++ b/apps/user/locales/fa-IR/wallet.json @@ -1,5 +1,6 @@ { "amount": "مقدار", + "assetOverview": "بررسی دارایی", "balance": "تعادل", "commission": "کمیسیون", "createdAt": "زمان", diff --git a/apps/user/locales/fi-FI/dashboard.json b/apps/user/locales/fi-FI/dashboard.json index d461475..796574b 100644 --- a/apps/user/locales/fi-FI/dashboard.json +++ b/apps/user/locales/fi-FI/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Omat tilaukset", "nextResetDays": "Seuraava nollaus/päivää", "noLimit": "Ei rajoitusta", + "noReset": "Ei nollata", "prompt": "kehotus", "purchaseSubscription": "Osta tilaus", "qrCode": "QR-koodi", diff --git a/apps/user/locales/fi-FI/wallet.json b/apps/user/locales/fi-FI/wallet.json index 1bdfef0..3070e06 100644 --- a/apps/user/locales/fi-FI/wallet.json +++ b/apps/user/locales/fi-FI/wallet.json @@ -1,5 +1,6 @@ { "amount": "Määrä", + "assetOverview": "Omaisuuden yleiskatsaus", "balance": "Saldo", "commission": "Komissio", "createdAt": "Aika", diff --git a/apps/user/locales/fr-FR/dashboard.json b/apps/user/locales/fr-FR/dashboard.json index 318d3a9..d73e9e3 100644 --- a/apps/user/locales/fr-FR/dashboard.json +++ b/apps/user/locales/fr-FR/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Mes abonnements", "nextResetDays": "Prochain réinitialisation/jours", "noLimit": "Pas de limite", + "noReset": "Pas de réinitialisation", "prompt": "invite", "purchaseSubscription": "Acheter un abonnement", "qrCode": "Code QR", diff --git a/apps/user/locales/fr-FR/wallet.json b/apps/user/locales/fr-FR/wallet.json index 17dcff6..8571aea 100644 --- a/apps/user/locales/fr-FR/wallet.json +++ b/apps/user/locales/fr-FR/wallet.json @@ -1,5 +1,6 @@ { "amount": "Montant", + "assetOverview": "Aperçu des actifs", "balance": "Solde", "commission": "Commission", "createdAt": "temps", diff --git a/apps/user/locales/hi-IN/dashboard.json b/apps/user/locales/hi-IN/dashboard.json index 4069873..b779e93 100644 --- a/apps/user/locales/hi-IN/dashboard.json +++ b/apps/user/locales/hi-IN/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "मेरी सदस्यताएँ", "nextResetDays": "अगली रीसेट/दिन", "noLimit": "कोई सीमा नहीं", + "noReset": "कोई रीसेट नहीं", "prompt": "प्रॉम्प्ट", "purchaseSubscription": "सदस्यता खरीदें", "qrCode": "क्यूआर कोड", diff --git a/apps/user/locales/hi-IN/wallet.json b/apps/user/locales/hi-IN/wallet.json index c37c7e7..d493139 100644 --- a/apps/user/locales/hi-IN/wallet.json +++ b/apps/user/locales/hi-IN/wallet.json @@ -1,5 +1,6 @@ { "amount": "राशि", + "assetOverview": "संपत्ति अवलोकन", "balance": "शेष", "commission": "आयोग", "createdAt": "समय", diff --git a/apps/user/locales/hu-HU/dashboard.json b/apps/user/locales/hu-HU/dashboard.json index 7a539cf..0a3ec7c 100644 --- a/apps/user/locales/hu-HU/dashboard.json +++ b/apps/user/locales/hu-HU/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Előfizetéseim", "nextResetDays": "Következő visszaállítás/nap", "noLimit": "Nincs korlát", + "noReset": "Nincs visszaállítás", "prompt": "figyelmeztetés", "purchaseSubscription": "Előfizetés vásárlása", "qrCode": "QR-kód", diff --git a/apps/user/locales/hu-HU/wallet.json b/apps/user/locales/hu-HU/wallet.json index f191026..ec1f469 100644 --- a/apps/user/locales/hu-HU/wallet.json +++ b/apps/user/locales/hu-HU/wallet.json @@ -1,5 +1,6 @@ { "amount": "Összeg", + "assetOverview": "Eszköz áttekintés", "balance": "Egyenleg", "commission": "Jutalék", "createdAt": "idő", diff --git a/apps/user/locales/ja-JP/dashboard.json b/apps/user/locales/ja-JP/dashboard.json index 78dd871..a946e29 100644 --- a/apps/user/locales/ja-JP/dashboard.json +++ b/apps/user/locales/ja-JP/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "私の購読", "nextResetDays": "次のリセット/日", "noLimit": "無制限", + "noReset": "リセットしない", "prompt": "プロンプト", "purchaseSubscription": "サブスクリプションを購入", "qrCode": "QRコード", diff --git a/apps/user/locales/ja-JP/wallet.json b/apps/user/locales/ja-JP/wallet.json index 77c6f97..f1f6ca3 100644 --- a/apps/user/locales/ja-JP/wallet.json +++ b/apps/user/locales/ja-JP/wallet.json @@ -1,5 +1,6 @@ { "amount": "金額", + "assetOverview": "資産概要", "balance": "残高", "commission": "手数料", "createdAt": "時間", diff --git a/apps/user/locales/ko-KR/dashboard.json b/apps/user/locales/ko-KR/dashboard.json index 24ca3db..cbddcc2 100644 --- a/apps/user/locales/ko-KR/dashboard.json +++ b/apps/user/locales/ko-KR/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "내 구독", "nextResetDays": "다음 초기화/일", "noLimit": "제한 없음", + "noReset": "리셋 없음", "prompt": "프롬프트", "purchaseSubscription": "구독 구매", "qrCode": "QR코드", diff --git a/apps/user/locales/ko-KR/wallet.json b/apps/user/locales/ko-KR/wallet.json index 0c6bc71..ffdbc34 100644 --- a/apps/user/locales/ko-KR/wallet.json +++ b/apps/user/locales/ko-KR/wallet.json @@ -1,5 +1,6 @@ { "amount": "금액", + "assetOverview": "자산 개요", "balance": "잔액", "commission": "위원회", "createdAt": "시간", diff --git a/apps/user/locales/no-NO/dashboard.json b/apps/user/locales/no-NO/dashboard.json index b9fd29f..a1dc7e5 100644 --- a/apps/user/locales/no-NO/dashboard.json +++ b/apps/user/locales/no-NO/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Mine abonnementer", "nextResetDays": "Neste tilbakestilling/dager", "noLimit": "Ingen grense", + "noReset": "Ingen tilbakestilling", "prompt": "Hint", "purchaseSubscription": "Kjøp abonnement", "qrCode": "QR-kode", diff --git a/apps/user/locales/no-NO/wallet.json b/apps/user/locales/no-NO/wallet.json index 71c7eb1..ed9aec4 100644 --- a/apps/user/locales/no-NO/wallet.json +++ b/apps/user/locales/no-NO/wallet.json @@ -1,5 +1,6 @@ { "amount": "Beløp", + "assetOverview": "Eiendomsoversikt", "balance": "Balanse", "commission": "Kommisjon", "createdAt": "Tid", diff --git a/apps/user/locales/pl-PL/dashboard.json b/apps/user/locales/pl-PL/dashboard.json index a0cd93f..c15bc6f 100644 --- a/apps/user/locales/pl-PL/dashboard.json +++ b/apps/user/locales/pl-PL/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Moje subskrypcje", "nextResetDays": "Następny reset/dni", "noLimit": "Bez limitu", + "noReset": "Brak resetu", "prompt": "Podpowiedź", "purchaseSubscription": "Zakup subskrypcji", "qrCode": "Kod QR", diff --git a/apps/user/locales/pl-PL/wallet.json b/apps/user/locales/pl-PL/wallet.json index abaa54e..8af3b19 100644 --- a/apps/user/locales/pl-PL/wallet.json +++ b/apps/user/locales/pl-PL/wallet.json @@ -1,5 +1,6 @@ { "amount": "kwota", + "assetOverview": "Przegląd aktywów", "balance": "saldo", "commission": "Prowizja", "createdAt": "czas", diff --git a/apps/user/locales/pt-BR/dashboard.json b/apps/user/locales/pt-BR/dashboard.json index d112baf..eab91f4 100644 --- a/apps/user/locales/pt-BR/dashboard.json +++ b/apps/user/locales/pt-BR/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Minhas assinaturas", "nextResetDays": "Próxima redefinição/dias", "noLimit": "Sem Limite", + "noReset": "Sem Redefinição", "prompt": "Sugestão", "purchaseSubscription": "Comprar Assinatura", "qrCode": "Código QR", diff --git a/apps/user/locales/pt-BR/wallet.json b/apps/user/locales/pt-BR/wallet.json index f042426..395a755 100644 --- a/apps/user/locales/pt-BR/wallet.json +++ b/apps/user/locales/pt-BR/wallet.json @@ -1,5 +1,6 @@ { "amount": "quantia", + "assetOverview": "Visão Geral do Ativo", "balance": "saldo", "commission": "Comissão", "createdAt": "hora", diff --git a/apps/user/locales/ro-RO/dashboard.json b/apps/user/locales/ro-RO/dashboard.json index f7c806e..0989928 100644 --- a/apps/user/locales/ro-RO/dashboard.json +++ b/apps/user/locales/ro-RO/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Abonamentele mele", "nextResetDays": "Următoarea resetare/zile", "noLimit": "Fără limită", + "noReset": "Fără Resetare", "prompt": "Sfat", "purchaseSubscription": "Achiziționează abonament", "qrCode": "cod QR", diff --git a/apps/user/locales/ro-RO/wallet.json b/apps/user/locales/ro-RO/wallet.json index c6c7bef..4a4cfba 100644 --- a/apps/user/locales/ro-RO/wallet.json +++ b/apps/user/locales/ro-RO/wallet.json @@ -1,5 +1,6 @@ { "amount": "Sumă", + "assetOverview": "Prezentare generală a activelor", "balance": "Sold", "commission": "Comision", "createdAt": "Timp", diff --git a/apps/user/locales/ru-RU/dashboard.json b/apps/user/locales/ru-RU/dashboard.json index 8fb5016..847bc88 100644 --- a/apps/user/locales/ru-RU/dashboard.json +++ b/apps/user/locales/ru-RU/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Мои подписки", "nextResetDays": "Следующее сброс/дней", "noLimit": "Без ограничений", + "noReset": "Нет сброса", "prompt": "Подсказка", "purchaseSubscription": "Купить подписку", "qrCode": "QR-код", diff --git a/apps/user/locales/ru-RU/wallet.json b/apps/user/locales/ru-RU/wallet.json index e134ffa..a62ceec 100644 --- a/apps/user/locales/ru-RU/wallet.json +++ b/apps/user/locales/ru-RU/wallet.json @@ -1,5 +1,6 @@ { "amount": "сумма", + "assetOverview": "Обзор актива", "balance": "Баланс", "commission": "Комиссия", "createdAt": "время", diff --git a/apps/user/locales/th-TH/dashboard.json b/apps/user/locales/th-TH/dashboard.json index 0c6fa5e..22c3290 100644 --- a/apps/user/locales/th-TH/dashboard.json +++ b/apps/user/locales/th-TH/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "การสมัครสมาชิกของฉัน", "nextResetDays": "รีเซ็ตครั้งถัดไป/วัน", "noLimit": "ไม่จำกัด", + "noReset": "ไม่มีการรีเซ็ต", "prompt": "คำแนะนำ", "purchaseSubscription": "ซื้อการสมัครสมาชิก", "qrCode": "คิวอาร์โค้ด", diff --git a/apps/user/locales/th-TH/wallet.json b/apps/user/locales/th-TH/wallet.json index 3826b03..55c1c35 100644 --- a/apps/user/locales/th-TH/wallet.json +++ b/apps/user/locales/th-TH/wallet.json @@ -1,5 +1,6 @@ { "amount": "จำนวนเงิน", + "assetOverview": "ภาพรวมสินทรัพย์", "balance": "ยอดคงเหลือ", "commission": "คอมมิชชั่น", "createdAt": "เวลา", diff --git a/apps/user/locales/tr-TR/dashboard.json b/apps/user/locales/tr-TR/dashboard.json index 862eb57..22d2243 100644 --- a/apps/user/locales/tr-TR/dashboard.json +++ b/apps/user/locales/tr-TR/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Aboneliklerim", "nextResetDays": "Sonraki Sıfırlama/Gün", "noLimit": "Sınırsız", + "noReset": "Sıfırlama Yok", "prompt": "istem", "purchaseSubscription": "Abonelik Satın Al", "qrCode": "QR kodu", diff --git a/apps/user/locales/tr-TR/wallet.json b/apps/user/locales/tr-TR/wallet.json index 4609d53..75c21fa 100644 --- a/apps/user/locales/tr-TR/wallet.json +++ b/apps/user/locales/tr-TR/wallet.json @@ -1,5 +1,6 @@ { "amount": "Tutar", + "assetOverview": "Varlık Genel Görünümü", "balance": "Bakiye", "commission": "Komisyon", "createdAt": "zaman", diff --git a/apps/user/locales/uk-UA/dashboard.json b/apps/user/locales/uk-UA/dashboard.json index 4fcbe4c..2815aae 100644 --- a/apps/user/locales/uk-UA/dashboard.json +++ b/apps/user/locales/uk-UA/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Мої підписки", "nextResetDays": "Наступне скидання/днів", "noLimit": "Без обмежень", + "noReset": "Без скидання", "prompt": "підказка", "purchaseSubscription": "Придбати підписку", "qrCode": "QR-код", diff --git a/apps/user/locales/uk-UA/wallet.json b/apps/user/locales/uk-UA/wallet.json index 13824c6..e46d0d3 100644 --- a/apps/user/locales/uk-UA/wallet.json +++ b/apps/user/locales/uk-UA/wallet.json @@ -1,5 +1,6 @@ { "amount": "Сума", + "assetOverview": "Огляд активів", "balance": "Баланс", "commission": "Комісія", "createdAt": "Час", diff --git a/apps/user/locales/vi-VN/dashboard.json b/apps/user/locales/vi-VN/dashboard.json index d11cae3..e469aab 100644 --- a/apps/user/locales/vi-VN/dashboard.json +++ b/apps/user/locales/vi-VN/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "Đăng ký của tôi", "nextResetDays": "Lần đặt lại tiếp theo/ngày", "noLimit": "Không Giới Hạn", + "noReset": "Không đặt lại", "prompt": "gợi ý", "purchaseSubscription": "Mua đăng ký", "qrCode": "Mã QR", diff --git a/apps/user/locales/vi-VN/wallet.json b/apps/user/locales/vi-VN/wallet.json index aac935c..b8d3517 100644 --- a/apps/user/locales/vi-VN/wallet.json +++ b/apps/user/locales/vi-VN/wallet.json @@ -1,5 +1,6 @@ { "amount": "Số tiền", + "assetOverview": "Tổng Quan Tài Sản", "balance": "Số dư", "commission": "Hoa hồng", "createdAt": "Thời gian", diff --git a/apps/user/locales/zh-CN/dashboard.json b/apps/user/locales/zh-CN/dashboard.json index 4e01a2b..b152f21 100644 --- a/apps/user/locales/zh-CN/dashboard.json +++ b/apps/user/locales/zh-CN/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "我的订阅", "nextResetDays": "下次重置/天", "noLimit": "无限制", + "noReset": "不重置", "prompt": "提示", "purchaseSubscription": "购买订阅", "qrCode": "二维码", diff --git a/apps/user/locales/zh-CN/wallet.json b/apps/user/locales/zh-CN/wallet.json index f1c627d..f6152db 100644 --- a/apps/user/locales/zh-CN/wallet.json +++ b/apps/user/locales/zh-CN/wallet.json @@ -1,5 +1,6 @@ { "amount": "金额", + "assetOverview": "资产概览", "balance": "余额", "commission": "佣金", "createdAt": "时间", diff --git a/apps/user/locales/zh-HK/dashboard.json b/apps/user/locales/zh-HK/dashboard.json index 6bf1e81..e9681f8 100644 --- a/apps/user/locales/zh-HK/dashboard.json +++ b/apps/user/locales/zh-HK/dashboard.json @@ -13,6 +13,7 @@ "mySubscriptions": "我的訂閱", "nextResetDays": "下次重置/天", "noLimit": "無限制", + "noReset": "無重置", "prompt": "提示", "purchaseSubscription": "購買訂閱", "qrCode": "QR碼", diff --git a/apps/user/locales/zh-HK/wallet.json b/apps/user/locales/zh-HK/wallet.json index ab3afaf..3b58273 100644 --- a/apps/user/locales/zh-HK/wallet.json +++ b/apps/user/locales/zh-HK/wallet.json @@ -1,5 +1,6 @@ { "amount": "金額", + "assetOverview": "資產概覽", "balance": "餘額", "commission": "佣金", "createdAt": "時間", diff --git a/packages/ui/src/utils/formatting.ts b/packages/ui/src/utils/formatting.ts index c128852..da3bf91 100644 --- a/packages/ui/src/utils/formatting.ts +++ b/packages/ui/src/utils/formatting.ts @@ -1,4 +1,4 @@ -import { intlFormat } from 'date-fns'; +import { differenceInMilliseconds, intlFormat } from 'date-fns'; export function formatBytes(bytes: number) { if (bytes === 0) return '0 B'; @@ -24,3 +24,10 @@ export function formatDate(date?: Date | number, showTime: boolean = true) { hour12: false, }); } + +export function differenceInDays(dateLeft: Date | number, dateRight: Date | number) { + const diffInMs = differenceInMilliseconds(dateLeft, dateRight); + const diffInDays = diffInMs / (1000 * 60 * 60 * 24); + if (diffInDays % 1 === 0) return diffInDays; + return Number(diffInDays.toFixed(1)); +} From 35f92c9329d9487b4bab7136a5fb3ee21825ef19 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Sat, 8 Mar 2025 15:43:20 +0700 Subject: [PATCH 07/17] =?UTF-8?q?=F0=9F=90=9B=20fix(affiliate):=20Update?= =?UTF-8?q?=20user=20identifier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/admin/services/admin/index.ts | 2 +- apps/admin/services/admin/typings.d.ts | 2 +- apps/admin/services/common/index.ts | 2 +- apps/admin/services/common/typings.d.ts | 2 +- apps/user/components/affiliate/index.tsx | 4 ++-- apps/user/locales/cs-CZ/affiliate.json | 2 +- apps/user/locales/de-DE/affiliate.json | 2 +- apps/user/locales/en-US/affiliate.json | 2 +- apps/user/locales/es-ES/affiliate.json | 2 +- apps/user/locales/es-MX/affiliate.json | 2 +- apps/user/locales/fa-IR/affiliate.json | 2 +- apps/user/locales/fi-FI/affiliate.json | 2 +- apps/user/locales/fr-FR/affiliate.json | 2 +- apps/user/locales/hi-IN/affiliate.json | 2 +- apps/user/locales/hu-HU/affiliate.json | 2 +- apps/user/locales/ja-JP/affiliate.json | 2 +- apps/user/locales/ko-KR/affiliate.json | 2 +- apps/user/locales/no-NO/affiliate.json | 2 +- apps/user/locales/pl-PL/affiliate.json | 2 +- apps/user/locales/pt-BR/affiliate.json | 2 +- apps/user/locales/ro-RO/affiliate.json | 2 +- apps/user/locales/ru-RU/affiliate.json | 2 +- apps/user/locales/th-TH/affiliate.json | 2 +- apps/user/locales/tr-TR/affiliate.json | 2 +- apps/user/locales/uk-UA/affiliate.json | 2 +- apps/user/locales/vi-VN/affiliate.json | 2 +- apps/user/locales/zh-CN/affiliate.json | 2 +- apps/user/locales/zh-HK/affiliate.json | 2 +- apps/user/services/common/index.ts | 2 +- apps/user/services/common/typings.d.ts | 2 +- apps/user/services/user/index.ts | 2 +- apps/user/services/user/typings.d.ts | 2 +- 32 files changed, 33 insertions(+), 33 deletions(-) diff --git a/apps/admin/services/admin/index.ts b/apps/admin/services/admin/index.ts index f9e3438..37cdd29 100644 --- a/apps/admin/services/admin/index.ts +++ b/apps/admin/services/admin/index.ts @@ -1,5 +1,5 @@ // @ts-ignore - + // API 更新时间: // API 唯一标识: import * as ads from './ads'; diff --git a/apps/admin/services/admin/typings.d.ts b/apps/admin/services/admin/typings.d.ts index da8b2f7..03f1ebf 100644 --- a/apps/admin/services/admin/typings.d.ts +++ b/apps/admin/services/admin/typings.d.ts @@ -1621,8 +1621,8 @@ declare namespace API { }; type UserAffiliate = { - email: string; avatar: string; + identifier: string; registered_at: number; enable: boolean; }; diff --git a/apps/admin/services/common/index.ts b/apps/admin/services/common/index.ts index 73b3bda..61ba129 100644 --- a/apps/admin/services/common/index.ts +++ b/apps/admin/services/common/index.ts @@ -1,5 +1,5 @@ // @ts-ignore - + // API 更新时间: // API 唯一标识: import * as auth from './auth'; diff --git a/apps/admin/services/common/typings.d.ts b/apps/admin/services/common/typings.d.ts index 878dfce..475780b 100644 --- a/apps/admin/services/common/typings.d.ts +++ b/apps/admin/services/common/typings.d.ts @@ -761,8 +761,8 @@ declare namespace API { }; type UserAffiliate = { - email: string; avatar: string; + identifier: string; registered_at: number; enable: boolean; }; diff --git a/apps/user/components/affiliate/index.tsx b/apps/user/components/affiliate/index.tsx index 43f7951..80fd4c2 100644 --- a/apps/user/components/affiliate/index.tsx +++ b/apps/user/components/affiliate/index.tsx @@ -96,8 +96,8 @@ export default function Affiliate() {
    • - {t('userEmail')} - {item.email} + {t('userIdentifier')} + {item.identifier}
    • {t('registrationTime')} diff --git a/apps/user/locales/cs-CZ/affiliate.json b/apps/user/locales/cs-CZ/affiliate.json index 52eb2f0..2220f14 100644 --- a/apps/user/locales/cs-CZ/affiliate.json +++ b/apps/user/locales/cs-CZ/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Záznamy o pozvání", "registrationTime": "Čas registrace", "totalCommission": "Celková provize", - "userEmail": "Uživatelský e-mail" + "userIdentifier": "Identifikátor uživatele" } diff --git a/apps/user/locales/de-DE/affiliate.json b/apps/user/locales/de-DE/affiliate.json index 8255489..98bb042 100644 --- a/apps/user/locales/de-DE/affiliate.json +++ b/apps/user/locales/de-DE/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Einladungsprotokolle", "registrationTime": "Registrierungszeit", "totalCommission": "Gesamtprovision", - "userEmail": "Benutzer-E-Mail" + "userIdentifier": "Benutzerkennung" } diff --git a/apps/user/locales/en-US/affiliate.json b/apps/user/locales/en-US/affiliate.json index 1a4a5cb..26e815b 100644 --- a/apps/user/locales/en-US/affiliate.json +++ b/apps/user/locales/en-US/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Invite Records", "registrationTime": "Registration Time", "totalCommission": "Total Commission", - "userEmail": "User Email" + "userIdentifier": "User Identifier" } diff --git a/apps/user/locales/es-ES/affiliate.json b/apps/user/locales/es-ES/affiliate.json index e3bc236..a5ce9fa 100644 --- a/apps/user/locales/es-ES/affiliate.json +++ b/apps/user/locales/es-ES/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Registros de invitación", "registrationTime": "Hora de registro", "totalCommission": "Comisión total", - "userEmail": "Correo electrónico del usuario" + "userIdentifier": "Identificador de usuario" } diff --git a/apps/user/locales/es-MX/affiliate.json b/apps/user/locales/es-MX/affiliate.json index 704bac2..edb9661 100644 --- a/apps/user/locales/es-MX/affiliate.json +++ b/apps/user/locales/es-MX/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Registros de invitación", "registrationTime": "Hora de registro", "totalCommission": "Comisión total", - "userEmail": "Correo electrónico del usuario" + "userIdentifier": "Identificador de Usuario" } diff --git a/apps/user/locales/fa-IR/affiliate.json b/apps/user/locales/fa-IR/affiliate.json index 3ad965b..c551559 100644 --- a/apps/user/locales/fa-IR/affiliate.json +++ b/apps/user/locales/fa-IR/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "سوابق دعوت", "registrationTime": "زمان ثبت‌نام", "totalCommission": "کمیسیون کل", - "userEmail": "ایمیل کاربر" + "userIdentifier": "شناسه کاربر" } diff --git a/apps/user/locales/fi-FI/affiliate.json b/apps/user/locales/fi-FI/affiliate.json index e0bd537..007adab 100644 --- a/apps/user/locales/fi-FI/affiliate.json +++ b/apps/user/locales/fi-FI/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Kutsuhistoria", "registrationTime": "Rekisteröintiaika", "totalCommission": "Kokonaisprovisio", - "userEmail": "Käyttäjän sähköposti" + "userIdentifier": "Käyttäjän tunnus" } diff --git a/apps/user/locales/fr-FR/affiliate.json b/apps/user/locales/fr-FR/affiliate.json index b58030f..bedf014 100644 --- a/apps/user/locales/fr-FR/affiliate.json +++ b/apps/user/locales/fr-FR/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Historique des invitations", "registrationTime": "Heure d'inscription", "totalCommission": "Commission totale", - "userEmail": "Adresse e-mail de l'utilisateur" + "userIdentifier": "Identifiant de l'utilisateur" } diff --git a/apps/user/locales/hi-IN/affiliate.json b/apps/user/locales/hi-IN/affiliate.json index 549ae42..5fbf7c4 100644 --- a/apps/user/locales/hi-IN/affiliate.json +++ b/apps/user/locales/hi-IN/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "आमंत्रण रिकॉर्ड", "registrationTime": "पंजीकरण समय", "totalCommission": "कुल कमीशन", - "userEmail": "उपयोगकर्ता ईमेल" + "userIdentifier": "उपयोगकर्ता पहचान" } diff --git a/apps/user/locales/hu-HU/affiliate.json b/apps/user/locales/hu-HU/affiliate.json index 619e329..5c30f74 100644 --- a/apps/user/locales/hu-HU/affiliate.json +++ b/apps/user/locales/hu-HU/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Meghívási rekordok", "registrationTime": "Regisztráció ideje", "totalCommission": "Teljes jutalék", - "userEmail": "Felhasználó e-mail" + "userIdentifier": "Felhasználói azonosító" } diff --git a/apps/user/locales/ja-JP/affiliate.json b/apps/user/locales/ja-JP/affiliate.json index 6003736..dd39f71 100644 --- a/apps/user/locales/ja-JP/affiliate.json +++ b/apps/user/locales/ja-JP/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "招待記録", "registrationTime": "登録時間", "totalCommission": "総手数料", - "userEmail": "ユーザーのメールアドレス" + "userIdentifier": "ユーザー識別子" } diff --git a/apps/user/locales/ko-KR/affiliate.json b/apps/user/locales/ko-KR/affiliate.json index 4e1bdc3..f4aa128 100644 --- a/apps/user/locales/ko-KR/affiliate.json +++ b/apps/user/locales/ko-KR/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "초대 기록", "registrationTime": "등록 시간", "totalCommission": "총 수수료", - "userEmail": "사용자 이메일" + "userIdentifier": "사용자 식별자" } diff --git a/apps/user/locales/no-NO/affiliate.json b/apps/user/locales/no-NO/affiliate.json index fd2639b..098baf0 100644 --- a/apps/user/locales/no-NO/affiliate.json +++ b/apps/user/locales/no-NO/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Invitasjonsoppføringer", "registrationTime": "Registreringstid", "totalCommission": "Totalprovisjon", - "userEmail": "Brukerens e-post" + "userIdentifier": "Brukeridentifikator" } diff --git a/apps/user/locales/pl-PL/affiliate.json b/apps/user/locales/pl-PL/affiliate.json index d2982a3..5d9fcf5 100644 --- a/apps/user/locales/pl-PL/affiliate.json +++ b/apps/user/locales/pl-PL/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Rekordy zaproszeń", "registrationTime": "Czas rejestracji", "totalCommission": "Łączna prowizja", - "userEmail": "Adres e-mail użytkownika" + "userIdentifier": "Identyfikator Użytkownika" } diff --git a/apps/user/locales/pt-BR/affiliate.json b/apps/user/locales/pt-BR/affiliate.json index 8cce85c..fa50c62 100644 --- a/apps/user/locales/pt-BR/affiliate.json +++ b/apps/user/locales/pt-BR/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Registros de Convite", "registrationTime": "Hora de Registro", "totalCommission": "Comissão Total", - "userEmail": "Email do usuário" + "userIdentifier": "Identificador do Usuário" } diff --git a/apps/user/locales/ro-RO/affiliate.json b/apps/user/locales/ro-RO/affiliate.json index 559bf3c..b1acfd5 100644 --- a/apps/user/locales/ro-RO/affiliate.json +++ b/apps/user/locales/ro-RO/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Înregistrări de invitații", "registrationTime": "Timp de înregistrare", "totalCommission": "Comision total", - "userEmail": "Email utilizator" + "userIdentifier": "Identificator utilizator" } diff --git a/apps/user/locales/ru-RU/affiliate.json b/apps/user/locales/ru-RU/affiliate.json index 9f36d91..7df7855 100644 --- a/apps/user/locales/ru-RU/affiliate.json +++ b/apps/user/locales/ru-RU/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Записи приглашений", "registrationTime": "Время регистрации", "totalCommission": "Общая сумма комиссии", - "userEmail": "Электронная почта пользователя" + "userIdentifier": "Идентификатор пользователя" } diff --git a/apps/user/locales/th-TH/affiliate.json b/apps/user/locales/th-TH/affiliate.json index 3dcc460..51f5c3f 100644 --- a/apps/user/locales/th-TH/affiliate.json +++ b/apps/user/locales/th-TH/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "บันทึกการเชิญ", "registrationTime": "เวลาลงทะเบียน", "totalCommission": "ค่าคอมมิชชั่นทั้งหมด", - "userEmail": "อีเมลผู้ใช้" + "userIdentifier": "รหัสผู้ใช้" } diff --git a/apps/user/locales/tr-TR/affiliate.json b/apps/user/locales/tr-TR/affiliate.json index b0516af..d1031ff 100644 --- a/apps/user/locales/tr-TR/affiliate.json +++ b/apps/user/locales/tr-TR/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Davet Kayıtları", "registrationTime": "Kayıt Zamanı", "totalCommission": "Toplam Komisyon", - "userEmail": "Kullanıcı E-postası" + "userIdentifier": "Kullanıcı Tanımlayıcı" } diff --git a/apps/user/locales/uk-UA/affiliate.json b/apps/user/locales/uk-UA/affiliate.json index 89fab64..6953d87 100644 --- a/apps/user/locales/uk-UA/affiliate.json +++ b/apps/user/locales/uk-UA/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Записи запрошень", "registrationTime": "Час реєстрації", "totalCommission": "Загальна сума комісії", - "userEmail": "Електронна пошта користувача" + "userIdentifier": "Ідентифікатор користувача" } diff --git a/apps/user/locales/vi-VN/affiliate.json b/apps/user/locales/vi-VN/affiliate.json index 2fcac95..1d5c936 100644 --- a/apps/user/locales/vi-VN/affiliate.json +++ b/apps/user/locales/vi-VN/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "Hồ sơ mời", "registrationTime": "Thời gian đăng ký", "totalCommission": "Tổng hoa hồng", - "userEmail": "Email người dùng" + "userIdentifier": "Mã người dùng" } diff --git a/apps/user/locales/zh-CN/affiliate.json b/apps/user/locales/zh-CN/affiliate.json index f5df07f..967ec6e 100644 --- a/apps/user/locales/zh-CN/affiliate.json +++ b/apps/user/locales/zh-CN/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "邀请记录", "registrationTime": "注册时间", "totalCommission": "佣金总额", - "userEmail": "用户邮箱" + "userIdentifier": "用户标识符" } diff --git a/apps/user/locales/zh-HK/affiliate.json b/apps/user/locales/zh-HK/affiliate.json index d28ba1b..68f00dc 100644 --- a/apps/user/locales/zh-HK/affiliate.json +++ b/apps/user/locales/zh-HK/affiliate.json @@ -7,5 +7,5 @@ "inviteRecords": "邀請記錄", "registrationTime": "註冊時間", "totalCommission": "佣金總額", - "userEmail": "使用者信箱" + "userIdentifier": "用戶識別碼" } diff --git a/apps/user/services/common/index.ts b/apps/user/services/common/index.ts index 73b3bda..61ba129 100644 --- a/apps/user/services/common/index.ts +++ b/apps/user/services/common/index.ts @@ -1,5 +1,5 @@ // @ts-ignore - + // API 更新时间: // API 唯一标识: import * as auth from './auth'; diff --git a/apps/user/services/common/typings.d.ts b/apps/user/services/common/typings.d.ts index 878dfce..475780b 100644 --- a/apps/user/services/common/typings.d.ts +++ b/apps/user/services/common/typings.d.ts @@ -761,8 +761,8 @@ declare namespace API { }; type UserAffiliate = { - email: string; avatar: string; + identifier: string; registered_at: number; enable: boolean; }; diff --git a/apps/user/services/user/index.ts b/apps/user/services/user/index.ts index f988131..12fe8d0 100644 --- a/apps/user/services/user/index.ts +++ b/apps/user/services/user/index.ts @@ -1,5 +1,5 @@ // @ts-ignore - + // API 更新时间: // API 唯一标识: import * as announcement from './announcement'; diff --git a/apps/user/services/user/typings.d.ts b/apps/user/services/user/typings.d.ts index 4e4b8c8..6d8e3cb 100644 --- a/apps/user/services/user/typings.d.ts +++ b/apps/user/services/user/typings.d.ts @@ -907,8 +907,8 @@ declare namespace API { }; type UserAffiliate = { - email: string; avatar: string; + identifier: string; registered_at: number; enable: boolean; }; From a5e2079ddca0029e0210c54b45214675202f4d28 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Sat, 8 Mar 2025 20:04:33 +0700 Subject: [PATCH 08/17] =?UTF-8?q?=F0=9F=90=9B=20fix(payment):=20Refactor?= =?UTF-8?q?=20purchaseCheckout=20usage=20and=20remove=20redundant=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/user/app/(main)/(user)/payment/page.tsx | 5 ++++- apps/user/components/subscribe/purchase.tsx | 12 +----------- apps/user/components/subscribe/recharge.tsx | 10 ---------- apps/user/components/subscribe/renewal.tsx | 12 +----------- apps/user/components/subscribe/reset-traffic.tsx | 10 ---------- 5 files changed, 6 insertions(+), 43 deletions(-) diff --git a/apps/user/app/(main)/(user)/payment/page.tsx b/apps/user/app/(main)/(user)/payment/page.tsx index 4226f1d..4cc7570 100644 --- a/apps/user/app/(main)/(user)/payment/page.tsx +++ b/apps/user/app/(main)/(user)/payment/page.tsx @@ -49,12 +49,15 @@ export default function Page() { const { data: payment } = useQuery({ enabled: !!orderNo && data?.status === 1, - queryKey: ['checkoutOrder', orderNo], + queryKey: ['purchaseCheckout', orderNo], queryFn: async () => { const { data } = await purchaseCheckout({ orderNo: orderNo!, returnUrl: window.location.href, }); + if (data.data?.type === 'link' && data.data.checkout_url) { + window.location.href = data.data.checkout_url; + } return data?.data; }, }); diff --git a/apps/user/components/subscribe/purchase.tsx b/apps/user/components/subscribe/purchase.tsx index 4ec165f..8abf49e 100644 --- a/apps/user/components/subscribe/purchase.tsx +++ b/apps/user/components/subscribe/purchase.tsx @@ -5,7 +5,6 @@ import DurationSelector from '@/components/subscribe/duration-selector'; import PaymentMethods from '@/components/subscribe/payment-methods'; import useGlobalStore from '@/config/use-global'; import { preCreateOrder, purchase } from '@/services/user/order'; -import { purchaseCheckout } from '@/services/user/portal'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@workspace/ui/components/button'; import { Card, CardContent } from '@workspace/ui/components/card'; @@ -70,20 +69,11 @@ export default function Purchase({ subscribe, setSubscribe }: Readonly) { const response = await recharge(params); const orderNo = response.data.data?.order_no; if (orderNo) { - const { data } = await purchaseCheckout({ - orderNo, - returnUrl: `${window.location.origin}/payment?order_no=${orderNo}`, - }); - const type = data.data?.type; - const checkout_url = data.data?.checkout_url; - if (type === 'link') { - window.location.href = checkout_url!; - } router.push(`/payment?order_no=${orderNo}`); setOpen(false); } diff --git a/apps/user/components/subscribe/renewal.tsx b/apps/user/components/subscribe/renewal.tsx index c8faf58..5b884d4 100644 --- a/apps/user/components/subscribe/renewal.tsx +++ b/apps/user/components/subscribe/renewal.tsx @@ -5,7 +5,6 @@ import DurationSelector from '@/components/subscribe/duration-selector'; import PaymentMethods from '@/components/subscribe/payment-methods'; import useGlobalStore from '@/config/use-global'; import { preCreateOrder, renewal } from '@/services/user/order'; -import { purchaseCheckout } from '@/services/user/portal'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@workspace/ui/components/button'; import { Card, CardContent } from '@workspace/ui/components/card'; @@ -78,20 +77,11 @@ export default function Renewal({ id, subscribe }: Readonly) { const response = await renewal(params as API.RenewalOrderRequest); const orderNo = response.data.data?.order_no; if (orderNo) { - const { data } = await purchaseCheckout({ - orderNo, - returnUrl: `${window.location.origin}/payment?order_no=${orderNo}`, - }); - const type = data.data?.type; - const checkout_url = data.data?.checkout_url; - if (type === 'link') { - window.location.href = checkout_url!; - } getUserInfo(); router.push(`/payment?order_no=${orderNo}`); } } catch (error) { - console.log(error); + /* empty */ } }); }, [params, router, getUserInfo]); diff --git a/apps/user/components/subscribe/reset-traffic.tsx b/apps/user/components/subscribe/reset-traffic.tsx index 33640a8..288a937 100644 --- a/apps/user/components/subscribe/reset-traffic.tsx +++ b/apps/user/components/subscribe/reset-traffic.tsx @@ -3,7 +3,6 @@ import { Display } from '@/components/display'; import useGlobalStore from '@/config/use-global'; import { resetTraffic } from '@/services/user/order'; -import { purchaseCheckout } from '@/services/user/portal'; import { Button } from '@workspace/ui/components/button'; import { Dialog, @@ -85,15 +84,6 @@ export default function ResetTraffic({ id, replacement }: Readonly Date: Sat, 8 Mar 2025 20:10:45 +0700 Subject: [PATCH 09/17] =?UTF-8?q?=F0=9F=90=9B=20fix(payment):=20Update=20c?= =?UTF-8?q?heckout=20type=20from=20'link'=20to=20'url'=20for=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/user/app/(main)/(user)/payment/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user/app/(main)/(user)/payment/page.tsx b/apps/user/app/(main)/(user)/payment/page.tsx index 4cc7570..a4c362c 100644 --- a/apps/user/app/(main)/(user)/payment/page.tsx +++ b/apps/user/app/(main)/(user)/payment/page.tsx @@ -55,7 +55,7 @@ export default function Page() { orderNo: orderNo!, returnUrl: window.location.href, }); - if (data.data?.type === 'link' && data.data.checkout_url) { + if (data.data?.type === 'url' && data.data.checkout_url) { window.location.href = data.data.checkout_url; } return data?.data; @@ -176,7 +176,7 @@ export default function Page() {
    )} - {data?.status === 1 && payment?.type === 'link' && ( + {data?.status === 1 && payment?.type === 'url' && (

    {t('waitingForPayment')}

    {countdownDisplay}

    From fc0da761b504bb33c2781d2388a070cd74992659 Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Mon, 10 Mar 2025 18:32:07 +0700 Subject: [PATCH 10/17] =?UTF-8?q?=F0=9F=90=9B=20fix(ui):=20Improve=20dashb?= =?UTF-8?q?oard=20layout=20and=20enhance=20button=20functionality;=20open?= =?UTF-8?q?=20checkout=20URLs=20in=20a=20new=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(main)/(user)/dashboard/content.tsx | 31 ++++++++++++++++--- apps/user/app/(main)/(user)/payment/page.tsx | 2 +- .../user/app/(main)/purchasing/order/page.tsx | 2 +- apps/user/components/footer.tsx | 14 +++++---- apps/user/components/header.tsx | 2 +- 5 files changed, 37 insertions(+), 14 deletions(-) diff --git a/apps/user/app/(main)/(user)/dashboard/content.tsx b/apps/user/app/(main)/(user)/dashboard/content.tsx index 315a262..b2ccdce 100644 --- a/apps/user/app/(main)/(user)/dashboard/content.tsx +++ b/apps/user/app/(main)/(user)/dashboard/content.tsx @@ -57,7 +57,11 @@ export default function Content() { const [protocol, setProtocol] = useState(''); - const { data: userSubscribe = [], refetch } = useQuery({ + const { + data: userSubscribe = [], + refetch, + isLoading, + } = useQuery({ queryKey: ['queryUserSubscribe'], queryFn: async () => { const { data } = await queryUserSubscribe(); @@ -88,10 +92,27 @@ export default function Content() { <> {userSubscribe.length ? ( <> -

    - - {t('mySubscriptions')} -

    +
    +

    + + {t('mySubscriptions')} +

    +
    + + +
    +
    {site.site_name} © All rights reserved. - - {t('tos')} - - - {t('privacyPolicy')} - +
    + + {t('tos')} + + + {t('privacyPolicy')} + +
    diff --git a/apps/user/components/header.tsx b/apps/user/components/header.tsx index b3bc992..aa1c1b8 100644 --- a/apps/user/components/header.tsx +++ b/apps/user/components/header.tsx @@ -19,7 +19,7 @@ export default function Header() { {site.site_logo && ( logo )} - {site.site_name} + {site.site_name} ); return ( From 710947209aeba74ac4c07a42da05cae7acdca06b Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Tue, 11 Mar 2025 20:06:12 +0700 Subject: [PATCH 11/17] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(payment):?= =?UTF-8?q?=20Reconstruct=20the=20payment=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/app/dashboard/payment/alipayf2f.tsx | 256 ----------- apps/admin/app/dashboard/payment/epay.tsx | 221 ---------- apps/admin/app/dashboard/payment/page.tsx | 29 +- .../app/dashboard/payment/payment-form.tsx | 396 ++++++++++++++++++ .../app/dashboard/payment/payment-table.tsx | 226 ++++++++++ .../app/dashboard/payment/stripe-alipay.tsx | 232 ---------- .../dashboard/payment/stripe-wechat-pay.tsx | 232 ---------- apps/admin/locales/cs-CZ/payment.json | 81 ++-- apps/admin/locales/de-DE/payment.json | 81 ++-- apps/admin/locales/en-US/payment.json | 85 ++-- apps/admin/locales/es-ES/payment.json | 81 ++-- apps/admin/locales/es-MX/payment.json | 81 ++-- apps/admin/locales/fa-IR/payment.json | 81 ++-- apps/admin/locales/fi-FI/payment.json | 81 ++-- apps/admin/locales/fr-FR/payment.json | 81 ++-- apps/admin/locales/hi-IN/payment.json | 81 ++-- apps/admin/locales/hu-HU/payment.json | 81 ++-- apps/admin/locales/ja-JP/payment.json | 81 ++-- apps/admin/locales/ko-KR/payment.json | 81 ++-- apps/admin/locales/no-NO/payment.json | 81 ++-- apps/admin/locales/pl-PL/payment.json | 81 ++-- apps/admin/locales/pt-BR/payment.json | 81 ++-- apps/admin/locales/ro-RO/payment.json | 81 ++-- apps/admin/locales/ru-RU/payment.json | 81 ++-- apps/admin/locales/th-TH/payment.json | 81 ++-- apps/admin/locales/tr-TR/payment.json | 81 ++-- apps/admin/locales/uk-UA/payment.json | 81 ++-- apps/admin/locales/vi-VN/payment.json | 81 ++-- apps/admin/locales/zh-CN/payment.json | 85 ++-- apps/admin/locales/zh-HK/payment.json | 81 ++-- apps/admin/services/admin/payment.ts | 131 +++--- apps/admin/services/admin/typings.d.ts | 149 ++++--- apps/admin/services/common/common.ts | 15 + apps/admin/services/common/typings.d.ts | 68 ++- 34 files changed, 1826 insertions(+), 2000 deletions(-) delete mode 100644 apps/admin/app/dashboard/payment/alipayf2f.tsx delete mode 100644 apps/admin/app/dashboard/payment/epay.tsx create mode 100644 apps/admin/app/dashboard/payment/payment-form.tsx create mode 100644 apps/admin/app/dashboard/payment/payment-table.tsx delete mode 100644 apps/admin/app/dashboard/payment/stripe-alipay.tsx delete mode 100644 apps/admin/app/dashboard/payment/stripe-wechat-pay.tsx diff --git a/apps/admin/app/dashboard/payment/alipayf2f.tsx b/apps/admin/app/dashboard/payment/alipayf2f.tsx deleted file mode 100644 index 51c5849..0000000 --- a/apps/admin/app/dashboard/payment/alipayf2f.tsx +++ /dev/null @@ -1,256 +0,0 @@ -'use client'; - -import { getAlipayF2FPaymentConfig, updateAlipayF2FPaymentConfig } from '@/services/admin/payment'; -import { useQuery } from '@tanstack/react-query'; -import { Label } from '@workspace/ui/components/label'; -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectTrigger, - SelectValue, -} from '@workspace/ui/components/select'; -import { Switch } from '@workspace/ui/components/switch'; -import { Table, TableBody, TableCell, TableRow } from '@workspace/ui/components/table'; -import { Textarea } from '@workspace/ui/components/textarea'; -import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input'; -import { unitConversion } from '@workspace/ui/utils'; -import { useTranslations } from 'next-intl'; -import { toast } from 'sonner'; - -export default function AlipayF2F() { - const t = useTranslations('payment'); - - const { data, refetch } = useQuery({ - queryKey: ['getAlipayF2FPaymentConfig'], - queryFn: async () => { - const { data } = await getAlipayF2FPaymentConfig(); - - return data.data; - }, - }); - - async function updateConfig(key: string, value: unknown) { - if (data?.[key] === value) return; - try { - await updateAlipayF2FPaymentConfig({ - ...data, - [key]: value, - } as API.UpdateAlipayF2fRequest); - toast.success(t('saveSuccess')); - refetch(); - } catch (error) { - /* empty */ - } - } - - return ( - - - - - -

    {t('enableDescription')}

    -
    - - { - updateConfig('enable', checked); - }} - /> - -
    - - - -

    {t('alipayf2f.sandboxDescription')}

    -
    - - { - updateConfig('config', { - ...data?.config, - sandbox: checked, - }); - }} - /> - -
    - - - -

    {t('showNameDescription')}

    -
    - - updateConfig('name', value)} - /> - -
    - - - -

    {t('iconUrlDescription')}

    -
    - - updateConfig('icon', value)} - /> - -
    - - - -

    {t('notifyUrlDescription')}

    -
    - - updateConfig('domain', value)} - /> - -
    - - - -

    {t('feeModeDescription')}

    -
    - - - -
    - - - -

    {t('feePercentDescription')}

    -
    - - updateConfig('fee_percent', value)} - suffix='%' - /> - -
    - - - -

    {t('fixedFeeDescription')}

    -
    - - unitConversion('centsToDollars', value)} - formatOutput={(value) => unitConversion('dollarsToCents', value)} - onValueBlur={(value) => updateConfig('fee_amount', value)} - /> - -
    - - - -

    - - - - updateConfig('config', { - ...data?.config, - app_id: value, - }) - } - /> - - - - - -

    - - -