A production-grade Lead Management dashboard built for internal sales teams.
Turn your Google Sheets into a real-time command centre — track, assign, follow up, and close every lead in one place.
Developed for CIEL HR · Built by Vijaya Kumar L
LeadFlow solves a real problem: sales teams drowning in Google Sheets. When leads come in from 10 different sources — SEO forms, WhatsApp chatbots, website chats — they end up scattered across multiple tabs, no one knows who owns what, and follow-ups get missed.
LeadFlow ingests every sheet automatically into a single workspace. Sales reps get a clean CRM view with status tracking, follow-up reminders, analytics, and full-text search — while the original sheets stay untouched as a backup.
Google Sheets (10 sources)
│
▼ (n8n trigger → POST /api/sync)
LeadFlow Database (PostgreSQL)
│
├─ CRM Layer: status · category · owner · remarks · tasks
│
└─ Dashboard (Next.js 16) ← sales team works here
Leads only ever flow IN. LeadFlow never writes back to the sheet.
Dashboard, Workspace, Lead Panel, My Day, Super Admin
| Dashboard | Lead Panel | My Day |
|---|---|---|
| Real-time sheet stats | Full-field view + follow-up reminders | Today's callbacks queue |
| Feature | Details |
|---|---|
| Workspace | Paginated lead table · search · status / category / date filters · 20 per page |
| All fields readable | Click any row → full panel with every column · URLs as links · copy buttons · no need to export |
| Wrap text toggle | Expand all cells inline without opening the panel |
| Lead annotations | Category (Hot/Warm/Cold) · Status · Owner — saved to DB, never touches the sheet |
| Remarks thread | Post-it style notes per lead · Ctrl+Enter to submit |
| Follow-up tasks | Set reminders on any lead with a title + due date |
| My Day | Daily work queue — Overdue · Today · Upcoming — one-tap complete or snooze |
| Email leads | Draft outbound emails from within the panel |
| Bulk assign | Select multiple rows → assign owner in one action |
| Keyboard nav | Press j / k to move between rows, Enter to open |
| Export | CSV or Excel · optional annotation columns (status/category/owner) |
| Global search | Search leads across all sheets with Ctrl+K |
| Feature | Details |
|---|---|
| Overview | All sheets at a glance — totals, counts, sync status |
| Analytics | Status breakdowns · monthly bar charts · per-sheet comparisons |
| Incoming | Month/year view of newly arrived leads with real pagination |
| Multi-workspace | Isolated per team — active workspace persists across all pages |
| Assigned view | See only leads assigned to you |
| Feature | Details |
|---|---|
| Control Panel | Platform-wide KPIs: users · workspaces · total leads · logins today |
| User Access Control | Add users · set roles · assign workspaces · reset passwords · suspend accounts |
| Workspace Config | Create · edit · delete workspaces with Google Sheet URL |
| SMTP Config | Outbound email setup · password encrypted with AES-256-GCM at rest |
| Security & Settings | Cloudflare Turnstile captcha on/off · platform banner text + link (no redeploy) |
| Action Logs | Full audit trail of every login · annotation · sync · admin action |
| Layer | Technology | Why |
|---|---|---|
| Frontend | Next.js 16 App Router + React 19 | Server components, streaming, App Router |
| Database | PostgreSQL 15+ | Self-hostable, no platform lock-in |
| Cache | Redis 7 (self-hosted Docker) | Session rate-limiting, API response caching |
| googleapis (Sheets API v4) | Read sheet rows for ingestion | |
| Auth | JWE-encrypted cookies (jose) | httpOnly, sameSite=lax, 7-day sessions |
| Captcha | Cloudflare Turnstile (optional) | Free, privacy-preserving login protection |
| Password | bcryptjs (cost 12) | Secure at-rest hashing |
| nodemailer | Draft + send from the lead panel | |
| Export | SheetJS (xlsx) | CSV + Excel export |
| Charts | Recharts | Status + monthly breakdowns |
| Docker | Multi-stage build | ~250 MB final image via standalone output |
┌─────────────────────────────────────────────────────────────┐
│ VPS Server │
│ │
│ ┌─────────────┐ ┌──────────────────┐ ┌───────────┐ │
│ │ Cloudflare │───▶│ Nginx Proxy Mgr │───▶│ leadflow │ │
│ │ (DNS + CDN) │ │ (SSL + routing) │ │ -app:3000 │ │
│ └─────────────┘ └──────────────────┘ └─────┬─────┘ │
│ │ │
│ ┌─────────────────────────────────────┤ │
│ │ │ │
│ ┌─────────▼──────┐ ┌─────────────┐ ┌─────────▼──────┐ │
│ │leadflow-postgrest│ │leadflow-redis│ │ kpr_postgres │ │
│ │ (REST API layer) │ │ (Redis 7) │ │ (Postgres 15) │ │
│ └─────────────────┘ └─────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘
- Docker + Docker Compose on your server
- PostgreSQL 15+ (a container or managed instance)
- A domain pointed at your server via Cloudflare
docker exec -it your_postgres_container psql -U postgres -c "
CREATE DATABASE leadflow;
CREATE USER leadflow_app WITH PASSWORD 'STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON DATABASE leadflow TO leadflow_app;
"
# Run the schema (one file, creates everything):
psql "postgresql://leadflow_app:STRONG_PASSWORD@localhost:5432/leadflow" \
-f migrations/000_complete_schema_postgres.sql# Generate a bcrypt hash:
node -e "console.log(require('bcryptjs').hashSync('YourPassword', 12))"
# Insert the user:
psql "postgresql://leadflow_app:STRONG_PASSWORD@localhost:5432/leadflow" -c "
INSERT INTO users (email, password_hash, role, status)
VALUES ('admin@yourcompany.com', 'PASTE_HASH', 'superadmin', 'active');
"cp .env.production.example .env.production
# Fill in all values — see SETUP.md for detailsdocker compose up -d --build- Add a Proxy Host in Nginx Proxy Manager pointing to
leadflow-app:3000 - Add an A record in Cloudflare pointing your domain to your server IP
- Set Cloudflare SSL/TLS to Full (strict)
Full step-by-step: see DEPLOY.md
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
✅ | PostgREST URL — http://leadflow-postgrest:3000 |
SUPABASE_SERVICE_ROLE_KEY |
✅ | JWT for PostgREST auth (generated once) |
REDIS_URL |
✅ | redis://:PASSWORD@leadflow-redis:6379 |
REDIS_PASSWORD |
✅ | Redis password (used in docker-compose too) |
POSTGRES_PASSWORD |
✅ | Password for leadflow_app DB user |
PGRST_JWT_SECRET |
✅ | PostgREST JWT signing secret (min 32 chars) |
SESSION_SECRET |
✅ | JWE cookie signing key (min 32 chars) |
SYNC_SECRET |
✅ | Shared secret for /api/sync webhook |
SMTP_ENCRYPTION_KEY |
✅ | AES-256 key for SMTP password encryption |
GOOGLE_SERVICE_ACCOUNT_EMAIL |
✅ | For Sheets API |
GOOGLE_PRIVATE_KEY |
✅ | For Sheets API |
Generate secrets: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
No Apps Script needed. Use n8n (free, self-hostable):
- Google Sheets Trigger —
Row Addedevent on your sheet - Code node — shape the payload:
{ "secret": "YOUR_SYNC_SECRET", "tabName": "Exact Tab Name", "rowIndex": 42, "rowData": { "Name": "...", "Email": "..." } } - HTTP Request —
POST https://your-domain.com/api/sync
The endpoint upserts on (sheet_id, row_index) — re-sending the same row updates it instead of duplicating.
- 🔐 JWE-encrypted session cookies (httpOnly, sameSite=lax)
- 🛡 Rate-limited login (10 attempts / 15 min, Redis-backed — survives multi-instance)
- 🤖 Cloudflare Turnstile captcha (optional — enabled in UI, no redeploy)
- 🔒 AES-256-GCM encryption for SMTP passwords at rest
- 🚫 CSP · HSTS · X-Frame-Options DENY · nosniff · Referrer-Policy
- 🏠 Workspace isolation enforced at API level (not just UI)
- 📋 Full audit log of every action
- 🚷 robots.txt blocks all search engine indexing
leadflow-v5/
├── src/
│ ├── app/
│ │ ├── (dashboard)/ # Main app layout + pages
│ │ │ ├── overview/
│ │ │ ├── workspace/ # Main leads table
│ │ │ ├── my-day/ # Follow-up reminders
│ │ │ ├── incoming/ # Month-filtered leads
│ │ │ ├── analytics/
│ │ │ └── feed-data/ # Sheet ingestion
│ │ ├── superadmin/ # Admin portal + pages
│ │ └── api/ # All API routes
│ ├── components/
│ │ ├── leads/ # LeadPanel, EmailModal
│ │ └── ui/ # Dropdown, DevBanner, TurnstileWidget
│ └── lib/
│ ├── auth/ # JWE session management
│ ├── cache/ # Redis client (ioredis)
│ ├── db/ # Supabase + Postgres clients
│ └── settings/ # Captcha + banner configuration
├── migrations/
│ └── 000_complete_schema_postgres.sql # Single-file DB setup
├── Dockerfile
├── docker-compose.yml
├── DEPLOY.md # Step-by-step VPS deployment
├── SETUP.md # Developer setup guide
└── INSTRUCTIONS.md # End-user guide
| Document | Purpose |
|---|---|
DEPLOY.md |
Step-by-step VPS deployment guide |
SETUP.md |
Developer environment + full schema reference |
INSTRUCTIONS.md |
End-user guide (sales reps + managers) |
POSTGRES_MIGRATION.md |
Moving from Supabase to self-hosted Postgres |
CHANGES.md |
Full changelog and security patches |
- Pipeline / Kanban board (drag Status columns)
- Contact deduplication (collapse same email/phone across sheets)
- Activity timeline (unified lead history)
- Conversion & source-ROI analytics
- Rep leaderboard + stale-lead SLA alerts
- WhatsApp click-to-send
- CSV direct import (without Google Sheets)
- Round-robin lead assignment
- Mobile-first field-rep view
LeadFlow was designed and developed by Vijaya Kumar L (Rise With VJ) for CIEL HR to replace fragmented Google Sheets-based lead tracking with a single, fast, secure internal CRM.
| Author | Vijaya Kumar L |
| Brand | Rise With VJ |
| GitHub | @risewithvj |
| vijayakumarl | |
| risewithvj@gmail.com | |
| Built for | CIEL HR |
MicroCRM License — Source-available, attribution required.
You may view, study, and deploy this for internal non-commercial use.
Commercial use, resale, or white-labelling requires written permission.
The original author credit must be retained in all copies and derivative works.
See LICENSE for the full terms.
Made with ❤️ by Vijaya Kumar L for CIEL HR