image replace stable

This commit is contained in:
rohit 2025-09-15 17:17:41 +05:30
parent c978c48fb6
commit 0c721ef7ed
2 changed files with 18821 additions and 278 deletions

File diff suppressed because one or more lines are too long

View File

@ -488,7 +488,7 @@ export default class PropertyTemplateSelector extends LightningElement {
}
get isModernHomeTemplateSelected() {
return this.selectedTemplateId === "modern-home-template";
return this.selectedTemplateId === "modern-home-template" || this.selectedTemplateId === "modern-home-a3-template";
}
get isGrandOakVillaTemplateSelected() {
@ -1317,6 +1317,11 @@ export default class PropertyTemplateSelector extends LightningElement {
// Force proper HTML rendering after content changes
setTimeout(() => {
this.forceHTMLRendering();
// Add pencil icon to hero section for modern home template
if (this.selectedTemplateId === 'modern-home-template' || this.selectedTemplateId === 'modern-home-a3-template') {
this.addPencilIconToHeroSection();
}
}, 100);
}
@ -1685,6 +1690,11 @@ export default class PropertyTemplateSelector extends LightningElement {
this.forceImageReload();
// Force proper HTML rendering to match PDF exactly
this.forceHTMLRendering();
// Add pencil icon to hero section for modern home template
if (this.selectedTemplateId === 'modern-home-template' || this.selectedTemplateId === 'modern-home-a3-template') {
this.addPencilIconToHeroSection();
}
}, 100);
return;
}
@ -1751,6 +1761,11 @@ export default class PropertyTemplateSelector extends LightningElement {
this.forceImageReload();
// Force proper HTML rendering to match PDF exactly
this.forceHTMLRendering();
// Add pencil icon to hero section for modern home template
if (this.selectedTemplateId === 'modern-home-template' || this.selectedTemplateId === 'modern-home-a3-template') {
this.addPencilIconToHeroSection();
}
}, 100);
} catch (error) {
this.error = "Error loading template: " + error.message;
@ -5042,6 +5057,44 @@ export default class PropertyTemplateSelector extends LightningElement {
justify-content: flex-end;
overflow: hidden;
}
/* Pencil icon styles - only visible in editor */
.hero-edit-icon {
position: absolute;
top: 20px;
right: 20px;
z-index: 1000;
background: rgba(0,0,0,0.7);
color: white;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
opacity: 0;
visibility: hidden;
}
.hero-edit-icon:hover {
background: rgba(0,0,0,0.9);
transform: scale(1.1);
}
/* Show pencil icon only in editor mode */
.enhanced-editor-content .hero-edit-icon {
opacity: 1;
visibility: visible;
}
/* Hide pencil icon in PDF generation */
@media print {
.hero-edit-icon {
display: none !important;
}
}
.hero-overlay {
background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.1) 100%);
padding: 40px;
@ -8137,34 +8190,7 @@ ${galleryPagesHTML}
</div>
</div>
</div>
<div class="floorplan-showcase">
<div class="floorplan-image-p5 penthouse"></div>
<div class="floorplan-info-p5">
<h4>${propertyName}</h4>
<div class="floorplan-stats-p5">
<div class="stat">
<div class="value">${this.propertyData.size
}</div>
<div class="label">SQ. FT.</div>
</div>
<div class="stat">
<div class="value">${this.propertyData.bedrooms
}</div>
<div class="label">BEDROOMS</div>
</div>
<div class="stat">
<div class="value">${this.propertyData.bathrooms
}</div>
<div class="label">BATHROOMS</div>
</div>
<div class="stat">
<div class="value">${this.propertyData.floor
}</div>
<div class="label">Floors</div>
</div>
</div>
</div>
</div>
<div>
<h3 class="section-title">Additional Information</h3>
<div class="additional-specs-grid">
@ -12894,266 +12920,130 @@ ${galleryPagesHTML}
editorContent.dispatchEvent(new Event("input", { bubbles: true }));
}
// NEW: Coordinate-based image detection with visual feedback
detectImageAtCoordinates(clickX, clickY, editor) {
console.log("=== NEW COORDINATE-BASED IMAGE DETECTION ===");
console.log("Click coordinates:", { x: clickX, y: clickY });
let clickedImage = null;
let bestMatch = null;
let bestScore = -1;
// Clear any existing visual feedback
this.clearImageSelectionFeedback();
// Method 1: Find all images and background images in the editor
const allImages = [];
// Get all IMG elements
const imgElements = editor.querySelectorAll("img");
imgElements.forEach(img => {
if (img.src && img.src.trim() !== "") {
allImages.push({
element: img,
type: 'img',
rect: img.getBoundingClientRect(),
src: img.src
});
}
});
// Get all elements with background images
const allElements = editor.querySelectorAll("*");
allElements.forEach(el => {
const computedStyle = window.getComputedStyle(el);
const backgroundImage = computedStyle.backgroundImage;
if (backgroundImage && backgroundImage !== "none" && backgroundImage !== "initial") {
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) { // Only consider visible elements
const virtualImg = document.createElement("img");
virtualImg.src = backgroundImage.replace(/url\(['"]?(.+?)['"]?\)/, "$1");
virtualImg.isBackgroundImage = true;
virtualImg.originalElement = el;
allImages.push({
element: virtualImg,
type: 'background',
rect: rect,
src: virtualImg.src,
originalElement: el
});
}
}
});
console.log(`Found ${allImages.length} images to check`);
// Method 2: Score each image based on click proximity and element importance
allImages.forEach((imageData, index) => {
const rect = imageData.rect;
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
// Calculate distance from click point to image center
const distance = Math.sqrt(
Math.pow(clickX - centerX, 2) + Math.pow(clickY - centerY, 2)
);
// Calculate if click is within image bounds
const isWithinBounds = (
clickX >= rect.left &&
clickX <= rect.right &&
clickY >= rect.top &&
clickY <= rect.bottom
);
// Calculate image area (larger images get higher priority)
const area = rect.width * rect.height;
// Calculate importance score based on element classes and position
let importanceScore = 0;
const originalElement = imageData.originalElement || imageData.element;
if (originalElement) {
const className = originalElement.className || '';
// Hero section images get highest priority
if (className.includes('hero') || className.includes('p1-image-side') ||
className.includes('p2-image') || className.includes('cover-page') ||
className.includes('banner')) {
importanceScore += 1000;
}
// Content images get medium priority
if (className.includes('content') || className.includes('main') ||
className.includes('section') || className.includes('card') ||
className.includes('property') || className.includes('image')) {
importanceScore += 500;
}
// Footer images get negative priority (should be avoided)
if (className.includes('footer') || className.includes('page-footer') ||
className.includes('p1-footer') || className.includes('agent-footer') ||
className.includes('company-logo') || className.includes('footer-logo')) {
importanceScore -= 1000;
}
}
// Calculate final score
let score = 0;
if (isWithinBounds) {
// Click is within image bounds - high priority
score = 1000 + importanceScore + (area / 1000); // Larger images get slight bonus
} else {
// Click is outside image bounds - lower priority based on distance
score = Math.max(0, 1000 - distance + importanceScore + (area / 1000));
}
console.log(`Image ${index}:`, {
type: imageData.type,
src: imageData.src.substring(0, 50) + '...',
className: originalElement?.className || 'N/A',
isWithinBounds,
distance: Math.round(distance),
area: Math.round(area),
importanceScore,
score: Math.round(score)
});
if (score > bestScore) {
bestScore = score;
bestMatch = imageData;
}
});
// Method 3: If we have a good match, use it
if (bestMatch && bestScore > 0) {
clickedImage = bestMatch.element;
console.log("✅ BEST MATCH FOUND:", {
type: bestMatch.type,
src: bestMatch.src.substring(0, 50) + '...',
score: Math.round(bestScore),
isWithinBounds: (
clickX >= bestMatch.rect.left &&
clickX <= bestMatch.rect.right &&
clickY >= bestMatch.rect.top &&
clickY <= bestMatch.rect.bottom
)
});
// Add visual feedback to show which image is selected
this.showImageSelectionFeedback(bestMatch.originalElement || bestMatch.element);
} else {
console.log("❌ NO SUITABLE IMAGE FOUND");
}
return clickedImage;
}
// Add visual feedback to show which image is selected
showImageSelectionFeedback(element) {
if (!element) return;
// Remove any existing feedback
this.clearImageSelectionFeedback();
// Add highlight to the selected element
element.style.outline = "3px solid #007bff";
element.style.outlineOffset = "2px";
element.style.boxShadow = "0 0 10px rgba(0, 123, 255, 0.5)";
// Store reference for cleanup
this.selectedElementForFeedback = element;
console.log("Visual feedback added to selected image");
}
// Clear visual feedback
clearImageSelectionFeedback() {
if (this.selectedElementForFeedback) {
this.selectedElementForFeedback.style.outline = "";
this.selectedElementForFeedback.style.outlineOffset = "";
this.selectedElementForFeedback.style.boxShadow = "";
this.selectedElementForFeedback = null;
}
}
// Setup editor click handler to deselect elements
setupEditorClickHandler() {
const editor = this.template.querySelector(".enhanced-editor-content");
if (editor && !editor.hasClickHandler) {
editor.addEventListener("click", (e) => {
console.log("=== NEW COORDINATE-BASED CLICK HANDLER ===");
console.log("=== CLICK EVENT DETECTED ===");
console.log("Click target:", e.target);
console.log("Click coordinates:", { x: e.clientX, y: e.clientY });
console.log("Target element:", e.target);
console.log("Target details:", {
tagName: e.target.tagName,
className: e.target.className,
id: e.target.id,
src: e.target.src
});
// Prevent default behavior
e.preventDefault();
e.stopPropagation();
// Enhanced image detection - check multiple ways to find images
let clickedImage = null;
try {
// Use the new coordinate-based image detection
const clickedImage = this.detectImageAtCoordinates(e.clientX, e.clientY, editor);
// Method 1: Direct image click
console.log("=== METHOD 1: Direct image click ===");
if (
e.target.tagName === "IMG" &&
e.target.src &&
e.target.src.trim() !== ""
) {
clickedImage = e.target;
console.log("✅ Method 1 SUCCESS: Direct IMG click detected", clickedImage);
} else {
console.log("❌ Method 1 FAILED: Not a direct IMG click");
}
// Method 2: Click on element containing an image (children)
console.log("=== METHOD 2: Element containing image ===");
if (!clickedImage && e.target.querySelector) {
const containedImg = e.target.querySelector("img");
if (
containedImg &&
containedImg.src &&
containedImg.src.trim() !== ""
) {
clickedImage = containedImg;
console.log("✅ Method 2 SUCCESS: Container with IMG detected", clickedImage);
} else {
console.log("❌ Method 2 FAILED: No IMG in container");
}
} else {
console.log("❌ Method 2 SKIPPED: No querySelector or already found image");
}
// Method 3: Click on element that is inside a container with an image (parent traversal)
console.log("=== METHOD 3: Parent traversal ===");
if (!clickedImage) {
console.log("❌ NO IMAGE FOUND AT CLICK LOCATION");
this.showError("No image found at the clicked location. Please click directly on an image.");
return;
}
console.log("✅ IMAGE FOUND:", clickedImage);
console.log("Image details:", {
tagName: clickedImage.tagName,
src: clickedImage.src,
className: clickedImage.className,
isBackgroundImage: clickedImage.isBackgroundImage,
originalElement: clickedImage.originalElement
let currentElement = e.target;
let traversalCount = 0;
while (currentElement && currentElement !== editor && traversalCount < 10) {
traversalCount++;
console.log(`Traversal step ${traversalCount}:`, {
tagName: currentElement.tagName,
className: currentElement.className,
id: currentElement.id
});
// Store the selected image element
this.selectedImageElement = clickedImage;
// Check if this is the same image as the last click
const isSameImage = this.isSameImageAsLast(clickedImage);
if (isSameImage) {
this.imageClickCount++;
console.log("Same image clicked, count:", this.imageClickCount);
// Check if current element is an IMG
if (
currentElement.tagName === "IMG" &&
currentElement.src &&
currentElement.src.trim() !== ""
) {
clickedImage = currentElement;
console.log("✅ Method 3 SUCCESS: Found IMG in parent traversal", clickedImage);
break;
}
// Check if current element contains an IMG
if (
currentElement.querySelector &&
currentElement.querySelector("img")
) {
const img = currentElement.querySelector("img");
if (img && img.src && img.src.trim() !== "") {
clickedImage = img;
console.log("✅ Method 3 SUCCESS: Found IMG in container during traversal", clickedImage);
break;
}
}
// Check siblings for IMG elements only if current element is positioned
if (
currentElement.parentElement &&
(currentElement.style.position === "absolute" ||
currentElement.style.position === "relative" ||
currentElement.classList.contains("draggable-element"))
) {
const siblingImg =
currentElement.parentElement.querySelector("img");
if (
siblingImg &&
siblingImg.src &&
siblingImg.src.trim() !== ""
) {
clickedImage = siblingImg;
break;
}
}
currentElement = currentElement.parentElement;
}
if (!clickedImage) {
console.log("❌ Method 3 FAILED: No IMG found in parent traversal");
}
} else {
this.imageClickCount = 1;
this.lastClickedImage = clickedImage;
console.log("Different image clicked, reset count to 1");
console.log("❌ Method 3 SKIPPED: Already found image");
}
// Set timeout to reset counter
this.clickTimeout = setTimeout(() => {
this.imageClickCount = 0;
this.lastClickedImage = null;
this.clearImageSelectionFeedback();
console.log("Click counter reset");
}, 1000);
// Method 4: Check for background images in the element hierarchy (enhanced for property cards)
console.log("=== METHOD 4: Background image detection ===");
if (!clickedImage) {
let currentElement = e.target;
let heroBackgroundImage = null;
let otherBackgroundImage = null;
let clickedElementBackgroundImage = null;
// Check if image can be replaced
if (this.canReplaceImage(clickedImage)) {
console.log("Image can be replaced, opening replacement dialog");
this.openImageReplacement(clickedImage);
} else {
console.log("Image cannot be replaced, showing error");
this.showError("This image cannot be replaced. Please try clicking on a different image.");
}
} catch (error) {
console.error("Error in image detection:", error);
this.showError("An error occurred while processing the image click.");
}
});
// First, check the clicked element and its immediate children for background images
console.log("Checking clicked element and immediate children first...");
const elementsToCheck = [currentElement];
editor.hasClickHandler = true;
}
}
addDeselectFunctionality() {
// Add immediate children that might have background images
if (currentElement.children) {
for (let child of currentElement.children) {
elementsToCheck.push(child);
}
@ -15637,6 +15527,12 @@ ${galleryPagesHTML}
console.log("Clicked image:", clickedImage);
console.log("Event:", event);
// Disable 3-click functionality for modern home template
if (this.isModernHomeTemplateSelected) {
console.log("❌ 3-click functionality disabled for modern home template");
return;
}
// Prevent replacement if file dialog is open
if (this.isFileDialogOpen) {
console.log("❌ File dialog open, ignoring image click");
@ -17010,6 +16906,180 @@ ${galleryPagesHTML}
return 'https://via.placeholder.com/400x300?text=No+Image+Available';
}
// Method to add pencil icon to hero section and second page image for image replacement
addPencilIconToHeroSection() {
console.log('addPencilIconToHeroSection called for template:', this.selectedTemplateId);
// Wait for the template to be loaded in the editor content
setTimeout(() => {
console.log('Looking for enhanced-editor-content...');
const editorContent = this.template.querySelector('.enhanced-editor-content');
if (editorContent) {
console.log('Enhanced editor content found');
// Add CSS styles for the pencil icon if not already added
if (!document.querySelector('#hero-edit-icon-styles')) {
console.log('Adding CSS styles for pencil icon');
const style = document.createElement('style');
style.id = 'hero-edit-icon-styles';
style.textContent = `
.hero-edit-icon, .visual-header-edit-icon {
position: absolute;
top: 20px;
right: 20px;
z-index: 1000;
background: rgba(0,0,0,0.7);
color: white;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
opacity: 1;
visibility: visible;
}
.hero-edit-icon:hover, .visual-header-edit-icon:hover {
background: rgba(0,0,0,0.9);
transform: scale(1.1);
}
/* Hide pencil icon in PDF generation */
@media print {
.hero-edit-icon, .visual-header-edit-icon {
display: none !important;
}
}
`;
document.head.appendChild(style);
}
// Add pencil icon to hero section (page 1)
console.log('Looking for hero section...');
const heroSection = editorContent.querySelector('.hero');
if (heroSection) {
console.log('Hero section found');
if (!heroSection.querySelector('.hero-edit-icon')) {
console.log('Creating pencil icon for hero section...');
// Create pencil icon element
const pencilIcon = document.createElement('div');
pencilIcon.className = 'hero-edit-icon';
pencilIcon.innerHTML = '<i class="fa-solid fa-pencil" style="font-size: 16px;"></i>';
pencilIcon.title = 'Replace Hero Image';
pencilIcon.onclick = () => {
console.log('Hero pencil icon clicked');
// Trigger image replacement for hero section
this.handleHeroImageReplacement();
};
// Add the pencil icon to the hero section
heroSection.appendChild(pencilIcon);
console.log('Pencil icon added to hero section successfully');
} else {
console.log('Hero section pencil icon already exists');
}
} else {
console.log('Hero section not found in editor content');
}
// Add pencil icon to visual header (page 2)
console.log('Looking for visual header...');
const visualHeader = editorContent.querySelector('.visual-header');
if (visualHeader) {
console.log('Visual header found');
if (!visualHeader.querySelector('.visual-header-edit-icon')) {
console.log('Creating pencil icon for visual header...');
// Create pencil icon element
const pencilIcon = document.createElement('div');
pencilIcon.className = 'visual-header-edit-icon';
pencilIcon.innerHTML = '<i class="fa-solid fa-pencil" style="font-size: 16px;"></i>';
pencilIcon.title = 'Replace Page 2 Image';
pencilIcon.onclick = () => {
console.log('Visual header pencil icon clicked');
// Trigger image replacement for visual header
this.handleVisualHeaderImageReplacement();
};
// Add the pencil icon to the visual header
visualHeader.appendChild(pencilIcon);
console.log('Pencil icon added to visual header successfully');
} else {
console.log('Visual header pencil icon already exists');
}
} else {
console.log('Visual header not found in editor content');
}
} else {
console.log('Enhanced editor content not found');
}
}, 1500); // Increased timeout to ensure content is loaded
}
// Handle hero image replacement
handleHeroImageReplacement() {
// Find the hero section background image
const editorContent = this.template.querySelector('.enhanced-editor-content');
if (editorContent) {
const heroSection = editorContent.querySelector('.hero');
if (heroSection) {
// Get the computed background image
const computedStyle = window.getComputedStyle(heroSection);
const backgroundImage = computedStyle.backgroundImage;
if (backgroundImage && backgroundImage !== 'none') {
// Create a virtual image element for the hero background
const virtualImg = document.createElement('img');
virtualImg.src = backgroundImage.replace(/url\(['"]?(.+?)['"]?\)/, '$1');
virtualImg.isBackgroundImage = true;
virtualImg.originalElement = heroSection;
// Open image replacement for this virtual image
this.openImageReplacement(virtualImg);
} else {
console.log('No background image found on hero section');
}
} else {
console.log('Hero section not found');
}
} else {
console.log('Enhanced editor content not found');
}
}
// Handle visual header image replacement (page 2)
handleVisualHeaderImageReplacement() {
// Find the visual header background image
const editorContent = this.template.querySelector('.enhanced-editor-content');
if (editorContent) {
const visualHeader = editorContent.querySelector('.visual-header');
if (visualHeader) {
// Get the computed background image
const computedStyle = window.getComputedStyle(visualHeader);
const backgroundImage = computedStyle.backgroundImage;
if (backgroundImage && backgroundImage !== 'none') {
// Create a virtual image element for the visual header background
const virtualImg = document.createElement('img');
virtualImg.src = backgroundImage.replace(/url\(['"]?(.+?)['"]?\)/, '$1');
virtualImg.isBackgroundImage = true;
virtualImg.originalElement = visualHeader;
// Open image replacement for this virtual image
this.openImageReplacement(virtualImg);
} else {
console.log('No background image found on visual header');
}
} else {
console.log('Visual header not found');
}
} else {
console.log('Enhanced editor content not found');
}
}
// Method to replace background-image URLs in CSS at runtime
replaceBackgroundImagesInHTML(htmlContent) {
if (!this.realPropertyImages || this.realPropertyImages.length === 0) {