You have built a CLI tool and a REST API with AI. Now let us go full-stack. You are going to build a complete analytics dashboard with a frontend, backend, database, authentication, and charts — all in one project.
The project: DevPulse — a developer activity dashboard that tracks coding sessions, displays charts, and lets users manage their data. It uses Next.js, Tailwind CSS, PostgreSQL, and Auth.js for authentication.
This project uses three AI tools together: Cursor Composer for scaffolding and UI, Claude Code for backend logic and database work, and Copilot for inline completions. You will see how they complement each other in a real workflow.
Step 1: Design in Plain English
Before writing any code, describe every screen. AI generates better code when it understands the full picture.
Prompt to Claude Code:
I want to build a developer activity dashboard called "DevPulse"
using Next.js 15 with the App Router.
Screens:
1. Login page — email/password with Auth.js
2. Dashboard — summary cards (total sessions, hours coded,
streak) and a line chart showing coding hours per day
for the last 30 days
3. Sessions page — table of all coding sessions with
date, duration, language, and notes. Add/edit/delete.
4. Settings page — update name, email, change password
Tech stack:
- Next.js 15 (App Router, Server Components)
- TypeScript (strict mode)
- Tailwind CSS v4 for styling
- PostgreSQL with Prisma
- Auth.js v5 for authentication
- Recharts for data visualization
- Shadcn/ui for component library
Write a PRD with the database schema, page layouts,
and API routes. Do not write any code yet.
Claude Code generated a detailed PRD with the database schema, all page layouts described in plain English, and a list of API routes.
What I reviewed and changed:
- Removed a “Teams” feature from the PRD. AI added it because dashboards often have teams. But this is a personal tool — no teams needed.
- Changed the session duration from a single integer (minutes) to start and end timestamps. This lets us calculate duration and also show time-of-day patterns in charts.
- Added a
languageenum instead of a free-text field. This prevents “JavaScript” vs “javascript” vs “JS” inconsistencies.
Step 2: Scaffold with Cursor Composer
Cursor Composer is excellent at generating project structure and boilerplate. I used it for the initial scaffold.
Prompt in Cursor Composer:
Create a Next.js 15 project with App Router. Set up:
- TypeScript strict mode
- Tailwind CSS v4
- Prisma with PostgreSQL
- Auth.js v5 with credentials provider
- Shadcn/ui initialized with the default theme
- Project structure with app/(auth) and app/(dashboard)
route groups
Cursor Composer created the project structure in one pass:
devpulse/
├── app/
│ ├── (auth)/
│ │ ├── login/page.tsx
│ │ └── register/page.tsx
│ ├── (dashboard)/
│ │ ├── layout.tsx # Sidebar + header
│ │ ├── page.tsx # Dashboard home
│ │ ├── sessions/page.tsx # Sessions table
│ │ └── settings/page.tsx # User settings
│ ├── api/
│ │ └── auth/[...nextauth]/route.ts
│ ├── layout.tsx
│ └── globals.css
├── components/
│ ├── ui/ # Shadcn components
│ ├── dashboard/
│ │ ├── summary-cards.tsx
│ │ ├── activity-chart.tsx
│ │ └── recent-sessions.tsx
│ └── sessions/
│ ├── session-table.tsx
│ └── session-form.tsx
├── lib/
│ ├── prisma.ts
│ ├── auth.ts
│ └── utils.ts
├── prisma/
│ └── schema.prisma
├── tailwind.config.ts
├── next.config.ts
└── package.json
What I checked:
- The
tsconfig.jsonhadstrict: true - Auth.js was version 5 (not v4 — the API changed significantly)
- Route groups
(auth)and(dashboard)were set up correctly - Prisma client was properly configured as a singleton
Step 3: Design and Styling with Tailwind
This is where AI shines — and where it needs the most guidance. AI generates functional UI, but the default output often looks like a generic template.
Prompt to Cursor:
Style the dashboard layout:
- Sidebar: dark background (#0f172a), white text,
icon + label for each nav item, active state with
a left border accent in blue (#3b82f6)
- Header: sticky top, shows user name and avatar on
the right, breadcrumb on the left
- Main content: light gray background (#f8fafc),
max-width 1200px centered
- Cards: white background, subtle shadow, rounded-lg,
12px padding
Use Tailwind classes only. No custom CSS.
Follow the Shadcn default theme for buttons and inputs.
What AI got right:
- The layout structure was solid — sidebar, header, main content area
- Responsive breakpoints worked out of the box
- Tailwind classes were correct and well-organized
What I had to fix manually:
- The sidebar did not collapse on mobile. I added a hamburger menu toggle.
- The active navigation state used
bg-blue-500(too bright) instead of a subtle left border. I changed it to match my specification. - Dark mode was not implemented at all. I did not ask for it, so that is fine — but be aware that AI will not add dark mode unless you ask.
The key lesson: be specific about visual details. “Make it look good” gives you generic output. “Dark sidebar with #0f172a background, blue accent border on active items” gives you exactly what you want.
Step 4: Connect the Database with Claude Code
I switched to Claude Code for the backend work. It handles database logic, migrations, and server actions well.
Prompt:
Read the PRD and the Prisma schema. Implement:
1. Server actions for sessions CRUD:
- createSession(data)
- updateSession(id, data)
- deleteSession(id)
- getSessions(userId, page, limit)
- getSessionStats(userId) — total sessions,
total hours, current streak, daily hours
for the last 30 days
2. All server actions must:
- Verify the user is authenticated
- Validate input with Zod
- Check ownership (user can only access own data)
- Return typed responses
Use Next.js Server Actions with "use server" directive.
Claude Code generated clean server actions. The stats query was efficient — it used a single Prisma query with groupBy to get daily hours instead of fetching all sessions and computing in JavaScript.
Issue found and fixed:
The currentStreak calculation was wrong. Claude Code counted consecutive days with sessions, but it counted from the first session ever — not from today backwards. If you coded on Monday, Tuesday, and Thursday, it returned a streak of 2 (Monday-Tuesday) instead of 1 (Thursday only).
I described the bug and Claude Code fixed it in one prompt:
The streak calculation is wrong. It should count
consecutive days backwards from today. If today is
Thursday and the user coded Thursday and Wednesday
but not Tuesday, the streak is 2. Fix getSessionStats.
Step 5: Add Charts and Data Visualization
Prompt to Cursor:
Create the activity chart component using Recharts.
Requirements:
- Line chart showing coding hours per day for the last 30 days
- X-axis: dates (show every 5th date label to avoid crowding)
- Y-axis: hours (0 to max hours, rounded up)
- Tooltip showing exact date and hours on hover
- Responsive — fills container width
- Use the blue accent color (#3b82f6) for the line
- Fill area under the line with a gradient (blue to transparent)
- Show a "No data" message if all values are zero
Data comes from the getSessionStats server action.
The component receives the data as a prop — do not
fetch inside the component.
Cursor generated a working chart component. Recharts is well-represented in training data, so AI handles it well.
What I fixed:
- The date labels overlapped on mobile. I added
tick={{ fontSize: 12 }}and reduced labels on small screens. - The gradient fill used an inline SVG
<defs>block that was not wrapped in the<ResponsiveContainer>. This caused a hydration mismatch between server and client rendering. I moved the chart to a client component with"use client".
Step 6: Add Authentication with Auth.js
Authentication deserves its own step because security-critical code needs careful review.
Prompt to Claude Code:
Set up Auth.js v5 with credentials provider.
Requirements:
- Email/password login
- Registration endpoint (server action)
- Password hashing with bcrypt (12 rounds)
- Session strategy: JWT
- Protected routes: all (dashboard) routes require auth
- Middleware to redirect unauthenticated users to /login
- Session includes: userId, email, name
Use the Auth.js v5 API (not v4). The configuration
goes in lib/auth.ts and the middleware in middleware.ts.
Security review — what I checked:
- Password hashing: bcrypt with 12 rounds — correct.
- Session callback: The JWT callback correctly added
userIdto the token, and the session callback exposed it. No password hash leaked into the session. - Middleware matcher: Protected all
/dashboardroutes but excluded/login,/register, and/api/auth. Correct. - CSRF protection: Auth.js v5 handles this automatically. Verified it was not disabled.
What I fixed:
- The registration server action did not check for existing emails before creating the user. Prisma would throw a unique constraint error, but the error message exposed the database field name. I added an explicit check with a user-friendly error message.
- The credentials provider returned
nullon invalid credentials without a clear error. I added specific error messages for “user not found” and “wrong password” — but displayed the same generic “Invalid credentials” to the user (to prevent user enumeration).
Step 7: Deploy to Vercel
Prompt:
Create deployment configuration for Vercel.
Requirements:
- vercel.json with build settings
- Environment variables list (.env.example)
- Database: use Vercel Postgres (or Neon — pick one)
- Run Prisma migrations on build
- Add a postinstall script for Prisma generate
Also create a GitHub Actions workflow that:
- Runs TypeScript type checking
- Runs ESLint
- Runs tests (when we add them)
- Deploys to Vercel on push to main
Claude Code generated the configuration files. The postinstall script for Prisma was important — Vercel’s build process needs prisma generate to run before the Next.js build.
What I fixed:
- Database URL format. Claude Code used a standard PostgreSQL connection string, but Vercel Postgres uses a
?sslmode=requireparameter. I added it. - Build command. The Prisma migration needed to run before the build, not after. I changed the build script to
prisma migrate deploy && next build. - Environment variables. Claude Code listed 4 variables. I added
NEXTAUTH_URLandNEXTAUTH_SECRET— both required by Auth.js in production.
Using Three Tools Together
This project demonstrated how different AI tools complement each other:
| Tool | Best at | Used for |
|---|---|---|
| Cursor Composer | Scaffolding, UI generation | Project setup, component creation, styling |
| Claude Code | Backend logic, database, debugging | Server actions, auth, data queries, deployment |
| Copilot | Inline completions, small edits | Filling in repetitive code, type definitions |
The workflow: Cursor Composer for big structural changes, Claude Code for logic and debugging, Copilot for the small stuff in between. Switching between tools feels natural once you know each tool’s strengths.
When tools disagreed:
Cursor and Claude Code sometimes generated different import paths or slightly different patterns. For example, Cursor imported Prisma as import { prisma } from "@/lib/prisma" while Claude Code used import { prisma } from "../lib/prisma". Keep your import conventions consistent — pick one style and mention it in your context files.
Time Tracking
| Step | Time | AI vs Manual |
|---|---|---|
| PRD and design | 10 min | AI: 6 min, Review: 4 min |
| Scaffold | 8 min | AI: 5 min, Review: 3 min |
| Styling | 25 min | AI: 10 min, Fix: 15 min |
| Database + server actions | 20 min | AI: 8 min, Review + fix: 12 min |
| Charts | 15 min | AI: 8 min, Fix: 7 min |
| Authentication | 20 min | AI: 8 min, Review + fix: 12 min |
| Deployment | 12 min | AI: 5 min, Fix: 7 min |
| Total | 110 min | AI: 50 min, Human: 60 min |
A full-stack dashboard in under two hours. Without AI, this project would take 2-3 days. The styling and authentication steps took the most human time — styling because visual details need manual refinement, and authentication because security code needs careful review.
Key Takeaways
- Multiple AI tools work well together when you know each tool’s strength. Use the right tool for the right job.
- Be specific about design. Color codes, spacing values, and layout rules produce better results than vague descriptions.
- Server Components and Server Actions are AI-friendly patterns. The clear separation of server and client code helps AI generate correct code.
- Auth.js v5 is different from v4. If AI generates v4 syntax, catch it early. Specify the version in your prompts.
- Charts need client components. Recharts and similar libraries require
"use client"— AI sometimes forgets this in a Server Components project.
What’s Next?
In the next article, you will build a mobile app with AI using Kotlin and Jetpack Compose. Mobile development brings unique challenges — AI tools struggle more with platform-specific APIs, and you will see how to handle that.
Part 17 of the Vibe Coding series.