"use client"; import { useState, useEffect, useMemo } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { Check, ArrowLeft } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { fetchSubscriptionPlans, SubscriptionPlan } from "@/lib/stripe"; import { DashboardLayout } from "@/components/layout/dashboard-layout"; export default function PricingPage() { return ( {/* Main Content */} ); } /**价格方案 */ function HomeModule5() { const [billingType, setBillingType] = useState<"month" | "year">("month"); const [plans, setPlans] = useState([]); const [loadingPlan, setLoadingPlan] = useState(null); // 跟踪哪个计划正在加载 // 监听支付完成消息 useEffect(() => { const handlePaymentResult = (ev: MessageEvent) => { if (ev.data.type === 'payment-result') { // 支付完成后清除loading状态 setLoadingPlan(null); } }; window.addEventListener('message', handlePaymentResult); return () => window.removeEventListener('message', handlePaymentResult); }, []); // 从后端获取订阅计划数据 useEffect(() => { const loadPlans = async () => { try { const plansData = await fetchSubscriptionPlans(); setPlans(plansData); } catch (err) { console.error("加载订阅计划失败:", err); } }; loadPlans(); }, []); const pricingPlans = useMemo< { title: string; price: number; credits: string; buttonText: string; features: string[]; issubscribed: boolean; }[] >(() => { return plans.map((plan) => { return { title: plan.display_name || plan.name, price: billingType === "month" ? plan.price_month / 100 : plan.price_year / 100, credits: plan.description, buttonText: plan.is_free ? "Try For Free" : "Subscribe Now", issubscribed: plan.is_subscribed, features: plan.features || [], }; }); }, [plans, billingType]); const handleSubscribe = async (planName: string) => { setLoadingPlan(planName); // 设置加载状态 try { const { createCheckoutSession, redirectToCheckout } = await import( "@/lib/stripe" ); // 从localStorage获取当前用户信息 const User = JSON.parse(localStorage.getItem("currentUser") || "{}"); if (!User.id) { throw new Error("Unable to obtain user ID, please log in again"); } // 1. 创建Checkout Session const result = await createCheckoutSession({ user_id: String(User.id), plan_name: planName, billing_cycle: billingType, }); if (!result.successful || !result.data) { throw new Error("create checkout session failed"); } // 通知当前窗口等待支付(显示loading模态框) window.postMessage({ type: "waiting-payment", paymentType: "subscription" }, "*"); // 在新标签页中打开Stripe支付页面,保持当前定价页面不变 window.open(result.data.checkout_url, '_blank'); } catch (error) { setLoadingPlan(null); // 出错时清除加载状态 throw new Error("create checkout session failed, please try again later"); } }; return ( Pick a plan and make it yours {/* 计费切换 */} setBillingType("month")} className={`box-border flex justify-center items-center rounded-full transition-all duration-300 ${ billingType === "month" ? "bg-white text-black" : "text-white hover:text-gray-300" } w-[4.5rem] text-sm sm:w-[5rem] sm:text-sm md:w-[6rem] md:text-base lg:w-[6rem] lg:text-base xl:w-[6rem] xl:text-base 2xl:w-[6rem] 2xl:text-base`} > Monthly setBillingType("year")} className={`box-border flex justify-center items-center rounded-full transition-all duration-300 ${ billingType === "year" ? "bg-white text-black" : "text-white hover:text-gray-300" } w-[5.5rem] text-sm sm:w-[6rem] sm:text-sm md:w-[7.125rem] md:text-base lg:w-[7.125rem] lg:text-base xl:w-[7.125rem] xl:text-base 2xl:w-[7.125rem] 2xl:text-base`} > Yearly - 20% {/* 主要价格卡片 */} {pricingPlans.map((plan, index) => ( {plan.title} ${plan.price} / {billingType === "month" ? "month" : "year"} {plan.credits} {plan.issubscribed ? ( Already Owned ) : ( handleSubscribe(plan.title)} className="w-full bg-white text-black rounded-full hover:bg-black hover:text-white transition-colors border border-white/20 py-2 mb-4 text-sm sm:py-3 sm:mb-4 sm:text-base md:py-[0.75rem] md:mb-[1rem] md:text-base lg:py-[0.75rem] lg:mb-[1rem] lg:text-base xl:py-[0.75rem] xl:mb-[1rem] xl:text-base 2xl:py-[0.875rem] 2xl:mb-[1.25rem] 2xl:text-lg" > {plan.buttonText} )} * Billed monthly until cancelled {plan.features.map((feature, featureIndex) => ( ✓ {feature} ))} ))} ); }
{plan.credits}
* Billed monthly until cancelled