"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { AuthGuard } from "@/components/AuthGuard"; import { Button, SectionTitle } from "@/components/ui"; import { useMe } from "@/hooks/useMe"; import { loadCommercialInvoices, loadCommercialPricing, loadCommercialTransactions, loadCommercialUsage, } from "../adapters"; import { commercialGet, commercialPost } from "../adapters/http"; import type { CommercialInvoice, CommercialTransaction, } from "../adapters/billing-ledger"; import type { AdapterResult, CommercialPricingItem, CommercialUsageCounter } from "../types"; import { CommercialEmptyState, PlanBadge, PlatformShell, SubscriptionBanner } from "../components"; type CommercialSubscription = { id?: string; status?: string | null; plan_code?: string | null; bundle_code?: string | null; pricing_item_code?: string | null; trial_ends_at?: string | null; current_period_end?: string | null; }; function BillingInner() { const { me, loading: meLoading } = useMe(); const [pricing, setPricing] = useState | null>(null); const [usage, setUsage] = useState | null>(null); const [invoices, setInvoices] = useState | null>(null); const [txns, setTxns] = useState | null>(null); const [sub, setSub] = useState(null); const [coupon, setCoupon] = useState(""); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); const [msg, setMsg] = useState(""); useEffect(() => { void loadCommercialPricing().then(setPricing); }, []); useEffect(() => { if (!me?.current_tenant_id) return; const tenantId = me.current_tenant_id; void (async () => { const dash = await commercialGet<{ subscription?: CommercialSubscription | null; }>(`/api/v1/commercial/dashboard?tenant_id=${encodeURIComponent(tenantId)}`); if (dash.ok) setSub(dash.data.subscription ?? null); else setError(dash.message || "بارگذاری اشتراک commercial ناموفق بود"); void Promise.all([ loadCommercialUsage(tenantId).then(setUsage), loadCommercialInvoices(tenantId).then(setInvoices), loadCommercialTransactions(tenantId).then(setTxns), ]); })(); }, [me?.current_tenant_id]); const activatePricing = async ( item: CommercialPricingItem, mode: "trial" | "active" | "upgrade" | "downgrade" ) => { if (!me?.current_tenant_id) return; setBusy(true); setError(""); setMsg(""); try { const commercial = await commercialPost<{ subscription?: CommercialSubscription; id?: string; status?: string; plan_code?: string | null; trial_ends_at?: string | null; }>("/api/v1/commercial/subscriptions", { tenant_id: me.current_tenant_id, pricing_item_code: item.pricing_item_code, mode, status: mode === "trial" ? "trialing" : "active", coupon_code: coupon.trim() || undefined, }); if (!commercial.ok) { setError(commercial.message || "فعال‌سازی نیازمند Commercial Runtime subscriptions API است."); return; } const next = commercial.data.subscription ?? { id: commercial.data.id, status: commercial.data.status, plan_code: commercial.data.plan_code ?? item.pricing_item_code, pricing_item_code: item.pricing_item_code, trial_ends_at: commercial.data.trial_ends_at ?? null, }; setSub(next); setMsg(`عملیات ${mode} از Commercial Runtime ثبت شد.`); } catch (err) { setError(err instanceof Error ? err.message : "عملیات ناموفق بود"); } finally { setBusy(false); } }; const applyCoupon = async () => { if (!me?.current_tenant_id || !coupon.trim()) return; setBusy(true); setError(""); setMsg(""); // Coupons SoT is Commercial Runtime registry — apply via published coupon codes // attached to pricing activation (coupon_code on subscriptions POST). setMsg( `کد «${coupon.trim()}» را هنگام Trial/فعال‌سازی ارسال کنید. مدیریت کوپن فقط در Admin Commercial.` ); setBusy(false); }; if (meLoading) { return (

در حال بارگذاری...

); } const planLabel = sub?.plan_code || sub?.pricing_item_code || sub?.bundle_code || undefined; return (
{sub ? (
{sub.trial_ends_at ? (

trial_ends_at: {sub.trial_ends_at}

) : null}
) : null} {error ?

{error}

: null} {msg ?

{msg}

: null}
setCoupon(e.target.value)} />
{!pricing || pricing.availability === "loading" ? (

در حال بارگذاری...

) : pricing.availability !== "ready" || !pricing.data.length ? ( ) : (
    {pricing.data.map((item) => (
  • {item.display_name}

    {item.pricing_item_code} {item.amount != null ? ` · ${item.amount} ${item.currency || ""}` : ""} {item.trial_days != null ? ` · trial ${item.trial_days}d` : ""}

  • ))}
)}
{usage?.availability === "ready" && usage.data.length ? (
    {usage.data.map((u) => (
  • {u.key}: {u.used ?? 0}/{u.limit ?? "∞"}
  • ))}
) : (

Usage از Commercial Runtime خالی است.

)}

لجر مالی متعلق به Payment است؛ Commercial فقط نمای خالی/پروکسی صادقانه برمی‌گرداند.

{invoices?.availability === "ready" && invoices.data.length ? (
    {invoices.data.map((inv) => (
  • {inv.id} · {inv.status} · {inv.amount}
  • ))}
) : (

فاکتوری نیست.

)} {txns?.availability === "ready" && txns.data.length ? (
    {txns.data.map((t) => (
  • {t.id} · {t.status} · {t.amount}
  • ))}
) : (

تراکنشی نیست.

)}
); } export function CommercialBillingPage() { return ( ); }