diff --git a/src/app/airdrop/page.tsx b/src/app/airdrop/page.tsx index 3d4ea7d5..7b068cd6 100644 --- a/src/app/airdrop/page.tsx +++ b/src/app/airdrop/page.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { CampaignHero } from "../../components/airdrop/CampaignHero"; import { UserPoints } from "../../components/airdrop/UserPoints"; import { Leaderboard } from "../../components/airdrop/Leaderboard"; +import { WeeklySnapshots } from "../../components/airdrop/WeeklySnapshots"; import { MilestoneTrack } from "../../components/airdrop/MilestoneTrack"; export const metadata: Metadata = { @@ -15,6 +16,7 @@ export default function AirdropPage() { + ); diff --git a/src/components/airdrop/WeeklySnapshots.tsx b/src/components/airdrop/WeeklySnapshots.tsx new file mode 100644 index 00000000..3735a6f8 --- /dev/null +++ b/src/components/airdrop/WeeklySnapshots.tsx @@ -0,0 +1,120 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; +import { formatUsdValue } from "../../../lib/usd-price"; + +interface Snapshot { + weekNumber: number; + weekStart: string; + newStories: number; + tokenBuys: number; + newReferrals: number; + mcapStart: number; + mcapEnd: number; + totalPlEarned: number; +} + +interface SnapshotsData { + snapshots: Snapshot[]; +} + +function formatWeekRange(weekStart: string): string { + const start = new Date(weekStart); + const end = new Date(start.getTime() + 6 * 86400000); + const fmt = (d: Date) => + d.toLocaleDateString("en-US", { month: "short", day: "numeric" }); + return `${fmt(start)}-${fmt(end)}`; +} + +export function WeeklySnapshots() { + const { data, isLoading } = useQuery({ + queryKey: ["airdrop-snapshots"], + queryFn: async () => { + const res = await fetch("/api/airdrop/snapshots"); + if (!res.ok) throw new Error("Failed to fetch snapshots"); + return res.json(); + }, + staleTime: 60_000, + }); + + if (isLoading || !data) { + return ( +
+
Loading snapshots...
+
+ ); + } + + if (data.snapshots.length === 0) { + return ( +
+

Weekly Recaps

+
+ No weekly snapshots yet โ€” check back after the first week! +
+
+ ); + } + + return ( +
+

Weekly Recaps

+ +
+ {[...data.snapshots].sort((a, b) => b.weekNumber - a.weekNumber).map((snap) => { + const mcapChange = snap.mcapEnd - snap.mcapStart; + const mcapPct = + snap.mcapStart > 0 + ? ((mcapChange / snap.mcapStart) * 100).toFixed(1) + : "โ€”"; + const mcapSign = mcapChange >= 0 ? "+" : ""; + + return ( +
+
+ Week {snap.weekNumber} Recap{" "} + + ({formatWeekRange(snap.weekStart)}) + +
+ +
+
+ New stories:{" "} + {snap.newStories} + {" ยท "}Token buys:{" "} + {snap.tokenBuys} +
+
+ New referrals:{" "} + {snap.newReferrals} +
+
+ MCap:{" "} + + {formatUsdValue(snap.mcapStart)} โ†’ {formatUsdValue(snap.mcapEnd)} + {" "} + = 0 ? "text-accent" : "text-error"} + > + ({mcapSign} + {mcapPct}%) + +
+
+ PL earned:{" "} + + {Math.round(snap.totalPlEarned).toLocaleString()} + +
+
+
+ ); + })} +
+
+ ); +}