64 lines
1.5 KiB
TypeScript
64 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
import { Loader2 } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export function ManageSubscriptionButton({
|
|
className,
|
|
}: {
|
|
className?: string;
|
|
}) {
|
|
const [isPending, setIsPending] = useState(false);
|
|
|
|
async function handleOpenPortal() {
|
|
if (isPending) {
|
|
return;
|
|
}
|
|
|
|
setIsPending(true);
|
|
|
|
try {
|
|
const response = await fetch("/api/billing/portal", {
|
|
method: "POST",
|
|
});
|
|
|
|
const data = (await response.json()) as { error?: string; url?: string };
|
|
|
|
if (!response.ok || !data.url) {
|
|
window.alert(data.error || "打开订阅管理失败,请稍后重试。");
|
|
return;
|
|
}
|
|
|
|
window.location.assign(data.url);
|
|
} catch (error) {
|
|
console.error("[ManageSubscriptionButton] portal error", error);
|
|
window.alert("打开订阅管理失败,请稍后重试。");
|
|
} finally {
|
|
setIsPending(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<button
|
|
className={cn(
|
|
"inline-flex items-center justify-center gap-2 rounded-xl border border-white/20 px-4 py-2 text-sm text-zinc-100 hover:border-white/40",
|
|
isPending && "cursor-not-allowed opacity-70",
|
|
className
|
|
)}
|
|
disabled={isPending}
|
|
onClick={handleOpenPortal}
|
|
type="button"
|
|
>
|
|
{isPending ? (
|
|
<>
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
正在跳转...
|
|
</>
|
|
) : (
|
|
"管理订阅"
|
|
)}
|
|
</button>
|
|
);
|
|
}
|