ApplyVibe — Job Application Tracker
Free-first full-stack SaaS for job seekers — Kanban pipeline, analytics dashboard, smart insights, and Auth.js auth. Built with Next.js 16, Prisma, and Neon PostgreSQL.
Architecture flow
Problem
Job seekers applying to hundreds of roles had no clean, free tool to track applications, visualize their pipeline, or understand what was and wasn't working. Existing tools were paid, overly complex, or just spreadsheets.
Students applying to hundreds of internships and full-time roles need to track stage progression, follow-up deadlines, and which sources are actually converting — not just a static list of companies. Notion templates and spreadsheets don't surface patterns or send reminders.
Solution
Built a purpose-built Next.js 16 full-stack app with server components, credentials auth, Prisma ORM, Neon PostgreSQL, and a full analytics + Kanban layer — all on the free tier, self-hostable, open-source.
Architecture
Frontend: Next.js 16 App Router — server components for dashboard, client hooks for Kanban/Analytics
Backend: Next.js API routes — 6 routes (register, applications CRUD, profile) with Zod validation
Infra: Neon PostgreSQL (serverless) + Prisma ORM + Vercel deploy + Auth.js JWT sessions
Key Features
- Auth.js v5 credentials auth with bcrypt + JWT + proxy.ts route protection
- Full application CRUD: company, role, source, stage, salary, visa, deadlines, recruiter info
- 11-stage dnd-kit Kanban board with live API sync on drag
- Analytics: funnel chart, weekly trend, source performance, KPI cards
- Smart Insights engine — rule-based analysis surfacing actionable patterns
- Settings: profile form, theme switcher (light/dark/system) via next-themes
Authentication System
Auth is fully custom — no third-party identity providers, no vendor lock-in. Users sign up with name, email, and password (bcrypt 12 rounds). Auth.js v5 issues a JWT session on successful login.
Route protection runs in proxy.ts (Next.js 16 Middleware) — every request to /dashboard, /kanban, /analytics, and /settings is checked at the edge — unauthenticated users are redirected to /login before the page renders.
The PrismaAdapter is already wired to the Auth.js config — adding OAuth providers (GitHub, Google) in the future requires only provider configuration, not a schema change.
Kanban Board
The Kanban board spans 11 columns: Saved → Applied → OA → Recruiter Screen → Interview 1 → Interview 2 → Final Round → Offer → Rejected → Ghosted → Withdrawn.
Built with dnd-kit using a PointerSensor with an 8px activation threshold. When a card is dropped, a PATCH /api/applications/[id] call fires immediately — UI updates optimistically so there's no visible lag.
Analytics Dashboard
The dashboard is a server component — data is fetched at request time from Neon PostgreSQL via Prisma, so the initial render always shows fresh data with no client loading flash.
- 5 KPI cards — total applications, interviews, offers, rejections, response rate
- 7-day activity area chart — applications submitted per day
- Pipeline distribution donut — proportion of applications at each stage
- Source performance bars — which job board produces the best response rate
- Smart Insights engine — rule-based analysis: low response rate warnings, overdue follow-ups, OA bottleneck detection
Challenges & Decisions
1.Next.js 16 renamed middleware.ts → proxy.ts
Problem
Route protection was silently failing — requests showed 0ms response time and a deprecation warning appeared: "middleware file convention is deprecated."
Fix
Renamed to proxy.ts and changed to export default (named exports also break in this convention).
2.Turbopack crashing in dev
Problem
npm run dev crashed with FATAL: An unexpected Turbopack error every few requests.
Fix
Added --webpack flag to the dev script. Next.js 16 enables Turbopack by default; --webpack forces stable Webpack for local dev only. Production builds unaffected.
3.Prisma generate failing on Vercel during npm install
Problem
Three root causes stacked: no postinstall script, Prisma's env() throwing before env vars were injected, then old commits being deployed.
Fix
Removed postinstall entirely. Changed prisma.config.ts to use process.env.DATABASE_URL ?? "postgresql://localhost/placeholder" — prisma generate doesn't connect to the DB so a fallback URL is safe.
4.Analytics funnel showing wrong counts
Problem
The stageOrder array only had 5 stages — indexOf() returned -1 for interview_2, final_round, rejected, ghosted, withdrawn, making those applications invisible.
Fix
Rewrote funnel logic with 7 pipeline stages and explicit handling of terminal stages — rejected/ghosted/withdrawn count in the 'applied' bucket since stage history isn't stored yet.
5.PATCH API had no validation
Problem
The PATCH route built updateData from raw request body — any string could be set as currentStage (breaking DB enums), and an empty body produced a 500 from Prisma.
Fix
Added applicationSchema.partial() Zod validation at the top of PATCH. Empty updateData returns 400. All fields validated against proper enums before touching the DB.
6.dnd-kit DragOverlay duplicate ID bug
Problem
DragOverlay rendered KanbanCard which called useDraggable({ id: app.id }) — two components shared the same dnd-kit ID, causing flaky drag behavior and console warnings.
Fix
Split into KanbanCardContent (pure JSX, no hooks) and KanbanCardOverlay (wraps content, no useDraggable). The draggable card registers the ID; the overlay is a visual clone only.
Design Note
Forest green palette (#245501 → #aad576) to feel calm and growth-oriented — opposite of the anxiety that job hunting usually triggers. snake_case throughout frontend types with explicit dbToApp() mapping from Prisma's camelCase.
Data models use snake_case throughout frontend types (company_name, current_stage) with an explicit dbToApp() mapping from Prisma's camelCase.
Impact
- 0 TypeScript errors across the full codebase — strict mode throughout.
- Auth secured with bcrypt 12 rounds + JWT sessions + middleware route protection on all app pages.
- 4 critical + 4 high + 6 medium bugs fixed during development — all documented with root cause and decision.
- ~12s clean build on Vercel — server components keep dashboard data fresh at request time, no client loading flash.
- Analytics funnel correctly handles all 11 stages including terminal states (rejected/ghosted/withdrawn).
What I'd improve next
senior signal- P0Stage history: Write to
ApplicationEventon every Kanban drag. Schema already exists. - P0Rate limiting: Upstash Redis or Vercel Edge Config on
/api/register. - P1Reflections + Reminders: Schema already exists, just needs UI.
- P1React Query / SWR: Replace manual
useApplicationshook with a proper cache layer. - P2OAuth: GitHub and Google — PrismaAdapter already wired, just needs provider config.
- P2CSV export + Prisma enums for
currentStage/source.