A ready-to-run Next.js (App Router) + Tailwind + Firebase/Firestore app for tracking your job applications. It includes Google sign‑in, an Applications table with search/filter/sort, edit/delete, a Dashboard with charts (Recharts), CSV import, and a per‑application status timeline drawer.
-
Auth: Google sign‑in (client‑side) with domain/redirect guardrails.
-
Applications
- Add new roles (with Notes and optional Rejection Reason).
- List with search, status filter, and sorting by Create Date, Update Date, or Status.
- Inline status change (records a status event + updates
statusUpdatedAt). - Details drawer with a timeline chart linking all historical statuses by their update date.
- Edit & Delete existing applications.
-
Dashboard
- Daily / Weekly / Monthly application counts.
- Status updates per day timeline.
- Rejection reasons pie chart.
- (Also includes simple funnel/status & weekly trend components.)
-
CSV Import (via PapaParse)
- Validates required headers and previews row count before import.
- Accepts ISO or epoch
createdAtand parsestagslists.
-
Offline-ready (IndexedDB cache enabled by default).
- Next.js (App Router, React 18)
- Tailwind CSS
- Firebase (Auth + Firestore)
- Recharts (visualizations)
- PapaParse (CSV parsing)
# from your Next.js project root
npm i firebase recharts papaparse
# Tailwind (if not already configured)
npx tailwindcss init -p
npm i -D tailwindcss postcss autoprefixerCreate .env.local (copy from .env.example) and fill from Firebase Console → Project settings → General → Your apps → Web app:
NEXT_PUBLIC_FB_API_KEY=...
NEXT_PUBLIC_FB_AUTH_DOMAIN=...
NEXT_PUBLIC_FB_PROJECT_ID=...
NEXT_PUBLIC_FB_STORAGE=...Restart dev server after editing envs.
- Authentication → Sign‑in method: Enable Google and set a Support email.
- Authentication → Settings → Authorized domains: add
localhost,127.0.0.1, your preview/prod domains (e.g.*.vercel.app). - Firestore: Create a database in production or test mode.
- Indexes: If prompted for an index on
users/{uid}/eventsforwhere(type=='status-change') + orderBy(at), click Create index.
Using emulators? Google OAuth does not work with the Auth Emulator. Use real Auth in dev, or switch to email/password when on the emulator.
npm run devOpen http://localhost:3000 and Sign in from the navbar.
.
├─ app/
│ ├─ layout.tsx
│ ├─ page.tsx # Dashboard
│ ├─ add/page.tsx # Add job
│ ├─ import/page.tsx # CSV import
│ └─ applications/
│ ├─ page.tsx # Table (search/filter/sort + Details drawer)
│ └─ [id]/page.tsx # Edit/Delete
├─ components/
│ ├─ Navbar.tsx
│ ├─ StatusBadge.tsx
│ ├─ ApplicationForm.tsx
│ ├─ ApplicationTable.tsx
│ └─ Charts/
│ ├─ CountsMultiPeriod.tsx # Daily/Weekly/Monthly counts
│ ├─ StatusUpdatesTimeline.tsx # Global: status updates per day
│ ├─ RejectionReasonsPie.tsx # Rejection reasons breakdown
│ ├─ AppStatusTimeline.tsx # Per‑app timeline (used in drawer)
│ ├─ FunnelByStatus.tsx
│ └─ AppsPerWeek.tsx
├─ lib/
│ ├─ firebase.ts
│ ├─ firestore.ts # CRUD + status events helpers
│ ├─ useUser.ts # Stable uid via auth listener
│ ├─ status.ts # Status constants + colors
│ ├─ types.ts # TS models
│ └─ utils.ts # Timestamp helpers, week key
├─ styles/
│ └─ globals.css
├─ tailwind.config.ts
├─ postcss.config.js
└─ .env.example
-
users/{uid}/applications/{appId}- Core:
title(string),company(string),status(enum),location(string?),jobUrl(string?) - Meta:
priority('High'|'Medium'|'Low'?),notes(string?),tags(string[]?) - Timestamps:
createdAt(TS),lastActionAt(TS),statusUpdatedAt(TS) - Outcomes:
rejectionReason(string?)
- Core:
-
users/{uid}/events/{eventId}(status timeline)type:'status-change'appId: stringfrom: Status (optional)to: Statusat: timestamp
statusUpdatedAtmirrors the latest status change and is used for sorting and display.
Saved | Applied | OA | Screen | Tech | Onsite | Offer | Accepted | Rejected
-
Search: title/company/location/notes.
-
Filter: by status.
-
Sort: Create date, Update date (
statusUpdatedAt), or Status (ordered viaSTATUS_ORDER). -
Inline status change: updates document and records an event; sets
statusUpdatedAt. -
Details Drawer: click Details → opens a side drawer with:
- Basic info (status, Status updated date, created date, notes).
- AppStatusTimeline chart showing all historical statuses linked by update time.
- Create a new application with status, notes, and optional rejection reason (if status is
Rejected).
- Edit fields and on status change: add a status event + update
statusUpdatedAt. - Delete: hard delete (optional: switch to soft delete with a
deletedAtflag).
- CountsMultiPeriod: 3 cards — Daily(30), Weekly(12 ISO weeks), Monthly(12 months).
- StatusUpdatesTimeline: global count of status changes per day.
- RejectionReasonsPie: distribution of rejection reasons.
- (Sample funnel and weekly components also included.)
- Upload
.csv→ validates and previews → import. - Required headers:
title, company, status - Optional:
location, jobUrl, createdAt, notes, rejectionReason, priority, jobType, remote, tags createdAt: ISO date (e.g.,2025-09-17) or epoch millis.tags: comma/semicolon/pipe separated → parsed intostring[].
Sample CSV
title,company,status,location,jobUrl,createdAt,notes,rejectionReason,tags
Frontend Engineer,Acme,Applied,Remote,https://acme/jobs/fe,2025-09-01,Referral by Kim,,frontend;react
SWE Intern,Fabrikam,Rejected,NYC,,1693526400000,OA score 67%,No sponsorship,intern|newgradauth/configuration-not-found: Enable Google provider for the same project named in.env.localand set Support email.auth/unauthorized-domain: Add your dev/prod domains under Authentication → Settings → Authorized domains.- Pop‑up blocked / Safari: fall back to
signInWithRedirectin your helper if needed. - UID lost on refresh: use
useUser(auth listener) instead ofauth.currentUserat render time. - Events index needed: accept the console prompt to create the composite index on
events.
SDKs prior to v10 often use enableIndexedDbPersistence(db). Newer SDKs recommend initializeFirestore(app, { localCache: persistentLocalCache(/* optional: persistentSingleTabManager() */) }). This repo uses the simpler approach; upgrade if you see deprecation warnings.
dev– run locallybuild– create production buildstart– run production server
- Soft delete & Archive view; bulk actions.
- Calendar reminders for follow‑ups / OA deadlines.
- Job page metadata scraping via server action or Cloud Function.
- Sharing/export (PDF, CSV export) and richer analytics.
- Advanced search (tags, company domain, tech stack).
MIT — do what you like; contributions welcome.