refactor: update billing card logic and localization

- Renamed variables for clarity, changing `isLifetimeMember` to `isLifetimePlan` and `canUpgrade` logic to improve readability.
- Added localization support for the price label in both English and Chinese.
- Optimized the UI to conditionally display messages for free and lifetime plans, enhancing user experience.
This commit is contained in:
javayhu 2025-04-06 23:08:53 +08:00
parent 9b0d354683
commit be82337924
4 changed files with 64 additions and 46 deletions

View File

@ -440,13 +440,13 @@
"loading": "Loading...",
"createCustomerPortalFailed": "Failed to open Stripe customer portal"
},
"price": "Price:",
"nextBillingDate": "Next billing date:",
"trialEnds": "Trial ends:",
"freePlanMessage": "You are currently on the free plan with limited features",
"lifetimeMessage": "You have lifetime access to all premium features",
"manageSubscription": "Manage Subscription",
"billingHistory": "Billing History",
"manageBilling": "Manage Subscription and Billing",
"manageSubscription": "Manage Subscription and Billing",
"manageBilling": "Manage Billing",
"upgradePlan": "Upgrade Plan",
"retry": "Retry",
"errorMessage": "Failed to get data"

View File

@ -440,13 +440,13 @@
"loading": "加载中...",
"createCustomerPortalFailed": "打开Stripe客户界面失败"
},
"price": "价格:",
"nextBillingDate": "下次账单日期:",
"trialEnds": "试用结束日期:",
"freePlanMessage": "您当前使用的是功能有限的免费方案",
"lifetimeMessage": "您拥有所有高级功能的终身使用权限",
"manageSubscription": "管理订阅",
"billingHistory": "账单历史",
"manageBilling": "管理订阅和账单",
"manageSubscription": "管理订阅和账单",
"manageBilling": "管理账单",
"retry": "重试",
"errorMessage": "获取数据失败"
},

View File

@ -1,9 +1,10 @@
'use client';
import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { useCopyToClipboard } from '@/hooks/use-clipboard';
import { isUrlCached } from '@/lib/serviceWorker';
import { cn, titleToNumber } from '@/lib/utils';
import { cn } from '@/lib/utils';
import * as RadioGroup from '@radix-ui/react-radio-group';
import { Check, Code2, Copy, Eye, Maximize, Terminal } from 'lucide-react';
import Link from 'next/link';
@ -16,7 +17,6 @@ import {
type ImperativePanelGroupHandle,
} from 'react-resizable-panels';
import { useMedia } from 'use-media';
import { Button } from '@/components/ui/button';
export interface BlockPreviewProps {
code?: string;
@ -36,6 +36,11 @@ const LGSIZE = 82;
const getCacheKey = (src: string) => `iframe-cache-${src}`;
const titleToNumber = (title: string): number => {
const titles = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"];
return titles.indexOf(title.toLowerCase()) + 1;
};
export const BlockPreview: React.FC<BlockPreviewProps> = ({
code,
preview,

View File

@ -27,7 +27,7 @@ export default function BillingCard() {
console.log('billing card, currentUser:', currentUser);
// Check if user is a lifetime member
const isLifetimeMember = currentUser?.lifetimeMember === true;
const isLifetimePlan = currentUser?.lifetimeMember === true;
const hasCustomerId = Boolean(currentUser?.customerId);
// Get all available plans
@ -35,7 +35,7 @@ export default function BillingCard() {
console.log('billing card, plans:', plans);
// Determine current plan based on user status
const currentPlan = isLifetimeMember
const currentPlan = isLifetimePlan
? plans.find(plan => plan.isLifetime)
: subscription
? plans.find(plan => plan.id === subscription?.planId)
@ -43,7 +43,9 @@ export default function BillingCard() {
console.log('billing card, currentPlan:', currentPlan);
// Show upgrade button if user is on free plan
const canUpgrade = currentPlan?.isFree;
// OPTIMIZE: should we show upgrade button if user has a subscription?
const isFreePlan = currentPlan?.isFree;
const canUpgrade = !isFreePlan;
console.log('billing card, canUpgrade:', canUpgrade);
// Get subscription price details
@ -60,9 +62,9 @@ export default function BillingCard() {
// Fetch customer subscription data
const fetchSubscription = async () => {
console.log('fetchSubscription, isLifetimeMember:', isLifetimeMember, 'hasCustomerId:', hasCustomerId);
console.log('fetchSubscription, isLifetimeMember:', isLifetimePlan, 'hasCustomerId:', hasCustomerId);
// Skip fetching if user is a lifetime member
if (isLifetimeMember) return;
if (isLifetimePlan) return;
// Skip fetching if user doesn't have a customer ID
if (!hasCustomerId) return;
@ -155,12 +157,13 @@ export default function BillingCard() {
<CardDescription>{t('currentPlan.description')}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{/* Plan name and status */}
<div className="flex items-center justify-between">
<div className="text-3xl font-medium">
{currentPlan?.name}
</div>
<Badge variant={currentPlan?.isFree || isLifetimeMember ? 'outline' : 'default'}>
{isLifetimeMember
<Badge variant={currentPlan?.isFree || isLifetimePlan ? 'outline' : 'default'}>
{isLifetimePlan
? t('status.lifetime')
: subscription?.status === 'active'
? t('status.active')
@ -170,11 +173,25 @@ export default function BillingCard() {
</Badge>
</div>
{/* Subscription plan details */}
{/* Free plan message */}
{isFreePlan && (
<div className="text-sm text-muted-foreground">
{t('freePlanMessage')}
</div>
)}
{/* Lifetime plan message */}
{isLifetimePlan && (
<div className="text-sm text-muted-foreground">
{t('lifetimeMessage')}
</div>
)}
{/* Subscription plan message */}
{subscription && currentPrice && (
<div className="text-sm text-muted-foreground space-y-1">
<div className="text-sm text-muted-foreground space-y-2">
<div>
{formatPrice(currentPrice.amount, currentPrice.currency)} / {currentPrice.interval === PlanIntervals.MONTH ?
{t('price')} {formatPrice(currentPrice.amount, currentPrice.currency)} / {currentPrice.interval === PlanIntervals.MONTH ?
t('interval.month') :
currentPrice.interval === PlanIntervals.YEAR ?
t('interval.year') :
@ -192,37 +209,13 @@ export default function BillingCard() {
)}
</div>
)}
{/* Free plan message */}
{currentPlan?.isFree && (
<div className="text-sm text-muted-foreground">
{t('freePlanMessage')}
</div>
)}
{/* Lifetime access message */}
{isLifetimeMember && (
<div className="text-sm text-muted-foreground">
{t('lifetimeMessage')}
</div>
)}
</CardContent>
<CardFooter>
<div className="grid w-full gap-3">
{/* Manage subscription button - only shown if user has a customer ID */}
{hasCustomerId && currentUser?.customerId && (
<CustomerPortalButton
customerId={currentUser.customerId}
className="w-full"
>
{t('manageBilling')}
</CustomerPortalButton>
)}
{/* View pricing plans button - only shown if user can upgrade */}
{canUpgrade && (
<div className="w-full gap-4 flex flex-col">
{/* Show upgrade plan button - only shown if user can upgrade */}
{isFreePlan && (
<Button
variant={subscription ? "outline" : "default"}
variant="default"
className="w-full cursor-pointer"
asChild
>
@ -231,6 +224,26 @@ export default function BillingCard() {
</LocaleLink>
</Button>
)}
{/* Manage billing button - only shown if user is lifetime member */}
{isLifetimePlan && currentUser?.customerId && (
<CustomerPortalButton
customerId={currentUser.customerId}
className="w-full"
>
{t('manageBilling')}
</CustomerPortalButton>
)}
{/* Manage subscription button - only shown if user has subscription */}
{subscription && currentUser?.customerId && (
<CustomerPortalButton
customerId={currentUser.customerId}
className="w-full"
>
{t('manageSubscription')}
</CustomerPortalButton>
)}
</div>
</CardFooter>
</Card>