feat(subscription): add VIP renewal price notice

This commit is contained in:
2026-07-15 11:50:44 +08:00
parent a7c1574e58
commit 3f3d1a0203
4 changed files with 42 additions and 1 deletions
@@ -200,16 +200,32 @@ describe("subscription screen helpers", () => {
expect( expect(
getFirstRechargeOfferView({ getFirstRechargeOfferView({
isFirstRecharge: true, isFirstRecharge: true,
subscriptionType: "vip",
vipPlans: vipViews, vipPlans: vipViews,
coinPlans: coinViews, coinPlans: coinViews,
}), }),
).toMatchObject({ ).toMatchObject({
badgeText: "50% OFF", badgeText: "50% OFF",
title: "First Recharge Offer", 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( expect(
getFirstRechargeOfferView({ getFirstRechargeOfferView({
isFirstRecharge: false, isFirstRecharge: false,
subscriptionType: "vip",
vipPlans: vipViews, vipPlans: vipViews,
coinPlans: coinViews, coinPlans: coinViews,
}), }),
@@ -68,6 +68,14 @@
line-height: 1.25; 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 { .offerStack {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -12,6 +12,7 @@ export interface FirstRechargeOfferView {
badgeText: string; badgeText: string;
title: string; title: string;
subtitle: string; subtitle: string;
renewalNotice: string | null;
} }
export function canChooseSubscriptionPayChannel( export function canChooseSubscriptionPayChannel(
@@ -159,6 +160,7 @@ export function getDefaultSubscriptionPlanId(input: {
export function getFirstRechargeOfferView(input: { export function getFirstRechargeOfferView(input: {
isFirstRecharge: boolean; isFirstRecharge: boolean;
subscriptionType: SubscriptionType;
vipPlans: readonly VipOfferPlanView[]; vipPlans: readonly VipOfferPlanView[];
coinPlans: readonly CoinsOfferPlanView[]; coinPlans: readonly CoinsOfferPlanView[];
}): FirstRechargeOfferView | null { }): FirstRechargeOfferView | null {
@@ -176,5 +178,9 @@ export function getFirstRechargeOfferView(input: {
title: "First Recharge Offer", title: "First Recharge Offer",
subtitle: subtitle:
"Your first recharge price is already applied. No code needed.", "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,
}; };
} }
+12 -1
View File
@@ -99,10 +99,16 @@ export function SubscriptionScreen({
() => () =>
getFirstRechargeOfferView({ getFirstRechargeOfferView({
isFirstRecharge: payment.isFirstRecharge, isFirstRecharge: payment.isFirstRecharge,
subscriptionType,
vipPlans: vipOfferPlans, vipPlans: vipOfferPlans,
coinPlans: directCoinsPlans, coinPlans: directCoinsPlans,
}), }),
[directCoinsPlans, payment.isFirstRecharge, vipOfferPlans], [
directCoinsPlans,
payment.isFirstRecharge,
subscriptionType,
vipOfferPlans,
],
); );
const selectedPlan = findSelectedSubscriptionPlan({ const selectedPlan = findSelectedSubscriptionPlan({
@@ -207,6 +213,11 @@ export function SubscriptionScreen({
<p className={styles.firstRechargeSubtitle}> <p className={styles.firstRechargeSubtitle}>
{firstRechargeOffer.subtitle} {firstRechargeOffer.subtitle}
</p> </p>
{firstRechargeOffer.renewalNotice ? (
<p className={styles.firstRechargeRenewalNotice}>
{firstRechargeOffer.renewalNotice}
</p>
) : null}
</div> </div>
</section> </section>
) : null} ) : null}