V1.0.3-beta
This commit is contained in:
parent
4306e591a6
commit
0b3e11ac6a
@ -2257,7 +2257,7 @@ export default class PropertyTemplateSelector extends LightningElement {
|
||||
.replace(/\"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
|
||||
const lines = raw.replace(/\r\n?/g, "\n").split("\n");
|
||||
const lines = raw.replace(/\r\n?/g, "\n").split("\n").map((l) => l.trim());
|
||||
|
||||
const bulletRe = /^\s*(?:[-*•]|\u2022)\s+(.*)$/; // -,*,• bullets
|
||||
const numberedRe = /^\s*(\d+)[\.)]\s+(.*)$/; // 1. or 1)
|
||||
@ -3249,6 +3249,7 @@ export default class PropertyTemplateSelector extends LightningElement {
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
${galleryPagesHTML}
|
||||
|
||||
</body>
|
||||
@ -3277,7 +3278,9 @@ export default class PropertyTemplateSelector extends LightningElement {
|
||||
"Property description not available.";
|
||||
|
||||
// Get smart images
|
||||
const exteriorImage = this.getExteriorImageUrl();
|
||||
const exteriorImage =
|
||||
this.getExteriorImageUrl() ||
|
||||
"https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200";
|
||||
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"
|
||||
@ -3433,20 +3436,20 @@ export default class PropertyTemplateSelector extends LightningElement {
|
||||
data.Last_Sold_Price__c || data.lastSoldPrice ;
|
||||
|
||||
// Location and POI data
|
||||
const schools = data.Schools__c || data.schools ;
|
||||
const schools = data.Schools__c || data.schools || "N/A";
|
||||
const shoppingCenters =
|
||||
data.Shopping_Centers__c || data.shoppingCenters ;
|
||||
data.Shopping_Centers__c || data.shoppingCenters || "N/A";
|
||||
const airportDistance =
|
||||
data.Airport_Distance__c || data.airportDistance ;
|
||||
data.Airport_Distance__c || data.airportDistance || "N/A";
|
||||
const nearbyLandmarks =
|
||||
data.Nearby_Landmarks__c || data.nearbyLandmarks;
|
||||
data.Nearby_Landmarks__c || data.nearbyLandmarks || "N/A";
|
||||
const transportation =
|
||||
data.Transportation__c || data.transportation ;
|
||||
const hospitals = data.Hospitals__c || data.hospitals ;
|
||||
data.Transportation__c || data.transportation || "N/A";
|
||||
const hospitals = data.Hospitals__c || data.hospitals || "N/A";
|
||||
const beachDistance =
|
||||
data.Beach_Distance__c || data.beachDistance;
|
||||
data.Beach_Distance__c || data.beachDistance || "N/A";
|
||||
const metroDistance =
|
||||
data.Metro_Distance__c || data.metroDistance;
|
||||
data.Metro_Distance__c || data.metroDistance || "N/A";
|
||||
|
||||
// Additional information
|
||||
const petFriendly = data.Pet_Friendly__c || data.petFriendly;
|
||||
@ -3863,41 +3866,41 @@ export default class PropertyTemplateSelector extends LightningElement {
|
||||
<div class="location-map-container"></div>
|
||||
<div class="location-content">
|
||||
<header class="page-header" style="margin-bottom: 0;">
|
||||
<h1 class="title">An Unrivaled <span>Setting</span></h1>
|
||||
<h1 class="title">Specifications & <span>Location Details</span></h1>
|
||||
<span class="property-name">${location}</span>
|
||||
</header>
|
||||
<div class="poi-grid">
|
||||
<div class="poi-item">
|
||||
<div class="poi-grid" style="margin-top: 12px;">
|
||||
<div class="poi-item" style="break-inside: avoid;">
|
||||
<div class="icon"><i class="fa-solid fa-school"></i></div>
|
||||
<div class="title">Schools</div>
|
||||
<div class="details">${schools}</div>
|
||||
</div>
|
||||
<div class="poi-item">
|
||||
<div class="poi-item" style="break-inside: avoid;">
|
||||
<div class="icon"><i class="fa-solid fa-shopping-cart"></i></div>
|
||||
<div class="title">Shopping</div>
|
||||
<div class="details">${shoppingCenters}</div>
|
||||
</div>
|
||||
<div class="poi-item">
|
||||
<div class="poi-item" style="break-inside: avoid;">
|
||||
<div class="icon"><i class="fa-solid fa-plane"></i></div>
|
||||
<div class="title">Airport</div>
|
||||
<div class="details">${airportDistance}</div>
|
||||
</div>
|
||||
<div class="poi-item">
|
||||
<div class="poi-item" style="break-inside: avoid;">
|
||||
<div class="icon"><i class="fa-solid fa-landmark"></i></div>
|
||||
<div class="title">Landmarks</div>
|
||||
<div class="details">${nearbyLandmarks}</div>
|
||||
</div>
|
||||
<div class="poi-item">
|
||||
<div class="poi-item" style="break-inside: avoid;">
|
||||
<div class="icon"><i class="fa-solid fa-bus"></i></div>
|
||||
<div class="title">Transportation</div>
|
||||
<div class="details">${transportation}</div>
|
||||
</div>
|
||||
<div class="poi-item">
|
||||
<div class="poi-item" style="break-inside: avoid;">
|
||||
<div class="icon"><i class="fa-solid fa-hospital"></i></div>
|
||||
<div class="title">Hospitals</div>
|
||||
<div class="details">${hospitals}</div>
|
||||
</div>
|
||||
<div class="poi-item">
|
||||
<div class="poi-item" style="break-inside: avoid;">
|
||||
<div class="icon"><i class="fa-solid fa-umbrella-beach"></i></div>
|
||||
<div class="title">Beach</div>
|
||||
<div class="details">${beachDistance}</div>
|
||||
@ -3960,6 +3963,558 @@ ${galleryPagesHTML}
|
||||
`
|
||||
}
|
||||
|
||||
createSerenityHouseTemplate() {
|
||||
const data = this.propertyData || {};
|
||||
|
||||
// Extract all available property data with fallbacks
|
||||
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 || "";
|
||||
const agentName =
|
||||
data.contactName || data.Agent_Name__c || data.agentName || "N/A";
|
||||
const agentPhone =
|
||||
data.contactPhone || data.Agent_Phone__c || data.agentPhone || "N/A";
|
||||
const agentEmail =
|
||||
data.contactEmail || data.Agent_Email__c || data.agentEmail || "N/A";
|
||||
const ownerName = data.Owner_Name__c || data.ownerName || "N/A";
|
||||
const ownerPhone = data.Owner_Phone__c || data.ownerPhone || "N/A";
|
||||
const ownerEmail = data.Owner_Email__c || data.ownerEmail || "N/A";
|
||||
|
||||
// Dynamic pricing with fallbacks
|
||||
const price =
|
||||
data.Sale_Price_Min__c ||
|
||||
data.Rent_Price_Min__c ||
|
||||
data.Price__c ||
|
||||
data.salePriceMin ||
|
||||
data.rentPriceMin ||
|
||||
data.price ||
|
||||
"Price on Request";
|
||||
const priceDisplay =
|
||||
price !== "Price on Request" ? `Offered at ${price}` : "Price on Request";
|
||||
|
||||
// Dynamic property details
|
||||
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 || "N/A";
|
||||
const propertyType = data.Property_Type__c || data.propertyType || "N/A";
|
||||
const status = data.Status__c || data.status || data.offeringType || "N/A";
|
||||
const yearBuilt =
|
||||
data.Build_Year__c || data.yearBuilt || data.buildYear || "N/A";
|
||||
const furnishing = data.Furnished__c || data.furnishing || "N/A";
|
||||
const parking = data.Parking_Spaces__c || data.parking || "N/A";
|
||||
|
||||
// Dynamic description
|
||||
const description = this.formatDescriptionForPDF(
|
||||
data.Description_English__c ||
|
||||
data.descriptionEnglish ||
|
||||
data.description ||
|
||||
"Property description not available."
|
||||
);
|
||||
|
||||
// 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"
|
||||
);
|
||||
|
||||
// Dynamic location details
|
||||
const schools = data.Schools__c || data.schools || "N/A";
|
||||
const shopping = data.Shopping_Centers__c || data.shoppingCenters || "N/A";
|
||||
const hospitals = data.Hospitals__c || data.hospitals || "N/A";
|
||||
const countryClub = data.Country_Club__c || data.countryClub || "N/A";
|
||||
const airport = data.Airport_Distance__c || data.airportDistance || "N/A";
|
||||
|
||||
// Dynamic additional info
|
||||
const petFriendly = 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 utilities =
|
||||
data.Utilities_Included__c || data.utilitiesIncluded || "N/A";
|
||||
|
||||
// Additional dynamic fields
|
||||
const floor = data.Floor__c || data.floor || "N/A";
|
||||
const maintenanceFee = data.Maintenance_Fee__c || data.maintenanceFee || "N/A";
|
||||
const serviceCharge = data.Service_Charge__c || data.serviceCharge || "N/A";
|
||||
const acres = data.Lot_Size__c || data.acres || "N/A";
|
||||
|
||||
// Build paginated gallery pages appended at the end (2 columns, last image full width)
|
||||
const allImages = Array.isArray(this.realPropertyImages)
|
||||
? this.realPropertyImages
|
||||
: [];
|
||||
const imagesPerPage = 7;
|
||||
let galleryPagesHTML = "";
|
||||
if (allImages.length > 0) {
|
||||
for (let i = 0; i < allImages.length; i += imagesPerPage) {
|
||||
const chunk = allImages.slice(i, i + imagesPerPage);
|
||||
const chunkHTML = chunk
|
||||
.map((img, idx) => {
|
||||
const title =
|
||||
img.title || img.pcrm__Title__c || `Property Image ${i + idx + 1}`;
|
||||
const extraStyle = idx === chunk.length - 1 ? " grid-column: 1 / -1;" : "";
|
||||
return `<img src="${img.url}" alt="${title}" style="width:100%; height:auto; display:block; break-inside: avoid; page-break-inside: avoid;${extraStyle}"/>`;
|
||||
})
|
||||
.join("");
|
||||
galleryPagesHTML += `
|
||||
<div class="brochure-page">
|
||||
<div class="page-layout">
|
||||
<h1 class="page-title-main">Property Gallery</h1>
|
||||
<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 20px;">${chunkHTML}</div>
|
||||
</div>
|
||||
<footer class="p1-footer" style="padding: 20px 60px;">
|
||||
<div><strong>Agent:</strong> ${agentName} | ${agentPhone} | ${agentEmail}</div>
|
||||
<div><strong>Owner:</strong> ${ownerName} | ${ownerPhone} | ${ownerEmail}</div>
|
||||
</footer>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Editorial Real Estate Brochure - Updated - A4 Size</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&family=Cormorant+Garamond:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<style>
|
||||
/* --- DESIGN SYSTEM & VARIABLES --- */
|
||||
:root {
|
||||
/* Color Palette */
|
||||
--color-bg: #FFFFFF;
|
||||
--color-off-white: #F8F7F5;
|
||||
--color-text-primary: #333333;
|
||||
--color-text-secondary: #777777;
|
||||
--color-accent-beige: #D4C7B8;
|
||||
--color-border: #EAEAEA;
|
||||
|
||||
/* Typography */
|
||||
--font-serif: 'Cormorant Garamond', serif;
|
||||
--font-sans: 'Lato', sans-serif;
|
||||
}
|
||||
|
||||
/* --- Print Media Queries for A4 --- */
|
||||
@media print {
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: white;
|
||||
}
|
||||
.brochure-page {
|
||||
width: 210mm !important;
|
||||
height: 297mm !important;
|
||||
box-shadow: none !important;
|
||||
page-break-after: always;
|
||||
}
|
||||
.brochure-page:last-child {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- GLOBAL & BODY STYLES --- */
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background-color: #d8d8d8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 50px;
|
||||
margin: 0;
|
||||
gap: 50px;
|
||||
}
|
||||
|
||||
.brochure-page {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
background-color: var(--color-bg);
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* --- PAGE 1: FRONT COVER --- */
|
||||
.p1-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.p1-image-side {
|
||||
flex: 1.2;
|
||||
background-image: url('${exteriorImage}');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
.p1-content-side {
|
||||
flex: 1;
|
||||
padding: 70px 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.p1-header .collection {
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 3px;
|
||||
color: var(--color-text-secondary);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.p1-main-title {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 5rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.1;
|
||||
color: var(--color-text-primary);
|
||||
margin: 20px 0;
|
||||
}
|
||||
.p1-address {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-secondary);
|
||||
border-left: 3px solid var(--color-accent-beige);
|
||||
padding-left: 20px;
|
||||
}
|
||||
.p1-ref-id {
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-secondary);
|
||||
margin-top: 15px;
|
||||
padding-left: 23px;
|
||||
}
|
||||
.p1-footer {
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.p1-footer strong {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
.p1-footer .area {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-primary);
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* --- SHARED STYLES for Content Pages --- */
|
||||
.page-layout {
|
||||
padding: 70px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.page-number {
|
||||
position: absolute;
|
||||
top: 70px; right: 70px;
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.2rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.page-title-main {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 3.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
margin: 0 0 15px 0;
|
||||
line-height: 1;
|
||||
}
|
||||
.page-title-sub {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.content-divider {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background-color: var(--color-border);
|
||||
margin: 30px 0;
|
||||
}
|
||||
.section-title {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: 20px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* --- PAGE 2: INTRODUCTION & NARRATIVE --- */
|
||||
.p2-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 60px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.p2-image {
|
||||
background-image: url('${interiorImage}');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
.p2-text p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.8;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.p2-text p:first-of-type::first-letter {
|
||||
font-family: var(--font-serif);
|
||||
font-size: 4rem;
|
||||
float: left;
|
||||
line-height: 1;
|
||||
margin-right: 15px;
|
||||
color: var(--color-accent-beige);
|
||||
}
|
||||
.pull-quote {
|
||||
border-left: 3px solid var(--color-accent-beige);
|
||||
padding-left: 25px;
|
||||
margin: 30px 0;
|
||||
font-family: var(--font-serif);
|
||||
font-size: 1.5rem;
|
||||
font-style: italic;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* --- PAGE 3: DETAILS & AMENITIES --- */
|
||||
.p3-main-content { flex-grow: 1; }
|
||||
.spec-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 30px;
|
||||
padding: 30px;
|
||||
background-color: var(--color-off-white);
|
||||
}
|
||||
.spec-item { text-align: center; }
|
||||
.spec-item .value {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
.spec-item .label {
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px 40px;
|
||||
}
|
||||
.details-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding-bottom: 10px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.details-item .label { color: var(--color-text-secondary); }
|
||||
.details-item .value { color: var(--color-text-primary); font-weight: 700; }
|
||||
.amenities-list { list-style: none; padding: 0; column-count: 2; column-gap: 40px;}
|
||||
.amenities-list li {
|
||||
margin-bottom: 12px;
|
||||
color: var(--color-text-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.9rem;
|
||||
-webkit-column-break-inside: avoid;
|
||||
page-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
}
|
||||
.amenities-list li i { color: var(--color-accent-beige); margin-right: 12px; }
|
||||
|
||||
/* --- PAGE 4: REVISED LAYOUT --- */
|
||||
.p4-section-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: var(--color-text-primary);
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
.p4-floorplan-container {
|
||||
height: 320px;
|
||||
background-color: var(--color-off-white);
|
||||
border: 1px solid var(--color-border);
|
||||
background-image: url('https://cdn.shopify.com/s/files/1/0024/0495/3953/files/Architect_s_floor_plan_for_a_house_in_black_and_white_large.jpg');
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.p4-info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 60px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.info-list .info-item, .location-list .item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.info-list .info-item strong, .location-list .item strong {
|
||||
color: var(--color-text-primary);
|
||||
margin-right: 15px;
|
||||
}
|
||||
.p4-contact-row {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
justify-content: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
.contact-card {
|
||||
background-color: var(--color-off-white);
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
max-width: 300px;
|
||||
}
|
||||
.contact-card .title {
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.contact-card .name { font-family: var(--font-serif); font-size: 1.5rem; font-weight: 600; }
|
||||
.contact-card .phone, .contact-card .email { font-size: 0.9rem; margin: 4px 0; color: var(--color-text-secondary); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="brochure-page">
|
||||
<div class="p1-container">
|
||||
<div class="p1-image-side"></div>
|
||||
<div class="p1-content-side">
|
||||
<header class="p1-header">
|
||||
<div class="collection">Elysian Estates Collection</div>
|
||||
<h1 class="p1-main-title">${propertyName}</h1>
|
||||
<p class="p1-address">${location}</p>
|
||||
<p class="p1-ref-id">Reference ID: ${referenceId}</p>
|
||||
</header>
|
||||
<footer class="p1-footer">
|
||||
<div class="area">${squareFeet} Sq. Ft. • ${bedrooms} Bedrooms • ${bathrooms} Bathrooms</div>
|
||||
${description}
|
||||
<br>
|
||||
<strong>${priceDisplay}</strong>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="brochure-page">
|
||||
<div class="page-layout">
|
||||
<span class="page-number">02</span>
|
||||
<h1 class="page-title-main">A Sanctuary of Modern Design</h1>
|
||||
<p class="page-title-sub">${propertyType} • ${status} • ${squareFeet} Sq. Ft.</p>
|
||||
<div class="p2-grid">
|
||||
<div class="p2-text">
|
||||
${description}
|
||||
<p class="pull-quote">A timeless residence built not just for living, but for thriving.</p>
|
||||
</div>
|
||||
<div class="p2-image"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="brochure-page">
|
||||
<div class="page-layout">
|
||||
<span class="page-number">03</span>
|
||||
<h1 class="page-title-main">Property Specifications</h1>
|
||||
<p class="page-title-sub">A comprehensive overview of the property's features, details, and amenities.</p>
|
||||
|
||||
<div class="p3-main-content">
|
||||
<div class="spec-grid">
|
||||
<div class="spec-item"><div class="value">${bedrooms}</div><div class="label">Bedrooms</div></div>
|
||||
<div class="spec-item"><div class="value">${bathrooms}</div><div class="label">Bathrooms</div></div>
|
||||
<div class="spec-item"><div class="value">${squareFeet}</div><div class="label">Square Feet</div></div>
|
||||
<div class="spec-item"><div class="value">${acres}</div><div class="label">Acres</div></div>
|
||||
</div>
|
||||
|
||||
<hr class="content-divider">
|
||||
|
||||
<h3 class="section-title">Property Details</h3>
|
||||
<div class="details-grid">
|
||||
<div class="details-item"><span class="label">Status</span><span class="value">${status}</span></div>
|
||||
<div class="details-item"><span class="label">Year Built</span><span class="value">${yearBuilt}</span></div>
|
||||
<div class="details-item"><span class="label">Type</span><span class="value">${propertyType}</span></div>
|
||||
<div class="details-item"><span class="label">Furnishing</span><span class="value">${furnishing}</span></div>
|
||||
<div class="details-item"><span class="label">Floor</span><span class="value">${floor}</span></div>
|
||||
<div class="details-item"><span class="label">Maintenance Fee</span><span class="value">${maintenanceFee}</span></div>
|
||||
<div class="details-item"><span class="label">Parking</span><span class="value">${parking}</span></div>
|
||||
<div class="details-item"><span class="label">Service Charge</span><span class="value">${serviceCharge}</span></div>
|
||||
</div>
|
||||
|
||||
<hr class="content-divider">
|
||||
|
||||
<h3 class="section-title">Amenities & Features</h3>
|
||||
<ul class="amenities-list">${this.generateAmenitiesListItems(data)}</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="brochure-page">
|
||||
<div class="page-layout">
|
||||
<span class="page-number">04</span>
|
||||
<h1 class="page-title-main" style="margin-bottom: 30px;">Floor Plan & Details</h1>
|
||||
|
||||
<div class="p4-info-grid">
|
||||
<div class="location-list">
|
||||
<h2 class="p4-section-title">Location & Nearby</h2>
|
||||
<div class="item"><strong>Schools</strong> <span>${schools}</span></div>
|
||||
<div class="item"><strong>Shopping</strong> <span>${shopping}</span></div>
|
||||
<div class="item"><strong>Hospitals</strong> <span>${hospitals}</span></div>
|
||||
<div class="item"><strong>Country Club</strong> <span>${countryClub}</span></div>
|
||||
<div class="item"><strong>Airport</strong> <span>${airport}</span></div>
|
||||
</div>
|
||||
<div class="info-list">
|
||||
<h2 class="p4-section-title">Additional Information</h2>
|
||||
<div class="info-item"><strong>Pet-Friendly</strong> <span>${petFriendly}</span></div>
|
||||
<div class="info-item"><strong>Smoking</strong> <span>${smoking}</span></div>
|
||||
<div class="info-item"><strong>Availability</strong> <span>${availability}</span></div>
|
||||
<div class="info-item"><strong>Utilities</strong> <span>${utilities}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="content-divider">
|
||||
|
||||
<h2 class="p4-section-title">Floor Plan & Location</h2>
|
||||
<div class="p4-floorplan-container"></div>
|
||||
|
||||
<div class="p4-contact-row">
|
||||
<div class="contact-card">
|
||||
<div class="title">Owner Information</div>
|
||||
<div class="name">${ownerName}</div>
|
||||
<p class="phone">${ownerPhone}</p>
|
||||
<p class="email">${ownerEmail}</p>
|
||||
</div>
|
||||
<div class="contact-card">
|
||||
<div class="title">Agent Information</div>
|
||||
<div class="name">${agentName}</div>
|
||||
<p class="phone">${agentPhone}</p>
|
||||
<p class="email">${agentEmail}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${galleryPagesHTML}
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
|
||||
createLuxuryMansionTemplate() {
|
||||
const data = this.propertyData || {};
|
||||
|
||||
@ -4652,6 +5207,8 @@ ${galleryPagesHTML}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
${galleryPagesHTML}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
@ -5941,9 +6498,27 @@ ${galleryPagesHTML}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise insert a visible tab (4 spaces) at caret
|
||||
editor.focus();
|
||||
document.execCommand("insertText", false, " ");
|
||||
// Otherwise add a visual tab (4 NBSP) at the start of the current block
|
||||
const getBlock = (node) => {
|
||||
let n = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
||||
while (
|
||||
n &&
|
||||
!/(P|DIV|LI|H1|H2|H3|H4|H5|H6)/i.test(n.tagName)
|
||||
) {
|
||||
n = n.parentElement;
|
||||
}
|
||||
return n || editor;
|
||||
};
|
||||
const block = getBlock(range.startContainer);
|
||||
if (!block) return;
|
||||
const TAB = "\u00A0\u00A0\u00A0\u00A0"; // 4 NBSP
|
||||
const first = block.firstChild;
|
||||
if (first && first.nodeType === Node.TEXT_NODE) {
|
||||
first.textContent = TAB + first.textContent;
|
||||
} else {
|
||||
block.insertBefore(document.createTextNode(TAB), first || null);
|
||||
}
|
||||
editor.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
}
|
||||
|
||||
handleOutdent() {
|
||||
@ -5975,21 +6550,25 @@ ${galleryPagesHTML}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, remove one indentation level (4 leading spaces) from current line
|
||||
editor.focus();
|
||||
const sel2 = window.getSelection();
|
||||
const range2 = sel2.getRangeAt(0);
|
||||
let container = range2.startContainer.nodeType === Node.TEXT_NODE
|
||||
? range2.startContainer.parentElement
|
||||
: range2.startContainer;
|
||||
// Move up to a block element
|
||||
while (container && !/^(P|DIV|LI|H1|H2|H3|H4|H5|H6)$/i.test(container.tagName)) {
|
||||
container = container.parentElement;
|
||||
// Otherwise, remove one indentation level (up to 4 NBSP/spaces) at start of the current block
|
||||
const getBlock = (node) => {
|
||||
let n = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
||||
while (
|
||||
n &&
|
||||
!/(P|DIV|LI|H1|H2|H3|H4|H5|H6)/i.test(n.tagName)
|
||||
) {
|
||||
n = n.parentElement;
|
||||
}
|
||||
if (container) {
|
||||
const text = container.innerHTML.replace(/^( |\s){1,4}/, "");
|
||||
container.innerHTML = text;
|
||||
return n || editor;
|
||||
};
|
||||
const block = getBlock(range1.startContainer);
|
||||
if (!block) return;
|
||||
const first = block.firstChild;
|
||||
if (first && first.nodeType === Node.TEXT_NODE) {
|
||||
// Remove up to 4 leading NBSP/spaces
|
||||
first.textContent = first.textContent.replace(/^(?:\u00A0|\s){1,4}/, "");
|
||||
}
|
||||
editor.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
}
|
||||
|
||||
handleFontFamilyChange(event) {
|
||||
@ -10152,8 +10731,9 @@ ${galleryPagesHTML}
|
||||
const editorRect = editor
|
||||
? editor.getBoundingClientRect()
|
||||
: { left: 0, top: 0 };
|
||||
const currentWidth = element.offsetWidth;
|
||||
const currentHeight = element.offsetHeight;
|
||||
const scale = this.zoom || 1;
|
||||
const currentWidth = rect.width / scale;
|
||||
const currentHeight = rect.height / scale;
|
||||
|
||||
// Insert a placeholder to avoid layout shift in the original flow
|
||||
const placeholder = document.createElement("div");
|
||||
@ -10165,7 +10745,6 @@ ${galleryPagesHTML}
|
||||
container.className = "draggable-image-container";
|
||||
container.style.position = "absolute"; // anchored to editor (set to relative elsewhere)
|
||||
// Account for preview zoom scale to avoid displacement
|
||||
const scale = this.zoom || 1;
|
||||
container.style.left =
|
||||
(rect.left - editorRect.left + (editor ? editor.scrollLeft : 0)) /
|
||||
scale +
|
||||
@ -10181,9 +10760,14 @@ ${galleryPagesHTML}
|
||||
// Set container and image sizing
|
||||
container.style.width = currentWidth + "px";
|
||||
container.style.height = currentHeight + "px";
|
||||
element.style.width = currentWidth + "px";
|
||||
element.style.height = currentHeight + "px";
|
||||
container.style.boxSizing = "border-box";
|
||||
element.style.width = "100%";
|
||||
element.style.height = "100%";
|
||||
element.style.maxWidth = "none";
|
||||
element.style.maxHeight = "none";
|
||||
element.style.margin = "0";
|
||||
element.style.display = "block";
|
||||
element.style.boxSizing = "border-box";
|
||||
element.style.objectFit = window.getComputedStyle(element).objectFit || "cover";
|
||||
|
||||
// Replace the image in the flow with placeholder, then move image to absolute container
|
||||
|
||||
Loading…
Reference in New Issue
Block a user