From aecc59c4f941829f7452bfc26bfb9eb6d7b32f5a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 9 Sep 2025 12:09:16 +0530 Subject: [PATCH] V1.0.0-rc-dev --- .../lwc/developmentPage/developmentPage.js | 2 +- .../propertyTemplateSelector.css | 79 +- .../propertyTemplateSelector.html | 23 +- .../propertyTemplateSelector.js | 1038 ++++++++++++----- previews/preview-modern-home.html | 66 +- template samples/the serenity.html | 4 +- template samples/the_vertice.html | 121 +- 7 files changed, 945 insertions(+), 388 deletions(-) diff --git a/force-app/main/default/lwc/developmentPage/developmentPage.js b/force-app/main/default/lwc/developmentPage/developmentPage.js index c6f12d1..c16ae58 100644 --- a/force-app/main/default/lwc/developmentPage/developmentPage.js +++ b/force-app/main/default/lwc/developmentPage/developmentPage.js @@ -1,7 +1,7 @@ import { LightningElement, track } from 'lwc'; export default class DevelopmentPage extends LightningElement { - @track showDevPage = true; + @track showDevPage = false; @track currentStep = 1; @track selectedTemplateId = ''; @track selectedPropertyId = ''; diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css index da76ac6..95e6a31 100644 --- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css +++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css @@ -4406,6 +4406,15 @@ late particularay display: block; } +/* Keep image stable when selection/handles appear */ +.enhanced-editor-content .draggable-image-container { + position: relative; +} +.enhanced-editor-content .draggable-image-container.selected, +.enhanced-editor-content .draggable-image-container.dragging { + position: absolute; /* only become absolute during drag */ +} + /* Left Toolbar - Original Layout */ .editor-toolbar.left { width: 300px; @@ -9059,6 +9068,60 @@ button, .btn, .toolbar-button, .export-pdf-btn { line-height: 1.4; } +/* Gallery Grid Styles for Modern Home Template */ +.modern-home-preview .gallery-section { + margin: 22px 0 10px 0; +} + +.modern-home-preview .gallery-title { + color: #003366; + font-size: 16px; + font-weight: 700; + margin: 0 0 10px 0; + border-bottom: 2px solid #003366; + padding-bottom: 6px; +} + +.modern-home-preview .gallery-grid { + display: grid !important; + grid-template-columns: repeat(3, 1fr) !important; + grid-template-rows: 90px 90px !important; + gap: 8px !important; +} + +.modern-home-preview .gallery-item { + background-size: cover; + background-position: center; + border-radius: 6px; + overflow: hidden; + position: relative; + width: 100% !important; + height: 100% !important; +} + +.modern-home-preview .gallery-item::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(to top, rgba(0,0,0,0.35), rgba(0,0,0,0)); +} + +.modern-home-preview .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); +} + +.modern-home-preview .g-item-1 { grid-column: 1 / 3; grid-row: 1 / 2; } +.modern-home-preview .g-item-2 { grid-column: 3 / 4; grid-row: 1 / 3; } +.modern-home-preview .g-item-3 { grid-column: 1 / 2; grid-row: 2 / 3; } +.modern-home-preview .g-item-4 { grid-column: 2 / 3; grid-row: 2 / 3; } + /* The Grand Oak Villa Template - Remove border and increase height */ .template-asgar1 { background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); @@ -11719,6 +11782,7 @@ img[draggable="true"] { line-height: 1.1; margin: 4px 0 8px 0; text-shadow: 0 2px 5px rgba(0,0,0,0.3); + color: #FFFFFF !important; } .vertice-preview .cover-content .address { @@ -11727,6 +11791,7 @@ img[draggable="true"] { border-top: 1px solid #0A6847; display: inline-block; padding-top: 8px; + color: #FFFFFF !important; } .vertice-preview .cover-footer { @@ -11848,4 +11913,16 @@ img[draggable="true"] { .vertice-preview .g-item-2 { grid-column: 3 / 4; grid-row: 1 / 3; background-image: url('https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800'); } .vertice-preview .g-item-3 { grid-column: 1 / 2; grid-row: 2 / 4; background-image: url('https://images.unsplash.com/photo-1600121848594-d8644e57abab?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800'); } .vertice-preview .g-item-4 { grid-column: 2 / 3; grid-row: 2 / 3; background-image: url('https://images.unsplash.com/photo-1595526114035-0d45ed16433d?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800'); } -.vertice-preview .g-item-5 { grid-column: 2 / 4; grid-row: 3 / 4; background-image: url('https://images.unsplash.com/photo-1512918728675-ed5a71a580a9?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800'); } \ No newline at end of file +.vertice-preview .g-item-5 { grid-column: 2 / 4; grid-row: 3 / 4; background-image: url('https://images.unsplash.com/photo-1512918728675-ed5a71a580a9?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800'); } + +/* Modern Home static preview gallery (separate & static) */ +.mh-static-gallery-section { margin: 22px 0 10px 0; } +.mh-static-gallery-title { color: #003366; font-size: 16px; font-weight: 700; margin: 0 0 10px 0; border-bottom: 2px solid #003366; padding-bottom: 6px; } +.mh-static-gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: 90px 90px; gap: 8px; } +.mh-static-gallery-item { background-size: cover; background-position: center; border-radius: 6px; overflow: hidden; position: relative; } +.mh-static-gallery-item::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.35), rgba(0,0,0,0)); } +.mh-static-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); } +.mh-item-1 { grid-column: 1 / 3; grid-row: 1 / 2; } +.mh-item-2 { grid-column: 3 / 4; grid-row: 1 / 3; } +.mh-item-3 { grid-column: 1 / 2; grid-row: 2 / 3; } +.mh-item-4 { grid-column: 2 / 3; grid-row: 2 / 3; } \ No newline at end of file diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html index 196fd68..b5595ec 100644 --- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html +++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html @@ -93,12 +93,12 @@ onclick={handleTemplateSelect}>
-
+

Modern Villa

123 Luxury Lane, Prestige City, PC 45678

-
AED 2,500,000
+
$2,500,000
4 Beds 3 Baths @@ -112,7 +112,7 @@

About this Property

-

This stunning modern villa offers contemporary living with premium finishes and an open-concept design perfect for entertaining.

+

{modernHomePreview}

@@ -120,6 +120,8 @@
Type: Single-Family
Year Built: 2023
Parking: 2-Car Garage
+
Floor: 2 Levels
+
Furnishing: Unfurnished
@@ -127,7 +129,20 @@
Master Suite
Open Floor Plan
Private Garden
+
Smart Home
+
Energy Efficient
+ + +

Location & Nearby

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

${basicPropertyName}

${basicLocation}
${basicStatus}

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) ? `
${value}
${label}
` : ""); + + 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;
-
${ - data.status || "FOR SALE" - }
+ ${hasValue(status) ? `
${status}
` : ""}

${propertyName}

-

${location}

+ ${hasValue(location) ? `

${location}

` : ""}
-
-
-
${bedrooms}
-
Bedrooms
-
-
-
${bathrooms}
-
Bathrooms
-
-
-
${squareFeet}
-
Sq. Ft.
-
-
-
${price}
-
Price
-
-
+
${coverFeaturesHTML}
@@ -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}
-
Agent: ${agentName} | ${agentPhone} | ${agentEmail}
-
Owner: ${ownerName} | ${ownerPhone}
+ ${agentLine}${ownerLine}
- +
-
-
-
-
`; } - 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) ? `
${value}
${label}
` : ""); + + 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 `
+ +
${has(status) ? `
${status}
` : ""}

${propertyName}

${has(location) ? `
${location}
` : ""}
${coverFeaturesHTML}
+

Description

${has(description) ? `

${description}

` : ""}
${agentLine}${ownerLine}
+

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}
${agentLine}${ownerLine}
+
${propertyGalleryHTML}
${agentLine}${ownerLine}
+
`; + } + 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
Cover

${propertyName}

${location}
${price}
Ref ID: ${referenceId}
${description}
${propertyGallery}
`; + 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 +
+ Cover +
+
+
${propertyType}
+
${propertyName}
+
${location}
+
+ +
+ +
+
+ +
+
+ ${visionHeading ? `

${visionHeading}

` : ""} + ${description} +
+
+
+
${propertyName}Page 02 / 04
+
+
+ +
+
+ +
+

Lifestyle Amenities

    ${amenitiesHTML}
+

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}
+
+
+
${propertyName}Page 03 / 04
+
+
+ +
+
+ +
+
    +
  • Landmarks: ${landmarks}
  • +
  • Transportation: ${transportation}
  • +
  • Schools: ${schools}
  • +
  • Shopping: ${shopping}
  • +
  • Airport: ${airport}
  • +
+
+ + +
${propertyName}Page 04 / 04
+
+
+ +`; } /* // 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
+ + + @@ -497,67 +496,67 @@
-

Two-Bedroom Residence

+

[Floorplan 1 Title]

-
1,450
+
[Floorplan 1 Area]
SQ. FT.
-
2
+
[Floorplan 1 Bedrooms]
BEDROOMS
-
2
+
[Floorplan 1 Bathrooms]
BATHROOMS
-
1
+
[Floorplan 1 Balcony]
BALCONY
-

A thoughtfully designed space perfect for urban professionals or small families, combining comfort with panoramic city views.

+
[Floorplan 1 Description HTML]
-

Three-Bedroom Penthouse

+

[Floorplan 2 Title]

-
3,200
+
[Floorplan 2 Area]
SQ. FT.
-
3
+
[Floorplan 2 Bedrooms]
BEDROOMS
-
3.5
+
[Floorplan 2 Bathrooms]
BATHROOMS
-
1
+
[Floorplan 2 Terrace]
TERRACE
-

The pinnacle of luxury living, this penthouse offers expansive spaces, premium finishes, and exclusive access to a private rooftop terrace.

+
[Floorplan 2 Description HTML]

Additional Information

-
Pets
Allowed (w/ restrictions)
-
Smoking
In designated areas
-
Availability
Q4 2025
-
Parking
2 Spaces per Unit
-
Security Deposit
2 Months
-
Utilities
Sub-metered
+
Pets
[Pets]
+
Smoking
[Smoking]
+
Availability
[Availability]
+
Parking
[Parking]
+
Security Deposit
[Security Deposit]
+
Utilities
[Utilities]
- THE VERTICE + [Property Name] Page 05 / 06
@@ -572,29 +571,29 @@

Schedule a Private Viewing

-

Experience The Vertice firsthand. Contact our sales executive to arrange an exclusive tour of the property and available residences.

+

[Viewing CTA Text]

-
Alexander Valentine
-
Sales Executive, Elysian Properties
+
[Contact Name]
+
[Contact Title]
- (555) 123-9876
- alex.v@elysian.com + [Contact Phone]
+ [Contact Email]

Neighborhood Highlights

    -
  • Landmarks: Central Park (5 min)
  • -
  • Transportation: Metro Line A (2 min walk)
  • -
  • Schools: Metropolis Intl. (10 min)
  • -
  • Shopping: The Galleria Mall (8 min)
  • -
  • Airport: 25 min drive
  • +
  • Landmarks: [Landmarks]
  • +
  • Transportation: [Transportation]
  • +
  • Schools: [Schools]
  • +
  • Shopping: [Shopping]
  • +
  • Airport: [Airport]