From 3f3d1a020398267c2854d013dabeae3b5a2a3032 Mon Sep 17 00:00:00 2001 From: chenhang Date: Wed, 15 Jul 2026 11:50:44 +0800 Subject: [PATCH] feat(subscription): add VIP renewal price notice --- .../subscription-screen.helpers.test.ts | 16 ++++++++++++++++ .../components/subscription-screen.module.css | 8 ++++++++ .../subscription/subscription-screen.helpers.ts | 6 ++++++ src/app/subscription/subscription-screen.tsx | 13 ++++++++++++- 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/app/subscription/__tests__/subscription-screen.helpers.test.ts b/src/app/subscription/__tests__/subscription-screen.helpers.test.ts index 46648e3d..828f85ed 100644 --- a/src/app/subscription/__tests__/subscription-screen.helpers.test.ts +++ b/src/app/subscription/__tests__/subscription-screen.helpers.test.ts @@ -200,16 +200,32 @@ describe("subscription screen helpers", () => { expect( getFirstRechargeOfferView({ isFirstRecharge: true, + subscriptionType: "vip", vipPlans: vipViews, coinPlans: coinViews, }), ).toMatchObject({ badgeText: "50% OFF", title: "First Recharge Offer", + renewalNotice: + "First month 50% off. Renews at the regular price from the second month.", + }); + expect( + getFirstRechargeOfferView({ + isFirstRecharge: true, + subscriptionType: "topup", + vipPlans: vipViews, + coinPlans: coinViews, + }), + ).toMatchObject({ + badgeText: "50% OFF", + title: "First Recharge Offer", + renewalNotice: null, }); expect( getFirstRechargeOfferView({ isFirstRecharge: false, + subscriptionType: "vip", vipPlans: vipViews, coinPlans: coinViews, }), diff --git a/src/app/subscription/components/subscription-screen.module.css b/src/app/subscription/components/subscription-screen.module.css index 9cba4d51..c4828288 100644 --- a/src/app/subscription/components/subscription-screen.module.css +++ b/src/app/subscription/components/subscription-screen.module.css @@ -68,6 +68,14 @@ line-height: 1.25; } +.firstRechargeRenewalNotice { + margin: var(--responsive-inline-gap-xs, 4px) 0 0; + color: #a23869; + font-size: var(--responsive-micro, 12px); + font-weight: 800; + line-height: 1.3; +} + .offerStack { display: flex; flex-direction: column; diff --git a/src/app/subscription/subscription-screen.helpers.ts b/src/app/subscription/subscription-screen.helpers.ts index a770a8cd..8f546c00 100644 --- a/src/app/subscription/subscription-screen.helpers.ts +++ b/src/app/subscription/subscription-screen.helpers.ts @@ -12,6 +12,7 @@ export interface FirstRechargeOfferView { badgeText: string; title: string; subtitle: string; + renewalNotice: string | null; } export function canChooseSubscriptionPayChannel( @@ -159,6 +160,7 @@ export function getDefaultSubscriptionPlanId(input: { export function getFirstRechargeOfferView(input: { isFirstRecharge: boolean; + subscriptionType: SubscriptionType; vipPlans: readonly VipOfferPlanView[]; coinPlans: readonly CoinsOfferPlanView[]; }): FirstRechargeOfferView | null { @@ -176,5 +178,9 @@ export function getFirstRechargeOfferView(input: { title: "First Recharge Offer", subtitle: "Your first recharge price is already applied. No code needed.", + renewalNotice: + input.subscriptionType === "vip" + ? "First month 50% off. Renews at the regular price from the second month." + : null, }; } diff --git a/src/app/subscription/subscription-screen.tsx b/src/app/subscription/subscription-screen.tsx index 82ab8e92..b46aa2cf 100644 --- a/src/app/subscription/subscription-screen.tsx +++ b/src/app/subscription/subscription-screen.tsx @@ -99,10 +99,16 @@ export function SubscriptionScreen({ () => getFirstRechargeOfferView({ isFirstRecharge: payment.isFirstRecharge, + subscriptionType, vipPlans: vipOfferPlans, coinPlans: directCoinsPlans, }), - [directCoinsPlans, payment.isFirstRecharge, vipOfferPlans], + [ + directCoinsPlans, + payment.isFirstRecharge, + subscriptionType, + vipOfferPlans, + ], ); const selectedPlan = findSelectedSubscriptionPlan({ @@ -207,6 +213,11 @@ export function SubscriptionScreen({

{firstRechargeOffer.subtitle}

+ {firstRechargeOffer.renewalNotice ? ( +

+ {firstRechargeOffer.renewalNotice} +

+ ) : null} ) : null}