Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17a89486e0 | |||
| eab0d41a64 | |||
| 0c721ef7ed | |||
| c978c48fb6 | |||
| 8b183d96be | |||
| 55c207b3cf | |||
|
|
1e9dda64a6 | ||
|
|
82ef064e5e | ||
|
|
3cb9090248 | ||
|
|
9a33862445 | ||
|
|
2bb4747426 | ||
|
|
9bb7f6a727 |
18473
backup_propertyTemplateSelector.js
Normal file
18473
backup_propertyTemplateSelector.js
Normal file
File diff suppressed because one or more lines are too long
@ -20,6 +20,7 @@ public with sharing class PropertyDataController {
|
||||
'pcrm__Sub_Community_Propertyfinder__c, pcrm__Property_Name_Propertyfinder__c, ' +
|
||||
'pcrm__City_Propertyfinder__c, ' +
|
||||
'pcrm__Rent_Available_From__c, pcrm__Rent_Available_To__c, ' +
|
||||
'Private_Amenities__c, ' +
|
||||
'Contact__c, Contact__r.FirstName, Contact__r.LastName, Contact__r.Email, Contact__r.Phone, ' +
|
||||
'Email__c, Phone__c, ' +
|
||||
'CreatedBy.Name, LastModifiedBy.Name, Owner.Name, ' +
|
||||
@ -55,6 +56,7 @@ public with sharing class PropertyDataController {
|
||||
System.debug('Build Year: ' + firstProp.pcrm__Build_Year__c);
|
||||
System.debug('Parking Spaces: ' + firstProp.pcrm__Parking_Spaces__c);
|
||||
System.debug('Offering Type: ' + firstProp.pcrm__Offering_Type__c);
|
||||
System.debug('Private Amenities: ' + firstProp.Private_Amenities__c);
|
||||
}
|
||||
|
||||
return properties;
|
||||
@ -87,6 +89,7 @@ public with sharing class PropertyDataController {
|
||||
'pcrm__Sub_Community_Propertyfinder__c, pcrm__Property_Name_Propertyfinder__c, ' +
|
||||
'pcrm__City_Propertyfinder__c, ' +
|
||||
'pcrm__Rent_Available_From__c, pcrm__Rent_Available_To__c, ' +
|
||||
'Private_Amenities__c, ' +
|
||||
'Contact__c, Contact__r.FirstName, Contact__r.LastName, Contact__r.Email, Contact__r.Phone, ' +
|
||||
'Email__c, Phone__c, ' +
|
||||
'CreatedBy.Name, LastModifiedBy.Name, Owner.Name, ' +
|
||||
@ -117,6 +120,7 @@ public with sharing class PropertyDataController {
|
||||
System.debug('Build Year: ' + property.pcrm__Build_Year__c);
|
||||
System.debug('Parking Spaces: ' + property.pcrm__Parking_Spaces__c);
|
||||
System.debug('Offering Type: ' + property.pcrm__Offering_Type__c);
|
||||
System.debug('Private Amenities: ' + property.Private_Amenities__c);
|
||||
}
|
||||
|
||||
return property;
|
||||
@ -175,4 +179,142 @@ public with sharing class PropertyDataController {
|
||||
throw new AuraHandledException('Failed to fetch property images: ' + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@AuraEnabled(cacheable=true)
|
||||
public static User getAgentData(String propertyId) {
|
||||
try {
|
||||
System.debug('=== FETCHING AGENT DATA FOR PROPERTY ===');
|
||||
System.debug('Property ID: ' + propertyId);
|
||||
|
||||
// First, get the property to find the related agent/owner
|
||||
pcrm__Property__c property = [
|
||||
SELECT Id, OwnerId, CreatedById, LastModifiedById, Contact__c, Contact__r.OwnerId
|
||||
FROM pcrm__Property__c
|
||||
WHERE Id = :propertyId
|
||||
LIMIT 1
|
||||
];
|
||||
|
||||
if (property == null) {
|
||||
System.debug('Property not found for ID: ' + propertyId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to get agent data from different sources in priority order
|
||||
User agentUser = null;
|
||||
|
||||
// Priority 1: Contact's owner (if contact exists)
|
||||
if (property.Contact__c != null && property.Contact__r.OwnerId != null) {
|
||||
try {
|
||||
agentUser = [
|
||||
SELECT Id, Name, FirstName, LastName, Email, Phone, MobilePhone,
|
||||
Title, Department, CompanyName, SmallPhotoUrl, FullPhotoUrl,
|
||||
Profile.Name, UserRole.Name
|
||||
FROM User
|
||||
WHERE Id = :property.Contact__r.OwnerId
|
||||
AND IsActive = true
|
||||
LIMIT 1
|
||||
];
|
||||
System.debug('Found agent from Contact Owner: ' + agentUser?.Name);
|
||||
} catch (Exception e) {
|
||||
System.debug('Error fetching Contact Owner: ' + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 2: Property Owner (if not found above)
|
||||
if (agentUser == null && property.OwnerId != null) {
|
||||
try {
|
||||
agentUser = [
|
||||
SELECT Id, Name, FirstName, LastName, Email, Phone, MobilePhone,
|
||||
Title, Department, CompanyName, SmallPhotoUrl, FullPhotoUrl,
|
||||
Profile.Name, UserRole.Name
|
||||
FROM User
|
||||
WHERE Id = :property.OwnerId
|
||||
AND IsActive = true
|
||||
LIMIT 1
|
||||
];
|
||||
System.debug('Found agent from Property Owner: ' + agentUser?.Name);
|
||||
} catch (Exception e) {
|
||||
System.debug('Error fetching Property Owner: ' + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 3: Property Creator (if not found above)
|
||||
if (agentUser == null && property.CreatedById != null) {
|
||||
try {
|
||||
agentUser = [
|
||||
SELECT Id, Name, FirstName, LastName, Email, Phone, MobilePhone,
|
||||
Title, Department, CompanyName, SmallPhotoUrl, FullPhotoUrl,
|
||||
Profile.Name, UserRole.Name
|
||||
FROM User
|
||||
WHERE Id = :property.CreatedById
|
||||
AND IsActive = true
|
||||
LIMIT 1
|
||||
];
|
||||
System.debug('Found agent from Property Creator: ' + agentUser?.Name);
|
||||
} catch (Exception e) {
|
||||
System.debug('Error fetching Property Creator: ' + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (agentUser != null) {
|
||||
System.debug('=== AGENT DATA FETCHED ===');
|
||||
System.debug('Agent Name: ' + agentUser.Name);
|
||||
System.debug('Agent Email: ' + agentUser.Email);
|
||||
System.debug('Agent Phone: ' + agentUser.Phone);
|
||||
System.debug('Agent Title: ' + agentUser.Title);
|
||||
System.debug('Agent Department: ' + agentUser.Department);
|
||||
} else {
|
||||
System.debug('No agent found for property: ' + propertyId);
|
||||
}
|
||||
|
||||
return agentUser;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.debug('Error fetching agent data: ' + e.getMessage());
|
||||
System.debug('Stack trace: ' + e.getStackTraceString());
|
||||
throw new AuraHandledException('Failed to fetch agent data: ' + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@AuraEnabled(cacheable=true)
|
||||
public static pcrm__Listing__c getListingData(String propertyId) {
|
||||
try {
|
||||
System.debug('=== FETCHING LISTING DATA BY PROPERTY ===');
|
||||
System.debug('Property ID: ' + propertyId);
|
||||
|
||||
// Query listing with related property and agent data using property ID
|
||||
String query = 'SELECT Id, Name, ' +
|
||||
'Property__r.Id, Property__r.Name, ' +
|
||||
'Listing_Agent__r.Id, Listing_Agent__r.Name, Listing_Agent__r.Email, Listing_Agent__r.Phone, Listing_Agent__r.Title, ' +
|
||||
'Select_Agent__r.Id, Select_Agent__r.Name, Select_Agent__r.Email, Select_Agent__r.Phone, Select_Agent__r.Title ' +
|
||||
'FROM pcrm__Listing__c ' +
|
||||
'WHERE Property__c = :propertyId ' +
|
||||
'ORDER BY CreatedDate DESC LIMIT 1';
|
||||
|
||||
List<pcrm__Listing__c> listings = Database.query(query);
|
||||
pcrm__Listing__c listing = listings.isEmpty() ? null : listings[0];
|
||||
|
||||
if (listing != null) {
|
||||
System.debug('=== LISTING DATA FETCHED ===');
|
||||
System.debug('Listing Name: ' + listing.Name);
|
||||
System.debug('Property ID: ' + listing.Property__r?.Id);
|
||||
System.debug('Property Name: ' + listing.Property__r?.Name);
|
||||
System.debug('Listing Agent ID: ' + listing.Listing_Agent__r?.Id);
|
||||
System.debug('Listing Agent Name: ' + listing.Listing_Agent__r?.Name);
|
||||
System.debug('Listing Agent Email: ' + listing.Listing_Agent__r?.Email);
|
||||
System.debug('Listing Agent Phone: ' + listing.Listing_Agent__r?.Phone);
|
||||
System.debug('Select Agent ID: ' + listing.Select_Agent__r?.Id);
|
||||
System.debug('Select Agent Name: ' + listing.Select_Agent__r?.Name);
|
||||
} else {
|
||||
System.debug('No listing found for Property ID: ' + propertyId);
|
||||
}
|
||||
|
||||
return listing;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.debug('Error fetching listing data: ' + e.getMessage());
|
||||
System.debug('Stack trace: ' + e.getStackTraceString());
|
||||
throw new AuraHandledException('Failed to fetch listing data: ' + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<context>Communities</context>
|
||||
<description>Font Awesome CDN Trusted Site</description>
|
||||
<endpointUrl>https://cdnjs.cloudflare.com</endpointUrl>
|
||||
<isActive>true</isActive>
|
||||
</CspTrustedSite>
|
||||
@ -5,10 +5,12 @@
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
||||
z-index: 9999;
|
||||
z-index: 99999;
|
||||
overflow-y: auto;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dev-header {
|
||||
@ -46,30 +48,69 @@
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.close-dev-btn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #ff4757;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
.dev-exit-section {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.dev-input {
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
padding: 8px 12px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
letter-spacing: 1px;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.close-dev-btn:hover {
|
||||
background: #ff3742;
|
||||
transform: scale(1.1);
|
||||
.dev-input:focus {
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.dev-input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.dev-close-btn {
|
||||
margin-top: 10px;
|
||||
padding: 8px 16px;
|
||||
background: #ff6b6b;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dev-close-btn:hover {
|
||||
background: #ff5252;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
|
||||
.dev-content {
|
||||
padding: 30px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dev-section {
|
||||
|
||||
@ -3,6 +3,17 @@
|
||||
<div class="dev-header">
|
||||
<h1>🚧 Development Area 🚧</h1>
|
||||
<p class="dev-warning">UNDER DEVELOPMENT - DO NOT TEST</p>
|
||||
<div class="dev-exit-section">
|
||||
<input
|
||||
type="text"
|
||||
class="dev-input"
|
||||
value={tbizzInput}
|
||||
onchange={handleTbizzInputChange}
|
||||
onkeydown={handleKeyPress}
|
||||
placeholder="Enter 'bweixx' to show/hide dev mode"
|
||||
/>
|
||||
<button class="dev-close-btn" onclick={hideDevPage}>✕ Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dev-content">
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
import { LightningElement, track } from 'lwc';
|
||||
|
||||
export default class DevelopmentPage extends LightningElement {
|
||||
@track showDevPage = false;
|
||||
@track showDevPage = false; // Hidden by default, can be shown by entering bweixx
|
||||
@track currentStep = 1;
|
||||
@track selectedTemplateId = '';
|
||||
@track selectedPropertyId = '';
|
||||
@track selectedPageSize = 'A4';
|
||||
@track currentTimestamp = '';
|
||||
@track debugMode = false;
|
||||
@track tbizzInput = ''; // Input field value for tbizz
|
||||
|
||||
// V-key click counter
|
||||
vKeyCount = 0;
|
||||
vKeyTimeout = null;
|
||||
// tbizz sequence detection for opening development mode
|
||||
tbizzSequence = '';
|
||||
tbizzTimeout = null;
|
||||
|
||||
connectedCallback() {
|
||||
this.updateTimestamp();
|
||||
@ -20,8 +21,8 @@ export default class DevelopmentPage extends LightningElement {
|
||||
|
||||
disconnectedCallback() {
|
||||
this.removeKeyListener();
|
||||
if (this.vKeyTimeout) {
|
||||
clearTimeout(this.vKeyTimeout);
|
||||
if (this.tbizzTimeout) {
|
||||
clearTimeout(this.tbizzTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,34 +35,23 @@ export default class DevelopmentPage extends LightningElement {
|
||||
}
|
||||
|
||||
handleKeyPress(event) {
|
||||
// Only listen for 'V' key (case insensitive)
|
||||
if (event.key.toLowerCase() === 'v') {
|
||||
this.vKeyCount++;
|
||||
|
||||
// Clear any existing timeout
|
||||
if (this.vKeyTimeout) {
|
||||
clearTimeout(this.vKeyTimeout);
|
||||
// Listen for Enter key to check bweixx input
|
||||
if (event.key === 'Enter') {
|
||||
if (this.tbizzInput.toLowerCase() === 'bweixx') {
|
||||
this.showDevPage = true; // Show development page when bweixx is entered
|
||||
this.tbizzInput = ''; // Clear input
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset counter after 3 seconds of inactivity
|
||||
this.vKeyTimeout = setTimeout(() => {
|
||||
this.vKeyCount = 0;
|
||||
}, 3000);
|
||||
// Handle input field changes
|
||||
handleTbizzInputChange(event) {
|
||||
this.tbizzInput = event.target.value;
|
||||
}
|
||||
|
||||
// Hide dev page after 4 V key presses
|
||||
if (this.vKeyCount >= 4) {
|
||||
// Hide development page
|
||||
hideDevPage() {
|
||||
this.showDevPage = false;
|
||||
this.vKeyCount = 0; // Reset counter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closeDevPage() {
|
||||
this.showDevPage = false;
|
||||
this.vKeyCount = 0;
|
||||
if (this.vKeyTimeout) {
|
||||
clearTimeout(this.vKeyTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
updateTimestamp() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -144,6 +144,30 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Draggable Gallery Section -->
|
||||
<template if:true={realPropertyImages}>
|
||||
<section class="draggable-gallery-section">
|
||||
<h2 class="gallery-title">Drag to Reorder Images</h2>
|
||||
<div class="gallery-grid">
|
||||
<template for:each={realPropertyImages} for:item="img" for:index="index">
|
||||
<div key={img.Id} class="draggable-gallery-item"
|
||||
draggable="true"
|
||||
data-index={index}
|
||||
ondragstart={handleDragStart}
|
||||
ondragend={handleDragEnd}
|
||||
ondragover={handleDragOver}
|
||||
ondragleave={handleDragLeave}
|
||||
ondrop={handleDrop}>
|
||||
<img src={img.url} alt={img.title} />
|
||||
<div class="drag-handle">
|
||||
<i class="fa-solid fa-grip-vertical"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<div class="agent-footer">
|
||||
<div class="agent-info">
|
||||
<h3 style="font-size: 0.95rem;">Sarah Johnson</h3>
|
||||
@ -440,15 +464,54 @@
|
||||
|
||||
<div class="property-selector">
|
||||
<label for="propertySelect">Property:</label>
|
||||
<select id="propertySelect" value={selectedPropertyId} onchange={handlePropertySelection}>
|
||||
<option value="">-- Select a Property --</option>
|
||||
<template for:each={propertiesWithSelected} for:item="property">
|
||||
<option key={property.Id} value={property.Id} selected={property._selected}>
|
||||
{property.Name} - {property.pcrm__Title_English__c} - {property.pcrm__Property_Type__c} -
|
||||
{property.pcrm__City_Bayut_Dubizzle__c}
|
||||
</option>
|
||||
|
||||
<!-- Simple Searchable Dropdown -->
|
||||
<div class="simple-dropdown-container">
|
||||
<!-- Search Input -->
|
||||
<div class="simple-search-container">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
value={propertySearchTerm}
|
||||
oninput={handlePropertySearch}
|
||||
class="simple-search-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Property Options List -->
|
||||
<div class="simple-options-container">
|
||||
<!-- Selected Property at Top -->
|
||||
<template if:true={selectedProperty}>
|
||||
<div class="simple-option selected-at-top"
|
||||
data-value={selectedProperty.Id}
|
||||
data-selected="true"
|
||||
onclick={handlePropertySelection}>
|
||||
✓ {selectedProperty.Name} - {selectedProperty.pcrm__Title_English__c} - {selectedProperty.pcrm__Property_Type__c} - {selectedProperty.pcrm__City_Bayut_Dubizzle__c}
|
||||
</div>
|
||||
<div class="separator-line"></div>
|
||||
</template>
|
||||
</select>
|
||||
|
||||
<!-- Default Option -->
|
||||
<template if:false={selectedProperty}>
|
||||
<div class="simple-option" data-value="" onclick={handlePropertySelection}>
|
||||
-- Select a Property --
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Other Properties -->
|
||||
<template for:each={filteredPropertiesWithSelected} for:item="property">
|
||||
<template if:false={property._selected}>
|
||||
<div key={property.Id}
|
||||
class="simple-option"
|
||||
data-value={property.Id}
|
||||
data-selected="false"
|
||||
onclick={handlePropertySelection}>
|
||||
{property.Name} - {property.pcrm__Title_English__c} - {property.pcrm__Property_Type__c} - {property.pcrm__City_Bayut_Dubizzle__c}
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -498,9 +561,73 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Property Details Display - Full Width -->
|
||||
<!-- Row 1.5: Pricing Information Options -->
|
||||
<div class="step2-grid-row-1-5">
|
||||
<div class="pricing-information-section">
|
||||
<h3>Pricing Information Options</h3>
|
||||
<p>Select which pricing fields to include in your report</p>
|
||||
|
||||
<!-- Price Display Toggle -->
|
||||
<div class="price-display-toggle">
|
||||
<label class="checkbox-item price-toggle-item">
|
||||
<input type="checkbox" name="showPrice" checked={showPrice} onchange={handlePriceToggleChange}>
|
||||
<span class="checkmark price-toggle-checkmark"></span>
|
||||
<div class="price-toggle-content">
|
||||
<span class="price-toggle-label">Show Actual Price</span>
|
||||
<span class="price-toggle-description">Uncheck to show "Price on Request" instead</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="pricing-options">
|
||||
<label class="checkbox-item pricing-checkbox-item">
|
||||
<input type="checkbox" name="includeRentPriceMin"
|
||||
checked={pricingSelection.includeRentPriceMin} onchange={handlePricingSelectionChange}>
|
||||
<span class="checkmark pricing-checkmark"></span>
|
||||
<div class="pricing-label-content">
|
||||
<span class="pricing-title">Rent Price (Min)</span>
|
||||
<span class="pricing-value">{propertyData.rentPriceMin}</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="checkbox-item pricing-checkbox-item">
|
||||
<input type="checkbox" name="includeRentPriceMax"
|
||||
checked={pricingSelection.includeRentPriceMax} onchange={handlePricingSelectionChange}>
|
||||
<span class="checkmark pricing-checkmark"></span>
|
||||
<div class="pricing-label-content">
|
||||
<span class="pricing-title">Rent Price (Max)</span>
|
||||
<span class="pricing-value">{propertyData.rentPriceMax}</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="checkbox-item pricing-checkbox-item">
|
||||
<input type="checkbox" name="includeSalePriceMin"
|
||||
checked={pricingSelection.includeSalePriceMin} onchange={handlePricingSelectionChange}>
|
||||
<span class="checkmark pricing-checkmark"></span>
|
||||
<div class="pricing-label-content">
|
||||
<span class="pricing-title">Sale Price (Min)</span>
|
||||
<span class="pricing-value">{propertyData.salePriceMin}</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="checkbox-item pricing-checkbox-item">
|
||||
<input type="checkbox" name="includeSalePriceMax"
|
||||
checked={pricingSelection.includeSalePriceMax} onchange={handlePricingSelectionChange}>
|
||||
<span class="checkmark pricing-checkmark"></span>
|
||||
<div class="pricing-label-content">
|
||||
<span class="pricing-title">Sale Price (Max)</span>
|
||||
<span class="pricing-value">{propertyData.salePriceMax}</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Row 3: Property Details Display - Full Width -->
|
||||
<template if:true={selectedPropertyId}>
|
||||
<div class="step2-grid-row-2">
|
||||
<div class="step2-grid-row-3">
|
||||
<div class="property-details-layout">
|
||||
<h3>Property Details Preview</h3>
|
||||
|
||||
@ -545,6 +672,33 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agent Information -->
|
||||
<div class="property-section-group agent-details-section">
|
||||
<h5>Agent Information</h5>
|
||||
<div class="property-grid">
|
||||
<div class="property-field">
|
||||
<span class="label">Agent Name:</span>
|
||||
<span class="value">{agentData.name}</span>
|
||||
</div>
|
||||
<div class="property-field">
|
||||
<span class="label">Agent Email:</span>
|
||||
<span class="value">{agentData.email}</span>
|
||||
</div>
|
||||
<div class="property-field">
|
||||
<span class="label">Agent Phone:</span>
|
||||
<span class="value">{agentData.phone}</span>
|
||||
</div>
|
||||
<div class="property-field">
|
||||
<span class="label">Agent Title:</span>
|
||||
<span class="value">{agentData.title}</span>
|
||||
</div>
|
||||
<div class="property-field">
|
||||
<span class="label">Company:</span>
|
||||
<span class="value">{agentData.company}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Top Rectangle: Specifications + Pricing -->
|
||||
@ -576,28 +730,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pricing Information -->
|
||||
<div class="property-section-group pricing-section">
|
||||
<h5>Pricing Information</h5>
|
||||
<div class="property-grid">
|
||||
<div class="property-field">
|
||||
<span class="label">Rent Price (Min):</span>
|
||||
<span class="value">{propertyData.rentPriceMin}</span>
|
||||
</div>
|
||||
<div class="property-field">
|
||||
<span class="label">Rent Price (Max):</span>
|
||||
<span class="value">{propertyData.rentPriceMax}</span>
|
||||
</div>
|
||||
<div class="property-field">
|
||||
<span class="label">Sale Price (Min):</span>
|
||||
<span class="value">{propertyData.salePriceMin}</span>
|
||||
</div>
|
||||
<div class="property-field">
|
||||
<span class="label">Sale Price (Max):</span>
|
||||
<span class="value">{propertyData.salePriceMax}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -669,6 +801,17 @@
|
||||
<span class="value">{propertyData.offeringType}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Private Amenities -->
|
||||
<template if:true={propertyData.privateAmenities}>
|
||||
<div class="amenities-list">
|
||||
<span class="label">Private Amenities:</span>
|
||||
<div class="amenities-display">
|
||||
<template for:each={propertyAmenitiesList} for:item="amenity">
|
||||
<span key={amenity} class="amenity-tag">{amenity}</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Rent Availability -->
|
||||
@ -814,6 +957,7 @@
|
||||
→
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@ -846,37 +990,71 @@
|
||||
<div class="toolbar-section">
|
||||
<div class="toolbar-section-title">Insert Property Data</div>
|
||||
<div class="property-insert-grid">
|
||||
<!-- Basic Information -->
|
||||
<button class="property-insert-btn" onclick={insertPropertyName}
|
||||
title="Insert Property Name">
|
||||
Name
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyPrice}
|
||||
title="Insert Property Price">
|
||||
Price
|
||||
<button class="property-insert-btn" onclick={insertPropertyTitle}
|
||||
title="Insert Property Title">
|
||||
Title
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyType}
|
||||
title="Insert Property Type">
|
||||
Type
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyBedrooms}
|
||||
title="Insert Bedrooms">
|
||||
Beds
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyBathrooms}
|
||||
title="Insert Bathrooms">
|
||||
Baths
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<button class="property-insert-btn" onclick={insertPropertySqft}
|
||||
title="Insert Square Footage">
|
||||
Sq Ft
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyAddress} title="Insert Address">
|
||||
Address
|
||||
<button class="property-insert-btn" onclick={insertProplsertyFloor}
|
||||
title="Insert Floor">
|
||||
Floor
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyDescription}
|
||||
title="Insert Description">
|
||||
Description
|
||||
<button class="property-insert-btn" onclick={insertPropertyBuildYear}
|
||||
title="Insert Build Year">
|
||||
Year
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyParking}
|
||||
title="Insert Parking">
|
||||
Parking
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyFurnished}
|
||||
title="Insert Furnished Status">
|
||||
Furnished
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyOfferingType}
|
||||
title="Insert Offering Type">
|
||||
Offering
|
||||
</button>
|
||||
<!-- Location Information -->
|
||||
<button class="property-insert-btn" onclick={insertPropertyCity}
|
||||
title="Insert City">
|
||||
City
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<button class="property-insert-btn" onclick={insertPropertyContactEmail}
|
||||
title="Insert Contact Email">
|
||||
Email
|
||||
</button>
|
||||
<button class="property-insert-btn" onclick={insertPropertyContactPhone}
|
||||
title="Insert Contact Phone">
|
||||
Phone
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1002,12 +1180,12 @@
|
||||
<button class="toolbar-button" onclick={handleBulletList}>
|
||||
<lightning-icon icon-name="utility:bullet_list"
|
||||
size="x-small"></lightning-icon>
|
||||
Bullet(*)
|
||||
Bullet
|
||||
</button>
|
||||
<button class="toolbar-button" onclick={handleNumberList}>
|
||||
<lightning-icon icon-name="utility:numbered_list"
|
||||
size="x-small"></lightning-icon>
|
||||
Number(1)
|
||||
Number
|
||||
</button>
|
||||
<button class="toolbar-button" onclick={handleIndent}>
|
||||
<lightning-icon icon-name="utility:indent" size="x-small"></lightning-icon>
|
||||
@ -1033,6 +1211,12 @@
|
||||
Image
|
||||
</button>
|
||||
</div>
|
||||
<!-- <div class="toolbar-group">
|
||||
<button class="toolbar-button" onclick={addNewPageWithDataType} disabled>
|
||||
<lightning-icon icon-name="utility:page" size="x-small"></lightning-icon>
|
||||
Add Page
|
||||
</button>
|
||||
</div> -->
|
||||
<div class="toolbar-group">
|
||||
<button class="toolbar-button" onclick={toggleSelectorMode}>
|
||||
<lightning-icon icon-name="utility:target" size="x-small"></lightning-icon>
|
||||
@ -1065,8 +1249,6 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div> <!-- Close enhanced-toolbar-scroll -->
|
||||
</div>
|
||||
</div>
|
||||
@ -1078,14 +1260,15 @@
|
||||
<div class="editor-right">
|
||||
<!-- Page Size and PDF Generation Header -->
|
||||
<div class="template-header-area">
|
||||
<!-- New Back Button -->
|
||||
<div class="header-left-actions">
|
||||
<!-- All buttons in a single line -->
|
||||
<div class="header-controls-line">
|
||||
<!-- Back Button -->
|
||||
<button class="btn btn-secondary" onclick={previousStep} title="Back">
|
||||
← Back
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Page Size Options -->
|
||||
<div class="page-size-section">
|
||||
<div class="page-size-group">
|
||||
<label class="page-size-label">Page Size:</label>
|
||||
<div class="page-size-options">
|
||||
<label class="page-size-option">
|
||||
@ -1093,15 +1276,15 @@
|
||||
onchange={handlePageSizeChange}>
|
||||
<span class="page-size-text">A4</span>
|
||||
</label>
|
||||
<label class="page-size-option">
|
||||
<!-- <label class="page-size-option">
|
||||
<input type="radio" name="pageSize" value="A3" onchange={handlePageSizeChange}>
|
||||
<span class="page-size-text">A3</span>
|
||||
</label>
|
||||
</label> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generate PDF Button -->
|
||||
<div class="generate-pdf-section">
|
||||
<template if:true={showGeneratePdfButton}>
|
||||
<button class="btn btn-primary generate-pdf-btn" onclick={generatePdfSimple} disabled={isGeneratingPdf}>
|
||||
<template if:false={isGeneratingPdf}>
|
||||
<span class="btn-icon">📄</span>
|
||||
@ -1112,6 +1295,7 @@
|
||||
Wait, our AI is generating the report...
|
||||
</template>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1124,22 +1308,13 @@
|
||||
<template if:true={previewPages}>
|
||||
<template for:each={previewPages} for:item="page" for:index="pageIndex">
|
||||
<div key={page.id} class="preview-page" data-page-size={selectedPageSize} data-page-number={pageIndex}>
|
||||
<div class="enhanced-editor-content" contenteditable="true" onkeyup={handleContentChange}
|
||||
onpaste={handleContentChange} ondragover={handleEditorDragOver} ondrop={handleEditorDrop}>
|
||||
<div class="enhanced-editor-content" contenteditable="true" onkeyup={handleContentChangeWithDynamicSizing}
|
||||
onpaste={handleContentChangeWithDynamicSizing} ondragover={handleEditorDragOver} ondrop={handleEditorDrop}>
|
||||
{page.content}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template if:false={previewPages}>
|
||||
<div class="preview-page" data-page-size={selectedPageSize} data-page-number="1">
|
||||
<div class="enhanced-editor-content" contenteditable="true" onkeyup={handleContentChange}
|
||||
onpaste={handleContentChange} ondragover={handleEditorDragOver} ondrop={handleEditorDrop}>
|
||||
<!-- Template content will be loaded here -->
|
||||
{htmlContent}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<template if:false={hasPropertyImages}>
|
||||
<div class="floating-placeholder" data-preview-only="true" style="position: absolute; bottom: 12px; left: 12px; display: flex; align-items: center; gap: 8px; z-index: 2000; pointer-events: none;">
|
||||
@ -1157,6 +1332,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Image Review Modal -->
|
||||
<div if:true={showImageReview} class="image-review-overlay">
|
||||
<div class="image-review-modal">
|
||||
@ -1292,6 +1468,7 @@
|
||||
<!-- Action Buttons -->
|
||||
<div class="image-replacement-actions">
|
||||
<button class="btn btn-secondary" onclick={closeImageReplacement}>Cancel</button>
|
||||
<button class="btn btn-primary" onclick={insertSelectedReplacementImage}>Insert Image</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1390,4 +1567,99 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New Section Modal -->
|
||||
<div if:true={showNewSectionModal} class="new-section-modal-overlay">
|
||||
<div class="new-section-modal">
|
||||
<div class="new-section-modal-header">
|
||||
<h3>Add New Section</h3>
|
||||
<button class="close-btn" onclick={closeNewSectionModal}>✕</button>
|
||||
</div>
|
||||
<div class="new-section-modal-content">
|
||||
<div class="section-type-selection">
|
||||
<h4>Choose Section Type:</h4>
|
||||
<div class="section-type-grid">
|
||||
<div class="section-type-option" onclick={selectSectionType} data-type="text">
|
||||
<div class="section-type-icon">📝</div>
|
||||
<div class="section-type-title">Text Section</div>
|
||||
<div class="section-type-description">Add a text-based section with title and content</div>
|
||||
</div>
|
||||
<div class="section-type-option" onclick={selectSectionType} data-type="image">
|
||||
<div class="section-type-icon">🖼️</div>
|
||||
<div class="section-type-title">Image Gallery</div>
|
||||
<div class="section-type-description">Add an image gallery section</div>
|
||||
</div>
|
||||
<div class="section-type-option" onclick={selectSectionType} data-type="features">
|
||||
<div class="section-type-icon">✨</div>
|
||||
<div class="section-type-title">Features List</div>
|
||||
<div class="section-type-description">Add a features and amenities section</div>
|
||||
</div>
|
||||
<div class="section-type-option" onclick={selectSectionType} data-type="contact">
|
||||
<div class="section-type-icon">📞</div>
|
||||
<div class="section-type-title">Contact Info</div>
|
||||
<div class="section-type-description">Add contact information section</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div if:true={selectedSectionType} class="section-configuration">
|
||||
<h4>Section Configuration:</h4>
|
||||
<div class="form-group">
|
||||
<label>Section Title:</label>
|
||||
<input type="text" class="form-input" value={newSectionTitle} onchange={handleSectionTitleChange} placeholder="Enter section title">
|
||||
</div>
|
||||
<div if:true={showSectionContentInput} class="form-group">
|
||||
<label>Section Content:</label>
|
||||
<textarea class="form-textarea" value={newSectionContent} onchange={handleSectionContentChange} placeholder="Enter section content"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="new-section-modal-actions">
|
||||
<button class="btn btn-secondary" onclick={closeNewSectionModal}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Type Selection Modal -->
|
||||
<div if:true={showDataTypeModal} class="data-type-modal-overlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 9999; display: flex; align-items: center; justify-content: center; animation: fadeIn 0.3s ease-out;">
|
||||
<div class="data-type-modal" style="background: white; border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.4); max-width: 700px; width: 90%; max-height: 85vh; overflow-y: auto; position: relative; animation: slideIn 0.3s ease-out; border: 1px solid #e0e0e0;">
|
||||
<div class="data-type-modal-header" style="padding: 24px 24px 16px 24px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h3 style="margin: 0; font-size: 24px; font-weight: 600; color: #333;">Select Page Type</h3>
|
||||
<button class="close-btn" onclick={closeDataTypeModal} style="background: none; border: none; font-size: 24px; color: #666; cursor: pointer; padding: 4px; border-radius: 4px; transition: all 0.2s;">✕</button>
|
||||
</div>
|
||||
<div class="data-type-modal-content" style="padding: 24px;">
|
||||
<div class="data-type-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||||
<div class="data-type-option" onclick={handleDataTypeSelection} data-type="text" style="border: 2px solid #e0e0e0; border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.3s ease; background: #fafafa; hover:border-color: #007bff; hover:background: #f0f8ff;">
|
||||
<div class="data-type-icon" style="font-size: 32px; margin-bottom: 12px;">📝</div>
|
||||
<div class="data-type-title" style="font-size: 18px; font-weight: 600; color: #333; margin-bottom: 8px;">Text Page</div>
|
||||
<div class="data-type-description" style="font-size: 14px; color: #666; line-height: 1.4;">Add a text-focused page for detailed content</div>
|
||||
</div>
|
||||
<div class="data-type-option" onclick={handleDataTypeSelection} data-type="gallery" style="border: 2px solid #e0e0e0; border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.3s ease; background: #fafafa; hover:border-color: #007bff; hover:background: #f0f8ff;">
|
||||
<div class="data-type-icon" style="font-size: 32px; margin-bottom: 12px;">🖼️</div>
|
||||
<div class="data-type-title" style="font-size: 18px; font-weight: 600; color: #333; margin-bottom: 8px;">Gallery Page</div>
|
||||
<div class="data-type-description" style="font-size: 14px; color: #666; line-height: 1.4;">Add an image gallery page</div>
|
||||
</div>
|
||||
<div class="data-type-option" onclick={handleDataTypeSelection} data-type="features" style="border: 2px solid #e0e0e0; border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.3s ease; background: #fafafa; hover:border-color: #007bff; hover:background: #f0f8ff;">
|
||||
<div class="data-type-icon" style="font-size: 32px; margin-bottom: 12px;">✨</div>
|
||||
<div class="data-type-title" style="font-size: 18px; font-weight: 600; color: #333; margin-bottom: 8px;">Features Page</div>
|
||||
<div class="data-type-description" style="font-size: 14px; color: #666; line-height: 1.4;">Add a features and amenities page</div>
|
||||
</div>
|
||||
<div class="data-type-option" onclick={handleDataTypeSelection} data-type="contact" style="border: 2px solid #e0e0e0; border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.3s ease; background: #fafafa; hover:border-color: #007bff; hover:background: #f0f8ff;">
|
||||
<div class="data-type-icon" style="font-size: 32px; margin-bottom: 12px;">📞</div>
|
||||
<div class="data-type-title" style="font-size: 18px; font-weight: 600; color: #333; margin-bottom: 8px;">Contact Page</div>
|
||||
<div class="data-type-description" style="font-size: 14px; color: #666; line-height: 1.4;">Add a contact information page</div>
|
||||
</div>
|
||||
<div class="data-type-option" onclick={handleDataTypeSelection} data-type="blank" style="border: 2px solid #e0e0e0; border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.3s ease; background: #fafafa; hover:border-color: #007bff; hover:background: #f0f8ff;">
|
||||
<div class="data-type-icon" style="font-size: 32px; margin-bottom: 12px;">📄</div>
|
||||
<div class="data-type-title" style="font-size: 18px; font-weight: 600; color: #333; margin-bottom: 8px;">Blank Page</div>
|
||||
<div class="data-type-description" style="font-size: 14px; color: #666; line-height: 1.4;">Add a blank page for custom content</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-type-modal-actions" style="padding: 16px 24px 24px 24px; border-top: 1px solid #e0e0e0; display: flex; justify-content: flex-end; gap: 12px;">
|
||||
<button class="btn btn-secondary" onclick={closeDataTypeModal} style="padding: 10px 20px; border: 1px solid #ccc; background: white; color: #333; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500;">Cancel</button>
|
||||
<button class="btn btn-primary" onclick={addPageWithSelectedDataType} disabled={selectedDataTypeDisabled} style="padding: 10px 20px; border: none; background: #007bff; color: white; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500;">Add Page</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<cacheControl>Public</cacheControl>
|
||||
<contentType>image/svg+xml</contentType>
|
||||
</StaticResource>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<cacheControl>Public</cacheControl>
|
||||
<contentType>image/svg+xml</contentType>
|
||||
</StaticResource>
|
||||
3
force-app/main/default/staticresources/PropertyLogo.svg
Normal file
3
force-app/main/default/staticresources/PropertyLogo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 31 KiB |
BIN
force-app/main/default/staticresources/companyLogo.jpeg
Normal file
BIN
force-app/main/default/staticresources/companyLogo.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
0
template samples/grand-oak.html
Normal file
0
template samples/grand-oak.html
Normal file
Loading…
Reference in New Issue
Block a user