626 lines
23 KiB
HTML
626 lines
23 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Property Brochure Generator - API Test</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
border-radius: 20px;
|
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header {
|
|
background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
|
|
color: white;
|
|
padding: 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2.5rem;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.header p {
|
|
font-size: 1.2rem;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.test-section {
|
|
padding: 40px;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.test-section h2 {
|
|
color: #2c3e50;
|
|
margin-bottom: 20px;
|
|
font-size: 1.8rem;
|
|
}
|
|
|
|
.api-info {
|
|
background: #f8f9fa;
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.api-info h3 {
|
|
color: #495057;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.api-endpoint {
|
|
background: #e9ecef;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
font-family: monospace;
|
|
margin: 10px 0;
|
|
}
|
|
|
|
.property-selector {
|
|
background: #f8f9fa;
|
|
padding: 30px;
|
|
border-radius: 15px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
}
|
|
|
|
.form-group select,
|
|
.form-group input,
|
|
.form-group textarea {
|
|
width: 100%;
|
|
padding: 12px;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 8px;
|
|
font-size: 16px;
|
|
transition: border-color 0.3s;
|
|
}
|
|
|
|
.form-group select:focus,
|
|
.form-group input:focus,
|
|
.form-group textarea:focus {
|
|
outline: none;
|
|
border-color: #3498db;
|
|
}
|
|
|
|
.btn {
|
|
background: #3498db;
|
|
color: white;
|
|
padding: 12px 24px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
transition: background 0.3s;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.btn:hover {
|
|
background: #2980b9;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #6c757d;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #5a6268;
|
|
}
|
|
|
|
.btn-success {
|
|
background: #28a745;
|
|
}
|
|
|
|
.btn-success:hover {
|
|
background: #218838;
|
|
}
|
|
|
|
.property-details {
|
|
background: #fff;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 15px;
|
|
padding: 30px;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
.property-details h3 {
|
|
color: #2c3e50;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 2px solid #3498db;
|
|
}
|
|
|
|
.detail-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.detail-item {
|
|
background: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #3498db;
|
|
}
|
|
|
|
.detail-label {
|
|
font-weight: 600;
|
|
color: #495057;
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.detail-value {
|
|
color: #2c3e50;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
padding: 40px;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.spinner {
|
|
border: 4px solid #f3f3f3;
|
|
border-top: 4px solid #3498db;
|
|
border-radius: 50%;
|
|
width: 40px;
|
|
height: 40px;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto 20px;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border: 1px solid #f5c6cb;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border: 1px solid #c3e6cb;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.api-status {
|
|
display: inline-block;
|
|
padding: 5px 15px;
|
|
border-radius: 20px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.status-online {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
}
|
|
|
|
.status-offline {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>Property Brochure Generator</h1>
|
|
<p>Test API Integration - Property Selection & Data Population</p>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>🔌 API Information</h2>
|
|
<div class="api-info">
|
|
<h3>Test API Endpoints</h3>
|
|
<div class="api-endpoint">GET /api/properties - Get all properties list</div>
|
|
<div class="api-endpoint">GET /api/properties/{name} - Get property by name (e.g., PR-00036)</div>
|
|
<div class="api-endpoint">POST /api/generate-pdf - Generate PDF brochure</div>
|
|
|
|
<div style="margin-top: 20px;">
|
|
<strong>API Status:</strong>
|
|
<span id="api-status" class="api-status status-offline">Checking...</span>
|
|
</div>
|
|
|
|
<div style="margin-top: 20px;">
|
|
<strong>Base URL:</strong>
|
|
<span class="api-endpoint">http://localhost:8001</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Property Selection Test</h2>
|
|
<div class="property-selector">
|
|
<div class="form-group">
|
|
<label for="property-select">Select Property:</label>
|
|
<select id="property-select" onchange="handlePropertySelection()">
|
|
<option value="">Choose a property to test...</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<button class="btn" onclick="loadProperties()">Load Properties</button>
|
|
<button class="btn btn-secondary" onclick="testPropertyByName()">Test Property by Name</button>
|
|
<button class="btn btn-success" onclick="testPDFGeneration()">Test PDF Generation</button>
|
|
</div>
|
|
|
|
<div id="loading" class="loading" style="display: none;">
|
|
<div class="spinner"></div>
|
|
<p>Loading...</p>
|
|
</div>
|
|
|
|
<div id="error" class="error" style="display: none;"></div>
|
|
<div id="success" class="success" style="display: none;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="property-details" class="property-details" style="display: none;">
|
|
<h3>Selected Property Details</h3>
|
|
<div id="details-content"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const API_BASE_URL = 'http://localhost:8001';
|
|
let properties = [];
|
|
|
|
// Check API status on page load
|
|
window.onload = function() {
|
|
checkAPIStatus();
|
|
};
|
|
|
|
// Check if API is running
|
|
async function checkAPIStatus() {
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/api/health`);
|
|
if (response.ok) {
|
|
document.getElementById('api-status').textContent = 'Online';
|
|
document.getElementById('api-status').className = 'api-status status-online';
|
|
loadProperties(); // Auto-load properties if API is online
|
|
} else {
|
|
document.getElementById('api-status').textContent = 'Offline';
|
|
document.getElementById('api-status').className = 'api-status status-offline';
|
|
}
|
|
} catch (error) {
|
|
document.getElementById('api-status').textContent = 'Offline';
|
|
document.getElementById('api-status').className = 'api-status status-offline';
|
|
showError('API is not running. Please start the test server: python test_api_server.py');
|
|
}
|
|
}
|
|
|
|
// Load all properties
|
|
async function loadProperties() {
|
|
showLoading(true);
|
|
hideMessages();
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/api/properties`);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
properties = result.data;
|
|
populatePropertyDropdown();
|
|
showSuccess(`Loaded ${result.count} properties successfully!`);
|
|
} else {
|
|
throw new Error(result.message || 'Failed to load properties');
|
|
}
|
|
} catch (error) {
|
|
showError(`Error loading properties: ${error.message}`);
|
|
} finally {
|
|
showLoading(false);
|
|
}
|
|
}
|
|
|
|
// Populate property dropdown
|
|
function populatePropertyDropdown() {
|
|
const select = document.getElementById('property-select');
|
|
select.innerHTML = '<option value="">Choose a property to test...</option>';
|
|
|
|
properties.forEach(prop => {
|
|
const option = document.createElement('option');
|
|
option.value = prop.value;
|
|
option.textContent = prop.label;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
// Handle property selection
|
|
async function handlePropertySelection() {
|
|
const select = document.getElementById('property-select');
|
|
const selectedId = select.value;
|
|
|
|
if (!selectedId) {
|
|
hidePropertyDetails();
|
|
return;
|
|
}
|
|
|
|
const selectedProperty = properties.find(p => p.value === selectedId);
|
|
if (selectedProperty) {
|
|
displayPropertyDetails(selectedProperty.property);
|
|
}
|
|
}
|
|
|
|
// Display property details
|
|
function displayPropertyDetails(property) {
|
|
const detailsDiv = document.getElementById('property-details');
|
|
const contentDiv = document.getElementById('details-content');
|
|
|
|
contentDiv.innerHTML = `
|
|
<div class="detail-grid">
|
|
<div class="detail-item">
|
|
<span class="detail-label">Property ID</span>
|
|
<span class="detail-value">${property.Name}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Property Type</span>
|
|
<span class="detail-value">${property.pcrm__Property_Type__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Location</span>
|
|
<span class="detail-value">${property.pcrm__Sub_Locality_Bayut_Dubizzle__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Sale Price</span>
|
|
<span class="detail-value">${property.pcrm__Sale_Price_max__c ? `AED ${property.pcrm__Sale_Price_max__c.toLocaleString()}` : 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Rent Price</span>
|
|
<span class="detail-value">${property.pcrm__Rent_Price_max__c ? `AED ${property.pcrm__Rent_Price_max__c.toLocaleString()}` : 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Bedrooms</span>
|
|
<span class="detail-value">${property.pcrm__Bedrooms__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Bathrooms</span>
|
|
<span class="detail-value">${property.pcrm__Bathrooms__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Size (sq ft)</span>
|
|
<span class="detail-value">${property.pcrm__Size__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Unit Number</span>
|
|
<span class="detail-value">${property.pcrm__Unit_Number__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Completion Status</span>
|
|
<span class="detail-value">${property.pcrm__Completion_Status__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Furnished</span>
|
|
<span class="detail-value">${property.pcrm__Furnished__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">View</span>
|
|
<span class="detail-value">${property.pcrm__View__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Tower</span>
|
|
<span class="detail-value">${property.pcrm__Tower_Bayut_Dubizzle__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">City</span>
|
|
<span class="detail-value">${property.pcrm__City_Bayut_Dubizzle__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Build Year</span>
|
|
<span class="detail-value">${property.pcrm__Build_Year__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Stories</span>
|
|
<span class="detail-value">${property.pcrm__Stories__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Parking Spaces</span>
|
|
<span class="detail-value">${property.pcrm__Parking_Spaces__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Service Charge</span>
|
|
<span class="detail-value">${property.pcrm__Service_Charge__c ? `AED ${property.pcrm__Service_Charge__c.toLocaleString()}` : 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Private Amenities</span>
|
|
<span class="detail-value">${property.pcrm__Private_Amenities__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Commercial Amenities</span>
|
|
<span class="detail-value">${property.pcrm__Commercial_Amenities__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Coordinates</span>
|
|
<span class="detail-value">${property.pcrm__Coordinates__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Description</span>
|
|
<span class="detail-value">${property.pcrm__Description_English__c || 'N/A'}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Title (English)</span>
|
|
<span class="detail-value">${property.pcrm__Title_English__c || 'N/A'}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
detailsDiv.style.display = 'block';
|
|
}
|
|
|
|
// Test property by name
|
|
async function testPropertyByName() {
|
|
const propertyName = prompt('Enter property name (e.g., PR-00036):');
|
|
if (!propertyName) return;
|
|
|
|
showLoading(true);
|
|
hideMessages();
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/api/properties/${propertyName}`);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
displayPropertyDetails(result.data);
|
|
showSuccess(`Property ${propertyName} loaded successfully!`);
|
|
} else {
|
|
throw new Error(result.message || 'Failed to load property');
|
|
}
|
|
} catch (error) {
|
|
showError(`Error loading property ${propertyName}: ${error.message}`);
|
|
} finally {
|
|
showLoading(false);
|
|
}
|
|
}
|
|
|
|
// Test PDF generation
|
|
async function testPDFGeneration() {
|
|
const select = document.getElementById('property-select');
|
|
const selectedId = select.value;
|
|
|
|
if (!selectedId) {
|
|
showError('Please select a property first');
|
|
return;
|
|
}
|
|
|
|
const selectedProperty = properties.find(p => p.value === selectedId);
|
|
if (!selectedProperty) {
|
|
showError('Selected property not found');
|
|
return;
|
|
}
|
|
|
|
showLoading(true);
|
|
hideMessages();
|
|
|
|
try {
|
|
const propertyData = {
|
|
propertyName: selectedProperty.property.Name,
|
|
propertyType: selectedProperty.property.pcrm__Property_Type__c || '',
|
|
location: selectedProperty.property.pcrm__Sub_Locality_Bayut_Dubizzle__c || '',
|
|
price: selectedProperty.property.pcrm__Sale_Price_max__c || selectedProperty.property.pcrm__Rent_Price_max__c || 0,
|
|
bedrooms: selectedProperty.property.pcrm__Bedrooms__c || 0,
|
|
bathrooms: selectedProperty.property.pcrm__Bathrooms__c || 0,
|
|
area: selectedProperty.property.pcrm__Size__c || 0,
|
|
description: selectedProperty.property.pcrm__Description_English__c || '',
|
|
titleEnglish: selectedProperty.property.pcrm__Title_English__c || '',
|
|
unitNumber: selectedProperty.property.pcrm__Unit_Number__c || '',
|
|
completionStatus: selectedProperty.property.pcrm__Completion_Status__c || '',
|
|
furnished: selectedProperty.property.pcrm__Furnished__c || '',
|
|
view: selectedProperty.property.pcrm__View__c || '',
|
|
tower: selectedProperty.property.pcrm__Tower_Bayut_Dubizzle__c || '',
|
|
city: selectedProperty.property.pcrm__City_Bayut_Dubizzle__c || '',
|
|
subCommunity: selectedProperty.property.pcrm__Sub_Community_Propertyfinder__c || '',
|
|
buildYear: selectedProperty.property.pcrm__Build_Year__c || '',
|
|
stories: selectedProperty.property.pcrm__Stories__c || 0,
|
|
parkingSpaces: selectedProperty.property.pcrm__Parking_Spaces__c || 0,
|
|
lotSize: selectedProperty.property.pcrm__Lot_Size__c || 0,
|
|
serviceCharge: selectedProperty.property.pcrm__Service_Charge__c || 0,
|
|
privateAmenities: selectedProperty.property.pcrm__Private_Amenities__c || '',
|
|
commercialAmenities: selectedProperty.property.pcrm__Commercial_Amenities__c || '',
|
|
coordinates: selectedProperty.property.pcrm__Coordinates__c || '',
|
|
amenities: [],
|
|
images: []
|
|
};
|
|
|
|
const response = await fetch(`${API_BASE_URL}/api/generate-pdf`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
property_data: propertyData,
|
|
template_name: 'professional-3pager'
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
showSuccess(`PDF generated successfully! Mock URL: ${result.pdf_url}`);
|
|
} else {
|
|
throw new Error(result.message || 'Failed to generate PDF');
|
|
}
|
|
} catch (error) {
|
|
showError(`Error generating PDF: ${error.message}`);
|
|
} finally {
|
|
showLoading(false);
|
|
}
|
|
}
|
|
|
|
// Utility functions
|
|
function showLoading(show) {
|
|
document.getElementById('loading').style.display = show ? 'block' : 'none';
|
|
}
|
|
|
|
function showError(message) {
|
|
const errorDiv = document.getElementById('error');
|
|
errorDiv.textContent = message;
|
|
errorDiv.style.display = 'block';
|
|
}
|
|
|
|
function showSuccess(message) {
|
|
const successDiv = document.getElementById('success');
|
|
successDiv.textContent = message;
|
|
successDiv.style.display = 'block';
|
|
}
|
|
|
|
function hideMessages() {
|
|
document.getElementById('error').style.display = 'none';
|
|
document.getElementById('success').style.display = 'none';
|
|
}
|
|
|
|
function hidePropertyDetails() {
|
|
document.getElementById('property-details').style.display = 'none';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |