forked from 77media/video-flow
修复很多问题
This commit is contained in:
parent
5b268abe5e
commit
a259d66f7c
@ -26,7 +26,6 @@ export default function RootLayout({
|
|||||||
}) {
|
}) {
|
||||||
const [showCallbackModal, setShowCallbackModal] = useState(false)
|
const [showCallbackModal, setShowCallbackModal] = useState(false)
|
||||||
const openCallback = async function (ev: MessageEvent<any>) {
|
const openCallback = async function (ev: MessageEvent<any>) {
|
||||||
console.log(ev)
|
|
||||||
if (ev.data.type === 'waiting-payment') {
|
if (ev.data.type === 'waiting-payment') {
|
||||||
setShowCallbackModal(true)
|
setShowCallbackModal(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,39 @@ import { fetchSubscriptionPlans, SubscriptionPlan } from "@/lib/stripe";
|
|||||||
|
|
||||||
export default function PricingPage() {
|
export default function PricingPage() {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 获取当前窗口尺寸
|
||||||
|
const currentWidth = window.innerWidth;
|
||||||
|
const currentHeight = window.innerHeight;
|
||||||
|
// 计算缩放比例 (1920x1080)
|
||||||
|
const wScale = currentWidth / 1920;
|
||||||
|
const hScale = currentHeight / 1080;
|
||||||
|
|
||||||
|
// 检查app节点是否存在
|
||||||
|
const pricingPage = document.getElementById("pricing-page");
|
||||||
|
if (!pricingPage) {
|
||||||
|
console.error("未找到app节点");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// setHPading((hScale || 1) * 10);
|
||||||
|
// 创建样式元素
|
||||||
|
const style = document.createElement("style");
|
||||||
|
|
||||||
|
// 设置CSS样式
|
||||||
|
style.textContent = `
|
||||||
|
#pricing-page {
|
||||||
|
transform-origin: top left;
|
||||||
|
transform: scale(${wScale}, ${hScale});
|
||||||
|
width: 1920px;
|
||||||
|
height: 1080px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 将样式添加到head
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full overflow-y-auto bg-black text-white pb-[10rem]">
|
<div className="w-full h-full overflow-y-auto bg-black text-white pb-[10rem]" id="pricing-page">
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<HomeModule5 />
|
<HomeModule5 />
|
||||||
</div>
|
</div>
|
||||||
@ -66,7 +97,7 @@ function HomeModule5() {
|
|||||||
}, [plans, billingType]);
|
}, [plans, billingType]);
|
||||||
|
|
||||||
const handleSubscribe = async (planName: string) => {
|
const handleSubscribe = async (planName: string) => {
|
||||||
if (planName === "hobby") {
|
if (planName === "Kickoff") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,14 +134,14 @@ function HomeModule5() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-alt="core-value-section"
|
data-alt="core-value-section"
|
||||||
className="home-module5 h-[1600px] relative flex flex-col items-center justify-center w-full bg-black snap-start"
|
className="home-module5 h-[1300px] relative flex flex-col items-center justify-center w-full bg-black snap-start"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
data-alt="core-value-content"
|
data-alt="core-value-content"
|
||||||
className="center z-10 flex flex-col items-center mb-[8rem]"
|
className="center z-10 flex flex-col items-center mb-[4rem]"
|
||||||
>
|
>
|
||||||
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal mb-[1.5rem]">
|
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal mb-[1.5rem]">
|
||||||
Start Creating
|
Pick a plan and make it yours
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{/* 计费切换 */}
|
{/* 计费切换 */}
|
||||||
@ -133,7 +164,7 @@ function HomeModule5() {
|
|||||||
: "text-white hover:text-gray-300"
|
: "text-white hover:text-gray-300"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Yearly - <span className="text-[#FFCC6D]">10%</span>
|
Yearly - <span className="text-[#FFCC6D]">20%</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -143,7 +174,7 @@ function HomeModule5() {
|
|||||||
{pricingPlans.map((plan, index) => (
|
{pricingPlans.map((plan, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className=" w-[26rem] h-[38.125rem] bg-black rounded-lg p-[1.5rem] border border-white/20"
|
className=" w-[24rem] h-[38.125rem] bg-black rounded-lg p-[1.5rem] border border-white/20"
|
||||||
>
|
>
|
||||||
<h3 className="text-white text-2xl font-normal mb-[1rem]">
|
<h3 className="text-white text-2xl font-normal mb-[1rem]">
|
||||||
{plan.title}
|
{plan.title}
|
||||||
|
|||||||
@ -714,7 +714,7 @@ export function ChatInputBox({ noData }: { noData: boolean }) {
|
|||||||
{!isExpanded && (
|
{!isExpanded && (
|
||||||
<div className="absolute top-2 right-2 z-10 flex items-center">
|
<div className="absolute top-2 right-2 z-10 flex items-center">
|
||||||
{/* 使用 Dropdown 替代手动控制显示/隐藏 */}
|
{/* 使用 Dropdown 替代手动控制显示/隐藏 */}
|
||||||
<Dropdown
|
{/* <Dropdown
|
||||||
open={showConfigOptions}
|
open={showConfigOptions}
|
||||||
onOpenChange={setShowConfigOptions}
|
onOpenChange={setShowConfigOptions}
|
||||||
popupRender={() => (
|
popupRender={() => (
|
||||||
@ -730,7 +730,6 @@ export function ChatInputBox({ noData }: { noData: boolean }) {
|
|||||||
placement={"left" as any}
|
placement={"left" as any}
|
||||||
trigger={["click"]}
|
trigger={["click"]}
|
||||||
>
|
>
|
||||||
{/* 配置项显示控制按钮 - 齿轮图标 */}
|
|
||||||
<Tooltip title="config" placement="top">
|
<Tooltip title="config" placement="top">
|
||||||
<button
|
<button
|
||||||
data-alt="config-toggle-button"
|
data-alt="config-toggle-button"
|
||||||
@ -739,7 +738,7 @@ export function ChatInputBox({ noData }: { noData: boolean }) {
|
|||||||
<Settings className="w-4 h-4 text-white/80" />
|
<Settings className="w-4 h-4 text-white/80" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Dropdown>
|
</Dropdown> */}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -771,7 +770,7 @@ export function ChatInputBox({ noData }: { noData: boolean }) {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
{/* 左侧功能按钮区域 */}
|
{/* 左侧功能按钮区域 */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* 获取创意按钮 */}
|
{/* 获取创意按钮
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title="Get creative ideas for your story"
|
title="Get creative ideas for your story"
|
||||||
placement="top"
|
placement="top"
|
||||||
@ -787,10 +786,10 @@ export function ChatInputBox({ noData }: { noData: boolean }) {
|
|||||||
<Lightbulb className="w-4 h-4" />
|
<Lightbulb className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip> */}
|
||||||
|
|
||||||
{/* 分隔线 */}
|
{/* 分隔线 */}
|
||||||
<div className="w-px h-4 bg-white/[0.20]"></div>
|
{/* <div className="w-px h-4 bg-white/[0.20]"></div> */}
|
||||||
|
|
||||||
{/* 模板故事按钮 */}
|
{/* 模板故事按钮 */}
|
||||||
<Tooltip title="Choose from movie templates" placement="top">
|
<Tooltip title="Choose from movie templates" placement="top">
|
||||||
@ -830,6 +829,7 @@ export function ChatInputBox({ noData }: { noData: boolean }) {
|
|||||||
isCreating={isCreating}
|
isCreating={isCreating}
|
||||||
handleCreateVideo={handleCreateVideo}
|
handleCreateVideo={handleCreateVideo}
|
||||||
icon={<Clapperboard className="w-5 h-5" />}
|
icon={<Clapperboard className="w-5 h-5" />}
|
||||||
|
className="mr-1 mb-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,14 +12,17 @@ import {
|
|||||||
LogOut,
|
LogOut,
|
||||||
PanelsLeftBottom,
|
PanelsLeftBottom,
|
||||||
Bell,
|
Bell,
|
||||||
} from 'lucide-react';
|
} from "lucide-react";
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from "framer-motion";
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from "react-dom";
|
||||||
import { useRouter, usePathname } from 'next/navigation';
|
import { useRouter, usePathname } from "next/navigation";
|
||||||
import React, { useRef, useEffect, useLayoutEffect, useState } from 'react';
|
import React, { useRef, useEffect, useLayoutEffect, useState } from "react";
|
||||||
import { logoutUser } from '@/lib/auth';
|
import { logoutUser } from "@/lib/auth";
|
||||||
import { createPortalSession, redirectToPortal, getUserSubscriptionInfo } from '@/lib/stripe';
|
import {
|
||||||
|
createPortalSession,
|
||||||
|
redirectToPortal,
|
||||||
|
getUserSubscriptionInfo,
|
||||||
|
} from "@/lib/stripe";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string;
|
id: string;
|
||||||
@ -27,6 +30,7 @@ interface User {
|
|||||||
email: string;
|
email: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
plan_name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TopBar({
|
export function TopBar({
|
||||||
@ -40,14 +44,14 @@ export function TopBar({
|
|||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
const currentUser: User = JSON.parse(
|
const [currentUser, setCurrentUser] = useState<User>(
|
||||||
localStorage.getItem("currentUser") || "{}"
|
JSON.parse(localStorage.getItem("currentUser") || "{}")
|
||||||
);
|
);
|
||||||
const pathname = usePathname()
|
const pathname = usePathname();
|
||||||
const [mounted, setMounted] = React.useState(false);
|
const [mounted, setMounted] = React.useState(false);
|
||||||
const [isLogin, setIsLogin] = useState(false);
|
const [isLogin, setIsLogin] = useState(false);
|
||||||
const [isManagingSubscription, setIsManagingSubscription] = useState(false);
|
const [isManagingSubscription, setIsManagingSubscription] = useState(false);
|
||||||
const [subscriptionStatus, setSubscriptionStatus] = useState<string>('');
|
const [subscriptionStatus, setSubscriptionStatus] = useState<string>("");
|
||||||
const [credits, setCredits] = useState<number>(100);
|
const [credits, setCredits] = useState<number>(100);
|
||||||
const [isLoadingSubscription, setIsLoadingSubscription] = useState(false);
|
const [isLoadingSubscription, setIsLoadingSubscription] = useState(false);
|
||||||
|
|
||||||
@ -59,11 +63,18 @@ export function TopBar({
|
|||||||
try {
|
try {
|
||||||
const response = await getUserSubscriptionInfo(String(currentUser.id));
|
const response = await getUserSubscriptionInfo(String(currentUser.id));
|
||||||
if (response.successful && response.data) {
|
if (response.successful && response.data) {
|
||||||
setSubscriptionStatus(response.data.subscription_status);
|
const status = response.data.subscription_status;
|
||||||
|
setSubscriptionStatus(status);
|
||||||
setCredits(response.data.credits);
|
setCredits(response.data.credits);
|
||||||
|
|
||||||
|
// 更新 currentUser 的 plan_name
|
||||||
|
setCurrentUser((prev) => ({
|
||||||
|
...prev,
|
||||||
|
plan_name: response.data.plan_name,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取订阅信息失败:', error);
|
console.error("获取订阅信息失败:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingSubscription(false);
|
setIsLoadingSubscription(false);
|
||||||
}
|
}
|
||||||
@ -86,7 +97,7 @@ export function TopBar({
|
|||||||
// 处理订阅管理
|
// 处理订阅管理
|
||||||
const handleManageSubscription = async () => {
|
const handleManageSubscription = async () => {
|
||||||
if (!currentUser?.id) {
|
if (!currentUser?.id) {
|
||||||
console.error('用户未登录');
|
console.error("用户未登录");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +105,7 @@ export function TopBar({
|
|||||||
try {
|
try {
|
||||||
const response = await createPortalSession({
|
const response = await createPortalSession({
|
||||||
user_id: String(currentUser.id),
|
user_id: String(currentUser.id),
|
||||||
return_url: window.location.origin + '/dashboard'
|
return_url: window.location.origin + "/dashboard",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.successful && response.data?.portal_url) {
|
if (response.successful && response.data?.portal_url) {
|
||||||
@ -199,20 +210,31 @@ export function TopBar({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{isLogin ? (
|
||||||
isLogin ?(<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
{/* Pricing Link */}
|
{/* Pricing Link */}
|
||||||
<Button
|
{pathname === "/" ? (
|
||||||
variant="ghost"
|
<Button
|
||||||
size="sm"
|
variant="ghost"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
localStorage.setItem("callBackUrl", pathname);
|
onClick={() => router.push("/create")}
|
||||||
window.open("/pricing", "_blank");
|
className="bg-white text-black rounded-full hover:bg-gray-100"
|
||||||
}}
|
>
|
||||||
className="text-gray-300 hover:text-white"
|
Go Start
|
||||||
>
|
</Button>
|
||||||
Pricing
|
) : (
|
||||||
</Button>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
localStorage.setItem("callBackUrl", pathname);
|
||||||
|
window.open("/pricing", "_blank");
|
||||||
|
}}
|
||||||
|
className="text-gray-300 hover:text-white"
|
||||||
|
>
|
||||||
|
Pricing
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Notifications */}
|
{/* Notifications */}
|
||||||
{/* <Button variant="ghost" size="sm" onClick={() => showQueueNotification(3, 10)}>
|
{/* <Button variant="ghost" size="sm" onClick={() => showQueueNotification(3, 10)}>
|
||||||
@ -274,9 +296,27 @@ export function TopBar({
|
|||||||
MF
|
MF
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="text-sm font-medium">
|
<div className="flex items-center gap-2">
|
||||||
{currentUser.name || currentUser.username}
|
<p className="text-sm font-medium">
|
||||||
</p>
|
{currentUser.name || currentUser.username}
|
||||||
|
</p>
|
||||||
|
{currentUser.plan_name &&
|
||||||
|
currentUser.plan_name !== "none" && (
|
||||||
|
<span
|
||||||
|
className={`inline-flex items-center px-2 py-1 text-xs font-medium rounded-full ${
|
||||||
|
currentUser.plan_name === "Kickoff"
|
||||||
|
? "bg-blue-100 text-blue-800"
|
||||||
|
: currentUser.plan_name === "Pro"
|
||||||
|
? "bg-purple-100 text-purple-800"
|
||||||
|
: currentUser.plan_name === "Ultra"
|
||||||
|
? "bg-orange-100 text-orange-800"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{currentUser.plan_name}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<p className="text-xs text-gray-500">
|
<p className="text-xs text-gray-500">
|
||||||
{currentUser.email}
|
{currentUser.email}
|
||||||
</p>
|
</p>
|
||||||
@ -298,7 +338,9 @@ export function TopBar({
|
|||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Sparkles className="h-4 w-4" />
|
<Sparkles className="h-4 w-4" />
|
||||||
<span className="text-white underline text-sm">
|
<span className="text-white underline text-sm">
|
||||||
{isLoadingSubscription ? '...' : `${credits} credits`}
|
{isLoadingSubscription
|
||||||
|
? "..."
|
||||||
|
: `${credits} credits`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
@ -311,7 +353,7 @@ export function TopBar({
|
|||||||
>
|
>
|
||||||
Upgrade
|
Upgrade
|
||||||
</Button>
|
</Button>
|
||||||
{subscriptionStatus === 'ACTIVE' && (
|
{subscriptionStatus === "ACTIVE" && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -323,62 +365,34 @@ export function TopBar({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Menu Items */}
|
|
||||||
<div className="p-2">
|
|
||||||
{/* <motion.button
|
|
||||||
whileHover={{ backgroundColor: 'rgba(255,255,255,0.1)' }}
|
|
||||||
className="w-full flex items-center space-x-2 px-3 py-2 rounded-md text-sm text-white"
|
|
||||||
onClick={() => router.push('/my-library')}
|
|
||||||
data-alt="my-library-button"
|
|
||||||
>
|
|
||||||
<Library className="h-4 w-4" />
|
|
||||||
<span>My Library</span>
|
|
||||||
</motion.button>
|
|
||||||
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ backgroundColor: 'rgba(255,255,255,0.1)' }}
|
|
||||||
className="w-full flex items-center space-x-2 px-3 py-2 rounded-md text-sm text-white"
|
|
||||||
onClick={() => {
|
|
||||||
// 处理退出登录
|
|
||||||
setIsOpen(false);
|
|
||||||
}}
|
|
||||||
data-alt="logout-button"
|
|
||||||
>
|
|
||||||
<LogOut className="h-4 w-4" />
|
|
||||||
<span>Logout</span>
|
|
||||||
</motion.button> */}
|
|
||||||
|
|
||||||
{/* Footer */}
|
|
||||||
<div className="mt-4 px-3 py-2 text-xs text-gray-400 text-center">
|
|
||||||
<div>Privacy Policy · Terms of Service</div>
|
|
||||||
<div>250819215404 | 2025/8/20 06:00:50</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>,
|
</motion.div>,
|
||||||
document.body
|
document.body
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</div>):(
|
</div>
|
||||||
<div className="flex items-center space-x-4">
|
) : (
|
||||||
<button
|
<div className="flex items-center space-x-4">
|
||||||
data-alt="login-button"
|
<Button
|
||||||
className="w-[8.5rem] h-[3rem] text-base text-gray-300 hover:text-white transition-colors"
|
variant="ghost"
|
||||||
onClick={() => router.push("/login")}
|
size="sm"
|
||||||
>
|
onClick={() => router.push("/login")}
|
||||||
Login
|
className="text-gray-300 hover:text-white"
|
||||||
</button>
|
data-alt="login-button"
|
||||||
<button
|
>
|
||||||
data-alt="signup-button"
|
Login
|
||||||
className="w-[8.5rem] h-[3rem] text-base bg-gray-200 text-gray-800 rounded-full hover:bg-white transition-colors"
|
</Button>
|
||||||
onClick={() => router.push("/signup")}
|
<Button
|
||||||
>
|
variant="ghost"
|
||||||
Sign Up
|
size="sm"
|
||||||
</button>
|
onClick={() => router.push("/signup")}
|
||||||
</div>
|
className="bg-gray-200 text-gray-800 rounded-full hover:bg-white"
|
||||||
)
|
data-alt="signup-button"
|
||||||
}
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -622,7 +622,7 @@ function HomeModule5() {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
data-alt="core-value-content"
|
data-alt="core-value-content"
|
||||||
className="center z-10 flex flex-col items-center mb-[8rem]"
|
className="center z-10 flex flex-col items-center mb-[4rem]"
|
||||||
>
|
>
|
||||||
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal mb-[1.5rem]">
|
<h2 className="text-white text-[3.375rem] leading-[100%] font-normal mb-[1.5rem]">
|
||||||
Pick a plan and make it yours
|
Pick a plan and make it yours
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user