feat: complete dashboard widgets, fix tooltips, integrate chatbot, and update docs
This commit is contained in:
+52
-12
@@ -165,11 +165,27 @@ function generateEmail(name, company = null) {
|
||||
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() {
|
||||
return RAW_LOCATIONS.map((loc, index) => {
|
||||
const isCommercial = loc.type === "Commercial" || loc.type === "Apartments";
|
||||
|
||||
const styleIndex = (index * 7) % PROPERTY_STYLES.length;
|
||||
// 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;
|
||||
@@ -191,20 +207,44 @@ function generateProperties() {
|
||||
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);
|
||||
// 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 isRented = index % 3 === 0;
|
||||
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));
|
||||
|
||||
const isRented = Math.random() > 0.7; // 30% rented
|
||||
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;
|
||||
|
||||
// Weighted Roof Condition
|
||||
// 40% Good, 30% Fair, 20% Excellent, 10% Needs Repair
|
||||
const roofCondition = getWeightedRandom(
|
||||
["Good", "Fair", "Excellent", "Needs Repair"],
|
||||
[0.4, 0.3, 0.2, 0.1]
|
||||
);
|
||||
|
||||
// Canvassing Status Randomization
|
||||
// 60% Neutral, 20% Hot Lead, 10% Customer, 10% Renovated
|
||||
const status = isCommercial ? "Neutral" : getWeightedRandom(
|
||||
["Neutral", "Hot Lead", "Customer", "Renovated"],
|
||||
[0.6, 0.2, 0.1, 0.1]
|
||||
);
|
||||
|
||||
// Neighborhood Rating (Weighted towards high 7-9)
|
||||
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),
|
||||
canvassingStatus: isCommercial ? "Neutral" : style.canvassingStatus,
|
||||
canvassingStatus: status,
|
||||
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}`,
|
||||
@@ -217,9 +257,9 @@ function generateProperties() {
|
||||
numberOfParkingSpaces: isCommercial ? 25 : 2,
|
||||
currentEstimatedMarketValue: value,
|
||||
propertyTaxAssessmentValue: Math.floor(value * 0.85),
|
||||
roofCondition: ["Good", "Fair", "Excellent", "Needs Repair"][index % 4],
|
||||
roofCondition: roofCondition,
|
||||
renovationDescription: isCommercial ? "Commercial Maintenance" : style.desc,
|
||||
renovationCost: Math.floor(value * 0.05),
|
||||
renovationCost: Math.floor(value * (Math.random() * 0.05 + 0.01)), // 1-6% of value
|
||||
lastMajorRenovationDate: "2020-05-15",
|
||||
lastRoofRepairReplacementDate: "2018-02-10",
|
||||
recentMajorRepairs: "Routine Maintenance",
|
||||
@@ -230,12 +270,12 @@ function generateProperties() {
|
||||
currentMonthlyRentAmount: rentAmount,
|
||||
leaseStartDate: isRented ? "2023-01-01" : null,
|
||||
leaseEndDate: isRented ? "2025-01-01" : null,
|
||||
ownerWillingToRent: index % 4 === 0,
|
||||
ownerWillingToRent: !isRented && Math.random() > 0.6,
|
||||
expectedMonthlyRentAmount: Math.floor(value * 0.008 / 100) * 100,
|
||||
rentalAvailabilityDate: null,
|
||||
rentalTermsPreference: null,
|
||||
schoolDistrict: "Plano ISD",
|
||||
neighborhoodRating: 8 + (index % 3),
|
||||
neighborhoodRating: neighRating,
|
||||
crimeRateIndex: "Low",
|
||||
publicTransportationAccess: "Bus Line 204",
|
||||
propertyConditionRating: isCommercial ? 4 : style.condition,
|
||||
@@ -260,10 +300,10 @@ function generateProperties() {
|
||||
creditScoreRange: "720+",
|
||||
ownershipType: isCommercial ? "Corporate" : "Individual",
|
||||
ownershipPercentage: "100%",
|
||||
willingToRentProperty: false,
|
||||
willingToRentProperty: Math.random() > 0.8,
|
||||
desiredMonthlyRentalPrice: null,
|
||||
rentalConditions: null,
|
||||
willingToSellProperty: index % 5 === 0,
|
||||
willingToSellProperty: Math.random() > 0.85,
|
||||
desiredSellingPrice: Math.floor(value * 1.1),
|
||||
minimumAcceptableSellingPrice: Math.floor(value * 1.05)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user