import React, { createContext, useContext, useState, useEffect, useMemo } from 'react'; import { toast } from 'sonner'; import { logger } from '../utils/logger'; // Fixed "today" for the demo so seeded dates never go stale or land in the future. export const DEMO_TODAY = new Date('2026-05-29T12:00:00'); // --- 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 Castillo", "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: 'LUP-1040', email: 'agent1@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Cody Tatum', xp: 12450, doorsKnocked: 342, leadsGained: 45, appointmentsSet: 18, streakDays: 5, achievements: ['Hot Spot Hunter', 'Storm Chaser'] }, { id: 'e2', type: 'employee', empId: 'LUP-1041', email: 'agent2@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Hannah Reyes', xp: 8200, doorsKnocked: 215, leadsGained: 30, appointmentsSet: 12, streakDays: 2, achievements: ['Star Closer'] }, { id: 'e3', type: 'employee', empId: 'LUP-1042', email: 'agent3@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Travis Boone', xp: 2100, doorsKnocked: 85, leadsGained: 5, appointmentsSet: 2, streakDays: 0, achievements: [] }, { id: 'e4', type: 'employee', empId: 'LUP-1043', email: 'agent4@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Shelby Greer', xp: 15600, doorsKnocked: 410, leadsGained: 65, appointmentsSet: 24, streakDays: 8, achievements: ['Map Master', 'Hot Spot Hunter', 'Storm Chaser'] }, { id: 'e5', type: 'employee', empId: 'LUP-1044', email: 'agent5@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Dalton Pruitt', xp: 5400, doorsKnocked: 156, leadsGained: 20, appointmentsSet: 8, streakDays: 1, achievements: ['First Knock'] }, // Admins (3 Admins) { id: 'a1', type: 'employee', empId: 'LUP-1010', email: 'admin@plano.com', password: 'password', role: 'ADMIN', name: 'Wade Hollis' }, { id: 'a2', type: 'employee', empId: 'LUP-1011', email: 'admin2@plano.com', password: 'password', role: 'ADMIN', name: 'Darlene Brooks' }, { id: 'a3', type: 'employee', empId: 'LUP-1012', email: 'admin3@plano.com', password: 'password', role: 'ADMIN', name: 'Roy Schaefer' }, // --- NEW ROLES (Owners Box) --- // Owner 1 — Active, strong financials, one project under dispute { id: 'own_001', type: 'owner', username: 'justin', email: 'justin@lynkeduppro.com', password: 'password', role: 'OWNER', empId: 'LUP-1001', name: 'Justin Johnson', companyName: 'Johnson Development Group', phone: '972-555-1234' }, // Owner 2 — High Volume, large commercial portfolio { id: 'own_002', type: 'owner', username: 'diana', email: 'diana@reevesconstruction.com', password: 'password', role: 'OWNER', empId: 'LUP-1002', 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', empId: 'LUP-3001', name: 'Mike Brennan', 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', empId: 'LUP-2001', name: 'Carlos Mendoza', companyName: 'Electric Pro Services', tradeType: 'Electrical', phone: '469-555-9012' }, { id: 'sub_002', type: 'subcontractor', username: 'maya', email: 'maya@skylineroof.com', password: 'password', role: 'SUBCONTRACTOR', empId: 'LUP-2002', name: 'Maya Patel', companyName: 'Skyline Roof Crew', tradeType: 'Roofing', phone: '214-555-1247' }, { id: 'sub_003', type: 'subcontractor', username: 'dwayne', email: 'dwayne@hollandpaint.com', password: 'password', role: 'SUBCONTRACTOR', empId: 'LUP-2003', name: 'Dwayne Boudreaux', companyName: 'Holland Painting Co.', tradeType: 'Painting', phone: '972-555-8312' }, { id: 'sub_004', type: 'subcontractor', username: 'jennifer', email: 'jenn@wuplumbing.com', password: 'password', role: 'SUBCONTRACTOR', empId: 'LUP-2004', name: 'Jennifer Castillo', companyName: 'Wu Plumbing', tradeType: 'Plumbing', phone: '469-555-3340' }, { id: 'sub_005', type: 'subcontractor', username: 'greg', email: 'greg@alstonwindows.com', password: 'password', role: 'SUBCONTRACTOR', empId: 'LUP-2005', name: 'Greg Alston', companyName: 'Alston Window Pros', tradeType: 'Windows & Glazing', phone: '214-555-0299' }, // 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 = [ // Cody Tatum (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' }, // Hannah Reyes (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' }, // Travis Boone (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' }, // Shelby Greer (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' }, // Dalton Pruitt (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 = [ // Cody Tatum (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' }, // Hannah Reyes (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' }, // Travis Boone (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' }, // Shelby Greer (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' }, // Dalton Pruitt (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: 'Cody Tatum', 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: 'Hannah Reyes', 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: 'Cody Tatum', 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@lynkeduppro.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: 130100, actualSpent: 94100, roiProjection: 18.6, pendingApprovals: 3, openChangeOrders: 2, openRFIs: 1, disputes: 1, upcomingMilestones: [ { name: 'Shingle Install Complete', project: '2604 Dunwick Dr', dueDate: '2026-02-14', projectId: 'PRJ-2026-001' }, { name: 'Electrical Hookup', project: '6613 Phoenix Pl', dueDate: '2026-02-14', projectId: 'PRJ-2026-003' }, { name: 'Flooring and Trim', project: '2612 Dunwick Dr', dueDate: '2026-02-18', projectId: 'PRJ-2026-004' }, { name: 'Panel Replacement', project: '3913 Arizona Pl', dueDate: '2026-02-20', projectId: 'PRJ-2026-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: 330500, actualSpent: 233100, roiProjection: 14.2, pendingApprovals: 5, openChangeOrders: 5, openRFIs: 3, disputes: 2, upcomingMilestones: [ { name: 'Section A - TPO Install', project: '6909 Custer Rd', dueDate: '2026-02-28', projectId: 'PRJ-2026-007' }, { name: 'Final Grade and Drainage', project: '2112 Winslow Dr', dueDate: '2026-02-10', projectId: 'PRJ-2026-008' }, { name: 'Floor 2 Installation', project: '7224 Independence Pkwy', dueDate: '2026-02-14', projectId: 'PRJ-2026-009' }, { name: 'Waterproofing Application', project: 'Legacy Town Center', dueDate: '2026-02-12', projectId: 'PRJ-2026-011' } ], cashFlowForecast: [ { month: 'Mar 2026', inflow: 95000, outflow: 72000 }, { month: 'Apr 2026', inflow: 78000, outflow: 58000 }, { month: 'May 2026', inflow: 60000, outflow: 42000 }, { month: 'Jun 2026', inflow: 42000, outflow: 30000 }, { month: 'Jul 2026', inflow: 28000, outflow: 17000 }, { month: 'Aug 2026', inflow: 16000, outflow: 9000 } ], 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', empId: 'LUP-1030', 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', empId: 'LUP-1031', name: 'Sarah Calloway', 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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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: 'PRJ-2026-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 Calloway - Pay Plan Agreement', entityId: 'p2', entityType: 'personnel', entityName: 'Sarah Calloway', vendorId: 'v1', documentType: 'PAY_PLAN', fileName: 'Sarah_PayPlan_Signed.pdf', fileUrl: '#', uploadedBy: 'Sarah Calloway', 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: Justin Johnson (own_001) — 6 Projects // Active owner, strong financials, one project under dispute // ===================================================================== // Project 1: Active, on track, healthy budget { id: 'PRJ-2026-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: 24500, actualCost: 16500, spent: 16500, variancePercent: -63.3, margin: 0.33, 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: 9000, actual: 7000 }, { name: 'ABC Supply Co.', category: 'Materials - Underlayment', allocated: 3500, committed: 2500, actual: 2500 }, { name: 'Texas Roofing Crew A', category: 'Labor - Tear Off', allocated: 8000, committed: 5000, actual: 5000 }, { name: 'Texas Roofing Crew A', category: 'Labor - Install', allocated: 12000, committed: 4500, actual: 0 }, { name: 'City of Plano', category: 'Permits & Inspection', allocated: 2500, committed: 2000, actual: 2000 }, { name: 'Junk King DFW', category: 'Cleanup & Haul-Away', allocated: 4000, committed: 1500, 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, { id: 'jtm_001_3', userId: 'sub_001', name: 'Carlos Mendoza', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null }, { id: 'jtm_001_4', userId: 'e1', name: 'Cody Tatum', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_001_5', userId: 'p2', name: 'Sarah Calloway', 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: 'PRJ-2026-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: 6800, actualCost: 6200, spent: 6200, variancePercent: -27.1, margin: 0.27, 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: 3927.83, submittedBy: 'con_001', status: 'paid', dueDate: '2025-12-05', datePaid: '2025-12-06' }, { id: 'inv_002_3', amount: 2027.83, submittedBy: 'con_001', status: 'pending', dueDate: '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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // Project 3: Delayed with missed milestone { id: 'PRJ-2026-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.12, 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 Calloway', 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Project Manager', status: 'active', commissionEnabled: false, jobCommission: null }, { id: 'jtm_003_3', userId: 'sub_001', name: 'Carlos Mendoza', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // Project 4: Active, on track, under budget { id: 'PRJ-2026-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: 76, 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: 4800, 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: 11500, 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: 'Justin Johnson', 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, { id: 'jtm_004_3', userId: 'e2', name: 'Hannah Reyes', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // Project 5: On Hold with pending dispute { id: 'PRJ-2026-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: 28850, 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 Calloway', 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // Project 6: Active, on track, early stage { id: 'PRJ-2026-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 budgetBreakdown: [ { name: 'Eaton Corporation', category: 'Materials - Panel & Breakers', allocated: 7500, committed: 5500, actual: 2500 }, { name: 'Sparky Electric', category: 'Labor - Panel Replacement', allocated: 5000, committed: 4000, actual: 2000 }, { name: 'Sparky Electric', category: 'Labor - Circuit Re-routing', allocated: 3000, committed: 2000, actual: 1000 }, { name: 'City of Plano', category: 'Permits & Inspection Fees', allocated: 1000, committed: 900, actual: 100 }, { name: 'Conduit Supply DFW', category: 'Conduit & Wiring Materials', allocated: 2000, committed: 0, actual: 0 } ], 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 Mendoza', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // ===================================================================== // OWNER 2: Diana Reeves (own_002) — 6 Projects // High volume owner, large commercial portfolio // ===================================================================== // Project 7: Active, on track, healthy budget (commercial) { id: 'PRJ-2026-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.22, 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, { id: 'jtm_007_3', userId: 'sub_001', name: 'Carlos Mendoza', email: 'carlos@electricpro.com', orgRole: 'SUBCONTRACTOR', jobRole: 'Subcontractor', status: 'active', commissionEnabled: false, jobCommission: null }, { id: 'jtm_007_4', userId: 'e4', name: 'Shelby Greer', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null } ] }, // Project 8: Active, on track, under budget { id: 'PRJ-2026-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: 52000, actualCost: 44500, spent: 44500, variancePercent: -40.7, margin: 0.30, commissionType: 'percent_net', commissionRate: 12, commission: 7500, startDate: '2025-11-18', endDate: '2026-02-28', completionPercentage: 88, healthScore: 80, openRFIs: 0, changeOrderCount: 2, pendingInvoiceCount: 2, // TODO: cross-reference — Contractor: Texas Builders LLC, Vendors: DFW Foundation Pros, ABC Supply Co. budgetBreakdown: [ { name: 'DFW Foundation Pros', category: 'Labor - Pier Installation', allocated: 28000, committed: 24000, actual: 20000 }, { name: 'DFW Foundation Pros', category: 'Labor - Slab Lifting', allocated: 12000, committed: 10000, actual: 9000 }, { name: 'ABC Supply Co.', category: 'Materials - Steel Piers', allocated: 8000, committed: 5500, actual: 4000 }, { name: 'Lone Star Plumbing', category: 'Plumbing Re-test & Reroute', allocated: 5000, committed: 4000, actual: 3000 }, { name: 'Structural Solutions TX', category: 'Engineering & Re-certification', allocated: 10000, committed: 8500, actual: 8500 }, { name: 'Reserve Fund', category: 'Contingency', allocated: 12000, committed: 0, actual: 0 } ], 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: 'pending', dueDate: '2026-01-15' }, { 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 Calloway', 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // Project 9: Delayed with missed milestone { id: 'PRJ-2026-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, budgetBreakdown: [ { name: 'Andersen Windows', category: 'Materials - Custom Windows', allocated: 18000, committed: 14000, actual: 14000 }, { name: 'Texas Builders LLC', category: 'Labor - Floor 1 Installation', allocated: 8000, committed: 8000, actual: 6000 }, { name: 'Texas Builders LLC', category: 'Labor - Floor 2 Installation', allocated: 4000, committed: 3000, actual: 0 }, { name: 'Seal-Rite TX', category: 'Exterior Trim & Sealant', allocated: 5000, committed: 4000, actual: 4000 }, { name: 'Texas Energy Audit Co.', category: 'Energy Audit & Verification', allocated: 4000, committed: 3000, actual: 1800 }, { name: 'Reserve Fund', category: 'Contingency', allocated: 3000, committed: 0, actual: 0 } ], 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: 'Hannah Reyes', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Inspector', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // Project 10: Completed, zero variance { id: 'PRJ-2026-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: 23800, margin: 0.32, commissionType: 'custom', commissionRate: 3500, commission: 3500, startDate: '2025-09-15', endDate: '2025-10-28', completionPercentage: 100, phase: 'Handover', approvedBudget: 35000, committedCost: 26500, actualCost: 23800, variancePercent: -32.0, healthScore: 100, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, budgetBreakdown: [ { name: 'DFW Asphalt Co.', category: 'Materials - Asphalt Mix', allocated: 12000, committed: 9000, actual: 8400 }, { name: 'DFW Asphalt Co.', category: 'Labor - Milling & Paving', allocated: 6000, committed: 4800, actual: 4000 }, { name: 'Texas Sealcoat Supply', category: 'Materials - Sealcoat', allocated: 5000, committed: 4000, actual: 3500 }, { name: 'Texas Builders LLC', category: 'Labor - Base Repair & Grading', allocated: 4000, committed: 3500, actual: 3000 }, { name: 'Striping Pro TX', category: 'Striping & ADA Signage', allocated: 5000, committed: 4000, actual: 3700 }, { name: 'Reserve Fund', category: 'Contingency', allocated: 3000, committed: 1200, actual: 1200 } ], 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: 'pending', dueDate: '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 Calloway', 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, { id: 'jtm_010_3', userId: 'e5', name: 'Dalton Pruitt', 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: 'PRJ-2026-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, budgetBreakdown: [ { name: 'ScaffoldPro DFW', category: 'Scaffolding & Site Protection', allocated: 20000, committed: 20000, actual: 20000 }, { name: 'Legacy Masonry Group', category: 'Masonry Repair & Restoration', allocated: 25000, committed: 23000, actual: 18400 }, { name: 'StoneSeal TX', category: 'Waterproofing & Coating', allocated: 15000, committed: 14000, actual: 12000 }, { name: 'Texas Builders LLC', category: 'Surface Cleaning & Prep', allocated: 12000, committed: 12000, actual: 10000 }, { name: 'Lone Star Plumbing', category: 'Window Sealant Replacement', allocated: 8000, committed: 7000, actual: 6000 }, { name: 'Premier Finishes', category: 'Final Paint & Restoration', allocated: 10000, committed: 9000, actual: 2000 }, { name: 'Reserve Fund', category: 'Contingency', allocated: 5000, committed: 3000, actual: 0 } ], 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 Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // Project 12: On Hold { id: 'PRJ-2026-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, budgetBreakdown: [ { name: 'Lone Star Plumbing', category: 'Labor - Main Line Replacement', allocated: 20000, committed: 12000, actual: 8600 }, { name: 'Ferguson Plumbing Supply', category: 'Materials - Pipe & Fittings', allocated: 12000, committed: 8000, actual: 6500 }, { name: 'Lone Star Plumbing', category: 'Labor - Branch Line Reroute', allocated: 8000, committed: 6000, actual: 4000 }, { name: 'AO Smith', category: 'Materials - Water Heaters', allocated: 6000, committed: 4000, actual: 2000 }, { name: 'Texas Builders LLC', category: 'Site Prep & Wall Access', allocated: 7000, committed: 4000, actual: 1500 }, { name: 'Reserve Fund', category: 'Contingency', allocated: 5000, committed: 0, actual: 0 } ], 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 Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_012_2', userId: 'e3', name: 'Travis Boone', email: 'agent3@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: false, jobCommission: null } ] }, // ===================================================================== // OWNER 1 (own_001) — Won Kanban Leads kl_023–kl_030 → PRJ-2026-013–020 // ===================================================================== // PRJ-2026-013 — Charlotte Norris (kl_023, kc_signed) { id: 'PRJ-2026-013', sourceLeadId: 'kl_023', ownerId: 'own_001', propertyId: 'P-9013', address: '2908 Roundabout Ln, Plano, TX 75023', customerName: 'Charlotte Norris', customerPhone: '(972) 555-0123', customerEmail: 'charlotte.n@gmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'active', phase: 'Pre-Construction', budget: 18200, approvedBudget: 18200, committedCost: 16600, actualCost: 0, spent: 0, variancePercent: -100.0, margin: 0.09, commissionType: 'percent_gross', commissionRate: 10, commission: 1820, startDate: '2026-03-22', endDate: '2026-05-03', completionPercentage: 25, healthScore: 88, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_013_1', milestone: 'Deposit — 30%', amount: 5460, dueDate: '2026-03-22', status: 'paid', paidDate: '2026-03-22' }, { id: 'ps_013_2', milestone: 'Progress — 40%', amount: 7280, dueDate: '2026-04-14', status: 'pending' }, { id: 'ps_013_3', milestone: 'Final — 30%', amount: 5460, dueDate: '2026-04-15', status: 'pending' }, ], invoices: [], milestones: [ { id: 'ms_013_1', name: 'Contract Signed', dueDate: '2026-03-22', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_013_2', name: 'Materials Ordered', dueDate: '2026-03-24', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_013_3', name: 'Permits Pulled', dueDate: '2026-04-05', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_013_4', name: 'Tear-off & Decking',dueDate: '2026-04-14', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_013_5', name: 'Roofing Install', dueDate: '2026-04-21', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_013_6', name: 'Final Inspection', dueDate: '2026-04-28', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_013_7', name: 'Invoice & Payment', dueDate: '2026-05-03', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-03-22', action: 'Contract Executed', user: 'Shelby Greer', details: 'Client signed. 30% deposit collected.' }, { date: '2026-03-24', action: 'Materials Ordered', user: 'Wade Hollis', details: 'GAF Timberline HDZ Charcoal — 24 squares ordered.' }, { date: '2026-04-05', action: 'Permit Submitted', user: 'Wade Hollis', details: 'Plano city permit application filed.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' }, { name: 'Permit Application #RFP-2026-1149', url: '#', type: 'permit' }, { name: 'Deposit Invoice #INV-013-1', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_013_1', userId: 'e4', name: 'Shelby Greer', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_013_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_013_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-014 — Henry Castaneda (kl_024, kc_signed) { id: 'PRJ-2026-014', sourceLeadId: 'kl_024', ownerId: 'own_001', propertyId: 'P-9014', address: '5614 Ridgewood Dr, Plano, TX 75093', customerName: 'Henry Castaneda', customerPhone: '(972) 555-0124', customerEmail: 'hcastaneda@yahoo.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Siding Replacement', status: 'active', phase: 'Pre-Construction', budget: 31500, approvedBudget: 31500, committedCost: 29300, actualCost: 0, spent: 0, variancePercent: -100.0, margin: 0.07, commissionType: 'percent_gross', commissionRate: 10, commission: 3150, startDate: '2026-03-20', endDate: '2026-05-01', completionPercentage: 20, healthScore: 85, openRFIs: 1, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [ { id: 'RFI-014-01', subject: 'HOA Color Approval', status: 'open', submittedBy: 'Hannah Reyes', dateOpened: '2026-03-25', dateClosed: null }, ], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_014_1', milestone: 'Deposit — 25%', amount: 7875, dueDate: '2026-03-20', status: 'paid', paidDate: '2026-03-20' }, { id: 'ps_014_2', milestone: 'Progress — 50%', amount: 15750, dueDate: '2026-04-25', status: 'pending' }, { id: 'ps_014_3', milestone: 'Final — 25%', amount: 7875, dueDate: '2026-05-01', status: 'pending' }, ], invoices: [], milestones: [ { id: 'ms_014_1', name: 'Contract Signed', dueDate: '2026-03-20', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_014_2', name: 'Materials Ordered',dueDate: '2026-03-23', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_014_3', name: 'Permit Pulled', dueDate: '2026-04-05', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_014_4', name: 'Prep & Demo', dueDate: '2026-04-15', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_014_5', name: 'Siding Install', dueDate: '2026-04-22', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_014_6', name: 'Trim & Caulk', dueDate: '2026-04-26', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_014_7', name: 'Final Inspection', dueDate: '2026-04-29', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_014_8', name: 'Invoice & Payment',dueDate: '2026-05-01', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-03-20', action: 'Contract Executed', user: 'Hannah Reyes', details: 'Insurance-approved scope. Full 2,400 sq ft siding.' }, { date: '2026-03-23', action: 'Materials Ordered', user: 'Wade Hollis', details: 'James Hardie Board & Batten — Cobblestone, 2,400 sq ft.' }, { date: '2026-03-25', action: 'RFI Opened', user: 'Hannah Reyes', details: 'HOA color approval required before start.' }, ], documents: [ { name: 'Scope of Work - Siding Replacement', url: '#', type: 'contract' }, { name: 'Permit Application #RFP-2026-1150', url: '#', type: 'permit' }, { name: 'Deposit Invoice #INV-014-1', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_014_1', userId: 'e2', name: 'Hannah Reyes', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_014_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_014_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-015 — Yvonne Blackwell (kl_025, kc_signed) { id: 'PRJ-2026-015', sourceLeadId: 'kl_025', ownerId: 'own_001', propertyId: 'P-9015', address: '3401 Parker Rd, Plano, TX 75023', customerName: 'Yvonne Blackwell', customerPhone: '(972) 555-0125', customerEmail: 'yblackwell@gmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'active', phase: 'Pre-Construction', budget: 27800, approvedBudget: 27800, committedCost: 25600, actualCost: 0, spent: 0, variancePercent: -100.0, margin: 0.08, commissionType: 'percent_gross', commissionRate: 10, commission: 2780, startDate: '2026-03-11', endDate: '2026-04-25', completionPercentage: 28, healthScore: 92, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_015_1', milestone: 'Deposit — 50%', amount: 13900, dueDate: '2026-03-11', status: 'paid', paidDate: '2026-03-11' }, { id: 'ps_015_2', milestone: 'Final — 50%', amount: 13900, dueDate: '2026-04-20', status: 'pending' }, ], invoices: [], milestones: [ { id: 'ms_015_1', name: 'Contract Signed', dueDate: '2026-03-11', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_015_2', name: 'Materials Ordered', dueDate: '2026-03-12', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_015_3', name: 'Permits Pulled', dueDate: '2026-03-20', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_015_4', name: 'Tear-off & Decking', dueDate: '2026-04-07', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_015_5', name: 'Roofing Install', dueDate: '2026-04-14', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_015_6', name: 'Final Inspection', dueDate: '2026-04-20', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_015_7', name: 'Invoice & Payment', dueDate: '2026-04-25', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-03-11', action: 'Contract Executed', user: 'Cody Tatum', details: 'Cash client. 50% deposit paid same day.' }, { date: '2026-03-12', action: 'Materials Ordered', user: 'Wade Hollis', details: 'Owens Corning TruDefinition Duration — 28 squares.' }, { date: '2026-03-20', action: 'Permit Approved', user: 'Wade Hollis', details: 'City of Plano residential roofing permit #RFP-2026-1148.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1148', url: '#', type: 'permit' }, { name: 'Deposit Invoice #INV-015-1', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_015_1', userId: 'e1', name: 'Cody Tatum', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_015_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_015_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-016 — Emmanuel Okafor (kl_026, kc_signed) { id: 'PRJ-2026-016', sourceLeadId: 'kl_026', ownerId: 'own_001', propertyId: 'P-9016', address: '4812 Mapleshade Ln, Plano, TX 75075', customerName: 'Emmanuel Okafor', customerPhone: '(972) 555-0126', customerEmail: 'e.okafor@outlook.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof + Gutters', status: 'active', phase: 'Pre-Construction', budget: 22400, approvedBudget: 22400, committedCost: 21200, actualCost: 0, spent: 0, variancePercent: -100.0, margin: 0.05, commissionType: 'percent_gross', commissionRate: 10, commission: 2240, startDate: '2026-03-25', endDate: '2026-05-05', completionPercentage: 22, healthScore: 86, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_016_1', milestone: 'Deposit — 30%', amount: 6720, dueDate: '2026-03-25', status: 'paid', paidDate: '2026-03-25' }, { id: 'ps_016_2', milestone: 'Progress — 40%', amount: 8960, dueDate: '2026-04-18', status: 'pending' }, { id: 'ps_016_3', milestone: 'Final — 30%', amount: 6720, dueDate: '2026-04-22', status: 'pending' }, ], invoices: [], milestones: [ { id: 'ms_016_1', name: 'Contract Signed', dueDate: '2026-03-25', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_016_2', name: 'Materials Ordered', dueDate: '2026-03-27', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_016_3', name: 'Permits Pulled', dueDate: '2026-04-08', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_016_4', name: 'Roofing Install', dueDate: '2026-04-18', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_016_5', name: 'Gutter Install', dueDate: '2026-04-20', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_016_6', name: 'Final Inspection', dueDate: '2026-04-22', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_016_7', name: 'Invoice & Payment', dueDate: '2026-05-05', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-03-25', action: 'Contract Executed', user: 'Dalton Pruitt', details: 'Insurance approved full roof + gutter scope.' }, { date: '2026-03-27', action: 'Materials Ordered', user: 'Wade Hollis', details: 'GAF shingles + K-style 6" gutters ordered.' }, ], documents: [ { name: 'Scope of Work - Roof + Gutters', url: '#', type: 'contract' }, { name: 'Permit Application #RFP-2026-1151', url: '#', type: 'permit' }, { name: 'Deposit Invoice #INV-016-1', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_016_1', userId: 'e5', name: 'Dalton Pruitt', email: 'agent5@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_016_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_016_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-017 — Adriana Voss (kl_027, kc_signed) { id: 'PRJ-2026-017', sourceLeadId: 'kl_027', ownerId: 'own_001', propertyId: 'P-9017', address: '1820 Custer Rd, Plano, TX 75075', customerName: 'Adriana Voss', customerPhone: '(972) 555-0127', customerEmail: 'adriana.v@gmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'active', phase: 'Pre-Construction', budget: 19600, approvedBudget: 19600, committedCost: 18100, actualCost: 0, spent: 0, variancePercent: -100.0, margin: 0.08, commissionType: 'percent_gross', commissionRate: 10, commission: 1960, startDate: '2026-03-28', endDate: '2026-05-10', completionPercentage: 24, healthScore: 80, openRFIs: 0, changeOrderCount: 1, pendingInvoiceCount: 0, budgetBreakdown: [ { name: 'Owens Corning', 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 }, ], changeOrders: [ { id: 'CO-017-01', title: 'Supplemental — Ice & Water Barrier', amount: 1800, status: 'approved', description: 'Insurance adjuster agreed to add ice/water shield on all valleys and eaves.' }, ], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_017_1', milestone: 'Deposit — 30%', amount: 5880, dueDate: '2026-03-28', status: 'paid', paidDate: '2026-03-28' }, { id: 'ps_017_2', milestone: 'Progress — 40%', amount: 7840, dueDate: '2026-04-25', status: 'pending' }, { id: 'ps_017_3', milestone: 'Final — 30%', amount: 5880, dueDate: '2026-04-28', status: 'pending' }, ], invoices: [], milestones: [ { id: 'ms_017_1', name: 'Contract Signed', dueDate: '2026-03-28', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_017_2', name: 'Materials Ordered', dueDate: '2026-04-02', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_017_3', name: 'Permits Pulled', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_017_4', name: 'Tear-off & Decking', dueDate: '2026-04-22', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_017_5', name: 'Roofing Install', dueDate: '2026-04-28', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_017_6', name: 'Final Inspection', dueDate: '2026-05-05', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_017_7', name: 'Invoice & Payment', dueDate: '2026-05-10', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-03-28', action: 'Contract Executed', user: 'Shelby Greer', details: 'Signed after 6-week negotiation on supplement items.' }, { date: '2026-04-02', action: 'Materials Ordered', user: 'Wade Hollis', details: 'Special color availability causing 1-week delay.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' }, { name: 'Permit Application #RFP-2026-1152', url: '#', type: 'permit' }, { name: 'Change Order CO-017-01', url: '#', type: 'change_order' }, ], teamMembers: [ { id: 'jtm_017_1', userId: 'e4', name: 'Shelby Greer', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_017_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_017_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-018 — Marvin Delgado (kl_028, kc_progress) { id: 'PRJ-2026-018', sourceLeadId: 'kl_028', ownerId: 'own_001', propertyId: 'P-9018', address: '6820 Windhaven Pkwy, Plano, TX 75024', customerName: 'Marvin Delgado', customerPhone: '(972) 555-0128', customerEmail: 'mdelgado@gmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'active', phase: 'Structural', budget: 16800, approvedBudget: 16800, committedCost: 15400, actualCost: 9800, spent: 9800, variancePercent: -41.7, margin: 0.08, commissionType: 'percent_gross', commissionRate: 10, commission: 1680, startDate: '2026-02-28', endDate: '2026-04-10', completionPercentage: 65, healthScore: 82, openRFIs: 0, changeOrderCount: 1, pendingInvoiceCount: 0, 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 }, ], changeOrders: [ { id: 'CO-018-01', title: 'Decking Replacement — 2 Sheets', amount: 480, status: 'approved', description: 'Two rotted OSB sheets discovered during tear-off. Replaced at cost.' }, ], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_018_1', milestone: 'Deposit — 30%', amount: 5040, dueDate: '2026-02-28', status: 'paid', paidDate: '2026-02-28' }, { id: 'ps_018_2', milestone: 'Progress — 40%', amount: 6720, dueDate: '2026-03-20', status: 'paid', paidDate: '2026-03-20' }, { id: 'ps_018_3', milestone: 'Final — 30%', amount: 5040, dueDate: '2026-04-10', status: 'pending' }, ], invoices: [ { id: 'inv_018_1', amount: 8600, submittedBy: 'ABC Supply Co.', status: 'paid', dueDate: '2026-03-18', datePaid: '2026-03-19' }, { id: 'inv_018_2', amount: 600, submittedBy: 'Plano Roofing Crew A', status: 'paid', dueDate: '2026-03-20', datePaid: '2026-03-21' }, { id: 'inv_018_3', amount: 600, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-03-10', datePaid: '2026-03-10' }, ], milestones: [ { id: 'ms_018_1', name: 'Contract Signed', dueDate: '2026-02-28', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_018_2', name: 'Materials Ordered', dueDate: '2026-03-02', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_018_3', name: 'Permits Pulled', dueDate: '2026-03-10', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_018_4', name: 'Tear-off & Decking', dueDate: '2026-03-18', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_018_5', name: 'Roofing Install', dueDate: '2026-04-07', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_018_6', name: 'Final Inspection', dueDate: '2026-04-08', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_018_7', name: 'Invoice & Payment', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-02-28', action: 'Contract Executed', user: 'Cody Tatum', details: 'Signed. Production team notified.' }, { date: '2026-03-18', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off crew on site. Two rotted deck boards found and replaced.' }, { date: '2026-03-20', action: 'Progress Invoice Paid',user: 'Wade Hollis', details: 'Client paid progress invoice on time.' }, { date: '2026-04-07', action: 'Decking Install', user: 'Wade Hollis', details: 'New decking going on today. Roofing shingles start tomorrow.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1130', url: '#', type: 'permit' }, { name: 'Change Order CO-018-01', url: '#', type: 'change_order' }, ], teamMembers: [ { id: 'jtm_018_1', userId: 'e1', name: 'Cody Tatum', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_018_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_018_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-019 — Sandra Tran (kl_029, kc_progress) { id: 'PRJ-2026-019', sourceLeadId: 'kl_029', ownerId: 'own_001', propertyId: 'P-9019', address: '2506 Springpark Dr, Plano, TX 75023', customerName: 'Sandra Tran', customerPhone: '(972) 555-0129', customerEmail: 's.tran@yahoo.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Siding Replacement', status: 'active', phase: 'Structural', budget: 28500, approvedBudget: 28500, committedCost: 27000, actualCost: 15600, spent: 15600, variancePercent: -45.3, margin: 0.05, commissionType: 'percent_gross', commissionRate: 10, commission: 2850, startDate: '2026-02-25', endDate: '2026-04-15', completionPercentage: 55, healthScore: 78, openRFIs: 0, changeOrderCount: 1, pendingInvoiceCount: 0, 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 }, ], changeOrders: [ { id: 'CO-019-01', title: 'Rot Repair — 2 Panels', amount: 380, status: 'approved', description: 'Two panels of rotted OSB substrate discovered during demo. Repaired.' }, ], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_019_1', milestone: 'Deposit — 25%', amount: 7125, dueDate: '2026-02-25', status: 'paid', paidDate: '2026-02-25' }, { id: 'ps_019_2', milestone: 'Progress — 50%', amount: 14250,dueDate: '2026-04-08', status: 'pending' }, { id: 'ps_019_3', milestone: 'Final — 25%', amount: 7125, dueDate: '2026-04-15', status: 'pending' }, ], invoices: [ { id: 'inv_019_1', amount: 14200, submittedBy: 'Owens Corning', status: 'paid', dueDate: '2026-03-25', datePaid: '2026-03-26' }, { id: 'inv_019_2', amount: 600, submittedBy: 'Plano Siding Crew', status: 'paid', dueDate: '2026-03-27', datePaid: '2026-03-28' }, { id: 'inv_019_3', amount: 800, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-03-05', datePaid: '2026-03-05' }, ], milestones: [ { id: 'ms_019_1', name: 'Contract Signed', dueDate: '2026-02-25', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_019_2', name: 'Materials Ordered',dueDate: '2026-02-27', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_019_3', name: 'Permit Pulled', dueDate: '2026-03-05', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_019_4', name: 'Prep & Demo', dueDate: '2026-03-25', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_019_5', name: 'Siding Install', dueDate: '2026-04-05', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_019_6', name: 'Trim & Caulk', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_019_7', name: 'Final Inspection', dueDate: '2026-04-12', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_019_8', name: 'Invoice & Payment',dueDate: '2026-04-15', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-02-25', action: 'Contract Executed', user: 'Hannah Reyes', details: 'Insurance approved full scope.' }, { date: '2026-03-25', action: 'Production Started', user: 'Wade Hollis', details: 'Demo complete. Minor rot on 2 panels — change order raised.' }, { date: '2026-04-05', action: 'Install Underway', user: 'Wade Hollis', details: 'Siding install Day 3. On track for Friday completion.' }, ], documents: [ { name: 'Scope of Work - Siding Replacement', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1135', url: '#', type: 'permit' }, { name: 'Change Order CO-019-01', url: '#', type: 'change_order' }, ], teamMembers: [ { id: 'jtm_019_1', userId: 'e2', name: 'Hannah Reyes', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_019_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_019_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-020 — Reginald Hopper (kl_030, kc_progress) { id: 'PRJ-2026-020', sourceLeadId: 'kl_030', ownerId: 'own_001', propertyId: 'P-9020', address: '1504 Estates Dr, Plano, TX 75093', customerName: 'Reginald Hopper', customerPhone: '(972) 555-0130', customerEmail: 'reg.hopper@icloud.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof + Gutters', status: 'active', phase: 'Structural', budget: 21200, approvedBudget: 21200, committedCost: 20000, actualCost: 16700, spent: 16700, variancePercent: -21.2, margin: 0.06, commissionType: 'percent_gross', commissionRate: 10, commission: 2120, startDate: '2026-02-24', endDate: '2026-04-10', completionPercentage: 78, healthScore: 88, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_020_1', milestone: 'Deposit — 30%', amount: 6360, dueDate: '2026-02-24', status: 'paid', paidDate: '2026-02-24' }, { id: 'ps_020_2', milestone: 'Progress — 40%', amount: 8480, dueDate: '2026-03-22', status: 'paid', paidDate: '2026-03-22' }, { id: 'ps_020_3', milestone: 'Final — 30%', amount: 6360, dueDate: '2026-04-10', status: 'pending' }, ], invoices: [ { id: 'inv_020_1', amount: 9800, submittedBy: 'ABC Supply Co.', status: 'paid', dueDate: '2026-03-20', datePaid: '2026-03-21' }, { id: 'inv_020_2', amount: 3100, submittedBy: 'LeafGuard DFW', status: 'paid', dueDate: '2026-04-07', datePaid: '2026-04-08' }, { id: 'inv_020_3', amount: 3200, submittedBy: 'Plano Roofing Crew B', status: 'paid', dueDate: '2026-03-22', datePaid: '2026-03-23' }, { id: 'inv_020_4', amount: 600, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-03-05', datePaid: '2026-03-05' }, ], milestones: [ { id: 'ms_020_1', name: 'Contract Signed', dueDate: '2026-02-24', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_020_2', name: 'Materials Ordered', dueDate: '2026-02-26', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_020_3', name: 'Permits Pulled', dueDate: '2026-03-05', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_020_4', name: 'Tear-off & Decking', dueDate: '2026-03-20', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_020_5', name: 'Roofing Install', dueDate: '2026-03-22', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_020_6', name: 'Gutter Install', dueDate: '2026-04-07', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_020_7', name: 'Final Inspection', dueDate: '2026-04-08', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_020_8', name: 'Invoice & Payment', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-02-24', action: 'Contract Executed', user: 'Shelby Greer', details: 'Insurance scope approved — deposit collected and production scheduled.' }, { date: '2026-03-20', action: 'Roofing Started', user: 'Darlene Brooks', details: 'Production underway — roofing crew Day 1.' }, { date: '2026-03-22', action: 'Roofing Complete', user: 'Darlene Brooks', details: 'Roof done. Progress invoice sent.' }, { date: '2026-04-07', action: 'Gutter Install', user: 'Darlene Brooks', details: 'Gutter crew arriving today for 1-day install.' }, ], documents: [ { name: 'Scope of Work - Roof + Gutters', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1125', url: '#', type: 'permit' }, { name: 'Progress Invoice #INV-020-2', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_020_1', userId: 'e4', name: 'Shelby Greer', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_020_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_020_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // ===================================================================== // OWNER 2 (own_002) — Won Kanban Leads kl_031–kl_038 → PRJ-2026-021–028 // ===================================================================== // PRJ-2026-021 — Nadia Thornton (kl_031, kc_progress) { id: 'PRJ-2026-021', sourceLeadId: 'kl_031', ownerId: 'own_002', propertyId: 'P-9021', address: '5150 Preston Rd, Plano, TX 75024', customerName: 'Nadia Thornton', customerPhone: '(972) 555-0131', customerEmail: 'nadia.t@gmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'active', phase: 'Structural', budget: 34500, approvedBudget: 34500, committedCost: 32900, actualCost: 22700, spent: 22700, variancePercent: -34.2, margin: 0.05, commissionType: 'percent_gross', commissionRate: 10, commission: 3450, startDate: '2026-03-03', endDate: '2026-04-25', completionPercentage: 60, healthScore: 90, openRFIs: 1, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [ { id: 'RFI-021-01', subject: 'Lifetime Warranty Registration', status: 'open', submittedBy: 'Dalton Pruitt', dateOpened: '2026-03-05', dateClosed: null }, ], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_021_1', milestone: 'Deposit — 100%', amount: 34500, dueDate: '2026-03-03', status: 'paid', paidDate: '2026-03-03' }, ], invoices: [ { id: 'inv_021_1', amount: 18800, submittedBy: 'Owens Corning', status: 'paid', dueDate: '2026-03-28', datePaid: '2026-03-29' }, { id: 'inv_021_2', amount: 3200, submittedBy: 'Plano Roofing Crew C', status: 'paid', dueDate: '2026-03-28', datePaid: '2026-03-30' }, { id: 'inv_021_3', amount: 700, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-03-12', datePaid: '2026-03-12' }, ], milestones: [ { id: 'ms_021_1', name: 'Contract Signed', dueDate: '2026-03-03', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_021_2', name: 'Materials Ordered', dueDate: '2026-03-05', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_021_3', name: 'Permits Pulled', dueDate: '2026-03-12', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_021_4', name: 'Tear-off & Decking', dueDate: '2026-03-28', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_021_5', name: 'Roofing Install', dueDate: '2026-03-28', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_021_6', name: 'Ridge & Detail', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_021_7', name: 'Final Inspection', dueDate: '2026-04-18', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_021_8', name: 'Invoice & Payment', dueDate: '2026-04-25', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-03-03', action: 'Contract Executed', user: 'Dalton Pruitt', details: 'Cash client — full payment upfront.' }, { date: '2026-03-28', action: 'Production Started', user: 'Darlene Brooks', details: 'Materials arrived. Premium install started.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement (Premium)', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1140', url: '#', type: 'permit' }, { name: 'Material PO — Owens Corning HDZ', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_021_1', userId: 'e5', name: 'Dalton Pruitt', email: 'agent5@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_021_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_021_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-022 — Clifford Aguilar (kl_032, kc_progress) { id: 'PRJ-2026-022', sourceLeadId: 'kl_032', ownerId: 'own_002', propertyId: 'P-9022', address: '3822 Haverwood Ln, Plano, TX 75023', customerName: 'Clifford Aguilar', customerPhone: '(972) 555-0132', customerEmail: 'c.aguilar@outlook.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Siding Repair', status: 'active', phase: 'Structural', budget: 9400, approvedBudget: 9400, committedCost: 8900, actualCost: 6700, spent: 6700, variancePercent: -28.7, margin: 0.05, commissionType: 'percent_gross', commissionRate: 10, commission: 940, startDate: '2026-03-07', endDate: '2026-04-10', completionPercentage: 70, healthScore: 84, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, budgetBreakdown: [ { name: 'James Hardie', 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_022_1', milestone: 'Deposit — 40%', amount: 3760, dueDate: '2026-03-07', status: 'paid', paidDate: '2026-03-07' }, { id: 'ps_022_2', milestone: 'Progress — 30%', amount: 2820, dueDate: '2026-04-01', status: 'paid', paidDate: '2026-04-02' }, { id: 'ps_022_3', milestone: 'Final — 30%', amount: 2820, dueDate: '2026-04-10', status: 'pending' }, ], invoices: [ { id: 'inv_022_1', amount: 4600, submittedBy: 'James Hardie', status: 'paid', dueDate: '2026-03-20', datePaid: '2026-03-21' }, { id: 'inv_022_2', amount: 1600, submittedBy: 'Plano Siding Crew', status: 'paid', dueDate: '2026-04-01', datePaid: '2026-04-02' }, { id: 'inv_022_3', amount: 500, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-03-20', datePaid: '2026-03-20' }, ], milestones: [ { id: 'ms_022_1', name: 'Contract Signed', dueDate: '2026-03-07', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_022_2', name: 'Materials Ordered', dueDate: '2026-03-09', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_022_3', name: 'Permit Pulled', dueDate: '2026-03-20', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_022_4', name: 'South Face', dueDate: '2026-04-01', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_022_5', name: 'West Face', dueDate: '2026-04-06', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_022_6', name: 'Final Inspection', dueDate: '2026-04-08', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_022_7', name: 'Invoice & Payment', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-03-07', action: 'Contract Executed', user: 'Cody Tatum', details: 'Partial repair — south and west faces only.' }, { date: '2026-04-01', action: 'South Face Done', user: 'Wade Hollis', details: 'South face siding complete. Starting west face.' }, ], documents: [ { name: 'Scope of Work - Siding Repair', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1142', url: '#', type: 'permit' }, { name: 'Material Invoice #INV-022-1', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_022_1', userId: 'e1', name: 'Cody Tatum', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_022_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_022_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-023 — Tamara Owens (kl_033, kc_progress) { id: 'PRJ-2026-023', sourceLeadId: 'kl_033', ownerId: 'own_002', propertyId: 'P-9023', address: '2010 Oak Creek Blvd, Plano, TX 75023', customerName: 'Tamara Owens', customerPhone: '(972) 555-0133', customerEmail: 'tamara.o@yahoo.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'active', phase: 'Structural', budget: 42000, approvedBudget: 42000, committedCost: 40400, actualCost: 18900, spent: 18900, variancePercent: -55.0, margin: 0.04, commissionType: 'percent_gross', commissionRate: 10, commission: 4200, startDate: '2026-02-15', endDate: '2026-04-15', completionPercentage: 45, healthScore: 74, openRFIs: 1, changeOrderCount: 0, pendingInvoiceCount: 0, budgetBreakdown: [ { name: 'Owens Corning', 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 }, ], changeOrders: [], rfis: [ { id: 'RFI-023-01', subject: 'Structural Load — Chimney Flashing', status: 'open', submittedBy: 'Hannah Reyes', dateOpened: '2026-04-05', dateClosed: null }, ], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_023_1', milestone: 'Deposit — 25%', amount: 10500, dueDate: '2026-02-15', status: 'paid', paidDate: '2026-02-15' }, { id: 'ps_023_2', milestone: 'Progress — 50%', amount: 21000, dueDate: '2026-04-08', status: 'pending' }, { id: 'ps_023_3', milestone: 'Final — 25%', amount: 10500, dueDate: '2026-04-15', status: 'pending' }, ], invoices: [ { id: 'inv_023_1', amount: 17200, submittedBy: 'Owens Corning', status: 'paid', dueDate: '2026-04-05', datePaid: '2026-04-06' }, { id: 'inv_023_2', amount: 800, submittedBy: 'Plano Roofing Crew A', status: 'paid', dueDate: '2026-04-05', datePaid: '2026-04-06' }, { id: 'inv_023_3', amount: 900, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-03-01', datePaid: '2026-03-01' }, ], milestones: [ { id: 'ms_023_1', name: 'Contract Signed', dueDate: '2026-02-15', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_023_2', name: 'Materials Ordered', dueDate: '2026-02-18', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_023_3', name: 'Permits Pulled', dueDate: '2026-03-01', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_023_4', name: 'Tear-off & Decking', dueDate: '2026-04-05', status: 'in_progress', assignedTo: 'con_001' }, { id: 'ms_023_5', name: 'Roofing Install', dueDate: '2026-04-10', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_023_6', name: 'Final Inspection', dueDate: '2026-04-12', status: 'pending', assignedTo: 'con_001' }, { id: 'ms_023_7', name: 'Invoice & Payment', dueDate: '2026-04-15', status: 'pending', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-02-15', action: 'Contract Executed', user: 'Hannah Reyes', details: 'Largest job this quarter — $42,000, 3,600 sq ft.' }, { date: '2026-04-05', action: 'Production Started', user: 'Wade Hollis', details: 'Day 1 of 3-day job. Two crews on site.' }, { date: '2026-04-05', action: 'RFI Opened', user: 'Hannah Reyes', details: 'Custom chimney flashing — architect sign-off required.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement (Large)', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1138', url: '#', type: 'permit' }, { name: 'Material Invoice #INV-023-1', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_023_1', userId: 'e2', name: 'Hannah Reyes', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_023_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_023_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-024 — Craig Singleton (kl_034, kc_complete) { id: 'PRJ-2026-024', sourceLeadId: 'kl_034', ownerId: 'own_002', propertyId: 'P-9024', address: '4110 Lorimar Dr, Plano, TX 75093', customerName: 'Craig Singleton', customerPhone: '(972) 555-0134', customerEmail: 'craig.s@gmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'completed', phase: 'Handover', budget: 17200, approvedBudget: 17200, committedCost: 16200, actualCost: 15580, spent: 15580, variancePercent: -9.4, margin: 0.06, commissionType: 'percent_gross', commissionRate: 10, commission: 1720, startDate: '2026-02-01', endDate: '2026-03-01', completionPercentage: 100, healthScore: 95, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_024_1', milestone: 'Deposit — 30%', amount: 5160, dueDate: '2026-02-01', status: 'paid', paidDate: '2026-02-01' }, { id: 'ps_024_2', milestone: 'Progress — 40%', amount: 6880, dueDate: '2026-02-21', status: 'paid', paidDate: '2026-02-22' }, { id: 'ps_024_3', milestone: 'Final — 30%', amount: 5160, dueDate: '2026-03-01', status: 'paid', paidDate: '2026-03-01' }, ], invoices: [ { id: 'inv_024_1', amount: 8980, submittedBy: 'ABC Supply Co.', status: 'paid', dueDate: '2026-02-20', datePaid: '2026-02-21' }, { id: 'inv_024_2', amount: 6000, submittedBy: 'Plano Roofing Crew B', status: 'paid', dueDate: '2026-02-21', datePaid: '2026-02-22' }, { id: 'inv_024_3', amount: 600, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-02-12', datePaid: '2026-02-12' }, ], milestones: [ { id: 'ms_024_1', name: 'Contract Signed', dueDate: '2026-02-01', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_024_2', name: 'Materials Ordered', dueDate: '2026-02-03', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_024_3', name: 'Permits Pulled', dueDate: '2026-02-12', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_024_4', name: 'Tear-off & Decking', dueDate: '2026-02-20', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_024_5', name: 'Roofing Install', dueDate: '2026-02-21', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_024_6', name: 'Final Inspection', dueDate: '2026-02-28', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_024_7', name: 'Invoice & Payment', dueDate: '2026-03-01', status: 'completed', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-02-01', action: 'Contract Executed', user: 'Cody Tatum', details: 'Insurance scope approved and signed — deposit collected at signing.' }, { date: '2026-02-20', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off crew on site.' }, { date: '2026-02-21', action: 'Install Complete', user: 'Wade Hollis', details: 'Full install done in 1 day.' }, { date: '2026-02-28', action: 'Final Inspection', user: 'Wade Hollis', details: 'Passed. Client extremely happy. 5-star review.' }, { date: '2026-03-01', action: 'Project Closed', user: 'Wade Hollis', details: 'Final payment received. File closed.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1112', url: '#', type: 'permit' }, { name: 'Final Invoice Package', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_024_1', userId: 'e1', name: 'Cody Tatum', email: 'agent1@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_024_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_024_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-025 — Bertha Fleming (kl_035, kc_complete) { id: 'PRJ-2026-025', sourceLeadId: 'kl_035', ownerId: 'own_002', propertyId: 'P-9025', address: '6230 West Parker Rd, Plano, TX 75093', customerName: 'Bertha Fleming', customerPhone: '(972) 555-0135', customerEmail: 'bertha.f@hotmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Siding Replacement', status: 'completed', phase: 'Handover', budget: 26400, approvedBudget: 26400, committedCost: 24800, actualCost: 24300, spent: 24300, variancePercent: -8.0, margin: 0.06, commissionType: 'percent_gross', commissionRate: 10, commission: 2640, startDate: '2026-01-28', endDate: '2026-02-22', completionPercentage: 100, healthScore: 93, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_025_1', milestone: 'Deposit — 25%', amount: 6600, dueDate: '2026-01-28', status: 'paid', paidDate: '2026-01-28' }, { id: 'ps_025_2', milestone: 'Progress — 50%', amount: 13200, dueDate: '2026-02-19', status: 'paid', paidDate: '2026-02-19' }, { id: 'ps_025_3', milestone: 'Final — 25%', amount: 6600, dueDate: '2026-02-22', status: 'paid', paidDate: '2026-02-22' }, ], invoices: [ { id: 'inv_025_1', amount: 13900, submittedBy: 'Owens Corning', status: 'paid', dueDate: '2026-02-15', datePaid: '2026-02-16' }, { id: 'inv_025_2', amount: 9600, submittedBy: 'Plano Siding Crew', status: 'paid', dueDate: '2026-02-19', datePaid: '2026-02-20' }, { id: 'inv_025_3', amount: 800, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-02-05', datePaid: '2026-02-05' }, ], milestones: [ { id: 'ms_025_1', name: 'Contract Signed', dueDate: '2026-01-28', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_025_2', name: 'Materials Ordered',dueDate: '2026-01-30', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_025_3', name: 'Permit Pulled', dueDate: '2026-02-05', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_025_4', name: 'Prep & Demo', dueDate: '2026-02-15', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_025_5', name: 'Siding Install', dueDate: '2026-02-19', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_025_6', name: 'Trim & Caulk', dueDate: '2026-02-20', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_025_7', name: 'Final Inspection', dueDate: '2026-02-21', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_025_8', name: 'Invoice & Payment',dueDate: '2026-02-22', status: 'completed', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-01-28', action: 'Contract Executed', user: 'Shelby Greer', details: 'Insurance scope approved — deposit collected at signing.' }, { date: '2026-02-15', action: 'Production Started', user: 'Darlene Brooks', details: 'Demo and install started same day.' }, { date: '2026-02-22', action: 'Project Closed', user: 'Darlene Brooks', details: 'Insurance check received and deposited. File closed.' }, ], documents: [ { name: 'Scope of Work - Siding Replacement', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1108', url: '#', type: 'permit' }, { name: 'Final Invoice Package', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_025_1', userId: 'e4', name: 'Shelby Greer', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_025_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_025_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-026 — Oliver Stanton (kl_036, kc_complete) { id: 'PRJ-2026-026', sourceLeadId: 'kl_036', ownerId: 'own_002', propertyId: 'P-9026', address: '3750 Legacy Dr, Plano, TX 75023', customerName: 'Oliver Stanton', customerPhone: '(972) 555-0136', customerEmail: 'oliver.st@outlook.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'completed', phase: 'Handover', budget: 22100, approvedBudget: 22100, committedCost: 20400, actualCost: 20200, spent: 20200, variancePercent: -8.6, margin: 0.08, commissionType: 'percent_gross', commissionRate: 10, commission: 2210, startDate: '2026-01-30', endDate: '2026-02-20', completionPercentage: 100, healthScore: 97, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_026_1', milestone: 'Full Payment — 100%', amount: 22100, dueDate: '2026-01-30', status: 'paid', paidDate: '2026-01-30' }, ], invoices: [ { id: 'inv_026_1', amount: 11800, submittedBy: 'GAF Materials', status: 'paid', dueDate: '2026-02-18', datePaid: '2026-02-19' }, { id: 'inv_026_2', amount: 7800, submittedBy: 'Plano Roofing Crew C', status: 'paid', dueDate: '2026-02-19', datePaid: '2026-02-20' }, { id: 'inv_026_3', amount: 600, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-02-07', datePaid: '2026-02-07' }, ], milestones: [ { id: 'ms_026_1', name: 'Contract Signed', dueDate: '2026-01-30', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_026_2', name: 'Materials Ordered', dueDate: '2026-01-31', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_026_3', name: 'Permits Pulled', dueDate: '2026-02-07', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_026_4', name: 'Tear-off & Decking', dueDate: '2026-02-18', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_026_5', name: 'Roofing Install', dueDate: '2026-02-19', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_026_6', name: 'Final Inspection', dueDate: '2026-02-20', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_026_7', name: 'Upfront Payment Collected', dueDate: '2026-01-30', status: 'completed', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-01-30', action: 'Contract Executed', user: 'Hannah Reyes', details: 'Cash client. Full payment upfront.' }, { date: '2026-02-18', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off started.' }, { date: '2026-02-20', action: 'Project Closed', user: 'Wade Hollis', details: 'Outstanding quality. Referral bonus owed per agreement.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1115', url: '#', type: 'permit' }, { name: 'Completion Certificate', url: '#', type: 'certificate' }, ], teamMembers: [ { id: 'jtm_026_1', userId: 'e2', name: 'Hannah Reyes', email: 'agent2@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_026_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_026_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-027 — Daphne Garrett (kl_037, kc_complete) { id: 'PRJ-2026-027', sourceLeadId: 'kl_037', ownerId: 'own_002', propertyId: 'P-9027', address: '1912 Mira Vista Dr, Plano, TX 75025', customerName: 'Daphne Garrett', customerPhone: '(972) 555-0137', customerEmail: 'daphne.g@gmail.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Gutters + Fascia', status: 'completed', phase: 'Handover', budget: 8900, approvedBudget: 8900, committedCost: 8600, actualCost: 8400, spent: 8400, variancePercent: -5.6, margin: 0.03, commissionType: 'percent_gross', commissionRate: 10, commission: 890, startDate: '2026-02-10', endDate: '2026-03-02', completionPercentage: 100, healthScore: 96, openRFIs: 0, changeOrderCount: 0, pendingInvoiceCount: 0, 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 }, ], changeOrders: [], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_027_1', milestone: 'Deposit — 50%', amount: 4450, dueDate: '2026-02-10', status: 'paid', paidDate: '2026-02-10' }, { id: 'ps_027_2', milestone: 'Final — 50%', amount: 4450, dueDate: '2026-03-02', status: 'paid', paidDate: '2026-03-02' }, ], invoices: [ { id: 'inv_027_1', amount: 3600, submittedBy: 'LeafGuard DFW', status: 'paid', dueDate: '2026-03-01', datePaid: '2026-03-02' }, { id: 'inv_027_2', amount: 1800, submittedBy: 'James Hardie', status: 'paid', dueDate: '2026-02-28', datePaid: '2026-03-01' }, { id: 'inv_027_3', amount: 2600, submittedBy: 'Plano Roofing Crew A', status: 'paid', dueDate: '2026-03-02', datePaid: '2026-03-02' }, { id: 'inv_027_4', amount: 400, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-02-12', datePaid: '2026-02-12' }, ], milestones: [ { id: 'ms_027_1', name: 'Contract Signed', dueDate: '2026-02-10', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_027_2', name: 'Materials Ordered', dueDate: '2026-02-12', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_027_3', name: 'Fascia Repair', dueDate: '2026-02-28', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_027_4', name: 'Gutter Install', dueDate: '2026-03-01', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_027_5', name: 'Final Walkthrough', dueDate: '2026-03-02', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_027_6', name: 'Invoice & Payment', dueDate: '2026-03-02', status: 'completed', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-02-10', action: 'Contract Executed', user: 'Dalton Pruitt', details: 'Insurance claim approved — scope agreed and deposit collected at signing.' }, { date: '2026-02-28', action: 'Production Started',user: 'Wade Hollis', details: 'Fascia repair started.' }, { date: '2026-03-02', action: 'Project Closed', user: 'Wade Hollis', details: 'Complete. Customer gave referral cards to 3 neighbors.' }, ], documents: [ { name: 'Scope of Work - Gutters + Fascia', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1118', url: '#', type: 'permit' }, { name: 'Final Invoice Package', url: '#', type: 'invoice' }, ], teamMembers: [ { id: 'jtm_027_1', userId: 'e5', name: 'Dalton Pruitt', email: 'agent5@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_027_2', userId: 'p1', name: 'Jesus Gonzales', email: 'jesus@lynkeduppro.com', orgRole: 'W2_EMPLOYEE', jobRole: 'Sales Rep', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_027_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', status: 'active', commissionEnabled: false, jobCommission: null }, ], }, // PRJ-2026-028 — Nathaniel Cruz (kl_038, kc_complete) { id: 'PRJ-2026-028', sourceLeadId: 'kl_038', ownerId: 'own_002', propertyId: 'P-9028', address: '5808 Tennyson Pkwy, Plano, TX 75024', customerName: 'Nathaniel Cruz', customerPhone: '(972) 555-0138', customerEmail: 'ncruz@icloud.com', contractorId: 'con_001', subcontractorIds: [], projectType: 'Roof Replacement', status: 'completed', phase: 'Handover', budget: 19400, approvedBudget: 19400, committedCost: 19400, actualCost: 18600, spent: 18600, variancePercent: -4.1, margin: 0.04, commissionType: 'percent_gross', commissionRate: 10, commission: 1940, startDate: '2026-02-04', endDate: '2026-03-20', completionPercentage: 100, healthScore: 87, openRFIs: 0, changeOrderCount: 1, pendingInvoiceCount: 0, budgetBreakdown: [ { name: 'Owens Corning', 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 }, ], changeOrders: [ { id: 'CO-028-01', title: 'Decking Replacement — 3 Sheets', amount: 720, status: 'approved', description: 'Three rotted OSB sheets discovered during tear-off. Replaced.' }, ], rfis: [], riskLog: [], issueLog: [], paymentSchedule: [ { id: 'ps_028_1', milestone: 'Deposit — 30%', amount: 5820, dueDate: '2026-02-04', status: 'paid', paidDate: '2026-02-04' }, { id: 'ps_028_2', milestone: 'Progress — 40%', amount: 7760, dueDate: '2026-03-12', status: 'paid', paidDate: '2026-03-14' }, { id: 'ps_028_3', milestone: 'Final — 30%', amount: 5820, dueDate: '2026-03-20', status: 'paid', paidDate: '2026-03-20' }, ], invoices: [ { id: 'inv_028_1', amount: 11200, submittedBy: 'Owens Corning', status: 'paid', dueDate: '2026-03-10', datePaid: '2026-03-11' }, { id: 'inv_028_2', amount: 6800, submittedBy: 'Plano Roofing Crew B', status: 'paid', dueDate: '2026-03-12', datePaid: '2026-03-13' }, { id: 'inv_028_3', amount: 600, submittedBy: 'City of Plano', status: 'paid', dueDate: '2026-02-15', datePaid: '2026-02-15' }, ], milestones: [ { id: 'ms_028_1', name: 'Contract Signed', dueDate: '2026-02-04', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_028_2', name: 'Materials Ordered', dueDate: '2026-02-06', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_028_3', name: 'Permits Pulled', dueDate: '2026-02-15', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_028_4', name: 'Tear-off & Decking', dueDate: '2026-03-10', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_028_5', name: 'Roofing Install', dueDate: '2026-03-12', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_028_6', name: 'Final Inspection', dueDate: '2026-03-18', status: 'completed', assignedTo: 'con_001' }, { id: 'ms_028_7', name: 'Invoice & Payment', dueDate: '2026-03-20', status: 'completed', assignedTo: 'con_001' }, ], activityTimeline: [ { date: '2026-02-04', action: 'Contract Executed', user: 'Shelby Greer', details: 'Insurance supplemental approved after negotiation — deposit collected at signing.' }, { date: '2026-03-10', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off found 3 rotted deck sheets. CO raised.' }, { date: '2026-03-12', action: 'Install Complete', user: 'Wade Hollis', details: 'Roofing complete.' }, { date: '2026-03-20', action: 'Project Closed', user: 'Wade Hollis', details: 'Longest close — 7 weeks due to complex insurance supplemental.' }, ], documents: [ { name: 'Scope of Work - Roof Replacement', url: '#', type: 'contract' }, { name: 'Permit #RFP-2026-1110', url: '#', type: 'permit' }, { name: 'Change Order CO-028-01', url: '#', type: 'change_order' }, ], teamMembers: [ { id: 'jtm_028_1', userId: 'e4', name: 'Shelby Greer', email: 'agent4@plano.com', orgRole: 'FIELD_AGENT', jobRole: 'Canvasser', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_028_2', userId: 'p2', name: 'Sarah Calloway', email: 'sarah@lynkeduppro.com', orgRole: '1099_SALES_REP', jobRole: 'Estimator', status: 'active', commissionEnabled: true, jobCommission: null }, { id: 'jtm_028_3', userId: 'con_001', name: 'Mike Brennan', email: 'mike@texasbuilders.com', orgRole: 'CONTRACTOR', jobRole: 'Lead Installer', 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) 413-8247', email: 'derek.holloway@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_new', bucketId: null, value: 14800, stormSource: true, notes: 'Hail damage from March storm. Insurance adjuster coming next week.', createdDate: '2026-03-31', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', 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: '(469) 827-5163', email: 'bcastillo@yahoo.com', jobType: 'Roof Inspection', insuranceType: 'Retail', assignedAgentId: null, assignedAgentName: null, columnId: 'kc_new', bucketId: null, value: 750, 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: '(214) 659-3082', email: 'a.reyes@outlook.com', jobType: 'Gutter Replacement', insuranceType: 'Retail', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_new', bucketId: null, value: 6200, notes: 'Referred by neighbor on Oak Creek Blvd. Wants full gutter system.', createdDate: '2026-04-02', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', 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: '(945) 731-6409', email: 'sylvia.nguyen@icloud.com', jobType: 'Siding Repair', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_new', bucketId: null, value: 11400, stormSource: true, notes: 'Storm damage to siding on north face. Photos sent.', createdDate: '2026-04-03', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', 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: '(469) 304-7851', email: 'rosei@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: null, assignedAgentName: null, columnId: 'kc_new', bucketId: null, value: 17600, 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) 861-2734', email: 'carolyn.e@gmail.com', jobType: 'Window Replacement', insuranceType: 'Retail', assignedAgentId: 'e5', assignedAgentName: 'Dalton Pruitt', columnId: 'kc_new', bucketId: null, value: 8900, notes: 'Wants 4 windows replaced. Budget is flexible.', createdDate: '2026-04-05', activity: [ { from: null, to: 'New Lead', by: 'Dalton Pruitt', 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: '(214) 748-9216', email: 'mtillman@hotmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_contacted', bucketId: null, value: 15200, stormSource: true, notes: 'Spoke on the phone. Wants appointment next Tuesday.', createdDate: '2026-03-25', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-03-25', note: 'Canvassing — hail dents visible on ridge cap.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', 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: '(469) 582-3047', email: 'diane.k@gmail.com', jobType: 'Roof Inspection', insuranceType: 'Retail', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_contacted', bucketId: null, value: 650, notes: 'Older roof, no storm damage. Wants inspection for peace of mind.', createdDate: '2026-03-26', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-03-26', note: 'Website form submission.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', 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) 937-4618', email: 'joel.f@yahoo.com', jobType: 'Siding Replacement', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_contacted', bucketId: null, value: 24700, stormSource: true, notes: 'Insurance claim in process. Wants estimate ASAP.', createdDate: '2026-03-24', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-03-24', note: 'Door knock — wind damage to siding panels.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', 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: '(945) 263-8174', email: 'pyuen@outlook.com', jobType: 'Gutter Repair', insuranceType: 'Retail', assignedAgentId: 'e3', assignedAgentName: 'Travis Boone', columnId: 'kc_contacted', bucketId: null, value: 1850, notes: 'Gutters pulling away from fascia. Straightforward repair job.', createdDate: '2026-03-27', activity: [ { from: null, to: 'New Lead', by: 'Travis Boone', date: '2026-03-27', note: 'Referral from neighborhood Facebook group.' }, { from: 'New Lead', to: 'Contacted', by: 'Travis Boone', 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: '(214) 391-5820', email: 'gerald.obi@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_contacted', bucketId: null, value: 31400, stormSource: true, notes: 'Large home — 3,200 sq ft. Multiple storm claims history.', createdDate: '2026-03-22', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-03-22', note: 'Canvassing — prior storm claim visible in public records.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', 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: '(469) 714-6039', email: 'vanessa.ob@icloud.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_appt', bucketId: null, value: 16500, stormSource: true, notes: 'Appointment Apr 9 at 10am. Adjuster already completed visit.', createdDate: '2026-03-18', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-03-18', note: 'Storm canvassing — visible hail damage.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-03-20', note: 'Phone call — very interested, adjuster already came out.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', 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) 528-7341', email: 'thomas.g@yahoo.com', jobType: 'Roof Inspection', insuranceType: 'Retail', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_appt', bucketId: null, value: 700, notes: 'Appointment Apr 8 at 2pm. Retired homeowner, very detail-oriented.', createdDate: '2026-03-20', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-03-20', note: 'Referral from existing client.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-03-22', note: 'Email exchange — wants full report after inspection.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', 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: '(214) 867-2953', email: 'lashonda.w@gmail.com', jobType: 'Siding + Gutters', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_appt', bucketId: null, value: 19300, stormSource: true, notes: 'Combo job — siding and gutters. Apr 10 morning slot.', createdDate: '2026-03-15', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-03-15', note: 'Door knock — wind damage to siding and gutters overflowing.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-03-17', note: 'Called and texted. Customer confirmed she filed insurance.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', 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: '(945) 482-7016', email: 'wpark@outlook.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e5', assignedAgentName: 'Dalton Pruitt', columnId: 'kc_appt', bucketId: null, value: 22800, stormSource: true, notes: 'Large flat-roof section at the back. Appointment Apr 11.', createdDate: '2026-03-19', activity: [ { from: null, to: 'New Lead', by: 'Dalton Pruitt', date: '2026-03-19', note: 'Website lead — searched storm damage repair.' }, { from: 'New Lead', to: 'Contacted', by: 'Dalton Pruitt', date: '2026-03-21', note: 'Spoke on phone. Mentioned flat roof section in addition to pitched.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Dalton Pruitt', 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: '(469) 639-4827', email: 'ingrid.s@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Retail', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_appt', bucketId: null, value: 24200, notes: 'Cash purchase — no insurance. Appointment Apr 12.', createdDate: '2026-03-23', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-03-23', note: 'Referral. Cash buyer — no insurance involved.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-03-25', note: 'Spoke briefly. She is very decisive — ready to move fast.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', 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) 784-3651', email: 'd.brooks@yahoo.com', jobType: 'Chimney + Roof Repair', insuranceType: 'Insurance', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_appt', bucketId: null, value: 12600, notes: 'Chimney flashing leaking badly. Appointment rescheduled twice — Apr 14 final.', createdDate: '2026-03-10', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-03-10', note: 'Inbound call — chimney leak causing interior ceiling damage.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-03-12', note: 'Full conversation — insurance claim open. Adjuster not yet assigned.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', date: '2026-03-14', note: 'Apr 1 appointment set.' }, { from: 'Appt Scheduled', to: 'Contacted', by: 'Hannah Reyes', date: '2026-03-30', note: 'Customer rescheduled — work conflict. Moved back to Contacted.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', 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: '(214) 503-9172', email: 'gloria.h@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_estimate', bucketId: null, value: 15900, stormSource: true, notes: 'Estimate sent Mar 28. Waiting on her to review with husband.', createdDate: '2026-03-05', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-03-05', note: 'Canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-03-07', note: 'Phone call — confirmed hail damage.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', date: '2026-03-10', note: 'Appointment set for Mar 15.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Cody Tatum', 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: '(469) 258-6743', email: 'devon.m@hotmail.com', jobType: 'Siding Replacement', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_estimate', bucketId: null, value: 20400, stormSource: true, notes: 'Estimate under review. Customer comparing quotes.', createdDate: '2026-03-08', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-03-08', note: 'Storm canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-03-10', note: 'Met at door — comparing two companies.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', date: '2026-03-13', note: 'Appointment Mar 18.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Shelby Greer', 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: '(945) 817-4390', email: 'karen.b@yahoo.com', jobType: 'Roof Replacement', insuranceType: 'Retail', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_estimate', bucketId: null, value: 24000, notes: 'Cash deal. Estimate $24K. Very interested — just reviewing financing options.', createdDate: '2026-03-12', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-03-12', note: 'Referral from sister who is an existing client.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-03-14', note: 'Long first call — very motivated buyer.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', date: '2026-03-16', note: 'Inspection Mar 20 at 9am.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Hannah Reyes', 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) 346-8025', email: 'lionel.c@gmail.com', jobType: 'Gutters + Fascia', insuranceType: 'Insurance', assignedAgentId: 'e5', assignedAgentName: 'Dalton Pruitt', columnId: 'kc_estimate', bucketId: null, value: 11700, notes: 'Adjuster approved full replacement. Estimate matches scope perfectly.', createdDate: '2026-03-01', activity: [ { from: null, to: 'New Lead', by: 'Dalton Pruitt', date: '2026-03-01', note: 'Referral — gutters completely detached on one side.' }, { from: 'New Lead', to: 'Contacted', by: 'Dalton Pruitt', date: '2026-03-03', note: 'Spoke on phone — insurance claim already filed.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Dalton Pruitt', date: '2026-03-05', note: 'Mar 10 appointment.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Dalton Pruitt', 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: '(214) 972-6438', email: 'rpatel@outlook.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_estimate', bucketId: null, value: 18300, notes: 'Estimate sent 3 weeks ago. Not responding to follow-ups.', createdDate: '2026-02-20', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-02-20', note: 'Canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-02-22', note: 'Brief call — seemed interested.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', date: '2026-02-25', note: 'Mar 1 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Cody Tatum', 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: '(469) 473-8152', email: 'charlotte.n@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_signed', bucketId: null, value: 18200, stormSource: true, projectId: 'PRJ-2026-013', notes: 'Signed Mar 22. Install scheduled for Apr 14.', createdDate: '2026-03-01', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-03-01', note: 'Storm canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-03-03', note: 'Phone call — eager to move quickly.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', date: '2026-03-05', note: 'Inspection Mar 8.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Shelby Greer', date: '2026-03-10', note: 'Estimate sent — $18,200.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Shelby Greer', 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) 617-3094', email: 'hcastaneda@yahoo.com', jobType: 'Siding Replacement', insuranceType: 'Insurance', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_signed', bucketId: null, value: 31500, projectId: 'PRJ-2026-014', notes: 'Signed. Full siding replacement — 2,400 sq ft. Production queue.', createdDate: '2026-02-28', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-02-28', note: 'Referral.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-03-02', note: 'Phone call and site visit same day.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', date: '2026-03-04', note: 'Mar 7 appointment.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Hannah Reyes', date: '2026-03-12', note: 'Estimate $31,500 for full siding.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Hannah Reyes', date: '2026-03-20', note: 'Signed. Insurance approved full scope.' }, ], }, { id: 'kl_025', name: 'Yvonne Blackwell', address: '3401 Parker Rd, Plano TX 75023', phone: '(214) 839-5761', email: 'yblackwell@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Retail', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_signed', bucketId: null, value: 27800, projectId: 'PRJ-2026-015', notes: 'Cash client. Signed fast — paid 50% deposit upfront.', createdDate: '2026-03-05', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-03-05', note: 'Referral — cash buyer, wants premium materials.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-03-06', note: 'Met same day — very decisive.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', date: '2026-03-07', note: 'Next-day inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Cody Tatum', date: '2026-03-09', note: 'Same-day estimate — $27,800 for full replacement + ice/water barrier.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Cody Tatum', 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: '(945) 564-2817', email: 'e.okafor@outlook.com', jobType: 'Roof + Gutters', insuranceType: 'Insurance', assignedAgentId: 'e5', assignedAgentName: 'Dalton Pruitt', columnId: 'kc_signed', bucketId: null, value: 22400, stormSource: true, projectId: 'PRJ-2026-016', notes: 'Combined roof and gutter job. Production scheduled Apr 18.', createdDate: '2026-03-03', activity: [ { from: null, to: 'New Lead', by: 'Dalton Pruitt', date: '2026-03-03', note: 'Storm canvassing — gutters completely detached on west side.' }, { from: 'New Lead', to: 'Contacted', by: 'Dalton Pruitt', date: '2026-03-05', note: 'Called and texted. Confirmed both roof and gutter damage.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Dalton Pruitt', date: '2026-03-08', note: 'Mar 12 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Dalton Pruitt', date: '2026-03-15', note: 'Combined estimate $22,400 sent.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Dalton Pruitt', 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: '(469) 381-7246', email: 'adriana.v@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_signed', bucketId: null, value: 19600, stormSource: true, projectId: 'PRJ-2026-017', notes: 'Signed after lengthy negotiation on supplemental items.', createdDate: '2026-02-15', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-02-15', note: 'Canvassing after January wind event.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-02-17', note: 'Called — open to inspection.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', date: '2026-02-19', note: 'Feb 22 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Shelby Greer', date: '2026-02-25', note: 'Estimate sent $19,600.' }, { from: 'Estimate Sent', to: 'Contacted', by: 'Shelby Greer', date: '2026-03-05', note: 'Customer pushed back on supplement items — moved back to review.' }, { from: 'Contacted', to: 'Estimate Sent', by: 'Shelby Greer', date: '2026-03-10', note: 'Revised estimate sent after supplement agreement with adjuster.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Shelby Greer', 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) 924-6183', email: 'mdelgado@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_progress', bucketId: null, value: 16800, stormSource: true, projectId: 'PRJ-2026-018', notes: 'Tear-off complete. New decking going on today.', createdDate: '2026-02-10', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-02-10', note: 'Storm canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-02-12', note: 'Call completed.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', date: '2026-02-14', note: 'Feb 17 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Cody Tatum', date: '2026-02-20', note: 'Estimate sent $16,800.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Cody Tatum', date: '2026-02-28', note: 'Signed. Production team notified.' }, { from: 'Signed', to: 'In Progress', by: 'Wade Hollis', 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: '(214) 457-3896', email: 's.tran@yahoo.com', jobType: 'Siding Replacement', insuranceType: 'Insurance', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_progress', bucketId: null, value: 28500, projectId: 'PRJ-2026-019', notes: 'Day 3 of install. On track for completion Friday.', createdDate: '2026-02-08', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-02-08', note: 'Referral.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-02-10', note: 'Initial call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', date: '2026-02-12', note: 'Feb 15 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Hannah Reyes', date: '2026-02-18', note: 'Estimate $28,500 for full siding.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Hannah Reyes', date: '2026-02-25', note: 'Signed.' }, { from: 'Signed', to: 'In Progress', by: 'Wade Hollis', 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: '(469) 726-5034', email: 'reg.hopper@icloud.com', jobType: 'Roof + Gutters', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_progress', bucketId: null, value: 21200, projectId: 'PRJ-2026-020', notes: 'Roofing done. Gutters crew arriving tomorrow.', createdDate: '2026-02-05', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-02-05', note: 'Door knock.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-02-07', note: 'Call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', date: '2026-02-10', note: 'Feb 14 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Shelby Greer', date: '2026-02-17', note: 'Estimate sent $21,200.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Shelby Greer', date: '2026-02-24', note: 'Signed.' }, { from: 'Signed', to: 'In Progress', by: 'Darlene Brooks',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: '(945) 318-6742', email: 'nadia.t@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Retail', assignedAgentId: 'e5', assignedAgentName: 'Dalton Pruitt', columnId: 'kc_progress', bucketId: null, value: 34500, projectId: 'PRJ-2026-021', notes: 'Premium GAF Timberline HDZ install. Custom color.', createdDate: '2026-02-12', activity: [ { from: null, to: 'New Lead', by: 'Dalton Pruitt', date: '2026-02-12', note: 'Referral — high-end home.' }, { from: 'New Lead', to: 'Contacted', by: 'Dalton Pruitt', date: '2026-02-14', note: 'Met in person. Requested premium material options.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Dalton Pruitt', date: '2026-02-17', note: 'Feb 20 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Dalton Pruitt', date: '2026-02-24', note: 'Premium estimate $34,500 including ice/water and lifetime warranty.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Dalton Pruitt', date: '2026-03-03', note: 'Signed — wants Barkwood color. Special order placed.' }, { from: 'Signed', to: 'In Progress', by: 'Darlene Brooks',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) 463-8195', email: 'c.aguilar@outlook.com', jobType: 'Siding Repair', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_progress', bucketId: null, value: 9400, stormSource: true, projectId: 'PRJ-2026-022', notes: 'Partial siding repair only — south and west faces. 2 days left.', createdDate: '2026-02-18', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-02-18', note: 'Storm canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-02-20', note: 'Called.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', date: '2026-02-22', note: 'Feb 25 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Cody Tatum', date: '2026-02-28', note: 'Partial repair estimate $9,400.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Cody Tatum', date: '2026-03-07', note: 'Signed.' }, { from: 'Signed', to: 'In Progress', by: 'Wade Hollis', 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: '(214) 692-4817', email: 'tamara.o@yahoo.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_progress', bucketId: null, value: 42000, stormSource: true, projectId: 'PRJ-2026-023', notes: 'Large job — 3,600 sq ft. Day 1 of 3.', createdDate: '2026-02-01', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-02-01', note: 'Referral — large home, significant hail damage.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-02-03', note: 'Met in person — urgent, water coming in.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', date: '2026-02-04', note: 'Next-day inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Hannah Reyes', date: '2026-02-08', note: 'Estimate $42,000 for 3,600 sq ft.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Hannah Reyes', date: '2026-02-15', note: 'Signed — largest job this quarter.' }, { from: 'Signed', to: 'In Progress', by: 'Wade Hollis', 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: '(469) 835-7263', email: 'craig.s@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_complete', bucketId: null, value: 17200, projectId: 'PRJ-2026-024', notes: 'Job complete. Final walkthrough done. Client extremely happy.', createdDate: '2026-01-15', activity: [ { from: null, to: 'New Lead', by: 'Cody Tatum', date: '2026-01-15', note: 'Canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-01-17', note: 'Call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', date: '2026-01-20', note: 'Jan 23 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Cody Tatum', date: '2026-01-25', note: 'Estimate $17,200.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Cody Tatum', date: '2026-02-01', note: 'Signed.' }, { from: 'Signed', to: 'In Progress', by: 'Wade Hollis', date: '2026-02-20', note: 'Install Day 1.' }, { from: 'In Progress', to: 'Complete', by: 'Wade Hollis', 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) 547-2381', email: 'bertha.f@hotmail.com', jobType: 'Siding Replacement', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_complete', bucketId: null, value: 26400, projectId: 'PRJ-2026-025', notes: 'Complete. Insurance check received and deposited.', createdDate: '2026-01-10', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-01-10', note: 'Referral.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-01-12', note: 'Call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', date: '2026-01-14', note: 'Jan 17 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Shelby Greer', date: '2026-01-20', note: 'Estimate $26,400.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Shelby Greer', date: '2026-01-28', note: 'Signed.' }, { from: 'Signed', to: 'In Progress', by: 'Darlene Brooks',date: '2026-02-15', note: 'Production started.' }, { from: 'In Progress', to: 'Complete', by: 'Darlene Brooks',date: '2026-02-22', note: 'Complete. Insurance proceeds processed.' }, ], }, { id: 'kl_036', name: 'Oliver Stanton', address: '3750 Legacy Dr, Plano TX 75023', phone: '(945) 671-4903', email: 'oliver.st@outlook.com', jobType: 'Roof Replacement', insuranceType: 'Retail', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_complete', bucketId: null, value: 22100, projectId: 'PRJ-2026-026', notes: 'Cash deal done. Referral bonus owed to Oliver per agreement.', createdDate: '2026-01-20', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-01-20', note: 'Referral — cash buyer.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-01-22', note: 'Same-day call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Hannah Reyes', date: '2026-01-23', note: 'Jan 25 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Hannah Reyes', date: '2026-01-27', note: 'Estimate $22,100.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Hannah Reyes', date: '2026-01-30', note: 'Signed within 72 hrs. Full payment upfront.' }, { from: 'Signed', to: 'In Progress', by: 'Wade Hollis', date: '2026-02-18', note: 'Install started.' }, { from: 'In Progress', to: 'Complete', by: 'Wade Hollis', 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: '(214) 728-5364', email: 'daphne.g@gmail.com', jobType: 'Gutters + Fascia', insuranceType: 'Insurance', assignedAgentId: 'e5', assignedAgentName: 'Dalton Pruitt', columnId: 'kc_complete', bucketId: null, value: 8900, projectId: 'PRJ-2026-027', notes: 'Complete. Customer requested referral card — strong close.', createdDate: '2026-01-25', activity: [ { from: null, to: 'New Lead', by: 'Dalton Pruitt', date: '2026-01-25', note: 'Door knock.' }, { from: 'New Lead', to: 'Contacted', by: 'Dalton Pruitt', date: '2026-01-27', note: 'Call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Dalton Pruitt', date: '2026-01-29', note: 'Feb 1 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Dalton Pruitt', date: '2026-02-04', note: 'Estimate $8,900.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Dalton Pruitt', date: '2026-02-10', note: 'Signed.' }, { from: 'Signed', to: 'In Progress', by: 'Wade Hollis', date: '2026-02-28', note: 'Install started.' }, { from: 'In Progress', to: 'Complete', by: 'Wade Hollis', 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: '(469) 948-3715', email: 'ncruz@icloud.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_complete', bucketId: null, value: 19400, stormSource: true, projectId: 'PRJ-2026-028', notes: 'Complete. Longest pipeline — 7 week close. Insurance supplemental was complex.', createdDate: '2026-01-05', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-01-05', note: 'Canvassing post-December storm.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-01-07', note: 'Call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Shelby Greer', date: '2026-01-10', note: 'Jan 14 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Shelby Greer', date: '2026-01-17', note: 'Estimate $19,400.' }, { from: 'Estimate Sent', to: 'Contacted', by: 'Shelby Greer', date: '2026-01-28', note: 'Insurance underpaid — back to negotiation.' }, { from: 'Contacted', to: 'Estimate Sent', by: 'Shelby Greer', date: '2026-02-05', note: 'Revised estimate submitted to adjuster.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Shelby Greer', date: '2026-02-18', note: 'Adjuster approved supplement. Signed.' }, { from: 'Signed', to: 'In Progress', by: 'Darlene Brooks',date: '2026-03-05', note: 'Production started.' }, { from: 'In Progress', to: 'Complete', by: 'Darlene Brooks',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) 831-4756', email: 'jasmine.f@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e3', assignedAgentName: 'Travis Boone', columnId: 'kc_estimate', bucketId: 'kc_stuck', value: 15800, notes: 'Stuck — adjuster denied claim. Customer disputing. No movement for 3 weeks.', createdDate: '2026-02-25', activity: [ { from: null, to: 'New Lead', by: 'Travis Boone', date: '2026-02-25', note: 'Canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Travis Boone', date: '2026-02-27', note: 'Call — insurance claim just filed.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Travis Boone', date: '2026-03-01', note: 'Mar 5 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Travis Boone', date: '2026-03-10', note: 'Estimate sent $15,800. Adjuster denied most of scope.' }, { from: 'Estimate Sent', to: 'Stuck', by: 'Travis Boone', 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: '(214) 376-9248', email: 'calvin.m@yahoo.com', jobType: 'Siding Replacement', insuranceType: 'Insurance', assignedAgentId: 'e2', assignedAgentName: 'Hannah Reyes', columnId: 'kc_contacted', bucketId: 'kc_stuck', value: 22600, notes: 'Stuck — homeowner going through divorce. Put project on hold indefinitely.', createdDate: '2026-03-10', activity: [ { from: null, to: 'New Lead', by: 'Hannah Reyes', date: '2026-03-10', note: 'Referral.' }, { from: 'New Lead', to: 'Contacted', by: 'Hannah Reyes', date: '2026-03-12', note: 'Good initial call but complicated personal situation mentioned.' }, { from: 'Contacted', to: 'Stuck', by: 'Hannah Reyes', 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: '(469) 514-8037', email: 'harriet.s@outlook.com', jobType: 'Roof Replacement', insuranceType: 'Retail', assignedAgentId: 'e5', assignedAgentName: 'Dalton Pruitt', columnId: 'kc_appt', bucketId: 'kc_stuck', value: 19800, notes: 'Stuck — missed 3 appointments. No answer on phone. Left door hanger.', createdDate: '2026-03-05', activity: [ { from: null, to: 'New Lead', by: 'Dalton Pruitt', date: '2026-03-05', note: 'Door knock — interested in retail replacement.' }, { from: 'New Lead', to: 'Contacted', by: 'Dalton Pruitt', date: '2026-03-07', note: 'Brief call — set appointment.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Dalton Pruitt', date: '2026-03-09', note: 'Mar 14 appointment set.' }, { from: 'Appt Scheduled',to: 'Stuck', by: 'Dalton Pruitt', 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: '(945) 247-8631', email: 'floyd.s@gmail.com', jobType: 'Roof Replacement', insuranceType: 'Insurance', assignedAgentId: 'e1', assignedAgentName: 'Cody Tatum', columnId: 'kc_estimate', bucketId: 'kc_followup', value: 17600, stormSource: true, 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: 'Cody Tatum', date: '2026-03-15', note: 'Storm canvassing.' }, { from: 'New Lead', to: 'Contacted', by: 'Cody Tatum', date: '2026-03-17', note: 'Call — interested but said call back after spring break.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Cody Tatum', date: '2026-03-18', note: 'Mar 20 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Cody Tatum', date: '2026-03-22', note: 'Estimate sent $17,600.' }, { from: 'Estimate Sent', to: 'Follow-up', by: 'Cody Tatum', 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) 683-5172', email: 'miriam.o@icloud.com', jobType: 'Roof Inspection', insuranceType: 'Retail', assignedAgentId: 'e4', assignedAgentName: 'Shelby Greer', columnId: 'kc_contacted', bucketId: 'kc_followup', value: 800, notes: 'Follow-up Apr 15 — currently traveling abroad. Callback confirmed via text.', createdDate: '2026-03-20', activity: [ { from: null, to: 'New Lead', by: 'Shelby Greer', date: '2026-03-20', note: 'Website form lead.' }, { from: 'New Lead', to: 'Contacted', by: 'Shelby Greer', date: '2026-03-22', note: 'Text conversation — customer is abroad until Apr 13.' }, { from: 'Contacted', to: 'Follow-up', by: 'Shelby Greer', 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: '(214) 491-7368', email: 'jerome.w@yahoo.com', jobType: 'Gutter Replacement', insuranceType: 'Insurance', assignedAgentId: 'e3', assignedAgentName: 'Travis Boone', columnId: 'kc_signed', bucketId: 'kc_followup', value: 6200, notes: 'Signed but waiting on HOA approval before starting. Follow-up Apr 20.', createdDate: '2026-03-08', activity: [ { from: null, to: 'New Lead', by: 'Travis Boone', date: '2026-03-08', note: 'Referral.' }, { from: 'New Lead', to: 'Contacted', by: 'Travis Boone', date: '2026-03-10', note: 'Call.' }, { from: 'Contacted', to: 'Appt Scheduled', by: 'Travis Boone', date: '2026-03-12', note: 'Mar 15 inspection.' }, { from: 'Appt Scheduled', to: 'Estimate Sent', by: 'Travis Boone', date: '2026-03-18', note: 'Estimate $6,200.' }, { from: 'Estimate Sent', to: 'Signed', by: 'Travis Boone', date: '2026-03-25', note: 'Signed — waiting on HOA approval to start.' }, { from: 'Signed', to: 'Follow-up', by: 'Travis Boone', 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: 'Shelby Greer', details: 'Client signed. 30% deposit collected.' }, { date: '2026-03-24', action: 'Materials Ordered', user: 'Wade Hollis', details: 'GAF Timberline HDZ Charcoal — 24 squares ordered.' }, { date: '2026-04-05', action: 'Permit Submitted', user: 'Wade Hollis', 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: 'Hannah Reyes', 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: 'Hannah Reyes', details: 'Insurance-approved scope. Full 2,400 sq ft siding.' }, { date: '2026-03-23', action: 'Materials Ordered', user: 'Wade Hollis', details: 'James Hardie Board & Batten — Cobblestone, 2,400 sq ft.' }, { date: '2026-03-25', action: 'RFI Opened', user: 'Hannah Reyes', 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: 'Cody Tatum', details: 'Cash client. 50% deposit paid same day.' }, { date: '2026-03-12', action: 'Materials Ordered', user: 'Wade Hollis', details: 'Owens Corning TruDefinition Duration — 28 squares.' }, { date: '2026-03-20', action: 'Permit Approved', user: 'Wade Hollis', 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: 'Dalton Pruitt', details: 'Insurance approved full roof + gutter scope.' }, { date: '2026-03-27', action: 'Materials Ordered', user: 'Wade Hollis', 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: 'Shelby Greer', 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: 'Shelby Greer', details: 'Signed after 6-week negotiation on supplement items.' }, { date: '2026-04-02', action: 'Materials Ordered', user: 'Wade Hollis', 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: 'Cody Tatum', 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: 'Cody Tatum', details: 'Signed. Production team notified.' }, { date: '2026-03-18', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off crew on site. Two rotted deck boards found and replaced.' }, { date: '2026-03-20', action: 'Progress Invoice Paid', user: 'Wade Hollis', details: 'Client paid progress invoice on time.' }, { date: '2026-04-07', action: 'Decking Install', user: 'Wade Hollis', 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: 'Hannah Reyes', 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: 'Hannah Reyes', details: 'Insurance approved full scope.' }, { date: '2026-03-25', action: 'Production Started', user: 'Wade Hollis', details: 'Demo complete. Minor rot on 2 panels — change order raised.' }, { date: '2026-04-05', action: 'Install Underway', user: 'Wade Hollis', 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: 'Shelby Greer', details: '' }, { date: '2026-03-20', action: 'Roofing Started', user: 'Darlene Brooks', details: 'Production underway — roofing crew Day 1.' }, { date: '2026-03-22', action: 'Roofing Complete', user: 'Darlene Brooks', details: 'Roof done. Progress invoice sent.' }, { date: '2026-04-07', action: 'Gutter Install', user: 'Darlene Brooks', 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: 'Dalton Pruitt', 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: 'Dalton Pruitt', details: 'Cash client — full payment upfront.' }, { date: '2026-03-28', action: 'Production Started', user: 'Darlene Brooks',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: 'Cody Tatum', details: 'Partial repair — south and west faces only.' }, { date: '2026-04-01', action: 'South Face Done', user: 'Wade Hollis', 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: 'Hannah Reyes', 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: 'Hannah Reyes', details: 'Largest job this quarter — $42,000, 3,600 sq ft.' }, { date: '2026-04-05', action: 'Production Started', user: 'Wade Hollis', details: 'Day 1 of 3-day job. Two crews on site.' }, { date: '2026-04-05', action: 'RFI Opened', user: 'Hannah Reyes', 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: 'Cody Tatum', details: '' }, { date: '2026-02-20', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off crew on site.' }, { date: '2026-02-21', action: 'Install Complete', user: 'Wade Hollis', details: 'Full install done in 1 day.' }, { date: '2026-02-28', action: 'Final Inspection', user: 'Wade Hollis', details: 'Passed. Client extremely happy. 5-star review.' }, { date: '2026-03-01', action: 'Project Closed', user: 'Wade Hollis', 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: 'Shelby Greer', details: '' }, { date: '2026-02-15', action: 'Production Started', user: 'Darlene Brooks', details: 'Demo and install started same day.' }, { date: '2026-02-22', action: 'Project Closed', user: 'Darlene Brooks', 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: 'Hannah Reyes', details: 'Cash client. Full payment upfront.' }, { date: '2026-02-18', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off started.' }, { date: '2026-02-20', action: 'Project Closed', user: 'Wade Hollis', 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: 'Dalton Pruitt', details: '' }, { date: '2026-02-28', action: 'Production Started', user: 'Wade Hollis', details: 'Fascia repair started.' }, { date: '2026-03-02', action: 'Project Closed', user: 'Wade Hollis', 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: 'Shelby Greer', 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: 'Shelby Greer', details: '' }, { date: '2026-03-10', action: 'Production Started', user: 'Wade Hollis', details: 'Tear-off found 3 rotted deck sheets. CO raised.' }, { date: '2026-03-12', action: 'Install Complete', user: 'Wade Hollis', details: 'Roofing complete.' }, { date: '2026-03-20', action: 'Project Closed', user: 'Wade Hollis', details: 'Longest close — 7 weeks due to complex insurance supplemental.' }, ], }, }; // --------------------------------------------------------------------------- // Subcontractor Management (Feature 8.2 – Task Assignment) // Subcontractors are independent of any single company so the same record can // be referenced by tasks coming from multiple Admin/Owner orgs. // --------------------------------------------------------------------------- export const SUBCONTRACTOR_TASK_PRIORITIES = [ { value: 'low', label: 'Low', color: '#6B7280' }, { value: 'medium', label: 'Medium', color: '#3B82F6' }, { value: 'high', label: 'High', color: '#EF4444' }, ]; export const SUBCONTRACTOR_TASK_STATUSES = [ 'Assigned', 'In Progress', 'On Hold', 'Completed', 'Cancelled', ]; // Statuses the subcontractor themselves can set from the Task Detail page. // Owner / admin can still flip a task to Cancelled from the admin page. export const SUBCONTRACTOR_SELF_STATUSES = ['Assigned', 'In Progress', 'On Hold', 'Completed']; export const SUBCONTRACTOR_EXPENSE_CATEGORIES = [ 'Materials', 'Equipment Rental', 'Travel / Fuel', 'Permits', 'Disposal / Dumpster', 'Other', ]; export const SUBCONTRACTOR_FEE_TYPES = [ 'Labour / Service Fee', 'Inspection Fee', 'Mobilization Fee', 'Overtime', 'Callback / Warranty', 'Other', ]; const MOCK_SUBCONTRACTORS = [ { id: 'sub_001', empId: 'LUP-2001', name: 'Carlos Mendoza', companyName: 'Electric Pro Services', tradeType: 'Electrical', email: 'carlos@electricpro.com', phone: '469-555-9012', status: 'active', rating: 4.8, companies: ['lynkeduppro'], joinedAt: '2024-08-01', }, { id: 'sub_002', empId: 'LUP-2002', name: 'Maya Patel', companyName: 'Skyline Roof Crew', tradeType: 'Roofing', email: 'maya@skylineroof.com', phone: '214-555-1247', status: 'active', rating: 4.6, companies: ['lynkeduppro', 'acmebuilds'], joinedAt: '2024-05-12', }, { id: 'sub_003', empId: 'LUP-2003', name: 'Dwayne Boudreaux', companyName: 'Holland Painting Co.', tradeType: 'Painting', email: 'dwayne@hollandpaint.com', phone: '972-555-8312', status: 'active', rating: 4.4, companies: ['lynkeduppro'], joinedAt: '2024-09-21', }, { id: 'sub_004', empId: 'LUP-2004', name: 'Jennifer Castillo', companyName: 'Wu Plumbing', tradeType: 'Plumbing', email: 'jenn@wuplumbing.com', phone: '469-555-3340', status: 'active', rating: 4.9, companies: ['lynkeduppro', 'acmebuilds', 'planobuilders'], joinedAt: '2024-02-10', }, { id: 'sub_005', empId: 'LUP-2005', name: 'Greg Alston', companyName: 'Alston Window Pros', tradeType: 'Windows & Glazing', email: 'greg@alstonwindows.com', phone: '214-555-0299', status: 'inactive', rating: 4.1, companies: ['lynkeduppro'], joinedAt: '2023-11-15', }, ]; const MOCK_SUBCONTRACTOR_TASKS = [ // ------------------------------------------------------------------- // sct_001 — Maya Patel (sub_002) — fully populated reference task // ------------------------------------------------------------------- { id: 'sct_001', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_002', subcontractorName: 'Maya Patel', assignedBy: 'own_001', assignedByName: 'Justin Johnson', title: 'Replace damaged ridge cap', location: '2612 Dunwick Dr, Plano, TX 75023', description: 'Two ridge cap shingles missing on the south slope after wind storm. Replace with matching tile and seal. Inspect adjacent tiles for hairline cracks while you are up there.', dueDate: '2026-05-25', priority: 'high', status: 'In Progress', photos: [ { id: 'sct_001_p1', name: 'Roof_Damage.jpg', url: '/assets/images/properties/Hail_Damaged_Shingles.jpg', annotation: 'Repair this corner' }, { id: 'sct_001_p2', name: 'Roof_Wide.jpg', url: '/assets/images/properties/Storm_Worn_Roof.jpg', annotation: 'Inspect adjacent tiles too' }, { id: 'sct_001_p3', name: 'Ridge_Closeup.jpg', url: '/assets/images/properties/Cracked_Storm_Shingles.jpg', annotation: 'Closeup of missing cap' }, { id: 'sct_001_p4', name: 'Roof_Reference.jpg', url: '/assets/images/properties/Overlapping_Roof_Shingles.jpg', annotation: '' }, ], statusHistory: [ { id: 'sct_001_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Task created and assigned.', at: '2026-05-15T09:12:00Z' }, { id: 'sct_001_h2', status: 'In Progress', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'On site, sourced materials.', at: '2026-05-16T08:30:00Z' }, ], thread: [ { id: 'sct_001_t1', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Please source them — match Owens Corning Estate Gray as best you can.', at: '2026-05-15T09:14:00Z' }, { id: 'sct_001_t2', channel: 'group', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'Got it. I’ll be there on May 20. Do you have shingles in storage or should I source them?', at: '2026-05-15T10:30:00Z', mine: true }, { id: 'sct_001_t3', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Source them and put it on the task expenses. Homeowner is OK to reimburse.', at: '2026-05-15T11:00:00Z' }, { id: 'sct_001_t4', channel: 'group', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'Sounds good — picking up at Home Depot tomorrow morning.', at: '2026-05-15T11:08:00Z', mine: true }, { id: 'sct_001_t5', channel: 'direct', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Heads up team: homeowner is sensitive about noise — keep work to daylight hours only. No early-morning starts.', at: '2026-05-15T11:05:00Z' }, { id: 'sct_001_t6', channel: 'group', senderId: 'a1', senderName: 'Wade Hollis', senderRole: 'Admin', body: 'Permits are not required for this scope. Skip the city portal step.', at: '2026-05-15T12:10:00Z' }, { id: 'sct_001_t7', channel: 'direct', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'Noted. Will text homeowner when crew is 30 min out.', at: '2026-05-15T12:25:00Z', mine: true }, ], expenses: [ { id: 'sct_001_e1', description: 'Owens Corning Estate Gray (single pkg)', category: 'Materials', amount: 64.5, notes: 'From Home Depot, kept receipt.', receiptUrl: 'mock://receipts/oc-estate-gray.pdf', createdAt: '2026-05-16T13:10:00Z' }, { id: 'sct_001_e2', description: 'Roofing nails 1.25"', category: 'Materials', amount: 14.99, notes: '', receiptUrl: '', createdAt: '2026-05-16T13:12:00Z' }, { id: 'sct_001_e3', description: 'Sealant tube', category: 'Materials', amount: 21.0, notes: 'NP1 polyurethane.', receiptUrl: '', createdAt: '2026-05-16T13:14:00Z' }, { id: 'sct_001_e4', description: 'Dumpster rental (half day)', category: 'Equipment Rental', amount: 85.0, notes: 'Shared with adjacent job.', receiptUrl: '', createdAt: '2026-05-16T15:00:00Z' }, { id: 'sct_001_e5', description: 'Fuel — round trip to supply yard', category: 'Travel / Fuel', amount: 18.4, notes: '', receiptUrl: '', createdAt: '2026-05-16T17:30:00Z' }, ], fees: [ { id: 'sct_001_f1', description: 'Labour — Ridge Cap Replacement', type: 'Labour / Service Fee', amount: 350, createdAt: '2026-05-16T13:20:00Z' }, { id: 'sct_001_f2', description: 'Mobilization to site', type: 'Mobilization Fee', amount: 75, createdAt: '2026-05-16T13:22:00Z' }, ], activities: [ { id: 'sct_001_a1', type: 'status_change', status: 'Assigned', note: 'Task created and assigned. Materials list shared with crew.', photos: [], actorId: 'own_001', actorName: 'Justin Johnson', actorRole: 'Owner', at: '2026-05-15T09:12:00Z', }, { id: 'sct_001_a2', type: 'status_change', status: 'In Progress', note: 'On site, sourced replacement Owens Corning Estate Gray. Starting on the south slope.', photos: [ { id: 'sct_001_a2_p1', url: '/assets/images/properties/Hail_Damaged_Shingles.jpg', name: 'arrival.jpg' }, ], actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor', at: '2026-05-16T08:30:00Z', }, { id: 'sct_001_a3', type: 'progress_update', status: 'In Progress', note: 'First two damaged ridge tiles removed. Decking underneath is clean — no rot.', photos: [ { id: 'sct_001_a3_p1', url: '/assets/images/properties/Storm_Worn_Roof.jpg', name: 'decking-clean.jpg' }, ], actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor', at: '2026-05-16T10:45:00Z', }, { id: 'sct_001_a4', type: 'progress_update', status: 'In Progress', note: 'Replacement ridge caps installed on lower row. Sealing next.', photos: [ { id: 'sct_001_a4_p1', url: '/assets/images/properties/Cracked_Storm_Shingles.jpg', name: 'lower-row.jpg' }, { id: 'sct_001_a4_p2', url: '/assets/images/properties/Overlapping_Roof_Shingles.jpg', name: 'wide-shot.jpg' }, ], actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor', at: '2026-05-16T14:20:00Z', }, ], projectId: 'PRJ-2026-004', paymentStatus: 'Unpaid', paymentRequestedAt: '2026-05-16T17:30:00Z', createdAt: '2026-05-15T09:12:00Z', updatedAt: '2026-05-16T17:30:00Z', }, // ------------------------------------------------------------------- // sct_002 — Carlos (sub_001) — In Progress electrical // ------------------------------------------------------------------- { id: 'sct_002', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_001', subcontractorName: 'Carlos Mendoza', assignedBy: 'own_001', assignedByName: 'Justin Johnson', title: 'Replace breaker panel', location: '6613 Phoenix Pl, Plano, TX 75023', description: 'Outdated 100A panel — upgrade to 200A. Permit pulled, materials staged on site. Coordinate Oncor disconnect for the morning of installation.', dueDate: '2026-05-22', priority: 'medium', status: 'In Progress', photos: [ { id: 'sct_002_p1', name: 'Old_Panel.jpg', url: '/assets/images/properties/Beige_Two_Story_House.jpg', annotation: 'Existing 100A panel — disconnect and remove' }, { id: 'sct_002_p2', name: 'Service_Mast.jpg', url: '/assets/images/properties/Modern_White_Suburban_Home.jpg', annotation: 'Service mast — verify clearance' }, ], statusHistory: [ { id: 'sct_002_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Task created and assigned.', at: '2026-05-10T15:40:00Z' }, { id: 'sct_002_h2', status: 'In Progress', actorId: 'sub_001', actorName: 'Carlos Mendoza', comment: 'On site, started disconnect.', at: '2026-05-12T08:20:00Z' }, ], thread: [ { id: 'sct_002_t1', channel: 'group', senderId: 'sub_001', senderName: 'Carlos Mendoza', senderRole: 'Subcontractor', body: 'Oncor disconnect is confirmed for May 12, 8 AM.', at: '2026-05-11T16:00:00Z', mine: true }, { id: 'sct_002_t2', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Perfect. Homeowner will be on site.', at: '2026-05-11T16:30:00Z' }, { id: 'sct_002_t3', channel: 'group', senderId: 'a1', senderName: 'Wade Hollis', senderRole: 'Admin', body: 'Inspection slot booked with the city for May 15.', at: '2026-05-12T09:30:00Z' }, ], expenses: [ { id: 'sct_002_e1', description: '200A panel — Square D', category: 'Materials', amount: 312.0, notes: '', receiptUrl: '', createdAt: '2026-05-11T09:00:00Z' }, { id: 'sct_002_e2', description: 'Conduit + fittings', category: 'Materials', amount: 47.25, notes: '3/4" EMT + connectors.', receiptUrl: '', createdAt: '2026-05-11T09:10:00Z' }, { id: 'sct_002_e3', description: 'Permit fee — City of Plano', category: 'Permits', amount: 95.0, notes: 'Reimbursable.', receiptUrl: '', createdAt: '2026-05-11T14:00:00Z' }, ], fees: [ { id: 'sct_002_f1', description: 'Labour — Day 1 disconnect / prep', type: 'Labour / Service Fee', amount: 425, createdAt: '2026-05-12T17:00:00Z' }, ], activities: [ { id: 'sct_002_a1', type: 'status_change', status: 'Assigned', note: 'Task created and assigned. Permit pulled, materials staged.', photos: [], actorId: 'own_001', actorName: 'Justin Johnson', actorRole: 'Owner', at: '2026-05-10T15:40:00Z', }, { id: 'sct_002_a2', type: 'status_change', status: 'In Progress', note: 'Oncor on site for disconnect. Cutting power to existing 100A panel and starting tear-out.', photos: [ { id: 'sct_002_a2_p1', url: '/assets/images/properties/Beige_Two_Story_House.jpg', name: 'oncor-disconnect.jpg' }, ], actorId: 'sub_001', actorName: 'Carlos Mendoza', actorRole: 'Subcontractor', at: '2026-05-12T08:20:00Z', }, { id: 'sct_002_a3', type: 'progress_update', status: 'In Progress', note: 'Old 100A panel removed. Service entrance wires capped and labelled. Mast looks good — no signs of corrosion.', photos: [ { id: 'sct_002_a3_p1', url: '/assets/images/properties/Modern_White_Suburban_Home.jpg', name: 'mast-check.jpg' }, { id: 'sct_002_a3_p2', url: '/assets/images/properties/Brick_Front_Porch_Home.jpg', name: 'old-panel-out.jpg' }, ], actorId: 'sub_001', actorName: 'Carlos Mendoza', actorRole: 'Subcontractor', at: '2026-05-12T10:30:00Z', }, { id: 'sct_002_a4', type: 'progress_update', status: 'In Progress', note: 'New 200A Square D panel mounted and bonded. Two new ground rods driven and connected.', photos: [ { id: 'sct_002_a4_p1', url: '/assets/images/properties/Sunny_Suburban_House.jpg', name: 'new-panel-mount.jpg' }, ], actorId: 'sub_001', actorName: 'Carlos Mendoza', actorRole: 'Subcontractor', at: '2026-05-12T13:15:00Z', }, { id: 'sct_002_a5', type: 'progress_update', status: 'In Progress', note: 'Migrated 20 of 24 home circuits to the new panel. Tested with multimeter — all live and balanced across legs. Remaining 4 circuits (HVAC + dryer) to be wired tomorrow before re-energizing.', photos: [ { id: 'sct_002_a5_p1', url: '/assets/images/properties/Luxury_Driveway_Residence.jpg', name: 'circuits-migrated.jpg' }, { id: 'sct_002_a5_p2', url: '/assets/images/properties/Overlapping_Roof_Shingles.jpg', name: 'panel-labels.jpg' }, ], actorId: 'sub_001', actorName: 'Carlos Mendoza', actorRole: 'Subcontractor', at: '2026-05-12T16:00:00Z', }, ], projectId: 'PRJ-2026-003', paymentStatus: 'Unpaid', paymentRequestedAt: '2026-05-12T17:00:00Z', createdAt: '2026-05-10T15:40:00Z', updatedAt: '2026-05-12T17:00:00Z', }, // ------------------------------------------------------------------- // sct_003 — Dwayne (sub_003) — Completed paint job // ------------------------------------------------------------------- { id: 'sct_003', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_003', subcontractorName: 'Dwayne Boudreaux', assignedBy: 'a1', assignedByName: 'Wade Hollis', title: 'Touch-up exterior paint', location: '3913 Arizona Pl, Plano, TX 75023', description: 'Front fascia and side trim need fresh coat after siding repair. Match existing Sherwin-Williams color.', dueDate: '2026-05-18', priority: 'low', status: 'Completed', photos: [ { id: 'sct_003_p1', name: 'Before.jpg', url: '/assets/images/properties/Brick_Front_Porch_Home.jpg', annotation: 'Before' }, ], statusHistory: [ { id: 'sct_003_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Task created.', at: '2026-05-05T11:00:00Z' }, { id: 'sct_003_h2', status: 'In Progress', actorId: 'sub_003', actorName: 'Dwayne Boudreaux', comment: 'Started prep work.', at: '2026-05-13T08:00:00Z' }, { id: 'sct_003_h3', status: 'Completed', actorId: 'sub_003', actorName: 'Dwayne Boudreaux', comment: 'Walked through with homeowner.', at: '2026-05-14T17:30:00Z' }, ], thread: [ { id: 'sct_003_t1', channel: 'group', senderId: 'sub_003', senderName: 'Dwayne Boudreaux', senderRole: 'Subcontractor', body: 'Wrapped up — homeowner signed off. Photos uploaded.', at: '2026-05-14T17:25:00Z', mine: true }, ], expenses: [ { id: 'sct_003_e1', description: 'Paint — SW Repose Gray (1 gal)', category: 'Materials', amount: 52.5, notes: '', receiptUrl: '', createdAt: '2026-05-13T07:30:00Z' }, ], fees: [ { id: 'sct_003_f1', description: 'Touch-up paint labour', type: 'Labour / Service Fee', amount: 180, createdAt: '2026-05-14T17:35:00Z' }, ], projectId: 'PRJ-2026-006', paymentStatus: 'Paid', paymentRequestedAt: '2026-05-14T17:35:00Z', paidAt: '2026-05-18T10:15:00Z', createdAt: '2026-05-05T11:00:00Z', updatedAt: '2026-05-14T17:30:00Z', }, // ------------------------------------------------------------------- // sct_004 — Maya (sub_002) — emergency tarping, Assigned // ------------------------------------------------------------------- { id: 'sct_004', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_002', subcontractorName: 'Maya Patel', assignedBy: 'own_001', assignedByName: 'Justin Johnson', title: 'Emergency tarp — storm damage', location: '2604 Dunwick Dr, Plano, TX 75023', description: 'Hailstorm overnight; 3–4 ft section of decking exposed near chimney. Tarp ASAP to prevent further water intrusion. Permanent repair will be scheduled after insurance scope.', dueDate: '2026-05-19', priority: 'high', status: 'Assigned', photos: [ { id: 'sct_004_p1', name: 'Storm_Damage.jpg', url: '/assets/images/properties/Broken_Roof_Surface.jpg', annotation: 'Exposed decking near chimney' }, { id: 'sct_004_p2', name: 'Roof_Texture.jpg', url: '/assets/images/properties/Residential_Roof_Texture.jpg', annotation: '' }, ], statusHistory: [ { id: 'sct_004_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Storm-response priority. Please confirm ETA.', at: '2026-05-18T22:40:00Z' }, ], thread: [ { id: 'sct_004_t1', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Can you make it first thing tomorrow morning?', at: '2026-05-18T22:42:00Z' }, { id: 'sct_004_t2', channel: 'group', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'Yes — on site by 7:30 AM. Bringing tarps and battens.', at: '2026-05-18T23:05:00Z', mine: true }, { id: 'sct_004_t3', channel: 'direct', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Customer is filing an insurance claim — make sure to document everything with photos.', at: '2026-05-18T23:10:00Z' }, ], expenses: [], fees: [], projectId: 'PRJ-2026-001', paymentStatus: 'Unpaid', paymentRequestedAt: null, createdAt: '2026-05-18T22:40:00Z', updatedAt: '2026-05-18T23:10:00Z', }, // ------------------------------------------------------------------- // sct_005 — Maya (sub_002) — Attic ventilation inspection, On Hold // ------------------------------------------------------------------- { id: 'sct_005', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_002', subcontractorName: 'Maya Patel', assignedBy: 'a1', assignedByName: 'Wade Hollis', title: 'Inspect attic ventilation', location: '6909 Custer Rd, Plano, TX 75023', description: 'Customer reports unusually high attic temps. Check ridge vent, soffit airflow, and intake/exhaust balance. Recommend remediation.', dueDate: '2026-05-30', priority: 'medium', status: 'On Hold', photos: [ { id: 'sct_005_p1', name: 'Attic_View.jpg', url: '/assets/images/properties/Roof_Inspection_Angle.jpg', annotation: 'Access from south gable' }, ], statusHistory: [ { id: 'sct_005_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Task created.', at: '2026-05-12T10:00:00Z' }, { id: 'sct_005_h2', status: 'In Progress', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'Initial pass done; baffles look blocked.', at: '2026-05-14T11:00:00Z' }, { id: 'sct_005_h3', status: 'On Hold', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'Waiting on homeowner to clear stored boxes from access path.', at: '2026-05-15T16:00:00Z' }, ], thread: [ { id: 'sct_005_t1', channel: 'group', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'Access is blocked — homeowner needs to move some storage before I can complete the inspection.', at: '2026-05-15T15:55:00Z', mine: true }, { id: 'sct_005_t2', channel: 'direct', senderId: 'a1', senderName: 'Wade Hollis', senderRole: 'Admin', body: 'I will reach out to the homeowner and reschedule.', at: '2026-05-15T16:20:00Z' }, ], expenses: [ { id: 'sct_005_e1', description: 'Infrared thermometer (rental)', category: 'Equipment Rental', amount: 35.0, notes: '', receiptUrl: '', createdAt: '2026-05-14T10:30:00Z' }, ], fees: [ { id: 'sct_005_f1', description: 'Inspection — initial visit', type: 'Inspection Fee', amount: 125, createdAt: '2026-05-14T13:00:00Z' }, ], activities: [ { id: 'sct_005_a1', type: 'status_change', status: 'Assigned', note: 'Task created. Customer reports unusually high attic temps.', photos: [], actorId: 'a1', actorName: 'Wade Hollis', actorRole: 'Admin', at: '2026-05-12T10:00:00Z', }, { id: 'sct_005_a2', type: 'status_change', status: 'In Progress', note: 'Initial inspection pass — soffit baffles appear blocked. Need attic access for confirmation.', photos: [ { id: 'sct_005_a2_p1', url: '/assets/images/properties/Roof_Inspection_Angle.jpg', name: 'south-gable.jpg' }, ], actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor', at: '2026-05-14T11:00:00Z', }, { id: 'sct_005_a3', type: 'status_change', status: 'On Hold', note: 'Work stopped — homeowner needs to clear stored boxes blocking attic access before I can finish.', photos: [ { id: 'sct_005_a3_p1', url: '/assets/images/properties/Beige_Two_Story_House.jpg', name: 'access-blocked.jpg' }, ], actorId: 'sub_002', actorName: 'Maya Patel', actorRole: 'Subcontractor', at: '2026-05-15T16:00:00Z', }, ], projectId: 'PRJ-2026-007', paymentStatus: 'Unpaid', paymentRequestedAt: '2026-05-14T13:00:00Z', createdAt: '2026-05-12T10:00:00Z', updatedAt: '2026-05-15T16:20:00Z', }, // ------------------------------------------------------------------- // sct_006 — Maya (sub_002) — Gutter replacement, Assigned // ------------------------------------------------------------------- { id: 'sct_006', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_002', subcontractorName: 'Maya Patel', assignedBy: 'own_001', assignedByName: 'Justin Johnson', title: 'Replace gutter on south elevation', location: '7224 Independence Pkwy, Plano, TX 75025', description: '30 ft section of K-style gutter — pulling away from fascia after storm. Replace with new 6" oversized gutter, match existing bronze color.', dueDate: '2026-06-02', priority: 'low', status: 'Assigned', photos: [], statusHistory: [ { id: 'sct_006_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Task created and assigned.', at: '2026-05-17T14:00:00Z' }, ], thread: [], expenses: [], fees: [], projectId: 'PRJ-2026-009', paymentStatus: 'Unpaid', paymentRequestedAt: null, createdAt: '2026-05-17T14:00:00Z', updatedAt: '2026-05-17T14:00:00Z', }, // ------------------------------------------------------------------- // sct_007 — Maya (sub_002) — completed earlier this month // ------------------------------------------------------------------- { id: 'sct_007', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_002', subcontractorName: 'Maya Patel', assignedBy: 'a1', assignedByName: 'Wade Hollis', title: 'Roof deck repair — north slope', location: '2604 Dunwick Dr, Plano, TX 75023', description: 'Sheathing soft in two ~2x2 ft areas. Cut out, replace with matching OSB, re-felt and re-shingle.', dueDate: '2026-05-08', priority: 'high', status: 'Completed', photos: [ { id: 'sct_007_p1', name: 'Deck_Repair.jpg', url: '/assets/images/properties/Cracked_Asphalt_Shingles.jpg', annotation: 'Soft section, top right' }, ], statusHistory: [ { id: 'sct_007_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Task created.', at: '2026-05-01T09:00:00Z' }, { id: 'sct_007_h2', status: 'In Progress', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'Tear-off underway.', at: '2026-05-05T07:30:00Z' }, { id: 'sct_007_h3', status: 'Completed', actorId: 'sub_002', actorName: 'Maya Patel', comment: 'Closed out, photos in.', at: '2026-05-07T16:45:00Z' }, ], thread: [ { id: 'sct_007_t1', channel: 'group', senderId: 'sub_002', senderName: 'Maya Patel', senderRole: 'Subcontractor', body: 'All done — closed out. Final photos uploaded.', at: '2026-05-07T16:40:00Z', mine: true }, ], expenses: [ { id: 'sct_007_e1', description: 'OSB sheathing 4x8 (2 sheets)', category: 'Materials', amount: 76.0, notes: '', receiptUrl: '', createdAt: '2026-05-05T07:00:00Z' }, { id: 'sct_007_e2', description: 'Underlayment roll', category: 'Materials', amount: 38.0, notes: '', receiptUrl: '', createdAt: '2026-05-05T07:05:00Z' }, { id: 'sct_007_e3', description: 'Disposal — dump fee', category: 'Disposal / Dumpster', amount: 55.0, notes: '', receiptUrl: '', createdAt: '2026-05-07T17:00:00Z' }, ], fees: [ { id: 'sct_007_f1', description: 'Labour — deck repair (2 days)', type: 'Labour / Service Fee', amount: 920, createdAt: '2026-05-07T16:50:00Z' }, ], projectId: 'PRJ-2026-001', paymentStatus: 'Paid', paymentRequestedAt: '2026-05-07T16:50:00Z', paidAt: '2026-05-12T14:00:00Z', createdAt: '2026-05-01T09:00:00Z', updatedAt: '2026-05-07T17:00:00Z', }, // ------------------------------------------------------------------- // sct_008 — Carlos (sub_001) — GFCI install, fully populated // ------------------------------------------------------------------- { id: 'sct_008', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_001', subcontractorName: 'Carlos Mendoza', assignedBy: 'a1', assignedByName: 'Wade Hollis', title: 'Install GFCI outlets — kitchen + bath', location: '2612 Dunwick Dr, Plano, TX 75023', description: 'Replace 4 standard receptacles with GFCI: 2 kitchen counter outlets (left of sink + island), 1 master bath vanity, 1 powder room vanity. Code update required as part of insurance work. Use Leviton SmartlockPro 20A devices. Test all branches with plug-in tester before signing off.', dueDate: '2026-05-26', priority: 'medium', status: 'In Progress', photos: [ { id: 'sct_008_p1', name: 'Kitchen_Outlet.jpg', url: '/assets/images/properties/Modern_White_Suburban_Home.jpg', annotation: 'Kitchen — left of sink, current standard receptacle' }, { id: 'sct_008_p2', name: 'Island_Outlet.jpg', url: '/assets/images/properties/Sunny_Suburban_House.jpg', annotation: 'Island outlet — replace with GFCI' }, { id: 'sct_008_p3', name: 'Master_Bath.jpg', url: '/assets/images/properties/Brick_Front_Porch_Home.jpg', annotation: 'Master bath vanity outlet' }, { id: 'sct_008_p4', name: 'Powder_Room.jpg', url: '/assets/images/properties/Luxury_Driveway_Residence.jpg', annotation: 'Powder room — confirm wire gauge' }, ], statusHistory: [ { id: 'sct_008_h1', status: 'Assigned', actorId: 'a1', actorName: 'Wade Hollis', comment: 'Task created and assigned.', at: '2026-05-16T13:00:00Z' }, { id: 'sct_008_h2', status: 'In Progress', actorId: 'sub_001', actorName: 'Carlos Mendoza', comment: 'On site, kitchen done; bath next.', at: '2026-05-19T10:15:00Z' }, ], thread: [ { id: 'sct_008_t1', channel: 'group', senderId: 'a1', senderName: 'Wade Hollis', senderRole: 'Admin', body: 'Insurance is reimbursing the code upgrade — keep receipts for the GFCI devices.', at: '2026-05-16T13:05:00Z' }, { id: 'sct_008_t2', channel: 'group', senderId: 'sub_001', senderName: 'Carlos Mendoza', senderRole: 'Subcontractor', body: 'Confirmed — Leviton 20A devices, will keep receipts. Targeting May 19 for the visit.', at: '2026-05-16T14:20:00Z', mine: true }, { id: 'sct_008_t3', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Homeowner has 2 small kids — please bring outlet covers as a courtesy.', at: '2026-05-17T09:00:00Z' }, { id: 'sct_008_t4', channel: 'group', senderId: 'sub_001', senderName: 'Carlos Mendoza', senderRole: 'Subcontractor', body: 'Will do. Already in my truck.', at: '2026-05-17T09:05:00Z', mine: true }, { id: 'sct_008_t5', channel: 'group', senderId: 'sub_001', senderName: 'Carlos Mendoza', senderRole: 'Subcontractor', body: 'Kitchen done. Both counter outlets and the island are on GFCI now. Starting bath after lunch.', at: '2026-05-19T12:30:00Z', mine: true }, { id: 'sct_008_t6', channel: 'direct', senderId: 'a1', senderName: 'Wade Hollis', senderRole: 'Admin', body: 'No permit needed for like-for-like swap. Skip the city portal.', at: '2026-05-16T13:10:00Z' }, { id: 'sct_008_t7', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Carlos — if you find any aluminum branch wiring, stop and flag it. We will scope as a change order.', at: '2026-05-17T10:00:00Z' }, { id: 'sct_008_t8', channel: 'direct', senderId: 'sub_001', senderName: 'Carlos Mendoza', senderRole: 'Subcontractor', body: 'All copper, no aluminum. Proceeding as scoped.', at: '2026-05-19T10:30:00Z', mine: true }, ], expenses: [ { id: 'sct_008_e1', description: 'Leviton 20A GFCI receptacles (qty 4)', category: 'Materials', amount: 92.0, notes: 'SmartlockPro tamper-resistant.', receiptUrl: 'mock://receipts/leviton-gfci.pdf', createdAt: '2026-05-18T16:00:00Z' }, { id: 'sct_008_e2', description: 'Wire nuts + electrical tape', category: 'Materials', amount: 11.5, notes: '', receiptUrl: '', createdAt: '2026-05-18T16:02:00Z' }, { id: 'sct_008_e3', description: 'Childproof outlet covers (10-pack)', category: 'Materials', amount: 8.99, notes: 'Courtesy for homeowner.', receiptUrl: '', createdAt: '2026-05-18T16:05:00Z' }, { id: 'sct_008_e4', description: 'Plug-in circuit tester', category: 'Equipment Rental', amount: 24.0, notes: 'Sperry Instruments GFCI tester.', receiptUrl: '', createdAt: '2026-05-18T16:10:00Z' }, { id: 'sct_008_e5', description: 'Fuel — Dunwick Dr round trip', category: 'Travel / Fuel', amount: 22.4, notes: '', receiptUrl: '', createdAt: '2026-05-19T18:00:00Z' }, ], fees: [ { id: 'sct_008_f1', description: 'Labour — Kitchen GFCI install (2 outlets + island)', type: 'Labour / Service Fee', amount: 220, createdAt: '2026-05-19T12:35:00Z' }, { id: 'sct_008_f2', description: 'Mobilization to site', type: 'Mobilization Fee', amount: 75, createdAt: '2026-05-19T08:00:00Z' }, ], activities: [ { id: 'sct_008_a1', type: 'status_change', status: 'Assigned', note: 'Task created and assigned to Carlos. Insurance covering code upgrade.', photos: [], actorId: 'a1', actorName: 'Wade Hollis', actorRole: 'Admin', at: '2026-05-16T13:00:00Z', }, { id: 'sct_008_a2', type: 'status_change', status: 'In Progress', note: 'Arrived on site. Verified wiring is copper (no aluminum). Starting kitchen counter outlets first.', photos: [ { id: 'sct_008_a2_p1', url: '/assets/images/properties/Modern_White_Suburban_Home.jpg', name: 'kitchen-before.jpg' }, ], actorId: 'sub_001', actorName: 'Carlos Mendoza', actorRole: 'Subcontractor', at: '2026-05-19T10:15:00Z', }, { id: 'sct_008_a3', type: 'progress_update', status: 'In Progress', note: 'Both kitchen counter outlets swapped to Leviton 20A GFCI. Tested with plug-in tester — passed.', photos: [ { id: 'sct_008_a3_p1', url: '/assets/images/properties/Sunny_Suburban_House.jpg', name: 'gfci-installed.jpg' }, ], actorId: 'sub_001', actorName: 'Carlos Mendoza', actorRole: 'Subcontractor', at: '2026-05-19T12:30:00Z', }, ], projectId: 'PRJ-2026-004', paymentStatus: 'Unpaid', paymentRequestedAt: '2026-05-19T12:35:00Z', createdAt: '2026-05-16T13:00:00Z', updatedAt: '2026-05-19T18:00:00Z', }, // ------------------------------------------------------------------- // sct_010 — Carlos (sub_001) — newly assigned, awaiting acknowledgement // ------------------------------------------------------------------- { id: 'sct_010', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_001', subcontractorName: 'Carlos Mendoza', assignedBy: 'own_001', assignedByName: 'Justin Johnson', title: 'Install EV charger circuit — garage', location: '3913 Arizona Pl, Plano, TX 75023', description: 'Homeowner purchased a Tesla Wall Connector. Run a dedicated 60A 240V circuit from the main panel to the garage wall. Coordinate with homeowner for outage window. Permit required.', dueDate: '2026-05-29', priority: 'medium', status: 'Assigned', photos: [ { id: 'sct_010_p1', name: 'Garage_Wall.jpg', url: '/assets/images/properties/Modern_White_Suburban_Home.jpg', annotation: 'Wall Connector mounting location' }, { id: 'sct_010_p2', name: 'Panel_Location.jpg', url: '/assets/images/properties/Brick_Front_Porch_Home.jpg', annotation: 'Main panel — check available breaker slots' }, ], statusHistory: [ { id: 'sct_010_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Task created and assigned. Please confirm ETA.', at: '2026-05-20T09:00:00Z' }, ], thread: [ { id: 'sct_010_t1', channel: 'group', senderId: 'own_001', senderName: 'Justin Johnson', senderRole: 'Owner', body: 'Carlos — homeowner is hoping to have this in before next weekend. Can you take a look at the panel and confirm slot availability?', at: '2026-05-20T09:02:00Z' }, ], expenses: [], fees: [], activities: [], projectId: 'PRJ-2026-006', paymentStatus: 'Unpaid', paymentRequestedAt: null, createdAt: '2026-05-20T09:00:00Z', updatedAt: '2026-05-20T09:02:00Z', }, // ------------------------------------------------------------------- // sct_009 — Jennifer Castillo (sub_004) — On Hold plumbing // ------------------------------------------------------------------- { id: 'sct_009', companyId: 'lynkeduppro', companyName: 'LynkedUp Pro Roofing', subcontractorId: 'sub_004', subcontractorName: 'Jennifer Castillo', assignedBy: 'own_001', assignedByName: 'Justin Johnson', title: 'Rough-in plumbing for ADU bathroom', location: '6112 Roundrock Dr, Plano, TX 75025', description: 'New ADU build — bathroom rough-in. Coordinate with framing inspection.', dueDate: '2026-06-15', priority: 'medium', status: 'On Hold', photos: [], statusHistory: [ { id: 'sct_009_h1', status: 'Assigned', actorId: 'own_001', actorName: 'Justin Johnson', comment: 'Task created.', at: '2026-05-09T10:00:00Z' }, { id: 'sct_009_h2', status: 'On Hold', actorId: 'sub_004', actorName: 'Jennifer Castillo', comment: 'Waiting on framing to finish.', at: '2026-05-13T12:00:00Z' }, ], thread: [], expenses: [], fees: [], projectId: null, paymentStatus: 'Unpaid', paymentRequestedAt: null, createdAt: '2026-05-09T10:00:00Z', updatedAt: '2026-05-13T12:00:00Z', }, ]; const MOCK_NOTIFICATIONS = [ // ============================================ // sub_002 — Maya Patel (default demo user) // ============================================ // -- Task Assigned ----------------------------- { id: 'notif_001', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: 'You have been assigned a task by LynkedUp Pro Roofing — "Replace damaged ridge cap"', taskId: 'sct_001', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-15T09:12:00Z', }, { id: 'notif_002', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: 'You have been assigned a task by LynkedUp Pro Roofing — "Emergency tarp — storm damage"', taskId: 'sct_004', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-18T22:40:00Z', }, { id: 'notif_003', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: 'You have been assigned a task by LynkedUp Pro Roofing — "Replace gutter on south elevation"', taskId: 'sct_006', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-17T14:00:00Z', }, { id: 'notif_004', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: 'You have been assigned a task by LynkedUp Pro Roofing — "Inspect attic ventilation"', taskId: 'sct_005', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-12T10:00:00Z', }, // -- Message Received -------------------------- { id: 'notif_005', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'message_received', message: 'Justin Johnson replied on sct_001 — "Please source them — match Owens Corning…"', taskId: 'sct_001', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-15T09:14:00Z', }, { id: 'notif_006', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'message_received', message: 'Justin Johnson sent a new message on sct_004 — "Can you make it first thing tomorrow morning?"', taskId: 'sct_004', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-18T22:42:00Z', }, { id: 'notif_007', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'message_received', message: 'Wade Hollis added an internal note on sct_005 — "I will reach out to the homeowner…"', taskId: 'sct_005', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-15T16:20:00Z', }, // -- Task Updated ------------------------------ { id: 'notif_008', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'task_updated', message: 'Due date for sct_001 was updated to May 25, 2026 by Justin Johnson.', taskId: 'sct_001', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-15T11:00:00Z', }, { id: 'notif_009', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'task_updated', message: 'Priority for sct_004 raised to High by Justin Johnson.', taskId: 'sct_004', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-18T22:50:00Z', }, { id: 'notif_010', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'task_updated', message: 'New photo attached to sct_001 by Justin Johnson.', taskId: 'sct_001', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-15T12:00:00Z', }, // -- Status Changed ---------------------------- { id: 'notif_011', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'status_changed', message: 'Status for sct_001 changed to In Progress by Maya Patel.', taskId: 'sct_001', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-16T08:30:00Z', }, { id: 'notif_012', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'status_changed', message: 'Status for sct_005 changed to On Hold by Maya Patel.', taskId: 'sct_005', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-15T16:00:00Z', }, { id: 'notif_013', recipientUserId: 'sub_002', recipientRole: 'SUBCONTRACTOR', type: 'status_changed', message: 'Status for sct_007 changed to Completed by Maya Patel.', taskId: 'sct_007', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-07T16:45:00Z', }, // ============================================ // sub_001 — Carlos // ============================================ { id: 'notif_021', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: 'You have been assigned a task by LynkedUp Pro Roofing — "Replace breaker panel"', taskId: 'sct_002', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-10T15:40:00Z', }, { id: 'notif_022', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: 'You have been assigned a task by LynkedUp Pro Roofing — "Install GFCI outlets — kitchen + bath"', taskId: 'sct_008', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-16T13:00:00Z', }, { id: 'notif_026', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'message_received', message: 'Wade Hollis sent a new message on sct_008 — "Insurance is reimbursing the code upgrade…"', taskId: 'sct_008', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-16T13:05:00Z', }, { id: 'notif_027', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'message_received', message: 'Justin Johnson sent a new message on sct_008 — "Homeowner has 2 small kids…"', taskId: 'sct_008', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-17T09:00:00Z', }, { id: 'notif_028', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'task_updated', message: 'Photos updated on sct_008 by Wade Hollis.', taskId: 'sct_008', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-16T13:30:00Z', }, { id: 'notif_029', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'status_changed', message: 'Status for sct_008 changed to In Progress by Carlos Mendoza.', taskId: 'sct_008', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-19T10:15:00Z', }, { id: 'notif_023', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'message_received', message: 'Justin Johnson confirmed Oncor disconnect on sct_002.', taskId: 'sct_002', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-11T16:30:00Z', }, { id: 'notif_024', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'task_updated', message: 'Permit fee on sct_002 was approved by Wade Hollis.', taskId: 'sct_002', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: false, createdAt: '2026-05-11T14:30:00Z', }, { id: 'notif_025', recipientUserId: 'sub_001', recipientRole: 'SUBCONTRACTOR', type: 'status_changed', message: 'Status for sct_002 changed to In Progress by Carlos Mendoza.', taskId: 'sct_002', fromCompanyId: 'lynkeduppro', fromCompanyName: 'LynkedUp Pro Roofing', isRead: true, createdAt: '2026-05-12T08:20:00Z', }, ]; // --- SEED HELPERS --- /** * Maps MOCK_STORM_ATTRIBUTION_LEADS into the shape LeadsListPage expects. * Field coverage: * id, firstName, lastName, address, city, state, * urgency, status, leadSource, stormSource (truthy obj), * phones[{ number, isPrimary }], * canvasserName, createdByName, createdAt */ function seedLeadsFromAttribution() { const canvassers = [ 'Cody Tatum', 'Hannah Reyes', 'Travis Boone', 'Shelby Greer', 'Dalton Pruitt', ]; const creators = [ 'Hannah Reyes', 'Travis Boone', 'Cody Tatum', 'Shelby Greer', 'Dalton Pruitt', ]; // Deterministic assignment based on SAL index so output is stable. return MOCK_STORM_ATTRIBUTION_LEADS.map((sal, idx) => { const canvasserName = canvassers[idx % canvassers.length]; const createdByName = creators[(idx + 2) % creators.length]; // Build a realistic phone number from the index const areaCode = idx % 2 === 0 ? '469' : '972'; const mid = String(500 + idx).padStart(3, '0'); const last = String(1000 + idx * 37).padStart(4, '0').slice(-4); const phoneNumber = `(${areaCode}) ${mid}-${last}`; return { id: sal.id, firstName: sal.firstName, lastName: sal.lastName, address: sal.address, city: sal.city, state: sal.state, urgency: sal.urgency, status: sal.status, leadSource: sal.leadSource, stormSource: sal.stormSource, // truthy object — renders Storm Zone badge createdAt: sal.createdAt, canvasserName, createdByName, phones: [{ number: phoneNumber, isPrimary: true }], isQuickCapture: false, }; }); } // --- 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(() => seedLeadsFromAttribution()); 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); const [stormZones, setStormZones] = useState(INITIAL_STORM_ZONES); // 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 Calloway', 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: 'own_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: 'own_002', name: 'Diana Reeves', email: 'diana@lynkeduppro.com', roleKey: 'OWNER', status: 'active', joinedAt: '2024-01-15', lastActivity: '2025-05-10', assignedJobsCount: 8 }, { id: 'om_003', userId: 'a1', name: 'Wade Hollis', email: 'admin1@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-02-10', lastActivity: '2025-05-11', assignedJobsCount: 6 }, { id: 'om_004', userId: 'a2', name: 'Darlene Brooks', email: 'admin2@lynkeduppro.com', roleKey: 'ADMIN', status: 'active', joinedAt: '2024-03-01', lastActivity: '2025-05-09', assignedJobsCount: 4 }, { id: 'om_005', userId: 'a3', name: 'Roy Schaefer', 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 Calloway', 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: 'e1', name: 'Cody Tatum', email: 'agent1@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-10', lastActivity: '2025-05-12', assignedJobsCount: 2 }, { id: 'om_009', userId: 'e2', name: 'Hannah Reyes', email: 'agent2@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-05-15', lastActivity: '2025-05-07', assignedJobsCount: 3 }, { id: 'om_010', userId: 'e3', name: 'Travis Boone', email: 'agent3@plano.com', roleKey: 'CANVASSER', status: 'invited', joinedAt: '2024-06-01', lastActivity: null, assignedJobsCount: 0 }, { id: 'om_011', userId: 'e4', name: 'Shelby Greer', email: 'agent4@plano.com', roleKey: 'CANVASSER', status: 'active', joinedAt: '2024-06-10', lastActivity: '2025-05-06', assignedJobsCount: 1 }, { id: 'om_012', userId: 'e5', name: 'Dalton Pruitt', 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 Mendoza', email: 'carlos@electricpro.com', roleKey: 'SUBCONTRACTOR', status: 'active', joinedAt: '2024-08-01', lastActivity: '2025-05-10', assignedJobsCount: 5 }, ]); // Subcontractor management (Feature 8.2) const [subcontractors, setSubcontractors] = useState(MOCK_SUBCONTRACTORS); const [subcontractorTasks, setSubcontractorTasks] = useState(MOCK_SUBCONTRACTOR_TASKS); const [notifications, setNotifications] = useState(MOCK_NOTIFICATIONS); // One lookup for any person across users/personnel/owners/subcontractors. const peopleIndex = useMemo(() => { const idx = {}; const add = (p) => { if (p?.id && !idx[p.id]) idx[p.id] = p; }; [...(users || []), ...(personnel || []), ...(owners || []), ...(subcontractors || [])].forEach(add); return idx; }, [users, personnel, owners, subcontractors]); const resolvePerson = (id) => { const p = peopleIndex[id]; if (!p) return { id, name: 'Unknown', email: '', role: '' }; return { id: p.id, name: p.name || p.fullName || 'Unknown', email: p.email || p.emailAddress || '', role: p.role || '' }; }; // 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; }; // ── Notifications ─────────────────────────────────────────────────────── // Designed so a real-time transport (websocket, SSE, push) can later // subscribe to `addNotification` without changing call sites. const addNotification = (payload) => { const notif = { id: `notif_${Date.now()}_${Math.floor(Math.random() * 1000)}`, isRead: false, createdAt: new Date().toISOString(), ...payload, }; setNotifications(prev => [notif, ...prev]); logger.info('Notification dispatched', { notif }); return notif; }; const markNotificationRead = (notificationId) => { setNotifications(prev => prev.map(n => n.id === notificationId ? { ...n, isRead: true } : n)); }; // ── Subcontractor Tasks (Feature 8.2) ────────────────────────────────── const addSubcontractorTask = (input) => { const subcontractor = subcontractors.find(s => s.id === input.subcontractorId); if (!subcontractor) { toast.error('Failed to assign task: subcontractor not found.'); return null; } const now = new Date().toISOString(); const task = { id: `sct_${Date.now()}`, companyId: input.companyId, companyName: input.companyName, subcontractorId: subcontractor.id, subcontractorName: subcontractor.name, assignedBy: input.assignedBy, assignedByName: input.assignedByName, title: input.title, location: input.location, description: input.description || '', dueDate: input.dueDate, priority: input.priority || 'medium', status: 'Assigned', photos: (input.photos || []).map((p, i) => ({ id: p.id || `${Date.now()}_${i}`, name: p.name, url: p.url, annotation: p.annotation || '', })), statusHistory: [{ id: `h_${Date.now()}`, status: 'Assigned', actorId: input.assignedBy, actorName: input.assignedByName, comment: 'Task created and assigned.', at: now, }], thread: [], expenses: [], fees: [], createdAt: now, updatedAt: now, }; setSubcontractorTasks(prev => [task, ...prev]); addNotification({ recipientUserId: subcontractor.id, recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: `You have been assigned a task by ${input.companyName}`, taskId: task.id, fromCompanyId: input.companyId, fromCompanyName: input.companyName, }); toast.success(`Task assigned to ${subcontractor.name}`); return task; }; const setSubcontractorTaskStatus = (taskId, nextStatus, comment, actor, photos = []) => { const now = new Date().toISOString(); let didChange = false; let companyId = null; let companyName = null; let assignedByUserId = null; const normalizedPhotos = (photos || []).map((p, i) => ({ id: p.id || `${Date.now()}_${i}_${Math.random().toString(36).slice(2, 7)}`, url: p.url, name: p.name || '', })); setSubcontractorTasks(prev => prev.map(t => { if (t.id !== taskId) return t; if (t.status === nextStatus) return t; didChange = true; companyId = t.companyId; companyName = t.companyName; assignedByUserId = t.assignedBy; const entry = { id: `h_${Date.now()}`, status: nextStatus, actorId: actor?.id || t.subcontractorId, actorName: actor?.name || t.subcontractorName, comment: comment?.trim() || '', at: now, }; const activityEntry = { id: `a_${Date.now()}`, type: 'status_change', status: nextStatus, note: comment?.trim() || '', photos: normalizedPhotos, actorId: actor?.id || t.subcontractorId, actorName: actor?.name || t.subcontractorName, actorRole: actor?.role || 'Subcontractor', at: now, }; return { ...t, status: nextStatus, statusHistory: [...(t.statusHistory || []), entry], activities: [...(t.activities || []), activityEntry], updatedAt: now, }; })); if (didChange) { // Notify owner / admin who assigned the task if (assignedByUserId) { addNotification({ recipientUserId: assignedByUserId, recipientRole: 'OWNER', type: 'status_changed', message: `Status changed to ${nextStatus} by ${actor?.name || 'subcontractor'}`, taskId, fromCompanyId: companyId, fromCompanyName: companyName, }); } toast.success(`Status updated to ${nextStatus}`); } }; const addSubcontractorTaskMessage = (taskId, { body, channel = 'group', sender }) => { if (!body?.trim()) return; const now = new Date().toISOString(); let companyId = null; let companyName = null; let assignedByUserId = null; let subId = null; setSubcontractorTasks(prev => prev.map(t => { if (t.id !== taskId) return t; companyId = t.companyId; companyName = t.companyName; assignedByUserId = t.assignedBy; subId = t.subcontractorId; const msg = { id: `m_${Date.now()}`, channel, senderId: sender?.id, senderName: sender?.name || 'You', senderRole: sender?.role || 'Subcontractor', body: body.trim(), at: now, mine: true, }; return { ...t, thread: [...(t.thread || []), msg], updatedAt: now, }; })); // Notify the other side const recipientUserId = sender?.id === assignedByUserId ? subId : assignedByUserId; if (recipientUserId) { addNotification({ recipientUserId, recipientRole: sender?.id === assignedByUserId ? 'SUBCONTRACTOR' : 'OWNER', type: 'message_received', message: `New message from ${sender?.name || 'a teammate'}`, taskId, fromCompanyId: companyId, fromCompanyName: companyName, }); } }; const addSubcontractorTaskExpense = (taskId, fields) => { const now = new Date().toISOString(); setSubcontractorTasks(prev => prev.map(t => t.id === taskId ? { ...t, expenses: [ ...(t.expenses || []), { id: `e_${Date.now()}`, description: fields.description?.trim() || '', category: fields.category || 'Materials', amount: Number(fields.amount) || 0, notes: fields.notes?.trim() || '', receiptUrl: fields.receiptUrl || '', createdAt: now, }, ], updatedAt: now, } : t)); toast.success('Expense logged'); }; const addSubcontractorTaskFee = (taskId, fields) => { const now = new Date().toISOString(); setSubcontractorTasks(prev => prev.map(t => t.id === taskId ? { ...t, fees: [ ...(t.fees || []), { id: `f_${Date.now()}`, description: fields.description?.trim() || '', type: fields.type || 'Labour / Service Fee', amount: Number(fields.amount) || 0, createdAt: now, }, ], updatedAt: now, } : t)); toast.success('Fee submitted'); }; const addSubcontractorTaskActivity = (taskId, { note, photos = [] }, actor) => { const trimmed = note?.trim() || ''; if (!trimmed) return; const now = new Date().toISOString(); const normalizedPhotos = (photos || []).map((p, i) => ({ id: p.id || `${Date.now()}_${i}_${Math.random().toString(36).slice(2, 7)}`, url: p.url, name: p.name || '', })); let companyId = null; let companyName = null; let assignedByUserId = null; let subId = null; setSubcontractorTasks(prev => prev.map(t => { if (t.id !== taskId) return t; companyId = t.companyId; companyName = t.companyName; assignedByUserId = t.assignedBy; subId = t.subcontractorId; const activityEntry = { id: `a_${Date.now()}`, type: 'progress_update', status: t.status, note: trimmed, photos: normalizedPhotos, actorId: actor?.id || t.subcontractorId, actorName: actor?.name || t.subcontractorName, actorRole: actor?.role || 'Subcontractor', at: now, }; const historyEntry = { id: `h_${Date.now()}`, status: t.status, actorId: actor?.id || t.subcontractorId, actorName: actor?.name || t.subcontractorName, comment: trimmed, at: now, isProgressUpdate: true, }; return { ...t, activities: [...(t.activities || []), activityEntry], statusHistory: [...(t.statusHistory || []), historyEntry], updatedAt: now, }; })); const recipientUserId = actor?.id === assignedByUserId ? subId : assignedByUserId; if (recipientUserId) { addNotification({ recipientUserId, recipientRole: actor?.id === assignedByUserId ? 'SUBCONTRACTOR' : 'OWNER', type: 'progress_update', message: `New progress update from ${actor?.name || 'subcontractor'}`, taskId, fromCompanyId: companyId, fromCompanyName: companyName, }); } toast.success('Progress update posted'); }; const updateSubcontractorTask = (taskId, data) => { let updatedSubId = null; let prevSubId = null; let companyName = null; let companyId = null; setSubcontractorTasks(prev => prev.map(t => { if (t.id !== taskId) return t; prevSubId = t.subcontractorId; companyName = t.companyName; companyId = t.companyId; const nextSubId = data.subcontractorId ?? t.subcontractorId; updatedSubId = nextSubId; const nextSub = subcontractors.find(s => s.id === nextSubId); return { ...t, ...data, subcontractorId: nextSubId, subcontractorName: nextSub?.name ?? t.subcontractorName, photos: data.photos ? data.photos.map((p, i) => ({ id: p.id || `${Date.now()}_${i}`, name: p.name, url: p.url, annotation: p.annotation || '', })) : t.photos, updatedAt: new Date().toISOString(), }; })); // If subcontractor changed, dispatch a fresh notification to the new one. if (updatedSubId && prevSubId && updatedSubId !== prevSubId) { addNotification({ recipientUserId: updatedSubId, recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: `You have been assigned a task by ${companyName}`, taskId, fromCompanyId: companyId, fromCompanyName: companyName, }); } toast.success('Task updated'); }; const cancelSubcontractorTask = (taskId) => { setSubcontractorTasks(prev => prev.map(t => t.id === taskId ? { ...t, status: 'Cancelled', updatedAt: new Date().toISOString() } : t, )); toast.success('Task cancelled'); }; const reassignSubcontractorTask = (taskId, newSubcontractorId) => { const subcontractor = subcontractors.find(s => s.id === newSubcontractorId); if (!subcontractor) { toast.error('Failed to reassign: subcontractor not found.'); return; } let companyName = null; let companyId = null; setSubcontractorTasks(prev => prev.map(t => { if (t.id !== taskId) return t; companyName = t.companyName; companyId = t.companyId; return { ...t, subcontractorId: subcontractor.id, subcontractorName: subcontractor.name, status: t.status === 'Cancelled' ? 'Assigned' : t.status, updatedAt: new Date().toISOString(), }; })); addNotification({ recipientUserId: subcontractor.id, recipientRole: 'SUBCONTRACTOR', type: 'task_assigned', message: `You have been assigned a task by ${companyName}`, taskId, fromCompanyId: companyId, fromCompanyName: companyName, }); toast.success(`Task reassigned to ${subcontractor.name}`); }; return ( { 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)); }, // Storm Zones stormZones, addStormZone: (zone) => { const newZone = { ...zone, id: `sz-${Date.now()}`, createdAt: new Date().toISOString() }; setStormZones(prev => [...prev, newZone]); return newZone; }, updateStormZone: (id, data) => { setStormZones(prev => prev.map(z => z.id === id ? { ...z, ...data } : z)); }, deleteStormZone: (id) => { setStormZones(prev => prev.filter(z => z.id !== id)); }, 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'); }, // Subcontractor Management (Feature 8.2) subcontractors, addSubcontractor: (sub) => { const newSub = { ...sub, id: sub.id || `sub_${Date.now()}`, status: sub.status || 'active', companies: sub.companies || [], joinedAt: sub.joinedAt || new Date().toISOString().slice(0, 10), }; setSubcontractors(prev => [...prev, newSub]); return newSub; }, subcontractorTasks, taskPriorities: SUBCONTRACTOR_TASK_PRIORITIES, taskStatuses: SUBCONTRACTOR_TASK_STATUSES, subSelfStatuses: SUBCONTRACTOR_SELF_STATUSES, expenseCategories: SUBCONTRACTOR_EXPENSE_CATEGORIES, feeTypes: SUBCONTRACTOR_FEE_TYPES, addSubcontractorTask, updateSubcontractorTask, cancelSubcontractorTask, reassignSubcontractorTask, setSubcontractorTaskStatus, addSubcontractorTaskMessage, addSubcontractorTaskExpense, addSubcontractorTaskFee, addSubcontractorTaskActivity, notifications, addNotification, markNotificationRead, resolvePerson, }}> {children} ); }; 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', ], }; export const MOCK_STORM_EVENTS = []; // ── Storm-sourced leads — canvasser door-knock results linked to storm events ─ // Each lead has a stormSource { id, areaName, date, maxHailSize, severity } and // a contractValue (number) for Closed leads. createdAt is shortly after storm date. // ── Canvasser zones — polygons drawn on Storm Intel map, assigned to agents ─── export const INITIAL_STORM_ZONES = [ { id: 'sz-001', name: 'Spring Creek Zone A', stormId: 'SE-001', polygon: [ [33.108, -96.720], [33.100, -96.702], [33.086, -96.702], [33.080, -96.720], [33.086, -96.738], [33.100, -96.738], ], assignedAgentId: 'REP-01', assignedAgentName: 'Carlos Mendez', color: '#F59E0B', status: 'active', createdAt: '2026-04-29T08:00:00Z', }, { id: 'sz-002', name: 'Parker Rd Zone B', stormId: 'SE-003', polygon: [ [33.033, -96.712], [33.025, -96.697], [33.011, -96.697], [33.003, -96.712], [33.011, -96.727], [33.025, -96.727], ], assignedAgentId: 'REP-02', assignedAgentName: 'Aisha Kumar', color: '#8B5CF6', status: 'active', createdAt: '2026-02-04T09:00:00Z', }, { id: 'sz-003', name: 'Haggard Park Zone C', stormId: 'SE-006', polygon: [ [33.047, -96.774], [33.039, -96.757], [33.025, -96.757], [33.017, -96.774], [33.025, -96.791], [33.039, -96.791], ], assignedAgentId: 'REP-04', assignedAgentName: 'Nina Patel', color: '#10B981', status: 'completed', createdAt: '2025-11-06T07:30:00Z', }, ]; export const MOCK_STORM_ATTRIBUTION_LEADS = [ // ── SE-001 · Apr 28 2026 · Severe · NE Plano ────────────────────────────── { id: 'SAL-001', firstName: 'John', lastName: 'Martinez', address: '4821 Spring Creek Pkwy', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-04-29T10:14:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, { id: 'SAL-002', firstName: 'Sarah', lastName: 'Kim', address: '4905 Custer Rd', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Appointed', contractValue: null, createdAt: '2026-04-29T14:32:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, { id: 'SAL-003', firstName: 'Robert', lastName: 'Chen', address: '5012 Independence Pkwy', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Closed', contractValue: 18500, createdAt: '2026-04-30T09:05:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, { id: 'SAL-004', firstName: 'Maria', lastName: 'Garcia', address: '4720 Alma Dr', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Closed', contractValue: 24200, createdAt: '2026-04-30T11:48:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, { id: 'SAL-005', firstName: 'David', lastName: 'Thompson', address: '5130 Spring Creek Pkwy', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Appointed', contractValue: null, createdAt: '2026-05-01T08:22:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, { id: 'SAL-006', firstName: 'Lisa', lastName: 'Anderson', address: '4845 Custer Rd', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-05-01T13:10:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, { id: 'SAL-007', firstName: 'James', lastName: 'Wilson', address: '5020 Alma Dr', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'New', contractValue: null, createdAt: '2026-05-02T10:00:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, { id: 'SAL-008', firstName: 'Emily', lastName: 'Davis', address: '4980 Spring Creek Pkwy', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-05-03T09:45:00Z', stormSource: { id: 'SE-001', areaName: 'NE Plano / Spring Creek Pkwy', date: '2026-04-28', maxHailSize: 2.5, severity: 'severe' } }, // ── SE-002 · Mar 15 2026 · Moderate · Central Plano ────────────────────── { id: 'SAL-009', firstName: 'Michael', lastName: 'Brown', address: '1520 W 14th St', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-03-16T10:20:00Z', stormSource: { id: 'SE-002', areaName: 'Central Plano / W 15th St', date: '2026-03-15', maxHailSize: 1.2, severity: 'moderate' } }, { id: 'SAL-010', firstName: 'Jennifer', lastName: 'Taylor', address: '1608 W 15th St', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Appointed', contractValue: null, createdAt: '2026-03-17T11:05:00Z', stormSource: { id: 'SE-002', areaName: 'Central Plano / W 15th St', date: '2026-03-15', maxHailSize: 1.2, severity: 'moderate' } }, { id: 'SAL-011', firstName: 'Christopher',lastName: 'Moore', address: '1542 Ave K', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Closed', contractValue: 16800, createdAt: '2026-03-18T09:30:00Z', stormSource: { id: 'SE-002', areaName: 'Central Plano / W 15th St', date: '2026-03-15', maxHailSize: 1.2, severity: 'moderate' } }, { id: 'SAL-012', firstName: 'Amanda', lastName: 'Jackson', address: '1615 W 15th St', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-03-19T14:00:00Z', stormSource: { id: 'SE-002', areaName: 'Central Plano / W 15th St', date: '2026-03-15', maxHailSize: 1.2, severity: 'moderate' } }, { id: 'SAL-013', firstName: 'Daniel', lastName: 'White', address: '1480 W 14th St', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'New', contractValue: null, createdAt: '2026-03-20T10:15:00Z', stormSource: { id: 'SE-002', areaName: 'Central Plano / W 15th St', date: '2026-03-15', maxHailSize: 1.2, severity: 'moderate' } }, // ── SE-003 · Feb 3 2026 · Significant · SE Plano ───────────────────────── { id: 'SAL-014', firstName: 'Jessica', lastName: 'Harris', address: '3201 Parker Rd', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Appointed', contractValue: null, createdAt: '2026-02-04T09:00:00Z', stormSource: { id: 'SE-003', areaName: 'SE Plano / Parker Rd Corridor', date: '2026-02-03', maxHailSize: 1.75, severity: 'significant' } }, { id: 'SAL-015', firstName: 'Ryan', lastName: 'Martinez', address: '3150 Parker Rd', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Closed', contractValue: 21400, createdAt: '2026-02-05T11:30:00Z', stormSource: { id: 'SE-003', areaName: 'SE Plano / Parker Rd Corridor', date: '2026-02-03', maxHailSize: 1.75, severity: 'significant' } }, { id: 'SAL-016', firstName: 'Ashley', lastName: 'Thompson', address: '3280 Heritage Pkwy', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Closed', contractValue: 19900, createdAt: '2026-02-05T14:20:00Z', stormSource: { id: 'SE-003', areaName: 'SE Plano / Parker Rd Corridor', date: '2026-02-03', maxHailSize: 1.75, severity: 'significant' } }, { id: 'SAL-017', firstName: 'Kevin', lastName: 'Johnson', address: '3190 Parker Rd', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-02-06T10:00:00Z', stormSource: { id: 'SE-003', areaName: 'SE Plano / Parker Rd Corridor', date: '2026-02-03', maxHailSize: 1.75, severity: 'significant' } }, { id: 'SAL-018', firstName: 'Stephanie', lastName: 'Davis', address: '3220 Heritage Pkwy', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Appointed', contractValue: null, createdAt: '2026-02-07T09:30:00Z', stormSource: { id: 'SE-003', areaName: 'SE Plano / Parker Rd Corridor', date: '2026-02-03', maxHailSize: 1.75, severity: 'significant' } }, { id: 'SAL-019', firstName: 'Brian', lastName: 'Wilson', address: '3160 Parker Rd', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-02-08T11:00:00Z', stormSource: { id: 'SE-003', areaName: 'SE Plano / Parker Rd Corridor', date: '2026-02-03', maxHailSize: 1.75, severity: 'significant' } }, // ── SE-004 · Jan 20 2026 · Moderate · W Plano ──────────────────────────── { id: 'SAL-020', firstName: 'Nicole', lastName: 'Garcia', address: '7200 Legacy Dr', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2026-01-21T10:00:00Z', stormSource: { id: 'SE-004', areaName: 'W Plano / Legacy West', date: '2026-01-20', maxHailSize: 1.0, severity: 'moderate' } }, { id: 'SAL-021', firstName: 'Andrew', lastName: 'Kim', address: '7150 Legacy Dr', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Appointed', contractValue: null, createdAt: '2026-01-22T13:45:00Z', stormSource: { id: 'SE-004', areaName: 'W Plano / Legacy West', date: '2026-01-20', maxHailSize: 1.0, severity: 'moderate' } }, { id: 'SAL-022', firstName: 'Michelle', lastName: 'Lee', address: '7210 Legacy Dr', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'New', contractValue: null, createdAt: '2026-01-23T09:15:00Z', stormSource: { id: 'SE-004', areaName: 'W Plano / Legacy West', date: '2026-01-20', maxHailSize: 1.0, severity: 'moderate' } }, // ── SE-005 · Dec 10 2025 · Trace · Allen / N Plano ─────────────────────── { id: 'SAL-023', firstName: 'Steven', lastName: 'Clark', address: '405 N Greenville Ave', city: 'Allen', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Closed', contractValue: 12400, createdAt: '2025-12-11T10:30:00Z', stormSource: { id: 'SE-005', areaName: 'Allen / N Plano Border', date: '2025-12-10', maxHailSize: 0.8, severity: 'trace' } }, { id: 'SAL-024', firstName: 'Melissa', lastName: 'Robinson', address: '415 N Greenville Ave', city: 'Allen', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2025-12-12T11:00:00Z', stormSource: { id: 'SE-005', areaName: 'Allen / N Plano Border', date: '2025-12-10', maxHailSize: 0.8, severity: 'trace' } }, // ── SE-006 · Nov 5 2025 · Significant · S Plano ────────────────────────── { id: 'SAL-025', firstName: 'Timothy', lastName: 'Walker', address: '1801 K Ave', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Closed', contractValue: 31200, createdAt: '2025-11-06T09:00:00Z', stormSource: { id: 'SE-006', areaName: 'S Plano / Haggard Park Area', date: '2025-11-05', maxHailSize: 1.6, severity: 'significant' } }, { id: 'SAL-026', firstName: 'Kimberly', lastName: 'Hall', address: '1750 J Ave', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Closed', contractValue: 18700, createdAt: '2025-11-07T10:15:00Z', stormSource: { id: 'SE-006', areaName: 'S Plano / Haggard Park Area', date: '2025-11-05', maxHailSize: 1.6, severity: 'significant' } }, { id: 'SAL-027', firstName: 'Joshua', lastName: 'Allen', address: '1820 K Ave', city: 'Plano', state: 'TX', urgency: 'high', leadSource: 'Door Knock', status: 'Closed', contractValue: 22900, createdAt: '2025-11-08T11:30:00Z', stormSource: { id: 'SE-006', areaName: 'S Plano / Haggard Park Area', date: '2025-11-05', maxHailSize: 1.6, severity: 'significant' } }, { id: 'SAL-028', firstName: 'Amy', lastName: 'Young', address: '1780 J Ave', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Appointed', contractValue: null, createdAt: '2025-11-09T09:45:00Z', stormSource: { id: 'SE-006', areaName: 'S Plano / Haggard Park Area', date: '2025-11-05', maxHailSize: 1.6, severity: 'significant' } }, { id: 'SAL-029', firstName: 'Brandon', lastName: 'King', address: '1810 K Ave', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2025-11-10T10:00:00Z', stormSource: { id: 'SE-006', areaName: 'S Plano / Haggard Park Area', date: '2025-11-05', maxHailSize: 1.6, severity: 'significant' } }, { id: 'SAL-030', firstName: 'Heather', lastName: 'Wright', address: '1760 J Ave', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2025-11-11T13:20:00Z', stormSource: { id: 'SE-006', areaName: 'S Plano / Haggard Park Area', date: '2025-11-05', maxHailSize: 1.6, severity: 'significant' } }, { id: 'SAL-031', firstName: 'Tyler', lastName: 'Scott', address: '1795 K Ave', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2025-11-12T09:00:00Z', stormSource: { id: 'SE-006', areaName: 'S Plano / Haggard Park Area', date: '2025-11-05', maxHailSize: 1.6, severity: 'significant' } }, // ── SE-007 · Sep 18 2025 · Moderate · Frisco / N Plano ─────────────────── { id: 'SAL-032', firstName: 'Amber', lastName: 'Green', address: '12001 Coit Rd', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Closed', contractValue: 17600, createdAt: '2025-09-19T10:00:00Z', stormSource: { id: 'SE-007', areaName: 'Frisco / N Plano', date: '2025-09-18', maxHailSize: 1.1, severity: 'moderate' } }, { id: 'SAL-033', firstName: 'Nathan', lastName: 'Adams', address: '12050 Coit Rd', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Closed', contractValue: 14300, createdAt: '2025-09-20T11:15:00Z', stormSource: { id: 'SE-007', areaName: 'Frisco / N Plano', date: '2025-09-18', maxHailSize: 1.1, severity: 'moderate' } }, { id: 'SAL-034', firstName: 'Brittany', lastName: 'Baker', address: '12020 Coit Rd', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'Contacted', contractValue: null, createdAt: '2025-09-21T09:30:00Z', stormSource: { id: 'SE-007', areaName: 'Frisco / N Plano', date: '2025-09-18', maxHailSize: 1.1, severity: 'moderate' } }, { id: 'SAL-035', firstName: 'Dylan', lastName: 'Nelson', address: '12080 Coit Rd', city: 'Plano', state: 'TX', urgency: 'standard', leadSource: 'Door Knock', status: 'New', contractValue: null, createdAt: '2025-09-22T10:45:00Z', stormSource: { id: 'SE-007', areaName: 'Frisco / N Plano', date: '2025-09-18', maxHailSize: 1.1, severity: 'moderate' } }, ];