A complete, production-ready job application tracking system with user and admin roles, built with React, Node.js, Express, and MongoDB.
- π Dashboard β Visual stats (total, interviews, offers, rejected) with bar & pie charts
- πΌ Applications CRUD β Add, edit, delete job applications with full details
- π Search & Filter β Search by title/company/notes, filter by status, sort by multiple fields
- π Pagination β Efficient paginated results
- π Email Notifications β Mocked email on status change (real SMTP ready)
- π€ AI Notes Suggestions β OpenAI-powered improvements for application notes
- π Activity Log β Full history of all user actions
- π€ Profile Management β Update name, email, and password
- β‘ Admin Dashboard β Platform-wide analytics and user growth charts
- π₯ User Management β View, suspend, activate, and delete users
- ποΈ All Applications β View and filter every application in the system
- π Analytics β Status breakdowns, monthly growth, key metrics
- π JWT authentication with role-based route protection
- π Password hashing with bcrypt (12 rounds)
- β‘ Rate limiting (100 req/15 min per IP)
- π± Fully responsive β mobile + desktop
- π Dark theme with clean modern UI
- Node.js v18+
- MongoDB (local or Atlas)
- npm or yarn
git clone <your-repo-url>
cd job-trackercd backend
npm installCreate your .env file:
cp .env.example .envEdit .env with your values:
PORT=5000
NODE_ENV=development
MONGODB_URI=mongodb://localhost:27017/job-tracker
JWT_SECRET=your_super_secret_key_change_this
JWT_EXPIRES_IN=7d
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=your_email@gmail.com
EMAIL_PASS=your_app_password
OPENAI_API_KEY=sk-your-openai-key # Optional β for AI notes feature
FRONTEND_URL=http://localhost:5173Seed sample data (optional but recommended):
npm run seedThis creates:
| Role | Password | |
|---|---|---|
| Admin | admin@jobtracker.com | Admin123! |
| User | alex@example.com | password123 |
| User | sarah@example.com | password123 |
Start the backend:
npm run dev # Development with nodemon
npm start # ProductionBackend runs at: http://localhost:5000
cd ../frontend
npm install
npm run devFrontend runs at: http://localhost:5173
The Vite dev server proxies
/apirequests tolocalhost:5000automatically.
job-tracker/
βββ backend/
β βββ controllers/
β β βββ authController.js # Signup, login, getMe
β β βββ jobController.js # CRUD, stats, AI suggest
β β βββ userController.js # Profile, password
β β βββ adminController.js # Admin analytics, user mgmt
β βββ middleware/
β β βββ auth.js # JWT protect + adminOnly
β β βββ errorHandler.js # Global error handling
β βββ models/
β β βββ User.js # User schema + bcrypt hooks
β β βββ Job.js # Job application schema
β β βββ ActivityLog.js # Audit trail (90-day TTL)
β βββ routes/
β β βββ authRoutes.js
β β βββ jobRoutes.js
β β βββ userRoutes.js
β β βββ adminRoutes.js
β βββ utils/
β β βββ emailService.js # Nodemailer email notifications
β β βββ seedData.js # Development seed script
β βββ .env.example
β βββ package.json
β βββ server.js
β
βββ frontend/
β βββ src/
β β βββ components/
β β β βββ common/
β β β β βββ LoadingScreen.jsx # Loading screen + Spinner
β β β β βββ Pagination.jsx # Reusable pagination
β β β β βββ StatusBadge.jsx # Job status badges
β β β βββ jobs/
β β β β βββ JobModal.jsx # Add/edit job modal + AI
β β β βββ layout/
β β β βββ Layout.jsx # App shell
β β β βββ Sidebar.jsx # Navigation sidebar
β β βββ context/
β β β βββ AuthContext.jsx # Global auth state
β β βββ pages/
β β β βββ Login.jsx
β β β βββ Register.jsx
β β β βββ Dashboard.jsx # Stats + charts
β β β βββ Jobs.jsx # Application list + CRUD
β β β βββ Profile.jsx # Profile + password
β β β βββ ActivityLog.jsx # User activity history
β β β βββ admin/
β β β βββ AdminDashboard.jsx
β β β βββ AdminUsers.jsx
β β β βββ AdminJobs.jsx
β β βββ services/
β β β βββ api.js # Axios + all API calls
β β βββ App.jsx # Router + route guards
β β βββ index.css # Tailwind + custom styles
β βββ package.json
β βββ vite.config.js
β βββ tailwind.config.js
β
βββ README.md
All protected routes require: Authorization: Bearer <token>
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /auth/signup |
β | Register new user |
| POST | /auth/login |
β | Login, get JWT |
| GET | /auth/me |
β | Get current user |
POST /auth/signup
{ "name": "Alex Johnson", "email": "alex@example.com", "password": "password123" }Response: { token, user: { _id, name, email, role, status } }
POST /auth/login
{ "email": "alex@example.com", "password": "password123" }Response: { token, user: { _id, name, email, role, status, lastLogin } }
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /jobs |
β | List user's jobs (paginated) |
| GET | /jobs/stats |
β | Dashboard statistics |
| GET | /jobs/activity |
β | User activity log |
| GET | /jobs/:id |
β | Get single job |
| POST | /jobs |
β | Create job |
| PUT | /jobs/:id |
β | Update job |
| DELETE | /jobs/:id |
β | Delete job |
| POST | /jobs/:id/ai-suggest |
β | AI notes suggestion |
GET /jobs β Query params:
?page=1&limit=10&status=Applied&search=stripe&sortBy=createdAt&sortOrder=desc
POST /jobs β Body:
{
"jobTitle": "Senior Engineer",
"company": "Stripe",
"status": "Applied",
"notes": "Applied via referral",
"dateApplied": "2024-08-15",
"location": "Remote",
"salary": "$150kβ$180k",
"jobUrl": "https://stripe.com/jobs/123"
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /users/profile |
β | Get profile |
| PUT | /users/profile |
β | Update name/email |
| PUT | /users/change-password |
β | Change password |
| GET | /users/activity |
β | Activity log |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /admin/stats |
β Admin | Platform analytics |
| GET | /admin/users |
β Admin | All users (paginated) |
| GET | /admin/users/:id |
β Admin | User detail + jobs |
| PATCH | /admin/users/:id/status |
β Admin | Suspend/activate user |
| DELETE | /admin/users/:id |
β Admin | Delete user + data |
| GET | /admin/jobs |
β Admin | All jobs (paginated) |
| GET | /admin/activity |
β Admin | System activity log |
| Variable | Required | Description |
|---|---|---|
MONGODB_URI |
β | MongoDB connection string |
JWT_SECRET |
β | Secret for JWT signing (keep secure!) |
JWT_EXPIRES_IN |
β | Token expiry (default: 7d) |
PORT |
β | Server port (default: 5000) |
NODE_ENV |
β | development or production |
FRONTEND_URL |
β | CORS allowed origin |
EMAIL_HOST |
β | SMTP host |
EMAIL_PORT |
β | SMTP port |
EMAIL_USER |
β | SMTP email address |
EMAIL_PASS |
β | SMTP password or app password |
OPENAI_API_KEY |
β | For AI notes suggestions |
- Set all environment variables in the platform dashboard
- Set
NODE_ENV=production - Set
FRONTEND_URL=https://your-frontend.vercel.app - Deploy from your Git repo
- Update
vite.config.jsproxy OR setVITE_API_URLenv var - In
src/services/api.js, changebaseURLto your backend URL:baseURL: import.meta.env.VITE_API_URL || '/api'
- Deploy frontend to Vercel/Netlify
- Create a free cluster at mongodb.com/atlas
- Whitelist your server's IP
- Copy the connection string to
MONGODB_URI
- Get an API key from platform.openai.com
- Add
OPENAI_API_KEY=sk-...to your.env - In the Jobs page β Edit a job β click π€ AI Suggest
The AI analyzes the job title, company, and current notes, then suggests improvements including interview prep, follow-up points, and research areas.
After running npm run seed in the backend:
- Admin account: Full admin dashboard access
- Alex's account: 10 pre-populated job applications across all statuses, spanning 6 months of data for realistic chart visualization
- Sarah's account: 2 applications for additional variety
MIT β free to use, modify, and distribute.