Spaces:
Runtime error
Runtime error
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
<title>SkinAI - Results</title> | |
<style> | |
/* Reset */ | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: Arial, sans-serif; | |
background-color: #f5f8fa; | |
color: #000; | |
} | |
/* Top Navigation with Enhanced Hover Effects */ | |
header { | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
padding: 15px 5%; | |
background-color: #fff; | |
border-bottom: 1px solid #ccc; | |
position: relative; | |
z-index: 10; | |
} | |
.logo { | |
font-size: 1.5rem; | |
font-weight: bold; | |
color: #333; | |
} | |
nav a { | |
text-decoration: none; | |
color: #333; | |
margin-left: 20px; | |
font-weight: 500; | |
padding: 5px 0; | |
position: relative; | |
transition: color 0.3s ease; | |
} | |
/* Underline hover effect for nav links */ | |
nav a:after { | |
content: ''; | |
position: absolute; | |
width: 0; | |
height: 2px; | |
bottom: 0; | |
left: 0; | |
background-color: #007BFF; | |
transition: width 0.3s ease; | |
} | |
nav a:hover { | |
color: #007BFF; | |
text-decoration: none; | |
} | |
nav a:hover:after { | |
width: 100%; | |
} | |
.help-btn { | |
background-color: #007BFF; | |
color: #fff; | |
border: none; | |
padding: 8px 16px; | |
border-radius: 4px; | |
cursor: pointer; | |
margin-left: 20px; | |
transition: all 0.3s ease; | |
} | |
.help-btn:hover { | |
background-color: #0056b3; | |
transform: translateY(-2px); | |
box-shadow: 0 4px 8px rgba(0,123,255,0.3); | |
} | |
/* Main Content Area */ | |
.container { | |
max-width: 1200px; | |
margin: 40px auto; | |
padding: 0 20px; | |
} | |
/* Results Section */ | |
.results-section { | |
background-color: #fff; | |
border-radius: 10px; | |
box-shadow: 0 5px 15px rgba(0,0,0,0.08); | |
padding: 40px; | |
margin-bottom: 40px; | |
} | |
.results-header { | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
margin-bottom: 30px; | |
} | |
.results-header h1 { | |
font-size: 2rem; | |
color: #333; | |
} | |
.results-header .new-analysis { | |
background-color: #f8f9fa; | |
color: #333; | |
border: 1px solid #ddd; | |
padding: 8px 16px; | |
border-radius: 4px; | |
text-decoration: none; | |
transition: all 0.3s ease; | |
} | |
.results-header .new-analysis:hover { | |
background-color: #e9ecef; | |
transform: translateY(-2px); | |
} | |
/* Results Grid */ | |
.results-grid { | |
display: grid; | |
grid-template-columns: 1fr 2fr; | |
gap: 30px; | |
} | |
/* Image Section */ | |
.image-section { | |
background-color: #f8f9fa; | |
border-radius: 8px; | |
padding: 20px; | |
text-align: center; | |
} | |
.image-section img { | |
max-width: 100%; | |
max-height: 300px; | |
border-radius: 8px; | |
margin-bottom: 15px; | |
box-shadow: 0 4px 12px rgba(0,0,0,0.1); | |
} | |
.image-caption { | |
font-size: 0.9rem; | |
color: #666; | |
} | |
/* Analysis Section */ | |
.analysis-section { | |
display: flex; | |
flex-direction: column; | |
gap: 20px; | |
} | |
.analysis-card { | |
background-color: #f8f9fa; | |
border-radius: 8px; | |
padding: 20px; | |
border-left: 4px solid #007BFF; | |
} | |
.analysis-card h2 { | |
font-size: 1.4rem; | |
color: #333; | |
margin-bottom: 10px; | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
} | |
.confidence-badge { | |
background-color: #e6f4ff; | |
color: #0066cc; | |
font-size: 0.9rem; | |
padding: 4px 12px; | |
border-radius: 20px; | |
font-weight: normal; | |
} | |
.analysis-card p { | |
font-size: 1rem; | |
color: #555; | |
line-height: 1.6; | |
margin-bottom: 15px; | |
} | |
/* Recommendations List */ | |
.recommendations-list { | |
margin-top: 10px; | |
} | |
.recommendations-list h3 { | |
font-size: 1.1rem; | |
color: #333; | |
margin-bottom: 15px; | |
} | |
.recommendations-list ul { | |
padding-left: 20px; | |
} | |
.recommendations-list li { | |
margin-bottom: 10px; | |
line-height: 1.5; | |
font-size: 0.95rem; | |
color: #444; | |
} | |
/* Other Possible Conditions */ | |
.other-conditions { | |
margin-top: 20px; | |
} | |
.other-conditions h3 { | |
font-size: 1.1rem; | |
color: #333; | |
margin-bottom: 15px; | |
} | |
.condition-bars { | |
display: flex; | |
flex-direction: column; | |
gap: 10px; | |
} | |
.condition-bar { | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
} | |
.condition-name { | |
min-width: 100px; | |
font-size: 0.9rem; | |
} | |
.progress-bar { | |
flex-grow: 1; | |
height: 10px; | |
background-color: #e9ecef; | |
border-radius: 5px; | |
overflow: hidden; | |
} | |
.progress-fill { | |
height: 100%; | |
background-color: #007BFF; | |
} | |
.condition-percentage { | |
font-size: 0.9rem; | |
color: #666; | |
min-width: 50px; | |
text-align: right; | |
} | |
/* Q&A Section */ | |
.qa-section { | |
background-color: #fff; | |
border-radius: 10px; | |
box-shadow: 0 5px 15px rgba(0,0,0,0.08); | |
padding: 40px; | |
} | |
.qa-section h2 { | |
font-size: 1.6rem; | |
color: #333; | |
margin-bottom: 25px; | |
} | |
.qa-form { | |
display: flex; | |
gap: 10px; | |
margin-bottom: 30px; | |
} | |
.qa-input { | |
flex-grow: 1; | |
padding: 12px 15px; | |
border: 1px solid #ddd; | |
border-radius: 4px; | |
font-size: 1rem; | |
} | |
.qa-btn { | |
background-color: #007BFF; | |
color: #fff; | |
border: none; | |
padding: 0 20px; | |
border-radius: 4px; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
} | |
.qa-btn:hover { | |
background-color: #0056b3; | |
} | |
.qa-response { | |
background-color: #f8f9fa; | |
border-radius: 8px; | |
padding: 20px; | |
border-left: 4px solid #28a745; | |
margin-bottom: 20px; | |
display: none; /* Initially hidden */ | |
} | |
.qa-response h3 { | |
font-size: 1.1rem; | |
color: #333; | |
margin-bottom: 10px; | |
} | |
.qa-response p { | |
font-size: 0.95rem; | |
color: #555; | |
line-height: 1.6; | |
} | |
.disclaimer { | |
margin-top: 20px; | |
padding: 15px; | |
background-color: #fff8e1; | |
border-left: 4px solid #ffc107; | |
font-size: 0.9rem; | |
color: #6c4a00; | |
line-height: 1.5; | |
} | |
/* Footer */ | |
footer { | |
text-align: center; | |
padding: 20px; | |
margin-top: 40px; | |
color: #777; | |
font-size: 0.9rem; | |
} | |
/* Loader */ | |
.loader-container { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
margin: 20px 0; | |
} | |
.loader { | |
border: 4px solid #f3f3f3; | |
border-top: 4px solid #007BFF; | |
border-radius: 50%; | |
width: 30px; | |
height: 30px; | |
animation: spin 1s linear infinite; | |
margin-right: 10px; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
/* Responsive */ | |
@media (max-width: 768px) { | |
.container { | |
padding: 0 15px; | |
margin: 20px auto; | |
} | |
.results-section, .qa-section { | |
padding: 30px 15px; | |
} | |
.results-grid { | |
grid-template-columns: 1fr; | |
} | |
.results-header { | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 15px; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<!-- Top Navigation --> | |
<header> | |
<div class="logo">SkinAI</div> | |
<nav> | |
<a href="/">Home</a> | |
<a href="/upload">Upload</a> | |
<a href="/result" style="color: #007BFF;">Results</a> | |
<a href="#">Contact</a> | |
<button class="help-btn">Help</button> | |
</nav> | |
</header> | |
<div class="container"> | |
<!-- Results Section --> | |
<section class="results-section"> | |
<div class="results-header"> | |
<h1>Analysis Results</h1> | |
<a href="upload" class="new-analysis">Upload New Image</a> | |
</div> | |
<div class="results-grid"> | |
<!-- Image Section --> | |
<div class="image-section"> | |
<img id="analyzed-image" src="#" alt="Analyzed Skin Image"> | |
<div class="image-caption">Uploaded on <span id="upload-date">April 8, 2025</span></div> | |
</div> | |
<!-- Analysis Section --> | |
<div class="analysis-section"> | |
<div class="analysis-card"> | |
<h2> | |
<span id="condition-name">Loading result...</span> | |
<span class="confidence-badge" id="confidence-level">--</span> | |
</h2> | |
<p id="condition-description">Analyzing your image...</p> | |
<!-- Loader (initially shown) --> | |
<div class="loader-container" id="analysis-loader"> | |
<div class="loader"></div> | |
<span>Processing image with AI...</span> | |
</div> | |
<!-- Recommendations (initially hidden) --> | |
<div class="recommendations-list" id="recommendations-section" style="display: none;"> | |
<h3>Recommendations</h3> | |
<ul id="recommendations-list"> | |
<!-- Recommendations will be inserted here --> | |
</ul> | |
</div> | |
<!-- Other Possible Conditions --> | |
<div class="other-conditions" id="other-conditions-section" style="display: none;"> | |
<h3>Other Possible Conditions</h3> | |
<div class="condition-bars" id="condition-bars"> | |
<!-- Condition bars will be inserted here --> | |
</div> | |
</div> | |
</div> | |
<div class="disclaimer"> | |
<strong>Disclaimer:</strong> This analysis is for informational purposes only and should not replace professional medical advice. Please consult a healthcare provider for proper diagnosis and treatment. | |
</div> | |
</div> | |
</div> | |
</section> | |
<!-- Q&A Section --> | |
<section class="qa-section"> | |
<h2>Ask About Your Condition</h2> | |
<div class="qa-form"> | |
<input type="text" class="qa-input" id="question-input" placeholder="Ask a question about your skin condition..."> | |
<button class="qa-btn" id="ask-btn">Ask</button> | |
</div> | |
<!-- QA Response (initially hidden) --> | |
<div class="qa-response" id="qa-response"> | |
<h3>Response</h3> | |
<p id="qa-answer">The answer will appear here.</p> | |
</div> | |
<div class="disclaimer"> | |
<strong>Important:</strong> The AI assistant provides general information about skin conditions. Your specific case may vary, and medical professionals should be consulted for personalized advice and treatment. | |
</div> | |
</section> | |
</div> | |
<footer> | |
<p>© 2025 SkinAI. All rights reserved. For educational purposes only. Not a substitute for professional medical advice.</p> | |
</footer> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
// Retrieve data from sessionStorage (set during upload) | |
const imageData = sessionStorage.getItem('analyzedImage'); | |
const analysisResult = JSON.parse(sessionStorage.getItem('analysisResult') || '{}'); | |
const condition = sessionStorage.getItem('detectedCondition') || ''; | |
// Set the image | |
if (imageData) { | |
document.getElementById('analyzed-image').src = imageData; | |
} else { | |
document.getElementById('analyzed-image').src = "/api/placeholder/400/320"; | |
document.getElementById('analyzed-image').alt = "No image available"; | |
} | |
// Set the current date | |
const now = new Date(); | |
document.getElementById('upload-date').textContent = now.toLocaleDateString('en-US', { | |
year: 'numeric', | |
month: 'long', | |
day: 'numeric' | |
}); | |
// Process and display results if available | |
if (Object.keys(analysisResult).length > 0) { | |
displayResults(analysisResult); | |
} else { | |
// If no results in session storage, check URL params | |
// This allows direct navigation to this page with the results as parameters | |
const urlParams = new URLSearchParams(window.location.search); | |
const resultParam = urlParams.get('result'); | |
if (resultParam) { | |
try { | |
const decodedResult = JSON.parse(decodeURIComponent(resultParam)); | |
displayResults(decodedResult); | |
} catch (e) { | |
console.error("Failed to parse result from URL parameter", e); | |
document.getElementById('condition-name').textContent = "No results available"; | |
document.getElementById('condition-description').textContent = "Please upload an image for analysis."; | |
document.getElementById('analysis-loader').style.display = "none"; | |
} | |
} else { | |
// No results available - show message | |
document.getElementById('condition-name').textContent = "No results available"; | |
document.getElementById('condition-description').textContent = "Please upload an image for analysis."; | |
document.getElementById('analysis-loader').style.display = "none"; | |
} | |
} | |
// Handle Q&A form submission | |
document.getElementById('ask-btn').addEventListener('click', function() { | |
askQuestion(); | |
}); | |
document.getElementById('question-input').addEventListener('keypress', function(e) { | |
if (e.key === 'Enter') { | |
askQuestion(); | |
} | |
}); | |
// Function to display analysis results | |
function displayResults(result) { | |
// Hide loader | |
document.getElementById('analysis-loader').style.display = "none"; | |
// Display primary condition and confidence | |
document.getElementById('condition-name').textContent = result.prediction || "Unknown Condition"; | |
const confidencePercent = result.confidence ? Math.round(result.confidence * 100) : 0; | |
document.getElementById('confidence-level').textContent = `${confidencePercent}% Confidence`; | |
// Display description | |
document.getElementById('condition-description').textContent = result.description || | |
"No detailed information available for this condition."; | |
// Display recommendations | |
if (result.recommendations && result.recommendations.length > 0) { | |
const recommendationsList = document.getElementById('recommendations-list'); | |
recommendationsList.innerHTML = ''; | |
result.recommendations.forEach(rec => { | |
const li = document.createElement('li'); | |
li.textContent = rec; | |
recommendationsList.appendChild(li); | |
}); | |
document.getElementById('recommendations-section').style.display = "block"; | |
} | |
// Display other possible conditions | |
if (result.topConditions && result.topConditions.length > 0) { | |
const conditionBars = document.getElementById('condition-bars'); | |
conditionBars.innerHTML = ''; | |
// Skip the first condition (already shown as primary) if it's the same as the top result | |
const startIndex = (result.topConditions[0][0] === result.prediction) ? 1 : 0; | |
// Display up to 4 additional conditions | |
for (let i = startIndex; i < Math.min(startIndex + 4, result.topConditions.length); i++) { | |
const condition = result.topConditions[i]; | |
const conditionName = condition[0]; | |
const probability = condition[1]; | |
const percentage = Math.round(probability * 100); | |
const conditionBar = document.createElement('div'); | |
conditionBar.className = 'condition-bar'; | |
conditionBar.innerHTML = ` | |
<div class="condition-name">${conditionName}</div> | |
<div class="progress-bar"> | |
<div class="progress-fill" style="width: ${percentage}%"></div> | |
</div> | |
<div class="condition-percentage">${percentage}%</div> | |
`; | |
conditionBars.appendChild(conditionBar); | |
} | |
document.getElementById('other-conditions-section').style.display = "block"; | |
} | |
// Store detected condition for Q&A | |
sessionStorage.setItem('detectedCondition', result.prediction || ''); | |
} | |
// Function to handle Q&A | |
function askQuestion() { | |
const questionInput = document.getElementById('question-input'); | |
const question = questionInput.value.trim(); | |
if (!question) return; | |
// Get the detected condition | |
const detectedCondition = sessionStorage.getItem('detectedCondition') || ''; | |
// Show loading indication | |
document.getElementById('qa-answer').textContent = "Getting answer..."; | |
document.getElementById('qa-response').style.display = "block"; | |
// Call the backend API | |
fetch('/ask', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
question: question, | |
condition: detectedCondition | |
}) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
document.getElementById('qa-answer').textContent = data.answer || | |
"I couldn't generate an answer for that question."; | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
document.getElementById('qa-answer').textContent = | |
"Sorry, there was an error processing your question. Please try again later."; | |
}); | |
// Clear the input | |
questionInput.value = ''; | |
} | |
}); | |
</script> | |
</body> | |
</html> |