hts/apps/migrant/app/[locale]/manage/plans/card.tsx

410 lines
12 KiB
TypeScript

"use client";
import { Loading } from "@/components/dashboard/loading";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTrigger,
} from "@/components/ui/dialog";
import { toast } from "@/components/ui/toaster";
// import { trpc } from "@/lib/trpc/client";
// import { PostHogEvent } from "@/providers/PostHogProvider";
// import { type Workspace } from "@aigxion/db";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
// import { getTenantId } from "@/lib/auth";
// import { db } from "@/lib/db";
import { ArrowLeft } from "lucide-react";
import Link from "next/link";
import { ChangePlanButton } from "./button";
import { PayInfo, PaymentInfo, PaymentOrder, payStaff, queryPayStatus, queryProductionList } from "@/lib/http/staff";
import { FadeIn } from "@/components/landing/fade-in";
import { cn } from "@/lib/utils";
import { Modal, QRCode, message } from "antd";
import Image from "next/image";
import { useTranslation } from "react-i18next";
type Props = {
newPlan: "free" | "pro";
// workspace: Workspace;
tier: string;
};
interface ProductInfo {
[key: string]: {
id: number;
name: string;
href: string;
price: number;
description: string;
buttonText: string;
features: string[];
footnotes: never[];
};
}
const tiers: ProductInfo = {
free: {
id: 1,
name: "月度计划",
href: "/manage",
price: 299,
description: "启动下一个 API 所需的一切!",
buttonText: "基础",
features: [
"100 个每月活动密钥",
"每月 2500 次成功验证",
"无限的API",
"7天免费保留",
],
footnotes: [],
},
pro: {
id: 2,
name: "季度计划",
href: "/manage",
price: 699,
description: "对于那些有团队和更苛刻需求的人",
buttonText: "Pro",
features: [
"250 每月 xxxxxxxx*",
"150,000 成功 xxxxxxx 包括 **",
"xxxxxxxx APIs",
"与团队成员的工作空间",
"90 天分析保留",
"90 天审核日志保留",
],
footnotes: [
// " * Additional active keys are billed at $0.10",
// " ** Additional verifications are billed at $10 per 100,000",
],
},
custom: {
id: 3,
name: "年度计划",
href: "/manage",
price: 1299,
description: "我们为有批量需求的人提供定制定价",
buttonText: "Go",
features: [
"自定义验证限制",
"自定义活动键限制",
"自定义分析保留",
"专用支持合同",
"每个API的白名单IP",
],
footnotes: [],
},
};
export const PlanCard: React.FC<Props> = ({ tier = "free", newPlan = "free" }) => {
const router = useRouter();
const [open, setOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [payQrcode, setPayQrcode] = useState("");
const path = usePathname()
const { t, i18n } = useTranslation()
const searchParams = useSearchParams()
const staffId = searchParams.get('staffId')
console.log("---PlanCard----", staffId, path)
const isChatPage = path.includes('chat')
useEffect(() => {
// console.log("------------email", infoRef.current, userData.auth_token)
async function initFunc() {
const data = await queryProductionList()
console.log(data)
}
initFunc()
}, []);
// const changePlan = trpc.workspace.changePlan.useMutation({
// onSuccess: (data, variables, _context) => {
// toast.success(data.title, {
// description: data.message,
// });
// PostHogEvent({
// name: "plan_changed",
// properties: { plan: variables.plan, workspace: variables.workspaceId },
// });
// setOpen(false);
// router.refresh();
// },
// onError: (error) => {
// toast.error(error.message);
// },
// });
async function pollOrder(orderId: string, interval: number, maxAttempts: number): Promise<PaymentOrder | null> {
let attempts = 0;
console.log("开始轮训订单", orderId)
const poll = async (): Promise<PaymentOrder | null> => {
try {
// 发送订单查询请求,假设返回的数据结构为 Order
const result = await queryPayStatus(orderId);
const order = result.data as PaymentOrder;
// 如果订单状态为已完成,或者达到最大尝试次数,则停止轮询并返回订单
if (order.state >= 2 || attempts >= maxAttempts) {
return order;
}
// 增加尝试次数
attempts++;
// 等待一段时间后继续轮询
await new Promise(resolve => setTimeout(resolve, interval));
// 递归调用自身,继续轮询
return poll();
} catch (error) {
// 处理轮询过程中的错误
console.error("Error occurred while polling order:", error);
return null;
}
};
return poll();
}
const isSamePlan = true;
return (
<div
key={tier}
className={
"border border-border flex w-full flex-col justify-between rounded-lg bg-white dark:bg-black p-8 lg:w-1/3 xl:p-10 "
}
>
<div className="flex items-center justify-between gap-x-4">
<h2 id={tier} className={"text-content text-2xl font-semibold leading-8"}>
{tiers[tier].name}
</h2>
</div>
<p className="mt-4 min-h-[3rem] text-sm leading-6 text-content-subtle">
{tiers[tier].description}
</p>
<p className="flex items-center mx-auto mt-6 gap-x-1">
{typeof tiers[tier].price === "number" ? (
<>
<span className="text-4xl font-bold tracking-tight text-center text-content">
{`${tiers[tier].price}`}
</span>
<span className="mx-auto text-sm font-semibold leading-6 text-center text-content-subtle">
{"/month"}
</span>
</>
) : (
<span className="mx-auto text-4xl font-bold tracking-tight text-center text-content">
{tiers[tier].price}
</span>
)}
</p>
<div className="flex flex-col justify-between grow">
<ul className="mt-8 space-y-3 text-sm leading-6 text-content-subtle xl:mt-10">
{tiers[tier].features.map((feature) => (
<li key={feature} className="flex gap-x-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className="flex-none w-5 h-6 text-gray-700"
aria-hidden="true"
>
<path
fill="currentColor"
fillRule="evenodd"
d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353l8.493-12.739a.75.75 0 0 1 1.04-.208Z"
clipRule="evenodd"
/>
</svg>
{feature}
</li>
))}
</ul>
{tiers[tier].footnotes && (
<ul className="mt-6 mb-8">
{tiers[tier].footnotes.map((footnote, i) => (
<li
// biome-ignore lint/suspicious/noArrayIndexKey: I got nothing better right now
key={`note-${i}`}
className="flex text-xs text-content-subtle gap-x-3"
>
{footnote}
</li>
))}
</ul>
)}
{/* {tier === "custom" ? (
<Link href="mailto:support@unkey.dev">
<Button
className="w-full col-span-1"
// variant={workspace.plan === "enterprise" ? "disabled" : "primary"}
// disabled={workspace.plan === "enterprise"}
>
Go
</Button>
</Link>
) : (
<ChangePlanButton
// workspace={workspace}
newPlan={newPlan}
label={tiers[tier].buttonText}
/>
)} */}
{/* <Button
className="w-full col-span-1"
// variant={workspace.plan === "enterprise" ? "disabled" : "primary"}
// disabled={workspace.plan === "enterprise"}
>
Go
</Button> */}
{
staffId && <Button
disabled={isLoading}
className="mt-4 "
type="submit"
onClick={async (e) => {
e.preventDefault()
setIsLoading(true)
// setTimeout(() => {
// setOpen(true)
// setIsLoading(false)
// }, 2000)
if (!!Number(staffId)) {
const result = await payStaff({
id: Number(staffId),
production_id: tiers[tier].id,
pay_type: "alipay",
} as PayInfo)
const typedData = result.data as PaymentInfo;
console.log("----payStaff---", typedData)
setPayQrcode(typedData.pay_url)
setOpen(true)
// 启动轮训
setTimeout(async () => {
await pollOrder(typedData.trade_no, 2000, 60)
router.push(`/manage/staffs`);
})
} else {
message.warning("无效员工ID")
}
}}
>
{isLoading ? <Loading /> : "Pay"}
</Button>
}
</div>
<Modal
// title="Modal 1000px width"
centered
open={open}
onOk={() => {
if (setOpen) {
setOpen(false)
}
}}
onCancel={() => {
setOpen(false)
}}
footer=""
// width={"50%"}
className={cn('min-w-[95%] lg:min-w-[700px] p-[20px]', "")}
>
<div className="mx-auto sm:max-w-2xl min-h-[500px] mb-[3rem] lg:mb-[5rem] rounded bg-[#fff]">
<FadeIn >
<h3 className='text-center px-6 sm:px-4 text-2xl font-bold py-[1.2rem]'>使</h3>
{/* <div className='flex flex-row items-center justify-around bg-[#F5F5F5] text-[#666666] mb-[2rem]'>
<Link href="/xauth/login" >
<Button className="h-8 " >Login</Button>
</Link>
<Link href="/xauth/login">
<Button className="h-8" >Signup</Button>
</Link>
<Link href="/xauth/login">
<Button className="h-8" >Download</Button>
</Link>
</div> */}
{payQrcode && <div className="rounded bg-clip-border overflow-clip">
{/* <figure
className='w-full'
><Image fill={true} src={payQrcode} className='w-full' alt="Hero Image" /></figure> */}
<QRCode
value={payQrcode}
size={200} // 二维码的大小
style={{ margin: 'auto' }}
imageSettings={{ // 二维码中间的logo图片
src: 'logoUrl',
height: 100,
width: 100,
excavate: true, // 中间图片所在的位置是否镂空
}}
/>
{/* <figure
className="w-full min-h-[146px] lg:min-h-[246px] "
// style={{ width: '100%', minHeight: "246px" }}
>
<img src={payQrcode} alt="Hero Image" style={{ width: '100%', marginBottom: '8px' }} />
</figure> */}
</div>}
</FadeIn>
</div>
</Modal>
</div>
);
};