| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
| <title>Medical Symptom Checker</title> |
| <style> |
| body { |
| font-family: 'Segoe UI', sans-serif; |
| background: linear-gradient(135deg, #fdfbfb, #ebedee); |
| margin: 0; |
| padding: 40px 20px; |
| color: #333; |
| } |
| |
| h1 { |
| text-align: center; |
| font-size: 2.5rem; |
| margin-bottom: 30px; |
| background: linear-gradient(to right, #3b82f6, #9333ea); |
| -webkit-background-clip: text; |
| background-clip: text; |
| -webkit-text-fill-color: transparent; |
| } |
| |
| .search-container { |
| text-align: center; |
| margin-bottom: 30px; |
| } |
| |
| input[type="text"] { |
| padding: 12px 20px; |
| width: 60%; |
| max-width: 500px; |
| border-radius: 30px; |
| border: 1px solid #ccc; |
| box-shadow: 0 4px 10px rgba(0,0,0,0.05); |
| transition: all 0.3s ease; |
| } |
| |
| input[type="text"]:focus { |
| outline: none; |
| border-color: #6366f1; |
| box-shadow: 0 4px 15px rgba(99,102,241,0.2); |
| } |
| |
| button { |
| padding: 12px 25px; |
| margin-left: 10px; |
| border: none; |
| border-radius: 30px; |
| background: linear-gradient(to right, #3b82f6, #9333ea); |
| color: white; |
| cursor: pointer; |
| font-weight: bold; |
| box-shadow: 0 4px 10px rgba(0,0,0,0.1); |
| transition: background 0.3s ease; |
| } |
| |
| button:hover { |
| background: linear-gradient(to right, #2563eb, #7e22ce); |
| } |
| |
| .selected-symptoms { |
| display: flex; |
| flex-wrap: wrap; |
| justify-content: center; |
| gap: 10px; |
| margin-bottom: 30px; |
| } |
| |
| .symptom-tag { |
| background: #e0e7ff; |
| color: #1e40af; |
| padding: 8px 15px; |
| border-radius: 20px; |
| display: flex; |
| align-items: center; |
| animation: fadeIn 0.3s ease; |
| } |
| |
| .remove-symptom { |
| margin-left: 10px; |
| cursor: pointer; |
| color: #555; |
| font-weight: bold; |
| } |
| |
| .container { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 20px; |
| justify-content: center; |
| } |
| |
| .panel { |
| flex: 1 1 300px; |
| background: white; |
| border-radius: 15px; |
| padding: 20px; |
| box-shadow: 0 10px 20px rgba(0,0,0,0.05); |
| transition: transform 0.2s ease; |
| animation: fadeIn 0.5s ease; |
| } |
| |
| .panel:hover { |
| transform: translateY(-5px); |
| } |
| |
| .panel h2 { |
| margin-top: 0; |
| background: linear-gradient(to right, #2563eb, #9333ea); |
| -webkit-background-clip: text; |
| background-clip: text; |
| -webkit-text-fill-color: transparent; |
| font-size: 1.5rem; |
| } |
| |
| ul { |
| list-style: none; |
| padding: 0; |
| } |
| |
| li { |
| padding: 10px 0; |
| border-bottom: 1px solid #eee; |
| transition: background 0.2s ease; |
| } |
| |
| .clickable { |
| color: #6366f1; |
| cursor: pointer; |
| } |
| |
| .clickable:hover { |
| text-decoration: underline; |
| } |
| |
| #error-notification { |
| background-color: #fee2e2; |
| color: #b91c1c; |
| padding: 12px 20px; |
| border-radius: 8px; |
| margin: 0 auto 20px auto; |
| max-width: 600px; |
| text-align: center; |
| display: none; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| @media screen and (max-width: 768px) { |
| input[type="text"] { |
| width: 80%; |
| } |
| .container { |
| flex-direction: column; |
| align-items: stretch; |
| } |
| } |
| |
| |
| .modal { |
| display: none; |
| position: fixed; |
| z-index: 1; |
| left: 0; |
| top: 0; |
| width: 100%; |
| height: 100%; |
| overflow: auto; |
| background-color: rgba(0,0,0,0.4); |
| } |
| |
| .modal-content { |
| background-color: #fefefe; |
| margin: 15% auto; |
| padding: 20px; |
| border: 1px solid #888; |
| width: 70%; |
| border-radius: 8px; |
| max-height: 70vh; |
| overflow-y: auto; |
| } |
| |
| .close { |
| color: #aaa; |
| float: right; |
| font-size: 28px; |
| font-weight: bold; |
| cursor: pointer; |
| } |
| |
| .close:hover, |
| .close:focus { |
| color: black; |
| text-decoration: none; |
| } |
| |
| .disease-info-title { |
| margin-top: 0; |
| color: #4285f4; |
| } |
| |
| .loading { |
| text-align: center; |
| padding: 20px; |
| } |
| |
| .disease-clickable { |
| cursor: pointer; |
| color: #333; |
| font-weight: bold; |
| } |
| |
| .disease-clickable:hover { |
| color: #4285f4; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>Medical Symptom Checker</h1> |
|
|
| <div class="search-container"> |
| <input type="text" id="symptom-search" placeholder="Enter a symptom..."/> |
| <button id="search-button">Search</button> |
| </div> |
|
|
| <div id="error-notification"></div> |
|
|
| <div class="selected-symptoms" id="selected-symptoms"> |
| |
| </div> |
|
|
| <div class="container"> |
| <div class="panel"> |
| <h2>Matching Diseases</h2> |
| <ul id="diseases-list"></ul> |
| </div> |
| <div class="panel"> |
| <h2>Similar Symptoms</h2> |
| <ul id="similar-list"></ul> |
| </div> |
| <div class="panel"> |
| <h2>Related Symptoms</h2> |
| <ul id="related-list"></ul> |
| </div> |
| </div> |
|
|
| |
| <div id="diseaseModal" class="modal"> |
| <div class="modal-content"> |
| <span class="close">×</span> |
| <h3 id="disease-info-title" class="disease-info-title">Disease Information</h3> |
| <div id="disease-info-content"> |
| <div id="loading" class="loading">Loading information...</div> |
| <div id="disease-details"></div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let selectedSymptoms = []; |
| |
| |
| const searchInput = document.getElementById('symptom-search'); |
| const searchButton = document.getElementById('search-button'); |
| const selectedSymptomsDiv = document.getElementById('selected-symptoms'); |
| const diseasesList = document.getElementById('diseases-list'); |
| const similarList = document.getElementById('similar-list'); |
| const relatedList = document.getElementById('related-list'); |
| const errorElement = document.getElementById('error-notification'); |
| const modal = document.getElementById('diseaseModal'); |
| const closeModal = document.getElementsByClassName('close')[0]; |
| const diseaseTitle = document.getElementById('disease-info-title'); |
| const diseaseDetails = document.getElementById('disease-details'); |
| const loadingElement = document.getElementById('loading'); |
| |
| |
| function showError(message) { |
| errorElement.textContent = message; |
| errorElement.style.display = 'block'; |
| setTimeout(() => { |
| errorElement.style.display = 'none'; |
| }, 5000); |
| } |
| |
| |
| async function performSearch(inputSymptom) { |
| |
| errorElement.style.display = 'none'; |
| |
| try { |
| |
| let symptomParam = inputSymptom; |
| if (!symptomParam && selectedSymptoms.length > 0) { |
| symptomParam = selectedSymptoms[0]; |
| } |
| |
| if (!symptomParam) return; |
| |
| let url = `${window.location.origin}/search?symptom=${encodeURIComponent(symptomParam)}`; |
| |
| |
| if (selectedSymptoms.length > 0) { |
| selectedSymptoms.forEach((s, index) => { |
| |
| if (index === 0 && s === symptomParam) return; |
| url += `&selected=${encodeURIComponent(s)}`; |
| }); |
| } |
| |
| const response = await fetch(url); |
| |
| if (!response.ok) { |
| throw new Error(`HTTP error! status: ${response.status}`); |
| } |
| |
| const data = await response.json(); |
| |
| displayResults(data); |
| |
| |
| if (inputSymptom) { |
| searchInput.value = ''; |
| } |
| } catch (error) { |
| showError(`Error searching for symptom: ${error.message}. Please try again.`); |
| console.error('Error fetching data:', error); |
| } |
| } |
| |
| |
| async function searchSymptoms() { |
| const symptom = searchInput.value.trim(); |
| if (!symptom) return; |
| performSearch(symptom); |
| } |
| |
| |
| function displayResults(data) { |
| |
| diseasesList.innerHTML = ''; |
| if (!data.matching_diseases || data.matching_diseases.length === 0) { |
| diseasesList.innerHTML = '<li>No matching diseases found</li>'; |
| } else { |
| data.matching_diseases.forEach(disease => { |
| diseasesList.innerHTML += `<li class="disease-clickable" onclick="showDiseaseInfo('${escapeJS(disease)}')">${escapeHTML(disease)}</li>`; |
| }); |
| } |
| |
| |
| similarList.innerHTML = ''; |
| if (data.semantic_matches && data.semantic_matches.length > 0) { |
| data.semantic_matches.forEach(symptom => { |
| if (!selectedSymptoms.includes(symptom)) { |
| similarList.innerHTML += `<li class="clickable" onclick="addSymptom('${escapeJS(symptom)}')">${escapeHTML(symptom)}</li>`; |
| } |
| }); |
| } else { |
| similarList.innerHTML = '<li>No similar symptoms found</li>'; |
| } |
| |
| |
| relatedList.innerHTML = ''; |
| if (!data.related_symptoms || data.related_symptoms.length === 0) { |
| relatedList.innerHTML = '<li>No related symptoms found</li>'; |
| } else { |
| |
| const sortedSymptoms = [...data.related_symptoms].sort((a, b) => a.localeCompare(b)); |
| |
| sortedSymptoms.forEach(symptom => { |
| if (!selectedSymptoms.includes(symptom)) { |
| relatedList.innerHTML += `<li class="clickable" onclick="addSymptom('${escapeJS(symptom)}')">${escapeHTML(symptom)}</li>`; |
| } |
| }); |
| } |
| |
| |
| updateSelectedSymptoms(); |
| } |
| |
| |
| function escapeHTML(str) { |
| return str |
| .replace(/&/g, '&') |
| .replace(/</g, '<') |
| .replace(/>/g, '>') |
| .replace(/"/g, '"') |
| .replace(/'/g, '''); |
| } |
| |
| |
| function escapeJS(str) { |
| return str |
| .replace(/\\/g, '\\\\') |
| .replace(/'/g, "\\'") |
| .replace(/"/g, '\\"') |
| .replace(/\n/g, '\\n') |
| .replace(/\r/g, '\\r') |
| .replace(/\t/g, '\\t'); |
| } |
| |
| |
| function addSymptom(symptom) { |
| if (!selectedSymptoms.includes(symptom)) { |
| selectedSymptoms.push(symptom); |
| updateSelectedSymptoms(); |
| |
| |
| |
| performSearch(null); |
| } |
| } |
| |
| |
| function removeSymptom(symptom) { |
| selectedSymptoms = selectedSymptoms.filter(s => s !== symptom); |
| updateSelectedSymptoms(); |
| if (selectedSymptoms.length > 0) { |
| performSearch(null); |
| } else { |
| |
| diseasesList.innerHTML = '<li>Enter a symptom to get started</li>'; |
| similarList.innerHTML = ''; |
| relatedList.innerHTML = ''; |
| } |
| } |
| |
| |
| function updateSelectedSymptoms() { |
| selectedSymptomsDiv.innerHTML = ''; |
| selectedSymptoms.forEach(symptom => { |
| const tag = document.createElement('div'); |
| tag.className = 'symptom-tag'; |
| tag.innerHTML = ` |
| ${escapeHTML(symptom)} |
| <span class="remove-symptom" onclick="removeSymptom('${escapeJS(symptom)}')">×</span> |
| `; |
| selectedSymptomsDiv.appendChild(tag); |
| }); |
| } |
| |
| |
| async function showDiseaseInfo(disease) { |
| |
| modal.style.display = "block"; |
| diseaseTitle.textContent = disease; |
| diseaseDetails.innerHTML = ''; |
| loadingElement.style.display = 'block'; |
| |
| try { |
| |
| const response = await fetch(`${window.location.origin}/disease-info?name=${encodeURIComponent(disease)}`); |
| |
| if (!response.ok) { |
| throw new Error(`HTTP error! status: ${response.status}`); |
| } |
| |
| const data = await response.json(); |
| loadingElement.style.display = 'none'; |
| |
| |
| diseaseDetails.innerHTML = data.info; |
| } catch (error) { |
| loadingElement.style.display = 'none'; |
| diseaseDetails.innerHTML = `<p class="error">Error getting information: ${error.message}</p>`; |
| console.error('Error fetching disease info:', error); |
| } |
| } |
| |
| |
| closeModal.onclick = function() { |
| modal.style.display = "none"; |
| } |
| |
| |
| window.onclick = function(event) { |
| if (event.target == modal) { |
| modal.style.display = "none"; |
| } |
| } |
| |
| |
| searchButton.addEventListener('click', searchSymptoms); |
| searchInput.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') { |
| searchSymptoms(); |
| } |
| }); |
| |
| |
| diseasesList.innerHTML = '<li>Enter a symptom to get started</li>'; |
| </script> |
| </body> |
| </html> |
|
|