import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { ArrowLeft, House, Tag, ShieldCheck, Plus, Trash2, Loader2 } from "lucide-react";
import { DateTimePicker } from "@/components/ui/date-time-picker";
import { toast } from "sonner";
import AppBreadcrumb from "@/components/generic/Breadcrumb/AppBreadcrumb";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { PageMeta } from "@/contexts/MetadataContext";
import { useTiers } from "@/contexts/AdminTiersContext";
import api from "@/utils/api.util";
// ─── Helpers ──────────────────────────────────────────────────────────────────
function SectionCard({ icon: Icon, title, description, children }) {
return (
{Icon && }
{title}
{description &&
{description}
}
{children}
);
}
const EMPTY_PROMO = {
code: "", type: "flat", value: "", currency: "USD",
max_discount: "", max_uses: "", expires_at: "", min_amount: "",
};
const WINDOW_UNITS = ["minutes", "hours", "days"];
// ─── Page ─────────────────────────────────────────────────────────────────────
export default function PaymentPolicy() {
const navigate = useNavigate();
const { planId } = useParams();
const { fetchPlan, plan, loading: planLoading } = useTiers();
const [policy, setPolicy] = useState(null);
const [policyLoading, setPolicyLoading] = useState(true);
const [saving, setSaving] = useState(false);
// ── Refund policy fields
const [refundAllowed, setRefundAllowed] = useState(true);
const [refundWindowValue, setRefundWindowValue] = useState(5);
const [refundWindowUnit, setRefundWindowUnit] = useState("minutes");
const [refundReasonReqd, setRefundReasonReqd] = useState(false);
// ── Promo rules
const [promoRules, setPromoRules] = useState([]);
const [addForm, setAddForm] = useState(EMPTY_PROMO);
const [showAdd, setShowAdd] = useState(false);
// ─── Load ────────────────────────────────────────────────────────────────────
useEffect(() => {
fetchPlan(planId);
setPolicyLoading(true);
api.get(`/admin/tier-policies/plans/${planId}/payment-policy`)
.then(({ data }) => {
const p = data.data;
if (p) {
setPolicy(p);
const rp = p.refund_policy ?? {};
setRefundAllowed(rp.allowed ?? true);
setRefundWindowValue(rp.window_value ?? 5);
setRefundWindowUnit(rp.window_unit ?? "minutes");
setRefundReasonReqd(rp.reason_required ?? false);
setPromoRules(p.promo_rules ?? []);
}
})
.catch(() => {})
.finally(() => setPolicyLoading(false));
}, [planId]);
// ─── Save ────────────────────────────────────────────────────────────────────
const handleSave = async () => {
setSaving(true);
try {
await api.put(`/admin/tier-policies/plans/${planId}/payment-policy`, {
refund_policy: {
allowed: refundAllowed,
window_value: Number(refundWindowValue),
window_unit: refundWindowUnit,
reason_required: refundReasonReqd,
},
promo_rules: promoRules,
});
toast("Payment policy saved.", {
action: {
label: "Close",
onClick: () => {}
}
});
navigate("/admin/tiers/plans");
} catch (err) {
toast(err?.response?.data?.message ?? "Could not save payment policy.", {
action: {
label: "Close",
onClick: () => {}
}
});
} finally {
setSaving(false);
}
};
// ─── Promo helpers ────────────────────────────────────────────────────────────
const handleAddPromo = () => {
const code = addForm.code.trim().toUpperCase();
if (!code) { toast("Code is required.", {
action: {
label: "Close",
onClick: () => {}
}
}); return; }
if (!addForm.value || Number(addForm.value) <= 0) { toast("Value must be greater than 0.", {
action: {
label: "Close",
onClick: () => {}
}
}); return; }
if (promoRules.some((r) => r.code.toUpperCase() === code)) {
toast("A rule with this code already exists.", {
action: {
label: "Close",
onClick: () => {}
}
}); return;
}
const rule = {
code,
type: addForm.type,
value: Number(addForm.value),
...(addForm.type === "flat" && addForm.currency ? { currency: addForm.currency.trim().toUpperCase() } : {}),
...(addForm.type === "percent" && addForm.max_discount ? { max_discount: Number(addForm.max_discount) } : {}),
...(addForm.max_uses ? { max_uses: Number(addForm.max_uses) } : {}),
...(addForm.expires_at ? { expires_at: addForm.expires_at } : {}),
...(addForm.min_amount ? { min_amount: Number(addForm.min_amount) } : {}),
};
setPromoRules((prev) => [...prev, rule]);
setAddForm(EMPTY_PROMO);
setShowAdd(false);
};
const handleRemovePromo = (code) =>
setPromoRules((prev) => prev.filter((r) => r.code !== code));
// ─── Render ───────────────────────────────────────────────────────────────────
const isLoading = planLoading || policyLoading;
return (
, to: "/admin" },
{ label: "Plans", to: "/admin/tiers/plans" },
{ label: plan?.label ?? `Plan #${planId}`, to: `/admin/tiers/plans/${planId}/view` },
{ label: "Payment Policy" },
]} />
Payment Policy
{plan?.tier} — {plan?.label}
{isLoading ? (
{[...Array(3)].map((_, i) => )}
) : (
{/* ── Refund Policy ─────────────────────────────────────────── */}
Allow Refunds
Users can request a refund within the window below.
{refundAllowed && (
<>
Require Reason
User must provide a reason when requesting a refund.
>
)}
{/* ── Promo Codes ───────────────────────────────────────────── */}
{/* Existing rules */}
{promoRules.length > 0 ? (
{promoRules.map((rule) => (
{rule.code}
{rule.type}
{rule.type === "flat"
? `${rule.currency ?? "USD"} ${Number(rule.value).toFixed(2)} off`
: `${rule.value}% off${rule.max_discount ? ` (max ${rule.max_discount})` : ""}`
}
{rule.max_uses && (
· {rule.max_uses} uses max
)}
{rule.expires_at && (
· expires {new Date(rule.expires_at).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" })}
)}
))}
) : (
No promo codes configured for this plan.
)}
{/* Add form */}
{showAdd ? (
New Promo Code
setAddForm((f) => ({ ...f, value: e.target.value }))}
/>
{addForm.type === "flat" && (
{localizedPrices.length === 0 && (
No localized prices set — only base currency available.
)}
)}
{addForm.type === "percent" && (
setAddForm((f) => ({ ...f, max_discount: e.target.value }))}
/>
)}
setAddForm((f) => ({ ...f, min_amount: e.target.value }))}
/>
) : (
)}
{/* ── Save ─────────────────────────────────────────────────── */}
)}
);
}