app / templates /index.html
leojoseph27
Initial commit: Health Monitoring System
d114840
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Heart Health Analysis System</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
:root[data-theme="dark"] {
--primary-color: #f03e5f;
--secondary-color: #f5768d;
--accent-color: #d1062a;
--text-color: #ffffff;
--text-light: #fccad3;
--background-start: #1a1a2e;
--background-end: #16213e;
--card-color: rgba(26, 26, 46, 0.8);
--border-radius: 12px;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
--transition: all 0.3s ease;
--placeholder-color: rgba(255, 255, 255, 0.7);
}
:root[data-theme="light"] {
--primary-color: #f03e5f;
--secondary-color: #f5768d;
--accent-color: #d1062a;
--text-color: #333333;
--text-light: #8a0018;
--background-start: #f5f5f5;
--background-end: #ffffff;
--card-color: rgba(255, 255, 255, 0.9);
--border-radius: 12px;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
--placeholder-color: rgba(138, 0, 24, 0.5);
}
body {
background: linear-gradient(135deg,
rgba(26, 26, 46, 0.95) 0%,
rgba(22, 33, 62, 0.95) 50%,
rgba(209, 6, 42, 0.2) 100%
);
color: var(--text-color);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
min-height: 100vh;
height: 100vh;
position: relative;
overflow: hidden;
width: 100%;
max-width: 100vw;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(
circle at top left,
rgba(114, 9, 183, 0.2) 0%,
transparent 50%
),
radial-gradient(
circle at top right,
rgba(76, 201, 240, 0.1) 0%,
transparent 50%
);
z-index: -1;
}
.container {
background-color: var(--card-color);
border-radius: var(--border-radius);
padding: 30px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
max-width: 100%;
max-height: calc(100vh - 40px);
overflow-y: auto;
overflow-x: hidden;
}
.header {
text-align: center;
margin-bottom: 40px;
animation: fadeInDown 1s ease;
padding: 20px;
}
.header h1 {
background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 700;
margin-bottom: 10px;
}
.header p {
color: var(--text-light);
font-size: 1.1rem;
}
.nav-tabs {
border: none;
margin-bottom: 30px;
background: rgba(26, 26, 46, 0.6);
padding: 10px;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.nav-tabs .nav-link {
border: none;
color: var(--text-light);
padding: 12px 24px;
border-radius: var(--border-radius);
transition: var(--transition);
font-weight: 500;
}
.nav-tabs .nav-link:hover {
background: linear-gradient(135deg, rgba(255, 77, 109, 0.2), rgba(114, 9, 183, 0.1));
color: var(--primary-color);
}
.nav-tabs .nav-link.active {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
color: white;
box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
}
.tab-content {
animation: fadeIn 0.5s ease;
}
.ecg-plot {
max-width: 100%;
width: 100%;
height: auto;
margin-top: 20px;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
animation: fadeIn 1s ease;
display: none;
}
#ecgPlotContainer {
width: 100%;
margin-top: 20px;
overflow: visible;
}
.analysis-section {
background-color: rgba(26, 26, 46, 0.6);
border-radius: var(--border-radius);
padding: 30px;
margin-bottom: 30px;
box-shadow: var(--box-shadow);
transition: var(--transition);
animation: fadeInUp 0.8s ease;
border: 1px solid rgba(255, 255, 255, 0.1);
max-width: 100%;
overflow: visible;
}
.analysis-section:hover {
transform: translateY(-5px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.form-control {
border-radius: var(--border-radius);
padding: 12px;
border: 2px solid rgba(255, 255, 255, 0.1);
transition: var(--transition);
background-color: rgba(26, 26, 46, 0.8);
color: var(--text-color);
}
.form-control:focus {
background-color: rgba(26, 26, 46, 0.9);
border-color: var(--primary-color);
color: var(--text-color);
box-shadow: 0 0 0 0.2rem rgba(255, 77, 109, 0.25);
}
.form-control::placeholder {
color: var(--placeholder-color);
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
border: none;
padding: 12px 24px;
border-radius: var(--border-radius);
font-weight: 500;
transition: var(--transition);
box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
}
.btn-primary:hover {
background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
}
.result-box {
border: none;
border-radius: var(--border-radius);
padding: 20px;
margin-top: 20px;
background: linear-gradient(135deg,
rgba(76, 201, 240, 0.1),
rgba(114, 9, 183, 0.05)
);
animation: fadeIn 0.8s ease;
border: 1px solid rgba(76, 201, 240, 0.2);
}
.result-box.anomaly {
background: linear-gradient(135deg,
rgba(240, 62, 95, 0.1),
rgba(209, 6, 42, 0.05)
);
border: 1px solid rgba(240, 62, 95, 0.2);
}
.result-box.normal {
background: linear-gradient(135deg,
rgba(76, 201, 240, 0.1),
rgba(76, 201, 240, 0.05)
);
border: 1px solid rgba(76, 201, 240, 0.2);
}
.progress {
height: 25px;
border-radius: var(--border-radius);
background-color: rgba(0, 0, 0, 0.3);
margin: 15px 0;
overflow: hidden;
}
.progress-bar {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
transition: width 1s ease;
}
.input-guidelines {
font-size: 0.85rem;
color: var(--text-light);
margin-top: 0.5rem;
padding-left: 5px;
}
.form-label {
font-weight: 600;
color: var(--text-color);
margin-bottom: 0.5rem;
}
.loading {
display: none;
text-align: center;
margin: 20px 0;
}
.loading i {
color: var(--primary-color);
font-size: 2rem;
animation: spin 1s linear infinite;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.alert {
border-radius: var(--border-radius);
padding: 15px;
margin-top: 20px;
animation: fadeIn 0.5s ease;
}
/* Custom scrollbar for dark theme */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
}
::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
}
/* Custom select dropdown styles */
select.form-control {
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%23ffffff' viewBox='0 0 16 16'%3E%3Cpath d='M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 35px;
}
select.form-control option {
background-color: rgba(26, 26, 46, 0.95);
color: var(--text-color);
padding: 10px;
}
/* Custom file input styles */
input[type="file"].form-control {
padding: 8px;
cursor: pointer;
}
input[type="file"].form-control::-webkit-file-upload-button {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
color: white;
border: none;
padding: 8px 16px;
border-radius: var(--border-radius);
margin-right: 10px;
cursor: pointer;
transition: var(--transition);
}
input[type="file"].form-control::-webkit-file-upload-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
}
/* Firefox specific styles */
input[type="file"].form-control::file-selector-button {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
color: white;
border: none;
padding: 8px 16px;
border-radius: var(--border-radius);
margin-right: 10px;
cursor: pointer;
transition: var(--transition);
}
input[type="file"].form-control::file-selector-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
}
/* Dropdown menu styles */
.dropdown-menu {
background-color: rgba(26, 26, 46, 0.95);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
}
.dropdown-item {
color: var(--text-color);
padding: 10px 20px;
}
.dropdown-item:hover {
background-color: rgba(255, 77, 109, 0.2);
color: var(--primary-color);
}
.dropdown-item.active {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
color: white;
}
.emergency-button {
position: fixed;
bottom: 30px;
right: 30px;
background: linear-gradient(135deg, var(--accent-color), #8a0018);
color: white;
border: none;
padding: 15px 30px;
border-radius: 50px;
font-size: 1.2rem;
font-weight: bold;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 4px 15px rgba(255, 0, 0, 0.3);
z-index: 1000;
display: flex;
align-items: center;
gap: 10px;
}
.emergency-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(255, 0, 0, 0.4);
}
.emergency-button i {
font-size: 1.4rem;
}
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
background: var(--card-color);
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--text-color);
padding: 10px 20px;
border-radius: var(--border-radius);
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: var(--transition);
z-index: 1000;
}
.theme-toggle:hover {
transform: translateY(-2px);
box-shadow: var(--box-shadow);
}
.theme-toggle i {
font-size: 1.2rem;
}
[data-theme="light"] .container {
background-color: var(--card-color);
border: 1px solid rgba(0, 0, 0, 0.1);
}
[data-theme="light"] .form-control {
background-color: rgba(255, 255, 255, 0.9);
border: 2px solid rgba(0, 0, 0, 0.1);
color: var(--text-color);
}
[data-theme="light"] .form-control:focus {
background-color: #ffffff;
border-color: var(--primary-color);
color: var(--text-color);
}
[data-theme="light"] .nav-tabs {
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 0, 0, 0.1);
}
[data-theme="light"] .analysis-section {
background-color: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 0, 0, 0.1);
}
[data-theme="light"] .result-box {
background: linear-gradient(135deg,
rgba(76, 201, 240, 0.1),
rgba(114, 9, 183, 0.05)
);
border: 1px solid rgba(76, 201, 240, 0.2);
}
[data-theme="light"] .result-box.anomaly {
background: linear-gradient(135deg,
rgba(240, 62, 95, 0.15),
rgba(209, 6, 42, 0.1)
);
border: 1px solid rgba(240, 62, 95, 0.3);
}
[data-theme="light"] .result-box.normal {
background: linear-gradient(135deg,
rgba(76, 201, 240, 0.15),
rgba(76, 201, 240, 0.1)
);
border: 1px solid rgba(76, 201, 240, 0.3);
}
/* AI Doctor specific styles */
#ai-doctor .analysis-section {
padding: 0;
background: transparent;
box-shadow: none;
height: calc(100vh - 100px);
display: flex;
flex-direction: column;
position: relative;
margin-bottom: 0;
}
#ai-doctor .header {
margin-bottom: 0;
padding: 15px 20px;
background: rgba(26, 26, 46, 0.9);
border-radius: var(--border-radius) var(--border-radius) 0 0;
border: 1px solid rgba(255, 255, 255, 0.1);
border-bottom: none;
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
z-index: 2;
text-align: center;
}
#ai-doctor .header h3 {
margin: 0;
color: var(--primary-color);
font-size: 1.2rem;
}
#ai-doctor .header p {
margin: 0;
opacity: 0.8;
font-size: 0.9rem;
}
#ai-doctor .header i {
font-size: 1.5rem;
color: var(--primary-color);
}
.chat-container {
display: flex;
flex-direction: column;
flex: 1;
background: rgba(26, 26, 46, 0.6);
border-radius: 0 0 var(--border-radius) var(--border-radius);
overflow: hidden;
position: relative;
border: 1px solid rgba(252, 202, 211, 0.1);
border-top: none;
margin: 0;
height: calc(100% - 60px);
}
.chat-messages {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 20px;
padding-bottom: 80px;
display: flex;
flex-direction: column;
gap: 15px;
background: linear-gradient(135deg,
rgba(26, 26, 46, 0.95) 0%,
rgba(22, 33, 62, 0.95) 50%,
rgba(209, 6, 42, 0.1) 100%
);
}
.message {
max-width: 75%;
padding: 12px 16px;
border-radius: 12px;
position: relative;
word-wrap: break-word;
overflow-wrap: break-word;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
margin: 2px 0;
}
.message.user {
align-self: flex-end;
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
color: white;
border-bottom-right-radius: 4px;
margin-left: 15%;
}
.message.doctor {
align-self: flex-start;
background: rgba(252, 202, 211, 0.1);
color: var(--text-color);
border-bottom-left-radius: 4px;
margin-right: 15%;
}
.chat-input-container {
position: absolute;
bottom: 0;
left: 20px;
right: auto;
width: calc(100% - 200px);
display: flex;
padding: 12px;
background: rgba(26, 26, 46, 0.95);
border-top: 1px solid rgba(252, 202, 211, 0.1);
backdrop-filter: blur(10px);
z-index: 1;
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
.chat-input {
flex: 1;
background: rgba(252, 202, 211, 0.1);
border: 1px solid rgba(252, 202, 211, 0.2);
border-radius: 20px;
color: var(--text-color);
padding: 12px 16px;
resize: none;
margin-right: 10px;
min-height: 45px;
transition: all 0.3s ease;
font-size: 1rem;
max-width: calc(100% - 60px);
}
.chat-input:focus {
background: rgba(252, 202, 211, 0.15);
border-color: var(--primary-color);
outline: none;
box-shadow: 0 0 0 2px rgba(240, 62, 95, 0.2);
}
#sendMessage {
align-self: flex-end;
padding: 12px 20px;
min-width: 45px;
font-weight: 500;
letter-spacing: 0.5px;
border-radius: 20px;
flex-shrink: 0;
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
}
#sendMessage:hover {
background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
}
.loading {
position: absolute;
bottom: 70px;
left: 50%;
transform: translateX(-50%);
background: rgba(26, 26, 46, 0.9);
padding: 10px 20px;
border-radius: 20px;
display: none;
align-items: center;
gap: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 1;
border: 1px solid rgba(252, 202, 211, 0.1);
}
.loading i {
color: var(--primary-color);
font-size: 1.2rem;
animation: spin 1s linear infinite;
}
.loading p {
margin: 0;
font-size: 0.9rem;
color: var(--text-light);
}
[data-theme="light"] select.form-control {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%23333333' viewBox='0 0 16 16'%3E%3Cpath d='M7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'/%3E%3C/svg%3E");
}
[data-theme="light"] select.form-control option {
background-color: rgba(255, 255, 255, 0.95);
color: var(--text-color);
}
[data-theme="light"] .chat-container {
background: rgba(252, 202, 211, 0.1);
border: 1px solid rgba(138, 0, 24, 0.1);
}
[data-theme="light"] .chat-messages {
background: linear-gradient(135deg,
rgba(252, 202, 211, 0.1) 0%,
rgba(245, 118, 141, 0.1) 50%,
rgba(240, 62, 95, 0.05) 100%
);
}
[data-theme="light"] .chat-input-container {
background: rgba(255, 255, 255, 0.95);
border-top: 1px solid rgba(138, 0, 24, 0.1);
}
[data-theme="light"] .chat-input {
background: rgba(255, 255, 255, 0.95);
border: 2px solid var(--accent-color);
color: var(--text-color);
}
[data-theme="light"] .chat-input:focus {
background: rgba(255, 255, 255, 0.95);
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(209, 6, 42, 0.2);
}
[data-theme="light"] .chat-input::placeholder {
color: rgba(138, 0, 24, 0.6);
}
[data-theme="light"] .loading {
background: rgba(255, 255, 255, 0.95);
border: 1px solid rgba(138, 0, 24, 0.1);
}
[data-theme="light"] .message.doctor {
background: rgba(252, 202, 211, 0.2);
}
[data-theme="light"] #ai-doctor .header {
background: rgba(252, 202, 211, 0.2);
border: 1px solid rgba(138, 0, 24, 0.1);
border-bottom: none;
}
[data-theme="light"] #ai-doctor .header h3 {
color: var(--accent-color);
}
[data-theme="light"] #ai-doctor .header p {
color: var(--text-light);
}
[data-theme="light"] #ai-doctor .header i {
color: var(--accent-color);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 0 20px;
}
.btn-logout {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
border: none;
color: white;
padding: 8px 16px;
border-radius: var(--border-radius);
font-weight: 500;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 8px;
}
.btn-logout:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(240, 62, 95, 0.3);
}
.btn-logout i {
font-size: 1.1em;
}
.analysis-container {
padding: 20px;
}
.analysis-header {
text-align: center;
margin-bottom: 30px;
}
.analysis-header h3 {
color: var(--primary-color);
margin-bottom: 10px;
}
.analysis-inputs {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.input-section {
background: rgba(255, 255, 255, 0.05);
padding: 15px;
border-radius: var(--border-radius);
}
.input-section h4 {
color: var(--text-light);
margin-bottom: 10px;
}
.result-box {
background: rgba(0, 0, 0, 0.2);
padding: 10px;
border-radius: var(--border-radius);
min-height: 100px;
max-height: 200px;
overflow-y: auto;
}
#generateAnalysis {
width: 100%;
padding: 15px;
margin: 20px 0;
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
border: none;
border-radius: var(--border-radius);
color: white;
font-size: 1.1rem;
cursor: pointer;
transition: var(--transition);
}
#generateAnalysis:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
}
.analysis-result {
background: rgba(255, 255, 255, 0.05);
padding: 20px;
border-radius: var(--border-radius);
margin-top: 20px;
display: none;
}
.analysis-result.show {
display: block;
}
</style>
</head>
<body>
<button class="theme-toggle" onclick="toggleTheme()">
<i class="fas fa-moon"></i>
<span>Dark Mode</span>
</button>
<div class="container">
<div class="header">
<div class="header-content">
<h1>Heart Health Analysis System</h1>
<button onclick="logout()" class="btn-logout">
<i class="fas fa-sign-out-alt"></i> Logout
</button>
</div>
</div>
<ul class="nav nav-tabs" id="analysisTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="heart-risk-tab" data-bs-toggle="tab" data-bs-target="#heart-risk" type="button" role="tab">
<i class="fas fa-heart"></i> Heart Disease Risk
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="ecg-tab" data-bs-toggle="tab" data-bs-target="#ecg" type="button" role="tab">
<i class="fas fa-heartbeat"></i> ECG Analysis
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="heart-sound-tab" data-bs-toggle="tab" data-bs-target="#heart-sound" type="button" role="tab">
<i class="fas fa-stethoscope"></i> Heart Sound Analysis
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="ai-doctor-tab" data-bs-toggle="tab" data-bs-target="#ai-doctor" type="button" role="tab">
<i class="fas fa-robot"></i> AI Doctor
</button>
</li>
</ul>
<div class="tab-content" id="analysisTabContent">
<!-- Heart Disease Risk Analysis -->
<div class="tab-pane fade show active" id="heart-risk" role="tabpanel">
<div class="analysis-section">
<h3><i class="fas fa-chart-line"></i> Heart Disease Risk Analysis</h3>
<form id="heartRiskForm">
<div class="row">
<div class="col-md-6 mb-3">
<label for="age" class="form-label">Age of Patient</label>
<input type="number" class="form-control" id="age" min="25" max="75" placeholder="Enter age (25-75)" required>
</div>
<div class="col-md-6 mb-3">
<label for="sex" class="form-label">Gender</label>
<select class="form-control" id="sex" required>
<option value="1">Male</option>
<option value="0">Female</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="cp" class="form-label">Category of Chest Pain</label>
<select class="form-control" id="cp" required>
<option value="1">Atypical Angina</option>
<option value="0">Typical Angina</option>
<option value="3">Asymptomatic</option>
<option value="2">Non-Angina</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="trestbps" class="form-label">Resting Blood Pressure</label>
<input type="number" class="form-control" id="trestbps" min="94" max="200" placeholder="Enter blood pressure (94-200 mmHg)" required>
</div>
<div class="col-md-6 mb-3">
<label for="chol" class="form-label">Cholesterol</label>
<input type="number" class="form-control" id="chol" min="126" max="564" placeholder="Enter cholesterol level (126-564 mg/dL)" required>
</div>
<div class="col-md-6 mb-3">
<label for="fbs" class="form-label">Fasting Blood Sugar</label>
<select class="form-control" id="fbs" required>
<option value="0">Less than 120 mg/dL (Normal)</option>
<option value="1">120 mg/dL or more (High)</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="restecg" class="form-label">Resting ECG Result</label>
<select class="form-control" id="restecg" required>
<option value="0">Normal</option>
<option value="1">ST-T Wave Abnormalities</option>
<option value="2">Left Ventricular Hypertrophy</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="thalach" class="form-label">Maximum Heart Rate</label>
<input type="number" class="form-control" id="thalach" min="71" max="202" placeholder="Enter heart rate (71-202 bpm)" required>
</div>
<div class="col-md-6 mb-3">
<label for="exang" class="form-label">Exercise Induced Angina</label>
<select class="form-control" id="exang" required>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="oldpeak" class="form-label">ST Depression Induced by Exercise</label>
<input type="number" step="0.1" class="form-control" id="oldpeak" min="0" max="6.2" placeholder="Enter ST Depression value (0-6.2)" required>
</div>
<div class="col-md-6 mb-3">
<label for="slope" class="form-label">Slope of Peak Exercise ST Segment</label>
<select class="form-control" id="slope" required>
<option value="0">Upsloping</option>
<option value="1">Flat</option>
<option value="2">Downsloping</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="ca" class="form-label">Number of Major Vessels Colored by Fluoroscopy</label>
<input type="number" class="form-control" id="ca" min="0" max="3" placeholder="Enter number of vessels (0-3)" required>
</div>
<div class="col-md-6 mb-3">
<label for="thal" class="form-label">Thalassemia</label>
<select class="form-control" id="thal" required>
<option value="0">Absent / Normal</option>
<option value="1">Mild Thalassemia</option>
<option value="2">Moderate Thalassemia</option>
<option value="3">Severe Thalassemia</option>
</select>
</div>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-chart-pie"></i> Analyze Heart Disease Risk
</button>
</form>
<div class="loading" id="heartLoading">
<i class="fas fa-spinner"></i>
<p>Analyzing heart disease risk...</p>
</div>
<div id="heartRiskResult" class="result-box">
<h4><i class="fas fa-clipboard-list"></i> Analysis Results</h4>
<div class="progress mb-3">
<div id="riskProgress" class="progress-bar" role="progressbar"></div>
</div>
<p id="riskPrediction"></p>
<p id="riskIndicators"></p>
</div>
</div>
</div>
<!-- ECG Analysis -->
<div class="tab-pane fade" id="ecg" role="tabpanel">
<div class="analysis-section">
<h3><i class="fas fa-heartbeat"></i> ECG Analysis</h3>
<form id="ecgForm">
<div class="mb-3">
<label for="ecgFile" class="form-label">Upload ECG Data (JSON file)</label>
<input type="file" class="form-control" id="ecgFile" accept=".json" required>
<div class="input-guidelines">Upload a JSON file containing 141 ECG values in the format: {"ecg_values": [value1, value2, ...]}</div>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-chart-line"></i> Analyze ECG
</button>
</form>
<div class="loading" id="ecgLoading">
<i class="fas fa-spinner"></i>
<p>Analyzing ECG data...</p>
</div>
<div id="ecgResult" class="result-box">
<h4><i class="fas fa-clipboard-list"></i> ECG Analysis Results</h4>
<p id="ecgPrediction"></p>
</div>
<div id="ecgPlotContainer" style="width: 100%; margin-top: 20px;">
<img id="ecgPlot" class="ecg-plot" src="" alt="ECG Plot">
</div>
</div>
</div>
<!-- Heart Sound Analysis -->
<div class="tab-pane fade" id="heart-sound" role="tabpanel">
<div class="analysis-section">
<h3><i class="fas fa-stethoscope"></i> Heart Sound Analysis</h3>
<form id="heartSoundForm">
<div class="mb-3">
<label for="audioFile" class="form-label">Upload Heart Sound (WAV format)</label>
<input type="file" class="form-control" id="audioFile" accept=".wav" required>
<div class="input-guidelines">Upload a WAV file containing heart sound recording</div>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-wave-square"></i> Analyze Heart Sound
</button>
</form>
<div class="loading" id="soundLoading">
<i class="fas fa-spinner"></i>
<p>Analyzing heart sound...</p>
</div>
<div id="heartSoundResult" class="result-box">
<h4><i class="fas fa-clipboard-list"></i> Heart Sound Analysis Results</h4>
<p id="soundPrediction"></p>
<p id="soundConfidence"></p>
</div>
</div>
</div>
<!-- AI Doctor -->
<div class="tab-pane fade" id="ai-doctor" role="tabpanel">
<div class="analysis-section">
<div class="header">
<i class="fas fa-robot"></i>
<div>
<h3>AI Doctor</h3>
<p>Your personal cardiologist assistant</p>
</div>
</div>
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="message doctor">
<div class="message-content">
<p>Hello! I'm your AI cardiologist. How can I help you with your heart health today?</p>
</div>
<div class="message-time">Just now</div>
</div>
</div>
<div class="chat-input-container">
<textarea id="userMessage" class="chat-input" placeholder="Type your message..." rows="1"></textarea>
<button id="sendMessage" class="btn btn-primary">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
<div class="loading" id="aiDoctorLoading">
<i class="fas fa-spinner"></i>
<p>AI Doctor is typing...</p>
</div>
</div>
</div>
<!-- AI Analysis -->
<div class="tab-pane" id="ai-analysis">
<div class="analysis-container">
<div class="analysis-header">
<h3>AI Analysis Report</h3>
<p>Comprehensive analysis of your heart health based on all test results</p>
</div>
<div class="analysis-content">
<div class="analysis-inputs">
<div class="input-section">
<h4>Heart Disease Risk Results</h4>
<div id="heartDiseaseResults" class="result-box"></div>
</div>
<div class="input-section">
<h4>ECG Analysis Results</h4>
<div id="ecgResults" class="result-box"></div>
</div>
<div class="input-section">
<h4>Heart Sound Analysis Results</h4>
<div id="heartSoundResults" class="result-box"></div>
</div>
</div>
<button id="generateAnalysis" class="btn btn-primary">
<i class="fas fa-robot"></i> Generate Analysis Report
</button>
<div id="aiAnalysisResult" class="analysis-result">
<!-- AI Analysis result will be displayed here -->
</div>
</div>
</div>
</div>
</div>
</div>
<a href="/emergency" class="emergency-button">
<i class="fas fa-exclamation-triangle"></i> EMERGENCY
</a>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-auth-compat.js"></script>
<script>
// Firebase configuration
const firebaseConfig = {{ firebase_config|tojson|safe }};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
// Check authentication state
firebase.auth().onAuthStateChanged((user) => {
if (!user) {
window.location.href = '/login';
}
});
// Logout function
function logout() {
// First, sign out from Firebase
firebase.auth().signOut()
.then(() => {
// Then call server-side logout
return fetch('/logout', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
})
.then(response => {
// Clear any local storage or session storage
localStorage.clear();
sessionStorage.clear();
// Redirect to login page
window.location.href = '/login';
})
.catch(error => {
console.error('Logout error:', error);
// Even if there's an error, try to redirect to login
window.location.href = '/login';
});
}
// Add loading indicators
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function() {
const loadingId = this.id.replace('Form', 'Loading');
const resultBox = document.getElementById(this.id.replace('Form', 'Result'));
document.getElementById(loadingId).style.display = 'block';
resultBox.style.display = 'none';
// Hide ECG plot when form is submitted
if (this.id === 'ecgForm') {
document.getElementById('ecgPlot').style.display = 'none';
}
});
});
// AI Doctor Chat Functionality
document.addEventListener('DOMContentLoaded', function() {
const sendButton = document.getElementById('sendMessage');
const userMessageInput = document.getElementById('userMessage');
const chatMessages = document.getElementById('chatMessages');
const aiDoctorLoading = document.getElementById('aiDoctorLoading');
// Send message when button is clicked
sendButton.addEventListener('click', sendMessage);
// Send message when Enter key is pressed (but allow Shift+Enter for new lines)
userMessageInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
function sendMessage() {
const message = userMessageInput.value.trim();
if (!message) return;
// Add user message to chat
addMessage(message, 'user');
// Clear input
userMessageInput.value = '';
// Show loading indicator
aiDoctorLoading.style.display = 'block';
// Send to server
fetch('/ai_doctor', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: message })
})
.then(response => response.json())
.then(data => {
// Hide loading indicator
aiDoctorLoading.style.display = 'none';
// Add AI response to chat
if (data.response) {
addMessage(data.response, 'doctor');
} else {
addMessage('I apologize, but I encountered an error processing your request. Please try again.', 'doctor');
}
})
.catch(error => {
console.error('Error:', error);
aiDoctorLoading.style.display = 'none';
addMessage('I apologize, but I encountered an error processing your request. Please try again.', 'doctor');
});
}
function addMessage(text, sender) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
const paragraph = document.createElement('p');
paragraph.textContent = text;
contentDiv.appendChild(paragraph);
const timeDiv = document.createElement('div');
timeDiv.className = 'message-time';
timeDiv.textContent = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
messageDiv.appendChild(contentDiv);
messageDiv.appendChild(timeDiv);
chatMessages.appendChild(messageDiv);
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
}
});
// Heart Disease Risk Analysis
document.getElementById('heartRiskForm').addEventListener('submit', async (e) => {
e.preventDefault();
const loading = document.getElementById('heartLoading');
const resultBox = document.getElementById('heartRiskResult');
try {
loading.style.display = 'block';
resultBox.style.display = 'none';
const data = {
age: document.getElementById('age').value,
sex: document.getElementById('sex').value,
cp: document.getElementById('cp').value,
trestbps: document.getElementById('trestbps').value,
chol: document.getElementById('chol').value,
fbs: document.getElementById('fbs').value,
restecg: document.getElementById('restecg').value,
thalach: document.getElementById('thalach').value,
exang: document.getElementById('exang').value,
oldpeak: document.getElementById('oldpeak').value,
slope: document.getElementById('slope').value,
ca: document.getElementById('ca').value,
thal: document.getElementById('thal').value
};
const response = await fetch('/analyze_heart', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
loading.style.display = 'none';
resultBox.style.display = 'block';
const probability = result.probability * 100;
const progressBar = document.getElementById('riskProgress');
progressBar.style.width = `${probability}%`;
progressBar.textContent = `${probability.toFixed(1)}%`;
progressBar.className = `progress-bar ${probability > 50 ? 'bg-danger' : 'bg-success'}`;
document.getElementById('riskPrediction').textContent =
`Prediction: ${result.prediction ? 'High Risk' : 'Low Risk'}`;
document.getElementById('riskIndicators').textContent =
`High Risk Indicators: ${result.high_risk_indicators}, Low Risk Indicators: ${result.low_risk_indicators}`;
// Update the AI Analysis tab with Heart Disease Risk results
const heartDiseaseResultsBox = document.getElementById('heartDiseaseResults');
if (heartDiseaseResultsBox) {
heartDiseaseResultsBox.innerHTML = `
<div class="result-content">
<p><strong>Risk Level:</strong> ${result.prediction ? 'High Risk' : 'Low Risk'}</p>
<p><strong>Probability:</strong> ${probability.toFixed(1)}%</p>
<p><strong>High Risk Indicators:</strong> ${result.high_risk_indicators}</p>
<p><strong>Low Risk Indicators:</strong> ${result.low_risk_indicators}</p>
</div>
`;
}
} catch (error) {
console.error('Error:', error);
loading.style.display = 'none';
resultBox.style.display = 'block';
resultBox.innerHTML = `<div class="alert alert-danger">An error occurred during analysis</div>`;
}
});
// ECG Analysis
document.getElementById('ecgForm').addEventListener('submit', async (e) => {
e.preventDefault();
const loading = document.getElementById('ecgLoading');
const resultBox = document.getElementById('ecgResult');
const file = document.getElementById('ecgFile').files[0];
if (!file) {
alert('Please select a JSON file');
return;
}
try {
loading.style.display = 'block';
resultBox.style.display = 'none';
const text = await file.text();
const data = JSON.parse(text);
const response = await fetch('/analyze_ecg', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
loading.style.display = 'none';
resultBox.style.display = 'block';
// Update result box class based on analysis
resultBox.className = `result-box ${result.is_anomaly ? 'anomaly' : 'normal'}`;
document.getElementById('ecgPrediction').textContent =
`ECG Analysis: ${result.is_anomaly ? 'Anomaly Detected' : 'Normal ECG'}`;
if (result.status === 'success' && result.plot_url) {
const plotImage = document.getElementById('ecgPlot');
plotImage.style.display = 'block';
plotImage.src = `data:image/png;base64,${result.plot_url}`;
} else {
document.getElementById('ecgPlot').style.display = 'none';
console.error('No plot URL received from server');
}
// Update the AI Analysis tab with ECG results
const ecgResultsBox = document.getElementById('ecgResults');
if (ecgResultsBox) {
ecgResultsBox.innerHTML = `
<div class="result-content">
<p><strong>ECG Analysis:</strong> ${result.is_anomaly ? 'Anomaly Detected' : 'Normal ECG'}</p>
<p><strong>Status:</strong> ${result.status}</p>
${result.plot_url ? `<img src="data:image/png;base64,${result.plot_url}" alt="ECG Plot" style="max-width: 100%; margin-top: 10px;">` : ''}
</div>
`;
}
} catch (error) {
console.error('Error:', error);
loading.style.display = 'none';
resultBox.style.display = 'block';
resultBox.innerHTML = `<div class="alert alert-danger">An error occurred during analysis. Please ensure the JSON file is properly formatted.</div>`;
}
});
// Heart Sound Analysis
document.getElementById('heartSoundForm').addEventListener('submit', async (e) => {
e.preventDefault();
const loading = document.getElementById('soundLoading');
const resultBox = document.getElementById('heartSoundResult');
const audioFile = document.getElementById('audioFile').files[0];
if (!audioFile) {
alert('Please select an audio file');
return;
}
try {
loading.style.display = 'block';
resultBox.style.display = 'none';
const formData = new FormData();
formData.append('audio', audioFile);
const response = await fetch('/analyze_audio', {
method: 'POST',
body: formData
});
const result = await response.json();
loading.style.display = 'none';
resultBox.style.display = 'block';
document.getElementById('soundPrediction').textContent =
`Detected Condition: ${result.disease}`;
document.getElementById('soundConfidence').textContent =
`Confidence: ${result.confidence}%`;
// Update the AI Analysis tab with Heart Sound results
const heartSoundResultsBox = document.getElementById('heartSoundResults');
if (heartSoundResultsBox) {
heartSoundResultsBox.innerHTML = `
<div class="result-content">
<p><strong>Detected Condition:</strong> ${result.disease}</p>
<p><strong>Confidence Level:</strong> ${result.confidence}%</p>
<p><strong>Analysis Status:</strong> ${result.status || 'Completed'}</p>
</div>
`;
}
} catch (error) {
console.error('Error:', error);
loading.style.display = 'none';
resultBox.style.display = 'block';
resultBox.innerHTML = `<div class="alert alert-danger">An error occurred during analysis</div>`;
}
});
// Theme toggle functionality
function toggleTheme() {
const html = document.documentElement;
const themeToggle = document.querySelector('.theme-toggle');
const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
// Update toggle button
const icon = themeToggle.querySelector('i');
const text = themeToggle.querySelector('span');
if (newTheme === 'dark') {
icon.className = 'fas fa-moon';
text.textContent = 'Dark Mode';
} else {
icon.className = 'fas fa-sun';
text.textContent = 'Light Mode';
}
}
// Set initial theme
document.addEventListener('DOMContentLoaded', () => {
const savedTheme = localStorage.getItem('theme') || 'dark';
document.documentElement.setAttribute('data-theme', savedTheme);
// Update toggle button
const themeToggle = document.querySelector('.theme-toggle');
const icon = themeToggle.querySelector('i');
const text = themeToggle.querySelector('span');
if (savedTheme === 'dark') {
icon.className = 'fas fa-moon';
text.textContent = 'Dark Mode';
} else {
icon.className = 'fas fa-sun';
text.textContent = 'Light Mode';
}
});
// Add AI Analysis tab to the navigation
document.addEventListener('DOMContentLoaded', function() {
const navTabs = document.querySelector('.nav-tabs');
const aiAnalysisTab = document.createElement('li');
aiAnalysisTab.className = 'nav-item';
aiAnalysisTab.innerHTML = `
<a class="nav-link" data-bs-toggle="tab" href="#ai-analysis">
<i class="fas fa-robot"></i> AI Analysis
</a>
`;
navTabs.appendChild(aiAnalysisTab);
});
// Function to collect results from all tabs
function collectResults() {
// Get heart disease risk parameters
const heartParams = {
age: document.getElementById('age').value,
sex: document.getElementById('sex').value,
cp: document.getElementById('cp').value,
trestbps: document.getElementById('trestbps').value,
chol: document.getElementById('chol').value,
fbs: document.getElementById('fbs').value,
restecg: document.getElementById('restecg').value,
thalach: document.getElementById('thalach').value,
exang: document.getElementById('exang').value,
oldpeak: document.getElementById('oldpeak').value,
slope: document.getElementById('slope').value,
ca: document.getElementById('ca').value,
thal: document.getElementById('thal').value
};
const results = {
heartDisease: document.getElementById('heartDiseaseResults').innerHTML,
ecg: document.getElementById('ecgResults').innerHTML,
heartSound: document.getElementById('heartSoundResults').innerHTML,
heartParams: heartParams
};
return results;
}
// Function to generate AI analysis
async function generateAnalysis() {
const results = collectResults();
// Check if we have at least one result
if (!results.heartDisease && !results.ecg && !results.heartSound) {
alert('Please complete at least one test to generate analysis');
return;
}
try {
const response = await fetch('/api/generate_analysis', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(results)
});
const data = await response.json();
if (data.status === 'success') {
const resultDiv = document.getElementById('aiAnalysisResult');
// Add heart disease risk parameters to the analysis
const heartParamsSection = `
<div class="report-section">
<h4>Heart Disease Risk Parameters</h4>
<div class="parameters-grid">
<div class="parameter-item">
<span class="parameter-label">Age:</span>
<span class="parameter-value">${results.heartParams.age} years</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Sex:</span>
<span class="parameter-value">${results.heartParams.sex === '1' ? 'Male' : 'Female'}</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Chest Pain Type:</span>
<span class="parameter-value">${getChestPainType(results.heartParams.cp)}</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Resting Blood Pressure:</span>
<span class="parameter-value">${results.heartParams.trestbps} mmHg</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Serum Cholesterol:</span>
<span class="parameter-value">${results.heartParams.chol} mg/dl</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Fasting Blood Sugar:</span>
<span class="parameter-value">${results.heartParams.fbs === '1' ? '> 120 mg/dl' : '<= 120 mg/dl'}</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Resting ECG Results:</span>
<span class="parameter-value">${getRestingECG(results.heartParams.restecg)}</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Maximum Heart Rate:</span>
<span class="parameter-value">${results.heartParams.thalach} bpm</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Exercise Induced Angina:</span>
<span class="parameter-value">${results.heartParams.exang === '1' ? 'Yes' : 'No'}</span>
</div>
<div class="parameter-item">
<span class="parameter-label">ST Depression:</span>
<span class="parameter-value">${results.heartParams.oldpeak} mm</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Slope of Peak Exercise:</span>
<span class="parameter-value">${getSlope(results.heartParams.slope)}</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Number of Major Vessels:</span>
<span class="parameter-value">${results.heartParams.ca}</span>
</div>
<div class="parameter-item">
<span class="parameter-label">Thalassemia:</span>
<span class="parameter-value">${getThalassemia(results.heartParams.thal)}</span>
</div>
</div>
</div>
`;
// Insert the parameters section after the header
const analysisHTML = data.analysis;
const headerEndIndex = analysisHTML.indexOf('</div>', analysisHTML.indexOf('<div class="report-header">')) + 6;
const finalHTML = analysisHTML.slice(0, headerEndIndex) + heartParamsSection + analysisHTML.slice(headerEndIndex);
resultDiv.innerHTML = `
<h4>AI Analysis Report</h4>
<div class="analysis-content">
${finalHTML}
</div>
`;
resultDiv.classList.add('show');
} else {
throw new Error(data.message);
}
} catch (error) {
console.error('Error generating analysis:', error);
alert('Error generating analysis. Please try again.');
}
}
// Helper functions for parameter value mapping
function getChestPainType(value) {
const types = {
'1': 'Typical Angina',
'2': 'Atypical Angina',
'3': 'Non-anginal Pain',
'4': 'Asymptomatic'
};
return types[value] || 'N/A';
}
function getRestingECG(value) {
const types = {
'0': 'Normal',
'1': 'ST-T Wave Abnormality',
'2': 'Left Ventricular Hypertrophy'
};
return types[value] || 'N/A';
}
function getSlope(value) {
const types = {
'0': 'Upsloping',
'1': 'Flat',
'2': 'Downsloping'
};
return types[value] || 'N/A';
}
function getThalassemia(value) {
const types = {
'0': 'Normal',
'1': 'Fixed Defect',
'2': 'Reversible Defect',
'3': 'Other'
};
return types[value] || 'N/A';
}
// Add event listener for the generate button
document.getElementById('generateAnalysis').addEventListener('click', generateAnalysis);
// Function to update results from other tabs
function updateResults(tab, content) {
const resultBox = document.getElementById(`${tab}Results`);
if (resultBox) {
resultBox.textContent = content;
}
}
</script>
</body>
</html>