ready to test

Testing

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-06-22 10:07:20 +08:00
parent 56d984a26a
commit fbef7cb6e6
283 changed files with 25961 additions and 1072 deletions
@@ -0,0 +1,217 @@
import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { ArrowLeft, House, CreditCard, BadgeCheck, User } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { Separator } from "@/components/ui/separator";
import AppBreadcrumb from "@/components/generic/Breadcrumb/AppBreadcrumb";
import { useTiers } from "@/contexts/AdminTiersContext";
import { PageMeta } from "@/contexts/MetadataContext";
const STATUS_BADGE = {
pending: "secondary",
completed: "default",
failed: "destructive",
cancelled: "outline",
expired: "outline",
refunded: "outline",
};
const TIER_BADGE = { free: "secondary", premium: "default", exclusive: "destructive" };
// ─── Provider label map (extend as you add more providers) ───────────────────
const PROVIDER_LABELS = {
paypal: "PayPal",
stripe: "Stripe",
gcash: "GCash",
};
function InfoRow({ label, children }) {
return (
<div className="flex flex-col gap-0.5">
<span className="text-xs text-muted-foreground uppercase tracking-wide">{label}</span>
<span className="text-sm font-medium break-all">
{children ?? <span className="text-muted-foreground italic">—</span>}
</span>
</div>
);
}
function SectionCard({ icon: Icon, title, children }) {
return (
<div className="rounded-lg border bg-card p-5 space-y-4">
<div className="flex items-center gap-2">
{Icon && <Icon className="h-4 w-4 text-muted-foreground" />}
<h2 className="text-sm font-semibold">{title}</h2>
</div>
<Separator />
{children}
</div>
);
}
// ─── Provider reference section — renders differently per provider ────────────
function ProviderReference({ payment }) {
const provider = payment.provider ?? "paypal";
const payload = payment.provider_payload ?? {};
const title = `${PROVIDER_LABELS[provider] ?? provider} Reference`;
return (
<SectionCard icon={CreditCard} title={title}>
<div className="grid grid-cols-1 gap-3">
{/* PayPal */}
{provider === "paypal" && (
<>
<InfoRow label="Order ID">{payload.order_id}</InfoRow>
<InfoRow label="Capture ID">{payload.capture_id}</InfoRow>
<InfoRow label="Payer ID">{payload.payer_id}</InfoRow>
{payload.checkout && (
<>
<InfoRow label="Subtotal">
{payment.currency} {Number(payload.checkout.subtotal ?? 0).toFixed(2)}
</InfoRow>
{Number(payload.checkout.discount) > 0 && (
<InfoRow label="Promo Applied">
{payload.checkout.promo_code} — -{payment.currency}{" "}
{Number(payload.checkout.discount).toFixed(2)}
</InfoRow>
)}
</>
)}
</>
)}
{/* Stripe — extend when needed */}
{provider === "stripe" && (
<>
<InfoRow label="Payment Intent ID">{payload.payment_intent_id}</InfoRow>
<InfoRow label="Charge ID">{payload.charge_id}</InfoRow>
<InfoRow label="Customer ID">{payload.customer_id}</InfoRow>
</>
)}
{/* GCash / other — generic fallback */}
{provider !== "paypal" && provider !== "stripe" && (
<>
<InfoRow label="Reference ID">{payload.reference_id}</InfoRow>
<InfoRow label="Transaction ID">{payload.transaction_id}</InfoRow>
</>
)}
{/* Cancelled info — shown for any provider */}
{payload.cancelled_at && (
<InfoRow label="Cancelled At">
{new Date(payload.cancelled_at).toLocaleString()}
</InfoRow>
)}
</div>
</SectionCard>
);
}
// ─── Main ─────────────────────────────────────────────────────────────────────
export default function ViewPayment() {
const navigate = useNavigate();
const { paymentId } = useParams();
const { fetchPayment, payment, loading } = useTiers();
useEffect(() => { fetchPayment(paymentId); }, [fetchPayment, paymentId]);
return (
<section className="bg-muted/60 min-h-full">
<PageMeta title={payment ? `${payment.plan?.label ?? 'Payment'} – Payment - STARR` : undefined} />
<div className="lg:container lg:mx-auto lg:px-0 flex flex-col items-start px-4 py-10">
<div className="w-full max-w-2xl mx-auto">
<div className="flex flex-col gap-2 mb-6">
<AppBreadcrumb items={[
{ label: "Home", icon: <House className="size-4" />, to: "/admin" },
{ label: "Payments", to: "/admin/tiers/payments" },
{ label: `Payment #${paymentId}` },
]} />
</div>
<div className="flex items-center gap-3 mb-6">
<Button type="button" variant="ghost" size="icon" onClick={() => navigate(-1)}>
<ArrowLeft className="h-4 w-4" />
</Button>
<div>
<h1 className="text-xl font-semibold">Payment Details</h1>
<p className="text-sm text-muted-foreground">Payment #{paymentId}</p>
</div>
</div>
{loading && !payment ? (
<div className="space-y-5">
{[...Array(3)].map((_, i) => <Skeleton key={i} className="h-32 w-full" />)}
</div>
) : !payment ? (
<p className="text-sm text-muted-foreground">Payment not found.</p>
) : (
<div className="space-y-5">
{/* Transaction */}
<SectionCard icon={CreditCard} title="Transaction">
<div className="grid grid-cols-2 gap-4">
<InfoRow label="Status">
<Badge variant={STATUS_BADGE[payment.status] ?? "outline"} className="capitalize mt-0.5">
{payment.status}
</Badge>
</InfoRow>
<InfoRow label="Provider">
{PROVIDER_LABELS[payment.provider] ?? payment.provider ?? "—"}
</InfoRow>
<InfoRow label="Amount">
{payment.currency} {Number(payment.amount).toFixed(2)}
</InfoRow>
{Number(payment.discount) > 0 && (
<InfoRow label="Discount">
-{payment.currency} {Number(payment.discount).toFixed(2)}
{payment.promo_code && ` (${payment.promo_code})`}
</InfoRow>
)}
<InfoRow label="Paid At">
{payment.paid_at ? new Date(payment.paid_at).toLocaleString() : "—"}
</InfoRow>
<InfoRow label="Created At">
{payment.createdAt ? new Date(payment.createdAt).toLocaleString() : "—"}
</InfoRow>
</div>
</SectionCard>
{/* User */}
<SectionCard icon={User} title="User">
<div className="grid grid-cols-2 gap-4">
<InfoRow label="User ID">{String(payment.user_id)}</InfoRow>
<InfoRow label="Email">{payment.user?.email}</InfoRow>
</div>
</SectionCard>
{/* Plan */}
{payment.plan && (
<SectionCard icon={BadgeCheck} title="Plan">
<div className="grid grid-cols-2 gap-4">
<InfoRow label="Label">{payment.plan.label}</InfoRow>
<InfoRow label="Tier">
<Badge variant={TIER_BADGE[payment.plan.tier] ?? "outline"} className="capitalize mt-0.5">
{payment.plan.tier}
</Badge>
</InfoRow>
<InfoRow label="Duration">{payment.plan.duration_days} days</InfoRow>
</div>
</SectionCard>
)}
{/* Provider reference — dynamic per provider */}
<ProviderReference payment={payment} />
</div>
)}
</div>
</div>
</section>
);
}