diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css
index 3370e35..da76ac6 100644
--- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css
+++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css
@@ -9814,25 +9814,21 @@ img[draggable="true"] {
}
/* Bullet and numbering styles */
-/* Allow bullets and numbers by default inside content */
-.enhanced-editor-content ul { list-style-type: disc; padding-left: 22px; }
-.enhanced-editor-content ol { list-style-type: decimal; padding-left: 22px; }
-
-ul li, ol li {
- margin-left: 20px;
- position: relative;
+/* Use native list markers so nested bullets and numbers render correctly */
+.enhanced-editor-content ul {
+ list-style-type: disc;
+ list-style-position: outside;
+ padding-left: 24px;
+ margin: 0 0 8px 0;
}
-
-ul li:before {
- content: "* ";
- position: absolute;
- left: -20px;
+.enhanced-editor-content ol {
+ list-style-type: decimal;
+ list-style-position: outside;
+ padding-left: 24px;
+ margin: 0 0 8px 0;
}
-
-ol li:before {
- content: "1. ";
- position: absolute;
- left: -20px;
+.enhanced-editor-content li {
+ margin: 4px 0;
}
/* Image Insertion Modal Styles */
@@ -11844,6 +11840,9 @@ ol li:before {
background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
}
+
+
+
.vertice-preview .gallery-item span { font-weight: 600; z-index: 2; font-size: 0.4rem; }
.vertice-preview .g-item-1 { grid-column: 1 / 3; grid-row: 1 / 2; background-image: url('https://images.unsplash.com/photo-1616046229478-9901c5536a45?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800'); }
.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'); }
diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html
index f03212f..196fd68 100644
--- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html
+++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html
@@ -780,11 +780,6 @@
-
-
-
diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js
index 7f62aeb..7ed9660 100644
--- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js
+++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js
@@ -1,1389 +1,1595 @@
-import { LightningElement, track, wire } from 'lwc';
-import { CurrentPageReference } from 'lightning/navigation';
-import getProperties from '@salesforce/apex/PropertyDataController.getProperties';
-import generatePDFFromHTML from '@salesforce/apex/PDFGenerationProxy.generatePDFFromHTML';
-import generateCompressedPDF from '@salesforce/apex/PDFGenerationProxy.generateCompressedPDF';
-import getPropertyImages from '@salesforce/apex/PropertyDataController.getPropertyImages';
+import { LightningElement, track, wire } from "lwc";
+import { CurrentPageReference } from "lightning/navigation";
+import getProperties from "@salesforce/apex/PropertyDataController.getProperties";
+import generatePDFFromHTML from "@salesforce/apex/PDFGenerationProxy.generatePDFFromHTML";
+import generateCompressedPDF from "@salesforce/apex/PDFGenerationProxy.generateCompressedPDF";
+import getPropertyImages from "@salesforce/apex/PropertyDataController.getPropertyImages";
export default class PropertyTemplateSelector extends LightningElement {
- @track currentStep = 1;
- htmlContent = ''; // Remove @track to prevent reactive updates
+ @track currentStep = 1;
+ htmlContent = ""; // Remove @track to prevent reactive updates
- // Lifecycle method - called when component is rendered
- renderedCallback() {
- // If we're on step 3 and have template/property selected, load the template
- if (this.currentStep === 3 && this.selectedTemplateId && this.selectedPropertyId) {
- this.loadTemplateInStep3();
- }
- };
- @track properties = [];
- @track selectedPropertyId = '';
- @track propertyData = {};
- @track isLoading = false;
- @track error = '';
- @track marketAnalysis = {
- includeMarketData: true,
- includeROIAnalysis: true,
- includeComparableSales: true,
- includeRentalYield: true,
- includeGrowthProjection: true
- };
-
- // PDF generation properties
- @track exportPdfButtonText = '📄 Generate PDF';
- @track showPdfPreview = false;
- @track editorContent = '';
- @track pageCount = 0;
- @track progressMessage = '';
- @track selectedPageSize = 'A4'; // Default page size
- @track zoom = 1.0; // Step 3 viewport zoom
- @track isGeneratingPdf = false; // Loading state for PDF generation
- @track previewPages = []; // Array of pages for viewport display
- cachedTemplateContent = null; // Cache template content to prevent regeneration
-
- // Image review properties
- @track showImageReview = false;
- @track selectedCategory = 'Interior'; // Will be updated when images load
- @track currentImageIndex = 0;
- @track totalImages = 0;
- @track currentImage = null;
-
- // Real property images from Salesforce
- @track realPropertyImages = [];
- @track propertyImages = [];
- @track imagesByCategory = {
- 'Interior': [
- { url: 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Interior View 1', category: 'Interior' },
- { url: 'https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800', title: 'Interior View 2', category: 'Interior' },
- { url: 'https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Interior View 3', category: 'Interior' }
- ],
- 'Exterior': [
- { url: 'https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Exterior View 1', category: 'Exterior' },
- { url: 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Exterior View 2', category: 'Exterior' }
- ],
- 'Kitchen': [
- { url: 'https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Kitchen View 1', category: 'Kitchen' },
- { url: 'https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Kitchen View 2', category: 'Kitchen' }
- ],
- 'Bedroom': [
- { url: 'https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Bedroom View 1', category: 'Bedroom' },
- { url: 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Bedroom View 2', category: 'Bedroom' }
- ],
- 'Living Area': [
- { url: 'https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800', title: 'Living Area View 1', category: 'Living Area' },
- { url: 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Living Area View 2', category: 'Living Area' }
- ],
- 'Parking': [
- { url: 'https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Parking View 1', category: 'Parking' }
- ],
- 'Anchor': [
- { url: 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Anchor View 1', category: 'Anchor' }
- ],
- 'Maps': [
- { url: 'https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Map View 1', category: 'Maps' }
- ]
- };
-
- // Capture URL param c__propertyId and hydrate selection
- @wire(CurrentPageReference)
- setPageRef(ref) {
- try {
- const pid = ref && ref.state && ref.state.c__propertyId;
- if (pid && pid !== this.selectedPropertyId) {
- this.selectedPropertyId = pid;
- const hydrate = () => {
- this.loadPropertyData();
- this.loadPropertyImages();
- };
- if (this.properties && this.properties.length > 0) {
- hydrate();
- } else {
- this._deferHydrateFromUrl = hydrate;
- }
- }
- } catch (e) {
- // ignore
- }
+ // Lifecycle method - called when component is rendered
+ renderedCallback() {
+ // If we're on step 3 and have template/property selected, load the template
+ if (
+ this.currentStep === 3 &&
+ this.selectedTemplateId &&
+ this.selectedPropertyId
+ ) {
+ this.loadTemplateInStep3();
}
+ }
+ @track properties = [];
+ @track selectedPropertyId = "";
+ @track propertyData = {};
+ @track isLoading = false;
+ @track error = "";
+ @track marketAnalysis = {
+ includeMarketData: true,
+ includeROIAnalysis: true,
+ includeComparableSales: true,
+ includeRentalYield: true,
+ includeGrowthProjection: true,
+ };
- // Template selection states - simplified approach
- @track selectedTemplateId = '';
+ // PDF generation properties
+ @track exportPdfButtonText = "📄 Generate PDF";
+ @track showPdfPreview = false;
+ @track editorContent = "";
+ @track pageCount = 0;
+ @track progressMessage = "";
+ @track selectedPageSize = "A4"; // Default page size
+ @track zoom = 1.0; // Step 3 viewport zoom
+ @track isGeneratingPdf = false; // Loading state for PDF generation
+ @track previewPages = []; // Array of pages for viewport display
+ cachedTemplateContent = null; // Cache template content to prevent regeneration
- // Image Replacement Variables
- @track showImageReplacement = false;
- @track selectedImageElement = null;
- @track replacementActiveTab = 'property'; // 'property' or 'upload'
- @track replacementSelectedCategory = 'Interior'; // Will be updated when images load
- @track filteredReplacementImages = [];
- @track uploadedImagePreview = null;
+ // Image review properties
+ @track showImageReview = false;
+ @track selectedCategory = "Interior"; // Will be updated when images load
+ @track currentImageIndex = 0;
+ @track totalImages = 0;
+ @track currentImage = null;
- // Triple click detection for image replacement
- @track imageClickCount = 0;
- @track lastClickedImage = null;
- @track clickTimeout = null;
+ // Real property images from Salesforce
+ @track realPropertyImages = [];
+ @track propertyImages = [];
+ @track imagesByCategory = {
+ Interior: [
+ {
+ url: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Interior View 1",
+ category: "Interior",
+ },
+ {
+ url: "https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800",
+ title: "Interior View 2",
+ category: "Interior",
+ },
+ {
+ url: "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Interior View 3",
+ category: "Interior",
+ },
+ ],
+ Exterior: [
+ {
+ url: "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Exterior View 1",
+ category: "Exterior",
+ },
+ {
+ url: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Exterior View 2",
+ category: "Exterior",
+ },
+ ],
+ Kitchen: [
+ {
+ url: "https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Kitchen View 1",
+ category: "Kitchen",
+ },
+ {
+ url: "https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Kitchen View 2",
+ category: "Kitchen",
+ },
+ ],
+ Bedroom: [
+ {
+ url: "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Bedroom View 1",
+ category: "Bedroom",
+ },
+ {
+ url: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Bedroom View 2",
+ category: "Bedroom",
+ },
+ ],
+ "Living Area": [
+ {
+ url: "https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=800",
+ title: "Living Area View 1",
+ category: "Living Area",
+ },
+ {
+ url: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Living Area View 2",
+ category: "Living Area",
+ },
+ ],
+ Parking: [
+ {
+ url: "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Parking View 1",
+ category: "Parking",
+ },
+ ],
+ Anchor: [
+ {
+ url: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Anchor View 1",
+ category: "Anchor",
+ },
+ ],
+ Maps: [
+ {
+ url: "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200",
+ title: "Map View 1",
+ category: "Maps",
+ },
+ ],
+ };
- // Undo/Redo functionality
- @track undoStack = [];
- @track redoStack = [];
- @track maxUndoSteps = 20;
-
- // Category selection tracking
- @track initialCategorySelected = false;
-
- // Template Save/Load Variables
- @track showSaveDialog = false;
- @track showLoadDialog = false;
- @track savedTemplates = [];
- @track saveTemplateName = '';
- @track showHtmlDialog = false;
- @track exportedHtml = '';
-
- // Table Dialog Variables
- @track showTableDialog = false;
- @track tableRows = 3;
- @track tableCols = 3;
- @track includeHeader = true;
-
- // Image insertion modal properties
- @track showImageModal = false;
- @track imageSource = 'property'; // 'property' or 'local'
- @track selectedImageCategory = 'all';
- @track selectedImageUrl = '';
- @track selectedImageName = '';
- @track uploadedImageData = '';
- @track renderKey = 0; // For forcing re-renders
- @track insertButtonDisabled = true; // Explicit button state
-
- // Table Drag and Drop Variables
- @track isDraggingTable = false;
- @track draggedTableData = null;
- @track selectorMode = false;
- @track showDownloadModal = false;
- @track downloadInfo = {};
- @track selectedElement = null;
- // z-index controls removed per request
-
- // Undo functionality
- @track undoStack = [];
- @track redoStack = [];
- maxUndoSteps = 50;
-
- // Computed properties for image replacement tabs
- get propertyImagesTabClass() {
- return this.replacementActiveTab === 'property' ? 'source-tab active' : 'source-tab';
+ // Capture URL param c__propertyId and hydrate selection
+ @wire(CurrentPageReference)
+ setPageRef(ref) {
+ try {
+ const pid = ref && ref.state && ref.state.c__propertyId;
+ if (pid && pid !== this.selectedPropertyId) {
+ this.selectedPropertyId = pid;
+ const hydrate = () => {
+ this.loadPropertyData();
+ this.loadPropertyImages();
+ };
+ if (this.properties && this.properties.length > 0) {
+ hydrate();
+ } else {
+ this._deferHydrateFromUrl = hydrate;
+ }
+ }
+ } catch (e) {
+ // ignore
}
+ }
- // Unified gallery section used across templates
- generateUnifiedGallerySectionHTML() {
- const imagesHTML = this.generatePropertyGalleryHTML();
- return `
+ // Template selection states - simplified approach
+ @track selectedTemplateId = "";
+
+ // Image Replacement Variables
+ @track showImageReplacement = false;
+ @track selectedImageElement = null;
+ @track replacementActiveTab = "property"; // 'property' or 'upload'
+ @track replacementSelectedCategory = "Interior"; // Will be updated when images load
+ @track filteredReplacementImages = [];
+ @track uploadedImagePreview = null;
+
+ // Triple click detection for image replacement
+ @track imageClickCount = 0;
+ @track lastClickedImage = null;
+ @track clickTimeout = null;
+
+ // Undo/Redo functionality
+ @track undoStack = [];
+ @track redoStack = [];
+ @track maxUndoSteps = 20;
+
+ // Category selection tracking
+ @track initialCategorySelected = false;
+
+ // Template Save/Load Variables
+ @track showSaveDialog = false;
+ @track showLoadDialog = false;
+ @track savedTemplates = [];
+ @track saveTemplateName = "";
+ @track showHtmlDialog = false;
+ @track exportedHtml = "";
+ // Table Dialog Variables
+ @track showTableDialog = false;
+ @track tableRows = 3;
+ @track tableCols = 3;
+ @track includeHeader = true;
+
+ // Image insertion modal properties
+ @track showImageModal = false;
+ @track imageSource = "property"; // 'property' or 'local'
+ @track selectedImageCategory = "all";
+ @track selectedImageUrl = "";
+ @track selectedImageName = "";
+ @track uploadedImageData = "";
+ @track renderKey = 0; // For forcing re-renders
+ @track insertButtonDisabled = true; // Explicit button state
+
+ // Table Drag and Drop Variables
+ @track isDraggingTable = false;
+ @track draggedTableData = null;
+ @track selectorMode = false;
+ @track showDownloadModal = false;
+ @track downloadInfo = {};
+ @track selectedElement = null;
+ // z-index controls removed per request
+
+ // Undo functionality
+ @track undoStack = [];
+ @track redoStack = [];
+ maxUndoSteps = 50;
+
+ // Computed properties for image replacement tabs
+ get propertyImagesTabClass() {
+ return this.replacementActiveTab === "property"
+ ? "source-tab active"
+ : "source-tab";
+ }
+
+ // Unified gallery section used across templates
+ generateUnifiedGallerySectionHTML() {
+ const imagesHTML = this.generatePropertyGalleryHTML();
+ return `
New page content...
", + }; + this.previewPages = [...this.previewPages, newPage]; + this.updatePageCount(); + } + + updatePageCount() { + const pageInfo = this.template?.querySelector(".page-info"); + if (pageInfo) { + const pageCount = this.previewPages.length || 1; + pageInfo.innerHTML = `${ + this.selectedPageSize + } - ${pageCount} page${pageCount > 1 ? "s" : ""}`; + } + } + + // Split content into pages based on page breaks + splitContentIntoPages(content) { + if (!content) return []; + + // Look for page break markers or split by content length + const pageBreakMarkers = [ + '', + '', + ]; + let pages = []; + + // Check if content has explicit page breaks + let hasPageBreaks = false; + pageBreakMarkers.forEach((marker) => { + if (content.includes(marker)) { + hasPageBreaks = true; + } + }); + + if (hasPageBreaks) { + // Split by page breaks + const pageContent = content.split(/Empty page
", + })); + } else { + // Single page for now - can be enhanced to auto-split based on content length + pages = [ + { + id: "page-1", + content: content, + }, + ]; } - // Page size change handler - handlePageSizeChange(event) { - const newPageSize = event.target.value; - this.selectedPageSize = newPageSize; - - // Update the preview frame with new dimensions - this.updatePreviewFrameSize(newPageSize); + return pages; + } - // Re-fit to width when page size changes - setTimeout(() => this.fitToWidth(), 0); + // Update preview pages when content changes + updatePreviewPages() { + if (this.htmlContent) { + this.previewPages = this.splitContentIntoPages(this.htmlContent); + } else { + this.previewPages = []; } + this.updatePageCount(); + } - // ===== Step 3 viewport zoom controls ===== - get zoomPercent() { - try { return `${Math.round((this.zoom || 1) * 100)}%`; } catch (e) { return '100%'; } + fitToWidth() { + const container = this.template?.querySelector(".pdf-viewport"); + if (!container) { + return; } + const baseWidth = this.selectedPageSize === "A3" ? 1123 : 794; + const available = Math.max((container.clientWidth || baseWidth) - 32, 100); + this.zoom = Math.max(Math.min(available / baseWidth, 4), 0.3); + } - get pdfCanvasStyle() { - const scale = this.zoom || 1; - return `transform: scale(${scale}) !important;`; + fitToPage() { + const container = this.template?.querySelector(".pdf-viewport"); + if (!container) { + return; } + const baseWidth = this.selectedPageSize === "A3" ? 1123 : 794; + const baseHeight = this.selectedPageSize === "A3" ? 1587 : 1123; + const availableW = Math.max((container.clientWidth || baseWidth) - 32, 100); + const availableH = Math.max( + (container.clientHeight || baseHeight) - 32, + 100 + ); + const scaleW = availableW / baseWidth; + const scaleH = availableH / baseHeight; + this.zoom = Math.max(Math.min(Math.min(scaleW, scaleH), 4), 0.3); + } + // Update preview frame size based on selected page size + updatePreviewFrameSize(pageSize) { + const previewFrame = this.template.querySelector( + ".enhanced-editor-content" + ); + if (previewFrame) { + // Update the data attribute for the CSS content + previewFrame.setAttribute("data-page-size", pageSize); - zoomIn() { this.zoom = Math.min((this.zoom || 1) + 0.1, 3); } - zoomOut() { this.zoom = Math.max((this.zoom || 1) - 0.1, 0.3); } - resetZoom() { this.zoom = 1; } - - // Handler methods for HTML onclick events - handleZoomIn() { this.zoomIn(); } - handleZoomOut() { this.zoomOut(); } - handleZoom100() { this.resetZoom(); } - handleFitWidth() { this.fitToWidth(); } - handleFitPage() { this.fitToPage(); } - - // Page management methods - addNewPage() { - const newPage = { - id: `page-${Date.now()}`, - content: 'New page content...
' - }; - this.previewPages = [...this.previewPages, newPage]; - this.updatePageCount(); - } - - updatePageCount() { - const pageInfo = this.template?.querySelector('.page-info'); - if (pageInfo) { - const pageCount = this.previewPages.length || 1; - pageInfo.innerHTML = `${this.selectedPageSize} - ${pageCount} page${pageCount > 1 ? 's' : ''}`; - } - } - - // Split content into pages based on page breaks - splitContentIntoPages(content) { - if (!content) return []; - - // Look for page break markers or split by content length - const pageBreakMarkers = ['', '']; - let pages = []; - - // Check if content has explicit page breaks - let hasPageBreaks = false; - pageBreakMarkers.forEach(marker => { - if (content.includes(marker)) { - hasPageBreaks = true; - } - }); - - if (hasPageBreaks) { - // Split by page breaks - const pageContent = content.split(/Empty page
' - })); - } else { - // Single page for now - can be enhanced to auto-split based on content length - pages = [{ - id: 'page-1', - content: content - }]; - } - - return pages; - } - - // Update preview pages when content changes - updatePreviewPages() { - if (this.htmlContent) { - this.previewPages = this.splitContentIntoPages(this.htmlContent); - } else { - this.previewPages = []; - } - this.updatePageCount(); + // Re-render content with new page size + this.renderContentInPages(pageSize); + + // Update page count based on new page size + this.updatePageCountForSize(pageSize); } + } - fitToWidth() { - const container = this.template?.querySelector('.pdf-viewport'); - if (!container) { return; } - const baseWidth = this.selectedPageSize === 'A3' ? 1123 : 794; - const available = Math.max((container.clientWidth || baseWidth) - 32, 100); - this.zoom = Math.max(Math.min(available / baseWidth, 4), 0.3); - } + // Render content in separate pages based on selected size + renderContentInPages(pageSize) { + const previewFrame = this.template.querySelector( + ".enhanced-editor-content" + ); + if (!previewFrame) return; - fitToPage() { - const container = this.template?.querySelector('.pdf-viewport'); - if (!container) { return; } - const baseWidth = this.selectedPageSize === 'A3' ? 1123 : 794; - const baseHeight = this.selectedPageSize === 'A3' ? 1587 : 1123; - const availableW = Math.max((container.clientWidth || baseWidth) - 32, 100); - const availableH = Math.max((container.clientHeight || baseHeight) - 32, 100); - const scaleW = availableW / baseWidth; - const scaleH = availableH / baseHeight; - this.zoom = Math.max(Math.min(Math.min(scaleW, scaleH), 4), 0.3); - } - // Update preview frame size based on selected page size - updatePreviewFrameSize(pageSize) { - const previewFrame = this.template.querySelector('.enhanced-editor-content'); - if (previewFrame) { - // Update the data attribute for the CSS content - previewFrame.setAttribute('data-page-size', pageSize); - - - // Re-render content with new page size - this.renderContentInPages(pageSize); - - // Update page count based on new page size - this.updatePageCountForSize(pageSize); - } - } + // Get the current template content + const templateHTML = this.createTemplateHTML(); + if (!templateHTML) return; - // Render content in separate pages based on selected size - renderContentInPages(pageSize) { - const previewFrame = this.template.querySelector('.enhanced-editor-content'); - if (!previewFrame) return; + // Clear existing content + previewFrame.innerHTML = ""; - // Get the current template content - const templateHTML = this.createTemplateHTML(); - if (!templateHTML) return; + // Split content into pages based on size + const pages = this.splitContentIntoPages(templateHTML, pageSize); - // Clear existing content - previewFrame.innerHTML = ''; + // Create page containers + pages.forEach((pageContent, index) => { + const pageContainer = document.createElement("div"); + pageContainer.className = `preview-page page-size-${pageSize.toLowerCase()}`; + pageContainer.setAttribute("data-page-number", `Page ${index + 1}`); + pageContainer.innerHTML = pageContent; + previewFrame.appendChild(pageContainer); + }); + } + // Split HTML content into pages based on page size + splitContentIntoPages(htmlContent, pageSize) { + const pages = []; + let currentPage = ""; + let currentHeight = 0; - // Split content into pages based on size - const pages = this.splitContentIntoPages(templateHTML, pageSize); - - // Create page containers - pages.forEach((pageContent, index) => { - const pageContainer = document.createElement('div'); - pageContainer.className = `preview-page page-size-${pageSize.toLowerCase()}`; - pageContainer.setAttribute('data-page-number', `Page ${index + 1}`); - pageContainer.innerHTML = pageContent; - previewFrame.appendChild(pageContainer); - }); + // Define page heights in mm + const pageHeights = { + A4: 297, + A3: 420, + }; - } + const maxHeight = pageHeights[pageSize] || 297; - // Split HTML content into pages based on page size - splitContentIntoPages(htmlContent, pageSize) { - const pages = []; - let currentPage = ''; - let currentHeight = 0; - - // Define page heights in mm - const pageHeights = { - 'A4': 297, - 'A3': 420 - }; - - const maxHeight = pageHeights[pageSize] || 297; - - // Create a temporary div to parse HTML - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = htmlContent; - - // Get all direct children (brochure pages) - const children = Array.from(tempDiv.children); - - children.forEach((child, index) => { - // If it's a brochure-page div, treat it as a separate page - if (child.classList.contains('brochure-page')) { - if (currentPage) { - pages.push(currentPage); - } - currentPage = child.outerHTML; - currentHeight = 0; - } else { - // For other content, check if it fits in current page - const estimatedHeight = this.estimateElementHeight(child, pageSize); - - if (currentHeight + estimatedHeight > maxHeight && currentPage) { - // Start new page - pages.push(currentPage); - currentPage = child.outerHTML; - currentHeight = estimatedHeight; - } else { - // Add to current page - currentPage += child.outerHTML; - currentHeight += estimatedHeight; - } - } - }); - - // Add the last page + // Create a temporary div to parse HTML + const tempDiv = document.createElement("div"); + tempDiv.innerHTML = htmlContent; + + // Get all direct children (brochure pages) + const children = Array.from(tempDiv.children); + + children.forEach((child, index) => { + // If it's a brochure-page div, treat it as a separate page + if (child.classList.contains("brochure-page")) { if (currentPage) { - pages.push(currentPage); + pages.push(currentPage); } - - return pages; + currentPage = child.outerHTML; + currentHeight = 0; + } else { + // For other content, check if it fits in current page + const estimatedHeight = this.estimateElementHeight(child, pageSize); + + if (currentHeight + estimatedHeight > maxHeight && currentPage) { + // Start new page + pages.push(currentPage); + currentPage = child.outerHTML; + currentHeight = estimatedHeight; + } else { + // Add to current page + currentPage += child.outerHTML; + currentHeight += estimatedHeight; + } + } + }); + + // Add the last page + if (currentPage) { + pages.push(currentPage); } - // Estimate element height based on page size - estimateElementHeight(element, pageSize) { - // Base height estimation in mm - let baseHeight = 50; // Default height - - // Adjust based on element type - if (element.tagName === 'IMG') { - baseHeight = 100; - } else if (element.tagName === 'H1') { - baseHeight = 30; - } else if (element.tagName === 'H2') { - baseHeight = 25; - } else if (element.tagName === 'P') { - baseHeight = 20; - } else if (element.tagName === 'DIV') { - baseHeight = 80; - } - - // Adjust for page size - if (pageSize === 'A3') { - baseHeight *= 1.4; // A3 is larger - } - - return baseHeight; + return pages; + } + + // Estimate element height based on page size + estimateElementHeight(element, pageSize) { + // Base height estimation in mm + let baseHeight = 50; // Default height + + // Adjust based on element type + if (element.tagName === "IMG") { + baseHeight = 100; + } else if (element.tagName === "H1") { + baseHeight = 30; + } else if (element.tagName === "H2") { + baseHeight = 25; + } else if (element.tagName === "P") { + baseHeight = 20; + } else if (element.tagName === "DIV") { + baseHeight = 80; } - // Update page count based on selected page size - updatePageCountForSize(pageSize) { - const previewFrame = this.template.querySelector('.enhanced-editor-content'); - if (previewFrame) { - let pageHeight = 297; // Default A4 height in mm - - switch (pageSize) { - case 'A3': - pageHeight = 420; // A3 height in mm - break; - case 'A4': - default: - pageHeight = 297; // A4 height in mm - break; - } - - // Calculate content height and estimate pages needed - const contentHeight = previewFrame.scrollHeight; - const contentHeightMm = (contentHeight * 0.264583); // Convert px to mm - const pagesNeeded = Math.ceil(contentHeightMm / pageHeight); - - this.pageCount = Math.max(1, Math.min(pagesNeeded, 20)); // Limit to 1-20 pages - } + // Adjust for page size + if (pageSize === "A3") { + baseHeight *= 1.4; // A3 is larger } - // Navigation methods - nextStep() { - if (this.currentStep === 1 && !this.selectedTemplateId) { - this.showError('Please select a template first.'); - return; - } - if (this.currentStep < 3) { - this.currentStep++; - // Reset click tracking when changing steps - this.resetImageClickTracking(); - // If moving to step 3, automatically load the template - if (this.currentStep === 3) { - this.loadTemplateInStep3(); - requestAnimationFrame(() => { - this.updatePreviewFrameSize(this.selectedPageSize || 'A4'); - }); - } - this.scrollToTop(); - } - } + return baseHeight; + } - previousStep() { - if (this.currentStep > 1) { - this.currentStep--; - // Reset click tracking when changing steps - this.resetImageClickTracking(); - if (this.currentStep === 1) { - this.resetStep1Grid(); - } - this.scrollToTop(); - } - } - goToStep(event) { - const step = parseInt(event.currentTarget.dataset.step); - this.currentStep = step; - // Reset click tracking when changing steps - this.resetImageClickTracking(); - if (this.currentStep === 1) { - this.resetStep1Grid(); - // Also reconstruct grid HTML from original snapshot if available to fully reset content - if (this.originalTemplateGridHTML && this.originalTemplateGridHTML.length > 50) { - const grid = this.template.querySelector('#all-templates'); - if (grid) { - grid.innerHTML = this.originalTemplateGridHTML; - } - } - // Restore any styles that could have been mutated during step 3 - this.restoreComponentStyles(); - // Rebind click handlers for fresh grid without page reload - requestAnimationFrame(() => { - const cards = this.template.querySelectorAll('#all-templates .template-card'); - cards.forEach(card => { - card.onclick = this.handleTemplateSelect.bind(this); - // Restore selected state if this card matches the selected template - if (this.selectedTemplateId && card.dataset.templateId === this.selectedTemplateId) { - card.classList.add('selected'); - } - }); - }); - } - // If going directly to step 3, load the template only if not already loaded - if (this.currentStep === 3 && (!this.htmlContent || this.htmlContent.trim() === '')) { - this.loadTemplateInStep3(); - requestAnimationFrame(() => { - this.updatePreviewFrameSize(this.selectedPageSize || 'A4'); - // Auto fit width for better initial experience - this.fitToWidth && this.fitToWidth(); - }); - } - this.scrollToTop(); - } + // Update page count based on selected page size + updatePageCountForSize(pageSize) { + const previewFrame = this.template.querySelector( + ".enhanced-editor-content" + ); + if (previewFrame) { + let pageHeight = 297; // Default A4 height in mm - // Scroll to top of page when changing steps - scrollToTop() { - window.scrollTo({ - top: 0, - behavior: 'smooth' + switch (pageSize) { + case "A3": + pageHeight = 420; // A3 height in mm + break; + case "A4": + default: + pageHeight = 297; // A4 height in mm + break; + } + + // Calculate content height and estimate pages needed + const contentHeight = previewFrame.scrollHeight; + const contentHeightMm = contentHeight * 0.264583; // Convert px to mm + const pagesNeeded = Math.ceil(contentHeightMm / pageHeight); + + this.pageCount = Math.max(1, Math.min(pagesNeeded, 20)); // Limit to 1-20 pages + } + } + + // Navigation methods + nextStep() { + if (this.currentStep === 1 && !this.selectedTemplateId) { + this.showError("Please select a template first."); + return; + } + if (this.currentStep < 3) { + this.currentStep++; + // Reset click tracking when changing steps + this.resetImageClickTracking(); + // If moving to step 3, automatically load the template + if (this.currentStep === 3) { + this.loadTemplateInStep3(); + requestAnimationFrame(() => { + this.updatePreviewFrameSize(this.selectedPageSize || "A4"); }); + } + this.scrollToTop(); } + } - // Load template content into step 3 enhanced editor - async loadTemplateInStep3() { - if (this.selectedTemplateId && this.selectedPropertyId) { - try { - // Use cached content if available to prevent regeneration - if (this.cachedTemplateContent && this.cachedTemplateContent.templateId === this.selectedTemplateId && this.cachedTemplateContent.propertyId === this.selectedPropertyId) { - this.htmlContent = this.cachedTemplateContent.html; - this.updatePreviewPages(); - this.updatePreviewFrameSize(this.selectedPageSize); - setTimeout(() => { - this.updatePageCountForSize(this.selectedPageSize); - this.updatePageCount(); - this.fitToWidth && this.fitToWidth(); - }, 100); - return; - } - - // Ensure property images are loaded before creating template - if (this.realPropertyImages.length === 0) { - await this.loadPropertyImages(); - } - - const templateHTML = this.createTemplateHTML(); - - // Replace any hardcoded background-image URLs with property images - const processedTemplateHTML = this.replaceBackgroundImagesInHTML(templateHTML); - - // Cache the template content - this.cachedTemplateContent = { - templateId: this.selectedTemplateId, - propertyId: this.selectedPropertyId, - html: processedTemplateHTML - }; - - // Set the HTML content for the template binding - this.htmlContent = processedTemplateHTML; - - // Update preview pages with the new content - this.updatePreviewPages(); - - // Set initial page size class and data attribute - this.updatePreviewFrameSize(this.selectedPageSize); - - // Update page count after template is loaded - setTimeout(() => { - this.updatePageCountForSize(this.selectedPageSize); - this.updatePageCount(); // Update page count display - // After content settles, fit viewport to width - this.fitToWidth && this.fitToWidth(); - }, 100); - } catch (error) { - this.error = 'Error loading template: ' + error.message; - - // Show error message in preview frame - const previewFrame = this.template.querySelector('.enhanced-editor-content'); - if (previewFrame) { - previewFrame.innerHTML = `${error.message}
Please try selecting a different template or contact support.
Please go back to step 1 and select a template.
Please go back to step 2 and select a property.
Please go back to step 1 and select a template.
Please go back to step 2 and select a property.