Landmarks: ${
data.Landmarks__c || data.nearbyLandmarks || "N/A"
@@ -2925,104 +2961,156 @@ return result;
data.Name ||
data.propertyName ||
data.pcrm__Title_English__c ||
- "The Grand Oak Villa";
+ "Property Name";
const location =
data.Address__c ||
data.location ||
- "123 Luxury Lane, Prestige City, PC 45678";
+ "Location";
const price =
data.Sale_Price_Min__c ||
data.Rent_Price_Min__c ||
data.Price__c ||
data.price ||
- "$4,500,000";
+ "Price on Request";
const referenceId =
data.pcrm__Title_English__c || data.Name || data.propertyName || "";
- const bedrooms = data.Bedrooms__c || data.bedrooms || "5";
- const bathrooms = data.Bathrooms__c || data.bathrooms || "6";
+ const bedrooms = data.Bedrooms__c || data.bedrooms || "N/A";
+ const bathrooms = data.Bathrooms__c || data.bathrooms || "N/A";
const squareFeet =
- data.Square_Feet__c || data.squareFeet || data.area || "6,200";
- const status = (data.Status__c || data.status || "FOR SALE").toString();
+ data.Square_Feet__c || data.squareFeet || data.area || "N/A";
+ const status = (data.Status__c || data.status || "N/A").toString();
// Enhanced property details
- const propertyType = data.Property_Type__c || data.propertyType || "Villa";
- const yearBuilt = data.Build_Year__c || data.yearBuilt || "2020";
+ const propertyType = data.Property_Type__c || data.propertyType;
+ const yearBuilt = data.Build_Year__c || data.yearBuilt;
const furnishing =
- data.Furnished__c || data.furnishing || "Fully Furnished";
- const parking = data.Parking_Spaces__c || data.parking || "2";
+ data.Furnished__c || data.furnishing;
+ const parking = data.Parking_Spaces__c || data.parking;
const description = this.formatDescriptionForPDF(
data.Description_English__c ||
data.descriptionEnglish ||
- data.description ||
- "An exquisite villa offering unparalleled luxury and sophistication in one of the most prestigious locations."
+ data.description
);
- const floor = data.Floor__c || data.floor || "Ground Floor";
+ const floor = data.Floor__c || data.floor;
const maintenanceFee =
- data.Maintenance_Fee__c || data.maintenanceFee || "N/A";
- const serviceCharge = data.Service_Charge__c || data.serviceCharge || "N/A";
+ data.Maintenance_Fee__c || data.maintenanceFee;
+ const serviceCharge = data.Service_Charge__c || data.serviceCharge;
+
+ // Pricing ranges for screenshot fields
+ const rentMinVal = data.Rent_Price_Min__c || data.rentPriceMin;
+ const rentMaxVal = data.Rent_Price_Max__c || data.rentPriceMax;
+ const saleMinVal = data.Sale_Price_Min__c || data.salePriceMin;
+ const saleMaxVal = data.Sale_Price_Max__c || data.salePriceMax;
+
+ // Location details for screenshot fields
+ const cityVal = data.City__c || data.city;
+ const communityVal = data.Community__c || data.community;
+ const subCommunityVal = data.Sub_Community__c || data.subCommunity;
+ const localityVal = data.Locality__c || data.locality;
+ const towerVal = data.Tower__c || data.tower;
+ const unitNumberVal = data.Unit_Number__c || data.unitNumber;
+
+ // Offering and availability
+ const offeringTypeVal = data.Offering_Type__c || data.offeringType;
+ const availableTo = data.Available_To__c || data.availableTo || data.rentAvailableTo;
// Additional property details
- const lotSize = data.Lot_Size__c || data.lotSize || "0.5 acres";
- const heating = data.Heating__c || data.heating || "Central Air";
- const cooling = data.Cooling__c || data.cooling || "Central Air";
- const roof = data.Roof__c || data.roof || "Tile";
- const exterior = data.Exterior__c || data.exterior || "Stone & Brick";
- const foundation = data.Foundation__c || data.foundation || "Concrete";
- const utilities = data.Utilities__c || data.utilities || "All Connected";
- const zoning = data.Zoning__c || data.zoning || "Residential";
- const hoa = data.HOA__c || data.hoa || "Yes";
- const hoaFee = data.HOA_Fee__c || data.hoaFee || "$500/month";
- const taxYear = data.Tax_Year__c || data.taxYear || "2024";
- const taxAmount = data.Tax_Amount__c || data.taxAmount || "$12,000/year";
- const lastSold = data.Last_Sold__c || data.lastSold || "2020";
+ const lotSize = data.Lot_Size__c || data.lotSize;
+ const heating = data.Heating__c || data.heating;
+ const cooling = data.Cooling__c || data.cooling;
+ const roof = data.Roof__c || data.roof;
+ const exterior = data.Exterior__c || data.exterior;
+ const foundation = data.Foundation__c || data.foundation;
+ const utilities = data.Utilities__c || data.utilities;
+ const zoning = data.Zoning__c || data.zoning;
+ const hoa = data.HOA__c || data.hoa;
+ const hoaFee = data.HOA_Fee__c || data.hoaFee;
+ const taxYear = data.Tax_Year__c || data.taxYear;
+ const taxAmount = data.Tax_Amount__c || data.taxAmount;
+ const lastSold = data.Last_Sold__c || data.lastSold;
const lastSoldPrice =
- data.Last_Sold_Price__c || data.lastSoldPrice || "$3,200,000";
+ data.Last_Sold_Price__c || data.lastSoldPrice;
// Location and POI data
- const schools = data.Schools__c || data.schools || "5 min drive";
+ const schools = data.Schools__c || data.schools;
const shoppingCenters =
- data.Shopping_Centers__c || data.shoppingCenters || "10 min drive";
+ data.Shopping_Centers__c || data.shoppingCenters;
const airportDistance =
- data.Airport_Distance__c || data.airportDistance || "25 min drive";
+ data.Airport_Distance__c || data.airportDistance;
const nearbyLandmarks =
- data.Nearby_Landmarks__c || data.nearbyLandmarks || "City Center 15 min";
+ data.Nearby_Landmarks__c || data.nearbyLandmarks;
const transportation =
- data.Transportation__c || data.transportation || "Metro 5 min walk";
- const hospitals = data.Hospitals__c || data.hospitals || "10 min drive";
+ data.Transportation__c || data.transportation;
+ const hospitals = data.Hospitals__c || data.hospitals;
const beachDistance =
- data.Beach_Distance__c || data.beachDistance || "30 min drive";
+ data.Beach_Distance__c || data.beachDistance;
const metroDistance =
- data.Metro_Distance__c || data.metroDistance || "5 min walk";
+ data.Metro_Distance__c || data.metroDistance;
// Additional information
- const petFriendly = data.Pet_Friendly__c || data.petFriendly || "Yes";
+ const petFriendly = data.Pet_Friendly__c || data.petFriendly;
const smokingAllowed =
- data.Smoking_Allowed__c || data.smokingAllowed || "No";
+ data.Smoking_Allowed__c || data.smokingAllowed;
const availableFrom =
- data.Available_From__c || data.availableFrom || "Immediate";
+ data.Available_From__c || data.availableFrom;
const minimumContract =
- data.Minimum_Contract__c || data.minimumContract || "12 months";
+ data.Minimum_Contract__c || data.minimumContract;
const securityDeposit =
- data.Security_Deposit__c || data.securityDeposit || "2 months rent";
+ data.Security_Deposit__c || data.securityDeposit;
const utilitiesIncluded =
data.Utilities_Included__c ||
- data.utilitiesIncluded ||
- "Water, Electricity";
+ data.utilitiesIncluded;
const internetIncluded =
- data.Internet_Included__c || data.internetIncluded || "Yes";
- const cableIncluded = data.Cable_Included__c || data.cableIncluded || "Yes";
+ data.Internet_Included__c || data.internetIncluded;
+ const cableIncluded = data.Cable_Included__c || data.cableIncluded;
+ // Minimal Grand Oak Villa template: screenshot fields only; no gallery or extras
+ const basicPropertyName =
+ data.Name || data.propertyName || data.pcrm__Title_English__c;
+ const basicLocation = data.Address__c || data.location;
+ const basicStatus = (data.Status__c || data.status).toString();
+ const basicPropertyType = data.Property_Type__c || data.propertyType;
+ const specBedrooms = data.Bedrooms__c || data.bedrooms;
+ const specBathrooms = data.Bathrooms__c || data.bathrooms;
+ const specFloor = data.Floor__c || data.floor;
+ const specSize =
+ data.Square_Feet__c || data.squareFeet || data.area;
+ const specBuildYear =
+ data.Build_Year__c || data.yearBuilt || data.buildYear;
+ const cName = data.contactName || data.Agent_Name__c;
+ const cEmail = data.contactEmail || data.Agent_Email__c;
+ const cPhone = data.contactPhone || data.Agent_Phone__c;
+ const rentMin = data.Rent_Price_Min__c || data.rentPriceMin;
+ const rentMax = data.Rent_Price_Max__c || data.rentPriceMax;
+ const saleMin = data.Sale_Price_Min__c || data.salePriceMin;
+ const saleMax = data.Sale_Price_Max__c || data.salePriceMax;
+ const city = data.City__c || data.city;
+ const community = data.Community__c || data.community;
+ const subCommunity = data.Sub_Community__c || data.subCommunity;
+ const locality = data.Locality__c || data.locality;
+ const tower = data.Tower__c || data.tower;
+ const unitNumber = data.Unit_Number__c || data.unitNumber;
+ const parkingSpaces =
+ data.Parking_Spaces__c || data.parkingSpaces || data.parking;
+ const furnishedSimple = data.Furnished__c || data.furnishing;
+ const offeringType = data.Offering_Type__c || data.offeringType;
+ const availableFromMin =
+ data.Available_From__c || data.rentAvailableFrom || data.availableFrom;
+ const availableToMax =
+ data.Available_To__c || data.rentAvailableTo || data.availableTo;
+
+ if (false) return `${basicPropertyName} - Brochure Basic Information Property Name: ${basicPropertyName}
Property Type: ${basicPropertyType}
Status: ${basicStatus}
Specifications Bedrooms: ${specBedrooms}
Bathrooms: ${specBathrooms}
Floor: ${specFloor}
Size: ${specSize}
Build Year: ${specBuildYear}
Contact Details Name: ${cName}
Email: ${cEmail}
Phone: ${cPhone}
Pricing Information Rent Price (Min): ${rentMin}
Rent Price (Max): ${rentMax}
Sale Price (Min): ${saleMin}
Sale Price (Max): ${saleMax}
Location Details City: ${city}
Community: ${community}
Sub Community: ${subCommunity}
Locality: ${locality}
Tower: ${tower}
Unit Number: ${unitNumber}
Amenities & Features Parking Spaces: ${parkingSpaces}
Furnished: ${furnishedSimple}
Offering Type: ${offeringType}
Availability Available From: ${availableFromMin}
Available To: ${availableToMax}
`;
// Agent and owner information
- const agentName = data.Agent_Name__c || data.agentName || "Olivia Sterling";
+ const agentName = data.Agent_Name__c || data.agentName;
const agentPhone =
- data.Agent_Phone__c || data.agentPhone || "(555) 987-6543";
+ data.Agent_Phone__c || data.agentPhone;
const agentEmail =
- data.Agent_Email__c || data.agentEmail || "olivia@elysianestates.com";
- const ownerName = data.Owner_Name__c || data.ownerName || "John & Jane Doe";
+ data.Agent_Email__c || data.agentEmail;
+ const ownerName = data.Owner_Name__c || data.ownerName;
const ownerPhone =
- data.Owner_Phone__c || data.ownerPhone || "(555) 111-2222";
+ data.Owner_Phone__c || data.ownerPhone;
const ownerEmail =
- data.Owner_Email__c || data.ownerEmail || "owner@email.com";
+ data.Owner_Email__c || data.ownerEmail;
// Get smart images
const exteriorImage = this.getExteriorImageUrl();
const interiorImage1 = this.getSmartImageForSection(
@@ -3091,20 +3179,123 @@ return result;
`;
}
createSampleTemplate() {
- const data = this.propertyData || {};
- const propertyName = data.propertyName || "The Serenity House";
- const location =
- data.location || "123 Luxury Lane, Prestige City, PC 45678";
- const price = data.price || "$4,500,000";
- const referenceId = data.referenceNumber || "ES-8821";
- const agentName = data.agentName || "Olivia Sterling";
- const agentPhone = data.agentPhone || "(555) 987-6543";
- const agentEmail = data.agentEmail || "olivia@elysianestates.com";
- const ownerName = data.ownerName || "John & Jane Doe";
- const ownerPhone = data.ownerPhone || "(555) 111-2222";
- const ownerEmail = data.ownerEmail || "owner.serenity@email.com";
+ const d = this.propertyData || {};
- // Return the complete Grand Oak Villa template with all dynamic data
+ const hasValue = (v) => v !== undefined && v !== null && v !== "" && v !== "N/A";
+
+ // Basic info (Step 2 fields)
+ const propertyName = hasValue(d.propertyName) ? d.propertyName : "";
+ const propertyType = hasValue(d.propertyType) ? d.propertyType : "";
+ const status = hasValue(d.status) ? d.status : "";
+ const referenceId = hasValue(d.referenceNumber) ? d.referenceNumber : "";
+ const location = hasValue(d.location) ? d.location : "";
+
+ // Contact
+ const agentName = hasValue(d.contactName) ? d.contactName : "";
+ const agentPhone = hasValue(d.contactPhone) ? d.contactPhone : "";
+ const agentEmail = hasValue(d.contactEmail) ? d.contactEmail : "";
+ const ownerName = hasValue(d.ownerName) ? d.ownerName : "";
+ const ownerPhone = hasValue(d.ownerPhone) ? d.ownerPhone : "";
+ const ownerEmail = hasValue(d.ownerEmail) ? d.ownerEmail : "";
+
+ // Specifications
+ const bedrooms = hasValue(d.bedrooms) ? d.bedrooms : "";
+ const bathrooms = hasValue(d.bathrooms) ? d.bathrooms : "";
+ const floor = hasValue(d.floor) ? d.floor : "";
+ const squareFeet = hasValue(d.area)
+ ? d.area
+ : (hasValue(d.size) ? `${d.size}${d.sizeUnit ? " " + d.sizeUnit : ""}` : "");
+ const yearBuilt = hasValue(d.buildYear) ? d.buildYear : (hasValue(d.yearBuilt) ? d.yearBuilt : "");
+
+ // Pricing
+ const rentMinVal = hasValue(d.rentPriceMin) ? d.rentPriceMin : "";
+ const rentMaxVal = hasValue(d.rentPriceMax) ? d.rentPriceMax : "";
+ const saleMinVal = hasValue(d.salePriceMin) ? d.salePriceMin : "";
+ const saleMaxVal = hasValue(d.salePriceMax) ? d.salePriceMax : "";
+
+ // Amenities & availability
+ const parking = hasValue(d.parking) ? d.parking : (hasValue(d.parkingSpaces) ? d.parkingSpaces : "");
+ const furnishing = hasValue(d.furnishing) ? d.furnishing : (hasValue(d.furnished) ? d.furnished : "");
+ const offeringTypeVal = hasValue(d.offeringType) ? d.offeringType : "";
+ const availableFrom = hasValue(d.rentAvailableFrom) ? d.rentAvailableFrom : "";
+ const availableTo = hasValue(d.rentAvailableTo) ? d.rentAvailableTo : "";
+
+ // Location details
+ const cityVal = hasValue(d.city) ? d.city : "";
+ const communityVal = hasValue(d.community) ? d.community : "";
+ const subCommunityVal = hasValue(d.subCommunity) ? d.subCommunity : "";
+ const localityVal = hasValue(d.locality) ? d.locality : "";
+ const towerVal = hasValue(d.tower) ? d.tower : "";
+ const unitNumberVal = hasValue(d.unitNumber) ? d.unitNumber : "";
+
+ // Descriptions
+ const description1 = hasValue(d.description) ? this.formatDescriptionForPDF(d.description) : "";
+ const descriptionEng = hasValue(d.descriptionEnglish) ? this.formatDescriptionForPDF(d.descriptionEnglish) : "";
+
+ // Images
+ const exteriorImage = this.getExteriorImageUrl();
+ const interiorImage1 = this.getSmartImageForSection("interior", exteriorImage);
+ const interiorImage2 = this.getSmartImageForSection("interior", interiorImage1);
+ const kitchenImage = this.getSmartImageForSection("kitchen", exteriorImage);
+ const bedroomImage = this.getSmartImageForSection("bedroom", exteriorImage);
+ const mapsImage = this.getMapsImageUrl();
+
+ // Helpers
+ const row = (label, value) => (hasValue(value) ? `
${label}: ${value}
` : "");
+ const feature = (label, value) => (hasValue(value) ? `
` : "");
+
+ const coverFeaturesHTML = [
+ feature("Bedrooms", bedrooms),
+ feature("Bathrooms", bathrooms),
+ feature("Sq. Ft.", squareFeet),
+ feature("Price", saleMinVal || rentMinVal)
+ ].join("");
+
+ const specsHTML = [
+ row("Reference ID", referenceId),
+ row("Status", status),
+ row("Type", propertyType),
+ row("Year Built", yearBuilt),
+ row("Floor", floor),
+ row("Parking", parking),
+ row("Furnishing", furnishing)
+ ].join("");
+
+ const amenitiesFeaturesHTML = [
+ row("Parking Spaces", parking),
+ row("Furnished", furnishing),
+ row("Offering Type", offeringTypeVal)
+ ].join("");
+
+ const pricingHTML = [
+ row("Rent Price (Min)", rentMinVal),
+ row("Rent Price (Max)", rentMaxVal),
+ row("Sale Price (Min)", saleMinVal),
+ row("Sale Price (Max)", saleMaxVal)
+ ].join("");
+
+ const availabilityHTML = [
+ row("Available From", availableFrom),
+ row("Available To", availableTo)
+ ].join("");
+
+ const locationHTML = [
+ row("City", cityVal),
+ row("Community", communityVal),
+ row("Sub Community", subCommunityVal),
+ row("Locality", localityVal),
+ row("Tower", towerVal),
+ row("Unit Number", unitNumberVal)
+ ].join("");
+
+ const agentLine = (hasValue(agentName) || hasValue(agentPhone) || hasValue(agentEmail))
+ ? `
Agent: ${[agentName, agentPhone, agentEmail].filter(hasValue).join(" | ")}
`
+ : "";
+ const ownerLine = (hasValue(ownerName) || hasValue(ownerPhone) || hasValue(ownerEmail))
+ ? `
Owner: ${[ownerName, ownerPhone, ownerEmail].filter(hasValue).join(" | ")}
`
+ : "";
+
+ // Return the complete dynamic template using only populated values
return `
@@ -3373,32 +3564,13 @@ return result;
${propertyName}
- ${location}
+ ${hasValue(location) ? ` ${location}
` : ""}
-
+
@@ -3411,141 +3583,59 @@ return result;
Description
-
${
- data.description ||
- "Nestled in the heart of Prestige City, The Grand Oak Villa is a masterpiece of modern architecture and timeless elegance. This expansive 6,200 sq. ft. residence offers unparalleled luxury and privacy."
- }
-
${
- data.descriptionEnglish ||
- "With soaring ceilings, bespoke finishes, and panoramic views from every room, this home is designed for those who appreciate the finer things in life. The open-plan living space is perfect for entertaining, featuring a gourmet chef's kitchen, a formal dining area, and a grand living room with a statement fireplace."
- }
+ ${hasValue(description1) ? `
${description1}
` : ""}
+ ${hasValue(descriptionEng) ? `
${descriptionEng}
` : ""}
Specifications
-
-
Reference ID: ${referenceId}
-
Status: ${
- data.status || "For Sale"
- }
-
Type: ${
- data.propertyType || "Villa"
- }
-
Year Built: ${
- data.yearBuilt || "2023"
- }
-
Floor: ${
- data.floor || "2 Levels"
- }
-
Parking: ${
- data.parking || "3-Car Garage"
- }
-
Furnishing: ${
- data.furnishing || "Partially Furnished"
- }
-
Maintenance Fee: ${
- data.maintenanceFee || "N/A"
- }
-
Service Charge: ${
- data.serviceCharge || "Included"
- }
-
+
${specsHTML}
Amenities & Features
-
- Infinity Pool
- Private Home Theater
- Gourmet Chef's Kitchen
- Wine Cellar
- Smart Home Automation
- Spa & Sauna Room
- Landscaped Gardens
- Outdoor Fire Pit
-
+
${amenitiesFeaturesHTML}
-
`;
}
- createLuxuryMansionTemplate() {
- const data = this.propertyData || {};
+ createGrandOakVillaTemplate() {
+ const d = this.propertyData || {};
+ const has = (v) => v !== undefined && v !== null && v !== "" && v !== "N/A";
+ // Basic
+ const propertyName = d.propertyName || d.Name || "";
+ const location = d.location || d.Address__c || "";
+ const status = (d.status || d.Status__c || "").toString();
+ const propertyType = d.propertyType || d.Property_Type__c || "";
+
+ // Contact
+ const agentName = d.contactName || d.Agent_Name__c || "";
+ const agentPhone = d.contactPhone || d.Agent_Phone__c || "";
+ const agentEmail = d.contactEmail || d.Agent_Email__c || "";
+ const ownerName = d.ownerName || d.Owner_Name__c || "";
+ const ownerPhone = d.ownerPhone || d.Owner_Phone__c || "";
+ const ownerEmail = d.ownerEmail || d.Owner_Email__c || "";
+
+ // Specs
+ const bedrooms = d.bedrooms || d.Bedrooms__c || "";
+ const bathrooms = d.bathrooms || d.Bathrooms__c || "";
+ const floor = d.floor || d.Floor__c || "";
+ const yearBuilt = d.buildYear || d.yearBuilt || d.Build_Year__c || "";
+ const sizeText = (d.area && d.area) || (d.size ? `${d.size}${d.sizeUnit ? ` ${d.sizeUnit}` : ""}` : (d.Square_Feet__c ? `${d.Square_Feet__c} sq ft` : ""));
+
+ // Pricing
+ const rentMinVal = d.rentPriceMin || d.Rent_Price_Min__c || "";
+ const rentMaxVal = d.rentPriceMax || d.Rent_Price_Max__c || "";
+ const saleMinVal = d.salePriceMin || d.Sale_Price_Min__c || "";
+ const saleMaxVal = d.salePriceMax || d.Sale_Price_Max__c || "";
+
+ // Amenities & availability
+ const parking = d.parking || d.Parking_Spaces__c || d.parkingSpaces || "";
+ const furnishing = d.furnishing || d.Furnished__c || d.furnished || "";
+ const offeringTypeVal = d.offeringType || d.Offering_Type__c || "";
+ const availableFrom = d.rentAvailableFrom || d.Available_From__c || "";
+ const availableTo = d.rentAvailableTo || d.Available_To__c || "";
+
+ // Location details
+ const cityVal = d.city || d.City__c || d.cityPropertyfinder || d.cityBayut || "";
+ const communityVal = d.community || d.Community__c || d.communityBayut || "";
+ const subCommunityVal = d.subCommunity || d.Sub_Community__c || d.subCommunityBayut || "";
+ const localityVal = d.locality || d.Locality__c || d.localityBayut || "";
+ const towerVal = d.tower || d.Tower__c || d.towerBayut || "";
+ const unitNumberVal = d.unitNumber || d.Unit_Number__c || "";
+
+ // Description
+ const description = this.formatDescriptionForPDF(d.descriptionEnglish || d.description || "");
+
+ // Images and gallery
+ const exteriorImage = this.getExteriorImageUrl();
+ const propertyGalleryHTML = this.generatePropertyGalleryHTML(d);
+
+ // Helpers
+ const row = (label, value) => (has(value) ? `
${label}: ${value}
` : "");
+ const feature = (label, value) => (has(value) ? `
` : "");
+
+ const coverFeaturesHTML = [
+ feature("Bedrooms", bedrooms),
+ feature("Bathrooms", bathrooms),
+ feature("Sq. Ft.", sizeText),
+ feature("Price", saleMinVal || rentMinVal)
+ ].join("");
+
+ const locationHTML = [
+ row("City", cityVal),
+ row("Community", communityVal),
+ row("Sub Community", subCommunityVal),
+ row("Locality", localityVal),
+ row("Tower", towerVal),
+ row("Unit Number", unitNumberVal)
+ ].join("");
+
+ const specsRightHTML = [
+ row("Bedrooms", bedrooms),
+ row("Bathrooms", bathrooms),
+ row("Floor", floor),
+ row("Size", sizeText),
+ row("Build Year", yearBuilt)
+ ].join("");
+
+ const pricingHTML = [
+ row("Rent Price (Min)", rentMinVal),
+ row("Rent Price (Max)", rentMaxVal),
+ row("Sale Price (Min)", saleMinVal),
+ row("Sale Price (Max)", saleMaxVal)
+ ].join("");
+
+ const amenitiesFeaturesHTML = [
+ row("Parking Spaces", parking),
+ row("Furnished", furnishing),
+ row("Offering Type", offeringTypeVal)
+ ].join("");
+
+ const availabilityHTML = [
+ row("Available From", availableFrom),
+ row("Available To", availableTo)
+ ].join("");
+
+ const agentLine = (has(agentName) || has(agentPhone) || has(agentEmail))
+ ? `
Agent: ${[agentName, agentPhone, agentEmail].filter(has).join(" | ")}
`
+ : "";
+ const ownerLine = (has(ownerName) || has(ownerPhone) || has(ownerEmail))
+ ? `
Owner: ${[ownerName, ownerPhone, ownerEmail].filter(has).join(" | ")}
`
+ : "";
+
+ return `
+
+
${propertyName} ${has(location) ? ` ${location}
` : ""}
+
Description ${has(description) ? `` : ""}
+
Basic Information ${[row("Property Name", propertyName), row("Property Type", propertyType), row("Status", status)].join("")}
Contact Details ${[row("Name", agentName), row("Email", agentEmail), row("Phone", agentPhone)].join("")}
Location Details ${locationHTML}
Specifications ${specsRightHTML}
Pricing Information ${pricingHTML}
Amenities & Features ${amenitiesFeaturesHTML}
Availability ${availabilityHTML}
+
+
`;
+ }
+ createLuxuryMansionTemplate() {
+ // Keep for backward-compatibility; delegate to Vertice generator
+ return this.createVerticeTemplate();
+ }
+
+ // Vertice-style dynamic brochure (cover, description, amenities/specs, highlights, gallery)
+ createVerticeTemplate() {
+ const data = this.propertyData || {};
+ // Use same safe fallback style as templates 1/2
const propertyName = data.Name || data.propertyName || "Property Name";
const propertyType = data.Property_Type__c || data.propertyType || "N/A";
const location = data.Address__c || data.location || "Location";
@@ -3618,18 +3802,103 @@ return result;
data.price ||
"Price on Request";
const referenceId =
- data.pcrm__Title_English__c || data.Name || data.propertyName || "N/A";
+ data.Reference_Number__c || data.pcrm__Title_English__c || data.Name || data.propertyName || "N/A";
const description = this.formatDescriptionForPDF(
- data.Description_English__c ||
- data.descriptionEnglish ||
- data.description ||
- "Property description not available."
+ data.Description_English__c || data.descriptionEnglish || data.description || "Property description not available."
);
- const propertyGallery = this.generatePropertyGalleryHTML();
+ const status = data.Status__c || data.status || "N/A";
+ const yearBuilt = data.Build_Year__c || data.yearBuilt || "N/A";
+ const technology = data.Technology__c || data.technology || "N/A";
+ const design = data.Design__c || data.design || "N/A";
+ const pets = data.Pet_Friendly__c || data.petFriendly || "N/A";
+ const smoking = data.Smoking_Allowed__c || data.smokingAllowed || "N/A";
+ const availability = data.Available_From__c || data.availableFrom || "N/A";
+ const parking = data.Parking_Spaces__c || data.parking || "N/A";
+ const securityDeposit = data.Security_Deposit__c || data.securityDeposit || "N/A";
+ const utilities = data.Utilities_Included__c || data.utilitiesIncluded || "N/A";
+ const landmarks = data.Landmarks__c || data.nearbyLandmarks || data.Nearby_Landmarks__c || "N/A";
+ const transportation = data.Transportation__c || data.transportation || "N/A";
+ const schools = data.Schools__c || data.schools || "N/A";
+ const shopping = data.Shopping_Centers__c || data.shoppingCenters || "N/A";
+ const airport = data.Airport_Distance__c || data.airportDistance || "N/A";
- return `
${propertyName} - Luxury Brochure
${propertyName} ${location}
${price}
Ref ID: ${referenceId}
`;
+ const amenitiesHTML = this.buildAmenitiesListForVertice(data);
+ const propertyGallery = this.generatePropertyGalleryHTML();
+ const coverImg = this.getExteriorImageUrl();
+ const featureImg = this.getSmartImageForSection("interior", coverImg);
+ const visionHeading = data.Title_English__c || data.titleEnglish || "";
+ const pageSubtitle = propertyType && propertyType !== "N/A" ? propertyType : "";
+
+ return `
${propertyName} - Urban Brochure
+
+
+
+
+
${propertyType}
+
${propertyName}
+
${location}
+
+
+
+
+
+
+
+
+
+ ${visionHeading ? `
${visionHeading} ` : ""}
+ ${description}
+
+
+
+
+
+
+
+
+
+
+
+
+
Key Specifications
+
Status ${status}
+
Type ${propertyType}
+
Year Built ${yearBuilt}
+
Technology ${technology}
+
Design ${design}
+
Pets ${pets}
+
Smoking ${smoking}
+
Availability ${availability}
+
Parking ${parking}
+
Security Deposit ${securityDeposit}
+
Utilities ${utilities}
+
+
+
+
+
+
+
+
+
+
+
+ Landmarks: ${landmarks}
+ Transportation: ${transportation}
+ Schools: ${schools}
+ Shopping: ${shopping}
+ Airport: ${airport}
+
+
+
+
${propertyGallery}
+
+
+
+
+`;
}
/*
// Dynamic additional info
@@ -3654,11 +3923,11 @@ return result;
createSerenityHouseTemplate() {
const data = this.propertyData || {};
- // Extract all available property data with fallbacks
+ // Extract all available property data with safe fallbacks (match templates 1/2)
const propertyName = data.Name || data.propertyName || "Property Name";
const location = data.Address__c || data.location || "Location";
const referenceId =
- data.pcrm__Title_English__c || data.Name || data.propertyName || "";
+ data.pcrm__Title_English__c || data.Name || data.propertyName || "N/A";
const agentName =
data.contactName || data.Agent_Name__c || data.agentName || "N/A";
const agentPhone =
@@ -3703,14 +3972,8 @@ return result;
// Get smart images
const exteriorImage = this.getExteriorImageUrl();
- const interiorImage = this.getSmartImageForSection(
- "interior",
- "https://images.unsplash.com/photo-1616486338812-3dadae4b4ace?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200"
- );
- const bedroomImage = this.getSmartImageForSection(
- "bedroom",
- "https://images.unsplash.com/photo-1586023492125-27b2c045efd7?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200"
- );
+ const interiorImage = this.getSmartImageForSection("interior", exteriorImage);
+ const bedroomImage = this.getSmartImageForSection("bedroom", exteriorImage);
// Generate property gallery
const propertyGallery = this.generatePropertyGalleryHTML();
@@ -3785,7 +4048,7 @@ return result;
.p1-image-side {
flex: 1.2;
height: 100%;
- background-image: url('https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200');
+ background-image: url('${exteriorImage}');
background-size: cover;
background-position: center;
min-height: 297mm;
@@ -4507,38 +4770,197 @@ return result;
return true;
}
- // ENHANCED BULLET FUNCTION
+ // Modern implementation without deprecated execCommand
handleBulletList() {
- const editorContent = this.template.querySelector(
- ".enhanced-editor-content"
- );
+ const editorContent = this.template.querySelector(".enhanced-editor-content");
if (!editorContent) {
this.showError("Editor not found");
return;
}
-
- editorContent.focus();
- // Use native command so selection toggles bullet list and supports nesting
- document.execCommand("insertUnorderedList", false, null);
- editorContent.dispatchEvent(new Event("input", { bubbles: true }));
+ this.insertList('ul');
}
- // ENHANCED NUMBERED LIST FUNCTION
+
handleNumberedList() {
- const editorContent = this.template.querySelector(
- ".enhanced-editor-content"
- );
+ const editorContent = this.template.querySelector(".enhanced-editor-content");
if (!editorContent) {
this.showError("Editor not found");
return;
}
+ this.insertList('ol');
+ }
+ // New unified list insertion method
+ insertList(listType) {
+ const editorContent = this.template.querySelector(".enhanced-editor-content");
+ const selection = window.getSelection();
editorContent.focus();
- // Use native command so selection toggles ordered list and supports nesting
- document.execCommand("insertOrderedList", false, null);
- editorContent.dispatchEvent(new Event("input", { bubbles: true }));
+
+ let range;
+ let selectedText = '';
+ if (selection.rangeCount > 0) {
+ range = selection.getRangeAt(0);
+ selectedText = range.toString().trim();
+ } else {
+ range = document.createRange();
+ range.selectNodeContents(editorContent);
+ range.collapse(false);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ const currentList = this.findParentList(range.commonAncestorContainer);
+ if (currentList && currentList.tagName.toLowerCase() === listType.toLowerCase()) {
+ this.convertListToParagraph(currentList);
+ return;
+ }
+ if (currentList && currentList.tagName.toLowerCase() !== listType.toLowerCase()) {
+ this.convertListType(currentList, listType);
+ return;
+ }
+ this.createNewList(listType, selectedText, range);
}
- // Alias for numbered list
+ // Helper method to find parent list element
+ findParentList(node) {
+ let current = node;
+ while (current && current !== document.body) {
+ if (current.nodeType === Node.ELEMENT_NODE) {
+ if (current.tagName === 'UL' || current.tagName === 'OL') {
+ return current;
+ }
+ if (current.tagName === 'LI') {
+ return current.parentElement;
+ }
+ }
+ current = current.parentElement;
+ }
+ return null;
+ }
+
+ // Convert list back to paragraphs
+ convertListToParagraph(list) {
+ const listItems = Array.from(list.querySelectorAll('li'));
+ const fragment = document.createDocumentFragment();
+ listItems.forEach(li => {
+ const p = document.createElement('p');
+ p.innerHTML = li.innerHTML || 'List item';
+ p.style.margin = '8px 0';
+ fragment.appendChild(p);
+ });
+ list.parentNode.replaceChild(fragment, list);
+ this.showSuccess("List converted to paragraphs");
+ }
+
+ // Convert one list type to another
+ convertListType(currentList, newListType) {
+ const newList = document.createElement(newListType);
+ const listItems = Array.from(currentList.querySelectorAll('li'));
+ listItems.forEach(li => {
+ const newLi = li.cloneNode(true);
+ newList.appendChild(newLi);
+ });
+ this.styleList(newList);
+ currentList.parentNode.replaceChild(newList, currentList);
+ const listTypeName = newListType === 'ul' ? 'bullet' : 'numbered';
+ this.showSuccess(`Converted to ${listTypeName} list`);
+ }
+
+ // Create a new list
+ createNewList(listType, selectedText, range) {
+ const list = document.createElement(listType);
+ if (selectedText) {
+ const lines = selectedText.split('\n').filter(line => line.trim());
+ if (lines.length > 1) {
+ lines.forEach(line => {
+ const li = document.createElement('li');
+ li.textContent = line.trim();
+ li.contentEditable = true;
+ list.appendChild(li);
+ });
+ } else {
+ const li = document.createElement('li');
+ li.textContent = selectedText || 'List item';
+ li.contentEditable = true;
+ list.appendChild(li);
+ }
+ } else {
+ const li = document.createElement('li');
+ li.textContent = 'List item';
+ li.contentEditable = true;
+ list.appendChild(li);
+ }
+ this.styleList(list);
+ try {
+ range.deleteContents();
+ range.insertNode(list);
+ const firstLi = list.querySelector('li');
+ if (firstLi) {
+ const newRange = document.createRange();
+ newRange.selectNodeContents(firstLi);
+ newRange.collapse(false);
+ const selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(newRange);
+ firstLi.focus();
+ }
+ const listTypeName = listType === 'ul' ? 'bullet' : 'numbered';
+ this.showSuccess(`${listTypeName} list created`);
+ } catch (error) {
+ console.error('Error inserting list:', error);
+ this.showError('Failed to create list');
+ }
+ }
+
+ // Apply consistent styling to lists
+ styleList(list) {
+ if (list.tagName === 'UL') {
+ list.style.listStyleType = 'disc';
+ } else if (list.tagName === 'OL') {
+ list.style.listStyleType = 'decimal';
+ }
+ list.style.paddingLeft = '22px';
+ list.style.margin = '0 0 8px 0';
+ list.style.lineHeight = '1.6';
+ const listItems = list.querySelectorAll('li');
+ listItems.forEach(li => {
+ li.style.margin = '4px 0';
+ li.style.paddingLeft = '4px';
+ if (!li.hasAttribute('contenteditable')) {
+ li.contentEditable = true;
+ }
+ li.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ this.handleListItemEnter(e.target, list);
+ }
+ });
+ });
+ }
+
+ // Handle Enter key in list items to create new items
+ handleListItemEnter(currentLi, list) {
+ const newLi = document.createElement('li');
+ newLi.textContent = '';
+ newLi.contentEditable = true;
+ newLi.style.margin = '4px 0';
+ newLi.style.paddingLeft = '4px';
+ newLi.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ this.handleListItemEnter(e.target, list);
+ }
+ });
+ currentLi.parentNode.insertBefore(newLi, currentLi.nextSibling);
+ newLi.focus();
+ const range = document.createRange();
+ range.setStart(newLi, 0);
+ range.collapse(true);
+ const selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ // Keep the alias for backward compatibility
handleNumberList() {
this.handleNumberedList();
}
@@ -6402,6 +6824,11 @@ newTop -= scrollDeltaY;
// Restore scrolling
document.body.style.overflow = "";
+
+ // Ensure image containers return to in-flow after drag
+ if (element.classList && element.classList.contains("draggable-image-container")) {
+ element.style.position = "relative";
+ }
}
};
@@ -9583,28 +10010,14 @@ newTop -= scrollDeltaY;
this.highlightSelectedElement(container);
return;
}
- // Wrap plain image and add handles; preserve on-screen size and position
- const editor = this.template.querySelector(".enhanced-editor-content");
- const rect = element.getBoundingClientRect();
- const editorRect = editor
- ? editor.getBoundingClientRect()
- : { left: 0, top: 0 };
+ // Wrap plain image and add handles; keep image position intact (no jump)
const currentWidth = element.offsetWidth;
const currentHeight = element.offsetHeight;
const container = document.createElement("div");
container.className = "draggable-image-container";
- container.style.position = "absolute";
- container.style.left =
- rect.left - editorRect.left + (editor ? editor.scrollLeft : 0) + "px";
- container.style.top =
- rect.top - editorRect.top + (editor ? editor.scrollTop : 0) + "px";
- container.style.zIndex =
- window.getComputedStyle(element).zIndex || "auto";
+ container.style.position = "relative"; // in-flow until drag begins
container.style.display = "inline-block";
-
- // Move the image into container and preserve size
- // Set container and image sizing
container.style.width = currentWidth + "px";
container.style.height = currentHeight + "px";
element.style.width = "100%";
@@ -9612,11 +10025,8 @@ newTop -= scrollDeltaY;
element.style.display = "block";
element.style.objectFit = "cover";
- if (editor) {
- editor.appendChild(container);
- } else {
+ // Insert container right before the image so layout doesn't shift
element.parentNode.insertBefore(container, element);
- }
container.appendChild(element);
container.classList.add("no-frame");
this.addResizeHandles(container);
diff --git a/previews/preview-modern-home.html b/previews/preview-modern-home.html
index 9b19ada..0d0e1e5 100644
--- a/previews/preview-modern-home.html
+++ b/previews/preview-modern-home.html
@@ -147,20 +147,21 @@
.amenities-grid {
display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 8px;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
margin: 20px 0;
}
.amenity-item {
background-color: var(--color-background);
- padding: 8px 12px;
+ padding: 10px 12px;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid #e8e8e8;
- border-radius: 4px;
+ border-radius: 6px;
font-size: 11px;
+ box-shadow: 0 2px 6px rgba(0,0,0,0.05);
}
.amenity-item .icon {
@@ -176,6 +177,52 @@
flex-shrink: 0;
}
+ /* Static preview gallery (separate & static) */
+ .gallery-section {
+ margin: 22px 0 10px 0;
+ }
+ .gallery-title {
+ color: var(--color-primary);
+ font-size: 16px;
+ font-weight: 700;
+ margin: 0 0 10px 0;
+ border-bottom: 2px solid var(--color-primary);
+ padding-bottom: 6px;
+ }
+ .gallery-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ grid-template-rows: 90px 90px;
+ gap: 8px;
+ }
+ .gallery-item {
+ background-size: cover;
+ background-position: center;
+ border-radius: 6px;
+ overflow: hidden;
+ position: relative;
+ }
+ .gallery-item::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(to top, rgba(0,0,0,0.35), rgba(0,0,0,0));
+ }
+ .gallery-item span {
+ position: absolute;
+ left: 8px;
+ bottom: 6px;
+ color: #fff;
+ font-size: 10px;
+ font-weight: 700;
+ z-index: 1;
+ text-shadow: 0 1px 2px rgba(0,0,0,0.4);
+ }
+ .g-item-1 { grid-column: 1 / 3; grid-row: 1 / 2; }
+ .g-item-2 { grid-column: 3 / 4; grid-row: 1 / 3; }
+ .g-item-3 { grid-column: 1 / 2; grid-row: 2 / 3; }
+ .g-item-4 { grid-column: 2 / 3; grid-row: 2 / 3; }
+
.agent-footer {
border-top: 3px solid var(--color-accent);
padding-top: 15px;
@@ -290,6 +337,17 @@
Smart Home
Energy Efficient