Spaces:
Build error
Build error
Update templates/index.html
Browse files- templates/index.html +546 -239
templates/index.html
CHANGED
@@ -3,223 +3,491 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title>
|
7 |
-
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.
|
8 |
-
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.
|
9 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
body {
|
|
|
11 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
12 |
min-height: 100vh;
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
14 |
}
|
|
|
15 |
.main-container {
|
16 |
-
background:
|
17 |
-
border-radius:
|
18 |
-
box-shadow:
|
19 |
-
backdrop-filter: blur(
|
20 |
-
margin:
|
21 |
-
max-width:
|
|
|
22 |
}
|
|
|
23 |
.header {
|
24 |
-
background: linear-gradient(135deg,
|
25 |
color: white;
|
26 |
-
padding:
|
27 |
-
border-radius: 20px 20px 0 0;
|
28 |
text-align: center;
|
|
|
|
|
29 |
}
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
38 |
}
|
39 |
-
|
40 |
-
|
41 |
-
|
|
|
|
|
|
|
|
|
42 |
}
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
46 |
}
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
-
|
56 |
-
|
|
|
57 |
}
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
63 |
}
|
64 |
-
|
65 |
-
|
|
|
|
|
66 |
}
|
67 |
-
|
68 |
-
|
69 |
-
border-
|
70 |
-
|
71 |
-
|
72 |
-
border-left: 4px solid #667eea;
|
73 |
}
|
74 |
-
|
75 |
-
|
76 |
-
font-
|
77 |
-
|
78 |
-
|
79 |
}
|
80 |
-
|
81 |
-
.
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
margin:
|
91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
}
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
text-align: center;
|
95 |
-
padding:
|
|
|
|
|
|
|
96 |
}
|
|
|
97 |
.spinner {
|
98 |
-
|
99 |
-
|
|
|
|
|
100 |
border-radius: 50%;
|
101 |
-
width: 50px;
|
102 |
-
height: 50px;
|
103 |
animation: spin 1s linear infinite;
|
104 |
-
margin: 0 auto
|
105 |
}
|
|
|
106 |
@keyframes spin {
|
107 |
0% { transform: rotate(0deg); }
|
108 |
100% { transform: rotate(360deg); }
|
109 |
}
|
110 |
-
|
|
|
|
|
|
|
111 |
height: 8px;
|
112 |
-
border-radius: 4px;
|
113 |
-
background: #e9ecef;
|
114 |
overflow: hidden;
|
|
|
115 |
}
|
116 |
-
|
|
|
117 |
height: 100%;
|
118 |
-
background: linear-gradient(90deg,
|
119 |
transition: width 0.3s ease;
|
120 |
-
}
|
121 |
-
.insight-card {
|
122 |
-
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
123 |
border-radius: 10px;
|
124 |
-
padding: 15px;
|
125 |
-
margin: 10px 0;
|
126 |
-
border-left: 4px solid #28a745;
|
127 |
}
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
padding: 15px;
|
132 |
-
margin: 10px 0;
|
133 |
-
border-left: 4px solid #ffc107;
|
134 |
}
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
141 |
}
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
.confidence-bar {
|
153 |
-
width:
|
154 |
height: 6px;
|
155 |
-
background:
|
156 |
border-radius: 3px;
|
157 |
overflow: hidden;
|
|
|
158 |
}
|
|
|
159 |
.confidence-fill {
|
160 |
height: 100%;
|
161 |
-
background: linear-gradient(90deg,
|
162 |
transition: width 0.3s ease;
|
|
|
163 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
.scene-concept {
|
165 |
-
background: rgba(
|
|
|
166 |
border-radius: 8px;
|
167 |
-
padding:
|
168 |
-
margin:
|
169 |
display: inline-block;
|
170 |
-
font-size: 0.
|
|
|
171 |
}
|
|
|
172 |
.market-tier-badge {
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
text-transform: uppercase;
|
178 |
-
letter-spacing:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
}
|
180 |
-
.tier-luxury { background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%); color: #333; }
|
181 |
-
.tier-premium { background: linear-gradient(135deg, #c0c0c0 0%, #e5e5e5 100%); color: #333; }
|
182 |
-
.tier-standard { background: linear-gradient(135deg, #cd7f32 0%, #daa520 100%); color: white; }
|
183 |
-
.tier-value { background: linear-gradient(135deg, #6c757d 0%, #495057 100%); color: white; }
|
184 |
</style>
|
185 |
</head>
|
186 |
<body>
|
187 |
<div class="container-fluid">
|
188 |
<div class="main-container">
|
189 |
<div class="header">
|
190 |
-
<h1><i class="fas fa-robot"></i>
|
191 |
-
<p
|
|
|
|
|
|
|
|
|
|
|
192 |
</div>
|
193 |
|
194 |
-
<div class="
|
195 |
<div class="row">
|
196 |
-
<div class="col-
|
197 |
-
<div class="upload-
|
198 |
-
<
|
199 |
-
|
200 |
-
|
201 |
-
<
|
202 |
-
|
203 |
-
<
|
204 |
-
|
|
|
|
|
|
|
|
|
205 |
</div>
|
206 |
|
207 |
-
<div id="imagePreview" class="
|
208 |
-
<img id="previewImg" class="img-fluid
|
209 |
</div>
|
210 |
</div>
|
211 |
|
212 |
-
<div class="col-
|
213 |
-
<div id="loading" class="loading" style="display: none;">
|
214 |
<div class="spinner"></div>
|
215 |
-
<h4>
|
216 |
<p>Multiple AI models are analyzing your image</p>
|
217 |
-
<div class="progress-
|
218 |
-
<div class="progress-
|
219 |
</div>
|
220 |
</div>
|
221 |
|
222 |
-
<div id="results"
|
223 |
<!-- Results will be populated here -->
|
224 |
</div>
|
225 |
</div>
|
@@ -228,7 +496,7 @@
|
|
228 |
</div>
|
229 |
</div>
|
230 |
|
231 |
-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.
|
232 |
<script>
|
233 |
const uploadArea = document.getElementById('uploadArea');
|
234 |
const imageInput = document.getElementById('imageInput');
|
@@ -268,15 +536,16 @@
|
|
268 |
|
269 |
function handleImageUpload(file) {
|
270 |
if (!file.type.startsWith('image/')) {
|
271 |
-
|
272 |
return;
|
273 |
}
|
274 |
|
275 |
-
// Show preview
|
276 |
const reader = new FileReader();
|
277 |
reader.onload = (e) => {
|
278 |
previewImg.src = e.target.result;
|
279 |
imagePreview.style.display = 'block';
|
|
|
280 |
};
|
281 |
reader.readAsDataURL(file);
|
282 |
|
@@ -284,6 +553,21 @@
|
|
284 |
analyzeImage(file);
|
285 |
}
|
286 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
287 |
|
288 |
|
289 |
function displayResults(data) {
|
@@ -291,7 +575,7 @@
|
|
291 |
|
292 |
if (data.error) {
|
293 |
results.innerHTML = `
|
294 |
-
<div class="error-card">
|
295 |
<h5><i class="fas fa-exclamation-triangle"></i> Analysis Error</h5>
|
296 |
<p>${data.error}</p>
|
297 |
</div>
|
@@ -313,79 +597,86 @@
|
|
313 |
|
314 |
results.innerHTML = `
|
315 |
<!-- Analysis Summary -->
|
316 |
-
<div class="result-card">
|
317 |
<div class="result-header">
|
318 |
<h4><i class="fas fa-chart-line"></i> Analysis Summary</h4>
|
319 |
</div>
|
320 |
<div class="result-body">
|
321 |
-
<div class="
|
322 |
-
<div class="
|
323 |
-
<div class="metric-
|
324 |
-
|
325 |
-
|
326 |
-
<div class="confidence-
|
327 |
-
<div class="confidence-fill" style="width: ${roomClass.confidence}%"></div>
|
328 |
-
</div>
|
329 |
-
<small>Confidence: ${roomClass.confidence}%</small>
|
330 |
</div>
|
|
|
331 |
</div>
|
332 |
-
|
333 |
-
|
334 |
-
|
|
|
335 |
<span class="score-badge ${getScoreClass(assessment.overall_score)}">${assessment.overall_score}/100</span>
|
336 |
-
<p class="mt-2 mb-0"><small>${assessment.professional_grade ? 'Professional Grade' : 'Standard Grade'}</small></p>
|
337 |
</div>
|
|
|
338 |
</div>
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
<div class="metric-card">
|
344 |
-
<h6><i class="fas fa-camera"></i> Quality Score</h6>
|
345 |
<span class="score-badge ${getScoreClass(quality.quality_score)}">${quality.quality_score}/100</span>
|
346 |
-
<p class="mt-2 mb-0"><small>${quality.quality_level}</small></p>
|
347 |
</div>
|
|
|
348 |
</div>
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
|
|
|
|
|
|
|
|
|
|
355 |
</div>
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
<
|
361 |
</div>
|
|
|
362 |
</div>
|
363 |
</div>
|
364 |
</div>
|
365 |
</div>
|
366 |
|
367 |
<!-- Market Analysis -->
|
368 |
-
<div class="result-card">
|
369 |
<div class="result-header">
|
370 |
<h4><i class="fas fa-chart-bar"></i> Market Analysis</h4>
|
371 |
</div>
|
372 |
<div class="result-body">
|
373 |
<div class="row">
|
374 |
<div class="col-md-6">
|
375 |
-
<
|
376 |
-
<p
|
377 |
-
<p><strong>Price Positioning:</strong> ${market.price_positioning}</p>
|
378 |
<p><strong>Competitive Position:</strong> ${market.competitive_position}</p>
|
|
|
|
|
|
|
|
|
|
|
379 |
</div>
|
380 |
<div class="col-md-6">
|
381 |
-
<h6>
|
382 |
-
<ul>
|
383 |
-
${(market.
|
384 |
</ul>
|
385 |
|
386 |
-
<h6>
|
387 |
-
<ul>
|
388 |
-
${(market.
|
389 |
</ul>
|
390 |
</div>
|
391 |
</div>
|
@@ -393,32 +684,32 @@
|
|
393 |
</div>
|
394 |
|
395 |
<!-- Property Insights -->
|
396 |
-
<div class="result-card">
|
397 |
<div class="result-header">
|
398 |
<h4><i class="fas fa-lightbulb"></i> Property Insights</h4>
|
399 |
</div>
|
400 |
<div class="result-body">
|
401 |
<div class="row">
|
402 |
<div class="col-md-6">
|
403 |
-
<h6><i class="fas fa-bullhorn"></i> Marketing Tips</h6>
|
404 |
-
<ul>
|
405 |
-
${(insights.marketing_tips || []).map(tip => `<li>${tip}</li>`).join('')}
|
406 |
</ul>
|
407 |
|
408 |
-
<h6><i class="fas fa-users"></i> Target Audience</h6>
|
409 |
-
<ul>
|
410 |
-
${(insights.target_audience || []).map(audience => `<li>${audience}</li>`).join('')}
|
411 |
</ul>
|
412 |
</div>
|
413 |
<div class="col-md-6">
|
414 |
-
<h6><i class="fas fa-dollar-sign"></i> Pricing Considerations</h6>
|
415 |
-
<ul>
|
416 |
-
${(insights.pricing_considerations || []).map(consideration => `<li>${consideration}</li>`).join('')}
|
417 |
</ul>
|
418 |
|
419 |
-
<h6><i class="fas fa-tools"></i> Improvement Suggestions</h6>
|
420 |
-
<ul>
|
421 |
-
${(insights.improvement_suggestions || []).map(suggestion => `<li>${suggestion}</li>`).join('')}
|
422 |
</ul>
|
423 |
</div>
|
424 |
</div>
|
@@ -426,17 +717,18 @@
|
|
426 |
</div>
|
427 |
|
428 |
<!-- Scene Analysis -->
|
429 |
-
<div class="result-card">
|
430 |
<div class="result-header">
|
431 |
<h4><i class="fas fa-palette"></i> Scene Analysis</h4>
|
432 |
</div>
|
433 |
<div class="result-body">
|
434 |
<div class="row">
|
435 |
<div class="col-md-6">
|
436 |
-
<h6>
|
|
|
437 |
<p><strong>Style Confidence:</strong> ${(scene.style_analysis.style_confidence * 100).toFixed(1)}%</p>
|
438 |
|
439 |
-
<h6>Scene Concepts
|
440 |
<div>
|
441 |
${(scene.scene_concepts || []).map(concept =>
|
442 |
`<span class="scene-concept">${concept.concept} (${(concept.confidence * 100).toFixed(1)}%)</span>`
|
@@ -444,17 +736,17 @@
|
|
444 |
</div>
|
445 |
</div>
|
446 |
<div class="col-md-6">
|
447 |
-
<h6>Quality Indicators
|
448 |
-
<ul>
|
449 |
${(scene.style_analysis.quality_indicators || []).map(indicator =>
|
450 |
-
`<li>${indicator.concept} (${(indicator.confidence * 100).toFixed(1)}%)</li>`
|
451 |
).join('')}
|
452 |
</ul>
|
453 |
|
454 |
-
<h6>Functionality Indicators
|
455 |
-
<ul>
|
456 |
${(scene.style_analysis.functionality_indicators || []).map(indicator =>
|
457 |
-
`<li>${indicator.concept} (${(indicator.confidence * 100).toFixed(1)}%)</li>`
|
458 |
).join('')}
|
459 |
</ul>
|
460 |
</div>
|
@@ -463,32 +755,27 @@
|
|
463 |
</div>
|
464 |
|
465 |
<!-- Detailed Analysis -->
|
466 |
-
<div class="result-card">
|
467 |
<div class="result-header">
|
468 |
<h4><i class="fas fa-microscope"></i> Detailed Analysis</h4>
|
469 |
</div>
|
470 |
<div class="result-body">
|
471 |
<div class="row">
|
472 |
<div class="col-md-6">
|
473 |
-
<h6>Strengths
|
474 |
-
<ul>
|
475 |
-
${(assessment.strengths || []).map(strength => `<li class="insight-
|
476 |
</ul>
|
477 |
|
478 |
-
<h6>Weaknesses
|
479 |
-
<ul>
|
480 |
-
${(assessment.weaknesses || []).map(weakness => `<li class="
|
481 |
</ul>
|
482 |
</div>
|
483 |
<div class="col-md-6">
|
484 |
-
<h6>Recommendations
|
485 |
-
<ul>
|
486 |
-
${(assessment.recommendations || []).map(rec => `<li class="insight-
|
487 |
-
</ul>
|
488 |
-
|
489 |
-
<h6>Market Opportunities:</h6>
|
490 |
-
<ul>
|
491 |
-
${(market.market_opportunities || []).map(opp => `<li class="insight-card">${opp}</li>`).join('')}
|
492 |
</ul>
|
493 |
</div>
|
494 |
</div>
|
@@ -496,14 +783,14 @@
|
|
496 |
</div>
|
497 |
|
498 |
<!-- Object Detection Results -->
|
499 |
-
<div class="result-card">
|
500 |
<div class="result-header">
|
501 |
<h4><i class="fas fa-cube"></i> Object Detection</h4>
|
502 |
</div>
|
503 |
<div class="result-body">
|
504 |
<div class="row">
|
505 |
<div class="col-md-6">
|
506 |
-
<h6>Detected Objects
|
507 |
${(objects.objects || []).map(obj => `
|
508 |
<div class="object-item">
|
509 |
<span><i class="fas fa-tag"></i> ${obj.label}</span>
|
@@ -515,13 +802,31 @@
|
|
515 |
`).join('')}
|
516 |
</div>
|
517 |
<div class="col-md-6">
|
518 |
-
<h6>Object Categories
|
519 |
-
<
|
520 |
-
|
521 |
-
|
522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
523 |
|
524 |
-
<h6>Composition Analysis
|
525 |
<p><strong>Object Density:</strong> ${objects.analysis.object_density?.toFixed(2) || 0}</p>
|
526 |
<p><strong>Composition Balance:</strong> ${(objects.analysis.composition_balance * 100).toFixed(1)}%</p>
|
527 |
</div>
|
@@ -530,12 +835,12 @@
|
|
530 |
</div>
|
531 |
|
532 |
<!-- AI Models Used -->
|
533 |
-
<div class="result-card">
|
534 |
<div class="result-header">
|
535 |
<h4><i class="fas fa-robot"></i> AI Models Used</h4>
|
536 |
</div>
|
537 |
<div class="result-body">
|
538 |
-
<div>
|
539 |
<span class="ai-model-badge">Room Classification</span>
|
540 |
<span class="ai-model-badge">BLIP Captioning</span>
|
541 |
<span class="ai-model-badge">DETR Detection</span>
|
@@ -543,7 +848,9 @@
|
|
543 |
<span class="ai-model-badge">CLIP Scene Understanding</span>
|
544 |
<span class="ai-model-badge">Market Analysis</span>
|
545 |
</div>
|
546 |
-
<p class="mt-3 mb-0
|
|
|
|
|
547 |
</div>
|
548 |
</div>
|
549 |
`;
|
@@ -562,11 +869,11 @@
|
|
562 |
|
563 |
// Simulate progress
|
564 |
let progress = 0;
|
565 |
-
const
|
566 |
const progressInterval = setInterval(() => {
|
567 |
progress += Math.random() * 15;
|
568 |
if (progress > 90) progress = 90;
|
569 |
-
|
570 |
}, 500);
|
571 |
|
572 |
fetch('/analyze', {
|
@@ -581,7 +888,7 @@
|
|
581 |
})
|
582 |
.then(data => {
|
583 |
clearInterval(progressInterval);
|
584 |
-
|
585 |
|
586 |
setTimeout(() => {
|
587 |
loading.style.display = 'none';
|
@@ -593,7 +900,7 @@
|
|
593 |
loading.style.display = 'none';
|
594 |
console.error('Analysis failed:', error);
|
595 |
results.innerHTML = `
|
596 |
-
<div class="error-card">
|
597 |
<h5><i class="fas fa-exclamation-triangle"></i> Analysis Failed</h5>
|
598 |
<p>Error: ${error.message}</p>
|
599 |
<p>Please try again or check the server logs.</p>
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>AI Property Analyzer</title>
|
7 |
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
8 |
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
9 |
<style>
|
10 |
+
:root {
|
11 |
+
--primary-color: #6366f1;
|
12 |
+
--primary-dark: #4f46e5;
|
13 |
+
--secondary-color: #8b5cf6;
|
14 |
+
--accent-color: #06b6d4;
|
15 |
+
--success-color: #10b981;
|
16 |
+
--warning-color: #f59e0b;
|
17 |
+
--error-color: #ef4444;
|
18 |
+
--text-primary: #1f2937;
|
19 |
+
--text-secondary: #6b7280;
|
20 |
+
--bg-primary: #ffffff;
|
21 |
+
--bg-secondary: #f9fafb;
|
22 |
+
--border-color: #e5e7eb;
|
23 |
+
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
24 |
+
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
25 |
+
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
26 |
+
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
27 |
+
}
|
28 |
+
|
29 |
+
* {
|
30 |
+
margin: 0;
|
31 |
+
padding: 0;
|
32 |
+
box-sizing: border-box;
|
33 |
+
}
|
34 |
+
|
35 |
body {
|
36 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
37 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
38 |
min-height: 100vh;
|
39 |
+
color: var(--text-primary);
|
40 |
+
line-height: 1.6;
|
41 |
+
}
|
42 |
+
|
43 |
+
.container-fluid {
|
44 |
+
padding: 2rem 1rem;
|
45 |
}
|
46 |
+
|
47 |
.main-container {
|
48 |
+
background: var(--bg-primary);
|
49 |
+
border-radius: 24px;
|
50 |
+
box-shadow: var(--shadow-xl);
|
51 |
+
backdrop-filter: blur(20px);
|
52 |
+
margin: 0 auto;
|
53 |
+
max-width: 1200px;
|
54 |
+
overflow: hidden;
|
55 |
}
|
56 |
+
|
57 |
.header {
|
58 |
+
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
59 |
color: white;
|
60 |
+
padding: 3rem 2rem;
|
|
|
61 |
text-align: center;
|
62 |
+
position: relative;
|
63 |
+
overflow: hidden;
|
64 |
}
|
65 |
+
|
66 |
+
.header::before {
|
67 |
+
content: '';
|
68 |
+
position: absolute;
|
69 |
+
top: 0;
|
70 |
+
left: 0;
|
71 |
+
right: 0;
|
72 |
+
bottom: 0;
|
73 |
+
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
|
74 |
+
opacity: 0.3;
|
75 |
}
|
76 |
+
|
77 |
+
.header h1 {
|
78 |
+
font-size: 2.5rem;
|
79 |
+
font-weight: 800;
|
80 |
+
margin-bottom: 0.5rem;
|
81 |
+
position: relative;
|
82 |
+
z-index: 1;
|
83 |
}
|
84 |
+
|
85 |
+
.header p {
|
86 |
+
font-size: 1.1rem;
|
87 |
+
opacity: 0.9;
|
88 |
+
margin-bottom: 1.5rem;
|
89 |
+
position: relative;
|
90 |
+
z-index: 1;
|
91 |
}
|
92 |
+
|
93 |
+
.badge {
|
94 |
+
background: rgba(255, 255, 255, 0.2);
|
95 |
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
96 |
+
backdrop-filter: blur(10px);
|
97 |
+
padding: 0.5rem 1rem;
|
98 |
+
border-radius: 50px;
|
99 |
+
font-size: 0.875rem;
|
100 |
+
font-weight: 500;
|
101 |
+
margin: 0.25rem;
|
102 |
+
display: inline-block;
|
103 |
}
|
104 |
+
|
105 |
+
.content-area {
|
106 |
+
padding: 3rem 2rem;
|
107 |
}
|
108 |
+
|
109 |
+
.upload-section {
|
110 |
+
background: var(--bg-secondary);
|
111 |
+
border-radius: 20px;
|
112 |
+
padding: 2rem;
|
113 |
+
margin-bottom: 2rem;
|
114 |
+
border: 2px dashed var(--border-color);
|
115 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
116 |
}
|
117 |
+
|
118 |
+
.upload-section:hover {
|
119 |
+
border-color: var(--primary-color);
|
120 |
+
background: rgba(99, 102, 241, 0.02);
|
121 |
}
|
122 |
+
|
123 |
+
.upload-section.dragover {
|
124 |
+
border-color: var(--primary-color);
|
125 |
+
background: rgba(99, 102, 241, 0.05);
|
126 |
+
transform: scale(1.02);
|
|
|
127 |
}
|
128 |
+
|
129 |
+
.upload-icon {
|
130 |
+
font-size: 3rem;
|
131 |
+
color: var(--primary-color);
|
132 |
+
margin-bottom: 1rem;
|
133 |
}
|
134 |
+
|
135 |
+
.upload-text h4 {
|
136 |
+
font-size: 1.5rem;
|
137 |
+
font-weight: 600;
|
138 |
+
margin-bottom: 0.5rem;
|
139 |
+
color: var(--text-primary);
|
140 |
+
}
|
141 |
+
|
142 |
+
.upload-text p {
|
143 |
+
color: var(--text-secondary);
|
144 |
+
margin-bottom: 1.5rem;
|
145 |
+
}
|
146 |
+
|
147 |
+
.btn-primary {
|
148 |
+
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
149 |
+
border: none;
|
150 |
+
padding: 0.75rem 2rem;
|
151 |
+
border-radius: 12px;
|
152 |
+
font-weight: 600;
|
153 |
+
transition: all 0.3s ease;
|
154 |
+
box-shadow: var(--shadow-md);
|
155 |
+
}
|
156 |
+
|
157 |
+
.btn-primary:hover {
|
158 |
+
transform: translateY(-2px);
|
159 |
+
box-shadow: var(--shadow-lg);
|
160 |
+
}
|
161 |
+
|
162 |
+
.image-preview {
|
163 |
+
margin-top: 1.5rem;
|
164 |
+
border-radius: 16px;
|
165 |
+
overflow: hidden;
|
166 |
+
box-shadow: var(--shadow-lg);
|
167 |
}
|
168 |
+
|
169 |
+
.image-preview img {
|
170 |
+
width: 100%;
|
171 |
+
height: auto;
|
172 |
+
max-height: 300px;
|
173 |
+
object-fit: cover;
|
174 |
+
}
|
175 |
+
|
176 |
+
.loading-section {
|
177 |
text-align: center;
|
178 |
+
padding: 3rem 2rem;
|
179 |
+
background: var(--bg-secondary);
|
180 |
+
border-radius: 20px;
|
181 |
+
margin-bottom: 2rem;
|
182 |
}
|
183 |
+
|
184 |
.spinner {
|
185 |
+
width: 60px;
|
186 |
+
height: 60px;
|
187 |
+
border: 4px solid var(--border-color);
|
188 |
+
border-top: 4px solid var(--primary-color);
|
189 |
border-radius: 50%;
|
|
|
|
|
190 |
animation: spin 1s linear infinite;
|
191 |
+
margin: 0 auto 1.5rem;
|
192 |
}
|
193 |
+
|
194 |
@keyframes spin {
|
195 |
0% { transform: rotate(0deg); }
|
196 |
100% { transform: rotate(360deg); }
|
197 |
}
|
198 |
+
|
199 |
+
.progress-container {
|
200 |
+
background: var(--border-color);
|
201 |
+
border-radius: 10px;
|
202 |
height: 8px;
|
|
|
|
|
203 |
overflow: hidden;
|
204 |
+
margin: 1rem 0;
|
205 |
}
|
206 |
+
|
207 |
+
.progress-bar {
|
208 |
height: 100%;
|
209 |
+
background: linear-gradient(90deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
210 |
transition: width 0.3s ease;
|
|
|
|
|
|
|
211 |
border-radius: 10px;
|
|
|
|
|
|
|
212 |
}
|
213 |
+
|
214 |
+
.results-section {
|
215 |
+
display: none;
|
|
|
|
|
|
|
216 |
}
|
217 |
+
|
218 |
+
.result-card {
|
219 |
+
background: var(--bg-primary);
|
220 |
+
border-radius: 20px;
|
221 |
+
box-shadow: var(--shadow-md);
|
222 |
+
margin-bottom: 1.5rem;
|
223 |
+
overflow: hidden;
|
224 |
+
transition: all 0.3s ease;
|
225 |
+
border: 1px solid var(--border-color);
|
226 |
}
|
227 |
+
|
228 |
+
.result-card:hover {
|
229 |
+
transform: translateY(-4px);
|
230 |
+
box-shadow: var(--shadow-xl);
|
231 |
+
}
|
232 |
+
|
233 |
+
.result-header {
|
234 |
+
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
235 |
+
color: white;
|
236 |
+
padding: 1.5rem 2rem;
|
237 |
+
font-weight: 600;
|
238 |
+
font-size: 1.25rem;
|
239 |
+
}
|
240 |
+
|
241 |
+
.result-body {
|
242 |
+
padding: 2rem;
|
243 |
+
}
|
244 |
+
|
245 |
+
.metric-grid {
|
246 |
+
display: grid;
|
247 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
248 |
+
gap: 1.5rem;
|
249 |
+
margin-bottom: 2rem;
|
250 |
+
}
|
251 |
+
|
252 |
+
.metric-card {
|
253 |
+
background: var(--bg-secondary);
|
254 |
+
border-radius: 16px;
|
255 |
+
padding: 1.5rem;
|
256 |
+
border-left: 4px solid var(--primary-color);
|
257 |
+
transition: all 0.3s ease;
|
258 |
}
|
259 |
+
|
260 |
+
.metric-card:hover {
|
261 |
+
transform: translateY(-2px);
|
262 |
+
box-shadow: var(--shadow-lg);
|
263 |
+
}
|
264 |
+
|
265 |
+
.metric-title {
|
266 |
+
font-size: 0.875rem;
|
267 |
+
font-weight: 600;
|
268 |
+
color: var(--text-secondary);
|
269 |
+
text-transform: uppercase;
|
270 |
+
letter-spacing: 0.05em;
|
271 |
+
margin-bottom: 0.5rem;
|
272 |
+
}
|
273 |
+
|
274 |
+
.metric-value {
|
275 |
+
font-size: 1.5rem;
|
276 |
+
font-weight: 700;
|
277 |
+
color: var(--text-primary);
|
278 |
+
margin-bottom: 0.25rem;
|
279 |
+
}
|
280 |
+
|
281 |
+
.metric-subtitle {
|
282 |
+
font-size: 0.875rem;
|
283 |
+
color: var(--text-secondary);
|
284 |
+
}
|
285 |
+
|
286 |
+
.score-badge {
|
287 |
+
display: inline-block;
|
288 |
+
padding: 0.5rem 1rem;
|
289 |
+
border-radius: 50px;
|
290 |
+
font-weight: 600;
|
291 |
+
font-size: 0.875rem;
|
292 |
+
text-transform: uppercase;
|
293 |
+
letter-spacing: 0.05em;
|
294 |
+
}
|
295 |
+
|
296 |
+
.score-excellent { background: var(--success-color); color: white; }
|
297 |
+
.score-good { background: var(--accent-color); color: white; }
|
298 |
+
.score-fair { background: var(--warning-color); color: white; }
|
299 |
+
.score-poor { background: var(--error-color); color: white; }
|
300 |
+
|
301 |
.confidence-bar {
|
302 |
+
width: 100%;
|
303 |
height: 6px;
|
304 |
+
background: var(--border-color);
|
305 |
border-radius: 3px;
|
306 |
overflow: hidden;
|
307 |
+
margin: 0.5rem 0;
|
308 |
}
|
309 |
+
|
310 |
.confidence-fill {
|
311 |
height: 100%;
|
312 |
+
background: linear-gradient(90deg, var(--success-color) 0%, var(--accent-color) 100%);
|
313 |
transition: width 0.3s ease;
|
314 |
+
border-radius: 3px;
|
315 |
}
|
316 |
+
|
317 |
+
.insight-list {
|
318 |
+
list-style: none;
|
319 |
+
padding: 0;
|
320 |
+
}
|
321 |
+
|
322 |
+
.insight-item {
|
323 |
+
background: var(--bg-secondary);
|
324 |
+
border-radius: 12px;
|
325 |
+
padding: 1rem;
|
326 |
+
margin-bottom: 0.75rem;
|
327 |
+
border-left: 4px solid var(--success-color);
|
328 |
+
transition: all 0.3s ease;
|
329 |
+
}
|
330 |
+
|
331 |
+
.insight-item:hover {
|
332 |
+
transform: translateX(4px);
|
333 |
+
}
|
334 |
+
|
335 |
+
.warning-item {
|
336 |
+
border-left-color: var(--warning-color);
|
337 |
+
}
|
338 |
+
|
339 |
+
.error-item {
|
340 |
+
border-left-color: var(--error-color);
|
341 |
+
}
|
342 |
+
|
343 |
+
.object-item {
|
344 |
+
background: var(--bg-secondary);
|
345 |
+
border-radius: 12px;
|
346 |
+
padding: 1rem;
|
347 |
+
margin-bottom: 0.5rem;
|
348 |
+
display: flex;
|
349 |
+
justify-content: space-between;
|
350 |
+
align-items: center;
|
351 |
+
transition: all 0.3s ease;
|
352 |
+
}
|
353 |
+
|
354 |
+
.object-item:hover {
|
355 |
+
background: var(--bg-primary);
|
356 |
+
box-shadow: var(--shadow-sm);
|
357 |
+
}
|
358 |
+
|
359 |
.scene-concept {
|
360 |
+
background: rgba(99, 102, 241, 0.1);
|
361 |
+
color: var(--primary-color);
|
362 |
border-radius: 8px;
|
363 |
+
padding: 0.5rem 0.75rem;
|
364 |
+
margin: 0.25rem;
|
365 |
display: inline-block;
|
366 |
+
font-size: 0.875rem;
|
367 |
+
font-weight: 500;
|
368 |
}
|
369 |
+
|
370 |
.market-tier-badge {
|
371 |
+
display: inline-block;
|
372 |
+
padding: 0.75rem 1.5rem;
|
373 |
+
border-radius: 50px;
|
374 |
+
font-weight: 700;
|
375 |
text-transform: uppercase;
|
376 |
+
letter-spacing: 0.1em;
|
377 |
+
font-size: 0.875rem;
|
378 |
+
}
|
379 |
+
|
380 |
+
.tier-luxury { background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%); color: #92400e; }
|
381 |
+
.tier-premium { background: linear-gradient(135deg, #9ca3af 0%, #6b7280 100%); color: white; }
|
382 |
+
.tier-standard { background: linear-gradient(135deg, #d97706 0%, #b45309 100%); color: white; }
|
383 |
+
.tier-value { background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%); color: white; }
|
384 |
+
|
385 |
+
.ai-model-badge {
|
386 |
+
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
387 |
+
color: white;
|
388 |
+
padding: 0.5rem 1rem;
|
389 |
+
border-radius: 50px;
|
390 |
+
font-size: 0.75rem;
|
391 |
+
font-weight: 500;
|
392 |
+
margin: 0.25rem;
|
393 |
+
display: inline-block;
|
394 |
+
}
|
395 |
+
|
396 |
+
.error-card {
|
397 |
+
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
|
398 |
+
border: 1px solid #fecaca;
|
399 |
+
border-radius: 16px;
|
400 |
+
padding: 1.5rem;
|
401 |
+
color: var(--error-color);
|
402 |
+
}
|
403 |
+
|
404 |
+
@media (max-width: 768px) {
|
405 |
+
.container-fluid {
|
406 |
+
padding: 1rem 0.5rem;
|
407 |
+
}
|
408 |
+
|
409 |
+
.header {
|
410 |
+
padding: 2rem 1rem;
|
411 |
+
}
|
412 |
+
|
413 |
+
.header h1 {
|
414 |
+
font-size: 2rem;
|
415 |
+
}
|
416 |
+
|
417 |
+
.content-area {
|
418 |
+
padding: 2rem 1rem;
|
419 |
+
}
|
420 |
+
|
421 |
+
.metric-grid {
|
422 |
+
grid-template-columns: 1fr;
|
423 |
+
}
|
424 |
+
}
|
425 |
+
|
426 |
+
.fade-in {
|
427 |
+
animation: fadeIn 0.6s ease-in-out;
|
428 |
+
}
|
429 |
+
|
430 |
+
@keyframes fadeIn {
|
431 |
+
from { opacity: 0; transform: translateY(20px); }
|
432 |
+
to { opacity: 1; transform: translateY(0); }
|
433 |
+
}
|
434 |
+
|
435 |
+
.slide-in {
|
436 |
+
animation: slideIn 0.8s ease-out;
|
437 |
+
}
|
438 |
+
|
439 |
+
@keyframes slideIn {
|
440 |
+
from { transform: translateX(-100%); opacity: 0; }
|
441 |
+
to { transform: translateX(0); opacity: 1; }
|
442 |
}
|
|
|
|
|
|
|
|
|
443 |
</style>
|
444 |
</head>
|
445 |
<body>
|
446 |
<div class="container-fluid">
|
447 |
<div class="main-container">
|
448 |
<div class="header">
|
449 |
+
<h1><i class="fas fa-robot"></i> AI Property Analyzer</h1>
|
450 |
+
<p>Advanced AI-powered real estate image analysis with comprehensive insights</p>
|
451 |
+
<div>
|
452 |
+
<span class="badge"><i class="fas fa-brain"></i> AI-Powered</span>
|
453 |
+
<span class="badge"><i class="fas fa-chart-line"></i> Market Analysis</span>
|
454 |
+
<span class="badge"><i class="fas fa-camera"></i> Quality Assessment</span>
|
455 |
+
</div>
|
456 |
</div>
|
457 |
|
458 |
+
<div class="content-area">
|
459 |
<div class="row">
|
460 |
+
<div class="col-lg-6">
|
461 |
+
<div class="upload-section" id="uploadArea">
|
462 |
+
<div class="upload-icon">
|
463 |
+
<i class="fas fa-cloud-upload-alt"></i>
|
464 |
+
</div>
|
465 |
+
<div class="upload-text">
|
466 |
+
<h4>Upload Property Image</h4>
|
467 |
+
<p>Drag and drop your image here or click to browse</p>
|
468 |
+
<input type="file" id="imageInput" accept="image/*" style="display: none;">
|
469 |
+
<button class="btn btn-primary" onclick="document.getElementById('imageInput').click()">
|
470 |
+
<i class="fas fa-upload"></i> Choose Image
|
471 |
+
</button>
|
472 |
+
</div>
|
473 |
</div>
|
474 |
|
475 |
+
<div id="imagePreview" class="image-preview" style="display: none;">
|
476 |
+
<img id="previewImg" class="img-fluid">
|
477 |
</div>
|
478 |
</div>
|
479 |
|
480 |
+
<div class="col-lg-6">
|
481 |
+
<div id="loading" class="loading-section" style="display: none;">
|
482 |
<div class="spinner"></div>
|
483 |
+
<h4>AI Analysis in Progress</h4>
|
484 |
<p>Multiple AI models are analyzing your image</p>
|
485 |
+
<div class="progress-container">
|
486 |
+
<div class="progress-bar" style="width: 0%"></div>
|
487 |
</div>
|
488 |
</div>
|
489 |
|
490 |
+
<div id="results" class="results-section">
|
491 |
<!-- Results will be populated here -->
|
492 |
</div>
|
493 |
</div>
|
|
|
496 |
</div>
|
497 |
</div>
|
498 |
|
499 |
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
500 |
<script>
|
501 |
const uploadArea = document.getElementById('uploadArea');
|
502 |
const imageInput = document.getElementById('imageInput');
|
|
|
536 |
|
537 |
function handleImageUpload(file) {
|
538 |
if (!file.type.startsWith('image/')) {
|
539 |
+
showNotification('Please select a valid image file.', 'error');
|
540 |
return;
|
541 |
}
|
542 |
|
543 |
+
// Show preview with animation
|
544 |
const reader = new FileReader();
|
545 |
reader.onload = (e) => {
|
546 |
previewImg.src = e.target.result;
|
547 |
imagePreview.style.display = 'block';
|
548 |
+
imagePreview.classList.add('fade-in');
|
549 |
};
|
550 |
reader.readAsDataURL(file);
|
551 |
|
|
|
553 |
analyzeImage(file);
|
554 |
}
|
555 |
|
556 |
+
function showNotification(message, type = 'info') {
|
557 |
+
const notification = document.createElement('div');
|
558 |
+
notification.className = `alert alert-${type === 'error' ? 'danger' : 'info'} alert-dismissible fade show position-fixed`;
|
559 |
+
notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
560 |
+
notification.innerHTML = `
|
561 |
+
${message}
|
562 |
+
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
563 |
+
`;
|
564 |
+
document.body.appendChild(notification);
|
565 |
+
|
566 |
+
setTimeout(() => {
|
567 |
+
notification.remove();
|
568 |
+
}, 5000);
|
569 |
+
}
|
570 |
+
|
571 |
|
572 |
|
573 |
function displayResults(data) {
|
|
|
575 |
|
576 |
if (data.error) {
|
577 |
results.innerHTML = `
|
578 |
+
<div class="error-card fade-in">
|
579 |
<h5><i class="fas fa-exclamation-triangle"></i> Analysis Error</h5>
|
580 |
<p>${data.error}</p>
|
581 |
</div>
|
|
|
597 |
|
598 |
results.innerHTML = `
|
599 |
<!-- Analysis Summary -->
|
600 |
+
<div class="result-card fade-in">
|
601 |
<div class="result-header">
|
602 |
<h4><i class="fas fa-chart-line"></i> Analysis Summary</h4>
|
603 |
</div>
|
604 |
<div class="result-body">
|
605 |
+
<div class="metric-grid">
|
606 |
+
<div class="metric-card">
|
607 |
+
<div class="metric-title"><i class="fas fa-home"></i> Room Type</div>
|
608 |
+
<div class="metric-value">${roomClass.room_type}</div>
|
609 |
+
<div class="confidence-bar">
|
610 |
+
<div class="confidence-fill" style="width: ${roomClass.confidence}%"></div>
|
|
|
|
|
|
|
611 |
</div>
|
612 |
+
<div class="metric-subtitle">Confidence: ${roomClass.confidence}%</div>
|
613 |
</div>
|
614 |
+
|
615 |
+
<div class="metric-card">
|
616 |
+
<div class="metric-title"><i class="fas fa-star"></i> Overall Score</div>
|
617 |
+
<div class="metric-value">
|
618 |
<span class="score-badge ${getScoreClass(assessment.overall_score)}">${assessment.overall_score}/100</span>
|
|
|
619 |
</div>
|
620 |
+
<div class="metric-subtitle">${assessment.professional_grade ? 'Professional Grade' : 'Standard Grade'}</div>
|
621 |
</div>
|
622 |
+
|
623 |
+
<div class="metric-card">
|
624 |
+
<div class="metric-title"><i class="fas fa-camera"></i> Quality Score</div>
|
625 |
+
<div class="metric-value">
|
|
|
|
|
626 |
<span class="score-badge ${getScoreClass(quality.quality_score)}">${quality.quality_score}/100</span>
|
|
|
627 |
</div>
|
628 |
+
<div class="metric-subtitle">${quality.quality_level}</div>
|
629 |
</div>
|
630 |
+
|
631 |
+
<div class="metric-card">
|
632 |
+
<div class="metric-title"><i class="fas fa-cube"></i> Objects Detected</div>
|
633 |
+
<div class="metric-value">${objects.objects.length}</div>
|
634 |
+
<div class="metric-subtitle">${objects.analysis.total_objects || 0} total objects</div>
|
635 |
+
</div>
|
636 |
+
|
637 |
+
<div class="metric-card">
|
638 |
+
<div class="metric-title"><i class="fas fa-ruler-combined"></i> Room Size</div>
|
639 |
+
<div class="metric-value">${roomSize.estimated_size}</div>
|
640 |
+
<div class="metric-subtitle">Confidence: ${roomSize.confidence}</div>
|
641 |
</div>
|
642 |
+
|
643 |
+
<div class="metric-card">
|
644 |
+
<div class="metric-title"><i class="fas fa-chart-bar"></i> Market Tier</div>
|
645 |
+
<div class="metric-value">
|
646 |
+
<span class="market-tier-badge tier-${market.market_tier.toLowerCase()}">${market.market_tier}</span>
|
647 |
</div>
|
648 |
+
<div class="metric-subtitle">${market.price_positioning}</div>
|
649 |
</div>
|
650 |
</div>
|
651 |
</div>
|
652 |
</div>
|
653 |
|
654 |
<!-- Market Analysis -->
|
655 |
+
<div class="result-card slide-in">
|
656 |
<div class="result-header">
|
657 |
<h4><i class="fas fa-chart-bar"></i> Market Analysis</h4>
|
658 |
</div>
|
659 |
<div class="result-body">
|
660 |
<div class="row">
|
661 |
<div class="col-md-6">
|
662 |
+
<h6 class="mb-3">Market Position</h6>
|
663 |
+
<p><strong>Target Market:</strong> ${market.target_market}</p>
|
|
|
664 |
<p><strong>Competitive Position:</strong> ${market.competitive_position}</p>
|
665 |
+
|
666 |
+
<h6 class="mb-3 mt-4">Competitive Advantages</h6>
|
667 |
+
<ul class="insight-list">
|
668 |
+
${(market.competitive_advantages || []).map(adv => `<li class="insight-item">${adv}</li>`).join('')}
|
669 |
+
</ul>
|
670 |
</div>
|
671 |
<div class="col-md-6">
|
672 |
+
<h6 class="mb-3">Differentiation Factors</h6>
|
673 |
+
<ul class="insight-list">
|
674 |
+
${(market.differentiation_factors || []).map(factor => `<li class="insight-item">${factor}</li>`).join('')}
|
675 |
</ul>
|
676 |
|
677 |
+
<h6 class="mb-3 mt-4">Market Opportunities</h6>
|
678 |
+
<ul class="insight-list">
|
679 |
+
${(market.market_opportunities || []).map(opp => `<li class="insight-item">${opp}</li>`).join('')}
|
680 |
</ul>
|
681 |
</div>
|
682 |
</div>
|
|
|
684 |
</div>
|
685 |
|
686 |
<!-- Property Insights -->
|
687 |
+
<div class="result-card slide-in">
|
688 |
<div class="result-header">
|
689 |
<h4><i class="fas fa-lightbulb"></i> Property Insights</h4>
|
690 |
</div>
|
691 |
<div class="result-body">
|
692 |
<div class="row">
|
693 |
<div class="col-md-6">
|
694 |
+
<h6 class="mb-3"><i class="fas fa-bullhorn"></i> Marketing Tips</h6>
|
695 |
+
<ul class="insight-list">
|
696 |
+
${(insights.marketing_tips || []).map(tip => `<li class="insight-item">${tip}</li>`).join('')}
|
697 |
</ul>
|
698 |
|
699 |
+
<h6 class="mb-3 mt-4"><i class="fas fa-users"></i> Target Audience</h6>
|
700 |
+
<ul class="insight-list">
|
701 |
+
${(insights.target_audience || []).map(audience => `<li class="insight-item">${audience}</li>`).join('')}
|
702 |
</ul>
|
703 |
</div>
|
704 |
<div class="col-md-6">
|
705 |
+
<h6 class="mb-3"><i class="fas fa-dollar-sign"></i> Pricing Considerations</h6>
|
706 |
+
<ul class="insight-list">
|
707 |
+
${(insights.pricing_considerations || []).map(consideration => `<li class="insight-item">${consideration}</li>`).join('')}
|
708 |
</ul>
|
709 |
|
710 |
+
<h6 class="mb-3 mt-4"><i class="fas fa-tools"></i> Improvement Suggestions</h6>
|
711 |
+
<ul class="insight-list">
|
712 |
+
${(insights.improvement_suggestions || []).map(suggestion => `<li class="insight-item warning-item">${suggestion}</li>`).join('')}
|
713 |
</ul>
|
714 |
</div>
|
715 |
</div>
|
|
|
717 |
</div>
|
718 |
|
719 |
<!-- Scene Analysis -->
|
720 |
+
<div class="result-card slide-in">
|
721 |
<div class="result-header">
|
722 |
<h4><i class="fas fa-palette"></i> Scene Analysis</h4>
|
723 |
</div>
|
724 |
<div class="result-body">
|
725 |
<div class="row">
|
726 |
<div class="col-md-6">
|
727 |
+
<h6 class="mb-3">Style Analysis</h6>
|
728 |
+
<p><strong>Dominant Style:</strong> ${scene.style_analysis.dominant_style}</p>
|
729 |
<p><strong>Style Confidence:</strong> ${(scene.style_analysis.style_confidence * 100).toFixed(1)}%</p>
|
730 |
|
731 |
+
<h6 class="mb-3 mt-4">Scene Concepts</h6>
|
732 |
<div>
|
733 |
${(scene.scene_concepts || []).map(concept =>
|
734 |
`<span class="scene-concept">${concept.concept} (${(concept.confidence * 100).toFixed(1)}%)</span>`
|
|
|
736 |
</div>
|
737 |
</div>
|
738 |
<div class="col-md-6">
|
739 |
+
<h6 class="mb-3">Quality Indicators</h6>
|
740 |
+
<ul class="insight-list">
|
741 |
${(scene.style_analysis.quality_indicators || []).map(indicator =>
|
742 |
+
`<li class="insight-item">${indicator.concept} (${(indicator.confidence * 100).toFixed(1)}%)</li>`
|
743 |
).join('')}
|
744 |
</ul>
|
745 |
|
746 |
+
<h6 class="mb-3 mt-4">Functionality Indicators</h6>
|
747 |
+
<ul class="insight-list">
|
748 |
${(scene.style_analysis.functionality_indicators || []).map(indicator =>
|
749 |
+
`<li class="insight-item">${indicator.concept} (${(indicator.confidence * 100).toFixed(1)}%)</li>`
|
750 |
).join('')}
|
751 |
</ul>
|
752 |
</div>
|
|
|
755 |
</div>
|
756 |
|
757 |
<!-- Detailed Analysis -->
|
758 |
+
<div class="result-card slide-in">
|
759 |
<div class="result-header">
|
760 |
<h4><i class="fas fa-microscope"></i> Detailed Analysis</h4>
|
761 |
</div>
|
762 |
<div class="result-body">
|
763 |
<div class="row">
|
764 |
<div class="col-md-6">
|
765 |
+
<h6 class="mb-3">Strengths</h6>
|
766 |
+
<ul class="insight-list">
|
767 |
+
${(assessment.strengths || []).map(strength => `<li class="insight-item">${strength}</li>`).join('')}
|
768 |
</ul>
|
769 |
|
770 |
+
<h6 class="mb-3 mt-4">Weaknesses</h6>
|
771 |
+
<ul class="insight-list">
|
772 |
+
${(assessment.weaknesses || []).map(weakness => `<li class="insight-item error-item">${weakness}</li>`).join('')}
|
773 |
</ul>
|
774 |
</div>
|
775 |
<div class="col-md-6">
|
776 |
+
<h6 class="mb-3">Recommendations</h6>
|
777 |
+
<ul class="insight-list">
|
778 |
+
${(assessment.recommendations || []).map(rec => `<li class="insight-item">${rec}</li>`).join('')}
|
|
|
|
|
|
|
|
|
|
|
779 |
</ul>
|
780 |
</div>
|
781 |
</div>
|
|
|
783 |
</div>
|
784 |
|
785 |
<!-- Object Detection Results -->
|
786 |
+
<div class="result-card slide-in">
|
787 |
<div class="result-header">
|
788 |
<h4><i class="fas fa-cube"></i> Object Detection</h4>
|
789 |
</div>
|
790 |
<div class="result-body">
|
791 |
<div class="row">
|
792 |
<div class="col-md-6">
|
793 |
+
<h6 class="mb-3">Detected Objects</h6>
|
794 |
${(objects.objects || []).map(obj => `
|
795 |
<div class="object-item">
|
796 |
<span><i class="fas fa-tag"></i> ${obj.label}</span>
|
|
|
802 |
`).join('')}
|
803 |
</div>
|
804 |
<div class="col-md-6">
|
805 |
+
<h6 class="mb-3">Object Categories</h6>
|
806 |
+
<div class="metric-grid">
|
807 |
+
<div class="metric-card">
|
808 |
+
<div class="metric-title">Furniture</div>
|
809 |
+
<div class="metric-value">${objects.analysis.object_categories?.furniture?.length || 0}</div>
|
810 |
+
<div class="metric-subtitle">items</div>
|
811 |
+
</div>
|
812 |
+
<div class="metric-card">
|
813 |
+
<div class="metric-title">Appliances</div>
|
814 |
+
<div class="metric-value">${objects.analysis.object_categories?.appliances?.length || 0}</div>
|
815 |
+
<div class="metric-subtitle">items</div>
|
816 |
+
</div>
|
817 |
+
<div class="metric-card">
|
818 |
+
<div class="metric-title">Fixtures</div>
|
819 |
+
<div class="metric-value">${objects.analysis.object_categories?.fixtures?.length || 0}</div>
|
820 |
+
<div class="metric-subtitle">items</div>
|
821 |
+
</div>
|
822 |
+
<div class="metric-card">
|
823 |
+
<div class="metric-title">Other</div>
|
824 |
+
<div class="metric-value">${objects.analysis.object_categories?.other?.length || 0}</div>
|
825 |
+
<div class="metric-subtitle">items</div>
|
826 |
+
</div>
|
827 |
+
</div>
|
828 |
|
829 |
+
<h6 class="mb-3 mt-4">Composition Analysis</h6>
|
830 |
<p><strong>Object Density:</strong> ${objects.analysis.object_density?.toFixed(2) || 0}</p>
|
831 |
<p><strong>Composition Balance:</strong> ${(objects.analysis.composition_balance * 100).toFixed(1)}%</p>
|
832 |
</div>
|
|
|
835 |
</div>
|
836 |
|
837 |
<!-- AI Models Used -->
|
838 |
+
<div class="result-card slide-in">
|
839 |
<div class="result-header">
|
840 |
<h4><i class="fas fa-robot"></i> AI Models Used</h4>
|
841 |
</div>
|
842 |
<div class="result-body">
|
843 |
+
<div class="text-center">
|
844 |
<span class="ai-model-badge">Room Classification</span>
|
845 |
<span class="ai-model-badge">BLIP Captioning</span>
|
846 |
<span class="ai-model-badge">DETR Detection</span>
|
|
|
848 |
<span class="ai-model-badge">CLIP Scene Understanding</span>
|
849 |
<span class="ai-model-badge">Market Analysis</span>
|
850 |
</div>
|
851 |
+
<p class="text-center mt-3 mb-0 text-muted">
|
852 |
+
<i class="fas fa-clock"></i> Analysis completed at ${new Date().toLocaleString()}
|
853 |
+
</p>
|
854 |
</div>
|
855 |
</div>
|
856 |
`;
|
|
|
869 |
|
870 |
// Simulate progress
|
871 |
let progress = 0;
|
872 |
+
const progressBar = document.querySelector('.progress-bar');
|
873 |
const progressInterval = setInterval(() => {
|
874 |
progress += Math.random() * 15;
|
875 |
if (progress > 90) progress = 90;
|
876 |
+
progressBar.style.width = progress + '%';
|
877 |
}, 500);
|
878 |
|
879 |
fetch('/analyze', {
|
|
|
888 |
})
|
889 |
.then(data => {
|
890 |
clearInterval(progressInterval);
|
891 |
+
progressBar.style.width = '100%';
|
892 |
|
893 |
setTimeout(() => {
|
894 |
loading.style.display = 'none';
|
|
|
900 |
loading.style.display = 'none';
|
901 |
console.error('Analysis failed:', error);
|
902 |
results.innerHTML = `
|
903 |
+
<div class="error-card fade-in">
|
904 |
<h5><i class="fas fa-exclamation-triangle"></i> Analysis Failed</h5>
|
905 |
<p>Error: ${error.message}</p>
|
906 |
<p>Please try again or check the server logs.</p>
|