2876 lines
117 KiB
HTML
2876 lines
117 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Professional Property Brochure Generator - Advanced Template System</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
:root {
|
||
--custom-accent: #007bff;
|
||
--accent-color: #007bff;
|
||
--header-style: modern;
|
||
--font-family: Arial, sans-serif;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||
background: #f8f9fa;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 40px;
|
||
padding: 30px 0;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 2.5rem;
|
||
font-weight: 700;
|
||
color: #2c3e50;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.header p {
|
||
font-size: 1.1rem;
|
||
color: #6c757d;
|
||
max-width: 600px;
|
||
margin: 0 auto 20px;
|
||
}
|
||
|
||
.header-features {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 15px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.feature-badge {
|
||
background: linear-gradient(135deg, #007bff, #0056b3);
|
||
color: white;
|
||
padding: 8px 16px;
|
||
border-radius: 20px;
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.3);
|
||
}
|
||
|
||
/* Step Navigation */
|
||
.step-navigation {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 40px;
|
||
background: white;
|
||
padding: 20px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.step-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin: 0 20px;
|
||
position: relative;
|
||
}
|
||
|
||
|
||
|
||
.step-number {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background: #e9ecef;
|
||
color: #6c757d;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
margin-right: 15px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.step-item.active .step-number {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.step-item.completed .step-number {
|
||
background: #28a745;
|
||
color: white;
|
||
}
|
||
|
||
.step-text {
|
||
font-weight: 500;
|
||
color: #6c757d;
|
||
}
|
||
|
||
.step-item.active .step-text {
|
||
color: #007bff;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.step-item.completed .step-text {
|
||
color: #28a745;
|
||
}
|
||
|
||
/* Step Content */
|
||
.step-content {
|
||
display: none;
|
||
animation: fadeIn 0.5s ease-in;
|
||
}
|
||
|
||
.step-content.active {
|
||
display: block;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* Step 1: Template Selection */
|
||
.template-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
|
||
gap: 30px;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.template-card {
|
||
background: white;
|
||
border-radius: 20px;
|
||
overflow: hidden;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 4px 16px rgba(0, 0, 0, 0.08);
|
||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
cursor: pointer;
|
||
border: 3px solid transparent;
|
||
position: relative;
|
||
}
|
||
|
||
.template-card:hover {
|
||
transform: translateY(-12px) scale(1.02);
|
||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.template-card.selected {
|
||
border-color: #28a745;
|
||
box-shadow: 0 20px 60px rgba(40, 167, 69, 0.3), 0 8px 24px rgba(40, 167, 69, 0.2);
|
||
}
|
||
|
||
.template-preview {
|
||
height: 280px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
.template-preview img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
transition: transform 0.4s ease;
|
||
}
|
||
|
||
.template-card:hover .template-preview img {
|
||
transform: scale(1.08);
|
||
}
|
||
|
||
.template-info {
|
||
padding: 25px;
|
||
min-height: 200px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.template-info h3 {
|
||
font-size: 1.4rem;
|
||
font-weight: 700;
|
||
color: #2c3e50;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.template-info p {
|
||
color: #6c757d;
|
||
font-size: 0.95rem;
|
||
line-height: 1.6;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.template-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.tag {
|
||
background: #e3f2fd;
|
||
color: #1976d2;
|
||
padding: 6px 12px;
|
||
border-radius: 20px;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
|
||
.template-selection-container {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.template-categories {
|
||
text-align: center;
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.template-categories h3 {
|
||
font-size: 1.5rem;
|
||
color: #2c3e50;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
|
||
|
||
/* Template Specifications */
|
||
.template-specs {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.spec {
|
||
background: #f8f9fa;
|
||
color: #495057;
|
||
padding: 6px 12px;
|
||
border-radius: 15px;
|
||
font-size: 0.8rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Page Count and Layout Info */
|
||
.page-count {
|
||
font-size: 1.2rem;
|
||
font-weight: 700;
|
||
margin-bottom: 8px;
|
||
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.layout-info {
|
||
font-size: 0.9rem;
|
||
opacity: 0.9;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.template-icon {
|
||
font-size: 3rem;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
/* Layout Options */
|
||
.layout-options {
|
||
background: white;
|
||
padding: 30px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.layout-options h3 {
|
||
text-align: center;
|
||
margin-bottom: 25px;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.layout-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.layout-option {
|
||
text-align: center;
|
||
padding: 20px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 15px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.layout-option:hover {
|
||
border-color: #007bff;
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.2);
|
||
}
|
||
|
||
.layout-preview {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
color: #007bff;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.layout-option span {
|
||
font-size: 0.9rem;
|
||
color: #6c757d;
|
||
}
|
||
|
||
/* Template-specific styles with better colors */
|
||
.professional-1pager .template-preview {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
}
|
||
|
||
.professional-3pager .template-preview {
|
||
background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
|
||
}
|
||
|
||
.professional-5pager .template-preview {
|
||
background: linear-gradient(135deg, #059669 0%, #10b981 100%);
|
||
}
|
||
|
||
.luxury-villa .template-preview {
|
||
background: linear-gradient(135deg, #2c1810 0%, #8b4513 100%);
|
||
}
|
||
|
||
.dubai-penthouse .template-preview {
|
||
background: linear-gradient(135deg, #dc2626 0%, #f87171 100%);
|
||
}
|
||
|
||
.modern-apartment .template-preview {
|
||
background: linear-gradient(135deg, #7c3aed 0%, #a855f7 100%);
|
||
}
|
||
|
||
.custom-template .template-preview {
|
||
background: linear-gradient(135deg, #1f2937 0%, #4b5563 100%);
|
||
}
|
||
|
||
/* Customization Options */
|
||
.customization-options {
|
||
background: white;
|
||
padding: 30px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.customization-options h3 {
|
||
text-align: center;
|
||
margin-bottom: 25px;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.customization-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.customization-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.customization-item label {
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.customization-item select {
|
||
padding: 10px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 8px;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* Content Modules */
|
||
.content-modules {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.module-item {
|
||
background: #f8f9fa;
|
||
padding: 20px;
|
||
border-radius: 15px;
|
||
border: 2px solid #e9ecef;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.module-item:hover {
|
||
border-color: #007bff;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.1);
|
||
}
|
||
|
||
.module-item input[type="checkbox"] {
|
||
width: auto;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.module-item label {
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
font-size: 1.1rem;
|
||
margin-bottom: 8px;
|
||
display: block;
|
||
}
|
||
|
||
.module-item p {
|
||
color: #6c757d;
|
||
font-size: 0.9rem;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
/* Investment Articles */
|
||
.investment-articles {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.article-item {
|
||
background: #f8f9fa;
|
||
padding: 20px;
|
||
border-radius: 15px;
|
||
border: 2px solid #e9ecef;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.article-item:hover {
|
||
border-color: #28a745;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 15px rgba(40, 167, 69, 0.1);
|
||
}
|
||
|
||
.article-item input[type="checkbox"] {
|
||
width: auto;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.article-item label {
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
font-size: 1.1rem;
|
||
margin-bottom: 8px;
|
||
display: block;
|
||
}
|
||
|
||
.article-item p {
|
||
color: #6c757d;
|
||
font-size: 0.9rem;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.blank-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.modern-template .template-preview {
|
||
background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.modern-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
left: -50%;
|
||
width: 200%;
|
||
height: 200%;
|
||
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
|
||
animation: rotate 20s linear infinite;
|
||
}
|
||
|
||
.luxury-template .template-preview {
|
||
background: linear-gradient(135deg, #2c1810 0%, #8b4513 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.luxury-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="luxury" width="20" height="20" patternUnits="userSpaceOnUse"><circle cx="10" cy="10" r="1" fill="rgba(255,255,255,0.1)"/></pattern></defs><rect width="100" height="100" fill="url(%23luxury)"/></svg>');
|
||
opacity: 0.4;
|
||
}
|
||
|
||
.premium-template .template-preview {
|
||
background: linear-gradient(135deg, #1a1a1a 0%, #4a4a4a 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.premium-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="premium" width="15" height="15" patternUnits="userSpaceOnUse"><path d="M 0 7.5 L 7.5 0 L 15 7.5 L 7.5 15 Z" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23premium)"/></svg>');
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.dubai-template .template-preview {
|
||
background: linear-gradient(135deg, #dc2626 0%, #f87171 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.dubai-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="dubai" width="25" height="25" patternUnits="userSpaceOnUse"><path d="M 0 12.5 L 12.5 0 L 25 12.5 L 12.5 25 Z" fill="none" stroke="rgba(255,255,255,0.15)" stroke-width="0.8"/></pattern></defs><rect width="100" height="100" fill="url(%23dubai)"/></svg>');
|
||
opacity: 0.4;
|
||
}
|
||
|
||
.villa-template .template-preview {
|
||
background: linear-gradient(135deg, #2c3e50 0%, #5a6c7d 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.villa-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="villa" width="30" height="30" patternUnits="userSpaceOnUse"><circle cx="15" cy="15" r="8" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23villa)"/></svg>');
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.penthouse-template .template-preview {
|
||
background: linear-gradient(135deg, #fcb69f 0%, #ffecd2 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.penthouse-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="penthouse" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M 0 10 L 10 0 L 20 10 L 10 20 Z" fill="none" stroke="rgba(139,69,19,0.2)" stroke-width="0.6"/></pattern></defs><rect width="100" height="100" fill="url(%23penthouse)"/></svg>');
|
||
opacity: 0.4;
|
||
}
|
||
|
||
.office-template .template-preview {
|
||
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.office-template .template-preview::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="office" width="18" height="18" patternUnits="userSpaceOnUse"><rect x="1" y="1" width="16" height="16" fill="none" stroke="rgba(255,255,255,0.2)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23office)"/></svg>');
|
||
opacity: 0.3;
|
||
}
|
||
|
||
@keyframes rotate {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* Enhanced template content */
|
||
.template-preview .preview-content {
|
||
position: relative;
|
||
z-index: 2;
|
||
text-align: center;
|
||
padding: 40px 20px;
|
||
color: white;
|
||
}
|
||
|
||
.template-preview .preview-content h4 {
|
||
font-size: 1.8rem;
|
||
font-weight: 700;
|
||
margin-bottom: 15px;
|
||
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.template-preview .preview-content .icon {
|
||
width: 80px;
|
||
height: 80px;
|
||
background: rgba(255,255,255,0.2);
|
||
border-radius: 50%;
|
||
margin: 0 auto 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 2.5rem;
|
||
backdrop-filter: blur(10px);
|
||
border: 2px solid rgba(255,255,255,0.3);
|
||
}
|
||
|
||
/* Template info enhancements */
|
||
.template-info h3 {
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
margin-bottom: 15px;
|
||
position: relative;
|
||
}
|
||
|
||
.template-info h3::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: -5px;
|
||
left: 0;
|
||
width: 40px;
|
||
height: 3px;
|
||
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.template-info p {
|
||
color: #6b7280;
|
||
font-size: 1rem;
|
||
line-height: 1.7;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
/* Enhanced tags */
|
||
.template-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.tag {
|
||
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
||
color: white;
|
||
padding: 8px 16px;
|
||
border-radius: 25px;
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.tag:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
||
}
|
||
|
||
/* Selection feedback enhancement */
|
||
.template-card.selected {
|
||
border-color: #10b981;
|
||
box-shadow: 0 25px 80px rgba(16, 185, 129, 0.4), 0 10px 30px rgba(16, 185, 129, 0.3);
|
||
transform: translateY(-15px) scale(1.03);
|
||
}
|
||
|
||
.template-card.selected::before {
|
||
content: '✓ SELECTED';
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: #10b981;
|
||
color: white;
|
||
padding: 8px 16px;
|
||
border-radius: 20px;
|
||
font-size: 0.8rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
z-index: 10;
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(1.05); }
|
||
}
|
||
|
||
/* Step 2: Property Data Form */
|
||
.form-container {
|
||
max-width: 800px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
padding: 40px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||
}
|
||
|
||
.form-section {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.form-section h3 {
|
||
font-size: 1.3rem;
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 2px solid #e9ecef;
|
||
}
|
||
|
||
.form-row {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select,
|
||
.form-group textarea {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 10px;
|
||
font-size: 1rem;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group select:focus,
|
||
.form-group textarea:focus {
|
||
outline: none;
|
||
border-color: #007bff;
|
||
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
|
||
}
|
||
|
||
.form-group textarea {
|
||
resize: vertical;
|
||
min-height: 100px;
|
||
}
|
||
|
||
.amenities-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 15px;
|
||
}
|
||
|
||
.amenity-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 10px;
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
border: 1px solid #e9ecef;
|
||
}
|
||
|
||
.amenity-item input[type="checkbox"] {
|
||
width: auto;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.image-upload {
|
||
border: 2px dashed #dee2e6;
|
||
border-radius: 10px;
|
||
padding: 30px;
|
||
text-align: center;
|
||
background: #f8f9fa;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.image-upload:hover {
|
||
border-color: #007bff;
|
||
background: #e3f2fd;
|
||
}
|
||
|
||
.image-upload input[type="file"] {
|
||
display: none;
|
||
}
|
||
|
||
/* Step 3: Preview */
|
||
.preview-container {
|
||
background: white;
|
||
padding: 40px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||
}
|
||
|
||
.preview-header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.preview-content {
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 15px;
|
||
padding: 30px;
|
||
background: #f8f9fa;
|
||
min-height: 600px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.preview-content h1, .preview-content h2, .preview-content h3 {
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.color-accent {
|
||
color: var(--accent-color);
|
||
transition: color 0.3s ease;
|
||
}
|
||
|
||
.pdf-preview {
|
||
width: 100%;
|
||
height: 600px;
|
||
border: none;
|
||
border-radius: 10px;
|
||
background: white;
|
||
}
|
||
|
||
/* Navigation Buttons */
|
||
.navigation-buttons {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 40px;
|
||
padding-top: 30px;
|
||
border-top: 1px solid #e9ecef;
|
||
}
|
||
|
||
.btn {
|
||
padding: 12px 30px;
|
||
border: none;
|
||
border-radius: 25px;
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: #0056b3;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #6c757d;
|
||
color: white;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: #545b62;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(108, 117, 125, 0.3);
|
||
}
|
||
|
||
.btn-success {
|
||
background: #28a745;
|
||
color: white;
|
||
}
|
||
|
||
.btn-success:hover {
|
||
background: #1e7e34;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
|
||
}
|
||
|
||
.btn:disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 768px) {
|
||
.step-navigation {
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
}
|
||
|
||
.step-item:not(:last-child)::after {
|
||
display: none;
|
||
}
|
||
|
||
.form-row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.template-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.navigation-buttons {
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
}
|
||
|
||
/* Loading Spinner */
|
||
.loading-spinner {
|
||
display: none;
|
||
text-align: center;
|
||
padding: 40px;
|
||
}
|
||
|
||
.spinner {
|
||
width: 50px;
|
||
height: 50px;
|
||
border: 4px solid #f3f3f3;
|
||
border-top: 4px solid #007bff;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin: 0 auto 20px;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* Image Upload Styles */
|
||
.image-upload-item {
|
||
background: #f8f9fa;
|
||
border: 2px dashed #dee2e6;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.image-upload-item:hover {
|
||
border-color: #007bff;
|
||
background: #f0f8ff;
|
||
}
|
||
|
||
.image-input-group {
|
||
display: flex;
|
||
gap: 15px;
|
||
align-items: center;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.room-name {
|
||
flex: 2;
|
||
padding: 12px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.image-file {
|
||
flex: 1;
|
||
padding: 8px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.remove-image-btn {
|
||
padding: 8px 16px;
|
||
background: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: background 0.3s ease;
|
||
}
|
||
|
||
.remove-image-btn:hover {
|
||
background: #c82333;
|
||
}
|
||
|
||
.add-image-btn {
|
||
background: #28a745;
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 24px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin: 20px 0;
|
||
transition: background 0.3s ease;
|
||
}
|
||
|
||
.add-image-btn:hover {
|
||
background: #218838;
|
||
}
|
||
|
||
.image-preview {
|
||
display: flex;
|
||
gap: 15px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.image-preview img {
|
||
width: 120px;
|
||
height: 90px;
|
||
object-fit: cover;
|
||
border-radius: 8px;
|
||
border: 2px solid #fff;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.section-description {
|
||
color: #6c757d;
|
||
font-size: 14px;
|
||
margin-bottom: 20px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.help-text {
|
||
color: #6c757d;
|
||
font-size: 13px;
|
||
font-style: italic;
|
||
margin-top: 10px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>Professional Property Brochure Generator</h1>
|
||
<p>Advanced Template System with Multi-Page Layouts & Market Analytics</p>
|
||
<div class="header-features">
|
||
<span class="feature-badge">Market Analytics</span>
|
||
<span class="feature-badge">Professional Templates</span>
|
||
<span class="feature-badge">Multi-Format Support</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step Navigation -->
|
||
<div class="step-navigation">
|
||
<div class="step-item active" id="step1-nav">
|
||
<div class="step-number">1</div>
|
||
<div class="step-text">Template & Layout</div>
|
||
</div>
|
||
<div class="step-item" id="step2-nav">
|
||
<div class="step-number">2</div>
|
||
<div class="step-text">Property & Market Data</div>
|
||
</div>
|
||
<div class="step-item" id="step3-nav">
|
||
<div class="step-number">3</div>
|
||
<div class="step-text">Content & Analytics</div>
|
||
</div>
|
||
<div class="step-item" id="step4-nav">
|
||
<div class="step-number">4</div>
|
||
<div class="step-text">Preview & Customize</div>
|
||
</div>
|
||
<div class="step-item" id="step5-nav">
|
||
<div class="step-number">5</div>
|
||
<div class="step-text">Generate & Export</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 1: Template & Layout Selection -->
|
||
<div id="step1" class="step-content active">
|
||
<div class="template-selection-container">
|
||
<div class="template-grid" id="all-templates">
|
||
<!-- Custom Template -->
|
||
<div class="template-card custom-template" onclick="selectTemplate('custom')">
|
||
<div class="template-preview">
|
||
<div class="preview-content">
|
||
<div class="page-count">CUSTOM</div>
|
||
<div class="layout-info">Flexible Layout</div>
|
||
<div class="template-icon">⚙</div>
|
||
</div>
|
||
</div>
|
||
<div class="template-info">
|
||
<h3>Custom Template</h3>
|
||
<p>Build your own template with custom layouts, image grids, and content structure.</p>
|
||
<div class="template-specs">
|
||
<span class="spec">Flexible Size</span>
|
||
<span class="spec">Custom Grid</span>
|
||
<span class="spec">Personal Design</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Professional 1-Pager -->
|
||
<div class="template-card professional-1pager" onclick="selectTemplate('professional-1pager')">
|
||
<div class="template-preview">
|
||
<div class="preview-content">
|
||
<div class="page-count">1 PAGE</div>
|
||
<div class="layout-info">2x2 Grid Layout</div>
|
||
<div class="template-icon">📄</div>
|
||
</div>
|
||
</div>
|
||
<div class="template-info">
|
||
<h3>Professional 1-Pager</h3>
|
||
<p>Compact single-page brochure with 2x2 image grid. Perfect for quick property overviews and executive summaries.</p>
|
||
<div class="template-specs">
|
||
<span class="spec">📏 A4 Portrait</span>
|
||
<span class="spec">4 Images</span>
|
||
<span class="spec">Market Data</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Professional 3-Pager -->
|
||
<div class="template-card professional-3pager" onclick="selectTemplate('professional-3pager')">
|
||
<div class="template-preview">
|
||
<div class="preview-content">
|
||
<div class="page-count">3 PAGES</div>
|
||
<div class="layout-info">4x4 Grid Layout</div>
|
||
<div class="template-icon">📚</div>
|
||
</div>
|
||
</div>
|
||
<div class="template-info">
|
||
<h3>Professional 3-Pager</h3>
|
||
<p>Comprehensive three-page brochure with detailed property analysis, market insights, and comprehensive property showcase.</p>
|
||
<div class="template-specs">
|
||
<span class="spec">📏 A4 Portrait</span>
|
||
<span class="spec">16 Images</span>
|
||
<span class="spec">ROI Analysis</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Professional 5-Pager -->
|
||
<div class="template-card professional-5pager" onclick="selectTemplate('professional-5pager')">
|
||
<div class="template-preview">
|
||
<div class="preview-content">
|
||
<div class="page-count">5 PAGES</div>
|
||
<div class="layout-info">6x6 Grid Layout</div>
|
||
<div class="template-icon">📖</div>
|
||
</div>
|
||
</div>
|
||
<div class="template-info">
|
||
<h3>Professional 5-Pager</h3>
|
||
<p>Premium five-page brochure with comprehensive market analysis, investment strategies, and detailed property showcase.</p>
|
||
<div class="template-specs">
|
||
<span class="spec">📏 A4 Portrait</span>
|
||
<span class="spec">36 Images</span>
|
||
<span class="spec">Investment Guide</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Luxury Villa Brochure -->
|
||
<div class="template-card luxury-villa" onclick="selectTemplate('luxury-villa')">
|
||
<div class="template-preview">
|
||
<div class="preview-content">
|
||
<div class="page-count">4 PAGES</div>
|
||
<div class="layout-info">Premium Layout</div>
|
||
<div class="template-icon">🏰</div>
|
||
</div>
|
||
</div>
|
||
<div class="template-info">
|
||
<h3>Luxury Villa Brochure</h3>
|
||
<p>Exclusive villa template with premium styling, sophisticated layouts, and luxury branding elements.</p>
|
||
<div class="template-specs">
|
||
<span class="spec">📏 A4 Portrait</span>
|
||
<span class="spec">20 Images</span>
|
||
<span class="spec">Premium Design</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dubai Penthouse -->
|
||
<div class="template-card dubai-penthouse" onclick="selectTemplate('dubai-penthouse')">
|
||
<div class="template-preview">
|
||
<div class="preview-content">
|
||
<div class="page-count">6 PAGES</div>
|
||
<div class="layout-info">Dubai Style</div>
|
||
<div class="template-icon">🌆</div>
|
||
</div>
|
||
</div>
|
||
<div class="template-info">
|
||
<h3>Dubai Penthouse</h3>
|
||
<p>Dubai-specific luxury penthouse template with iconic branding and local market insights.</p>
|
||
<div class="template-specs">
|
||
<span class="spec">📏 A4 Portrait</span>
|
||
<span class="spec">30 Images</span>
|
||
<span class="spec">Dubai Market</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modern Apartment -->
|
||
<div class="template-card modern-apartment" onclick="selectTemplate('modern-apartment')">
|
||
<div class="template-preview">
|
||
<div class="preview-content">
|
||
<div class="page-count">3 PAGES</div>
|
||
<div class="layout-info">Contemporary</div>
|
||
<div class="template-icon">🏢</div>
|
||
</div>
|
||
</div>
|
||
<div class="template-info">
|
||
<h3>Modern Apartment</h3>
|
||
<p>Contemporary apartment template with clean lines, modern aesthetics, and urban lifestyle focus.</p>
|
||
<div class="template-specs">
|
||
<span class="spec">📏 A4 Portrait</span>
|
||
<span class="spec">15 Images</span>
|
||
<span class="spec">Urban Style</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="layout-options" id="layoutOptions" style="display: none;">
|
||
<h3>Layout Configuration</h3>
|
||
<div class="layout-grid">
|
||
<div class="layout-option" onclick="selectLayout('2x2')">
|
||
<div class="layout-preview">2×2</div>
|
||
<span>2x2 Grid (4 Images)</span>
|
||
</div>
|
||
<div class="layout-option" onclick="selectLayout('3x3')">
|
||
<div class="layout-preview">3×3</div>
|
||
<span>3x3 Grid (9 Images)</span>
|
||
</div>
|
||
<div class="layout-option" onclick="selectLayout('4x4')">
|
||
<div class="layout-preview">4×4</div>
|
||
<span>4x4 Grid (16 Images)</span>
|
||
</div>
|
||
<div class="layout-option" onclick="selectLayout('6x6')">
|
||
<div class="layout-preview">6×6</div>
|
||
<span>6x6 Grid (36 Images)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="navigation-buttons">
|
||
<div></div>
|
||
<button class="btn btn-primary" onclick="nextStep()" id="step1-next" disabled>Next: Property & Market Data</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 2: Property Details Form -->
|
||
<div id="step2" class="step-content">
|
||
<div class="form-container">
|
||
<h2 style="text-align: center; margin-bottom: 30px; color: #2c3e50;">Property Information</h2>
|
||
|
||
<div class="form-section">
|
||
<h3>Property Selection</h3>
|
||
<div class="form-group" style="margin-bottom: 20px;">
|
||
<label>Select Property from Database</label>
|
||
<select id="propertySelect" onchange="handlePropertySelection()" style="background: #e3f2fd; border: 2px solid #007bff;">
|
||
<option value="">Choose a property from your Salesforce data...</option>
|
||
</select>
|
||
<div id="propertyLoading" style="display: none; text-align: center; padding: 10px; color: #007bff;">
|
||
<i>Loading properties...</i>
|
||
</div>
|
||
<div id="propertyError" style="display: none; color: #dc3545; padding: 10px; background: #f8d7da; border-radius: 5px; margin-top: 10px;"></div>
|
||
|
||
<!-- Property Statistics Display -->
|
||
<div id="propertyStats"></div>
|
||
|
||
<!-- Salesforce Credentials Form -->
|
||
<div id="salesforceCredentialsForm" style="display: none; background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0; border: 2px solid #007bff;">
|
||
<h4 style="margin: 0 0 15px 0; color: #007bff;">Salesforce Credentials Required</h4>
|
||
<p style="margin: 0 0 15px 0; color: #666;">Enter your Salesforce sandbox credentials to fetch real-time property data:</p>
|
||
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px;">
|
||
<div>
|
||
<label style="display: block; margin-bottom: 5px; font-weight: 600;">Client ID:</label>
|
||
<input type="text" id="sfClientId" placeholder="Connected App Consumer Key" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 5px; font-weight: 600;">Client Secret:</label>
|
||
<input type="password" id="sfClientSecret" placeholder="Connected App Consumer Secret" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 5px; font-weight: 600;">Username:</label>
|
||
<input type="email" id="sfUsername" placeholder="Your sandbox email" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 5px; font-weight: 600;">Password:</label>
|
||
<input type="password" id="sfPassword" placeholder="Your sandbox password" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
|
||
</div>
|
||
<div>
|
||
<label style="display: block; margin-bottom: 5px; font-weight: 600;">Security Token:</label>
|
||
<input type="text" id="sfSecurityToken" placeholder="Your security token" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top: 20px;">
|
||
<button onclick="saveSalesforceCredentials()" style="background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-weight: 600;">
|
||
Save Credentials & Connect
|
||
</button>
|
||
<button onclick="hideCredentialForm()" style="background: #6c757d; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-weight: 600; margin-left: 10px;">
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
|
||
<div style="margin-top: 15px; padding: 10px; background: #e3f2fd; border-radius: 5px; font-size: 0.9em; color: #1976d2;">
|
||
<strong>Need help?</strong> Check the <code>REAL-TIME-SETUP.md</code> file for detailed setup instructions.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h3>Basic Information</h3>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Property Name *</label>
|
||
<input type="text" id="propertyName" placeholder="Auto-filled when property selected" required readonly style="background: #f8f9fa;">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Property Type *</label>
|
||
<select id="propertyType" required>
|
||
<option value="">Select property type</option>
|
||
<option value="Apartment">Apartment</option>
|
||
<option value="Villa">Villa</option>
|
||
<option value="Penthouse">Penthouse</option>
|
||
<option value="Townhouse">Townhouse</option>
|
||
<option value="Office">Office</option>
|
||
<option value="Retail">Retail</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Location *</label>
|
||
<input type="text" id="location" placeholder="Auto-filled when property selected" required readonly style="background: #f8f9fa;">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Price (AED) *</label>
|
||
<input type="number" id="price" placeholder="Auto-filled when property selected" required readonly style="background: #f8f9fa;">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Property Specifications</h3>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Bedrooms *</label>
|
||
<input type="number" id="bedrooms" placeholder="Auto-filled when property selected" required readonly style="background: #f8f9fa;">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Bathrooms *</label>
|
||
<input type="number" id="bathrooms" placeholder="Auto-filled when property selected" required readonly style="background: #f8f9fa;">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Area (sq ft) *</label>
|
||
<input type="number" id="area" placeholder="Auto-filled when property selected" required readonly style="background: #f8f9fa;">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Description</h3>
|
||
<div class="form-group">
|
||
<label>Property Description</label>
|
||
<textarea id="description" placeholder="Auto-filled when property selected" readonly style="background: #f8f9fa;"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Amenities</h3>
|
||
<div class="amenities-grid">
|
||
<div class="amenity-item">
|
||
<input type="checkbox" id="pool" value="Swimming Pool">
|
||
<label for="pool">Swimming Pool</label>
|
||
</div>
|
||
<div class="amenity-item">
|
||
<input type="checkbox" id="gym" value="Gym">
|
||
<label for="gym">Gym</label>
|
||
</div>
|
||
<div class="amenity-item">
|
||
<input type="checkbox" id="parking" value="Parking">
|
||
<label for="parking">Parking</label>
|
||
</div>
|
||
<div class="amenity-item">
|
||
<input type="checkbox" id="security" value="Security">
|
||
<label for="security">Security</label>
|
||
</div>
|
||
<div class="amenity-item">
|
||
<input type="checkbox" id="garden" value="Garden">
|
||
<label for="garden">Garden</label>
|
||
</div>
|
||
<div class="amenity-item">
|
||
<input type="checkbox" id="balcony" value="Balcony">
|
||
<label for="balcony">Balcony</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Property Images with Room Names</h3>
|
||
<p class="section-description">Upload images for different rooms and areas. Each image will be tagged with its room name for professional presentation.</p>
|
||
|
||
<div id="imageUploads">
|
||
<div class="image-upload-item">
|
||
<div class="image-input-group">
|
||
<input type="text" class="room-name" placeholder="Room/Area Name (e.g., Master Bedroom, Kitchen, Living Room)" />
|
||
<input type="file" class="image-file" accept="image/*" />
|
||
<button type="button" class="remove-image-btn" onclick="removeImageUpload(this)">Remove</button>
|
||
</div>
|
||
<div class="image-preview"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<button type="button" class="add-image-btn" onclick="addImageUpload()">+ Add Another Image</button>
|
||
<p class="help-text">Recommended: Upload 8-16 high-quality images for best results. Supported formats: JPG, PNG, WebP</p>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Market Data & Analytics</h3>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Market Trend</label>
|
||
<select id="marketTrend">
|
||
<option value="rising">Rising Market</option>
|
||
<option value="stable">Stable Market</option>
|
||
<option value="declining">Declining Market</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>ROI Potential (%)</label>
|
||
<input type="number" id="roiPotential" placeholder="Expected ROI" min="0" max="100">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Average Price per sq ft</label>
|
||
<input type="number" id="avgPricePerSqft" placeholder="Market average">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Market Demand</label>
|
||
<select id="marketDemand">
|
||
<option value="high">High Demand</option>
|
||
<option value="medium">Medium Demand</option>
|
||
<option value="low">Low Demand</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Location Advantages</label>
|
||
<textarea id="locationAdvantages" placeholder="Describe location benefits (transport, schools, shopping, etc.)"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Investment Information</h3>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Investment Type</label>
|
||
<select id="investmentType">
|
||
<option value="buy-to-live">Buy to Live</option>
|
||
<option value="buy-to-rent">Buy to Rent</option>
|
||
<option value="buy-to-sell">Buy to Sell</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Expected Rental Yield (%)</label>
|
||
<input type="number" id="rentalYield" placeholder="Annual rental yield" min="0" max="20">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Investment Highlights</label>
|
||
<textarea id="investmentHighlights" placeholder="Key investment benefits and features"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="navigation-buttons">
|
||
<button class="btn btn-secondary" onclick="previousStep()">Previous</button>
|
||
<button class="btn btn-primary" onclick="nextStep()" id="step2-next">Next: Content & Analytics</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 3: Content & Analytics -->
|
||
<div id="step3" class="step-content">
|
||
<div class="form-container">
|
||
<h2 style="text-align: center; margin-bottom: 30px; color: #2c3e50;">Content & Analytics Configuration</h2>
|
||
|
||
<div class="form-section">
|
||
<h3>Content Modules</h3>
|
||
<div class="content-modules">
|
||
<div class="module-item">
|
||
<input type="checkbox" id="marketResearch" value="market-research" checked>
|
||
<label for="marketResearch">Market Research Report</label>
|
||
<p>Include comprehensive market analysis and trends</p>
|
||
</div>
|
||
<div class="module-item">
|
||
<input type="checkbox" id="investmentGuide" value="investment-guide" checked>
|
||
<label for="investmentGuide">Investment Guide</label>
|
||
<p>Add investment strategies and ROI analysis</p>
|
||
</div>
|
||
<div class="module-item">
|
||
<input type="checkbox" id="dubaiInsights" value="dubai-insights" checked>
|
||
<label for="dubaiInsights">Dubai Market Insights</label>
|
||
<p>Include Dubai-specific market information</p>
|
||
</div>
|
||
<div class="module-item">
|
||
<input type="checkbox" id="locationAnalysis" value="location-analysis" checked>
|
||
<label for="locationAnalysis">Location Analysis</label>
|
||
<p>Add detailed location benefits and amenities</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Investment Articles</h3>
|
||
<div class="investment-articles">
|
||
<div class="article-item">
|
||
<input type="checkbox" id="whyInvestDubai" value="why-invest-dubai" checked>
|
||
<label for="whyInvestDubai">"Why Invest in Dubai" Article</label>
|
||
<p>Comprehensive guide to Dubai real estate investment</p>
|
||
</div>
|
||
<div class="module-item">
|
||
<input type="checkbox" id="marketTrends" value="market-trends" checked>
|
||
<label for="marketTrends">Market Trends Analysis</label>
|
||
<p>Current market trends and future predictions</p>
|
||
</div>
|
||
<div class="module-item">
|
||
<input type="checkbox" id="roiStrategies" value="roi-strategies" checked>
|
||
<label for="roiStrategies">ROI Optimization Strategies</label>
|
||
<p>Tips for maximizing return on investment</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-section">
|
||
<h3>Custom Content</h3>
|
||
<div class="form-group">
|
||
<label>Additional Content</label>
|
||
<textarea id="additionalContent" placeholder="Add any additional content, articles, or information you'd like to include in your brochure"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="navigation-buttons">
|
||
<button class="btn btn-secondary" onclick="previousStep()">Previous</button>
|
||
<button class="btn btn-primary" onclick="nextStep()" id="step3-next">Next: Preview & Customize</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 4: Preview & Customize -->
|
||
<div id="step4" class="step-content">
|
||
<div class="preview-container">
|
||
<div class="preview-header">
|
||
<h2 style="color: #2c3e50; margin-bottom: 10px;">Preview & Customize</h2>
|
||
<p style="color: #6c757d;">Review and customize your property brochure before final generation</p>
|
||
</div>
|
||
|
||
<div class="preview-content">
|
||
<div id="previewContent">
|
||
<!-- Preview content will be generated here -->
|
||
</div>
|
||
</div>
|
||
|
||
<div class="customization-options">
|
||
<h3>Customization Options</h3>
|
||
<div class="customization-grid">
|
||
<div class="customization-item">
|
||
<label>Header Style</label>
|
||
<select id="headerStyle">
|
||
<option value="modern">Modern</option>
|
||
<option value="classic">Classic</option>
|
||
<option value="luxury">Luxury</option>
|
||
</select>
|
||
</div>
|
||
<div class="customization-item">
|
||
<label>Color Scheme</label>
|
||
<select id="colorScheme">
|
||
<option value="blue">Blue</option>
|
||
<option value="green">Green</option>
|
||
<option value="purple">Purple</option>
|
||
<option value="gold">Gold</option>
|
||
</select>
|
||
</div>
|
||
<div class="customization-item">
|
||
<label>Font Style</label>
|
||
<select id="fontStyle">
|
||
<option value="sans-serif">Sans Serif</option>
|
||
<option value="serif">Serif</option>
|
||
<option value="modern">Modern</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="navigation-buttons">
|
||
<button class="btn btn-secondary" onclick="previousStep()">Previous</button>
|
||
<button class="btn btn-success" onclick="generatePDF()" id="generate-pdf-btn">Next: Generate & Export</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 5: Generate & Export -->
|
||
<div id="step5" class="step-content">
|
||
<div class="preview-container">
|
||
<div class="preview-header">
|
||
<h2 style="color: #2c3e50; margin-bottom: 10px;">PDF Generation & Export</h2>
|
||
<p style="color: #6c757d;">Your high-quality property brochure is being generated</p>
|
||
</div>
|
||
|
||
<div id="loadingSpinner" class="loading-spinner">
|
||
<div class="spinner"></div>
|
||
<h3>Generating your PDF...</h3>
|
||
<p>This may take a few moments. Please wait.</p>
|
||
</div>
|
||
|
||
<div id="pdfResult" style="display: none;">
|
||
<div style="text-align: center; padding: 40px;">
|
||
<div style="font-size: 4rem; color: #28a745; margin-bottom: 20px;">✅</div>
|
||
<h3 style="color: #28a745; margin-bottom: 15px;">PDF Generated Successfully!</h3>
|
||
<p style="color: #6c757d; margin-bottom: 30px;">Your high-quality property brochure is ready for download.</p>
|
||
<button class="btn btn-success" onclick="downloadPDF()" style="font-size: 1.1rem; padding: 15px 40px;">📥 Download PDF</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="navigation-buttons">
|
||
<button class="btn btn-secondary" onclick="previousStep()">Previous</button>
|
||
<button class="btn btn-primary" onclick="startOver()">Start Over</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
let currentStep = 1;
|
||
let selectedTemplate = null;
|
||
let selectedLayout = null;
|
||
let propertyData = {};
|
||
let uploadedImages = [];
|
||
let apiBaseUrl = 'http://localhost:8000/api';
|
||
let salesforceApiUrl = 'https://test.salesforce.com';
|
||
let availableProperties = [];
|
||
let salesforceCredentials = {
|
||
clientId: '',
|
||
clientSecret: '',
|
||
username: '',
|
||
password: '',
|
||
securityToken: ''
|
||
};
|
||
|
||
// Auto-load credentials on page load
|
||
window.onload = function() {
|
||
loadAutoCredentials();
|
||
};
|
||
|
||
// Auto-credential loading function
|
||
function loadAutoCredentials() {
|
||
console.log('Loading auto-generated Salesforce credentials...');
|
||
|
||
// Try to load from auto-credentials.js first
|
||
const script = document.createElement('script');
|
||
script.src = 'auto-credentials.js';
|
||
script.onload = function() {
|
||
console.log('Auto-credentials script loaded');
|
||
// The script will automatically set localStorage
|
||
checkAndLoadCredentials();
|
||
};
|
||
script.onerror = function() {
|
||
console.log('Auto-credentials script not found, checking localStorage');
|
||
checkAndLoadCredentials();
|
||
};
|
||
document.head.appendChild(script);
|
||
}
|
||
|
||
function checkAndLoadCredentials() {
|
||
const savedCredentials = localStorage.getItem('salesforceCredentials');
|
||
if (savedCredentials) {
|
||
try {
|
||
const creds = JSON.parse(savedCredentials);
|
||
salesforceCredentials = creds;
|
||
console.log('Salesforce credentials loaded from localStorage');
|
||
|
||
// Auto-hide credential form if credentials exist
|
||
const form = document.getElementById('salesforceCredentialsForm');
|
||
if (form) {
|
||
form.style.display = 'none';
|
||
}
|
||
|
||
// Show success message
|
||
showSuccess('Salesforce credentials auto-loaded successfully!');
|
||
|
||
} catch (e) {
|
||
console.error('Error loading saved credentials:', e);
|
||
}
|
||
} else {
|
||
console.log('No saved credentials found');
|
||
}
|
||
}
|
||
|
||
// Real-time customization updates
|
||
function updateCustomizationPreview() {
|
||
const headerStyle = document.getElementById('headerStyle').value;
|
||
const colorScheme = document.getElementById('colorScheme').value;
|
||
const fontStyle = document.getElementById('fontStyle').value;
|
||
|
||
// Update preview content with new styles
|
||
const previewContent = document.getElementById('previewContent');
|
||
if (previewContent) {
|
||
// Apply color scheme
|
||
const colorMap = {
|
||
'blue': '#007bff',
|
||
'green': '#28a745',
|
||
'purple': '#6f42c1',
|
||
'gold': '#ffc107'
|
||
};
|
||
|
||
const selectedColor = colorMap[colorScheme] || '#007bff';
|
||
|
||
// Update preview styling
|
||
previewContent.style.setProperty('--accent-color', selectedColor);
|
||
previewContent.style.setProperty('--header-style', headerStyle);
|
||
previewContent.style.setProperty('--font-family', fontStyle === 'serif' ? 'Georgia, serif' : 'Arial, sans-serif');
|
||
|
||
// Add visual feedback
|
||
document.body.style.setProperty('--custom-accent', selectedColor);
|
||
|
||
// Update any color-coded elements in the preview
|
||
const colorElements = previewContent.querySelectorAll('.color-accent');
|
||
colorElements.forEach(el => {
|
||
el.style.color = selectedColor;
|
||
});
|
||
|
||
// Update header styling
|
||
const headers = previewContent.querySelectorAll('h1, h2, h3');
|
||
headers.forEach(header => {
|
||
if (headerStyle === 'luxury') {
|
||
header.style.fontWeight = '900';
|
||
header.style.textShadow = '2px 2px 4px rgba(0,0,0,0.3)';
|
||
} else if (headerStyle === 'classic') {
|
||
header.style.fontWeight = '700';
|
||
header.style.fontStyle = 'italic';
|
||
} else {
|
||
header.style.fontWeight = '600';
|
||
header.style.textShadow = 'none';
|
||
header.style.fontStyle = 'normal';
|
||
}
|
||
});
|
||
|
||
// Show visual feedback
|
||
showCustomizationFeedback(headerStyle, colorScheme, fontStyle);
|
||
}
|
||
}
|
||
|
||
function showCustomizationFeedback(headerStyle, colorScheme, fontStyle) {
|
||
// Create or update feedback element
|
||
let feedback = document.getElementById('customization-feedback');
|
||
if (!feedback) {
|
||
feedback = document.createElement('div');
|
||
feedback.id = 'customization-feedback';
|
||
feedback.style.cssText = `
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: #28a745;
|
||
color: white;
|
||
padding: 15px 20px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
||
z-index: 1000;
|
||
font-weight: 600;
|
||
transform: translateX(100%);
|
||
transition: transform 0.3s ease;
|
||
`;
|
||
document.body.appendChild(feedback);
|
||
}
|
||
|
||
feedback.innerHTML = `
|
||
<div style="display: flex; align-items: center; gap: 10px;">
|
||
<span style="font-size: 1.2rem;">✨</span>
|
||
<span>Style Updated: ${headerStyle} • ${colorScheme} • ${fontStyle}</span>
|
||
</div>
|
||
`;
|
||
|
||
// Show feedback
|
||
setTimeout(() => {
|
||
feedback.style.transform = 'translateX(0)';
|
||
}, 100);
|
||
|
||
// Hide feedback after 3 seconds
|
||
setTimeout(() => {
|
||
feedback.style.transform = 'translateX(100%)';
|
||
}, 3000);
|
||
}
|
||
|
||
// Salesforce Direct Integration Functions
|
||
async function authenticateWithSalesforce() {
|
||
try {
|
||
// Check if credentials are set
|
||
if (!salesforceCredentials.clientId || !salesforceCredentials.clientSecret ||
|
||
!salesforceCredentials.username || !salesforceCredentials.password ||
|
||
!salesforceCredentials.securityToken) {
|
||
|
||
// Show credential input form
|
||
showCredentialInputForm();
|
||
return false;
|
||
}
|
||
|
||
const authUrl = `${salesforceApiUrl}/services/oauth2/token`;
|
||
const formData = new FormData();
|
||
formData.append('grant_type', 'password');
|
||
formData.append('client_id', salesforceCredentials.clientId);
|
||
formData.append('client_secret', salesforceCredentials.clientSecret);
|
||
formData.append('username', salesforceCredentials.username);
|
||
formData.append('password', salesforceCredentials.password + salesforceCredentials.securityToken);
|
||
|
||
const response = await fetch(authUrl, {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`Authentication failed: ${response.status}`);
|
||
}
|
||
|
||
const authData = await response.json();
|
||
window.salesforceAccessToken = authData.access_token;
|
||
window.salesforceInstanceUrl = authData.instance_url;
|
||
|
||
console.log('Authenticated with Salesforce:', authData.instance_url);
|
||
return true;
|
||
|
||
} catch (error) {
|
||
console.error('Salesforce authentication failed:', error);
|
||
showError(`Salesforce authentication failed: ${error.message}`);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async function loadPropertiesFromSalesforce() {
|
||
try {
|
||
document.getElementById('propertyLoading').style.display = 'block';
|
||
document.getElementById('propertyError').style.display = 'none';
|
||
|
||
// First authenticate
|
||
const isAuthenticated = await authenticateWithSalesforce();
|
||
if (!isAuthenticated) {
|
||
return;
|
||
}
|
||
|
||
// Query all properties from Salesforce
|
||
const query = `
|
||
SELECT Id, Name, pcrm__Property_Type__c, pcrm__Sub_Locality_Bayut_Dubizzle__c,
|
||
pcrm__Sale_Price_max__c, pcrm__Rent_Price_max__c, pcrm__Bedrooms__c,
|
||
pcrm__Bathrooms__c, pcrm__Size__c, pcrm__Description_English__c,
|
||
pcrm__Title_English__c, pcrm__Unit_Number__c, pcrm__Completion_Status__c,
|
||
pcrm__Furnished__c, pcrm__View__c, pcrm__Tower_Bayut_Dubizzle__c,
|
||
pcrm__Community_Propertyfinder__c, pcrm__Sub_Community_Propertyfinder__c,
|
||
pcrm__City_Propertyfinder__c, pcrm__City_Bayut_Dubizzle__c,
|
||
pcrm__Private_Amenities__c, pcrm__Commercial_Amenities__c,
|
||
pcrm__Coordinates__c, pcrm__Build_Year__c, pcrm__Stories__c,
|
||
pcrm__Parking_Spaces__c, pcrm__Lot_Size__c, pcrm__Service_Charge__c,
|
||
pcrm__Floor__c, pcrm__Total_Units__c, pcrm__Developer__c,
|
||
pcrm__Handover_Date__c, pcrm__Payment_Plan__c, pcrm__Maintenance_Company__c
|
||
FROM pcrm__Property__c
|
||
ORDER BY Name
|
||
`;
|
||
|
||
const queryUrl = `${window.salesforceInstanceUrl}/services/data/v59.0/query`;
|
||
const params = new URLSearchParams({ q: query });
|
||
|
||
const response = await fetch(`${queryUrl}?${params}`, {
|
||
headers: {
|
||
'Authorization': `Bearer ${window.salesforceAccessToken}`,
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`Salesforce API Error: ${response.status}`);
|
||
}
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.records && result.records.length > 0) {
|
||
// Format properties for dropdown
|
||
availableProperties = result.records.map(prop => {
|
||
const propertyType = prop.pcrm__Property_Type__c || 'Unknown';
|
||
const location = prop.pcrm__Sub_Locality_Bayut_Dubizzle__c || 'Unknown Location';
|
||
const price = prop.pcrm__Sale_Price_max__c || prop.pcrm__Rent_Price_max__c;
|
||
const priceText = price ? `AED ${price.toLocaleString()}` : 'Price on request';
|
||
|
||
const label = `${prop.Name} - ${propertyType} - ${location} - ${priceText}`;
|
||
|
||
return {
|
||
label: label,
|
||
value: prop.Id,
|
||
property: prop,
|
||
summary: {
|
||
type: propertyType,
|
||
location: location,
|
||
price: price,
|
||
bedrooms: prop.pcrm__Bedrooms__c,
|
||
bathrooms: prop.pcrm__Bathrooms__c,
|
||
area: prop.pcrm__Size__c,
|
||
status: prop.pcrm__Completion_Status__c
|
||
}
|
||
};
|
||
});
|
||
|
||
populatePropertyDropdown();
|
||
console.log(`Loaded ${availableProperties.length} properties directly from Salesforce`);
|
||
|
||
// Show success message
|
||
showSuccess(`Successfully loaded ${availableProperties.length} properties from your Salesforce sandbox!`);
|
||
|
||
} else {
|
||
throw new Error('No properties found in Salesforce');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Error loading properties from Salesforce:', error);
|
||
document.getElementById('propertyError').innerHTML = `
|
||
<strong>Error loading properties from Salesforce:</strong> ${error.message}<br>
|
||
<small>Please check your credentials and ensure the pcrm__Property__c object exists</small>
|
||
`;
|
||
document.getElementById('propertyError').style.display = 'block';
|
||
} finally {
|
||
document.getElementById('propertyLoading').style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// Legacy function for backward compatibility
|
||
async function loadPropertiesFromAPI() {
|
||
// Redirect to Salesforce function
|
||
await loadPropertiesFromSalesforce();
|
||
}
|
||
|
||
// Credential management functions
|
||
function showCredentialInputForm() {
|
||
document.getElementById('salesforceCredentialsForm').style.display = 'block';
|
||
|
||
// Load saved credentials if they exist
|
||
const savedCredentials = localStorage.getItem('salesforceCredentials');
|
||
if (savedCredentials) {
|
||
try {
|
||
const creds = JSON.parse(savedCredentials);
|
||
document.getElementById('sfClientId').value = creds.clientId || '';
|
||
document.getElementById('sfClientSecret').value = creds.clientSecret || '';
|
||
document.getElementById('sfUsername').value = creds.username || '';
|
||
document.getElementById('sfPassword').value = creds.password || '';
|
||
document.getElementById('sfSecurityToken').value = creds.securityToken || '';
|
||
} catch (e) {
|
||
console.error('Error loading saved credentials:', e);
|
||
}
|
||
}
|
||
}
|
||
|
||
function hideCredentialForm() {
|
||
document.getElementById('salesforceCredentialsForm').style.display = 'none';
|
||
}
|
||
|
||
function saveSalesforceCredentials() {
|
||
const clientId = document.getElementById('sfClientId').value.trim();
|
||
const clientSecret = document.getElementById('sfClientSecret').value.trim();
|
||
const username = document.getElementById('sfUsername').value.trim();
|
||
const password = document.getElementById('sfPassword').value.trim();
|
||
const securityToken = document.getElementById('sfSecurityToken').value.trim();
|
||
|
||
if (!clientId || !clientSecret || !username || !password || !securityToken) {
|
||
alert('Please fill in all credential fields');
|
||
return;
|
||
}
|
||
|
||
// Save credentials
|
||
salesforceCredentials = {
|
||
clientId,
|
||
clientSecret,
|
||
username,
|
||
password,
|
||
securityToken
|
||
};
|
||
|
||
// Save to localStorage for persistence
|
||
localStorage.setItem('salesforceCredentials', JSON.stringify(salesforceCredentials));
|
||
|
||
// Hide form
|
||
hideCredentialForm();
|
||
|
||
// Show success message
|
||
showSuccess('Salesforce credentials saved successfully! Now loading properties...');
|
||
|
||
// Load properties
|
||
setTimeout(() => {
|
||
loadPropertiesFromSalesforce();
|
||
}, 1000);
|
||
}
|
||
|
||
function showSuccess(message) {
|
||
let successDiv = document.getElementById('successMessage');
|
||
if (!successDiv) {
|
||
successDiv = document.createElement('div');
|
||
successDiv.id = 'successMessage';
|
||
successDiv.style.cssText = `
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: #28a745;
|
||
color: white;
|
||
padding: 15px 20px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
||
z-index: 1000;
|
||
font-weight: 600;
|
||
transform: translateX(100%);
|
||
transition: transform 0.3s ease;
|
||
max-width: 400px;
|
||
`;
|
||
document.body.appendChild(successDiv);
|
||
}
|
||
|
||
successDiv.innerHTML = `
|
||
<div style="display: flex; align-items: center; gap: 10px;">
|
||
<span style="font-size: 1.2rem;">✅</span>
|
||
<span>${message}</span>
|
||
</div>
|
||
`;
|
||
|
||
// Show feedback
|
||
setTimeout(() => {
|
||
successDiv.style.transform = 'translateX(0)';
|
||
}, 100);
|
||
|
||
// Hide feedback after 4 seconds
|
||
setTimeout(() => {
|
||
successDiv.style.transform = 'translateX(100%)';
|
||
}, 4000);
|
||
}
|
||
|
||
function showError(message) {
|
||
let errorDiv = document.getElementById('errorMessage');
|
||
if (!errorDiv) {
|
||
errorDiv = document.createElement('div');
|
||
errorDiv.id = 'errorMessage';
|
||
errorDiv.style.cssText = `
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: #dc3545;
|
||
color: white;
|
||
padding: 15px 20px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
||
z-index: 1000;
|
||
font-weight: 600;
|
||
transform: translateX(100%);
|
||
transition: transform 0.3s ease;
|
||
max-width: 400px;
|
||
`;
|
||
document.body.appendChild(errorDiv);
|
||
}
|
||
|
||
errorDiv.innerHTML = `
|
||
<div style="display: flex; align-items: center; gap: 10px;">
|
||
<span style="font-size: 1.2rem;">❌</span>
|
||
<span>${message}</span>
|
||
</div>
|
||
`;
|
||
|
||
// Show feedback
|
||
setTimeout(() => {
|
||
errorDiv.style.transform = 'translateX(0)';
|
||
}, 100);
|
||
|
||
// Hide feedback after 5 seconds
|
||
setTimeout(() => {
|
||
errorDiv.style.transform = 'translateX(100%)';
|
||
}, 5000);
|
||
}
|
||
|
||
function populatePropertyDropdown() {
|
||
const select = document.getElementById('propertySelect');
|
||
select.innerHTML = '<option value="">Choose a property from your Salesforce data...</option>';
|
||
|
||
availableProperties.forEach(prop => {
|
||
const option = document.createElement('option');
|
||
option.value = prop.value;
|
||
option.textContent = prop.label;
|
||
option.dataset.property = JSON.stringify(prop.property);
|
||
select.appendChild(option);
|
||
});
|
||
|
||
// Show property statistics
|
||
showPropertyStatistics();
|
||
}
|
||
|
||
function showPropertyStatistics() {
|
||
const stats = {
|
||
total: availableProperties.length,
|
||
types: {},
|
||
locations: {}
|
||
};
|
||
|
||
availableProperties.forEach(prop => {
|
||
const type = prop.property.pcrm__Property_Type__c || 'Unknown';
|
||
const location = prop.property.pcrm__Sub_Locality_Bayut_Dubizzle__c || 'Unknown';
|
||
|
||
stats.types[type] = (stats.types[type] || 0) + 1;
|
||
stats.locations[location] = (stats.locations[location] || 0) + 1;
|
||
});
|
||
|
||
console.log('Property Statistics:', stats);
|
||
|
||
// Show stats in the UI if there's a stats element
|
||
const statsElement = document.getElementById('propertyStats');
|
||
if (statsElement) {
|
||
statsElement.innerHTML = `
|
||
<div style="background: #e3f2fd; padding: 15px; border-radius: 10px; margin: 15px 0;">
|
||
<h4 style="margin: 0 0 10px 0; color: #1976d2;">Property Database Statistics</h4>
|
||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px;">
|
||
<div>
|
||
<strong>Total Properties:</strong> ${stats.total}
|
||
</div>
|
||
<div>
|
||
<strong>Property Types:</strong> ${Object.keys(stats.types).length}
|
||
</div>
|
||
<div>
|
||
<strong>Locations:</strong> ${Object.keys(stats.locations).length}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
function handlePropertySelection() {
|
||
const select = document.getElementById('propertySelect');
|
||
const selectedId = select.value;
|
||
|
||
if (!selectedId) {
|
||
clearPropertyForm();
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const selectedOption = select.options[select.selectedIndex];
|
||
const property = JSON.parse(selectedOption.dataset.property);
|
||
|
||
// Enhanced property type mapping
|
||
const propertyTypeMap = {
|
||
'AP': 'Apartment',
|
||
'VI': 'Villa',
|
||
'PE': 'Penthouse',
|
||
'TH': 'Townhouse',
|
||
'BU': 'Office',
|
||
'BW': 'Retail'
|
||
};
|
||
|
||
// Fill form fields with enhanced data
|
||
document.getElementById('propertyName').value = property.Name || '';
|
||
|
||
const propertyType = propertyTypeMap[property.pcrm__Property_Type__c] || 'Apartment';
|
||
document.getElementById('propertyType').value = propertyType;
|
||
|
||
document.getElementById('location').value = property.pcrm__Sub_Locality_Bayut_Dubizzle__c || '';
|
||
document.getElementById('price').value = property.pcrm__Sale_Price_max__c || property.pcrm__Rent_Price_max__c || '';
|
||
document.getElementById('bedrooms').value = property.pcrm__Bedrooms__c || '';
|
||
document.getElementById('bathrooms').value = property.pcrm__Bathrooms__c || '';
|
||
document.getElementById('area').value = property.pcrm__Size__c || '';
|
||
document.getElementById('description').value = property.pcrm__Description_English__c || '';
|
||
|
||
// Auto-select amenities based on property data
|
||
if (property.pcrm__Private_Amenities__c) {
|
||
selectAmenitiesFromString(property.pcrm__Private_Amenities__c);
|
||
}
|
||
|
||
// Store complete property data for PDF generation with all new fields
|
||
propertyData = {
|
||
...propertyData,
|
||
propertyName: property.Name,
|
||
propertyType: propertyType,
|
||
location: property.pcrm__Sub_Locality_Bayut_Dubizzle__c,
|
||
price: property.pcrm__Sale_Price_max__c || property.pcrm__Rent_Price_max__c,
|
||
bedrooms: property.pcrm__Bedrooms__c,
|
||
bathrooms: property.pcrm__Bathrooms__c,
|
||
area: property.pcrm__Size__c,
|
||
description: property.pcrm__Description_English__c,
|
||
titleEnglish: property.pcrm__Title_English__c,
|
||
unitNumber: property.pcrm__Unit_Number__c,
|
||
completionStatus: property.pcrm__Completion_Status__c,
|
||
furnished: property.pcrm__Furnished__c,
|
||
view: property.pcrm__View__c,
|
||
tower: property.pcrm__Tower_Bayut_Dubizzle__c,
|
||
city: property.pcrm__City_Bayut_Dubizzle__c,
|
||
subCommunity: property.pcrm__Sub_Community_Propertyfinder__c,
|
||
buildYear: property.pcrm__Build_Year__c,
|
||
stories: property.pcrm__Stories__c,
|
||
parkingSpaces: property.pcrm__Parking_Spaces__c,
|
||
lotSize: property.pcrm__Lot_Size__c,
|
||
serviceCharge: property.pcrm__Service_Charge__c,
|
||
privateAmenities: property.pcrm__Private_Amenities__c,
|
||
commercialAmenities: property.pcrm__Commercial_Amenities__c,
|
||
coordinates: property.pcrm__Coordinates__c,
|
||
floor: property.pcrm__Floor__c,
|
||
totalUnits: property.pcrm__Total_Units__c,
|
||
developer: property.pcrm__Developer__c,
|
||
handoverDate: property.pcrm__Handover_Date__c,
|
||
paymentPlan: property.pcrm__Payment_Plan__c,
|
||
maintenanceCompany: property.pcrm__Maintenance_Company__c
|
||
};
|
||
|
||
console.log('Property selected and form populated with enhanced data:', propertyData);
|
||
|
||
// Enhanced visual feedback with property details
|
||
showPropertySelectedFeedback(property.Name, propertyType, property.pcrm__Sub_Locality_Bayut_Dubizzle__c);
|
||
|
||
} catch (error) {
|
||
console.error('Error handling property selection:', error);
|
||
alert('Error loading property data. Please try again.');
|
||
}
|
||
}
|
||
|
||
function clearPropertyForm() {
|
||
document.getElementById('propertyName').value = '';
|
||
document.getElementById('propertyType').value = '';
|
||
document.getElementById('location').value = '';
|
||
document.getElementById('price').value = '';
|
||
document.getElementById('bedrooms').value = '';
|
||
document.getElementById('bathrooms').value = '';
|
||
document.getElementById('area').value = '';
|
||
document.getElementById('description').value = '';
|
||
|
||
// Clear amenities
|
||
document.querySelectorAll('.amenity-item input[type="checkbox"]').forEach(checkbox => {
|
||
checkbox.checked = false;
|
||
});
|
||
}
|
||
|
||
function selectAmenitiesFromString(amenitiesString) {
|
||
if (!amenitiesString) return;
|
||
|
||
const amenities = amenitiesString.split(',').map(a => a.trim().toLowerCase());
|
||
|
||
document.querySelectorAll('.amenity-item input[type="checkbox"]').forEach(checkbox => {
|
||
const label = checkbox.nextElementSibling.textContent.toLowerCase();
|
||
checkbox.checked = amenities.some(amenity =>
|
||
amenity.includes(label) || label.includes(amenity)
|
||
);
|
||
});
|
||
}
|
||
|
||
function showPropertySelectedFeedback(propertyName, propertyType, location) {
|
||
let feedback = document.getElementById('property-selected-feedback');
|
||
if (!feedback) {
|
||
feedback = document.createElement('div');
|
||
feedback.id = 'property-selected-feedback';
|
||
feedback.style.cssText = `
|
||
position: fixed;
|
||
top: 80px;
|
||
right: 20px;
|
||
background: #28a745;
|
||
color: white;
|
||
padding: 20px;
|
||
border-radius: 15px;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
|
||
z-index: 1000;
|
||
font-weight: 600;
|
||
transform: translateX(100%);
|
||
transition: transform 0.3s ease;
|
||
max-width: 350px;
|
||
`;
|
||
document.body.appendChild(feedback);
|
||
}
|
||
|
||
feedback.innerHTML = `
|
||
<div style="display: flex; flex-direction: column; gap: 8px;">
|
||
<div style="display: flex; align-items: center; gap: 10px; font-size: 1.1rem;">
|
||
<span style="font-size: 1.3rem;">🏠</span>
|
||
<span><strong>Property Loaded Successfully!</strong></span>
|
||
</div>
|
||
<div style="font-size: 0.9rem; opacity: 0.9;">
|
||
<div><strong>Name:</strong> ${propertyName}</div>
|
||
<div><strong>Type:</strong> ${propertyType}</div>
|
||
<div><strong>Location:</strong> ${location}</div>
|
||
</div>
|
||
<div style="font-size: 0.8rem; opacity: 0.8; margin-top: 5px;">
|
||
All fields have been auto-populated from your Salesforce data
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Show feedback
|
||
setTimeout(() => {
|
||
feedback.style.transform = 'translateX(0)';
|
||
}, 100);
|
||
|
||
// Hide feedback after 5 seconds
|
||
setTimeout(() => {
|
||
feedback.style.transform = 'translateX(100%)';
|
||
}, 5000);
|
||
}
|
||
|
||
// Template category management
|
||
|
||
|
||
// Template selection
|
||
function selectTemplate(templateName) {
|
||
try {
|
||
// Remove previous selection
|
||
document.querySelectorAll('.template-card').forEach(card => {
|
||
card.classList.remove('selected');
|
||
});
|
||
|
||
// Select new template
|
||
if (event && event.currentTarget) {
|
||
event.currentTarget.classList.add('selected');
|
||
}
|
||
selectedTemplate = templateName;
|
||
|
||
// Show layout options for custom template
|
||
const layoutOptions = document.getElementById('layoutOptions');
|
||
if (templateName === 'custom' && layoutOptions) {
|
||
layoutOptions.style.display = 'block';
|
||
} else if (layoutOptions) {
|
||
layoutOptions.style.display = 'none';
|
||
}
|
||
|
||
// Enable next button
|
||
const step1Next = document.getElementById('step1-next');
|
||
if (step1Next) {
|
||
step1Next.disabled = false;
|
||
}
|
||
} catch (error) {
|
||
console.error('Error in selectTemplate:', error);
|
||
}
|
||
}
|
||
|
||
// Layout selection
|
||
function selectLayout(layout) {
|
||
try {
|
||
selectedLayout = layout;
|
||
// Update layout preview
|
||
document.querySelectorAll('.layout-option').forEach(option => {
|
||
option.style.borderColor = '#e9ecef';
|
||
});
|
||
if (event && event.currentTarget) {
|
||
event.currentTarget.style.borderColor = '#007bff';
|
||
}
|
||
} catch (error) {
|
||
console.error('Error in selectLayout:', error);
|
||
}
|
||
}
|
||
|
||
// Step navigation
|
||
function nextStep() {
|
||
if (currentStep < 5) {
|
||
if (validateCurrentStep()) {
|
||
currentStep++;
|
||
updateStepDisplay();
|
||
}
|
||
}
|
||
}
|
||
|
||
function previousStep() {
|
||
if (currentStep > 1) {
|
||
currentStep--;
|
||
updateStepDisplay();
|
||
}
|
||
}
|
||
|
||
function updateStepDisplay() {
|
||
// Hide all steps
|
||
document.querySelectorAll('.step-content').forEach(step => {
|
||
step.classList.remove('active');
|
||
});
|
||
|
||
// Show current step
|
||
document.getElementById(`step${currentStep}`).classList.add('active');
|
||
|
||
// Update navigation
|
||
updateStepNavigation();
|
||
|
||
// Handle step-specific actions
|
||
if (currentStep === 2) {
|
||
// Check if we have Salesforce credentials
|
||
const savedCredentials = localStorage.getItem('salesforceCredentials');
|
||
if (savedCredentials) {
|
||
try {
|
||
const creds = JSON.parse(savedCredentials);
|
||
salesforceCredentials = creds;
|
||
// Load properties from Salesforce
|
||
loadPropertiesFromSalesforce();
|
||
} catch (e) {
|
||
console.error('Error loading saved credentials:', e);
|
||
showCredentialInputForm();
|
||
}
|
||
} else {
|
||
// Show credential form if no credentials exist
|
||
showCredentialInputForm();
|
||
}
|
||
} else if (currentStep === 4) {
|
||
generatePreview();
|
||
} else if (currentStep === 5) {
|
||
// Show loading spinner when entering step 5
|
||
document.getElementById('loadingSpinner').style.display = 'block';
|
||
document.getElementById('pdfResult').style.display = 'none';
|
||
}
|
||
}
|
||
|
||
function updateStepNavigation() {
|
||
document.querySelectorAll('.step-item').forEach((item, index) => {
|
||
item.classList.remove('active', 'completed');
|
||
if (index + 1 === currentStep) {
|
||
item.classList.add('active');
|
||
} else if (index + 1 < currentStep) {
|
||
item.classList.add('completed');
|
||
}
|
||
});
|
||
}
|
||
|
||
function validateCurrentStep() {
|
||
if (currentStep === 1) {
|
||
if (!selectedTemplate) {
|
||
alert('Please select a template first.');
|
||
return false;
|
||
}
|
||
if (selectedTemplate === 'custom' && !selectedLayout) {
|
||
alert('Please select a layout for custom template.');
|
||
return false;
|
||
}
|
||
} else if (currentStep === 2) {
|
||
const requiredFields = ['propertyName', 'propertyType', 'location', 'price', 'bedrooms', 'bathrooms', 'area'];
|
||
for (let field of requiredFields) {
|
||
if (!document.getElementById(field).value) {
|
||
alert('Please fill in all required fields.');
|
||
return false;
|
||
}
|
||
}
|
||
} else if (currentStep === 3) {
|
||
const contentModules = Array.from(document.querySelectorAll('#step3 input[type="checkbox"]:checked'));
|
||
if (contentModules.length === 0) {
|
||
alert('Please select at least one content module.');
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
async function generatePreview() {
|
||
try {
|
||
// Collect images with room names
|
||
const imagesWithNames = [];
|
||
if (window.propertyImages) {
|
||
imagesWithNames.push(...window.propertyImages);
|
||
}
|
||
|
||
// Collect form data
|
||
propertyData = {
|
||
template: selectedTemplate,
|
||
layout: selectedLayout,
|
||
propertyName: document.getElementById('propertyName').value,
|
||
propertyType: document.getElementById('propertyType').value,
|
||
location: document.getElementById('location').value,
|
||
price: document.getElementById('price').value,
|
||
bedrooms: document.getElementById('bedrooms').value,
|
||
bathrooms: document.getElementById('bathrooms').value,
|
||
area: document.getElementById('area').value,
|
||
description: document.getElementById('description').value,
|
||
amenities: Array.from(document.querySelectorAll('#step2 input[type="checkbox"]:checked')).map(cb => cb.value),
|
||
images: imagesWithNames.map(img => img.data), // Extract base64 data
|
||
imageNames: imagesWithNames.map(img => img.name), // Extract room names
|
||
// Market Data
|
||
marketTrend: document.getElementById('marketTrend').value,
|
||
roiPotential: document.getElementById('roiPotential').value,
|
||
avgPricePerSqft: document.getElementById('avgPricePerSqft').value,
|
||
marketDemand: document.getElementById('marketDemand').value,
|
||
locationAdvantages: document.getElementById('locationAdvantages').value,
|
||
// Investment Data
|
||
investmentType: document.getElementById('investmentType').value,
|
||
rentalYield: document.getElementById('rentalYield').value,
|
||
investmentHighlights: document.getElementById('investmentHighlights').value,
|
||
// Content Modules
|
||
contentModules: Array.from(document.querySelectorAll('#step3 input[type="checkbox"]:checked')).map(cb => cb.value),
|
||
additionalContent: document.getElementById('additionalContent').value
|
||
};
|
||
|
||
// Call API to generate preview
|
||
const response = await fetch(`${apiBaseUrl}/preview`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(propertyData)
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const previewData = await response.json();
|
||
|
||
if (previewData.success) {
|
||
// Display preview content
|
||
const previewContent = document.getElementById('previewContent');
|
||
previewContent.innerHTML = previewData.preview.preview_html;
|
||
} else {
|
||
throw new Error(previewData.message || 'Failed to generate preview');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Error generating preview:', error);
|
||
alert('Error generating preview. Please try again.');
|
||
|
||
// Fallback to local preview
|
||
const previewContent = document.getElementById('previewContent');
|
||
previewContent.innerHTML = generateLocalPreview(propertyData);
|
||
}
|
||
}
|
||
|
||
function generateLocalPreview(data) {
|
||
// Fallback preview generation if API fails
|
||
let preview = `
|
||
<div style="background: white; padding: 30px; border-radius: 15px; box-shadow: 0 4px 20px rgba(0,0,0,0.1);">
|
||
<div style="text-align: center; margin-bottom: 30px;">
|
||
<h1 style="color: #2c3e50; font-size: 2.5rem; margin-bottom: 10px;">${data.propertyName}</h1>
|
||
<p style="color: #6c757d; font-size: 1.2rem;">${data.propertyType} in ${data.location}</p>
|
||
</div>
|
||
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-bottom: 30px;">
|
||
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
|
||
<h3 style="color: #2c3e50; margin-bottom: 15px;">Property Details</h3>
|
||
<p><strong>Type:</strong> ${data.propertyType}</p>
|
||
<p><strong>Location:</strong> ${data.location}</p>
|
||
<p><strong>Price:</strong> AED ${data.price}</p>
|
||
<p><strong>Bedrooms:</strong> ${data.bedrooms}</p>
|
||
<p><strong>Bathrooms:</strong> ${data.bathrooms}</p>
|
||
<p><strong>Area:</strong> ${data.area} sq ft</p>
|
||
</div>
|
||
|
||
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
|
||
<h3 style="color: #2c3e50; margin-bottom: 15px;">Amenities</h3>
|
||
${data.amenities.length > 0 ? data.amenities.map(amenity => `<p>• ${amenity}</p>`).join('') : '<p>No amenities selected</p>'}
|
||
</div>
|
||
</div>
|
||
|
||
${data.description ? `
|
||
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin-bottom: 30px;">
|
||
<h3 style="color: #2c3e50; margin-bottom: 15px;">Description</h3>
|
||
<p>${data.description}</p>
|
||
</div>
|
||
` : ''}
|
||
|
||
<div style="text-align: center; color: #6c757d; font-style: italic;">
|
||
<p>Template: ${data.template.charAt(0).toUpperCase() + data.template.slice(1)}</p>
|
||
<p>This is a preview. The final PDF will be generated with the selected template styling.</p>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
return preview;
|
||
}
|
||
|
||
async function generatePDF() {
|
||
try {
|
||
// Show loading spinner
|
||
document.getElementById('loadingSpinner').style.display = 'block';
|
||
document.getElementById('generate-pdf-btn').disabled = true;
|
||
|
||
// Prepare request data
|
||
const requestData = {
|
||
property_data: propertyData,
|
||
template_name: selectedTemplate
|
||
};
|
||
|
||
// Call API to generate PDF
|
||
const response = await fetch(`${apiBaseUrl}/generate-pdf`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(requestData)
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
// Store PDF URL for download (remove the /api prefix since it's already in the URL)
|
||
const pdfUrl = result.pdf_url.replace('/api', '');
|
||
window.generatedPdfUrl = pdfUrl;
|
||
|
||
// Move to step 5 (PDF Export)
|
||
currentStep = 5;
|
||
updateStepDisplay();
|
||
|
||
// Show PDF result
|
||
document.getElementById('loadingSpinner').style.display = 'none';
|
||
document.getElementById('pdfResult').style.display = 'block';
|
||
} else {
|
||
throw new Error(result.error || 'Failed to generate PDF');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Error generating PDF:', error);
|
||
alert('Error generating PDF. Please try again.');
|
||
|
||
// Hide loading spinner and re-enable button
|
||
document.getElementById('loadingSpinner').style.display = 'none';
|
||
document.getElementById('generate-pdf-btn').disabled = false;
|
||
}
|
||
}
|
||
|
||
async function downloadPDF() {
|
||
try {
|
||
if (window.generatedPdfUrl) {
|
||
// Download the generated PDF
|
||
const response = await fetch(`${apiBaseUrl}${window.generatedPdfUrl}`);
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const blob = await response.blob();
|
||
const url = window.URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `property_brochure_${selectedTemplate}_${Date.now()}.pdf`;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
window.URL.revokeObjectURL(url);
|
||
document.body.removeChild(a);
|
||
} else {
|
||
// Fallback: generate a demo PDF
|
||
alert('Generating demo PDF...');
|
||
const response = await fetch(`${apiBaseUrl}/download-pdf/demo`);
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const blob = await response.blob();
|
||
const url = window.URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `demo_property_brochure.pdf`;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
window.URL.revokeObjectURL(url);
|
||
document.body.removeChild(a);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error downloading PDF:', error);
|
||
alert('Error downloading PDF. Please try again.');
|
||
}
|
||
}
|
||
|
||
function startOver() {
|
||
currentStep = 1;
|
||
selectedTemplate = null;
|
||
selectedLayout = null;
|
||
propertyData = {};
|
||
uploadedImages = [];
|
||
|
||
// Reset form
|
||
document.querySelectorAll('input, select, textarea').forEach(el => {
|
||
if (el.type === 'checkbox') {
|
||
el.checked = false;
|
||
} else {
|
||
el.value = '';
|
||
}
|
||
});
|
||
|
||
// Reset template selection
|
||
document.querySelectorAll('.template-card').forEach(card => {
|
||
card.classList.remove('selected');
|
||
});
|
||
|
||
// Reset layout options
|
||
document.getElementById('layoutOptions').style.display = 'none';
|
||
document.querySelectorAll('.layout-option').forEach(option => {
|
||
option.style.borderColor = '#e9ecef';
|
||
});
|
||
|
||
|
||
|
||
// Reset buttons
|
||
document.getElementById('step1-next').disabled = true;
|
||
|
||
// Reset preview and result displays
|
||
document.getElementById('previewContent').innerHTML = '';
|
||
document.getElementById('loadingSpinner').style.display = 'none';
|
||
document.getElementById('pdfResult').style.display = 'none';
|
||
|
||
// Update display
|
||
updateStepDisplay();
|
||
}
|
||
|
||
// Image upload handling
|
||
document.getElementById('propertyImages').addEventListener('change', function(e) {
|
||
const files = e.target.files;
|
||
if (files.length > 0) {
|
||
document.getElementById('imagePreview').style.display = 'block';
|
||
const imageGrid = document.getElementById('imageGrid');
|
||
imageGrid.innerHTML = '';
|
||
uploadedImages = [];
|
||
|
||
Array.from(files).forEach((file, index) => {
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
uploadedImages.push(e.target.result);
|
||
const imgDiv = document.createElement('div');
|
||
imgDiv.innerHTML = `
|
||
<div style="position: relative; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
||
<img src="${e.target.result}" style="width: 100%; height: 150px; object-fit: cover;">
|
||
<button onclick="removeImage(${index})" style="position: absolute; top: 5px; right: 5px; background: #dc3545; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; cursor: pointer;">×</button>
|
||
</div>
|
||
`;
|
||
imageGrid.appendChild(imgDiv);
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
}
|
||
});
|
||
|
||
function removeImage(index) {
|
||
uploadedImages.splice(index, 1);
|
||
// Refresh image preview
|
||
document.getElementById('propertyImages').dispatchEvent(new Event('change'));
|
||
}
|
||
|
||
// Check API connection on page load
|
||
async function checkApiConnection() {
|
||
try {
|
||
const response = await fetch(`${apiBaseUrl}/health`);
|
||
if (response.ok) {
|
||
console.log('API server is running');
|
||
} else {
|
||
console.warn('API server responded with error');
|
||
}
|
||
} catch (error) {
|
||
console.warn('Cannot connect to API server. Make sure it\'s running on http://localhost:8000');
|
||
console.warn('The application will work with local preview generation only.');
|
||
}
|
||
}
|
||
|
||
// Initialize
|
||
updateStepNavigation();
|
||
checkApiConnection();
|
||
|
||
// Add event listeners for real-time customization updates
|
||
document.getElementById('headerStyle').addEventListener('change', updateCustomizationPreview);
|
||
document.getElementById('colorScheme').addEventListener('change', updateCustomizationPreview);
|
||
document.getElementById('fontStyle').addEventListener('change', updateCustomizationPreview);
|
||
|
||
// Debug logging
|
||
console.log('Property Brochure Generator initialized successfully');
|
||
console.log('Available templates:', document.querySelectorAll('.template-card').length);
|
||
console.log('Available steps:', document.querySelectorAll('.step-content').length);
|
||
|
||
// Image Upload Functions
|
||
function addImageUpload() {
|
||
const imageUploads = document.getElementById('imageUploads');
|
||
const newUpload = document.createElement('div');
|
||
newUpload.className = 'image-upload-item';
|
||
newUpload.innerHTML = `
|
||
<div class="image-input-group">
|
||
<input type="text" class="room-name" placeholder="Room/Area Name (e.g., Master Bedroom, Kitchen, Living Room)" />
|
||
<input type="file" class="image-file" accept="image/*" onchange="handleImageUpload(this)" />
|
||
<button type="button" class="remove-image-btn" onclick="removeImageUpload(this)">Remove</button>
|
||
</div>
|
||
<div class="image-preview"></div>
|
||
`;
|
||
imageUploads.appendChild(newUpload);
|
||
}
|
||
|
||
function removeImageUpload(button) {
|
||
const uploadItem = button.closest('.image-upload-item');
|
||
uploadItem.remove();
|
||
}
|
||
|
||
function handleImageUpload(input) {
|
||
const file = input.files[0];
|
||
const uploadItem = input.closest('.image-upload-item');
|
||
const preview = uploadItem.querySelector('.image-preview');
|
||
const roomName = uploadItem.querySelector('.room-name').value || 'Unnamed Room';
|
||
|
||
if (file) {
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
const img = document.createElement('img');
|
||
img.src = e.target.result;
|
||
img.alt = roomName;
|
||
img.title = roomName;
|
||
|
||
// Clear previous previews
|
||
preview.innerHTML = '';
|
||
preview.appendChild(img);
|
||
|
||
// Store image data with room name
|
||
if (!window.propertyImages) window.propertyImages = [];
|
||
window.propertyImages.push({
|
||
name: roomName,
|
||
data: e.target.result,
|
||
file: file
|
||
});
|
||
};
|
||
reader.readAsDataURL(file);
|
||
}
|
||
}
|
||
|
||
// Update room name when it changes
|
||
document.addEventListener('input', function(e) {
|
||
if (e.target.classList.contains('room-name')) {
|
||
const uploadItem = e.target.closest('.image-upload-item');
|
||
const preview = uploadItem.querySelector('.image-preview img');
|
||
if (preview) {
|
||
preview.alt = e.target.value || 'Unnamed Room';
|
||
preview.title = e.target.value || 'Unnamed Room';
|
||
|
||
// Update stored image data
|
||
if (window.propertyImages) {
|
||
const index = Array.from(document.querySelectorAll('.image-upload-item')).indexOf(uploadItem);
|
||
if (index >= 0 && window.propertyImages[index]) {
|
||
window.propertyImages[index].name = e.target.value || 'Unnamed Room';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |