sksameermujahid's picture
Upload 23 files
6e3dbdb verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Property Verifier</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--primary: #4361ee;
--secondary: #3f37c9;
--success: #4cc9f0;
--danger: #f72585;
--warning: #f8961e;
--info: #4895ef;
--light: #f8f9fa;
--dark: #212529;
--gray: #6c757d;
--border-radius: 12px;
--box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.price-analysis-content {
padding: 15px;
}
.price-overview {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: var(--border-radius);
}
.price-main {
display: flex;
gap: 20px;
}
.price-value, .price-assessment {
display: flex;
flex-direction: column;
}
.label {
font-size: 0.9rem;
color: var(--gray);
}
.value {
font-size: 1.2rem;
font-weight: 600;
color: var(--dark);
}
.price-confidence {
display: flex;
align-items: center;
gap: 10px;
}
.confidence-bar {
width: 100px;
height: 8px;
background: #e9ecef;
border-radius: 4px;
overflow: hidden;
}
.confidence-fill {
height: 100%;
background: var(--primary);
transition: width 0.3s ease;
}
.confidence-value {
font-size: 0.9rem;
color: var(--gray);
}
.price-details {
display: grid;
gap: 20px;
}
.price-ranges, .market-trends, .price-factors, .risk-indicators {
background: white;
padding: 15px;
border-radius: var(--border-radius);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.range-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-top: 10px;
}
.range-item {
padding: 10px;
border-radius: var(--border-radius);
text-align: center;
}
.range-item.budget {
background: #e3f2fd;
}
.range-item.mid-range {
background: #f3e5f5;
}
.range-item.premium {
background: #fff3e0;
}
.range-label {
display: block;
font-size: 0.9rem;
color: var(--gray);
margin-bottom: 5px;
}
.range-value {
font-weight: 600;
color: var(--dark);
}
.trend-info {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-top: 10px;
}
.trend-item {
display: flex;
flex-direction: column;
}
.trend-label {
font-size: 0.9rem;
color: var(--gray);
}
.trend-value {
font-weight: 600;
color: var(--dark);
}
.factors-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-top: 10px;
}
.factor-item {
padding: 10px;
background: #f8f9fa;
border-radius: var(--border-radius);
}
.factor-label {
display: block;
font-size: 0.9rem;
color: var(--gray);
margin-bottom: 5px;
}
.factor-details {
display: flex;
flex-direction: column;
gap: 5px;
}
.factor-value {
font-weight: 600;
color: var(--dark);
}
.factor-impact {
font-size: 0.8rem;
color: var(--gray);
}
/* Enhanced Price Analysis Styles */
.price-comparison-summary {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
padding: 20px;
border-radius: var(--border-radius);
margin: 20px 0;
border-left: 4px solid var(--primary);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.price-comparison-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 15px;
}
.price-comparison-item {
text-align: center;
padding: 15px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.price-comparison-label {
font-size: 0.9rem;
color: var(--gray);
margin-bottom: 8px;
font-weight: 500;
}
.price-comparison-value {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 5px;
}
.price-comparison-value.listing {
color: var(--dark);
}
.price-comparison-value.market {
color: var(--primary);
}
.price-assessment-summary {
text-align: center;
padding: 12px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.price-assessment-label {
font-size: 0.9rem;
color: var(--gray);
margin-bottom: 5px;
font-weight: 500;
}
.price-assessment-value {
font-size: 1.2rem;
font-weight: 600;
}
.price-assessment-value.above-market {
color: #dc3545;
}
.price-assessment-value.below-market {
color: #28a745;
}
.price-assessment-value.market-rate {
color: #17a2b8;
}
/* Enhanced Risk Indicators */
.risk-indicators {
background: white;
padding: 15px;
border-radius: var(--border-radius);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.risk-list {
list-style: none;
padding: 0;
margin: 0;
}
.risk-item {
padding: 10px 15px;
margin-bottom: 8px;
border-radius: 6px;
border-left: 4px solid;
font-weight: 500;
}
.risk-item.warning {
background: #fff3e0;
color: #e65100;
border-left-color: #ff9800;
}
.risk-item.success {
background: #e8f5e9;
color: #2e7d32;
border-left-color: #4caf50;
}
/* Responsive Design for Price Analysis */
@media (max-width: 768px) {
.price-comparison-grid {
grid-template-columns: 1fr;
gap: 15px;
}
.price-overview {
flex-direction: column;
gap: 15px;
align-items: stretch;
}
.price-main {
justify-content: space-between;
}
.factors-grid {
grid-template-columns: 1fr;
}
.trend-info {
grid-template-columns: 1fr;
}
.range-grid {
grid-template-columns: 1fr;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
}
h1 {
font-size: 2.5rem;
color: var(--primary);
margin-bottom: 10px;
}
.subtitle {
font-size: 1.1rem;
color: var(--gray);
}
.card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
padding: 25px;
margin-bottom: 25px;
}
.card-header {
border-bottom: 1px solid #eee;
padding-bottom: 15px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 1.5rem;
color: var(--dark);
font-weight: 600;
}
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--dark);
}
.form-control {
width: 100%;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: var(--border-radius);
font-size: 1rem;
transition: border-color 0.3s;
}
.form-control:focus {
border-color: var(--primary);
outline: none;
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1);
}
textarea.form-control {
min-height: 100px;
resize: vertical;
}
.btn {
display: inline-block;
padding: 12px 24px;
background-color: var(--primary);
color: white;
border: none;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
}
.btn:hover {
background-color: var(--secondary);
transform: translateY(-2px);
}
.btn-block {
display: block;
width: 100%;
}
.section-title {
font-size: 1.2rem;
color: var(--primary);
margin-bottom: 15px;
font-weight: 600;
}
.results-container {
display: none;
margin-top: 30px;
}
.results-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(450px, 1fr));
gap: 25px;
}
.result-card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
padding: 20px;
height: 100%;
}
.result-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.result-icon {
width: 40px;
height: 40px;
background-color: var(--light);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
}
.result-title {
font-size: 1.2rem;
font-weight: 600;
color: var(--dark);
}
.trust-score {
text-align: center;
padding: 20px;
}
.score-value {
font-size: 3rem;
font-weight: 700;
color: var(--primary);
}
.score-label {
font-size: 1rem;
color: var(--gray);
}
.progress-container {
margin: 15px 0;
}
.progress-bar {
height: 10px;
background-color: #eee;
border-radius: 5px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: var(--primary);
border-radius: 5px;
transition: width 0.5s ease-in-out;
}
.alert {
padding: 15px;
border-radius: var(--border-radius);
margin-bottom: 20px;
font-weight: 500;
}
.alert-danger {
background-color: rgba(247, 37, 133, 0.1);
color: var(--danger);
border-left: 4px solid var(--danger);
}
.alert-warning {
background-color: rgba(248, 150, 30, 0.1);
color: var(--warning);
border-left: 4px solid var(--warning);
}
.alert-success {
background-color: rgba(76, 201, 240, 0.1);
color: var(--success);
border-left: 4px solid var(--success);
}
.suggestion-list {
list-style-type: none;
padding: 0;
}
.suggestion-item {
padding: 10px 15px;
background-color: rgba(67, 97, 238, 0.05);
border-radius: var(--border-radius);
margin-bottom: 10px;
border-left: 3px solid var(--primary);
}
.image-preview {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}
.preview-item {
width: 100px;
height: 100px;
border-radius: 8px;
overflow: hidden;
position: relative;
}
.preview-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.preview-remove {
position: absolute;
top: 5px;
right: 5px;
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.loading {
display: none;
text-align: center;
padding: 30px;
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(67, 97, 238, 0.1);
border-radius: 50%;
border-top-color: var(--primary);
animation: spin 1s ease-in-out infinite;
margin: 0 auto 20px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.chart-container {
position: relative;
height: 200px;
margin-bottom: 20px;
}
.pdf-preview {
background-color: #f8f9fa;
padding: 15px;
border-radius: var(--border-radius);
margin-top: 10px;
max-height: 200px;
overflow-y: auto;
}
.pdf-filename {
font-weight: 500;
margin-bottom: 5px;
}
.image-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
margin-top: 20px;
}
.gallery-item {
border-radius: var(--border-radius);
overflow: hidden;
box-shadow: var(--box-shadow);
aspect-ratio: 1;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.badge {
display: inline-block;
padding: 5px 10px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
margin-right: 5px;
margin-bottom: 5px;
}
.badge-primary { background-color: rgba(67, 97, 238, 0.1); color: var(--primary); }
.badge-success { background-color: rgba(76, 201, 240, 0.1); color: var(--success); }
.badge-warning { background-color: rgba(248, 150, 30, 0.1); color: var(--warning); }
.badge-danger { background-color: rgba(247, 37, 133, 0.1); color: var(--danger); }
.explanation-box {
background-color: #f8f9fa;
border-radius: var(--border-radius);
padding: 15px;
margin-top: 15px;
border-left: 4px solid var(--info);
}
.explanation-title {
font-weight: 600;
color: var(--info);
margin-bottom: 10px;
}
@media (max-width: 768px) {
.form-grid, .results-grid {
grid-template-columns: 1fr;
}
.card {
padding: 15px;
}
}
.property-summary {
padding: 15px;
}
.property-details p {
margin-bottom: 8px;
}
.final-verdict {
padding: 15px;
}
.verdict-box {
display: flex;
align-items: center;
padding: 15px;
border-radius: var(--border-radius);
margin-bottom: 15px;
background-color: #f8f9fa;
}
.verdict-icon {
font-size: 2rem;
margin-right: 15px;
}
.verdict-text {
font-size: 1.2rem;
font-weight: 600;
}
.verdict-legitimate {
background-color: rgba(76, 201, 240, 0.1);
border-left: 4px solid var(--success);
}
.verdict-suspicious {
background-color: rgba(248, 150, 30, 0.1);
border-left: 4px solid var(--warning);
}
.verdict-fraudulent {
background-color: rgba(247, 37, 133, 0.1);
border-left: 4px solid var(--danger);
}
.verification-scores {
padding: 15px;
}
.score-item {
margin-bottom: 15px;
}
.score-label {
font-weight: 500;
margin-bottom: 5px;
}
.score-bar-container {
display: flex;
align-items: center;
}
.score-bar {
height: 10px;
background-color: #e9ecef;
border-radius: 5px;
flex-grow: 1;
margin-right: 10px;
position: relative;
overflow: hidden;
}
.score-bar::before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: var(--primary);
border-radius: 5px;
width: 0%;
transition: width 0.5s ease;
}
.score-value {
font-weight: 600;
min-width: 40px;
text-align: right;
}
.red-flags {
padding: 15px;
}
.verdict-score {
text-align: center;
margin: 15px 0;
padding: 10px;
background-color: rgba(67, 97, 238, 0.05);
border-radius: var(--border-radius);
}
.verdict-reasoning {
margin: 15px 0;
padding: 15px;
background-color: rgba(67, 97, 238, 0.05);
border-radius: var(--border-radius);
border-left: 4px solid var(--primary);
}
.reasoning-title {
font-weight: 600;
color: var(--primary);
margin-bottom: 10px;
font-size: 1rem;
}
.reasoning-text {
color: var(--dark);
line-height: 1.5;
font-size: 0.9rem;
}
.verdict-details {
margin-top: 20px;
}
.verdict-section {
margin-bottom: 15px;
padding: 10px;
border-radius: var(--border-radius);
}
.verdict-section h4 {
font-size: 1rem;
margin-bottom: 10px;
color: var(--dark);
}
#criticalIssuesSection {
background-color: rgba(247, 37, 133, 0.05);
border-left: 4px solid var(--danger);
}
#warningsSection {
background-color: rgba(248, 150, 30, 0.05);
border-left: 4px solid var(--warning);
}
#recommendationsSection {
background-color: rgba(76, 201, 240, 0.05);
border-left: 4px solid var(--success);
}
/* Location Verification Styles */
.location-verification {
padding: 15px;
}
.verification-section {
margin-bottom: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: var(--border-radius);
}
.verification-section h5 {
color: var(--primary);
margin-bottom: 15px;
font-size: 1.1rem;
}
.verification-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding: 8px;
background-color: white;
border-radius: 8px;
}
.verification-label {
font-weight: 500;
color: var(--dark);
}
.verification-value {
padding: 4px 8px;
border-radius: 4px;
font-weight: 500;
}
.verification-value.valid {
background-color: rgba(76, 201, 240, 0.1);
color: var(--success);
}
.verification-value.invalid {
background-color: rgba(247, 37, 133, 0.1);
color: var(--danger);
}
.verification-summary {
margin-top: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: var(--border-radius);
}
.summary-item {
margin-bottom: 15px;
}
.summary-label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: var(--dark);
}
.summary-value {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-weight: 500;
}
.summary-value.valid {
background-color: rgba(76, 201, 240, 0.1);
color: var(--success);
}
.summary-value.invalid {
background-color: rgba(247, 37, 133, 0.1);
color: var(--danger);
}
.progress {
height: 10px;
background-color: #e9ecef;
border-radius: 5px;
overflow: hidden;
}
.progress-bar {
height: 100%;
transition: width 0.5s ease;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>AI Property Verifier & Fraud Detection</h1>
<p class="subtitle">Powered by advanced AI models to verify property listings and detect potential fraud</p>
</header>
<!-- Error message container -->
<div id="errorContainer" class="alert alert-danger" style="display:none;"></div>
<div class="card">
<div class="card-header">
<h2 class="card-title">Property Details</h2>
</div>
<form id="propertyForm">
<div class="section-title">Basic Information</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label" for="propertyName">Property Name</label>
<input type="text" class="form-control" id="propertyName" name="property_name" required>
</div>
<div class="form-group">
<label class="form-label" for="propertyType">Property Type</label>
<select class="form-control" id="propertyType" name="property_type" required>
<option value="">Select Type</option>
<option value="Apartment">Apartment</option>
<option value="House">House</option>
<option value="Condo">Condo</option>
<option value="Townhouse">Townhouse</option>
<option value="Villa">Villa</option>
<option value="Land">Land</option>
<option value="Commercial">Commercial</option>
<option value="Other">Other</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="status">Status</label>
<select class="form-control" id="status" name="status" required>
<option value="">Select Status</option>
<option value="For Sale">For Sale</option>
<option value="For Rent">For Rent</option>
<option value="Sold">Sold</option>
<option value="Under Contract">Under Contract</option>
<option value="Pending">Pending</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label" for="description">Property Description</label>
<textarea class="form-control" id="description" name="description" rows="4" required></textarea>
</div>
<div class="section-title">Location Details</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label" for="address">Address</label>
<input type="text" class="form-control" id="address" name="address" required>
</div>
<div class="form-group">
<label class="form-label" for="city">City</label>
<input type="text" class="form-control" id="city" name="city" required>
</div>
<div class="form-group">
<label class="form-label" for="state">State/Province</label>
<input type="text" class="form-control" id="state" name="state" required>
</div>
<div class="form-group">
<label class="form-label" for="country">Country</label>
<input type="text" class="form-control" id="country" name="country" required>
</div>
<div class="form-group">
<label class="form-label" for="zip">Zip/Postal Code</label>
<input type="text" class="form-control" id="zip" name="zip" required>
</div>
<div class="form-group">
<label class="form-label" for="latitude">Latitude</label>
<input type="text" class="form-control" id="latitude" name="latitude" placeholder="e.g. 40.7128">
</div>
<div class="form-group">
<label class="form-label" for="longitude">Longitude</label>
<input type="text" class="form-control" id="longitude" name="longitude" placeholder="e.g. -74.0060">
</div>
</div>
<div class="section-title">Property Specifications</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label" for="bedrooms">Bedrooms</label>
<input type="number" class="form-control" id="bedrooms" name="bedrooms" min="0">
</div>
<div class="form-group">
<label class="form-label" for="bathrooms">Bathrooms</label>
<input type="number" class="form-control" id="bathrooms" name="bathrooms" min="0" step="0.5">
</div>
<div class="form-group">
<label class="form-label" for="totalRooms">Total Rooms</label>
<input type="number" class="form-control" id="totalRooms" name="total_rooms" min="0">
</div>
<div class="form-group">
<label class="form-label" for="yearBuilt">Year Built</label>
<input type="number" class="form-control" id="yearBuilt" name="year_built" min="1800" max="2100">
</div>
<div class="form-group">
<label class="form-label" for="parking">Parking Spaces</label>
<input type="number" class="form-control" id="parking" name="parking" min="0">
</div>
<div class="form-group">
<label class="form-label" for="sqFt">Square Feet</label>
<input type="text" class="form-control" id="sqFt" name="sq_ft" min="0">
</div>
<div class="form-group">
<label class="form-label" for="marketValue">Market Value</label>
<input type="text" class="form-control" id="marketValue" name="market_value" min="0">
</div>
</div>
<div class="form-group">
<label class="form-label" for="amenities">Amenities (comma separated)</label>
<input type="text" class="form-control" id="amenities" name="amenities" placeholder="e.g. Pool, Gym, Garden, Garage">
</div>
<div class="form-group">
<label class="form-label" for="nearbyLandmarks">Nearby Landmarks</label>
<input type="text" class="form-control" id="nearbyLandmarks" name="nearby_landmarks" placeholder="e.g. School, Hospital, Park, Shopping Mall">
</div>
<div class="form-group">
<label class="form-label" for="legalDetails">Legal & Infrastructure Details</label>
<textarea class="form-control" id="legalDetails" name="legal_details" rows="3" placeholder="Include zoning, permits, utilities, etc."></textarea>
</div>
<div class="section-title">Documents & Images</div>
<div class="form-group">
<label class="form-label" for="images">Upload Images (JPG/PNG)</label>
<input type="file" class="form-control" id="images" name="images" accept="image/jpeg, image/png" multiple>
<div class="image-preview" id="imagePreview"></div>
</div>
<div class="form-group">
<label class="form-label" for="documents">Upload Documents (PDF)</label>
<input type="file" class="form-control" id="documents" name="documents" accept="application/pdf" multiple>
<div id="pdfPreview"></div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-block" id="submitBtn">Verify Property with AI</button>
</div>
</form>
</div>
<div class="loading" id="loadingIndicator">
<div class="spinner"></div>
<p>AI models are analyzing your property data...</p>
<p class="subtitle">This may take a moment as we're processing multiple AI models</p>
</div>
<div class="results-container" id="resultsContainer">
<div class="card">
<div class="card-header">
<h2 class="card-title">AI Verification Results</h2>
</div>
<div class="results-grid">
<div class="result-card">
<div class="result-header">
<div class="result-icon">🏠</div>
<div class="result-title">Property Summary</div>
</div>
<div class="property-summary">
<h3 id="propertyTitle">Property Details</h3>
<div class="property-details">
<p><strong>Name:</strong> <span id="summaryName"></span></p>
<p><strong>Type:</strong> <span id="summaryType"></span></p>
<p><strong>Status:</strong> <span id="summaryStatus"></span></p>
<p><strong>Location:</strong> <span id="summaryLocation"></span></p>
<p><strong>Price:</strong> <span id="summaryPrice"></span></p>
<p><strong>Size:</strong> <span id="summarySize"></span></p>
<p><strong>Bedrooms/Bathrooms:</strong> <span id="summaryRooms"></span></p>
</div>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">⚠️</div>
<div class="result-title">Final Verdict</div>
</div>
<div class="final-verdict" id="finalVerdict">
<div class="verdict-box" id="verdictBox">
<div class="verdict-icon" id="verdictIcon">⏳</div>
<div class="verdict-text" id="verdictText">Analysis in progress...</div>
</div>
<div class="verdict-score">
<div class="score-label">Overall Score</div>
<div class="score-value" id="verdictScoreValue">--</div>
<div class="progress-container">
<div class="progress-bar">
<div class="progress-fill" id="verdictScoreBar" style="width: 0%"></div>
</div>
</div>
</div>
<div class="verdict-reasoning">
<div class="reasoning-title">AI Reasoning</div>
<div class="reasoning-text" id="verdictReasoning">Analysis in progress...</div>
</div>
<div class="verdict-details">
<div class="verdict-section" id="criticalIssuesSection">
<h4>Critical Issues</h4>
<ul id="criticalIssuesList" class="suggestion-list">
<!-- Will be populated by JavaScript -->
</ul>
</div>
<div class="verdict-section" id="warningsSection">
<h4>Warnings</h4>
<ul id="warningsList" class="suggestion-list">
<!-- Will be populated by JavaScript -->
</ul>
</div>
<div class="verdict-section" id="recommendationsSection">
<h4>Recommendations</h4>
<ul id="recommendationsList" class="suggestion-list">
<!-- Will be populated by JavaScript -->
</ul>
</div>
</div>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ”</div>
<div class="result-title">Detailed Verification</div>
</div>
<div class="verification-scores">
<div class="score-item">
<div class="score-label">Trust Score</div>
<div class="score-bar-container">
<div class="score-bar" id="trustBar"></div>
<div class="score-value" id="trustValue">--</div>
</div>
</div>
<div class="score-item">
<div class="score-label">Image Authenticity</div>
<div class="score-bar-container">
<div class="score-bar" id="imageBar"></div>
<div class="score-value" id="imageValue">--</div>
</div>
</div>
<div class="score-item">
<div class="score-label">Document Verification</div>
<div class="score-bar-container">
<div class="score-bar" id="documentBar"></div>
<div class="score-value" id="documentValue">--</div>
</div>
</div>
<div class="score-item">
<div class="score-label">Content Quality</div>
<div class="score-bar-container">
<div class="score-bar" id="contentBar"></div>
<div class="score-value" id="contentValue">--</div>
</div>
</div>
<div class="score-item">
<div class="score-label">Location Accuracy</div>
<div class="score-bar-container">
<div class="score-bar" id="locationBar"></div>
<div class="score-value" id="locationValue">--</div>
</div>
</div>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">🚩</div>
<div class="result-title">Red Flags</div>
</div>
<div class="red-flags">
<ul id="redFlagsList" class="suggestion-list">
<!-- Will be populated by JavaScript -->
</ul>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ“Š</div>
<div class="result-title">Trust Score</div>
</div>
<div class="trust-score">
<div class="score-value" id="trustScoreValue">--</div>
<div class="score-label">Trust Score</div>
<div class="progress-container">
<div class="progress-bar">
<div class="progress-fill" id="trustScoreBar" style="width: 0%"></div>
</div>
</div>
</div>
<div class="chart-container">
<canvas id="trustScoreChart"></canvas>
</div>
<div class="explanation-box">
<div class="explanation-title">AI Reasoning</div>
<div id="trustReasoning"></div>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ”</div>
<div class="result-title">Fraud Analysis</div>
</div>
<div id="fraudAlertContainer"></div>
<div class="chart-container">
<canvas id="fraudAnalysisChart"></canvas>
</div>
<div class="explanation-box">
<div class="explanation-title">AI Reasoning</div>
<div id="fraudReasoning"></div>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ“</div>
<div class="result-title">AI Summary</div>
</div>
<div id="aiSummary"></div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ’‘</div>
<div class="result-title">Improvement Suggestions</div>
</div>
<div id="improvementSuggestions"></div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">🏠</div>
<div class="result-title">Property Quality Assessment</div>
</div>
<div id="qualityAssessment"></div>
<div class="chart-container">
<canvas id="qualityChart"></canvas>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ“</div>
<div class="result-title">Location Analysis</div>
</div>
<div id="locationAnalysis"></div>
<div class="chart-container">
<canvas id="locationChart"></canvas>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ’°</div>
<div class="result-title">Price Analysis</div>
</div>
<div id="priceAnalysis"></div>
<div class="chart-container">
<canvas id="priceChart"></canvas>
</div>
</div>
<div class="price-analysis-content">
<div class="price-overview">
<div class="price-main">
<div class="price-value">
<span class="label">Price per sq.ft.</span>
<span class="value" id="price-per-sqft">β‚Ή0</span>
</div>
<div class="price-assessment">
<span class="label">Assessment</span>
<span class="value" id="price-assessment">Unknown</span>
</div>
</div>
<div class="price-confidence">
<div class="confidence-bar">
<div class="confidence-fill" id="price-confidence-bar"></div>
</div>
<span class="confidence-value" id="price-confidence-value">0%</span>
</div>
</div>
<div class="price-details">
<div class="price-ranges">
<h4>Price Ranges</h4>
<div class="range-grid">
<div class="range-item budget">
<span class="range-label">Budget</span>
<span class="range-value" id="budget-range">β‚Ή0 - β‚Ή0</span>
</div>
<div class="range-item mid-range">
<span class="range-label">Mid-Range</span>
<span class="range-value" id="mid-range">β‚Ή0 - β‚Ή0</span>
</div>
<div class="range-item premium">
<span class="range-label">Premium</span>
<span class="range-value" id="premium-range">β‚Ή0 - β‚Ή0</span>
</div>
</div>
</div>
<div class="market-trends">
<h4>Market Trends</h4>
<div class="trend-info">
<div class="trend-item">
<span class="trend-label">City Tier</span>
<span class="trend-value" id="city-tier">Unknown</span>
</div>
<div class="trend-item">
<span class="trend-label">Price Trend</span>
<span class="trend-value" id="price-trend">Unknown</span>
</div>
<div class="trend-item">
<span class="trend-label">Market Average</span>
<span class="trend-value" id="market-average">β‚Ή0</span>
</div>
<div class="trend-item">
<span class="trend-label">Deviation</span>
<span class="trend-value" id="price-deviation">0%</span>
</div>
</div>
</div>
<div class="price-factors">
<h4>Price Factors</h4>
<div class="factors-grid">
<div class="factor-item">
<span class="factor-label">Property Age</span>
<div class="factor-details" id="age-factor">
<span class="factor-value">Unknown</span>
<span class="factor-impact">Impact: Unknown</span>
</div>
</div>
<div class="factor-item">
<span class="factor-label">Size Efficiency</span>
<div class="factor-details" id="size-factor">
<span class="factor-value">Unknown</span>
<span class="factor-impact">Impact: Unknown</span>
</div>
</div>
<div class="factor-item">
<span class="factor-label">Amenities</span>
<div class="factor-details" id="amenities-factor">
<span class="factor-value">Unknown</span>
<span class="factor-impact">Impact: Unknown</span>
</div>
</div>
</div>
</div>
<div class="risk-indicators">
<h4>Risk Indicators</h4>
<ul class="risk-list" id="risk-indicators">
<!-- Risk indicators will be populated dynamically -->
</ul>
</div>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">βš–οΈ</div>
<div class="result-title">Legal Analysis</div>
</div>
<div id="legalAnalysis"></div>
<div class="chart-container">
<canvas id="legalChart"></canvas>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ”„</div>
<div class="result-title">Cross-Validation Checks</div>
</div>
<div id="crossValidation"></div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ“„</div>
<div class="result-title">Document Analysis</div>
</div>
<div id="documentAnalysis"></div>
<div class="chart-container">
<canvas id="documentChart"></canvas>
</div>
</div>
<div class="result-card">
<div class="result-header">
<div class="result-icon">πŸ–ΌοΈ</div>
<div class="result-title">Image Analysis</div>
</div>
<div id="imageAnalysis"></div>
<div class="image-gallery" id="imageGallery"></div>
</div>
</div>
</div>
</div>
</div>
<script>
// Global variables to store form data
let uploadedImages = [];
let uploadedPDFs = [];
// Initialize charts
let trustScoreChart;
let fraudAnalysisChart;
let qualityChart;
let locationChart;
let priceChart;
let legalChart;
let documentChart;
document.addEventListener('DOMContentLoaded', function() {
// Request location access when page loads
requestLocationAccess();
const propertyForm = document.getElementById('propertyForm');
const loadingIndicator = document.getElementById('loadingIndicator');
const resultsContainer = document.getElementById('resultsContainer');
const imageInput = document.getElementById('images');
const imagePreview = document.getElementById('imagePreview');
const pdfInput = document.getElementById('documents');
const pdfPreview = document.getElementById('pdfPreview');
// Handle image uploads
imageInput.addEventListener('change', function(e) {
handleImageUpload(e.target.files);
});
// Handle PDF uploads
pdfInput.addEventListener('change', function(e) {
handlePDFUpload(e.target.files);
});
// Form submission
propertyForm.addEventListener('submit', function(e) {
e.preventDefault();
submitForm();
});
// Initialize charts
initCharts();
});
function requestLocationAccess() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
// Update form fields with coordinates
document.getElementById('latitude').value = latitude;
document.getElementById('longitude').value = longitude;
// Send to backend to get address details
fetch('/get-location', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
latitude: latitude,
longitude: longitude
}),
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
// Fill form fields with location data
document.getElementById('address').value = data.address || '';
document.getElementById('city').value = data.city || '';
document.getElementById('state').value = data.state || '';
document.getElementById('country').value = data.country || '';
document.getElementById('zip').value = data.postal_code || '';
console.log('Location data loaded successfully');
} else {
console.error('Error getting location details:', data.message);
}
})
.catch(error => {
console.error('Error getting location details:', error);
});
},
function(error) {
console.error('Error getting location:', error.message);
// Show a message to the user about location access
const locationMessage = document.createElement('div');
locationMessage.className = 'alert alert-warning';
locationMessage.innerHTML = 'Location access denied or unavailable. Please enter your location manually.';
document.querySelector('.container').prepend(locationMessage);
// Auto-remove the message after 5 seconds
setTimeout(() => {
locationMessage.remove();
}, 5000);
},
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
} else {
console.error('Geolocation is not supported by this browser');
}
}
function handleImageUpload(files) {
const imagePreview = document.getElementById('imagePreview');
imagePreview.innerHTML = ''; // Clear existing previews
uploadedImages = []; // Reset uploaded images array
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.type.match('image.*')) continue;
const reader = new FileReader();
reader.onload = function(e) {
const imageData = e.target.result;
uploadedImages.push({
name: file.name,
data: imageData,
file: file
});
// Create preview
const previewItem = document.createElement('div');
previewItem.className = 'preview-item';
previewItem.innerHTML = `
<img src="${imageData}" alt="${file.name}">
<button type="button" class="preview-remove" data-index="${uploadedImages.length - 1}">Γ—</button>
`;
imagePreview.appendChild(previewItem);
// Add remove functionality
previewItem.querySelector('.preview-remove').addEventListener('click', function() {
const index = parseInt(this.getAttribute('data-index'));
uploadedImages.splice(index, 1);
imagePreview.removeChild(previewItem);
updateImagePreviews();
});
};
reader.readAsDataURL(file);
}
}
function updateImagePreviews() {
const imagePreview = document.getElementById('imagePreview');
imagePreview.innerHTML = '';
uploadedImages.forEach((image, index) => {
const previewItem = document.createElement('div');
previewItem.className = 'preview-item';
previewItem.innerHTML = `
<img src="${image.data}" alt="${image.name}">
<button type="button" class="preview-remove" data-index="${index}">Γ—</button>
`;
imagePreview.appendChild(previewItem);
previewItem.querySelector('.preview-remove').addEventListener('click', function() {
uploadedImages.splice(index, 1);
updateImagePreviews();
});
});
}
function handlePDFUpload(files) {
const pdfPreview = document.getElementById('pdfPreview');
pdfPreview.innerHTML = ''; // Clear existing previews
uploadedPDFs = []; // Reset uploaded PDFs array
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (file.type !== 'application/pdf') continue;
uploadedPDFs.push({
name: file.name,
file: file
});
// Create preview
const previewItem = document.createElement('div');
previewItem.className = 'pdf-preview';
previewItem.innerHTML = `
<div class="pdf-filename">${file.name}</div>
<button type="button" class="btn" data-index="${uploadedPDFs.length - 1}">Remove</button>
`;
pdfPreview.appendChild(previewItem);
// Add remove functionality
previewItem.querySelector('.btn').addEventListener('click', function() {
const index = parseInt(this.getAttribute('data-index'));
uploadedPDFs.splice(index, 1);
pdfPreview.removeChild(previewItem);
updatePDFPreviews();
});
}
}
function updatePDFPreviews() {
const pdfPreview = document.getElementById('pdfPreview');
pdfPreview.innerHTML = '';
uploadedPDFs.forEach((pdf, index) => {
const previewItem = document.createElement('div');
previewItem.className = 'pdf-preview';
previewItem.innerHTML = `
<div class="pdf-filename">${pdf.name}</div>
<button type="button" class="btn" data-index="${index}">Remove</button>
`;
pdfPreview.appendChild(previewItem);
previewItem.querySelector('.btn').addEventListener('click', function() {
uploadedPDFs.splice(index, 1);
updatePDFPreviews();
});
});
}
function initCharts() {
try {
// Store all charts in an array for easier management
window.charts = [];
// Trust Score Chart initialization
const trustCtx = document.getElementById('trustScoreChart').getContext('2d');
trustScoreChart = new Chart(trustCtx, {
type: 'doughnut',
data: {
datasets: [{
data: [0, 100],
backgroundColor: [
'#4361ee',
'#f1f1f1'
],
borderWidth: 0
}]
},
options: {
cutout: '70%',
circumference: 180,
rotation: -90,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false
}
},
maintainAspectRatio: false
}
});
charts.push(trustScoreChart);
// Fraud Analysis Chart (Bar)
const fraudAnalysisCtx = document.getElementById('fraudAnalysisChart').getContext('2d');
fraudAnalysisChart = new Chart(fraudAnalysisCtx, {
type: 'bar',
data: {
labels: ['Legitimate', 'Suspicious', 'Fraudulent'],
datasets: [{
label: 'Fraud Indicators',
data: [0, 0, 0],
backgroundColor: [
'#4cc9f0',
'#f8961e',
'#f72585'
],
borderWidth: 0
}]
},
options: {
indexAxis: 'y',
plugins: {
legend: {
display: false
}
},
scales: {
x: {
beginAtZero: true,
max: 100
}
},
maintainAspectRatio: false
}
});
// Quality Assessment Chart
const qualityCtx = document.getElementById('qualityChart').getContext('2d');
qualityChart = new Chart(qualityCtx, {
type: 'radar',
data: {
labels: ['Completeness', 'Accuracy', 'Clarity', 'Authenticity', 'Detail'],
datasets: [{
label: 'Quality Score',
data: [0, 0, 0, 0, 0],
backgroundColor: 'rgba(67, 97, 238, 0.2)',
borderColor: '#4361ee',
borderWidth: 2,
pointBackgroundColor: '#4361ee'
}]
},
options: {
scales: {
r: {
beginAtZero: true,
max: 100
}
},
maintainAspectRatio: false
}
});
// Location Analysis Chart
const locationCtx = document.getElementById('locationChart').getContext('2d');
locationChart = new Chart(locationCtx, {
type: 'pie',
data: {
labels: ['Complete', 'Partial', 'Missing'],
datasets: [{
data: [0, 0, 0],
backgroundColor: [
'#4cc9f0',
'#f8961e',
'#f72585'
],
borderWidth: 0
}]
},
options: {
plugins: {
legend: {
position: 'bottom'
}
},
maintainAspectRatio: false
}
});
// Price Analysis Chart
const priceCtx = document.getElementById('priceChart').getContext('2d');
priceChart = new Chart(priceCtx, {
type: 'bar',
data: {
labels: ['Market Value', 'Price per Sq.Ft.'],
datasets: [{
label: 'Price Analysis',
data: [0, 0],
backgroundColor: [
'#4361ee',
'#4895ef'
],
borderWidth: 0
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
},
maintainAspectRatio: false
}
});
// Legal Analysis Chart
const legalCtx = document.getElementById('legalChart').getContext('2d');
legalChart = new Chart(legalCtx, {
type: 'doughnut',
data: {
labels: ['Complete', 'Partial', 'Missing'],
datasets: [{
data: [0, 0, 0],
backgroundColor: [
'#4cc9f0',
'#f8961e',
'#f72585'
],
borderWidth: 0
}]
},
options: {
plugins: {
legend: {
position: 'bottom'
}
},
maintainAspectRatio: false
}
});
// Document Analysis Chart
const documentCtx = document.getElementById('documentChart').getContext('2d');
documentChart = new Chart(documentCtx, {
type: 'polarArea',
data: {
labels: ['Authentic', 'Suspicious', 'Incomplete'],
datasets: [{
data: [0, 0, 0],
backgroundColor: [
'#4cc9f0',
'#f8961e',
'#f72585'
],
borderWidth: 0
}]
},
options: {
plugins: {
legend: {
position: 'bottom'
}
},
maintainAspectRatio: false
}
});
// Add window resize handler for chart responsiveness
window.addEventListener('resize', debounce(() => {
charts.forEach(chart => {
if (chart && typeof chart.resize === 'function') {
chart.resize();
}
});
}, 250));
} catch (error) {
console.error('Error initializing charts:', error);
document.getElementById('chartErrors').innerHTML =
'<div class="alert alert-danger">Error initializing charts. Please refresh the page.</div>';
}
}
// Utility function for debouncing
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Data validation function
function validateAnalysisData(data) {
return {
trustScore: {
score: data.trust_score?.score ?? 0,
reasoning: data.trust_score?.reasoning ?? 'No reasoning provided'
},
fraudClassification: {
alertLevel: data.fraud_classification?.alert_level ?? 'low',
classification: data.fraud_classification?.classification ?? 'Unknown',
confidence: data.fraud_classification?.confidence ?? 0,
indicators: data.fraud_classification?.fraud_indicators ?? [],
scores: data.fraud_classification?.indicator_scores ?? []
},
qualityAssessment: {
assessment: data.quality_assessment?.assessment ?? 'Unknown',
score: data.quality_assessment?.score ?? 0,
isAiGenerated: data.quality_assessment?.is_ai_generated ?? false,
reasoning: data.quality_assessment?.reasoning ?? 'No reasoning provided'
},
// ... other validations
};
}
// Safe chart update function
function updateChart(chart, newData, options = {}) {
try {
if (chart && typeof chart.update === 'function') {
chart.data = newData;
chart.update(options);
return true;
}
return false;
} catch (error) {
console.error('Error updating chart:', error);
return false;
}
}
function showError(message) {
const errorContainer = document.getElementById('errorContainer');
errorContainer.textContent = message;
errorContainer.style.display = 'block';
setTimeout(() => { errorContainer.style.display = 'none'; }, 8000);
}
// Patch submitForm to show errors from backend
function submitForm() {
const propertyForm = document.getElementById('propertyForm');
const loadingIndicator = document.getElementById('loadingIndicator');
const resultsContainer = document.getElementById('resultsContainer');
console.log("πŸš€ Starting form submission...");
loadingIndicator.style.display = 'block';
resultsContainer.style.display = 'none';
const formData = new FormData(propertyForm);
// Debug: Log form data
console.log("πŸ“‹ Form data being sent:");
for (let [key, value] of formData.entries()) {
console.log(` ${key}: ${value}`);
}
console.log("🌐 Making request to /verify endpoint...");
// Add images and PDFs from preview arrays if needed
fetch('/verify', {
method: 'POST',
body: formData
})
.then(response => {
console.log("πŸ“‘ Response received:", response.status, response.statusText);
return response.json();
})
.then(data => {
console.log("βœ… Data received:", data);
loadingIndicator.style.display = 'none';
if (data.status === 'error' || data.error) {
console.error("❌ Server error:", data.error);
showError(data.error || 'An error occurred. Please check your input and try again.');
return;
}
displayResults(data); // <-- Ensure this is called to populate all results
resultsContainer.style.display = 'block';
})
.catch(error => {
console.error("❌ Fetch error:", error);
loadingIndicator.style.display = 'none';
showError('Server error: ' + (error.message || error));
});
}
function displayResults(data) {
console.log("Received data:", JSON.stringify(data));
// Update Price Analysis section
if (data.price_analysis) {
updatePriceAnalysis(data.price_analysis);
}
// Validate and sanitize data
const validatedData = validateAnalysisData(data);
try {
// Display Trust Score with validated data
const trustScore = validatedData.trustScore.score;
document.getElementById('trustScoreValue').textContent = trustScore;
document.getElementById('trustScoreBar').style.width = `${trustScore}%`;
document.getElementById('trustReasoning').textContent = validatedData.trustScore.reasoning;
// Update Trust Score Chart safely
updateChart(trustScoreChart, {
datasets: [{
data: [trustScore, 100 - trustScore]
}]
});
// Display Fraud Analysis
const fraudLevel = validatedData.fraudClassification.alertLevel;
const fraudContainer = document.getElementById('fraudAlertContainer');
fraudContainer.innerHTML = '';
const alertClass = fraudLevel === 'high' ? 'alert-danger' :
fraudLevel === 'medium' ? 'alert-warning' : 'alert-success';
const alertDiv = document.createElement('div');
alertDiv.className = `alert ${alertClass}`;
// Create detailed fraud analysis display
let fraudContent = `<h4>Fraud Classification</h4>`;
fraudContent += `<p>Alert Level: ${fraudLevel.toUpperCase()}</p>`;
if (data.fraud_classification.high_risk && data.fraud_classification.high_risk.length > 0) {
fraudContent += `<div class="risk-section">
<h5>High Risk Indicators:</h5>
<ul>`;
data.fraud_classification.high_risk.forEach(([indicator, score]) => {
fraudContent += `<li>${indicator} (${Math.round(score * 100)}% confidence)</li>`;
});
fraudContent += `</ul></div>`;
}
if (data.fraud_classification.medium_risk && data.fraud_classification.medium_risk.length > 0) {
fraudContent += `<div class="risk-section">
<h5>Medium Risk Indicators:</h5>
<ul>`;
data.fraud_classification.medium_risk.forEach(([indicator, score]) => {
fraudContent += `<li>${indicator} (${Math.round(score * 100)}% confidence)</li>`;
});
fraudContent += `</ul></div>`;
}
if (data.fraud_classification.low_risk && data.fraud_classification.low_risk.length > 0) {
fraudContent += `<div class="risk-section">
<h5>Low Risk Indicators:</h5>
<ul>`;
data.fraud_classification.low_risk.forEach(([indicator, score]) => {
fraudContent += `<li>${indicator} (${Math.round(score * 100)}% confidence)</li>`;
});
fraudContent += `</ul></div>`;
}
alertDiv.innerHTML = fraudContent;
fraudContainer.appendChild(alertDiv);
// Update Fraud Analysis Chart
const fraudChartData = {
labels: ['High Risk', 'Medium Risk', 'Low Risk'],
datasets: [{
data: [
data.fraud_classification.high_risk ? data.fraud_classification.high_risk.reduce((sum, [_, score]) => sum + score, 0) * 100 : 0,
data.fraud_classification.medium_risk ? data.fraud_classification.medium_risk.reduce((sum, [_, score]) => sum + score, 0) * 100 : 0,
data.fraud_classification.low_risk ? data.fraud_classification.low_risk.reduce((sum, [_, score]) => sum + score, 0) * 100 : 0
],
backgroundColor: ['#dc3545', '#ffc107', '#28a745']
}]
};
updateChart(fraudAnalysisChart, fraudChartData);
document.getElementById('fraudReasoning').textContent = `This property was classified as ${validatedData.fraudClassification.classification} based on AI analysis of the listing details.`;
// Display AI Summary
document.getElementById('aiSummary').textContent = data.summary || "No summary available";
// Display Improvement Suggestions
const improvementContainer = document.getElementById('improvementSuggestions');
improvementContainer.innerHTML = '';
// Create sections for different types of improvements
const sections = {
'Critical Issues': [],
'Important Improvements': [],
'Optional Enhancements': []
};
// Safely check if improvement_suggestions exists and is an array
if (data && data.improvement_suggestions && Array.isArray(data.improvement_suggestions)) {
// Categorize improvements based on severity
data.improvement_suggestions.forEach(suggestion => {
if (suggestion && typeof suggestion === 'string') {
if (suggestion.toLowerCase().includes('critical') ||
suggestion.toLowerCase().includes('required') ||
suggestion.toLowerCase().includes('must')) {
sections['Critical Issues'].push(suggestion);
} else if (suggestion.toLowerCase().includes('should') ||
suggestion.toLowerCase().includes('recommended')) {
sections['Important Improvements'].push(suggestion);
} else {
sections['Optional Enhancements'].push(suggestion);
}
}
});
}
// Display each section
Object.entries(sections).forEach(([title, suggestions]) => {
if (suggestions.length > 0) {
const sectionDiv = document.createElement('div');
sectionDiv.className = 'improvement-section mb-4';
const sectionTitle = document.createElement('h5');
sectionTitle.className = title === 'Critical Issues' ? 'text-danger' :
title === 'Important Improvements' ? 'text-warning' : 'text-info';
sectionTitle.textContent = title;
const suggestionList = document.createElement('ul');
suggestionList.className = 'list-unstyled';
suggestions.forEach(suggestion => {
const li = document.createElement('li');
li.className = 'mb-2';
li.innerHTML = `<i class="fas fa-arrow-right me-2"></i>${suggestion}`;
suggestionList.appendChild(li);
});
sectionDiv.appendChild(sectionTitle);
sectionDiv.appendChild(suggestionList);
improvementContainer.appendChild(sectionDiv);
}
});
// If no suggestions are available, display a message
if (!Object.values(sections).some(suggestions => suggestions.length > 0)) {
improvementContainer.innerHTML = '<p class="text-muted">No improvement suggestions available at this time.</p>';
}
// Display Quality Assessment
const qualityDiv = document.getElementById('qualityAssessment');
qualityDiv.innerHTML = '';
if (data.quality_assessment) {
const qualityScore = data.quality_assessment.score || 0;
const assessment = data.quality_assessment.assessment || 'Not assessed';
const reasoning = data.quality_assessment.reasoning || 'No reasoning provided';
const isAiGenerated = data.quality_assessment.is_ai_generated || false;
qualityDiv.innerHTML = `
<div class="quality-score mb-4">
<h5>Content Quality Score</h5>
<div class="score-display">
<span class="score-value">${qualityScore}%</span>
<div class="progress">
<div class="progress-bar ${getScoreColorClass(qualityScore)}"
role="progressbar"
style="width: ${qualityScore}%"
aria-valuenow="${qualityScore}"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
</div>
<div class="quality-details">
<div class="detail-item">
<strong>Assessment:</strong> ${assessment}
</div>
<div class="detail-item">
<strong>AI Generated:</strong> ${isAiGenerated ? 'Yes' : 'No'}
</div>
<div class="detail-item">
<strong>Analysis:</strong>
<p class="reasoning-text">${reasoning}</p>
</div>
</div>
`;
// Update Quality Chart
const qualityChartData = {
labels: ['Completeness', 'Accuracy', 'Clarity', 'Authenticity', 'Detail'],
datasets: [{
label: 'Quality Metrics',
data: [
data.quality_assessment.completeness_score || 0,
data.quality_assessment.accuracy_score || 0,
data.quality_assessment.clarity_score || 0,
data.quality_assessment.authenticity_score || 0,
data.quality_assessment.detail_score || 0
],
backgroundColor: 'rgba(67, 97, 238, 0.2)',
borderColor: '#4361ee',
borderWidth: 2,
pointBackgroundColor: '#4361ee'
}]
};
updateChart(qualityChart, qualityChartData);
} else {
qualityDiv.innerHTML = '<p class="text-muted">No quality assessment available at this time.</p>';
}
// Add helper function for score color classes
function getScoreColorClass(score) {
if (score >= 80) return 'bg-success';
if (score >= 60) return 'bg-info';
if (score >= 40) return 'bg-warning';
return 'bg-danger';
}
// Display Location Analysis
const locationDiv = document.getElementById('locationAnalysis');
if (data.location_analysis) {
locationDiv.innerHTML = `
<div class="location-verification">
<h4>Location Verification Results</h4>
<div class="verification-section">
<h5>Address Verification</h5>
<div class="verification-item">
<span class="verification-label">Address Format:</span>
<span class="verification-value ${data.location_analysis.address_format_valid ? 'valid' : 'invalid'}">
${data.location_analysis.address_format_valid ? 'βœ“ Valid' : 'βœ— Invalid'}
</span>
</div>
<div class="verification-item">
<span class="verification-label">Address in City:</span>
<span class="verification-value ${data.location_analysis.address_in_city ? 'valid' : 'invalid'}">
${data.location_analysis.address_in_city ? 'βœ“ Verified' : 'βœ— Not Found'}
</span>
</div>
</div>
<div class="verification-section">
<h5>City & State Verification</h5>
<div class="verification-item">
<span class="verification-label">City in State:</span>
<span class="verification-value ${data.location_analysis.city_in_state ? 'valid' : 'invalid'}">
${data.location_analysis.city_in_state ? 'βœ“ Verified' : 'βœ— Not Found'}
</span>
</div>
<div class="verification-item">
<span class="verification-label">State in Country:</span>
<span class="verification-value ${data.location_analysis.state_in_country ? 'valid' : 'invalid'}">
${data.location_analysis.state_in_country ? 'βœ“ Verified' : 'βœ— Not Found'}
</span>
</div>
</div>
<div class="verification-section">
<h5>Postal Code Verification</h5>
<div class="verification-item">
<span class="verification-label">Postal Code Format:</span>
<span class="verification-value ${data.location_analysis.postal_code_valid ? 'valid' : 'invalid'}">
${data.location_analysis.postal_code_valid ? 'βœ“ Valid' : 'βœ— Invalid'}
</span>
</div>
<div class="verification-item">
<span class="verification-label">Postal Code in City:</span>
<span class="verification-value ${data.location_analysis.postal_code_in_city ? 'valid' : 'invalid'}">
${data.location_analysis.postal_code_in_city ? 'βœ“ Verified' : 'βœ— Not Found'}
</span>
</div>
</div>
<div class="verification-section">
<h5>Coordinates Verification</h5>
<div class="verification-item">
<span class="verification-label">Coordinates Format:</span>
<span class="verification-value ${data.location_analysis.coordinates_valid ? 'valid' : 'invalid'}">
${data.location_analysis.coordinates_valid ? 'βœ“ Valid' : 'βœ— Invalid'}
</span>
</div>
<div class="verification-item">
<span class="verification-label">Coordinates in City:</span>
<span class="verification-value ${data.location_analysis.coordinates_in_city ? 'valid' : 'invalid'}">
${data.location_analysis.coordinates_in_city ? 'βœ“ Verified' : 'βœ— Not Found'}
</span>
</div>
</div>
<div class="verification-section">
<h5>Location Quality</h5>
<div class="verification-item">
<span class="verification-label">Overall Quality:</span>
<span class="verification-value ${data.location_analysis.location_quality === 'verified' ? 'valid' : 'invalid'}">
${data.location_analysis.location_quality === 'verified' ? 'βœ“ High Quality' : 'βœ— Low Quality'}
</span>
</div>
<div class="verification-item">
<span class="verification-label">City Tier:</span>
<span class="verification-value">
${data.location_analysis.city_tier.toUpperCase()}
</span>
</div>
</div>
<div class="verification-section">
<h5>Nearby Landmarks</h5>
<div class="verification-item">
<span class="verification-label">Landmarks Provided:</span>
<span class="verification-value ${data.location_analysis.landmarks_analysis.provided ? 'valid' : 'invalid'}">
${data.location_analysis.landmarks_analysis.provided ? 'βœ“ Yes' : 'βœ— No'}
</span>
</div>
${data.location_analysis.landmarks_analysis.types.length > 0 ? `
<div class="verification-item">
<span class="verification-label">Landmark Types:</span>
<span class="verification-value">
${data.location_analysis.landmarks_analysis.types.join(', ')}
</span>
</div>
` : ''}
</div>
<div class="verification-summary">
<h5>Verification Summary</h5>
<div class="summary-item">
<span class="summary-label">Overall Score:</span>
<div class="progress">
<div class="progress-bar ${getScoreColorClass(data.location_analysis.completeness_score)}"
role="progressbar"
style="width: ${data.location_analysis.completeness_score}%"
aria-valuenow="${data.location_analysis.completeness_score}"
aria-valuemin="0"
aria-valuemax="100">
${data.location_analysis.completeness_score}%
</div>
</div>
</div>
<div class="summary-item">
<span class="summary-label">Verification Status:</span>
<span class="summary-value ${data.location_analysis.verification_status === 'verified' ? 'valid' : 'invalid'}">
${data.location_analysis.verification_status === 'verified' ? 'βœ“ Verified' : 'βœ— Unverified'}
</span>
</div>
</div>
</div>
`;
// Update Location Chart
updateChart(locationChart, {
datasets: [{
data: [
data.location_analysis.completeness_score || 0,
100 - (data.location_analysis.completeness_score || 0),
data.location_analysis.coordinates_check === 'coordinates_missing' ? 30 : 0
]
}]
});
} else {
locationDiv.innerHTML = '<p>No location analysis available</p>';
}
// Display Price Analysis
const priceDiv = document.getElementById('priceAnalysis');
if (data.price_analysis && data.price_analysis.has_price) {
priceDiv.innerHTML = `
<p><strong>Assessment:</strong> ${data.price_analysis.assessment || "Unknown"}</p>
<p><strong>Price:</strong> β‚Ή${(data.price_analysis.price || 0).toLocaleString()}</p>
${data.price_analysis.has_sqft ? `<p><strong>Price per Sq.Ft.:</strong> β‚Ή${(data.price_analysis.price_per_sqft || 0).toLocaleString(undefined, {maximumFractionDigits: 2})}</p>` : ''}
<p><strong>Confidence:</strong> ${Math.round((data.price_analysis.confidence || 0) * 100)}%</p>
`;
// Update Price Chart
updateChart(priceChart, {
labels: ['Market Value (thousands)', 'Price per Sq.Ft.'],
datasets: [{
data: [
(data.price_analysis.price || 0) / 1000, // Scale down for better visualization
data.price_analysis.price_per_sqft || 0
]
}]
});
} else {
priceDiv.innerHTML = `<p>No price information provided for analysis.</p>`;
}
// Display Legal Analysis
const legalDiv = document.getElementById('legalAnalysis');
if (data.legal_analysis) {
legalDiv.innerHTML = `
<p><strong>Assessment:</strong> ${data.legal_analysis.assessment || "Unknown"}</p>
<p><strong>Completeness:</strong> ${data.legal_analysis.completeness_score || 0}%</p>
<p><strong>Summary:</strong> ${data.legal_analysis.summary || "No summary available"}</p>
${data.legal_analysis.terms_found && data.legal_analysis.terms_found.length > 0 ? `<p><strong>Legal Terms Found:</strong> ${data.legal_analysis.terms_found.join(', ')}</p>` : ''}
${data.legal_analysis.potential_issues ? '<p class="alert alert-warning">Potential legal issues detected</p>' : ''}
`;
// Update Legal Chart
updateChart(legalChart, {
datasets: [{
data: [
data.legal_analysis.completeness_score || 0,
100 - (data.legal_analysis.completeness_score || 0),
data.legal_analysis.potential_issues ? 30 : 0
]
}]
});
} else {
legalDiv.innerHTML = '<p>No legal analysis available</p>';
}
// Display Cross-Validation Checks
const crossValidationDiv = document.getElementById('crossValidation');
crossValidationDiv.innerHTML = '<ul class="suggestion-list">';
try {
// Safely check if cross_validation exists and is an array
if (data && data.cross_validation && Array.isArray(data.cross_validation)) {
// Only proceed if the array has items
if (data.cross_validation.length > 0) {
data.cross_validation.forEach(check => {
if (check && typeof check === 'object') {
const status = check.status || 'unknown';
const checkName = check.check || 'Check';
const message = check.message || 'No details available';
// Determine status class
let statusClass = 'badge-warning'; // Default
if (['consistent', 'valid', 'reasonable', 'match', 'likely_valid'].includes(status)) {
statusClass = 'badge-success';
} else if (['suspicious', 'inconsistent', 'invalid', 'no_match'].includes(status)) {
statusClass = 'badge-danger';
}
crossValidationDiv.innerHTML += `
<li class="suggestion-item">
<span class="badge ${statusClass}">${status}</span>
<strong>${checkName}:</strong> ${message}
</li>
`;
}
});
} else {
crossValidationDiv.innerHTML += '<li class="suggestion-item">No cross-validation checks performed</li>';
}
} else {
crossValidationDiv.innerHTML += '<li class="suggestion-item">No cross-validation data available</li>';
}
} catch (error) {
console.error("Error displaying cross-validation:", error);
crossValidationDiv.innerHTML += '<li class="suggestion-item">Error displaying cross-validation results</li>';
}
crossValidationDiv.innerHTML += '</ul>';
// Display Document Analysis
const documentDiv = document.getElementById('documentAnalysis');
documentDiv.innerHTML = '';
if (data.document_analysis && data.document_analysis.pdf_count > 0) {
// Calculate summary statistics
let totalVerificationScore = 0;
let authenticCount = 0;
let suspiciousCount = 0;
let incompleteCount = 0;
let totalConfidence = 0;
let totalDocumentConfidence = 0;
let totalAuthenticityConfidence = 0;
let documentsWithSignatures = 0;
let documentsWithDates = 0;
let propertyRelatedCount = 0;
data.document_analysis.pdf_analysis.forEach((pdf, index) => {
if (pdf) {
totalVerificationScore += pdf.verification_score || 0;
totalConfidence += pdf.confidence || 0;
totalDocumentConfidence += pdf.document_confidence || 0;
totalAuthenticityConfidence += pdf.authenticity_confidence || 0;
if (pdf.contains_signatures) documentsWithSignatures++;
if (pdf.contains_dates) documentsWithDates++;
if (pdf.is_property_related) propertyRelatedCount++;
if (pdf.authenticity_assessment && pdf.authenticity_assessment.toLowerCase().includes('authentic')) {
authenticCount++;
} else if (pdf.authenticity_assessment && pdf.authenticity_assessment.toLowerCase().includes('suspicious')) {
suspiciousCount++;
} else {
incompleteCount++;
}
}
});
const avgVerificationScore = data.document_analysis.pdf_count > 0 ? (totalVerificationScore / data.document_analysis.pdf_count).toFixed(1) : 0;
const avgConfidence = data.document_analysis.pdf_count > 0 ? (totalConfidence / data.document_analysis.pdf_count * 100).toFixed(1) : 0;
const avgDocumentConfidence = data.document_analysis.pdf_count > 0 ? (totalDocumentConfidence / data.document_analysis.pdf_count * 100).toFixed(1) : 0;
const avgAuthenticityConfidence = data.document_analysis.pdf_count > 0 ? (totalAuthenticityConfidence / data.document_analysis.pdf_count * 100).toFixed(1) : 0;
// Create summary section
documentDiv.innerHTML = `
<div class="document-summary">
<h4>Document Analysis Summary</h4>
<div class="summary-grid">
<div class="summary-item">
<h5>Total Documents</h5>
<p class="summary-value">${data.document_analysis.pdf_count}</p>
</div>
<div class="summary-item">
<h5>Property Related</h5>
<p class="summary-value">${propertyRelatedCount} of ${data.document_analysis.pdf_count}</p>
</div>
<div class="summary-item">
<h5>Avg Verification Score</h5>
<p class="summary-value">${avgVerificationScore}%</p>
</div>
<div class="summary-item">
<h5>Avg Confidence</h5>
<p class="summary-value">${avgConfidence}%</p>
</div>
<div class="summary-item">
<h5>With Signatures</h5>
<p class="summary-value">${documentsWithSignatures}</p>
</div>
<div class="summary-item">
<h5>With Dates</h5>
<p class="summary-value">${documentsWithDates}</p>
</div>
</div>
</div>
`;
// Create detailed analysis for each document
const documentsContainer = document.createElement('div');
documentsContainer.className = 'documents-container';
documentsContainer.innerHTML = '<h4>Detailed Document Analysis</h4>';
data.document_analysis.pdf_analysis.forEach((pdf, index) => {
if (pdf) {
const documentCard = document.createElement('div');
documentCard.className = 'document-card';
// Create key information display
let keyInfoHtml = '';
if (pdf.key_info && Object.keys(pdf.key_info).length > 0) {
keyInfoHtml = '<div class="key-info-section"><h6>Key Information:</h6><ul>';
Object.entries(pdf.key_info).forEach(([key, value]) => {
if (Array.isArray(value)) {
keyInfoHtml += `<li><strong>${key}:</strong> ${value.slice(0, 3).join(', ')}${value.length > 3 ? '...' : ''}</li>`;
} else {
keyInfoHtml += `<li><strong>${key}:</strong> ${value}</li>`;
}
});
keyInfoHtml += '</ul></div>';
}
// Create real estate indicators display
let indicatorsHtml = '';
if (pdf.real_estate_indicators && pdf.real_estate_indicators.length > 0) {
indicatorsHtml = '<div class="indicators-section"><h6>Real Estate Indicators:</h6><ul>';
pdf.real_estate_indicators.slice(0, 5).forEach(indicator => {
indicatorsHtml += `<li>${indicator}</li>`;
});
if (pdf.real_estate_indicators.length > 5) {
indicatorsHtml += `<li>... and ${pdf.real_estate_indicators.length - 5} more</li>`;
}
indicatorsHtml += '</ul></div>';
}
// Create legal terms display
let legalTermsHtml = '';
if (pdf.legal_terms_found && pdf.legal_terms_found.length > 0) {
legalTermsHtml = '<div class="legal-terms-section"><h6>Legal Terms Found:</h6><ul>';
pdf.legal_terms_found.slice(0, 5).forEach(term => {
legalTermsHtml += `<li>${term}</li>`;
});
if (pdf.legal_terms_found.length > 5) {
legalTermsHtml += `<li>... and ${pdf.legal_terms_found.length - 5} more</li>`;
}
legalTermsHtml += '</ul></div>';
}
// Create keyword analysis display
let keywordAnalysisHtml = '';
if (pdf.keyword_analysis && Object.keys(pdf.keyword_analysis).length > 0) {
keywordAnalysisHtml = '<div class="keyword-analysis-section"><h6>Keyword Analysis:</h6><ul>';
Object.entries(pdf.keyword_analysis).forEach(([category, count]) => {
keywordAnalysisHtml += `<li><strong>${category}:</strong> ${count} matches</li>`;
});
keywordAnalysisHtml += '</ul></div>';
}
documentCard.innerHTML = `
<div class="document-header">
<h5>Document ${index + 1}</h5>
<span class="status-badge ${pdf.is_property_related ? 'success' : 'warning'}">
${pdf.is_property_related ? 'Property Related' : 'Non-Property'}
</span>
</div>
<div class="document-metrics">
<div class="metric">
<span class="metric-label">Document Type:</span>
<span class="metric-value">${typeof pdf.document_type === 'object' ? (pdf.document_type.classification || 'Unknown') : (pdf.document_type || 'Unknown')}</span>
</div>
<div class="metric">
<span class="metric-label">Type Confidence:</span>
<span class="metric-value">${Math.round((typeof pdf.document_type === 'object' ? (pdf.document_type.confidence || 0) : (pdf.document_confidence || 0)) * 100)}%</span>
</div>
<div class="metric">
<span class="metric-label">Authenticity:</span>
<span class="metric-value">${typeof pdf.authenticity === 'object' ? (pdf.authenticity.assessment || 'Unknown') : (pdf.authenticity_assessment || 'Unknown')}</span>
</div>
<div class="metric">
<span class="metric-label">Auth Confidence:</span>
<span class="metric-value">${Math.round((typeof pdf.authenticity === 'object' ? (pdf.authenticity.confidence || 0) : (pdf.authenticity_confidence || 0)) * 100)}%</span>
</div>
<div class="metric">
<span class="metric-label">Verification Score:</span>
<span class="metric-value">${Math.round(pdf.verification_score || 0)}%</span>
</div>
<div class="metric">
<span class="metric-label">Overall Confidence:</span>
<span class="metric-value">${Math.round((pdf.confidence || 0) * 100)}%</span>
</div>
</div>
<div class="document-details">
<div class="detail-section">
<h6>Summary:</h6>
<p>${pdf.summary || 'No summary available'}</p>
</div>
<div class="detail-section">
<h6>Document Features:</h6>
<ul>
<li><strong>Contains Signatures:</strong> ${pdf.contains_signatures ? 'Yes' : 'No'}</li>
<li><strong>Contains Dates:</strong> ${pdf.contains_dates ? 'Yes' : 'No'}</li>
</ul>
</div>
${keyInfoHtml}
${indicatorsHtml}
${legalTermsHtml}
${keywordAnalysisHtml}
</div>
<div class="model-info">
<small>Model: ${pdf.model_used || 'Static Analysis'}</small>
</div>
`;
documentsContainer.appendChild(documentCard);
}
});
documentDiv.appendChild(documentsContainer);
// Update Document Chart
updateChart(documentChart, {
labels: ['Authentic', 'Suspicious', 'Incomplete'],
datasets: [{
data: [authenticCount, suspiciousCount, incompleteCount],
backgroundColor: ['#28a745', '#ffc107', '#dc3545']
}]
});
// Add CSS for document analysis
const style = document.createElement('style');
style.textContent = `
.document-summary {
margin-bottom: 30px;
}
.document-summary h4 {
color: var(--primary);
border-bottom: 2px solid var(--primary);
padding-bottom: 5px;
margin-bottom: 20px;
}
.documents-container {
margin-top: 30px;
}
.documents-container h4 {
color: var(--primary);
margin-bottom: 20px;
}
.document-card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
padding: 20px;
margin-bottom: 20px;
}
.document-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.document-header h5 {
margin: 0;
color: var(--dark);
}
.document-metrics {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.document-details {
margin-bottom: 15px;
}
.detail-section {
margin-bottom: 15px;
}
.detail-section h6 {
color: var(--dark);
margin-bottom: 8px;
}
.detail-section p {
color: var(--gray);
margin: 0;
line-height: 1.5;
}
.detail-section ul {
list-style: none;
padding: 0;
margin: 0;
}
.detail-section li {
padding: 4px 0;
color: var(--gray);
}
.key-info-section, .indicators-section, .legal-terms-section, .keyword-analysis-section {
margin-bottom: 15px;
}
.key-info-section h6, .indicators-section h6, .legal-terms-section h6, .keyword-analysis-section h6 {
color: var(--dark);
margin-bottom: 8px;
}
.key-info-section ul, .indicators-section ul, .legal-terms-section ul, .keyword-analysis-section ul {
list-style: none;
padding: 0;
margin: 0;
}
.key-info-section li, .indicators-section li, .legal-terms-section li, .keyword-analysis-section li {
padding: 4px 0;
font-size: 0.85rem;
color: var(--gray);
}
`;
document.head.appendChild(style);
} else {
documentDiv.innerHTML = '<p>No documents were uploaded for analysis.</p>';
}
// Display Image Analysis
const imageAnalysisDiv = document.getElementById('imageAnalysis');
const imageGallery = document.getElementById('imageGallery');
imageAnalysisDiv.innerHTML = '';
imageGallery.innerHTML = '';
if (data.image_analysis && data.images && data.images.length > 0) {
// Create containers for real estate and non-real estate images
const realEstateContainer = document.createElement('div');
realEstateContainer.className = 'image-section';
realEstateContainer.innerHTML = '<h4>Real Estate Images</h4>';
const nonRealEstateContainer = document.createElement('div');
nonRealEstateContainer.className = 'image-section';
nonRealEstateContainer.innerHTML = '<h4>Non-Real Estate Images</h4>';
let propertyRelatedCount = 0;
let totalConfidence = 0;
let totalRealEstateConfidence = 0;
let totalAuthenticityScore = 0;
let aiGeneratedCount = 0;
data.image_analysis.image_analysis.forEach((img, index) => {
if (img && img.is_property_related) {
propertyRelatedCount++;
}
if (img) {
totalConfidence += img.confidence || 0;
totalRealEstateConfidence += img.real_estate_confidence || 0;
totalAuthenticityScore += img.authenticity_score || 0;
if (img.is_ai_generated) {
aiGeneratedCount++;
}
}
});
const avgConfidence = data.image_analysis.image_count > 0 ? (totalConfidence / data.image_analysis.image_count * 100).toFixed(1) : 0;
const avgRealEstateConfidence = data.image_analysis.image_count > 0 ? (totalRealEstateConfidence / data.image_analysis.image_count * 100).toFixed(1) : 0;
const avgAuthenticityScore = data.image_analysis.image_count > 0 ? (totalAuthenticityScore / data.image_analysis.image_count * 100).toFixed(1) : 0;
imageAnalysisDiv.innerHTML = `
<div class="analysis-summary">
<div class="summary-grid">
<div class="summary-item">
<h5>Total Images Analyzed</h5>
<p class="summary-value">${data.image_analysis.image_count}</p>
</div>
<div class="summary-item">
<h5>Property-Related Images</h5>
<p class="summary-value">${propertyRelatedCount} of ${data.image_analysis.image_count}</p>
</div>
<div class="summary-item">
<h5>Average Confidence</h5>
<p class="summary-value">${avgConfidence}%</p>
</div>
<div class="summary-item">
<h5>Real Estate Confidence</h5>
<p class="summary-value">${avgRealEstateConfidence}%</p>
</div>
<div class="summary-item">
<h5>Authenticity Score</h5>
<p class="summary-value">${avgAuthenticityScore}%</p>
</div>
<div class="summary-item">
<h5>AI Generated</h5>
<p class="summary-value">${aiGeneratedCount} images</p>
</div>
</div>
<div class="model-info">
<p><strong>Model Used:</strong> ${data.image_analysis.image_model_used ? data.image_analysis.image_model_used.join(', ') : 'Static Analysis'}</p>
</div>
</div>
`;
// Display images with detailed analysis
data.images.forEach((imgData, index) => {
const imgAnalysis = data.image_analysis.image_analysis[index];
const galleryItem = document.createElement('div');
galleryItem.className = 'gallery-item';
// Create detailed analysis card
const analysisCard = document.createElement('div');
analysisCard.className = 'analysis-card';
// Create image container
const imageContainer = document.createElement('div');
imageContainer.className = 'image-container';
imageContainer.innerHTML = `
<img src="data:image/jpeg;base64,${imgData}" alt="Property Image ${index + 1}">
<div class="image-overlay">
<div class="image-label">${imgAnalysis && imgAnalysis.predicted_label ? imgAnalysis.predicted_label : 'Unknown'}</div>
<div class="confidence-badge">${imgAnalysis ? Math.round((imgAnalysis.confidence || 0) * 100) : 0}%</div>
</div>
`;
// Create analysis details
const analysisDetails = document.createElement('div');
analysisDetails.className = 'analysis-details';
if (imgAnalysis) {
const isPropertyRelated = imgAnalysis.is_property_related ? 'Yes' : 'No';
const isAiGenerated = imgAnalysis.is_ai_generated ? 'Yes' : 'No';
const authenticityScore = Math.round((imgAnalysis.authenticity_score || 0) * 100);
const realEstateConfidence = Math.round((imgAnalysis.real_estate_confidence || 0) * 100);
// Create top predictions list
let topPredictionsHtml = '';
if (imgAnalysis.top_predictions && imgAnalysis.top_predictions.length > 0) {
topPredictionsHtml = '<div class="top-predictions"><h6>Top Predictions:</h6><ul>';
imgAnalysis.top_predictions.slice(0, 3).forEach(pred => {
const confidence = Math.round((pred.confidence || 0) * 100);
topPredictionsHtml += `<li>${pred.label} (${confidence}%)</li>`;
});
topPredictionsHtml += '</ul></div>';
}
// Create image quality info
let qualityInfo = '';
if (imgAnalysis.image_quality) {
qualityInfo = `
<div class="quality-info">
<h6>Image Quality:</h6>
<p>Resolution: ${imgAnalysis.image_quality.resolution || 'Unknown'}</p>
<p>Quality Score: ${Math.round((imgAnalysis.image_quality.quality_score || 0) * 100)}%</p>
</div>
`;
}
analysisDetails.innerHTML = `
<div class="analysis-header">
<h5>Image Analysis #${index + 1}</h5>
<span class="status-badge ${imgAnalysis.is_property_related ? 'success' : 'warning'}">
${imgAnalysis.is_property_related ? 'Real Estate' : 'Non-Real Estate'}
</span>
</div>
<div class="analysis-metrics">
<div class="metric">
<span class="metric-label">Confidence:</span>
<span class="metric-value">${Math.round((imgAnalysis.confidence || 0) * 100)}%</span>
</div>
<div class="metric">
<span class="metric-label">Real Estate Confidence:</span>
<span class="metric-value">${realEstateConfidence}%</span>
</div>
<div class="metric">
<span class="metric-label">Authenticity:</span>
<span class="metric-value">${authenticityScore}%</span>
</div>
<div class="metric">
<span class="metric-label">AI Generated:</span>
<span class="metric-value">${isAiGenerated}</span>
</div>
</div>
${topPredictionsHtml}
${qualityInfo}
<div class="model-info">
<small>Model: ${imgAnalysis.model_used || 'Static Analysis'}</small>
</div>
`;
} else {
analysisDetails.innerHTML = '<p>Analysis not available</p>';
}
analysisCard.appendChild(imageContainer);
analysisCard.appendChild(analysisDetails);
// Add to appropriate container based on classification
if (imgAnalysis && imgAnalysis.is_property_related) {
realEstateContainer.appendChild(analysisCard);
} else {
nonRealEstateContainer.appendChild(analysisCard);
}
});
// Add both containers to the gallery
imageGallery.appendChild(realEstateContainer);
imageGallery.appendChild(nonRealEstateContainer);
// Add enhanced CSS for the new image display
const style = document.createElement('style');
style.textContent = `
.image-section {
margin-bottom: 30px;
}
.image-section h4 {
margin-bottom: 15px;
color: var(--primary);
border-bottom: 2px solid var(--primary);
padding-bottom: 5px;
}
.summary-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.summary-item {
background: #f8f9fa;
padding: 15px;
border-radius: var(--border-radius);
text-align: center;
}
.summary-item h5 {
font-size: 0.9rem;
color: var(--gray);
margin-bottom: 5px;
}
.summary-value {
font-size: 1.2rem;
font-weight: 600;
color: var(--primary);
margin: 0;
}
.analysis-card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
overflow: hidden;
margin-bottom: 20px;
}
.image-container {
position: relative;
width: 100%;
height: 200px;
overflow: hidden;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover;
}
.image-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.8);
padding: 10px;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
}
.image-label {
font-size: 0.9rem;
font-weight: 500;
}
.confidence-badge {
background: var(--primary);
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 500;
}
.analysis-details {
padding: 15px;
}
.analysis-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.analysis-header h5 {
margin: 0;
color: var(--dark);
}
.status-badge {
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
}
.status-badge.success {
background: #d4edda;
color: #155724;
}
.status-badge.warning {
background: #fff3cd;
color: #856404;
}
.analysis-metrics {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 15px;
}
.metric {
display: flex;
justify-content: space-between;
padding: 8px;
background: #f8f9fa;
border-radius: 6px;
}
.metric-label {
font-size: 0.9rem;
color: var(--gray);
}
.metric-value {
font-weight: 600;
color: var(--dark);
}
.top-predictions {
margin-bottom: 15px;
}
.top-predictions h6 {
font-size: 0.9rem;
color: var(--dark);
margin-bottom: 8px;
}
.top-predictions ul {
list-style: none;
padding: 0;
margin: 0;
}
.top-predictions li {
padding: 4px 0;
font-size: 0.85rem;
color: var(--gray);
}
.quality-info {
margin-bottom: 15px;
}
.quality-info h6 {
font-size: 0.9rem;
color: var(--dark);
margin-bottom: 8px;
}
.quality-info p {
font-size: 0.85rem;
color: var(--gray);
margin: 2px 0;
}
.model-info {
text-align: right;
}
.model-info small {
color: var(--gray);
font-style: italic;
}
`;
document.head.appendChild(style);
} else {
imageAnalysisDiv.innerHTML = '<p>No images were uploaded for analysis.</p>';
}
// Update Property Summary
document.getElementById('summaryName').textContent = document.getElementById('propertyName').value || 'Not provided';
document.getElementById('summaryType').textContent = document.getElementById('propertyType').value || 'Not provided';
document.getElementById('summaryStatus').textContent = document.getElementById('status').value || 'Not provided';
document.getElementById('summaryLocation').textContent =
`${document.getElementById('address').value || ''}, ${document.getElementById('city').value || ''}, ${document.getElementById('state').value || ''}, India`;
document.getElementById('summaryPrice').textContent = document.getElementById('marketValue').value ? `β‚Ή${document.getElementById('marketValue').value}` : 'Not provided';
document.getElementById('summarySize').textContent = document.getElementById('sqFt').value ? `${document.getElementById('sqFt').value} sq. ft.` : 'Not provided';
document.getElementById('summaryRooms').textContent =
`${document.getElementById('bedrooms').value || '0'} BHK`; // BHK is common in Indian real estate
// Update Final Verdict
const verdictBox = document.getElementById('verdictBox');
const verdictIcon = document.getElementById('verdictIcon');
const verdictText = document.getElementById('verdictText');
const verdictScoreValue = document.getElementById('verdictScoreValue');
const verdictScoreBar = document.getElementById('verdictScoreBar');
// Get final verdict data - fix the property names to match backend
const finalVerdict = data.final_verdict || {};
const verdictStatus = finalVerdict.verdict || 'unknown';
const verdictScore = finalVerdict.overall_score || 0; // Changed from 'final_score' to 'overall_score'
const verdictConfidence = finalVerdict.confidence || 'low';
// Update verdict display with proper status mapping
if (verdictStatus.includes('HIGH RISK') || verdictStatus.includes('FRAUDULENT')) {
verdictBox.className = 'verdict-box verdict-fraudulent';
verdictIcon.textContent = '❌';
verdictText.textContent = verdictStatus;
} else if (verdictStatus.includes('SUSPICIOUS') || verdictStatus.includes('VERIFICATION REQUIRED')) {
verdictBox.className = 'verdict-box verdict-suspicious';
verdictIcon.textContent = '⚠️';
verdictText.textContent = verdictStatus;
} else if (verdictStatus.includes('VERIFIED') || verdictStatus.includes('LEGITIMATE')) {
verdictBox.className = 'verdict-box verdict-legitimate';
verdictIcon.textContent = 'βœ…';
verdictText.textContent = verdictStatus;
} else {
verdictBox.className = 'verdict-box';
verdictIcon.textContent = '❓';
verdictText.textContent = verdictStatus || 'VERDICT UNKNOWN';
}
// Update verdict score
verdictScoreValue.textContent = `${Math.round(verdictScore)}%`;
verdictScoreBar.style.width = `${verdictScore}%`;
verdictScoreBar.className = `progress-fill ${getScoreColorClass(verdictScore)}`;
// Display reasoning from final verdict
const verdictReasoning = document.getElementById('verdictReasoning');
if (verdictReasoning && finalVerdict.reasoning) {
verdictReasoning.textContent = finalVerdict.reasoning;
}
// Display critical issues from cross validation
const criticalIssuesList = document.getElementById('criticalIssuesList');
criticalIssuesList.innerHTML = '';
const criticalIssues = [];
// Get high severity issues from cross validation
if (data.cross_validation) {
data.cross_validation.forEach(check => {
if (check.severity === 'high' || check.status === 'invalid' || check.status === 'suspicious') {
criticalIssues.push(`${check.check}: ${check.message}`);
}
});
}
if (criticalIssues.length > 0) {
criticalIssues.forEach(issue => {
const li = document.createElement('li');
li.className = 'suggestion-item';
li.innerHTML = `<i class="fas fa-exclamation-circle"></i> ${issue}`;
criticalIssuesList.appendChild(li);
});
document.getElementById('criticalIssuesSection').style.display = 'block';
} else {
document.getElementById('criticalIssuesSection').style.display = 'none';
}
// Display warnings from medium severity issues
const warningsList = document.getElementById('warningsList');
warningsList.innerHTML = '';
const warnings = [];
// Get medium severity issues from cross validation
if (data.cross_validation) {
data.cross_validation.forEach(check => {
if (check.severity === 'medium' || check.status === 'inconsistent') {
warnings.push(`${check.check}: ${check.message}`);
}
});
}
if (warnings.length > 0) {
warnings.forEach(warning => {
const li = document.createElement('li');
li.className = 'suggestion-item';
li.innerHTML = `<i class="fas fa-exclamation-triangle"></i> ${warning}`;
warningsList.appendChild(li);
});
document.getElementById('warningsSection').style.display = 'block';
} else {
document.getElementById('warningsSection').style.display = 'none';
}
// Display recommendations from suggestions
const recommendationsList = document.getElementById('recommendationsList');
recommendationsList.innerHTML = '';
let hasRecommendations = false;
// Try different suggestion structures
if (data.suggestions && data.suggestions.suggestions && Array.isArray(data.suggestions.suggestions)) {
data.suggestions.suggestions.forEach(suggestionObj => {
if (suggestionObj && suggestionObj.suggestion) {
const li = document.createElement('li');
li.className = 'suggestion-item';
li.innerHTML = `<i class="fas fa-check-circle"></i> ${suggestionObj.suggestion}`;
recommendationsList.appendChild(li);
hasRecommendations = true;
}
});
} else if (data.suggestions && data.suggestions.improvements && Array.isArray(data.suggestions.improvements)) {
data.suggestions.improvements.forEach(recommendation => {
if (recommendation && typeof recommendation === 'string') {
const li = document.createElement('li');
li.className = 'suggestion-item';
li.innerHTML = `<i class="fas fa-check-circle"></i> ${recommendation}`;
recommendationsList.appendChild(li);
hasRecommendations = true;
}
});
}
// If no suggestions from data, generate basic recommendations based on issues
if (!hasRecommendations) {
const basicRecommendations = [
"Provide a valid property name (not just numbers)",
"Add more detailed property description",
"Include property documents for verification",
"Add high-quality property images",
"Verify property specifications (bedrooms, bathrooms, size)",
"Review and adjust property pricing to market rates",
"Include legal documentation and compliance details"
];
basicRecommendations.forEach(recommendation => {
const li = document.createElement('li');
li.className = 'suggestion-item';
li.innerHTML = `<i class="fas fa-check-circle"></i> ${recommendation}`;
recommendationsList.appendChild(li);
hasRecommendations = true;
});
}
if (hasRecommendations) {
document.getElementById('recommendationsSection').style.display = 'block';
} else {
document.getElementById('recommendationsSection').style.display = 'none';
}
// Update Verification Scores
updateScoreBar('trustBar', 'trustValue', trustScore);
// Image authenticity score
let imageScore = 0;
if (data.image_analysis && data.image_analysis.image_analysis) {
const propertyImages = data.image_analysis.image_analysis.filter(img => img && img.is_property_related);
imageScore = data.image_analysis.image_count > 0 ?
Math.round((propertyImages.length / data.image_analysis.image_count) * 100) : 0;
}
updateScoreBar('imageBar', 'imageValue', imageScore);
// Document verification score
let docScore = 0;
if (data.document_analysis && data.document_analysis.pdf_analysis) {
const verificationScores = data.document_analysis.pdf_analysis.map(
pdf => pdf.verification_score || 0
);
docScore = verificationScores.length > 0 ?
Math.round(verificationScores.reduce((a, b) => a + b, 0) / verificationScores.length) : 0;
}
updateScoreBar('documentBar', 'documentValue', docScore);
// Content quality score
const contentScore = validatedData.qualityAssessment ? validatedData.qualityAssessment.score : 0;
updateScoreBar('contentBar', 'contentValue', contentScore);
// Location accuracy score
const locationScore = data.location_analysis ? data.location_analysis.completeness_score || 0 : 0;
updateScoreBar('locationBar', 'locationValue', locationScore);
// Update Red Flags
const redFlagsList = document.getElementById('redFlagsList');
redFlagsList.innerHTML = '';
const redFlags = [];
// Check for inconsistencies and issues
if (data.cross_validation) {
data.cross_validation.forEach(check => {
if (check.status === 'inconsistent' || check.status === 'invalid' ||
check.status === 'suspicious' || check.status === 'no_match') {
redFlags.push(`${check.check}: ${check.message}`);
}
});
}
if (validatedData.qualityAssessment && validatedData.qualityAssessment.isAiGenerated) {
redFlags.push('Description appears to be AI-generated, which may indicate a fake listing');
}
if (data.price_analysis &&
(data.price_analysis.assessment === 'suspicious pricing' ||
data.price_analysis.assessment === 'overpriced' ||
data.price_analysis.assessment === 'underpriced')) {
redFlags.push(`Price is ${data.price_analysis.assessment} for this type of property`);
}
if (data.legal_analysis && data.legal_analysis.potential_issues) {
redFlags.push('Potential legal issues detected in the property documentation');
}
if (data.image_analysis && data.image_analysis.image_count > 0) {
const propertyImages = data.image_analysis.image_analysis.filter(img => img && img.is_property_related);
if (propertyImages.length === 0) {
redFlags.push('None of the uploaded images appear to be related to real estate');
}
}
// If no red flags, add a positive message
if (redFlags.length === 0) {
redFlags.push('No significant red flags detected in this listing');
}
redFlags.forEach(flag => {
const li = document.createElement('li');
li.className = 'suggestion-item';
li.textContent = flag;
redFlagsList.appendChild(li);
});
} catch (error) {
console.error('Error displaying results:', error);
document.getElementById('resultsContainer').innerHTML =
'<div class="alert alert-danger">Error displaying results. Please try again.</div>';
}
}
function updateScoreBar(barId, valueId, score) {
const bar = document.getElementById(barId);
const value = document.getElementById(valueId);
if (bar && value) {
bar.style.setProperty('--score-width', `${score}%`);
bar.style.background = `linear-gradient(to right,
${getScoreColor(score)} ${score}%,
#e9ecef ${score}%)`;
value.textContent = `${score}%`;
}
}
function getScoreColor(score) {
if (score >= 70) return 'var(--success)';
if (score >= 40) return 'var(--warning)';
return 'var(--danger)';
}
// Price Analysis Chart initialization
const priceCtx = document.getElementById('priceChart').getContext('2d');
priceChart = new Chart(priceCtx, {
type: 'bar',
data: {
labels: ['Market Value', 'Price per Sq.Ft.'],
datasets: [{
label: 'Price Analysis',
data: [0, 0],
backgroundColor: [
'#4361ee',
'#4895ef'
],
borderWidth: 0
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
},
maintainAspectRatio: false
}
});
// Update Price Analysis Section
function updatePriceAnalysis(data) {
try {
console.log('Price Analysis Data:', data);
// Main price info - Show both listing price and market average
const listingPricePerSqft = data.formatted_price_per_sqft || 'β‚Ή0';
const marketAveragePerSqft = data.market_average ? `β‚Ή${data.market_average.toLocaleString(undefined, {maximumFractionDigits: 2})}` : 'β‚Ή0';
const deviation = data.deviation_percentage || 0;
// Update price per sq.ft. display to show both values
document.getElementById('price-per-sqft').innerHTML = `
<div style="margin-bottom: 8px;">
<span style="font-size: 0.9rem; color: #6c757d;">Listing Price:</span><br>
<span style="font-size: 1.3rem; font-weight: 600; color: #212529;">${listingPricePerSqft}</span>
</div>
<div>
<span style="font-size: 0.9rem; color: #6c757d;">Market Average:</span><br>
<span style="font-size: 1.3rem; font-weight: 600; color: #4361ee;">${marketAveragePerSqft}</span>
</div>
`;
// Update assessment with clear comparison
let assessmentText = data.assessment || 'Unknown';
let assessmentColor = '#6c757d';
if (deviation > 20) {
assessmentText = `${Math.abs(deviation).toFixed(0)}% above market average`;
assessmentColor = '#dc3545';
} else if (deviation < -20) {
assessmentText = `${Math.abs(deviation).toFixed(0)}% below market average`;
assessmentColor = '#28a745';
} else if (Math.abs(deviation) <= 20) {
assessmentText = 'Market rate';
assessmentColor = '#17a2b8';
}
document.getElementById('price-assessment').textContent = assessmentText;
document.getElementById('price-assessment').style.color = assessmentColor;
// Confidence
const confidence = Math.round((data.confidence || 0) * 100);
const confidenceBar = document.getElementById('price-confidence-bar');
const confidenceValue = document.getElementById('price-confidence-value');
confidenceValue.textContent = `${confidence}%`;
confidenceBar.style.width = `${confidence}%`;
confidenceBar.style.backgroundColor = getScoreColor(confidence);
// Price ranges
if (data.price_ranges) {
const ranges = data.price_ranges;
if (ranges.budget && ranges.budget.min !== undefined && ranges.budget.max !== undefined) {
document.getElementById('budget-range').textContent = `β‚Ή${ranges.budget.min.toLocaleString()} - β‚Ή${ranges.budget.max.toLocaleString()}`;
} else {
document.getElementById('budget-range').textContent = 'β‚Ή0 - β‚Ή0';
}
if (ranges.mid_range && ranges.mid_range.min !== undefined && ranges.mid_range.max !== undefined) {
document.getElementById('mid-range').textContent = `β‚Ή${ranges.mid_range.min.toLocaleString()} - β‚Ή${ranges.mid_range.max.toLocaleString()}`;
} else {
document.getElementById('mid-range').textContent = 'β‚Ή0 - β‚Ή0';
}
if (ranges.premium && ranges.premium.min !== undefined && ranges.premium.max !== undefined) {
document.getElementById('premium-range').textContent = `β‚Ή${ranges.premium.min.toLocaleString()} - β‚Ή${ranges.premium.max.toLocaleString()}`;
} else {
document.getElementById('premium-range').textContent = 'β‚Ή0 - β‚Ή0';
}
} else {
document.getElementById('budget-range').textContent = 'β‚Ή0 - β‚Ή0';
document.getElementById('mid-range').textContent = 'β‚Ή0 - β‚Ή0';
document.getElementById('premium-range').textContent = 'β‚Ή0 - β‚Ή0';
}
// Market trends
if (data.market_trends) {
document.getElementById('city-tier').textContent = data.city_tier || 'Unknown';
document.getElementById('price-trend').textContent = data.market_trends.trend || 'Unknown';
document.getElementById('market-average').textContent = marketAveragePerSqft;
document.getElementById('price-deviation').textContent = `${deviation.toFixed(1)}%`;
// Color code the deviation
const deviationElement = document.getElementById('price-deviation');
if (deviation > 20) {
deviationElement.style.color = '#dc3545';
} else if (deviation < -20) {
deviationElement.style.color = '#28a745';
} else {
deviationElement.style.color = '#17a2b8';
}
} else {
document.getElementById('city-tier').textContent = 'Unknown';
document.getElementById('price-trend').textContent = 'Unknown';
document.getElementById('market-average').textContent = 'β‚Ή0';
document.getElementById('price-deviation').textContent = '0%';
}
// Price factors
if (data.price_factors && data.price_factors.property_age) {
const age = data.price_factors.property_age;
document.getElementById('age-factor').innerHTML =
`<span class="factor-value">${age.impact ? age.impact.toUpperCase() : 'UNKNOWN'}</span><span class="factor-impact">${age.description || 'Impact: Unknown'}</span>`;
} else {
document.getElementById('age-factor').innerHTML = '<span class="factor-value">Unknown</span><span class="factor-impact">Impact: Unknown</span>';
}
if (data.price_factors && data.price_factors.size_efficiency) {
const size = data.price_factors.size_efficiency;
document.getElementById('size-factor').innerHTML =
`<span class="factor-value">${size.impact ? size.impact.toUpperCase() : 'UNKNOWN'}</span><span class="factor-impact">${size.description || 'Impact: Unknown'}</span>`;
} else {
document.getElementById('size-factor').innerHTML = '<span class="factor-value">Unknown</span><span class="factor-impact">Impact: Unknown</span>';
}
if (data.price_factors && data.price_factors.amenities) {
const amenities = data.price_factors.amenities;
document.getElementById('amenities-factor').innerHTML =
`<span class="factor-value">${amenities.impact ? amenities.impact.toUpperCase() : 'UNKNOWN'}</span><span class="factor-impact">${amenities.description || 'Impact: Unknown'}</span>`;
} else {
document.getElementById('amenities-factor').innerHTML = '<span class="factor-value">Unknown</span><span class="factor-impact">Impact: Unknown</span>';
}
// Risk indicators
const riskList = document.getElementById('risk-indicators');
riskList.innerHTML = '';
if (data.risk_indicators && data.risk_indicators.length > 0) {
data.risk_indicators.forEach(risk => {
const li = document.createElement('li');
li.className = 'risk-item warning';
li.innerHTML = `⚠️ ${risk}`;
riskList.appendChild(li);
});
} else {
const li = document.createElement('li');
li.className = 'risk-item success';
li.innerHTML = 'βœ… No significant risks identified';
riskList.appendChild(li);
}
// Add a summary comparison section
const priceComparisonDiv = document.createElement('div');
priceComparisonDiv.className = 'price-comparison-summary';
// Determine assessment class for styling
let assessmentClass = 'market-rate';
if (deviation > 20) {
assessmentClass = 'above-market';
} else if (deviation < -20) {
assessmentClass = 'below-market';
}
const comparisonHTML = `
<h4 style="margin-bottom: 15px; color: #4361ee; font-size: 1.2rem;">Price Comparison Summary</h4>
<div class="price-comparison-grid">
<div class="price-comparison-item">
<div class="price-comparison-label">Listing Price per sq.ft.</div>
<div class="price-comparison-value listing">${listingPricePerSqft}</div>
</div>
<div class="price-comparison-item">
<div class="price-comparison-label">Market Average per sq.ft.</div>
<div class="price-comparison-value market">${marketAveragePerSqft}</div>
</div>
</div>
<div class="price-assessment-summary">
<div class="price-assessment-label">Price Assessment</div>
<div class="price-assessment-value ${assessmentClass}">${assessmentText}</div>
</div>
`;
priceComparisonDiv.innerHTML = comparisonHTML;
// Insert the comparison section after the price overview
const priceOverview = document.querySelector('.price-overview');
if (priceOverview && priceOverview.parentNode) {
priceOverview.parentNode.insertBefore(priceComparisonDiv, priceOverview.nextSibling);
}
} catch (error) {
console.error('Error updating price analysis:', error);
}
}
function updatePriceRanges(ranges) {
try {
const elements = {
'budget-range': ranges.budget,
'mid-range': ranges.mid_range,
'premium-range': ranges.premium
};
Object.entries(elements).forEach(([id, range]) => {
const element = document.getElementById(id);
if (element) {
element.innerHTML = `
<div class="range-value">β‚Ή${range.min.toLocaleString()} - β‚Ή${range.max.toLocaleString()}</div>
<div class="range-description">${range.description}</div>
`;
}
});
} catch (error) {
console.error('Error updating price ranges:', error);
}
}
function updateMarketTrends(trends) {
try {
// City tier
const cityTier = document.getElementById('city-tier');
if (cityTier) {
const tierIcon = trends.city_tier === 'metro' ? 'πŸ™οΈ' :
trends.city_tier === 'tier-1' ? 'πŸŒ†' :
trends.city_tier === 'tier-2' ? '🏘️' : '🏑';
cityTier.innerHTML = `${tierIcon} ${trends.city_tier.toUpperCase()}`;
}
// Price trend
const priceTrend = document.getElementById('price-trend');
if (priceTrend) {
const trendIcon = trends.avg_price_range.trend === 'increasing' ? 'πŸ“ˆ' :
trends.avg_price_range.trend === 'decreasing' ? 'πŸ“‰' : '➑️';
priceTrend.innerHTML = `${trendIcon} ${trends.avg_price_range.trend.toUpperCase()}`;
}
// Market average
const marketAvg = document.getElementById('market-average');
if (marketAvg) {
const avgPrice = trends.price_per_sqft.market_avg;
marketAvg.innerHTML = `
<div class="trend-value">β‚Ή${avgPrice.toLocaleString()}</div>
<div class="trend-description">Market Average</div>
`;
}
// Price deviation
const priceDeviation = document.getElementById('price-deviation');
if (priceDeviation) {
const deviation = trends.price_per_sqft.deviation;
const deviationColor = deviation > 20 ? '#f72585' : deviation > 10 ? '#f8961e' : '#4cc9f0';
priceDeviation.innerHTML = `
<div class="trend-value" style="color: ${deviationColor}">${deviation.toFixed(1)}%</div>
<div class="trend-description">From Market Average</div>
`;
}
} catch (error) {
console.error('Error updating market trends:', error);
}
}
function updatePriceFactors(factors) {
try {
// Property age factor
if (factors.age_factor) {
const ageFactor = document.getElementById('age-factor');
if (ageFactor) {
const ageImpact = factors.age_factor.impact === 'high' ? 'πŸ”΄' :
factors.age_factor.impact === 'medium' ? '🟑' : '🟒';
ageFactor.innerHTML = `
<span class="factor-value">${factors.age_factor.property_age} years</span>
<span class="factor-impact">${ageImpact} Impact: ${factors.age_factor.impact.toUpperCase()}</span>
`;
}
}
// Size factor
if (factors.size_factor) {
const sizeFactor = document.getElementById('size-factor');
if (sizeFactor) {
const sizeEfficiency = factors.size_factor.efficiency === 'high' ? '🟒' :
factors.size_factor.efficiency === 'medium' ? '🟑' : 'πŸ”΄';
sizeFactor.innerHTML = `
<span class="factor-value">${sizeEfficiency} ${factors.size_factor.efficiency.toUpperCase()}</span>
<span class="factor-impact">Size: ${factors.size_factor.size.toLocaleString()} sq.ft.</span>
`;
}
}
// Amenities factor
if (factors.amenities_factor) {
const amenitiesFactor = document.getElementById('amenities-factor');
if (amenitiesFactor) {
const amenitiesImpact = factors.amenities_factor.impact === 'high' ? '🟒' :
factors.amenities_factor.impact === 'medium' ? '🟑' : 'πŸ”΄';
amenitiesFactor.innerHTML = `
<span class="factor-value">${amenitiesImpact} ${factors.amenities_factor.count} amenities</span>
<span class="factor-impact">Impact: ${factors.amenities_factor.impact.toUpperCase()}</span>
`;
}
}
} catch (error) {
console.error('Error updating price factors:', error);
}
}
function updateRiskIndicators(indicators) {
try {
const riskList = document.getElementById('risk-indicators');
if (!riskList) return;
riskList.innerHTML = '';
if (indicators.length > 0) {
indicators.forEach(risk => {
const li = document.createElement('li');
li.innerHTML = `⚠️ ${risk}`;
li.style.background = '#fff3e0';
li.style.color = '#e65100';
riskList.appendChild(li);
});
} else {
const li = document.createElement('li');
li.innerHTML = 'βœ… No significant risks identified';
li.style.background = '#e8f5e9';
li.style.color = '#2e7d32';
riskList.appendChild(li);
}
} catch (error) {
console.error('Error updating risk indicators:', error);
}
}
</script>
</body>
</html>