Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Prowider β€” Professional Lead Distribution System

Next.js TypeScript PostgreSQL Tailwind CSS Prisma License

A production-ready lead generation and intelligent distribution platform built with modern web technologies. Prowider uses a fair, efficient allocation algorithm to distribute customer leads to service providers while respecting monthly quotas.

View Live Demo β€’ GitHub Issues β€’ Report Bug


✨ Features

  • πŸš€ Real-time Dashboard β€” Live provider metrics with Server-Sent Events (SSE)
  • βš–οΈ Fair Lead Allocation β€” Intelligent round-robin algorithm with quota management
  • πŸ”’ Concurrency-Safe β€” Serializable database transactions prevent race conditions
  • πŸ“ž Customer Forms β€” Easy lead submission with service selection
  • πŸ”Œ Webhook Support β€” Idempotent quota reset via webhooks
  • πŸ“Š Provider Analytics β€” Track leads, quota usage, and performance
  • πŸ› οΈ Admin Tools β€” Built-in testing suite and load generation
  • 🎨 Responsive UI β€” Mobile-friendly design with Tailwind CSS

πŸ—οΈ Architecture

Tech Stack

Layer Technology Version Purpose
Frontend Next.js (App Router) 16.2 UI, routing, SSE streaming
Backend Node.js + Express Built-in API routes and business logic
Database PostgreSQL Latest Persistent data storage
ORM Prisma 7.8 Type-safe database access
Real-time Server-Sent Events Native Live dashboard updates
Styling Tailwind CSS 4 Utility-first CSS framework
Validation Zod 4.4 Type-safe schema validation

Data Model

Service (service, id, name) 
  β”œβ”€β”€ Lead (customer inquiry with phone, name, city, description)
  β”‚   └── LeadAssignment (assigned to 3 providers per lead)
  β”œβ”€β”€ Provider (8 providers, each with monthly quota of 10 leads)
  └── AllocationState (tracks round-robin pointer per service)

WebhookEvent (idempotency keys for quota resets)

πŸš€ Quick Start

Prerequisites

  • Node.js 18+ or 20+
  • npm or yarn
  • PostgreSQL 12+
  • Git

Installation

# 1. Clone the repository
git clone https://github.com/Shree-svg/Prowider.git
cd Prowider

# 2. Install dependencies
npm install

# 3. Set up environment variables
cp .env.local.example .env.local

# Edit .env.local and add your PostgreSQL connection string:
# DATABASE_URL="postgresql://user:password@localhost:5432/prowider"

Database Setup

# 1. Create database schema
npx prisma db push

# 2. Seed with demo data (3 services, 8 providers, allocation state)
npx prisma db seed

# 3. (Optional) Generate Prisma client after schema changes
npx prisma generate

Running the Application

# Development mode (hot reload enabled)
npm run dev

# Production build
npm run build

# Start production server
npm start

# The application will be available at http://localhost:3000

Database Migrations

# Deploy pending migrations (production)
npm run db:migrate

# View Prisma Studio (interactive database explorer)
npx prisma studio

πŸ“ Routes & Pages

URL Path Purpose Access
/ Home page with navigation Public
/request-service Customer lead submission form Public
/dashboard Real-time provider dashboard & analytics Public*
/test-tools Webhook testing & load generation Development

*Dashboard displays real-time lead allocations and provider quotas via SSE.


🎯 Core Features

1. Intelligent Lead Allocation

Prowider implements a sophisticated allocation algorithm that:

  • Assigns exactly 3 providers per lead to maximize reach
  • Guarantees mandatory assignments for critical providers
  • Uses persistent round-robin to fairly distribute remaining slots
  • Respects monthly quotas (default: 10 leads/provider)
  • Survives server restarts via database-backed state

Example Allocation Rules

Service Mandatory Providers Round-Robin Pool
Service 1 Provider 1 [2, 3, 4]
Service 2 Provider 5 [6, 7, 8]
Service 3 Providers 1, 4 [2, 3, 5, 6, 7, 8]

2. Concurrency & Consistency

  • βœ… Serializable Transactions β€” Highest isolation level prevents anomalies
  • βœ… Row-Level Locking β€” SELECT … FOR UPDATE ensures atomic state updates
  • βœ… Distributed-Safe β€” Works correctly across multiple server instances
  • βœ… No In-Memory Locks β€” All synchronization at database level

3. Duplicate Prevention

  • Database Constraint β€” UNIQUE(phone, serviceId) on Lead table
  • API Response β€” Returns 409 Conflict with clear error message
  • Enforced at DB Level β€” Not just application logic

4. Webhook Idempotency

Quota resets via webhooks are idempotent:

POST /api/webhook
{
  "idempotencyKey": "unique-key-12345",
  "action": "reset_quota",
  "providerId": 1
}
  • βœ… Same key: Returns 200 with idempotent: true, no quota reset
  • βœ… New key: Processes quota reset, records key in database
  • βœ… Race-condition safe: Key recording happens in same transaction

πŸ”§ API Endpoints

Leads

# Create a new lead
POST /api/leads
Content-Type: application/json

{
  "name": "John Doe",
  "phone": "+1234567890",
  "city": "New York",
  "description": "Need business consultation",
  "serviceId": 1
}

# Response (Success): 201 Created
{
  "success": true,
  "leadId": 42,
  "providers": [1, 3, 5]
}

# Response (Duplicate): 409 Conflict
{
  "error": "Lead already exists for this phone and service"
}

Providers

# Get all providers
GET /api/providers

# Response: 200 OK
{
  "providers": [
    { "id": 1, "name": "Provider A", "monthlyQuota": 10, "leadsCount": 7 },
    { "id": 2, "name": "Provider B", "monthlyQuota": 10, "leadsCount": 5 },
    ...
  ]
}

Services

# Get all services
GET /api/services

# Response: 200 OK
{
  "services": [
    { "id": 1, "name": "Business Consultation" },
    { "id": 2, "name": "Legal Services" },
    { "id": 3, "name": "Tax Planning" }
  ]
}

Real-time Updates

# Server-Sent Events stream
GET /api/sse

# Pushes updates when leads are created:
data: {
  "type": "lead_created",
  "lead": { "id": 42, "name": "John Doe", "providers": [1, 3, 5] },
  "timestamp": "2024-05-18T10:30:00Z"
}

Webhook Quota Reset

POST /api/webhook
Content-Type: application/json

{
  "idempotencyKey": "reset-provider-1-2024-05",
  "action": "reset_quota",
  "providerId": 1
}

# Response: 200 OK
{
  "success": true,
  "idempotent": false,
  "message": "Provider 1 quota reset to 0"
}

πŸ“Š Testing & Development

Testing Tools

Access the internal testing suite at /test-tools:

  • Manual Lead Creation β€” Create leads with custom data
  • Load Testing β€” Generate bulk leads to test system capacity
  • Webhook Testing β€” Send test webhooks with custom payloads
  • Real-time Monitoring β€” Watch allocations happen live

Running Tests

# Lint the codebase (ESLint + Next.js rules)
npm run lint

# Generate TypeScript types
npm run db:generate

# Seed database with fresh demo data
npm run db:seed

# View database in Prisma Studio
npx prisma studio

πŸ” Environment Variables

Create a .env.local file:

# Required: PostgreSQL connection string
DATABASE_URL="postgresql://user:password@localhost:5432/prowider"

# Optional: Enable debug logging (development only)
DEBUG=prowider:*

# Optional: Node environment
NODE_ENV=development  # or production

πŸ“ˆ Production Deployment

Deploy to Vercel

# 1. Push code to GitHub
git push origin main

# 2. Connect repository to Vercel
# Visit: https://vercel.com/new

# 3. Set environment variables in Vercel dashboard
# - DATABASE_URL: Your production PostgreSQL URL

# 4. Vercel auto-deploys on push

Deploy to Railway, Render, or Self-Hosted

Similar process β€” ensure DATABASE_URL is set and run migrations:

npm run db:migrate  # Deploy schema changes
npm run build       # Build Next.js app
npm start          # Start server

πŸ› οΈ Development

Project Structure

Prowider/
β”œβ”€β”€ app/                    # Next.js app router
β”‚   β”œβ”€β”€ api/               # API routes
β”‚   β”‚   β”œβ”€β”€ leads/        # Lead creation
β”‚   β”‚   β”œβ”€β”€ providers/    # Provider data
β”‚   β”‚   β”œβ”€β”€ services/     # Service data
β”‚   β”‚   β”œβ”€β”€ sse/          # Server-Sent Events
β”‚   β”‚   β”œβ”€β”€ webhook/      # Webhook handler
β”‚   β”‚   └── test/         # Testing endpoints
β”‚   β”œβ”€β”€ dashboard/        # Real-time dashboard UI
β”‚   β”œβ”€β”€ request-service/  # Customer form page
β”‚   β”œβ”€β”€ test-tools/       # Testing UI
β”‚   └── layout.tsx        # Root layout
β”œβ”€β”€ lib/                   # Utility functions
β”œβ”€β”€ prisma/
β”‚   β”œβ”€β”€ schema.prisma     # Database schema
β”‚   └── seed.ts           # Database seed script
β”œβ”€β”€ public/               # Static assets
β”œβ”€β”€ tailwind.config.mjs   # Tailwind configuration
β”œβ”€β”€ tsconfig.json         # TypeScript configuration
└── next.config.ts        # Next.js configuration

Code Style

  • TypeScript β€” Strict mode enabled
  • ESLint β€” Next.js recommended rules
  • Formatting β€” Prettier compatible
  • Tailwind β€” Utility-first CSS

Run linter:

npm run lint

🚨 Design Highlights

Why Serializable + Row Locks?

Problem: Multiple servers allocating leads concurrently might skip providers.

Solution:

  1. Serializable isolation β€” Strongest database guarantee
  2. SELECT … FOR UPDATE β€” Locks AllocationState row during transaction
  3. Result β€” Concurrent requests queue naturally; no race conditions

Why Database-Backed Idempotency?

Problem: Retried webhooks could double-reset quotas.

Solution:

  1. Check idempotencyKey in WebhookEvent table first
  2. If exists: Return 200 immediately (idempotent)
  3. If new: Process reset + record key in same transaction
  4. Result β€” Transactionally safe; no race window

πŸ“ License

This project is licensed under the MIT License β€” see LICENSE file for details.


🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Code Guidelines

  • Write TypeScript (no any types)
  • Follow ESLint rules
  • Add comments for complex logic
  • Test changes locally before pushing

πŸ› Issues & Feedback

Found a bug or have a suggestion?


πŸ“ž Support

For questions or issues:

  1. Check the Troubleshooting section
  2. Review API Endpoints documentation
  3. Open an issue on GitHub
  4. Check existing GitHub issues for similar problems

Troubleshooting

Q: Database connection fails
A: Verify DATABASE_URL is correct and PostgreSQL server is running.

Q: npm run db:seed fails
A: Run npx prisma db push first to create schema.

Q: Real-time dashboard not updating
A: Ensure Server-Sent Events (SSE) is not blocked by proxies/firewalls.

Q: Build fails with TypeScript errors
A: Run npm run db:generate to regenerate Prisma types.


πŸ™ Acknowledgments


πŸ“œ Version History

  • v0.1.0 (Current) β€” Initial release with core lead distribution, real-time dashboard, and webhook support

⬆ Back to Top

Made with ❀️ by Shree-svg

About

Intelligent lead distribution platform with fair round-robin allocation, serializable DB transactions, row-level locking, real-time SSE dashboard, and idempotent webhooks.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages