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 language enum 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.json had strict: 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:

  1. Password hashing: bcrypt with 12 rounds — correct.
  2. Session callback: The JWT callback correctly added userId to the token, and the session callback exposed it. No password hash leaked into the session.
  3. Middleware matcher: Protected all /dashboard routes but excluded /login, /register, and /api/auth. Correct.
  4. 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 null on 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:

  1. Database URL format. Claude Code used a standard PostgreSQL connection string, but Vercel Postgres uses a ?sslmode=require parameter. I added it.
  2. Build command. The Prisma migration needed to run before the build, not after. I changed the build script to prisma migrate deploy && next build.
  3. Environment variables. Claude Code listed 4 variables. I added NEXTAUTH_URL and NEXTAUTH_SECRET — both required by Auth.js in production.

Using Three Tools Together

This project demonstrated how different AI tools complement each other:

ToolBest atUsed for
Cursor ComposerScaffolding, UI generationProject setup, component creation, styling
Claude CodeBackend logic, database, debuggingServer actions, auth, data queries, deployment
CopilotInline completions, small editsFilling 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

StepTimeAI vs Manual
PRD and design10 minAI: 6 min, Review: 4 min
Scaffold8 minAI: 5 min, Review: 3 min
Styling25 minAI: 10 min, Fix: 15 min
Database + server actions20 minAI: 8 min, Review + fix: 12 min
Charts15 minAI: 8 min, Fix: 7 min
Authentication20 minAI: 8 min, Review + fix: 12 min
Deployment12 minAI: 5 min, Fix: 7 min
Total110 minAI: 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.