fix(core): show past due in ui (#13477)

fix CLOUD-238, CLOUD-239

#### PR Dependency Tree


* **PR #13477** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Payment method management is now always available directly within AI
and Pro plan cards.
* **Bug Fixes**
* Past-due subscriptions are now included in subscription status
results, ensuring they appear in billing views.
* **Style**
* Plan actions are moved inline within each plan’s description for a
cleaner, more compact layout.
  * Actions are grouped horizontally with improved spacing.
  * Minor class name and spacing tweaks for consistent styling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Peng Xiao
2025-08-12 17:00:32 +08:00
committed by GitHub
parent 678dc15365
commit fda7e9008d
4 changed files with 53 additions and 44 deletions

View File

@@ -461,7 +461,11 @@ export class UserSubscriptionResolver {
where: {
targetId: user.id,
status: {
in: [SubscriptionStatus.Active, SubscriptionStatus.Trialing],
in: [
SubscriptionStatus.Active,
SubscriptionStatus.Trialing,
SubscriptionStatus.PastDue,
],
},
},
});

View File

@@ -76,30 +76,36 @@ export const AIPlanCard = ({ onClick }: { onClick: () => void }) => {
status={subscription?.status}
/>
}
desc={billingTip}
desc={
<>
{billingTip}
<div className={styles.planActionContainer}>
{price?.yearlyAmount ? (
subscription ? (
isOnetime ? (
<AIRedeemCodeButton className={styles.planAction} />
) : subscription.canceledAt ? (
<AIResume className={styles.planAction} />
) : (
<AICancel className={styles.planAction} />
)
) : (
<AISubscribe className={styles.planAction}>
{t[
'com.affine.payment.billing-setting.ai.start-free-trial'
]()}
</AISubscribe>
)
) : null}
<PaymentMethodUpdater
inCardView
className={styles.managementInCard}
variant="primary"
/>
</div>
</>
}
/>
{price?.yearlyAmount ? (
subscription ? (
isOnetime ? (
<AIRedeemCodeButton className={styles.planAction} />
) : subscription.canceledAt ? (
<AIResume className={styles.planAction} />
) : (
<AICancel className={styles.planAction} />
)
) : (
<AISubscribe className={styles.planAction}>
{t['com.affine.payment.billing-setting.ai.start-free-trial']()}
</AISubscribe>
)
) : null}
{subscription?.status === SubscriptionStatus.PastDue ? (
<PaymentMethodUpdater
inCardView
className={styles.manageMentInCard}
variant="primary"
/>
) : null}
</div>
<p className={styles.planPrice}>
{subscription ? priceReadable : '$0'}

View File

@@ -89,14 +89,14 @@ export const ProPlanCard = ({
}}
/>
<CloudExpirationInfo />
<PlanAction
plan={currentPlan}
subscriptionStatus={proSubscription?.status}
gotoPlansSetting={gotoCloudPlansSetting}
/>
</>
}
/>
<PlanAction
plan={currentPlan}
subscriptionStatus={proSubscription?.status}
gotoPlansSetting={gotoCloudPlansSetting}
/>
</div>
<p className={styles.planPrice}>
${amount}
@@ -148,7 +148,6 @@ const CloudExpirationInfo = () => {
const PlanAction = ({
plan,
subscriptionStatus,
gotoPlansSetting,
}: {
plan: string;
@@ -165,7 +164,7 @@ const PlanAction = ({
}
return (
<>
<div className={styles.planActionContainer}>
<Button
className={styles.planAction}
variant="primary"
@@ -175,13 +174,11 @@ const PlanAction = ({
? t['com.affine.payment.billing-setting.change-plan']()
: t['com.affine.payment.billing-setting.upgrade']()}
</Button>
{subscriptionStatus === SubscriptionStatus.PastDue ? (
<PaymentMethodUpdater
inCardView
className={styles.manageMentInCard}
variant="primary"
/>
) : null}
</>
<PaymentMethodUpdater
inCardView
className={styles.managementInCard}
variant="primary"
/>
</div>
);
};

View File

@@ -23,7 +23,6 @@ export const currentPlan = style({
});
export const planAction = style({
width: 'auto !important',
marginTop: '8px',
});
export const planPrice = style({
fontSize: cssVar('fontH6'),
@@ -35,6 +34,12 @@ export const billingFrequency = style({
export const paymentMethod = style({
marginTop: '24px',
});
export const planActionContainer = style({
display: 'flex',
gap: '12px',
marginTop: '12px',
});
globalStyle('.dangerous-setting .name', {
color: cssVarV2('status/error'),
});
@@ -129,7 +134,4 @@ export const cardLabelIcon = style({
width: '14px',
height: '14px',
});
export const manageMentInCard = style({
marginTop: '8px',
marginLeft: '12px',
});
export const managementInCard = style({});