Update web/diagnosis.html
Browse files- web/diagnosis.html +57 -145
web/diagnosis.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
| 9 |
<title>Diagnóstico IA</title>
|
| 10 |
<link rel="stylesheet" href="css/simplebar.css">
|
| 11 |
<link href="https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,100;0,200;0,300;0,400;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
|
| 12 |
-
<
|
| 13 |
<link rel="stylesheet" href="css/select2.css">
|
| 14 |
<link rel="stylesheet" href="css/dropzone.css">
|
| 15 |
<link rel="stylesheet" href="css/uppy.min.css">
|
|
@@ -195,7 +195,7 @@
|
|
| 195 |
<!-- Navegación superior -->
|
| 196 |
<nav class="topnav navbar navbar-light">
|
| 197 |
<button type="button" class="navbar-toggler text-muted mt-2 p-0 mr-3 collapseSidebar">
|
| 198 |
-
<i
|
| 199 |
</button>
|
| 200 |
|
| 201 |
<ul class="nav">
|
|
@@ -213,7 +213,7 @@
|
|
| 213 |
</nav>
|
| 214 |
<aside class="sidebar-left border-right bg-white shadow" id="leftSidebar" data-simplebar>
|
| 215 |
<a href="#" class="btn collapseSidebar toggle-btn d-lg-none text-muted ml-2 mt-3" data-toggle="toggle">
|
| 216 |
-
<i
|
| 217 |
</a>
|
| 218 |
<nav class="vertnav navbar navbar-light">
|
| 219 |
<!-- nav bar -->
|
|
@@ -226,31 +226,31 @@
|
|
| 226 |
<ul class="navbar-nav flex-fill w-100 mb-2">
|
| 227 |
<li class="nav-item">
|
| 228 |
<a class="nav-link" href="./index.html">
|
| 229 |
-
<i
|
| 230 |
<span class="ml-3 item-text">Dashboard</span>
|
| 231 |
</a>
|
| 232 |
</li>
|
| 233 |
<li class="nav-item active">
|
| 234 |
<a class="nav-link" href="./diagnosis.html">
|
| 235 |
-
<i
|
| 236 |
<span class="ml-3 item-text">Diagnóstico</span>
|
| 237 |
</a>
|
| 238 |
</li>
|
| 239 |
<li class="nav-item">
|
| 240 |
<a class="nav-link" href="./patients.html">
|
| 241 |
-
<i
|
| 242 |
<span class="ml-3 item-text">Pacientes</span>
|
| 243 |
</a>
|
| 244 |
</li>
|
| 245 |
<li class="nav-item">
|
| 246 |
<a class="nav-link" href="./historial-consultas.html">
|
| 247 |
-
<i
|
| 248 |
<span class="ml-3 item-text">Consultas</span>
|
| 249 |
</a>
|
| 250 |
</li>
|
| 251 |
<li class="nav-item active">
|
| 252 |
<a class="nav-link" href="./users.html">
|
| 253 |
-
<i
|
| 254 |
<span class="ml-3 item-text">Usuarios</span>
|
| 255 |
</a>
|
| 256 |
</li>
|
|
@@ -267,7 +267,7 @@
|
|
| 267 |
<div class="row align-items-center mb-4">
|
| 268 |
<div class="col">
|
| 269 |
<h2 class="h3 page-title">
|
| 270 |
-
<i
|
| 271 |
Diagnóstico con Inteligencia Artificial
|
| 272 |
</h2>
|
| 273 |
<p class="text-muted">Diagnostico de retinopatía diabética mediante análisis de imágenes</p>
|
|
@@ -280,7 +280,7 @@
|
|
| 280 |
<div class="card shadow mb-4">
|
| 281 |
<div class="card-header">
|
| 282 |
<h5 class="card-title mb-0">
|
| 283 |
-
<i
|
| 284 |
</h5>
|
| 285 |
</div>
|
| 286 |
<div class="card-body">
|
|
@@ -294,7 +294,7 @@
|
|
| 294 |
|
| 295 |
<!-- Zona de carga 2 -->
|
| 296 |
<div id="uploadZone2" class="upload-zone" data-zone="2">
|
| 297 |
-
<i
|
| 298 |
<h5 class="text-muted">Arrastra una imagen aquí o haz clic para seleccionar</h5>
|
| 299 |
<p class="text-muted mb-0">Formatos soportados: JPG, PNG, JPEG (Máx. 10MB)</p>
|
| 300 |
</div>
|
|
@@ -304,13 +304,13 @@
|
|
| 304 |
<img id="previewImg2" class="image-preview" alt="Vista previa">
|
| 305 |
<div class="mt-3">
|
| 306 |
<button class="btn btn-success btn-lg mr-2" id="analyzeBtn2">
|
| 307 |
-
<i
|
| 308 |
</button>
|
| 309 |
<button class="btn btn-outline-secondary mr-2" id="clearBtn2">
|
| 310 |
-
<i
|
| 311 |
</button>
|
| 312 |
<button class="btn btn-warning gradcam-btn" id="gradcamBtn2" style="display: none;">
|
| 313 |
-
<i
|
| 314 |
</button>
|
| 315 |
</div>
|
| 316 |
</div>
|
|
@@ -320,7 +320,7 @@
|
|
| 320 |
<div class="card shadow mb-4 analysis-card">
|
| 321 |
<div class="card-body">
|
| 322 |
<h5 class="mb-4">
|
| 323 |
-
<i
|
| 324 |
</h5>
|
| 325 |
<div id="resultContent2"></div>
|
| 326 |
</div>
|
|
@@ -337,7 +337,7 @@
|
|
| 337 |
|
| 338 |
<!-- Zona de carga 1 -->
|
| 339 |
<div id="uploadZone1" class="upload-zone" data-zone="1">
|
| 340 |
-
<i
|
| 341 |
<h5 class="text-muted">Arrastra una imagen aquí o haz clic para seleccionar</h5>
|
| 342 |
<p class="text-muted mb-0">Formatos soportados: JPG, PNG, JPEG (Máx. 10MB)</p>
|
| 343 |
</div>
|
|
@@ -347,13 +347,13 @@
|
|
| 347 |
<img id="previewImg1" class="image-preview" alt="Vista previa">
|
| 348 |
<div class="mt-3">
|
| 349 |
<button class="btn btn-success btn-lg mr-2" id="analyzeBtn1">
|
| 350 |
-
<i
|
| 351 |
</button>
|
| 352 |
<button class="btn btn-outline-secondary mr-2" id="clearBtn1">
|
| 353 |
-
<i
|
| 354 |
</button>
|
| 355 |
<button class="btn btn-warning gradcam-btn" id="gradcamBtn1" style="display: none;">
|
| 356 |
-
<i
|
| 357 |
</button>
|
| 358 |
</div>
|
| 359 |
</div>
|
|
@@ -363,7 +363,7 @@
|
|
| 363 |
<div class="card shadow mb-4 analysis-card">
|
| 364 |
<div class="card-body">
|
| 365 |
<h5 class="mb-4">
|
| 366 |
-
<i
|
| 367 |
</h5>
|
| 368 |
<div id="resultContent1"></div>
|
| 369 |
</div>
|
|
@@ -380,7 +380,7 @@
|
|
| 380 |
<div class="card shadow mb-4">
|
| 381 |
<div class="card-header">
|
| 382 |
<h6 class="card-title mb-0">
|
| 383 |
-
<i
|
| 384 |
</h6>
|
| 385 |
</div>
|
| 386 |
<div class="card-body">
|
|
@@ -436,7 +436,7 @@
|
|
| 436 |
</div>
|
| 437 |
|
| 438 |
<button class="btn btn-primary btn-block" id="saveConsultationBtn" type="button">
|
| 439 |
-
<i
|
| 440 |
</button>
|
| 441 |
</div>
|
| 442 |
</div>
|
|
@@ -445,7 +445,7 @@
|
|
| 445 |
<div class="card shadow mb-4">
|
| 446 |
<div class="card-header">
|
| 447 |
<h6 class="card-title mb-0">
|
| 448 |
-
<i
|
| 449 |
</h6>
|
| 450 |
</div>
|
| 451 |
<div class="card-body">
|
|
@@ -465,32 +465,6 @@
|
|
| 465 |
</main>
|
| 466 |
</div>
|
| 467 |
|
| 468 |
-
<!-- Modal de Confirmación de Consulta -->
|
| 469 |
-
<div class="modal fade" id="consultationResultModal" tabindex="-1" role="dialog" aria-labelledby="consultationResultModalLabel" aria-hidden="true">
|
| 470 |
-
<div class="modal-dialog modal-dialog-centered" role="document">
|
| 471 |
-
<div class="modal-content" style="border-radius: 12px; overflow: hidden;">
|
| 472 |
-
<div class="modal-header" id="consultationResultHeader">
|
| 473 |
-
<h5 class="modal-title" id="consultationResultModalLabel">
|
| 474 |
-
<i id="consultationResultIcon" class="fe mr-2"></i>
|
| 475 |
-
<span id="consultationResultTitle"></span>
|
| 476 |
-
</h5>
|
| 477 |
-
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
| 478 |
-
<span aria-hidden="true">×</span>
|
| 479 |
-
</button>
|
| 480 |
-
</div>
|
| 481 |
-
<div class="modal-body text-center py-4">
|
| 482 |
-
<div id="consultationResultBody"></div>
|
| 483 |
-
</div>
|
| 484 |
-
<div class="modal-footer justify-content-center">
|
| 485 |
-
<button type="button" class="btn" id="consultationResultCloseBtn" data-dismiss="modal">Cerrar</button>
|
| 486 |
-
<a href="historial-consultas.html" class="btn btn-outline-primary" id="consultationResultViewBtn" style="display:none;">
|
| 487 |
-
<i class="fe fe-list fe-16 mr-1"></i>Ver Historial
|
| 488 |
-
</a>
|
| 489 |
-
</div>
|
| 490 |
-
</div>
|
| 491 |
-
</div>
|
| 492 |
-
</div>
|
| 493 |
-
|
| 494 |
<script src="js/jquery.min.js"></script>
|
| 495 |
<script src="js/popper.min.js"></script>
|
| 496 |
<script src="js/moment.min.js"></script>
|
|
@@ -530,40 +504,6 @@
|
|
| 530 |
|
| 531 |
updateLastUpdate();
|
| 532 |
setInterval(updateLastUpdate, 60000);
|
| 533 |
-
|
| 534 |
-
// Autocompletar paciente si viene desde patients.html
|
| 535 |
-
const urlParams = new URLSearchParams(window.location.search);
|
| 536 |
-
const patientIdFromUrl = urlParams.get('patient');
|
| 537 |
-
if (patientIdFromUrl) {
|
| 538 |
-
autoSelectPatientById(parseInt(patientIdFromUrl));
|
| 539 |
-
}
|
| 540 |
-
}
|
| 541 |
-
|
| 542 |
-
// AUTOSELECCIONAR PACIENTE POR ID (desde URL)
|
| 543 |
-
function autoSelectPatientById(patientId) {
|
| 544 |
-
api.getPatient(patientId).then(function(response) {
|
| 545 |
-
if (response && response.success && response.patient) {
|
| 546 |
-
const patient = response.patient;
|
| 547 |
-
selectedPatient = patient;
|
| 548 |
-
|
| 549 |
-
const age = patient.birthDate ? (moment ? moment().diff(moment(patient.birthDate), 'years') : 'N/A') : 'N/A';
|
| 550 |
-
$('#patientSearch').val(patient.name);
|
| 551 |
-
$('#selectedPatientInfo').show();
|
| 552 |
-
$('#patientName').text(patient.name);
|
| 553 |
-
$('#patientDetails').html(`
|
| 554 |
-
<strong>ID:</strong> ${patient.patientID} |
|
| 555 |
-
<strong>Edad:</strong> ${age} años
|
| 556 |
-
${patient.diabetesType ? '| <strong>Diabetes:</strong> ' + patient.diabetesType : ''}
|
| 557 |
-
`);
|
| 558 |
-
$('#selectedPatientId').val(patient.patientID);
|
| 559 |
-
enableSaveButton();
|
| 560 |
-
console.log('Paciente autoseleccionado:', patient.name);
|
| 561 |
-
} else {
|
| 562 |
-
console.warn('No se pudo cargar el paciente con ID:', patientId);
|
| 563 |
-
}
|
| 564 |
-
}).catch(function(err) {
|
| 565 |
-
console.error('Error cargando paciente por ID:', err);
|
| 566 |
-
});
|
| 567 |
}
|
| 568 |
|
| 569 |
// AUTENTICACION
|
|
@@ -785,7 +725,7 @@
|
|
| 785 |
}
|
| 786 |
|
| 787 |
$(`#analyzeBtn${zoneNumber}`).prop('disabled', false)
|
| 788 |
-
.html('<i
|
| 789 |
.removeClass('pulse-animation');
|
| 790 |
|
| 791 |
showAnalysisResults(mockResult, zoneNumber);
|
|
@@ -798,7 +738,7 @@
|
|
| 798 |
api.predict(imageData, fileName).then(function(result) {
|
| 799 |
// Restaurar boton
|
| 800 |
$(`#analyzeBtn${zoneNumber}`).prop('disabled', false)
|
| 801 |
-
.html('<i
|
| 802 |
.removeClass('pulse-animation');
|
| 803 |
|
| 804 |
if (result && result.success) {
|
|
@@ -880,7 +820,7 @@
|
|
| 880 |
<div class="card shadow-sm mt-3">
|
| 881 |
<div class="card-header d-flex justify-content-between align-items-center">
|
| 882 |
<h6 class="mb-0">
|
| 883 |
-
<i
|
| 884 |
</h6>
|
| 885 |
<span class="badge badge-pill ${isPositive ? 'badge-warning' : 'badge-success'}">${confidence}%</span>
|
| 886 |
</div>
|
|
@@ -908,7 +848,7 @@
|
|
| 908 |
<div class="border-right">
|
| 909 |
<h6 class="text-muted mb-0">Análisis realizado</h6>
|
| 910 |
<small class="text-muted">
|
| 911 |
-
<i
|
| 912 |
${moment ? moment(result.timestamp).format('DD/MM/YYYY HH:mm') : new Date(result.timestamp).toLocaleString()}
|
| 913 |
</small>
|
| 914 |
</div>
|
|
@@ -916,7 +856,7 @@
|
|
| 916 |
<div class="col-6">
|
| 917 |
<h6 class="text-muted mb-0">Modelo utilizado</h6>
|
| 918 |
<small class="text-muted">
|
| 919 |
-
<i
|
| 920 |
EfficientNetB0
|
| 921 |
</small>
|
| 922 |
</div>
|
|
@@ -1071,7 +1011,7 @@
|
|
| 1071 |
console.log('Eel no disponible - simulando guardado');
|
| 1072 |
setTimeout(() => {
|
| 1073 |
$('#saveConsultationBtn').prop('disabled', false)
|
| 1074 |
-
.html('<i
|
| 1075 |
showAlert('success', 'Consulta guardada correctamente (simulación)');
|
| 1076 |
}, 1500);
|
| 1077 |
return;
|
|
@@ -1083,21 +1023,16 @@
|
|
| 1083 |
|
| 1084 |
// Restaurar boton
|
| 1085 |
$('#saveConsultationBtn').prop('disabled', false)
|
| 1086 |
-
.html('<i
|
| 1087 |
|
| 1088 |
if (response && response.success) {
|
|
|
|
| 1089 |
console.log('Consulta guardada exitosamente:', response);
|
| 1090 |
-
showConsultationResultModal(true, selectedPatient.name);
|
| 1091 |
} else {
|
| 1092 |
const errorMsg = response?.message || 'Error al guardar la consulta';
|
|
|
|
| 1093 |
console.error('Error guardando consulta:', response);
|
| 1094 |
-
showConsultationResultModal(false, null, errorMsg);
|
| 1095 |
}
|
| 1096 |
-
}).catch(function(err) {
|
| 1097 |
-
$('#saveConsultationBtn').prop('disabled', false)
|
| 1098 |
-
.html('<i class="fe fe-save fe-16 mr-2"></i>Guardar Consulta');
|
| 1099 |
-
console.error('Error de red al guardar consulta:', err);
|
| 1100 |
-
showConsultationResultModal(false, null, 'No se pudo conectar con el servidor. Intenta nuevamente.');
|
| 1101 |
});
|
| 1102 |
}
|
| 1103 |
|
|
@@ -1132,7 +1067,7 @@ if (false) { /* removed */ }
|
|
| 1132 |
api.gradcam(imageData, fileName, lastResult).then(function(response) {
|
| 1133 |
// Restaurar boton
|
| 1134 |
$(`#gradcamBtn${zoneNumber}`).prop('disabled', false)
|
| 1135 |
-
.html('<i
|
| 1136 |
|
| 1137 |
if (response && response.success) {
|
| 1138 |
console.log('Grad-CAM generado exitosamente');
|
|
@@ -1162,7 +1097,7 @@ if (false) { /* removed */ }
|
|
| 1162 |
<div class="modal-content">
|
| 1163 |
<div class="modal-header bg-danger text-white">
|
| 1164 |
<h5 class="modal-title">
|
| 1165 |
-
<i
|
| 1166 |
<span class="badge badge-light text-danger ml-2">Retinopatía Detectada</span>
|
| 1167 |
</h5>
|
| 1168 |
<button type="button" class="close text-white" data-dismiss="modal">
|
|
@@ -1177,7 +1112,7 @@ if (false) { /* removed */ }
|
|
| 1177 |
alt="Zoom HD - Región Crítica">
|
| 1178 |
<div class="mt-3">
|
| 1179 |
<h6 class="text-danger mb-2">
|
| 1180 |
-
<i
|
| 1181 |
</h6>
|
| 1182 |
<p class="text-muted mb-0">
|
| 1183 |
<strong>Activación máxima:</strong> ${maxAct} |
|
|
@@ -1194,7 +1129,7 @@ if (false) { /* removed */ }
|
|
| 1194 |
</div>
|
| 1195 |
<div class="alert alert-${alertType}">
|
| 1196 |
<small>
|
| 1197 |
-
<i
|
| 1198 |
${clinical}
|
| 1199 |
</small>
|
| 1200 |
</div>
|
|
@@ -1202,10 +1137,10 @@ if (false) { /* removed */ }
|
|
| 1202 |
</div>
|
| 1203 |
<div class="modal-footer">
|
| 1204 |
<button type="button" class="btn btn-outline-primary" onclick="downloadGradCAMImage(${zoneNumber})">
|
| 1205 |
-
<i
|
| 1206 |
</button>
|
| 1207 |
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
| 1208 |
-
<i
|
| 1209 |
</button>
|
| 1210 |
</div>
|
| 1211 |
</div>
|
|
@@ -1297,7 +1232,7 @@ if (false) { /* removed */ }
|
|
| 1297 |
|
| 1298 |
const alertHtml = `
|
| 1299 |
<div class="alert ${alertClass} alert-dismissible fade show" role="alert">
|
| 1300 |
-
|
| 1301 |
${message}
|
| 1302 |
<button type="button" class="close" data-dismiss="alert">
|
| 1303 |
<span aria-hidden="true">×</span>
|
|
@@ -1312,47 +1247,6 @@ if (false) { /* removed */ }
|
|
| 1312 |
}, 5000);
|
| 1313 |
}
|
| 1314 |
|
| 1315 |
-
function showConsultationResultModal(success, patientName, errorMessage) {
|
| 1316 |
-
const header = $('#consultationResultHeader');
|
| 1317 |
-
const icon = $('#consultationResultIcon');
|
| 1318 |
-
const title = $('#consultationResultTitle');
|
| 1319 |
-
const body = $('#consultationResultBody');
|
| 1320 |
-
const closeBtn = $('#consultationResultCloseBtn');
|
| 1321 |
-
const viewBtn = $('#consultationResultViewBtn');
|
| 1322 |
-
|
| 1323 |
-
if (success) {
|
| 1324 |
-
header.removeClass('bg-danger text-white').addClass('bg-success text-white');
|
| 1325 |
-
icon.removeClass().addClass('fe fe-check-circle mr-2');
|
| 1326 |
-
title.text('Consulta Guardada Exitosamente');
|
| 1327 |
-
body.html(`
|
| 1328 |
-
<div style="font-size: 4rem; color: #28a745; margin-bottom: 1rem;">
|
| 1329 |
-
<i class="fe fe-check-circle"></i>
|
| 1330 |
-
</div>
|
| 1331 |
-
<h5 class="text-success mb-2">¡Consulta registrada!</h5>
|
| 1332 |
-
<p class="text-muted mb-0">
|
| 1333 |
-
La consulta del paciente <strong>${patientName}</strong> fue guardada correctamente en el sistema.
|
| 1334 |
-
</p>
|
| 1335 |
-
`);
|
| 1336 |
-
closeBtn.removeClass('btn-danger').addClass('btn-success').text('Continuar');
|
| 1337 |
-
viewBtn.show();
|
| 1338 |
-
} else {
|
| 1339 |
-
header.removeClass('bg-success text-white').addClass('bg-danger text-white');
|
| 1340 |
-
icon.removeClass().addClass('fe fe-x-circle mr-2');
|
| 1341 |
-
title.text('No se pudo guardar la consulta');
|
| 1342 |
-
body.html(`
|
| 1343 |
-
<div style="font-size: 4rem; color: #dc3545; margin-bottom: 1rem;">
|
| 1344 |
-
<i class="fe fe-x-circle"></i>
|
| 1345 |
-
</div>
|
| 1346 |
-
<h5 class="text-danger mb-2">Error al guardar</h5>
|
| 1347 |
-
<p class="text-muted mb-0">${errorMessage || 'Ocurrió un error inesperado. Por favor intenta nuevamente.'}</p>
|
| 1348 |
-
`);
|
| 1349 |
-
closeBtn.removeClass('btn-success').addClass('btn-danger').text('Cerrar');
|
| 1350 |
-
viewBtn.hide();
|
| 1351 |
-
}
|
| 1352 |
-
|
| 1353 |
-
$('#consultationResultModal').modal('show');
|
| 1354 |
-
}
|
| 1355 |
-
|
| 1356 |
// === FUNCIONES GLOBALES ===
|
| 1357 |
window.downloadGradCAMImage = function(zoneNumber) {
|
| 1358 |
if (!window.currentGradCAMData) {
|
|
@@ -1514,5 +1408,23 @@ if (false) { /* removed */ }
|
|
| 1514 |
<script src="js/gauge.min.js"></script>
|
| 1515 |
<script src="js/jquery.sparkline.min.js"></script>
|
| 1516 |
<script src="js/apps.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1517 |
</body>
|
| 1518 |
</html>
|
|
|
|
| 9 |
<title>Diagnóstico IA</title>
|
| 10 |
<link rel="stylesheet" href="css/simplebar.css">
|
| 11 |
<link href="https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,100;0,200;0,300;0,400;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
|
| 12 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 13 |
<link rel="stylesheet" href="css/select2.css">
|
| 14 |
<link rel="stylesheet" href="css/dropzone.css">
|
| 15 |
<link rel="stylesheet" href="css/uppy.min.css">
|
|
|
|
| 195 |
<!-- Navegación superior -->
|
| 196 |
<nav class="topnav navbar navbar-light">
|
| 197 |
<button type="button" class="navbar-toggler text-muted mt-2 p-0 mr-3 collapseSidebar">
|
| 198 |
+
<i data-feather="menu" class="navbar-toggler-icon"></i>
|
| 199 |
</button>
|
| 200 |
|
| 201 |
<ul class="nav">
|
|
|
|
| 213 |
</nav>
|
| 214 |
<aside class="sidebar-left border-right bg-white shadow" id="leftSidebar" data-simplebar>
|
| 215 |
<a href="#" class="btn collapseSidebar toggle-btn d-lg-none text-muted ml-2 mt-3" data-toggle="toggle">
|
| 216 |
+
<i data-feather="x"><span class="sr-only"></span></i>
|
| 217 |
</a>
|
| 218 |
<nav class="vertnav navbar navbar-light">
|
| 219 |
<!-- nav bar -->
|
|
|
|
| 226 |
<ul class="navbar-nav flex-fill w-100 mb-2">
|
| 227 |
<li class="nav-item">
|
| 228 |
<a class="nav-link" href="./index.html">
|
| 229 |
+
<i data-feather="home" class="fe-16"></i>
|
| 230 |
<span class="ml-3 item-text">Dashboard</span>
|
| 231 |
</a>
|
| 232 |
</li>
|
| 233 |
<li class="nav-item active">
|
| 234 |
<a class="nav-link" href="./diagnosis.html">
|
| 235 |
+
<i data-feather="eye" class="fe-16"></i>
|
| 236 |
<span class="ml-3 item-text">Diagnóstico</span>
|
| 237 |
</a>
|
| 238 |
</li>
|
| 239 |
<li class="nav-item">
|
| 240 |
<a class="nav-link" href="./patients.html">
|
| 241 |
+
<i data-feather="users" class="fe-16"></i>
|
| 242 |
<span class="ml-3 item-text">Pacientes</span>
|
| 243 |
</a>
|
| 244 |
</li>
|
| 245 |
<li class="nav-item">
|
| 246 |
<a class="nav-link" href="./historial-consultas.html">
|
| 247 |
+
<i data-feather="file-text" class="fe-16"></i>
|
| 248 |
<span class="ml-3 item-text">Consultas</span>
|
| 249 |
</a>
|
| 250 |
</li>
|
| 251 |
<li class="nav-item active">
|
| 252 |
<a class="nav-link" href="./users.html">
|
| 253 |
+
<i data-feather="user" class="fe-16"></i>
|
| 254 |
<span class="ml-3 item-text">Usuarios</span>
|
| 255 |
</a>
|
| 256 |
</li>
|
|
|
|
| 267 |
<div class="row align-items-center mb-4">
|
| 268 |
<div class="col">
|
| 269 |
<h2 class="h3 page-title">
|
| 270 |
+
<i data-feather="eye" class="fe-24 mr-2 text-primary"></i>
|
| 271 |
Diagnóstico con Inteligencia Artificial
|
| 272 |
</h2>
|
| 273 |
<p class="text-muted">Diagnostico de retinopatía diabética mediante análisis de imágenes</p>
|
|
|
|
| 280 |
<div class="card shadow mb-4">
|
| 281 |
<div class="card-header">
|
| 282 |
<h5 class="card-title mb-0">
|
| 283 |
+
<i data-feather="upload" class="fe-16 mr-2"></i>Cargar Imágenes para Análisis
|
| 284 |
</h5>
|
| 285 |
</div>
|
| 286 |
<div class="card-body">
|
|
|
|
| 294 |
|
| 295 |
<!-- Zona de carga 2 -->
|
| 296 |
<div id="uploadZone2" class="upload-zone" data-zone="2">
|
| 297 |
+
<i data-feather="camera" class="fe-48 text-muted mb-3"></i>
|
| 298 |
<h5 class="text-muted">Arrastra una imagen aquí o haz clic para seleccionar</h5>
|
| 299 |
<p class="text-muted mb-0">Formatos soportados: JPG, PNG, JPEG (Máx. 10MB)</p>
|
| 300 |
</div>
|
|
|
|
| 304 |
<img id="previewImg2" class="image-preview" alt="Vista previa">
|
| 305 |
<div class="mt-3">
|
| 306 |
<button class="btn btn-success btn-lg mr-2" id="analyzeBtn2">
|
| 307 |
+
<i data-feather="zap" class="fe-16 mr-2"></i>Analizar con IA
|
| 308 |
</button>
|
| 309 |
<button class="btn btn-outline-secondary mr-2" id="clearBtn2">
|
| 310 |
+
<i data-feather="x" class="fe-16 mr-2"></i>Limpiar
|
| 311 |
</button>
|
| 312 |
<button class="btn btn-warning gradcam-btn" id="gradcamBtn2" style="display: none;">
|
| 313 |
+
<i data-feather="eye" class="fe-16 mr-2"></i>Ver Grad-CAM
|
| 314 |
</button>
|
| 315 |
</div>
|
| 316 |
</div>
|
|
|
|
| 320 |
<div class="card shadow mb-4 analysis-card">
|
| 321 |
<div class="card-body">
|
| 322 |
<h5 class="mb-4">
|
| 323 |
+
<i data-feather="activity" class="fe-16 mr-2"></i>Resultados - Ojo Derecho
|
| 324 |
</h5>
|
| 325 |
<div id="resultContent2"></div>
|
| 326 |
</div>
|
|
|
|
| 337 |
|
| 338 |
<!-- Zona de carga 1 -->
|
| 339 |
<div id="uploadZone1" class="upload-zone" data-zone="1">
|
| 340 |
+
<i data-feather="camera" class="fe-48 text-muted mb-3"></i>
|
| 341 |
<h5 class="text-muted">Arrastra una imagen aquí o haz clic para seleccionar</h5>
|
| 342 |
<p class="text-muted mb-0">Formatos soportados: JPG, PNG, JPEG (Máx. 10MB)</p>
|
| 343 |
</div>
|
|
|
|
| 347 |
<img id="previewImg1" class="image-preview" alt="Vista previa">
|
| 348 |
<div class="mt-3">
|
| 349 |
<button class="btn btn-success btn-lg mr-2" id="analyzeBtn1">
|
| 350 |
+
<i data-feather="zap" class="fe-16 mr-2"></i>Analizar con IA
|
| 351 |
</button>
|
| 352 |
<button class="btn btn-outline-secondary mr-2" id="clearBtn1">
|
| 353 |
+
<i data-feather="x" class="fe-16 mr-2"></i>Limpiar
|
| 354 |
</button>
|
| 355 |
<button class="btn btn-warning gradcam-btn" id="gradcamBtn1" style="display: none;">
|
| 356 |
+
<i data-feather="eye" class="fe-16 mr-2"></i>Ver Grad-CAM
|
| 357 |
</button>
|
| 358 |
</div>
|
| 359 |
</div>
|
|
|
|
| 363 |
<div class="card shadow mb-4 analysis-card">
|
| 364 |
<div class="card-body">
|
| 365 |
<h5 class="mb-4">
|
| 366 |
+
<i data-feather="activity" class="fe-16 mr-2"></i>Resultados - Ojo Izquierdo
|
| 367 |
</h5>
|
| 368 |
<div id="resultContent1"></div>
|
| 369 |
</div>
|
|
|
|
| 380 |
<div class="card shadow mb-4">
|
| 381 |
<div class="card-header">
|
| 382 |
<h6 class="card-title mb-0">
|
| 383 |
+
<i data-feather="user" class="fe-16 mr-2"></i>Información del Paciente y Consulta
|
| 384 |
</h6>
|
| 385 |
</div>
|
| 386 |
<div class="card-body">
|
|
|
|
| 436 |
</div>
|
| 437 |
|
| 438 |
<button class="btn btn-primary btn-block" id="saveConsultationBtn" type="button">
|
| 439 |
+
<i data-feather="save" class="fe-16 mr-2"></i>Guardar Consulta
|
| 440 |
</button>
|
| 441 |
</div>
|
| 442 |
</div>
|
|
|
|
| 445 |
<div class="card shadow mb-4">
|
| 446 |
<div class="card-header">
|
| 447 |
<h6 class="card-title mb-0">
|
| 448 |
+
<i data-feather="info" class="fe-16 mr-2"></i>Importante
|
| 449 |
</h6>
|
| 450 |
</div>
|
| 451 |
<div class="card-body">
|
|
|
|
| 465 |
</main>
|
| 466 |
</div>
|
| 467 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
<script src="js/jquery.min.js"></script>
|
| 469 |
<script src="js/popper.min.js"></script>
|
| 470 |
<script src="js/moment.min.js"></script>
|
|
|
|
| 504 |
|
| 505 |
updateLastUpdate();
|
| 506 |
setInterval(updateLastUpdate, 60000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
}
|
| 508 |
|
| 509 |
// AUTENTICACION
|
|
|
|
| 725 |
}
|
| 726 |
|
| 727 |
$(`#analyzeBtn${zoneNumber}`).prop('disabled', false)
|
| 728 |
+
.html('<i data-feather="zap" class="fe-16 mr-2"></i>Analizar con IA')
|
| 729 |
.removeClass('pulse-animation');
|
| 730 |
|
| 731 |
showAnalysisResults(mockResult, zoneNumber);
|
|
|
|
| 738 |
api.predict(imageData, fileName).then(function(result) {
|
| 739 |
// Restaurar boton
|
| 740 |
$(`#analyzeBtn${zoneNumber}`).prop('disabled', false)
|
| 741 |
+
.html('<i data-feather="zap" class="fe-16 mr-2"></i>Analizar con IA')
|
| 742 |
.removeClass('pulse-animation');
|
| 743 |
|
| 744 |
if (result && result.success) {
|
|
|
|
| 820 |
<div class="card shadow-sm mt-3">
|
| 821 |
<div class="card-header d-flex justify-content-between align-items-center">
|
| 822 |
<h6 class="mb-0">
|
| 823 |
+
<i data-feather="bar-chart-2" class="fe-16 mr-2"></i>Detalles del Análisis
|
| 824 |
</h6>
|
| 825 |
<span class="badge badge-pill ${isPositive ? 'badge-warning' : 'badge-success'}">${confidence}%</span>
|
| 826 |
</div>
|
|
|
|
| 848 |
<div class="border-right">
|
| 849 |
<h6 class="text-muted mb-0">Análisis realizado</h6>
|
| 850 |
<small class="text-muted">
|
| 851 |
+
<i data-feather="clock" class="fe-12 mr-1"></i>
|
| 852 |
${moment ? moment(result.timestamp).format('DD/MM/YYYY HH:mm') : new Date(result.timestamp).toLocaleString()}
|
| 853 |
</small>
|
| 854 |
</div>
|
|
|
|
| 856 |
<div class="col-6">
|
| 857 |
<h6 class="text-muted mb-0">Modelo utilizado</h6>
|
| 858 |
<small class="text-muted">
|
| 859 |
+
<i data-feather="cpu" class="fe-12 mr-1"></i>
|
| 860 |
EfficientNetB0
|
| 861 |
</small>
|
| 862 |
</div>
|
|
|
|
| 1011 |
console.log('Eel no disponible - simulando guardado');
|
| 1012 |
setTimeout(() => {
|
| 1013 |
$('#saveConsultationBtn').prop('disabled', false)
|
| 1014 |
+
.html('<i data-feather="save" class="fe-16 mr-2"></i>Guardar Consulta');
|
| 1015 |
showAlert('success', 'Consulta guardada correctamente (simulación)');
|
| 1016 |
}, 1500);
|
| 1017 |
return;
|
|
|
|
| 1023 |
|
| 1024 |
// Restaurar boton
|
| 1025 |
$('#saveConsultationBtn').prop('disabled', false)
|
| 1026 |
+
.html('<i data-feather="save" class="fe-16 mr-2"></i>Guardar Consulta');
|
| 1027 |
|
| 1028 |
if (response && response.success) {
|
| 1029 |
+
showAlert('success', 'Consulta guardada correctamente');
|
| 1030 |
console.log('Consulta guardada exitosamente:', response);
|
|
|
|
| 1031 |
} else {
|
| 1032 |
const errorMsg = response?.message || 'Error al guardar la consulta';
|
| 1033 |
+
showAlert('error', errorMsg);
|
| 1034 |
console.error('Error guardando consulta:', response);
|
|
|
|
| 1035 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1036 |
});
|
| 1037 |
}
|
| 1038 |
|
|
|
|
| 1067 |
api.gradcam(imageData, fileName, lastResult).then(function(response) {
|
| 1068 |
// Restaurar boton
|
| 1069 |
$(`#gradcamBtn${zoneNumber}`).prop('disabled', false)
|
| 1070 |
+
.html('<i data-feather="eye" class="fe-16 mr-2"></i>Ver Grad-CAM');
|
| 1071 |
|
| 1072 |
if (response && response.success) {
|
| 1073 |
console.log('Grad-CAM generado exitosamente');
|
|
|
|
| 1097 |
<div class="modal-content">
|
| 1098 |
<div class="modal-header bg-danger text-white">
|
| 1099 |
<h5 class="modal-title">
|
| 1100 |
+
<i data-feather="eye" class="mr-2"></i>Zoom HD - ${eyeName}
|
| 1101 |
<span class="badge badge-light text-danger ml-2">Retinopatía Detectada</span>
|
| 1102 |
</h5>
|
| 1103 |
<button type="button" class="close text-white" data-dismiss="modal">
|
|
|
|
| 1112 |
alt="Zoom HD - Región Crítica">
|
| 1113 |
<div class="mt-3">
|
| 1114 |
<h6 class="text-danger mb-2">
|
| 1115 |
+
<i data-feather="zoom-in" class="mr-2"></i>Zona Crítica en Alta Resolución
|
| 1116 |
</h6>
|
| 1117 |
<p class="text-muted mb-0">
|
| 1118 |
<strong>Activación máxima:</strong> ${maxAct} |
|
|
|
|
| 1129 |
</div>
|
| 1130 |
<div class="alert alert-${alertType}">
|
| 1131 |
<small>
|
| 1132 |
+
<i data-feather="info" class="mr-1"></i>
|
| 1133 |
${clinical}
|
| 1134 |
</small>
|
| 1135 |
</div>
|
|
|
|
| 1137 |
</div>
|
| 1138 |
<div class="modal-footer">
|
| 1139 |
<button type="button" class="btn btn-outline-primary" onclick="downloadGradCAMImage(${zoneNumber})">
|
| 1140 |
+
<i data-feather="download" class="mr-2"></i>Descargar Imagen
|
| 1141 |
</button>
|
| 1142 |
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
| 1143 |
+
<i data-feather="x" class="mr-2"></i>Cerrar
|
| 1144 |
</button>
|
| 1145 |
</div>
|
| 1146 |
</div>
|
|
|
|
| 1232 |
|
| 1233 |
const alertHtml = `
|
| 1234 |
<div class="alert ${alertClass} alert-dismissible fade show" role="alert">
|
| 1235 |
+
${(function(){var n=type==='error'?'x-circle':type==='success'?'check-circle':type==='warning'?'alert-triangle':'info';return typeof feather!=='undefined'?feather.icons[n].toSvg({'width':16,'height':16,'stroke-width':1.8,'style':'margin-right:6px;vertical-align:middle;'}):'';})()}
|
| 1236 |
${message}
|
| 1237 |
<button type="button" class="close" data-dismiss="alert">
|
| 1238 |
<span aria-hidden="true">×</span>
|
|
|
|
| 1247 |
}, 5000);
|
| 1248 |
}
|
| 1249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1250 |
// === FUNCIONES GLOBALES ===
|
| 1251 |
window.downloadGradCAMImage = function(zoneNumber) {
|
| 1252 |
if (!window.currentGradCAMData) {
|
|
|
|
| 1408 |
<script src="js/gauge.min.js"></script>
|
| 1409 |
<script src="js/jquery.sparkline.min.js"></script>
|
| 1410 |
<script src="js/apps.js"></script>
|
| 1411 |
+
<script>
|
| 1412 |
+
// Feather Icons: reemplaza <i data-feather="X"> con SVG inline
|
| 1413 |
+
// Inmune a problemas de fuentes locales
|
| 1414 |
+
function _initFeather() {
|
| 1415 |
+
if (typeof feather !== 'undefined') feather.replace({ 'stroke-width': 1.8 });
|
| 1416 |
+
}
|
| 1417 |
+
document.addEventListener('DOMContentLoaded', _initFeather);
|
| 1418 |
+
// Re-render al inyectar HTML dinámico (modals, cards, tablas)
|
| 1419 |
+
var _fObserver = new MutationObserver(function(mutations) {
|
| 1420 |
+
var needsRefresh = mutations.some(function(m) {
|
| 1421 |
+
return m.addedNodes.length > 0;
|
| 1422 |
+
});
|
| 1423 |
+
if (needsRefresh) _initFeather();
|
| 1424 |
+
});
|
| 1425 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 1426 |
+
_fObserver.observe(document.body, { childList: true, subtree: true });
|
| 1427 |
+
});
|
| 1428 |
+
</script>
|
| 1429 |
</body>
|
| 1430 |
</html>
|