55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useState } from "react";
|
|
import { apiFetch } from "@/lib/api";
|
|
|
|
type PricingActionProps = {
|
|
plan: "free" | "pro" | "elite";
|
|
label: string;
|
|
primary: boolean;
|
|
};
|
|
|
|
export function PricingAction({ plan, label, primary }: PricingActionProps) {
|
|
const [status, setStatus] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
const className = `mt-8 block w-full text-center ${primary ? "btn-primary" : "btn-secondary"} disabled:opacity-60`;
|
|
|
|
if (plan === "free") {
|
|
return (
|
|
<Link href="/register" className={className}>
|
|
{label}
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
const startCheckout = async () => {
|
|
setLoading(true);
|
|
setStatus("Opening checkout...");
|
|
const appUrl = window.location.origin;
|
|
const response = await apiFetch<{ url: string }>("/api/billing/checkout", {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
plan,
|
|
successUrl: `${appUrl}/settings/subscription?upgraded=1`,
|
|
cancelUrl: `${appUrl}/pricing`,
|
|
}),
|
|
});
|
|
setLoading(false);
|
|
if (response.error || !response.data?.url) {
|
|
setStatus(response.error?.message ?? "Sign in before choosing a paid plan.");
|
|
return;
|
|
}
|
|
window.location.href = response.data.url;
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<button type="button" onClick={startCheckout} disabled={loading} className={className}>
|
|
{loading ? "Opening..." : label}
|
|
</button>
|
|
{status ? <p className="mt-3 text-center text-xs text-muted-foreground">{status}</p> : null}
|
|
</>
|
|
);
|
|
}
|