feat: implement credit packages management with translations and server integration
- Added new credit packages structure in English and Chinese JSON files for better localization. - Introduced server-side functions to retrieve credit packages and package details. - Updated client-side components to utilize new credit package retrieval methods. - Refactored existing code to enhance modularity and maintainability by separating client and server logic. - Removed obsolete credit package retrieval functions to streamline the codebase.
This commit is contained in:
parent
f7f7be2ef0
commit
e430a0c319
@ -111,6 +111,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreditPackages": {
|
||||
"basic": {
|
||||
"name": "Basic",
|
||||
"description": "Basic features for personal use"
|
||||
},
|
||||
"standard": {
|
||||
"name": "Standard",
|
||||
"description": "Standard features for personal use"
|
||||
},
|
||||
"premium": {
|
||||
"name": "Premium",
|
||||
"description": "Premium features for personal use"
|
||||
},
|
||||
"enterprise": {
|
||||
"name": "Enterprise",
|
||||
"description": "Enterprise features for personal use"
|
||||
}
|
||||
},
|
||||
"NotFoundPage": {
|
||||
"title": "404",
|
||||
"message": "Sorry, the page you are looking for does not exist.",
|
||||
@ -498,8 +516,8 @@
|
||||
"error": "Failed to unban user"
|
||||
},
|
||||
"close": "Close"
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"profile": {
|
||||
|
@ -112,6 +112,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreditPackages": {
|
||||
"basic": {
|
||||
"name": "基础版",
|
||||
"description": "基础版功能介绍放这里"
|
||||
},
|
||||
"standard": {
|
||||
"name": "标准版",
|
||||
"description": "标准版功能介绍放这里"
|
||||
},
|
||||
"premium": {
|
||||
"name": "高级版",
|
||||
"description": "高级版功能介绍放这里"
|
||||
},
|
||||
"enterprise": {
|
||||
"name": "企业版",
|
||||
"description": "企业版功能介绍放这里"
|
||||
}
|
||||
},
|
||||
"NotFoundPage": {
|
||||
"title": "404",
|
||||
"message": "抱歉,您正在寻找的页面不存在",
|
||||
|
@ -1,12 +1,12 @@
|
||||
'use server';
|
||||
|
||||
import { getCreditPackageById } from '@/credits';
|
||||
import {
|
||||
addMonthlyFreeCredits,
|
||||
addRegisterGiftCredits,
|
||||
consumeCredits,
|
||||
getUserCredits,
|
||||
} from '@/credits/credits';
|
||||
import { getCreditPackageByIdInServer } from '@/credits/server';
|
||||
import { getSession } from '@/lib/server';
|
||||
import { confirmPaymentIntent, createPaymentIntent } from '@/payment';
|
||||
import { createSafeActionClient } from 'next-safe-action';
|
||||
@ -77,7 +77,7 @@ export const createCreditPaymentIntent = actionClient
|
||||
const { packageId } = parsedInput;
|
||||
|
||||
// Find the credit package
|
||||
const creditPackage = getCreditPackageById(packageId);
|
||||
const creditPackage = getCreditPackageByIdInServer(packageId);
|
||||
if (!creditPackage) {
|
||||
return { success: false, error: 'Invalid credit package' };
|
||||
}
|
||||
@ -127,7 +127,7 @@ export const confirmCreditPayment = actionClient
|
||||
const { packageId, paymentIntentId } = parsedInput;
|
||||
|
||||
// Find the credit package
|
||||
const creditPackage = getCreditPackageById(packageId);
|
||||
const creditPackage = getCreditPackageByIdInServer(packageId);
|
||||
if (!creditPackage) {
|
||||
return { success: false, error: 'Invalid credit package' };
|
||||
}
|
||||
|
@ -19,7 +19,11 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { getCreditPackageById, getCreditPackages } from '@/credits';
|
||||
import {
|
||||
getCreditPackageByIdInClient,
|
||||
getCreditPackagesInClient,
|
||||
} from '@/credits/client';
|
||||
import type { CreditPackage } from '@/credits/types';
|
||||
import { formatPrice } from '@/lib/formatter';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useTransactionStore } from '@/stores/transaction-store';
|
||||
@ -117,6 +121,10 @@ export function CreditPackages() {
|
||||
});
|
||||
};
|
||||
|
||||
const getPackageInfo = (packageId: string): CreditPackage | undefined => {
|
||||
return getCreditPackageByIdInClient(packageId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Card className="w-full">
|
||||
@ -150,7 +158,7 @@ export function CreditPackages() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
||||
{getCreditPackages().map((pkg) => (
|
||||
{getCreditPackagesInClient().map((pkg) => (
|
||||
<Card
|
||||
key={pkg.id}
|
||||
className={cn(
|
||||
@ -224,15 +232,17 @@ export function CreditPackages() {
|
||||
<DialogTitle>{t('completePurchase')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
{paymentDialog.clientSecret && paymentDialog.packageId && (
|
||||
<StripePaymentForm
|
||||
clientSecret={paymentDialog.clientSecret}
|
||||
packageId={paymentDialog.packageId}
|
||||
packageInfo={getCreditPackageById(paymentDialog.packageId)!}
|
||||
onPaymentSuccess={handlePaymentSuccess}
|
||||
onPaymentCancel={handlePaymentCancel}
|
||||
/>
|
||||
)}
|
||||
{paymentDialog.clientSecret &&
|
||||
paymentDialog.packageId &&
|
||||
getPackageInfo(paymentDialog.packageId) && (
|
||||
<StripePaymentForm
|
||||
clientSecret={paymentDialog.clientSecret}
|
||||
packageId={paymentDialog.packageId}
|
||||
packageInfo={getPackageInfo(paymentDialog.packageId)!}
|
||||
onPaymentSuccess={handlePaymentSuccess}
|
||||
onPaymentCancel={handlePaymentCancel}
|
||||
/>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
@ -77,9 +77,9 @@ function PaymentForm({
|
||||
onPaymentSuccess,
|
||||
onPaymentCancel,
|
||||
}: PaymentFormProps) {
|
||||
const t = useTranslations('Dashboard.settings.credits.packages');
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
const t = useTranslations('Dashboard.settings.credits.packages');
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const { triggerRefresh } = useTransactionStore();
|
||||
|
||||
@ -103,31 +103,31 @@ function PaymentForm({
|
||||
if (error) {
|
||||
console.error('PaymentForm, payment error:', error);
|
||||
throw new Error(error.message || 'Payment failed');
|
||||
} else {
|
||||
// The payment was successful
|
||||
const paymentIntent = await stripe.retrievePaymentIntent(clientSecret);
|
||||
if (paymentIntent.paymentIntent) {
|
||||
const result = await confirmCreditPayment({
|
||||
packageId,
|
||||
paymentIntentId: paymentIntent.paymentIntent.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (result?.data?.success) {
|
||||
console.log('PaymentForm, payment success');
|
||||
// Trigger refresh for transaction-dependent UI components
|
||||
triggerRefresh();
|
||||
// The payment was successful
|
||||
const paymentIntent = await stripe.retrievePaymentIntent(clientSecret);
|
||||
if (paymentIntent.paymentIntent) {
|
||||
const result = await confirmCreditPayment({
|
||||
packageId,
|
||||
paymentIntentId: paymentIntent.paymentIntent.id,
|
||||
});
|
||||
|
||||
// Show success toast
|
||||
onPaymentSuccess();
|
||||
// toast.success(`${packageInfo.credits} credits have been added to your account.`);
|
||||
} else {
|
||||
console.error('PaymentForm, payment error:', result?.data?.error);
|
||||
throw new Error(result?.data?.error || 'Failed to confirm payment');
|
||||
}
|
||||
if (result?.data?.success) {
|
||||
console.log('PaymentForm, payment success');
|
||||
// Trigger refresh for transaction-dependent UI components
|
||||
triggerRefresh();
|
||||
|
||||
// Show success toast
|
||||
onPaymentSuccess();
|
||||
// toast.success(`${packageInfo.credits} credits have been added to your account.`);
|
||||
} else {
|
||||
console.error('PaymentForm, no payment intent found');
|
||||
throw new Error('No payment intent found');
|
||||
console.error('PaymentForm, payment error:', result?.data?.error);
|
||||
throw new Error(result?.data?.error || 'Failed to confirm payment');
|
||||
}
|
||||
} else {
|
||||
console.error('PaymentForm, no payment intent found');
|
||||
throw new Error('No payment intent found');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('PaymentForm, payment error:', error);
|
||||
|
58
src/config/credits-config.tsx
Normal file
58
src/config/credits-config.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
'use client';
|
||||
|
||||
import type { CreditPackage } from '@/credits/types';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { websiteConfig } from './website';
|
||||
|
||||
/**
|
||||
* Get credit packages with translations for client components
|
||||
*
|
||||
* NOTICE: This function should only be used in client components.
|
||||
* If you need to get the credit packages in server components, use getAllCreditPackages instead.
|
||||
* Use this function when showing the credit packages to the user.
|
||||
*
|
||||
* docs:
|
||||
* https://mksaas.com/docs/config/credits
|
||||
*
|
||||
* @returns The credit packages with translated content
|
||||
*/
|
||||
export function getCreditPackages(): Record<string, CreditPackage> {
|
||||
const t = useTranslations('CreditPackages');
|
||||
const creditConfig = websiteConfig.credits;
|
||||
const packages: Record<string, CreditPackage> = {};
|
||||
|
||||
// Add translated content to each plan
|
||||
if (creditConfig.packages.basic) {
|
||||
packages.basic = {
|
||||
...creditConfig.packages.basic,
|
||||
name: t('basic.name'),
|
||||
description: t('basic.description'),
|
||||
};
|
||||
}
|
||||
|
||||
if (creditConfig.packages.standard) {
|
||||
packages.standard = {
|
||||
...creditConfig.packages.standard,
|
||||
name: t('standard.name'),
|
||||
description: t('standard.description'),
|
||||
};
|
||||
}
|
||||
|
||||
if (creditConfig.packages.premium) {
|
||||
packages.premium = {
|
||||
...creditConfig.packages.premium,
|
||||
name: t('premium.name'),
|
||||
description: t('premium.description'),
|
||||
};
|
||||
}
|
||||
|
||||
if (creditConfig.packages.enterprise) {
|
||||
packages.enterprise = {
|
||||
...creditConfig.packages.enterprise,
|
||||
name: t('enterprise.name'),
|
||||
description: t('enterprise.description'),
|
||||
};
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
21
src/credits/client.ts
Normal file
21
src/credits/client.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { getCreditPackages } from '@/config/credits-config';
|
||||
import type { CreditPackage } from './types';
|
||||
|
||||
/**
|
||||
* Get credit packages, used in client components
|
||||
* @returns Credit packages
|
||||
*/
|
||||
export function getCreditPackagesInClient(): CreditPackage[] {
|
||||
return Object.values(getCreditPackages());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get credit package by id, used in client components
|
||||
* @param id - Credit package id
|
||||
* @returns Credit package
|
||||
*/
|
||||
export function getCreditPackageByIdInClient(
|
||||
id: string
|
||||
): CreditPackage | undefined {
|
||||
return getCreditPackagesInClient().find((pkg) => pkg.id === id);
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { websiteConfig } from '@/config/website';
|
||||
|
||||
/**
|
||||
* Get credit packages
|
||||
* @returns Credit packages
|
||||
*/
|
||||
export function getCreditPackages() {
|
||||
return Object.values(websiteConfig.credits.packages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get credit package by id
|
||||
* @param id - Credit package id
|
||||
* @returns Credit package
|
||||
*/
|
||||
export function getCreditPackageById(id: string) {
|
||||
return getCreditPackages().find((pkg) => pkg.id === id);
|
||||
}
|
23
src/credits/server.ts
Normal file
23
src/credits/server.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { websiteConfig } from '@/config/website';
|
||||
import type { CreditPackage } from './types';
|
||||
|
||||
/**
|
||||
* Get all credit packages, used in server components
|
||||
* @returns Credit packages
|
||||
*/
|
||||
export function getAllCreditPackagesInServer(): CreditPackage[] {
|
||||
return Object.values(websiteConfig.credits.packages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get credit package by id, used in server components
|
||||
* @param id - Credit package id
|
||||
* @returns Credit package
|
||||
*/
|
||||
export function getCreditPackageByIdInServer(
|
||||
id: string
|
||||
): CreditPackage | undefined {
|
||||
return websiteConfig.credits.packages[
|
||||
id as keyof typeof websiteConfig.credits.packages
|
||||
];
|
||||
}
|
Loading…
Reference in New Issue
Block a user