A Linear-inspired job application tracker built with Next.js 16 and Firebase.
- Add jobs via URL (auto-scrapes title, company, location) or manual form
- Markdown editor for job description and notes/timeline
- Kanban board with drag-and-drop status columns
- Table view with inline status updates
- Command palette (βK) to search across all jobs
- Status timeline history per job
- Contacts per job with email/LinkedIn
- Stats page: funnel, source breakdown, tag cloud, KPIs
- CSV export
- Google + email auth
- Real-time Firestore sync
- Framework: Next.js 16 (App Router)
- Backend: Firebase (Firestore, Auth, Storage)
- Styling: Tailwind CSS
- Drag & drop: @dnd-kit
- Search: cmdk
- Charts: recharts
npx create-next-app@latest job-hunt --typescript --tailwind --app --src-dir=no --import-alias="@/*"
cd job-huntnpm install firebase @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities cmdk recharts- Go to Firebase Console β New project
- Enable Firestore (production mode)
- Enable Authentication β Google + Email/Password
- Enable Storage
- Copy your web app config
cp .env.local.example .env.local
# Fill in your Firebase valuesnpm install -g firebase-tools
firebase login
firebase init # select Firestore, use existing project
firebase deploy --only firestoreDrop all files from this repo into your Next.js project maintaining the folder structure.
npm run devOpen http://localhost:3000.
job-hunt/
βββ app/
β βββ (auth)/
β β βββ login/page.tsx
β β βββ layout.tsx
β βββ (dashboard)/
β β βββ layout.tsx β sidebar + topbar shell
β β βββ page.tsx β dashboard overview
β β βββ jobs/
β β β βββ page.tsx β job list (table + kanban toggle)
β β β βββ [id]/page.tsx β job detail
β β β βββ new/page.tsx β add job (URL or manual)
β β βββ contacts/page.tsx β people at companies
β β βββ stats/page.tsx β charts, funnel, heatmap
β β βββ settings/page.tsx
β βββ api/
β βββ scrape/route.ts β URL β job metadata
β βββ export/route.ts β CSV export
β
βββ components/
β βββ jobs/
β β βββ JobCard.tsx
β β βββ JobTable.tsx
β β βββ KanbanBoard.tsx
β β βββ JobForm.tsx β manual entry + markdown editor
β β βββ StatusBadge.tsx
β β βββ TagPill.tsx
β βββ ui/ β shadcn/ui components
β βββ layout/
β β βββ Sidebar.tsx
β β βββ CommandPalette.tsx β Cmd+K search
β βββ shared/
β βββ MarkdownEditor.tsx β for description field
β βββ FilterBar.tsx
β
βββ lib/
β βββ firebase/
β β βββ config.ts
β β βββ jobs.ts β CRUD operations
β β βββ contacts.ts
β β βββ auth.ts
β βββ hooks/
β β βββ useJobs.ts
β β βββ useSearch.ts
β β βββ useFilters.ts
β βββ utils/
β βββ scraper.ts
β βββ formatters.ts
β
βββ types/
β βββ index.ts β Job, Contact, Tag, Status types
β
βββ functions/ β Firebase Cloud Functions
βββ src/
β βββ scrapeJob.ts
β βββ sendReminder.ts
βββ package.json
| Shortcut | Action |
|---|---|
| βK | Open command palette |
| βN | New job (wire up in layout) |
| Esc | Close palette |
Call POST /api/export with { jobs: Job[] } in the body. Returns a .csv file.
Add an export button in the jobs page:
async function handleExport() {
const res = await fetch("/api/export", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jobs: allJobs }),
});
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "jobs.csv";
a.click();
}NEXT_PUBLIC_FIREBASE_API_KEY = your_api_key;
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN = your_auth_domain;
NEXT_PUBLIC_FIREBASE_PROJECT_ID = your_project_id;
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET = your_storage_bucket;
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID = your_messaging_sender_id;
NEXT_PUBLIC_FIREBASE_APP_ID = your_app_id;













