Skip to content

AnujKV123/rfp-management-system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

RFP Management System

An intelligent Request for Proposal (RFP) management system that automates the creation, distribution, and analysis of RFPs using AI-powered natural language processing. The system automatically processes vendor proposals received via email and provides AI-driven comparison and recommendations.

Table of Contents

Features

  • Natural Language RFP Creation: Create structured RFPs from plain text descriptions using AI
  • Automated Email Distribution: Send RFPs to multiple vendors via email
  • Intelligent Email Processing: Automatically poll and process vendor proposal emails
  • AI-Powered Proposal Extraction: Extract structured data from vendor proposals
  • Smart Comparison: AI-driven comparison of proposals based on price, delivery, and completeness
  • Vendor Management: Manage vendor database with contact information
  • Real-time Dashboard: Track RFPs, proposals, and vendor responses

Tech Stack

Frontend

  • Framework: React 18.2 with TypeScript
  • Build Tool: Vite 5.0
  • Routing: React Router DOM 6.20
  • Styling: Tailwind CSS 3.4
  • Icons: Lucide React

Backend

  • Runtime: Node.js (TypeScript)
  • Framework: Express 4.18
  • Database: PostgreSQL 8.16
  • AI Provider: OpenAI GPT-4o-mini
  • Email:
    • SMTP: Nodemailer 6.9
    • IMAP: node-imap 0.8
    • Parsing: mailparser 3.6

Key Libraries

  • cors: Cross-origin resource sharing
  • dotenv: Environment variable management
  • pg: PostgreSQL client
  • openai: OpenAI API client
  • ts-node-dev: Development server with hot reload

Prerequisites

Before setting up the project, ensure you have:

  • Node.js: Version 18.x or higher
  • npm: Version 9.x or higher (comes with Node.js)
  • PostgreSQL: Version 12.x or higher
  • OpenAI API Key: Required for AI-powered features
  • Email Account: Gmail or compatible SMTP/IMAP email account with:
    • SMTP access enabled
    • IMAP access enabled
    • App-specific password (if using Gmail with 2FA)

Setting up Gmail for Email Integration

If using Gmail:

  1. Enable 2-Factor Authentication on your Google account
  2. Generate an App Password:
    • Go to Google Account Settings → Security
    • Under "Signing in to Google", select "App passwords"
    • Generate a new app password for "Mail"
    • Use this password in your .env file

Project Setup

1. Clone the Repository

git clone <repository-url>
cd rfp-management-system

2. Install Dependencies

Backend Setup

cd backend
npm install

Frontend Setup

cd frontend
npm install

3. Database Setup

Create PostgreSQL Database

# Connect to PostgreSQL
psql -U postgres

# Create database
CREATE DATABASE rfp_management;

# Exit psql
\q

Initialize Database Schema

cd backend
npm run db:init

This will create all necessary tables, indexes, and triggers defined in backend/src/config/schema.sql.

4. Configuration

Backend Configuration

Create backend/.env file from the example:

cd backend
cp .env.example .env

Edit backend/.env with your configuration:

# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=rfp_management
DB_USER=postgres
DB_PASSWORD=your_postgres_password

# Server Configuration
PORT=3000

# OpenAI API Configuration
OPENAI_API_KEY=sk-your-openai-api-key-here
AI_TIMEOUT_MS=30000
AI_MAX_RETRIES=3

# Email Service Configuration (Gmail example)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-specific-password

# IMAP Configuration
IMAP_HOST=imap.gmail.com
IMAP_PORT=993
IMAP_USER=your-email@gmail.com
IMAP_PASSWORD=your-app-specific-password
IMAP_TLS=true

# Email Polling Configuration
EMAIL_POLL_INTERVAL_MS=60000
EMAIL_FROM=your-email@gmail.com
EMAIL_REPLY_TO=your-email@gmail.com

Frontend Configuration

Create frontend/.env file from the example:

cd frontend
cp .env.example .env

Edit frontend/.env:

# Backend API URL
VITE_API_URL=http://localhost:3000

# API Timeout (milliseconds)
VITE_API_TIMEOUT=30000

Running the Application

Development Mode

Start Backend Server

cd backend
npm run dev

The backend server will start on http://localhost:3000 (or the PORT specified in .env).

Features:

  • Hot reload on file changes
  • Email polling service starts automatically
  • Polls for new emails every 60 seconds (configurable)

Start Frontend Development Server

cd frontend
npm run dev

The frontend will start on http://localhost:5173 (Vite default).

Production Mode

Build Backend

cd backend
npm run build
npm start

Build Frontend

cd frontend
npm run build
npm run preview

Seed Data (Optional)

To populate the database with sample data:

cd backend
npm run seed

This will create:

  • Sample vendors
  • Sample RFPs
  • Sample proposals

API Documentation

Base URL: http://localhost:3000/api

Health Check

GET /api/health

Check if the API is running.

Response:

{
  "status": "ok"
}

Vendors

POST /api/vendors

Create a new vendor.

Request Body:

{
  "name": "Acme Corporation",
  "email": "contact@acme.com",
  "contactPerson": "John Doe",
  "phone": "+1-555-0123",
  "address": "123 Business St, City, State 12345"
}

Success Response (201):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Acme Corporation",
  "email": "contact@acme.com",
  "contactPerson": "John Doe",
  "phone": "+1-555-0123",
  "address": "123 Business St, City, State 12345",
  "createdAt": "2024-01-15T10:30:00.000Z",
  "updatedAt": "2024-01-15T10:30:00.000Z"
}

Error Response (400):

{
  "error": "Validation failed: email is required"
}

GET /api/vendors

List all vendors with pagination.

Query Parameters:

  • page (optional): Page number (default: 1)
  • limit (optional): Items per page (default: 50, max: 100)

Success Response (200):

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Acme Corporation",
      "email": "contact@acme.com",
      "contactPerson": "John Doe",
      "phone": "+1-555-0123",
      "address": "123 Business St, City, State 12345",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "totalPages": 1
  }
}

GET /api/vendors/:id

Get vendor details by ID.

Success Response (200):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Acme Corporation",
  "email": "contact@acme.com",
  "contactPerson": "John Doe",
  "phone": "+1-555-0123",
  "address": "123 Business St, City, State 12345",
  "createdAt": "2024-01-15T10:30:00.000Z",
  "updatedAt": "2024-01-15T10:30:00.000Z"
}

Error Response (404):

{
  "error": "Vendor not found: 550e8400-e29b-41d4-a716-446655440000"
}

PUT /api/vendors/:id

Update vendor information.

Request Body:

{
  "name": "Acme Corporation Ltd",
  "contactPerson": "Jane Smith"
}

Success Response (200):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Acme Corporation Ltd",
  "email": "contact@acme.com",
  "contactPerson": "Jane Smith",
  "phone": "+1-555-0123",
  "address": "123 Business St, City, State 12345",
  "createdAt": "2024-01-15T10:30:00.000Z",
  "updatedAt": "2024-01-15T11:00:00.000Z"
}

DELETE /api/vendors/:id

Delete a vendor.

Success Response (204): No content

Error Response (404):

{
  "error": "Vendor not found: 550e8400-e29b-41d4-a716-446655440000"
}

RFPs

POST /api/rfps

Create an RFP from natural language description.

Request Body:

{
  "description": "We need 50 laptops with 16GB RAM and 512GB SSD. Budget is $50,000 USD. Delivery needed within 30 days. Payment terms: Net 30. Warranty: 3 years minimum."
}

Success Response (201):

{
  "id": "660e8400-e29b-41d4-a716-446655440001",
  "description": "We need 50 laptops...",
  "structuredData": {
    "items": [
      {
        "name": "Laptop",
        "quantity": 50,
        "specifications": "16GB RAM, 512GB SSD"
      }
    ],
    "budget": 50000,
    "currency": "USD",
    "deliveryTimeline": "30 days",
    "paymentTerms": "Net 30",
    "warrantyRequirements": "3 years minimum",
    "additionalTerms": null
  },
  "status": "draft",
  "createdAt": "2024-01-15T10:30:00.000Z",
  "sentAt": null
}

Error Response (400):

{
  "error": "Description is required"
}

GET /api/rfps

List all RFPs with pagination.

Query Parameters:

  • page (optional): Page number (default: 1)
  • limit (optional): Items per page (default: 50, max: 100)

Success Response (200):

{
  "data": [
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "description": "We need 50 laptops...",
      "structuredData": { ... },
      "status": "sent",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "sentAt": "2024-01-15T11:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "totalPages": 1
  }
}

GET /api/rfps/:id

Get RFP details with vendors and proposals.

Success Response (200):

{
  "id": "660e8400-e29b-41d4-a716-446655440001",
  "description": "We need 50 laptops...",
  "structuredData": { ... },
  "status": "sent",
  "createdAt": "2024-01-15T10:30:00.000Z",
  "sentAt": "2024-01-15T11:00:00.000Z",
  "vendors": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Acme Corporation",
      "email": "contact@acme.com",
      "sentAt": "2024-01-15T11:00:00.000Z"
    }
  ],
  "proposals": [
    {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "rfpId": "660e8400-e29b-41d4-a716-446655440001",
      "vendorId": "550e8400-e29b-41d4-a716-446655440000",
      "vendorName": "Acme Corporation",
      "extractedData": { ... },
      "completenessScore": 0.95,
      "aiSummary": "Strong proposal with competitive pricing...",
      "receivedAt": "2024-01-16T09:00:00.000Z",
      "processingStatus": "completed"
    }
  ]
}

POST /api/rfps/:id/send

Send RFP to selected vendors via email.

Request Body:

{
  "vendorIds": [
    "550e8400-e29b-41d4-a716-446655440000",
    "550e8400-e29b-41d4-a716-446655440001"
  ]
}

Success Response (200):

{
  "message": "RFP sent successfully",
  "rfp": {
    "id": "660e8400-e29b-41d4-a716-446655440001",
    "status": "sent",
    "sentAt": "2024-01-15T11:00:00.000Z"
  },
  "results": [
    {
      "vendorId": "550e8400-e29b-41d4-a716-446655440000",
      "vendorEmail": "contact@acme.com",
      "success": true,
      "sentAt": "2024-01-15T11:00:00.000Z"
    }
  ]
}

Partial Failure Response (207):

{
  "message": "RFP sent with some failures",
  "rfp": { ... },
  "results": [
    {
      "vendorId": "550e8400-e29b-41d4-a716-446655440000",
      "vendorEmail": "contact@acme.com",
      "success": true,
      "sentAt": "2024-01-15T11:00:00.000Z"
    },
    {
      "vendorId": "550e8400-e29b-41d4-a716-446655440001",
      "vendorEmail": "invalid@vendor.com",
      "success": false,
      "error": "SMTP connection failed"
    }
  ]
}

GET /api/rfps/:id/proposals

Get all proposals for an RFP with pagination.

Query Parameters:

  • page (optional): Page number (default: 1)
  • limit (optional): Items per page (default: 50, max: 100)

Success Response (200):

{
  "data": [
    {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "rfpId": "660e8400-e29b-41d4-a716-446655440001",
      "vendorId": "550e8400-e29b-41d4-a716-446655440000",
      "vendorName": "Acme Corporation",
      "extractedData": {
        "items": [
          {
            "name": "Laptop",
            "unitPrice": 950,
            "totalPrice": 47500,
            "specifications": "16GB RAM, 512GB SSD, Intel i7"
          }
        ],
        "totalPrice": 47500,
        "currency": "USD",
        "deliveryTimeline": "25 days",
        "paymentTerms": "Net 30",
        "warrantyOffered": "3 years comprehensive",
        "additionalConditions": "Free shipping included"
      },
      "completenessScore": 0.95,
      "aiSummary": "Strong proposal with competitive pricing...",
      "receivedAt": "2024-01-16T09:00:00.000Z",
      "processingStatus": "completed"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "totalPages": 1
  }
}

GET /api/rfps/:id/comparison

Get AI-powered comparison of all proposals for an RFP.

Success Response (200):

{
  "rfpId": "660e8400-e29b-41d4-a716-446655440001",
  "proposals": [ ... ],
  "analysis": {
    "priceComparison": [
      {
        "vendorId": "550e8400-e29b-41d4-a716-446655440000",
        "vendorName": "Acme Corporation",
        "totalPrice": 47500,
        "priceRank": 1
      }
    ],
    "deliveryComparison": [
      {
        "vendorId": "550e8400-e29b-41d4-a716-446655440000",
        "vendorName": "Acme Corporation",
        "deliveryDays": 25,
        "deliveryRank": 1
      }
    ],
    "completenessComparison": [
      {
        "vendorId": "550e8400-e29b-41d4-a716-446655440000",
        "vendorName": "Acme Corporation",
        "score": 95,
        "missingItems": []
      }
    ]
  },
  "recommendation": {
    "recommendedVendorId": "550e8400-e29b-41d4-a716-446655440000",
    "reasoning": "Acme Corporation offers the best overall value with competitive pricing, fast delivery, and complete proposal coverage.",
    "alternativeOptions": "Consider Vendor B if faster delivery is critical."
  },
  "generatedAt": "2024-01-16T10:00:00.000Z"
}

Error Response (404):

{
  "error": "No proposals found for RFP: 660e8400-e29b-41d4-a716-446655440001"
}

Proposals

POST /api/proposals/process-email

Webhook endpoint for processing incoming proposal emails.

Request Body:

{
  "from": "vendor@acme.com",
  "to": "rfp@yourcompany.com",
  "subject": "Re: Request for Proposal - 660e8400-e29b-41d4-a716-446655440001",
  "body": "We are pleased to submit our proposal...",
  "htmlBody": "<html>...</html>",
  "attachments": []
}

Success Response (201):

{
  "message": "Email processed successfully",
  "proposal": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "rfpId": "660e8400-e29b-41d4-a716-446655440001",
    "vendorId": "550e8400-e29b-41d4-a716-446655440000",
    "extractedData": { ... },
    "completenessScore": 0.95,
    "aiSummary": "Strong proposal...",
    "receivedAt": "2024-01-16T09:00:00.000Z",
    "processingStatus": "completed"
  }
}

Error Response (400):

{
  "error": "Could not match email to RFP"
}

Design Decisions & Assumptions

Architecture Decisions

1. AI-First Approach

  • Decision: Use OpenAI GPT-4o-mini for all natural language processing tasks
  • Reasoning:
    • Provides flexibility for handling various RFP formats
    • Reduces need for rigid parsing rules
    • Enables intelligent comparison and recommendations
  • Trade-offs:
    • Requires API key and incurs costs
    • Dependent on external service availability
    • Response times vary (30s timeout configured)

2. Email Polling vs Webhooks

  • Decision: Implemented IMAP polling for incoming emails
  • Reasoning:
    • Simpler setup without requiring public endpoints
    • Works with any IMAP-compatible email provider
    • No need for webhook configuration or SSL certificates
  • Trade-offs:
    • Slight delay in processing (60-second poll interval)
    • More resource-intensive than webhooks
  • Alternative: Could implement webhook support for real-time processing

3. Repository Pattern

  • Decision: Used repository pattern for data access
  • Reasoning:
    • Separates business logic from data access
    • Makes testing easier with dependency injection
    • Allows easy database migration if needed
  • Implementation: BaseRepository with specialized repositories for each entity

4. JSONB for Structured Data

  • Decision: Store RFP structured data and proposal extracted data as JSONB
  • Reasoning:
    • Flexible schema for varying RFP requirements
    • Efficient querying with PostgreSQL JSONB operators
    • Reduces need for complex relational schemas
  • Trade-offs: Less type safety, requires validation at application level

5. Completeness Scoring

  • Decision: AI-generated completeness scores (0-1 scale)
  • Reasoning:
    • Provides quick assessment of proposal quality
    • Helps identify missing information
    • Enables ranking and comparison
  • Calculation: Based on how well proposal addresses RFP requirements

Key Assumptions

Email Processing

  1. RFP ID in Email: Vendors will include the RFP ID in their response email (subject or body)

    • System looks for patterns: "RFP ID: xxx" or "Reference RFP ID: xxx"
    • Falls back to dead letter queue if no match found
  2. Email Format: Vendor proposals can be in various formats (plain text, HTML, tables)

    • AI service is flexible enough to handle different formats
    • Attachments are stored but not currently processed
  3. Single Proposal per Email: Each email contains one proposal for one RFP

    • Multiple proposals require multiple emails
  4. Vendor Email Matching: Vendor is identified by email address

    • Email must match a vendor in the database
    • Case-insensitive matching

RFP Creation

  1. Natural Language Input: Users provide RFP details in natural language

    • AI extracts structured data
    • Missing fields are flagged for user review
  2. Currency Standardization: All prices in single currency per RFP

    • No automatic currency conversion
  3. Item Specifications: Flexible text field for specifications

    • No rigid structure enforced

Proposal Comparison

  1. Comparable Proposals: All proposals for an RFP are comparable

    • Same currency assumed
    • Similar item structures
  2. Delivery Timeline: Expressed in days for comparison

    • AI converts various formats ("2 weeks", "30 days") to days
  3. Price Ranking: Lower price is better (rank 1 = lowest)

    • Doesn't account for quality differences

Data Retention

  1. Raw Email Storage: All incoming emails stored indefinitely

    • Enables reprocessing if needed
    • Audit trail for proposals
  2. No Automatic Deletion: RFPs, proposals, and vendors are never auto-deleted

    • Manual cleanup required

Security

  1. No Authentication: Current implementation has no user authentication

    • Suitable for internal/demo use only
    • Production deployment requires auth layer
  2. Email Credentials: Stored in environment variables

    • Should use secrets management in production
  3. API Access: No rate limiting or API keys

    • Open access to all endpoints

Limitations

  1. Email Provider: Tested primarily with Gmail

    • Other providers may require configuration adjustments
  2. Attachment Processing: Attachments are stored but not analyzed

    • Future enhancement: Extract data from PDF/Excel attachments
  3. Concurrent Email Processing: Sequential processing of emails

    • Could be parallelized for better performance
  4. AI Timeout: 30-second timeout for AI operations

    • Very large RFPs or many proposals may timeout
    • Configurable via AI_TIMEOUT_MS
  5. No Real-time Updates: Frontend requires manual refresh

    • Could implement WebSocket for real-time updates
  6. Single Language: English only

    • AI could support other languages with prompt modifications

Troubleshooting

Database Connection Issues

Problem: Cannot connect to PostgreSQL

Solutions:

  1. Verify PostgreSQL is running:

    # Windows
    pg_ctl status
    
    # Linux/Mac
    sudo systemctl status postgresql
  2. Check database credentials in .env

  3. Ensure database exists:

    psql -U postgres -l
  4. Test connection:

    psql -U postgres -d rfp_management

Email Connection Issues

Problem: Cannot send or receive emails

Solutions:

  1. Verify SMTP/IMAP credentials in .env
  2. For Gmail:
    • Enable 2FA
    • Generate app-specific password
    • Enable "Less secure app access" (if not using app password)
  3. Check firewall settings for ports 587 (SMTP) and 993 (IMAP)
  4. Test SMTP connection:
    telnet smtp.gmail.com 587

OpenAI API Issues

Problem: AI operations failing or timing out

Solutions:

  1. Verify API key is correct in .env
  2. Check OpenAI account has credits
  3. Increase timeout:
    AI_TIMEOUT_MS=60000
  4. Reduce input size (shorter RFP descriptions)
  5. Check OpenAI status: https://status.openai.com

Email Polling Not Working

Problem: Incoming emails not being processed

Solutions:

  1. Check backend logs for errors
  2. Verify IMAP credentials
  3. Manually test email fetch:
    • Send test email to configured address
    • Check if email appears in inbox
    • Wait for poll interval (default 60s)
  4. Reduce poll interval for testing:
    EMAIL_POLL_INTERVAL_MS=10000

Frontend Cannot Connect to Backend

Problem: API calls failing from frontend

Solutions:

  1. Verify backend is running on correct port
  2. Check VITE_API_URL in frontend/.env
  3. Verify CORS is enabled in backend
  4. Check browser console for errors
  5. Test API directly:
    curl http://localhost:3000/api/health

Build Errors

Problem: TypeScript compilation errors

Solutions:

  1. Clear node_modules and reinstall:

    rm -rf node_modules package-lock.json
    npm install
  2. Check Node.js version:

    node --version  # Should be 18.x or higher
  3. Clear TypeScript cache:

    rm -rf dist
    npm run build

AI Tools Usage

Used ChatGPT for generating boilerplate code, resolving bugs more efficiently, and producing comprehensive documentation.


Note: This is a demonstration/development system. For production use, implement proper authentication, authorization, secrets management, monitoring, and error handling.

About

An intelligent Request for Proposal (RFP) management system that automates the creation, distribution, and analysis of RFPs using AI-powered natural language processing.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors