Pathfinder Docs

Documentation Preview

Rental Heatmap Report - Azure CosmosDB Integration

Source: `docs/integrations/RENTAL_HEATMAP_AZURE_INTEGRATION.md`View on GitHub

Rental Heatmap Report - Azure CosmosDB Integration

Overview

The Rental Market Heatmap Report has been successfully integrated with Azure CosmosDB rental listing data, providing real-time market analysis based on actual property listings.


Integration Architecture

Azure CosmosDB
     ↓
Supabase (properties table)
     ↓
API: /api/rental-listings
     ↓
Heatmap Report Page
     ↓
Report Visualization

Data Flow

1. Data Sync from Azure

Azure listings are synced to Supabase's properties table with the following fields:

Relevant Fields for Heatmap:

  • price → Monthly rent amount
  • beds → Number of bedrooms
  • city → City location
  • state → State (default: Oregon)
  • zip_code → ZIP code
  • property_manager → Property manager name
  • availability → Current availability status
  • market_status → Market status

Additional Metadata:

  • property_name / address - Property identification
  • baths, sqft - Property details
  • azure_listing_id - Reference to original Azure listing
  • last_synced_at - Sync timestamp

2. API Endpoint: /api/rental-listings

Location: app/api/rental-listings/route.ts

Method: GET

Query Parameters:

  • city (optional) - Filter by city name (case-insensitive)
  • state (optional) - Filter by state (default: OR)
  • minPrice (optional) - Minimum monthly rent
  • maxPrice (optional) - Maximum monthly rent

Response:

{
  "success": true,
  "count": 150,
  "listings": [
    {
      "id": "uuid",
      "unitType": "1BR",
      "bedrooms": 1,
      "monthlyRent": 1200,
      "zipCode": "97203",
      "neighborhood": "Portland",
      "acceptsVoucher": false,
      "address": "123 Main St",
      "propertyManager": "ABC Property Management",
      "availability": "Available"
    },
    // ... more listings
  ]
}

Data Transformation:

The API automatically converts database fields to the RentalListing interface:

{
  id: property.id,
  unitType: formatUnitType(property.beds), // 0 → Studio, 1 → 1BR, etc.
  bedrooms: property.beds,
  monthlyRent: property.price,
  zipCode: property.zip_code,
  neighborhood: property.city,
  // Additional context
  address: property.address,
  propertyManager: property.property_manager,
  availability: property.availability,
}

3. Heatmap Report Page

Location: app/access-crm/reports/rental-heatmap/page.tsx

Features:

✅ Real Data Integration

  • Fetches live data from Azure via API
  • Defaults to real data on load
  • Falls back to sample data if no listings found

✅ Data Source Toggle

  • 🟢 Real Data - Pulls from Azure CosmosDB
  • 🔵 Sample Data - Uses generated demo data
  • Visual indicator showing which source is active

✅ Filtering Capabilities

  • Filter by city (e.g., "Portland")
  • More filters can be added (state, price range, etc.)
  • Real-time report regeneration on filter change

✅ Error Handling

  • Graceful fallback to sample data on error
  • Clear error messages with recovery options
  • Warning banners when using fallback data

✅ Data Source Display

Shows exactly where data is coming from:

  • "Azure CosmosDB (150 listings)"
  • "Sample Data (No Azure listings found)"
  • "Sample Data (Demo)"

Usage Examples

Basic Usage (Default)

Navigate to /access-crm/reports/rental-heatmap

The report will automatically:

  1. Fetch Oregon listings from Azure
  2. Generate heatmap with real data
  3. Display market statistics
  4. Show affordability analysis

Filtered by City

// In the UI: Enter "Portland" in the City filter

// API call made:
GET /api/rental-listings?state=OR&city=Portland

// Result: Report shows only Portland listings

Switch to Sample Data

Click the "🔵 Sample Data" toggle button to see demo data for testing/comparison purposes.


Implementation Details

API Route (app/api/rental-listings/route.ts)

// Fetch properties with valid pricing
let query = supabase
  .from('properties')
  .select(/* fields */)
  .not('price', 'is', null)
  .gt('price', 0);

// Apply filters
if (city) query = query.ilike('city', `%${city}%`);
if (state) query = query.eq('state', state);

// Transform to RentalListing format
const listings = properties.map(property => ({
  id: property.id,
  unitType: formatUnitType(property.beds),
  monthlyRent: property.price,
  // ... more fields
}));

Unit Type Formatting

function formatUnitType(beds: number | null): string | undefined {
  if (beds === 0) return 'Studio';
  if (beds === 1) return '1BR';
  if (beds === 2) return '2BR';
  if (beds === 3) return '3BR';
  if (beds >= 4) return '4BR+';
  return undefined;
}

Report Generation

// Fetch from API
const response = await fetch('/api/rental-listings?state=OR&city=Portland');
const data = await response.json();

// Generate report
const report = generateRentalHeatmapReport(data.listings, {
  region: 'Oregon Market Area',
  reportingPeriodStart: thirtyDaysAgo,
  reportingPeriodEnd: now,
});

Data Validation

The API ensures data quality:

Required Fields:

  • price must not be null
  • price must be greater than 0

Optional Fields:

  • Missing beds → defaults to undefined (normalized to 1BR)
  • Missing city → defaults to "Unknown"
  • Missing zip_code → undefined (optional in report)

Transformations:

  • Bedrooms → Unit Type conversion
  • Database snake_case → camelCase
  • Null handling with safe defaults

Performance Considerations

Optimizations Implemented:

  1. Batch Fetching

    • Automatically fetches in batches of 1000
    • Handles unlimited number of listings
    • No pagination limit
    • Progress logging for large datasets
  2. Indexed Queries

    • Filter by price IS NOT NULL
    • Filter by state (indexed column)
    • City uses ilike (case-insensitive)
  3. Selective Fields

    • Only fetch required columns
    • Reduces payload size
    • Faster query execution
  4. Client-Side Caching

    • React state management
    • Re-fetch only on filter change
    • Efficient re-rendering

Expected Performance:

  • < 1s - Fetch 100-500 listings
  • 1-3s - Fetch 500-2000 listings
  • 3-5s - Fetch 2000-5000 listings
  • 5-10s - Fetch 5000+ listings

Note: The API automatically handles large datasets by fetching in batches of 1000 records, ensuring all listings are included regardless of total count.


Monitoring & Debugging

Check Data Source

Look for the data source indicator in the UI:

Source: Azure CosmosDB (150 listings)

API Debugging

# Test API directly
curl http://localhost:3000/api/rental-listings?state=OR

# With filters
curl "http://localhost:3000/api/rental-listings?state=OR&city=Portland"

Console Logging

The page logs useful information:

console.log('Fetching rental listings from database...');
console.warn('No real data available, falling back to sample data');
console.error('Error generating report:', error);

Future Enhancements

Planned Features:

  • Additional Filters

    • Price range slider
    • Property type filter
    • Availability status filter
    • Voucher acceptance filter
  • Caching Layer

    • Redis cache for API responses
    • Configurable TTL
    • Cache invalidation on sync
  • Real-Time Updates

    • WebSocket connection
    • Live data refresh
    • Notification on new listings
  • Advanced Analytics

    • Historical trends
    • Seasonal patterns
    • Comparative analysis
  • Export Options

    • PDF report generation
    • CSV data export
    • Email scheduling

Troubleshooting

Issue: "No Azure listings found"

Possible Causes:

  1. No properties synced yet
  2. All properties have null/zero prices
  3. Filters too restrictive

Solutions:

# Check if properties exist
SELECT COUNT(*) FROM properties WHERE price > 0;

# Run sync if needed
# Navigate to /access-crm → Sync Properties

Issue: API returns 500 error

Check:

  1. Supabase connection
  2. Environment variables
  3. Server logs

Debug:

// In route.ts, add logging
console.log('Fetching properties:', { city, state });
console.log('Query result:', data);

Issue: Report shows wrong data

Verify:

  1. Data source indicator (Real vs Sample)
  2. Active filters
  3. Browser console for errors

Testing

Test Real Data Integration

  1. Navigate to /access-crm/reports/rental-heatmap
  2. Ensure "🟢 Real Data" is selected
  3. Check data source shows "Azure CosmosDB"
  4. Verify listing count matches database

Test Filters

  1. Click "Filters" button
  2. Enter city name (e.g., "Portland")
  3. Verify report regenerates
  4. Check listing count decreases appropriately

Test Error Handling

  1. Disconnect from database (simulate error)
  2. Verify graceful fallback to sample data
  3. Check error message displays
  4. Verify "Switch to Sample Data" button works

Configuration

API Defaults

// Default query parameters
const DEFAULT_STATE = 'OR';
const DEFAULT_MIN_PRICE = 0;
const DEFAULT_ORDER = 'created_at DESC';

Report Defaults

// Default region
const DEFAULT_REGION = 'Oregon Market Area';

// Default date range
const REPORT_PERIOD_DAYS = 30;

Summary

Fully Integrated with Azure CosmosDB ✅ Real-Time Data from Supabase properties table ✅ Flexible Filtering by city, state, price ✅ Error Resilient with graceful fallbacks ✅ User-Friendly toggle between real and sample data ✅ Well-Documented API and implementation

The integration is complete and production-ready! Users can now generate market heatmap reports using actual rental listing data from Azure CosmosDB.


Last Updated: November 24, 2025
Status: ✅ Complete and Production Ready
Data Source: Azure CosmosDB → Supabase → API → Heatmap


Use links in each imported doc to open its source.