feat: Release CRM V2 (Clean History)
Squashed commits for V2 release including Dark Mode, Chatbot, and Landing Page enhancements.
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
|
||||
// --- 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: ["https://images.unsplash.com/photo-1600596542815-2a4d9f6facb8?w=800", "https://images.unsplash.com/photo-1564013799919-ab600027ffc6?w=800", "https://images.unsplash.com/photo-1512917774080-9991f1c4c750?w=800"],
|
||||
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: ["https://images.unsplash.com/photo-1512917774080-9991f1c4c750?w=800", "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=800", "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?w=800"],
|
||||
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: ["https://images.unsplash.com/photo-1605276374104-dee2a0ed3cd6?w=800", "https://images.unsplash.com/photo-1600566753376-12c8ab7fb75b?w=800", "https://images.unsplash.com/photo-1600585152220-90363fe7e115?w=800"],
|
||||
baseValue: 320000,
|
||||
areaBase: 2400,
|
||||
beds: 3,
|
||||
baths: 2,
|
||||
yearBase: 1980,
|
||||
desc: "Great potential, needs cosmetic updates.",
|
||||
condition: 2
|
||||
},
|
||||
{
|
||||
label: "Renovated Modern",
|
||||
canvassingStatus: "Renovated",
|
||||
photos: ["https://images.unsplash.com/photo-1600585154526-990dced4db0d?w=800", "https://images.unsplash.com/photo-1600573472592-401b489a3cdc?w=800", "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=800"],
|
||||
baseValue: 580000,
|
||||
areaBase: 3200,
|
||||
beds: 4,
|
||||
baths: 3.5,
|
||||
yearBase: 1990,
|
||||
desc: "Recently renovated with modern aesthetics.",
|
||||
condition: 5
|
||||
}
|
||||
];
|
||||
|
||||
const COMMERCIAL_VARIANTS = [
|
||||
{ name: "Legacy Retail Group", type: "Retail Mix", email: "leasing@legacyretail.com" },
|
||||
{ name: "Custer Office Park LLC", type: "Office Space", email: "mgmt@custeroffice.com" },
|
||||
{ name: "Plano Investments Inc", type: "Mixed Use", email: "contact@planoinvest.com" },
|
||||
{ name: "North Texas Prop Co", type: "Service Center", email: "info@ntxprops.com" }
|
||||
];
|
||||
|
||||
const REAL_NAMES = [
|
||||
"James Beauford", "Sarah Miller", "Robert Henderson", "Karen Smith", "Michael Chang",
|
||||
"Patricia O'Neil", "Dr. Anish Patel", "Marcus Johnson", "Elena Rodriguez", "Greg Alston",
|
||||
"Linda Grey", "Bill Thompson", "Jennifer Wu", "David Kowalski", "Omar Farooq",
|
||||
"Lisa Chen", "Tom Baker", "Rachel Green", "Steve Martin", "Angela White"
|
||||
];
|
||||
|
||||
const OCCUPATIONS = [
|
||||
"Software Engineer", "Petroleum Engineer", "Teacher", "Nurse Practitioner",
|
||||
"Small Business Owner", "Accountant", "Marketing Director", "retired",
|
||||
"Attorney", "Real Estate Agent", "Sales Manager", "Consultant"
|
||||
];
|
||||
|
||||
const EMPLOYERS_BY_OCCUPATION = {
|
||||
"Software Engineer": ["Google", "Microsoft", "Toyota Connected", "Capital One", "Intuit"],
|
||||
"Petroleum Engineer": ["ExxonMobil", "Chevron", "Pioneer Natural Resources", "Denbury"],
|
||||
"Teacher": ["Plano ISD", "Frisco ISD", "Dallas ISD", "Richardson ISD"],
|
||||
"Nurse Practitioner": ["Baylor Scott & White", "Medical City Plano", "Texas Health Resources"],
|
||||
"Small Business Owner": ["Self Employed", "Local Boutique", "Consultancy Firm"],
|
||||
"Accountant": ["Deloitte", "PwC", "EY", "KPMG", "Local CPA Firm"],
|
||||
"Marketing Director": ["PepsiCo", "Toyota", "Frito-Lay", "Yum! Brands"],
|
||||
"retired": ["N/A"],
|
||||
"Attorney": ["Jones Day", "Baker Botts", "Norton Rose Fulbright", "Local Law Firm"],
|
||||
"Real Estate Agent": ["Keller Williams", "Ebby Halliday", "Compass", "Remax"],
|
||||
"Sales Manager": ["Oracle", "Salesforce", "AT&T", "Samsung"],
|
||||
"Consultant": ["McKinsey", "BCG", "Accenture", "Freelance"]
|
||||
};
|
||||
|
||||
// --- 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}`;
|
||||
}
|
||||
|
||||
function generateProperties() {
|
||||
return RAW_LOCATIONS.map((loc, index) => {
|
||||
const isCommercial = loc.type === "Commercial" || loc.type === "Apartments";
|
||||
|
||||
const styleIndex = (index * 7) % 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];
|
||||
}
|
||||
|
||||
const builtYear = isCommercial ? 1995 + (index % 10) : style.yearBase + (index % 15);
|
||||
const sqft = isCommercial ? 12000 + (index * 500) : style.areaBase + (index * 132);
|
||||
const value = isCommercial ? 2500000 + (index * 100000) : style.baseValue + (index * 12500);
|
||||
|
||||
const isRented = index % 3 === 0;
|
||||
const tenantName = isRented ? (isCommercial ? "Multiple Commercial Tenants" : REAL_NAMES[(index + 5) % REAL_NAMES.length]) : null;
|
||||
const tenantOcc = isRented ? (isCommercial ? "Retail/Office Mix" : OCCUPATIONS[(index + 3) % OCCUPATIONS.length]) : null;
|
||||
const rentAmount = isRented ? Math.floor(value * 0.008 / 100) * 100 : null;
|
||||
|
||||
return {
|
||||
id: index + 1,
|
||||
center: [loc.lat, loc.lng],
|
||||
polygon: generatePolygon(loc.lat, loc.lng),
|
||||
canvassingStatus: isCommercial ? "Neutral" : style.canvassingStatus,
|
||||
photos: isCommercial ? ["https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=800", "https://images.unsplash.com/photo-1542665952-14513db15293?w=800"] : style.photos,
|
||||
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: ["Good", "Fair", "Excellent", "Needs Repair"][index % 4],
|
||||
renovationDescription: isCommercial ? "Commercial Maintenance" : style.desc,
|
||||
renovationCost: Math.floor(value * 0.05),
|
||||
lastMajorRenovationDate: "2020-05-15",
|
||||
lastRoofRepairReplacementDate: "2018-02-10",
|
||||
recentMajorRepairs: "Routine Maintenance",
|
||||
currentlyRented: isRented,
|
||||
currentTenantName: tenantName,
|
||||
tenantOccupation: tenantOcc,
|
||||
tenantAnnualIncome: isRented ? "$100k - $150k" : null,
|
||||
currentMonthlyRentAmount: rentAmount,
|
||||
leaseStartDate: isRented ? "2023-01-01" : null,
|
||||
leaseEndDate: isRented ? "2025-01-01" : null,
|
||||
ownerWillingToRent: index % 4 === 0,
|
||||
expectedMonthlyRentAmount: Math.floor(value * 0.008 / 100) * 100,
|
||||
rentalAvailabilityDate: null,
|
||||
rentalTermsPreference: null,
|
||||
schoolDistrict: "Plano ISD",
|
||||
neighborhoodRating: 8 + (index % 3),
|
||||
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"
|
||||
},
|
||||
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: false,
|
||||
desiredMonthlyRentalPrice: null,
|
||||
rentalConditions: null,
|
||||
willingToSellProperty: index % 5 === 0,
|
||||
desiredSellingPrice: Math.floor(value * 1.1),
|
||||
minimumAcceptableSellingPrice: Math.floor(value * 1.05)
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// --- NEW DATA (Users & Meetings) ---
|
||||
|
||||
// --- NEW DATA (Users & Meetings) ---
|
||||
|
||||
const MOCK_USERS = [
|
||||
// Customers
|
||||
{ id: 'c1', type: 'customer', username: 'alice', email: 'alice@example.com', password: 'password', name: 'Alice Customer' },
|
||||
{ id: 'c2', type: 'customer', username: 'bob', email: 'bob@example.com', password: 'password', name: 'Bob Buyer' },
|
||||
{ id: 'c3', type: 'customer', username: 'charlie', email: 'charlie@example.com', password: 'password', name: 'Charlie Client' },
|
||||
|
||||
// Field Agents (5 Agents)
|
||||
{ id: 'e1', type: 'employee', empId: 'FA001', email: 'agent1@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Frank Agent' },
|
||||
{ id: 'e2', type: 'employee', empId: 'FA002', email: 'agent2@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Fiona Field' },
|
||||
{ id: 'e3', type: 'employee', empId: 'FA003', email: 'agent3@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Fred Flyer' },
|
||||
{ id: 'e4', type: 'employee', empId: 'FA004', email: 'agent4@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Felicity Fast' },
|
||||
{ id: 'e5', type: 'employee', empId: 'FA005', email: 'agent5@plano.com', password: 'password', role: 'FIELD_AGENT', name: 'Felix Fixer' },
|
||||
|
||||
// Admins (3 Admins)
|
||||
{ id: 'a1', type: 'employee', empId: 'ADM01', email: 'admin@plano.com', password: 'password', role: 'ADMIN', name: 'Adam Admin' },
|
||||
{ id: 'a2', type: 'employee', empId: 'ADM02', email: 'admin2@plano.com', password: 'password', role: 'ADMIN', name: 'Amanda Manager' },
|
||||
{ id: 'a3', type: 'employee', empId: 'ADM03', email: 'admin3@plano.com', password: 'password', role: 'ADMIN', name: 'Arthur Director' }
|
||||
];
|
||||
|
||||
// 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`;
|
||||
|
||||
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.'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return meetings;
|
||||
}
|
||||
|
||||
const MOCK_MEETINGS = generateMockMeetings();
|
||||
|
||||
// --- 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);
|
||||
|
||||
// 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}` }]);
|
||||
};
|
||||
|
||||
return (
|
||||
<MockStoreContext.Provider value={{
|
||||
properties,
|
||||
setProperties,
|
||||
users,
|
||||
meetings,
|
||||
updatePropertyStatus,
|
||||
addMeeting
|
||||
}}>
|
||||
{children}
|
||||
</MockStoreContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useMockStore = () => useContext(MockStoreContext);
|
||||
Reference in New Issue
Block a user