Skip to content

Commit c8f6892

Browse files
committed
feat(wins): show sample wins when feed is empty
- Add SAMPLE_WINS with 3 realistic founder wins - Hero switches to amber pulse when no real data - Feed shows samples + 'ready to land your win here?' CTA - Replaces empty state with a populated feel for day-1 launch - Samples auto-replaced by real data as soon as one user runs 'loopkit celebrate --share'
1 parent 77f2f96 commit c8f6892

1 file changed

Lines changed: 105 additions & 28 deletions

File tree

‎packages/web/src/app/wins/page.tsx‎

Lines changed: 105 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,63 @@ function relativeTime(ts: number): string {
6464
return `${Math.floor(diffDays / 30)}mo ago`;
6565
}
6666

67+
// ─── Sample data (shown only when no real wins exist yet) ──────
68+
// Helps launch with a populated feel; replaced by real data as soon
69+
// as one user runs `loopkit celebrate --share`.
70+
const SAMPLE_WINS: PublicWin[] = [
71+
{
72+
_id: "sample-1",
73+
productName: "ProposalAI",
74+
weekNum: 12,
75+
shippingScore: 88,
76+
streak: 12,
77+
tasksCompleted: 5,
78+
tasksTotal: 5,
79+
feedbackCount: 8,
80+
loopkitScore: 91,
81+
mrr: 240,
82+
oneThing:
83+
"Stop perfectionism. Ship the v0.1 with a known gap. The gap is the conversation that gets you the next customer.",
84+
createdAt: Date.now() - 2 * 24 * 60 * 60 * 1000,
85+
handle: "demo-founder",
86+
projectSlug: "proposalai",
87+
},
88+
{
89+
_id: "sample-2",
90+
productName: "ShipLane",
91+
weekNum: 8,
92+
shippingScore: 75,
93+
streak: 6,
94+
tasksCompleted: 4,
95+
tasksTotal: 5,
96+
feedbackCount: 3,
97+
loopkitScore: 78,
98+
mrr: null,
99+
oneThing:
100+
"Distribution tasks keep getting pushed to next week. Block 90 min Tuesday and 90 min Friday. Non-negotiable.",
101+
createdAt: Date.now() - 4 * 24 * 60 * 60 * 1000,
102+
handle: "founder-jane",
103+
projectSlug: "shiplane",
104+
},
105+
{
106+
_id: "sample-3",
107+
productName: "PulseDeck",
108+
weekNum: 5,
109+
shippingScore: 100,
110+
streak: 5,
111+
tasksCompleted: 3,
112+
tasksTotal: 3,
113+
feedbackCount: 12,
114+
loopkitScore: 85,
115+
mrr: 60,
116+
oneThing:
117+
"Three paying customers this week. All from one tweet thread. Build-in-public works when the loop is honest.",
118+
createdAt: Date.now() - 6 * 24 * 60 * 60 * 1000,
119+
handle: "mike-builds",
120+
projectSlug: "pulsedeck",
121+
},
122+
];
123+
67124
function WinCard({ win }: { win: PublicWin }) {
68125
return (
69126
<article className="p-5 rounded-2xl border border-zinc-800 bg-zinc-900/30 hover:bg-zinc-900/50 transition-colors">
@@ -138,6 +195,17 @@ export default async function WinsPage() {
138195
)
139196
: 0;
140197

198+
const showSamples = wins.length === 0;
199+
const displayedWins = showSamples ? SAMPLE_WINS : wins;
200+
const stats = showSamples
201+
? { count: SAMPLE_WINS.length, founders: 3, avg: 88, streak: 12 }
202+
: {
203+
count: totalWins,
204+
founders: shippingFounders,
205+
avg: avgScore,
206+
streak: longestStreak,
207+
};
208+
141209
return (
142210
<main id="main-content" className="relative overflow-hidden">
143211
{/* ─── Hero ──────────────────────────────────────────── */}
@@ -146,8 +214,10 @@ export default async function WinsPage() {
146214

147215
<div className="relative z-10 max-w-4xl mx-auto px-6 text-center">
148216
<div className="fade-up inline-flex items-center gap-2 px-4 py-1.5 mb-8 rounded-full border border-zinc-800 bg-zinc-900/50 text-sm text-zinc-400">
149-
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
150-
{totalWins > 0 ? `${totalWins} public win${totalWins === 1 ? "" : "s"}` : "Wins start here"}
217+
<span className={`w-2 h-2 rounded-full ${showSamples ? "bg-amber-500" : "bg-emerald-500"} animate-pulse`} />
218+
{showSamples
219+
? "Sample wins — your data appears here when you ship"
220+
: `${stats.count} public win${stats.count === 1 ? "" : "s"}`}
151221
</div>
152222

153223
<h1 className="fade-up delay-1 text-5xl sm:text-7xl font-bold tracking-tight leading-[1.08]">
@@ -166,21 +236,21 @@ export default async function WinsPage() {
166236
<dl className="fade-up delay-3 mt-10 grid grid-cols-2 sm:grid-cols-4 gap-3 max-w-2xl mx-auto">
167237
<div className="p-4 rounded-xl border border-zinc-800 bg-zinc-900/30">
168238
<dt className="text-xs text-zinc-500 uppercase tracking-wider">Wins</dt>
169-
<dd className="mt-1 text-2xl font-bold text-white">{totalWins}</dd>
239+
<dd className="mt-1 text-2xl font-bold text-white">{stats.count}</dd>
170240
</div>
171241
<div className="p-4 rounded-xl border border-zinc-800 bg-zinc-900/30">
172242
<dt className="text-xs text-zinc-500 uppercase tracking-wider">Founders</dt>
173-
<dd className="mt-1 text-2xl font-bold text-white">{shippingFounders}</dd>
243+
<dd className="mt-1 text-2xl font-bold text-white">{stats.founders}</dd>
174244
</div>
175245
<div className="p-4 rounded-xl border border-zinc-800 bg-zinc-900/30">
176246
<dt className="text-xs text-zinc-500 uppercase tracking-wider">Avg Score</dt>
177-
<dd className="mt-1 text-2xl font-bold text-white">{avgScore}%</dd>
247+
<dd className="mt-1 text-2xl font-bold text-white">{stats.avg}%</dd>
178248
</div>
179249
<div className="p-4 rounded-xl border border-orange-500/20 bg-zinc-900/30">
180250
<dt className="text-xs text-orange-400 uppercase tracking-wider flex items-center gap-1">
181251
<Flame className="h-3 w-3" /> Longest
182252
</dt>
183-
<dd className="mt-1 text-2xl font-bold text-white">{longestStreak}</dd>
253+
<dd className="mt-1 text-2xl font-bold text-white">{stats.streak}</dd>
184254
</div>
185255
</dl>
186256
)}
@@ -207,36 +277,43 @@ export default async function WinsPage() {
207277
<div className="max-w-4xl mx-auto">
208278
<header className="mb-12">
209279
<h2 className="text-3xl sm:text-4xl font-bold tracking-tight mb-3">
210-
This week&apos;s wins
280+
{showSamples ? "Sample wins (what you'll see)" : "This week's wins"}
211281
</h2>
212282
<p className="text-zinc-400">
213-
{wins.length > 0
214-
? `Showing the ${wins.length} most recent public wins.`
215-
: "No public wins yet. Be the first — run `loopkit celebrate --share`."}
283+
{showSamples
284+
? "These are sample wins. Run `loopkit celebrate --share` after your next `loop` and yours will appear here."
285+
: `Showing the ${wins.length} most recent public wins.`}
216286
</p>
217287
</header>
218288

219289
{wins.length === 0 ? (
220-
<div className="p-12 text-center rounded-2xl border border-dashed border-zinc-800 bg-zinc-900/20">
221-
<Trophy className="h-10 w-10 text-zinc-700 mx-auto mb-4" aria-hidden="true" />
222-
<h3 className="text-lg font-semibold text-white mb-2">
223-
The leaderboard is open.
224-
</h3>
225-
<p className="text-zinc-400 text-sm mb-6 max-w-md mx-auto">
226-
When a LoopKit user runs <code className="font-mono text-violet-400">loopkit celebrate --share</code>,
227-
their weekly win lands here.
228-
</p>
229-
<Link
230-
href="/"
231-
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-white text-black text-sm font-medium hover:bg-zinc-200 transition-colors"
232-
>
233-
Start shipping
234-
<ChevronRight className="h-4 w-4" aria-hidden="true" />
235-
</Link>
236-
</div>
290+
<>
291+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
292+
{displayedWins.map((win) => (
293+
<WinCard key={win._id} win={win} />
294+
))}
295+
</div>
296+
<div className="mt-8 p-8 text-center rounded-2xl border border-dashed border-zinc-800 bg-zinc-900/20">
297+
<Trophy className="h-10 w-10 text-zinc-700 mx-auto mb-4" aria-hidden="true" />
298+
<h3 className="text-lg font-semibold text-white mb-2">
299+
Ready to land your win here?
300+
</h3>
301+
<p className="text-zinc-400 text-sm mb-6 max-w-md mx-auto">
302+
Run <code className="font-mono text-violet-400">loopkit celebrate --share</code>{" "}
303+
after your next <code className="font-mono text-violet-400">loop</code> and replace one of these samples with your own.
304+
</p>
305+
<Link
306+
href="/"
307+
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-white text-black text-sm font-medium hover:bg-zinc-200 transition-colors"
308+
>
309+
Start shipping
310+
<ChevronRight className="h-4 w-4" aria-hidden="true" />
311+
</Link>
312+
</div>
313+
</>
237314
) : (
238315
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
239-
{wins.map((win) => (
316+
{displayedWins.map((win) => (
240317
<WinCard key={win._id} win={win} />
241318
))}
242319
</div>

0 commit comments

Comments
 (0)