A production-ready Applicant Tracking System (ATS) built with React, TypeScript, TailwindCSS, Firebase (Auth + Firestore + Storage), and Firebase Cloud Functions.
ATS-System/
βββ frontend/ # React + TypeScript + TailwindCSS
β βββ src/
β β βββ config/ # Firebase config + env vars
β β βββ contexts/ # AuthContext (Firebase Auth)
β β βββ lib/ # Firebase SDK init, API client
β β βββ components/ # Sidebar, NewScanModal, CandidateDetailModal, ProtectedLayout
β β βββ pages/ # AuthPage, DashboardPage, ScansListPage, ScanResultsPage
β β βββ types/ # Shared TypeScript interfaces
β βββ ...
βββ functions/ # Firebase Cloud Functions (Node.js + TypeScript)
β βββ src/
β βββ index.ts # Express API routes (start scan, import, download, export)
β βββ parser.ts # PDF/DOCX parsing + info extraction
β βββ scoring.ts # ATS scoring engine (TF-IDF + keyword matching)
β βββ types.ts # Shared types
βββ firestore.rules # Firestore security rules
βββ storage.rules # Firebase Storage security rules
βββ firebase.json # Firebase project config + emulator settings
βββ firestore.indexes.json # Firestore composite indexes
| Field | Type | Description |
|---|---|---|
hrId |
string | Firebase Auth UID of HR user |
title |
string | Job title |
jobDescription |
string | Full job description text |
keywords |
array | [{ keyword, weight (1-10), required }] |
status |
string | pending | processing | done | failed |
progress |
object | { done: number, total: number } |
createdAt |
timestamp | Creation time |
updatedAt |
timestamp | Last update time |
| Field | Type | Description |
|---|---|---|
resumeId |
string | Reference to resumes/{resumeId} |
name |
string | Extracted candidate name |
emails |
string[] | Extracted email addresses |
phones |
string[] | Extracted phone numbers |
atsScore |
number | Final ATS score (0-100) |
scoreBreakdown |
object | { keywordScore, similarityScore, penaltyScore, matchedKeywords, missingRequiredKeywords, similarityPercent } |
parsedTextSnippet |
string | First 500 chars of parsed text |
createdAt |
timestamp | Processing time |
| Field | Type | Description |
|---|---|---|
hrId |
string | Owner HR user ID |
scanId |
string | Parent scan ID |
fileName |
string | Original filename |
mimeType |
string | application/pdf or DOCX MIME |
storagePath |
string | Firebase Storage path |
source |
string | upload | db (portal import) |
fileSize |
number | File size in bytes |
createdAt |
timestamp | Upload time |
All endpoints require Authorization: Bearer <Firebase ID Token> header.
| Method | Path | Description |
|---|---|---|
POST |
/scans/:scanId/start |
Start async scan processing |
POST |
/scans/:scanId/import |
Import from portal DB URL |
GET |
/resumes/:resumeId/download |
Get signed download URL (15 min) |
POST |
/scans/:scanId/export-zip |
Get signed URLs for bulk download |
- Node.js 18+
- Firebase CLI:
npm install -g firebase-tools - Java (for Firestore emulator): Download JDK
# Install frontend dependencies
cd frontend
npm install
# Install functions dependencies
cd ../functions
npm install# Login to Firebase
firebase login
# Create a new Firebase project (or use existing)
firebase projects:create your-project-id
# Set the project
firebase use your-project-id# Copy the example env file
cp frontend/.env.example frontend/.env
# Edit frontend/.env with your Firebase project values
# Get values from: Firebase Console β Project Settings β Your apps β Web appYour .env should look like:
VITE_FIREBASE_API_KEY=AIzaSy...
VITE_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=your-project-id
VITE_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=123456789
VITE_FIREBASE_APP_ID=1:123456789:web:abcdef
VITE_USE_EMULATOR=true# From project root
firebase emulators:startThis starts:
- Auth Emulator: http://127.0.0.1:9099
- Firestore Emulator: http://127.0.0.1:8080
- Functions Emulator: http://127.0.0.1:5001
- Storage Emulator: http://127.0.0.1:9199
- Emulator UI: http://127.0.0.1:4000
# In a new terminal
cd functions
npm run build:watch# In a new terminal
cd frontend
npm run devOpen http://localhost:3000 π
# Build frontend
cd frontend && npm run build && cd ..
# Deploy all Firebase services
firebase deploy# Deploy only functions
firebase deploy --only functions
# Deploy only hosting
firebase deploy --only hosting
# Deploy only rules
firebase deploy --only firestore:rules,storage:rules- HR users can only read/write their own scans and resumes
- Candidates subcollection is write-protected (only Cloud Functions can write)
- Scan creation validates required fields and ownership
- HR users can only access files in
/resumes/{their-uid}/ - Upload validation: PDF/DOCX only, max 20MB
- Export files in
/exports/{uid}/are write-protected (Cloud Functions only)
The scoring engine (functions/src/scoring.ts) computes a 0β100 score with three components:
| Component | Max Points | Description |
|---|---|---|
| Keyword Score | 50 | Weighted keyword matching with diminishing returns (capped at 3 occurrences) |
| JD Similarity | 30 | TF-IDF cosine similarity between resume text and job description |
| Penalties | -20 | Missing required keywords + short text penalty |
Score Interpretation:
- 70β100: π’ Strong match
- 40β69: π‘ Moderate match
- 0β39: π΄ Weak match
HR can import resumes from an external API by providing a URL that returns:
[
{ "id": "cand_001", "fileUrl": "https://example.com/cv/john.pdf", "candidateName": "John Doe" },
{ "id": "cand_002", "fileUrl": "https://example.com/cv/jane.docx" }
]Limits:
- Max 50 files per import
- Max 20MB per file
- Only PDF and DOCX accepted
- HTTP/HTTPS only (configurable domain allowlist)
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, TailwindCSS v4, Vite |
| Backend | Firebase Cloud Functions (Node.js 18) |
| Database | Firebase Firestore |
| Storage | Firebase Storage |
| Auth | Firebase Authentication (email/password) |
| PDF Parsing | pdf-parse |
| DOCX Parsing | mammoth |
| Zip Export | jszip + file-saver (client-side) |
| HTTP Client | node-fetch (functions) |
| File | Purpose |
|---|---|
functions/src/scoring.ts |
ATS scoring engine (modular, unit-testable) |
functions/src/parser.ts |
Resume text extraction + info extraction |
functions/src/index.ts |
Cloud Functions API routes |
frontend/src/pages/ScanResultsPage.tsx |
Results dashboard with filters |
frontend/src/components/NewScanModal.tsx |
Scan creation with file upload |
firestore.rules |
Firestore security rules |
storage.rules |
Storage security rules |
| Page 1 | Page 2 | Page 3 |
|---|---|---|
![]() |
![]() |
![]() |


