Pull to refresh

System Architecture & Documentation

Reference guide for the Lead/Deal analysis experience and enrichment pipeline.

Table of Contents

  1. PropertyInsightsCard
  2. EnhancedLeviAnalysis
  3. Lead Detail Page Layout
  4. Deal Detail Page Overview
  5. n8n Workflow Integration
  6. Branding & Data Source Disclosure
  7. SMS Campaign System (Acquisition Control)

1) PropertyInsightsCard

File: components/leads/PropertyInsightsCard.jsx

What it displays

  • Always renders grouped sections from top-level columns in enhanced_deal_analysis:
    • OWNERSHIP: owner_name, ownership_months (converted to years), is_absentee, is_distressed
    • TAX & LIENS: tax_assessed_value, tax_delinquent, lien_count
    • SALE HISTORY: last_sale_date, last_sale_price, previous_listing_price, previous_listing_dom
    • VALUATION: estimated_market_value, price_per_acre, lot_size_acres, asking_price
  • If propertyreach_raw (JSONB) exists, it expands with additional sections using exact camelCase fields from the raw object:
    • PROPERTY DETAILS: apn, landUse, bedrooms, bathrooms, rooms, stories, squareFeet, groundFloorSqft, lotAcres, lotSquareFeet, yearBuilt, pool, deck, hoa, hoaType, vacant, airConditioning, garage/garageUnits, appliances (array)
    • LOCATION: fullAddress, city, state, zip, county, fips, latitude, longitude (shown as GPS), floodZone
    • OWNER DETAILS: contacts[] → name, age, gender, education, occupation, language, phones, emails, mailingAddress
    • FINANCIAL: assessedImprovement, marketImprovement, linkedEstimate, pricePerSqFt, estimatedEquity (shown as percent)
    • SALE & LISTING: lastSaleDate, lastSalePrice, loanRecording, priorPurchaseMethod, listingDate/status/type/price
    • LINKED PROPERTIES: linkedProperties count, linkedVacant count
  • Formatting: currency values show as $ with commas, dates are human-readable, booleans display as Yes/No badges, GPS as decimal coordinates, arrays comma-separated.

Data source & fetching

  • Reads from Supabase view/table enhanced_deal_analysis via supabaseProxy using: viewName = enhanced_deal_analysis?lead_id=eq.${leadId}&order=analyzed_at.desc&limit=1 (schema: public).
  • Primary fields come from individual columns; optional propertyreach_raw JSONB is parsed when present for expanded view.
  • Hidden entirely if no record is returned for the lead.

Placement

  • Rendered on Lead Detail immediately after Quick Actions.

2) EnhancedLeviAnalysis

File: components/leads/EnhancedLeviAnalysis.jsx

  • Sections shown: Property & Valuation, Risk Assessment, Pricing Strategy, Scorecard, Due Diligence, Analyst Notes.
  • Change: The previous “PROPERTYREACH ENRICHMENT DATA” accordion (and its empty-state message) was removed. All enrichment now lives in PropertyInsightsCard.

3) Lead Detail Page Layout Order

File: pages/LeadDetail.jsx

  1. Property Images
  2. Quick Actions
  3. Property Insights Card (this new card)
  4. Quick Links
  5. Deal Score (DealSnapshotStrip)
  6. Quick Stats
  7. LEVI Analysis
  8. Lead Information

4) Deal Detail Page Overview

File: pages/DealDetails.jsx (overview)

  • Consolidated dashboard for a single deal with tabs for Acquisition, Disposition, Financials, Due Diligence, and AI-driven Intelligence.
  • Shows property imagery/maps, enhanced analysis (LEVI), risk & valuation metrics, comparable sales, exit strategies, and collaborative artifacts (contacts, documents, timeline, notes).
  • Pulls enriched data and KPIs from Supabase mirrors and Base44 entities, surfacing current stage and calculated KPIs.

5) n8n Workflow Integration

  • The GPT Agent Land Deal Processor (n8n) writes the full PropertyReach API payload to enhanced_deal_analysis.propertyreach_raw (JSONB) via Supabase.
  • Older analyses may have propertyreach_raw = NULL; on new runs it is populated. When present, PropertyInsightsCard automatically expands to display detailed sections backed by raw fields.

6) Branding & Data Source Disclosure

  • End-user UI intentionally hides third-party branding. The card header reads “Property Insights” with no source attributions.
  • References to the underlying data provider are internal-only within documentation and code comments.

7) SMS Campaign System (Acquisition Control)

Page: pages/AcquisitionControl.jsx

Architecture Overview

The Campaign System is a 4-tab interface for managing bulk SMS outreach. It uses two new entities (SMSCampaign, LeadList) plus the existing Lead entity. Sending is handled client-side via direct n8n webhook calls.

Entities

  • SMSCampaign (entities/SMSCampaign.json): Stores campaign metadata (name, status, schedule, send counts). Status lifecycle: draft → scheduled → sending → completed/paused. RLS restricted to admin/staff/owner/programmer roles.
  • LeadList (entities/LeadList.json): Represents an imported CSV file. Links to a campaign via campaign_id. The batch_id field matches the base44_id on Lead records for filtering. Status lifecycle: uploaded → mapped → ready → sending → sent.
  • Lead (existing): CSV-imported leads are tagged with source='csv_import' and base44_id=<batch_id>. The batch_id is generated from the list label + timestamp.

Components

  • components/acquisition/UploadListsTab.jsx — Multi-file CSV upload with column auto-mapping, label editing, and batch import. Creates LeadList records on import.
  • components/acquisition/CampaignsTab.jsx — Campaign CRUD, list assignment modal, schedule modal, Send Now with real-time progress, Pause/Resume.
  • components/acquisition/ListsTab.jsx — List table with assign-to-campaign, direct send, and delete actions.
  • components/acquisition/QuickSendTab.jsx — Legacy ad-hoc send using Supabase-backed countEligibleLeads and getEligibleLeads backend functions.
  • components/acquisition/SMSProgressBar.jsx — Reusable progress bar showing sent/success/failed counts.

SMS Delivery Flow

  1. Leads are fetched from Base44 Lead entity filtered by base44_id = batch_id and status = 'new'.
  2. For each lead, a randomized message is generated using the lead's city/county.
  3. Message is POSTed to https://n8n.srv1251391.hstgr.cloud/webhook/send-sms-manual with {phone, message, leadId}.
  4. On success, the Lead record is updated: status → 'contacted', last_contact_at → now().
  5. Campaign/List entity records are updated with final send counts and status.

Access Control

The page checks user.role against ['admin', 'staff', 'owner', 'programmer']. Unauthorized users see a "Access Denied" screen. Both entities have matching RLS rules.

Quick Send (Legacy)

The Quick Send tab uses two backend functions (functions/countEligibleLeads.js and functions/getEligibleLeads.js) that query the Supabase leads table directly, cross-referencing the conversations table to exclude already-contacted phones. This path is separate from the campaign system and exists for ad-hoc sends.