5585 lines
307 KiB
React
5585 lines
307 KiB
React
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||
import { toast } from 'sonner';
|
||
import { logger } from '../utils/logger';
|
||
|
||
// --- DATA CONSTANTS (Extracted from old App.jsx) ---
|
||
|
||
const RAW_LOCATIONS = [
|
||
// Residential
|
||
{ address: "2612 Dunwick Dr, Plano, TX 75023", lat: 33.07084060970922, lng: -96.74558398435258, type: "Residential" },
|
||
{ address: "2608 Dunwick Dr, Plano, TX 75023", lat: 33.070834939506284, lng: -96.74532009555337, type: "Residential" },
|
||
{ address: "2604 Dunwick Dr, Plano, TX 75023", lat: 33.07081036862272, lng: -96.74507199497292, type: "Residential" },
|
||
{ address: "2600 Dunwick Dr, Plano, TX 75023", lat: 33.07083115937081, lng: -96.74483066077192, type: "Residential" },
|
||
{ address: "2516 Dunwick Dr, Plano, TX 75023", lat: 33.070770677180924, lng: -96.74459158203076, type: "Residential" },
|
||
{ address: "2637 Rothland Ln, Plano, TX 75023", lat: 33.070426683935366, lng: -96.74557721797312, type: "Residential" },
|
||
{ address: "2633 Rothland Ln, Plano, TX 75023", lat: 33.07042290378234, lng: -96.74534265015158, type: "Residential" },
|
||
{ address: "2629 Rothland Ln, Plano, TX 75023", lat: 33.07042857401182, lng: -96.74508552773183, type: "Residential" },
|
||
{ address: "2617 Rothland Ln, Plano, TX 75023", lat: 33.070432354164595, lng: -96.74435024782977, type: "Residential" },
|
||
{ address: "2513 Rothland Ln, Plano, TX 75023", lat: 33.07016207283135, lng: -96.74304208113281, type: "Residential" },
|
||
// New Residential Data
|
||
{ address: "6613 Phoenix Pl, Plano, TX 75023, USA", lat: 33.063266298016536, lng: -96.76932248857813, type: "Residential" },
|
||
{ address: "6609 Phoenix Pl, Plano, TX 75023, USA", lat: 33.063141994627365, lng: -96.76932089372718, type: "Residential" },
|
||
{ address: "6605 Phoenix Pl, Plano, TX 75023, USA", lat: 33.06302437405446, lng: -96.76933365253466, type: "Residential" },
|
||
{ address: "6601 Phoenix Pl, Plano, TX 75023, USA", lat: 33.06290541672423, lng: -96.7693424242148, type: "Residential" },
|
||
{ address: "6617 Phoenix Pl, Plano, TX 75023, USA", lat: 33.063379067434504, lng: -96.7693376006764, type: "Residential" },
|
||
{ address: "3913 Arizona Pl, Plano, TX 75023, USA", lat: 33.06318117622371, lng: -96.7698079594919, type: "Residential" },
|
||
{ address: "3917 Arizona Pl, Plano, TX 75023, USA", lat: 33.063193097393324, lng: -96.76997201609075, type: "Residential" },
|
||
{ address: "3921 Arizona Pl, Plano, TX 75023, USA", lat: 33.0631946868825, lng: -96.77014555573001, type: "Residential" },
|
||
{ address: "3920 Arizona Pl, Plano, TX 75023, USA", lat: 33.06252788822516, lng: -96.77010020638036, type: "Residential" },
|
||
{ address: "3916 Arizona Pl, Plano, TX 75023, USA", lat: 33.06254219373469, lng: -96.76993520147747, type: "Residential" },
|
||
{ address: "3912 Arizona Pl, Plano, TX 75023, USA", lat: 33.06254378323562, lng: -96.76975502370993, type: "Residential" },
|
||
{ address: "3908 Arizona Pl, Plano, TX 75023, USA", lat: 33.06253583573075, lng: -96.76959191541512, type: "Residential" },
|
||
{ address: "3904 Arizona Pl, Plano, TX 75023, USA", lat: 33.062526298723945, lng: -96.769445876593, type: "Residential" },
|
||
{ address: "3900 Arizona Pl, Plano, TX 75023, USA", lat: 33.06252153022017, lng: -96.76928656151433, type: "Residential" },
|
||
{ address: "3917 Sailmaker Ln, Plano, TX 75023, USA", lat: 33.0622004503711, lng: -96.76993899469362, type: "Residential" },
|
||
{ address: "3913 Sailmaker Ln, Plano, TX 75023, USA", lat: 33.06218773431336, lng: -96.76979295587151, type: "Residential" },
|
||
{ address: "3909 Sailmaker Ln, Plano, TX 75023, USA", lat: 33.06220362938525, lng: -96.76961846792823, type: "Residential" },
|
||
{ address: "3905 Sailmaker Ln, Plano, TX 75023, USA", lat: 33.06221157692012, lng: -96.76945725624147, type: "Residential" },
|
||
{ address: "3901 Sailmaker Ln, Plano, TX 75023, USA", lat: 33.06219091332797, lng: -96.7692808716901, type: "Residential" },
|
||
|
||
// Commercial
|
||
{ address: "6909 Custer Rd, Plano, TX 75023", lat: 33.07020596727293, lng: -96.7390826077087, type: "Commercial" },
|
||
{ address: "2112 Winslow Dr, Plano, TX 75023", lat: 33.069360828751016, lng: -96.73770127012321, type: "Commercial" },
|
||
{ address: "7224 Independence Pkwy, Plano, TX 75025", lat: 33.07506839285908, lng: -96.74984052259599, type: "Commercial" },
|
||
// New Commercial Data
|
||
{ address: "Dallas/Plano Marriott at Legacy Town Center, 7121 Bishop Rd, Plano, TX 75024, United States", lat: 33.074457402913424, lng: -96.8222322519585, type: "Commercial" },
|
||
{ address: "Legacy Town Center Plano, 5760 Daniel Rd, Plano, TX 75024, United States", lat: 33.07360554164325, lng: -96.82142050370054, type: "Commercial" },
|
||
{ address: "WeWork Office Space & Coworking, 6900 Dallas Pkwy Suite 300, Plano, TX 75024, United States", lat: 33.07281088750022, lng: -96.8235902233433, type: "Commercial" },
|
||
{ address: "Truluck's Ocean's Finest Seafood and Crab, 7161 Bishop Rd, Plano, TX 75024, United States", lat: 33.07535565705897, lng: -96.82174939646889, type: "Commercial" },
|
||
{ address: "Del Frisco's Grille, 7200 Bishop Rd Suite D9, Plano, TX 75024, United States", lat: 33.07630819707901, lng: -96.82124417346868, type: "Commercial" },
|
||
{ address: "Scruffy Duffies, 5865 Kincaid Rd E8, Plano, TX 75024, United States", lat: 33.07601336437884, lng: -96.82257940570773, type: "Commercial" },
|
||
{ address: "Half Shells, 7201 Bishop Rd #E4, Plano, TX 75024, United States", lat: 33.07635733578195, lng: -96.82171330913596, type: "Commercial" },
|
||
{ address: "Mi Cocina, 5760 Legacy Dr #B7, Plano, TX 75024, United States", lat: 33.07743082031222, lng: -96.82109531315771, type: "Commercial" },
|
||
{ address: "Leasing Office space and The Kincaid at Legacy Apartment, 7200 Dallas Pkwy, Plano, TX 75024, United States", lat: 33.07619102009707, lng: -96.82340039310428, type: "Commercial" },
|
||
{ address: "Issil Beauty Spa, 7140 Bishop Rd #F4, Plano, TX 75024, United States", lat: 33.07550110630488, lng: -96.82122287598153, type: "Commercial" },
|
||
|
||
// Apartments
|
||
{ address: "2100 Legacy Dr, Plano, TX 75023", lat: 33.07035321131275, lng: -96.73493675556306, type: "Apartments" },
|
||
{ address: "2401 Brown Deer Trail, Plano, TX 75023", lat: 33.06897509321587, lng: -96.7419549494157, type: "Residential" },
|
||
];
|
||
|
||
const PROPERTY_STYLES = [
|
||
{
|
||
label: "Luxury Estate",
|
||
canvassingStatus: "Customer",
|
||
photos: [
|
||
{ id: 'le_1', url: "/assets/images/properties/Luxury_Driveway_Residence.jpg", caption: "Front Exterior" },
|
||
{ id: 'le_2', url: "/assets/images/properties/Sunny_Suburban_House.jpg", caption: "Side Elevation" },
|
||
{ id: 'le_3', url: "/assets/images/properties/Residential_Roof_Texture.jpg", caption: "Roof Quality" }
|
||
],
|
||
baseValue: 850000,
|
||
areaBase: 4200,
|
||
beds: 5,
|
||
baths: 4.5,
|
||
yearBase: 2010,
|
||
desc: "Luxury estate with custom finishes throughout.",
|
||
condition: 5
|
||
},
|
||
{
|
||
label: "Standard Family Home",
|
||
canvassingStatus: "Neutral",
|
||
photos: [
|
||
{ id: 'sf_1', url: "/assets/images/properties/Modern_White_Suburban_Home.jpg", caption: "Front View" },
|
||
{ id: 'sf_2', url: "/assets/images/properties/Brick_Front_Porch_Home.jpg", caption: "Entryway" },
|
||
{ id: 'sf_3', url: "/assets/images/properties/Overlapping_Roof_Shingles.jpg", caption: "Roof Condition" },
|
||
{ id: 'sf_4', url: "/assets/images/properties/Red_Shingle_Cottage.jpg", caption: "Cottage Style" }
|
||
],
|
||
baseValue: 450000,
|
||
areaBase: 2800,
|
||
beds: 4,
|
||
baths: 3,
|
||
yearBase: 1995,
|
||
desc: "Well-maintained family home in established neighborhood.",
|
||
condition: 4
|
||
},
|
||
{
|
||
label: "Fixer Upper",
|
||
canvassingStatus: "Hot Lead",
|
||
photos: [
|
||
{ id: 'fu_1', url: "/assets/images/properties/Beige_Two_Story_House.jpg", caption: "Street View" },
|
||
{ id: 'fu_2', url: "/assets/images/properties/Hail_Damaged_Shingles.jpg", caption: "Hail Damage" },
|
||
{ id: 'fu_3', url: "/assets/images/properties/Broken_Roof_Surface.jpg", caption: "Critical Repairs Needed" },
|
||
{ id: 'fu_4', url: "/assets/images/properties/Cracked_Storm_Shingles.jpg", caption: "Storm Damage" },
|
||
{ id: 'fu_5', url: "/assets/images/properties/Storm_Worn_Roof.jpg", caption: "Worn Roof" }
|
||
],
|
||
baseValue: 320000,
|
||
areaBase: 2400,
|
||
beds: 3,
|
||
baths: 2,
|
||
yearBase: 1980,
|
||
desc: "Great potential, needs cosmetic updates.",
|
||
condition: 2
|
||
},
|
||
{
|
||
label: "Renovated Modern",
|
||
canvassingStatus: "Renovated",
|
||
photos: [
|
||
{ id: 'rm_1', url: "/assets/images/properties/Minimal_Flat_Roof_House.jpg", caption: "Modern Facade" },
|
||
{ id: 'rm_2', url: "/assets/images/properties/Red_Tiled_Roof_House.jpg", caption: "New Roof" },
|
||
{ id: 'rm_3', url: "/assets/images/properties/Roof_Inspection_Angle.jpg", caption: "Drone Inspection" },
|
||
{ id: 'rm_4', url: "/assets/images/properties/Red_Metal_Roof_Home.jpg", caption: "Metal Roof Upgrade" }
|
||
],
|
||
baseValue: 580000,
|
||
areaBase: 3200,
|
||
beds: 4,
|
||
baths: 3.5,
|
||
yearBase: 1990,
|
||
desc: "Recently renovated with modern aesthetics.",
|
||
condition: 5
|
||
}
|
||
];
|
||
|
||
const COMMERCIAL_VARIANTS = [
|
||
{ name: "Legacy Retail Group", type: "Retail Mix", email: "leasing@legacyretail.com" },
|
||
{ name: "Custer Office Park LLC", type: "Office Space", email: "mgmt@custeroffice.com" },
|
||
{ name: "Plano Investments Inc", type: "Mixed Use", email: "contact@planoinvest.com" },
|
||
{ name: "North Texas Prop Co", type: "Service Center", email: "info@ntxprops.com" }
|
||
];
|
||
|
||
const REAL_NAMES = [
|
||
"James Beauford", "Sarah Miller", "Robert Henderson", "Karen Smith", "Michael Chang",
|
||
"Patricia O'Neil", "Dr. Anish Patel", "Marcus Johnson", "Elena Rodriguez", "Greg Alston",
|
||
"Linda Grey", "Bill Thompson", "Jennifer Wu", "David Kowalski", "Omar Farooq",
|
||
"Lisa Chen", "Tom Baker", "Rachel Green", "Steve Martin", "Angela White"
|
||
];
|
||
|
||
const OCCUPATIONS = [
|
||
"Software Engineer", "Petroleum Engineer", "Teacher", "Nurse Practitioner",
|
||
"Small Business Owner", "Accountant", "Marketing Director", "retired",
|
||
"Attorney", "Real Estate Agent", "Sales Manager", "Consultant"
|
||
];
|
||
|
||
const EMPLOYERS_BY_OCCUPATION = {
|
||
"Software Engineer": ["Google", "Microsoft", "Toyota Connected", "Capital One", "Intuit"],
|
||
"Petroleum Engineer": ["ExxonMobil", "Chevron", "Pioneer Natural Resources", "Denbury"],
|
||
"Teacher": ["Plano ISD", "Frisco ISD", "Dallas ISD", "Richardson ISD"],
|
||
"Nurse Practitioner": ["Baylor Scott & White", "Medical City Plano", "Texas Health Resources"],
|
||
"Small Business Owner": ["Self Employed", "Local Boutique", "Consultancy Firm"],
|
||
"Accountant": ["Deloitte", "PwC", "EY", "KPMG", "Local CPA Firm"],
|
||
"Marketing Director": ["PepsiCo", "Toyota", "Frito-Lay", "Yum! Brands"],
|
||
"retired": ["N/A"],
|
||
"Attorney": ["Jones Day", "Baker Botts", "Norton Rose Fulbright", "Local Law Firm"],
|
||
"Real Estate Agent": ["Keller Williams", "Ebby Halliday", "Compass", "Remax"],
|
||
"Sales Manager": ["Oracle", "Salesforce", "AT&T", "Samsung"],
|
||
"Consultant": ["McKinsey", "BCG", "Accenture", "Freelance"]
|
||
};
|
||
|
||
// --- NEW INSURANCE SCHEMA CONSTANTS ---
|
||
const INSURANCE_COMPANIES = [
|
||
"State Farm", "Allstate", "Liberty Mutual", "Farmers", "Nationwide",
|
||
"USAA", "Chubb", "Travelers", "Progressive", "American Family"
|
||
];
|
||
|
||
const ADJUSTER_TYPES = ["Staff Adjuster", "Independent Adjuster", "Public Adjuster"];
|
||
|
||
// --- DATA GENERATION HELPERS ---
|
||
|
||
function generatePolygon(lat, lng) {
|
||
const dLat = 0.0001; // ~11 meters
|
||
const dLng = 0.0001; // ~11 meters
|
||
return [
|
||
[lat + dLat, lng - dLng], // Top Left
|
||
[lat + dLat, lng + dLng], // Top Right
|
||
[lat - dLat, lng + dLng], // Bottom Right
|
||
[lat - dLat, lng - dLng], // Bottom Left
|
||
];
|
||
}
|
||
|
||
function generateEmail(name, company = null) {
|
||
if (company) return company;
|
||
const domains = ["gmail.com", "yahoo.com", "outlook.com", "icloud.com"];
|
||
const domain = domains[Math.floor(Math.random() * domains.length)];
|
||
const parts = name.toLowerCase().split(' ');
|
||
const last = parts[parts.length - 1].replace(/[^a-z]/g, '');
|
||
const first = parts[0].replace(/[^a-z]/g, '');
|
||
return `${first}.${last}@${domain}`;
|
||
}
|
||
|
||
// Weighted Random Helper
|
||
function getWeightedRandom(items, weights) {
|
||
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
||
const random = Math.random() * totalWeight;
|
||
let weightSum = 0;
|
||
|
||
for (let i = 0; i < items.length; i++) {
|
||
weightSum += weights[i];
|
||
if (random <= weightSum) {
|
||
return items[i];
|
||
}
|
||
}
|
||
return items[items.length - 1]; // Fallback
|
||
}
|
||
|
||
function generateProperties() {
|
||
// 1. Generate Base Properties
|
||
const allProperties = RAW_LOCATIONS.map((loc, index) => {
|
||
const isCommercial = loc.type === "Commercial" || loc.type === "Apartments";
|
||
|
||
// Shuffle styles slightly differently than index-based to avoid repeating patterns
|
||
const styleIndex = Math.floor(Math.random() * PROPERTY_STYLES.length);
|
||
const style = isCommercial ? null : PROPERTY_STYLES[styleIndex];
|
||
|
||
let ownerName, ownerEmail, occupation, income, employerName;
|
||
|
||
if (isCommercial) {
|
||
const commVariant = COMMERCIAL_VARIANTS[index % COMMERCIAL_VARIANTS.length];
|
||
ownerName = commVariant.name;
|
||
ownerEmail = commVariant.email;
|
||
occupation = "Commercial Owner";
|
||
income = "$1M+";
|
||
employerName = "Self";
|
||
} else {
|
||
ownerName = REAL_NAMES[index % REAL_NAMES.length];
|
||
ownerEmail = generateEmail(ownerName);
|
||
occupation = OCCUPATIONS[index % OCCUPATIONS.length];
|
||
|
||
const potentialEmployers = EMPLOYERS_BY_OCCUPATION[occupation] || ["Self Employed"];
|
||
employerName = potentialEmployers[index % potentialEmployers.length];
|
||
income = ["$80k - $120k", "$120k - $180k", "$200k+", "$60k - $90k"][index % 4];
|
||
}
|
||
|
||
// Randomize Year and Value to break lines
|
||
const yearOffset = Math.floor(Math.random() * 20) - 10; // +/- 10 years
|
||
const builtYear = isCommercial ? 1995 + (index % 10) : style.yearBase + yearOffset;
|
||
|
||
const valueVariance = (Math.random() * 0.4) - 0.2; // +/- 20%
|
||
const baseSqft = isCommercial ? 12000 + (index * 500) : style.areaBase;
|
||
const sqft = Math.floor(baseSqft * (1 + (valueVariance / 2))); // Size correlates with value but not perfectly
|
||
|
||
const baseVal = isCommercial ? 2500000 + (index * 100000) : style.baseValue;
|
||
const value = Math.floor(baseVal * (1 + valueVariance));
|
||
|
||
// Rental Logic: Target ~10-15 rented (approx 40-50% chance given ~30 props)
|
||
const isRented = Math.random() > 0.55;
|
||
|
||
let tenantData = {};
|
||
if (isRented) {
|
||
const tName = isCommercial ? "Global Corp Inc." : REAL_NAMES[(index + 5) % REAL_NAMES.length];
|
||
const tOcc = isCommercial ? "Retail/Office Mix" : OCCUPATIONS[(index + 3) % OCCUPATIONS.length];
|
||
const tPhone = `469-555-${2000 + index}`;
|
||
const tEmail = generateEmail(tName.split(' ')[0] || "tenant", isCommercial ? "tenant.com" : null);
|
||
const tEmployer = isCommercial ? "N/A" : (EMPLOYERS_BY_OCCUPATION[tOcc]?.[0] || "Self");
|
||
const tIncome = isCommercial ? "$5M+" : ["$60k", "$85k", "$110k", "$140k"][index % 4];
|
||
const tFamily = isCommercial ? "N/A" : (Math.random() > 0.4 ? "Family" : "Single");
|
||
const tLeaseType = isCommercial ? "Commercial NNN" : "Residential Standard";
|
||
|
||
tenantData = {
|
||
currentlyRented: true,
|
||
currentTenantName: tName,
|
||
tenantPhone: tPhone,
|
||
tenantEmail: tEmail,
|
||
tenantOccupation: tOcc,
|
||
tenantEmployer: tEmployer,
|
||
tenantAnnualIncome: tIncome,
|
||
livingStatus: tFamily,
|
||
leaseType: tLeaseType,
|
||
currentMonthlyRentAmount: Math.floor(value * 0.008 / 100) * 100,
|
||
leaseStartDate: "2023-01-01",
|
||
leaseEndDate: "2025-01-01",
|
||
leaseSignedDate: "2022-12-15",
|
||
ownerWillingToRent: false // Already rented
|
||
};
|
||
} else {
|
||
tenantData = {
|
||
currentlyRented: false,
|
||
currentTenantName: null,
|
||
tenantPhone: null,
|
||
tenantEmail: null,
|
||
tenantOccupation: null,
|
||
tenantEmployer: null,
|
||
tenantAnnualIncome: null,
|
||
livingStatus: null,
|
||
leaseType: null,
|
||
currentMonthlyRentAmount: null,
|
||
leaseStartDate: null,
|
||
leaseEndDate: null,
|
||
leaseSignedDate: null,
|
||
ownerWillingToRent: Math.random() > 0.6
|
||
};
|
||
}
|
||
|
||
const rentAmount = tenantData.currentMonthlyRentAmount; // For use below if needed, though mapped in tenantData now
|
||
|
||
// Weighted Roof Condition
|
||
const roofCondition = getWeightedRandom(
|
||
["Good", "Fair", "Excellent", "Needs Repair"],
|
||
[0.4, 0.3, 0.2, 0.1]
|
||
);
|
||
|
||
// Neighborhood Rating
|
||
const neighRating = getWeightedRandom([7, 8, 9, 10, 6, 5], [0.3, 0.3, 0.2, 0.1, 0.05, 0.05]);
|
||
|
||
return {
|
||
id: index + 1,
|
||
center: [loc.lat, loc.lng],
|
||
polygon: generatePolygon(loc.lat, loc.lng),
|
||
// Placeholder status, will be overwritten
|
||
canvassingStatus: "Neutral",
|
||
photos: isCommercial ? [
|
||
{ id: `com_${index}_1`, url: "/assets/images/properties/Apartment_Complex.jpg", caption: "Exterior View" },
|
||
{ id: `com_${index}_2`, url: "/assets/images/properties/Mall_Rooftop_HVAC_Units.jpg", caption: "HVAC Systems" }
|
||
] : style.photos,
|
||
|
||
// --- NEW FIELDS FOR ADMIN ACTION CENTER ---
|
||
assignedAgentId: null, // Default unassigned
|
||
lastContactDate: null,
|
||
pendingSignature: false,
|
||
proposalSentDate: null,
|
||
proposalValue: 0,
|
||
// ------------------------------------------
|
||
|
||
propertyData: {
|
||
propertyId: `P-${2600 + index}`,
|
||
propertyAddress: loc.address,
|
||
propertyType: loc.type,
|
||
totalBuiltUpArea: sqft,
|
||
yearBuilt: builtYear,
|
||
numberOfBedrooms: isCommercial ? 0 : style.beds,
|
||
numberOfBathrooms: isCommercial ? 4 : style.baths,
|
||
numberOfParkingSpaces: isCommercial ? 25 : 2,
|
||
currentEstimatedMarketValue: value,
|
||
propertyTaxAssessmentValue: Math.floor(value * 0.85),
|
||
roofCondition: roofCondition,
|
||
renovationDescription: isCommercial ? "Commercial Maintenance" : style.desc,
|
||
renovationCost: Math.floor(value * (Math.random() * 0.05 + 0.01)),
|
||
lastMajorRenovationDate: "2020-05-15",
|
||
lastRoofRepairReplacementDate: "2018-02-10",
|
||
recentMajorRepairs: "Routine Maintenance",
|
||
|
||
// Spread new robust tenant data
|
||
...tenantData,
|
||
|
||
expectedMonthlyRentAmount: Math.floor(value * 0.008 / 100) * 100,
|
||
rentalAvailabilityDate: null,
|
||
rentalTermsPreference: null,
|
||
schoolDistrict: "Plano ISD",
|
||
neighborhoodRating: neighRating,
|
||
crimeRateIndex: "Low",
|
||
publicTransportationAccess: "Bus Line 204",
|
||
propertyConditionRating: isCommercial ? 4 : style.condition,
|
||
furnishingStatus: "Unfurnished",
|
||
notes: isCommercial ? "Prime commercial location" : style.desc,
|
||
latestPurchasePrice: Math.floor(value * 0.6),
|
||
latestPurchaseDate: "2015-06-20",
|
||
latestSaleListingDate: null,
|
||
latestSaleAskingPrice: null,
|
||
lotPlotSize: isCommercial ? "1.5 Acres" : "0.22 Acres",
|
||
// New Fields for Chatbot
|
||
pendingSignature: Math.random() > 0.9, // 10% chance
|
||
closedDate: Math.random() > 0.8 ? "2026-01-15" : null // Simple mock for revenue queries
|
||
},
|
||
ownerData: {
|
||
ownerId: `OWN-${200 + index}`,
|
||
fullName: ownerName,
|
||
primaryPhoneNumber: `972-555-${1000 + index}`,
|
||
secondaryPhoneNumber: null,
|
||
emailAddress: ownerEmail,
|
||
mailingAddress: loc.address,
|
||
occupation: occupation,
|
||
employerName: employerName,
|
||
annualIncomeRange: income,
|
||
creditScoreRange: "720+",
|
||
ownershipType: isCommercial ? "Corporate" : "Individual",
|
||
ownershipPercentage: "100%",
|
||
willingToRentProperty: Math.random() > 0.8,
|
||
desiredMonthlyRentalPrice: null,
|
||
rentalConditions: null,
|
||
willingToSellProperty: Math.random() > 0.85,
|
||
desiredSellingPrice: Math.floor(value * 1.1),
|
||
minimumAcceptableSellingPrice: Math.floor(value * 1.05)
|
||
},
|
||
// New Insurance Data Schema
|
||
insuranceData: (() => {
|
||
const claimFiled = Math.random() > 0.8; // 20% likely
|
||
if (!claimFiled) {
|
||
return {
|
||
insurance_company: getWeightedRandom(INSURANCE_COMPANIES, Array(10).fill(0.1)),
|
||
insurance_company_not_listed: false,
|
||
damage_location: null,
|
||
date_of_loss: null,
|
||
claim_filed: false,
|
||
claim_number: null,
|
||
has_paperwork: false,
|
||
adjuster_name: null,
|
||
adjuster_phone: null,
|
||
adjuster_ext: null,
|
||
adjuster_type: null,
|
||
adjuster_fax: null,
|
||
adjuster_email: null,
|
||
met_with_adjuster: false,
|
||
claim_approved: false
|
||
};
|
||
}
|
||
|
||
// If claim filed, populate detailed data
|
||
const lossDate = new Date();
|
||
lossDate.setDate(lossDate.getDate() - Math.floor(Math.random() * 60)); // Last 60 days
|
||
|
||
return {
|
||
insurance_company: getWeightedRandom(INSURANCE_COMPANIES, Array(10).fill(0.1)),
|
||
insurance_company_not_listed: false,
|
||
damage_location: "Roof, Gutters, Fence",
|
||
date_of_loss: lossDate.toISOString().split('T')[0],
|
||
claim_filed: true,
|
||
claim_number: `CLM-${Math.floor(Math.random() * 1000000)}`,
|
||
has_paperwork: Math.random() > 0.3,
|
||
adjuster_name: REAL_NAMES[Math.floor(Math.random() * REAL_NAMES.length)],
|
||
adjuster_phone: `214-555-${Math.floor(Math.random() * 9000) + 1000}`,
|
||
adjuster_ext: `${Math.floor(Math.random() * 900) + 100}`,
|
||
adjuster_type: ADJUSTER_TYPES[Math.floor(Math.random() * ADJUSTER_TYPES.length)],
|
||
adjuster_fax: `214-555-${Math.floor(Math.random() * 9000) + 1000}`,
|
||
adjuster_email: `adjuster.${Math.floor(Math.random() * 100)}@insurance.com`,
|
||
met_with_adjuster: Math.random() > 0.5,
|
||
claim_approved: Math.random() > 0.7
|
||
};
|
||
})()
|
||
};
|
||
});
|
||
|
||
// 2. Assign Statuses with Strict Counts
|
||
// User Request: 4-9 Hot Leads, 15-20 Customers
|
||
const countHotLeads = Math.floor(Math.random() * (9 - 4 + 1)) + 4; // 4 to 9
|
||
const countCustomers = Math.floor(Math.random() * (20 - 15 + 1)) + 15; // 15 to 20
|
||
|
||
// Create indices array and shuffle it
|
||
const indices = allProperties.map((_, i) => i);
|
||
for (let i = indices.length - 1; i > 0; i--) {
|
||
const j = Math.floor(Math.random() * (i + 1));
|
||
[indices[i], indices[j]] = [indices[j], indices[i]];
|
||
}
|
||
|
||
// Assign based on shuffled indices
|
||
indices.forEach((propIndex, i) => {
|
||
if (i < countHotLeads) {
|
||
allProperties[propIndex].canvassingStatus = "Hot Lead";
|
||
} else if (i < countHotLeads + countCustomers) {
|
||
allProperties[propIndex].canvassingStatus = "Customer";
|
||
} else {
|
||
// Remaining are Neutral, Renovated, or Not Interested
|
||
// Weighted: 70% Neutral, 20% Not Interested, 10% Renovated
|
||
allProperties[propIndex].canvassingStatus = getWeightedRandom(
|
||
["Neutral", "Not Interested", "Renovated"],
|
||
[0.7, 0.2, 0.1]
|
||
);
|
||
}
|
||
});
|
||
|
||
// --- INJECT REQUIRED MOCK DATA FOR ACTION CENTER ---
|
||
// A. Unassigned Leads (assignedAgentId: null, canvassingStatus: "Lead")
|
||
// Target addresses: 2600 Dunwick Dr, 3900 Arizona Pl, 2112 Winslow Dr, Dallas/Plano Marriott, Legacy Town Center
|
||
const unassignedTargets = [
|
||
"2600 Dunwick Dr, Plano, TX 75023",
|
||
"3900 Arizona Pl, Plano, TX 75023, USA", // Note: original data has USA
|
||
"2112 Winslow Dr, Plano, TX 75023",
|
||
"Dallas/Plano Marriott at Legacy Town Center, 7121 Bishop Rd, Plano, TX 75024, United States",
|
||
"Legacy Town Center Plano, 5760 Daniel Rd, Plano, TX 75024, United States"
|
||
];
|
||
|
||
const unassignedValues = {
|
||
"2600 Dunwick Dr, Plano, TX 75023": 53173,
|
||
"3900 Arizona Pl, Plano, TX 75023, USA": 11608,
|
||
"2112 Winslow Dr, Plano, TX 75023": 40669,
|
||
"Dallas/Plano Marriott at Legacy Town Center, 7121 Bishop Rd, Plano, TX 75024, United States": 15766,
|
||
"Legacy Town Center Plano, 5760 Daniel Rd, Plano, TX 75024, United States": 12792
|
||
};
|
||
|
||
allProperties.forEach(p => {
|
||
const addr = p.propertyData.propertyAddress;
|
||
if (unassignedTargets.includes(addr)) {
|
||
p.canvassingStatus = "Lead"; // "Lead" = Unassigned basically in this context, or just "Neutral" but we track assignments
|
||
p.assignedAgentId = null;
|
||
p.propertyData.currentEstimatedMarketValue = unassignedValues[addr];
|
||
} else {
|
||
// Default random assignment for others if not unassigned
|
||
if (p.canvassingStatus !== "Hot Lead") { // Keep Hot Leads potentially unassigned or specific
|
||
// p.assignedAgentId = "e1"; // Optional: assign everything else
|
||
}
|
||
}
|
||
});
|
||
|
||
// B. Hot Leads Not Contacted
|
||
// Data: 2629 Rothland Ln (2026-02-08), 3920 Arizona Pl (2026-02-08), 3912 Arizona Pl (Never), 3909 Sailmaker Ln (Never)
|
||
const hotLeadTargets = {
|
||
"2629 Rothland Ln, Plano, TX 75023": "2026-02-08T10:00:00Z",
|
||
"3920 Arizona Pl, Plano, TX 75023, USA": "2026-02-08T14:30:00Z",
|
||
"3912 Arizona Pl, Plano, TX 75023, USA": null,
|
||
"3909 Sailmaker Ln, Plano, TX 75023, USA": null
|
||
};
|
||
|
||
allProperties.forEach(p => {
|
||
if (hotLeadTargets.hasOwnProperty(p.propertyData.propertyAddress)) {
|
||
p.canvassingStatus = "Hot Lead";
|
||
p.lastContactDate = hotLeadTargets[p.propertyData.propertyAddress];
|
||
|
||
// Set values if needed match User Data?
|
||
// The prompt gave values: 2629 ($20,298), 3920 ($84,288), 3912 ($73,307), 3909 ($72,563)
|
||
if (p.propertyData.propertyAddress.includes("2629 Rothland")) p.propertyData.currentEstimatedMarketValue = 20298;
|
||
if (p.propertyData.propertyAddress.includes("3920 Arizona")) p.propertyData.currentEstimatedMarketValue = 84288;
|
||
if (p.propertyData.propertyAddress.includes("3912 Arizona")) p.propertyData.currentEstimatedMarketValue = 73307;
|
||
if (p.propertyData.propertyAddress.includes("3909 Sailmaker")) p.propertyData.currentEstimatedMarketValue = 72563;
|
||
}
|
||
});
|
||
|
||
// C. Pending Signatures
|
||
// Data: Client 1 (2612 Dunwick), Client 2 (2608 Dunwick), Client 9 (2617 Rothland), Client 12 (6609 Phoenix)
|
||
const signatureTargets = {
|
||
"2612 Dunwick Dr, Plano, TX 75023": { date: "2026-02-05", val: 8521 },
|
||
"2608 Dunwick Dr, Plano, TX 75023": { date: "2026-02-09", val: 16468 },
|
||
"2617 Rothland Ln, Plano, TX 75023": { date: "2026-02-08", val: 18392 },
|
||
"6609 Phoenix Pl, Plano, TX 75023, USA": { date: "2026-02-06", val: 31768 }
|
||
};
|
||
|
||
allProperties.forEach(p => {
|
||
if (signatureTargets.hasOwnProperty(p.propertyData.propertyAddress)) {
|
||
p.pendingSignature = true;
|
||
p.proposalSentDate = signatureTargets[p.propertyData.propertyAddress].date;
|
||
p.proposalValue = signatureTargets[p.propertyData.propertyAddress].val;
|
||
p.propertyData.pendingSignature = true; // Sync with inner object if needed, but we extended outer
|
||
} else {
|
||
p.pendingSignature = false;
|
||
}
|
||
});
|
||
|
||
// -------------------------------------------------------
|
||
|
||
// 3. Assign Tenants (Strict Counts & Data Depth)
|
||
// User Request: 10-15 Total Rented. 5-8 with "Complete" info, rest "Sparse" (Mandatory only).
|
||
// Current Date Context: Feb 5, 2026.
|
||
|
||
const totalRented = Math.floor(Math.random() * (15 - 10 + 1)) + 10; // 10 to 15
|
||
const fullDetailCount = Math.floor(Math.random() * (8 - 5 + 1)) + 5; // 5 to 8
|
||
const sparseDetailCount = totalRented - fullDetailCount;
|
||
|
||
// Reshuffle for tenant assignment (independent of status)
|
||
const tenantIndices = allProperties.map((_, i) => i);
|
||
for (let i = tenantIndices.length - 1; i > 0; i--) {
|
||
const j = Math.floor(Math.random() * (i + 1));
|
||
[tenantIndices[i], tenantIndices[j]] = [tenantIndices[j], tenantIndices[i]];
|
||
}
|
||
|
||
tenantIndices.forEach((propIndex, i) => {
|
||
if (i < totalRented) {
|
||
const prop = allProperties[propIndex];
|
||
const isFullDetail = i < fullDetailCount;
|
||
const isCommercial = prop.propertyData.propertyType === "Commercial" || prop.propertyData.propertyType === "Apartments";
|
||
const val = prop.propertyData.currentEstimatedMarketValue;
|
||
|
||
// Generate Base Data (Mandatory)
|
||
const tName = isCommercial ? "Global Corp Inc." : REAL_NAMES[(propIndex + 5) % REAL_NAMES.length];
|
||
const tPhone = `469-555-${3000 + propIndex}`;
|
||
|
||
// Optional Data (Full vs Sparse)
|
||
const tEmail = isFullDetail ? generateEmail(tName.split(' ')[0], isCommercial ? "enterprises.com" : null) : "";
|
||
const tOcc = isFullDetail ? (isCommercial ? "Retail/Office Mix" : OCCUPATIONS[(propIndex + 3) % OCCUPATIONS.length]) : "";
|
||
const tEmployer = isFullDetail ? (isCommercial ? "N/A" : (EMPLOYERS_BY_OCCUPATION[tOcc]?.[0] || "Self")) : "";
|
||
const tIncome = isFullDetail ? (isCommercial ? "$5M+" : ["$60k", "$85k", "$110k", "$140k"][propIndex % 4]) : "";
|
||
const tFamily = isFullDetail ? (isCommercial ? "N/A" : (Math.random() > 0.4 ? "Family" : "Single")) : "Single";
|
||
const tLeaseType = isFullDetail ? (isCommercial ? "Commercial NNN" : "Residential Standard") : "Residential Standard";
|
||
|
||
// Dates (Relative to Feb 2026)
|
||
// Start: Jan 2025 - Jan 2026
|
||
const startYear = Math.random() > 0.5 ? 2025 : 2026;
|
||
const tStart = `${startYear}-01-15`;
|
||
|
||
// Signed: 1 month before start
|
||
const tSigned = `${startYear - 1}-12-15`;
|
||
|
||
// End: 1 year after start (2026 or 2027)
|
||
const tEnd = `${startYear + 1}-01-14`;
|
||
|
||
const tRent = Math.floor(val * 0.008 / 100) * 100;
|
||
|
||
// Apply updates
|
||
allProperties[propIndex].propertyData = {
|
||
...allProperties[propIndex].propertyData,
|
||
currentlyRented: true,
|
||
currentTenantName: tName,
|
||
tenantPhone: tPhone,
|
||
|
||
// Optional / Detail Dependent
|
||
tenantEmail: tEmail,
|
||
tenantOccupation: tOcc,
|
||
tenantEmployer: tEmployer,
|
||
tenantAnnualIncome: tIncome,
|
||
livingStatus: tFamily,
|
||
leaseType: tLeaseType,
|
||
|
||
// Dates & Financials (Assign sparse ones too if needed, or leave blank? User said sparse has MANDATORY only. But Rent amount is needed to be "Rented" usually. I'll add Rent/Dates to Full, and maybe just Rent to Sparse?)
|
||
// Re-reading: "The remaining entries may include only the mandatory fields."
|
||
// I will add rent/dates to ALL rented properties because otherwise the map might look broken for "Rented" status without a lease end date. But strictly, I could leave them empty.
|
||
// Let's populate Dates/Rent for ALL, but Personal Info (Email/Job/Income) only for Full.
|
||
|
||
currentMonthlyRentAmount: tRent,
|
||
leaseStartDate: tStart,
|
||
leaseEndDate: tEnd,
|
||
leaseSignedDate: tSigned,
|
||
|
||
ownerWillingToRent: false
|
||
};
|
||
}
|
||
});
|
||
|
||
// 4. Refine Renovation Dates (Recent for 'Renovated' status)
|
||
// User Request: Not older than 6 months from Feb 2026 (Aug 2025 - Feb 2026)
|
||
allProperties.forEach(prop => {
|
||
if (prop.canvassingStatus === "Renovated") {
|
||
const monthsBack = Math.floor(Math.random() * 6); // 0 to 5 months ago
|
||
// Base date: Feb 2026.
|
||
// 0 months ago = Feb 2026. 1 = Jan 2026... 5 = Sep 2025.
|
||
// Let's pick a random day.
|
||
const year = monthsBack <= 1 ? 2026 : 2025;
|
||
let month = 2 - monthsBack; // Feb(2) - 0 = 2. Feb(2) - 5 = -3.
|
||
if (month <= 0) month += 12; // Wrap around to previous year
|
||
|
||
const day = Math.floor(Math.random() * 28) + 1;
|
||
const monthStr = month.toString().padStart(2, '0');
|
||
const dayStr = day.toString().padStart(2, '0');
|
||
|
||
prop.propertyData.lastMajorRenovationDate = `${year}-${monthStr}-${dayStr}`;
|
||
prop.propertyData.recentMajorRepairs = "Full Interior & Exterior Renovation";
|
||
prop.propertyData.renovationDescription = "Modernized kitchen, new flooring, fresh paint, roof inspection";
|
||
}
|
||
});
|
||
|
||
return allProperties;
|
||
}
|
||
|
||
// --- NEW DATA (Users & Meetings) ---
|
||
|
||
const MOCK_USERS = [
|
||
// Customers
|
||
{ id: 'c1', type: 'customer', username: 'alice', email: 'alice@example.com', password: 'password', name: 'Alice Customer', propertyId: 'P-2600' },
|
||
{ id: 'c2', type: 'customer', username: 'bob', email: 'bob@example.com', password: 'password', name: 'Bob Buyer' },
|
||
{ id: 'c3', type: 'customer', username: 'charlie', email: 'charlie@example.com', password: 'password', name: 'Charlie Client' },
|
||
|
||
// Field Agents (5 Agents)
|
||
{ id: 'e1', type: 'employee', empId: 'FA001', email: 'agent1@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Frank Agent', xp: 12450, doorsKnocked: 342, leadsGained: 45, appointmentsSet: 18, streakDays: 5, achievements: ['Hot Spot Hunter', 'Storm Chaser'] },
|
||
{ id: 'e2', type: 'employee', empId: 'FA002', email: 'agent2@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Fiona Field', xp: 8200, doorsKnocked: 215, leadsGained: 30, appointmentsSet: 12, streakDays: 2, achievements: ['Star Closer'] },
|
||
{ id: 'e3', type: 'employee', empId: 'FA003', email: 'agent3@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Fred Flyer', xp: 2100, doorsKnocked: 85, leadsGained: 5, appointmentsSet: 2, streakDays: 0, achievements: [] },
|
||
{ id: 'e4', type: 'employee', empId: 'FA004', email: 'agent4@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Felicity Fast', xp: 15600, doorsKnocked: 410, leadsGained: 65, appointmentsSet: 24, streakDays: 8, achievements: ['Map Master', 'Hot Spot Hunter', 'Storm Chaser'] },
|
||
{ id: 'e5', type: 'employee', empId: 'FA005', email: 'agent5@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Felix Fixer', xp: 5400, doorsKnocked: 156, leadsGained: 20, appointmentsSet: 8, streakDays: 1, achievements: ['First Knock'] },
|
||
|
||
// Admins (3 Admins)
|
||
{ id: 'a1', type: 'employee', empId: 'ADM01', email: 'admin@plano.com', password: 'password', role: 'ADMIN', name: 'Adam Admin' },
|
||
{ id: 'a2', type: 'employee', empId: 'ADM02', email: 'admin2@plano.com', password: 'password', role: 'ADMIN', name: 'Amanda Manager' },
|
||
{ id: 'a3', type: 'employee', empId: 'ADM03', email: 'admin3@plano.com', password: 'password', role: 'ADMIN', name: 'Arthur Director' },
|
||
|
||
// --- NEW ROLES (Owners Box) ---
|
||
// Owner 1 — Active, strong financials, one project under dispute
|
||
{
|
||
id: 'own_001',
|
||
type: 'owner',
|
||
username: 'justin',
|
||
email: 'justin@johnsondev.com',
|
||
password: 'password',
|
||
role: 'OWNER',
|
||
name: 'Justin Johnson',
|
||
companyName: 'Johnson Development Group',
|
||
phone: '972-555-1234'
|
||
},
|
||
// Owner 2 — High Risk, vendor payment delays, over-budget project
|
||
{
|
||
id: 'own_002',
|
||
type: 'owner',
|
||
username: 'diana',
|
||
email: 'diana@reevesconstruction.com',
|
||
password: 'password',
|
||
role: 'OWNER',
|
||
name: 'Diana Reeves',
|
||
companyName: 'Reeves Construction Partners',
|
||
phone: '214-555-5678'
|
||
},
|
||
// Contractor
|
||
{
|
||
id: 'con_001',
|
||
type: 'contractor',
|
||
username: 'mike',
|
||
email: 'mike@texasbuilders.com',
|
||
password: 'password',
|
||
role: 'CONTRACTOR',
|
||
name: 'Mike Contractor',
|
||
companyName: 'Texas Builders LLC',
|
||
licenseNumber: 'TX-GC-123456',
|
||
phone: '214-555-4567'
|
||
},
|
||
// Subcontractor
|
||
{
|
||
id: 'sub_001',
|
||
type: 'subcontractor',
|
||
username: 'carlos',
|
||
email: 'carlos@electricpro.com',
|
||
password: 'password',
|
||
role: 'SUBCONTRACTOR',
|
||
name: 'Carlos Subcontractor',
|
||
companyName: 'Electric Pro Services',
|
||
tradeType: 'electrical',
|
||
phone: '469-555-9012'
|
||
},
|
||
// Vendor (Supplier)
|
||
{
|
||
id: 'ven_001',
|
||
type: 'vendor',
|
||
username: 'abc_supply',
|
||
email: 'sales@abcsupply.com',
|
||
password: 'password',
|
||
role: 'VENDOR',
|
||
name: 'ABC Supply Co.',
|
||
companyName: 'ABC Supply',
|
||
tradeType: 'materials',
|
||
phone: '972-555-8888'
|
||
}
|
||
];
|
||
|
||
// --- NEW MOCK SALES HISTORY FOR LEADERBOARD ---
|
||
// Dates are in March 2026 (current month) so the default "This Month" filter shows data.
|
||
// A subset also falls in the current week (Mar 15-21) for the "This Week" filter.
|
||
// Amounts reflect residential storm-damage roofing jobs ($12K–$42K per deal).
|
||
const MOCK_SALES_HISTORY = [
|
||
// Frank Agent (High Revenue, Low Volume - "The Sniper") — fewer deals, bigger jobs
|
||
{ id: 'tx_1', agentId: 'e1', date: '2026-05-14', amount: 38500, status: 'closed_won' }, // this week
|
||
{ id: 'tx_2', agentId: 'e1', date: '2026-05-12', amount: 42000, status: 'closed_won' }, // this week
|
||
{ id: 'tx_3', agentId: 'e1', date: '2026-05-06', amount: 31800, status: 'closed_won' },
|
||
{ id: 'tx_4', agentId: 'e1', date: '2026-05-03', amount: 0, status: 'closed_lost' },
|
||
|
||
// Fiona Field (High Volume, Mid Revenue - "The Grinder") — more deals, consistent value
|
||
{ id: 'tx_5', agentId: 'e2', date: '2026-05-13', amount: 18200, status: 'closed_won' }, // this week
|
||
{ id: 'tx_6', agentId: 'e2', date: '2026-05-11', amount: 16800, status: 'closed_won' }, // this week
|
||
{ id: 'tx_7', agentId: 'e2', date: '2026-05-08', amount: 21400, status: 'closed_won' },
|
||
{ id: 'tx_8', agentId: 'e2', date: '2026-05-06', amount: 19600, status: 'closed_won' },
|
||
{ id: 'tx_9', agentId: 'e2', date: '2026-05-05', amount: 17300, status: 'closed_won' },
|
||
{ id: 'tx_10', agentId: 'e2', date: '2026-05-02', amount: 0, status: 'closed_lost' },
|
||
{ id: 'tx_11', agentId: 'e2', date: '2026-05-01', amount: 0, status: 'closed_lost' },
|
||
|
||
// Fred Flyer (New/Low Performance) — one close this month, struggling
|
||
{ id: 'tx_12', agentId: 'e3', date: '2026-05-13', amount: 12400, status: 'closed_won' }, // this week
|
||
{ id: 'tx_13', agentId: 'e3', date: '2026-05-07', amount: 0, status: 'closed_lost' },
|
||
{ id: 'tx_14', agentId: 'e3', date: '2026-05-04', amount: 0, status: 'closed_lost' },
|
||
|
||
// Felicity Fast (Consistent) — reliable mid-tier producer
|
||
{ id: 'tx_15', agentId: 'e4', date: '2026-05-12', amount: 24800, status: 'closed_won' }, // this week
|
||
{ id: 'tx_16', agentId: 'e4', date: '2026-05-08', amount: 23200, status: 'closed_won' },
|
||
{ id: 'tx_17', agentId: 'e4', date: '2026-05-05', amount: 26500, status: 'closed_won' },
|
||
|
||
// Felix Fixer (Closer - High Win Rate) — doesn't lose, picks his battles
|
||
{ id: 'tx_18', agentId: 'e5', date: '2026-05-14', amount: 29500, status: 'closed_won' }, // this week
|
||
{ id: 'tx_19', agentId: 'e5', date: '2026-05-08', amount: 34800, status: 'closed_won' },
|
||
];
|
||
|
||
// --- MOCK CANVASSER DOOR-KNOCK HISTORY ---
|
||
// Each record = one door-knock session / lead attempt by a canvasser.
|
||
// status: 'verified' | 'unverified' | 'converted'
|
||
// verified = lead confirmed by team lead / admin
|
||
// unverified = logged by canvasser, pending review
|
||
// converted = verified lead that became a paying client
|
||
// Dates in May 2026 — days 11-14 fall in current week, all in current month.
|
||
const MOCK_CANVASSER_HISTORY = [
|
||
// Frank Agent (e1) — solid verifier, good conversion
|
||
{ id: 'ck_001', agentId: 'e1', date: '2026-05-14', status: 'verified', address: '2612 Dunwick Dr' },
|
||
{ id: 'ck_002', agentId: 'e1', date: '2026-05-14', status: 'converted', address: '2608 Dunwick Dr' },
|
||
{ id: 'ck_003', agentId: 'e1', date: '2026-05-13', status: 'verified', address: '2604 Dunwick Dr' },
|
||
{ id: 'ck_004', agentId: 'e1', date: '2026-05-13', status: 'unverified', address: '2600 Dunwick Dr' },
|
||
{ id: 'ck_005', agentId: 'e1', date: '2026-05-12', status: 'converted', address: '2516 Dunwick Dr' },
|
||
{ id: 'ck_006', agentId: 'e1', date: '2026-05-08', status: 'verified', address: '2637 Rothland Ln' },
|
||
{ id: 'ck_007', agentId: 'e1', date: '2026-05-08', status: 'unverified', address: '2633 Rothland Ln' },
|
||
{ id: 'ck_008', agentId: 'e1', date: '2026-05-06', status: 'verified', address: '2629 Rothland Ln' },
|
||
{ id: 'ck_009', agentId: 'e1', date: '2026-05-04', status: 'verified', address: '6613 Phoenix Pl' },
|
||
{ id: 'ck_010', agentId: 'e1', date: '2026-05-02', status: 'converted', address: '6609 Phoenix Pl' },
|
||
|
||
// Fiona Field (e2) — high volume, moderate verification rate
|
||
{ id: 'ck_011', agentId: 'e2', date: '2026-05-14', status: 'verified', address: '6605 Phoenix Pl' },
|
||
{ id: 'ck_012', agentId: 'e2', date: '2026-05-14', status: 'unverified', address: '6601 Phoenix Pl' },
|
||
{ id: 'ck_013', agentId: 'e2', date: '2026-05-13', status: 'converted', address: '6617 Phoenix Pl' },
|
||
{ id: 'ck_014', agentId: 'e2', date: '2026-05-13', status: 'verified', address: '3913 Arizona Pl' },
|
||
{ id: 'ck_015', agentId: 'e2', date: '2026-05-11', status: 'unverified', address: '3917 Arizona Pl' },
|
||
{ id: 'ck_016', agentId: 'e2', date: '2026-05-08', status: 'verified', address: '3921 Arizona Pl' },
|
||
{ id: 'ck_017', agentId: 'e2', date: '2026-05-08', status: 'unverified', address: '3920 Arizona Pl' },
|
||
{ id: 'ck_018', agentId: 'e2', date: '2026-05-06', status: 'converted', address: '3916 Arizona Pl' },
|
||
{ id: 'ck_019', agentId: 'e2', date: '2026-05-05', status: 'verified', address: '3912 Arizona Pl' },
|
||
{ id: 'ck_020', agentId: 'e2', date: '2026-05-04', status: 'unverified', address: '3908 Arizona Pl' },
|
||
{ id: 'ck_021', agentId: 'e2', date: '2026-05-03', status: 'verified', address: '3904 Arizona Pl' },
|
||
{ id: 'ck_022', agentId: 'e2', date: '2026-05-01', status: 'unverified', address: '3900 Arizona Pl' },
|
||
|
||
// Fred Flyer (e3) — low volume, mostly unverified (new canvasser)
|
||
{ id: 'ck_023', agentId: 'e3', date: '2026-05-14', status: 'unverified', address: '3917 Sailmaker Ln' },
|
||
{ id: 'ck_024', agentId: 'e3', date: '2026-05-12', status: 'verified', address: '3913 Sailmaker Ln' },
|
||
{ id: 'ck_025', agentId: 'e3', date: '2026-05-07', status: 'unverified', address: '3909 Sailmaker Ln' },
|
||
{ id: 'ck_026', agentId: 'e3', date: '2026-05-05', status: 'unverified', address: '3905 Sailmaker Ln' },
|
||
{ id: 'ck_027', agentId: 'e3', date: '2026-05-03', status: 'converted', address: '3901 Sailmaker Ln' },
|
||
|
||
// Felicity Fast (e4) — top performer, best verified count + conversion
|
||
{ id: 'ck_028', agentId: 'e4', date: '2026-05-14', status: 'converted', address: '2612 Dunwick Dr' },
|
||
{ id: 'ck_029', agentId: 'e4', date: '2026-05-14', status: 'verified', address: '2608 Dunwick Dr' },
|
||
{ id: 'ck_030', agentId: 'e4', date: '2026-05-13', status: 'converted', address: '2604 Dunwick Dr' },
|
||
{ id: 'ck_031', agentId: 'e4', date: '2026-05-12', status: 'verified', address: '2600 Dunwick Dr' },
|
||
{ id: 'ck_032', agentId: 'e4', date: '2026-05-11', status: 'verified', address: '6613 Phoenix Pl' },
|
||
{ id: 'ck_033', agentId: 'e4', date: '2026-05-08', status: 'converted', address: '6609 Phoenix Pl' },
|
||
{ id: 'ck_034', agentId: 'e4', date: '2026-05-07', status: 'verified', address: '6605 Phoenix Pl' },
|
||
{ id: 'ck_035', agentId: 'e4', date: '2026-05-06', status: 'unverified', address: '6601 Phoenix Pl' },
|
||
{ id: 'ck_036', agentId: 'e4', date: '2026-05-05', status: 'converted', address: '6617 Phoenix Pl' },
|
||
{ id: 'ck_037', agentId: 'e4', date: '2026-05-04', status: 'verified', address: '3913 Arizona Pl' },
|
||
{ id: 'ck_038', agentId: 'e4', date: '2026-05-03', status: 'verified', address: '3917 Arizona Pl' },
|
||
{ id: 'ck_039', agentId: 'e4', date: '2026-05-02', status: 'unverified', address: '3921 Arizona Pl' },
|
||
{ id: 'ck_040', agentId: 'e4', date: '2026-05-01', status: 'converted', address: '3920 Arizona Pl' },
|
||
{ id: 'ck_041', agentId: 'e4', date: '2026-05-01', status: 'verified', address: '3916 Arizona Pl' },
|
||
|
||
// Felix Fixer (e5) — selective, fewer knocks, decent conversion rate
|
||
{ id: 'ck_042', agentId: 'e5', date: '2026-05-14', status: 'converted', address: '3912 Arizona Pl' },
|
||
{ id: 'ck_043', agentId: 'e5', date: '2026-05-13', status: 'verified', address: '3908 Arizona Pl' },
|
||
{ id: 'ck_044', agentId: 'e5', date: '2026-05-11', status: 'unverified', address: '3904 Arizona Pl' },
|
||
{ id: 'ck_045', agentId: 'e5', date: '2026-05-07', status: 'converted', address: '3900 Arizona Pl' },
|
||
{ id: 'ck_046', agentId: 'e5', date: '2026-05-05', status: 'verified', address: '3917 Sailmaker Ln' },
|
||
{ id: 'ck_047', agentId: 'e5', date: '2026-05-03', status: 'unverified', address: '3913 Sailmaker Ln' },
|
||
{ id: 'ck_048', agentId: 'e5', date: '2026-05-01', status: 'verified', address: '3909 Sailmaker Ln' },
|
||
];
|
||
|
||
// Helper to generate meetings
|
||
function generateMockMeetings() {
|
||
const agents = ['e1', 'e2', 'e3', 'e4', 'e5'];
|
||
const statuses = ['Scheduled', 'Rescheduled', 'Completed', 'Converted', 'Cancelled'];
|
||
const meetings = [];
|
||
let meetingIdCounter = 1;
|
||
|
||
agents.forEach(agentId => {
|
||
// Generate ~7 meetings per agent
|
||
for (let i = 0; i < 7; i++) {
|
||
const isPast = i < 4; // First 4 are past/completed/converted
|
||
const status = isPast
|
||
? (Math.random() > 0.3 ? 'Completed' : (Math.random() > 0.5 ? 'Converted' : 'Cancelled')) // Past mix
|
||
: (Math.random() > 0.3 ? 'Scheduled' : 'Rescheduled'); // Future mix
|
||
|
||
// Date Logic
|
||
const date = new Date();
|
||
date.setDate(date.getDate() + (isPast ? -1 * (i + 1) * 2 : (i + 1) * 2)); // Spread out days
|
||
|
||
// Deal Value (only for relevant statuses)
|
||
let dealValue = null;
|
||
if (['Completed', 'Converted'].includes(status)) {
|
||
dealValue = Math.floor(Math.random() * (50000 - 5000) + 5000); // 5k to 50k
|
||
}
|
||
|
||
// Time Logic (09:00 to 16:00)
|
||
const hour = 9 + (i % 8);
|
||
const timeString = `${hour.toString().padStart(2, '0')}:00`;
|
||
|
||
// New Data Fields for Chatbot Context
|
||
const issues = [
|
||
"Leaking skylight in master bath",
|
||
"Missing shingles after recent storm",
|
||
"Water spots on ceiling",
|
||
"Gutter detachment on north side",
|
||
"General roof inspection requested"
|
||
];
|
||
const comments = [
|
||
"Customer is worried about upcoming rain.",
|
||
"Insurance adjuster already visited.",
|
||
"Prefer morning appointments.",
|
||
"Dog in backyard, please knock front door.",
|
||
"Previous contractor did poor work."
|
||
];
|
||
|
||
meetings.push({
|
||
id: `m${meetingIdCounter++}`,
|
||
agentId: agentId,
|
||
customerName: REAL_NAMES[(meetingIdCounter * 3) % REAL_NAMES.length], // Pick random names
|
||
propertyId: `P-${2600 + (meetingIdCounter * 2)}`,
|
||
date: date.toISOString().split('T')[0],
|
||
time: timeString,
|
||
status: status,
|
||
dealValue: dealValue,
|
||
notes: status === 'Converted' ? 'Contract signed for full roof replacement.' :
|
||
status === 'Rescheduled' ? 'Client verified availability.' : 'Standard consultation.',
|
||
// New Fields
|
||
issueDescription: issues[i % issues.length],
|
||
customerComments: comments[i % comments.length],
|
||
outcome: status === 'Converted' ? 'Signed Contract' : status === 'Completed' ? 'Quote Sent' : 'Pending'
|
||
});
|
||
}
|
||
});
|
||
|
||
// Generate Alice's Demo Meetings
|
||
const aliceMeetings = [
|
||
// Upcoming
|
||
{
|
||
id: `m-alice-1`,
|
||
agentId: 'e1',
|
||
customerName: 'Alice Customer',
|
||
customerId: 'c1', // Explicit ID link
|
||
propertyId: 'P-Alice-1',
|
||
date: new Date(Date.now() + 86400000 * 2).toISOString().split('T')[0], // 2 days from now
|
||
time: '14:00',
|
||
status: 'Scheduled',
|
||
agentName: 'Frank Agent',
|
||
notes: 'Initial Roof Inspection',
|
||
issueDescription: 'Noticed missing shingles after last storm.',
|
||
customerComments: 'Gate code is 1234.',
|
||
outcome: 'Pending'
|
||
},
|
||
// History
|
||
{
|
||
id: `m-alice-2`,
|
||
agentId: 'e2',
|
||
customerName: 'Alice Customer',
|
||
customerId: 'c1',
|
||
propertyId: 'P-Alice-1',
|
||
date: new Date(Date.now() - 86400000 * 15).toISOString().split('T')[0], // 15 days ago
|
||
time: '10:00',
|
||
status: 'Completed',
|
||
agentName: 'Fiona Field',
|
||
dealValue: 1500,
|
||
notes: 'Minor repairs completed. Payment received.',
|
||
issueDescription: 'Leak in garage.',
|
||
customerComments: 'Please call before arriving.',
|
||
outcome: 'Repairs Completed'
|
||
},
|
||
{
|
||
id: `m-alice-3`,
|
||
agentId: 'e1',
|
||
customerName: 'Alice Customer',
|
||
customerId: 'c1',
|
||
propertyId: 'P-Alice-1',
|
||
date: new Date(Date.now() - 86400000 * 45).toISOString().split('T')[0], // 45 days ago
|
||
time: '11:00',
|
||
status: 'Completed',
|
||
agentName: 'Frank Agent',
|
||
dealValue: 0,
|
||
notes: 'Consultation - Quote provided.',
|
||
issueDescription: 'General inspection.',
|
||
customerComments: '',
|
||
outcome: 'Quote Sent'
|
||
}
|
||
];
|
||
|
||
meetings.push(...aliceMeetings);
|
||
|
||
return meetings;
|
||
}
|
||
|
||
const MOCK_MEETINGS = generateMockMeetings();
|
||
|
||
// --- OWNER PROFILES ---
|
||
|
||
const MOCK_OWNERS = [
|
||
{
|
||
id: 'own_001',
|
||
companyName: 'Johnson Development Group',
|
||
primaryContact: 'Justin Johnson',
|
||
email: 'justin@johnsondev.com',
|
||
phone: '972-555-1234',
|
||
companyType: 'Real Estate Developer',
|
||
portfolioSize: 6,
|
||
activeProjects: 4,
|
||
completedProjects: 1,
|
||
onHoldProjects: 1,
|
||
totalInvestmentValue: 187000,
|
||
riskScore: 28,
|
||
status: 'Active',
|
||
location: { city: 'Plano', state: 'TX' },
|
||
memberSince: '2023-06-15',
|
||
lastActivityDate: '2026-02-15',
|
||
dashboardSummary: {
|
||
totalBudget: 187000,
|
||
committedCost: 156200,
|
||
actualSpent: 131500,
|
||
roiProjection: 12.4,
|
||
pendingApprovals: 3,
|
||
openChangeOrders: 2,
|
||
openRFIs: 1,
|
||
disputes: 1,
|
||
upcomingMilestones: [
|
||
{ name: 'Shingle Install Complete', project: '2604 Dunwick Dr', dueDate: '2026-02-14', projectId: 'proj_001' },
|
||
{ name: 'Electrical Hookup', project: '6613 Phoenix Pl', dueDate: '2026-02-14', projectId: 'proj_003' },
|
||
{ name: 'Flooring and Trim', project: '2612 Dunwick Dr', dueDate: '2026-02-18', projectId: 'proj_004' },
|
||
{ name: 'Panel Replacement', project: '3913 Arizona Pl', dueDate: '2026-02-20', projectId: 'proj_006' }
|
||
],
|
||
cashFlowForecast: [
|
||
{ month: 'Mar 2026', inflow: 45000, outflow: 38000 },
|
||
{ month: 'Apr 2026', inflow: 32000, outflow: 28000 },
|
||
{ month: 'May 2026', inflow: 18000, outflow: 15000 },
|
||
{ month: 'Jun 2026', inflow: 12000, outflow: 8000 },
|
||
{ month: 'Jul 2026', inflow: 8000, outflow: 5000 },
|
||
{ month: 'Aug 2026', inflow: 5000, outflow: 3000 }
|
||
],
|
||
monthlySpend: [
|
||
{ month: 'Mar 2025', amount: 0 },
|
||
{ month: 'Apr 2025', amount: 0 },
|
||
{ month: 'May 2025', amount: 0 },
|
||
{ month: 'Jun 2025', amount: 0 },
|
||
{ month: 'Jul 2025', amount: 0 },
|
||
{ month: 'Aug 2025', amount: 0 },
|
||
{ month: 'Sep 2025', amount: 0 },
|
||
{ month: 'Oct 2025', amount: 0 },
|
||
{ month: 'Nov 2025', amount: 8500 },
|
||
{ month: 'Dec 2025', amount: 33200 },
|
||
{ month: 'Jan 2026', amount: 52800 },
|
||
{ month: 'Feb 2026', amount: 37000 }
|
||
]
|
||
}
|
||
},
|
||
{
|
||
id: 'own_002',
|
||
companyName: 'Reeves Construction Partners',
|
||
primaryContact: 'Diana Reeves',
|
||
email: 'diana@reevesconstruction.com',
|
||
phone: '214-555-5678',
|
||
companyType: 'Institutional',
|
||
portfolioSize: 6,
|
||
activeProjects: 3,
|
||
completedProjects: 1,
|
||
onHoldProjects: 2,
|
||
totalInvestmentValue: 430000,
|
||
riskScore: 72,
|
||
status: 'High Risk',
|
||
location: { city: 'Plano', state: 'TX' },
|
||
memberSince: '2024-01-10',
|
||
lastActivityDate: '2026-02-12',
|
||
dashboardSummary: {
|
||
totalBudget: 430000,
|
||
committedCost: 385000,
|
||
actualSpent: 291300,
|
||
roiProjection: 4.8,
|
||
pendingApprovals: 5,
|
||
openChangeOrders: 5,
|
||
openRFIs: 3,
|
||
disputes: 2,
|
||
upcomingMilestones: [
|
||
{ name: 'Section A - TPO Install', project: '6909 Custer Rd', dueDate: '2026-02-28', projectId: 'proj_007' },
|
||
{ name: 'Final Grade and Drainage', project: '2112 Winslow Dr', dueDate: '2026-02-10', projectId: 'proj_008' },
|
||
{ name: 'Floor 2 Installation', project: '7224 Independence Pkwy', dueDate: '2026-02-14', projectId: 'proj_009' },
|
||
{ name: 'Waterproofing Application', project: 'Legacy Town Center', dueDate: '2026-02-12', projectId: 'proj_011' }
|
||
],
|
||
cashFlowForecast: [
|
||
{ month: 'Mar 2026', inflow: 85000, outflow: 92000 },
|
||
{ month: 'Apr 2026', inflow: 72000, outflow: 68000 },
|
||
{ month: 'May 2026', inflow: 55000, outflow: 45000 },
|
||
{ month: 'Jun 2026', inflow: 40000, outflow: 32000 },
|
||
{ month: 'Jul 2026', inflow: 25000, outflow: 18000 },
|
||
{ month: 'Aug 2026', inflow: 15000, outflow: 10000 }
|
||
],
|
||
monthlySpend: [
|
||
{ month: 'Mar 2025', amount: 0 },
|
||
{ month: 'Apr 2025', amount: 0 },
|
||
{ month: 'May 2025', amount: 0 },
|
||
{ month: 'Jun 2025', amount: 0 },
|
||
{ month: 'Jul 2025', amount: 0 },
|
||
{ month: 'Aug 2025', amount: 0 },
|
||
{ month: 'Sep 2025', amount: 14200 },
|
||
{ month: 'Oct 2025', amount: 35000 },
|
||
{ month: 'Nov 2025', amount: 52000 },
|
||
{ month: 'Dec 2025', amount: 68400 },
|
||
{ month: 'Jan 2026', amount: 74200 },
|
||
{ month: 'Feb 2026', amount: 47500 }
|
||
]
|
||
}
|
||
}
|
||
];
|
||
|
||
// --- NEW OWNERS BOX DATA SCHEMAS ---
|
||
|
||
const MOCK_PERSONNEL = [
|
||
{
|
||
id: 'p1',
|
||
name: 'Jesus Gonzales',
|
||
role: 'W2_EMPLOYEE',
|
||
email: 'jesus@lynkeduppro.com',
|
||
phone: '214-555-0101',
|
||
team: 'Sales Team A',
|
||
manager: 'Justin Johnson',
|
||
sensitiveData: {
|
||
ssn: '123-45-6789',
|
||
bankAccount: '9876543210'
|
||
},
|
||
compliance: {
|
||
w9Signed: true,
|
||
payPlanSigned: true,
|
||
driverLicense: {
|
||
uploaded: true,
|
||
expirationDate: '2026-08-15',
|
||
status: 'compliant'
|
||
}
|
||
},
|
||
status: 'active'
|
||
},
|
||
{
|
||
id: 'p2',
|
||
name: 'Sarah Sales',
|
||
role: '1099_SALES_REP',
|
||
email: 'sarah@lynkeduppro.com',
|
||
phone: '214-555-0102',
|
||
team: 'Sales Team A',
|
||
manager: 'Jesus Gonzales',
|
||
sensitiveData: {
|
||
ssn: '987-65-4321',
|
||
bankAccount: '1234567890'
|
||
},
|
||
compliance: {
|
||
w9Signed: true,
|
||
payPlanSigned: false,
|
||
driverLicense: {
|
||
uploaded: true,
|
||
expirationDate: '2025-12-01',
|
||
status: 'expiring'
|
||
}
|
||
},
|
||
status: 'active'
|
||
}
|
||
];
|
||
|
||
const MOCK_VENDORS = [
|
||
{
|
||
id: 'v1',
|
||
vendorName: 'Texas Roofing Crew A',
|
||
vendorType: 'roofing_crew',
|
||
primaryContact: {
|
||
name: 'Juan Perez',
|
||
email: 'juan@texasroofing.com',
|
||
phone: '469-555-0201'
|
||
},
|
||
sensitiveData: {
|
||
ein: '12-3456789',
|
||
bankAccount: '1122334455'
|
||
},
|
||
compliance: {
|
||
w9: { uploaded: true, status: 'approved' },
|
||
subcontractorAgreement: { uploaded: true, status: 'approved' },
|
||
coi: {
|
||
uploaded: true,
|
||
expirationDate: '2026-06-30',
|
||
status: 'compliant'
|
||
}
|
||
},
|
||
spend: {
|
||
totalSpend: 218000,
|
||
lastPayment: { date: '2026-02-08', amount: 18500 },
|
||
ytdSpend: 67000,
|
||
pendingInvoices: 4200
|
||
},
|
||
performance: {
|
||
rating: 4.8,
|
||
onTimeRate: 0.95,
|
||
jobsCompleted: 42,
|
||
activeJobs: 3
|
||
},
|
||
status: 'active'
|
||
},
|
||
{
|
||
id: 'v2',
|
||
vendorName: 'Sparky Electric',
|
||
vendorType: 'electrical',
|
||
primaryContact: {
|
||
name: 'Mike Spark',
|
||
email: 'mike@sparky.com',
|
||
phone: '469-555-0202'
|
||
},
|
||
sensitiveData: {
|
||
ein: '98-7654321',
|
||
bankAccount: '5544332211'
|
||
},
|
||
compliance: {
|
||
w9: { uploaded: true, status: 'approved' },
|
||
subcontractorAgreement: { uploaded: false, status: 'pending' },
|
||
coi: {
|
||
uploaded: true,
|
||
expirationDate: '2026-01-15',
|
||
status: 'expired'
|
||
}
|
||
},
|
||
spend: {
|
||
totalSpend: 9200,
|
||
lastPayment: { date: '2025-12-15', amount: 1800 },
|
||
ytdSpend: 0,
|
||
pendingInvoices: 3400
|
||
},
|
||
performance: {
|
||
rating: 4.2,
|
||
onTimeRate: 0.88,
|
||
jobsCompleted: 8,
|
||
activeJobs: 1
|
||
},
|
||
status: 'non_compliant'
|
||
},
|
||
{
|
||
id: 'v3',
|
||
vendorName: 'ABC Supply Co.',
|
||
vendorType: 'material_supplier',
|
||
primaryContact: {
|
||
name: 'Sales Desk',
|
||
email: 'sales@abcsupply.com',
|
||
phone: '972-555-8888'
|
||
},
|
||
sensitiveData: {
|
||
ein: '55-4433221',
|
||
bankAccount: '9988776655'
|
||
},
|
||
compliance: {
|
||
w9: { uploaded: true, status: 'approved' },
|
||
subcontractorAgreement: { uploaded: true, status: 'approved' },
|
||
coi: { uploaded: true, status: 'compliant', expirationDate: '2027-01-01' }
|
||
},
|
||
spend: {
|
||
totalSpend: 312000,
|
||
lastPayment: { date: '2026-02-12', amount: 22400 },
|
||
ytdSpend: 98000,
|
||
pendingInvoices: 15600
|
||
},
|
||
performance: {
|
||
rating: 5.0,
|
||
onTimeRate: 0.99,
|
||
jobsCompleted: 124,
|
||
activeJobs: 7
|
||
},
|
||
status: 'active'
|
||
},
|
||
{
|
||
id: 'v4',
|
||
vendorName: 'Lone Star Plumbing',
|
||
vendorType: 'plumbing',
|
||
primaryContact: {
|
||
name: 'Ray Gutierrez',
|
||
email: 'ray@lonestarplumbing.com',
|
||
phone: '469-555-0304'
|
||
},
|
||
sensitiveData: {
|
||
ein: '33-2211009',
|
||
bankAccount: '4455667788'
|
||
},
|
||
compliance: {
|
||
w9: { uploaded: true, status: 'approved' },
|
||
subcontractorAgreement: { uploaded: true, status: 'approved' },
|
||
coi: {
|
||
uploaded: true,
|
||
expirationDate: '2026-03-05',
|
||
status: 'compliant'
|
||
}
|
||
},
|
||
spend: {
|
||
totalSpend: 86000,
|
||
lastPayment: { date: '2026-01-20', amount: 7200 },
|
||
ytdSpend: 14500,
|
||
pendingInvoices: 8900
|
||
},
|
||
performance: {
|
||
rating: 4.5,
|
||
onTimeRate: 0.91,
|
||
jobsCompleted: 19,
|
||
activeJobs: 2
|
||
},
|
||
status: 'active'
|
||
},
|
||
{
|
||
id: 'v5',
|
||
vendorName: 'DFW Foundation Pros',
|
||
vendorType: 'foundation',
|
||
primaryContact: {
|
||
name: 'Tom Atkins',
|
||
email: 'tom@dfwfoundation.com',
|
||
phone: '214-555-0405'
|
||
},
|
||
sensitiveData: {
|
||
ein: '44-5566778',
|
||
bankAccount: '7788991100'
|
||
},
|
||
compliance: {
|
||
w9: { uploaded: false, status: 'pending' },
|
||
subcontractorAgreement: { uploaded: true, status: 'approved' },
|
||
coi: {
|
||
uploaded: true,
|
||
expirationDate: '2026-09-15',
|
||
status: 'compliant'
|
||
}
|
||
},
|
||
spend: {
|
||
totalSpend: 142000,
|
||
lastPayment: { date: '2026-01-10', amount: 24000 },
|
||
ytdSpend: 24000,
|
||
pendingInvoices: 18500
|
||
},
|
||
performance: {
|
||
rating: 3.9,
|
||
onTimeRate: 0.82,
|
||
jobsCompleted: 11,
|
||
activeJobs: 2
|
||
},
|
||
status: 'non_compliant'
|
||
},
|
||
{
|
||
id: 'v6',
|
||
vendorName: 'North Texas HVAC Solutions',
|
||
vendorType: 'hvac',
|
||
primaryContact: {
|
||
name: 'Linda Foster',
|
||
email: 'linda@ntxhvac.com',
|
||
phone: '972-555-0506'
|
||
},
|
||
sensitiveData: {
|
||
ein: '66-7788990',
|
||
bankAccount: '2233445566'
|
||
},
|
||
compliance: {
|
||
w9: { uploaded: true, status: 'approved' },
|
||
subcontractorAgreement: { uploaded: true, status: 'approved' },
|
||
coi: {
|
||
uploaded: true,
|
||
expirationDate: '2026-11-20',
|
||
status: 'compliant'
|
||
}
|
||
},
|
||
spend: {
|
||
totalSpend: 178000,
|
||
lastPayment: { date: '2026-02-05', amount: 9800 },
|
||
ytdSpend: 32000,
|
||
pendingInvoices: 6700
|
||
},
|
||
performance: {
|
||
rating: 4.6,
|
||
onTimeRate: 0.93,
|
||
jobsCompleted: 27,
|
||
activeJobs: 3
|
||
},
|
||
status: 'active'
|
||
}
|
||
];
|
||
|
||
const MOCK_ORDERS = [
|
||
{
|
||
id: 'ORD-001',
|
||
vendorId: 'v3',
|
||
projectId: 'proj_001',
|
||
projectAddress: '2604 Dunwick Dr, Plano, TX 75023',
|
||
orderDate: '2026-01-18',
|
||
dueDate: '2026-01-28',
|
||
deliveryDate: '2026-01-27',
|
||
status: 'delivered',
|
||
items: [
|
||
{
|
||
id: 'item_001',
|
||
description: 'Roofing Shingles - Architectural',
|
||
quantity: 50,
|
||
unit: 'bundles',
|
||
unitPrice: 45.00,
|
||
total: 2250.00,
|
||
specifications: 'GAF Timberline HDZ, Charcoal'
|
||
},
|
||
{
|
||
id: 'item_002',
|
||
description: 'Roofing Underlayment',
|
||
quantity: 10,
|
||
unit: 'rolls',
|
||
unitPrice: 85.00,
|
||
total: 850.00,
|
||
specifications: 'Synthetic, 10 sq per roll'
|
||
}
|
||
],
|
||
subtotal: 3100.00,
|
||
tax: 255.75,
|
||
total: 3355.75,
|
||
deliveryAddress: {
|
||
street: '2604 Dunwick Dr',
|
||
city: 'Plano',
|
||
state: 'TX',
|
||
zip: '75023',
|
||
instructions: 'Deliver to side yard, call foreman on arrival'
|
||
},
|
||
timeline: [
|
||
{ date: '2026-01-18', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2026-01-19', event: 'Order Confirmed', status: 'completed' },
|
||
{ date: '2026-01-24', event: 'Shipped', status: 'completed' },
|
||
{ date: '2026-01-27', event: 'Delivered', status: 'completed' }
|
||
],
|
||
documents: [
|
||
{ id: 'odoc_001', type: 'PO', name: 'PO-2604-Dunwick.pdf', url: '#' },
|
||
{ id: 'odoc_002', type: 'Invoice', name: 'INV-ORD001.pdf', url: '#' }
|
||
],
|
||
// TODO: cross-reference — Vendor: ABC Supply Co., Project Contractor: Texas Builders LLC
|
||
notes: 'Rush order for weather window',
|
||
contactPerson: 'Juan Perez',
|
||
contactPhone: '469-555-0201'
|
||
},
|
||
{
|
||
id: 'ORD-002',
|
||
vendorId: 'v3',
|
||
projectId: 'proj_002',
|
||
projectAddress: '2617 Rothland Ln, Plano, TX 75023',
|
||
orderDate: '2025-11-10',
|
||
dueDate: '2025-11-20',
|
||
deliveryDate: '2025-11-19',
|
||
status: 'delivered',
|
||
items: [
|
||
{
|
||
id: 'item_003',
|
||
description: 'Gutter System - Seamless Aluminum',
|
||
quantity: 150,
|
||
unit: 'linear feet',
|
||
unitPrice: 12.50,
|
||
total: 1875.00,
|
||
specifications: 'White, 5-inch K-style'
|
||
},
|
||
{
|
||
id: 'item_004',
|
||
description: 'Downspout Extensions',
|
||
quantity: 8,
|
||
unit: 'pieces',
|
||
unitPrice: 28.00,
|
||
total: 224.00,
|
||
specifications: 'Aluminum, matching white'
|
||
}
|
||
],
|
||
subtotal: 2099.00,
|
||
tax: 173.17,
|
||
total: 2272.17,
|
||
deliveryAddress: {
|
||
street: '2617 Rothland Ln',
|
||
city: 'Plano',
|
||
state: 'TX',
|
||
zip: '75023',
|
||
instructions: 'Leave at garage side'
|
||
},
|
||
timeline: [
|
||
{ date: '2025-11-10', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2025-11-11', event: 'Order Confirmed', status: 'completed' },
|
||
{ date: '2025-11-17', event: 'Shipped', status: 'completed' },
|
||
{ date: '2025-11-19', event: 'Delivered', status: 'completed' }
|
||
],
|
||
documents: [
|
||
{ id: 'odoc_003', type: 'PO', name: 'PO-2617-Rothland.pdf', url: '#' },
|
||
{ id: 'odoc_004', type: 'Delivery Receipt', name: 'DR-ORD002.pdf', url: '#' }
|
||
],
|
||
// TODO: cross-reference — Vendor: ABC Supply Co., Project Contractor: Texas Builders LLC
|
||
notes: 'Completed project delivery - all materials accounted for',
|
||
contactPerson: 'Sales Desk',
|
||
contactPhone: '972-555-8888'
|
||
},
|
||
{
|
||
id: 'ORD-003',
|
||
vendorId: 'v6',
|
||
projectId: 'proj_003',
|
||
projectAddress: '6613 Phoenix Pl, Plano, TX 75023',
|
||
orderDate: '2026-01-25',
|
||
dueDate: '2026-02-08',
|
||
deliveryDate: null,
|
||
status: 'shipped',
|
||
items: [
|
||
{
|
||
id: 'item_005',
|
||
description: 'Commercial HVAC Unit - 5 Ton',
|
||
quantity: 2,
|
||
unit: 'units',
|
||
unitPrice: 4200.00,
|
||
total: 8400.00,
|
||
specifications: 'Carrier 14 SEER2, R-410A'
|
||
},
|
||
{
|
||
id: 'item_006',
|
||
description: 'Ductwork - Flex',
|
||
quantity: 200,
|
||
unit: 'linear feet',
|
||
unitPrice: 6.50,
|
||
total: 1300.00,
|
||
specifications: 'R-8 insulated, 8-inch diameter'
|
||
}
|
||
],
|
||
subtotal: 9700.00,
|
||
tax: 800.25,
|
||
total: 10500.25,
|
||
deliveryAddress: {
|
||
street: '6613 Phoenix Pl',
|
||
city: 'Plano',
|
||
state: 'TX',
|
||
zip: '75023',
|
||
instructions: 'Equipment pad on south side of building'
|
||
},
|
||
timeline: [
|
||
{ date: '2026-01-25', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2026-01-27', event: 'Order Confirmed', status: 'completed' },
|
||
{ date: '2026-02-03', event: 'Shipped', status: 'completed' },
|
||
{ date: '2026-02-08', event: 'Estimated Delivery', status: 'pending' }
|
||
],
|
||
documents: [
|
||
{ id: 'odoc_005', type: 'PO', name: 'PO-6613-Phoenix-HVAC.pdf', url: '#' }
|
||
],
|
||
// TODO: cross-reference — Vendor: North Texas HVAC Solutions, Project Contractor: Texas Builders LLC
|
||
notes: 'HVAC units backordered 5 days, pushed delivery date',
|
||
contactPerson: 'Linda Foster',
|
||
contactPhone: '972-555-0506'
|
||
},
|
||
{
|
||
id: 'ORD-004',
|
||
vendorId: 'v5',
|
||
projectId: 'proj_008',
|
||
projectAddress: '2112 Winslow Dr, Plano, TX 75023',
|
||
orderDate: '2025-12-02',
|
||
dueDate: '2025-12-15',
|
||
deliveryDate: '2025-12-14',
|
||
status: 'delivered',
|
||
items: [
|
||
{
|
||
id: 'item_007',
|
||
description: 'Steel Piers - Foundation',
|
||
quantity: 18,
|
||
unit: 'piers',
|
||
unitPrice: 420.00,
|
||
total: 7560.00,
|
||
specifications: 'Galvanized steel, 12-foot depth rated'
|
||
},
|
||
{
|
||
id: 'item_008',
|
||
description: 'Hydraulic Jacks - Rental',
|
||
quantity: 6,
|
||
unit: 'units',
|
||
unitPrice: 180.00,
|
||
total: 1080.00,
|
||
specifications: '20-ton capacity, 30-day rental'
|
||
}
|
||
],
|
||
subtotal: 8640.00,
|
||
tax: 712.80,
|
||
total: 9352.80,
|
||
deliveryAddress: {
|
||
street: '2112 Winslow Dr',
|
||
city: 'Plano',
|
||
state: 'TX',
|
||
zip: '75023',
|
||
instructions: 'Loading dock access required, heavy equipment'
|
||
},
|
||
timeline: [
|
||
{ date: '2025-12-02', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2025-12-03', event: 'Order Confirmed', status: 'completed' },
|
||
{ date: '2025-12-10', event: 'Shipped', status: 'completed' },
|
||
{ date: '2025-12-14', event: 'Delivered', status: 'completed' }
|
||
],
|
||
documents: [
|
||
{ id: 'odoc_006', type: 'PO', name: 'PO-Winslow-Foundation.pdf', url: '#' },
|
||
{ id: 'odoc_007', type: 'Invoice', name: 'INV-ORD004.pdf', url: '#' },
|
||
{ id: 'odoc_008', type: 'Delivery Receipt', name: 'DR-ORD004.pdf', url: '#' }
|
||
],
|
||
// TODO: cross-reference — Vendor: DFW Foundation Pros, Project Contractor: Texas Builders LLC
|
||
notes: 'Foundation repair materials - additional piers ordered due to scope expansion',
|
||
contactPerson: 'Tom Atkins',
|
||
contactPhone: '214-555-0405'
|
||
},
|
||
{
|
||
id: 'ORD-005',
|
||
vendorId: 'v3',
|
||
projectId: 'proj_007',
|
||
projectAddress: '6909 Custer Rd, Plano, TX 75023',
|
||
orderDate: '2026-02-01',
|
||
dueDate: '2026-02-14',
|
||
deliveryDate: null,
|
||
status: 'confirmed',
|
||
items: [
|
||
{
|
||
id: 'item_009',
|
||
description: 'Commercial TPO Membrane',
|
||
quantity: 120,
|
||
unit: 'rolls',
|
||
unitPrice: 95.00,
|
||
total: 11400.00,
|
||
specifications: '60 mil, white, 10-ft wide'
|
||
},
|
||
{
|
||
id: 'item_010',
|
||
description: 'Roof Insulation Boards',
|
||
quantity: 300,
|
||
unit: 'sheets',
|
||
unitPrice: 18.50,
|
||
total: 5550.00,
|
||
specifications: 'Polyiso, 2-inch, 4x8'
|
||
},
|
||
{
|
||
id: 'item_011',
|
||
description: 'Mechanical Fasteners',
|
||
quantity: 40,
|
||
unit: 'boxes',
|
||
unitPrice: 52.00,
|
||
total: 2080.00,
|
||
specifications: 'FM-approved, #14 x 3-inch'
|
||
}
|
||
],
|
||
subtotal: 19030.00,
|
||
tax: 1569.98,
|
||
total: 20599.98,
|
||
deliveryAddress: {
|
||
street: '6909 Custer Rd',
|
||
city: 'Plano',
|
||
state: 'TX',
|
||
zip: '75023',
|
||
instructions: 'Crane lift to rooftop, coordinate 48hrs in advance'
|
||
},
|
||
timeline: [
|
||
{ date: '2026-02-01', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2026-02-03', event: 'Order Confirmed', status: 'completed' },
|
||
{ date: '2026-02-10', event: 'Estimated Ship Date', status: 'pending' },
|
||
{ date: '2026-02-14', event: 'Estimated Delivery', status: 'pending' }
|
||
],
|
||
documents: [
|
||
{ id: 'odoc_009', type: 'PO', name: 'PO-Custer-Commercial-Roof.pdf', url: '#' }
|
||
],
|
||
// TODO: cross-reference — Vendor: ABC Supply Co., Project Contractor: Texas Builders LLC
|
||
notes: 'Large commercial order - confirm crane availability before ship',
|
||
contactPerson: 'Sales Desk',
|
||
contactPhone: '972-555-8888'
|
||
},
|
||
{
|
||
id: 'ORD-006',
|
||
vendorId: 'v4',
|
||
projectId: 'proj_012',
|
||
projectAddress: 'Dallas/Plano Marriott at Legacy Town Center, 7121 Bishop Rd, Plano, TX 75024',
|
||
orderDate: '2026-01-15',
|
||
dueDate: '2026-01-30',
|
||
deliveryDate: '2026-01-29',
|
||
status: 'delivered',
|
||
items: [
|
||
{
|
||
id: 'item_012',
|
||
description: 'Commercial Grade PVC Pipe - 4 inch',
|
||
quantity: 400,
|
||
unit: 'linear feet',
|
||
unitPrice: 8.75,
|
||
total: 3500.00,
|
||
specifications: 'Schedule 40, SDR-26'
|
||
},
|
||
{
|
||
id: 'item_013',
|
||
description: 'Ball Valves - Brass',
|
||
quantity: 24,
|
||
unit: 'pieces',
|
||
unitPrice: 42.00,
|
||
total: 1008.00,
|
||
specifications: '2-inch, full port, lead-free'
|
||
},
|
||
{
|
||
id: 'item_014',
|
||
description: 'Water Heater - Commercial Tankless',
|
||
quantity: 3,
|
||
unit: 'units',
|
||
unitPrice: 2800.00,
|
||
total: 8400.00,
|
||
specifications: 'Rinnai CU199iN, 199K BTU'
|
||
}
|
||
],
|
||
subtotal: 12908.00,
|
||
tax: 1064.91,
|
||
total: 13972.91,
|
||
deliveryAddress: {
|
||
street: '7121 Bishop Rd',
|
||
city: 'Plano',
|
||
state: 'TX',
|
||
zip: '75024',
|
||
instructions: 'Service entrance, coordinate with building management'
|
||
},
|
||
timeline: [
|
||
{ date: '2026-01-15', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2026-01-16', event: 'Order Confirmed', status: 'completed' },
|
||
{ date: '2026-01-24', event: 'Shipped', status: 'completed' },
|
||
{ date: '2026-01-29', event: 'Delivered', status: 'completed' }
|
||
],
|
||
documents: [
|
||
{ id: 'odoc_010', type: 'PO', name: 'PO-Marriott-Plumbing.pdf', url: '#' },
|
||
{ id: 'odoc_011', type: 'Delivery Receipt', name: 'DR-ORD006.pdf', url: '#' },
|
||
{ id: 'odoc_012', type: 'Invoice', name: 'INV-ORD006.pdf', url: '#' }
|
||
],
|
||
// TODO: cross-reference — Vendor: Lone Star Plumbing, Project Contractor: Texas Builders LLC
|
||
notes: 'Plumbing overhaul materials - project currently on hold pending permit review',
|
||
contactPerson: 'Ray Gutierrez',
|
||
contactPhone: '469-555-0304'
|
||
},
|
||
{
|
||
id: 'ORD-007',
|
||
vendorId: 'v3',
|
||
projectId: 'proj_004',
|
||
projectAddress: '2612 Dunwick Dr, Plano, TX 75023',
|
||
orderDate: '2026-02-15',
|
||
dueDate: '2026-02-28',
|
||
deliveryDate: null,
|
||
status: 'pending',
|
||
items: [
|
||
{ id: 'item_015', description: 'Interior Drywall Sheets', quantity: 80, unit: 'sheets', unitPrice: 14.50, total: 1160.00, specifications: '4x8, 1/2-inch, fire-rated' },
|
||
{ id: 'item_016', description: 'Joint Compound', quantity: 12, unit: 'buckets', unitPrice: 22.00, total: 264.00, specifications: 'All-purpose, 5-gallon' }
|
||
],
|
||
subtotal: 1424.00,
|
||
tax: 117.48,
|
||
total: 1541.48,
|
||
deliveryAddress: { street: '2612 Dunwick Dr', city: 'Plano', state: 'TX', zip: '75023', instructions: 'Front entrance, stack in garage' },
|
||
timeline: [
|
||
{ date: '2026-02-15', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2026-02-28', event: 'Estimated Delivery', status: 'pending' }
|
||
],
|
||
documents: [{ id: 'odoc_013', type: 'PO', name: 'PO-Dunwick-Drywall.pdf', url: '#' }],
|
||
notes: 'Awaiting vendor confirmation',
|
||
contactPerson: 'Sales Desk',
|
||
contactPhone: '972-555-8888'
|
||
},
|
||
{
|
||
id: 'ORD-008',
|
||
vendorId: 'v3',
|
||
projectId: 'proj_009',
|
||
projectAddress: '7224 Independence Pkwy, Plano, TX 75025',
|
||
orderDate: '2026-02-10',
|
||
dueDate: '2026-02-22',
|
||
deliveryDate: null,
|
||
status: 'shipped',
|
||
items: [
|
||
{ id: 'item_017', description: 'Window Sealant - Exterior Grade', quantity: 48, unit: 'tubes', unitPrice: 8.50, total: 408.00, specifications: 'Silicone, clear, UV-resistant' },
|
||
{ id: 'item_018', description: 'Flashing Tape - Self-Adhesive', quantity: 20, unit: 'rolls', unitPrice: 35.00, total: 700.00, specifications: '6-inch, peel-and-stick, 75-ft' }
|
||
],
|
||
subtotal: 1108.00,
|
||
tax: 91.41,
|
||
total: 1199.41,
|
||
deliveryAddress: { street: '7224 Independence Pkwy', city: 'Plano', state: 'TX', zip: '75025', instructions: 'Leave at side entrance' },
|
||
timeline: [
|
||
{ date: '2026-02-10', event: 'Order Placed', status: 'completed' },
|
||
{ date: '2026-02-11', event: 'Order Confirmed', status: 'completed' },
|
||
{ date: '2026-02-17', event: 'Shipped', status: 'completed' },
|
||
{ date: '2026-02-22', event: 'Estimated Delivery', status: 'pending' }
|
||
],
|
||
documents: [{ id: 'odoc_014', type: 'PO', name: 'PO-Independence-Sealant.pdf', url: '#' }],
|
||
notes: 'In transit via FedEx Freight',
|
||
contactPerson: 'Sales Desk',
|
||
contactPhone: '972-555-8888'
|
||
}
|
||
];
|
||
|
||
const MOCK_VENDOR_INVOICES = [
|
||
{
|
||
id: 'INV-2026-001',
|
||
vendorId: 'v3',
|
||
orderId: 'ORD-002',
|
||
projectId: 'proj_002',
|
||
project: '2617 Rothland Ln, Plano, TX 75023',
|
||
invoiceDate: '2025-11-22',
|
||
dueDate: '2025-12-07',
|
||
amount: 2272.17,
|
||
status: 'paid',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Gutter System and Downspout Materials'
|
||
},
|
||
{
|
||
id: 'INV-2026-002',
|
||
vendorId: 'v3',
|
||
orderId: 'ORD-001',
|
||
projectId: 'proj_001',
|
||
project: '2604 Dunwick Dr, Plano, TX 75023',
|
||
invoiceDate: '2026-01-28',
|
||
dueDate: '2026-02-12',
|
||
amount: 3355.75,
|
||
status: 'pending',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Roofing Shingles and Underlayment Delivery'
|
||
},
|
||
{
|
||
id: 'INV-2026-003',
|
||
vendorId: 'v6',
|
||
orderId: 'ORD-003',
|
||
projectId: 'proj_003',
|
||
project: '6613 Phoenix Pl, Plano, TX 75023',
|
||
invoiceDate: '2026-02-04',
|
||
dueDate: '2026-02-19',
|
||
amount: 10500.25,
|
||
status: 'pending',
|
||
paymentTerms: 'Net 15',
|
||
description: 'HVAC Units and Ductwork Delivery'
|
||
},
|
||
{
|
||
id: 'INV-2026-004',
|
||
vendorId: 'v5',
|
||
orderId: 'ORD-004',
|
||
projectId: 'proj_008',
|
||
project: '2112 Winslow Dr, Plano, TX 75023',
|
||
invoiceDate: '2025-12-16',
|
||
dueDate: '2025-12-31',
|
||
amount: 9352.80,
|
||
status: 'paid',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Foundation Steel Piers and Equipment Rental'
|
||
},
|
||
{
|
||
id: 'INV-2026-005',
|
||
vendorId: 'v3',
|
||
orderId: 'ORD-005',
|
||
projectId: 'proj_007',
|
||
project: '6909 Custer Rd, Plano, TX 75023',
|
||
invoiceDate: '2026-02-03',
|
||
dueDate: '2026-02-18',
|
||
amount: 20599.98,
|
||
status: 'pending',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Commercial TPO Roofing Materials'
|
||
},
|
||
{
|
||
id: 'INV-2026-006',
|
||
vendorId: 'v4',
|
||
orderId: 'ORD-006',
|
||
projectId: 'proj_012',
|
||
project: 'Dallas/Plano Marriott, 7121 Bishop Rd, Plano, TX 75024',
|
||
invoiceDate: '2026-01-30',
|
||
dueDate: '2026-02-14',
|
||
amount: 13972.91,
|
||
status: 'pending',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Commercial Plumbing Materials and Water Heaters'
|
||
},
|
||
{
|
||
id: 'INV-2026-007',
|
||
vendorId: 'v1',
|
||
orderId: 'ORD-LABOR-001',
|
||
projectId: 'proj_004',
|
||
project: '2612 Dunwick Dr, Plano, TX 75023',
|
||
invoiceDate: '2026-02-10',
|
||
dueDate: '2026-02-25',
|
||
amount: 8750.00,
|
||
status: 'pending',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Interior Renovation - Roofing Crew Labor Week 3'
|
||
},
|
||
{
|
||
id: 'INV-2026-008',
|
||
vendorId: 'v3',
|
||
orderId: 'ORD-MAT-006',
|
||
projectId: 'proj_006',
|
||
project: '3913 Arizona Pl, Plano, TX 75023',
|
||
invoiceDate: '2026-02-07',
|
||
dueDate: '2026-02-22',
|
||
amount: 2850.00,
|
||
status: 'pending',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Electrical Panel and Circuit Breaker Materials'
|
||
},
|
||
{
|
||
id: 'INV-2025-045',
|
||
vendorId: 'v1',
|
||
orderId: 'ORD-HIST-001',
|
||
projectId: 'proj_010',
|
||
project: '2100 Legacy Dr, Plano, TX 75023',
|
||
invoiceDate: '2025-10-15',
|
||
dueDate: '2025-10-30',
|
||
amount: 14200.00,
|
||
status: 'paid',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Parking Lot Resurfacing Crew Labor'
|
||
},
|
||
{
|
||
id: 'INV-2025-046',
|
||
vendorId: 'v3',
|
||
orderId: 'ORD-HIST-002',
|
||
projectId: 'proj_010',
|
||
project: '2100 Legacy Dr, Plano, TX 75023',
|
||
invoiceDate: '2025-10-02',
|
||
dueDate: '2025-10-17',
|
||
amount: 8400.00,
|
||
status: 'paid',
|
||
paymentTerms: 'Net 15',
|
||
description: 'Asphalt and Sealant Materials'
|
||
}
|
||
];
|
||
|
||
const MOCK_DOCUMENTS = [
|
||
{
|
||
id: 'd1',
|
||
name: 'Jesus Gonzales - Driver License',
|
||
entityId: 'p1',
|
||
entityType: 'personnel',
|
||
entityName: 'Jesus Gonzales',
|
||
vendorId: 'v1',
|
||
documentType: 'DL',
|
||
fileName: 'JG_DriverLicense.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Jesus Gonzales',
|
||
uploadedAt: '2025-08-15',
|
||
expirationDate: '2026-08-15',
|
||
status: 'approved',
|
||
aiConfidence: 0.98,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2025-08-16'
|
||
},
|
||
{
|
||
id: 'd2',
|
||
name: 'Sparky Electric - Certificate of Insurance',
|
||
entityId: 'v2',
|
||
entityType: 'vendor',
|
||
entityName: 'Sparky Electric',
|
||
vendorId: 'v2',
|
||
documentType: 'COI',
|
||
fileName: 'Sparky_COI_2025.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Mike Spark',
|
||
uploadedAt: '2025-01-10',
|
||
expirationDate: '2026-01-15',
|
||
status: 'expired',
|
||
aiConfidence: 0.95,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2025-01-11'
|
||
},
|
||
{
|
||
id: 'd3',
|
||
name: 'Sarah Sales - Pay Plan Agreement',
|
||
entityId: 'p2',
|
||
entityType: 'personnel',
|
||
entityName: 'Sarah Sales',
|
||
vendorId: 'v1',
|
||
documentType: 'PAY_PLAN',
|
||
fileName: 'Sarah_PayPlan_Signed.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Sarah Sales',
|
||
uploadedAt: '2026-02-14',
|
||
expirationDate: '2027-02-14',
|
||
status: 'pending_review',
|
||
aiConfidence: 0.88,
|
||
reviewedBy: null,
|
||
reviewedAt: null
|
||
},
|
||
{
|
||
id: 'd4',
|
||
name: 'Lone Star Plumbing - Certificate of Insurance',
|
||
entityId: 'v4',
|
||
entityType: 'vendor',
|
||
entityName: 'Lone Star Plumbing',
|
||
vendorId: 'v4',
|
||
documentType: 'COI',
|
||
fileName: 'LoneStarPlumbing_COI_2026.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Ray Gutierrez',
|
||
uploadedAt: '2025-03-10',
|
||
expirationDate: '2026-03-05',
|
||
status: 'approved',
|
||
aiConfidence: 0.96,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2025-03-11'
|
||
},
|
||
{
|
||
id: 'd5',
|
||
name: 'DFW Foundation Pros - W9 Form',
|
||
entityId: 'v5',
|
||
entityType: 'vendor',
|
||
entityName: 'DFW Foundation Pros',
|
||
vendorId: 'v5',
|
||
documentType: 'W9',
|
||
fileName: 'DFW_Foundation_W9.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Tom Atkins',
|
||
uploadedAt: '2026-01-20',
|
||
expirationDate: '2027-01-20',
|
||
status: 'pending_review',
|
||
aiConfidence: 0.82,
|
||
reviewedBy: null,
|
||
reviewedAt: null
|
||
},
|
||
{
|
||
id: 'd6',
|
||
name: 'Texas Roofing Crew A - Subcontractor Agreement',
|
||
entityId: 'v1',
|
||
entityType: 'vendor',
|
||
entityName: 'Texas Roofing Crew A',
|
||
vendorId: 'v1',
|
||
documentType: 'SUB_AGREEMENT',
|
||
fileName: 'TexasRoofing_SubAgreement_2026.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Juan Perez',
|
||
uploadedAt: '2025-12-01',
|
||
expirationDate: '2026-12-01',
|
||
status: 'approved',
|
||
aiConfidence: 0.97,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2025-12-02'
|
||
},
|
||
{
|
||
id: 'd7',
|
||
name: 'North Texas HVAC - Certificate of Insurance',
|
||
entityId: 'v6',
|
||
entityType: 'vendor',
|
||
entityName: 'North Texas HVAC Solutions',
|
||
vendorId: 'v6',
|
||
documentType: 'COI',
|
||
fileName: 'NTXHVAC_COI_2026.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Linda Foster',
|
||
uploadedAt: '2025-11-25',
|
||
expirationDate: '2026-11-20',
|
||
status: 'approved',
|
||
aiConfidence: 0.94,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2025-11-26'
|
||
},
|
||
{
|
||
id: 'd8',
|
||
name: 'ABC Supply Co. - Certificate of Insurance',
|
||
entityId: 'v3',
|
||
entityType: 'vendor',
|
||
entityName: 'ABC Supply Co.',
|
||
vendorId: 'v3',
|
||
documentType: 'COI',
|
||
fileName: 'ABCSupply_COI_2027.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Sales Desk',
|
||
uploadedAt: '2026-01-05',
|
||
expirationDate: '2027-01-01',
|
||
status: 'approved',
|
||
aiConfidence: 0.99,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2026-01-06'
|
||
},
|
||
{
|
||
id: 'd9',
|
||
name: 'Sparky Electric - W9 Form',
|
||
entityId: 'v2',
|
||
entityType: 'vendor',
|
||
entityName: 'Sparky Electric',
|
||
vendorId: 'v2',
|
||
documentType: 'W9',
|
||
fileName: 'Sparky_W9_2026.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Mike Spark',
|
||
uploadedAt: '2026-02-01',
|
||
expirationDate: '2027-02-01',
|
||
status: 'approved',
|
||
aiConfidence: 0.91,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2026-02-02'
|
||
},
|
||
{
|
||
id: 'd10',
|
||
name: 'DFW Foundation Pros - Certificate of Insurance',
|
||
entityId: 'v5',
|
||
entityType: 'vendor',
|
||
entityName: 'DFW Foundation Pros',
|
||
vendorId: 'v5',
|
||
documentType: 'COI',
|
||
fileName: 'DFW_Foundation_COI_2026.pdf',
|
||
fileUrl: '#',
|
||
uploadedBy: 'Tom Atkins',
|
||
uploadedAt: '2025-09-20',
|
||
expirationDate: '2026-09-15',
|
||
status: 'approved',
|
||
aiConfidence: 0.93,
|
||
reviewedBy: 'Admin',
|
||
reviewedAt: '2025-09-21'
|
||
}
|
||
];
|
||
|
||
const MOCK_PROJECTS = [
|
||
// =====================================================================
|
||
// OWNER 1: Marcus Whitfield (own_001) — 6 Projects
|
||
// Active owner, strong financials, one project under dispute
|
||
// =====================================================================
|
||
|
||
// Project 1: Active, on track, healthy budget
|
||
{
|
||
id: 'proj_001',
|
||
ownerId: 'own_001',
|
||
propertyId: 'P-2602',
|
||
address: '2604 Dunwick Dr, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: ['sub_001'],
|
||
projectType: 'Roof Replacement',
|
||
status: 'active',
|
||
phase: 'Structural',
|
||
budget: 45000,
|
||
approvedBudget: 45000,
|
||
committedCost: 38500,
|
||
actualCost: 22500,
|
||
spent: 22500,
|
||
variancePercent: -50.0,
|
||
margin: 0.40,
|
||
commissionType: 'percent_gross',
|
||
commissionRate: 10,
|
||
commission: 4500,
|
||
startDate: '2026-01-20',
|
||
endDate: '2026-03-05',
|
||
completionPercentage: 55,
|
||
healthScore: 82,
|
||
openRFIs: 0,
|
||
changeOrderCount: 0,
|
||
pendingInvoiceCount: 2,
|
||
budgetBreakdown: [
|
||
{ name: 'ABC Supply Co.', category: 'Materials - Shingles', allocated: 15000, committed: 14200, actual: 8500 },
|
||
{ name: 'ABC Supply Co.', category: 'Materials - Underlayment', allocated: 3500, committed: 3355, actual: 3355 },
|
||
{ name: 'Texas Roofing Crew A', category: 'Labor - Tear Off', allocated: 8000, committed: 8000, actual: 8000 },
|
||
{ name: 'Texas Roofing Crew A', category: 'Labor - Install', allocated: 12000, committed: 7200, actual: 0 },
|
||
{ name: 'City of Plano', category: 'Permits & Inspection', allocated: 2500, committed: 2500, actual: 2645 },
|
||
{ name: 'Junk King DFW', category: 'Cleanup & Haul-Away', allocated: 4000, committed: 3245, actual: 0 }
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
riskLog: [
|
||
{ id: 'risk_001_1', description: 'Weather delay risk during February', severity: 'Medium', likelihood: 'Likely', mitigation: 'Monitor 10-day forecast, flex schedule for rain days', status: 'monitoring' },
|
||
{ id: 'risk_001_2', description: 'Material price increase from supplier', severity: 'Low', likelihood: 'Unlikely', mitigation: 'Price locked with PO', status: 'mitigated' }
|
||
],
|
||
issueLog: [],
|
||
paymentSchedule: [
|
||
{ id: 'ps_001_1', milestone: 'Mobilization (20%)', amount: 9000, dueDate: '2026-01-22', status: 'paid' },
|
||
{ id: 'ps_001_2', milestone: 'Tear-off Complete (30%)', amount: 13500, dueDate: '2026-02-01', status: 'paid' },
|
||
{ id: 'ps_001_3', milestone: 'Install Complete (30%)', amount: 13500, dueDate: '2026-02-20', status: 'pending' },
|
||
{ id: 'ps_001_4', milestone: 'Final Inspection (20%)', amount: 9000, dueDate: '2026-03-05', status: 'pending' }
|
||
],
|
||
activityTimeline: [
|
||
{ date: '2026-02-12', action: 'Invoice Submitted', user: 'ABC Supply Co.', details: 'INV-001-2 for roofing materials - $3,355.75' },
|
||
{ date: '2026-02-08', action: 'Milestone Completed', user: 'Texas Roofing Crew A', details: 'Dry-In Inspection passed' },
|
||
{ date: '2026-02-01', action: 'Payment Released', user: 'Justin Johnson', details: 'Tear-off milestone payment - $12,000' },
|
||
{ date: '2026-01-27', action: 'Material Delivered', user: 'ABC Supply Co.', details: 'Shingles and underlayment delivered to site' },
|
||
{ date: '2026-01-20', action: 'Project Started', user: 'Texas Builders LLC', details: 'Roof replacement project kicked off' }
|
||
],
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: ABC Supply Co., Texas Roofing Crew A
|
||
milestones: [
|
||
{ id: 'ms_001_1', name: 'Material Delivery', dueDate: '2026-01-25', status: 'completed', assignedTo: 'v3' },
|
||
{ id: 'ms_001_2', name: 'Roof Tear-off', dueDate: '2026-01-30', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_001_3', name: 'Dry-In Inspection', dueDate: '2026-02-03', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_001_4', name: 'Shingle Install', dueDate: '2026-02-14', status: 'in_progress', assignedTo: 'v1' },
|
||
{ id: 'ms_001_5', name: 'Final Inspection', dueDate: '2026-03-01', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_001_1', amount: 12000, submittedBy: 'con_001', status: 'paid', dueDate: '2026-02-01', datePaid: '2026-02-02' },
|
||
{ id: 'inv_001_2', amount: 3355.75, submittedBy: 'v3', status: 'pending', dueDate: '2026-02-12' },
|
||
{ id: 'inv_001_3', amount: 7200, submittedBy: 'v1', status: 'pending', dueDate: '2026-02-20' }
|
||
],
|
||
documents: [
|
||
{ name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' },
|
||
{ name: 'Permit #26-0401', url: '#', type: 'permit' },
|
||
{ name: 'Material Order PO-2604', url: '#', type: 'invoice' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_001_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: { type: 'percent_gross', rate: 12 } },
|
||
{ id: 'jtm_001_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_001_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_001_4', userId: 'e1', name: 'Frank Agent', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null },
|
||
{ id: 'jtm_001_5', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'pending', commissionEnabled: true, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 2: Completed, zero variance (perfectly on budget)
|
||
{
|
||
id: 'proj_002',
|
||
ownerId: 'own_001',
|
||
propertyId: 'P-2608',
|
||
address: '2617 Rothland Ln, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: [],
|
||
projectType: 'Gutter Replacement',
|
||
status: 'completed',
|
||
phase: 'Handover',
|
||
budget: 8500,
|
||
approvedBudget: 8500,
|
||
committedCost: 8500,
|
||
actualCost: 8500,
|
||
spent: 8500,
|
||
variancePercent: 0,
|
||
margin: 0.35,
|
||
commissionType: 'flat',
|
||
commissionRate: 850,
|
||
commission: 850,
|
||
startDate: '2025-11-15',
|
||
endDate: '2025-12-10',
|
||
completionPercentage: 100,
|
||
healthScore: 100,
|
||
openRFIs: 0,
|
||
changeOrderCount: 0,
|
||
pendingInvoiceCount: 0,
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: ABC Supply Co.
|
||
milestones: [
|
||
{ id: 'ms_002_1', name: 'Old Gutter Removal', dueDate: '2025-11-18', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_002_2', name: 'Material Delivery', dueDate: '2025-11-20', status: 'completed', assignedTo: 'v3' },
|
||
{ id: 'ms_002_3', name: 'Seamless Gutter Install', dueDate: '2025-11-28', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_002_4', name: 'Downspout Routing', dueDate: '2025-12-03', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_002_5', name: 'Final Walkthrough', dueDate: '2025-12-08', status: 'completed', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_002_1', amount: 2272.17, submittedBy: 'v3', status: 'paid', dueDate: '2025-11-25', datePaid: '2025-11-26' },
|
||
{ id: 'inv_002_2', amount: 4200, submittedBy: 'con_001', status: 'paid', dueDate: '2025-12-05', datePaid: '2025-12-06' },
|
||
{ id: 'inv_002_3', amount: 2027.83, submittedBy: 'con_001', status: 'paid', dueDate: '2025-12-10', datePaid: '2025-12-10' }
|
||
],
|
||
documents: [
|
||
{ name: 'Scope of Work - Gutter', url: '#', type: 'contract' },
|
||
{ name: 'Completion Certificate', url: '#', type: 'certificate' },
|
||
{ name: 'Final Invoice Package', url: '#', type: 'invoice' },
|
||
{ name: 'Warranty Registration', url: '#', type: 'warranty' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_002_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null },
|
||
{ id: 'jtm_002_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 3: Delayed with missed milestone
|
||
{
|
||
id: 'proj_003',
|
||
ownerId: 'own_001',
|
||
propertyId: 'P-2610',
|
||
address: '6613 Phoenix Pl, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: ['sub_001'],
|
||
projectType: 'HVAC Installation',
|
||
status: 'active',
|
||
phase: 'MEP',
|
||
budget: 32000,
|
||
approvedBudget: 32000,
|
||
committedCost: 28200,
|
||
actualCost: 18400,
|
||
spent: 18400,
|
||
variancePercent: -42.5,
|
||
margin: 0.30,
|
||
commissionType: 'percent_net',
|
||
commissionRate: 15,
|
||
commission: 3200,
|
||
startDate: '2026-01-06',
|
||
endDate: '2026-02-28',
|
||
completionPercentage: 42,
|
||
healthScore: 58,
|
||
openRFIs: 1,
|
||
changeOrderCount: 2,
|
||
pendingInvoiceCount: 2,
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: North Texas HVAC Solutions, ABC Supply Co.
|
||
milestones: [
|
||
{ id: 'ms_003_1', name: 'Site Preparation', dueDate: '2026-01-10', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_003_2', name: 'Ductwork Rough-In', dueDate: '2026-01-20', status: 'completed', assignedTo: 'v6' },
|
||
{ id: 'ms_003_3', name: 'Equipment Delivery', dueDate: '2026-01-30', status: 'completed', assignedTo: 'v6' },
|
||
{ id: 'ms_003_4', name: 'Unit Installation', dueDate: '2026-02-07', status: 'in_progress', assignedTo: 'v6' },
|
||
{ id: 'ms_003_5', name: 'Electrical Hookup', dueDate: '2026-02-14', status: 'pending', assignedTo: 'sub_001' },
|
||
{ id: 'ms_003_6', name: 'System Test and Commissioning', dueDate: '2026-02-25', status: 'pending', assignedTo: 'v6' }
|
||
],
|
||
budgetBreakdown: [
|
||
{ name: 'Comfort Air Systems', category: 'Equipment & Materials', allocated: 15000, committed: 15000, actual: 12500 },
|
||
{ name: 'Comfort Air Systems', category: 'Labor - HVAC', allocated: 8000, committed: 8000, actual: 4000 },
|
||
{ name: 'Sparky Electric', category: 'Labor - Electrical', allocated: 3000, committed: 3000, actual: 0 },
|
||
{ name: 'City of Plano', category: 'Permits & Fees', allocated: 1000, committed: 1000, actual: 950 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 5000, committed: 1200, actual: 950 }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_003_1', amount: 8200, submittedBy: 'con_001', status: 'paid', dueDate: '2026-01-15', datePaid: '2026-01-16' },
|
||
{ id: 'inv_003_2', amount: 10500.25, submittedBy: 'v6', status: 'pending', dueDate: '2026-02-19' },
|
||
{ id: 'inv_003_3', amount: 3400, submittedBy: 'v2', status: 'pending', dueDate: '2026-02-22' }
|
||
],
|
||
changeOrders: [
|
||
{ id: 'CO-003-01', title: 'Upsize Return Ducts', amount: 1200, status: 'approved', dateSubmitted: '2026-01-22', description: 'Increase return duct size for better airflow in master bedroom.' },
|
||
{ id: 'CO-003-02', title: 'Smart Thermostat Upgrade', amount: 450, status: 'pending', dateSubmitted: '2026-02-18', description: 'Upgrade to Ecobee Premium thermostats per owner request.' }
|
||
],
|
||
rfis: [
|
||
{ id: 'RFI-003-01', subject: 'Condenser Pad Location', status: 'closed', submittedBy: 'North Texas HVAC', dateOpened: '2026-01-12', dateClosed: '2026-01-14' },
|
||
{ id: 'RFI-003-02', subject: 'Duct Routing Conflict in Attic', status: 'open', submittedBy: 'Texas Builders LLC', dateOpened: '2026-02-15' }
|
||
],
|
||
riskLog: [
|
||
{ id: 'risk_003_1', description: 'Equipment backorder from manufacturer', severity: 'High', likelihood: 'Likely', mitigation: 'Source equivalent unit from secondary distributor', status: 'open' }
|
||
],
|
||
issueLog: [
|
||
{ id: 'issue_003_1', title: 'Failed framing inspection for duct clearance', priority: 'High', status: 'open', assignedTo: 'Texas Builders LLC', dateReported: '2026-02-14' }
|
||
],
|
||
activityTimeline: [
|
||
{ date: '2026-02-15', action: 'RFI Submitted', user: 'Site Supervisor', details: 'Duct routing conflict in attic' },
|
||
{ date: '2026-02-14', action: 'Inspection Failed', user: 'City Inspector', details: 'Framing clearance insufficient for 12" ducts' },
|
||
{ date: '2026-01-30', action: 'Milestone Completed', user: 'North Texas HVAC', details: 'Equipment Delivery phase finished' }
|
||
],
|
||
documents: [
|
||
{ name: 'HVAC Design Plans', url: '#', type: 'plans' },
|
||
{ name: 'Mechanical Permit #26-0187', url: '#', type: 'permit' },
|
||
{ name: 'Equipment Spec Sheets', url: '#', type: 'specification' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_003_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: { type: 'flat', rate: 2000 } },
|
||
{ id: 'jtm_003_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Project Manager', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_003_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 4: Active, over budget (spent > budget)
|
||
{
|
||
id: 'proj_004',
|
||
ownerId: 'own_001',
|
||
propertyId: 'P-2600',
|
||
address: '2612 Dunwick Dr, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: ['sub_001'],
|
||
projectType: 'Interior Renovation',
|
||
status: 'active',
|
||
phase: 'Finishing',
|
||
budget: 55000,
|
||
approvedBudget: 55000,
|
||
committedCost: 62300,
|
||
actualCost: 62300,
|
||
spent: 62300,
|
||
variancePercent: 13.3,
|
||
margin: -0.13,
|
||
commissionType: 'custom',
|
||
commissionRate: 5500,
|
||
commission: 5500,
|
||
startDate: '2025-12-01',
|
||
endDate: '2026-02-28',
|
||
completionPercentage: 78,
|
||
healthScore: 35,
|
||
openRFIs: 1,
|
||
changeOrderCount: 2,
|
||
pendingInvoiceCount: 2,
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: ABC Supply Co., Texas Roofing Crew A, Sparky Electric
|
||
milestones: [
|
||
{ id: 'ms_004_1', name: 'Demo and Abatement', dueDate: '2025-12-10', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_004_2', name: 'Framing and Structural', dueDate: '2025-12-22', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_004_3', name: 'Electrical Rough-In', dueDate: '2026-01-08', status: 'completed', assignedTo: 'sub_001' },
|
||
{ id: 'ms_004_4', name: 'Plumbing Rough-In', dueDate: '2026-01-15', status: 'completed', assignedTo: 'v4' },
|
||
{ id: 'ms_004_5', name: 'Drywall and Paint', dueDate: '2026-02-05', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_004_6', name: 'Flooring and Trim', dueDate: '2026-02-18', status: 'in_progress', assignedTo: 'con_001' },
|
||
{ id: 'ms_004_7', name: 'Final Punch List', dueDate: '2026-02-25', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
budgetBreakdown: [
|
||
{ name: 'DemoCrew Pro', category: 'Demolition', allocated: 5000, committed: 5500, actual: 5500 },
|
||
{ name: 'Texas Builders LLC', category: 'Framing & Structural', allocated: 8000, committed: 8500, actual: 8500 },
|
||
{ name: 'Sparky Electric', category: 'Electrical', allocated: 7000, committed: 7000, actual: 4800 },
|
||
{ name: 'DFW Foundation Pros', category: 'Plumbing', allocated: 6000, committed: 6500, actual: 6500 },
|
||
{ name: 'Premier Finishes', category: 'Drywall & Paint', allocated: 9000, committed: 10000, actual: 10000 },
|
||
{ name: 'FloorCraft Inc.', category: 'Flooring & Trim', allocated: 12000, committed: 15500, actual: 15500 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 8000, committed: 9300, actual: 11500 }
|
||
],
|
||
changeOrders: [
|
||
{ id: 'CO-004-01', title: 'Scope Expansion - Kitchen Island', amount: 4500, status: 'approved', dateSubmitted: '2026-01-10', description: 'Add a custom 6ft island with quartz countertop and built-in microwave.' },
|
||
{ id: 'CO-004-02', title: 'Hardwood Flooring Upgrade', amount: 2800, status: 'approved', dateSubmitted: '2026-01-25', description: 'Upgrade from LVP to engineered hardwood in living areas.' }
|
||
],
|
||
rfis: [
|
||
{ id: 'RFI-004-01', subject: 'Load-bearing wall clarification', status: 'closed', submittedBy: 'Texas Builders LLC', dateOpened: '2025-12-15', dateClosed: '2025-12-16' },
|
||
{ id: 'RFI-004-02', subject: 'Paint finish in bathrooms', status: 'open', submittedBy: 'Texas Builders LLC', dateOpened: '2026-02-02' }
|
||
],
|
||
riskLog: [
|
||
{ id: 'risk_004_1', description: 'Flooring shipment delayed at port', severity: 'High', likelihood: 'Likely', mitigation: 'Source alternative matching material from local distributor', status: 'open' },
|
||
{ id: 'risk_004_2', description: 'Paint color mismatch between batches', severity: 'Medium', likelihood: 'Possible', mitigation: 'Verify batch numbers before final coat', status: 'mitigated' }
|
||
],
|
||
issueLog: [
|
||
{ id: 'issue_004_1', title: 'Drywall texturing inconsistent in hallway', priority: 'Medium', status: 'open', assignedTo: 'Texas Builders LLC', dateReported: '2026-02-16' },
|
||
{ id: 'issue_004_2', title: 'Plumbing rough-in failed initial inspection', priority: 'High', status: 'resolved', assignedTo: 'Lone Star Plumbing', dateReported: '2026-01-16' }
|
||
],
|
||
activityTimeline: [
|
||
{ date: '2026-02-16', action: 'Issue Reported', user: 'Site Supervisor', details: 'Drywall texture inconsistent in main hallway' },
|
||
{ date: '2026-02-05', action: 'Milestone Completed', user: 'Texas Builders LLC', details: 'Drywall and Paint phase finished' },
|
||
{ date: '2026-01-22', action: 'Payment Released', user: 'Marcus Whitfield', details: 'Truss and framing payment - $12,800' },
|
||
{ date: '2026-01-18', action: 'Inspection Passed', user: 'City Inspector', details: 'Plumbing rough-in re-inspection passed' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_004_1', amount: 18000, submittedBy: 'con_001', status: 'paid', dueDate: '2025-12-15', datePaid: '2025-12-16' },
|
||
{ id: 'inv_004_2', amount: 15500, submittedBy: 'con_001', status: 'paid', dueDate: '2026-01-10', datePaid: '2026-01-12' },
|
||
{ id: 'inv_004_3', amount: 12800, submittedBy: 'v3', status: 'paid', dueDate: '2026-01-20', datePaid: '2026-01-22' },
|
||
{ id: 'inv_004_3b', amount: 4800, submittedBy: 'sub_001', status: 'paid', dueDate: '2026-01-12', datePaid: '2026-01-14' },
|
||
{ id: 'inv_004_4', amount: 8750, submittedBy: 'v1', status: 'pending', dueDate: '2026-02-25' },
|
||
{ id: 'inv_004_5', amount: 7250, submittedBy: 'con_001', status: 'pending', dueDate: '2026-02-28' }
|
||
],
|
||
documents: [
|
||
{ name: 'Interior Renovation Contract', url: '#', type: 'contract' },
|
||
{ name: 'Building Permit #25-1892', url: '#', type: 'permit' },
|
||
{ name: 'Change Order #1 - Scope Expansion', url: '#', type: 'change_order' },
|
||
{ name: 'Change Order #2 - Material Upgrade', url: '#', type: 'change_order' },
|
||
{ name: 'Asbestos Abatement Report', url: '#', type: 'report' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_004_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', commissionEnabled: true, jobCommission: { type: 'percent_net', rate: 8 } },
|
||
{ id: 'jtm_004_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_004_3', userId: 'e2', name: 'Fiona Field', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 5: On Hold with pending dispute
|
||
{
|
||
id: 'proj_005',
|
||
ownerId: 'own_001',
|
||
propertyId: 'P-2601',
|
||
address: '2608 Dunwick Dr, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: [],
|
||
projectType: 'Siding Replacement',
|
||
status: 'on_hold',
|
||
phase: 'Structural',
|
||
budget: 28000,
|
||
approvedBudget: 28000,
|
||
committedCost: 20700,
|
||
actualCost: 14200,
|
||
spent: 14200,
|
||
variancePercent: -49.3,
|
||
margin: 0.25,
|
||
commissionType: 'percent_gross',
|
||
commissionRate: 10,
|
||
commission: 2800,
|
||
startDate: '2026-01-06',
|
||
endDate: '2026-02-20',
|
||
completionPercentage: 38,
|
||
healthScore: 42,
|
||
openRFIs: 0,
|
||
changeOrderCount: 1,
|
||
pendingInvoiceCount: 1,
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: ABC Supply Co.
|
||
milestones: [
|
||
{ id: 'ms_005_1', name: 'Scaffolding Setup', dueDate: '2026-01-08', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_005_2', name: 'Old Siding Removal', dueDate: '2026-01-15', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_005_3', name: 'Moisture Barrier Install', dueDate: '2026-01-22', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_005_4', name: 'New Siding Install', dueDate: '2026-02-05', status: 'pending', assignedTo: 'con_001' },
|
||
{ id: 'ms_005_5', name: 'Trim and Caulking', dueDate: '2026-02-15', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
budgetBreakdown: [
|
||
{ name: 'ABC Supply Co.', category: 'Materials - Siding', allocated: 12000, committed: 12000, actual: 8000 },
|
||
{ name: 'Siding Solutions TX', category: 'Labor - Prep & Install', allocated: 9000, committed: 9000, actual: 3500 },
|
||
{ name: 'Sunbelt Rentals', category: 'Equipment Rental', allocated: 2000, committed: 2000, actual: 1500 },
|
||
{ name: 'Texas Builders LLC', category: 'Structural Repairs (CO)', allocated: 0, committed: 850, actual: 850 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 5000, committed: 5000, actual: 350 }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_005_1', amount: 8400, submittedBy: 'con_001', status: 'paid', dueDate: '2026-01-12', datePaid: '2026-01-13' },
|
||
{ id: 'inv_005_2', amount: 5800, submittedBy: 'v3', status: 'paid', dueDate: '2026-01-20', datePaid: '2026-01-21' },
|
||
{ id: 'inv_005_3', amount: 6500, submittedBy: 'con_001', status: 'pending', dueDate: '2026-02-10' }
|
||
],
|
||
changeOrders: [
|
||
{ id: 'CO-005-01', title: 'Rotten Fascia Board Replacement', amount: 850, status: 'approved', dateSubmitted: '2026-01-18', description: 'Replace 40 linear feet of rotted fascia discovered during tear-off.' }
|
||
],
|
||
rfis: [
|
||
{ id: 'RFI-005-01', subject: 'Vapor Barrier Specification', status: 'closed', submittedBy: 'Texas Builders LLC', dateOpened: '2026-01-20', dateClosed: '2026-01-21' }
|
||
],
|
||
riskLog: [
|
||
{ id: 'risk_005_1', description: 'Water damage hidden behind existing siding', severity: 'High', likelihood: 'Certain', mitigation: 'Halt work, assess rot, submit change order for structural repair.', status: 'realized' }
|
||
],
|
||
issueLog: [
|
||
{ id: 'issue_005_1', title: 'Defective siding panels delivered', priority: 'Critical', status: 'open', assignedTo: 'ABC Supply Co.', dateReported: '2026-02-04' }
|
||
],
|
||
activityTimeline: [
|
||
{ date: '2026-02-06', action: 'Project Paused', user: 'System', details: 'Status changed to On Hold due to material dispute' },
|
||
{ date: '2026-02-04', action: 'Issue Reported', user: 'Site Supervisor', details: 'Batch of siding panels showed unacceptable warping' },
|
||
{ date: '2026-01-22', action: 'Milestone Completed', user: 'Texas Builders LLC', details: 'Moisture Barrier Install finished' }
|
||
],
|
||
documents: [
|
||
{ name: 'Siding Replacement Contract', url: '#', type: 'contract' },
|
||
{ name: 'Dispute Notice - Material Defect', url: '#', type: 'dispute' },
|
||
{ name: 'Manufacturer Warranty Claim', url: '#', type: 'warranty' },
|
||
{ name: 'Site Photos - Defective Panels', url: '#', type: 'photos' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_005_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null },
|
||
{ id: 'jtm_005_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 6: Active, on track, early stage
|
||
{
|
||
id: 'proj_006',
|
||
ownerId: 'own_001',
|
||
propertyId: 'P-2625',
|
||
address: '3913 Arizona Pl, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: ['sub_001'],
|
||
projectType: 'Electrical Upgrade',
|
||
status: 'active',
|
||
phase: 'MEP',
|
||
budget: 18500,
|
||
approvedBudget: 18500,
|
||
committedCost: 12400,
|
||
actualCost: 5600,
|
||
spent: 5600,
|
||
variancePercent: -69.7,
|
||
margin: 0.38,
|
||
commissionType: 'flat',
|
||
commissionRate: 1850,
|
||
commission: 1850,
|
||
startDate: '2026-02-03',
|
||
endDate: '2026-03-15',
|
||
completionPercentage: 22,
|
||
healthScore: 88,
|
||
openRFIs: 0,
|
||
changeOrderCount: 0,
|
||
pendingInvoiceCount: 1,
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: Sparky Electric
|
||
milestones: [
|
||
{ id: 'ms_006_1', name: 'Panel Assessment', dueDate: '2026-02-05', status: 'completed', assignedTo: 'sub_001' },
|
||
{ id: 'ms_006_2', name: 'Permit Approval', dueDate: '2026-02-10', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_006_3', name: 'Panel Replacement', dueDate: '2026-02-20', status: 'in_progress', assignedTo: 'sub_001' },
|
||
{ id: 'ms_006_4', name: 'Circuit Re-routing', dueDate: '2026-03-01', status: 'pending', assignedTo: 'sub_001' },
|
||
{ id: 'ms_006_5', name: 'City Inspection', dueDate: '2026-03-12', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_006_1', amount: 2200, submittedBy: 'sub_001', status: 'paid', dueDate: '2026-02-08', datePaid: '2026-02-09' },
|
||
{ id: 'inv_006_2', amount: 3400, submittedBy: 'sub_001', status: 'pending', dueDate: '2026-02-22' }
|
||
],
|
||
documents: [
|
||
{ name: 'Electrical Upgrade Proposal', url: '#', type: 'contract' },
|
||
{ name: 'Electrical Permit #26-0223', url: '#', type: 'permit' },
|
||
{ name: 'Load Calculation Report', url: '#', type: 'report' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_006_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null },
|
||
{ id: 'jtm_006_2', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// =====================================================================
|
||
// OWNER 2: Diana Reeves (own_002) — 6 Projects
|
||
// High risk owner, vendor payment delays, over-budget project
|
||
// =====================================================================
|
||
|
||
// Project 7: Active, on track, healthy budget (commercial)
|
||
{
|
||
id: 'proj_007',
|
||
ownerId: 'own_002',
|
||
propertyId: 'P-2640',
|
||
address: '6909 Custer Rd, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: ['sub_001'],
|
||
projectType: 'Commercial Roof Replacement',
|
||
status: 'active',
|
||
phase: 'Structural',
|
||
budget: 125000,
|
||
approvedBudget: 125000,
|
||
committedCost: 98000,
|
||
actualCost: 48000,
|
||
spent: 48000,
|
||
variancePercent: -61.6,
|
||
margin: 0.35,
|
||
commissionType: 'percent_gross',
|
||
commissionRate: 10,
|
||
commission: 12500,
|
||
startDate: '2026-01-27',
|
||
endDate: '2026-04-15',
|
||
completionPercentage: 32,
|
||
healthScore: 78,
|
||
openRFIs: 0,
|
||
changeOrderCount: 0,
|
||
pendingInvoiceCount: 2,
|
||
budgetBreakdown: [
|
||
{ name: 'Firestone Building Products', category: 'Materials - TPO Membrane', allocated: 38000, committed: 35400, actual: 11400 },
|
||
{ name: 'Owens Corning', category: 'Materials - Insulation', allocated: 18000, committed: 16800, actual: 5550 },
|
||
{ name: 'ABC Supply Co.', category: 'Materials - Fasteners/Adhesive', allocated: 8000, committed: 7200, actual: 2080 },
|
||
{ name: 'Texas Roofing Crew A', category: 'Labor - Section A', allocated: 22000, committed: 18500, actual: 18500 },
|
||
{ name: 'Texas Roofing Crew A', category: 'Labor - Section B', allocated: 22000, committed: 8000, actual: 0 },
|
||
{ name: 'All American Crane', category: 'Crane Rental', allocated: 8000, committed: 5600, actual: 4200 },
|
||
{ name: 'City of Plano', category: 'Permits & Inspection', allocated: 4000, committed: 3500, actual: 3270 },
|
||
{ name: 'SafetyFirst Supply', category: 'Safety Equipment', allocated: 5000, committed: 3000, actual: 3000 }
|
||
],
|
||
changeOrders: [
|
||
{ id: 'CO-007-1', title: 'Additional Drainage Scuppers', amount: 4200, status: 'pending', dateSubmitted: '2026-02-10', description: 'Building management requested 4 additional scuppers on east elevation for improved water management' }
|
||
],
|
||
rfis: [
|
||
{ id: 'RFI-007-1', subject: 'Deck Condition Under Section B', status: 'open', submittedBy: 'Texas Roofing Crew A', dateOpened: '2026-02-14', dateClosed: null },
|
||
{ id: 'RFI-007-2', subject: 'Flashing Detail at Parapet Wall', status: 'closed', submittedBy: 'Texas Builders LLC', dateOpened: '2026-02-03', dateClosed: '2026-02-06' }
|
||
],
|
||
riskLog: [
|
||
{ id: 'risk_007_1', description: 'Roof deck deterioration may require additional repairs', severity: 'High', likelihood: 'Possible', mitigation: 'Structural assessment scheduled before Section B tear-off', status: 'open' },
|
||
{ id: 'risk_007_2', description: 'High winds could delay membrane installation', severity: 'Medium', likelihood: 'Likely', mitigation: 'Schedule membrane work on low-wind days, use mechanical fasteners as backup', status: 'monitoring' },
|
||
{ id: 'risk_007_3', description: 'Supply chain delay on TPO rolls', severity: 'Low', likelihood: 'Unlikely', mitigation: 'Full order confirmed and 60% already delivered', status: 'mitigated' }
|
||
],
|
||
issueLog: [
|
||
{ id: 'issue_007_1', title: 'Crane access blocked by tenant parking', priority: 'Medium', status: 'resolved', assignedTo: 'Texas Builders LLC', dateReported: '2026-02-01' },
|
||
{ id: 'issue_007_2', title: 'Section A tear-off revealed wet insulation', priority: 'High', status: 'open', assignedTo: 'Texas Roofing Crew A', dateReported: '2026-02-14' }
|
||
],
|
||
paymentSchedule: [
|
||
{ id: 'ps_007_1', milestone: 'Mobilization (20%)', amount: 25000, dueDate: '2026-01-30', status: 'paid' },
|
||
{ id: 'ps_007_2', milestone: 'Section A Complete (25%)', amount: 31250, dueDate: '2026-03-01', status: 'pending' },
|
||
{ id: 'ps_007_3', milestone: 'Section B Complete (25%)', amount: 31250, dueDate: '2026-03-30', status: 'pending' },
|
||
{ id: 'ps_007_4', milestone: 'Final Inspection (20%)', amount: 25000, dueDate: '2026-04-10', status: 'pending' },
|
||
{ id: 'ps_007_5', milestone: 'Warranty Registration (10%)', amount: 12500, dueDate: '2026-04-15', status: 'pending' }
|
||
],
|
||
activityTimeline: [
|
||
{ date: '2026-02-14', action: 'RFI Submitted', user: 'Texas Roofing Crew A', details: 'RFI-007-1: Deck condition under Section B needs assessment' },
|
||
{ date: '2026-02-14', action: 'Issue Reported', user: 'Texas Roofing Crew A', details: 'Wet insulation found during Section A tear-off' },
|
||
{ date: '2026-02-10', action: 'Change Order Submitted', user: 'Texas Builders LLC', details: 'CO-007-1: Additional drainage scuppers requested - $4,200' },
|
||
{ date: '2026-02-06', action: 'RFI Closed', user: 'Texas Builders LLC', details: 'RFI-007-2: Flashing detail resolved per manufacturer spec' },
|
||
{ date: '2026-02-03', action: 'Milestone Completed', user: 'ABC Supply Co.', details: 'Material procurement complete - all TPO delivered' },
|
||
{ date: '2026-01-30', action: 'Payment Released', user: 'Diana Reeves', details: 'Mobilization payment - $25,000' },
|
||
{ date: '2026-01-27', action: 'Project Started', user: 'Texas Builders LLC', details: 'Commercial roof replacement kicked off' }
|
||
],
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: ABC Supply Co., Texas Roofing Crew A
|
||
milestones: [
|
||
{ id: 'ms_007_1', name: 'Roof Survey and Assessment', dueDate: '2026-01-30', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_007_2', name: 'Material Procurement', dueDate: '2026-02-07', status: 'completed', assignedTo: 'v3' },
|
||
{ id: 'ms_007_3', name: 'Section A - Tear Off', dueDate: '2026-02-14', status: 'completed', assignedTo: 'v1' },
|
||
{ id: 'ms_007_4', name: 'Section A - TPO Install', dueDate: '2026-02-28', status: 'in_progress', assignedTo: 'v1' },
|
||
{ id: 'ms_007_5', name: 'Section B - Tear Off', dueDate: '2026-03-15', status: 'pending', assignedTo: 'v1' },
|
||
{ id: 'ms_007_6', name: 'Section B - TPO Install', dueDate: '2026-03-30', status: 'pending', assignedTo: 'v1' },
|
||
{ id: 'ms_007_7', name: 'Final Inspection and Warranty', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_007_1', amount: 25000, submittedBy: 'con_001', status: 'paid', dueDate: '2026-02-05', datePaid: '2026-02-06' },
|
||
{ id: 'inv_007_2', amount: 20599.98, submittedBy: 'v3', status: 'pending', dueDate: '2026-02-18' },
|
||
{ id: 'inv_007_3', amount: 18500, submittedBy: 'v1', status: 'pending', dueDate: '2026-02-28' }
|
||
],
|
||
documents: [
|
||
{ name: 'Commercial Roofing Contract', url: '#', type: 'contract' },
|
||
{ name: 'Commercial Building Permit #26-0312', url: '#', type: 'permit' },
|
||
{ name: 'TPO Manufacturer Spec', url: '#', type: 'specification' },
|
||
{ name: 'Roof Survey Report', url: '#', type: 'report' },
|
||
{ name: 'Safety Plan - Fall Protection', url: '#', type: 'safety' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_007_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', commissionEnabled: true, jobCommission: { type: 'percent_gross', rate: 5 } },
|
||
{ id: 'jtm_007_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_007_3', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_007_4', userId: 'e4', name: 'Felicity Fast', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 8: Active, over budget (spent > budget)
|
||
{
|
||
id: 'proj_008',
|
||
ownerId: 'own_002',
|
||
propertyId: 'P-2641',
|
||
address: '2112 Winslow Dr, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: [],
|
||
projectType: 'Foundation Repair',
|
||
status: 'active',
|
||
phase: 'Finishing',
|
||
budget: 75000,
|
||
approvedBudget: 75000,
|
||
committedCost: 91500,
|
||
actualCost: 91500,
|
||
spent: 91500,
|
||
variancePercent: 22.0,
|
||
margin: -0.22,
|
||
commissionType: 'percent_net',
|
||
commissionRate: 12,
|
||
commission: 7500,
|
||
startDate: '2025-11-18',
|
||
endDate: '2026-02-28',
|
||
completionPercentage: 88,
|
||
healthScore: 25,
|
||
openRFIs: 0,
|
||
changeOrderCount: 2,
|
||
pendingInvoiceCount: 2,
|
||
// TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: DFW Foundation Pros, ABC Supply Co.
|
||
milestones: [
|
||
{ id: 'ms_008_1', name: 'Structural Engineering Report', dueDate: '2025-11-22', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_008_2', name: 'Pier Installation - East', dueDate: '2025-12-06', status: 'completed', assignedTo: 'v5' },
|
||
{ id: 'ms_008_3', name: 'Pier Installation - West', dueDate: '2025-12-20', status: 'completed', assignedTo: 'v5' },
|
||
{ id: 'ms_008_4', name: 'Slab Lifting', dueDate: '2026-01-10', status: 'completed', assignedTo: 'v5' },
|
||
{ id: 'ms_008_5', name: 'Plumbing Re-test', dueDate: '2026-01-25', status: 'completed', assignedTo: 'v4' },
|
||
{ id: 'ms_008_6', name: 'Final Grade and Drainage', dueDate: '2026-02-10', status: 'in_progress', assignedTo: 'con_001' },
|
||
{ id: 'ms_008_7', name: 'Engineering Re-certification', dueDate: '2026-02-25', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_008_1', amount: 30000, submittedBy: 'con_001', status: 'paid', dueDate: '2025-12-01', datePaid: '2025-12-03' },
|
||
{ id: 'inv_008_2', amount: 9352.80, submittedBy: 'v5', status: 'paid', dueDate: '2025-12-20', datePaid: '2025-12-22' },
|
||
{ id: 'inv_008_3', amount: 24000, submittedBy: 'v5', status: 'paid', dueDate: '2026-01-15', datePaid: '2026-01-18' },
|
||
{ id: 'inv_008_4', amount: 18500, submittedBy: 'v5', status: 'pending', dueDate: '2026-02-15' },
|
||
{ id: 'inv_008_5', amount: 9647.20, submittedBy: 'con_001', status: 'pending', dueDate: '2026-02-28' }
|
||
],
|
||
documents: [
|
||
{ name: 'Foundation Repair Contract', url: '#', type: 'contract' },
|
||
{ name: 'Structural Engineering Report', url: '#', type: 'report' },
|
||
{ name: 'Pier Layout Diagram', url: '#', type: 'plans' },
|
||
{ name: 'Change Order #1 - Additional Piers', url: '#', type: 'change_order' },
|
||
{ name: 'Change Order #2 - Plumbing Reroute', url: '#', type: 'change_order' },
|
||
{ name: 'Soil Analysis Report', url: '#', type: 'report' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_008_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null },
|
||
{ id: 'jtm_008_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 9: Delayed with missed milestone
|
||
{
|
||
id: 'proj_009',
|
||
ownerId: 'own_002',
|
||
propertyId: 'P-2642',
|
||
address: '7224 Independence Pkwy, Plano, TX 75025',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: ['sub_001'],
|
||
projectType: 'Window Replacement',
|
||
status: 'active',
|
||
budget: 42000,
|
||
spent: 25800,
|
||
margin: 0.28,
|
||
commissionType: 'percent_gross',
|
||
commissionRate: 10,
|
||
commission: 4200,
|
||
startDate: '2026-01-13',
|
||
endDate: '2026-03-10',
|
||
completionPercentage: 50,
|
||
phase: 'Finishing',
|
||
approvedBudget: 42000,
|
||
committedCost: 32000,
|
||
actualCost: 25800,
|
||
variancePercent: -38.6,
|
||
healthScore: 55,
|
||
openRFIs: 0,
|
||
changeOrderCount: 0,
|
||
pendingInvoiceCount: 1,
|
||
milestones: [
|
||
{ id: 'ms_009_1', name: 'Window Measurements', dueDate: '2026-01-16', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_009_2', name: 'Custom Window Fabrication', dueDate: '2026-01-30', status: 'completed', assignedTo: 'v3' },
|
||
{ id: 'ms_009_3', name: 'Floor 1 Installation', dueDate: '2026-02-07', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_009_4', name: 'Floor 2 Installation', dueDate: '2026-02-14', status: 'in_progress', assignedTo: 'con_001' },
|
||
{ id: 'ms_009_5', name: 'Exterior Trim and Sealant', dueDate: '2026-02-25', status: 'pending', assignedTo: 'con_001' },
|
||
{ id: 'ms_009_6', name: 'Energy Audit Verification', dueDate: '2026-03-05', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_009_1', amount: 14000, submittedBy: 'v3', status: 'paid', dueDate: '2026-02-01', datePaid: '2026-02-03' },
|
||
{ id: 'inv_009_2', amount: 8200, submittedBy: 'con_001', status: 'paid', dueDate: '2026-02-10', datePaid: '2026-02-11' },
|
||
{ id: 'inv_009_3', amount: 9800, submittedBy: 'con_001', status: 'pending', dueDate: '2026-02-25' }
|
||
],
|
||
documents: [
|
||
{ name: 'Window Replacement Agreement', url: '#', type: 'contract' },
|
||
{ name: 'Custom Window Order Confirmation', url: '#', type: 'purchase_order' },
|
||
{ name: 'Energy Rating Certificates', url: '#', type: 'certificate' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_009_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null },
|
||
{ id: 'jtm_009_2', userId: 'e2', name: 'Fiona Field', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 10: Completed, zero variance
|
||
{
|
||
id: 'proj_010',
|
||
ownerId: 'own_002',
|
||
propertyId: 'P-2656',
|
||
address: '2100 Legacy Dr, Plano, TX 75023',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: [],
|
||
projectType: 'Parking Lot Resurface',
|
||
status: 'completed',
|
||
budget: 35000,
|
||
spent: 35000,
|
||
margin: 0.30,
|
||
commissionType: 'custom',
|
||
commissionRate: 3500,
|
||
commission: 3500,
|
||
startDate: '2025-09-15',
|
||
endDate: '2025-10-28',
|
||
completionPercentage: 100,
|
||
phase: 'Handover',
|
||
approvedBudget: 35000,
|
||
committedCost: 35000,
|
||
actualCost: 35000,
|
||
variancePercent: 0.0,
|
||
healthScore: 100,
|
||
openRFIs: 0,
|
||
changeOrderCount: 0,
|
||
pendingInvoiceCount: 0,
|
||
milestones: [
|
||
{ id: 'ms_010_1', name: 'Surface Milling', dueDate: '2025-09-20', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_010_2', name: 'Base Repair', dueDate: '2025-09-28', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_010_3', name: 'Asphalt Paving', dueDate: '2025-10-08', status: 'completed', assignedTo: 'v1' },
|
||
{ id: 'ms_010_4', name: 'Sealcoat Application', dueDate: '2025-10-18', status: 'completed', assignedTo: 'v1' },
|
||
{ id: 'ms_010_5', name: 'Striping and Signage', dueDate: '2025-10-25', status: 'completed', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_010_1', amount: 8400, submittedBy: 'v3', status: 'paid', dueDate: '2025-09-25', datePaid: '2025-09-26' },
|
||
{ id: 'inv_010_2', amount: 14200, submittedBy: 'v1', status: 'paid', dueDate: '2025-10-12', datePaid: '2025-10-13' },
|
||
{ id: 'inv_010_3', amount: 12400, submittedBy: 'con_001', status: 'paid', dueDate: '2025-10-28', datePaid: '2025-10-28' }
|
||
],
|
||
documents: [
|
||
{ name: 'Paving Contract', url: '#', type: 'contract' },
|
||
{ name: 'Completion Certificate', url: '#', type: 'certificate' },
|
||
{ name: 'ADA Compliance Verification', url: '#', type: 'report' },
|
||
{ name: 'Final Payment Release', url: '#', type: 'invoice' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_010_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: { type: 'flat', rate: 1500 } },
|
||
{ id: 'jtm_010_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_010_3', userId: 'e5', name: 'Felix Fixer', email: 'agent5@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'pending', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 11: Disputed with open change orders and unresolved issues
|
||
{
|
||
id: 'proj_011',
|
||
ownerId: 'own_002',
|
||
propertyId: 'P-2645',
|
||
address: 'Legacy Town Center, 5760 Daniel Rd, Plano, TX 75024',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: ['sub_001'],
|
||
projectType: 'Exterior Facade Restoration',
|
||
status: 'disputed',
|
||
budget: 95000,
|
||
spent: 68400,
|
||
margin: 0.20,
|
||
commissionType: 'flat',
|
||
commissionRate: 9500,
|
||
commission: 9500,
|
||
startDate: '2025-12-08',
|
||
endDate: '2026-03-20',
|
||
completionPercentage: 62,
|
||
phase: 'MEP',
|
||
approvedBudget: 95000,
|
||
committedCost: 88000,
|
||
actualCost: 68400,
|
||
variancePercent: -28.0,
|
||
healthScore: 30,
|
||
openRFIs: 2,
|
||
changeOrderCount: 3,
|
||
pendingInvoiceCount: 2,
|
||
milestones: [
|
||
{ id: 'ms_011_1', name: 'Scaffolding and Protection', dueDate: '2025-12-12', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_011_2', name: 'Surface Cleaning and Prep', dueDate: '2025-12-22', status: 'completed', assignedTo: 'con_001' },
|
||
{ id: 'ms_011_3', name: 'Masonry Repair - Phase 1', dueDate: '2026-01-10', status: 'completed', assignedTo: 'v1' },
|
||
{ id: 'ms_011_4', name: 'Masonry Repair - Phase 2', dueDate: '2026-01-28', status: 'completed', assignedTo: 'v1' },
|
||
{ id: 'ms_011_5', name: 'Waterproofing Application', dueDate: '2026-02-12', status: 'in_progress', assignedTo: 'con_001' },
|
||
{ id: 'ms_011_6', name: 'Window Sealant Replacement', dueDate: '2026-02-28', status: 'pending', assignedTo: 'v4' },
|
||
{ id: 'ms_011_7', name: 'Final Restoration and Paint', dueDate: '2026-03-15', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_011_1', amount: 22000, submittedBy: 'con_001', status: 'paid', dueDate: '2025-12-18', datePaid: '2025-12-20' },
|
||
{ id: 'inv_011_2', amount: 18400, submittedBy: 'v1', status: 'paid', dueDate: '2026-01-15', datePaid: '2026-01-18' },
|
||
{ id: 'inv_011_3', amount: 15200, submittedBy: 'v3', status: 'paid', dueDate: '2026-01-30', datePaid: '2026-02-02' },
|
||
{ id: 'inv_011_4', amount: 12800, submittedBy: 'con_001', status: 'pending', dueDate: '2026-02-20' },
|
||
{ id: 'inv_011_5', amount: 8900, submittedBy: 'v4', status: 'pending', dueDate: '2026-03-05' }
|
||
],
|
||
documents: [
|
||
{ name: 'Facade Restoration Master Contract', url: '#', type: 'contract' },
|
||
{ name: 'Building Permit #25-2101', url: '#', type: 'permit' },
|
||
{ name: 'Change Order #1 - Hidden Water Damage', url: '#', type: 'change_order' },
|
||
{ name: 'Change Order #2 - Additional Masonry', url: '#', type: 'change_order' },
|
||
{ name: 'Change Order #3 - Structural Reinforcement', url: '#', type: 'change_order' },
|
||
{ name: 'Dispute Filing - Scope Disagreement', url: '#', type: 'dispute' },
|
||
{ name: 'RFI #1 - Original vs Current Scope', url: '#', type: 'rfi' },
|
||
{ name: 'RFI #2 - Waterproofing Method', url: '#', type: 'rfi' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_011_1', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Project Manager', status: 'active', commissionEnabled: false, jobCommission: null },
|
||
{ id: 'jtm_011_2', userId: 'con_001', name: 'Mike Contractor', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
},
|
||
|
||
// Project 12: On Hold
|
||
{
|
||
id: 'proj_012',
|
||
ownerId: 'own_002',
|
||
propertyId: 'P-2644',
|
||
address: 'Dallas/Plano Marriott at Legacy Town Center, 7121 Bishop Rd, Plano, TX 75024',
|
||
contractorId: 'con_001',
|
||
subcontractorIds: [],
|
||
projectType: 'Plumbing Overhaul',
|
||
status: 'on_hold',
|
||
budget: 58000,
|
||
spent: 22600,
|
||
margin: 0.32,
|
||
commissionType: 'percent_gross',
|
||
commissionRate: 10,
|
||
commission: 5800,
|
||
startDate: '2026-01-13',
|
||
endDate: '2026-04-01',
|
||
completionPercentage: 28,
|
||
phase: 'Foundation',
|
||
approvedBudget: 58000,
|
||
committedCost: 34000,
|
||
actualCost: 22600,
|
||
variancePercent: -61.0,
|
||
healthScore: 45,
|
||
openRFIs: 0,
|
||
changeOrderCount: 0,
|
||
pendingInvoiceCount: 2,
|
||
milestones: [
|
||
{ id: 'ms_012_1', name: 'Plumbing Assessment', dueDate: '2026-01-17', status: 'completed', assignedTo: 'v4' },
|
||
{ id: 'ms_012_2', name: 'Material Procurement', dueDate: '2026-01-25', status: 'completed', assignedTo: 'v4' },
|
||
{ id: 'ms_012_3', name: 'Main Line Replacement', dueDate: '2026-02-05', status: 'completed', assignedTo: 'v4' },
|
||
{ id: 'ms_012_4', name: 'Water Heater Installation', dueDate: '2026-02-18', status: 'pending', assignedTo: 'v4' },
|
||
{ id: 'ms_012_5', name: 'Branch Line Reroute', dueDate: '2026-03-05', status: 'pending', assignedTo: 'v4' },
|
||
{ id: 'ms_012_6', name: 'Pressure Testing', dueDate: '2026-03-20', status: 'pending', assignedTo: 'v4' },
|
||
{ id: 'ms_012_7', name: 'City Inspection', dueDate: '2026-03-28', status: 'pending', assignedTo: 'con_001' }
|
||
],
|
||
invoices: [
|
||
{ id: 'inv_012_1', amount: 8600, submittedBy: 'v4', status: 'paid', dueDate: '2026-01-22', datePaid: '2026-01-24' },
|
||
{ id: 'inv_012_2', amount: 13972.91, submittedBy: 'v4', status: 'pending', dueDate: '2026-02-14' },
|
||
{ id: 'inv_012_3', amount: 12000, submittedBy: 'con_001', status: 'pending', dueDate: '2026-02-28' }
|
||
],
|
||
documents: [
|
||
{ name: 'Plumbing Overhaul Contract', url: '#', type: 'contract' },
|
||
{ name: 'Plumbing Permit #26-0156', url: '#', type: 'permit' },
|
||
{ name: 'Scope Hold Notice - Permit Review', url: '#', type: 'notice' },
|
||
{ name: 'Material Delivery Receipt', url: '#', type: 'receipt' }
|
||
],
|
||
teamMembers: [
|
||
{ id: 'jtm_012_1', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null },
|
||
{ id: 'jtm_012_2', userId: 'e3', name: 'Fred Flyer', email: 'agent3@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: false, jobCommission: null }
|
||
]
|
||
}
|
||
];
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// LynkDispatch AI — Mock Data
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export const DISPATCH_REPS = [
|
||
{
|
||
id: 'REP-01', name: 'Carlos Mendez', initials: 'CM',
|
||
status: 'en_route', // available | en_route | busy
|
||
currentLat: 33.0612, currentLng: -96.7891,
|
||
todayAppointments: 3, maxDaily: 6,
|
||
skills: ['insurance_claim', 'retail_estimate'],
|
||
performanceScore: 94, rating: 4.8, closeRate: 82,
|
||
},
|
||
{
|
||
id: 'REP-02', name: 'Aisha Kumar', initials: 'AK',
|
||
status: 'available',
|
||
currentLat: 33.0812, currentLng: -96.7234,
|
||
todayAppointments: 2, maxDaily: 6,
|
||
skills: ['roof_inspection', 'emergency_tarp'],
|
||
performanceScore: 88, rating: 4.9, closeRate: 76,
|
||
},
|
||
{
|
||
id: 'REP-03', name: 'Tom Bradley', initials: 'TB',
|
||
status: 'busy',
|
||
currentLat: 33.0389, currentLng: -96.7401,
|
||
todayAppointments: 5, maxDaily: 6,
|
||
skills: ['retail_estimate', 'roof_inspection'],
|
||
performanceScore: 79, rating: 4.5, closeRate: 68,
|
||
},
|
||
{
|
||
id: 'REP-04', name: 'Nina Patel', initials: 'NP',
|
||
status: 'available',
|
||
currentLat: 33.0516, currentLng: -96.7512,
|
||
todayAppointments: 1, maxDaily: 6,
|
||
skills: ['insurance_claim', 'roof_inspection', 'emergency_tarp'],
|
||
performanceScore: 91, rating: 4.7, closeRate: 79,
|
||
},
|
||
{
|
||
id: 'REP-05', name: 'Derek Walsh', initials: 'DW',
|
||
status: 'en_route',
|
||
currentLat: 33.0198, currentLng: -96.7234,
|
||
todayAppointments: 4, maxDaily: 6,
|
||
skills: ['retail_estimate', 'roof_inspection'],
|
||
performanceScore: 82, rating: 4.6, closeRate: 74,
|
||
},
|
||
];
|
||
|
||
const DISPATCH_LEADS_INITIAL = [
|
||
{
|
||
id: 'LD-1001', source: 'call_center', leadType: 'emergency_tarp', urgency: 'emergency',
|
||
customer: { name: 'John Smith', phone: '(972) 555-1001', email: 'john.smith@gmail.com' },
|
||
property: { address: '2612 Dunwick Dr', city: 'Plano', state: 'TX', zip: '75023', lat: 33.0708, lng: -96.7456 },
|
||
callCenter: { id: 'CC-01', name: 'Plano Command Center' },
|
||
notes: 'Customer reports major tarp blowoff after last night\'s storm — exposed decking on NE corner. Needs immediate coverage before rain resumes.',
|
||
estimatedDuration: 60, status: 'unassigned', arrivedMinsAgo: 8, assignedRepId: null,
|
||
},
|
||
{
|
||
id: 'LD-1002', source: 'website_form', leadType: 'roof_inspection', urgency: 'high',
|
||
customer: { name: 'Maria Gonzalez', phone: '(972) 555-1002', email: 'maria.gonzalez@gmail.com' },
|
||
property: { address: '4817 Shady Brook Ln', city: 'Plano', state: 'TX', zip: '75093', lat: 33.0652, lng: -96.8021 },
|
||
webSource: { campaign: 'Google Ads — Storm Season 2026' },
|
||
notes: 'Noticed curling shingles and granule loss in gutters. No active leak but concerned about upcoming storm season. Wants full inspection.',
|
||
estimatedDuration: 45, status: 'unassigned', arrivedMinsAgo: 14, assignedRepId: null,
|
||
},
|
||
{
|
||
id: 'LD-1003', source: 'canvassing', leadType: 'insurance_claim', urgency: 'high',
|
||
customer: { name: 'Robert Chen', phone: '(972) 555-1003', email: 'robert.chen@outlook.com' },
|
||
property: { address: '1234 Oak Creek Blvd', city: 'Plano', state: 'TX', zip: '75075', lat: 33.0421, lng: -96.7138 },
|
||
canvassedBy: { agentId: 'U-05', name: 'Marcus Webb' },
|
||
notes: 'Filed insurance claim after hail event 3/18. Adjuster visit scheduled 3/28 — needs estimate in hand beforehand. Cooperative and responsive.',
|
||
estimatedDuration: 45, status: 'assigned', arrivedMinsAgo: 22, assignedRepId: 'REP-01',
|
||
},
|
||
{
|
||
id: 'LD-1004', source: 'referral', leadType: 'retail_estimate', urgency: 'standard',
|
||
customer: { name: 'Sarah Mitchell', phone: '(972) 555-1004', email: 'sarah.mitchell@yahoo.com' },
|
||
property: { address: '890 Custer Rd', city: 'Plano', state: 'TX', zip: '75075', lat: 33.0389, lng: -96.7334 },
|
||
referredBy: { type: 'client', id: 'CL-2201', name: 'Tom Hargrove' },
|
||
notes: 'Retail estimate for full reroof. No insurance involvement. Wants architectural shingles — GAF Timberline HDZ preferred. Budget-conscious.',
|
||
estimatedDuration: 30, status: 'confirmed', arrivedMinsAgo: 35, assignedRepId: 'REP-03',
|
||
},
|
||
{
|
||
id: 'LD-1005', source: 'call_center', leadType: 'roof_inspection', urgency: 'standard',
|
||
customer: { name: 'James Wilson', phone: '(972) 555-1005', email: 'j.wilson85@gmail.com' },
|
||
property: { address: '321 Independence Pkwy', city: 'Plano', state: 'TX', zip: '75023', lat: 33.0812, lng: -96.7521 },
|
||
callCenter: { id: 'CC-02', name: 'DFW Ops Hub' },
|
||
notes: 'At-risk — customer called twice to reschedule. Currently assigned but rep running behind. Possible missed appointment window.',
|
||
estimatedDuration: 45, status: 'at_risk', arrivedMinsAgo: 47, assignedRepId: 'REP-02',
|
||
},
|
||
{
|
||
id: 'LD-1006', source: 'website_form', leadType: 'insurance_claim', urgency: 'high',
|
||
customer: { name: 'Lisa Thompson', phone: '(972) 555-1006', email: 'lisathompson@icloud.com' },
|
||
property: { address: '7654 Preston Rd', city: 'Plano', state: 'TX', zip: '75024', lat: 33.0934, lng: -96.8002 },
|
||
webSource: { campaign: 'Organic Search' },
|
||
notes: 'Large residential — ridge cap blown off, potential decking exposure. Adjuster already completed visit, needs supplement review before signing.',
|
||
estimatedDuration: 60, status: 'unassigned', arrivedMinsAgo: 5, assignedRepId: null,
|
||
},
|
||
{
|
||
id: 'LD-1007', source: 'canvassing', leadType: 'retail_estimate', urgency: 'standard',
|
||
customer: { name: 'David Park', phone: '(972) 555-1007', email: 'dpark.plano@gmail.com' },
|
||
property: { address: '2200 Alma Dr', city: 'Plano', state: 'TX', zip: '75075', lat: 33.0516, lng: -96.7289 },
|
||
canvassedBy: { agentId: 'U-07', name: 'Priya Nair' },
|
||
notes: 'Customer confirmed appointment. Wants two options: repair patch vs full replacement. Existing roof is 19 years old.',
|
||
estimatedDuration: 30, status: 'confirmed', arrivedMinsAgo: 62, assignedRepId: 'REP-04',
|
||
},
|
||
{
|
||
id: 'LD-1008', source: 'referral', leadType: 'roof_inspection', urgency: 'standard',
|
||
customer: { name: 'Angela Moore', phone: '(972) 555-1008', email: 'angela.moore72@gmail.com' },
|
||
property: { address: '555 Legacy Dr', city: 'Plano', state: 'TX', zip: '75023', lat: 33.0744, lng: -96.7612 },
|
||
referredBy: { type: 'customer', id: 'CU-0892', name: 'Daniel Reyes' },
|
||
notes: 'Referral from existing customer. Friendly homeowner, flexible scheduling. Roof is 12 years old with no known damage — proactive inspection only.',
|
||
estimatedDuration: 45, status: 'unassigned', arrivedMinsAgo: 28, assignedRepId: null,
|
||
},
|
||
{
|
||
id: 'LD-1009', source: 'call_center', leadType: 'emergency_tarp', urgency: 'emergency',
|
||
customer: { name: 'Carlos Rivera', phone: '(972) 555-1009', email: 'c.rivera@hotmail.com' },
|
||
property: { address: '3301 Avenue K', city: 'Plano', state: 'TX', zip: '75074', lat: 33.0198, lng: -96.6989 },
|
||
callCenter: { id: 'CC-01', name: 'Plano Command Center' },
|
||
notes: 'ACTIVE LEAK — water through master bedroom ceiling. Family with newborn on premises. Must dispatch immediately regardless of weather conditions.',
|
||
estimatedDuration: 90, status: 'unassigned', arrivedMinsAgo: 3, assignedRepId: null,
|
||
},
|
||
{
|
||
id: 'LD-1010', source: 'website_form', leadType: 'retail_estimate', urgency: 'standard',
|
||
customer: { name: 'Patricia Davis', phone: '(972) 555-1010', email: 'pdavis.home@gmail.com' },
|
||
property: { address: '6711 W Spring Creek Pkwy', city: 'Plano', state: 'TX', zip: '75024', lat: 33.0888, lng: -96.7756 },
|
||
webSource: { campaign: 'Facebook — Spring Roofing Promo' },
|
||
notes: 'At-risk of going cold — no response to last two callbacks. Rep assigned but contact not confirmed. Try alternate number if available.',
|
||
estimatedDuration: 30, status: 'at_risk', arrivedMinsAgo: 55, assignedRepId: 'REP-05',
|
||
},
|
||
{
|
||
id: 'LD-1011', source: 'canvassing', leadType: 'insurance_claim', urgency: 'high',
|
||
customer: { name: 'Michael Anderson', phone: '(972) 555-1011', email: 'michael.a@outlook.com' },
|
||
property: { address: '1800 Jupiter Rd', city: 'Plano', state: 'TX', zip: '75074', lat: 33.0267, lng: -96.7102 },
|
||
canvassedBy: { agentId: 'U-05', name: 'Marcus Webb' },
|
||
notes: 'Multiple hail impacts on canvass route. Customer receptive, confirmed hail event on property. Adjuster not yet contacted — walk them through the process.',
|
||
estimatedDuration: 60, status: 'unassigned', arrivedMinsAgo: 11, assignedRepId: null,
|
||
},
|
||
{
|
||
id: 'LD-1012', source: 'referral', leadType: 'roof_inspection', urgency: 'standard',
|
||
customer: { name: 'Jennifer White', phone: '(972) 555-1012', email: 'jwhite.plano@gmail.com' },
|
||
property: { address: '4400 W Parker Rd', city: 'Plano', state: 'TX', zip: '75093', lat: 33.0612, lng: -96.8145 },
|
||
referredBy: { type: 'client', id: 'CL-1178', name: 'Brenda Kowalski' },
|
||
notes: 'Confirmed appointment. Customer already has a competing estimate — come in strong on materials quality. GAF or Owens Corning preferred. Price-sensitive but quality-motivated.',
|
||
estimatedDuration: 45, status: 'confirmed', arrivedMinsAgo: 72, assignedRepId: 'REP-01',
|
||
},
|
||
];
|
||
|
||
// ─── Hail Recon mock data ────────────────────────────────────────────────────
|
||
// Simulates ImpactDatesForLatLong responses, keyed by leadId.
|
||
// hailSize in inches. Each event = one hail storm hit on that property.
|
||
export const MOCK_HAIL_HISTORY = {
|
||
'LD-1001': [
|
||
{ date: '2026-04-28', hailSize: 2.00, stormId: 'TX-2026-0428' },
|
||
{ date: '2026-01-14', hailSize: 1.50, stormId: 'TX-2026-0114' },
|
||
{ date: '2025-09-03', hailSize: 1.00, stormId: 'TX-2025-0903' },
|
||
],
|
||
'LD-1002': [
|
||
{ date: '2026-04-28', hailSize: 1.75, stormId: 'TX-2026-0428' },
|
||
{ date: '2025-11-22', hailSize: 1.25, stormId: 'TX-2025-1122' },
|
||
],
|
||
'LD-1003': [
|
||
{ date: '2026-03-18', hailSize: 2.25, stormId: 'TX-2026-0318' },
|
||
{ date: '2025-07-09', hailSize: 1.50, stormId: 'TX-2025-0709' },
|
||
{ date: '2025-02-14', hailSize: 0.75, stormId: 'TX-2025-0214' },
|
||
],
|
||
'LD-1004': [
|
||
{ date: '2026-03-18', hailSize: 1.50, stormId: 'TX-2026-0318' },
|
||
],
|
||
'LD-1005': [
|
||
{ date: '2026-04-28', hailSize: 1.75, stormId: 'TX-2026-0428' },
|
||
{ date: '2026-01-14', hailSize: 1.25, stormId: 'TX-2026-0114' },
|
||
],
|
||
'LD-1006': [
|
||
{ date: '2026-04-28', hailSize: 2.00, stormId: 'TX-2026-0428' },
|
||
{ date: '2026-03-18', hailSize: 1.75, stormId: 'TX-2026-0318' },
|
||
{ date: '2025-10-05', hailSize: 1.25, stormId: 'TX-2025-1005' },
|
||
{ date: '2025-06-17', hailSize: 1.00, stormId: 'TX-2025-0617' },
|
||
],
|
||
'LD-1007': [
|
||
{ date: '2025-08-21', hailSize: 1.00, stormId: 'TX-2025-0821' },
|
||
],
|
||
'LD-1008': [],
|
||
'LD-1009': [
|
||
{ date: '2026-04-28', hailSize: 2.50, stormId: 'TX-2026-0428' },
|
||
{ date: '2026-01-14', hailSize: 1.75, stormId: 'TX-2026-0114' },
|
||
{ date: '2025-09-03', hailSize: 1.50, stormId: 'TX-2025-0903' },
|
||
{ date: '2025-04-11', hailSize: 1.25, stormId: 'TX-2025-0411' },
|
||
],
|
||
'LD-1010': [
|
||
{ date: '2026-04-28', hailSize: 1.50, stormId: 'TX-2026-0428' },
|
||
{ date: '2025-12-30', hailSize: 1.00, stormId: 'TX-2025-1230' },
|
||
],
|
||
'LD-1011': [
|
||
{ date: '2026-04-28', hailSize: 2.25, stormId: 'TX-2026-0428' },
|
||
{ date: '2026-03-18', hailSize: 2.00, stormId: 'TX-2026-0318' },
|
||
{ date: '2025-11-22', hailSize: 1.50, stormId: 'TX-2025-1122' },
|
||
],
|
||
'LD-1012': [],
|
||
};
|
||
|
||
// Pre-baked AI recommendations keyed by lead ID — no algorithm needed, just convincing data.
|
||
// Each rep entry: repId, score, slotStart/End, factors (0-100 each), reasons[], stormReasons[], routePath[[lat,lng]]
|
||
export const DISPATCH_RECOMMENDATIONS = {
|
||
'LD-1001': [
|
||
{
|
||
repId: 'REP-04', score: 91, slotStart: '1:30 PM', slotEnd: '2:30 PM',
|
||
distanceMi: '2.1',
|
||
factors: { proximity: 88, driveTime: 90, calendarFit: 95, weather: 90, skillMatch: 100 },
|
||
reasons: ['Closest eligible rep — 2.1 mi', '12 min drive time estimated', 'Insurance claim certified', 'Buffer preserved before next appointment'],
|
||
stormReasons: ['Lightning risk before 2 PM — slot pushed to 1:30 PM', 'Rep carries emergency tarp kit'],
|
||
aiInsight: 'Calendar and proximity align perfectly — earliest safe window before afternoon storm risk',
|
||
conflictFlag: null,
|
||
routePath: [[33.0516, -96.7512], [33.0612, -96.7484], [33.0708, -96.7456]],
|
||
},
|
||
{
|
||
repId: 'REP-02', score: 79, slotStart: '3:00 PM', slotEnd: '4:00 PM',
|
||
distanceMi: '3.8',
|
||
factors: { proximity: 72, driveTime: 75, calendarFit: 82, weather: 80, skillMatch: 85 },
|
||
reasons: ['Available at 3 PM', '18 min drive — moderate traffic', 'Roof inspection certified'],
|
||
stormReasons: ['Afternoon slot carries weather risk — monitor closely'],
|
||
aiInsight: 'Solid backup with roof certification — afternoon slot viable but weather requires monitoring',
|
||
conflictFlag: 'Post-3 PM weather risk — confirm conditions before dispatch',
|
||
routePath: [[33.0812, -96.7234], [33.0760, -96.7345], [33.0708, -96.7456]],
|
||
},
|
||
{
|
||
repId: 'REP-01', score: 71, slotStart: '4:30 PM', slotEnd: '5:30 PM',
|
||
distanceMi: '5.2',
|
||
factors: { proximity: 62, driveTime: 60, calendarFit: 78, weather: 70, skillMatch: 100 },
|
||
reasons: ['Available later in the day', '24 min drive from current location', 'High performer — score 94'],
|
||
stormReasons: ['Late afternoon — weather conditions improving'],
|
||
aiInsight: 'High performer but distance and late slot reduce overall optimality for this job type',
|
||
conflictFlag: '20 min buffer only after completion — schedule at risk if job runs long',
|
||
routePath: [[33.0612, -96.7891], [33.0660, -96.7674], [33.0708, -96.7456]],
|
||
},
|
||
],
|
||
'LD-1002': [
|
||
{
|
||
repId: 'REP-01', score: 88, slotStart: '2:00 PM', slotEnd: '2:45 PM',
|
||
distanceMi: '1.4',
|
||
factors: { proximity: 92, driveTime: 88, calendarFit: 85, weather: 88, skillMatch: 100 },
|
||
reasons: ['Closest rep — 1.4 mi from lead location', '8 min drive time', 'Buffer maintained after current job'],
|
||
stormReasons: ['Weather clear until 3 PM — slot timing is safe', 'Rep familiar with West Plano territory'],
|
||
aiInsight: 'Nearest eligible rep with strong calendar fit — weather window closes cleanly after completion',
|
||
conflictFlag: null,
|
||
routePath: [[33.0612, -96.7891], [33.0632, -96.7956], [33.0652, -96.8021]],
|
||
},
|
||
{
|
||
repId: 'REP-04', score: 74, slotStart: '3:30 PM', slotEnd: '4:15 PM',
|
||
distanceMi: '4.1',
|
||
factors: { proximity: 70, driveTime: 68, calendarFit: 85, weather: 72, skillMatch: 100 },
|
||
reasons: ['Available at 3:30 PM', '22 min drive from current position', 'Insurance claim certified'],
|
||
stormReasons: ['Afternoon slot — weather risk moderate after 3 PM'],
|
||
aiInsight: 'Certified fallback with open afternoon — drive time and weather reduce confidence vs. top pick',
|
||
conflictFlag: 'Afternoon weather window tight — start punctually or reschedule',
|
||
routePath: [[33.0516, -96.7512], [33.0584, -96.7767], [33.0652, -96.8021]],
|
||
},
|
||
],
|
||
'LD-1003': [
|
||
{
|
||
repId: 'REP-01', score: 87, slotStart: '11:00 AM', slotEnd: '11:45 AM',
|
||
distanceMi: '2.8',
|
||
factors: { proximity: 85, driveTime: 88, calendarFit: 90, weather: 85, skillMatch: 100 },
|
||
reasons: ['Currently assigned — optimal for continuity', '11 min drive from current stop', 'Insurance claim specialist'],
|
||
stormReasons: ['Morning slot — no weather concerns'],
|
||
aiInsight: 'Continuity assignment maximizes close probability — same rep builds on prior customer rapport',
|
||
conflictFlag: null,
|
||
routePath: [[33.0612, -96.7891], [33.0517, -96.7515], [33.0421, -96.7138]],
|
||
},
|
||
{
|
||
repId: 'REP-05', score: 67, slotStart: '1:00 PM', slotEnd: '1:45 PM',
|
||
distanceMi: '4.4',
|
||
factors: { proximity: 65, driveTime: 62, calendarFit: 72, weather: 75, skillMatch: 60 },
|
||
reasons: ['Available afternoon slot', '19 min drive', 'Less specialized — retail estimate primary skill'],
|
||
stormReasons: ['Afternoon — monitor weather after 1 PM'],
|
||
aiInsight: 'Capable fallback but skill mismatch lowers confidence for insurance claim type',
|
||
conflictFlag: 'Skill mismatch — retail primary, insurance secondary skill only',
|
||
routePath: [[33.0198, -96.7234], [33.0310, -96.7186], [33.0421, -96.7138]],
|
||
},
|
||
],
|
||
'LD-1004': [
|
||
{
|
||
repId: 'REP-03', score: 95, slotStart: '10:30 AM', slotEnd: '11:00 AM',
|
||
distanceMi: '0.6',
|
||
factors: { proximity: 98, driveTime: 97, calendarFit: 88, weather: 95, skillMatch: 100 },
|
||
reasons: ['0.6 mi away — optimal proximity', '4 min drive time', 'Retail estimate specialist'],
|
||
stormReasons: ['Morning slot — conditions favorable'],
|
||
aiInsight: 'Near-perfect assignment — virtually on-site with ideal skill set and favorable morning conditions',
|
||
conflictFlag: null,
|
||
routePath: [[33.0389, -96.7401], [33.0389, -96.7368], [33.0389, -96.7334]],
|
||
},
|
||
{
|
||
repId: 'REP-05', score: 73, slotStart: '2:00 PM', slotEnd: '2:30 PM',
|
||
distanceMi: '3.3',
|
||
factors: { proximity: 70, driveTime: 68, calendarFit: 80, weather: 72, skillMatch: 80 },
|
||
reasons: ['Available afternoon', '16 min drive', 'Retail estimate capable'],
|
||
stormReasons: ['Afternoon slot — watch post-2 PM weather'],
|
||
aiInsight: 'Retail-capable with open afternoon — viable if top pick becomes unavailable',
|
||
conflictFlag: null,
|
||
routePath: [[33.0198, -96.7234], [33.0294, -96.7284], [33.0389, -96.7334]],
|
||
},
|
||
],
|
||
'LD-1005': [
|
||
{
|
||
repId: 'REP-02', score: 94, slotStart: '12:00 PM', slotEnd: '12:45 PM',
|
||
distanceMi: '0.3',
|
||
factors: { proximity: 97, driveTime: 96, calendarFit: 90, weather: 92, skillMatch: 95 },
|
||
reasons: ['0.3 mi from lead — effectively on-site', '3 min drive time', 'Roof inspection specialist', 'Low daily load — 2 of 6 slots used'],
|
||
stormReasons: ['Midday window — weather acceptable', 'Rep carries safety equipment'],
|
||
aiInsight: 'Exceptional proximity with light workload — highest-confidence dispatch of the day',
|
||
conflictFlag: null,
|
||
routePath: [[33.0812, -96.7234], [33.0812, -96.7378], [33.0812, -96.7521]],
|
||
},
|
||
{
|
||
repId: 'REP-04', score: 77, slotStart: '2:30 PM', slotEnd: '3:15 PM',
|
||
distanceMi: '3.6',
|
||
factors: { proximity: 72, driveTime: 70, calendarFit: 88, weather: 78, skillMatch: 90 },
|
||
reasons: ['Available at 2:30 PM', '17 min drive', 'Roof inspection certified'],
|
||
stormReasons: ['Afternoon — post-3 PM weather worsening, start punctually'],
|
||
aiInsight: 'Certified backup with clean afternoon window — must start on time before weather closes in',
|
||
conflictFlag: 'Start before 3 PM — weather deteriorates sharply after',
|
||
routePath: [[33.0516, -96.7512], [33.0664, -96.7517], [33.0812, -96.7521]],
|
||
},
|
||
],
|
||
'LD-1006': [
|
||
{
|
||
repId: 'REP-01', score: 83, slotStart: '3:00 PM', slotEnd: '4:00 PM',
|
||
distanceMi: '3.1',
|
||
factors: { proximity: 80, driveTime: 78, calendarFit: 85, weather: 82, skillMatch: 100 },
|
||
reasons: ['Best available slot — 3 PM', '14 min drive from current route', 'Insurance claim specialist'],
|
||
stormReasons: ['Wind advisory active — exterior inspection may need delay', 'Insurance claim can proceed indoors'],
|
||
aiInsight: 'Best certified specialist available — indoor claim process can proceed despite active wind advisory',
|
||
conflictFlag: 'Wind advisory active — verify exterior conditions before starting roof portion',
|
||
routePath: [[33.0612, -96.7891], [33.0773, -96.7947], [33.0934, -96.8002]],
|
||
},
|
||
{
|
||
repId: 'REP-02', score: 69, slotStart: '4:30 PM', slotEnd: '5:30 PM',
|
||
distanceMi: '5.8',
|
||
factors: { proximity: 65, driveTime: 62, calendarFit: 75, weather: 68, skillMatch: 85 },
|
||
reasons: ['Late afternoon availability', '26 min drive', 'Roof inspection skilled'],
|
||
stormReasons: ['Late afternoon — conditions deteriorating, not recommended'],
|
||
aiInsight: 'Low-confidence option — weather and distance combine unfavorably for late afternoon exterior work',
|
||
conflictFlag: 'Not recommended — conditions deteriorating by 4:30 PM, daylight limited',
|
||
routePath: [[33.0812, -96.7234], [33.0873, -96.7618], [33.0934, -96.8002]],
|
||
},
|
||
],
|
||
'LD-1007': [
|
||
{
|
||
repId: 'REP-04', score: 87, slotStart: '11:30 AM', slotEnd: '12:00 PM',
|
||
distanceMi: '1.5',
|
||
factors: { proximity: 90, driveTime: 88, calendarFit: 92, weather: 90, skillMatch: 80 },
|
||
reasons: ['1.5 mi away — close proximity', '9 min drive time', 'Low daily load', 'Buffer before afternoon appointments'],
|
||
stormReasons: ['Morning window — safe conditions'],
|
||
aiInsight: 'Light daily load with close proximity — midday window maximizes buffer for afternoon pipeline',
|
||
conflictFlag: null,
|
||
routePath: [[33.0516, -96.7512], [33.0516, -96.7401], [33.0516, -96.7289]],
|
||
},
|
||
{
|
||
repId: 'REP-03', score: 78, slotStart: '1:30 PM', slotEnd: '2:00 PM',
|
||
distanceMi: '2.9',
|
||
factors: { proximity: 78, driveTime: 75, calendarFit: 70, weather: 82, skillMatch: 100 },
|
||
reasons: ['Retail estimate specialist', '12 min drive', 'Near current work site'],
|
||
stormReasons: ['Early afternoon — conditions acceptable'],
|
||
aiInsight: 'Specialist-level skill match but heavier daily load reduces calendar fit score',
|
||
conflictFlag: '5 appointments today — buffer is tight if any job runs over',
|
||
routePath: [[33.0389, -96.7401], [33.0453, -96.7345], [33.0516, -96.7289]],
|
||
},
|
||
],
|
||
'LD-1008': [
|
||
{
|
||
repId: 'REP-04', score: 86, slotStart: '2:00 PM', slotEnd: '2:45 PM',
|
||
distanceMi: '2.7',
|
||
factors: { proximity: 84, driveTime: 85, calendarFit: 90, weather: 85, skillMatch: 95 },
|
||
reasons: ['2.7 mi from lead — manageable drive', '15 min estimated travel', 'Roof inspection certified', 'Open afternoon slot'],
|
||
stormReasons: ['Weather acceptable until 3:30 PM', 'Book for 2 PM to maximize safe window'],
|
||
aiInsight: 'Certified for roof inspection with open slot — 2 PM booking maximizes safe weather window',
|
||
conflictFlag: null,
|
||
routePath: [[33.0516, -96.7512], [33.0630, -96.7562], [33.0744, -96.7612]],
|
||
},
|
||
{
|
||
repId: 'REP-02', score: 75, slotStart: '3:30 PM', slotEnd: '4:15 PM',
|
||
distanceMi: '4.1',
|
||
factors: { proximity: 72, driveTime: 70, calendarFit: 82, weather: 74, skillMatch: 90 },
|
||
reasons: ['Available at 3:30 PM', '19 min drive', 'Roof inspection specialist'],
|
||
stormReasons: ['Late afternoon — weather risk elevated after 3 PM'],
|
||
aiInsight: 'Roof specialist with afternoon availability — weather risk increases as slot approaches 4 PM',
|
||
conflictFlag: '15 min buffer after completion — schedule may be tight if delayed',
|
||
routePath: [[33.0812, -96.7234], [33.0778, -96.7423], [33.0744, -96.7612]],
|
||
},
|
||
{
|
||
repId: 'REP-01', score: 68, slotStart: '5:00 PM', slotEnd: '5:45 PM',
|
||
distanceMi: '5.5',
|
||
factors: { proximity: 62, driveTime: 58, calendarFit: 70, weather: 68, skillMatch: 80 },
|
||
reasons: ['End of day slot only', '22 min drive', 'High performer — score 94'],
|
||
stormReasons: ['Evening — conditions improving but daylight limited'],
|
||
aiInsight: 'High performer but 5 PM slot limits daylight for roof work — use only as last resort',
|
||
conflictFlag: 'Daylight limited at 5 PM — roof inspection may be incomplete',
|
||
routePath: [[33.0612, -96.7891], [33.0678, -96.7752], [33.0744, -96.7612]],
|
||
},
|
||
],
|
||
'LD-1009': [
|
||
{
|
||
repId: 'REP-05', score: 97, slotStart: '11:00 AM', slotEnd: '12:30 PM',
|
||
distanceMi: '0.8',
|
||
factors: { proximity: 99, driveTime: 98, calendarFit: 95, weather: 95, skillMatch: 90 },
|
||
reasons: ['0.8 mi away — nearest rep on site', '5 min drive time', 'Emergency certified', 'PRIORITY: emergency tarp needed'],
|
||
stormReasons: ['EMERGENCY — dispatch immediately', 'Rep stocked with emergency tarp materials'],
|
||
aiInsight: 'PRIORITY dispatch — nearest certified rep with emergency materials on hand, 5 min ETA',
|
||
conflictFlag: null,
|
||
routePath: [[33.0198, -96.7234], [33.0198, -96.7112], [33.0198, -96.6989]],
|
||
},
|
||
{
|
||
repId: 'REP-03', score: 71, slotStart: '1:00 PM', slotEnd: '2:30 PM',
|
||
distanceMi: '4.6',
|
||
factors: { proximity: 65, driveTime: 62, calendarFit: 75, weather: 72, skillMatch: 75 },
|
||
reasons: ['Backup option — 5 appointments today', '21 min drive', 'Roof inspection capable'],
|
||
stormReasons: ['Secondary option — use only if REP-05 unavailable'],
|
||
aiInsight: 'Emergency backup only — heavier daily load and longer drive make this a secondary option',
|
||
conflictFlag: '5 appointments today — minimal buffer if any prior job overruns',
|
||
routePath: [[33.0389, -96.7401], [33.0294, -96.7195], [33.0198, -96.6989]],
|
||
},
|
||
],
|
||
'LD-1010': [
|
||
{
|
||
repId: 'REP-01', score: 84, slotStart: '2:30 PM', slotEnd: '3:00 PM',
|
||
distanceMi: '2.9',
|
||
factors: { proximity: 82, driveTime: 80, calendarFit: 88, weather: 84, skillMatch: 80 },
|
||
reasons: ['Best proximity match — 2.9 mi', '16 min drive', 'High performer — score 94', 'Manageable daily load'],
|
||
stormReasons: ['Afternoon slot — monitor weather', 'Retail estimate can proceed partially indoors'],
|
||
aiInsight: 'Top performer with manageable load — retail estimate can proceed indoors if afternoon weather shifts',
|
||
conflictFlag: null,
|
||
routePath: [[33.0612, -96.7891], [33.0750, -96.7824], [33.0888, -96.7756]],
|
||
},
|
||
{
|
||
repId: 'REP-02', score: 72, slotStart: '4:00 PM', slotEnd: '4:30 PM',
|
||
distanceMi: '4.8',
|
||
factors: { proximity: 70, driveTime: 68, calendarFit: 78, weather: 70, skillMatch: 80 },
|
||
reasons: ['Available late afternoon', '22 min drive', 'Roof inspection certified'],
|
||
stormReasons: ['4 PM slot — weather conditions deteriorating, not ideal'],
|
||
aiInsight: 'Viable backup but 4 PM timing and weather trend reduce confidence for exterior estimate',
|
||
conflictFlag: '4 PM slot — low daylight and deteriorating conditions for exterior work',
|
||
routePath: [[33.0812, -96.7234], [33.0850, -96.7495], [33.0888, -96.7756]],
|
||
},
|
||
],
|
||
'LD-1011': [
|
||
{
|
||
repId: 'REP-05', score: 89, slotStart: '12:30 PM', slotEnd: '1:30 PM',
|
||
distanceMi: '1.6',
|
||
factors: { proximity: 90, driveTime: 88, calendarFit: 85, weather: 88, skillMatch: 90 },
|
||
reasons: ['Closest rep — 1.6 mi', '9 min drive time', 'Insurance claim proficient', 'Midday slot timing is optimal'],
|
||
stormReasons: ['Midday window — acceptable conditions', 'Insurance claim can be initiated outdoors then moved inside'],
|
||
aiInsight: 'Close proximity with optimal midday timing — insurance initiation can flex indoors if needed',
|
||
conflictFlag: null,
|
||
routePath: [[33.0198, -96.7234], [33.0233, -96.7168], [33.0267, -96.7102]],
|
||
},
|
||
{
|
||
repId: 'REP-03', score: 76, slotStart: '2:00 PM', slotEnd: '3:00 PM',
|
||
distanceMi: '3.5',
|
||
factors: { proximity: 74, driveTime: 70, calendarFit: 78, weather: 76, skillMatch: 80 },
|
||
reasons: ['Available at 2 PM', '18 min drive', 'Retail estimate skilled'],
|
||
stormReasons: ['Afternoon slot — conditions marginal after 2:30 PM'],
|
||
aiInsight: 'Partial skill match with afternoon availability — conditions deteriorate mid-appointment window',
|
||
conflictFlag: 'Skill alignment moderate — retail primary, insurance secondary only',
|
||
routePath: [[33.0389, -96.7401], [33.0328, -96.7252], [33.0267, -96.7102]],
|
||
},
|
||
],
|
||
'LD-1012': [
|
||
{
|
||
repId: 'REP-01', score: 92, slotStart: '10:00 AM', slotEnd: '10:45 AM',
|
||
distanceMi: '1.7',
|
||
factors: { proximity: 94, driveTime: 92, calendarFit: 88, weather: 92, skillMatch: 95 },
|
||
reasons: ['Currently assigned — optimal', '1.7 mi from current position', 'Morning slot — ideal weather window', 'Insurance + retail estimate certified'],
|
||
stormReasons: ['Morning slot — safe window before any afternoon weather'],
|
||
aiInsight: 'Morning assignment with ideal conditions — dual-certified rep maximizes flexibility for this job type',
|
||
conflictFlag: null,
|
||
routePath: [[33.0612, -96.7891], [33.0612, -96.8018], [33.0612, -96.8145]],
|
||
},
|
||
{
|
||
repId: 'REP-04', score: 70, slotStart: '1:00 PM', slotEnd: '1:45 PM',
|
||
distanceMi: '5.3',
|
||
factors: { proximity: 65, driveTime: 62, calendarFit: 82, weather: 70, skillMatch: 85 },
|
||
reasons: ['Afternoon availability', '24 min drive from current location', 'Roof inspection certified'],
|
||
stormReasons: ['Afternoon — conditions acceptable but watch post-2 PM'],
|
||
aiInsight: 'Longer drive reduces score significantly — afternoon window is viable if morning rep unavailable',
|
||
conflictFlag: '24 min drive cuts into slot buffer — delays cascade to next appointment',
|
||
routePath: [[33.0516, -96.7512], [33.0564, -96.7829], [33.0612, -96.8145]],
|
||
},
|
||
],
|
||
};
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Weekly performance data — Mon Mar 20 → Sun Mar 26 (current week)
|
||
// repBreakdown keys match DISPATCH_REPS ids
|
||
// ---------------------------------------------------------------------------
|
||
export const DISPATCH_WEEKLY_STATS = [
|
||
{
|
||
day: 'Mon', date: 'Mar 20', dispatched: 7, avgWait: 18, emergency: 0,
|
||
stormDay: false, isToday: false,
|
||
repBreakdown: { 'REP-01': 2, 'REP-02': 2, 'REP-03': 1, 'REP-04': 1, 'REP-05': 1 },
|
||
},
|
||
{
|
||
day: 'Tue', date: 'Mar 21', dispatched: 9, avgWait: 14, emergency: 1,
|
||
stormDay: false, isToday: false,
|
||
repBreakdown: { 'REP-01': 2, 'REP-02': 2, 'REP-03': 2, 'REP-04': 2, 'REP-05': 1 },
|
||
},
|
||
{
|
||
day: 'Wed', date: 'Mar 22', dispatched: 6, avgWait: 22, emergency: 0,
|
||
stormDay: false, isToday: false,
|
||
repBreakdown: { 'REP-01': 1, 'REP-02': 2, 'REP-03': 1, 'REP-04': 1, 'REP-05': 1 },
|
||
},
|
||
{
|
||
day: 'Thu', date: 'Mar 23', dispatched: 14, avgWait: 9, emergency: 4,
|
||
stormDay: true, isToday: false,
|
||
repBreakdown: { 'REP-01': 3, 'REP-02': 3, 'REP-03': 3, 'REP-04': 3, 'REP-05': 2 },
|
||
},
|
||
{
|
||
day: 'Fri', date: 'Mar 24', dispatched: 10, avgWait: 13, emergency: 2,
|
||
stormDay: false, isToday: false,
|
||
repBreakdown: { 'REP-01': 2, 'REP-02': 2, 'REP-03': 2, 'REP-04': 2, 'REP-05': 2 },
|
||
},
|
||
{
|
||
day: 'Sat', date: 'Mar 25', dispatched: 5, avgWait: 20, emergency: 0,
|
||
stormDay: false, isToday: false,
|
||
repBreakdown: { 'REP-01': 1, 'REP-02': 1, 'REP-03': 1, 'REP-04': 1, 'REP-05': 1 },
|
||
},
|
||
{
|
||
day: 'Sun', date: 'Mar 26', dispatched: 3, avgWait: 24, emergency: 0,
|
||
stormDay: false, isToday: true,
|
||
repBreakdown: { 'REP-01': 1, 'REP-02': 1, 'REP-03': 0, 'REP-04': 1, 'REP-05': 0 },
|
||
},
|
||
];
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// MASTER TEMPLATES
|
||
// ---------------------------------------------------------------------------
|
||
|
||
export const TEMPLATE_CATEGORIES = ['Roofing', 'Siding', 'Gutters', 'Windows', 'Insulation', 'Other'];
|
||
|
||
const MASTER_TEMPLATES_INITIAL = [
|
||
{
|
||
id: 'atlas_glassmaster',
|
||
name: 'Atlas GlassMaster',
|
||
category: 'Roofing',
|
||
brand: 'Atlas',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood. If bad or rotten wood is discovered, it will be replaced at a price of $50 per sheet.",
|
||
"Install 3' of Atlas WeatherMaster ice and water shield at all gutter lines, rakes edges, and valleys.",
|
||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||
"Install Atlas Glassmaster AR Shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'Atlas Glassmaster AR', baseUnitCost: 37.67, uom: 'SQ' },
|
||
{ desc: 'Atlas Pro-Cut Hip & Ridge', baseUnitCost: 78.50, uom: 'BD' },
|
||
{ desc: 'Atlas 6X Pro-Cut Starter', baseUnitCost: 70.80, uom: 'BD' },
|
||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'atlas_glassmaster_srs',
|
||
name: 'Atlas GlassMaster - SRS',
|
||
category: 'Roofing',
|
||
brand: 'SRS',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install 3' of Atlas WeatherMaster ice and water shield at all gutter lines, rakes edges, and valleys.",
|
||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||
"Install Atlas Glassmaster AR Shingles (SRS distribution)."
|
||
],
|
||
materials: [
|
||
{ desc: 'Atlas Glassmaster AR', baseUnitCost: 37.67, uom: 'SQ' },
|
||
{ desc: 'Atlas Pro-Cut Hip & Ridge', baseUnitCost: 78.50, uom: 'BD' },
|
||
{ desc: 'Atlas 6X Pro-Cut Starter', baseUnitCost: 70.80, uom: 'BD' },
|
||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'atlas_pinnacle_pristine',
|
||
name: 'Atlas Pinnacle Pristine',
|
||
category: 'Roofing',
|
||
brand: 'Atlas',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install 3' of Atlas WeatherMaster ice and water shield.",
|
||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||
"Install Atlas Pinnacle Pristine Lifetime Dimensional Shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'Atlas Pinnacle Pristine', baseUnitCost: 42.50, uom: 'SQ' },
|
||
{ desc: 'Atlas Pro-Cut High Profile Hip & Ridge', baseUnitCost: 85.00, uom: 'BD' },
|
||
{ desc: 'Atlas Pro-Cut Starter', baseUnitCost: 72.00, uom: 'BD' },
|
||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'atlas_stormmaster_shake',
|
||
name: 'Atlas StormMaster Shake',
|
||
category: 'Roofing',
|
||
brand: 'Atlas',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install 3' of Atlas WeatherMaster ice and water shield at all eaves and valleys.",
|
||
"Install Atlas Summit 60 Synthetic underlayment.",
|
||
"Install Atlas StormMaster Shake Impact Resistant Shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'Atlas StormMaster Shake', baseUnitCost: 55.00, uom: 'SQ' },
|
||
{ desc: 'Atlas Pro-Cut Hip & Ridge', baseUnitCost: 78.50, uom: 'BD' },
|
||
{ desc: 'Atlas 6X Pro-Cut Starter', baseUnitCost: 70.80, uom: 'BD' },
|
||
{ desc: 'Atlas Summit 60 Synthetic Underlayment', baseUnitCost: 116.00, uom: 'RL' },
|
||
{ desc: 'Atlas WeatherMaster Ice & Water', baseUnitCost: 65.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'certainteed_landmark',
|
||
name: 'CertainTeed Landmark AR',
|
||
category: 'Roofing',
|
||
brand: 'CertainTeed',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install CertainTeed WinterGuard ice and water shield.",
|
||
"Install CertainTeed RoofRunner Synthetic underlayment.",
|
||
"Install CertainTeed Landmark AR Shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'CertainTeed Landmark AR', baseUnitCost: 44.00, uom: 'SQ' },
|
||
{ desc: 'CertainTeed ShadowRidge', baseUnitCost: 82.00, uom: 'BD' },
|
||
{ desc: 'CertainTeed SwiftStart', baseUnitCost: 75.00, uom: 'BD' },
|
||
{ desc: 'CertainTeed RoofRunner', baseUnitCost: 125.00, uom: 'RL' },
|
||
{ desc: 'CertainTeed WinterGuard', baseUnitCost: 70.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'certainteed_presidential_shake',
|
||
name: 'CertainTeed Presidential Shake TL',
|
||
category: 'Roofing',
|
||
brand: 'CertainTeed',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install CertainTeed WinterGuard ice and water shield at all eaves and valleys.",
|
||
"Install CertainTeed RoofRunner Synthetic underlayment.",
|
||
"Install CertainTeed Presidential Shake TL triple-layer shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'CertainTeed Presidential Shake TL', baseUnitCost: 72.00, uom: 'SQ' },
|
||
{ desc: 'CertainTeed Presidential Ridge', baseUnitCost: 92.00, uom: 'BD' },
|
||
{ desc: 'CertainTeed SwiftStart', baseUnitCost: 75.00, uom: 'BD' },
|
||
{ desc: 'CertainTeed RoofRunner', baseUnitCost: 125.00, uom: 'RL' },
|
||
{ desc: 'CertainTeed WinterGuard', baseUnitCost: 70.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'gaf_timberline_hdz',
|
||
name: 'GAF Timberline HDZ',
|
||
category: 'Roofing',
|
||
brand: 'GAF',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install GAF WeatherWatch ice and water shield at eaves, rakes, and valleys.",
|
||
"Install GAF FeltBuster Synthetic underlayment.",
|
||
"Install GAF Timberline HDZ Laminated Architectural Shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'GAF Timberline HDZ', baseUnitCost: 45.00, uom: 'SQ' },
|
||
{ desc: 'GAF TimberTex Premium Ridge Cap', baseUnitCost: 85.00, uom: 'BD' },
|
||
{ desc: 'GAF Pro-Start Starter Strip', baseUnitCost: 68.00, uom: 'BD' },
|
||
{ desc: 'GAF FeltBuster Synthetic Underlayment', baseUnitCost: 110.00, uom: 'RL' },
|
||
{ desc: 'GAF WeatherWatch Ice & Water', baseUnitCost: 68.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'gaf_timberline_ultra_hd',
|
||
name: 'GAF Timberline Ultra HD',
|
||
category: 'Roofing',
|
||
brand: 'GAF',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install GAF WeatherWatch ice and water shield at eaves, rakes, and valleys.",
|
||
"Install GAF FeltBuster Synthetic underlayment.",
|
||
"Install GAF Timberline Ultra HD extra-thick dimensional shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'GAF Timberline Ultra HD', baseUnitCost: 52.00, uom: 'SQ' },
|
||
{ desc: 'GAF TimberTex Premium Ridge Cap', baseUnitCost: 85.00, uom: 'BD' },
|
||
{ desc: 'GAF Pro-Start Starter Strip', baseUnitCost: 68.00, uom: 'BD' },
|
||
{ desc: 'GAF FeltBuster Synthetic Underlayment', baseUnitCost: 110.00, uom: 'RL' },
|
||
{ desc: 'GAF WeatherWatch Ice & Water', baseUnitCost: 68.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'iko_dynasty',
|
||
name: 'IKO Dynasty',
|
||
category: 'Roofing',
|
||
brand: 'IKO',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install IKO ArmourGard ice and water shield at eaves, rakes, and valleys.",
|
||
"Install IKO RoofGard-SB Synthetic underlayment.",
|
||
"Install IKO Dynasty Impact Resistant shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'IKO Dynasty', baseUnitCost: 54.00, uom: 'SQ' },
|
||
{ desc: 'IKO Hip & Ridge 12', baseUnitCost: 80.00, uom: 'BD' },
|
||
{ desc: 'IKO Leading Edge Plus Starter Strip', baseUnitCost: 71.00, uom: 'BD' },
|
||
{ desc: 'IKO RoofGard-SB Synthetic Underlayment', baseUnitCost: 118.00, uom: 'RL' },
|
||
{ desc: 'IKO ArmourGard Ice & Water', baseUnitCost: 67.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'malarkey_vista_ar',
|
||
name: 'Malarkey Vista AR',
|
||
category: 'Roofing',
|
||
brand: 'Malarkey',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Re-nail any loose wood.",
|
||
"Install Malarkey EverGuard ice and water shield at eaves and valleys.",
|
||
"Install Malarkey UDL 40 Synthetic underlayment.",
|
||
"Install Malarkey Vista AR Architectural Shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'Malarkey Vista AR', baseUnitCost: 46.00, uom: 'SQ' },
|
||
{ desc: 'Malarkey Ridglass Hip & Ridge', baseUnitCost: 83.00, uom: 'BD' },
|
||
{ desc: 'Malarkey Windsor Starter Strip', baseUnitCost: 73.00, uom: 'BD' },
|
||
{ desc: 'Malarkey UDL 40 Synthetic Underlayment', baseUnitCost: 122.00, uom: 'RL' },
|
||
{ desc: 'Malarkey EverGuard Ice & Water', baseUnitCost: 69.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'owens_corning_duration',
|
||
name: 'Owens Corning Duration',
|
||
category: 'Roofing',
|
||
brand: 'Owens Corning',
|
||
description: [
|
||
"Remove existing shingles down to deck.",
|
||
"Inspect and re-nail decking.",
|
||
"Install Owens Corning WeatherLock G ice and water shield.",
|
||
"Install Owens Corning ProArmor Synthetic underlayment.",
|
||
"Install Owens Corning TruDefinition Duration Shingles."
|
||
],
|
||
materials: [
|
||
{ desc: 'OC TruDefinition Duration', baseUnitCost: 46.50, uom: 'SQ' },
|
||
{ desc: 'OC ProEdge Hip & Ridge', baseUnitCost: 88.00, uom: 'BD' },
|
||
{ desc: 'OC Starter Strip Plus', baseUnitCost: 78.00, uom: 'BD' },
|
||
{ desc: 'OC ProArmor Underlayment', baseUnitCost: 130.00, uom: 'RL' },
|
||
{ desc: 'OC WeatherLock G', baseUnitCost: 75.00, uom: 'RL' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Plastic Cap Nails', baseUnitCost: 28.50, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
{
|
||
id: 'mulehide_epdm',
|
||
name: 'Mulehide .060 EPDM System',
|
||
category: 'Roofing',
|
||
brand: 'Mulehide',
|
||
description: [
|
||
"Remove existing flat roof membrane down to deck.",
|
||
"Inspect and repair decking as needed.",
|
||
"Install Mulehide .060 EPDM membrane fully adhered system.",
|
||
"Install Mulehide bonding adhesive.",
|
||
"Install drip edge and termination bars at perimeter."
|
||
],
|
||
materials: [
|
||
{ desc: 'Mulehide .060 EPDM Membrane', baseUnitCost: 85.00, uom: 'SQ' },
|
||
{ desc: 'Mulehide Bonding Adhesive', baseUnitCost: 48.00, uom: 'EA' },
|
||
{ desc: 'Mulehide EPDM Lap Sealant', baseUnitCost: 12.00, uom: 'EA' },
|
||
{ desc: 'Mulehide Termination Bar', baseUnitCost: 4.50, uom: 'LF' },
|
||
{ desc: 'Coil Nails 1 1/4"', baseUnitCost: 35.00, uom: 'BX' },
|
||
{ desc: 'Geocel 2300 Clear Sealant', baseUnitCost: 8.95, uom: 'EA' }
|
||
]
|
||
},
|
||
];
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// MOCK ESTIMATES
|
||
// ---------------------------------------------------------------------------
|
||
|
||
const MOCK_ESTIMATES_INITIAL = [
|
||
{
|
||
id: 'est_001',
|
||
clientName: 'Robert Chambers',
|
||
address: '2612 Dunwick Dr, Plano, TX 75023',
|
||
date: '2026-03-28',
|
||
status: 'Approved',
|
||
total: 18450.00,
|
||
templateId: 'certainteed_landmark',
|
||
templateName: 'CertainTeed Landmark AR',
|
||
estimatedBy: 'Marcus Webb',
|
||
roofArea: 28.4,
|
||
notes: 'Hail damage from March 15 storm. Insurance claim approved.',
|
||
},
|
||
{
|
||
id: 'est_002',
|
||
clientName: 'Sandra Kim',
|
||
address: '6613 Phoenix Pl, Plano, TX 75023',
|
||
date: '2026-03-25',
|
||
status: 'Sent',
|
||
total: 22100.00,
|
||
templateId: 'gaf_timberline_hdz',
|
||
templateName: 'GAF Timberline HDZ',
|
||
estimatedBy: 'Kenji Flores',
|
||
roofArea: 32.1,
|
||
notes: 'Full replacement. Customer wants upgraded ridge cap.',
|
||
},
|
||
{
|
||
id: 'est_003',
|
||
clientName: 'David Okonkwo',
|
||
address: '3913 Arizona Pl, Plano, TX 75023',
|
||
date: '2026-03-22',
|
||
status: 'Draft',
|
||
total: 14800.00,
|
||
templateId: 'atlas_glassmaster',
|
||
templateName: 'Atlas GlassMaster',
|
||
estimatedBy: 'Marcus Webb',
|
||
roofArea: 24.6,
|
||
notes: null,
|
||
},
|
||
{
|
||
id: 'est_004',
|
||
clientName: 'Patricia Hollis',
|
||
address: '3905 Sailmaker Ln, Plano, TX 75023',
|
||
date: '2026-03-18',
|
||
status: 'Approved',
|
||
total: 31200.00,
|
||
templateId: 'certainteed_presidential_shake',
|
||
templateName: 'CertainTeed Presidential Shake TL',
|
||
estimatedBy: 'Aisha Patel',
|
||
roofArea: 38.7,
|
||
notes: 'Premium upgrade. Customer requested Presidential Shake for curb appeal.',
|
||
},
|
||
{
|
||
id: 'est_005',
|
||
clientName: 'James Thornton',
|
||
address: '6909 Custer Rd, Plano, TX 75023',
|
||
date: '2026-03-14',
|
||
status: 'Rejected',
|
||
total: 28750.00,
|
||
templateId: 'gaf_timberline_ultra_hd',
|
||
templateName: 'GAF Timberline Ultra HD',
|
||
estimatedBy: 'Kenji Flores',
|
||
roofArea: 35.2,
|
||
notes: 'Customer rejected — going with competitor. Follow up in 30 days.',
|
||
},
|
||
{
|
||
id: 'est_006',
|
||
clientName: 'Maria Gonzalez',
|
||
address: '2608 Dunwick Dr, Plano, TX 75023',
|
||
date: '2026-03-10',
|
||
status: 'Approved',
|
||
total: 16900.00,
|
||
templateId: 'atlas_stormmaster_shake',
|
||
templateName: 'Atlas StormMaster Shake',
|
||
estimatedBy: 'Marcus Webb',
|
||
roofArea: 26.8,
|
||
notes: 'Impact resistant upgrade. HOA approved color: Weathered Wood.',
|
||
},
|
||
{
|
||
id: 'est_007',
|
||
clientName: 'Thomas Reeves',
|
||
address: '7224 Independence Pkwy, Plano, TX 75025',
|
||
date: '2026-03-05',
|
||
status: 'Sent',
|
||
total: 54200.00,
|
||
templateId: 'mulehide_epdm',
|
||
templateName: 'Mulehide .060 EPDM System',
|
||
estimatedBy: 'Aisha Patel',
|
||
roofArea: 62.0,
|
||
notes: 'Commercial flat roof. 3 HVAC curb flashings included.',
|
||
},
|
||
{
|
||
id: 'est_008',
|
||
clientName: 'Denise Watkins',
|
||
address: '3901 Sailmaker Ln, Plano, TX 75023',
|
||
date: '2026-02-28',
|
||
status: 'Expired',
|
||
total: 19350.00,
|
||
templateId: 'iko_dynasty',
|
||
templateName: 'IKO Dynasty',
|
||
estimatedBy: 'Kenji Flores',
|
||
roofArea: 29.1,
|
||
notes: 'Estimate expired — no response after 3 follow-ups. Reactivate on request.',
|
||
},
|
||
{
|
||
id: 'est_009',
|
||
clientName: 'Calvin Monroe',
|
||
address: '2100 Legacy Dr, Plano, TX 75023',
|
||
date: '2026-02-20',
|
||
status: 'Approved',
|
||
total: 11600.00,
|
||
templateId: 'owens_corning_duration',
|
||
templateName: 'Owens Corning Duration',
|
||
estimatedBy: 'Marcus Webb',
|
||
roofArea: 18.9,
|
||
notes: 'Apartment unit — common area roof section only.',
|
||
},
|
||
{
|
||
id: 'est_010',
|
||
clientName: 'Brenda Castillo',
|
||
address: '6601 Phoenix Pl, Plano, TX 75023',
|
||
date: '2026-02-14',
|
||
status: 'Draft',
|
||
total: 17250.00,
|
||
templateId: 'malarkey_vista_ar',
|
||
templateName: 'Malarkey Vista AR',
|
||
estimatedBy: 'Aisha Patel',
|
||
roofArea: 27.3,
|
||
notes: null,
|
||
},
|
||
];
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// KANBAN BOARD DATA
|
||
// ---------------------------------------------------------------------------
|
||
|
||
const KANBAN_COLUMNS_INITIAL = [
|
||
{ id: 'kc_new', type: 'stage', name: 'New Lead', order: 0, color: '#3B82F6', campaignMessage: "Hi {name}, thanks for reaching out to Plano Realty! We'll be in touch shortly to schedule your free roof inspection. — Plano Roofing Team" },
|
||
{ id: 'kc_contacted',type: 'stage', name: 'Contacted', order: 1, color: '#6366F1', campaignMessage: "Hi {name}, great connecting with you today! I'll send over some info and follow up with next steps this week. — Plano Roofing Team" },
|
||
{ id: 'kc_appt', type: 'stage', name: 'Appt Scheduled', order: 2, color: '#8B5CF6', campaignMessage: "Hi {name}, your inspection appointment is confirmed! We look forward to meeting you and assessing your roof. — Plano Roofing Team" },
|
||
{ id: 'kc_estimate', type: 'stage', name: 'Estimate Sent', order: 3, color: '#F59E0B', campaignMessage: "Hi {name}, your detailed estimate has been sent! Please review at your convenience and reach out with any questions. — Plano Roofing Team" },
|
||
{ id: 'kc_signed', type: 'stage', name: 'Signed', order: 4, color: '#10B981', campaignMessage: "Hi {name}, welcome to the Plano Roofing family! Your contract is signed and we're excited to get your project underway. — Plano Roofing Team" },
|
||
{ id: 'kc_progress', type: 'stage', name: 'In Progress', order: 5, color: '#06B6D4', campaignMessage: "Hi {name}, just a quick update — your roofing project is now actively in progress! We'll keep you posted on key milestones. — Plano Roofing Team" },
|
||
{ id: 'kc_complete', type: 'stage', name: 'Complete', order: 6, color: '#22C55E', campaignMessage: "Hi {name}, your project is complete! We hope you love the results. A 5-star review would mean the world to us. — Plano Roofing Team" },
|
||
{ id: 'kc_stuck', type: 'bucket', name: 'Stuck', order: 7, color: '#EF4444', campaignMessage: null },
|
||
{ id: 'kc_followup', type: 'bucket', name: 'Follow-up', order: 8, color: '#F97316', campaignMessage: null },
|
||
];
|
||
|
||
const KANBAN_LEADS_INITIAL = [
|
||
// ── NEW LEAD (6 leads) ──────────────────────────────────────────────────
|
||
{
|
||
id: 'kl_001', name: 'Derek Holloway', address: '2814 Ravenswood Dr, Plano TX 75023',
|
||
phone: '(972) 555-0101', email: 'derek.holloway@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_new', bucketId: null,
|
||
notes: 'Hail damage from March storm. Insurance adjuster coming next week.',
|
||
createdDate: '2026-03-31',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-03-31', note: 'Lead created via door knock — visible hail damage on ridge line.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_002', name: 'Brenda Castillo', address: '5501 Shady Brook Ln, Plano TX 75093',
|
||
phone: '(972) 555-0102', email: 'bcastillo@yahoo.com',
|
||
jobType: 'Roof Inspection', insuranceType: 'Retail',
|
||
assignedAgentId: null, assignedAgentName: null,
|
||
columnId: 'kc_new', bucketId: null,
|
||
notes: 'Walked in via website form. No agent assigned yet.',
|
||
createdDate: '2026-04-01',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'System', date: '2026-04-01', note: 'Lead submitted via website form — Google Ads campaign.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_003', name: 'Antonio Reyes', address: '1122 Custer Rd, Plano TX 75075',
|
||
phone: '(972) 555-0103', email: 'a.reyes@outlook.com',
|
||
jobType: 'Gutter Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_new', bucketId: null,
|
||
notes: 'Referred by neighbor on Oak Creek Blvd. Wants full gutter system.',
|
||
createdDate: '2026-04-02',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-04-02', note: 'Referral from existing client Tom Hargrove.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_004', name: 'Sylvia Nguyen', address: '3308 Roundrock Trl, Plano TX 75023',
|
||
phone: '(972) 555-0104', email: 'sylvia.nguyen@icloud.com',
|
||
jobType: 'Siding Repair', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_new', bucketId: null,
|
||
notes: 'Storm damage to siding on north face. Photos sent.',
|
||
createdDate: '2026-04-03',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-04-03', note: 'Canvassing lead — storm damage clearly visible.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_005', name: 'Raymond Osei', address: '4720 Preston Rd, Plano TX 75024',
|
||
phone: '(972) 555-0105', email: 'rosei@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: null, assignedAgentName: null,
|
||
columnId: 'kc_new', bucketId: null,
|
||
notes: 'Called in from radio ad. Wants urgent inspection.',
|
||
createdDate: '2026-04-04',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'System', date: '2026-04-04', note: 'Inbound call — radio ad source.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_006', name: 'Carolyn Estrada', address: '6013 Ohio Dr, Plano TX 75093',
|
||
phone: '(972) 555-0106', email: 'carolyn.e@gmail.com',
|
||
jobType: 'Window Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e5', assignedAgentName: 'Felix Fixer',
|
||
columnId: 'kc_new', bucketId: null,
|
||
notes: 'Wants 4 windows replaced. Budget is flexible.',
|
||
createdDate: '2026-04-05',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felix Fixer', date: '2026-04-05', note: 'Door knock — homeowner interested in window upgrade.' },
|
||
],
|
||
},
|
||
|
||
// ── CONTACTED (5 leads) ─────────────────────────────────────────────────
|
||
{
|
||
id: 'kl_007', name: 'Marcus Tillman', address: '2201 Willow Bend Dr, Plano TX 75093',
|
||
phone: '(972) 555-0107', email: 'mtillman@hotmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_contacted', bucketId: null,
|
||
notes: 'Spoke on the phone. Wants appointment next Tuesday.',
|
||
createdDate: '2026-03-25',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-03-25', note: 'Canvassing — hail dents visible on ridge cap.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-03-27', note: 'Spoke by phone, confirmed homeowner interest. Appointment TBD.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_008', name: 'Diane Kowalski', address: '815 Independence Pkwy, Plano TX 75023',
|
||
phone: '(972) 555-0108', email: 'diane.k@gmail.com',
|
||
jobType: 'Roof Inspection', insuranceType: 'Retail',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_contacted', bucketId: null,
|
||
notes: 'Older roof, no storm damage. Wants inspection for peace of mind.',
|
||
createdDate: '2026-03-26',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-03-26', note: 'Website form submission.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-03-28', note: 'Left voicemail and sent email. Customer responded via email.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_009', name: 'Joel Fernandez', address: '3901 Legacy Dr, Plano TX 75023',
|
||
phone: '(972) 555-0109', email: 'joel.f@yahoo.com',
|
||
jobType: 'Siding Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_contacted', bucketId: null,
|
||
notes: 'Insurance claim in process. Wants estimate ASAP.',
|
||
createdDate: '2026-03-24',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-03-24', note: 'Door knock — wind damage to siding panels.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-03-26', note: 'Phone call completed. Adjuster visit scheduled for Mar 30.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_010', name: 'Patricia Yuen', address: '7742 Kings Rd, Plano TX 75024',
|
||
phone: '(972) 555-0110', email: 'pyuen@outlook.com',
|
||
jobType: 'Gutter Repair', insuranceType: 'Retail',
|
||
assignedAgentId: 'e3', assignedAgentName: 'Fred Flyer',
|
||
columnId: 'kc_contacted', bucketId: null,
|
||
notes: 'Gutters pulling away from fascia. Straightforward repair job.',
|
||
createdDate: '2026-03-27',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fred Flyer', date: '2026-03-27', note: 'Referral from neighborhood Facebook group.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fred Flyer', date: '2026-03-29', note: 'Quick call — confirmed issue and availability. Working on appointment.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_011', name: 'Gerald Obi', address: '5220 Midway Rd, Plano TX 75093',
|
||
phone: '(972) 555-0111', email: 'gerald.obi@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_contacted', bucketId: null,
|
||
notes: 'Large home — 3,200 sq ft. Multiple storm claims history.',
|
||
createdDate: '2026-03-22',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-03-22', note: 'Canvassing — prior storm claim visible in public records.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-03-25', note: 'Met in person at door. Very interested — asked for references.' },
|
||
],
|
||
},
|
||
|
||
// ── APPT SCHEDULED (6 leads) ────────────────────────────────────────────
|
||
{
|
||
id: 'kl_012', name: 'Vanessa Obrien', address: '1450 Park Blvd, Plano TX 75074',
|
||
phone: '(972) 555-0112', email: 'vanessa.ob@icloud.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_appt', bucketId: null,
|
||
notes: 'Appointment Apr 9 at 10am. Adjuster already completed visit.',
|
||
createdDate: '2026-03-18',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-03-18', note: 'Storm canvassing — visible hail damage.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-03-20', note: 'Phone call — very interested, adjuster already came out.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-03-22', note: 'Appointment confirmed for Apr 9 at 10am.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_013', name: 'Thomas Greer', address: '892 Mapleshade Ln, Plano TX 75075',
|
||
phone: '(972) 555-0113', email: 'thomas.g@yahoo.com',
|
||
jobType: 'Roof Inspection', insuranceType: 'Retail',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_appt', bucketId: null,
|
||
notes: 'Appointment Apr 8 at 2pm. Retired homeowner, very detail-oriented.',
|
||
createdDate: '2026-03-20',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-03-20', note: 'Referral from existing client.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-03-22', note: 'Email exchange — wants full report after inspection.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-03-25', note: 'Confirmed Apr 8 at 2pm via email.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_014', name: 'Lashonda Webb', address: '3015 Plano Pkwy, Plano TX 75074',
|
||
phone: '(972) 555-0114', email: 'lashonda.w@gmail.com',
|
||
jobType: 'Siding + Gutters', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_appt', bucketId: null,
|
||
notes: 'Combo job — siding and gutters. Apr 10 morning slot.',
|
||
createdDate: '2026-03-15',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-03-15', note: 'Door knock — wind damage to siding and gutters overflowing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-03-17', note: 'Called and texted. Customer confirmed she filed insurance.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-03-21', note: 'Apr 10 morning slot — customer will have spouse present.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_015', name: 'Winston Park', address: '6601 Ohio Dr, Plano TX 75093',
|
||
phone: '(972) 555-0115', email: 'wpark@outlook.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e5', assignedAgentName: 'Felix Fixer',
|
||
columnId: 'kc_appt', bucketId: null,
|
||
notes: 'Large flat-roof section at the back. Appointment Apr 11.',
|
||
createdDate: '2026-03-19',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felix Fixer', date: '2026-03-19', note: 'Website lead — searched storm damage repair.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felix Fixer', date: '2026-03-21', note: 'Spoke on phone. Mentioned flat roof section in addition to pitched.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felix Fixer', date: '2026-03-24', note: 'Confirmed Apr 11 at 9am. Will need extra time for flat section.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_016', name: 'Ingrid Solis', address: '2244 Aster Ct, Plano TX 75025',
|
||
phone: '(972) 555-0116', email: 'ingrid.s@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_appt', bucketId: null,
|
||
notes: 'Cash purchase — no insurance. Appointment Apr 12.',
|
||
createdDate: '2026-03-23',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-03-23', note: 'Referral. Cash buyer — no insurance involved.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-03-25', note: 'Spoke briefly. She is very decisive — ready to move fast.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-03-27', note: 'Apr 12 at 11am confirmed.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_017', name: 'Darnell Brooks', address: '4408 Springbrook Dr, Plano TX 75024',
|
||
phone: '(972) 555-0117', email: 'd.brooks@yahoo.com',
|
||
jobType: 'Chimney + Roof Repair', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_appt', bucketId: null,
|
||
notes: 'Chimney flashing leaking badly. Appointment rescheduled twice — Apr 14 final.',
|
||
createdDate: '2026-03-10',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-03-10', note: 'Inbound call — chimney leak causing interior ceiling damage.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-03-12', note: 'Full conversation — insurance claim open. Adjuster not yet assigned.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-03-14', note: 'Apr 1 appointment set.' },
|
||
{ from: 'Appt Scheduled', to: 'Contacted', by: 'Fiona Field', date: '2026-03-30', note: 'Customer rescheduled — work conflict. Moved back to Contacted.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-04-01', note: 'Rescheduled again — Apr 14 this time. Confirmed via text.' },
|
||
],
|
||
},
|
||
|
||
// ── ESTIMATE SENT (5 leads) ─────────────────────────────────────────────
|
||
{
|
||
id: 'kl_018', name: 'Gloria Hutchins', address: '1738 Roundrock Trl, Plano TX 75023',
|
||
phone: '(972) 555-0118', email: 'gloria.h@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_estimate', bucketId: null,
|
||
notes: 'Estimate sent Mar 28. Waiting on her to review with husband.',
|
||
createdDate: '2026-03-05',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-03-05', note: 'Canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-03-07', note: 'Phone call — confirmed hail damage.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-03-10', note: 'Appointment set for Mar 15.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Frank Agent', date: '2026-03-28', note: 'Inspection completed. Estimate emailed + printed copy left.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_019', name: 'Devon Mathis', address: '5902 Braewood Dr, Plano TX 75093',
|
||
phone: '(972) 555-0119', email: 'devon.m@hotmail.com',
|
||
jobType: 'Siding Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_estimate', bucketId: null,
|
||
notes: 'Estimate under review. Customer comparing quotes.',
|
||
createdDate: '2026-03-08',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-03-08', note: 'Storm canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-03-10', note: 'Met at door — comparing two companies.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-03-13', note: 'Appointment Mar 18.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-03-25', note: 'Estimate sent via email. Mentioned competitor quote is $1,200 less.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_020', name: 'Karen Blankenship', address: '3127 Arbor Creek Dr, Plano TX 75025',
|
||
phone: '(972) 555-0120', email: 'karen.b@yahoo.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_estimate', bucketId: null,
|
||
notes: 'Cash deal. Estimate $24K. Very interested — just reviewing financing options.',
|
||
createdDate: '2026-03-12',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-03-12', note: 'Referral from sister who is an existing client.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-03-14', note: 'Long first call — very motivated buyer.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-03-16', note: 'Inspection Mar 20 at 9am.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Fiona Field', date: '2026-03-22', note: 'Detailed estimate sent. Customer asked about payment plans.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_021', name: 'Lionel Chambers', address: '720 Coit Rd, Plano TX 75075',
|
||
phone: '(972) 555-0121', email: 'lionel.c@gmail.com',
|
||
jobType: 'Gutters + Fascia', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e5', assignedAgentName: 'Felix Fixer',
|
||
columnId: 'kc_estimate', bucketId: null,
|
||
notes: 'Adjuster approved full replacement. Estimate matches scope perfectly.',
|
||
createdDate: '2026-03-01',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felix Fixer', date: '2026-03-01', note: 'Referral — gutters completely detached on one side.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felix Fixer', date: '2026-03-03', note: 'Spoke on phone — insurance claim already filed.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felix Fixer', date: '2026-03-05', note: 'Mar 10 appointment.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felix Fixer', date: '2026-03-18', note: 'Estimate emailed. Adjuster scope matches ours exactly — good sign.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_022', name: 'Ruthanne Patel', address: '4321 Hedgcoxe Rd, Plano TX 75024',
|
||
phone: '(972) 555-0122', email: 'rpatel@outlook.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_estimate', bucketId: null,
|
||
notes: 'Estimate sent 3 weeks ago. Not responding to follow-ups.',
|
||
createdDate: '2026-02-20',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-02-20', note: 'Canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-02-22', note: 'Brief call — seemed interested.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-02-25', note: 'Mar 1 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Frank Agent', date: '2026-03-10', note: 'Estimate sent. No response to 3 follow-up texts since.' },
|
||
],
|
||
},
|
||
|
||
// ── SIGNED (5 leads) ────────────────────────────────────────────────────
|
||
{
|
||
id: 'kl_023', name: 'Charlotte Norris', address: '2908 Roundabout Ln, Plano TX 75023',
|
||
phone: '(972) 555-0123', email: 'charlotte.n@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_signed', bucketId: null,
|
||
notes: 'Signed Mar 22. Install scheduled for Apr 14.',
|
||
createdDate: '2026-03-01',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-03-01', note: 'Storm canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-03-03', note: 'Phone call — eager to move quickly.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-03-05', note: 'Inspection Mar 8.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-03-10', note: 'Estimate sent — $18,200.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felicity Fast', date: '2026-03-22', note: 'Contract signed! Install Apr 14. Deposit received.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_024', name: 'Henry Castaneda', address: '5614 Ridgewood Dr, Plano TX 75093',
|
||
phone: '(972) 555-0124', email: 'hcastaneda@yahoo.com',
|
||
jobType: 'Siding Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_signed', bucketId: null,
|
||
notes: 'Signed. Full siding replacement — 2,400 sq ft. Production queue.',
|
||
createdDate: '2026-02-28',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-02-28', note: 'Referral.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-03-02', note: 'Phone call and site visit same day.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-03-04', note: 'Mar 7 appointment.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Fiona Field', date: '2026-03-12', note: 'Estimate $31,500 for full siding.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Fiona Field', date: '2026-03-20', note: 'Signed. Insurance approved full scope.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_025', name: 'Yvonne Blackwell', address: '3401 Parker Rd, Plano TX 75023',
|
||
phone: '(972) 555-0125', email: 'yblackwell@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_signed', bucketId: null,
|
||
notes: 'Cash client. Signed fast — paid 50% deposit upfront.',
|
||
createdDate: '2026-03-05',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-03-05', note: 'Referral — cash buyer, wants premium materials.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-03-06', note: 'Met same day — very decisive.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-03-07', note: 'Next-day inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Frank Agent', date: '2026-03-09', note: 'Same-day estimate — $27,800 for full replacement + ice/water barrier.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Frank Agent', date: '2026-03-11', note: 'Signed within 48 hrs. 50% deposit paid.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_026', name: 'Emmanuel Okafor', address: '4812 Mapleshade Ln, Plano TX 75075',
|
||
phone: '(972) 555-0126', email: 'e.okafor@outlook.com',
|
||
jobType: 'Roof + Gutters', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e5', assignedAgentName: 'Felix Fixer',
|
||
columnId: 'kc_signed', bucketId: null,
|
||
notes: 'Combined roof and gutter job. Production scheduled Apr 18.',
|
||
createdDate: '2026-03-03',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felix Fixer', date: '2026-03-03', note: 'Storm canvassing — gutters completely detached on west side.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felix Fixer', date: '2026-03-05', note: 'Called and texted. Confirmed both roof and gutter damage.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felix Fixer', date: '2026-03-08', note: 'Mar 12 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felix Fixer', date: '2026-03-15', note: 'Combined estimate $22,400 sent.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felix Fixer', date: '2026-03-25', note: 'Signed after adjuster confirmation. Install Apr 18.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_027', name: 'Adriana Voss', address: '1820 Custer Rd, Plano TX 75075',
|
||
phone: '(972) 555-0127', email: 'adriana.v@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_signed', bucketId: null,
|
||
notes: 'Signed after lengthy negotiation on supplemental items.',
|
||
createdDate: '2026-02-15',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-02-15', note: 'Canvassing after January wind event.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-02-17', note: 'Called — open to inspection.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-02-19', note: 'Feb 22 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-02-25', note: 'Estimate sent $19,600.' },
|
||
{ from: 'Estimate Sent', to: 'Contacted', by: 'Felicity Fast', date: '2026-03-05', note: 'Customer pushed back on supplement items — moved back to review.' },
|
||
{ from: 'Contacted', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-03-10', note: 'Revised estimate sent after supplement agreement with adjuster.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felicity Fast', date: '2026-03-28', note: 'Signed after adjuster approved supplemental items.' },
|
||
],
|
||
},
|
||
|
||
// ── IN PROGRESS (6 leads) ───────────────────────────────────────────────
|
||
{
|
||
id: 'kl_028', name: 'Marvin Delgado', address: '6820 Windhaven Pkwy, Plano TX 75024',
|
||
phone: '(972) 555-0128', email: 'mdelgado@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_progress', bucketId: null,
|
||
notes: 'Tear-off complete. New decking going on today.',
|
||
createdDate: '2026-02-10',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-02-10', note: 'Storm canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-02-12', note: 'Call completed.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-02-14', note: 'Feb 17 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Frank Agent', date: '2026-02-20', note: 'Estimate sent $16,800.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Frank Agent', date: '2026-02-28', note: 'Signed. Production team notified.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Adam Admin', date: '2026-03-18', note: 'Production started — tear-off crew on site.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_029', name: 'Sandra Tran', address: '2506 Springpark Dr, Plano TX 75023',
|
||
phone: '(972) 555-0129', email: 's.tran@yahoo.com',
|
||
jobType: 'Siding Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_progress', bucketId: null,
|
||
notes: 'Day 3 of install. On track for completion Friday.',
|
||
createdDate: '2026-02-08',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-02-08', note: 'Referral.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-02-10', note: 'Initial call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-02-12', note: 'Feb 15 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Fiona Field', date: '2026-02-18', note: 'Estimate $28,500 for full siding.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Fiona Field', date: '2026-02-25', note: 'Signed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Adam Admin', date: '2026-03-25', note: 'Siding crew on site — Day 1.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_030', name: 'Reginald Hopper', address: '1504 Estates Dr, Plano TX 75093',
|
||
phone: '(972) 555-0130', email: 'reg.hopper@icloud.com',
|
||
jobType: 'Roof + Gutters', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_progress', bucketId: null,
|
||
notes: 'Roofing done. Gutters crew arriving tomorrow.',
|
||
createdDate: '2026-02-05',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-02-05', note: 'Door knock.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-02-07', note: 'Call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-02-10', note: 'Feb 14 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-02-17', note: 'Estimate sent $21,200.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felicity Fast', date: '2026-02-24', note: 'Signed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Amanda Manager',date: '2026-03-20', note: 'Production underway — roofing crew Day 1.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_031', name: 'Nadia Thornton', address: '5150 Preston Rd, Plano TX 75024',
|
||
phone: '(972) 555-0131', email: 'nadia.t@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e5', assignedAgentName: 'Felix Fixer',
|
||
columnId: 'kc_progress', bucketId: null,
|
||
notes: 'Premium GAF Timberline HDZ install. Custom color.',
|
||
createdDate: '2026-02-12',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felix Fixer', date: '2026-02-12', note: 'Referral — high-end home.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felix Fixer', date: '2026-02-14', note: 'Met in person. Requested premium material options.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felix Fixer', date: '2026-02-17', note: 'Feb 20 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felix Fixer', date: '2026-02-24', note: 'Premium estimate $34,500 including ice/water and lifetime warranty.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felix Fixer', date: '2026-03-03', note: 'Signed — wants Barkwood color. Special order placed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Amanda Manager',date: '2026-03-28', note: 'Materials arrived. Install started today.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_032', name: 'Clifford Aguilar', address: '3822 Haverwood Ln, Plano TX 75023',
|
||
phone: '(972) 555-0132', email: 'c.aguilar@outlook.com',
|
||
jobType: 'Siding Repair', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_progress', bucketId: null,
|
||
notes: 'Partial siding repair only — south and west faces. 2 days left.',
|
||
createdDate: '2026-02-18',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-02-18', note: 'Storm canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-02-20', note: 'Called.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-02-22', note: 'Feb 25 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Frank Agent', date: '2026-02-28', note: 'Partial repair estimate $9,400.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Frank Agent', date: '2026-03-07', note: 'Signed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Adam Admin', date: '2026-04-01', note: 'Crew on site — south face complete.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_033', name: 'Tamara Owens', address: '2010 Oak Creek Blvd, Plano TX 75023',
|
||
phone: '(972) 555-0133', email: 'tamara.o@yahoo.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_progress', bucketId: null,
|
||
notes: 'Large job — 3,600 sq ft. Day 1 of 3.',
|
||
createdDate: '2026-02-01',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-02-01', note: 'Referral — large home, significant hail damage.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-02-03', note: 'Met in person — urgent, water coming in.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-02-04', note: 'Next-day inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Fiona Field', date: '2026-02-08', note: 'Estimate $42,000 for 3,600 sq ft.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Fiona Field', date: '2026-02-15', note: 'Signed — largest job this quarter.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Adam Admin', date: '2026-04-05', note: 'Production started — 3-day job. Two crews deployed.' },
|
||
],
|
||
},
|
||
|
||
// ── COMPLETE (5 leads) ──────────────────────────────────────────────────
|
||
{
|
||
id: 'kl_034', name: 'Craig Singleton', address: '4110 Lorimar Dr, Plano TX 75093',
|
||
phone: '(972) 555-0134', email: 'craig.s@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_complete', bucketId: null,
|
||
notes: 'Job complete. Final walkthrough done. Client extremely happy.',
|
||
createdDate: '2026-01-15',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-01-15', note: 'Canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-01-17', note: 'Call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-01-20', note: 'Jan 23 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Frank Agent', date: '2026-01-25', note: 'Estimate $17,200.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Frank Agent', date: '2026-02-01', note: 'Signed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Adam Admin', date: '2026-02-20', note: 'Install Day 1.' },
|
||
{ from: 'In Progress', to: 'Complete', by: 'Adam Admin', date: '2026-03-01', note: 'Job complete. Final walkthrough passed. Client left 5-star review.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_035', name: 'Bertha Fleming', address: '6230 West Parker Rd, Plano TX 75093',
|
||
phone: '(972) 555-0135', email: 'bertha.f@hotmail.com',
|
||
jobType: 'Siding Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_complete', bucketId: null,
|
||
notes: 'Complete. Insurance check received and deposited.',
|
||
createdDate: '2026-01-10',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-01-10', note: 'Referral.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-01-12', note: 'Call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-01-14', note: 'Jan 17 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-01-20', note: 'Estimate $26,400.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felicity Fast', date: '2026-01-28', note: 'Signed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Amanda Manager',date: '2026-02-15', note: 'Production started.' },
|
||
{ from: 'In Progress', to: 'Complete', by: 'Amanda Manager',date: '2026-02-22', note: 'Complete. Insurance proceeds processed.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_036', name: 'Oliver Stanton', address: '3750 Legacy Dr, Plano TX 75023',
|
||
phone: '(972) 555-0136', email: 'oliver.st@outlook.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_complete', bucketId: null,
|
||
notes: 'Cash deal done. Referral bonus owed to Oliver per agreement.',
|
||
createdDate: '2026-01-20',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-01-20', note: 'Referral — cash buyer.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-01-22', note: 'Same-day call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fiona Field', date: '2026-01-23', note: 'Jan 25 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Fiona Field', date: '2026-01-27', note: 'Estimate $22,100.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Fiona Field', date: '2026-01-30', note: 'Signed within 72 hrs. Full payment upfront.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Adam Admin', date: '2026-02-18', note: 'Install started.' },
|
||
{ from: 'In Progress', to: 'Complete', by: 'Adam Admin', date: '2026-02-20', note: 'Complete — quick 2-day job. Outstanding quality.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_037', name: 'Daphne Garrett', address: '1912 Mira Vista Dr, Plano TX 75025',
|
||
phone: '(972) 555-0137', email: 'daphne.g@gmail.com',
|
||
jobType: 'Gutters + Fascia', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e5', assignedAgentName: 'Felix Fixer',
|
||
columnId: 'kc_complete', bucketId: null,
|
||
notes: 'Complete. Customer requested referral card — strong close.',
|
||
createdDate: '2026-01-25',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felix Fixer', date: '2026-01-25', note: 'Door knock.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felix Fixer', date: '2026-01-27', note: 'Call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felix Fixer', date: '2026-01-29', note: 'Feb 1 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felix Fixer', date: '2026-02-04', note: 'Estimate $8,900.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felix Fixer', date: '2026-02-10', note: 'Signed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Adam Admin', date: '2026-02-28', note: 'Install started.' },
|
||
{ from: 'In Progress', to: 'Complete', by: 'Adam Admin', date: '2026-03-02', note: 'Job done. Customer gave referral card to 3 neighbors.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_038', name: 'Nathaniel Cruz', address: '5808 Tennyson Pkwy, Plano TX 75024',
|
||
phone: '(972) 555-0138', email: 'ncruz@icloud.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_complete', bucketId: null,
|
||
notes: 'Complete. Longest pipeline — 7 week close. Insurance supplemental was complex.',
|
||
createdDate: '2026-01-05',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-01-05', note: 'Canvassing post-December storm.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-01-07', note: 'Call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felicity Fast', date: '2026-01-10', note: 'Jan 14 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-01-17', note: 'Estimate $19,400.' },
|
||
{ from: 'Estimate Sent', to: 'Contacted', by: 'Felicity Fast', date: '2026-01-28', note: 'Insurance underpaid — back to negotiation.' },
|
||
{ from: 'Contacted', to: 'Estimate Sent', by: 'Felicity Fast', date: '2026-02-05', note: 'Revised estimate submitted to adjuster.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Felicity Fast', date: '2026-02-18', note: 'Adjuster approved supplement. Signed.' },
|
||
{ from: 'Signed', to: 'In Progress', by: 'Amanda Manager',date: '2026-03-05', note: 'Production started.' },
|
||
{ from: 'In Progress', to: 'Complete', by: 'Amanda Manager',date: '2026-03-12', note: 'Job complete. Long road but great outcome.' },
|
||
],
|
||
},
|
||
|
||
// ── STUCK bucket (3 leads — have columnId from last stage + bucketId) ───
|
||
{
|
||
id: 'kl_039', name: 'Jasmine Fowler', address: '2730 Hedgcoxe Rd, Plano TX 75024',
|
||
phone: '(972) 555-0139', email: 'jasmine.f@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e3', assignedAgentName: 'Fred Flyer',
|
||
columnId: 'kc_estimate', bucketId: 'kc_stuck',
|
||
notes: 'Stuck — adjuster denied claim. Customer disputing. No movement for 3 weeks.',
|
||
createdDate: '2026-02-25',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fred Flyer', date: '2026-02-25', note: 'Canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fred Flyer', date: '2026-02-27', note: 'Call — insurance claim just filed.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fred Flyer', date: '2026-03-01', note: 'Mar 5 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Fred Flyer', date: '2026-03-10', note: 'Estimate sent $15,800. Adjuster denied most of scope.' },
|
||
{ from: 'Estimate Sent', to: 'Stuck', by: 'Fred Flyer', date: '2026-03-18', note: 'Moved to Stuck — adjuster denial under dispute. Waiting on public adjuster.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_040', name: 'Calvin Merritt', address: '3640 Alma Dr, Plano TX 75023',
|
||
phone: '(972) 555-0140', email: 'calvin.m@yahoo.com',
|
||
jobType: 'Siding Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e2', assignedAgentName: 'Fiona Field',
|
||
columnId: 'kc_contacted', bucketId: 'kc_stuck',
|
||
notes: 'Stuck — homeowner going through divorce. Put project on hold indefinitely.',
|
||
createdDate: '2026-03-10',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fiona Field', date: '2026-03-10', note: 'Referral.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fiona Field', date: '2026-03-12', note: 'Good initial call but complicated personal situation mentioned.' },
|
||
{ from: 'Contacted', to: 'Stuck', by: 'Fiona Field', date: '2026-03-20', note: 'Moved to Stuck — customer put everything on hold due to divorce proceedings.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_041', name: 'Harriet Simmons', address: '4905 Ohio Dr, Plano TX 75093',
|
||
phone: '(972) 555-0141', email: 'harriet.s@outlook.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Retail',
|
||
assignedAgentId: 'e5', assignedAgentName: 'Felix Fixer',
|
||
columnId: 'kc_appt', bucketId: 'kc_stuck',
|
||
notes: 'Stuck — missed 3 appointments. No answer on phone. Left door hanger.',
|
||
createdDate: '2026-03-05',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felix Fixer', date: '2026-03-05', note: 'Door knock — interested in retail replacement.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felix Fixer', date: '2026-03-07', note: 'Brief call — set appointment.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Felix Fixer', date: '2026-03-09', note: 'Mar 14 appointment set.' },
|
||
{ from: 'Appt Scheduled',to: 'Stuck', by: 'Felix Fixer', date: '2026-03-28', note: 'No-showed 3 appointments. Phone goes to voicemail. Marked stuck.' },
|
||
],
|
||
},
|
||
|
||
// ── FOLLOW-UP bucket (3 leads) ───────────────────────────────────────────
|
||
{
|
||
id: 'kl_042', name: 'Floyd Saunders', address: '1344 Spring Creek Pkwy, Plano TX 75023',
|
||
phone: '(972) 555-0142', email: 'floyd.s@gmail.com',
|
||
jobType: 'Roof Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e1', assignedAgentName: 'Frank Agent',
|
||
columnId: 'kc_estimate', bucketId: 'kc_followup',
|
||
notes: 'Follow-up Apr 10 — customer said to call after spring break. Very warm lead.',
|
||
createdDate: '2026-03-15',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Frank Agent', date: '2026-03-15', note: 'Storm canvassing.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Frank Agent', date: '2026-03-17', note: 'Call — interested but said call back after spring break.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Frank Agent', date: '2026-03-18', note: 'Mar 20 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Frank Agent', date: '2026-03-22', note: 'Estimate sent $17,600.' },
|
||
{ from: 'Estimate Sent', to: 'Follow-up', by: 'Frank Agent', date: '2026-03-25', note: 'Moved to Follow-up — scheduled callback Apr 10.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_043', name: 'Miriam Obando', address: '2217 Independence Pkwy, Plano TX 75025',
|
||
phone: '(972) 555-0143', email: 'miriam.o@icloud.com',
|
||
jobType: 'Roof Inspection', insuranceType: 'Retail',
|
||
assignedAgentId: 'e4', assignedAgentName: 'Felicity Fast',
|
||
columnId: 'kc_contacted', bucketId: 'kc_followup',
|
||
notes: 'Follow-up Apr 15 — currently traveling abroad. Callback confirmed via text.',
|
||
createdDate: '2026-03-20',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Felicity Fast', date: '2026-03-20', note: 'Website form lead.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Felicity Fast', date: '2026-03-22', note: 'Text conversation — customer is abroad until Apr 13.' },
|
||
{ from: 'Contacted', to: 'Follow-up', by: 'Felicity Fast', date: '2026-03-23', note: 'Moved to Follow-up — callback Apr 15 confirmed via text.' },
|
||
],
|
||
},
|
||
{
|
||
id: 'kl_044', name: 'Jerome Watkins', address: '3908 Plano Pkwy, Plano TX 75074',
|
||
phone: '(972) 555-0144', email: 'jerome.w@yahoo.com',
|
||
jobType: 'Gutter Replacement', insuranceType: 'Insurance',
|
||
assignedAgentId: 'e3', assignedAgentName: 'Fred Flyer',
|
||
columnId: 'kc_signed', bucketId: 'kc_followup',
|
||
notes: 'Signed but waiting on HOA approval before starting. Follow-up Apr 20.',
|
||
createdDate: '2026-03-08',
|
||
activity: [
|
||
{ from: null, to: 'New Lead', by: 'Fred Flyer', date: '2026-03-08', note: 'Referral.' },
|
||
{ from: 'New Lead', to: 'Contacted', by: 'Fred Flyer', date: '2026-03-10', note: 'Call.' },
|
||
{ from: 'Contacted', to: 'Appt Scheduled', by: 'Fred Flyer', date: '2026-03-12', note: 'Mar 15 inspection.' },
|
||
{ from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Fred Flyer', date: '2026-03-18', note: 'Estimate $6,200.' },
|
||
{ from: 'Estimate Sent', to: 'Signed', by: 'Fred Flyer', date: '2026-03-25', note: 'Signed — waiting on HOA approval to start.' },
|
||
{ from: 'Signed', to: 'Follow-up', by: 'Fred Flyer', date: '2026-03-28', note: 'HOA review pending. Follow-up Apr 20 for approval status.' },
|
||
],
|
||
},
|
||
];
|
||
|
||
// ── Enriched project data for signed/in-progress/complete leads ──────────────
|
||
// Keyed by lead ID. Only present for leads that have become active jobs.
|
||
const KANBAN_PROJECT_DATA = {
|
||
// ── SIGNED LEADS ──────────────────────────────────────────────────────────
|
||
kl_023: {
|
||
estimatedAmount: 18200, actualCost: 0, variancePercent: 0, completionPct: 25, healthScore: 88,
|
||
contractorName: 'Plano Roofing Crew A', contractorPhone: '(972) 555-9001',
|
||
budgetBreakdown: [
|
||
{ name: 'ABC Supply Co.', category: 'Materials', allocated: 9800, committed: 9800, actual: 0 },
|
||
{ name: 'Plano Roofing Crew A', category: 'Labor', allocated: 6200, committed: 6200, actual: 0 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 0 },
|
||
{ name: 'Reserve Fund', category: 'Contingency',allocated: 1600, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-03-22', notes: 'Deposit received.' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-03-24', notes: 'GAF Timberline HDZ — Charcoal ordered.' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'in_progress',date: '2026-04-05', notes: 'City permit submitted, pending approval.' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 30%', amount: 5460, status: 'paid', dueDate: '2026-03-22', paidDate: '2026-03-22' },
|
||
{ id: 'inv2', description: 'Progress — 40%', amount: 7280, status: 'pending', dueDate: '2026-04-14', paidDate: null },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 5460, status: 'pending', dueDate: '2026-04-15', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-03-22', action: 'Contract Executed', user: 'Felicity Fast', details: 'Client signed. 30% deposit collected.' },
|
||
{ date: '2026-03-24', action: 'Materials Ordered', user: 'Adam Admin', details: 'GAF Timberline HDZ Charcoal — 24 squares ordered.' },
|
||
{ date: '2026-04-05', action: 'Permit Submitted', user: 'Adam Admin', details: 'Plano city permit application filed.' },
|
||
],
|
||
},
|
||
kl_024: {
|
||
estimatedAmount: 31500, actualCost: 0, variancePercent: 0, completionPct: 20, healthScore: 85,
|
||
contractorName: 'Plano Siding Crew', contractorPhone: '(972) 555-9002',
|
||
budgetBreakdown: [
|
||
{ name: 'Owens Corning', category: 'Materials', allocated: 17000, committed: 17000, actual: 0 },
|
||
{ name: 'Plano Siding Crew', category: 'Labor', allocated: 11500, committed: 11500, actual: 0 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 800, committed: 800, actual: 0 },
|
||
{ name: 'Reserve Fund', category: 'Contingency',allocated: 2200, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-03-20', notes: 'Insurance approved full scope.' },
|
||
{ id: 'm2', name: 'Materials Ordered',status: 'completed', date: '2026-03-23', notes: 'James Hardie Board & Batten ordered.' },
|
||
{ id: 'm3', name: 'Permit Pulled', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm4', name: 'Prep & Demo', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm5', name: 'Siding Install', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm6', name: 'Trim & Caulk', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm8', name: 'Invoice & Payment',status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [
|
||
{ id: 'rfi1', subject: 'HOA Color Approval', status: 'open', submittedBy: 'Fiona Field', openedDate: '2026-03-25', closedDate: null, description: 'Need written HOA approval for Cobblestone color before install.' },
|
||
],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 25%', amount: 7875, status: 'paid', dueDate: '2026-03-20', paidDate: '2026-03-20' },
|
||
{ id: 'inv2', description: 'Progress — 50%', amount: 15750,status: 'pending', dueDate: '2026-04-25', paidDate: null },
|
||
{ id: 'inv3', description: 'Final — 25%', amount: 7875, status: 'pending', dueDate: '2026-05-01', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-03-20', action: 'Contract Executed', user: 'Fiona Field', details: 'Insurance-approved scope. Full 2,400 sq ft siding.' },
|
||
{ date: '2026-03-23', action: 'Materials Ordered', user: 'Adam Admin', details: 'James Hardie Board & Batten — Cobblestone, 2,400 sq ft.' },
|
||
{ date: '2026-03-25', action: 'RFI Opened', user: 'Fiona Field', details: 'HOA color approval required before start.' },
|
||
],
|
||
},
|
||
kl_025: {
|
||
estimatedAmount: 27800, actualCost: 0, variancePercent: 0, completionPct: 28, healthScore: 92,
|
||
contractorName: 'Plano Roofing Crew B', contractorPhone: '(972) 555-9003',
|
||
budgetBreakdown: [
|
||
{ name: 'GAF Materials', category: 'Materials', allocated: 15200, committed: 15200, actual: 0 },
|
||
{ name: 'Plano Roofing Crew B', category: 'Labor', allocated: 9800, committed: 9800, actual: 0 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 0 },
|
||
{ name: 'Reserve Fund', category: 'Contingency',allocated: 2200, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-03-11', notes: '50% deposit received.' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-03-12', notes: 'Owens Corning TruDefinition — Driftwood.' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-03-20', notes: 'Permit approved.' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 50%', amount: 13900, status: 'paid', dueDate: '2026-03-11', paidDate: '2026-03-11' },
|
||
{ id: 'inv2', description: 'Final — 50%', amount: 13900, status: 'pending', dueDate: '2026-04-20', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-03-11', action: 'Contract Executed', user: 'Frank Agent', details: 'Cash client. 50% deposit paid same day.' },
|
||
{ date: '2026-03-12', action: 'Materials Ordered', user: 'Adam Admin', details: 'Owens Corning TruDefinition Duration — 28 squares.' },
|
||
{ date: '2026-03-20', action: 'Permit Approved', user: 'Adam Admin', details: 'City of Plano residential roofing permit #RFP-2026-1148.' },
|
||
],
|
||
},
|
||
kl_026: {
|
||
estimatedAmount: 22400, actualCost: 0, variancePercent: 0, completionPct: 22, healthScore: 86,
|
||
contractorName: 'Plano Roofing Crew A', contractorPhone: '(972) 555-9001',
|
||
budgetBreakdown: [
|
||
{ name: 'ABC Supply Co.', category: 'Roofing Materials', allocated: 10800, committed: 10800, actual: 0 },
|
||
{ name: 'LeafGuard DFW', category: 'Gutter Materials', allocated: 3400, committed: 3400, actual: 0 },
|
||
{ name: 'Plano Roofing Crew A', category: 'Labor', allocated: 6400, committed: 6400, actual: 0 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 0 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 1200, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-03-25', notes: 'Install Apr 18.' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-03-27', notes: 'Roofing + gutters ordered.' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm4', name: 'Roofing Install', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm5', name: 'Gutter Install', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 30%', amount: 6720, status: 'paid', dueDate: '2026-03-25', paidDate: '2026-03-25' },
|
||
{ id: 'inv2', description: 'Progress — 40%', amount: 8960, status: 'pending', dueDate: '2026-04-18', paidDate: null },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 6720, status: 'pending', dueDate: '2026-04-22', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-03-25', action: 'Contract Executed', user: 'Felix Fixer', details: 'Insurance approved full roof + gutter scope.' },
|
||
{ date: '2026-03-27', action: 'Materials Ordered', user: 'Adam Admin', details: 'GAF shingles + K-style 6" gutters ordered.' },
|
||
],
|
||
},
|
||
kl_027: {
|
||
estimatedAmount: 19600, actualCost: 0, variancePercent: 0, completionPct: 24, healthScore: 80,
|
||
contractorName: 'Plano Roofing Crew C', contractorPhone: '(972) 555-9004',
|
||
budgetBreakdown: [
|
||
{ name: 'James Hardie', category: 'Materials', allocated: 10500, committed: 10500, actual: 0 },
|
||
{ name: 'Plano Roofing Crew C', category: 'Labor', allocated: 7000, committed: 7000, actual: 0 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 0 },
|
||
{ name: 'Reserve Fund', category: 'Contingency',allocated: 1500, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-03-28', notes: 'Supplement approved after adjuster re-inspection.' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'in_progress',date: '2026-04-02', notes: 'Waiting on special color availability.' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking',status: 'pending', date: null, notes: '' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [
|
||
{ id: 'co1', title: 'Supplemental — Ice & Water Barrier', amount: 1800, status: 'approved', requestedBy: 'Felicity Fast', date: '2026-03-10', description: 'Insurance adjuster agreed to add ice/water shield on all valleys and eaves.' },
|
||
],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 30%', amount: 5880, status: 'paid', dueDate: '2026-03-28', paidDate: '2026-03-28' },
|
||
{ id: 'inv2', description: 'Progress — 40%', amount: 7840, status: 'pending', dueDate: '2026-04-25', paidDate: null },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 5880, status: 'pending', dueDate: '2026-04-28', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-03-28', action: 'Contract Executed', user: 'Felicity Fast', details: 'Signed after 6-week negotiation on supplement items.' },
|
||
{ date: '2026-04-02', action: 'Materials Ordered', user: 'Adam Admin', details: 'Special color availability causing 1-week delay.' },
|
||
],
|
||
},
|
||
|
||
// ── IN PROGRESS LEADS ─────────────────────────────────────────────────────
|
||
kl_028: {
|
||
estimatedAmount: 16800, actualCost: 9200, variancePercent: -3.6, completionPct: 65, healthScore: 82,
|
||
contractorName: 'Plano Roofing Crew A', contractorPhone: '(972) 555-9001',
|
||
budgetBreakdown: [
|
||
{ name: 'ABC Supply Co.', category: 'Materials', allocated: 9000, committed: 9000, actual: 8600 },
|
||
{ name: 'Plano Roofing Crew A', category: 'Labor', allocated: 5800, committed: 5800, actual: 600 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 600 },
|
||
{ name: 'Reserve Fund', category: 'Contingency',allocated: 1400, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-02-28', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-03-02', notes: '' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-03-10', notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'completed', date: '2026-03-18', notes: 'Tear-off complete. Two rotted deck boards replaced.' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'in_progress',date: '2026-04-07', notes: 'New decking being installed today.' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [
|
||
{ id: 'co1', title: 'Decking Replacement — 2 Sheets', amount: 480, status: 'approved', requestedBy: 'Frank Agent', date: '2026-03-18', description: 'Two rotted OSB sheets discovered during tear-off. Replaced at cost.' },
|
||
],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 30%', amount: 5040, status: 'paid', dueDate: '2026-02-28', paidDate: '2026-02-28' },
|
||
{ id: 'inv2', description: 'Progress — 40%', amount: 6720, status: 'paid', dueDate: '2026-03-20', paidDate: '2026-03-20' },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 5040, status: 'pending', dueDate: '2026-04-10', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-02-28', action: 'Contract Executed', user: 'Frank Agent', details: 'Signed. Production team notified.' },
|
||
{ date: '2026-03-18', action: 'Production Started', user: 'Adam Admin', details: 'Tear-off crew on site. Two rotted deck boards found and replaced.' },
|
||
{ date: '2026-03-20', action: 'Progress Invoice Paid', user: 'Adam Admin', details: 'Client paid progress invoice on time.' },
|
||
{ date: '2026-04-07', action: 'Decking Install', user: 'Adam Admin', details: 'New decking going on today. Roofing shingles start tomorrow.' },
|
||
],
|
||
},
|
||
kl_029: {
|
||
estimatedAmount: 28500, actualCost: 14800, variancePercent: 1.4, completionPct: 55, healthScore: 78,
|
||
contractorName: 'Plano Siding Crew', contractorPhone: '(972) 555-9002',
|
||
budgetBreakdown: [
|
||
{ name: 'Owens Corning', category: 'Materials', allocated: 15400, committed: 15400, actual: 14200 },
|
||
{ name: 'Plano Siding Crew', category: 'Labor', allocated: 10400, committed: 10400, actual: 600 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 800, committed: 800, actual: 800 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 1900, committed: 400, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-02-25', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered',status: 'completed', date: '2026-02-27', notes: '' },
|
||
{ id: 'm3', name: 'Permit Pulled', status: 'completed', date: '2026-03-05', notes: '' },
|
||
{ id: 'm4', name: 'Prep & Demo', status: 'completed', date: '2026-03-25', notes: 'Old siding removed. Minor rot on 2 panels fixed.' },
|
||
{ id: 'm5', name: 'Siding Install', status: 'in_progress',date: '2026-04-05', notes: 'Day 3 of install. On track.' },
|
||
{ id: 'm6', name: 'Trim & Caulk', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm8', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [
|
||
{ id: 'co1', title: 'Rot Repair — 2 Panels', amount: 380, status: 'approved', requestedBy: 'Fiona Field', date: '2026-03-25', description: 'Two panels of rotted OSB substrate discovered during demo. Repaired.' },
|
||
],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 25%', amount: 7125, status: 'paid', dueDate: '2026-02-25', paidDate: '2026-02-25' },
|
||
{ id: 'inv2', description: 'Progress — 50%', amount: 14250,status: 'pending', dueDate: '2026-04-08', paidDate: null },
|
||
{ id: 'inv3', description: 'Final — 25%', amount: 7125, status: 'pending', dueDate: '2026-04-15', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-02-25', action: 'Contract Executed', user: 'Fiona Field', details: 'Insurance approved full scope.' },
|
||
{ date: '2026-03-25', action: 'Production Started', user: 'Adam Admin', details: 'Demo complete. Minor rot on 2 panels — change order raised.' },
|
||
{ date: '2026-04-05', action: 'Install Underway', user: 'Adam Admin', details: 'Siding install Day 3. On track for Friday completion.' },
|
||
],
|
||
},
|
||
kl_030: {
|
||
estimatedAmount: 21200, actualCost: 16100, variancePercent: -4.2, completionPct: 78, healthScore: 88,
|
||
contractorName: 'Plano Roofing Crew B', contractorPhone: '(972) 555-9003',
|
||
budgetBreakdown: [
|
||
{ name: 'ABC Supply Co.', category: 'Roofing Materials', allocated: 10200, committed: 10200, actual: 9800 },
|
||
{ name: 'LeafGuard DFW', category: 'Gutter Materials', allocated: 3200, committed: 3200, actual: 3100 },
|
||
{ name: 'Plano Roofing Crew B', category: 'Labor', allocated: 6000, committed: 6000, actual: 3200 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 600 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 1200, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-02-24', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-02-26', notes: '' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-03-05', notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking',status: 'completed', date: '2026-03-20', notes: 'Clean tear-off. No deck issues.' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'completed', date: '2026-03-22', notes: 'Roofing complete.' },
|
||
{ id: 'm6', name: 'Gutter Install', status: 'in_progress',date: '2026-04-07', notes: 'Gutter crew arriving today.' },
|
||
{ id: 'm7', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm8', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 30%', amount: 6360, status: 'paid', dueDate: '2026-02-24', paidDate: '2026-02-24' },
|
||
{ id: 'inv2', description: 'Progress — 40%', amount: 8480, status: 'paid', dueDate: '2026-03-22', paidDate: '2026-03-22' },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 6360, status: 'pending', dueDate: '2026-04-10', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-02-24', action: 'Contract Executed', user: 'Felicity Fast', details: '' },
|
||
{ date: '2026-03-20', action: 'Roofing Started', user: 'Amanda Manager', details: 'Production underway — roofing crew Day 1.' },
|
||
{ date: '2026-03-22', action: 'Roofing Complete', user: 'Amanda Manager', details: 'Roof done. Progress invoice sent.' },
|
||
{ date: '2026-04-07', action: 'Gutter Install', user: 'Amanda Manager', details: 'Gutter crew arriving today for 1-day install.' },
|
||
],
|
||
},
|
||
kl_031: {
|
||
estimatedAmount: 34500, actualCost: 22000, variancePercent: 2.9, completionPct: 60, healthScore: 90,
|
||
contractorName: 'Plano Roofing Crew C', contractorPhone: '(972) 555-9004',
|
||
budgetBreakdown: [
|
||
{ name: 'Owens Corning', category: 'Premium Materials', allocated: 19200, committed: 19200, actual: 18800 },
|
||
{ name: 'Plano Roofing Crew C', category: 'Labor', allocated: 12000, committed: 12000, actual: 3200 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 700, committed: 700, actual: 700 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 2600, committed: 1000, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-03-03', notes: 'Cash client. Barkwood color selected.' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-03-05', notes: 'Special order — GAF Timberline HDZ Barkwood. 3-week lead time.' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-03-12', notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'completed', date: '2026-03-28', notes: 'Premium deck prep complete. No issues.' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'in_progress',date: '2026-03-28', notes: 'Premium HDZ install underway.' },
|
||
{ id: 'm6', name: 'Ridge & Detail', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm8', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [
|
||
{ id: 'rfi1', subject: 'Lifetime Warranty Registration', status: 'open', submittedBy: 'Felix Fixer', openedDate: '2026-03-05', closedDate: null, description: 'GAF factory warranty registration requires installer certification — need to confirm with crew.' },
|
||
],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 100%', amount: 34500, status: 'paid', dueDate: '2026-03-03', paidDate: '2026-03-03' },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-03-03', action: 'Contract Executed', user: 'Felix Fixer', details: 'Cash client — full payment upfront.' },
|
||
{ date: '2026-03-28', action: 'Production Started', user: 'Amanda Manager',details: 'Materials arrived. Premium install started.' },
|
||
],
|
||
},
|
||
kl_032: {
|
||
estimatedAmount: 9400, actualCost: 6200, variancePercent: -1.1, completionPct: 70, healthScore: 84,
|
||
contractorName: 'Plano Siding Crew', contractorPhone: '(972) 555-9002',
|
||
budgetBreakdown: [
|
||
{ name: 'GAF Materials', category: 'Materials', allocated: 4800, committed: 4800, actual: 4600 },
|
||
{ name: 'Plano Siding Crew', category: 'Labor', allocated: 3600, committed: 3600, actual: 1600 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 500, committed: 500, actual: 500 },
|
||
{ name: 'Reserve Fund', category: 'Contingency',allocated: 500, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-03-07', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered',status: 'completed', date: '2026-03-09', notes: '' },
|
||
{ id: 'm3', name: 'Permit Pulled', status: 'completed', date: '2026-03-20', notes: '' },
|
||
{ id: 'm4', name: 'South Face', status: 'completed', date: '2026-04-01', notes: 'South face complete.' },
|
||
{ id: 'm5', name: 'West Face', status: 'in_progress',date: '2026-04-06', notes: '2 days remaining.' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment',status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 40%', amount: 3760, status: 'paid', dueDate: '2026-03-07', paidDate: '2026-03-07' },
|
||
{ id: 'inv2', description: 'Progress — 30%', amount: 2820, status: 'paid', dueDate: '2026-04-01', paidDate: '2026-04-02' },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 2820, status: 'pending', dueDate: '2026-04-10', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-03-07', action: 'Contract Executed', user: 'Frank Agent', details: 'Partial repair — south and west faces only.' },
|
||
{ date: '2026-04-01', action: 'South Face Done', user: 'Adam Admin', details: 'South face siding complete. Starting west face.' },
|
||
],
|
||
},
|
||
kl_033: {
|
||
estimatedAmount: 42000, actualCost: 18000, variancePercent: 0.8, completionPct: 45, healthScore: 74,
|
||
contractorName: 'Plano Roofing Crew A', contractorPhone: '(972) 555-9001',
|
||
budgetBreakdown: [
|
||
{ name: 'James Hardie', category: 'Materials', allocated: 22000, committed: 22000, actual: 17200 },
|
||
{ name: 'Plano Roofing Crew A', category: 'Labor', allocated: 15500, committed: 15500, actual: 800 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 900, committed: 900, actual: 900 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 3600, committed: 2000, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-02-15', notes: 'Largest job this quarter.' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-02-18', notes: '3 pallets — 36 squares.' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-03-01', notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'in_progress',date: '2026-04-05', notes: 'Day 1 of 3. Two crews deployed.' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'pending', date: null, notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'pending', date: null, notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [
|
||
{ id: 'rfi1', subject: 'Structural Load — Chimney Flashing', status: 'open', submittedBy: 'Fiona Field', openedDate: '2026-04-05', closedDate: null, description: 'Large chimney requires custom step flashing — need architect sign-off for load specs.' },
|
||
],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 25%', amount: 10500, status: 'paid', dueDate: '2026-02-15', paidDate: '2026-02-15' },
|
||
{ id: 'inv2', description: 'Progress — 50%', amount: 21000, status: 'pending', dueDate: '2026-04-08', paidDate: null },
|
||
{ id: 'inv3', description: 'Final — 25%', amount: 10500, status: 'pending', dueDate: '2026-04-15', paidDate: null },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-02-15', action: 'Contract Executed', user: 'Fiona Field', details: 'Largest job this quarter — $42,000, 3,600 sq ft.' },
|
||
{ date: '2026-04-05', action: 'Production Started', user: 'Adam Admin', details: 'Day 1 of 3-day job. Two crews on site.' },
|
||
{ date: '2026-04-05', action: 'RFI Opened', user: 'Fiona Field', details: 'Custom chimney flashing — architect sign-off required.' },
|
||
],
|
||
},
|
||
|
||
// ── COMPLETE LEADS ────────────────────────────────────────────────────────
|
||
kl_034: {
|
||
estimatedAmount: 17200, actualCost: 16950, variancePercent: -1.5, completionPct: 100, healthScore: 95,
|
||
contractorName: 'Plano Roofing Crew B', contractorPhone: '(972) 555-9003',
|
||
budgetBreakdown: [
|
||
{ name: 'ABC Supply Co.', category: 'Materials', allocated: 9200, committed: 9200, actual: 8980 },
|
||
{ name: 'Plano Roofing Crew B', category: 'Labor', allocated: 6000, committed: 6000, actual: 6000 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 600 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 1400, committed: 400, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-02-01', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-02-03', notes: '' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-02-12', notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'completed', date: '2026-02-20', notes: 'Clean tear-off.' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'completed', date: '2026-02-21', notes: 'Install complete in 1 day.' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'completed', date: '2026-02-28', notes: 'Passed. Client gave 5-star review.' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'completed', date: '2026-03-01', notes: 'Full payment received.' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 30%', amount: 5160, status: 'paid', dueDate: '2026-02-01', paidDate: '2026-02-01' },
|
||
{ id: 'inv2', description: 'Progress — 40%', amount: 6880, status: 'paid', dueDate: '2026-02-21', paidDate: '2026-02-22' },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 5160, status: 'paid', dueDate: '2026-03-01', paidDate: '2026-03-01' },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-02-01', action: 'Contract Executed', user: 'Frank Agent', details: '' },
|
||
{ date: '2026-02-20', action: 'Production Started', user: 'Adam Admin', details: 'Tear-off crew on site.' },
|
||
{ date: '2026-02-21', action: 'Install Complete', user: 'Adam Admin', details: 'Full install done in 1 day.' },
|
||
{ date: '2026-02-28', action: 'Final Inspection', user: 'Adam Admin', details: 'Passed. Client extremely happy. 5-star review.' },
|
||
{ date: '2026-03-01', action: 'Project Closed', user: 'Adam Admin', details: 'Final payment received. File closed.' },
|
||
],
|
||
},
|
||
kl_035: {
|
||
estimatedAmount: 26400, actualCost: 26100, variancePercent: -1.1, completionPct: 100, healthScore: 93,
|
||
contractorName: 'Plano Siding Crew', contractorPhone: '(972) 555-9002',
|
||
budgetBreakdown: [
|
||
{ name: 'Owens Corning', category: 'Materials', allocated: 14200, committed: 14200, actual: 13900 },
|
||
{ name: 'Plano Siding Crew', category: 'Labor', allocated: 9600, committed: 9600, actual: 9600 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 800, committed: 800, actual: 800 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 1800, committed: 200, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-01-28', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered',status: 'completed', date: '2026-01-30', notes: '' },
|
||
{ id: 'm3', name: 'Permit Pulled', status: 'completed', date: '2026-02-05', notes: '' },
|
||
{ id: 'm4', name: 'Prep & Demo', status: 'completed', date: '2026-02-15', notes: 'Old siding removed cleanly.' },
|
||
{ id: 'm5', name: 'Siding Install', status: 'completed', date: '2026-02-19', notes: '' },
|
||
{ id: 'm6', name: 'Trim & Caulk', status: 'completed', date: '2026-02-20', notes: '' },
|
||
{ id: 'm7', name: 'Final Inspection', status: 'completed', date: '2026-02-21', notes: 'Insurance proceeds released.' },
|
||
{ id: 'm8', name: 'Invoice & Payment',status: 'completed', date: '2026-02-22', notes: 'Full payment received.' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 25%', amount: 6600, status: 'paid', dueDate: '2026-01-28', paidDate: '2026-01-28' },
|
||
{ id: 'inv2', description: 'Progress — 50%', amount: 13200,status: 'paid', dueDate: '2026-02-19', paidDate: '2026-02-19' },
|
||
{ id: 'inv3', description: 'Final — 25%', amount: 6600, status: 'paid', dueDate: '2026-02-22', paidDate: '2026-02-22' },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-01-28', action: 'Contract Executed', user: 'Felicity Fast', details: '' },
|
||
{ date: '2026-02-15', action: 'Production Started', user: 'Amanda Manager', details: 'Demo and install started same day.' },
|
||
{ date: '2026-02-22', action: 'Project Closed', user: 'Amanda Manager', details: 'Insurance check received and deposited. File closed.' },
|
||
],
|
||
},
|
||
kl_036: {
|
||
estimatedAmount: 22100, actualCost: 21800, variancePercent: -1.4, completionPct: 100, healthScore: 97,
|
||
contractorName: 'Plano Roofing Crew C', contractorPhone: '(972) 555-9004',
|
||
budgetBreakdown: [
|
||
{ name: 'GAF Materials', category: 'Materials', allocated: 12000, committed: 12000, actual: 11800 },
|
||
{ name: 'Plano Roofing Crew C', category: 'Labor', allocated: 7800, committed: 7800, actual: 7800 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 600 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 1700, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-01-30', notes: 'Full payment upfront.' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-01-31', notes: '' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-02-07', notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'completed', date: '2026-02-18', notes: 'Clean. No deck issues.' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'completed', date: '2026-02-19', notes: '2-day install complete.' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'completed', date: '2026-02-20', notes: 'Outstanding quality noted.' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'completed', date: '2026-01-30', notes: 'Pre-paid.' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Full Payment — 100%', amount: 22100, status: 'paid', dueDate: '2026-01-30', paidDate: '2026-01-30' },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-01-30', action: 'Contract Executed', user: 'Fiona Field', details: 'Cash client. Full payment upfront.' },
|
||
{ date: '2026-02-18', action: 'Production Started', user: 'Adam Admin', details: 'Tear-off started.' },
|
||
{ date: '2026-02-20', action: 'Project Closed', user: 'Adam Admin', details: 'Outstanding quality. Referral bonus owed per agreement.' },
|
||
],
|
||
},
|
||
kl_037: {
|
||
estimatedAmount: 8900, actualCost: 8650, variancePercent: -2.8, completionPct: 100, healthScore: 96,
|
||
contractorName: 'Plano Roofing Crew A', contractorPhone: '(972) 555-9001',
|
||
budgetBreakdown: [
|
||
{ name: 'LeafGuard DFW', category: 'Gutter Materials', allocated: 3800, committed: 3800, actual: 3600 },
|
||
{ name: 'James Hardie', category: 'Fascia Materials', allocated: 1800, committed: 1800, actual: 1800 },
|
||
{ name: 'Plano Roofing Crew A', category: 'Labor', allocated: 2600, committed: 2600, actual: 2600 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 400, committed: 400, actual: 400 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 300, committed: 0, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-02-10', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-02-12', notes: '' },
|
||
{ id: 'm3', name: 'Fascia Repair', status: 'completed', date: '2026-02-28', notes: 'Fascia replaced on all 4 sides.' },
|
||
{ id: 'm4', name: 'Gutter Install', status: 'completed', date: '2026-03-01', notes: '1-day install.' },
|
||
{ id: 'm5', name: 'Final Walkthrough', status: 'completed', date: '2026-03-02', notes: 'Customer gave 3 referral cards.' },
|
||
{ id: 'm6', name: 'Invoice & Payment', status: 'completed', date: '2026-03-02', notes: '' },
|
||
],
|
||
changeOrders: [],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 50%', amount: 4450, status: 'paid', dueDate: '2026-02-10', paidDate: '2026-02-10' },
|
||
{ id: 'inv2', description: 'Final — 50%', amount: 4450, status: 'paid', dueDate: '2026-03-02', paidDate: '2026-03-02' },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-02-10', action: 'Contract Executed', user: 'Felix Fixer', details: '' },
|
||
{ date: '2026-02-28', action: 'Production Started', user: 'Adam Admin', details: 'Fascia repair started.' },
|
||
{ date: '2026-03-02', action: 'Project Closed', user: 'Adam Admin', details: 'Complete. Customer gave referral cards to 3 neighbors.' },
|
||
],
|
||
},
|
||
kl_038: {
|
||
estimatedAmount: 19400, actualCost: 20100, variancePercent: 3.6, completionPct: 100, healthScore: 87,
|
||
contractorName: 'Plano Roofing Crew B', contractorPhone: '(972) 555-9003',
|
||
budgetBreakdown: [
|
||
{ name: 'James Hardie', category: 'Materials', allocated: 10400, committed: 10400, actual: 11200 },
|
||
{ name: 'Plano Roofing Crew B', category: 'Labor', allocated: 6800, committed: 6800, actual: 6800 },
|
||
{ name: 'City of Plano', category: 'Permits', allocated: 600, committed: 600, actual: 600 },
|
||
{ name: 'Reserve Fund', category: 'Contingency', allocated: 1600, committed: 1600, actual: 0 },
|
||
],
|
||
milestones: [
|
||
{ id: 'm1', name: 'Contract Signed', status: 'completed', date: '2026-02-04', notes: '' },
|
||
{ id: 'm2', name: 'Materials Ordered', status: 'completed', date: '2026-02-06', notes: '' },
|
||
{ id: 'm3', name: 'Permits Pulled', status: 'completed', date: '2026-02-15', notes: '' },
|
||
{ id: 'm4', name: 'Tear-off & Decking', status: 'completed', date: '2026-03-10', notes: '3 rotted deck sheets replaced — CO raised.' },
|
||
{ id: 'm5', name: 'Roofing Install', status: 'completed', date: '2026-03-12', notes: '' },
|
||
{ id: 'm6', name: 'Final Inspection', status: 'completed', date: '2026-03-18', notes: '' },
|
||
{ id: 'm7', name: 'Invoice & Payment', status: 'completed', date: '2026-03-20', notes: 'Complex insurance supplemental took 2 extra weeks.' },
|
||
],
|
||
changeOrders: [
|
||
{ id: 'co1', title: 'Decking Replacement — 3 Sheets', amount: 720, status: 'approved', requestedBy: 'Felicity Fast', date: '2026-03-10', description: 'Three rotted OSB sheets discovered during tear-off. Replaced.' },
|
||
],
|
||
rfis: [],
|
||
invoices: [
|
||
{ id: 'inv1', description: 'Deposit — 30%', amount: 5820, status: 'paid', dueDate: '2026-02-04', paidDate: '2026-02-04' },
|
||
{ id: 'inv2', description: 'Progress — 40%', amount: 7760, status: 'paid', dueDate: '2026-03-12', paidDate: '2026-03-14' },
|
||
{ id: 'inv3', description: 'Final — 30%', amount: 5820, status: 'paid', dueDate: '2026-03-20', paidDate: '2026-03-20' },
|
||
],
|
||
workTimeline: [
|
||
{ date: '2026-02-04', action: 'Contract Executed', user: 'Felicity Fast', details: '' },
|
||
{ date: '2026-03-10', action: 'Production Started', user: 'Adam Admin', details: 'Tear-off found 3 rotted deck sheets. CO raised.' },
|
||
{ date: '2026-03-12', action: 'Install Complete', user: 'Adam Admin', details: 'Roofing complete.' },
|
||
{ date: '2026-03-20', action: 'Project Closed', user: 'Adam Admin', details: 'Longest close — 7 weeks due to complex insurance supplemental.' },
|
||
],
|
||
},
|
||
};
|
||
|
||
// --- CONTEXT SETUP ---
|
||
|
||
const MockStoreContext = createContext();
|
||
|
||
export const MockStoreProvider = ({ children }) => {
|
||
const [properties, setProperties] = useState([]);
|
||
const [users, setUsers] = useState(MOCK_USERS);
|
||
const [meetings, setMeetings] = useState(MOCK_MEETINGS);
|
||
const [salesHistory, setSalesHistory] = useState(MOCK_SALES_HISTORY);
|
||
|
||
// New Data States
|
||
const [owners, setOwners] = useState(MOCK_OWNERS);
|
||
const [personnel, setPersonnel] = useState(MOCK_PERSONNEL);
|
||
const [vendors, setVendors] = useState(MOCK_VENDORS);
|
||
const [documents, setDocuments] = useState(MOCK_DOCUMENTS);
|
||
const [projects, setProjects] = useState(MOCK_PROJECTS);
|
||
const [orders, setOrders] = useState(MOCK_ORDERS);
|
||
const [vendorInvoices, setVendorInvoices] = useState(MOCK_VENDOR_INVOICES);
|
||
const [leads, setLeads] = useState([]);
|
||
const [dispatchLeads, setDispatchLeads] = useState(DISPATCH_LEADS_INITIAL);
|
||
const [templates, setTemplates] = useState(MASTER_TEMPLATES_INITIAL);
|
||
const [estimates, setEstimates] = useState(MOCK_ESTIMATES_INITIAL);
|
||
const [templateAccessRoles, setTemplateAccessRoles] = useState([]);
|
||
const [templateAccessUsers, setTemplateAccessUsers] = useState([]);
|
||
const [templateAccessExcluded, setTemplateAccessExcluded] = useState([]);
|
||
const [kanbanColumns, setKanbanColumns] = useState(KANBAN_COLUMNS_INITIAL);
|
||
const [kanbanLeads, setKanbanLeads] = useState(KANBAN_LEADS_INITIAL);
|
||
|
||
// Commission Settings — Org defaults + per-user overrides
|
||
const [orgCommissionDefaults, setOrgCommissionDefaults] = useState({
|
||
type: 'percent_gross',
|
||
rate: 0,
|
||
});
|
||
const [userCommissionOverrides, setUserCommissionOverrides] = useState([
|
||
{ userId: 'p1', userName: 'Jesus Gonzales', enabled: true, type: 'percent_gross', rate: 12 },
|
||
{ userId: 'p2', userName: 'Sarah Sales', enabled: true, type: 'flat', rate: 2000 },
|
||
]);
|
||
|
||
// ── Org Settings & Access Control ──
|
||
const [orgSettings, setOrgSettings] = useState({
|
||
orgName: 'LynkedUp Pro Roofing',
|
||
orgEmail: 'info@lynkeduppro.com',
|
||
orgPhone: '214-555-1000',
|
||
orgAddress: '5800 Legacy Dr, Plano, TX 75024',
|
||
timezone: 'America/Chicago',
|
||
});
|
||
|
||
const [orgRoles, setOrgRoles] = useState([
|
||
{
|
||
id: 'role_owner',
|
||
name: 'Org Owner',
|
||
key: 'OWNER',
|
||
description: 'Full access to all modules, settings, billing, and access control',
|
||
isSystem: true,
|
||
memberCount: 2,
|
||
color: '#fda913',
|
||
},
|
||
{
|
||
id: 'role_admin',
|
||
name: 'Admin / Manager',
|
||
key: 'ADMIN',
|
||
description: 'Manage jobs, view financials, assign team, verify leads, configure commissions',
|
||
isSystem: true,
|
||
memberCount: 3,
|
||
color: '#3b82f6',
|
||
},
|
||
{
|
||
id: 'role_sales',
|
||
name: 'Sales Rep',
|
||
key: 'SALES_REP',
|
||
description: 'View/manage assigned jobs, verify canvasser leads, view own commission',
|
||
isSystem: true,
|
||
memberCount: 2,
|
||
color: '#a855f7',
|
||
},
|
||
{
|
||
id: 'role_canvasser',
|
||
name: 'Canvasser',
|
||
key: 'CANVASSER',
|
||
description: 'Log door knocks, add leads, view leaderboard, view verified leads',
|
||
isSystem: true,
|
||
memberCount: 5,
|
||
color: '#10b981',
|
||
},
|
||
{
|
||
id: 'role_subcontractor',
|
||
name: 'Subcontractor',
|
||
key: 'SUBCONTRACTOR',
|
||
description: 'Receive tasks, update status, reply to tasks, log expenses',
|
||
isSystem: true,
|
||
memberCount: 1,
|
||
color: '#06b6d4',
|
||
},
|
||
]);
|
||
|
||
const [orgPermissions, setOrgPermissions] = useState({
|
||
jobs: { OWNER: ['view','edit','delete'], ADMIN: ['view','edit','delete'], SALES_REP: ['view','edit'], CANVASSER: [], SUBCONTRACTOR: [] },
|
||
financials: { OWNER: ['view','edit'], ADMIN: ['view','edit'], SALES_REP: ['view'], CANVASSER: [], SUBCONTRACTOR: [] },
|
||
commission: { OWNER: ['view','edit'], ADMIN: ['view','edit'], SALES_REP: ['view'], CANVASSER: [], SUBCONTRACTOR: [] },
|
||
team: { OWNER: ['view','invite','remove'], ADMIN: ['view','invite'], SALES_REP: ['view'], CANVASSER: [], SUBCONTRACTOR: [] },
|
||
leads: { OWNER: ['view','log','verify'], ADMIN: ['view','log','verify'], SALES_REP: ['view','verify'], CANVASSER: ['view','log'], SUBCONTRACTOR: [] },
|
||
leaderboard: { OWNER: ['view'], ADMIN: ['view'], SALES_REP: ['view'], CANVASSER: ['view'], SUBCONTRACTOR: [] },
|
||
subcontractor_tasks: { OWNER: ['view','assign'], ADMIN: ['view','assign'], SALES_REP: [], CANVASSER: [], SUBCONTRACTOR: ['view','receive'] },
|
||
});
|
||
|
||
const PERMISSION_MODULES = {
|
||
jobs: { label: 'Jobs', actions: ['view','edit','delete'] },
|
||
financials: { label: 'Financials', actions: ['view','edit'] },
|
||
commission: { label: 'Commission Settings', actions: ['view','edit'] },
|
||
team: { label: 'Team Members', actions: ['view','invite','remove'] },
|
||
leads: { label: 'Leads & Door Knocks', actions: ['view','log','verify'] },
|
||
leaderboard: { label: 'Leaderboard', actions: ['view'] },
|
||
subcontractor_tasks: { label: 'Subcontractor Tasks', actions: ['view','assign','receive'] },
|
||
};
|
||
|
||
// Per-user permission overrides: grant or deny specific module+action regardless of role
|
||
// Shape: [{ userId, moduleKey, action, effect: 'grant' | 'deny' }]
|
||
const [userPermissionOverrides, setUserPermissionOverrides] = useState([]);
|
||
|
||
const [roleCommissionOverrides, setRoleCommissionOverrides] = useState([
|
||
{ roleKey: 'SALES_REP', enabled: true, type: 'percent_net', rate: 5 },
|
||
{ roleKey: 'CANVASSER', enabled: true, type: 'flat', rate: 300 },
|
||
]);
|
||
|
||
const [orgMembers, setOrgMembers] = useState([
|
||
{ id: 'om_001', userId: 'owner_001', name: 'Justin Johnson', email: 'justin@lynkeduppro.com', roleKey: 'OWNER', status: 'active', joinedAt: '2024-01-15', lastActivity: '2025-05-12', assignedJobsCount: 14 },
|
||
{ id: 'om_002', userId: 'owner_002', name: 'Maria Garcia', email: 'maria@lynkeduppro.com', roleKey: 'OWNER', status: 'active', joinedAt: '2024-01-15', lastActivity: '2025-05-10', assignedJobsCount: 8 },
|
||
{ id: 'om_003', userId: 'ADM01', name: 'Admin One', email: 'admin1@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-02-10', lastActivity: '2025-05-11', assignedJobsCount: 6 },
|
||
{ id: 'om_004', userId: 'ADM02', name: 'Admin Two', email: 'admin2@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-03-01', lastActivity: '2025-05-09', assignedJobsCount: 4 },
|
||
{ id: 'om_005', userId: 'ADM03', name: 'Admin Three', email: 'admin3@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-04-20', lastActivity: '2025-05-08', assignedJobsCount: 3 },
|
||
{ id: 'om_006', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', roleKey: 'SALES_REP', status: 'active', joinedAt: '2024-03-15', lastActivity: '2025-05-12', assignedJobsCount: 9, individualCommission: { enabled: true, type: 'percent_gross', rate: 12 } },
|
||
{ id: 'om_007', userId: 'p2', name: 'Sarah Sales', email: 'sarah@lynkeduppro.com', roleKey: 'SALES_REP', status: 'active', joinedAt: '2024-04-01', lastActivity: '2025-05-11', assignedJobsCount: 7, individualCommission: { enabled: true, type: 'flat', rate: 2000 } },
|
||
{ id: 'om_008', userId: 'FA001', name: 'Frank Agent', email: 'agent1@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-10', lastActivity: '2025-05-12', assignedJobsCount: 2 },
|
||
{ id: 'om_009', userId: 'FA002', name: 'Fiona Field', email: 'agent2@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-15', lastActivity: '2025-05-07', assignedJobsCount: 3 },
|
||
{ id: 'om_010', userId: 'FA003', name: 'Fred Flyer', email: 'agent3@plano.com', roleKey: 'CANVASSER', status: 'invited', joinedAt: '2024-06-01', lastActivity: null, assignedJobsCount: 0 },
|
||
{ id: 'om_011', userId: 'FA004', name: 'Felicity Fast', email: 'agent4@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-06-10', lastActivity: '2025-05-06', assignedJobsCount: 1 },
|
||
{ id: 'om_012', userId: 'FA005', name: 'Felix Fixer', email: 'agent5@plano.com', roleKey: 'CANVASSER', status: 'suspended', joinedAt: '2024-07-01', lastActivity: '2025-03-20', assignedJobsCount: 0 },
|
||
{ id: 'om_013', userId: 'sub_001', name: 'Carlos Subcontractor', email: 'carlos@electricpro.com', roleKey: 'SUBCONTRACTOR', status: 'active', joinedAt: '2024-08-01', lastActivity: '2025-05-10', assignedJobsCount: 5 },
|
||
]);
|
||
|
||
// Initialize properties once
|
||
useEffect(() => {
|
||
const data = generateProperties();
|
||
setProperties(data);
|
||
}, []);
|
||
|
||
const updatePropertyStatus = (id, newStatus) => {
|
||
setProperties(prev => prev.map(p =>
|
||
p.id === id ? { ...p, canvassingStatus: newStatus } : p
|
||
));
|
||
};
|
||
|
||
const addMeeting = (meeting) => {
|
||
setMeetings(prev => [...prev, { ...meeting, id: `m${prev.length + 1}` }]);
|
||
};
|
||
|
||
// --- NEW ACTIONS for ADMIN CENTER ---
|
||
|
||
const assignAgent = (propertyId, agentId) => {
|
||
let found = false;
|
||
setProperties(prev => prev.map(p => {
|
||
if (p.id === propertyId || p.propertyData.propertyId === propertyId) {
|
||
found = true;
|
||
return { ...p, assignedAgentId: agentId, canvassingStatus: "Assigned" };
|
||
}
|
||
return p;
|
||
}));
|
||
|
||
if (!found) {
|
||
toast.error("Failed to assign agent: Property not found.");
|
||
return false;
|
||
}
|
||
|
||
const agent = users.find(u => u.id === agentId);
|
||
toast.success(`Agent ${agent ? agent.name : 'assigned'} linked to property.`);
|
||
logger.info(`Property ${propertyId} assigned to Agent ${agentId}`);
|
||
return true;
|
||
};
|
||
|
||
const markLeadContacted = (propertyId) => {
|
||
let found = false;
|
||
setProperties(prev => prev.map(p => {
|
||
if (p.id === propertyId || p.propertyData.propertyId === propertyId) {
|
||
found = true;
|
||
return { ...p, lastContactDate: new Date().toISOString() };
|
||
}
|
||
return p;
|
||
}));
|
||
|
||
if (!found) {
|
||
toast.error("Action failed: Property not found.");
|
||
return false;
|
||
}
|
||
|
||
toast.success("Lead marked as contacted.");
|
||
return true;
|
||
};
|
||
|
||
const sendReminder = (propertyId) => {
|
||
const prop = properties.find(p => p.id === propertyId || p.propertyData.propertyId === propertyId);
|
||
if (!prop) {
|
||
toast.error("Failed to send reminder: Client not found.");
|
||
return false;
|
||
}
|
||
|
||
const name = prop?.ownerData?.fullName || "Client";
|
||
toast.success(`Reminder sent to ${name}`);
|
||
logger.info(`Reminder sent to property ${propertyId}`);
|
||
return true;
|
||
};
|
||
|
||
return (
|
||
<MockStoreContext.Provider value={{
|
||
properties,
|
||
users,
|
||
meetings,
|
||
salesHistory, // Exported for Leaderboard
|
||
canvasserHistory: MOCK_CANVASSER_HISTORY, // Exported for Canvasser Leaderboard
|
||
owners,
|
||
personnel,
|
||
vendors,
|
||
documents,
|
||
projects,
|
||
orders,
|
||
vendorInvoices,
|
||
leads,
|
||
addLead: (lead) => {
|
||
const newLead = {
|
||
...lead,
|
||
id: `lead_${Date.now()}`,
|
||
createdAt: new Date().toISOString(),
|
||
status: 'New',
|
||
};
|
||
setLeads(prev => [newLead, ...prev]);
|
||
return newLead;
|
||
},
|
||
|
||
// Templates
|
||
templates,
|
||
addTemplate: (tpl) => {
|
||
const newTpl = { ...tpl, id: `tpl_${Date.now()}` };
|
||
setTemplates(prev => [...prev, newTpl]);
|
||
return newTpl;
|
||
},
|
||
updateTemplate: (id, data) => {
|
||
setTemplates(prev => prev.map(t => t.id === id ? { ...t, ...data } : t));
|
||
},
|
||
deleteTemplate: (id) => {
|
||
setTemplates(prev => prev.filter(t => t.id !== id));
|
||
},
|
||
templateAccessRoles,
|
||
templateAccessUsers,
|
||
templateAccessExcluded,
|
||
setTemplateAccessRoles,
|
||
setTemplateAccessUsers,
|
||
setTemplateAccessExcluded,
|
||
|
||
// Kanban
|
||
kanbanColumns,
|
||
kanbanLeads,
|
||
moveKanbanLead: (leadId, newColumnId, newBucketId) => {
|
||
setKanbanLeads(prev => prev.map(l => {
|
||
if (l.id !== leadId) return l;
|
||
const col = KANBAN_COLUMNS_INITIAL.find(c => c.id === newColumnId) ||
|
||
kanbanColumns.find(c => c.id === newColumnId);
|
||
const stageName = col?.name ?? newColumnId;
|
||
const newActivity = {
|
||
from: l.bucketId
|
||
? l.kanbanColumns?.find?.(c => c.id === l.bucketId)?.name ?? 'Bucket'
|
||
: (kanbanColumns.find(c => c.id === l.columnId)?.name ?? l.columnId),
|
||
to: stageName,
|
||
by: 'You',
|
||
date: new Date().toISOString().slice(0, 10),
|
||
note: null,
|
||
};
|
||
return { ...l, columnId: newColumnId, bucketId: newBucketId ?? null, activity: [newActivity, ...l.activity] };
|
||
}));
|
||
},
|
||
addKanbanColumn: (col) => {
|
||
const newCol = { ...col, id: `kc_custom_${Date.now()}` };
|
||
setKanbanColumns(prev => [...prev, newCol]);
|
||
return newCol;
|
||
},
|
||
updateKanbanColumn: (id, data) => {
|
||
setKanbanColumns(prev => prev.map(c => c.id === id ? { ...c, ...data } : c));
|
||
},
|
||
deleteKanbanColumn: (id) => {
|
||
setKanbanColumns(prev => prev.filter(c => c.id !== id));
|
||
},
|
||
updateKanbanLead: (id, data) => {
|
||
setKanbanLeads(prev => prev.map(l => l.id === id ? { ...l, ...data } : l));
|
||
},
|
||
kanbanProjectData: KANBAN_PROJECT_DATA,
|
||
|
||
// Estimates
|
||
estimates,
|
||
addEstimate: (est) => {
|
||
const newEst = { ...est, id: `est_${Date.now()}`, date: new Date().toISOString().slice(0, 10) };
|
||
setEstimates(prev => [newEst, ...prev]);
|
||
return newEst;
|
||
},
|
||
updateEstimate: (id, data) => {
|
||
setEstimates(prev => prev.map(e => e.id === id ? { ...e, ...data } : e));
|
||
},
|
||
|
||
// LynkDispatch
|
||
dispatchLeads,
|
||
assignDispatchLead: (leadId, repId) => {
|
||
setDispatchLeads(prev => prev.map(l =>
|
||
l.id === leadId ? { ...l, assignedRepId: repId, status: 'confirmed' } : l
|
||
));
|
||
},
|
||
updateLeadNotes: (leadId, notes) => {
|
||
setDispatchLeads(prev => prev.map(l =>
|
||
l.id === leadId ? { ...l, notes } : l
|
||
));
|
||
},
|
||
|
||
updatePropertyStatus,
|
||
addMeeting,
|
||
updateUser: (updatedUser) => {
|
||
setUsers(prev => prev.map(u => u.id === updatedUser.id ? updatedUser : u));
|
||
},
|
||
assignAgent,
|
||
markLeadContacted,
|
||
sendReminder,
|
||
|
||
// Commission Settings
|
||
orgCommissionDefaults,
|
||
userCommissionOverrides,
|
||
updateOrgCommissionDefaults: (defaults) => {
|
||
setOrgCommissionDefaults(prev => ({ ...prev, ...defaults }));
|
||
},
|
||
setUserCommissionOverride: (override) => {
|
||
setUserCommissionOverrides(prev => {
|
||
const idx = prev.findIndex(o => o.userId === override.userId);
|
||
if (idx >= 0) return prev.map((o, i) => i === idx ? { ...o, ...override } : o);
|
||
return [...prev, override];
|
||
});
|
||
},
|
||
removeUserCommissionOverride: (userId) => {
|
||
setUserCommissionOverrides(prev => prev.filter(o => o.userId !== userId));
|
||
},
|
||
updateProject: (projectId, data) => {
|
||
setProjects(prev => prev.map(p => p.id === projectId ? { ...p, ...data } : p));
|
||
},
|
||
addJobTeamMember: (projectId, member) => {
|
||
setProjects(prev => prev.map(p => p.id === projectId
|
||
? { ...p, teamMembers: [...(p.teamMembers || []), { ...member, id: `jtm_${Date.now()}` }] }
|
||
: p
|
||
));
|
||
},
|
||
removeJobTeamMember: (projectId, memberId) => {
|
||
setProjects(prev => prev.map(p => p.id === projectId
|
||
? { ...p, teamMembers: (p.teamMembers || []).filter(m => m.id !== memberId) }
|
||
: p
|
||
));
|
||
},
|
||
updateJobTeamMember: (projectId, memberId, data) => {
|
||
setProjects(prev => prev.map(p => p.id === projectId
|
||
? { ...p, teamMembers: (p.teamMembers || []).map(m => m.id === memberId ? { ...m, ...data } : m) }
|
||
: p
|
||
));
|
||
},
|
||
|
||
// Org Settings & Access Control
|
||
orgSettings,
|
||
updateOrgSettings: (data) => setOrgSettings(prev => ({ ...prev, ...data })),
|
||
orgRoles,
|
||
updateOrgRole: (roleId, data) => setOrgRoles(prev => prev.map(r => r.id === roleId ? { ...r, ...data } : r)),
|
||
orgPermissions,
|
||
PERMISSION_MODULES,
|
||
updateOrgPermissions: (moduleKey, roleKey, actions) => {
|
||
setOrgPermissions(prev => ({
|
||
...prev,
|
||
[moduleKey]: { ...prev[moduleKey], [roleKey]: actions },
|
||
}));
|
||
},
|
||
userPermissionOverrides,
|
||
setUserPermissionOverride: (userId, moduleKey, action, effect) => {
|
||
setUserPermissionOverrides(prev => {
|
||
const filtered = prev.filter(o => !(o.userId === userId && o.moduleKey === moduleKey && o.action === action));
|
||
return [...filtered, { userId, moduleKey, action, effect }];
|
||
});
|
||
},
|
||
removeUserPermissionOverride: (userId, moduleKey, action) => {
|
||
setUserPermissionOverrides(prev =>
|
||
prev.filter(o => !(o.userId === userId && o.moduleKey === moduleKey && o.action === action))
|
||
);
|
||
},
|
||
clearUserPermissionOverrides: (userId) => {
|
||
setUserPermissionOverrides(prev => prev.filter(o => o.userId !== userId));
|
||
},
|
||
roleCommissionOverrides,
|
||
setRoleCommissionOverride: (override) => {
|
||
setRoleCommissionOverrides(prev => {
|
||
const idx = prev.findIndex(o => o.roleKey === override.roleKey);
|
||
if (idx >= 0) return prev.map((o, i) => i === idx ? { ...o, ...override } : o);
|
||
return [...prev, override];
|
||
});
|
||
},
|
||
removeRoleCommissionOverride: (roleKey) => {
|
||
setRoleCommissionOverrides(prev => prev.filter(o => o.roleKey !== roleKey));
|
||
},
|
||
orgMembers,
|
||
updateOrgMember: (memberId, data) => {
|
||
setOrgMembers(prev => prev.map(m => m.id === memberId ? { ...m, ...data } : m));
|
||
},
|
||
addOrgMember: (member) => {
|
||
const newMember = {
|
||
...member,
|
||
id: `om_${Date.now()}`,
|
||
joinedAt: new Date().toISOString().slice(0, 10),
|
||
lastActivity: null,
|
||
assignedJobsCount: 0,
|
||
status: member.status || 'invited',
|
||
};
|
||
setOrgMembers(prev => [...prev, newMember]);
|
||
return newMember;
|
||
},
|
||
removeOrgMember: (memberId) => {
|
||
setOrgMembers(prev => prev.filter(m => m.id !== memberId));
|
||
},
|
||
suspendOrgMember: (memberId) => {
|
||
setOrgMembers(prev => prev.map(m => m.id === memberId ? { ...m, status: 'suspended' } : m));
|
||
},
|
||
reactivateOrgMember: (memberId) => {
|
||
setOrgMembers(prev => prev.map(m => m.id === memberId ? { ...m, status: 'active' } : m));
|
||
},
|
||
resendOrgInvite: (memberId) => {
|
||
toast.success('Invite resent successfully');
|
||
},
|
||
}}>
|
||
{children}
|
||
</MockStoreContext.Provider>
|
||
);
|
||
};
|
||
|
||
export const useMockStore = () => useContext(MockStoreContext);
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Lead Creation Form Options
|
||
// ---------------------------------------------------------------------------
|
||
export const LEAD_FORM_OPTIONS = {
|
||
leadTypes: [
|
||
'Residential',
|
||
'Commercial',
|
||
'Multi-Family',
|
||
'Industrial',
|
||
'HOA / Community',
|
||
],
|
||
leadSources: [
|
||
'Door Knock',
|
||
'Referral',
|
||
'Storm Chase',
|
||
'Online / Web',
|
||
'Mailer / Postcard',
|
||
'Sign Call',
|
||
'Insurance Agent Referral',
|
||
'Repeat Customer',
|
||
'Social Media',
|
||
'Other',
|
||
],
|
||
workTypes: [
|
||
'Roof Replacement',
|
||
'Roof Repair',
|
||
'Gutters',
|
||
'Siding',
|
||
'Windows',
|
||
'Solar',
|
||
'Inspection Only',
|
||
'Emergency Tarping',
|
||
'Other',
|
||
],
|
||
tradeTypes: [
|
||
'Roofing',
|
||
'Gutter',
|
||
'Siding',
|
||
'Windows',
|
||
'Solar',
|
||
'General Contractor',
|
||
],
|
||
urgencyLevels: [
|
||
{ label: 'Standard', value: 'standard', color: '#6B7280' },
|
||
{ label: 'High', value: 'high', color: '#F59E0B' },
|
||
{ label: 'Emergency', value: 'emergency', color: '#EF4444' },
|
||
],
|
||
priorityLevels: [
|
||
{ label: 'Low', value: 'low', color: '#6B7280' },
|
||
{ label: 'Medium', value: 'medium', color: '#3B82F6' },
|
||
{ label: 'High', value: 'high', color: '#F59E0B' },
|
||
{ label: 'Critical', value: 'critical', color: '#EF4444' },
|
||
],
|
||
insuranceCompanies: [
|
||
'State Farm',
|
||
'Allstate',
|
||
'USAA',
|
||
'Farmers Insurance',
|
||
'Liberty Mutual',
|
||
'Progressive',
|
||
'Nationwide',
|
||
'American Family',
|
||
'Travelers',
|
||
'GEICO',
|
||
'Chubb',
|
||
'Erie Insurance',
|
||
'Auto-Owners',
|
||
'Other',
|
||
],
|
||
claimStatuses: [
|
||
'Not Filed',
|
||
'Filed – Pending Adjuster',
|
||
'Adjuster Scheduled',
|
||
'Adjuster Completed',
|
||
'Approved',
|
||
'Denied',
|
||
'Supplement In Progress',
|
||
'Closed',
|
||
],
|
||
phoneTypes: ['Mobile', 'Home', 'Work', 'Other'],
|
||
emailTypes: ['Personal', 'Work', 'Other'],
|
||
usStates: [
|
||
'AL','AK','AZ','AR','CA','CO','CT','DE','FL','GA',
|
||
'HI','ID','IL','IN','IA','KS','KY','LA','ME','MD',
|
||
'MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ',
|
||
'NM','NY','NC','ND','OH','OK','OR','PA','RI','SC',
|
||
'SD','TN','TX','UT','VT','VA','WA','WV','WI','WY',
|
||
],
|
||
};
|