From 3e24080466bd12e1558a1c49a02d9c26248e1f70 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Fri, 29 May 2026 21:23:12 +0800 Subject: [PATCH] feat: implement AccountCenter dashboard with subscription and payment management features --- frontend/components/account/AccountCenter.tsx | 550 +++++++++--------- .../account/__tests__/paymentShell.test.ts | 15 + 2 files changed, 295 insertions(+), 270 deletions(-) diff --git a/frontend/components/account/AccountCenter.tsx b/frontend/components/account/AccountCenter.tsx index 7ea5a63d..d2faa2ff 100644 --- a/frontend/components/account/AccountCenter.tsx +++ b/frontend/components/account/AccountCenter.tsx @@ -337,6 +337,7 @@ export function AccountCenter() { isSubscribed && queuedExtensionDays > 0, ); const canAccessPaidTelegramGroup = Boolean(isSubscribed && !isTrialSubscription); + const hasTelegramPanel = Boolean(isTrialSubscription || canAccessPaidTelegramGroup); const telegramBound = Number(backend?.telegram_pricing?.telegram_id || 0) > 0; const displayExpiryRaw = isSubscribed @@ -900,9 +901,13 @@ export function AccountCenter() { {/* Telegram Bot Section & Payment Details */} {showSecondarySections ? ( -
+
{isTrialSubscription && ( -
+
)} {canAccessPaidTelegramGroup && ( -
+
+

{copy.paymentMgmt} @@ -1043,275 +1044,282 @@ export function AccountCenter() { )}

) : null} -
-

- {copy.proPlan} -

-
- {displayPlanList.map((plan) => { - const code = String(plan.plan_code || ""); - const active = code === selectedPlanCode; - const isQuarterly = code === "pro_quarterly"; - return ( +
+
+
+

+ {copy.proPlan} +

+
+ {displayPlanList.map((plan) => { + const code = String(plan.plan_code || ""); + const active = code === selectedPlanCode; + const isQuarterly = code === "pro_quarterly"; + return ( + + ); + })} +
+ {appliedReferralCode && selectedPlanCode === "pro_monthly" ? ( +

+ {copy.referralDiscountHint} +

+ ) : null} +
+
+
+

+ {copy.referralTitle} +

+ + {copy.referralInviteLimit} + +
+
+
+

+ {copy.referralMyCode} +

+
+ + {referralCode || "--"} + + {referralCode ? ( + + ) : null} +
+

+ {copy.referralRewardHint} +

+
+
+

+ {copy.referralApplyLabel} +

+ {appliedReferralCode ? ( +
+ {appliedReferralCode} +
+ ) : ( +
+ setReferralCodeInput(event.target.value)} + placeholder={copy.referralApplyPlaceholder} + className="min-w-0 flex-1 rounded-xl border border-slate-300 bg-white px-3 py-2 text-xs text-slate-950 outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20" + /> + +
+ )} +

+ {copy.referralDiscountHint} +

+
+
+
+
+ + + + + +

+ {copy.paymentGuardHint} +

+
+
+
+ {availableChainList.length > 1 && ( +
+

+ {copy.paymentNetwork} +

+
+ {availableChainList.map((chain) => { + const active = chain.chain_id === selectedPaymentChainId; + return ( + + ); + })} +
+
+ )} + {availableTokenList.length > 0 && ( +
+

+ {copy.paymentToken} +

+
+ {availableTokenList.map((token) => { + const active = + token.address === + (resolvedSelectedTokenAddress || + token.address); + return ( + + ); + })} +
+
+ )} + + {/* Payment Method Tabs */} +
+

+ {copy.paymentMethodLabel} +

+
+ - ); - })} -
- {appliedReferralCode && selectedPlanCode === "pro_monthly" ? ( -

- {copy.referralDiscountHint} -

- ) : null} -
-
-
-

- {copy.referralTitle} -

- - {copy.referralInviteLimit} - -
-
-
-

- {copy.referralMyCode} -

-
- - {referralCode || "--"} - - {referralCode ? ( - - ) : null}
-

- {copy.referralRewardHint} -

-
-
-

- {copy.referralApplyLabel} -

- {appliedReferralCode ? ( -
- {appliedReferralCode} -
- ) : ( -
- setReferralCodeInput(event.target.value)} - placeholder={copy.referralApplyPlaceholder} - className="min-w-0 flex-1 rounded-xl border border-slate-300 bg-white px-3 py-2 text-xs text-slate-950 outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20" - /> - -
- )} -

- {copy.referralDiscountHint} -

-
-
-
-
- - - - - -

- {copy.paymentGuardHint} -

-
- {availableChainList.length > 1 && ( -
-

- {copy.paymentNetwork} -

-
- {availableChainList.map((chain) => { - const active = chain.chain_id === selectedPaymentChainId; - return ( - - ); - })} -
-
- )} - {availableTokenList.length > 0 && ( -
-

- {copy.paymentToken} -

-
- {availableTokenList.map((token) => { - const active = - token.address === - (resolvedSelectedTokenAddress || - token.address); - return ( - - ); - })} -
-
- )} - {/* Payment Method Tabs */} -
-

- {copy.paymentMethodLabel} -

-
- - -
- - {paymentMethodTab === "wallet" ? ( -
-

- {copy.paymentWalletDesc} -

-
- {copy.paymentGasWarning} -
+ {paymentMethodTab === "wallet" ? ( +
+

+ {copy.paymentWalletDesc} +

+
+ {copy.paymentGasWarning} +
{boundWallets.length ? (
@@ -1574,6 +1582,8 @@ export function AccountCenter() { )}
+
+
) : ( diff --git a/frontend/components/account/__tests__/paymentShell.test.ts b/frontend/components/account/__tests__/paymentShell.test.ts index e48df0ed..0af3e062 100644 --- a/frontend/components/account/__tests__/paymentShell.test.ts +++ b/frontend/components/account/__tests__/paymentShell.test.ts @@ -173,6 +173,21 @@ export function runTests() { accountFeatureSource.includes("只有 USDC 可能无法完成授权或支付"), "payment wallet tab must warn users that Polygon POL gas is required in addition to USDC", ); + assert( + !accountCenterSource.includes("md:w-96"), + "account payment management must not use a narrow fixed sidebar that creates an overlong column", + ); + assert( + accountCenterSource.includes("items-start") && + accountCenterSource.includes("xl:grid-cols-[minmax(0,0.9fr)_minmax(620px,1.1fr)]"), + "account secondary sections must align cards to the top and give payment management a wider responsive column", + ); + assert( + accountCenterSource.includes("data-testid=\"payment-management-grid\"") && + accountCenterSource.includes("lg:grid-cols-[minmax(0,1fr)_minmax(320px,380px)]") && + accountCenterSource.includes("data-testid=\"payment-guard-grid\""), + "payment management must split plans/referrals from payment controls and compact guard details into grids", + ); assert( !appAnalyticsSource.includes('NEXT_PUBLIC_POLYWEATHER_APP_ANALYTICS === "true"') && !analyticsRouteSource.includes('NEXT_PUBLIC_POLYWEATHER_APP_ANALYTICS === "true"'),