diff --git a/messages/en.json b/messages/en.json index 6d36c79..df82b0a 100644 --- a/messages/en.json +++ b/messages/en.json @@ -634,7 +634,7 @@ "types": { "MONTHLY_REFRESH": "Monthly Refresh", "REGISTER_GIFT": "Register Gift", - "PURCHASE_PACKAGE": "Purchased Credits", + "PURCHASE": "Purchased Credits", "USAGE": "Consumed Credits", "EXPIRE": "Expired Credits", "SUBSCRIPTION_RENEWAL": "Subscription Renewal", diff --git a/messages/zh.json b/messages/zh.json index 674010e..1e4ba2a 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -635,7 +635,7 @@ "types": { "MONTHLY_REFRESH": "每月赠送", "REGISTER_GIFT": "注册赠送", - "PURCHASE_PACKAGE": "购买积分", + "PURCHASE": "购买积分", "USAGE": "使用积分", "EXPIRE": "过期积分", "SUBSCRIPTION_RENEWAL": "订阅月度积分", diff --git a/src/actions/get-credit-stats.ts b/src/actions/get-credit-stats.ts index 2c6f652..e5e2106 100644 --- a/src/actions/get-credit-stats.ts +++ b/src/actions/get-credit-stats.ts @@ -8,6 +8,9 @@ import { addDays } from 'date-fns'; import { and, eq, gte, isNotNull, lte, sql, sum } from 'drizzle-orm'; import { createSafeActionClient } from 'next-safe-action'; +const CREDITS_EXPIRATION_DAYS = 30; +const CREDITS_MONTHLY_DAYS = 30; + // Create a safe action client const actionClient = createSafeActionClient(); @@ -28,7 +31,7 @@ export const getCreditStatsAction = actionClient.action(async () => { const userId = session.user.id; // Get credits expiring in the next 30 days - const thirtyDaysFromNow = addDays(new Date(), 30); + const thirtyDaysFromNow = addDays(new Date(), CREDITS_EXPIRATION_DAYS); const expiringCredits = await db .select({ amount: sum(creditTransaction.remainingAmount), @@ -47,7 +50,7 @@ export const getCreditStatsAction = actionClient.action(async () => { ); // Get credits from subscription renewals (recent 30 days) - const thirtyDaysAgo = addDays(new Date(), -30); + const thirtyDaysAgo = addDays(new Date(), -CREDITS_MONTHLY_DAYS); const subscriptionCredits = await db .select({ amount: sum(creditTransaction.amount), diff --git a/src/components/settings/billing/billing-card.tsx b/src/components/settings/billing/billing-card.tsx index 51827b3..253fefb 100644 --- a/src/components/settings/billing/billing-card.tsx +++ b/src/components/settings/billing/billing-card.tsx @@ -92,7 +92,9 @@ export default function BillingCard() { return ( - {t('currentPlan.title')} + + {t('currentPlan.title')} + {t('currentPlan.description')} @@ -103,7 +105,7 @@ export default function BillingCard() { - + ); @@ -114,7 +116,9 @@ export default function BillingCard() { return ( - {t('currentPlan.title')} + + {t('currentPlan.title')} + {t('currentPlan.description')} @@ -139,7 +143,9 @@ export default function BillingCard() { return ( - {t('currentPlan.title')} + + {t('currentPlan.title')} + {t('currentPlan.description')} diff --git a/src/components/settings/billing/credits-balance-card.tsx b/src/components/settings/billing/credits-balance-card.tsx index a5e9f1b..504b1ee 100644 --- a/src/components/settings/billing/credits-balance-card.tsx +++ b/src/components/settings/billing/credits-balance-card.tsx @@ -18,6 +18,7 @@ import { LocaleLink, useLocaleRouter } from '@/i18n/navigation'; import { formatDate } from '@/lib/formatter'; import { cn } from '@/lib/utils'; import { Routes } from '@/routes'; +import { Loader2Icon } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { useSearchParams } from 'next/navigation'; import { useEffect, useRef, useState } from 'react'; @@ -30,7 +31,12 @@ export default function CreditsBalanceCard() { const hasHandledSession = useRef(false); // Use the credits hook to get balance - const { balance, isLoading, error, refresh } = useCredits(); + const { + balance, + isLoading: isLoadingBalance, + error, + refresh: refreshBalance, + } = useCredits(); // Get payment info to check plan type const { currentPlan } = usePayment(); @@ -44,7 +50,7 @@ export default function CreditsBalanceCard() { subscriptionCredits: { amount: number }; lifetimeCredits: { amount: number }; } | null>(null); - const [statsLoading, setStatsLoading] = useState(false); + const [isLoadingStats, setIsLoadingStats] = useState(true); // Don't render if credits are disabled if (!websiteConfig.credits.enableCredits) { @@ -53,7 +59,7 @@ export default function CreditsBalanceCard() { // Function to fetch credit statistics const fetchCreditStats = async () => { - setStatsLoading(true); + setIsLoadingStats(true); try { const result = await getCreditStatsAction(); if (result?.data?.success && result.data.data) { @@ -64,7 +70,7 @@ export default function CreditsBalanceCard() { } catch (error) { console.error('Failed to fetch credit stats:', error); } finally { - setStatsLoading(false); + setIsLoadingStats(false); } }; @@ -84,7 +90,7 @@ export default function CreditsBalanceCard() { }, 0); // Refresh credits data to show updated balance - refresh(); + refreshBalance(); // Refresh credit stats fetchCreditStats(); @@ -93,24 +99,28 @@ export default function CreditsBalanceCard() { url.searchParams.delete('session_id'); localeRouter.replace(Routes.SettingsBilling + url.search); } - }, [searchParams, localeRouter, refresh]); + }, [searchParams, localeRouter, refreshBalance, fetchCreditStats]); // Render loading skeleton - if (isLoading) { + const isPageLoading = isLoadingBalance || isLoadingStats; + if (isPageLoading) { return ( - {t('title')} + {t('title')} {t('description')} -
- - +
+ +
+
+ +
- + ); @@ -121,7 +131,7 @@ export default function CreditsBalanceCard() { return ( - {t('title')} + {t('title')} {t('description')} @@ -159,7 +169,7 @@ export default function CreditsBalanceCard() { {/* Balance information */}
{/* Plan-based credits info */} - {!statsLoading && creditStats && ( + {!isLoadingStats && creditStats && ( <> {/* Subscription credits (for paid plans) */} {!currentPlan?.isFree &&