Files
igny8/frontend/src/pages/Payment.tsx
IGNY8 VPS (Salman) 4bffede052 docs & ux improvmeents
2025-12-25 20:31:58 +00:00

122 lines
4.9 KiB
TypeScript

import { useEffect, useMemo, useState } from "react";
import { useLocation, useNavigate, Link } from "react-router-dom";
import { useAuthStore } from "../store/authStore";
const PLAN_COPY: Record<string, { name: string; price: string; content: string }> = {
starter: { name: "Starter", price: "$49/mo", content: "50 content pieces/month" },
growth: { name: "Growth", price: "$149/mo", content: "200 content pieces/month" },
scale: { name: "Scale", price: "$349/mo", content: "500 content pieces/month" },
};
export default function Payment() {
const location = useLocation();
const navigate = useNavigate();
const user = useAuthStore((s) => s.user);
const [contactEmail, setContactEmail] = useState("");
const [note, setNote] = useState("");
const [error, setError] = useState("");
const planSlug = useMemo(() => new URLSearchParams(location.search).get("plan") || "", [location.search]);
const plan = useMemo(() => {
const slugFromAccount = user?.account?.plan?.slug;
const slug = planSlug || slugFromAccount || "";
return slug ? PLAN_COPY[slug] : null;
}, [planSlug, user?.account?.plan?.slug]);
const mailtoHref = useMemo(() => {
if (!plan || !contactEmail.trim()) return "";
const subject = encodeURIComponent(`Payment submitted for ${plan.name}`);
const body = encodeURIComponent(
`Plan: ${plan.name}\nAccount: ${user?.account?.slug || user?.account?.id || "-"}\nEmail: ${contactEmail}\nNotes/Reference: ${note || "-"}`
);
return `mailto:sales@igny8.com?subject=${subject}&body=${body}`;
}, [plan, contactEmail, note, user?.account?.slug, user?.account?.id]);
useEffect(() => {
if (!plan || !user) {
navigate("/pricing", { replace: true });
}
}, [plan, navigate, user]);
const handleRequest = (e: React.MouseEvent<HTMLAnchorElement>) => {
if (!plan) {
e.preventDefault();
navigate("/pricing", { replace: true });
return;
}
if (!contactEmail.trim()) {
e.preventDefault();
setError("Please enter a contact email.");
return;
}
setError("");
};
return (
<div className="min-h-screen flex items-center justify-center bg-slate-50 px-4 py-16">
<div className="w-full max-w-3xl bg-white rounded-2xl shadow-lg p-8 space-y-6 border border-slate-100">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-slate-500">Confirm your plan</p>
<h1 className="text-2xl font-semibold text-slate-900">Complete your subscription</h1>
</div>
<Link to="/pricing" className="text-sm text-blue-600 hover:text-blue-700">
Change plan
</Link>
</div>
{plan && (
<div className="rounded-xl border border-slate-200 bg-slate-50 p-4">
<h2 className="text-lg font-semibold text-slate-900">{plan.name}</h2>
<p className="text-slate-700">{plan.price}</p>
<p className="text-sm text-slate-600">{plan.content}</p>
<p className="text-xs text-amber-700 mt-2">
Payment is completed offline (bank transfer). Submit your email and reference below; we will verify and activate your account.
</p>
</div>
)}
<div className="space-y-4">
<label className="block text-sm font-medium text-slate-800">
Contact email
<input
type="email"
value={contactEmail}
onChange={(e) => setContactEmail(e.target.value)}
placeholder="you@example.com"
className="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2 text-slate-900 focus:border-blue-500 focus:outline-none"
/>
</label>
<label className="block text-sm font-medium text-slate-800">
Notes (optional)
<textarea
value={note}
onChange={(e) => setNote(e.target.value)}
placeholder="Company name, billing contact, or questions"
className="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2 text-slate-900 focus:border-blue-500 focus:outline-none"
rows={3}
/>
</label>
</div>
<div className="flex items-center justify-between">
<Link to="/signup" className="text-sm text-slate-600 hover:text-slate-800">
Prefer the free plan? Start your trial
</Link>
<a
href={mailtoHref || "#"}
onClick={handleRequest}
className={`inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-semibold text-white ${
contactEmail.trim() ? "bg-blue-600 hover:bg-blue-700" : "bg-blue-400 cursor-not-allowed"
}`}
aria-disabled={!contactEmail.trim()}
>
Request payment instructions
</a>
</div>
{error && <p className="text-sm text-red-600">{error}</p>}
</div>
</div>
);
}