What this gives you: One screen that shows your entire business. Revenue from Stripe, leads from your CRM, content performance, pipeline status - all pulling live data in real-time. No more switching between 10 tabs to understand how things are going.
Paste this guide into Claude Code and it will walk you through every step interactively.
Why You Need a Command Centre
An AI assistant that lives only in a terminal is powerful but fragile. Every piece of business context - revenue, pipeline, content queue, client status - lives scattered across 8 different tabs. When you open Claude Code to do something, half the time you spend is gathering that context manually before you can even start.
A dashboard solves this. One URL. Every metric that matters, live, server-rendered, on a single screen. Revenue from Stripe. Pipeline from GHL. Content queue from Trello. Upcoming calls from Calendar. Open invoices from your accounting tool. When you open Claude Code with your dashboard alongside it, you have full business context in 10 seconds. No tab-switching, no manual data fetching, no copy-paste.
The second function is the pricing engine. Most service businesses price inconsistently - different quotes for similar work, forgotten line items, no record of what was charged when. A dashboard-embedded pricing tool fixes this: a structured form with your service components, complexity multipliers, and bundle logic. Same input, same output, every time. When a prospect asks "what would this cost?" you have an answer in 30 seconds, not 30 minutes.
The third function is Claude-powered quote generation. Paste a meeting transcript, click generate - Claude reads the transcript, identifies what services were discussed, maps them to your pricing components, and produces a draft proposal. Your dashboard becomes the interface where rough calls become structured quotes.
The Stack
The stack is deliberately minimal. Every choice is driven by two constraints: fast to build, zero infrastructure to manage.
Technology
Role in the Stack
Next.js
App Router, TypeScript. Server components handle all data fetching and keep API keys off the browser. API routes give you server-side endpoints for webhooks and data aggregation.
Tailwind CSS
Utility classes, dark theme by default. No CSS files to maintain. The design system lives in the component classes.
Vercel
Deploy by pushing to git. Zero config. Automatic HTTPS. Environment variables stored in the Vercel dashboard, never in code.
No database
All data is fetched live from source APIs (Stripe, GHL, Beehiiv) at request time via server components. No sync jobs, no stale data, no database to maintain.
The dashboard contains live revenue and pipeline data - it needs to be behind auth. A full OAuth implementation is overkill for a private internal tool. A simple password check with a cookie is the right call: takes 30 minutes to implement, impossible to accidentally forget to apply to new pages.
// middleware.ts - runs on every requestimport { NextResponse } from 'next/server'import type { NextRequest } from 'next/server'export function middleware(request: NextRequest) {
// Skip auth check for the login page and auth API
const { pathname } = request.nextUrl
if (pathname === '/' || pathname.startsWith('/api/auth')) {
return NextResponse.next()
}
// Check for auth cookie
const auth = request.cookies.get('dashboard-auth')
if (!auth || auth.value !== process.env.AUTH_COOKIE_VALUE) {
return NextResponse.redirect(new URL('/', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/api/live-data']
}
Tip: Generate a random AUTH_COOKIE_VALUE - something like a 32-character random string. This is what gets stored in the cookie and checked on every request. Store both DASHBOARD_PASSWORD and AUTH_COOKIE_VALUE in Vercel's environment variables, not in code.
Server-Side API Routes: Keys Never Hit the Browser
Every API call that needs a secret key goes through a Next.js API route or server component. The browser never sees a secret. This is not just good security hygiene - it is the architecture that makes the dashboard maintainable. API keys rotate, services change, and when all your external calls are in one place (lib/stripe.ts, lib/ghl.ts), you update one file instead of hunting through components.
The pricing engine is a structured set of components with defined costs, multipliers, and bundle logic. It lives in lib/pricing.ts and is used both by the interactive pricing tool in the dashboard and by the Claude-powered quote generation endpoint.
The quote generation endpoint takes a raw meeting transcript, sends it to Claude via the Anthropic API, and gets back a structured quote recommendation that maps the discussion to your exact service components and pricing.
// app/api/generate-quote/route.tsimport Anthropic from '@anthropic-ai/sdk'import { SERVICES, COMPLEXITY_MULTIPLIERS, calculateQuote } from '@/lib/pricing'const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })
export async function POST(req: Request) {
const { transcript } = await req.json()
const serviceList = Object.entries(SERVICES)
.map(([key, s]) => `- ${key}: ${s.label} (£${s.setup} setup, £${s.monthly}/mo) - ${s.description}`)
.join('\n')
const message = await client.messages.create({
model: 'claude-opus-4-5',
max_tokens: 1024,
messages: [{
role: 'user',
content: `You are a pricing assistant. Analyse this sales call transcript and recommend which services to include in the quote.
Available services:
${serviceList}
Complexity levels: simple (standard), moderate (custom logic), complex (legacy integrations), enterprise (custom dev)
Transcript:
${transcript}
Respond with JSON only:
{
"services": ["service_key_1", "service_key_2"],
"complexity": "simple|moderate|complex|enterprise",
"reasoning": "one sentence explaining the recommendation",
"notes": "any special requirements or flags from the transcript"
}`
}]
})
const raw = (message.content[0] as any).text
const parsed = JSON.parse(raw)
const quote = calculateQuote(parsed.services, parsed.complexity)
return Response.json({ ...quote, ...parsed })
}
Want a custom AI dashboard built for your business?
Live revenue, pipeline, content queue, and a pricing engine - deployed in a week.
The deployment workflow is deliberately simple: push to the main branch and Vercel deploys automatically. No CI/CD config, no Docker, no SSH. Vercel detects the Next.js project, builds it, and deploys it to a global edge network. HTTPS is automatic. Custom domain setup takes 2 minutes.
Initial setup
Push your project to a GitHub repository (private is fine)
Vercel auto-detects Next.js and sets the build command
Add all environment variables in Project Settings > Environment Variables
Deploy - first deploy takes 60-90 seconds
Environment variables in Vercel
# These go in Vercel dashboard, NOT in your codeSTRIPE_SECRET_KEY=sk_live_...
GHL_API_KEY=eyJhbGci...
GHL_LOCATION_ID=abc123xyz
BEEHIIV_API_KEY=bh_...
BEEHIIV_PUB_ID=pub_...
ANTHROPIC_API_KEY=sk-ant-...
DASHBOARD_PASSWORD=your-password
AUTH_COOKIE_VALUE=random-32-char-string
Warning: In Next.js, any environment variable you want to access in server code must NOT be prefixed with NEXT_PUBLIC_. Variables prefixed with NEXT_PUBLIC_ are bundled into the browser JavaScript. API keys should never have that prefix.
Subsequent deploys
# Make changes, then:git add .
git commit -m "add content queue section"git push origin main
# Vercel picks it up automatically - live in ~60 seconds
If the automatic deploy doesn't trigger (rare but happens), use vercel --prod from the project directory as a fallback. Install the Vercel CLI with npm i -g vercel and run vercel login once.
Branch previews
Every non-main branch gets its own preview URL automatically. Push a dev branch and get your-project-dev.vercel.app. This makes testing changes safe - verify the preview URL works before merging to main.
Tip: Add your dashboard URL to your CLAUDE.md under Key File Locations. When Claude knows your dashboard URL, it can reference it in responses, and you can tell it "check my dashboard" as part of a larger workflow - for example, having it summarise the key metrics it can see from the live-data endpoint.