A job-application tracker.
Per application: company, role, salary, work mode, job URL, job description, contact, the resume PDF and cover letter you sent, and a timeline of status changes (applied → screening → interviewing → offer / accepted / rejected / withdrawn / ghosted). Interview events additionally store a date/time and format (phone / video / onsite).
A sortable, filterable table backs the dashboard. Search by company or role, narrow by status, edit fields inline. Typing a company name in the new-application form filters the table to existing entries at that company.
Paste a job posting URL into the new-application form and Import to autofill it: the server fetches the page and reads its schema.org JobPosting metadata (with OpenGraph/heading fallbacks) to pull the company, role, job description, salary, and work mode. The fields prefill the row (company and role stay editable) before you save. No external API or key — it's free; accuracy is best on ATS/career sites that publish structured data, and login-gated or JS-only boards may not be readable.
Each row expands into a detail view with the full field set and an event timeline. Every status change becomes an event with an optional note.
A Sankey diagram shows how applications transition between statuses; searching highlights the matching paths and mutes the rest. A 90-day heatmap on the stats row shows daily application volume.
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Server Actions) |
| UI | Tailwind CSS v4, shadcn/ui, Lucide |
| Database | PostgreSQL + Drizzle ORM |
| Auth | Clerk |
| Charts | @nivo/sankey; custom SVG heatmap |
| Hosting | Railway — Next.js, Postgres, bucket storage |
- Every query against
applicationsandapplication_eventsfilters byuser_id. The denormalizeduser_idon events skips a join. - Resumes and cover letters live in Railway buckets via signed URLs; Postgres stores keys + metadata.
- Forward-only Drizzle migrations. Railway runs
drizzle-kit migrateas a release step before traffic switches.
cp .env.example .env.local # paste Clerk keys
pnpm install
docker compose up -d # local Postgres
pnpm db:migrate
pnpm dev # http://localhost:3000Requires Node 20+, pnpm, Docker.
| Command | Purpose |
|---|---|
pnpm dev |
Dev server |
pnpm build / pnpm start |
Production build / serve |
pnpm lint |
ESLint |
pnpm test |
Vitest (needs the local Postgres container) |
pnpm db:generate |
Generate a migration from schema changes |
pnpm db:migrate |
Apply pending migrations |
pnpm db:studio |
Drizzle Studio |
pnpm format |
Prettier |


