-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{currentImage.title}
+
{currentImage.category}
+
+
+
+
+
No images found for {selectedCategory}
+
+
+
+
+
+
+
+
+ {currentImageIndex} of {totalImages}
+
+
+
diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js
index a22fa6e..52e2a83 100644
--- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js
+++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js
@@ -1,3060 +1,901 @@
import { LightningElement, track, wire } from 'lwc';
-import { getRecord } from 'lightning/uiRecordApi';
import getProperties from '@salesforce/apex/PropertyDataController.getProperties';
-import getPropertyDetails from '@salesforce/apex/PropertyDataController.getPropertyDetails';
-import getPropertyCount from '@salesforce/apex/PropertyDataController.getPropertyCount';
-import generatePreview from '@salesforce/apex/PdfApiController.generatePreview';
-import generatePdf from '@salesforce/apex/PdfApiController.generatePdf';
-import generatePdfServerSide from '@salesforce/apex/PdfApiController.generatePdfServerSide';
-import callPythonApi from '@salesforce/apex/PropertyPdfGeneratorController.callPythonApi';
+import generatePDFFromHTML from '@salesforce/apex/PDFGenerationProxy.generatePDFFromHTML';
+import getPropertyImages from '@salesforce/apex/PropertyDataController.getPropertyImages';
export default class PropertyTemplateSelector extends LightningElement {
@track currentStep = 1;
+
+ // 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 selectedTemplate = '';
- @track selectedTemplateData = {};
- @track propertyData = {
- propertyName: '',
- propertyType: '',
- location: '',
- price: '',
- bedrooms: '',
- bathrooms: '',
- area: '',
- titleEnglish: '',
- description: '',
- images: []
- };
- @track marketAnalysis = {
- includeMarketData: false,
- includeROIAnalysis: false,
- includeComparableSales: false,
- includeRentalYield: false,
- includeGrowthProjection: false
- };
- @track editorContent = '';
+ @track propertyData = {};
@track isLoading = false;
- @track error;
-
- // Editor state properties
- @track currentFontFamily = 'Arial, sans-serif';
- @track currentFontSize = '16px';
- @track currentFontWeight = 'normal';
- @track currentTextAlign = 'left';
- @track currentTextColor = '#000000';
-
- // Available fonts and sizes
- @track availableFonts = [
- 'Arial, sans-serif',
- 'Helvetica, sans-serif',
- 'Times New Roman, serif',
- 'Georgia, serif',
- 'Verdana, sans-serif',
- 'Tahoma, sans-serif',
- 'Trebuchet MS, sans-serif',
- 'Impact, sans-serif',
- 'Comic Sans MS, cursive'
- ];
-
- @track availableFontSizes = [
- '8px', '10px', '12px', '14px', '16px', '18px', '20px', '24px', '28px', '32px', '36px', '48px', '72px'
- ];
-
- @track availableColors = [
- '#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF',
- '#FFA500', '#800080', '#008000', '#FFC0CB', '#A52A2A', '#808080', '#000080', '#800000'
- ];
+ @track error = '';
+ @track marketAnalysis = {
+ includeMarketData: true,
+ includeROIAnalysis: true,
+ includeComparableSales: true,
+ includeRentalYield: true,
+ includeGrowthProjection: true
+ };
- // High-standard property templates with actual content and different heights
- @track templates = [
- {
- Id: 'blank-template',
- Name: 'Blank Template',
- Description: 'Start from scratch with complete creative freedom.',
- Category: 'Custom',
- Style: 'minimalist',
- Preview: 'β«',
- Fields: ['custom'],
- Height: 'medium'
- },
- {
- Id: 'everkind-template',
- Name: 'Luxury Villa',
- Description: 'Premium property presentation with elegant typography and sophisticated layout.',
- Category: 'Luxury',
- Style: 'elegant',
- Preview: 'π°',
- Fields: ['all', 'market'],
- Height: 'tall'
- },
- {
- Id: 'shift-template',
- Name: 'Modern Apartments',
- Description: 'Contemporary apartment showcase with modern design elements.',
- Category: 'Modern',
- Style: 'contemporary',
- Preview: 'π’',
- Fields: ['all', 'amenities'],
- Height: 'tall'
- },
- {
- Id: 'saintbarts-template',
- Name: 'Beachfront Properties',
- Description: 'Luxury beachfront property presentation with ocean views.',
- Category: 'Beachfront',
- Style: 'luxury',
- Preview: 'π',
- Fields: ['all', 'views'],
- Height: 'tall'
- },
- {
- Id: 'learnoy-template',
- Name: 'Investment Properties',
- Description: 'Data-driven template with ROI analysis and market insights.',
- Category: 'Investment',
- Style: 'analytical',
- Preview: 'π°',
- Fields: ['all', 'market', 'analysis'],
- Height: 'medium'
- },
- {
- Id: 'leafamp-template',
- Name: 'Urban Properties',
- Description: 'City living showcase with urban lifestyle focus.',
- Category: 'Urban',
- Style: 'urban',
- Preview: 'ποΈ',
- Fields: ['all', 'lifestyle'],
- Height: 'medium'
- },
- {
- Id: 'coreshift-template',
- Name: 'Commercial Properties',
- Description: 'Professional commercial property presentation.',
- Category: 'Commercial',
- Style: 'professional',
- Preview: 'π’',
- Fields: ['all', 'business'],
- Height: 'medium'
- }
- ];
+ // PDF generation properties
+ @track exportPdfButtonText = 'π Generate PDF';
+ @track showPdfPreview = false;
+ @track editorContent = '';
+ @track pageCount = 0;
+ @track progressMessage = '';
+ @track selectedPageSize = 'A4'; // Default page size
- // Wire service to fetch properties
+ // Image review properties
+ @track showImageReview = false;
+ @track selectedCategory = 'Interior';
+ @track currentImageIndex = 0;
+ @track totalImages = 0;
+ @track currentImage = null;
+
+ // Real property images from Salesforce
+ @track realPropertyImages = [];
+ @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' }
+ ]
+ };
+
+ // Template selection states - simplified approach
+ @track selectedTemplateId = '';
+
+ // Computed properties for template selection
+ get isBlankTemplateSelected() {
+ return this.selectedTemplateId === 'blank-template';
+ }
+
+ get isEverkindTemplateSelected() {
+ return this.selectedTemplateId === 'everkind-template';
+ }
+
+ get isShiftTemplateSelected() {
+ return this.selectedTemplateId === 'shift-template';
+ }
+
+ get isSaintbartsTemplateSelected() {
+ return this.selectedTemplateId === 'saintbarts-template';
+ }
+
+ get isLearnoyTemplateSelected() {
+ return this.selectedTemplateId === 'learnoy-template';
+ }
+
+ get isLeafampTemplateSelected() {
+ return this.selectedTemplateId === 'leafamp-template';
+ }
+
+ get isCoreshiftTemplateSelected() {
+ return this.selectedTemplateId === 'coreshift-template';
+ }
+
+ get isModernHomeTemplateSelected() {
+ return this.selectedTemplateId === 'modern-home-template';
+ }
+
+ get isAsgar1TemplateSelected() {
+ return this.selectedTemplateId === 'asgar-1-template';
+ }
+
+ get isSampleTemplateSelected() {
+ return this.selectedTemplateId === 'sample-template';
+ }
+
+ get isSerenityHouseTemplateSelected() {
+ return this.selectedTemplateId === 'serenity-house-template';
+ }
+
+ get isLuxuryMansionTemplateSelected() {
+ return this.selectedTemplateId === 'luxury-mansion-template';
+ }
+
+ // Image review computed properties
+ get isFirstImage() {
+ return this.currentImageIndex === 0;
+ }
+
+ get isLastImage() {
+ return this.currentImageIndex === this.totalImages - 1;
+ }
+
+ get displayImageIndex() {
+ return this.currentImageIndex + 1;
+ }
+
+ // Computed properties for step visibility
+ get step1Class() {
+ return this.currentStep === 1 ? 'step-content active' : 'step-content';
+ }
+
+ get step2Class() {
+ return this.currentStep === 2 ? 'step-content active' : 'step-content';
+ }
+
+ get step3Class() {
+ return this.currentStep === 3 ? 'step-content active' : 'step-content';
+ }
+
+ // Step navigation classes
+ get step1NavClass() {
+ return this.currentStep === 1 ? 'step-nav-item active' : 'step-nav-item';
+ }
+
+ get step2NavClass() {
+ return this.currentStep === 2 ? 'step-nav-item active' : 'step-nav-item';
+ }
+
+ get step3NavClass() {
+ return this.currentStep === 3 ? 'step-nav-item active' : 'step-nav-item';
+ }
+
+ get isNextButtonDisabled() {
+ return !this.selectedTemplateId;
+ }
+
+ get isNextButtonDisabledStep2() {
+ return !this.selectedPropertyId;
+ }
+
+ // Wire method to get properties
@wire(getProperties)
wiredProperties({ error, data }) {
- console.log('=== WIRE SERVICE CALLED ===');
- console.log('Wire service called with:', { error, data });
-
if (data) {
- console.log('=== RAW DATA RECEIVED ===');
- console.log('Data type:', typeof data);
- console.log('Data length:', data ? data.length : 'null/undefined');
- console.log('Is array:', Array.isArray(data));
- console.log('Data keys:', data ? Object.keys(data) : 'null');
-
- // Check if data is valid
- if (Array.isArray(data) && data.length > 0) {
this.properties = data;
- this.error = undefined;
- console.log(`=== PROPERTIES LOADED ===`);
- console.log(`Loaded ${this.properties.length} properties from Salesforce:`, this.properties);
-
- // Log each property in detail
- this.properties.forEach((property, index) => {
- console.log(`Property ${index + 1}:`, {
- Id: property.Id,
- Name: property.Name,
- PropertyType: property.pcrm__Property_Type__c,
- Location: property.pcrm__City_Bayut_Dubizzle__c,
- Status: property.pcrm__Status__c,
- Bedrooms: property.pcrm__Bedrooms__c,
- Bathrooms: property.pcrm__Bathrooms__c,
- Area: property.pcrm__Size__c,
- Price: property.pcrm__Sale_Price_min__c,
- TitleEnglish: property.pcrm__Title_English__c,
- Description: property.pcrm__Description_English__c
- });
- });
-
- // Force a re-render by updating a tracked property
- this.properties = [...this.properties];
- console.log('Properties array updated, length:', this.properties.length);
-
-
-
- } else {
- console.error('Data is not a valid array or is empty:', data);
- this.properties = [];
- }
-
} else if (error) {
- this.error = error;
- console.error('=== ERROR LOADING PROPERTIES ===');
- console.error('Error loading properties:', error);
- } else {
- console.log('=== NO DATA AND NO ERROR ===');
- console.log('No data and no error from wire service');
+ this.error = 'Error loading properties: ' + error.body.message;
}
}
- // Manually load properties if wire service fails
- manuallyLoadProperties() {
- console.log('Manually loading properties...');
- getProperties()
- .then(result => {
- if (result) {
- this.properties = result;
- console.log(`Manually loaded ${this.properties.length} properties:`, this.properties);
- } else {
- console.error('No properties returned from manual load');
- }
- })
- .catch(error => {
- console.error('Error manually loading properties:', error);
- });
- }
-
- // Lifecycle method
- connectedCallback() {
- console.log('=== CONNECTED CALLBACK TRIGGERED ===');
- console.log('Component connected, current step:', this.currentStep);
- console.log('Component properties before reset:', this.properties);
-
- this.resetStepState();
- console.log('Component properties after reset:', this.properties);
-
- // Load properties immediately
- console.log('About to call loadProperties...');
- this.loadProperties();
- console.log('loadProperties called, properties array:', this.properties);
-
-
- }
-
- // Additional lifecycle method for debugging
- renderedCallback() {
- console.log('=== RENDERED CALLBACK TRIGGERED ===');
- console.log('Component rendered, current step:', this.currentStep);
- console.log('Properties array in renderedCallback:', this.properties);
- console.log('Properties length in renderedCallback:', this.properties.length);
-
- // Add a small delay and check again
- setTimeout(() => {
- console.log('=== DELAYED CHECK IN RENDERED CALLBACK ===');
- console.log('Properties after delay:', this.properties);
- console.log('Properties length after delay:', this.properties.length);
- }, 1000);
- }
-
-
-
- // Load properties with error handling
- loadProperties() {
- console.log('=== LOADING PROPERTIES STARTED ===');
- console.log('Component state before loading:', {
- currentStep: this.currentStep,
- properties: this.properties,
- isLoading: this.isLoading
- });
-
- // Don't reload if properties are already loaded
- if (this.properties && this.properties.length > 0) {
- console.log('Properties already loaded, skipping reload');
- return;
- }
-
- this.isLoading = true;
-
- // Test if the import is working
- console.log('PropertyDataController import test:', {
- getProperties: typeof getProperties,
- getPropertyDetails: typeof getPropertyDetails,
- getPropertyCount: typeof getPropertyCount
- });
-
- getProperties()
- .then(result => {
- console.log('=== PROPERTIES LOADED SUCCESSFULLY ===');
- console.log('Raw result:', result);
- console.log('Result type:', typeof result);
- console.log('Result length:', result ? result.length : 'null/undefined');
-
- if (result && result.length > 0) {
- this.properties = result;
- console.log(`Successfully loaded ${this.properties.length} properties:`, this.properties);
-
- // Log first few properties for debugging
- if (this.properties.length > 0) {
- console.log('First property:', this.properties[0]);
- console.log('Property fields available:', Object.keys(this.properties[0]));
- console.log('Property Name field:', this.properties[0].Name);
- console.log('Property Type field:', this.properties[0].pcrm__Property_Type__c);
- console.log('Property City field:', this.properties[0].pcrm__City_Bayut_Dubizzle__c);
- }
-
- // Force a re-render
- this.properties = [...this.properties];
- } else {
- console.error('No properties returned from load');
- console.log('Result was falsy or empty:', result);
- this.properties = [];
- }
- })
- .catch(error => {
- console.error('=== ERROR LOADING PROPERTIES ===');
- console.error('Error details:', error);
- console.error('Error message:', error.message);
- console.error('Error body:', error.body);
- console.error('Error stack:', error.stack);
- this.showError('Failed to load properties: ' + (error.message || error.body?.message || 'Unknown error'));
- this.properties = [];
- })
- .finally(() => {
- console.log('=== PROPERTIES LOADING COMPLETED ===');
- console.log('Final properties array:', this.properties);
- console.log('Properties length:', this.properties.length);
- this.isLoading = false;
- });
- }
-
- // Reset step state
- resetStepState() {
- this.currentStep = 1;
- this.selectedTemplate = '';
- this.selectedTemplateData = null;
- this.selectedPropertyId = '';
- this.clearPropertyData();
- this.resetMarketAnalysis();
- this.editorContent = '';
- this.showHeader = false;
- }
-
- // Reset market analysis options
- resetMarketAnalysis() {
- this.marketAnalysis = {
- includeMarketData: false,
- includeROIAnalysis: false,
- includeComparableSales: false,
- includeRentalYield: false,
- includeGrowthProjection: false
- };
- }
-
- // Clear property data
- clearPropertyData() {
- this.propertyData = {
- propertyName: '',
- propertyType: '',
- location: '',
- price: '',
- bedrooms: '',
- bathrooms: '',
- area: '',
- titleEnglish: '',
- description: '',
- images: []
- };
- }
-
- // Handle template selection
+ // Template selection handler
handleTemplateSelect(event) {
const templateId = event.currentTarget.dataset.templateId;
- console.log('Template selected:', templateId);
+ console.log('=== TEMPLATE SELECTION DEBUG ===');
+ console.log('Selected template ID:', templateId);
+ console.log('Event target:', event.currentTarget);
+ console.log('Dataset:', event.currentTarget.dataset);
+ console.log('BEFORE - selectedTemplateId:', this.selectedTemplateId);
- if (templateId) {
- this.selectedTemplate = templateId;
-
- // Find the selected template from the templates array
- const selectedTemplate = this.templates.find(template => template.Id === templateId);
-
- if (selectedTemplate) {
- this.selectedTemplateData = selectedTemplate;
- console.log('Selected template data:', this.selectedTemplateData);
-
- // Show success message
- this.showSuccess(`Template "${selectedTemplate.Name}" selected successfully!`);
- } else {
- console.error('Selected template not found in templates array');
- this.showError('Selected template not found');
- }
- } else {
- this.selectedTemplate = '';
- this.selectedTemplateData = null;
- }
- }
-
- // Handle property selection
- handlePropertySelection(event) {
- const selectedPropertyId = event.target.value;
- console.log('=== PROPERTY SELECTION ===');
- console.log('Selected Property ID:', selectedPropertyId);
-
- // Set the selected property ID
- this.selectedPropertyId = selectedPropertyId;
-
- if (selectedPropertyId) {
- // Find the selected property from the properties array
- const selectedProperty = this.properties.find(prop => prop.Id === selectedPropertyId);
-
- if (selectedProperty) {
- console.log('=== SELECTED PROPERTY DATA ===');
- console.log('Full property object:', selectedProperty);
-
- // Populate propertyData with actual fields from pcrm__Property__c
- this.propertyData = {
- id: selectedProperty.Id,
- name: selectedProperty.Name,
- propertyName: selectedProperty.Name,
-
- // Property Type and Status
- propertyType: selectedProperty.pcrm__Property_Type__c || 'Not specified',
- status: selectedProperty.pcrm__Status__c || 'Not specified',
-
- // Basic Details
- bedrooms: selectedProperty.pcrm__Bedrooms__c || 'Not specified',
- bathrooms: selectedProperty.pcrm__Bathrooms__c || 'Not specified',
- area: selectedProperty.pcrm__Size__c ? `${selectedProperty.pcrm__Size__c} sq ft` : 'Not specified',
-
- // Pricing
- salePriceMin: selectedProperty.pcrm__Sale_Price_min__c || 'Not specified',
- salePriceMax: selectedProperty.pcrm__Sale_Price_max__c || 'Not specified',
- rentPriceMin: selectedProperty.pcrm__Rent_Price_min__c || 'Not specified',
- rentPriceMax: selectedProperty.pcrm__Rent_Price_max__c || 'Not specified',
-
- // Generate price display
- price: this.generatePriceDisplay(selectedProperty),
-
- // Location Details
- city: selectedProperty.pcrm__City_Bayut_Dubizzle__c || 'Not specified',
- community: selectedProperty.pcrm__Community_Propertyfinder__c || 'Not specified',
- locality: selectedProperty.pcrm__Locality_Bayut_Dubizzle__c || 'Not specified',
- subLocality: selectedProperty.pcrm__Sub_Locality_Bayut_Dubizzle__c || 'Not specified',
- tower: selectedProperty.pcrm__Tower_Bayut_Dubizzle__c || 'Not specified',
- unitNumber: selectedProperty.pcrm__Unit_Number__c || 'Not specified',
-
- // Generate location display
- location: this.generateLocationDisplay(selectedProperty),
-
- // Description and Title
- description: selectedProperty.pcrm__Description_English__c || 'No description available',
- title: selectedProperty.pcrm__Title_English__c || selectedProperty.Name || 'Not specified',
-
- // Additional Details - Use actual values if available
- furnished: selectedProperty.pcrm__Furnished__c || 'Not specified',
- floor: selectedProperty.pcrm__Floor__c || 'Not specified',
- buildYear: selectedProperty.pcrm__Build_Year__c || 'Not specified',
- parkingSpaces: selectedProperty.pcrm__Parking_Spaces__c || 'Not specified',
- offeringType: selectedProperty.pcrm__Offering_Type__c || 'Not specified',
- view: 'Not specified', // This field was removed from Apex
- developer: 'Not specified', // This field was removed from Apex
-
- // Amenities
- privateAmenities: 'Not specified', // This field was removed from Apex
-
- // Reference
- referenceNumber: selectedProperty.Name,
- externalId: 'Not specified', // This field was removed from Apex
-
- // Timestamps
- createdDate: selectedProperty.CreatedDate || 'Not specified',
- lastModifiedDate: selectedProperty.LastModifiedDate || 'Not specified',
- createdBy: 'System',
- lastModifiedBy: 'System'
- };
-
- console.log('=== POPULATED PROPERTY DATA ===');
- console.log('Property Data object:', this.propertyData);
- console.log('Property Name:', this.propertyData.propertyName);
- console.log('Property Type:', this.propertyData.propertyType);
- console.log('Location:', this.propertyData.location);
- console.log('Price:', this.propertyData.price);
- console.log('Bedrooms:', this.propertyData.bedrooms);
- console.log('Bathrooms:', this.propertyData.bathrooms);
- console.log('Area:', this.propertyData.area);
- console.log('Description:', this.propertyData.description);
- console.log('Status:', this.propertyData.status);
- console.log('Reference Number:', this.propertyData.referenceNumber);
- console.log('City:', this.propertyData.city);
- console.log('Community:', this.propertyData.community);
-
- this.showSuccess(`Property "${this.propertyData.propertyName}" selected successfully!`);
-
- // Show property preview
- this.showPropertyPreview();
-
- } else {
- console.error('Selected property not found in properties array');
- this.showError('Selected property not found. Please try again.');
- }
- } else {
- this.propertyData = null;
- this.selectedPropertyId = '';
- console.log('No property selected');
- }
- }
-
- // Generate price display based on available pricing data
- generatePriceDisplay(property) {
- if (property.pcrm__Sale_Price_min__c && property.pcrm__Sale_Price_max__c) {
- return `AED ${this.formatNumber(property.pcrm__Sale_Price_min__c)} - ${this.formatNumber(property.pcrm__Sale_Price_max__c)} (Sale)`;
- } else if (property.pcrm__Rent_Price_min__c && property.pcrm__Rent_Price_max__c) {
- return `AED ${this.formatNumber(property.pcrm__Rent_Price_min__c)} - ${this.formatNumber(property.pcrm__Rent_Price_max__c)} (Rent)`;
- } else if (property.pcrm__Sale_Price_min__c) {
- return `AED ${this.formatNumber(property.pcrm__Sale_Price_min__c)} (Sale)`;
- } else if (property.pcrm__Rent_Price_min__c) {
- return `AED ${this.formatNumber(property.pcrm__Rent_Price_min__c)} (Rent)`;
- } else {
- return 'Price on Application';
- }
- }
-
- // Generate location display based on available location data
- generateLocationDisplay(property) {
- const parts = [];
-
- // Use all available location fields
- if (property.pcrm__Unit_Number__c) parts.push(`Unit ${property.pcrm__Unit_Number__c}`);
- if (property.pcrm__Tower_Bayut_Dubizzle__c) parts.push(`Tower ${property.pcrm__Tower_Bayut_Dubizzle__c}`);
- if (property.pcrm__Sub_Locality_Bayut_Dubizzle__c) parts.push(property.pcrm__Sub_Locality_Bayut_Dubizzle__c);
- if (property.pcrm__Locality_Bayut_Dubizzle__c) parts.push(property.pcrm__Locality_Bayut_Dubizzle__c);
- if (property.pcrm__Community_Propertyfinder__c) parts.push(property.pcrm__Community_Propertyfinder__c);
- if (property.pcrm__City_Bayut_Dubizzle__c) parts.push(property.pcrm__City_Bayut_Dubizzle__c);
-
- return parts.length > 0 ? parts.join(', ') : 'Dubai';
- }
-
- // Format number with commas
- formatNumber(num) {
- if (num == null) return '0';
- return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
- }
-
- // Market analysis checkbox handling
- handleMarketAnalysisChange(event) {
- const field = event.target.name;
- this.marketAnalysis[field] = event.target.checked;
- }
-
- // Generate template content
- generateTemplateContent() {
- console.log('=== generateTemplateContent called ===');
- console.log('selectedTemplateData:', this.selectedTemplateData);
- console.log('selectedPropertyId:', this.selectedPropertyId);
- console.log('propertyData:', this.propertyData);
-
- if (!this.selectedTemplateData) {
- this.showError('Please select a template first.');
- return;
- }
-
- if (!this.selectedPropertyId) {
- this.showError('Please select a property first.');
- return;
- }
-
- if (!this.propertyData || !this.propertyData.propertyName) {
- this.showError('Property data not loaded. Please try selecting the property again.');
- return;
- }
-
- this.isLoading = true;
-
- // Generate simple HTML content for template preview
- this.editorContent = this.createSimpleTemplateHTML();
-
- console.log('Generated editor content length:', this.editorContent.length);
-
- // Move to step 3 (editor) and show header
- this.currentStep = 3;
- this.showHeader = true;
- this.isLoading = false;
-
- // Initialize the editor after content is set and DOM is updated
- setTimeout(() => {
- this.initializeEditor();
- }, 100);
- }
-
- // Create simple template HTML for fast Step 2
- createSimpleTemplateHTML() {
- console.log('=== CREATING SIMPLE TEMPLATE HTML ===');
-
- if (!this.propertyData) {
- return '
No Property Data
Please select a property first.
';
- }
-
- // Simple template with basic styling
- return `
-
-
${this.propertyData.propertyName || 'Property Brochure'}
-
${this.propertyData.propertyType || 'Property Type'} in ${this.propertyData.location || 'Location'}
-
-
-
-
-
Price
-
${this.propertyData.price || 'N/A'}
-
-
-
Bedrooms
-
${this.propertyData.bedrooms || 'N/A'}
-
-
-
Bathrooms
-
${this.propertyData.bathrooms || 'N/A'}
-
-
-
Area
-
${this.propertyData.area || 'N/A'}
-
-
-
-
-
Property Details
-
-
Type: ${this.propertyData.propertyType || 'N/A'}
-
Location: ${this.propertyData.location || 'N/A'}
-
Price: ${this.propertyData.price || 'N/A'}
-
Bedrooms: ${this.propertyData.bedrooms || 'N/A'}
-
Bathrooms: ${this.propertyData.bathrooms || 'N/A'}
-
Area: ${this.propertyData.area || 'N/A'}
-
-
-
-
-
Template generated successfully! You can now edit this content and export as PDF.
-
- `;
- }
-
- // Initialize the rich text editor
- initializeEditor() {
- console.log('=== initializeEditor called ===');
- console.log('Selected template data:', this.selectedTemplateData);
- console.log('Property data:', this.propertyData);
-
- if (!this.selectedTemplateData || !this.propertyData) {
- console.error('Missing template or property data for editor initialization');
- this.showError('Please select a template and property first');
- return;
- }
-
- // Use the already generated content from Step 2
- console.log('Using existing editor content:', this.editorContent);
-
- // Initialize pages for multi-page editing
- this.initializePages();
-
- // Set up editor events
- setTimeout(() => {
- this.setupEditorEvents();
- console.log('Editor initialized successfully');
- this.showSuccess('Editor loaded with your selected template and property data!');
- }, 100);
- }
-
- setupEditorEvents() {
- console.log('=== setupEditorEvents called ===');
-
- // Get the editor frame
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) {
- console.error('Editor frame not found');
- return;
- }
-
- // Make the editor frame content editable
- editorFrame.contentEditable = true;
- editorFrame.style.cssText = `
- min-height: 600px;
- padding: 30px;
- border: 2px solid #e0e0e0;
- border-radius: 12px;
- background: white;
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- line-height: 1.6;
- color: #333;
- outline: none;
- box-shadow: 0 4px 20px rgba(0,0,0,0.1);
- `;
-
- // Set the content
- editorFrame.innerHTML = this.editorContent;
-
- // Add event listeners
- editorFrame.addEventListener('keydown', this.handleKeyboardShortcuts.bind(this));
- editorFrame.addEventListener('input', this.handleTextSelection.bind(this));
- editorFrame.addEventListener('dragover', this.handleDragOver.bind(this));
- editorFrame.addEventListener('drop', this.handleImageDrop.bind(this));
-
- console.log('Editor events set up successfully');
- }
-
- handleEditorInput(event) {
- // Handle editor content changes
- this.editorContent = event.target.innerHTML;
- }
-
- handlePaste(event) {
- // Clean up pasted content
- event.preventDefault();
- const text = event.clipboardData.getData('text/plain');
- document.execCommand('insertText', false, text);
- }
-
- makeElementsDraggable(editor) {
- const draggableElements = editor.querySelectorAll('[draggable="true"]');
- draggableElements.forEach(element => {
- this.makeElementDraggable(element);
- });
- }
-
- // Handle text selection
- handleTextSelection() {
- const selection = window.getSelection();
- if (selection.rangeCount > 0) {
- this.updateToolbarState();
- }
- }
-
- // Update toolbar state based on selection
- updateToolbarState() {
- const selection = window.getSelection();
- if (selection.rangeCount > 0) {
- const range = selection.getRangeAt(0);
- const container = range.commonAncestorContainer;
-
- // Update font family
- if (container.nodeType === Node.TEXT_NODE) {
- const parent = container.parentElement;
- if (parent) {
- this.currentFontFamily = getComputedStyle(parent).fontFamily;
- this.currentFontSize = getComputedStyle(parent).fontSize;
- this.currentFontWeight = getComputedStyle(parent).fontWeight;
- this.currentTextAlign = getComputedStyle(parent).textAlign;
- this.currentTextColor = getComputedStyle(parent).color;
- }
- }
- }
- }
-
- // Handle drag over for images
- handleDragOver(event) {
- event.preventDefault();
- event.dataTransfer.dropEffect = 'copy';
- }
-
- // Handle image drop
- handleImageDrop(event) {
- event.preventDefault();
-
- const files = event.dataTransfer.files;
- if (files.length > 0) {
- const file = files[0];
- if (file.type.startsWith('image/')) {
- this.insertImageFromFile(file);
- }
- }
- }
-
- // Insert image from file
- insertImageFromFile(file) {
- const reader = new FileReader();
- reader.onload = (e) => {
- const img = document.createElement('img');
- img.src = e.target.result;
- img.style.maxWidth = '100%';
- img.style.height = 'auto';
- img.style.margin = '10px 0';
- img.style.borderRadius = '8px';
- img.style.boxShadow = '0 2px 10px rgba(0,0,0,0.1)';
- img.draggable = true;
-
- // Insert at cursor position or append
- const editorFrame = this.template.querySelector('.preview-frame');
- if (window.getSelection && window.getSelection().rangeCount > 0) {
- const selection = window.getSelection();
- const range = selection.getRangeAt(0);
- range.insertNode(img);
- } else {
- editorFrame.appendChild(img);
- }
-
- this.showSuccess('Image inserted successfully!');
- };
- reader.readAsDataURL(file);
- }
-
- // Insert image placeholder
- insertImage() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- // Create a file input element
- const fileInput = document.createElement('input');
- fileInput.type = 'file';
- fileInput.accept = 'image/*';
- fileInput.style.display = 'none';
-
- fileInput.onchange = (event) => {
- const file = event.target.files[0];
- if (file) {
- this.insertImageFromFile(file);
- }
- };
-
- // Trigger file selection
- document.body.appendChild(fileInput);
- fileInput.click();
- document.body.removeChild(fileInput);
- }
-
- // Make image draggable
- makeImageDraggable(img) {
- let isDragging = false;
- let currentX;
- let currentY;
- let initialX;
- let initialY;
- let xOffset = 0;
- let yOffset = 0;
-
- img.addEventListener('mousedown', (e) => {
- initialX = e.clientX - xOffset;
- initialY = e.clientY - yOffset;
-
- if (e.target === img) {
- isDragging = true;
- }
- });
-
- document.addEventListener('mousemove', (e) => {
- if (isDragging) {
- e.preventDefault();
- currentX = e.clientX - initialX;
- currentY = e.clientY - initialY;
- xOffset = currentX;
- yOffset = currentY;
-
- img.style.transform = `translate(${currentX}px, ${currentY}px)`;
- }
- });
-
- document.addEventListener('mouseup', () => {
- initialX = currentX;
- initialY = currentY;
- isDragging = false;
- });
- }
-
- // Handle keyboard shortcuts
- handleKeyboardShortcuts(event) {
- if (event.ctrlKey || event.metaKey) {
- switch (event.key) {
- case 'b':
- event.preventDefault();
- this.execCommand('bold');
+ // Set the selected template
+ switch (templateId) {
+ case 'blank-template':
+ this.selectedTemplateId = 'blank-template';
+ console.log('Set selectedTemplateId to blank-template');
break;
- case 'i':
- event.preventDefault();
- this.execCommand('italic');
- break;
- case 'u':
- event.preventDefault();
- this.execCommand('underline');
- break;
- case 'z':
- if (event.shiftKey) {
- event.preventDefault();
- this.execCommand('redo');
- } else {
- event.preventDefault();
- this.execCommand('undo');
- }
- break;
- }
- }
- }
-
- // Execute editor commands
- execCommand(command, value = null) {
- document.execCommand(command, false, value);
- this.updateToolbarState();
- }
-
- // Text formatting methods
- setFontFamily(fontFamily) {
- this.execCommand('fontName', fontFamily);
- }
-
- setFontSize(size) {
- this.execCommand('fontSize', size);
- }
-
- setBold() {
- this.execCommand('bold');
- }
-
- setItalic() {
- this.execCommand('italic');
- }
-
- setUnderline() {
- this.execCommand('underline');
- }
-
- setTextAlign(align) {
- this.execCommand('justify' + align.charAt(0).toUpperCase() + align.slice(1));
- }
-
- setTextColor(color) {
- this.execCommand('foreColor', color);
- }
-
- setBackgroundColor(color) {
- this.execCommand('hiliteColor', color);
- }
-
- // Text alignment methods
- setTextAlignLeft() {
- this.setTextAlign('left');
- }
-
- setTextAlignCenter() {
- this.setTextAlign('center');
- }
-
- setTextAlignRight() {
- this.setTextAlign('right');
- }
-
- setTextAlignJustify() {
- this.setTextAlign('justify');
- }
-
- // List insertion methods
- insertUnorderedList() {
- this.insertList('unordered');
- }
-
- insertOrderedList() {
- this.insertList('ordered');
- }
-
- // Shape addition methods
- addLineShape() {
- const line = document.createElement('hr');
- line.style.cssText = `
- border: none;
- height: 3px;
- background: linear-gradient(90deg, #667eea, #764ba2);
- margin: 20px 0;
- border-radius: 2px;
- cursor: move;
- `;
- line.draggable = true;
- this.insertElement(line);
- }
-
- // Add rectangle shape
- addRectangleShape() {
- const rect = document.createElement('div');
- rect.style.cssText = `
- width: 100px;
- height: 60px;
- background: linear-gradient(135deg, #667eea, #764ba2);
- border-radius: 8px;
- margin: 20px 0;
- cursor: move;
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
- font-weight: 600;
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
- `;
- rect.innerHTML = 'Rectangle';
- rect.draggable = true;
- this.insertElement(rect);
- }
-
- // Add circle shape
- addCircleShape() {
- const circle = document.createElement('div');
- circle.style.cssText = `
- width: 80px;
- height: 80px;
- background: linear-gradient(135deg, #ff6b6b, #ee5a24);
- border-radius: 50%;
- margin: 20px 0;
- cursor: move;
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
- font-weight: 600;
- box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
- `;
- circle.innerHTML = 'Circle';
- circle.draggable = true;
- this.insertElement(circle);
- }
-
- // Insert element at cursor or append
- insertElement(element) {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- if (window.getSelection && window.getSelection().rangeCount > 0) {
- const selection = window.getSelection();
- const range = selection.getRangeAt(0);
- range.insertNode(element);
- } else {
- editorFrame.appendChild(element);
- }
-
- // Make draggable
- this.makeElementDraggable(element);
-
- this.showSuccess('Shape added! Drag to move, click to edit.');
- }
-
- // Make element draggable
- makeElementDraggable(element) {
- let isDragging = false;
- let currentX;
- let currentY;
- let initialX;
- let initialY;
- let xOffset = 0;
- let yOffset = 0;
-
- element.addEventListener('mousedown', (e) => {
- initialX = e.clientX - xOffset;
- initialY = e.clientY - yOffset;
-
- if (e.target === element) {
- isDragging = true;
- }
- });
-
- document.addEventListener('mousemove', (e) => {
- if (isDragging) {
- e.preventDefault();
- currentX = e.clientX - initialX;
- currentY = e.clientY - initialY;
- xOffset = currentX;
- yOffset = currentY;
-
- element.style.transform = `translate(${currentX}px, ${currentY}px)`;
- }
- });
-
- document.addEventListener('mouseup', () => {
- initialX = currentX;
- initialY = currentY;
- isDragging = false;
- });
- }
-
- // Insert elements
- insertLink() {
- const url = prompt('Enter URL:');
- if (url) {
- this.execCommand('createLink', url);
- }
- }
-
- // Insert table with custom popup
- insertTable() {
- // Create custom popup
- const popup = document.createElement('div');
- popup.style.cssText = `
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: white;
- border: 2px solid #667eea;
- border-radius: 12px;
- padding: 30px;
- box-shadow: 0 10px 30px rgba(0,0,0,0.3);
- z-index: 10000;
- min-width: 300px;
- text-align: center;
- `;
-
- popup.innerHTML = `
-
Create Table
-
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- document.body.appendChild(popup);
-
- // Add event listeners
- document.getElementById('createTableBtn').addEventListener('click', () => {
- const rows = parseInt(document.getElementById('tableRows').value);
- const cols = parseInt(document.getElementById('tableCols').value);
- this.createTable(rows, cols);
- document.body.removeChild(popup);
- });
-
- document.getElementById('cancelTableBtn').addEventListener('click', () => {
- document.body.removeChild(popup);
- });
- }
-
- // Create table with specified dimensions
- createTable(rows, cols) {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- let tableHTML = '
';
-
- // Create header row
- tableHTML += '';
- for (let i = 0; i < cols; i++) {
- tableHTML += `| Header ${i + 1} | `;
- }
- tableHTML += '
';
-
- // Create data rows
- tableHTML += '';
- for (let i = 0; i < rows - 1; i++) {
- tableHTML += '';
- for (let j = 0; j < cols; j++) {
- tableHTML += `| Cell ${i + 1}-${j + 1} | `;
- }
- tableHTML += '
';
- }
- tableHTML += '
';
-
- // Insert at cursor position or append
- if (window.getSelection && window.getSelection().rangeCount > 0) {
- const selection = window.getSelection();
- const range = selection.getRangeAt(0);
- const tableElement = document.createElement('div');
- tableElement.innerHTML = tableHTML;
- range.insertNode(tableElement.firstChild);
- } else {
- editorFrame.innerHTML += tableHTML;
- }
- }
-
- // Add new page
- addNewPage() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- const pageBreak = document.createElement('div');
- pageBreak.style.cssText = `
- page-break-before: always;
- margin-top: 40px;
- padding-top: 40px;
- border-top: 3px dashed #667eea;
- text-align: center;
- color: #667eea;
- font-weight: 600;
- font-size: 1.2rem;
- `;
- pageBreak.innerHTML = 'π New Page - Click to edit content below';
- pageBreak.contentEditable = true;
-
- editorFrame.appendChild(pageBreak);
-
- // Add editable content area below
- const contentArea = document.createElement('div');
- contentArea.style.cssText = `
- min-height: 400px;
- padding: 20px;
- margin: 20px 0;
- border: 2px dashed #dee2e6;
- border-radius: 8px;
- background: #f8f9fa;
- `;
- contentArea.innerHTML = '
Click here to add content for this new page
';
- contentArea.contentEditable = true;
-
- editorFrame.appendChild(contentArea);
-
- this.showSuccess('New page added! Content will be separated when exporting to PDF.');
- }
-
- // Change z-index of selected element
- changeZIndex(direction) {
- const selection = window.getSelection();
- if (selection.rangeCount === 0) return;
-
- const range = selection.getRangeAt(0);
- let element = range.commonAncestorContainer;
-
- // Find the closest element
- while (element && element.nodeType !== Node.ELEMENT_NODE) {
- element = element.parentElement;
- }
-
- if (element && element.style) {
- const currentZIndex = parseInt(element.style.zIndex) || 0;
- const newZIndex = direction === 'up' ? currentZIndex + 1 : Math.max(0, currentZIndex - 1);
- element.style.zIndex = newZIndex;
-
- this.showSuccess(`Z-index changed to ${newZIndex}`);
- }
- }
-
- // Bring element to front
- bringToFront() {
- this.changeZIndex('up');
- }
-
- // Send element to back
- sendToBack() {
- this.changeZIndex('down');
- }
-
- // Set font size with proper functionality
- setFontSize(size) {
- this.currentFontSize = size;
- this.execCommand('fontSize', size);
- this.updateToolbarState();
- }
-
- // Set font family with proper functionality
- setFontFamily(font) {
- this.currentFontFamily = font;
- this.execCommand('fontName', font);
- this.updateToolbarState();
- }
-
- // Execute command with proper error handling
- execCommand(command, value = null) {
- try {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return false;
-
- // Focus the editor first
- editorFrame.focus();
-
- let result = false;
- if (value !== null) {
- result = document.execCommand(command, false, value);
- } else {
- result = document.execCommand(command, false);
- }
-
- if (!result) {
- console.warn(`Command ${command} failed, trying alternative approach`);
- // Alternative approach for some commands
- if (command === 'fontSize') {
- this.applyFontSize(value);
- } else if (command === 'fontName') {
- this.applyFontFamily(value);
- }
- }
-
- return result;
- } catch (error) {
- console.error(`Error executing command ${command}:`, error);
- return false;
- }
- }
-
- // Alternative font size application
- applyFontSize(size) {
- const selection = window.getSelection();
- if (selection.rangeCount === 0) return;
-
- const range = selection.getRangeAt(0);
- const span = document.createElement('span');
- span.style.fontSize = size;
-
- try {
- range.surroundContents(span);
- } catch (e) {
- // If surroundContents fails, try a different approach
- const fragment = range.extractContents();
- span.appendChild(fragment);
- range.insertNode(span);
- }
- }
-
- // Alternative font family application
- applyFontFamily(font) {
- const selection = window.getSelection();
- if (selection.rangeCount === 0) return;
-
- const range = selection.getRangeAt(0);
- const span = document.createElement('span');
- span.style.fontFamily = font;
-
- try {
- range.surroundContents(span);
- } catch (e) {
- const fragment = range.extractContents();
- span.appendChild(fragment);
- range.insertNode(span);
- }
- }
-
- // List insertion methods
- insertList(type) {
- this.execCommand(type === 'ordered' ? 'insertOrderedList' : 'insertUnorderedList');
- }
-
- // Clear formatting
- clearFormatting() {
- this.execCommand('removeFormat', false, null);
- }
-
- // Undo/Redo
- undo() {
- this.execCommand('undo');
- }
-
- redo() {
- this.execCommand('redo');
- }
-
- // Add new text block
- addTextBlock() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- const textBlock = document.createElement('div');
- textBlock.style.cssText = `
- background: #f8f9fa;
- border: 2px dashed #dee2e6;
- border-radius: 8px;
- padding: 20px;
- margin: 20px 0;
- min-height: 100px;
- position: relative;
- cursor: text;
- clear: both;
- `;
- textBlock.innerHTML = '
New text block - Click to edit
';
- textBlock.contentEditable = true;
-
- // Insert at cursor position or append at the end
- if (window.getSelection && window.getSelection().rangeCount > 0) {
- const selection = window.getSelection();
- const range = selection.getRangeAt(0);
-
- // Check if cursor is inside an existing element
- let targetElement = range.commonAncestorContainer;
- while (targetElement && targetElement.nodeType !== Node.ELEMENT_NODE) {
- targetElement = targetElement.parentElement;
- }
-
- if (targetElement && targetElement !== editorFrame) {
- // Insert after the current element
- targetElement.parentNode.insertBefore(textBlock, targetElement.nextSibling);
- } else {
- // Insert at cursor position
- range.insertNode(textBlock);
- }
- } else {
- // Append at the end
- editorFrame.appendChild(textBlock);
- }
-
- // Focus on the new text block
- textBlock.focus();
-
- this.showSuccess('Text block added! Click to edit content.');
- }
-
- // Add image placeholder
- addImagePlaceholder() {
- const img = document.createElement('div');
- img.innerHTML = 'πΌοΈ Image Placeholder - Drag image here or click to upload';
- img.style.width = '200px';
- img.style.height = '150px';
- img.style.border = '2px dashed #ccc';
- img.style.borderRadius = '8px';
- img.style.display = 'flex';
- img.style.alignItems = 'center';
- img.style.justifyContent = 'center';
- img.style.textAlign = 'center';
- img.style.cursor = 'pointer';
- img.style.backgroundColor = '#f8f9fa';
- img.style.fontSize = '12px';
- img.style.color = '#6c757d';
-
- img.addEventListener('click', () => {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = 'image/*';
- input.onchange = (e) => {
- if (e.target.files[0]) {
- this.insertImage(e.target.files[0]);
- img.remove();
- }
- };
- input.click();
- });
-
- const selection = window.getSelection();
- if (selection.rangeCount > 0) {
- const range = selection.getRangeAt(0);
- range.insertNode(img);
- range.collapse(false);
- }
-
- selection.removeAllRanges();
- }
-
- // Add shape/divider
- addShape() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- const shape = document.createElement('div');
- shape.innerHTML = '
Shape
';
- shape.style.cssText = 'margin: 10px; cursor: move;';
- shape.draggable = true;
-
- // Insert at cursor position or append
- if (window.getSelection && window.getSelection().rangeCount > 0) {
- const selection = window.getSelection();
- const range = selection.getRangeAt(0);
- range.insertNode(shape);
- } else {
- editorFrame.appendChild(shape);
- }
-
- // Make draggable
- this.makeElementDraggable(shape);
-
- this.showSuccess('Shape added! Drag to move, click to edit.');
- }
-
- // Save template
- saveTemplate() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (editorFrame) {
- this.editorContent = editorFrame.innerHTML;
- this.showSuccess('Template saved successfully!');
- }
- }
-
- // Reset template
- resetTemplate() {
- if (confirm('Are you sure you want to reset the template? This action cannot be undone.')) {
- this.generateTemplateContent();
- this.showSuccess('Template reset to original content.');
- }
- }
-
- // Create template HTML based on selected template and property data
- createTemplateHTML() {
- console.log('=== CREATING TEMPLATE HTML WITH COMPLETE DATA ===');
- console.log('Template ID:', this.selectedTemplateData.Id);
- console.log('Property Data:', this.propertyData);
-
- if (!this.propertyData) {
- console.error('No property data available');
- return '
No Property Data
Please select a property first.
';
- }
-
- let htmlContent = '';
-
- // Create comprehensive property brochure based on template
- switch (this.selectedTemplateData.Id) {
case 'everkind-template':
- htmlContent = this.createEverkindTemplate();
+ this.selectedTemplateId = 'everkind-template';
+ console.log('Set selectedTemplateId to everkind-template');
break;
- case 'corporate-template':
- htmlContent = this.createCorporateTemplate();
+ case 'shift-template':
+ this.selectedTemplateId = 'shift-template';
+ console.log('Set selectedTemplateId to shift-template');
break;
- case 'luxury-template':
- htmlContent = this.createLuxuryTemplate();
+ case 'saintbarts-template':
+ this.selectedTemplateId = 'saintbarts-template';
+ console.log('Set selectedTemplateId to saintbarts-template');
break;
- case 'modern-template':
- htmlContent = this.createModernTemplate();
+ case 'learnoy-template':
+ this.selectedTemplateId = 'learnoy-template';
+ console.log('Set selectedTemplateId to learnoy-template');
break;
- case 'classic-template':
- htmlContent = this.createClassicTemplate();
+ case 'leafamp-template':
+ this.selectedTemplateId = 'leafamp-template';
+ console.log('Set selectedTemplateId to leafamp-template');
break;
- case 'premium-template':
- htmlContent = this.createPremiumTemplate();
+ case 'coreshift-template':
+ this.selectedTemplateId = 'coreshift-template';
+ console.log('Set selectedTemplateId to coreshift-template');
break;
- case 'standard-template':
- htmlContent = this.createStandardTemplate();
+ case 'modern-home-template':
+ this.selectedTemplateId = 'modern-home-template';
+ console.log('Set selectedTemplateId to modern-home-template');
+ break;
+ case 'asgar-1-template':
+ this.selectedTemplateId = 'asgar-1-template';
+ console.log('Set selectedTemplateId to asgar-1-template');
+ break;
+ case 'serenity-house-template':
+ this.selectedTemplateId = 'serenity-house-template';
+ console.log('Set selectedTemplateId to serenity-house-template');
+ break;
+ case 'sample-template':
+ this.selectedTemplateId = 'sample-template';
+ console.log('Set selectedTemplateId to sample-template');
+ break;
+ case 'luxury-mansion-template':
+ this.selectedTemplateId = 'luxury-mansion-template';
+ console.log('Set selectedTemplateId to luxury-mansion-template');
break;
default:
- htmlContent = this.createDefaultTemplate();
+ console.log('No matching case found for template ID:', templateId);
+ break;
}
- console.log('=== TEMPLATE HTML CREATED ===');
- console.log('HTML Content Length:', htmlContent.length);
- console.log('HTML Preview:', htmlContent.substring(0, 500));
+ console.log('AFTER switch - selectedTemplateId:', this.selectedTemplateId);
+ console.log('Final state - selectedTemplateId:', this.selectedTemplateId);
+ console.log('Final state - isModernHomeTemplateSelected:', this.isModernHomeTemplateSelected);
+ console.log('Final state - isAsgar1TemplateSelected:', this.isAsgar1TemplateSelected);
+ console.log('Final state - isSampleTemplateSelected:', this.isSampleTemplateSelected);
+ console.log('Final state - isLuxuryMansionTemplateSelected:', this.isLuxuryMansionTemplateSelected);
+ console.log('=== END TEMPLATE SELECTION DEBUG ===');
- return htmlContent;
- }
-
- // Create Everkind template with complete data
- createEverkindTemplate() {
- return `
-
-
-
-
-
Price
-
${this.propertyData.price}
-
-
-
Bedrooms
-
${this.propertyData.bedrooms}
-
-
-
Bathrooms
-
${this.propertyData.bathrooms}
-
-
-
Area
-
${this.propertyData.area} sq ft
-
-
-
-
-
Property Details
-
-
Status: ${this.propertyData.status}
-
Type: ${this.propertyData.propertySubType}
-
Floor: ${this.propertyData.floorNumber} of ${this.propertyData.totalFloors}
-
Parking: ${this.propertyData.parkingSpaces} spaces
-
Year Built: ${this.propertyData.yearBuilt}
-
Furnishing: ${this.propertyData.furnishingStatus}
-
Maintenance Fee: ${this.propertyData.maintenanceFee}
-
Service Charge: ${this.propertyData.serviceCharge}
-
-
-
-
-
Agent & Owner Information
-
-
-
Agent Details
-
Name: ${this.propertyData.agentName}
-
Phone: ${this.propertyData.agentPhone}
-
Email: ${this.propertyData.agentEmail}
-
-
-
Owner Details
-
Name: ${this.propertyData.ownerName}
-
Phone: ${this.propertyData.ownerPhone}
-
Email: ${this.propertyData.ownerEmail}
-
-
-
-
-
-
Amenities & Features
-
-
-
Amenities
-
${this.propertyData.amenities}
-
-
-
Features
-
${this.propertyData.features}
-
-
-
-
-
-
Location & Nearby
-
-
Nearby Landmarks: ${this.propertyData.nearbyLandmarks}
-
Transportation: ${this.propertyData.transportation}
-
Schools: ${this.propertyData.schools}
-
Hospitals: ${this.propertyData.hospitals}
-
Shopping Centers: ${this.propertyData.shoppingCenters}
-
Beach Distance: ${this.propertyData.beachDistance}
-
Airport Distance: ${this.propertyData.airportDistance}
-
Metro Distance: ${this.propertyData.metroDistance}
-
-
-
-
-
Property Description
-
${this.propertyData.description}
-
-
-
-
Additional Information
-
-
Pet Friendly: ${this.propertyData.petFriendly}
-
Smoking Allowed: ${this.propertyData.smokingAllowed}
-
Available From: ${this.propertyData.availableFrom}
-
Minimum Contract: ${this.propertyData.minimumContract}
-
Security Deposit: ${this.propertyData.securityDeposit}
-
Utilities Included: ${this.propertyData.utilitiesIncluded}
-
Internet Included: ${this.propertyData.internetIncluded}
-
Cable Included: ${this.propertyData.cableIncluded}
-
-
- `;
- }
-
- // Enhanced PDF generation with better error handling
- async generatePdf() {
- if (!this.selectedTemplateData || !this.selectedPropertyId) {
- this.showError('Please select a template and property first');
- return;
+ // Force a re-render to ensure the UI updates
+ this.template.querySelectorAll('.template-card').forEach(card => {
+ card.classList.remove('selected');
+ });
+ if (event.currentTarget) {
+ event.currentTarget.classList.add('selected');
}
+ }
- this.isLoading = true;
- this.showSuccess('Generating PDF... Please wait.');
+ resetTemplateSelections() {
+ this.selectedTemplateId = '';
+ }
- try {
- // Get the current editor content
- const editorFrame = this.template.querySelector('.preview-frame');
- const currentContent = editorFrame ? editorFrame.innerHTML : this.editorContent;
+ // Page size change handler
+ handlePageSizeChange(event) {
+ const newPageSize = event.target.value;
+ console.log('Page size changed to:', newPageSize);
+ this.selectedPageSize = newPageSize;
+
+ // Update the preview frame with new dimensions
+ this.updatePreviewFrameSize(newPageSize);
+ }
- // Prepare data for PDF generation
- const propertyData = {
- template: this.selectedTemplateData.Id,
- description: currentContent,
- area: this.propertyData.area || '',
- bathrooms: this.propertyData.bathrooms || '',
- bedrooms: this.propertyData.bedrooms || '',
- price: this.propertyData.price || '',
- location: this.propertyData.location || '',
- propertyType: this.propertyData.propertyType || '',
- propertyName: this.propertyData.propertyName || '',
- status: this.propertyData.status || ''
- };
-
- console.log('Sending data for PDF generation:', propertyData);
-
- // Call the Apex controller
- const result = await generatePdf({ propertyData: JSON.stringify(propertyData) });
+ // Update preview frame size based on selected page size
+ updatePreviewFrameSize(pageSize) {
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (previewFrame) {
+ // Update the data attribute for the CSS content
+ previewFrame.setAttribute('data-page-size', pageSize);
- if (result && result.success) {
- if (result.pdfData) {
- // Download the PDF
- this.downloadPdfFile(result.pdfData, result.filename || 'property-brochure.pdf');
- this.showSuccess('PDF generated successfully!');
- this.closePdfPreview();
- } else if (result.htmlContent) {
- // Fallback to HTML download
- this.downloadHtmlFile(result.htmlContent, 'property-brochure.html');
- this.showSuccess('HTML content downloaded. PDF generation is being processed.');
- } else {
- this.showError('Unexpected response format from PDF API');
+ console.log('Preview frame updated for page size:', pageSize);
+
+ // Re-render content with new page size
+ this.renderContentInPages(pageSize);
+
+ // Update page count based on new page size
+ this.updatePageCountForSize(pageSize);
+ }
+ }
+
+ // Render content in separate pages based on selected size
+ renderContentInPages(pageSize) {
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (!previewFrame) return;
+
+ // Get the current template content
+ const templateHTML = this.createTemplateHTML();
+ if (!templateHTML) return;
+
+ // Clear existing content
+ previewFrame.innerHTML = '';
+
+ // 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);
+ });
+
+ console.log(`Rendered ${pages.length} pages for ${pageSize} size`);
+ }
+
+ // 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 {
- this.showError(result?.error || 'PDF generation failed');
+ // 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;
+ }
}
- } catch (error) {
- console.error('PDF generation error:', error);
- this.showError('PDF generation failed: ' + error.message);
- } finally {
- this.isLoading = false;
+ });
+
+ // Add the last page
+ if (currentPage) {
+ pages.push(currentPage);
}
+
+ return pages;
}
- // Download PDF file
- downloadPdfFile(base64Data, filename) {
- try {
- // Convert base64 to blob
- const byteCharacters = atob(base64Data);
- const byteNumbers = new Array(byteCharacters.length);
- for (let i = 0; i < byteCharacters.length; i++) {
- byteNumbers[i] = byteCharacters.charCodeAt(i);
- }
- const byteArray = new Uint8Array(byteNumbers);
- const blob = new Blob([byteArray], { type: 'application/pdf' });
-
- // Create download link
- const link = document.createElement('a');
- link.href = URL.createObjectURL(blob);
- link.download = filename;
- link.style.display = 'none';
-
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
-
- // Clean up
- URL.revokeObjectURL(link.href);
- } catch (error) {
- console.error('Error downloading PDF:', error);
- this.showError('Error downloading PDF: ' + error.message);
+ // 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;
}
- // Download HTML file as fallback
- downloadHtmlFile(htmlContent, filename) {
- try {
- const blob = new Blob([htmlContent], { type: 'text/html' });
- const link = document.createElement('a');
- link.href = URL.createObjectURL(blob);
- link.download = filename;
- link.style.display = 'none';
+ // Update page count based on selected page size
+ updatePageCountForSize(pageSize) {
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (previewFrame) {
+ let pageHeight = 297; // Default A4 height in mm
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
+ switch (pageSize) {
+ case 'A3':
+ pageHeight = 420; // A3 height in mm
+ break;
+ case 'A4':
+ default:
+ pageHeight = 297; // A4 height in mm
+ break;
+ }
- URL.revokeObjectURL(link.href);
- } catch (error) {
- console.error('Error downloading HTML:', error);
- this.showError('Error downloading HTML: ' + error.message);
+ // 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
+ console.log(`Estimated ${this.pageCount} ${pageSize} pages based on content height`);
}
}
// Navigation methods
nextStep() {
- if (this.canProceed) {
- const nextStepNumber = this.currentStep + 1;
- this.currentStep = nextStepNumber;
-
- // Initialize editor when moving to step 3
- if (nextStepNumber === 3) {
- console.log('Moving to step 3, initializing editor...');
- setTimeout(() => {
- this.initializeEditor();
- }, 100);
+ if (this.currentStep < 3) {
+ this.currentStep++;
+ // If moving to step 3, automatically load the template
+ if (this.currentStep === 3) {
+ this.loadTemplateInStep3();
}
+ this.scrollToTop();
}
}
previousStep() {
- this.currentStep = Math.max(this.currentStep - 1, 1);
+ if (this.currentStep > 1) {
+ this.currentStep--;
+ this.scrollToTop();
+ }
}
goToStep(event) {
const step = parseInt(event.currentTarget.dataset.step);
- if (this.isStepAccessible(step)) {
- this.currentStep = step;
+ this.currentStep = step;
+ // If going directly to step 3, load the template
+ if (this.currentStep === 3) {
+ this.loadTemplateInStep3();
+ }
+ this.scrollToTop();
+ }
+
+ // Scroll to top of page when changing steps
+ scrollToTop() {
+ window.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }
+
+ // Load template content into step 3 preview frame
+ loadTemplateInStep3() {
+ if (this.selectedTemplateId && this.selectedPropertyId) {
+ try {
+ const templateHTML = this.createTemplateHTML();
+ console.log('Loading template into step 3:', this.selectedTemplateId);
+ console.log('Template HTML length:', templateHTML.length);
+
+ // Find the preview frame and load the template
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (previewFrame) {
+ // Clear any existing content
+ previewFrame.innerHTML = '';
+
+ // Create a temporary container to parse the HTML
+ const tempDiv = document.createElement('div');
+ tempDiv.innerHTML = templateHTML;
+
+ // Append the parsed content to the preview frame
+ while (tempDiv.firstChild) {
+ previewFrame.appendChild(tempDiv.firstChild);
+ }
+
+ console.log('Template loaded successfully into preview frame');
+
+ // Add edit icons to ALL images (simple solution)
+ setTimeout(() => {
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (previewFrame) {
+ const images = previewFrame.querySelectorAll('img');
+ console.log(`Found ${images.length} images to add edit icons to`);
+
+ images.forEach((img, index) => {
+ // Skip if already has edit button
+ if (img.closest('.template-image-container')) {
+ console.log(`Image ${index + 1} already has edit button, skipping`);
+ return;
+ }
+
+ console.log(`Adding edit button to image ${index + 1}:`, img.src);
+
+ // Create edit button
+ const editBtn = document.createElement('button');
+ editBtn.className = 'template-image-edit-btn';
+ editBtn.innerHTML = 'βοΈ';
+ editBtn.title = 'Edit Image';
+ editBtn.style.position = 'absolute';
+ editBtn.style.top = '5px';
+ editBtn.style.right = '5px';
+ editBtn.style.background = 'rgba(0, 123, 255, 0.9)';
+ editBtn.style.border = 'none';
+ editBtn.style.borderRadius = '50%';
+ editBtn.style.width = '30px';
+ editBtn.style.height = '30px';
+ editBtn.style.cursor = 'pointer';
+ editBtn.style.fontSize = '14px';
+ editBtn.style.zIndex = '1001';
+ editBtn.style.display = 'flex';
+ editBtn.style.alignItems = 'center';
+ editBtn.style.justifyContent = 'center';
+ editBtn.style.transition = 'all 0.3s ease';
+ editBtn.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.2)';
+
+ // Create container
+ const container = document.createElement('div');
+ container.className = 'template-image-container';
+ container.style.position = 'relative';
+ container.style.display = 'inline-block';
+ container.style.width = '100%';
+ container.style.height = '100%';
+
+ // Replace image with container
+ img.parentNode.insertBefore(container, img);
+ container.appendChild(img);
+ container.appendChild(editBtn);
+
+ // Add hover effects
+ editBtn.addEventListener('mouseenter', () => {
+ editBtn.style.background = 'rgba(0, 123, 255, 1)';
+ editBtn.style.transform = 'scale(1.1)';
+ });
+
+ editBtn.addEventListener('mouseleave', () => {
+ editBtn.style.background = 'rgba(0, 123, 255, 0.9)';
+ editBtn.style.transform = 'scale(1)';
+ });
+
+ // Add click handler with custom popup
+ editBtn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ this.showCustomImageEditModal(img);
+ });
+
+ console.log(`Successfully added edit button to image ${index + 1}`);
+ });
+
+ console.log(`Successfully processed ${images.length} images`);
+ }
+ }, 1000);
+
+ // Set initial page size class and data attribute
+ this.updatePreviewFrameSize(this.selectedPageSize);
+
+ // Update page count after template is loaded
+ setTimeout(() => {
+ this.updatePageCountForSize(this.selectedPageSize);
+ }, 100);
+
+ } else {
+ console.error('Preview frame not found');
+ }
+ } catch (error) {
+ console.error('Error loading template in step 3:', error);
+ this.error = 'Error loading template: ' + error.message;
+
+ // Show error message in preview frame
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (previewFrame) {
+ previewFrame.innerHTML = `
+
Error Loading Template
+
${error.message}
+
Please try selecting a different template or contact support.
+
`;
+ }
+ }
+ } else {
+ // Show a message if template or property is not selected
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (previewFrame) {
+ if (!this.selectedTemplateId) {
+ previewFrame.innerHTML = '
No Template Selected
Please go back to step 1 and select a template.
';
+ } else if (!this.selectedPropertyId) {
+ previewFrame.innerHTML = '
No Property Selected
Please go back to step 2 and select a property.
';
+ }
+ }
+ console.log('Template or property not selected, cannot load template');
}
}
- // Computed properties
- get canProceed() {
- if (this.currentStep === 1) return this.selectedTemplate;
- if (this.currentStep === 2) {
- const hasProperty = this.selectedPropertyId && this.propertyData && this.propertyData.propertyName;
- console.log('Step 2 validation:', {
- selectedPropertyId: this.selectedPropertyId,
- hasPropertyData: !!this.propertyData,
- propertyName: this.propertyData?.propertyName,
- canProceed: hasProperty
+ // Property selection handler
+ handlePropertySelection(event) {
+ this.selectedPropertyId = event.target.value;
+ if (this.selectedPropertyId) {
+ this.loadPropertyData();
+ // Auto-scroll to property preview section after a short delay to ensure data is loaded
+ setTimeout(() => {
+ this.scrollToPropertyPreview();
+ // Initialize image review with Interior category
+ // Auto-select category will be handled in loadPropertyImages
+ }, 300);
+ }
+ }
+
+ // Scroll to property preview section
+ scrollToPropertyPreview() {
+ const propertyPreviewSection = this.template.querySelector('.property-details-layout');
+ if (propertyPreviewSection) {
+ // Get the element's position and add offset for better positioning
+ const elementTop = propertyPreviewSection.offsetTop;
+ const offset = 100; // 100px offset from the top
+
+ window.scrollTo({
+ top: elementTop - offset,
+ behavior: 'smooth'
});
- return hasProperty;
- }
- return true;
- }
-
- get isStepAccessible() {
- return (step) => {
- if (step === 1) return true;
- if (step === 2) return this.selectedTemplate;
- if (step === 3) return this.selectedTemplate && this.selectedPropertyId;
- return false;
- };
- }
-
- get step1Class() {
- return this.currentStep === 1 ? 'step-content active' : 'step-content';
- }
-
- get step2Class() {
- return this.currentStep === 2 ? 'step-content active' : 'step-content';
- }
-
- get step3Class() {
- return this.currentStep === 3 ? 'step-content active' : 'step-content';
- }
-
- get step1NavClass() {
- return this.currentStep >= 1 ? 'step-nav active' : 'step-nav';
- }
-
- get step2NavClass() {
- return this.currentStep >= 2 ? 'step-nav active' : 'step-nav';
- }
-
- get step3NavClass() {
- return this.currentStep >= 3 ? 'step-nav active' : 'step-nav';
- }
-
- get isNextButtonDisabled() {
- if (this.currentStep === 1) return !this.selectedTemplate;
- if (this.currentStep === 2) {
- const hasProperty = this.selectedPropertyId && this.propertyData && this.propertyData.propertyName;
- console.log('Next button validation:', {
- selectedPropertyId: this.selectedPropertyId,
- hasPropertyData: !!this.propertyData,
- propertyName: this.propertyData?.propertyName,
- isDisabled: !hasProperty
- });
- return !hasProperty;
- }
- return false;
- }
-
- get exportPdfButtonText() {
- return this.isLoading ? 'Generating PDF...' : 'Export PDF';
- }
-
- get formattedPropertyOptions() {
- return this.properties.map(property => ({
- label: `${property.Name} - ${property.pcrm__Property_Type__c || 'N/A'} - ${property.pcrm__City_Bayut_Dubizzle__c || 'N/A'}`,
- value: property.Id
- }));
- }
-
- // Template selection getters
- get emptyTemplateSelected() {
- return this.selectedTemplate === 'empty-template';
- }
-
- get luxuryVillaSelected() {
- return this.selectedTemplate === 'luxury-villa';
- }
-
- get modernApartmentSelected() {
- return this.selectedTemplate === 'modern-apartment';
- }
-
- get beachfrontPropertySelected() {
- return this.selectedTemplate === 'beachfront-property';
- }
-
- get investmentPropertySelected() {
- return this.selectedTemplate === 'investment-property';
- }
-
- get urbanPropertySelected() {
- return this.selectedTemplate === 'urban-property';
- }
-
- get blankTemplateSelected() {
- return this.selectedTemplate === 'blank-template';
- }
-
- get everkindTemplateSelected() {
- return this.selectedTemplate === 'everkind-template';
- }
-
- get shiftTemplateSelected() {
- return this.selectedTemplate === 'shift-template';
- }
-
- get saintbartsTemplateSelected() {
- return this.selectedTemplate === 'saintbarts-template';
- }
-
- get learnoyTemplateSelected() {
- return this.selectedTemplate === 'learnoy-template';
- }
-
- get leafampTemplateSelected() {
- return this.selectedTemplate === 'leafamp-template';
- }
-
- get coreshiftTemplateSelected() {
- return this.selectedTemplate === 'coreshift-template';
- }
-
- // Page Management
- @track currentPage = 1;
- @track totalPages = 3;
- @track pages = [];
-
- // Initialize pages for multi-page editing
- initializePages() {
- console.log('=== initializePages called ===');
-
- this.pages = [
- { id: 1, title: 'Cover Page', content: '' },
- { id: 2, title: 'Property Details', content: '' },
- { id: 3, title: 'Amenities & Features', content: '' }
- ];
- this.currentPage = 1;
- this.totalPages = 3;
-
- // Set first page as active by default
- setTimeout(() => {
- this.updatePageNavigation();
- }, 100);
-
- console.log('Pages initialized:', this.pages);
- console.log('Current page:', this.currentPage);
- console.log('Total pages:', this.totalPages);
- }
-
- // Page navigation
- goToPage(event) {
- const pageNum = parseInt(event.currentTarget.dataset.page);
- if (pageNum && pageNum <= this.totalPages) {
- this.currentPage = pageNum;
- this.updatePageNavigation();
- this.scrollToPage(pageNum);
- console.log(`Navigated to page ${pageNum}`);
}
}
- scrollToPage(pageNum) {
- const pageElement = this.template.querySelector(`[data-page="${pageNum}"]`);
- if (pageElement) {
- pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
- console.log(`Scrolled to page ${pageNum}`);
+ // Helper function to safely get property values with validation
+ getPropertyValue(property, fieldPath, defaultValue = 'N/A') {
+ try {
+ if (!property) return defaultValue;
+
+ // Handle nested object paths like 'Contact__r.FirstName'
+ if (fieldPath.includes('.')) {
+ const parts = fieldPath.split('.');
+ let value = property;
+ for (const part of parts) {
+ if (value && typeof value === 'object' && value[part] !== undefined) {
+ value = value[part];
} else {
- console.warn(`Page element not found for page ${pageNum}`);
+ return defaultValue;
+ }
+ }
+ return value || defaultValue;
+ }
+
+ // Handle direct field access
+ return property[fieldPath] || defaultValue;
+ } catch (error) {
+ console.warn(`Error accessing field ${fieldPath}:`, error);
+ return defaultValue;
}
}
- updatePageNavigation() {
- // Update active page button
- const pageButtons = this.template.querySelectorAll('.page-nav-btn');
- pageButtons.forEach(btn => {
- btn.classList.remove('active');
- if (parseInt(btn.dataset.page) === this.currentPage) {
- btn.classList.add('active');
- }
- });
-
- // Update page visibility
- const allPages = this.template.querySelectorAll('.page-container');
- allPages.forEach((page, index) => {
- const pageNum = index + 1;
- if (pageNum === this.currentPage) {
- page.style.display = 'block';
- page.classList.add('active-page');
- } else {
- page.style.display = 'none';
- page.classList.remove('active-page');
- }
- });
-
- console.log(`Updated navigation: Current page ${this.currentPage}, Total pages ${this.totalPages}`);
- }
-
- // Add new page
- addNewPage() {
- const newPageNum = this.totalPages + 1;
- const newPage = {
- id: newPageNum,
- title: `Page ${newPageNum}`,
- content: ''
- };
-
- this.pages.push(newPage);
- this.totalPages = newPageNum;
-
- // Add new page to DOM
- this.addPageToDOM(newPage);
-
- // Update navigation
- this.updatePageNavigation();
-
- // Show success message
- this.showSuccess(`Page ${newPageNum} added successfully!`);
- }
-
- addPageToDOM(page) {
- const editorArea = this.template.querySelector('.editor-content-area');
- const pageContainer = document.createElement('div');
- pageContainer.className = 'page-container';
- pageContainer.dataset.page = page.id;
-
- pageContainer.innerHTML = `
-
-
-
-
- `;
-
- editorArea.appendChild(pageContainer);
-
- // Add navigation button
- this.addNavigationButton(page.id);
- }
-
- addNavigationButton(pageNum) {
- const navContainer = this.template.querySelector('.page-navigation');
- const newButton = document.createElement('button');
- newButton.className = 'page-nav-btn';
- newButton.dataset.page = pageNum;
- newButton.onclick = this.goToPage.bind(this);
- newButton.textContent = pageNum;
-
- // Insert before the + button
- const addButton = navContainer.querySelector('.page-nav-btn:last-child');
- navContainer.insertBefore(newButton, addButton);
- }
-
- // Delete current page
- deleteCurrentPage() {
- if (this.totalPages <= 1) {
- this.showError('Cannot delete the last page. At least one page is required.');
- return;
- }
-
- if (confirm(`Are you sure you want to delete Page ${this.currentPage}?`)) {
- // Remove page from DOM
- const pageElement = this.template.querySelector(`[data-page="${this.currentPage}"]`);
- if (pageElement) {
- pageElement.remove();
- }
-
- // Remove from pages array
- this.pages = this.pages.filter(p => p.id !== this.currentPage);
- this.totalPages--;
-
- // Update page IDs for remaining pages
- this.reorderPages();
-
- // Go to previous page or first page
- if (this.currentPage > this.totalPages) {
- this.currentPage = this.totalPages;
- }
-
- // Update navigation
- this.updatePageNavigation();
- this.updatePageNumbers();
-
- this.showSuccess(`Page ${this.currentPage} deleted successfully!`);
- }
- }
-
- reorderPages() {
- this.pages.forEach((page, index) => {
- page.id = index + 1;
- });
- }
-
- updatePageNumbers() {
- const pageElements = this.template.querySelectorAll('.page-container');
- pageElements.forEach((element, index) => {
- const pageNum = index + 1;
- element.dataset.page = pageNum;
- element.querySelector('.page-number').textContent = `Page ${pageNum}`;
- });
-
- // Update navigation buttons
- const navButtons = this.template.querySelectorAll('.page-nav-btn:not(:last-child)');
- navButtons.forEach((btn, index) => {
- const pageNum = index + 1;
- btn.dataset.page = pageNum;
- btn.textContent = pageNum;
- });
- }
-
- // Duplicate current page
- duplicatePage() {
- const currentPageData = this.pages.find(p => p.id === this.currentPage);
- if (currentPageData) {
- const newPageNum = this.totalPages + 1;
- const duplicatedPage = {
- id: newPageNum,
- title: `${currentPageData.title} (Copy)`,
- content: currentPageData.content
+ // Load property data with comprehensive validation
+ async loadPropertyData() {
+ const selectedProperty = this.properties.find(prop => prop.Id === this.selectedPropertyId);
+ if (selectedProperty) {
+ console.log('=== LOADING PROPERTY DATA ===');
+ console.log('Selected property:', selectedProperty);
+
+ // Set the selectedProperty for use in PDF generation
+ this.selectedProperty = selectedProperty;
+
+ // Helper function for safe property access
+ const get = (fieldPath, defaultValue = 'N/A') => this.getPropertyValue(selectedProperty, fieldPath, defaultValue);
+
+ this.propertyData = {
+ // Basic Information
+ propertyName: get('Name', 'Property Name'),
+ propertyType: get('pcrm__Property_Type__c', 'Property Type'),
+ status: get('pcrm__Status__c', 'Available'),
+ referenceNumber: get('Name', 'REF-001'),
+
+ // Location Details
+ location: get('pcrm__City_Bayut_Dubizzle__c') !== 'N/A' ? get('pcrm__City_Bayut_Dubizzle__c') :
+ get('pcrm__City_Propertyfinder__c') !== 'N/A' ? get('pcrm__City_Propertyfinder__c') : 'Location',
+ city: get('pcrm__City_Propertyfinder__c', 'City'),
+ community: get('pcrm__Community_Propertyfinder__c', 'Community'),
+ subCommunity: get('pcrm__Sub_Community_Propertyfinder__c', 'Sub Community'),
+ locality: get('pcrm__Locality_Bayut_Dubizzle__c', 'Locality'),
+ subLocality: get('pcrm__Sub_Locality_Bayut_Dubizzle__c', 'Sub Locality'),
+ tower: get('pcrm__Tower_Bayut_Dubizzle__c', 'Tower'),
+ unitNumber: get('pcrm__Unit_Number__c', 'Unit Number'),
+
+ // Additional Location Fields
+ cityBayut: get('pcrm__City_Bayut_Dubizzle__c'),
+ cityPropertyfinder: get('pcrm__City_Propertyfinder__c'),
+ communityBayut: get('pcrm__Community_Propertyfinder__c'),
+ subCommunityBayut: get('pcrm__Sub_Community_Propertyfinder__c'),
+ localityBayut: get('pcrm__Locality_Bayut_Dubizzle__c'),
+ subLocalityBayut: get('pcrm__Sub_Locality_Bayut_Dubizzle__c'),
+ towerBayut: get('pcrm__Tower_Bayut_Dubizzle__c'),
+
+ // Rent Availability
+ rentAvailableFrom: get('pcrm__Rent_Available_From__c'),
+ rentAvailableTo: get('pcrm__Rent_Available_To__c'),
+
+ // Contact Details
+ contactName: (() => {
+ const firstName = get('Contact__r.FirstName', '');
+ const lastName = get('Contact__r.LastName', '');
+ if (firstName !== 'N/A' && lastName !== 'N/A') {
+ return `${firstName} ${lastName}`.trim();
+ } else if (firstName !== 'N/A') {
+ return firstName;
+ } else if (lastName !== 'N/A') {
+ return lastName;
+ }
+ return 'Contact Not Linked';
+ })(),
+ contactEmail: get('Email__c'),
+ contactPhone: get('Phone__c'),
+
+ // Specifications
+ bedrooms: get('pcrm__Bedrooms__c'),
+ bathrooms: get('pcrm__Bathrooms__c'),
+ floor: get('pcrm__Floor__c'),
+ size: get('pcrm__Size__c'),
+ sizeUnit: 'sq ft', // Default unit since field doesn't exist
+ buildYear: get('pcrm__Build_Year__c'),
+
+ // Parking & Amenities
+ parkingSpaces: get('pcrm__Parking_Spaces__c'),
+
+ // Furnishing & Details
+ furnished: get('pcrm__Furnished__c'),
+
+ // Pricing Information
+ rentPriceMin: (() => {
+ const value = get('pcrm__Rent_Price_min__c');
+ return value !== 'N/A' && !isNaN(value) ? `AED ${parseFloat(value).toLocaleString()}` : 'N/A';
+ })(),
+ rentPriceMax: (() => {
+ const value = get('pcrm__Rent_Price_max__c');
+ return value !== 'N/A' && !isNaN(value) ? `AED ${parseFloat(value).toLocaleString()}` : 'N/A';
+ })(),
+ salePriceMin: (() => {
+ const value = get('pcrm__Sale_Price_min__c');
+ return value !== 'N/A' && !isNaN(value) ? `AED ${parseFloat(value).toLocaleString()}` : 'N/A';
+ })(),
+ salePriceMax: (() => {
+ const value = get('pcrm__Sale_Price_max__c');
+ return value !== 'N/A' && !isNaN(value) ? `AED ${parseFloat(value).toLocaleString()}` : 'N/A';
+ })(),
+
+ // Description & Title
+ titleEnglish: get('pcrm__Title_English__c'),
+ descriptionEnglish: get('pcrm__Description_English__c', 'This beautiful property offers exceptional value and modern amenities.'),
+
+ // Offering Type
+ offeringType: get('pcrm__Offering_Type__c'),
+
+ // Legacy fields for backward compatibility
+ price: (() => {
+ const value = get('pcrm__Rent_Price_min__c');
+ return value !== 'N/A' && !isNaN(value) ? `AED ${parseFloat(value).toLocaleString()}` : 'Price on Request';
+ })(),
+ area: (() => {
+ const value = get('pcrm__Size__c');
+ return value !== 'N/A' && !isNaN(value) ? `${value} sq ft` : 'N/A';
+ })(),
+ yearBuilt: get('pcrm__Build_Year__c'),
+ parking: (() => {
+ const value = get('pcrm__Parking_Spaces__c');
+ return value !== 'N/A' && !isNaN(value) ? `${value} Parking Space(s)` : 'N/A';
+ })(),
+ furnishing: get('pcrm__Furnished__c')
};
- this.pages.push(duplicatedPage);
- this.totalPages = newPageNum;
-
- // Add new page to DOM
- this.addPageToDOM(duplicatedPage);
-
- // Update navigation
- this.updatePageNavigation();
-
- this.showSuccess(`Page ${this.currentPage} duplicated as Page ${newPageNum}!`);
+ console.log('=== PROPERTY DATA MAPPED ===');
+ console.log('Mapped property data:', this.propertyData);
+
+ // Load property images
+ await this.loadPropertyImages();
}
}
- // Initialize editor with pages
- initializeEditor() {
- console.log('=== initializeEditor called ===');
- console.log('Selected template data:', this.selectedTemplateData);
- console.log('Property data:', this.propertyData);
-
- if (!this.selectedTemplateData || !this.propertyData) {
- console.error('Missing template or property data for editor initialization');
- this.showError('Please select a template and property first');
+ // Load property images from Image Genie
+ async loadPropertyImages() {
+ if (!this.selectedPropertyId) {
+ this.realPropertyImages = [];
return;
}
- // Generate template HTML with property data
- const templateHTML = this.createTemplateHTML();
- console.log('Generated template HTML:', templateHTML);
-
- // Set the editor content
- this.editorContent = templateHTML;
-
- // Initialize pages for multi-page editing
- this.initializePages();
-
- // Set up editor events
- setTimeout(() => {
- this.setupEditorEvents();
- console.log('Editor initialized successfully');
- this.showSuccess('Editor loaded with your selected template and property data!');
- }, 100);
- }
-
- // Error handling
- clearError() {
- this.error = null;
- }
-
- showError(message) {
- this.error = message;
- setTimeout(() => this.clearError(), 5000);
- }
-
- showSuccess(message) {
- // Create a temporary success message
- const successDiv = document.createElement('div');
- successDiv.style.cssText = `
- position: fixed;
- top: 20px;
- right: 20px;
- background: #28a745;
- color: white;
- padding: 15px 20px;
- border-radius: 5px;
- z-index: 10000;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
- animation: slideInRight 0.3s ease;
- `;
- successDiv.textContent = message;
-
- document.body.appendChild(successDiv);
-
- setTimeout(() => {
- if (document.body.contains(successDiv)) {
- document.body.removeChild(successDiv);
- }
- }, 5000);
- }
-
- @track showPdfPreview = false;
- @track showHeader = false;
- @track progressMessage = '';
-
- // Show PDF preview popup
- showPdfPreview() {
- if (!this.selectedTemplateData || !this.selectedPropertyId) {
- this.showError('Please select a template and property first');
- return;
- }
-
- // Get current editor content
- const editorFrame = this.template.querySelector('.preview-frame');
- if (editorFrame) {
- this.editorContent = editorFrame.innerHTML;
- }
-
- this.showPdfPreview = true;
- this.showSuccess('Preview loaded. Review your content before generating PDF.');
- }
-
- // Close PDF preview popup
- closePdfPreview() {
- this.showPdfPreview = false;
- }
-
- // Edit header section
- editHeader() {
- const headerSection = this.template.querySelector('.header-section');
- if (headerSection) {
- headerSection.contentEditable = !headerSection.contentEditable;
- const editBtn = headerSection.querySelector('.edit-btn');
- if (editBtn) {
- editBtn.textContent = headerSection.contentEditable ? 'πΎ Save' : 'βοΈ Edit Header';
- }
-
- if (headerSection.contentEditable) {
- headerSection.focus();
- this.showSuccess('Header is now editable. Click Save when done.');
- } else {
- this.showSuccess('Header saved successfully!');
- }
- }
- }
-
- // Edit footer section
- editFooter() {
- const footerSection = this.template.querySelector('.footer-section');
- if (footerSection) {
- footerSection.contentEditable = !footerSection.contentEditable;
- const editBtn = footerSection.querySelector('.edit-btn');
- if (editBtn) {
- editBtn.textContent = footerSection.contentEditable ? 'πΎ Save' : 'βοΈ Edit Footer';
- }
-
- if (footerSection.contentEditable) {
- footerSection.focus();
- this.showSuccess('Footer is now editable. Click Save when done.');
- } else {
- this.showSuccess('Footer saved successfully!');
- }
- }
- }
-
- // Add text block with proper positioning (no overlap)
- addTextBlock() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- const textBlock = document.createElement('div');
- textBlock.style.cssText = `
- background: #f8f9fa;
- border: 2px dashed #dee2e6;
- border-radius: 8px;
- padding: 20px;
- margin: 20px 0;
- min-height: 100px;
- position: relative;
- cursor: text;
- clear: both;
- `;
- textBlock.innerHTML = '
New text block - Click to edit
';
- textBlock.contentEditable = true;
-
- // Insert at cursor position or append at the end
- if (window.getSelection && window.getSelection().rangeCount > 0) {
- const selection = window.getSelection();
- const range = selection.getRangeAt(0);
-
- // Check if cursor is inside an existing element
- let targetElement = range.commonAncestorContainer;
- while (targetElement && targetElement.nodeType !== Node.ELEMENT_NODE) {
- targetElement = targetElement.parentElement;
- }
-
- if (targetElement && targetElement !== editorFrame) {
- // Insert after the current element
- targetElement.parentNode.insertBefore(textBlock, targetElement.nextSibling);
- } else {
- // Insert at cursor position
- range.insertNode(textBlock);
- }
- } else {
- // Append at the end
- editorFrame.appendChild(textBlock);
- }
-
- // Focus on the new text block
- textBlock.focus();
-
- this.showSuccess('Text block added! Click to edit content.');
- }
-
- // Insert image with proper positioning
- insertImage() {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = 'image/*';
- input.style.display = 'none';
-
- input.onchange = (e) => {
- const file = e.target.files[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = (event) => {
- const img = document.createElement('img');
- img.src = event.target.result;
- img.style.cssText = `
- max-width: 100%;
- height: auto;
- border-radius: 8px;
- margin: 20px 0;
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
- cursor: move;
- clear: both;
- `;
- img.draggable = true;
-
- this.insertElement(img);
- };
- reader.readAsDataURL(file);
- }
- };
-
- document.body.appendChild(input);
- input.click();
- document.body.removeChild(input);
- }
-
- // Toolbar Methods
- setFontFamily(event) {
- const fontFamily = event.target.value;
- this.execCommand('fontName', false, fontFamily);
- }
-
- setFontSize(event) {
- const fontSize = event.target.value;
- this.execCommand('fontSize', false, fontSize);
- }
-
- setBold() {
- this.execCommand('bold', false, null);
- }
-
- setItalic() {
- this.execCommand('italic', false, null);
- }
-
- setUnderline() {
- this.execCommand('underline', false, null);
- }
-
- setTextColor(event) {
- const color = event.target.value;
- this.execCommand('foreColor', false, color);
- }
-
- setBackgroundColor(event) {
- const color = event.target.value;
- this.execCommand('hiliteColor', false, color);
- }
-
- setTextAlign(event) {
- const align = event.currentTarget.dataset.align;
- this.execCommand('justifyLeft', false, null);
- if (align === 'center') this.execCommand('justifyCenter', false, null);
- if (align === 'right') this.execCommand('justifyRight', false, null);
- if (align === 'justify') this.execCommand('justifyFull', false, null);
- }
-
- insertList(event) {
- const type = event.currentTarget.dataset.type;
- if (type === 'ul') {
- this.execCommand('insertUnorderedList', false, null);
- } else {
- this.execCommand('insertOrderedList', false, null);
- }
- }
-
- decreaseIndent() {
- this.execCommand('outdent', false, null);
- }
-
- increaseIndent() {
- this.execCommand('indent', false, null);
- }
-
- addTextBlock() {
- const textBlock = document.createElement('div');
- textBlock.contentEditable = true;
- textBlock.innerHTML = '
New text block - Click to edit
';
- textBlock.style.cssText = 'padding: 20px; margin: 10px; border: 1px dashed #ccc; background: #f9f9f9; cursor: text;';
- textBlock.onclick = function() { this.focus(); };
-
- const editor = this.template.querySelector('.editor-content');
- editor.appendChild(textBlock);
- }
-
- addImagePlaceholder() {
- const imagePlaceholder = document.createElement('div');
- imagePlaceholder.innerHTML = '
Image Placeholder
Click to upload
';
- imagePlaceholder.style.cssText = 'margin: 10px; cursor: pointer;';
- imagePlaceholder.onclick = () => this.insertImage();
-
- const editor = this.template.querySelector('.editor-content');
- editor.appendChild(imagePlaceholder);
- }
-
- addShape() {
- const shape = document.createElement('div');
- shape.innerHTML = '
Shape
';
- shape.style.cssText = 'margin: 10px; cursor: move;';
- shape.draggable = true;
- this.makeElementDraggable(shape);
-
- const editor = this.template.querySelector('.editor-content');
- editor.appendChild(shape);
- }
-
- insertLink() {
- const url = prompt('Enter URL:');
- if (url) {
- this.execCommand('createLink', false, url);
- }
- }
-
- bringForward() {
- const selection = window.getSelection();
- if (selection.rangeCount > 0) {
- const range = selection.getRangeAt(0);
- const element = range.commonAncestorContainer.nodeType === 1 ? range.commonAncestorContainer : range.commonAncestorContainer.parentElement;
- if (element && element.style) {
- const currentZ = parseInt(element.style.zIndex) || 0;
- element.style.zIndex = currentZ + 1;
- }
- }
- }
-
- sendBackward() {
- const selection = window.getSelection();
- if (selection.rangeCount > 0) {
- const range = selection.getRangeAt(0);
- const element = range.commonAncestorContainer.nodeType === 1 ? range.commonAncestorContainer : range.commonAncestorContainer.parentElement;
- if (element && element.style) {
- const currentZ = parseInt(element.style.zIndex) || 0;
- element.style.zIndex = Math.max(0, currentZ - 1);
- }
- }
- }
-
- addNewPage() {
- const pageBreak = document.createElement('div');
- pageBreak.innerHTML = '
Page Break
';
- pageBreak.style.cssText = 'margin: 10px;';
-
- const editor = this.template.querySelector('.editor-content');
- editor.appendChild(pageBreak);
- }
-
- toggleHeader() {
- this.showHeader = !this.showHeader;
- this.updateHeaderVisibility();
- }
-
- toggleFooter() {
- // Toggle footer visibility
- const footer = this.template.querySelector('.editor-footer');
- if (footer) {
- footer.style.display = footer.style.display === 'none' ? 'block' : 'none';
- }
- }
-
- updateHeaderVisibility() {
- const header = this.template.querySelector('.editor-header');
- if (header) {
- header.style.display = this.showHeader ? 'block' : 'none';
- }
- }
-
- // Handle PDF generation - Direct LWC generation without external API
- async handleDownload() {
- console.log('=== DIRECT PDF GENERATION STARTED ===');
- console.log('Selected template data:', this.selectedTemplateData);
- console.log('Selected property ID:', this.selectedPropertyId);
- console.log('Property data:', this.propertyData);
-
- if (!this.selectedTemplateData || !this.selectedPropertyId) {
- console.error('=== VALIDATION FAILED ===');
- console.error('Missing template or property selection');
- this.showError('Please select a template and property first');
- return;
- }
-
- this.isLoading = true;
- console.log('Starting direct PDF generation...');
-
try {
- // Get current editor content
- const editorFrame = this.template.querySelector('.preview-frame');
- if (editorFrame) {
- this.editorContent = editorFrame.innerHTML;
- console.log('=== EDITOR CONTENT CAPTURED ===');
- console.log('Editor content length:', this.editorContent.length);
- console.log('Editor content preview:', this.editorContent.substring(0, 200) + '...');
- } else {
- console.error('=== EDITOR FRAME NOT FOUND ===');
- console.error('Could not find .preview-frame element');
- this.showError('Editor content not found');
- return;
+ console.log('=== LOADING PROPERTY IMAGES ===');
+ console.log('Property ID:', this.selectedPropertyId);
+
+ const images = await getPropertyImages({ propertyId: this.selectedPropertyId });
+ console.log('Loaded images:', images);
+
+ // Group images by category
+ this.realPropertyImages = images;
+
+ console.log('Real property images loaded:', this.realPropertyImages);
+
+ if (this.realPropertyImages && this.realPropertyImages.length > 0) {
+ const firstCategory = this.realPropertyImages[0].category;
+ console.log('Auto-selecting first available category:', firstCategory);
+ this.selectCategory(firstCategory);
}
-
- // Generate PDF directly using simple approach
- await this.generatePdfSimple();
} catch (error) {
- console.error('=== PDF GENERATION ERROR ===');
- console.error('Error generating PDF:', error);
- console.error('Error stack:', error.stack);
- this.showError('Error generating PDF: ' + error.message);
+ console.error('Error loading property images:', error);
+ this.realPropertyImages = [];
+ }
+ }
+
+ // Market analysis change handler
+ handleMarketAnalysisChange(event) {
+ const { name, checked } = event.target;
+ this.marketAnalysis[name] = checked;
+ }
+
+ // Generate template content
+ async generateTemplateContent() {
+ if (!this.selectedTemplateId || !this.selectedPropertyId) {
+ this.error = 'Please select both a template and a property.';
+ return;
+ }
+
+ this.isLoading = true;
+ this.error = '';
+
+ try {
+ const templateHTML = this.createTemplateHTML();
+
+ // Generate PDF using the template HTML
+ const result = await generatePropertyPDF({
+ propertyData: JSON.stringify(this.propertyData),
+ templateName: this.selectedTemplateId,
+ generatePDF: true
+ });
+
+ if (result.success) {
+ // Handle successful PDF generation
+ console.log('PDF generated successfully:', result.pdfUrl);
+ // You can add logic here to display the PDF or provide download link
+ } else {
+ this.error = result.error || 'Failed to generate PDF.';
+ }
+ } catch (error) {
+ this.error = 'Error generating PDF: ' + error.body.message;
} finally {
this.isLoading = false;
- console.log('=== PDF GENERATION COMPLETED ===');
}
}
- // Show progress indicator
- showProgress(message) {
- this.isLoading = true;
- this.progressMessage = message;
- console.log('Progress:', message);
- }
-
- // Hide progress indicator
- hideProgress() {
- this.isLoading = false;
- this.progressMessage = '';
- }
-
- // Insert table
- insertTable() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) return;
-
- const table = document.createElement('table');
- table.style.cssText = `
- border-collapse: collapse;
- width: 100%;
- margin: 20px 0;
- border: 2px solid #dee2e6;
- border-radius: 8px;
- overflow: hidden;
- `;
-
- // Create table header
- const thead = document.createElement('thead');
- const headerRow = document.createElement('tr');
- for (let i = 0; i < 3; i++) {
- const th = document.createElement('th');
- th.textContent = `Header ${i + 1}`;
- th.style.cssText = `
- background: #667eea;
- color: white;
- padding: 12px;
- text-align: left;
- border: 1px solid #dee2e6;
- `;
- headerRow.appendChild(th);
- }
- thead.appendChild(headerRow);
- table.appendChild(thead);
-
- // Create table body
- const tbody = document.createElement('tbody');
- for (let i = 0; i < 3; i++) {
- const row = document.createElement('tr');
- for (let j = 0; j < 3; j++) {
- const td = document.createElement('td');
- td.textContent = `Cell ${i + 1}-${j + 1}`;
- td.style.cssText = `
- padding: 12px;
- border: 1px solid #dee2e6;
- background: ${i % 2 === 0 ? '#f8f9fa' : 'white'};
- `;
- td.contentEditable = true;
- row.appendChild(td);
- }
- tbody.appendChild(row);
- }
- table.appendChild(tbody);
-
- // Insert at cursor position or append
- if (window.getSelection && window.getSelection().rangeCount > 0) {
- const selection = window.getSelection();
- const range = selection.getRangeAt(0);
- range.insertNode(table);
- } else {
- editorFrame.appendChild(table);
- }
-
- this.showSuccess('Table inserted! Click on cells to edit content.');
- }
-
- // Add missing methods referenced in HTML template
- handleFontFamilyChange(event) {
- const fontFamily = event.target.value;
- this.setFontFamily(fontFamily);
- }
-
- handleFontSizeChange(event) {
- const fontSize = event.target.value;
- this.setFontSize(fontSize);
- }
-
- handleBold() {
- this.setBold();
- }
-
- handleItalic() {
- this.setItalic();
- }
-
- handleUnderline() {
- this.setUnderline();
- }
-
- handleHighlight() {
- this.setTextColor('#ffff00'); // Yellow highlight
- }
-
- handleAlignLeft() {
- this.setTextAlign('left');
- }
-
- handleAlignCenter() {
- this.setTextAlign('center');
- }
-
- handleAlignRight() {
- this.setTextAlign('right');
- }
-
- handleJustify() {
- this.setTextAlign('justify');
- }
-
- handleBulletList() {
- this.insertUnorderedList();
- }
-
- handleNumberList() {
- this.insertOrderedList();
- }
-
- handleIndent() {
- this.increaseIndent();
- }
-
- handleOutdent() {
- this.decreaseIndent();
- }
-
- handleBringForward() {
- this.bringToFront();
- }
-
- handleSendBackward() {
- this.sendToBack();
- }
-
- handleTextColorChange(event) {
- const color = event.target.value;
- this.setTextColor(color);
- }
-
- handleBackgroundColorChange(event) {
- const color = event.target.value;
- this.setBackgroundColor(color);
- }
-
- handleSave() {
- this.saveTemplate();
- }
-
- handleReset() {
- this.resetTemplate();
- }
-
- insertText() {
- this.showTextInsertPopup();
- }
-
- insertImage() {
- this.showImageInsertPopup();
- }
-
- addShape() {
- this.showShapeInsertPopup();
- }
-
- insertTable() {
- this.showTableInsertPopup();
- }
-
- // Text formatting methods
- setFontFamily(fontFamily) {
- document.execCommand('fontName', false, fontFamily);
- }
-
- setFontSize(fontSize) {
- document.execCommand('fontSize', false, fontSize);
- }
-
- setBold() {
- document.execCommand('bold', false, null);
- }
-
- setItalic() {
- document.execCommand('italic', false, null);
- }
-
- setUnderline() {
- document.execCommand('underline', false, null);
- }
-
- setTextColor(color) {
- document.execCommand('foreColor', false, color);
- }
-
- setTextAlign(align) {
- document.execCommand('justify' + align.charAt(0).toUpperCase() + align.slice(1), false, null);
- }
-
- // Template management methods
- saveTemplate() {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (editorFrame) {
- this.editorContent = editorFrame.innerHTML;
- this.showSuccess('Template saved successfully!');
- }
- }
-
- resetTemplate() {
- if (confirm('Are you sure you want to reset the template? This will clear all changes.')) {
- const editorFrame = this.template.querySelector('.preview-frame');
- if (editorFrame) {
- editorFrame.innerHTML = this.editorContent || '';
- this.showSuccess('Template reset successfully!');
- }
- }
- }
-
- // Custom popup methods
- showTextInsertPopup() {
- this.showCustomPopup('Insert Text', `
-
- `);
- }
-
- showImageInsertPopup() {
- this.showCustomPopup('Insert Image', `
-
- `);
- }
-
- showShapeInsertPopup() {
- this.showCustomPopup('Insert Shape', `
-
- `);
- }
-
- showTableInsertPopup() {
- this.showCustomPopup('Insert Table', `
-
- `);
- }
-
- // Helper method to show custom popups
- showCustomPopup(title, content) {
- // Remove any existing popup
- const existingPopup = this.template.querySelector('.custom-popup');
- if (existingPopup) {
- existingPopup.remove();
- }
-
- // Create popup container
- const popup = document.createElement('div');
- popup.className = 'custom-popup';
- popup.innerHTML = `
-
-
- `;
-
- // Add to body
- document.body.appendChild(popup);
-
- // Bind methods to the popup context
- popup.insertTextFromPopup = () => this.insertTextFromPopup();
- popup.insertImageFromPopup = () => this.insertImageFromPopup();
- popup.insertShapeFromPopup = () => this.insertShapeFromPopup();
- popup.insertTableFromPopup = () => this.insertTableFromPopup();
- }
-
- // Popup action methods
- insertTextFromPopup() {
- const textInput = document.getElementById('textInput');
- const textStyle = document.getElementById('textStyle');
-
- if (textInput && textInput.value.trim()) {
- const text = textInput.value.trim();
- const style = textStyle ? textStyle.value : 'p';
- const html = `<${style}>${text}${style}>`;
-
- document.execCommand('insertHTML', false, html);
- document.querySelector('.custom-popup').remove();
- this.showSuccess('Text inserted successfully!');
- }
- }
-
- insertImageFromPopup() {
- const imageUrl = document.getElementById('imageUrl');
- const imageAlt = document.getElementById('imageAlt');
- const imageWidth = document.getElementById('imageWidth');
-
- if (imageUrl && imageUrl.value.trim()) {
- const url = imageUrl.value.trim();
- const alt = imageAlt ? imageAlt.value.trim() : 'Image';
- const width = imageWidth ? imageWidth.value : '300';
-
- const html = `

`;
-
- document.execCommand('insertHTML', false, html);
- document.querySelector('.custom-popup').remove();
- this.showSuccess('Image inserted successfully!');
- }
- }
-
- insertShapeFromPopup() {
- const shapeType = document.getElementById('shapeType');
- const shapeWidth = document.getElementById('shapeWidth');
- const shapeHeight = document.getElementById('shapeHeight');
- const shapeColor = document.getElementById('shapeColor');
-
- if (shapeType && shapeWidth && shapeHeight) {
- const type = shapeType.value;
- const width = shapeWidth.value || '100';
- const height = shapeHeight.value || '100';
- const color = shapeColor ? shapeColor.value : '#667eea';
-
- let html = '';
- switch (type) {
- case 'rectangle':
- html = `
`;
- break;
- case 'circle':
- html = `
`;
- break;
- case 'triangle':
- html = `
`;
- break;
- case 'diamond':
- html = `
`;
- break;
- }
-
- document.execCommand('insertHTML', false, html);
- document.querySelector('.custom-popup').remove();
- this.showSuccess('Shape inserted successfully!');
- }
- }
-
- insertTableFromPopup() {
- const tableRows = document.getElementById('tableRows');
- const tableCols = document.getElementById('tableCols');
- const tableBorder = document.getElementById('tableBorder');
-
- if (tableRows && tableCols) {
- const rows = parseInt(tableRows.value) || 3;
- const cols = parseInt(tableCols.value) || 3;
- const border = tableBorder ? tableBorder.value : '1';
-
- let html = '
';
-
- for (let i = 0; i < rows; i++) {
- html += '';
- for (let j = 0; j < cols; j++) {
- html += `| Cell ${i+1}-${j+1} | `;
- }
- html += '
';
- }
-
- html += '
';
-
- document.execCommand('insertHTML', false, html);
- document.querySelector('.custom-popup').remove();
- this.showSuccess('Table inserted successfully!');
- }
- }
-
- // Create safe property display string
- getPropertyDisplayString(property) {
- if (!property) return 'Unknown Property';
-
- const name = property.Name || 'Unnamed';
- const type = property.pcrm__Property_Type__c || 'N/A';
- const city = property.pcrm__City_Bayut_Dubizzle__c || 'N/A';
-
- return `${name} - ${type} - ${city}`;
- }
-
- // Validate property data
- validatePropertyData(property) {
- if (!property) return false;
-
- const requiredFields = ['Id', 'Name'];
- const hasRequiredFields = requiredFields.every(field => property[field]);
-
- if (!hasRequiredFields) {
- console.warn('Property missing required fields:', property);
- return false;
- }
-
- return true;
- }
-
- // Show property preview
- showPropertyPreview() {
- console.log('=== SHOWING PROPERTY PREVIEW ===');
- console.log('Property Data:', this.propertyData);
-
- // Force a re-render to show the property details
- this.propertyData = { ...this.propertyData };
-
- // Show success message with property details
- const previewMessage = `
- Property Selected: ${this.propertyData.propertyName}
- Type: ${this.propertyData.propertyType}
- Location: ${this.propertyData.location}
- Price: ${this.propertyData.price}
- Bedrooms: ${this.propertyData.bedrooms}
- Bathrooms: ${this.propertyData.bathrooms}
- Area: ${this.propertyData.area}
- `;
-
- console.log('Preview Message:', previewMessage);
- this.showSuccess(`Property preview loaded! ${this.propertyData.propertyName}`);
- }
-
- // Validate property data for PDF generation
- validatePropertyDataForPdf() {
- console.log('=== VALIDATING PROPERTY DATA FOR PDF ===');
-
- if (!this.propertyData) {
- this.showError('No property data available');
- return false;
- }
-
- // Check required fields
- const requiredFields = ['propertyName', 'propertyType', 'location', 'price'];
- const missingFields = requiredFields.filter(field => !this.propertyData[field] || this.propertyData[field] === 'Not specified');
-
- if (missingFields.length > 0) {
- console.error('Missing required fields:', missingFields);
- this.showError(`Missing required fields: ${missingFields.join(', ')}`);
- return false;
- }
-
- // Clean up data for PDF generation
- const cleanedData = {};
- Object.keys(this.propertyData).forEach(key => {
- const value = this.propertyData[key];
- if (value && value !== 'Not specified' && value !== 'N/A') {
- cleanedData[key] = value;
- } else {
- cleanedData[key] = 'Not available';
- }
- });
-
- console.log('Cleaned property data for PDF:', cleanedData);
- this.propertyData = cleanedData;
-
- return true;
- }
-
- // Generate PDF using Python API
- async generatePdfSimple() {
+ // Scroll to Generate PDF button at the top
+ scrollToGeneratePdf() {
try {
- console.log('=== GENERATING PDF VIA PYTHON API ===');
+ // Find the Generate PDF button at the top
+ const generatePdfButton = this.template.querySelector('.export-pdf-btn');
+ if (generatePdfButton) {
+ // Scroll to the button with smooth animation
+ generatePdfButton.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center'
+ });
+
+ // Add a subtle highlight effect
+ generatePdfButton.style.boxShadow = '0 0 20px rgba(102, 126, 234, 0.6)';
+ setTimeout(() => {
+ generatePdfButton.style.boxShadow = '';
+ }, 2000);
+
+ console.log('Scrolled to Generate PDF button successfully');
+ } else {
+ console.warn('Generate PDF button not found');
+ }
+ } catch (error) {
+ console.error('Error scrolling to Generate PDF button:', error);
+ }
+ }
+
+ // Simple PDF generation method for the export button - using the working approach from first prompt
+ async generatePdfSimple() {
+ if (!this.selectedTemplateId || !this.selectedPropertyId) {
+ this.error = 'Please select both a template and a property.';
+ return;
+ }
+
+ try {
+ console.log('=== GENERATING PDF VIA EXTERNAL API ===');
// Show progress
- this.showProgress('Generating PDF via Python API...');
+ this.showProgress('Starting AI-powered PDF generation...');
// Get current editor content
const editorFrame = this.template.querySelector('.preview-frame');
@@ -3064,85 +905,185 @@ export default class PropertyTemplateSelector extends LightningElement {
this.editorContent = editorFrame.innerHTML;
- // Generate PDF using Python API
- await this.generatePdfViaPythonApi();
+ // Generate PDF using external API
+ await this.generatePdfViaExternalApi();
- this.showSuccess('PDF generated successfully!');
+ // Success message is handled in generatePdfViaExternalApi
+ } catch (error) {
+ console.error('=== PDF GENERATION ERROR ===');
+ console.error('Error generating PDF:', error);
+ this.showError('PDF generation failed: ' + error.message);
+ }
+ }
+
+ // Generate PDF via external API using Apex proxy
+ async generatePdfViaExternalApi() {
+ try {
+ console.log('Calling external PDF generation API via Apex proxy...');
+
+ // Show loading state
+ this.isLoading = true;
+ this.showProgress('Preparing content for AI processing...');
+
+ // First, ensure we have template content loaded
+ let htmlContent = '';
+
+ // Check if preview frame has content
+ const previewFrame = this.template.querySelector('.preview-frame');
+ if (!previewFrame) {
+ throw new Error('Preview frame not found');
+ }
+
+ htmlContent = previewFrame.innerHTML;
+ console.log('Initial HTML content from preview frame:', htmlContent);
+ console.log('HTML content length:', htmlContent.length, 'characters');
+
+ // If preview frame is empty, generate the template HTML first
+ if (!htmlContent || htmlContent.trim() === '' || htmlContent.length < 100) {
+ console.log('Preview frame is empty or has minimal content, generating template HTML...');
+ this.showProgress('Generating template content...');
+
+ // Generate the template HTML using the selected template and property
+ if (this.selectedTemplateId && this.selectedPropertyId) {
+ // Create a complete HTML template with property data
+ htmlContent = this.createCompleteTemplateHTML();
+ console.log('Generated template HTML length:', htmlContent.length, 'characters');
+
+ // Load it into the preview frame so user can see it
+ previewFrame.innerHTML = htmlContent;
+ console.log('Template HTML loaded into preview frame');
+ } else {
+ throw new Error('No template or property selected');
+ }
+ }
+
+ // Ensure we have a complete HTML document with page size information
+ if (!htmlContent.includes('')) {
+ htmlContent = `
+
+
+
+
+
Property Brochure - ${this.selectedPageSize}
+
+
+
+
+ ${htmlContent}
+
+`;
+ }
+
+ console.log('Final HTML content to send:', htmlContent);
+ console.log('Final HTML content length:', htmlContent.length, 'characters');
+
+ // Update progress message
+ this.showProgress('Wait, our AI is generating report...');
+
+ // Call the Apex method with the complete HTML and page size
+ const base64PDF = await generatePDFFromHTML({
+ htmlContent: htmlContent,
+ pageSize: this.selectedPageSize
+ });
+
+ if (base64PDF) {
+ console.log('PDF generated successfully via Apex proxy');
+ console.log('PDF base64 length:', base64PDF.length, 'characters');
+
+ // Update progress message
+ this.showProgress('Processing PDF response...');
+
+ // Convert base64 to blob and open/download
+ const pdfBlob = this.base64ToBlob(base64PDF, 'application/pdf');
+ const pdfUrl = window.URL.createObjectURL(pdfBlob);
+
+ console.log('PDF blob created:', pdfBlob.size, 'bytes');
+ console.log('PDF blob type:', pdfBlob.type);
+ console.log('PDF URL created:', pdfUrl);
+
+ // Hide loading state
+ this.isLoading = false;
+ this.hideProgress();
+
+ // Download PDF directly in same tab (no popup needed)
+ const downloadLink = document.createElement('a');
+ downloadLink.href = pdfUrl;
+ downloadLink.download = `${this.selectedProperty?.Name || this.propertyData?.propertyName || 'Property'}_Brochure_${this.selectedPageSize}.pdf`;
+ downloadLink.style.display = 'none';
+
+ // Append to body, click, and remove
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+ document.body.removeChild(downloadLink);
+
+ // Clean up the blob URL to free memory
+ setTimeout(() => {
+ window.URL.revokeObjectURL(pdfUrl);
+ }, 1000);
+
+ // Show success message with download instructions
+ this.showSuccess('PDF generated successfully! Download started...');
+
+ // Fallback: If download doesn't work, show instructions
+ setTimeout(() => {
+ if (this.template.querySelector('.success-message')) {
+ const successMsg = this.template.querySelector('.success-message');
+ successMsg.innerHTML += '
π‘ If download didn\'t start, right-click the PDF link below and select "Save as..."';
+
+ // Add a visible download link as fallback
+ const fallbackLink = document.createElement('a');
+ fallbackLink.href = pdfUrl;
+ fallbackLink.textContent = 'π Click here to download PDF';
+ fallbackLink.className = 'slds-button slds-button_brand';
+ fallbackLink.style.marginTop = '10px';
+ fallbackLink.style.display = 'inline-block';
+
+ successMsg.appendChild(fallbackLink);
+ }
+ }, 2000);
+ } else {
+ throw new Error('PDF generation returned empty result');
+ }
} catch (error) {
console.error('=== PDF GENERATION ERROR ===');
console.error('Error generating PDF:', error);
this.showError('PDF generation failed: ' + error.message);
} finally {
+ this.isLoading = false;
this.hideProgress();
}
}
- // Generate PDF via Python API
- async generatePdfViaPythonApi() {
- try {
- console.log('Calling Python API via Apex proxy...');
-
- // Prepare the HTML content from editor
- const editorFrame = this.template.querySelector('.preview-frame');
- if (!editorFrame) {
- throw new Error('Editor frame not found');
- }
-
- const htmlContent = editorFrame.innerHTML;
-
- // Prepare property data
- const propertyData = {
- propertyName: this.propertyData.propertyName || 'Property Brochure',
- propertyType: this.propertyData.propertyType || 'Property Type',
- location: this.propertyData.location || 'Location',
- price: this.propertyData.price || 'N/A',
- bedrooms: this.propertyData.bedrooms || 'N/A',
- bathrooms: this.propertyData.bathrooms || 'N/A',
- area: this.propertyData.area || 'N/A'
- };
-
- console.log('Calling Apex proxy for Python API...');
-
- // Call Apex method that will proxy the Python API call
- const result = await callPythonApi({
- htmlContent: htmlContent,
- propertyData: JSON.stringify(propertyData),
- templateName: this.selectedTemplate || 'default'
- });
-
- if (result) {
- console.log('PDF generated successfully via Apex proxy');
-
- // Convert base64 to blob and download
- const pdfBlob = this.base64ToBlob(result, 'application/pdf');
- const url = window.URL.createObjectURL(pdfBlob);
- const link = document.createElement('a');
- link.href = url;
- link.download = `property_brochure_${Date.now()}.pdf`;
-
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
-
- window.URL.revokeObjectURL(url);
-
- this.showSuccess('PDF generated and downloaded successfully!');
- } else {
- throw new Error('No PDF content returned from Apex proxy');
- }
-
- } catch (error) {
- console.error('Error in generatePdfViaPythonApi:', error);
- this.showError(`PDF generation failed: ${error.message}`);
-
- // Fallback to Apex method
- console.log('Falling back to Apex PDF generation...');
- await this.generatePdfViaApex();
- }
- }
-
- // Helper method to convert base64 to blob
+ // Helper method to convert base64 to blob (from first prompt)
base64ToBlob(base64, mimeType) {
const byteCharacters = atob(base64);
const byteNumbers = new Array(byteCharacters.length);
@@ -3152,60 +1093,1941 @@ export default class PropertyTemplateSelector extends LightningElement {
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], { type: mimeType });
}
-
- // Fallback Apex method
- async generatePdfViaApex() {
+
+ // Create complete template HTML with property data
+ createCompleteTemplateHTML() {
try {
- console.log('Calling Apex PDF generation...');
+ console.log('Creating complete template HTML...');
+ console.log('Selected template ID:', this.selectedTemplateId);
+ console.log('Selected property ID:', this.selectedPropertyId);
+ console.log('Property data:', this.selectedProperty);
- // Prepare property data
- const propertyData = {
- propertyName: this.propertyData.propertyName || 'Property Brochure',
- propertyType: this.propertyData.propertyType || 'Property Type',
- location: this.propertyData.location || 'Location',
- price: this.propertyData.price || 'N/A',
- bedrooms: this.propertyData.bedrooms || 'N/A',
- bathrooms: this.propertyData.bathrooms || 'N/A',
- area: this.propertyData.area || 'N/A'
- };
-
- // Create Visualforce page URL with parameters
- const baseUrl = window.location.origin;
- const visualforceUrl = `${baseUrl}/apex/PropertyPdfGenerator`;
-
- // Add query parameters
- const params = new URLSearchParams({
- template: this.selectedTemplate || 'default',
- propertyData: JSON.stringify(propertyData),
- pdfId: Date.now().toString()
- });
-
- const fullUrl = `${visualforceUrl}?${params.toString()}`;
-
- console.log('Opening PDF URL:', fullUrl);
-
- // Open the Visualforce page in a new window/tab
- const pdfWindow = window.open(fullUrl, '_blank');
-
- if (pdfWindow) {
- setTimeout(() => {
- try {
- if (!pdfWindow.closed) {
- pdfWindow.close();
- }
- } catch (e) {
- console.log('Could not check window status');
- }
- }, 2000);
- } else {
- throw new Error('Could not open PDF window. Popup may be blocked.');
+ if (!this.selectedProperty) {
+ throw new Error('No property data available');
}
- console.log('PDF generation initiated via Apex');
+ // Create a professional property brochure HTML
+ const html = `
+
+
+
+
+
+
+
+
+
+
+
Property Information
+
+
+ Property Type:
+ ${this.selectedProperty.Property_Type__c || 'N/A'}
+
+
+
+ Location:
+ ${this.selectedProperty.Location__c || 'N/A'}
+
+
+
+ Price:
+ ${this.selectedProperty.Price__c ? '$' + this.selectedProperty.Price__c.toLocaleString() : 'N/A'}
+
+
+
+
+
Additional Details
+
+
+ Bedrooms:
+ ${this.selectedProperty.Bedrooms__c || 'N/A'}
+
+
+
+ Bathrooms:
+ ${this.selectedProperty.Bathrooms__c || 'N/A'}
+
+
+
+ Square Feet:
+ ${this.selectedProperty.Square_Feet__c ? this.selectedProperty.Square_Feet__c.toLocaleString() + ' sq ft' : 'N/A'}
+
+
+
+
+
+
+
Description
+
+ ${this.selectedProperty.Description__c || 'This beautiful property offers exceptional value and is located in a prime area. Contact us for more details and to schedule a viewing.'}
+
+
+
+
+
+
+
+
+
+ `;
+
+ console.log('Complete template HTML generated successfully');
+ return html;
} catch (error) {
- console.error('Error in generatePdfViaApex:', error);
- throw error;
+ console.error('Error creating template HTML:', error);
+ // Return a fallback HTML if there's an error
+ return `
+
+
Property Brochure
+
Property: ${this.selectedProperty?.Name || 'Selected Property'}
+
Template: ${this.selectedTemplateId || 'Selected Template'}
+
Generated on: ${new Date().toLocaleDateString()}
+
+ `;
}
}
-}
+
+ // Progress and message methods (from first prompt)
+ showProgress(message) {
+ this.isLoading = true;
+ this.progressMessage = message;
+ console.log('Progress:', message);
+ }
+
+ hideProgress() {
+ this.isLoading = false;
+ this.progressMessage = '';
+ }
+
+ showSuccess(message) {
+ this.progressMessage = message;
+ this.error = '';
+ console.log('Success:', message);
+ }
+
+ showError(message) {
+ this.error = message;
+ this.progressMessage = '';
+ console.error('Error:', message);
+ }
+
+ // Handle PDF response using the working approach from first prompt
+ handlePdfResponse(result) {
+ try {
+ console.log('Handling PDF response:', result);
+
+ if (result.pdfUrl) {
+ // For the working approach, we need to create a proper download
+ this.downloadPDF(result.pdfUrl);
+ } else {
+ this.error = 'PDF generated but no download URL received';
+ }
+ } catch (error) {
+ console.error('Error handling PDF response:', error);
+ this.error = 'Error handling PDF response: ' + error.message;
+ }
+ }
+
+ // Download PDF method
+ downloadPDF(pdfUrl) {
+ try {
+ // Create a temporary link element
+ const link = document.createElement('a');
+ link.href = pdfUrl;
+ link.download = `property-brochure-${this.selectedTemplateId}-${Date.now()}.pdf`;
+
+ // Append to body, click, and remove
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ this.progressMessage = 'PDF download started!';
+ console.log('PDF download initiated:', pdfUrl);
+
+ // Fallback: If download doesn't work, show instructions
+ setTimeout(() => {
+ if (this.template.querySelector('.progress-message')) {
+ const progressMsg = this.template.querySelector('.progress-message');
+ progressMsg.innerHTML += '
π‘ If download didn\'t start, right-click the PDF link below and select "Save as..."';
+
+ // Add a visible download link as fallback
+ const fallbackLink = document.createElement('a');
+ fallbackLink.href = pdfUrl;
+ fallbackLink.textContent = 'π Click here to download PDF';
+ fallbackLink.className = 'slds-button slds-button_brand';
+ fallbackLink.style.marginTop = '10px';
+ fallbackLink.style.display = 'inline-block';
+
+ progressMsg.appendChild(fallbackLink);
+ }
+ }, 2000);
+ } catch (error) {
+ console.error('Error downloading PDF:', error);
+ this.error = 'Error downloading PDF: ' + error.message;
+ }
+ }
+
+ // Create template HTML based on selection
+ createTemplateHTML() {
+ console.log('=== CREATE TEMPLATE HTML DEBUG ===');
+ console.log('this.selectedTemplateId:', this.selectedTemplateId);
+ console.log('typeof this.selectedTemplateId:', typeof this.selectedTemplateId);
+
+ switch (this.selectedTemplateId) {
+ case 'blank-template':
+ console.log('Returning blank template');
+ return this.createBlankTemplate();
+ case 'everkind-template':
+ console.log('Returning everkind template');
+ return this.createEverkindTemplate();
+ case 'shift-template':
+ console.log('Returning shift template');
+ return this.createShiftTemplate();
+ case 'saintbarts-template':
+ console.log('Returning saintbarts template');
+ return this.createSaintbartsTemplate();
+ case 'learnoy-template':
+ console.log('Returning learnoy template');
+ return this.createLearnoyTemplate();
+ case 'leafamp-template':
+ console.log('Returning leafamp template');
+ return this.createLeafampTemplate();
+ case 'coreshift-template':
+ console.log('Returning coreshift template');
+ return this.createCoreshiftTemplate();
+ case 'modern-home-template':
+ console.log('Returning modern home template');
+ return this.createModernHomeTemplate();
+ case 'asgar-1-template':
+ console.log('Returning asgar1 template');
+ return this.createAsgar1Template();
+ case 'sample-template':
+ console.log('Returning sample template');
+ return this.createSampleTemplate();
+ case 'serenity-house-template':
+ console.log('Returning serenity house template');
+ return this.createSerenityHouseTemplate();
+ case 'luxury-mansion-template':
+ console.log('Returning luxury mansion template');
+ return this.createLuxuryMansionTemplate();
+ default:
+ console.log('No matching case found, returning blank template');
+ console.log('Available cases: blank-template, everkind-template, shift-template, saintbarts-template, learnoy-template, leafamp-template, coreshift-template, modern-home-template, asgar-1-template, sample-template, luxury-mansion-template');
+ return this.createBlankTemplate();
+ }
+ }
+
+ // Template methods
+ createBlankTemplate() {
+ const data = this.propertyData || {};
+ const propertyName = data.propertyName || "Property Name";
+ const location = data.location || "Location";
+ const price = data.price || "Price";
+ const bedrooms = data.bedrooms || "N/A";
+ const bathrooms = data.bathrooms || "N/A";
+ const size = data.size || "N/A";
+ const sizeUnit = data.sizeUnit || "sq ft";
+ const status = data.status || "Available";
+ const propertyType = data.propertyType || "Property Type";
+ const city = data.city || "City";
+ const community = data.community || "Community";
+ const subCommunity = data.subCommunity || "Sub Community";
+ const furnished = data.furnished || "N/A";
+ const parkingSpaces = data.parkingSpaces || "N/A";
+ const buildYear = data.buildYear || "N/A";
+ const titleEnglish = data.titleEnglish || "Property Title";
+ const descriptionEnglish = data.descriptionEnglish || "Property Description";
+ const rentPriceMin = data.rentPriceMin || "N/A";
+ const salePriceMin = data.salePriceMin || "N/A";
+
+ return `
+
+
+
+
${propertyName}
+
${location}
+
${price}
+
+
+
+
+
Basic Information
+
+
Property Type: ${propertyType}
+
Status: ${status}
+
City: ${city}
+
Community: ${community}
+
Sub Community: ${subCommunity}
+
Furnished: ${furnished}
+
+
+
+
+
+
Contact Details
+
+
Name: ${data.contactName || 'N/A'}
+
Email: ${data.contactEmail || 'N/A'}
+
Phone: ${data.contactPhone || 'N/A'}
+
+
+
+
+
+
Location Details
+
+
City (Bayut): ${data.cityBayut || 'N/A'}
+
City (Propertyfinder): ${data.cityPropertyfinder || 'N/A'}
+
Community (Bayut): ${data.communityBayut || 'N/A'}
+
Sub Community (Bayut): ${data.subCommunityBayut || 'N/A'}
+
Locality (Bayut): ${data.localityBayut || 'N/A'}
+
Sub Locality (Bayut): ${data.subLocalityBayut || 'N/A'}
+
Tower (Bayut): ${data.towerBayut || 'N/A'}
+
Unit Number: ${data.unitNumber || 'N/A'}
+
+
+
+
+
+
Specifications
+
+
Bedrooms: ${bedrooms}
+
Bathrooms: ${bathrooms}
+
Size: ${size} ${sizeUnit}
+
Parking Spaces: ${parkingSpaces}
+
Build Year: ${buildYear}
+
Floor: ${data.floor || 'N/A'}
+
+
+
+
+
+
Pricing Information
+
+
Rent Price: ${rentPriceMin}
+
Sale Price: ${salePriceMin}
+
Rent Price (Max): ${data.rentPriceMax || 'N/A'}
+
Sale Price (Max): ${data.salePriceMax || 'N/A'}
+
+
+
+
+
+
Rent Availability
+
+
Rent Available From: ${data.rentAvailableFrom || 'N/A'}
+
Rent Available To: ${data.rentAvailableTo || 'N/A'}
+
+
+
+
+
+
Description
+
+ Title: ${titleEnglish}
+
+
+ ${descriptionEnglish}
+
+
+
+
+
+
Amenities & Features
+
+
Parking Spaces: ${data.parkingSpaces || 'N/A'}
+
Furnished: ${data.furnished || 'N/A'}
+
Offering Type: ${data.offeringType || 'N/A'}
+
Build Year: ${data.buildYear || 'N/A'}
+
+
+
+
+
+
Custom Content Area
+
This is a blank template that you can customize with your own content.
+
Add images, text, and styling to create your perfect property brochure.
+
+
+ `;
+ }
+
+ createEverkindTemplate() {
+ return `
Luxury VillaLuxury Villa Template
Elegance. Precision. Luxury.
`;
+ }
+
+ createShiftTemplate() {
+ const data = this.propertyData || {};
+ const propertyName = data.propertyName || "SHIFT PROPERTY";
+ const location = data.location || "Modern Living";
+ const price = data.price || "Starting from $1,500,000";
+ const referenceId = data.referenceNumber || "SP-2025-001";
+
+ return `
Shift Property - Modern LivingAbout Shift Property
Experience the future of living with Shift Property, where innovation meets comfort in perfect harmony.
Contact Information
Reference ID: ${referenceId}
Agent: ${data.agentName || "Innovation Specialist"}
Phone: ${data.agentPhone || "(555) 789-0123"}
`;
+ }
+
+ createSaintbartsTemplate() {
+ const data = this.propertyData || {};
+ const propertyName = data.propertyName || "SAINT BARTS VILLA";
+ const location = data.location || "Caribbean Paradise";
+ const price = data.price || "Starting from $3,200,000";
+ const referenceId = data.referenceNumber || "SB-2025-001";
+
+ return `
Saint Barts Villa - Caribbean ParadiseAbout Saint Barts Villa
Discover paradise at Saint Barts Villa, where tropical luxury meets Caribbean charm in an idyllic setting.
Contact Information
Reference ID: ${referenceId}
Agent: ${data.agentName || "Caribbean Specialist"}
Phone: ${data.agentPhone || "(555) 456-7890"}
`;
+ }
+
+ createLearnoyTemplate() {
+ const data = this.propertyData || {};
+ const propertyName = data.propertyName || "LEARNOY ESTATE";
+ const location = data.location || "Heritage Collection";
+ const price = data.price || "Starting from $2,800,000";
+ const referenceId = data.referenceNumber || "LE-2025-001";
+
+ return `
Learnoy Estate - Heritage CollectionAbout Learnoy Estate
Experience timeless elegance at Learnoy Estate, where heritage meets modern luxury in perfect harmony.
Contact Information
Reference ID: ${referenceId}
Agent: ${data.agentName || "Heritage Specialist"}
Phone: ${data.agentPhone || "(555) 321-6540"}
`;
+ }
+
+ createModernHomeTemplate() {
+ console.log("=== CREATING REAL ESTATE MODERN HOME TEMPLATE ===");
+
+ const data = this.propertyData || {};
+ const propertyName = data.propertyName || "Property Name";
+ const propertyType = data.propertyType || "Property Type";
+ const location = data.location || "Location";
+ const price = data.price || "Price on Request";
+ const bedrooms = data.bedrooms || "N/A";
+ const bathrooms = data.bathrooms || "N/A";
+ const area = data.area || "N/A";
+ const description = data.description || "This beautiful property offers exceptional value and modern amenities. Located in a prime area, it represents an excellent investment opportunity.";
+
+ return `
`;
+ }
+
+ createAsgar1Template() {
+ const data = this.propertyData || {};
+
+ // Basic property information
+ const propertyName = data.propertyName || "The Grand Oak Villa";
+ const location = data.location || "123 Luxury Lane, Prestige City, PC 45678";
+ const price = data.price || "$4,500,000";
+ const referenceId = data.referenceNumber || "GV-2025-001";
+ const bedrooms = data.bedrooms || "5";
+ const bathrooms = data.bathrooms || "6";
+ const area = data.area || "6,200";
+ const squareFeet = data.area || "6,200";
+ const sizeUnit = data.sizeUnit || "sq ft";
+
+ // Contact information
+ const contactName = data.contactName || "Contact Name";
+ const contactPhone = data.contactPhone || "Contact Phone";
+ const contactEmail = data.contactEmail || "contact@email.com";
+ const ownerName = data.ownerName || "Owner Name";
+ const ownerPhone = data.ownerPhone || "Owner Phone";
+ const ownerEmail = data.ownerEmail || "owner@email.com";
+
+ // Property specifications
+ const status = data.status || "For Sale";
+ const propertyType = data.propertyType || "Single-Family Home";
+ const yearBuilt = data.yearBuilt || "2023";
+ const floor = data.floor || "2 Levels";
+ const parking = data.parking || "3-Car Garage";
+ const furnishing = data.furnishing || "Partially Furnished";
+ const maintenanceFee = data.maintenanceFee || "$1,200/month";
+ const serviceCharge = data.serviceCharge || "Included";
+
+ // Property details
+ const acres = data.acres || "0.75";
+
+ // Location details
+ const cityBayut = data.cityBayut || "City";
+ const cityPropertyfinder = data.cityPropertyfinder || "City";
+ const communityBayut = data.communityBayut || "Community";
+ const subCommunityBayut = data.subCommunityBayut || "Sub Community";
+ const localityBayut = data.localityBayut || "Locality";
+ const subLocalityBayut = data.subLocalityBayut || "Sub Locality";
+ const towerBayut = data.towerBayut || "Tower";
+
+ // Nearby amenities
+ const schools = data.schools || "5 min drive";
+ const shoppingCenters = data.shoppingCenters || "10 min drive";
+ const hospitals = data.hospitals || "12 min drive";
+ const countryClub = data.countryClub || "8 min drive";
+ const airportDistance = data.airportDistance || "20 min drive";
+ const nearbyLandmarks = data.nearbyLandmarks || "Nearby Landmarks";
+ const transportation = data.transportation || "Public Transport";
+ const beachDistance = data.beachDistance || "15 min drive";
+ const metroDistance = data.metroDistance || "8 min walk";
+
+ // Additional information
+ const petFriendly = data.petFriendly || "By Approval";
+ const smokingAllowed = data.smokingAllowed || "Not Permitted";
+ const availableFrom = data.availableFrom || "Immediate";
+ const utilitiesIncluded = data.utilitiesIncluded || "Not Included";
+ const internetIncluded = data.internetIncluded || "Not Included";
+ const cableIncluded = data.cableIncluded || "Not Included";
+
+ // Additional property fields
+ const titleEnglish = data.titleEnglish || "Property Title";
+ const descriptionEnglish = data.descriptionEnglish || "Property Description";
+ const amenities = data.amenities || "No specific amenities listed.";
+ const features = data.features || "No specific features listed.";
+ const size = data.size || "N/A";
+ const parkingSpaces = data.parkingSpaces || "N/A";
+ const buildYear = data.buildYear || "N/A";
+ const offeringType = data.offeringType || "N/A";
+
+ // Financial and availability fields
+ const rentPriceMin = data.rentPriceMin || "N/A";
+ const salePriceMin = data.salePriceMin || "N/A";
+ const rentAvailableFrom = data.rentAvailableFrom || "N/A";
+ const rentAvailableTo = data.rentAvailableTo || "N/A";
+ const minimumContract = data.minimumContract || "N/A";
+ const securityDeposit = data.securityDeposit || "N/A";
+
+ return `
`;
+ }
+
+ createSampleTemplate() {
+ const data = this.propertyData || {};
+ const propertyName = data.propertyName || "The Serenity House";
+ const location = data.location || "123 Luxury Lane, Prestige City, PC 45678";
+ const price = data.price || "$4,500,000";
+ const referenceId = data.referenceNumber || "ES-8821";
+ const agentName = data.agentName || "Olivia Sterling";
+ const agentPhone = data.agentPhone || "(555) 987-6543";
+ const agentEmail = data.agentEmail || "olivia@elysianestates.com";
+ const ownerName = data.ownerName || "John & Jane Doe";
+ const ownerPhone = data.ownerPhone || "(555) 111-2222";
+ const ownerEmail = data.ownerEmail || "owner.serenity@email.com";
+
+ return `
`;
+ }
+
+ createLuxuryMansionTemplate() {
+ const data = this.propertyData || {};
+
+ // Extract all available property data with fallbacks
+ const propertyName = data.propertyName || "THE VERTICE";
+ const location = data.location || "18 Skyline Avenue, Metropolis Centre, MC 90210";
+ const referenceId = data.referenceNumber || "VP-2025-001";
+ const agentName = data.agentName || "Alexander Valentine";
+ const agentPhone = data.agentPhone || "(555) 123-9876";
+ const agentEmail = data.agentEmail || "alex.v@elysian.com";
+
+ // Dynamic pricing with fallbacks
+ const price = data.salePriceMin || data.rentPriceMin || data.price || "Starting from $1,200,000";
+
+ // Dynamic property details
+ const bedrooms = data.bedrooms || "2-3";
+ const bathrooms = data.bathrooms || "2-3.5";
+ const squareFeet = data.squareFeet || data.area || "1,450 - 3,200";
+ const propertyType = data.propertyType || "Condominium";
+ const status = data.status || data.offeringType || "New Development";
+ const yearBuilt = data.yearBuilt || data.buildYear || "2025";
+ const furnishing = data.furnishing || "Unfurnished";
+ const parking = data.parking || "2 Spaces per Unit";
+
+ // Dynamic description
+ const description = data.descriptionEnglish || data.description || "A bold statement on modern urban living.";
+
+ // Dynamic location details
+ const schools = data.schools || "Metropolis Intl. (10 min)";
+ const shopping = data.shoppingCenters || "The Galleria Mall (8 min)";
+ const hospitals = data.hospitals || "City Medical Center (15 min)";
+ const landmarks = data.landmarks || "Central Park (5 min)";
+ const transportation = data.transportation || "Metro Line A (2 min walk)";
+ const airport = data.airportDistance || "25 min drive";
+
+ // Dynamic additional info
+ const petFriendly = data.petFriendly || "Allowed (w/ restrictions)";
+ const smoking = data.smokingAllowed || "In designated areas";
+ const availability = data.availableFrom || "Q4 2025";
+ const utilities = data.utilitiesIncluded || "Sub-metered";
+
+ // Dynamic amenities
+ const amenities = data.amenities || [
+ "Rooftop Infinity Pool", "Fitness Center", "Residents' Sky Lounge",
+ "Private Cinema Room", "Wellness Spa & Sauna", "Business Center",
+ "24/7 Concierge", "Secure Parking"
+ ];
+
+ return `