James Chang / Work / Fantastic Leagues / Dashboard
The Fantastic Leagues · Product · Last Updated
Admin Dashboard
Two prompts became one. The lesson: the best prompts encode what you already know about your system’s constraints.
The first version of this dashboard was built from two separate prompts — a 1,847-word architectural spec and a 762-word AI insights integration. Together they described an enterprise-grade system: Stripe-level dashboards, seed data generators, bucketed counts, cross-tenant flags.
The problem? This is a single-league fantasy app with 14 users. About 60% of the spec was irrelevant. The refined prompt below is what I actually use now — it names the exact metrics, calls out the real constraints (Supabase connection pool), and integrates the insight engine from the start instead of as a follow-up.
claude "Build an executive admin dashboard with Stripe/Linear pattern"
What it specified
- Hero metric + 6–8 stat tiles in 3-col grid + funnels + activity feed
- Tooltips, skeleton loading, empty states, dashed borders
- Coalescing cache with stampede prevention, SQL-level aggregation
- Okabe-Ito colorblind palette,
tabular-nums,isAnimationActive={false} - Seed data: 5 hero accounts, deterministic PRNG, gated by env var
- Security: admin-only middleware, rate limiting, bucketed count ranges
What was overkill
- Seed data generator — 14 real users, no need for synthetic data
- Bucketed counts — privacy theatre for a single-league app
- Cross-tenant flags — there’s one tenant
- 6–8 generic tile slots — should name the exact metrics
claude "Add contextual AI insights to the admin page"
What it added
- Rule-based insight engine:
(dashboardData) => { priority, analysis, action } - Priority-colored borders: high=red, medium=amber, low=blue
- Claude API enrichment on weekly cron, stored as JSON
- Sparkle icon for AI-generated, arrow icon before actions
The problem
- Separate prompt — insights should be designed alongside the tiles, not bolted on
- No fallback rules — tiles went empty when no rule matched
claude "Build an executive admin dashboard at /admin/dashboard"
Reference: Stripe Dashboard, Linear Analytics.
What to build
- Hero metric: Active Users, text-6xl, delta badge, sparkline — “is the business healthy?”
- 6 stat tiles (3-col): Total Users, Sessions, Seasons, Trades, AI Insights, Roster Moves
- Each tile: big number, delta %, subtitle, sparkline, tooltip, drill-through, inline AI insight
- Three states: skeleton, empty (dashed + “Collecting data...”), populated
- 3 funnels: Onboarding (Signup → Profile → Team) · Season Lifecycle · Retention (30d/7d)
- Activity feed: last 10 audit log entries
Architecture constraints
- Server: single
GET /api/admin/dashboard?days=30. All queries viaprisma.$transaction(Supabase pool is small). Coalescing cache (5-min TTL). Sparklines computed sequentially to avoid pool exhaustion. - Client: lazy-loaded route,
React.memoon charts,isAnimationActive={false},debounce={150}
Insight engine
- Every tile gets exactly 1 insight. Rule-based, zero API cost.
- Every rule has a fallback branch — no tile is ever empty
- Examples: Users delta ≤ 0% → HIGH “Acquisition stalled” → “Launch invite campaign”
- Trades == 0 → MEDIUM “Quiet market” → “Post to trading block”
Tech notes
- Recharts (sparklines only), existing design tokens (
--lg-*), lucide-react icons tabular-nums+tracking-tighton all numbers- Admin-only via
requireAuth+requireAdminmiddleware
The takeaway
The original prompt was architecturally comprehensive — it described a system that would work at Stripe scale. But for a single-league fantasy app with 14 users, ~60% of the spec was irrelevant: seed data, bucketed counts, cross-tenant flags.
The refined prompt is context-aware. It names the exact metrics (Users, Sessions, Trades — not “6–8 tiles”), calls out the real constraints (Supabase’s connection pool is small, so sparklines must be sequential), and integrates the insight engine from the start instead of bolting it on as a follow-up.
The lesson: The best prompts encode what you already know about your system’s constraints, not what a generic enterprise system might need.