89
src/app/raised/page.tsx
Normal file
89
src/app/raised/page.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import Link from "next/link";
|
||||
import { appTitle } from "@/lib/public-env";
|
||||
|
||||
export const metadata = {
|
||||
title: `Dollars raised — ${appTitle()}`,
|
||||
description: "Live totals from confirmed Stripe donations in this deployment.",
|
||||
};
|
||||
|
||||
export default async function RaisedPage() {
|
||||
const [agg, donorRows] = await Promise.all([
|
||||
prisma.donation.aggregate({
|
||||
_sum: { amountUsdCents: true },
|
||||
_count: true,
|
||||
}),
|
||||
prisma.donation.groupBy({
|
||||
by: ["userId"],
|
||||
_count: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const raisedUsd = (agg._sum.amountUsdCents ?? 0) / 100;
|
||||
const donationCount = agg._count;
|
||||
const uniqueDonors = donorRows.length;
|
||||
const goalUsd = parseFloat(process.env.PUBLIC_CAMPAIGN_GOAL_USD ?? "250000");
|
||||
const pct = goalUsd > 0 ? Math.min(100, Math.round((raisedUsd / goalUsd) * 100)) : 0;
|
||||
|
||||
const formatted = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(raisedUsd);
|
||||
|
||||
return (
|
||||
<main className="min-h-[70vh] border-b border-white/10 py-16">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6">
|
||||
<p className="text-xs uppercase tracking-[0.32em] text-sky-200/80">Transparency</p>
|
||||
<h1 className="mt-4 text-4xl font-semibold text-white sm:text-5xl">Total dollars raised</h1>
|
||||
<p className="mt-4 text-lg text-slate-400">
|
||||
Sum of successful donations processed through Stripe for this app ({appTitle()}). Updates as webhooks confirm
|
||||
payments.
|
||||
</p>
|
||||
|
||||
<div className="mt-12 rounded-[28px] border border-white/10 bg-gradient-to-br from-sky-500/15 via-indigo-900/40 to-fuchsia-900/30 p-8 shadow-[0_0_100px_rgba(56,189,248,0.12)]">
|
||||
<p className="text-sm uppercase tracking-[0.2em] text-slate-400">Confirmed via Stripe</p>
|
||||
<p className="mt-4 font-mono text-5xl font-semibold tracking-tight text-white sm:text-6xl">{formatted}</p>
|
||||
<p className="mt-6 flex flex-wrap gap-6 text-sm text-slate-300">
|
||||
<span>
|
||||
<strong className="text-white">{donationCount}</strong> donation{donationCount === 1 ? "" : "s"}
|
||||
</span>
|
||||
<span>
|
||||
<strong className="text-white">{uniqueDonors}</strong> supporter{uniqueDonors === 1 ? "" : "s"}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className="flex justify-between text-xs text-slate-500">
|
||||
<span>$0</span>
|
||||
<span>
|
||||
Goal {new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }).format(goalUsd)}{" "}
|
||||
<span className="text-slate-600">(PUBLIC_CAMPAIGN_GOAL_USD)</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 h-3 overflow-hidden rounded-full border border-white/10 bg-black/40">
|
||||
<div
|
||||
className="h-full rounded-full bg-gradient-to-r from-sky-400 via-indigo-400 to-fuchsia-400 transition-[width]"
|
||||
style={{ width: `${pct}%` }}
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-2 text-center text-xs text-slate-500">{pct}% of demo goal</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 rounded-2xl border border-white/10 bg-white/5 p-6 text-sm text-slate-400">
|
||||
<p className="font-medium text-white">Note</p>
|
||||
<p className="mt-2 leading-relaxed">
|
||||
This total reflects <code className="rounded bg-black/30 px-1">Donation</code> rows created by the Stripe webhook
|
||||
only—only processed charges count. Configure committee reporting separately for compliance.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 flex flex-wrap gap-4">
|
||||
<Link href="/#donate" className="rounded-full bg-gradient-to-r from-sky-500 to-indigo-500 px-6 py-3 text-sm font-semibold text-white">
|
||||
Donate
|
||||
</Link>
|
||||
<Link href="/" className="rounded-full border border-white/15 px-6 py-3 text-sm font-semibold text-white hover:bg-white/5">
|
||||
Back home
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user