leo861 commited on
Commit
eacc539
·
verified ·
1 Parent(s): a060fca

Upload folder using huggingface_hub

Browse files
app/templates/base.html ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Heart Emergency Response System</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
9
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
10
+ integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
11
+ crossorigin=""/>
12
+ <style>
13
+ :root {
14
+ --primary-color: #ff4d6d;
15
+ --secondary-color: #4cc9f0;
16
+ --accent-color: #7209b7;
17
+ --text-color: #ffffff;
18
+ --text-light: #e0e0e0;
19
+ --background-start: #1a1a2e;
20
+ --background-end: #16213e;
21
+ --card-color: rgba(26, 26, 46, 0.8);
22
+ --border-radius: 12px;
23
+ --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
24
+ --transition: all 0.3s ease;
25
+ }
26
+
27
+ body {
28
+ background: linear-gradient(135deg,
29
+ rgba(26, 26, 46, 0.95) 0%,
30
+ rgba(22, 33, 62, 0.95) 50%,
31
+ rgba(114, 9, 183, 0.2) 100%
32
+ );
33
+ color: var(--text-color);
34
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
35
+ min-height: 100vh;
36
+ margin: 0;
37
+ padding: 0;
38
+ }
39
+
40
+ body::before {
41
+ content: '';
42
+ position: fixed;
43
+ top: 0;
44
+ left: 0;
45
+ width: 100%;
46
+ height: 100%;
47
+ background: radial-gradient(
48
+ circle at top left,
49
+ rgba(114, 9, 183, 0.2) 0%,
50
+ transparent 50%
51
+ ),
52
+ radial-gradient(
53
+ circle at top right,
54
+ rgba(76, 201, 240, 0.1) 0%,
55
+ transparent 50%
56
+ );
57
+ z-index: -1;
58
+ }
59
+
60
+ .container {
61
+ max-width: 1200px;
62
+ margin: 0 auto;
63
+ padding: 20px;
64
+ }
65
+
66
+ /* Custom scrollbar */
67
+ ::-webkit-scrollbar {
68
+ width: 8px;
69
+ }
70
+
71
+ ::-webkit-scrollbar-track {
72
+ background: rgba(0, 0, 0, 0.2);
73
+ }
74
+
75
+ ::-webkit-scrollbar-thumb {
76
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
77
+ border-radius: 4px;
78
+ }
79
+
80
+ ::-webkit-scrollbar-thumb:hover {
81
+ background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
82
+ }
83
+
84
+ /* Animations */
85
+ @keyframes fadeIn {
86
+ from { opacity: 0; }
87
+ to { opacity: 1; }
88
+ }
89
+
90
+ @keyframes fadeInUp {
91
+ from {
92
+ opacity: 0;
93
+ transform: translateY(20px);
94
+ }
95
+ to {
96
+ opacity: 1;
97
+ transform: translateY(0);
98
+ }
99
+ }
100
+
101
+ @keyframes fadeInDown {
102
+ from {
103
+ opacity: 0;
104
+ transform: translateY(-20px);
105
+ }
106
+ to {
107
+ opacity: 1;
108
+ transform: translateY(0);
109
+ }
110
+ }
111
+
112
+ @keyframes spin {
113
+ 0% { transform: rotate(0deg); }
114
+ 100% { transform: rotate(360deg); }
115
+ }
116
+
117
+ /* Common components */
118
+ .btn {
119
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
120
+ color: white;
121
+ border: none;
122
+ padding: 12px 24px;
123
+ border-radius: var(--border-radius);
124
+ font-weight: 500;
125
+ transition: var(--transition);
126
+ cursor: pointer;
127
+ }
128
+
129
+ .btn:hover {
130
+ transform: translateY(-2px);
131
+ box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
132
+ }
133
+
134
+ .card {
135
+ background: rgba(26, 26, 46, 0.9);
136
+ border-radius: var(--border-radius);
137
+ padding: 20px;
138
+ box-shadow: var(--box-shadow);
139
+ border: 1px solid rgba(255, 255, 255, 0.1);
140
+ animation: fadeInUp 0.8s ease;
141
+ }
142
+
143
+ .alert {
144
+ border-radius: var(--border-radius);
145
+ padding: 15px;
146
+ margin: 10px 0;
147
+ animation: fadeIn 0.5s ease;
148
+ }
149
+
150
+ .alert-success {
151
+ background: rgba(76, 201, 240, 0.2);
152
+ border: 1px solid var(--secondary-color);
153
+ color: var(--text-color);
154
+ }
155
+
156
+ .alert-danger {
157
+ background: rgba(255, 77, 109, 0.2);
158
+ border: 1px solid var(--primary-color);
159
+ color: var(--text-color);
160
+ }
161
+
162
+ /* Loading spinner */
163
+ .spinner {
164
+ width: 40px;
165
+ height: 40px;
166
+ border: 4px solid rgba(255, 255, 255, 0.1);
167
+ border-left-color: var(--primary-color);
168
+ border-radius: 50%;
169
+ animation: spin 1s linear infinite;
170
+ }
171
+
172
+ .loading {
173
+ display: flex;
174
+ justify-content: center;
175
+ align-items: center;
176
+ gap: 10px;
177
+ color: var(--text-light);
178
+ }
179
+ </style>
180
+ {% block head %}{% endblock %}
181
+ </head>
182
+ <body>
183
+ {% block content %}{% endblock %}
184
+
185
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
186
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
187
+ integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
188
+ crossorigin=""></script>
189
+ {% block scripts %}{% endblock %}
190
+ </body>
191
+ </html>
app/templates/emergency_map.html ADDED
@@ -0,0 +1,1192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block head %}
4
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
5
+ <style>
6
+ .container-fluid {
7
+ padding: 0;
8
+ height: 100vh;
9
+ display: flex;
10
+ }
11
+
12
+ #emergencyMap {
13
+ height: 100vh;
14
+ width: 70%;
15
+ position: relative;
16
+ }
17
+
18
+ .control-panel {
19
+ width: 30%;
20
+ height: 100vh;
21
+ background: rgba(26, 26, 46, 0.95);
22
+ padding: 20px;
23
+ overflow-y: auto;
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: 15px;
27
+ }
28
+
29
+ .control-section {
30
+ background: rgba(255, 255, 255, 0.05);
31
+ border-radius: var(--border-radius);
32
+ padding: 15px;
33
+ margin-bottom: 15px;
34
+ }
35
+
36
+ .section-title {
37
+ color: var(--text-light);
38
+ font-size: 1.1rem;
39
+ margin-bottom: 10px;
40
+ display: flex;
41
+ align-items: center;
42
+ gap: 8px;
43
+ }
44
+
45
+ .emergency-button {
46
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
47
+ color: white;
48
+ border: none;
49
+ padding: 12px 20px;
50
+ border-radius: var(--border-radius);
51
+ font-size: 1rem;
52
+ font-weight: bold;
53
+ cursor: pointer;
54
+ transition: var(--transition);
55
+ width: 100%;
56
+ margin-bottom: 8px;
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ gap: 8px;
61
+ }
62
+
63
+ .emergency-button:hover {
64
+ transform: translateY(-2px);
65
+ box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
66
+ }
67
+
68
+ .emergency-button.secondary {
69
+ background: rgba(255, 255, 255, 0.1);
70
+ }
71
+
72
+ .emergency-description {
73
+ width: 100%;
74
+ padding: 10px;
75
+ margin-top: 10px;
76
+ background: rgba(255, 255, 255, 0.1);
77
+ border: 1px solid rgba(255, 255, 255, 0.2);
78
+ border-radius: var(--border-radius);
79
+ color: var(--text-color);
80
+ resize: vertical;
81
+ min-height: 60px;
82
+ }
83
+
84
+ .emergency-timer {
85
+ color: var(--primary-color);
86
+ font-size: 1.1rem;
87
+ text-align: center;
88
+ margin-top: 10px;
89
+ font-weight: bold;
90
+ }
91
+
92
+ .medical-form {
93
+ display: none;
94
+ background: rgba(255, 255, 255, 0.05);
95
+ padding: 15px;
96
+ border-radius: var(--border-radius);
97
+ margin-top: 10px;
98
+ }
99
+
100
+ .medical-form input,
101
+ .medical-form textarea {
102
+ width: 100%;
103
+ padding: 8px;
104
+ margin-bottom: 10px;
105
+ background: rgba(255, 255, 255, 0.1);
106
+ border: 1px solid rgba(255, 255, 255, 0.2);
107
+ border-radius: var(--border-radius);
108
+ color: var(--text-color);
109
+ }
110
+
111
+ .emergency-contacts {
112
+ display: flex;
113
+ flex-direction: column;
114
+ gap: 10px;
115
+ }
116
+
117
+ .contact-item {
118
+ display: flex;
119
+ align-items: center;
120
+ gap: 10px;
121
+ background: rgba(255, 255, 255, 0.05);
122
+ padding: 10px;
123
+ border-radius: var(--border-radius);
124
+ }
125
+
126
+ .contact-item button {
127
+ background: none;
128
+ border: none;
129
+ color: var(--text-light);
130
+ cursor: pointer;
131
+ padding: 5px;
132
+ }
133
+
134
+ .contact-item button:hover {
135
+ color: var(--primary-color);
136
+ }
137
+
138
+ .hospital-list {
139
+ max-height: 200px;
140
+ overflow-y: auto;
141
+ }
142
+
143
+ .hospital-item {
144
+ background: rgba(255, 255, 255, 0.05);
145
+ padding: 10px;
146
+ margin-bottom: 8px;
147
+ border-radius: var(--border-radius);
148
+ cursor: pointer;
149
+ transition: var(--transition);
150
+ }
151
+
152
+ .hospital-item:hover {
153
+ background: rgba(255, 255, 255, 0.1);
154
+ }
155
+
156
+ .location-request {
157
+ position: absolute;
158
+ top: 20px;
159
+ left: 50%;
160
+ transform: translateX(-50%);
161
+ background: rgba(26, 26, 46, 0.95);
162
+ padding: 15px 25px;
163
+ border-radius: var(--border-radius);
164
+ z-index: 1000;
165
+ text-align: center;
166
+ }
167
+
168
+ .location-request button {
169
+ background: var(--primary-color);
170
+ color: white;
171
+ border: none;
172
+ padding: 8px 20px;
173
+ border-radius: var(--border-radius);
174
+ margin-top: 10px;
175
+ cursor: pointer;
176
+ }
177
+
178
+ .medical-records-list {
179
+ margin-top: 10px;
180
+ max-height: 200px;
181
+ overflow-y: auto;
182
+ }
183
+
184
+ .medical-record-item {
185
+ display: flex;
186
+ justify-content: space-between;
187
+ align-items: center;
188
+ background: rgba(255, 255, 255, 0.05);
189
+ padding: 10px;
190
+ margin-bottom: 8px;
191
+ border-radius: var(--border-radius);
192
+ }
193
+
194
+ .record-info {
195
+ flex: 1;
196
+ }
197
+
198
+ .record-actions {
199
+ display: flex;
200
+ gap: 8px;
201
+ }
202
+
203
+ .record-actions button {
204
+ background: none;
205
+ border: none;
206
+ color: var(--text-light);
207
+ cursor: pointer;
208
+ padding: 5px;
209
+ }
210
+
211
+ .record-actions button:hover {
212
+ color: var(--primary-color);
213
+ }
214
+
215
+ .hospital-header {
216
+ display: flex;
217
+ justify-content: space-between;
218
+ align-items: center;
219
+ margin-bottom: 5px;
220
+ }
221
+
222
+ .hospital-details {
223
+ font-size: 0.9em;
224
+ color: var(--text-light);
225
+ margin-bottom: 10px;
226
+ }
227
+
228
+ .hospital-details i {
229
+ width: 20px;
230
+ text-align: center;
231
+ margin-right: 5px;
232
+ }
233
+
234
+ .select-hospital-btn {
235
+ background: var(--primary-color);
236
+ color: white;
237
+ border: none;
238
+ padding: 5px 10px;
239
+ border-radius: var(--border-radius);
240
+ cursor: pointer;
241
+ font-size: 0.9em;
242
+ }
243
+
244
+ .hospital-item.selected {
245
+ border: 2px solid var(--primary-color);
246
+ }
247
+
248
+ .directions-panel {
249
+ max-height: 300px;
250
+ overflow-y: auto;
251
+ }
252
+
253
+ .direction-step {
254
+ display: flex;
255
+ align-items: center;
256
+ padding: 5px;
257
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
258
+ }
259
+
260
+ .step-number {
261
+ background: var(--primary-color);
262
+ color: white;
263
+ width: 24px;
264
+ height: 24px;
265
+ border-radius: 50%;
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ margin-right: 10px;
270
+ }
271
+
272
+ .step-instruction {
273
+ flex: 1;
274
+ }
275
+
276
+ .step-distance {
277
+ color: var(--text-light);
278
+ font-size: 0.9em;
279
+ }
280
+
281
+ .modal {
282
+ position: fixed;
283
+ top: 0;
284
+ left: 0;
285
+ width: 100%;
286
+ height: 100%;
287
+ background: rgba(0, 0, 0, 0.7);
288
+ display: flex;
289
+ justify-content: center;
290
+ align-items: center;
291
+ z-index: 1000;
292
+ }
293
+
294
+ .modal-content {
295
+ background: var(--bg-color);
296
+ padding: 20px;
297
+ border-radius: var(--border-radius);
298
+ width: 90%;
299
+ max-width: 500px;
300
+ }
301
+
302
+ .contact-form {
303
+ display: flex;
304
+ flex-direction: column;
305
+ gap: 15px;
306
+ }
307
+
308
+ .contact-form input {
309
+ width: 100%;
310
+ padding: 10px;
311
+ border: 1px solid rgba(255, 255, 255, 0.2);
312
+ border-radius: var(--border-radius);
313
+ background: rgba(255, 255, 255, 0.1);
314
+ color: var(--text-color);
315
+ }
316
+
317
+ .contact-form-buttons {
318
+ display: flex;
319
+ gap: 10px;
320
+ justify-content: flex-end;
321
+ }
322
+
323
+ .file-upload-container {
324
+ position: relative;
325
+ margin: 10px 0;
326
+ }
327
+
328
+ .file-upload-input {
329
+ display: none;
330
+ }
331
+
332
+ .file-upload-label {
333
+ display: block;
334
+ padding: 10px;
335
+ background: rgba(255, 255, 255, 0.1);
336
+ border: 1px dashed rgba(255, 255, 255, 0.2);
337
+ border-radius: var(--border-radius);
338
+ text-align: center;
339
+ cursor: pointer;
340
+ transition: var(--transition);
341
+ }
342
+
343
+ .file-upload-label:hover {
344
+ background: rgba(255, 255, 255, 0.15);
345
+ }
346
+
347
+ .file-info {
348
+ margin-top: 5px;
349
+ font-size: 0.9em;
350
+ color: var(--text-light);
351
+ }
352
+
353
+ .ecg-analysis-section {
354
+ margin-top: 15px;
355
+ }
356
+
357
+ .ecg-analysis-section h4 {
358
+ margin-bottom: 10px;
359
+ color: var(--text-light);
360
+ }
361
+
362
+ .analysis-result {
363
+ margin-top: 15px;
364
+ padding: 10px;
365
+ background: rgba(255, 255, 255, 0.05);
366
+ border-radius: var(--border-radius);
367
+ display: none;
368
+ }
369
+
370
+ .analysis-result.show {
371
+ display: block;
372
+ }
373
+
374
+ .analysis-result h5 {
375
+ color: var(--text-light);
376
+ margin-bottom: 10px;
377
+ }
378
+
379
+ .analysis-result pre {
380
+ background: rgba(0, 0, 0, 0.2);
381
+ padding: 10px;
382
+ border-radius: var(--border-radius);
383
+ overflow-x: auto;
384
+ white-space: pre-wrap;
385
+ word-wrap: break-word;
386
+ }
387
+ </style>
388
+ {% endblock %}
389
+
390
+ {% block content %}
391
+ <div class="container-fluid">
392
+ <div id="emergencyMap"></div>
393
+
394
+ <div class="control-panel">
395
+ <div class="control-section">
396
+ <div class="section-title">
397
+ <i class="fas fa-map-marker-alt"></i> Location
398
+ </div>
399
+ <div id="currentLocation">Requesting location...</div>
400
+ <button id="startJourney" class="emergency-button secondary" disabled>
401
+ <i class="fas fa-route"></i> Start Journey to Nearest Hospital
402
+ </button>
403
+ </div>
404
+
405
+ <div class="control-section">
406
+ <div class="section-title">
407
+ <i class="fas fa-hospital"></i> Nearby Hospitals
408
+ </div>
409
+ <div class="hospital-list" id="hospitalList">
410
+ Loading nearby hospitals...
411
+ </div>
412
+ </div>
413
+
414
+ <div class="control-section">
415
+ <div class="section-title">
416
+ <i class="fas fa-phone-alt"></i> Emergency Contacts
417
+ </div>
418
+ <div class="emergency-contacts" id="emergencyContacts">
419
+ <button class="emergency-button secondary" id="addContact">
420
+ <i class="fas fa-plus"></i> Add Emergency Contact
421
+ </button>
422
+ </div>
423
+ </div>
424
+
425
+ <div class="control-section">
426
+ <div class="section-title">
427
+ <i class="fas fa-file-medical"></i> Medical Records
428
+ </div>
429
+ <div class="file-upload-container">
430
+ <label class="file-upload-label" for="medicalRecordUpload">
431
+ <i class="fas fa-upload"></i> Upload Medical Records
432
+ <div class="file-info">Supported formats: CSV, TXT, JSON</div>
433
+ </label>
434
+ <input type="file" id="medicalRecordUpload" class="file-upload-input" accept=".csv,.txt,.json">
435
+ </div>
436
+ <div class="medical-records-list" id="medicalRecordsList">
437
+ <!-- Medical records will be displayed here -->
438
+ </div>
439
+ <button class="emergency-button secondary" id="editMedicalInfo">
440
+ <i class="fas fa-edit"></i> View Medical Information
441
+ </button>
442
+ <div class="medical-form" id="medicalForm">
443
+ <input type="text" placeholder="Current Medications" id="currentMedications">
444
+ <textarea placeholder="Allergies" id="allergies"></textarea>
445
+ <button class="emergency-button" id="saveMedicalInfo">Save Information</button>
446
+ </div>
447
+ </div>
448
+ </div>
449
+ </div>
450
+
451
+ <div class="location-request" id="locationRequest">
452
+ <div>This app needs your location to provide emergency services</div>
453
+ <button id="allowLocation">Allow Location Access</button>
454
+ </div>
455
+
456
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
457
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
458
+ <script>
459
+ // Make functions globally available
460
+ window.selectHospital = function(index) {
461
+ selectedHospital = nearbyHospitals[index];
462
+ if (selectedHospital) {
463
+ // Update the start journey button
464
+ const startJourneyBtn = document.getElementById('startJourney');
465
+ startJourneyBtn.innerHTML = `<i class="fas fa-route"></i> Start Journey to ${selectedHospital.name}`;
466
+ startJourneyBtn.disabled = false;
467
+
468
+ // Highlight selected hospital
469
+ document.querySelectorAll('.hospital-item').forEach((item, i) => {
470
+ item.classList.toggle('selected', i === index);
471
+ });
472
+
473
+ // Center map on selected hospital
474
+ map.setView([selectedHospital.lat, selectedHospital.lon], 15);
475
+ }
476
+ };
477
+
478
+ window.startJourneyToHospital = function(index) {
479
+ const hospital = nearbyHospitals[index];
480
+ if (hospital && userLocation) {
481
+ // Clear existing route
482
+ if (currentRoute) {
483
+ map.removeLayer(currentRoute);
484
+ }
485
+
486
+ // Get detailed route with turn-by-turn directions
487
+ fetch(`https://router.project-osrm.org/route/v1/driving/${userLocation.lng},${userLocation.lat};${hospital.lon},${hospital.lat}?overview=full&geometries=geojson&steps=true`)
488
+ .then(response => response.json())
489
+ .then(data => {
490
+ if (data.routes && data.routes[0]) {
491
+ const route = data.routes[0];
492
+ currentRoute = L.geoJSON(route.geometry, {
493
+ style: {
494
+ color: '#4CAF50',
495
+ weight: 5,
496
+ opacity: 0.7
497
+ }
498
+ }).addTo(map);
499
+
500
+ // Fit map to show the entire route
501
+ map.fitBounds(currentRoute.getBounds());
502
+
503
+ // Show turn-by-turn directions
504
+ const steps = route.legs[0].steps;
505
+ let directions = `
506
+ <div class="directions-panel">
507
+ <h3>Directions to ${hospital.name}</h3>
508
+ <div class="directions-list">
509
+ ${steps.map((step, index) => `
510
+ <div class="direction-step">
511
+ <span class="step-number">${index + 1}</span>
512
+ <span class="step-instruction">${step.maneuver.instruction}</span>
513
+ <span class="step-distance">${Math.round(step.distance)}m</span>
514
+ </div>
515
+ `).join('')}
516
+ </div>
517
+ </div>
518
+ `;
519
+
520
+ // Create a popup with directions
521
+ L.popup({maxWidth: 300})
522
+ .setLatLng([hospital.lat, hospital.lon])
523
+ .setContent(directions)
524
+ .openOn(map);
525
+ }
526
+ });
527
+ }
528
+ };
529
+ </script>
530
+ <script type="module">
531
+ import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-app.js";
532
+ import { getAuth, onAuthStateChanged, signInWithCustomToken } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-auth.js";
533
+ import { getDatabase, ref, onValue, set, update, remove, push, get } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-database.js";
534
+
535
+ // Initialize Firebase
536
+ const firebaseConfig = {
537
+ apiKey: "AIzaSyBDr1xcrLxfemIRTydmgjTcG6mHgx919Rs",
538
+ authDomain: "help-6661c.firebaseapp.com",
539
+ projectId: "help-6661c",
540
+ storageBucket: "help-6661c.firebasestorage.app",
541
+ messagingSenderId: "2944311795",
542
+ appId: "1:2944311795:web:61d2b982c75a446df7f286",
543
+ measurementId: "G-H8RJ7C4Z3K",
544
+ databaseURL: "https://help-6661c-default-rtdb.firebaseio.com"
545
+ };
546
+
547
+ const app = initializeApp(firebaseConfig);
548
+ const auth = getAuth();
549
+ const database = getDatabase();
550
+
551
+ // Check authentication status and load data
552
+ onAuthStateChanged(auth, async (user) => {
553
+ if (user) {
554
+ console.log('User is signed in:', user.uid);
555
+ loadUserData(user.uid);
556
+ } else {
557
+ console.log('No user is signed in');
558
+ // Redirect to login if not authenticated
559
+ window.location.href = '/login';
560
+ }
561
+ });
562
+
563
+ async function loadUserData(userId) {
564
+ try {
565
+ // Load emergency contacts
566
+ const contactsRef = ref(database, `users/${userId}/emergency_contacts`);
567
+ onValue(contactsRef, (snapshot) => {
568
+ const contacts = snapshot.val();
569
+ const contactsDiv = document.getElementById('emergencyContacts');
570
+
571
+ // Clear existing contacts except the add button
572
+ const addButton = document.getElementById('addContact');
573
+ contactsDiv.innerHTML = '';
574
+ contactsDiv.appendChild(addButton);
575
+
576
+ if (contacts) {
577
+ Object.entries(contacts).forEach(([id, contact]) => {
578
+ const contactElement = document.createElement('div');
579
+ contactElement.className = 'contact-item';
580
+ contactElement.dataset.contactId = id;
581
+ contactElement.innerHTML = `
582
+ <div class="contact-info">
583
+ <div class="contact-name">${contact.name}</div>
584
+ <div class="contact-phone">${contact.phone}</div>
585
+ </div>
586
+ <div class="contact-actions">
587
+ <button onclick="callContact('${contact.phone}')" title="Call"><i class="fas fa-phone"></i></button>
588
+ <button onclick="removeContact('${id}')" title="Remove"><i class="fas fa-trash"></i></button>
589
+ </div>
590
+ `;
591
+ contactsDiv.insertBefore(contactElement, addButton);
592
+ });
593
+ }
594
+ });
595
+
596
+ // Load medical information
597
+ const medicalInfoRef = ref(database, `users/${userId}/medical_info`);
598
+ onValue(medicalInfoRef, (snapshot) => {
599
+ const medicalInfo = snapshot.val();
600
+ if (medicalInfo) {
601
+ document.getElementById('currentMedications').value = medicalInfo.medications || '';
602
+ document.getElementById('allergies').value = medicalInfo.allergies || '';
603
+ }
604
+ });
605
+
606
+ // Load medical records
607
+ const recordsRef = ref(database, `users/${userId}/medical_records`);
608
+ onValue(recordsRef, (snapshot) => {
609
+ const records = snapshot.val();
610
+ const recordsList = document.getElementById('medicalRecordsList');
611
+ recordsList.innerHTML = '';
612
+
613
+ if (records) {
614
+ Object.entries(records).forEach(([id, record]) => {
615
+ const recordElement = document.createElement('div');
616
+ recordElement.className = 'record-item';
617
+ recordElement.dataset.recordId = id;
618
+ recordElement.innerHTML = `
619
+ <div class="record-info">
620
+ <div class="record-name">${record.name}</div>
621
+ <div class="record-date">${new Date(record.uploaded_at).toLocaleDateString()}</div>
622
+ </div>
623
+ <div class="record-actions">
624
+ <button onclick="viewRecord('${id}')" title="View"><i class="fas fa-eye"></i></button>
625
+ <button onclick="downloadRecord('${id}')" title="Download"><i class="fas fa-download"></i></button>
626
+ <button onclick="removeRecord('${id}')" title="Remove"><i class="fas fa-trash"></i></button>
627
+ </div>
628
+ `;
629
+ recordsList.appendChild(recordElement);
630
+ });
631
+ }
632
+ });
633
+ } catch (error) {
634
+ console.error('Error loading user data:', error);
635
+ }
636
+ }
637
+
638
+ function addEmergencyContact(name, phone) {
639
+ const user = auth.currentUser;
640
+ if (!user) {
641
+ alert('You must be logged in to add contacts');
642
+ return;
643
+ }
644
+
645
+ // Save to Firebase
646
+ const contactsRef = ref(database, `users/${user.uid}/emergency_contacts`);
647
+ const newContactRef = push(contactsRef);
648
+
649
+ set(newContactRef, {
650
+ name: name,
651
+ phone: phone,
652
+ added_at: new Date().toISOString()
653
+ })
654
+ .then(() => {
655
+ // Remove the modal
656
+ document.querySelector('.modal').remove();
657
+ })
658
+ .catch(error => {
659
+ console.error('Error saving contact:', error);
660
+ alert('Error saving contact. Please try again.');
661
+ });
662
+ }
663
+
664
+ function removeContact(contactId) {
665
+ if (confirm('Are you sure you want to remove this contact?')) {
666
+ const user = auth.currentUser;
667
+ if (user) {
668
+ const contactRef = ref(database, `users/${user.uid}/emergency_contacts/${contactId}`);
669
+ remove(contactRef)
670
+ .then(() => {
671
+ // Remove from UI
672
+ const contactElement = document.querySelector(`.contact-item[data-contact-id="${contactId}"]`);
673
+ if (contactElement) {
674
+ contactElement.remove();
675
+ }
676
+ })
677
+ .catch(error => {
678
+ console.error('Error removing contact:', error);
679
+ alert('Error removing contact. Please try again.');
680
+ });
681
+ }
682
+ }
683
+ }
684
+
685
+ // Make functions globally available
686
+ window.addEmergencyContact = addEmergencyContact;
687
+ window.removeContact = removeContact;
688
+ window.callContact = function(phone) {
689
+ if (phone) {
690
+ window.location.href = `tel:${phone}`;
691
+ }
692
+ };
693
+ window.viewRecord = viewRecord;
694
+ window.removeRecord = removeRecord;
695
+ window.downloadRecord = downloadRecord;
696
+
697
+ // Make showContactForm globally available
698
+ window.showContactForm = function() {
699
+ const modal = document.createElement('div');
700
+ modal.className = 'modal';
701
+ modal.innerHTML = `
702
+ <div class="modal-content">
703
+ <h3>Add Emergency Contact</h3>
704
+ <div class="contact-form">
705
+ <input type="text" id="contactName" placeholder="Enter contact name" required>
706
+ <input type="tel" id="contactPhone" placeholder="Enter contact phone number" required>
707
+ <div class="contact-form-buttons">
708
+ <button class="emergency-button secondary" onclick="this.closest('.modal').remove()">Cancel</button>
709
+ <button class="emergency-button" id="saveContactBtn">Save Contact</button>
710
+ </div>
711
+ </div>
712
+ </div>
713
+ `;
714
+ document.body.appendChild(modal);
715
+
716
+ // Add event listener to the save button
717
+ document.getElementById('saveContactBtn').addEventListener('click', function() {
718
+ const name = document.getElementById('contactName').value;
719
+ const phone = document.getElementById('contactPhone').value;
720
+
721
+ if (name && phone) {
722
+ addEmergencyContact(name, phone);
723
+ document.querySelector('.modal').remove();
724
+ } else {
725
+ alert('Please fill in all fields');
726
+ }
727
+ });
728
+ };
729
+
730
+ // Initialize contact form handling
731
+ document.addEventListener('DOMContentLoaded', function() {
732
+ const addContactBtn = document.getElementById('addContact');
733
+ if (addContactBtn) {
734
+ addContactBtn.addEventListener('click', window.showContactForm);
735
+ }
736
+ });
737
+
738
+ // Initialize the map
739
+ window.map = L.map('emergencyMap').setView([0, 0], 15);
740
+
741
+ // Create the street map layer
742
+ const streetLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
743
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
744
+ });
745
+
746
+ // Create the satellite layer
747
+ const satelliteLayer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
748
+ attribution: '&copy; <a href="https://www.esri.com/">Esri</a>'
749
+ });
750
+
751
+ // Add the street layer by default
752
+ streetLayer.addTo(window.map);
753
+
754
+ // Add layer control to switch between street and satellite views
755
+ const baseMaps = {
756
+ "Street Map": streetLayer,
757
+ "Satellite": satelliteLayer
758
+ };
759
+ L.control.layers(baseMaps).addTo(window.map);
760
+
761
+ let isVolunteer = false;
762
+ let emergencyTimeout = null;
763
+ let currentEmergency = null;
764
+ window.userLocation = null;
765
+ window.nearbyHospitals = [];
766
+ window.currentRoute = null;
767
+ window.selectedHospital = null;
768
+
769
+ // Socket.io connection
770
+ const socket = io();
771
+
772
+ socket.on('connect', () => {
773
+ console.log('Connected to server');
774
+ });
775
+
776
+ socket.on('emergency_alert', (data) => {
777
+ if (isVolunteer) {
778
+ showEmergencyAlert(data);
779
+ }
780
+ });
781
+
782
+ // Location handling
783
+ document.getElementById('allowLocation').addEventListener('click', () => {
784
+ if (navigator.geolocation) {
785
+ navigator.geolocation.getCurrentPosition(
786
+ (position) => {
787
+ window.userLocation = {
788
+ lat: position.coords.latitude,
789
+ lng: position.coords.longitude
790
+ };
791
+ window.map.setView([window.userLocation.lat, window.userLocation.lng], 15);
792
+ document.getElementById('locationRequest').style.display = 'none';
793
+ updateCurrentLocation();
794
+ findNearbyHospitals();
795
+ },
796
+ (error) => {
797
+ console.error('Error getting location:', error);
798
+ alert('Please enable location access to use emergency features');
799
+ }
800
+ );
801
+ }
802
+ });
803
+
804
+ function updateCurrentLocation() {
805
+ if (window.userLocation) {
806
+ document.getElementById('currentLocation').textContent =
807
+ `Current Location: ${window.userLocation.lat.toFixed(4)}, ${window.userLocation.lng.toFixed(4)}`;
808
+ document.getElementById('startJourney').disabled = false;
809
+ }
810
+ }
811
+
812
+ async function findNearbyHospitals() {
813
+ try {
814
+ const response = await fetch(`/api/nearby_hospitals?lat=${window.userLocation.lat}&lon=${window.userLocation.lng}`);
815
+ const data = await response.json();
816
+
817
+ if (data.hospitals) {
818
+ window.nearbyHospitals = data.hospitals;
819
+ displayHospitals(data.hospitals);
820
+
821
+ // Add markers for each hospital
822
+ data.hospitals.forEach((hospital, index) => {
823
+ L.marker([hospital.lat, hospital.lon], {
824
+ icon: L.divIcon({
825
+ className: 'hospital-marker',
826
+ html: '<i class="fas fa-hospital" style="color: #4CAF50; font-size: 20px;"></i>'
827
+ })
828
+ })
829
+ .bindPopup(`<b>${hospital.name}</b><br>${hospital.address}<br>Distance: ${(hospital.distance).toFixed(0)}m`)
830
+ .addTo(window.map);
831
+ });
832
+ }
833
+ } catch (error) {
834
+ console.error('Error finding hospitals:', error);
835
+ }
836
+ }
837
+
838
+ function displayHospitals(hospitals) {
839
+ const hospitalList = document.getElementById('hospitalList');
840
+ hospitalList.innerHTML = hospitals.map((hospital, index) => `
841
+ <div class="hospital-item" onclick="selectHospital(${index})">
842
+ <div class="hospital-header">
843
+ <strong>${hospital.name}</strong>
844
+ <span class="distance">${(hospital.distance).toFixed(0)}m</span>
845
+ </div>
846
+ <div class="hospital-details">
847
+ <div>${hospital.address}</div>
848
+ ${hospital.phone ? `<div><i class="fas fa-phone"></i> ${hospital.phone}</div>` : ''}
849
+ ${hospital.website ? `<div><i class="fas fa-globe"></i> <a href="${hospital.website}" target="_blank">Website</a></div>` : ''}
850
+ </div>
851
+ <button class="select-hospital-btn" onclick="event.stopPropagation(); startJourneyToHospital(${index})">
852
+ <i class="fas fa-route"></i> Start Journey
853
+ </button>
854
+ </div>
855
+ `).join('');
856
+ }
857
+
858
+ // Update the start journey button click handler
859
+ document.getElementById('startJourney').addEventListener('click', function() {
860
+ if (window.nearbyHospitals.length > 0) {
861
+ // If no specific hospital is selected, use the first (nearest) hospital
862
+ const hospitalIndex = window.selectedHospital ?
863
+ window.nearbyHospitals.findIndex(h => h.name === window.selectedHospital.name) : 0;
864
+ window.startJourneyToHospital(hospitalIndex);
865
+ }
866
+ });
867
+
868
+ // Medical records handling
869
+ document.getElementById('medicalRecordUpload').addEventListener('change', (e) => {
870
+ const file = e.target.files[0];
871
+ if (file) {
872
+ if (file.type === 'text/csv' || file.type === 'text/plain' || file.type === 'application/json') {
873
+ uploadMedicalRecord(file);
874
+ } else {
875
+ alert('Please upload a CSV, TXT, or JSON file');
876
+ }
877
+ }
878
+ });
879
+
880
+ document.getElementById('editMedicalInfo').addEventListener('click', () => {
881
+ const form = document.getElementById('medicalForm');
882
+ form.style.display = form.style.display === 'none' ? 'block' : 'none';
883
+ });
884
+
885
+ document.getElementById('saveMedicalInfo').addEventListener('click', () => {
886
+ const medications = document.getElementById('currentMedications').value;
887
+ const allergies = document.getElementById('allergies').value;
888
+ saveMedicalInfo(medications, allergies);
889
+ });
890
+
891
+ async function saveMedicalInfo(medications, allergies) {
892
+ try {
893
+ const user = auth.currentUser;
894
+ if (user) {
895
+ const medicalInfoRef = ref(database, `users/${user.uid}/medical_info`);
896
+ await set(medicalInfoRef, {
897
+ medications: medications,
898
+ allergies: allergies,
899
+ updated_at: new Date().toISOString()
900
+ });
901
+ alert('Medical information saved successfully');
902
+ document.getElementById('medicalForm').style.display = 'none';
903
+ } else {
904
+ alert('You must be logged in to save medical information');
905
+ }
906
+ } catch (error) {
907
+ console.error('Error saving medical info:', error);
908
+ alert('Error saving medical information. Please try again.');
909
+ }
910
+ }
911
+
912
+ async function uploadMedicalRecord(file) {
913
+ try {
914
+ const user = auth.currentUser;
915
+ if (!user) {
916
+ alert('You must be logged in to upload medical records');
917
+ return;
918
+ }
919
+
920
+ const reader = new FileReader();
921
+ reader.onload = async function(e) {
922
+ const content = e.target.result;
923
+ const recordsRef = ref(database, `users/${user.uid}/medical_records`);
924
+ const newRecordRef = push(recordsRef);
925
+
926
+ await set(newRecordRef, {
927
+ name: file.name,
928
+ content: content,
929
+ type: file.type,
930
+ uploaded_at: new Date().toISOString()
931
+ });
932
+
933
+ alert('Medical record uploaded successfully');
934
+ };
935
+ reader.readAsText(file);
936
+ } catch (error) {
937
+ console.error('Error uploading record:', error);
938
+ alert('Error uploading medical record. Please try again.');
939
+ }
940
+ }
941
+
942
+ async function viewRecord(recordId) {
943
+ try {
944
+ const user = auth.currentUser;
945
+ if (user) {
946
+ const recordRef = ref(database, `users/${user.uid}/medical_records/${recordId}`);
947
+ const snapshot = await get(recordRef);
948
+ const record = snapshot.val();
949
+
950
+ if (record) {
951
+ const modal = document.createElement('div');
952
+ modal.className = 'modal';
953
+ modal.innerHTML = `
954
+ <div class="modal-content">
955
+ <h3>${record.name}</h3>
956
+ <div class="record-content">
957
+ <pre>${record.content}</pre>
958
+ </div>
959
+ <div class="modal-buttons">
960
+ <button class="emergency-button" onclick="this.closest('.modal').remove()">Close</button>
961
+ </div>
962
+ </div>
963
+ `;
964
+ document.body.appendChild(modal);
965
+ }
966
+ }
967
+ } catch (error) {
968
+ console.error('Error viewing record:', error);
969
+ alert('Error viewing record. Please try again.');
970
+ }
971
+ }
972
+
973
+ async function removeRecord(recordId) {
974
+ if (confirm('Are you sure you want to remove this record?')) {
975
+ try {
976
+ const user = auth.currentUser;
977
+ if (user) {
978
+ const recordRef = ref(database, `users/${user.uid}/medical_records/${recordId}`);
979
+ await remove(recordRef);
980
+ alert('Record removed successfully');
981
+ }
982
+ } catch (error) {
983
+ console.error('Error removing record:', error);
984
+ alert('Error removing record. Please try again.');
985
+ }
986
+ }
987
+ }
988
+
989
+ async function downloadRecord(recordId) {
990
+ try {
991
+ const user = auth.currentUser;
992
+ if (user) {
993
+ const recordRef = ref(database, `users/${user.uid}/medical_records/${recordId}`);
994
+ const snapshot = await get(recordRef);
995
+ const record = snapshot.val();
996
+
997
+ if (record) {
998
+ // Create a blob from the content
999
+ const blob = new Blob([record.content], { type: record.type || 'text/plain' });
1000
+
1001
+ // Create a download link
1002
+ const url = URL.createObjectURL(blob);
1003
+ const a = document.createElement('a');
1004
+ a.href = url;
1005
+ a.download = record.name;
1006
+
1007
+ // Trigger the download
1008
+ document.body.appendChild(a);
1009
+ a.click();
1010
+
1011
+ // Clean up
1012
+ document.body.removeChild(a);
1013
+ URL.revokeObjectURL(url);
1014
+ }
1015
+ }
1016
+ } catch (error) {
1017
+ console.error('Error downloading record:', error);
1018
+ alert('Error downloading record. Please try again.');
1019
+ }
1020
+ }
1021
+
1022
+ // ECG data handling
1023
+ document.getElementById('ecgDataUpload').addEventListener('change', (e) => {
1024
+ const file = e.target.files[0];
1025
+ if (file) {
1026
+ if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
1027
+ alert('Please upload a JSON file');
1028
+ e.target.value = '';
1029
+ return;
1030
+ }
1031
+ uploadECGData(file);
1032
+ }
1033
+ });
1034
+
1035
+ async function uploadECGData(file) {
1036
+ try {
1037
+ const user = auth.currentUser;
1038
+ if (user) {
1039
+ const timestamp = new Date().toISOString();
1040
+ const recordRef = ref(database, `users/${user.uid}/ecg_data/${timestamp}`);
1041
+
1042
+ const reader = new FileReader();
1043
+ reader.onload = async (e) => {
1044
+ try {
1045
+ // Validate JSON format
1046
+ const content = e.target.result;
1047
+ const jsonData = JSON.parse(content);
1048
+
1049
+ // Store the data
1050
+ await set(recordRef, {
1051
+ filename: file.name,
1052
+ type: 'application/json',
1053
+ content: jsonData,
1054
+ uploaded_at: timestamp
1055
+ });
1056
+
1057
+ // Show analysis result
1058
+ const resultDiv = document.getElementById('ecgAnalysisResult');
1059
+ resultDiv.innerHTML = `
1060
+ <h5>ECG Analysis Result</h5>
1061
+ <pre>${JSON.stringify(analyzeECGData(jsonData), null, 2)}</pre>
1062
+ `;
1063
+ resultDiv.classList.add('show');
1064
+
1065
+ alert('ECG data uploaded and analyzed successfully');
1066
+ } catch (error) {
1067
+ console.error('Error parsing JSON:', error);
1068
+ alert('Invalid JSON format. Please upload a valid JSON file.');
1069
+ }
1070
+ };
1071
+ reader.readAsText(file);
1072
+ }
1073
+ } catch (error) {
1074
+ console.error('Error uploading ECG data:', error);
1075
+ alert('Error uploading ECG data');
1076
+ }
1077
+ }
1078
+
1079
+ function analyzeECGData(data) {
1080
+ // This is a placeholder for actual ECG analysis
1081
+ // You would implement your ECG analysis logic here
1082
+ return {
1083
+ status: 'success',
1084
+ timestamp: new Date().toISOString(),
1085
+ analysis: {
1086
+ heartRate: calculateHeartRate(data),
1087
+ anomalies: detectAnomalies(data),
1088
+ recommendations: generateRecommendations(data)
1089
+ }
1090
+ };
1091
+ }
1092
+
1093
+ function calculateHeartRate(data) {
1094
+ // Placeholder for heart rate calculation
1095
+ return '60-100 BPM (Normal)';
1096
+ }
1097
+
1098
+ function detectAnomalies(data) {
1099
+ // Placeholder for anomaly detection
1100
+ return ['No significant anomalies detected'];
1101
+ }
1102
+
1103
+ function generateRecommendations(data) {
1104
+ // Placeholder for recommendations
1105
+ return ['Continue regular monitoring'];
1106
+ }
1107
+
1108
+ // Emergency button functionality
1109
+ document.getElementById('triggerEmergency').addEventListener('click', async function() {
1110
+ if (!window.userLocation) {
1111
+ alert('Please enable location access first');
1112
+ return;
1113
+ }
1114
+
1115
+ const description = document.getElementById('emergencyDescription').value;
1116
+
1117
+ try {
1118
+ // Call all emergency contacts
1119
+ const contacts = document.querySelectorAll('.contact-item');
1120
+ contacts.forEach(contact => {
1121
+ const phone = contact.querySelector('.contact-phone').textContent;
1122
+ callContact(phone, description);
1123
+ });
1124
+
1125
+ // Send emergency alert to server
1126
+ const response = await fetch('/api/emergency', {
1127
+ method: 'POST',
1128
+ headers: {
1129
+ 'Content-Type': 'application/json'
1130
+ },
1131
+ body: JSON.stringify({
1132
+ lat: window.userLocation.lat,
1133
+ lng: window.userLocation.lng,
1134
+ description: description
1135
+ })
1136
+ });
1137
+
1138
+ const result = await response.json();
1139
+ if (result.status === 'success') {
1140
+ currentEmergency = result.emergency_id;
1141
+ startEmergencyTimer();
1142
+ showEmergencyMarker(window.userLocation.lat, window.userLocation.lng);
1143
+ }
1144
+ } catch (error) {
1145
+ console.error('Error sending emergency alert:', error);
1146
+ }
1147
+ });
1148
+
1149
+ function startEmergencyTimer() {
1150
+ let timeLeft = 300; // 5 minutes in seconds
1151
+ const timerElement = document.getElementById('emergencyTimer');
1152
+
1153
+ if (emergencyTimeout) {
1154
+ clearInterval(emergencyTimeout);
1155
+ }
1156
+
1157
+ emergencyTimeout = setInterval(() => {
1158
+ timeLeft--;
1159
+ const minutes = Math.floor(timeLeft / 60);
1160
+ const seconds = timeLeft % 60;
1161
+ timerElement.textContent = `Calling emergency services in: ${minutes}:${seconds.toString().padStart(2, '0')}`;
1162
+
1163
+ if (timeLeft <= 0) {
1164
+ clearInterval(emergencyTimeout);
1165
+ callEmergencyServices();
1166
+ }
1167
+ }, 1000);
1168
+ }
1169
+
1170
+ function callEmergencyServices() {
1171
+ alert('Emergency services have been notified!');
1172
+ document.getElementById('emergencyTimer').textContent = 'Emergency services notified';
1173
+ }
1174
+
1175
+ function showEmergencyMarker(lat, lng) {
1176
+ // Clear existing markers
1177
+ window.map.eachLayer((layer) => {
1178
+ if (layer instanceof L.Marker) {
1179
+ window.map.removeLayer(layer);
1180
+ }
1181
+ });
1182
+
1183
+ // Add emergency marker
1184
+ L.marker([lat, lng], {
1185
+ icon: L.divIcon({
1186
+ className: 'emergency-marker',
1187
+ html: '<i class="fas fa-exclamation-triangle" style="color: red; font-size: 24px;"></i>'
1188
+ })
1189
+ }).addTo(window.map);
1190
+ }
1191
+ </script>
1192
+ {% endblock %}
app/templates/index.html ADDED
@@ -0,0 +1,1716 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Heart Health Analysis System</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
9
+ <style>
10
+ :root[data-theme="dark"] {
11
+ --primary-color: #f03e5f;
12
+ --secondary-color: #f5768d;
13
+ --accent-color: #d1062a;
14
+ --text-color: #ffffff;
15
+ --text-light: #fccad3;
16
+ --background-start: #1a1a2e;
17
+ --background-end: #16213e;
18
+ --card-color: rgba(26, 26, 46, 0.8);
19
+ --border-radius: 12px;
20
+ --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
21
+ --transition: all 0.3s ease;
22
+ --placeholder-color: rgba(255, 255, 255, 0.7);
23
+ }
24
+
25
+ :root[data-theme="light"] {
26
+ --primary-color: #f03e5f;
27
+ --secondary-color: #f5768d;
28
+ --accent-color: #d1062a;
29
+ --text-color: #333333;
30
+ --text-light: #8a0018;
31
+ --background-start: #f5f5f5;
32
+ --background-end: #ffffff;
33
+ --card-color: rgba(255, 255, 255, 0.9);
34
+ --border-radius: 12px;
35
+ --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
36
+ --transition: all 0.3s ease;
37
+ --placeholder-color: rgba(138, 0, 24, 0.5);
38
+ }
39
+
40
+ body {
41
+ background: linear-gradient(135deg,
42
+ rgba(26, 26, 46, 0.95) 0%,
43
+ rgba(22, 33, 62, 0.95) 50%,
44
+ rgba(209, 6, 42, 0.2) 100%
45
+ );
46
+ color: var(--text-color);
47
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
48
+ padding: 20px;
49
+ min-height: 100vh;
50
+ height: 100vh;
51
+ position: relative;
52
+ overflow: hidden;
53
+ width: 100%;
54
+ max-width: 100vw;
55
+ }
56
+
57
+ body::before {
58
+ content: '';
59
+ position: fixed;
60
+ top: 0;
61
+ left: 0;
62
+ width: 100%;
63
+ height: 100%;
64
+ background: radial-gradient(
65
+ circle at top left,
66
+ rgba(114, 9, 183, 0.2) 0%,
67
+ transparent 50%
68
+ ),
69
+ radial-gradient(
70
+ circle at top right,
71
+ rgba(76, 201, 240, 0.1) 0%,
72
+ transparent 50%
73
+ );
74
+ z-index: -1;
75
+ }
76
+
77
+ .container {
78
+ background-color: var(--card-color);
79
+ border-radius: var(--border-radius);
80
+ padding: 30px;
81
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
82
+ backdrop-filter: blur(10px);
83
+ border: 1px solid rgba(255, 255, 255, 0.1);
84
+ max-width: 100%;
85
+ max-height: calc(100vh - 40px);
86
+ overflow-y: auto;
87
+ overflow-x: hidden;
88
+ }
89
+
90
+ .header {
91
+ text-align: center;
92
+ margin-bottom: 40px;
93
+ animation: fadeInDown 1s ease;
94
+ padding: 20px;
95
+ }
96
+
97
+ .header h1 {
98
+ background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
99
+ -webkit-background-clip: text;
100
+ -webkit-text-fill-color: transparent;
101
+ font-weight: 700;
102
+ margin-bottom: 10px;
103
+ }
104
+
105
+ .header p {
106
+ color: var(--text-light);
107
+ font-size: 1.1rem;
108
+ }
109
+
110
+ .nav-tabs {
111
+ border: none;
112
+ margin-bottom: 30px;
113
+ background: rgba(26, 26, 46, 0.6);
114
+ padding: 10px;
115
+ border-radius: var(--border-radius);
116
+ box-shadow: var(--box-shadow);
117
+ border: 1px solid rgba(255, 255, 255, 0.1);
118
+ }
119
+
120
+ .nav-tabs .nav-link {
121
+ border: none;
122
+ color: var(--text-light);
123
+ padding: 12px 24px;
124
+ border-radius: var(--border-radius);
125
+ transition: var(--transition);
126
+ font-weight: 500;
127
+ }
128
+
129
+ .nav-tabs .nav-link:hover {
130
+ background: linear-gradient(135deg, rgba(255, 77, 109, 0.2), rgba(114, 9, 183, 0.1));
131
+ color: var(--primary-color);
132
+ }
133
+
134
+ .nav-tabs .nav-link.active {
135
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
136
+ color: white;
137
+ box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
138
+ }
139
+
140
+ .tab-content {
141
+ animation: fadeIn 0.5s ease;
142
+ }
143
+
144
+ .ecg-plot {
145
+ max-width: 100%;
146
+ width: 100%;
147
+ height: auto;
148
+ margin-top: 20px;
149
+ border-radius: var(--border-radius);
150
+ box-shadow: var(--box-shadow);
151
+ animation: fadeIn 1s ease;
152
+ display: none;
153
+ }
154
+
155
+ #ecgPlotContainer {
156
+ width: 100%;
157
+ margin-top: 20px;
158
+ overflow: visible;
159
+ }
160
+
161
+ .analysis-section {
162
+ background-color: rgba(26, 26, 46, 0.6);
163
+ border-radius: var(--border-radius);
164
+ padding: 30px;
165
+ margin-bottom: 30px;
166
+ box-shadow: var(--box-shadow);
167
+ transition: var(--transition);
168
+ animation: fadeInUp 0.8s ease;
169
+ border: 1px solid rgba(255, 255, 255, 0.1);
170
+ max-width: 100%;
171
+ overflow: visible;
172
+ }
173
+
174
+ .analysis-section:hover {
175
+ transform: translateY(-5px);
176
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
177
+ }
178
+
179
+ .form-control {
180
+ border-radius: var(--border-radius);
181
+ padding: 12px;
182
+ border: 2px solid rgba(255, 255, 255, 0.1);
183
+ transition: var(--transition);
184
+ background-color: rgba(26, 26, 46, 0.8);
185
+ color: var(--text-color);
186
+ }
187
+
188
+ .form-control:focus {
189
+ background-color: rgba(26, 26, 46, 0.9);
190
+ border-color: var(--primary-color);
191
+ color: var(--text-color);
192
+ box-shadow: 0 0 0 0.2rem rgba(255, 77, 109, 0.25);
193
+ }
194
+
195
+ .form-control::placeholder {
196
+ color: var(--placeholder-color);
197
+ }
198
+
199
+ .btn-primary {
200
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
201
+ border: none;
202
+ padding: 12px 24px;
203
+ border-radius: var(--border-radius);
204
+ font-weight: 500;
205
+ transition: var(--transition);
206
+ box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
207
+ }
208
+
209
+ .btn-primary:hover {
210
+ background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
211
+ }
212
+
213
+ .result-box {
214
+ border: none;
215
+ border-radius: var(--border-radius);
216
+ padding: 20px;
217
+ margin-top: 20px;
218
+ background: linear-gradient(135deg,
219
+ rgba(76, 201, 240, 0.1),
220
+ rgba(114, 9, 183, 0.05)
221
+ );
222
+ animation: fadeIn 0.8s ease;
223
+ border: 1px solid rgba(76, 201, 240, 0.2);
224
+ }
225
+
226
+ .result-box.anomaly {
227
+ background: linear-gradient(135deg,
228
+ rgba(240, 62, 95, 0.1),
229
+ rgba(209, 6, 42, 0.05)
230
+ );
231
+ border: 1px solid rgba(240, 62, 95, 0.2);
232
+ }
233
+
234
+ .result-box.normal {
235
+ background: linear-gradient(135deg,
236
+ rgba(76, 201, 240, 0.1),
237
+ rgba(76, 201, 240, 0.05)
238
+ );
239
+ border: 1px solid rgba(76, 201, 240, 0.2);
240
+ }
241
+
242
+ .progress {
243
+ height: 25px;
244
+ border-radius: var(--border-radius);
245
+ background-color: rgba(0, 0, 0, 0.3);
246
+ margin: 15px 0;
247
+ overflow: hidden;
248
+ }
249
+
250
+ .progress-bar {
251
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
252
+ transition: width 1s ease;
253
+ }
254
+
255
+ .input-guidelines {
256
+ font-size: 0.85rem;
257
+ color: var(--text-light);
258
+ margin-top: 0.5rem;
259
+ padding-left: 5px;
260
+ }
261
+
262
+ .form-label {
263
+ font-weight: 600;
264
+ color: var(--text-color);
265
+ margin-bottom: 0.5rem;
266
+ }
267
+
268
+ .loading {
269
+ display: none;
270
+ text-align: center;
271
+ margin: 20px 0;
272
+ }
273
+
274
+ .loading i {
275
+ color: var(--primary-color);
276
+ font-size: 2rem;
277
+ animation: spin 1s linear infinite;
278
+ }
279
+
280
+ @keyframes fadeIn {
281
+ from { opacity: 0; }
282
+ to { opacity: 1; }
283
+ }
284
+
285
+ @keyframes fadeInUp {
286
+ from {
287
+ opacity: 0;
288
+ transform: translateY(20px);
289
+ }
290
+ to {
291
+ opacity: 1;
292
+ transform: translateY(0);
293
+ }
294
+ }
295
+
296
+ @keyframes fadeInDown {
297
+ from {
298
+ opacity: 0;
299
+ transform: translateY(-20px);
300
+ }
301
+ to {
302
+ opacity: 1;
303
+ transform: translateY(0);
304
+ }
305
+ }
306
+
307
+ @keyframes spin {
308
+ 0% { transform: rotate(0deg); }
309
+ 100% { transform: rotate(360deg); }
310
+ }
311
+
312
+ .alert {
313
+ border-radius: var(--border-radius);
314
+ padding: 15px;
315
+ margin-top: 20px;
316
+ animation: fadeIn 0.5s ease;
317
+ }
318
+
319
+ /* Custom scrollbar for dark theme */
320
+ ::-webkit-scrollbar {
321
+ width: 8px;
322
+ }
323
+
324
+ ::-webkit-scrollbar-track {
325
+ background: rgba(0, 0, 0, 0.2);
326
+ }
327
+
328
+ ::-webkit-scrollbar-thumb {
329
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
330
+ border-radius: 4px;
331
+ }
332
+
333
+ ::-webkit-scrollbar-thumb:hover {
334
+ background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
335
+ }
336
+
337
+ /* Custom select dropdown styles */
338
+ select.form-control {
339
+ appearance: none;
340
+ 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");
341
+ background-repeat: no-repeat;
342
+ background-position: right 12px center;
343
+ padding-right: 35px;
344
+ }
345
+
346
+ select.form-control option {
347
+ background-color: rgba(26, 26, 46, 0.95);
348
+ color: var(--text-color);
349
+ padding: 10px;
350
+ }
351
+
352
+ /* Custom file input styles */
353
+ input[type="file"].form-control {
354
+ padding: 8px;
355
+ cursor: pointer;
356
+ }
357
+
358
+ input[type="file"].form-control::-webkit-file-upload-button {
359
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
360
+ color: white;
361
+ border: none;
362
+ padding: 8px 16px;
363
+ border-radius: var(--border-radius);
364
+ margin-right: 10px;
365
+ cursor: pointer;
366
+ transition: var(--transition);
367
+ }
368
+
369
+ input[type="file"].form-control::-webkit-file-upload-button:hover {
370
+ transform: translateY(-2px);
371
+ box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
372
+ }
373
+
374
+ /* Firefox specific styles */
375
+ input[type="file"].form-control::file-selector-button {
376
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
377
+ color: white;
378
+ border: none;
379
+ padding: 8px 16px;
380
+ border-radius: var(--border-radius);
381
+ margin-right: 10px;
382
+ cursor: pointer;
383
+ transition: var(--transition);
384
+ }
385
+
386
+ input[type="file"].form-control::file-selector-button:hover {
387
+ transform: translateY(-2px);
388
+ box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
389
+ }
390
+
391
+ /* Dropdown menu styles */
392
+ .dropdown-menu {
393
+ background-color: rgba(26, 26, 46, 0.95);
394
+ border: 1px solid rgba(255, 255, 255, 0.1);
395
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
396
+ }
397
+
398
+ .dropdown-item {
399
+ color: var(--text-color);
400
+ padding: 10px 20px;
401
+ }
402
+
403
+ .dropdown-item:hover {
404
+ background-color: rgba(255, 77, 109, 0.2);
405
+ color: var(--primary-color);
406
+ }
407
+
408
+ .dropdown-item.active {
409
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
410
+ color: white;
411
+ }
412
+
413
+ .emergency-button {
414
+ position: fixed;
415
+ bottom: 30px;
416
+ right: 30px;
417
+ background: linear-gradient(135deg, var(--accent-color), #8a0018);
418
+ color: white;
419
+ border: none;
420
+ padding: 15px 30px;
421
+ border-radius: 50px;
422
+ font-size: 1.2rem;
423
+ font-weight: bold;
424
+ cursor: pointer;
425
+ transition: var(--transition);
426
+ box-shadow: 0 4px 15px rgba(255, 0, 0, 0.3);
427
+ z-index: 1000;
428
+ display: flex;
429
+ align-items: center;
430
+ gap: 10px;
431
+ }
432
+
433
+ .emergency-button:hover {
434
+ transform: translateY(-2px);
435
+ box-shadow: 0 6px 20px rgba(255, 0, 0, 0.4);
436
+ }
437
+
438
+ .emergency-button i {
439
+ font-size: 1.4rem;
440
+ }
441
+
442
+ .theme-toggle {
443
+ position: fixed;
444
+ top: 20px;
445
+ right: 20px;
446
+ background: var(--card-color);
447
+ border: 1px solid rgba(255, 255, 255, 0.1);
448
+ color: var(--text-color);
449
+ padding: 10px 20px;
450
+ border-radius: var(--border-radius);
451
+ cursor: pointer;
452
+ display: flex;
453
+ align-items: center;
454
+ gap: 8px;
455
+ transition: var(--transition);
456
+ z-index: 1000;
457
+ }
458
+
459
+ .theme-toggle:hover {
460
+ transform: translateY(-2px);
461
+ box-shadow: var(--box-shadow);
462
+ }
463
+
464
+ .theme-toggle i {
465
+ font-size: 1.2rem;
466
+ }
467
+
468
+ [data-theme="light"] .container {
469
+ background-color: var(--card-color);
470
+ border: 1px solid rgba(0, 0, 0, 0.1);
471
+ }
472
+
473
+ [data-theme="light"] .form-control {
474
+ background-color: rgba(255, 255, 255, 0.9);
475
+ border: 2px solid rgba(0, 0, 0, 0.1);
476
+ color: var(--text-color);
477
+ }
478
+
479
+ [data-theme="light"] .form-control:focus {
480
+ background-color: #ffffff;
481
+ border-color: var(--primary-color);
482
+ color: var(--text-color);
483
+ }
484
+
485
+ [data-theme="light"] .nav-tabs {
486
+ background: rgba(255, 255, 255, 0.9);
487
+ border: 1px solid rgba(0, 0, 0, 0.1);
488
+ }
489
+
490
+ [data-theme="light"] .analysis-section {
491
+ background-color: rgba(255, 255, 255, 0.9);
492
+ border: 1px solid rgba(0, 0, 0, 0.1);
493
+ }
494
+
495
+ [data-theme="light"] .result-box {
496
+ background: linear-gradient(135deg,
497
+ rgba(76, 201, 240, 0.1),
498
+ rgba(114, 9, 183, 0.05)
499
+ );
500
+ border: 1px solid rgba(76, 201, 240, 0.2);
501
+ }
502
+
503
+ [data-theme="light"] .result-box.anomaly {
504
+ background: linear-gradient(135deg,
505
+ rgba(240, 62, 95, 0.15),
506
+ rgba(209, 6, 42, 0.1)
507
+ );
508
+ border: 1px solid rgba(240, 62, 95, 0.3);
509
+ }
510
+
511
+ [data-theme="light"] .result-box.normal {
512
+ background: linear-gradient(135deg,
513
+ rgba(76, 201, 240, 0.15),
514
+ rgba(76, 201, 240, 0.1)
515
+ );
516
+ border: 1px solid rgba(76, 201, 240, 0.3);
517
+ }
518
+
519
+ /* AI Doctor specific styles */
520
+ #ai-doctor .analysis-section {
521
+ padding: 0;
522
+ background: transparent;
523
+ box-shadow: none;
524
+ height: calc(100vh - 100px);
525
+ display: flex;
526
+ flex-direction: column;
527
+ position: relative;
528
+ margin-bottom: 0;
529
+ }
530
+
531
+ #ai-doctor .header {
532
+ margin-bottom: 0;
533
+ padding: 15px 20px;
534
+ background: rgba(26, 26, 46, 0.9);
535
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
536
+ border: 1px solid rgba(255, 255, 255, 0.1);
537
+ border-bottom: none;
538
+ display: flex;
539
+ align-items: center;
540
+ justify-content: center;
541
+ gap: 15px;
542
+ z-index: 2;
543
+ text-align: center;
544
+ }
545
+
546
+ #ai-doctor .header h3 {
547
+ margin: 0;
548
+ color: var(--primary-color);
549
+ font-size: 1.2rem;
550
+ }
551
+
552
+ #ai-doctor .header p {
553
+ margin: 0;
554
+ opacity: 0.8;
555
+ font-size: 0.9rem;
556
+ }
557
+
558
+ #ai-doctor .header i {
559
+ font-size: 1.5rem;
560
+ color: var(--primary-color);
561
+ }
562
+
563
+ .chat-container {
564
+ display: flex;
565
+ flex-direction: column;
566
+ flex: 1;
567
+ background: rgba(26, 26, 46, 0.6);
568
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
569
+ overflow: hidden;
570
+ position: relative;
571
+ border: 1px solid rgba(252, 202, 211, 0.1);
572
+ border-top: none;
573
+ margin: 0;
574
+ height: calc(100% - 60px);
575
+ }
576
+
577
+ .chat-messages {
578
+ flex: 1;
579
+ overflow-y: auto;
580
+ overflow-x: hidden;
581
+ padding: 20px;
582
+ padding-bottom: 80px;
583
+ display: flex;
584
+ flex-direction: column;
585
+ gap: 15px;
586
+ background: linear-gradient(135deg,
587
+ rgba(26, 26, 46, 0.95) 0%,
588
+ rgba(22, 33, 62, 0.95) 50%,
589
+ rgba(209, 6, 42, 0.1) 100%
590
+ );
591
+ }
592
+
593
+ .message {
594
+ max-width: 75%;
595
+ padding: 12px 16px;
596
+ border-radius: 12px;
597
+ position: relative;
598
+ word-wrap: break-word;
599
+ overflow-wrap: break-word;
600
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
601
+ margin: 2px 0;
602
+ }
603
+
604
+ .message.user {
605
+ align-self: flex-end;
606
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
607
+ color: white;
608
+ border-bottom-right-radius: 4px;
609
+ margin-left: 15%;
610
+ }
611
+
612
+ .message.doctor {
613
+ align-self: flex-start;
614
+ background: rgba(252, 202, 211, 0.1);
615
+ color: var(--text-color);
616
+ border-bottom-left-radius: 4px;
617
+ margin-right: 15%;
618
+ }
619
+
620
+ .chat-input-container {
621
+ position: absolute;
622
+ bottom: 0;
623
+ left: 20px;
624
+ right: auto;
625
+ width: calc(100% - 200px);
626
+ display: flex;
627
+ padding: 12px;
628
+ background: rgba(26, 26, 46, 0.95);
629
+ border-top: 1px solid rgba(252, 202, 211, 0.1);
630
+ backdrop-filter: blur(10px);
631
+ z-index: 1;
632
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
633
+ }
634
+
635
+ .chat-input {
636
+ flex: 1;
637
+ background: rgba(252, 202, 211, 0.1);
638
+ border: 1px solid rgba(252, 202, 211, 0.2);
639
+ border-radius: 20px;
640
+ color: var(--text-color);
641
+ padding: 12px 16px;
642
+ resize: none;
643
+ margin-right: 10px;
644
+ min-height: 45px;
645
+ transition: all 0.3s ease;
646
+ font-size: 1rem;
647
+ max-width: calc(100% - 60px);
648
+ }
649
+
650
+ .chat-input:focus {
651
+ background: rgba(252, 202, 211, 0.15);
652
+ border-color: var(--primary-color);
653
+ outline: none;
654
+ box-shadow: 0 0 0 2px rgba(240, 62, 95, 0.2);
655
+ }
656
+
657
+ #sendMessage {
658
+ align-self: flex-end;
659
+ padding: 12px 20px;
660
+ min-width: 45px;
661
+ font-weight: 500;
662
+ letter-spacing: 0.5px;
663
+ border-radius: 20px;
664
+ flex-shrink: 0;
665
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
666
+ }
667
+
668
+ #sendMessage:hover {
669
+ background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
670
+ }
671
+
672
+ .loading {
673
+ position: absolute;
674
+ bottom: 70px;
675
+ left: 50%;
676
+ transform: translateX(-50%);
677
+ background: rgba(26, 26, 46, 0.9);
678
+ padding: 10px 20px;
679
+ border-radius: 20px;
680
+ display: none;
681
+ align-items: center;
682
+ gap: 10px;
683
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
684
+ z-index: 1;
685
+ border: 1px solid rgba(252, 202, 211, 0.1);
686
+ }
687
+
688
+ .loading i {
689
+ color: var(--primary-color);
690
+ font-size: 1.2rem;
691
+ animation: spin 1s linear infinite;
692
+ }
693
+
694
+ .loading p {
695
+ margin: 0;
696
+ font-size: 0.9rem;
697
+ color: var(--text-light);
698
+ }
699
+
700
+ [data-theme="light"] select.form-control {
701
+ 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");
702
+ }
703
+
704
+ [data-theme="light"] select.form-control option {
705
+ background-color: rgba(255, 255, 255, 0.95);
706
+ color: var(--text-color);
707
+ }
708
+
709
+ [data-theme="light"] .chat-container {
710
+ background: rgba(252, 202, 211, 0.1);
711
+ border: 1px solid rgba(138, 0, 24, 0.1);
712
+ }
713
+
714
+ [data-theme="light"] .chat-messages {
715
+ background: linear-gradient(135deg,
716
+ rgba(252, 202, 211, 0.1) 0%,
717
+ rgba(245, 118, 141, 0.1) 50%,
718
+ rgba(240, 62, 95, 0.05) 100%
719
+ );
720
+ }
721
+
722
+ [data-theme="light"] .chat-input-container {
723
+ background: rgba(255, 255, 255, 0.95);
724
+ border-top: 1px solid rgba(138, 0, 24, 0.1);
725
+ }
726
+
727
+ [data-theme="light"] .chat-input {
728
+ background: rgba(255, 255, 255, 0.95);
729
+ border: 2px solid var(--accent-color);
730
+ color: var(--text-color);
731
+ }
732
+
733
+ [data-theme="light"] .chat-input:focus {
734
+ background: rgba(255, 255, 255, 0.95);
735
+ border-color: var(--accent-color);
736
+ box-shadow: 0 0 0 2px rgba(209, 6, 42, 0.2);
737
+ }
738
+
739
+ [data-theme="light"] .chat-input::placeholder {
740
+ color: rgba(138, 0, 24, 0.6);
741
+ }
742
+
743
+ [data-theme="light"] .loading {
744
+ background: rgba(255, 255, 255, 0.95);
745
+ border: 1px solid rgba(138, 0, 24, 0.1);
746
+ }
747
+
748
+ [data-theme="light"] .message.doctor {
749
+ background: rgba(252, 202, 211, 0.2);
750
+ }
751
+
752
+ [data-theme="light"] #ai-doctor .header {
753
+ background: rgba(252, 202, 211, 0.2);
754
+ border: 1px solid rgba(138, 0, 24, 0.1);
755
+ border-bottom: none;
756
+ }
757
+
758
+ [data-theme="light"] #ai-doctor .header h3 {
759
+ color: var(--accent-color);
760
+ }
761
+
762
+ [data-theme="light"] #ai-doctor .header p {
763
+ color: var(--text-light);
764
+ }
765
+
766
+ [data-theme="light"] #ai-doctor .header i {
767
+ color: var(--accent-color);
768
+ }
769
+
770
+ .header-content {
771
+ display: flex;
772
+ justify-content: space-between;
773
+ align-items: center;
774
+ width: 100%;
775
+ padding: 0 20px;
776
+ }
777
+
778
+ .btn-logout {
779
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
780
+ border: none;
781
+ color: white;
782
+ padding: 8px 16px;
783
+ border-radius: var(--border-radius);
784
+ font-weight: 500;
785
+ cursor: pointer;
786
+ transition: var(--transition);
787
+ display: flex;
788
+ align-items: center;
789
+ gap: 8px;
790
+ }
791
+
792
+ .btn-logout:hover {
793
+ transform: translateY(-2px);
794
+ box-shadow: 0 4px 12px rgba(240, 62, 95, 0.3);
795
+ }
796
+
797
+ .btn-logout i {
798
+ font-size: 1.1em;
799
+ }
800
+
801
+ .analysis-container {
802
+ padding: 20px;
803
+ }
804
+
805
+ .analysis-header {
806
+ text-align: center;
807
+ margin-bottom: 30px;
808
+ }
809
+
810
+ .analysis-header h3 {
811
+ color: var(--primary-color);
812
+ margin-bottom: 10px;
813
+ }
814
+
815
+ .analysis-inputs {
816
+ display: grid;
817
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
818
+ gap: 20px;
819
+ margin-bottom: 30px;
820
+ }
821
+
822
+ .input-section {
823
+ background: rgba(255, 255, 255, 0.05);
824
+ padding: 15px;
825
+ border-radius: var(--border-radius);
826
+ }
827
+
828
+ .input-section h4 {
829
+ color: var(--text-light);
830
+ margin-bottom: 10px;
831
+ }
832
+
833
+ .result-box {
834
+ background: rgba(0, 0, 0, 0.2);
835
+ padding: 10px;
836
+ border-radius: var(--border-radius);
837
+ min-height: 100px;
838
+ max-height: 200px;
839
+ overflow-y: auto;
840
+ }
841
+
842
+ #generateAnalysis {
843
+ width: 100%;
844
+ padding: 15px;
845
+ margin: 20px 0;
846
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
847
+ border: none;
848
+ border-radius: var(--border-radius);
849
+ color: white;
850
+ font-size: 1.1rem;
851
+ cursor: pointer;
852
+ transition: var(--transition);
853
+ }
854
+
855
+ #generateAnalysis:hover {
856
+ transform: translateY(-2px);
857
+ box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3);
858
+ }
859
+
860
+ .analysis-result {
861
+ background: rgba(255, 255, 255, 0.05);
862
+ padding: 20px;
863
+ border-radius: var(--border-radius);
864
+ margin-top: 20px;
865
+ display: none;
866
+ }
867
+
868
+ .analysis-result.show {
869
+ display: block;
870
+ }
871
+ </style>
872
+ </head>
873
+ <body>
874
+ <button class="theme-toggle" onclick="toggleTheme()">
875
+ <i class="fas fa-moon"></i>
876
+ <span>Dark Mode</span>
877
+ </button>
878
+
879
+ <div class="container">
880
+ <div class="header">
881
+ <div class="header-content">
882
+ <h1>Heart Health Analysis System</h1>
883
+ <button onclick="logout()" class="btn-logout">
884
+ <i class="fas fa-sign-out-alt"></i> Logout
885
+ </button>
886
+ </div>
887
+ </div>
888
+
889
+ <ul class="nav nav-tabs" id="analysisTabs" role="tablist">
890
+ <li class="nav-item" role="presentation">
891
+ <button class="nav-link active" id="heart-risk-tab" data-bs-toggle="tab" data-bs-target="#heart-risk" type="button" role="tab">
892
+ <i class="fas fa-heart"></i> Heart Disease Risk
893
+ </button>
894
+ </li>
895
+ <li class="nav-item" role="presentation">
896
+ <button class="nav-link" id="ecg-tab" data-bs-toggle="tab" data-bs-target="#ecg" type="button" role="tab">
897
+ <i class="fas fa-heartbeat"></i> ECG Analysis
898
+ </button>
899
+ </li>
900
+ <li class="nav-item" role="presentation">
901
+ <button class="nav-link" id="heart-sound-tab" data-bs-toggle="tab" data-bs-target="#heart-sound" type="button" role="tab">
902
+ <i class="fas fa-stethoscope"></i> Heart Sound Analysis
903
+ </button>
904
+ </li>
905
+ <li class="nav-item" role="presentation">
906
+ <button class="nav-link" id="ai-doctor-tab" data-bs-toggle="tab" data-bs-target="#ai-doctor" type="button" role="tab">
907
+ <i class="fas fa-robot"></i> AI Doctor
908
+ </button>
909
+ </li>
910
+ </ul>
911
+
912
+ <div class="tab-content" id="analysisTabContent">
913
+ <!-- Heart Disease Risk Analysis -->
914
+ <div class="tab-pane fade show active" id="heart-risk" role="tabpanel">
915
+ <div class="analysis-section">
916
+ <h3><i class="fas fa-chart-line"></i> Heart Disease Risk Analysis</h3>
917
+ <form id="heartRiskForm">
918
+ <div class="row">
919
+ <div class="col-md-6 mb-3">
920
+ <label for="age" class="form-label">Age of Patient</label>
921
+ <input type="number" class="form-control" id="age" min="25" max="75" placeholder="Enter age (25-75)" required>
922
+ </div>
923
+ <div class="col-md-6 mb-3">
924
+ <label for="sex" class="form-label">Gender</label>
925
+ <select class="form-control" id="sex" required>
926
+ <option value="1">Male</option>
927
+ <option value="0">Female</option>
928
+ </select>
929
+ </div>
930
+ <div class="col-md-6 mb-3">
931
+ <label for="cp" class="form-label">Category of Chest Pain</label>
932
+ <select class="form-control" id="cp" required>
933
+ <option value="1">Atypical Angina</option>
934
+ <option value="0">Typical Angina</option>
935
+ <option value="3">Asymptomatic</option>
936
+ <option value="2">Non-Angina</option>
937
+ </select>
938
+ </div>
939
+ <div class="col-md-6 mb-3">
940
+ <label for="trestbps" class="form-label">Resting Blood Pressure</label>
941
+ <input type="number" class="form-control" id="trestbps" min="94" max="200" placeholder="Enter blood pressure (94-200 mmHg)" required>
942
+ </div>
943
+ <div class="col-md-6 mb-3">
944
+ <label for="chol" class="form-label">Cholesterol</label>
945
+ <input type="number" class="form-control" id="chol" min="126" max="564" placeholder="Enter cholesterol level (126-564 mg/dL)" required>
946
+ </div>
947
+ <div class="col-md-6 mb-3">
948
+ <label for="fbs" class="form-label">Fasting Blood Sugar</label>
949
+ <select class="form-control" id="fbs" required>
950
+ <option value="0">Less than 120 mg/dL (Normal)</option>
951
+ <option value="1">120 mg/dL or more (High)</option>
952
+ </select>
953
+ </div>
954
+ <div class="col-md-6 mb-3">
955
+ <label for="restecg" class="form-label">Resting ECG Result</label>
956
+ <select class="form-control" id="restecg" required>
957
+ <option value="0">Normal</option>
958
+ <option value="1">ST-T Wave Abnormalities</option>
959
+ <option value="2">Left Ventricular Hypertrophy</option>
960
+ </select>
961
+ </div>
962
+ <div class="col-md-6 mb-3">
963
+ <label for="thalach" class="form-label">Maximum Heart Rate</label>
964
+ <input type="number" class="form-control" id="thalach" min="71" max="202" placeholder="Enter heart rate (71-202 bpm)" required>
965
+ </div>
966
+ <div class="col-md-6 mb-3">
967
+ <label for="exang" class="form-label">Exercise Induced Angina</label>
968
+ <select class="form-control" id="exang" required>
969
+ <option value="1">Yes</option>
970
+ <option value="0">No</option>
971
+ </select>
972
+ </div>
973
+ <div class="col-md-6 mb-3">
974
+ <label for="oldpeak" class="form-label">ST Depression Induced by Exercise</label>
975
+ <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>
976
+ </div>
977
+ <div class="col-md-6 mb-3">
978
+ <label for="slope" class="form-label">Slope of Peak Exercise ST Segment</label>
979
+ <select class="form-control" id="slope" required>
980
+ <option value="0">Upsloping</option>
981
+ <option value="1">Flat</option>
982
+ <option value="2">Downsloping</option>
983
+ </select>
984
+ </div>
985
+ <div class="col-md-6 mb-3">
986
+ <label for="ca" class="form-label">Number of Major Vessels Colored by Fluoroscopy</label>
987
+ <input type="number" class="form-control" id="ca" min="0" max="3" placeholder="Enter number of vessels (0-3)" required>
988
+ </div>
989
+ <div class="col-md-6 mb-3">
990
+ <label for="thal" class="form-label">Thalassemia</label>
991
+ <select class="form-control" id="thal" required>
992
+ <option value="0">Absent / Normal</option>
993
+ <option value="1">Mild Thalassemia</option>
994
+ <option value="2">Moderate Thalassemia</option>
995
+ <option value="3">Severe Thalassemia</option>
996
+ </select>
997
+ </div>
998
+ </div>
999
+ <button type="submit" class="btn btn-primary">
1000
+ <i class="fas fa-chart-pie"></i> Analyze Heart Disease Risk
1001
+ </button>
1002
+ </form>
1003
+ <div class="loading" id="heartLoading">
1004
+ <i class="fas fa-spinner"></i>
1005
+ <p>Analyzing heart disease risk...</p>
1006
+ </div>
1007
+ <div id="heartRiskResult" class="result-box">
1008
+ <h4><i class="fas fa-clipboard-list"></i> Analysis Results</h4>
1009
+ <div class="progress mb-3">
1010
+ <div id="riskProgress" class="progress-bar" role="progressbar"></div>
1011
+ </div>
1012
+ <p id="riskPrediction"></p>
1013
+ <p id="riskIndicators"></p>
1014
+ </div>
1015
+ </div>
1016
+ </div>
1017
+
1018
+ <!-- ECG Analysis -->
1019
+ <div class="tab-pane fade" id="ecg" role="tabpanel">
1020
+ <div class="analysis-section">
1021
+ <h3><i class="fas fa-heartbeat"></i> ECG Analysis</h3>
1022
+ <form id="ecgForm">
1023
+ <div class="mb-3">
1024
+ <label for="ecgFile" class="form-label">Upload ECG Data (JSON file)</label>
1025
+ <input type="file" class="form-control" id="ecgFile" accept=".json" required>
1026
+ <div class="input-guidelines">Upload a JSON file containing 141 ECG values in the format: {"ecg_values": [value1, value2, ...]}</div>
1027
+ </div>
1028
+ <button type="submit" class="btn btn-primary">
1029
+ <i class="fas fa-chart-line"></i> Analyze ECG
1030
+ </button>
1031
+ </form>
1032
+ <div class="loading" id="ecgLoading">
1033
+ <i class="fas fa-spinner"></i>
1034
+ <p>Analyzing ECG data...</p>
1035
+ </div>
1036
+ <div id="ecgResult" class="result-box">
1037
+ <h4><i class="fas fa-clipboard-list"></i> ECG Analysis Results</h4>
1038
+ <p id="ecgPrediction"></p>
1039
+ </div>
1040
+ <div id="ecgPlotContainer" style="width: 100%; margin-top: 20px;">
1041
+ <img id="ecgPlot" class="ecg-plot" src="" alt="ECG Plot">
1042
+ </div>
1043
+ </div>
1044
+ </div>
1045
+
1046
+ <!-- Heart Sound Analysis -->
1047
+ <div class="tab-pane fade" id="heart-sound" role="tabpanel">
1048
+ <div class="analysis-section">
1049
+ <h3><i class="fas fa-stethoscope"></i> Heart Sound Analysis</h3>
1050
+ <form id="heartSoundForm">
1051
+ <div class="mb-3">
1052
+ <label for="audioFile" class="form-label">Upload Heart Sound (WAV format)</label>
1053
+ <input type="file" class="form-control" id="audioFile" accept=".wav" required>
1054
+ <div class="input-guidelines">Upload a WAV file containing heart sound recording</div>
1055
+ </div>
1056
+ <button type="submit" class="btn btn-primary">
1057
+ <i class="fas fa-wave-square"></i> Analyze Heart Sound
1058
+ </button>
1059
+ </form>
1060
+ <div class="loading" id="soundLoading">
1061
+ <i class="fas fa-spinner"></i>
1062
+ <p>Analyzing heart sound...</p>
1063
+ </div>
1064
+ <div id="heartSoundResult" class="result-box">
1065
+ <h4><i class="fas fa-clipboard-list"></i> Heart Sound Analysis Results</h4>
1066
+ <p id="soundPrediction"></p>
1067
+ <p id="soundConfidence"></p>
1068
+ </div>
1069
+ </div>
1070
+ </div>
1071
+
1072
+ <!-- AI Doctor -->
1073
+ <div class="tab-pane fade" id="ai-doctor" role="tabpanel">
1074
+ <div class="analysis-section">
1075
+ <div class="header">
1076
+ <i class="fas fa-robot"></i>
1077
+ <div>
1078
+ <h3>AI Doctor</h3>
1079
+ <p>Your personal cardiologist assistant</p>
1080
+ </div>
1081
+ </div>
1082
+
1083
+ <div class="chat-container">
1084
+ <div class="chat-messages" id="chatMessages">
1085
+ <div class="message doctor">
1086
+ <div class="message-content">
1087
+ <p>Hello! I'm your AI cardiologist. How can I help you with your heart health today?</p>
1088
+ </div>
1089
+ <div class="message-time">Just now</div>
1090
+ </div>
1091
+ </div>
1092
+
1093
+ <div class="chat-input-container">
1094
+ <textarea id="userMessage" class="chat-input" placeholder="Type your message..." rows="1"></textarea>
1095
+ <button id="sendMessage" class="btn btn-primary">
1096
+ <i class="fas fa-paper-plane"></i>
1097
+ </button>
1098
+ </div>
1099
+ </div>
1100
+
1101
+ <div class="loading" id="aiDoctorLoading">
1102
+ <i class="fas fa-spinner"></i>
1103
+ <p>AI Doctor is typing...</p>
1104
+ </div>
1105
+ </div>
1106
+ </div>
1107
+
1108
+ <!-- AI Analysis -->
1109
+ <div class="tab-pane" id="ai-analysis">
1110
+ <div class="analysis-container">
1111
+ <div class="analysis-header">
1112
+ <h3>AI Analysis Report</h3>
1113
+ <p>Comprehensive analysis of your heart health based on all test results</p>
1114
+ </div>
1115
+ <div class="analysis-content">
1116
+ <div class="analysis-inputs">
1117
+ <div class="input-section">
1118
+ <h4>Heart Disease Risk Results</h4>
1119
+ <div id="heartDiseaseResults" class="result-box"></div>
1120
+ </div>
1121
+ <div class="input-section">
1122
+ <h4>ECG Analysis Results</h4>
1123
+ <div id="ecgResults" class="result-box"></div>
1124
+ </div>
1125
+ <div class="input-section">
1126
+ <h4>Heart Sound Analysis Results</h4>
1127
+ <div id="heartSoundResults" class="result-box"></div>
1128
+ </div>
1129
+ </div>
1130
+ <button id="generateAnalysis" class="btn btn-primary">
1131
+ <i class="fas fa-robot"></i> Generate Analysis Report
1132
+ </button>
1133
+ <div id="aiAnalysisResult" class="analysis-result">
1134
+ <!-- AI Analysis result will be displayed here -->
1135
+ </div>
1136
+ </div>
1137
+ </div>
1138
+ </div>
1139
+ </div>
1140
+ </div>
1141
+
1142
+ <a href="/emergency" class="emergency-button">
1143
+ <i class="fas fa-exclamation-triangle"></i> EMERGENCY
1144
+ </a>
1145
+
1146
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
1147
+ <script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-app-compat.js"></script>
1148
+ <script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-auth-compat.js"></script>
1149
+ <script>
1150
+ // Firebase configuration
1151
+ const firebaseConfig = {{ firebase_config|tojson|safe }};
1152
+
1153
+ // Initialize Firebase
1154
+ firebase.initializeApp(firebaseConfig);
1155
+
1156
+ // Check authentication state
1157
+ firebase.auth().onAuthStateChanged((user) => {
1158
+ if (!user) {
1159
+ window.location.href = '/login';
1160
+ }
1161
+ });
1162
+
1163
+ // Logout function
1164
+ function logout() {
1165
+ // First, sign out from Firebase
1166
+ firebase.auth().signOut()
1167
+ .then(() => {
1168
+ // Then call server-side logout
1169
+ return fetch('/logout', {
1170
+ method: 'GET',
1171
+ headers: {
1172
+ 'Accept': 'application/json',
1173
+ 'Content-Type': 'application/json'
1174
+ }
1175
+ });
1176
+ })
1177
+ .then(response => {
1178
+ // Clear any local storage or session storage
1179
+ localStorage.clear();
1180
+ sessionStorage.clear();
1181
+
1182
+ // Redirect to login page
1183
+ window.location.href = '/login';
1184
+ })
1185
+ .catch(error => {
1186
+ console.error('Logout error:', error);
1187
+ // Even if there's an error, try to redirect to login
1188
+ window.location.href = '/login';
1189
+ });
1190
+ }
1191
+
1192
+ // Add loading indicators
1193
+ document.querySelectorAll('form').forEach(form => {
1194
+ form.addEventListener('submit', function() {
1195
+ const loadingId = this.id.replace('Form', 'Loading');
1196
+ const resultBox = document.getElementById(this.id.replace('Form', 'Result'));
1197
+ document.getElementById(loadingId).style.display = 'block';
1198
+ resultBox.style.display = 'none';
1199
+
1200
+ // Hide ECG plot when form is submitted
1201
+ if (this.id === 'ecgForm') {
1202
+ document.getElementById('ecgPlot').style.display = 'none';
1203
+ }
1204
+ });
1205
+ });
1206
+
1207
+ // AI Doctor Chat Functionality
1208
+ document.addEventListener('DOMContentLoaded', function() {
1209
+ const sendButton = document.getElementById('sendMessage');
1210
+ const userMessageInput = document.getElementById('userMessage');
1211
+ const chatMessages = document.getElementById('chatMessages');
1212
+ const aiDoctorLoading = document.getElementById('aiDoctorLoading');
1213
+
1214
+ // Send message when button is clicked
1215
+ sendButton.addEventListener('click', sendMessage);
1216
+
1217
+ // Send message when Enter key is pressed (but allow Shift+Enter for new lines)
1218
+ userMessageInput.addEventListener('keydown', function(e) {
1219
+ if (e.key === 'Enter' && !e.shiftKey) {
1220
+ e.preventDefault();
1221
+ sendMessage();
1222
+ }
1223
+ });
1224
+
1225
+ function sendMessage() {
1226
+ const message = userMessageInput.value.trim();
1227
+ if (!message) return;
1228
+
1229
+ // Add user message to chat
1230
+ addMessage(message, 'user');
1231
+
1232
+ // Clear input
1233
+ userMessageInput.value = '';
1234
+
1235
+ // Show loading indicator
1236
+ aiDoctorLoading.style.display = 'block';
1237
+
1238
+ // Send to server
1239
+ fetch('/ai_doctor', {
1240
+ method: 'POST',
1241
+ headers: {
1242
+ 'Content-Type': 'application/json'
1243
+ },
1244
+ body: JSON.stringify({ query: message })
1245
+ })
1246
+ .then(response => response.json())
1247
+ .then(data => {
1248
+ // Hide loading indicator
1249
+ aiDoctorLoading.style.display = 'none';
1250
+
1251
+ // Add AI response to chat
1252
+ if (data.response) {
1253
+ addMessage(data.response, 'doctor');
1254
+ } else {
1255
+ addMessage('I apologize, but I encountered an error processing your request. Please try again.', 'doctor');
1256
+ }
1257
+ })
1258
+ .catch(error => {
1259
+ console.error('Error:', error);
1260
+ aiDoctorLoading.style.display = 'none';
1261
+ addMessage('I apologize, but I encountered an error processing your request. Please try again.', 'doctor');
1262
+ });
1263
+ }
1264
+
1265
+ function addMessage(text, sender) {
1266
+ const messageDiv = document.createElement('div');
1267
+ messageDiv.className = `message ${sender}`;
1268
+
1269
+ const contentDiv = document.createElement('div');
1270
+ contentDiv.className = 'message-content';
1271
+
1272
+ const paragraph = document.createElement('p');
1273
+ paragraph.textContent = text;
1274
+ contentDiv.appendChild(paragraph);
1275
+
1276
+ const timeDiv = document.createElement('div');
1277
+ timeDiv.className = 'message-time';
1278
+ timeDiv.textContent = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
1279
+
1280
+ messageDiv.appendChild(contentDiv);
1281
+ messageDiv.appendChild(timeDiv);
1282
+
1283
+ chatMessages.appendChild(messageDiv);
1284
+
1285
+ // Scroll to bottom
1286
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1287
+ }
1288
+ });
1289
+
1290
+ // Heart Disease Risk Analysis
1291
+ document.getElementById('heartRiskForm').addEventListener('submit', async (e) => {
1292
+ e.preventDefault();
1293
+ const loading = document.getElementById('heartLoading');
1294
+ const resultBox = document.getElementById('heartRiskResult');
1295
+
1296
+ try {
1297
+ loading.style.display = 'block';
1298
+ resultBox.style.display = 'none';
1299
+
1300
+ const data = {
1301
+ age: document.getElementById('age').value,
1302
+ sex: document.getElementById('sex').value,
1303
+ cp: document.getElementById('cp').value,
1304
+ trestbps: document.getElementById('trestbps').value,
1305
+ chol: document.getElementById('chol').value,
1306
+ fbs: document.getElementById('fbs').value,
1307
+ restecg: document.getElementById('restecg').value,
1308
+ thalach: document.getElementById('thalach').value,
1309
+ exang: document.getElementById('exang').value,
1310
+ oldpeak: document.getElementById('oldpeak').value,
1311
+ slope: document.getElementById('slope').value,
1312
+ ca: document.getElementById('ca').value,
1313
+ thal: document.getElementById('thal').value
1314
+ };
1315
+
1316
+ const response = await fetch('/analyze_heart', {
1317
+ method: 'POST',
1318
+ headers: {
1319
+ 'Content-Type': 'application/json'
1320
+ },
1321
+ body: JSON.stringify(data)
1322
+ });
1323
+
1324
+ const result = await response.json();
1325
+
1326
+ loading.style.display = 'none';
1327
+ resultBox.style.display = 'block';
1328
+
1329
+ const probability = result.probability * 100;
1330
+ const progressBar = document.getElementById('riskProgress');
1331
+ progressBar.style.width = `${probability}%`;
1332
+ progressBar.textContent = `${probability.toFixed(1)}%`;
1333
+ progressBar.className = `progress-bar ${probability > 50 ? 'bg-danger' : 'bg-success'}`;
1334
+
1335
+ document.getElementById('riskPrediction').textContent =
1336
+ `Prediction: ${result.prediction ? 'High Risk' : 'Low Risk'}`;
1337
+ document.getElementById('riskIndicators').textContent =
1338
+ `High Risk Indicators: ${result.high_risk_indicators}, Low Risk Indicators: ${result.low_risk_indicators}`;
1339
+
1340
+ // Update the AI Analysis tab with Heart Disease Risk results
1341
+ const heartDiseaseResultsBox = document.getElementById('heartDiseaseResults');
1342
+ if (heartDiseaseResultsBox) {
1343
+ heartDiseaseResultsBox.innerHTML = `
1344
+ <div class="result-content">
1345
+ <p><strong>Risk Level:</strong> ${result.prediction ? 'High Risk' : 'Low Risk'}</p>
1346
+ <p><strong>Probability:</strong> ${probability.toFixed(1)}%</p>
1347
+ <p><strong>High Risk Indicators:</strong> ${result.high_risk_indicators}</p>
1348
+ <p><strong>Low Risk Indicators:</strong> ${result.low_risk_indicators}</p>
1349
+ </div>
1350
+ `;
1351
+ }
1352
+ } catch (error) {
1353
+ console.error('Error:', error);
1354
+ loading.style.display = 'none';
1355
+ resultBox.style.display = 'block';
1356
+ resultBox.innerHTML = `<div class="alert alert-danger">An error occurred during analysis</div>`;
1357
+ }
1358
+ });
1359
+
1360
+ // ECG Analysis
1361
+ document.getElementById('ecgForm').addEventListener('submit', async (e) => {
1362
+ e.preventDefault();
1363
+ const loading = document.getElementById('ecgLoading');
1364
+ const resultBox = document.getElementById('ecgResult');
1365
+ const file = document.getElementById('ecgFile').files[0];
1366
+
1367
+ if (!file) {
1368
+ alert('Please select a JSON file');
1369
+ return;
1370
+ }
1371
+
1372
+ try {
1373
+ loading.style.display = 'block';
1374
+ resultBox.style.display = 'none';
1375
+
1376
+ const text = await file.text();
1377
+ const data = JSON.parse(text);
1378
+
1379
+ const response = await fetch('/analyze_ecg', {
1380
+ method: 'POST',
1381
+ headers: {
1382
+ 'Content-Type': 'application/json'
1383
+ },
1384
+ body: JSON.stringify(data)
1385
+ });
1386
+
1387
+ const result = await response.json();
1388
+
1389
+ loading.style.display = 'none';
1390
+ resultBox.style.display = 'block';
1391
+
1392
+ // Update result box class based on analysis
1393
+ resultBox.className = `result-box ${result.is_anomaly ? 'anomaly' : 'normal'}`;
1394
+
1395
+ document.getElementById('ecgPrediction').textContent =
1396
+ `ECG Analysis: ${result.is_anomaly ? 'Anomaly Detected' : 'Normal ECG'}`;
1397
+
1398
+ if (result.status === 'success' && result.plot_url) {
1399
+ const plotImage = document.getElementById('ecgPlot');
1400
+ plotImage.style.display = 'block';
1401
+ plotImage.src = `data:image/png;base64,${result.plot_url}`;
1402
+ } else {
1403
+ document.getElementById('ecgPlot').style.display = 'none';
1404
+ console.error('No plot URL received from server');
1405
+ }
1406
+
1407
+ // Update the AI Analysis tab with ECG results
1408
+ const ecgResultsBox = document.getElementById('ecgResults');
1409
+ if (ecgResultsBox) {
1410
+ ecgResultsBox.innerHTML = `
1411
+ <div class="result-content">
1412
+ <p><strong>ECG Analysis:</strong> ${result.is_anomaly ? 'Anomaly Detected' : 'Normal ECG'}</p>
1413
+ <p><strong>Status:</strong> ${result.status}</p>
1414
+ ${result.plot_url ? `<img src="data:image/png;base64,${result.plot_url}" alt="ECG Plot" style="max-width: 100%; margin-top: 10px;">` : ''}
1415
+ </div>
1416
+ `;
1417
+ }
1418
+ } catch (error) {
1419
+ console.error('Error:', error);
1420
+ loading.style.display = 'none';
1421
+ resultBox.style.display = 'block';
1422
+ resultBox.innerHTML = `<div class="alert alert-danger">An error occurred during analysis. Please ensure the JSON file is properly formatted.</div>`;
1423
+ }
1424
+ });
1425
+
1426
+ // Heart Sound Analysis
1427
+ document.getElementById('heartSoundForm').addEventListener('submit', async (e) => {
1428
+ e.preventDefault();
1429
+ const loading = document.getElementById('soundLoading');
1430
+ const resultBox = document.getElementById('heartSoundResult');
1431
+ const audioFile = document.getElementById('audioFile').files[0];
1432
+
1433
+ if (!audioFile) {
1434
+ alert('Please select an audio file');
1435
+ return;
1436
+ }
1437
+
1438
+ try {
1439
+ loading.style.display = 'block';
1440
+ resultBox.style.display = 'none';
1441
+
1442
+ const formData = new FormData();
1443
+ formData.append('audio', audioFile);
1444
+
1445
+ const response = await fetch('/analyze_audio', {
1446
+ method: 'POST',
1447
+ body: formData
1448
+ });
1449
+
1450
+ const result = await response.json();
1451
+
1452
+ loading.style.display = 'none';
1453
+ resultBox.style.display = 'block';
1454
+
1455
+ document.getElementById('soundPrediction').textContent =
1456
+ `Detected Condition: ${result.disease}`;
1457
+ document.getElementById('soundConfidence').textContent =
1458
+ `Confidence: ${result.confidence}%`;
1459
+
1460
+ // Update the AI Analysis tab with Heart Sound results
1461
+ const heartSoundResultsBox = document.getElementById('heartSoundResults');
1462
+ if (heartSoundResultsBox) {
1463
+ heartSoundResultsBox.innerHTML = `
1464
+ <div class="result-content">
1465
+ <p><strong>Detected Condition:</strong> ${result.disease}</p>
1466
+ <p><strong>Confidence Level:</strong> ${result.confidence}%</p>
1467
+ <p><strong>Analysis Status:</strong> ${result.status || 'Completed'}</p>
1468
+ </div>
1469
+ `;
1470
+ }
1471
+ } catch (error) {
1472
+ console.error('Error:', error);
1473
+ loading.style.display = 'none';
1474
+ resultBox.style.display = 'block';
1475
+ resultBox.innerHTML = `<div class="alert alert-danger">An error occurred during analysis</div>`;
1476
+ }
1477
+ });
1478
+
1479
+ // Theme toggle functionality
1480
+ function toggleTheme() {
1481
+ const html = document.documentElement;
1482
+ const themeToggle = document.querySelector('.theme-toggle');
1483
+ const currentTheme = html.getAttribute('data-theme');
1484
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
1485
+
1486
+ html.setAttribute('data-theme', newTheme);
1487
+ localStorage.setItem('theme', newTheme);
1488
+
1489
+ // Update toggle button
1490
+ const icon = themeToggle.querySelector('i');
1491
+ const text = themeToggle.querySelector('span');
1492
+ if (newTheme === 'dark') {
1493
+ icon.className = 'fas fa-moon';
1494
+ text.textContent = 'Dark Mode';
1495
+ } else {
1496
+ icon.className = 'fas fa-sun';
1497
+ text.textContent = 'Light Mode';
1498
+ }
1499
+ }
1500
+
1501
+ // Set initial theme
1502
+ document.addEventListener('DOMContentLoaded', () => {
1503
+ const savedTheme = localStorage.getItem('theme') || 'dark';
1504
+ document.documentElement.setAttribute('data-theme', savedTheme);
1505
+
1506
+ // Update toggle button
1507
+ const themeToggle = document.querySelector('.theme-toggle');
1508
+ const icon = themeToggle.querySelector('i');
1509
+ const text = themeToggle.querySelector('span');
1510
+ if (savedTheme === 'dark') {
1511
+ icon.className = 'fas fa-moon';
1512
+ text.textContent = 'Dark Mode';
1513
+ } else {
1514
+ icon.className = 'fas fa-sun';
1515
+ text.textContent = 'Light Mode';
1516
+ }
1517
+ });
1518
+
1519
+ // Add AI Analysis tab to the navigation
1520
+ document.addEventListener('DOMContentLoaded', function() {
1521
+ const navTabs = document.querySelector('.nav-tabs');
1522
+ const aiAnalysisTab = document.createElement('li');
1523
+ aiAnalysisTab.className = 'nav-item';
1524
+ aiAnalysisTab.innerHTML = `
1525
+ <a class="nav-link" data-bs-toggle="tab" href="#ai-analysis">
1526
+ <i class="fas fa-robot"></i> AI Analysis
1527
+ </a>
1528
+ `;
1529
+ navTabs.appendChild(aiAnalysisTab);
1530
+ });
1531
+
1532
+ // Function to collect results from all tabs
1533
+ function collectResults() {
1534
+ // Get heart disease risk parameters
1535
+ const heartParams = {
1536
+ age: document.getElementById('age').value,
1537
+ sex: document.getElementById('sex').value,
1538
+ cp: document.getElementById('cp').value,
1539
+ trestbps: document.getElementById('trestbps').value,
1540
+ chol: document.getElementById('chol').value,
1541
+ fbs: document.getElementById('fbs').value,
1542
+ restecg: document.getElementById('restecg').value,
1543
+ thalach: document.getElementById('thalach').value,
1544
+ exang: document.getElementById('exang').value,
1545
+ oldpeak: document.getElementById('oldpeak').value,
1546
+ slope: document.getElementById('slope').value,
1547
+ ca: document.getElementById('ca').value,
1548
+ thal: document.getElementById('thal').value
1549
+ };
1550
+
1551
+ const results = {
1552
+ heartDisease: document.getElementById('heartDiseaseResults').innerHTML,
1553
+ ecg: document.getElementById('ecgResults').innerHTML,
1554
+ heartSound: document.getElementById('heartSoundResults').innerHTML,
1555
+ heartParams: heartParams
1556
+ };
1557
+ return results;
1558
+ }
1559
+
1560
+ // Function to generate AI analysis
1561
+ async function generateAnalysis() {
1562
+ const results = collectResults();
1563
+
1564
+ // Check if we have at least one result
1565
+ if (!results.heartDisease && !results.ecg && !results.heartSound) {
1566
+ alert('Please complete at least one test to generate analysis');
1567
+ return;
1568
+ }
1569
+
1570
+ try {
1571
+ const response = await fetch('/api/generate_analysis', {
1572
+ method: 'POST',
1573
+ headers: {
1574
+ 'Content-Type': 'application/json'
1575
+ },
1576
+ body: JSON.stringify(results)
1577
+ });
1578
+
1579
+ const data = await response.json();
1580
+ if (data.status === 'success') {
1581
+ const resultDiv = document.getElementById('aiAnalysisResult');
1582
+
1583
+ // Add heart disease risk parameters to the analysis
1584
+ const heartParamsSection = `
1585
+ <div class="report-section">
1586
+ <h4>Heart Disease Risk Parameters</h4>
1587
+ <div class="parameters-grid">
1588
+ <div class="parameter-item">
1589
+ <span class="parameter-label">Age:</span>
1590
+ <span class="parameter-value">${results.heartParams.age} years</span>
1591
+ </div>
1592
+ <div class="parameter-item">
1593
+ <span class="parameter-label">Sex:</span>
1594
+ <span class="parameter-value">${results.heartParams.sex === '1' ? 'Male' : 'Female'}</span>
1595
+ </div>
1596
+ <div class="parameter-item">
1597
+ <span class="parameter-label">Chest Pain Type:</span>
1598
+ <span class="parameter-value">${getChestPainType(results.heartParams.cp)}</span>
1599
+ </div>
1600
+ <div class="parameter-item">
1601
+ <span class="parameter-label">Resting Blood Pressure:</span>
1602
+ <span class="parameter-value">${results.heartParams.trestbps} mmHg</span>
1603
+ </div>
1604
+ <div class="parameter-item">
1605
+ <span class="parameter-label">Serum Cholesterol:</span>
1606
+ <span class="parameter-value">${results.heartParams.chol} mg/dl</span>
1607
+ </div>
1608
+ <div class="parameter-item">
1609
+ <span class="parameter-label">Fasting Blood Sugar:</span>
1610
+ <span class="parameter-value">${results.heartParams.fbs === '1' ? '> 120 mg/dl' : '<= 120 mg/dl'}</span>
1611
+ </div>
1612
+ <div class="parameter-item">
1613
+ <span class="parameter-label">Resting ECG Results:</span>
1614
+ <span class="parameter-value">${getRestingECG(results.heartParams.restecg)}</span>
1615
+ </div>
1616
+ <div class="parameter-item">
1617
+ <span class="parameter-label">Maximum Heart Rate:</span>
1618
+ <span class="parameter-value">${results.heartParams.thalach} bpm</span>
1619
+ </div>
1620
+ <div class="parameter-item">
1621
+ <span class="parameter-label">Exercise Induced Angina:</span>
1622
+ <span class="parameter-value">${results.heartParams.exang === '1' ? 'Yes' : 'No'}</span>
1623
+ </div>
1624
+ <div class="parameter-item">
1625
+ <span class="parameter-label">ST Depression:</span>
1626
+ <span class="parameter-value">${results.heartParams.oldpeak} mm</span>
1627
+ </div>
1628
+ <div class="parameter-item">
1629
+ <span class="parameter-label">Slope of Peak Exercise:</span>
1630
+ <span class="parameter-value">${getSlope(results.heartParams.slope)}</span>
1631
+ </div>
1632
+ <div class="parameter-item">
1633
+ <span class="parameter-label">Number of Major Vessels:</span>
1634
+ <span class="parameter-value">${results.heartParams.ca}</span>
1635
+ </div>
1636
+ <div class="parameter-item">
1637
+ <span class="parameter-label">Thalassemia:</span>
1638
+ <span class="parameter-value">${getThalassemia(results.heartParams.thal)}</span>
1639
+ </div>
1640
+ </div>
1641
+ </div>
1642
+ `;
1643
+
1644
+ // Insert the parameters section after the header
1645
+ const analysisHTML = data.analysis;
1646
+ const headerEndIndex = analysisHTML.indexOf('</div>', analysisHTML.indexOf('<div class="report-header">')) + 6;
1647
+ const finalHTML = analysisHTML.slice(0, headerEndIndex) + heartParamsSection + analysisHTML.slice(headerEndIndex);
1648
+
1649
+ resultDiv.innerHTML = `
1650
+ <h4>AI Analysis Report</h4>
1651
+ <div class="analysis-content">
1652
+ ${finalHTML}
1653
+ </div>
1654
+ `;
1655
+ resultDiv.classList.add('show');
1656
+ } else {
1657
+ throw new Error(data.message);
1658
+ }
1659
+ } catch (error) {
1660
+ console.error('Error generating analysis:', error);
1661
+ alert('Error generating analysis. Please try again.');
1662
+ }
1663
+ }
1664
+
1665
+ // Helper functions for parameter value mapping
1666
+ function getChestPainType(value) {
1667
+ const types = {
1668
+ '1': 'Typical Angina',
1669
+ '2': 'Atypical Angina',
1670
+ '3': 'Non-anginal Pain',
1671
+ '4': 'Asymptomatic'
1672
+ };
1673
+ return types[value] || 'N/A';
1674
+ }
1675
+
1676
+ function getRestingECG(value) {
1677
+ const types = {
1678
+ '0': 'Normal',
1679
+ '1': 'ST-T Wave Abnormality',
1680
+ '2': 'Left Ventricular Hypertrophy'
1681
+ };
1682
+ return types[value] || 'N/A';
1683
+ }
1684
+
1685
+ function getSlope(value) {
1686
+ const types = {
1687
+ '0': 'Upsloping',
1688
+ '1': 'Flat',
1689
+ '2': 'Downsloping'
1690
+ };
1691
+ return types[value] || 'N/A';
1692
+ }
1693
+
1694
+ function getThalassemia(value) {
1695
+ const types = {
1696
+ '0': 'Normal',
1697
+ '1': 'Fixed Defect',
1698
+ '2': 'Reversible Defect',
1699
+ '3': 'Other'
1700
+ };
1701
+ return types[value] || 'N/A';
1702
+ }
1703
+
1704
+ // Add event listener for the generate button
1705
+ document.getElementById('generateAnalysis').addEventListener('click', generateAnalysis);
1706
+
1707
+ // Function to update results from other tabs
1708
+ function updateResults(tab, content) {
1709
+ const resultBox = document.getElementById(`${tab}Results`);
1710
+ if (resultBox) {
1711
+ resultBox.textContent = content;
1712
+ }
1713
+ }
1714
+ </script>
1715
+ </body>
1716
+ </html>
app/templates/login.html ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Login - Heart Health Analysis System</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
9
+ <style>
10
+ :root[data-theme="dark"] {
11
+ --primary-color: #f03e5f;
12
+ --secondary-color: #f5768d;
13
+ --accent-color: #d1062a;
14
+ --text-color: #ffffff;
15
+ --text-light: #fccad3;
16
+ --background-start: #1a1a2e;
17
+ --background-end: #16213e;
18
+ --card-color: rgba(26, 26, 46, 0.8);
19
+ --border-radius: 12px;
20
+ --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
21
+ --transition: all 0.3s ease;
22
+ }
23
+
24
+ body {
25
+ background: linear-gradient(135deg,
26
+ rgba(26, 26, 46, 0.95) 0%,
27
+ rgba(22, 33, 62, 0.95) 50%,
28
+ rgba(209, 6, 42, 0.2) 100%
29
+ );
30
+ color: var(--text-color);
31
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
32
+ min-height: 100vh;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ padding: 20px;
37
+ }
38
+
39
+ .login-container {
40
+ background: var(--card-color);
41
+ border-radius: var(--border-radius);
42
+ padding: 40px;
43
+ box-shadow: var(--box-shadow);
44
+ width: 100%;
45
+ max-width: 400px;
46
+ border: 1px solid rgba(255, 255, 255, 0.1);
47
+ }
48
+
49
+ .login-header {
50
+ text-align: center;
51
+ margin-bottom: 30px;
52
+ }
53
+
54
+ .login-header h1 {
55
+ color: var(--primary-color);
56
+ font-size: 2rem;
57
+ margin-bottom: 10px;
58
+ }
59
+
60
+ .login-header p {
61
+ color: var(--text-light);
62
+ opacity: 0.8;
63
+ }
64
+
65
+ .form-control {
66
+ background: rgba(255, 255, 255, 0.1);
67
+ border: 2px solid rgba(255, 255, 255, 0.1);
68
+ color: var(--text-color);
69
+ padding: 12px;
70
+ border-radius: var(--border-radius);
71
+ margin-bottom: 20px;
72
+ }
73
+
74
+ .form-control:focus {
75
+ background: rgba(255, 255, 255, 0.15);
76
+ border-color: var(--primary-color);
77
+ color: var(--text-color);
78
+ box-shadow: 0 0 0 2px rgba(240, 62, 95, 0.2);
79
+ }
80
+
81
+ .btn-login {
82
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
83
+ border: none;
84
+ color: white;
85
+ padding: 12px;
86
+ border-radius: var(--border-radius);
87
+ width: 100%;
88
+ font-weight: 500;
89
+ margin-top: 10px;
90
+ transition: var(--transition);
91
+ }
92
+
93
+ .btn-login:hover {
94
+ transform: translateY(-2px);
95
+ box-shadow: 0 4px 12px rgba(240, 62, 95, 0.3);
96
+ }
97
+
98
+ .register-link {
99
+ text-align: center;
100
+ margin-top: 20px;
101
+ color: var(--text-light);
102
+ }
103
+
104
+ .register-link a {
105
+ color: var(--primary-color);
106
+ text-decoration: none;
107
+ font-weight: 500;
108
+ }
109
+
110
+ .register-link a:hover {
111
+ text-decoration: underline;
112
+ }
113
+
114
+ .alert {
115
+ background: rgba(240, 62, 95, 0.1);
116
+ border: 1px solid rgba(240, 62, 95, 0.2);
117
+ color: var(--text-color);
118
+ border-radius: var(--border-radius);
119
+ padding: 12px;
120
+ margin-bottom: 20px;
121
+ display: none;
122
+ }
123
+ </style>
124
+ </head>
125
+ <body>
126
+ <div class="login-container">
127
+ <div class="login-header">
128
+ <h1><i class="fas fa-heartbeat"></i> Login</h1>
129
+ <p>Welcome back to Heart Health Analysis System</p>
130
+ </div>
131
+
132
+ <div class="alert alert-danger" id="errorAlert" role="alert"></div>
133
+
134
+ <form id="loginForm">
135
+ <div class="mb-3">
136
+ <input type="email" class="form-control" id="email" placeholder="Email address" required>
137
+ </div>
138
+ <div class="mb-3">
139
+ <input type="password" class="form-control" id="password" placeholder="Password" required>
140
+ </div>
141
+ <button type="submit" class="btn btn-login">
142
+ <i class="fas fa-sign-in-alt"></i> Login
143
+ </button>
144
+ </form>
145
+
146
+ <div class="register-link">
147
+ Don't have an account? <a href="/register">Register here</a>
148
+ </div>
149
+ </div>
150
+
151
+ <script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-app-compat.js"></script>
152
+ <script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-auth-compat.js"></script>
153
+ <script>
154
+ // Firebase configuration
155
+ const firebaseConfig = {{ firebase_config|tojson|safe }};
156
+
157
+ // Initialize Firebase
158
+ firebase.initializeApp(firebaseConfig);
159
+
160
+ document.getElementById('loginForm').addEventListener('submit', async (e) => {
161
+ e.preventDefault();
162
+ const email = document.getElementById('email').value;
163
+ const password = document.getElementById('password').value;
164
+ const errorAlert = document.getElementById('errorAlert');
165
+
166
+ try {
167
+ const userCredential = await firebase.auth().signInWithEmailAndPassword(email, password);
168
+ const idToken = await userCredential.user.getIdToken();
169
+
170
+ // Verify token with server
171
+ const response = await fetch('/verify-token', {
172
+ method: 'POST',
173
+ headers: {
174
+ 'Content-Type': 'application/json',
175
+ },
176
+ body: JSON.stringify({ idToken }),
177
+ });
178
+
179
+ if (!response.ok) {
180
+ const errorData = await response.json();
181
+ throw new Error(errorData.message || 'Token verification failed');
182
+ }
183
+
184
+ // Only redirect after successful token verification
185
+ window.location.href = '/';
186
+ } catch (error) {
187
+ console.error('Login error:', error);
188
+ errorAlert.style.display = 'block';
189
+ errorAlert.textContent = error.message || 'Login failed. Please try again.';
190
+ }
191
+ });
192
+
193
+ // Check if user is already logged in
194
+ firebase.auth().onAuthStateChanged((user) => {
195
+ if (user) {
196
+ // Only redirect if we're not already on the login page
197
+ if (!window.location.pathname.includes('/login')) {
198
+ window.location.href = '/';
199
+ }
200
+ }
201
+ });
202
+ </script>
203
+ </body>
204
+ </html>
app/templates/register.html ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Register - Heart Health Analysis System</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
9
+ <style>
10
+ :root[data-theme="dark"] {
11
+ --primary-color: #f03e5f;
12
+ --secondary-color: #f5768d;
13
+ --accent-color: #d1062a;
14
+ --text-color: #ffffff;
15
+ --text-light: #fccad3;
16
+ --background-start: #1a1a2e;
17
+ --background-end: #16213e;
18
+ --card-color: rgba(26, 26, 46, 0.8);
19
+ --border-radius: 12px;
20
+ --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
21
+ --transition: all 0.3s ease;
22
+ }
23
+
24
+ body {
25
+ background: linear-gradient(135deg,
26
+ rgba(26, 26, 46, 0.95) 0%,
27
+ rgba(22, 33, 62, 0.95) 50%,
28
+ rgba(209, 6, 42, 0.2) 100%
29
+ );
30
+ color: var(--text-color);
31
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
32
+ min-height: 100vh;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ padding: 20px;
37
+ }
38
+
39
+ .register-container {
40
+ background: var(--card-color);
41
+ border-radius: var(--border-radius);
42
+ padding: 40px;
43
+ box-shadow: var(--box-shadow);
44
+ width: 100%;
45
+ max-width: 400px;
46
+ border: 1px solid rgba(255, 255, 255, 0.1);
47
+ }
48
+
49
+ .register-header {
50
+ text-align: center;
51
+ margin-bottom: 30px;
52
+ }
53
+
54
+ .register-header h1 {
55
+ color: var(--primary-color);
56
+ font-size: 2rem;
57
+ margin-bottom: 10px;
58
+ }
59
+
60
+ .register-header p {
61
+ color: var(--text-light);
62
+ opacity: 0.8;
63
+ }
64
+
65
+ .form-control {
66
+ background: rgba(255, 255, 255, 0.1);
67
+ border: 2px solid rgba(255, 255, 255, 0.1);
68
+ color: var(--text-color);
69
+ padding: 12px;
70
+ border-radius: var(--border-radius);
71
+ margin-bottom: 20px;
72
+ }
73
+
74
+ .form-control:focus {
75
+ background: rgba(255, 255, 255, 0.15);
76
+ border-color: var(--primary-color);
77
+ color: var(--text-color);
78
+ box-shadow: 0 0 0 2px rgba(240, 62, 95, 0.2);
79
+ }
80
+
81
+ .btn-register {
82
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
83
+ border: none;
84
+ color: white;
85
+ padding: 12px;
86
+ border-radius: var(--border-radius);
87
+ width: 100%;
88
+ font-weight: 500;
89
+ margin-top: 10px;
90
+ transition: var(--transition);
91
+ }
92
+
93
+ .btn-register:hover {
94
+ transform: translateY(-2px);
95
+ box-shadow: 0 4px 12px rgba(240, 62, 95, 0.3);
96
+ }
97
+
98
+ .login-link {
99
+ text-align: center;
100
+ margin-top: 20px;
101
+ color: var(--text-light);
102
+ }
103
+
104
+ .login-link a {
105
+ color: var(--primary-color);
106
+ text-decoration: none;
107
+ font-weight: 500;
108
+ }
109
+
110
+ .login-link a:hover {
111
+ text-decoration: underline;
112
+ }
113
+
114
+ .alert {
115
+ background: rgba(240, 62, 95, 0.1);
116
+ border: 1px solid rgba(240, 62, 95, 0.2);
117
+ color: var(--text-color);
118
+ border-radius: var(--border-radius);
119
+ padding: 12px;
120
+ margin-bottom: 20px;
121
+ display: none;
122
+ }
123
+ </style>
124
+ </head>
125
+ <body>
126
+ <div class="register-container">
127
+ <div class="register-header">
128
+ <h1><i class="fas fa-user-plus"></i> Register</h1>
129
+ <p>Create your account for Heart Health Analysis System</p>
130
+ </div>
131
+
132
+ <div class="alert alert-danger" id="errorAlert" role="alert"></div>
133
+
134
+ <form id="registerForm">
135
+ <div class="mb-3">
136
+ <input type="text" class="form-control" id="name" placeholder="Full Name" required>
137
+ </div>
138
+ <div class="mb-3">
139
+ <input type="email" class="form-control" id="email" placeholder="Email address" required>
140
+ </div>
141
+ <div class="mb-3">
142
+ <input type="password" class="form-control" id="password" placeholder="Password" required>
143
+ </div>
144
+ <div class="mb-3">
145
+ <input type="password" class="form-control" id="confirmPassword" placeholder="Confirm Password" required>
146
+ </div>
147
+ <button type="submit" class="btn btn-register">
148
+ <i class="fas fa-user-plus"></i> Register
149
+ </button>
150
+ </form>
151
+
152
+ <div class="login-link">
153
+ Already have an account? <a href="/login">Login here</a>
154
+ </div>
155
+ </div>
156
+
157
+ <script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-app-compat.js"></script>
158
+ <script src="https://www.gstatic.com/firebasejs/9.6.0/firebase-auth-compat.js"></script>
159
+ <script>
160
+ // Firebase configuration
161
+ const firebaseConfig = {{ firebase_config|tojson|safe }};
162
+
163
+ // Initialize Firebase
164
+ firebase.initializeApp(firebaseConfig);
165
+
166
+ document.getElementById('registerForm').addEventListener('submit', async (e) => {
167
+ e.preventDefault();
168
+ const name = document.getElementById('name').value;
169
+ const email = document.getElementById('email').value;
170
+ const password = document.getElementById('password').value;
171
+ const confirmPassword = document.getElementById('confirmPassword').value;
172
+ const errorAlert = document.getElementById('errorAlert');
173
+
174
+ if (password !== confirmPassword) {
175
+ errorAlert.style.display = 'block';
176
+ errorAlert.textContent = 'Passwords do not match';
177
+ return;
178
+ }
179
+
180
+ try {
181
+ const userCredential = await firebase.auth().createUserWithEmailAndPassword(email, password);
182
+ await userCredential.user.updateProfile({
183
+ displayName: name
184
+ });
185
+
186
+ const idToken = await userCredential.user.getIdToken();
187
+
188
+ // Verify token with server
189
+ const response = await fetch('/verify-token', {
190
+ method: 'POST',
191
+ headers: {
192
+ 'Content-Type': 'application/json',
193
+ },
194
+ body: JSON.stringify({ idToken }),
195
+ });
196
+
197
+ if (!response.ok) {
198
+ throw new Error('Token verification failed');
199
+ }
200
+
201
+ // Redirect to main app on successful registration
202
+ window.location.href = '/';
203
+ } catch (error) {
204
+ errorAlert.style.display = 'block';
205
+ errorAlert.textContent = error.message;
206
+ }
207
+ });
208
+
209
+ // Check if user is already logged in
210
+ firebase.auth().onAuthStateChanged((user) => {
211
+ if (user) {
212
+ window.location.href = '/';
213
+ }
214
+ });
215
+ </script>
216
+ </body>
217
+ </html>