Add your content here
+
+
+
+
-
+
+ Property Gallery
+
${chunkHTML}
-
+
@@ -5585,7 +5693,7 @@ ${galleryPagesHTML}
const allImages = Array.isArray(this.realPropertyImages)
? this.realPropertyImages
: [];
- const imagesPerPage = 6; // Optimal for A4 with 2x3 grid
+ const imagesPerPage = 8; // 2x4 grid for better space utilization
let galleryPagesHTML = "";
if (allImages.length > 0) {
for (let i = 0; i < allImages.length; i += imagesPerPage) {
@@ -5605,8 +5713,8 @@ ${galleryPagesHTML}
img.url ? `https://salesforce.tech4biz.io${img.url}` :
'https://via.placeholder.com/400x200?text=No+Image';
- return `
About this Property
${description}
-
+ return `
-
@@ -6235,7 +6342,7 @@ ${galleryPagesHTML}
const allImages = Array.isArray(this.realPropertyImages)
? this.realPropertyImages
: [];
- const imagesPerPage = 6; // Optimal for A4 with 2x3 grid
+ const imagesPerPage = 8; // 2x4 grid for better space utilization
let galleryPagesHTML = "";
if (allImages.length > 0) {
for (let i = 0; i < allImages.length; i += imagesPerPage) {
@@ -6255,8 +6362,8 @@ ${galleryPagesHTML}
img.url ? `https://salesforce.tech4biz.io${img.url}` :
'https://via.placeholder.com/400x200?text=No+Image';
- return `
+
`;
})
@@ -5617,10 +5725,9 @@ ${galleryPagesHTML}
Property Gallery
- ${propertyName}${chunkHTML}
+ ${chunkHTML}
-
+ return `
+
`;
})
@@ -6264,10 +6371,8 @@ ${galleryPagesHTML}
galleryPagesHTML += `
- ${pageNumber}
Property Gallery
-Visual Tour • ${propertyName}
-${chunkHTML}
+ ${chunkHTML}
Agent: ${agentName} | ${agentEmail} | ${agentPhone}
@@ -7015,7 +7120,7 @@ ${galleryPagesHTML}
const allImages = Array.isArray(this.realPropertyImages)
? this.realPropertyImages
: [];
- const imagesPerPage = 6; // Optimal for A4 with 2x3 grid
+ const imagesPerPage = 8; // 2x4 grid for better space utilization
let galleryPagesHTML = "";
if (allImages.length > 0) {
for (let i = 0; i < allImages.length; i += imagesPerPage) {
@@ -7035,8 +7140,8 @@ ${galleryPagesHTML}
img.url ? `https://salesforce.tech4biz.io${img.url}` :
'https://via.placeholder.com/400x200?text=No+Image';
- return `
-
+ return `
-
+
`;
})
@@ -7046,10 +7151,9 @@ ${galleryPagesHTML}
Property Gallery
- Visual Tour${chunkHTML}
+ ${chunkHTML}
@@ -7942,6 +8046,543 @@ ${galleryPagesHTML}
// Development mode properties
@track debugMode = false;
+ // New page addition properties
+ @track showDataTypeModal = false;
+ @track selectedDataType = '';
+ @track forceReRender = false;
+
+ // Computed property for button disabled state
+ get selectedDataTypeDisabled() {
+ return !this.selectedDataType;
+ }
+
+ // Computed property for showing PDF button only on step 3
+ get showGeneratePdfButton() {
+ return this.currentStep === 3;
+ }
+
+ // Structure content for PDF generation
+ structureContentForPdf(htmlContent) {
+ try {
+ // Create a temporary div to parse the HTML
+ const tempDiv = document.createElement('div');
+ tempDiv.innerHTML = htmlContent;
+
+ // Find all brochure pages
+ const brochurePages = tempDiv.querySelectorAll('.brochure-page, .brochure');
+
+ if (brochurePages.length === 0) {
+ console.warn('No brochure pages found in content');
+ return htmlContent;
+ }
+
+ // Create a properly structured HTML document
+ let structuredHtml = `
+
+
+
+
+ Property Brochure
+
+
+`;
+
+ // Add each page to the structured HTML
+ brochurePages.forEach((page, index) => {
+ if (index > 0) {
+ structuredHtml += '';
+ }
+ structuredHtml += page.outerHTML;
+ });
+
+ structuredHtml += '';
+
+ console.log(`Structured ${brochurePages.length} pages for PDF generation`);
+ return structuredHtml;
+
+ } catch (error) {
+ console.error('Error structuring content for PDF:', error);
+ return htmlContent; // Return original content if structuring fails
+ }
+ }
+
+ // Helper function to get template-specific footer
+ getTemplateSpecificFooter() {
+ const logoUrl = 'https://tso3listingimages.s3.amazonaws.com/00DFV000001HtSX/a0LFV000001NhJq2AK/companyLogo.jpeg?t=1757834589169';
+
+ switch (this.selectedTemplateId) {
+ case 'modern-home-template':
+ return `
+
+ `;
+
+ case 'grand-oak-villa-template':
+ return `
+
+ `;
+
+ case 'serenity-house-template':
+ return `
+
+ `;
+
+ case 'luxury-mansion-template':
+ return `
+
+ `;
+
+ default:
+ return `
+
tags or newlines + const lines = selectedHTML.split(/(
|\r?\n)/i) + .filter((l) => l.trim() && !l.match(/^(
|\r?\n)$/i)) + .map(l => l.trim()); + if (lines.length > 0) { + return lines; + } + } + } + + // If no selection or single line, try to get lines from the container + const container = range.commonAncestorContainer; + let htmlContent = ""; + + // If we're in a text node, get the parent element's HTML + if (container.nodeType === Node.TEXT_NODE) { + htmlContent = container.parentElement.innerHTML || container.textContent; + } else { + htmlContent = container.innerHTML || container.textContent; + } + + // Split by
tags or newlines and filter out empty lines + const lines = htmlContent.split(/(
|\r?\n)/i) + .filter((l) => l.trim() && !l.match(/^(
|\r?\n)$/i)) + .map(l => l.trim()); + + return lines; + } catch (error) { + console.error("Error getting selected lines with HTML:", error); + return null; + } + } + + // Get selected HTML content preserving formatting + getSelectedHTML(range) { + try { + const contents = range.cloneContents(); + const div = document.createElement('div'); + div.appendChild(contents); + + // For multiple line selections, we need to handle different scenarios + const html = div.innerHTML; + + // If the selection contains multiple elements or line breaks, preserve them + if (html.includes('
{ const li = document.createElement("li"); - li.textContent = line.trim(); + // Preserve HTML formatting and font size + li.innerHTML = line.trim(); li.contentEditable = true; list.appendChild(li); }); } else { // Single line selected const li = document.createElement("li"); - li.textContent = selectedText || "List item"; + // Preserve HTML formatting and font size + li.innerHTML = selectedText || "List item"; li.contentEditable = true; list.appendChild(li); } } else { // No text selected - check if we're in a paragraph with multiple lines - const selectedLines = this.getSelectedLines(range); + const selectedLines = this.getSelectedLinesWithHTML(range); if (selectedLines && selectedLines.length > 1) { // Multiple lines detected in current paragraph selectedLines.forEach((line) => { const li = document.createElement("li"); - li.textContent = line.trim(); + // Preserve HTML formatting and font size + li.innerHTML = line.trim(); li.contentEditable = true; list.appendChild(li); }); } else { // Default single list item const li = document.createElement("li"); - li.textContent = "List item"; + li.innerHTML = "List item"; li.contentEditable = true; list.appendChild(li); } @@ -8626,6 +9333,11 @@ ${galleryPagesHTML} items.forEach((li) => { li.style.margin = "4px 0"; li.style.paddingLeft = "4px"; + // Preserve original font size - don't override it + if (!li.style.fontSize) { + // Only set default font size if none exists + li.style.fontSize = "inherit"; + } if (!li.hasAttribute("contenteditable")) li.contentEditable = true; li.addEventListener("keydown", (e) => { if (e.key === "Enter") { @@ -12617,6 +13329,12 @@ ${galleryPagesHTML} } } + // Special handling for hero sections - always allow replacement + if (imageElement.originalElement && imageElement.originalElement.classList.contains('hero')) { + console.log("Image is in hero section - can replace"); + return true; + } + // Allow replacement of any image that's not explicitly a footer console.log("Image type not explicitly blocked - allowing replacement"); return true; // Default to allowing replacement for unknown types @@ -13888,8 +14606,8 @@ ${galleryPagesHTML} this.realPropertyImages.forEach((image, index) => { const title = image.title || image.pcrm__Title__c || `Property Image ${index + 1}`; - galleryHTML += `
+ Agent: ${this.agentData?.name || 'N/A'} | ${this.agentData?.email || 'N/A'} | ${this.agentData?.phone || 'N/A'}
+
+
+
+
+
+ Agent: ${this.agentData?.name || 'N/A'} | ${this.agentData?.email || 'N/A'} | ${this.agentData?.phone || 'N/A'}
+
+
+
+
+
+ Agent: ${this.agentData?.name || 'N/A'} | ${this.agentData?.email || 'N/A'} | ${this.agentData?.phone || 'N/A'}
+
+
+
+
+
+ Agent: ${this.agentData?.name || 'N/A'} | ${this.agentData?.email || 'N/A'} | ${this.agentData?.phone || 'N/A'}
+
+
+
+
+
+
`;
+ }
+ }
+
+ // Dynamic page addition feature
+ addNewPage() {
+ try {
+ const editor = this.template.querySelector('.enhanced-editor-content');
+ if (!editor) {
+ this.showError("Editor not found");
+ return;
+ }
+
+ // Create a new page with basic structure
+ const newPageHTML = `
+
+ Agent: ${this.agentData?.name || 'N/A'} | ${this.agentData?.email || 'N/A'} | ${this.agentData?.phone || 'N/A'}
+
+
+
+
+
+
+
+
+
+ `;
+
+ // Insert the new page at the end of the editor
+ editor.insertAdjacentHTML('beforeend', newPageHTML);
+
+ // Update the page count
+ this.updatePageCount();
+
+ // Show success message
+ this.showSuccess("New page added successfully! You can now edit the content.");
+
+ // Scroll to the new page
+ const newPage = editor.querySelector('.brochure-page:last-child');
+ if (newPage) {
+ newPage.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+
+ } catch (error) {
+ console.error("Error adding new page:", error);
+ this.showError("Failed to add new page. Please try again.");
+ }
+ }
+
+ // Add new page with data type selection
+ addNewPageWithDataType() {
+ try {
+ const editor = this.template.querySelector('.enhanced-editor-content');
+ if (!editor) {
+ this.showError("Editor not found");
+ return;
+ }
+
+ // Show data type selection modal
+ this.showDataTypeSelectionModal();
+
+ } catch (error) {
+ console.error("Error adding new page with data type:", error);
+ this.showError("Failed to add new page. Please try again.");
+ }
+ }
+
+ // Show data type selection modal
+ showDataTypeSelectionModal() {
+ this.showDataTypeModal = true;
+ }
+
+ // Close data type selection modal
+ closeDataTypeModal() {
+ this.showDataTypeModal = false;
+ this.selectedDataType = '';
+ }
+
+ // Handle data type selection
+ handleDataTypeSelection(event) {
+ const selectedType = event.currentTarget.dataset.type;
+ console.log('Selected data type:', selectedType);
+
+ // Update the reactive property
+ this.selectedDataType = selectedType;
+
+ // Force reactivity by updating the template
+ setTimeout(() => {
+ const modal = this.template.querySelector('.data-type-modal');
+ if (modal) {
+ modal.setAttribute('data-selected', selectedType);
+ }
+ }, 0);
+
+ console.log('selectedDataType after update:', this.selectedDataType);
+ console.log('selectedDataTypeDisabled:', this.selectedDataTypeDisabled);
+
+ // Remove previous selection styling
+ const allOptions = this.template.querySelectorAll('.data-type-option');
+ allOptions.forEach(option => {
+ option.style.borderColor = '#e0e0e0';
+ option.style.backgroundColor = '#fafafa';
+ });
+
+ // Add selection styling to clicked option
+ const selectedOption = event.currentTarget;
+ selectedOption.style.borderColor = '#007bff';
+ selectedOption.style.backgroundColor = '#f0f8ff';
+ }
+
+ // Add page based on selected data type
+ addPageWithSelectedDataType() {
+ if (!this.selectedDataType) {
+ this.showError("Please select a data type");
+ return;
+ }
+
+ try {
+ const editor = this.template.querySelector('.enhanced-editor-content');
+ if (!editor) {
+ this.showError("Editor not found");
+ return;
+ }
+
+ let newPageHTML = '';
+
+ switch (this.selectedDataType) {
+ case 'text':
+ newPageHTML = this.createTextPage();
+ break;
+ case 'gallery':
+ newPageHTML = this.createGalleryPage();
+ break;
+ case 'features':
+ newPageHTML = this.createFeaturesPage();
+ break;
+ case 'contact':
+ newPageHTML = this.createContactPage();
+ break;
+ case 'blank':
+ newPageHTML = this.createBlankPage();
+ break;
+ default:
+ newPageHTML = this.createBlankPage();
+ }
+
+ // Insert the new page at the end of the editor
+ editor.insertAdjacentHTML('beforeend', newPageHTML);
+
+ // Update the page count
+ this.updatePageCount();
+
+ // Close modal and show success
+ this.closeDataTypeModal();
+ this.showSuccess("New page added successfully!");
+
+ // Scroll to the new page
+ const newPage = editor.querySelector('.brochure-page:last-child');
+ if (newPage) {
+ newPage.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+
+ } catch (error) {
+ console.error("Error adding page with data type:", error);
+ this.showError("Failed to add new page. Please try again.");
+ }
+ }
+
+ // Create different page types
+ createTextPage() {
+ return `
+
+
+
+
+
+
+ Section Title
+Add your content here. This is a new page that has been dynamically added to your brochure.
+You can edit this content just like any other section in your template.
+
+
+ Additional Information
+
+
+
+
+ Feature 1
+Add your feature description here.
+
+
+ Feature 2
+Add your feature description here.
+
+ Agent: ${this.agentData?.name || 'N/A'} | ${this.agentData?.email || 'N/A'} | ${this.agentData?.phone || 'N/A'}
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ createGalleryPage() {
+ return `
+
+
+
+
+
+ Section Title
+Add your text content here. This is a text-focused page for detailed descriptions, information, or any written content.
+You can edit this content just like any other section in your template.
+
+ ${this.getTemplateSpecificFooter()}
+
+
+
+
+ `;
+ }
+
+ createFeaturesPage() {
+ return `
+
+
+
+
+
+
+
+
+
+
+ Add your images here
+
+
+
+
+
+ Add your images here
+
+
+
+
+
+ Add your images here
+
+
+
+
+
+ Add your images here
+
+
+
+
+
+ Add your images here
+
+
+
+
+
+ Add your images here
+
+ ${this.getTemplateSpecificFooter()}
+
+
+
+
+ `;
+ }
+
+ createContactPage() {
+ return `
+
+
+
+
+
+
+
+ Feature 1
+
+
+
+ Feature 2
+
+
+
+ Feature 3
+
+
+
+ Feature 4
+
+
+ ${this.getTemplateSpecificFooter()}
+
+
+
+
+ `;
+ }
+
+ createBlankPage() {
+ return `
+
+
+
+
+
+
+
+ Contact Details
+
+ Agent: ${this.agentData?.name || 'N/A'}
+
+
+ Email: ${this.agentData?.email || 'N/A'}
+
+
+ Phone: ${this.agentData?.phone || 'N/A'}
+
+
+
+ Property Information
+
+ Property: ${this.propertyData?.propertyName || 'N/A'}
+
+
+ Location: ${this.propertyData?.city || 'N/A'}
+
+
+ Type: ${this.propertyData?.propertyType || 'N/A'}
+
+
+ ${this.getTemplateSpecificFooter()}
+
+
+
+ `;
+ }
+
+ // Update page count after adding new pages
+ updatePageCount() {
+ try {
+ const editor = this.template.querySelector('.enhanced-editor-content');
+ if (!editor) return;
+
+ const pages = editor.querySelectorAll('.brochure-page, .brochure');
+ const pageCount = pages.length;
+
+ // Update any page count displays
+ const pageCountElements = this.template.querySelectorAll('.page-count, .total-pages');
+ pageCountElements.forEach(element => {
+ element.textContent = pageCount;
+ });
+
+ console.log(`Page count updated: ${pageCount} pages`);
+ } catch (error) {
+ console.error("Error updating page count:", error);
+ }
+ }
+
+ // Ensure PDF generation section remains visible
+ ensurePdfSectionVisible() {
+ try {
+ const pdfSection = this.template.querySelector('.generate-pdf-section');
+ if (pdfSection) {
+ // Ensure the PDF section is visible
+ pdfSection.style.display = 'block';
+ pdfSection.style.visibility = 'visible';
+ pdfSection.style.opacity = '1';
+
+ // Scroll to make sure it's in view
+ pdfSection.scrollIntoView({ behavior: 'smooth', block: 'end' });
+
+ console.log('PDF section visibility ensured');
+ } else {
+ console.warn('PDF generation section not found');
+ }
+ } catch (error) {
+ console.error('Error ensuring PDF section visibility:', error);
+ }
+ }
+
// Development page event handlers
handleClearData() {
this.currentStep = 1;
@@ -8438,6 +9079,69 @@ ${galleryPagesHTML}
}
}
+ // Get selected lines with HTML formatting preserved
+ getSelectedLinesWithHTML(range) {
+ try {
+ // If we have a selection range, try to get only the selected lines with HTML
+ if (range.toString().trim()) {
+ const selectedHTML = this.getSelectedHTML(range);
+ if (selectedHTML) {
+ // For multiple line selections, split by
+
+
+
+ ${this.getTemplateSpecificFooter()}
+
+ tags or newlines + const lines = selectedHTML.split(/(
|\r?\n)/i) + .filter((l) => l.trim() && !l.match(/^(
|\r?\n)$/i)) + .map(l => l.trim()); + if (lines.length > 0) { + return lines; + } + } + } + + // If no selection or single line, try to get lines from the container + const container = range.commonAncestorContainer; + let htmlContent = ""; + + // If we're in a text node, get the parent element's HTML + if (container.nodeType === Node.TEXT_NODE) { + htmlContent = container.parentElement.innerHTML || container.textContent; + } else { + htmlContent = container.innerHTML || container.textContent; + } + + // Split by
tags or newlines and filter out empty lines + const lines = htmlContent.split(/(
|\r?\n)/i) + .filter((l) => l.trim() && !l.match(/^(
|\r?\n)$/i)) + .map(l => l.trim()); + + return lines; + } catch (error) { + console.error("Error getting selected lines with HTML:", error); + return null; + } + } + + // Get selected HTML content preserving formatting + getSelectedHTML(range) { + try { + const contents = range.cloneContents(); + const div = document.createElement('div'); + div.appendChild(contents); + + // For multiple line selections, we need to handle different scenarios + const html = div.innerHTML; + + // If the selection contains multiple elements or line breaks, preserve them + if (html.includes('
{ const li = document.createElement("li"); - li.textContent = line.trim(); + // Preserve HTML formatting and font size + li.innerHTML = line.trim(); li.contentEditable = true; list.appendChild(li); }); } else { // Single line selected const li = document.createElement("li"); - li.textContent = selectedText || "List item"; + // Preserve HTML formatting and font size + li.innerHTML = selectedText || "List item"; li.contentEditable = true; list.appendChild(li); } } else { // No text selected - check if we're in a paragraph with multiple lines - const selectedLines = this.getSelectedLines(range); + const selectedLines = this.getSelectedLinesWithHTML(range); if (selectedLines && selectedLines.length > 1) { // Multiple lines detected in current paragraph selectedLines.forEach((line) => { const li = document.createElement("li"); - li.textContent = line.trim(); + // Preserve HTML formatting and font size + li.innerHTML = line.trim(); li.contentEditable = true; list.appendChild(li); }); } else { // Default single list item const li = document.createElement("li"); - li.textContent = "List item"; + li.innerHTML = "List item"; li.contentEditable = true; list.appendChild(li); } @@ -8626,6 +9333,11 @@ ${galleryPagesHTML} items.forEach((li) => { li.style.margin = "4px 0"; li.style.paddingLeft = "4px"; + // Preserve original font size - don't override it + if (!li.style.fontSize) { + // Only set default font size if none exists + li.style.fontSize = "inherit"; + } if (!li.hasAttribute("contenteditable")) li.contentEditable = true; li.addEventListener("keydown", (e) => { if (e.key === "Enter") { @@ -12617,6 +13329,12 @@ ${galleryPagesHTML} } } + // Special handling for hero sections - always allow replacement + if (imageElement.originalElement && imageElement.originalElement.classList.contains('hero')) { + console.log("Image is in hero section - can replace"); + return true; + } + // Allow replacement of any image that's not explicitly a footer console.log("Image type not explicitly blocked - allowing replacement"); return true; // Default to allowing replacement for unknown types @@ -13888,8 +14606,8 @@ ${galleryPagesHTML} this.realPropertyImages.forEach((image, index) => { const title = image.title || image.pcrm__Title__c || `Property Image ${index + 1}`; - galleryHTML += `
-
+ galleryHTML += `
+
${title}
@@ -13907,8 +14625,8 @@ ${galleryPagesHTML}
imagesSubset.forEach((image, index) => {
const title =
image.title || image.pcrm__Title__c || `Property Image ${index + 1}`;
- galleryHTML += `
-
+ galleryHTML += `
+
${title}