import { redirect } from "next/navigation"; import { buyBadge } from "@/actions/draw-badge"; import { ContentCard, EmptyState, StatBlock } from "@/components/public/ui"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; // Reads the live users + website_drawbadges tables and writes credits/badges on // purchase — must never be statically rendered. export const dynamic = "force-dynamic"; export const metadata = { title: "Draw a Badge" }; const DEFAULT_PRICE = 50; type DrawBadge = { id: bigint; badgeUrl: string; badgeName: string; badgeDesc: string; }; const BOUGHT_NOTE = (code: string) => `Badge "${code}" has been added to your inventory. Enjoy!`; const ERROR_NOTE: Record = { invalid: "That badge is no longer available.", credits: "You don't have enough credits to buy this badge.", fail: "Something went wrong. Please try again.", }; export default async function DrawBadgePage({ searchParams, }: { // Next 16: searchParams is a Promise. searchParams: Promise<{ bought?: string; error?: string }>; }) { const session = await auth(); if (!session?.user?.id) redirect("/login"); const { bought = "", error = "" } = await searchParams; // Resolve the flat purchase price + the buyer's balance + the published // badges, all fail-soft so the page still renders if the DB is unreachable. let price = DEFAULT_PRICE; let credits = 0; let badges: DrawBadge[] = []; try { const raw = await siteSettings.get("drawbadge.price", String(DEFAULT_PRICE)); const n = Number(raw); if (Number.isFinite(n) && n >= 0) price = Math.floor(n); } catch { price = DEFAULT_PRICE; } try { const buyer = await prisma.user.findUnique({ where: { id: Number(session.user.id) }, select: { credits: true }, }); credits = buyer?.credits ?? 0; } catch { credits = 0; } try { badges = await prisma.websiteDrawbadges.findMany({ where: { published: true }, select: { id: true, badgeUrl: true, badgeName: true, badgeDesc: true }, orderBy: { id: "desc" }, }); } catch { badges = []; } return (
{bought ? (

{BOUGHT_NOTE(bought)}

) : null} {error ? (

{ERROR_NOTE[error] ?? ERROR_NOTE.fail}

) : null}
0 ? `${badges.length} badge(s) available` : undefined } padded={badges.length === 0} > {badges.length === 0 ? ( No badges have been published yet. Check back soon! ) : (
{badges.map((b) => { const affordable = credits >= price; return (
{b.badgeUrl ? ( // Badge images are arbitrary external URLs stored by the // draw-badge tool, so a plain is correct here. // eslint-disable-next-line @next/next/no-img-element ) : null}

{b.badgeName}

cr {price.toLocaleString()}
{b.badgeDesc ? (

{b.badgeDesc}

) : null}
); })}
)}
); }