|
<!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);
|
|
}
|
|
|
|
* {
|
|
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;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.form-grid, .results-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.card {
|
|
padding: 15px;
|
|
}
|
|
}
|
|
</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>
|
|
|
|
<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="squareFeet">Square Feet</label>
|
|
<input type="number" class="form-control" id="squareFeet" name="square_feet" 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="2024">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="price">Price</label>
|
|
<input type="number" class="form-control" id="price" name="price" min="0" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-title">Documents & Images</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Property Images</label>
|
|
<input type="file" class="form-control" id="images" name="images" multiple accept="image/*">
|
|
<div class="image-preview" id="imagePreview"></div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Property Documents (PDF)</label>
|
|
<input type="file" class="form-control" id="documents" name="documents" multiple accept=".pdf">
|
|
<div class="pdf-preview" id="pdfPreview"></div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-block">Verify Property</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="loading" id="loading">
|
|
<div class="spinner"></div>
|
|
<p>Analyzing property details...</p>
|
|
</div>
|
|
|
|
<div class="results-container" id="results">
|
|
<div class="results-grid">
|
|
<div class="result-card">
|
|
<div class="result-header">
|
|
<div class="result-icon">📊</div>
|
|
<h3 class="result-title">Trust Score</h3>
|
|
</div>
|
|
<div class="trust-score">
|
|
<div class="score-value" id="trustScore">0</div>
|
|
<div class="score-label">Overall Trust Score</div>
|
|
<div class="progress-container">
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" id="trustScoreBar"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="result-card">
|
|
<div class="result-header">
|
|
<div class="result-icon">🔍</div>
|
|
<h3 class="result-title">Fraud Analysis</h3>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="fraudChart"></canvas>
|
|
</div>
|
|
<div id="fraudDetails"></div>
|
|
</div>
|
|
|
|
<div class="result-card">
|
|
<div class="result-header">
|
|
<div class="result-icon">📝</div>
|
|
<h3 class="result-title">Quality Assessment</h3>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="qualityChart"></canvas>
|
|
</div>
|
|
<div id="qualityDetails"></div>
|
|
</div>
|
|
|
|
<div class="result-card">
|
|
<div class="result-header">
|
|
<div class="result-icon">📍</div>
|
|
<h3 class="result-title">Location Analysis</h3>
|
|
</div>
|
|
<div id="locationDetails"></div>
|
|
</div>
|
|
|
|
<div class="result-card">
|
|
<div class="result-header">
|
|
<div class="result-icon">💰</div>
|
|
<h3 class="result-title">Price Analysis</h3>
|
|
</div>
|
|
<div id="priceDetails"></div>
|
|
</div>
|
|
|
|
<div class="result-card">
|
|
<div class="result-header">
|
|
<div class="result-icon">⚖️</div>
|
|
<h3 class="result-title">Legal Analysis</h3>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="legalChart"></canvas>
|
|
</div>
|
|
<div id="legalDetails"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">Final Verdict</h2>
|
|
</div>
|
|
<div class="final-verdict">
|
|
<div class="verdict-box" id="verdictBox">
|
|
<div class="verdict-icon">✅</div>
|
|
<div class="verdict-text" id="verdictText">Analyzing...</div>
|
|
</div>
|
|
<div id="verdictDetails"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
|
|
let uploadedImages = [];
|
|
let uploadedPDFs = [];
|
|
let fraudChart = null;
|
|
let qualityChart = null;
|
|
let legalChart = null;
|
|
|
|
|
|
function initializeCharts() {
|
|
|
|
const fraudCtx = document.getElementById('fraudChart').getContext('2d');
|
|
fraudChart = new Chart(fraudCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: ['Low Risk', 'Medium Risk', 'High Risk'],
|
|
datasets: [{
|
|
data: [0, 0, 0],
|
|
backgroundColor: [
|
|
'rgba(76, 201, 240, 0.8)',
|
|
'rgba(248, 150, 30, 0.8)',
|
|
'rgba(247, 37, 133, 0.8)'
|
|
]
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false
|
|
}
|
|
});
|
|
|
|
|
|
const qualityCtx = document.getElementById('qualityChart').getContext('2d');
|
|
qualityChart = new Chart(qualityCtx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: ['Completeness', 'Accuracy', 'Consistency'],
|
|
datasets: [{
|
|
label: 'Score',
|
|
data: [0, 0, 0],
|
|
backgroundColor: 'rgba(67, 97, 238, 0.8)'
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
max: 100
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
const legalCtx = document.getElementById('legalChart').getContext('2d');
|
|
legalChart = new Chart(legalCtx, {
|
|
type: 'radar',
|
|
data: {
|
|
labels: ['Documentation', 'Compliance', 'Risk Level'],
|
|
datasets: [{
|
|
label: 'Score',
|
|
data: [0, 0, 0],
|
|
backgroundColor: 'rgba(67, 97, 238, 0.2)',
|
|
borderColor: 'rgba(67, 97, 238, 1)',
|
|
pointBackgroundColor: 'rgba(67, 97, 238, 1)'
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
r: {
|
|
beginAtZero: true,
|
|
max: 100
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
document.getElementById('propertyForm').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
|
|
document.getElementById('loading').style.display = 'block';
|
|
document.getElementById('results').style.display = 'none';
|
|
|
|
try {
|
|
const formData = new FormData(e.target);
|
|
|
|
|
|
uploadedImages.forEach((file, index) => {
|
|
formData.append(`image_${index}`, file);
|
|
});
|
|
|
|
|
|
uploadedPDFs.forEach((file, index) => {
|
|
formData.append(`document_${index}`, file);
|
|
});
|
|
|
|
|
|
const response = await fetch('/verify', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Verification failed');
|
|
}
|
|
|
|
const data = await response.json();
|
|
displayResults(data);
|
|
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('An error occurred during verification. Please try again.');
|
|
} finally {
|
|
document.getElementById('loading').style.display = 'none';
|
|
}
|
|
});
|
|
|
|
|
|
document.getElementById('images').addEventListener('change', (e) => {
|
|
const files = Array.from(e.target.files);
|
|
uploadedImages = files;
|
|
displayImagePreviews(files);
|
|
});
|
|
|
|
document.getElementById('documents').addEventListener('change', (e) => {
|
|
const files = Array.from(e.target.files);
|
|
uploadedPDFs = files;
|
|
displayPDFPreviews(files);
|
|
});
|
|
|
|
|
|
function displayImagePreviews(files) {
|
|
const preview = document.getElementById('imagePreview');
|
|
preview.innerHTML = '';
|
|
|
|
files.forEach((file, index) => {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
const div = document.createElement('div');
|
|
div.className = 'preview-item';
|
|
div.innerHTML = `
|
|
<img src="${e.target.result}" alt="Preview">
|
|
<button class="preview-remove" onclick="removeImage(${index})">×</button>
|
|
`;
|
|
preview.appendChild(div);
|
|
};
|
|
reader.readAsDataURL(file);
|
|
});
|
|
}
|
|
|
|
|
|
function displayPDFPreviews(files) {
|
|
const preview = document.getElementById('pdfPreview');
|
|
preview.innerHTML = '';
|
|
|
|
files.forEach((file, index) => {
|
|
const div = document.createElement('div');
|
|
div.className = 'pdf-filename';
|
|
div.innerHTML = `
|
|
${file.name}
|
|
<button class="preview-remove" onclick="removePDF(${index})">×</button>
|
|
`;
|
|
preview.appendChild(div);
|
|
});
|
|
}
|
|
|
|
|
|
function removeImage(index) {
|
|
uploadedImages.splice(index, 1);
|
|
displayImagePreviews(uploadedImages);
|
|
}
|
|
|
|
|
|
function removePDF(index) {
|
|
uploadedPDFs.splice(index, 1);
|
|
displayPDFPreviews(uploadedPDFs);
|
|
}
|
|
|
|
|
|
function displayResults(data) {
|
|
|
|
document.getElementById('results').style.display = 'block';
|
|
|
|
|
|
const trustScore = data.trust_score || 0;
|
|
document.getElementById('trustScore').textContent = trustScore;
|
|
document.getElementById('trustScoreBar').style.width = `${trustScore}%`;
|
|
|
|
|
|
if (data.fraud_analysis) {
|
|
updateFraudAnalysis(data.fraud_analysis);
|
|
}
|
|
|
|
|
|
if (data.quality_assessment) {
|
|
updateQualityAssessment(data.quality_assessment);
|
|
}
|
|
|
|
|
|
if (data.location_analysis) {
|
|
updateLocationAnalysis(data.location_analysis);
|
|
}
|
|
|
|
|
|
if (data.price_analysis) {
|
|
updatePriceAnalysis(data.price_analysis);
|
|
}
|
|
|
|
|
|
if (data.legal_analysis) {
|
|
updateLegalAnalysis(data.legal_analysis);
|
|
}
|
|
|
|
|
|
updateFinalVerdict(data);
|
|
}
|
|
|
|
|
|
function updateFraudAnalysis(analysis) {
|
|
|
|
fraudChart.data.datasets[0].data = [
|
|
analysis.low_risk || 0,
|
|
analysis.medium_risk || 0,
|
|
analysis.high_risk || 0
|
|
];
|
|
fraudChart.update();
|
|
|
|
|
|
const details = document.getElementById('fraudDetails');
|
|
details.innerHTML = `
|
|
<div class="alert ${getAlertClass(analysis.alert_level)}">
|
|
${analysis.summary || 'No fraud indicators detected.'}
|
|
</div>
|
|
${analysis.indicators ? `
|
|
<div class="red-flags">
|
|
<h4>Risk Indicators:</h4>
|
|
<ul>
|
|
${analysis.indicators.map(indicator => `
|
|
<li>${indicator}</li>
|
|
`).join('')}
|
|
</ul>
|
|
</div>
|
|
` : ''}
|
|
`;
|
|
}
|
|
|
|
|
|
function updateQualityAssessment(assessment) {
|
|
|
|
qualityChart.data.datasets[0].data = [
|
|
assessment.completeness || 0,
|
|
assessment.accuracy || 0,
|
|
assessment.consistency || 0
|
|
];
|
|
qualityChart.update();
|
|
|
|
|
|
const details = document.getElementById('qualityDetails');
|
|
details.innerHTML = `
|
|
<div class="explanation-box">
|
|
<div class="explanation-title">Quality Assessment</div>
|
|
<p>${assessment.summary || 'No quality issues detected.'}</p>
|
|
</div>
|
|
${assessment.suggestions ? `
|
|
<div class="suggestion-list">
|
|
${assessment.suggestions.map(suggestion => `
|
|
<div class="suggestion-item">${suggestion}</div>
|
|
`).join('')}
|
|
</div>
|
|
` : ''}
|
|
`;
|
|
}
|
|
|
|
|
|
function updateLocationAnalysis(analysis) {
|
|
const details = document.getElementById('locationDetails');
|
|
details.innerHTML = `
|
|
<div class="explanation-box">
|
|
<div class="explanation-title">Location Analysis</div>
|
|
<p>${analysis.summary || 'Location verified.'}</p>
|
|
</div>
|
|
${analysis.verification ? `
|
|
<div class="verification-scores">
|
|
${Object.entries(analysis.verification).map(([key, value]) => `
|
|
<div class="score-item">
|
|
<div class="score-label">${formatLabel(key)}</div>
|
|
<div class="score-bar-container">
|
|
<div class="score-bar" style="--score: ${value}%"></div>
|
|
<div class="score-value">${value}%</div>
|
|
</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
` : ''}
|
|
`;
|
|
}
|
|
|
|
|
|
function updatePriceAnalysis(analysis) {
|
|
const details = document.getElementById('priceDetails');
|
|
details.innerHTML = `
|
|
<div class="explanation-box">
|
|
<div class="explanation-title">Price Analysis</div>
|
|
<p>${analysis.summary || 'Price analysis completed.'}</p>
|
|
</div>
|
|
${analysis.market_trends ? `
|
|
<div class="property-summary">
|
|
<h4>Market Trends</h4>
|
|
<p>${analysis.market_trends}</p>
|
|
</div>
|
|
` : ''}
|
|
${analysis.price_factors ? `
|
|
<div class="property-summary">
|
|
<h4>Price Factors</h4>
|
|
<ul>
|
|
${analysis.price_factors.map(factor => `
|
|
<li>${factor}</li>
|
|
`).join('')}
|
|
</ul>
|
|
</div>
|
|
` : ''}
|
|
${analysis.risk_indicators ? `
|
|
<div class="red-flags">
|
|
<h4>Risk Indicators</h4>
|
|
<ul>
|
|
${analysis.risk_indicators.map(indicator => `
|
|
<li>${indicator}</li>
|
|
`).join('')}
|
|
</ul>
|
|
</div>
|
|
` : ''}
|
|
`;
|
|
}
|
|
|
|
|
|
function updateLegalAnalysis(analysis) {
|
|
|
|
legalChart.data.datasets[0].data = [
|
|
analysis.documentation_score || 0,
|
|
analysis.compliance_score || 0,
|
|
analysis.risk_score || 0
|
|
];
|
|
legalChart.update();
|
|
|
|
|
|
const details = document.getElementById('legalDetails');
|
|
details.innerHTML = `
|
|
<div class="explanation-box">
|
|
<div class="explanation-title">Legal Analysis</div>
|
|
<p>${analysis.summary || 'Legal analysis completed.'}</p>
|
|
</div>
|
|
${analysis.issues ? `
|
|
<div class="red-flags">
|
|
<h4>Legal Issues</h4>
|
|
<ul>
|
|
${analysis.issues.map(issue => `
|
|
<li>${issue}</li>
|
|
`).join('')}
|
|
</ul>
|
|
</div>
|
|
` : ''}
|
|
${analysis.recommendations ? `
|
|
<div class="suggestion-list">
|
|
${analysis.recommendations.map(recommendation => `
|
|
<div class="suggestion-item">${recommendation}</div>
|
|
`).join('')}
|
|
</div>
|
|
` : ''}
|
|
`;
|
|
}
|
|
|
|
|
|
function updateFinalVerdict(data) {
|
|
const verdictBox = document.getElementById('verdictBox');
|
|
const verdictText = document.getElementById('verdictText');
|
|
const verdictDetails = document.getElementById('verdictDetails');
|
|
|
|
|
|
let verdictClass = 'verdict-legitimate';
|
|
let verdictIcon = '✅';
|
|
|
|
if (data.trust_score < 60) {
|
|
verdictClass = 'verdict-fraudulent';
|
|
verdictIcon = '❌';
|
|
} else if (data.trust_score < 80) {
|
|
verdictClass = 'verdict-suspicious';
|
|
verdictIcon = '⚠️';
|
|
}
|
|
|
|
|
|
verdictBox.className = `verdict-box ${verdictClass}`;
|
|
verdictBox.querySelector('.verdict-icon').textContent = verdictIcon;
|
|
verdictText.textContent = data.verdict || 'Analysis complete';
|
|
|
|
|
|
verdictDetails.innerHTML = `
|
|
<div class="verification-scores">
|
|
${Object.entries(data.scores || {}).map(([key, value]) => `
|
|
<div class="score-item">
|
|
<div class="score-label">${formatLabel(key)}</div>
|
|
<div class="score-bar-container">
|
|
<div class="score-bar" style="--score: ${value}%"></div>
|
|
<div class="score-value">${value}%</div>
|
|
</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
|
|
function getAlertClass(level) {
|
|
switch (level?.toLowerCase()) {
|
|
case 'high':
|
|
return 'alert-danger';
|
|
case 'medium':
|
|
return 'alert-warning';
|
|
case 'low':
|
|
return 'alert-success';
|
|
default:
|
|
return 'alert-success';
|
|
}
|
|
}
|
|
|
|
function formatLabel(key) {
|
|
return key.split('_')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', initializeCharts);
|
|
</script>
|
|
</body>
|
|
</html> |