aarnal80 commited on
Commit
c952be1
·
verified ·
1 Parent(s): 046250c

Update js/main.js

Browse files
Files changed (1) hide show
  1. js/main.js +73 -135
js/main.js CHANGED
@@ -1,55 +1,43 @@
1
  // js/main.js
2
 
3
  // --- INICIO: Importaciones de Módulos ---
4
- import { renderIaConfigForm, transcriptionProviders, getIaConfig } from './iaConfigModule.js'; // Añadido getIaConfig
5
  import { initRecorder } from './recordingModule.js';
6
  import { analyzeMedical } from './analysisModule.js';
7
  import { copyText } from './clipboardModule.js';
8
- // NUEVO: Importar funciones del módulo de análisis de laboratorio
9
- import { analyzeLabResults, displayLabResults } from './labAnalysisModule.js';
10
  // --- FIN: Importaciones de Módulos ---
11
 
12
  // Esperar a que el DOM esté completamente cargado
13
  window.addEventListener('DOMContentLoaded', () => {
14
 
15
- // --- INICIO: Variables y Estado Global (dentro del scope del DOMContentLoaded) ---
16
- // Variable para guardar el último resultado completo del análisis de laboratorio (para re-filtrar)
17
- let lastLabResultText = '';
18
  // --- FIN: Variables y Estado Global ---
19
 
20
  // --- INICIO: Función para Actualizar Etiquetas de Modelo ---
21
- // Actualiza las etiquetas que muestran qué modelo IA se está usando
22
  function updateModelLabels() {
23
- const cfg = getIaConfig(); // Usar la función importada para obtener config
24
  const transProvider = cfg?.transcription?.provider;
25
  const transModel = cfg?.transcription?.models?.[transProvider] || 'N/A';
26
  const llmProvider = cfg?.llm?.provider;
27
- const llmModel = cfg?.llm?.model || 'N/A'; // Usar el modelo LLM general
28
 
29
- // Etiqueta modelo Transcripción
30
  const transLabel = document.getElementById('trans-model-label');
31
  if (transLabel) transLabel.textContent = transModel ? `(${transModel})` : '';
32
-
33
- // Etiqueta modelo Análisis Médico
34
  const analysisLabel = document.getElementById('analysis-model-label');
35
  if (analysisLabel) analysisLabel.textContent = llmModel ? `(${llmModel})` : '';
36
-
37
- // NUEVO: Etiqueta modelo Análisis Laboratorio
38
  const labLabel = document.getElementById('lab-model-label');
39
  if (labLabel) labLabel.textContent = llmModel ? `(${llmModel})` : '';
40
  }
41
- // Llamada inicial para establecer las etiquetas
42
- updateModelLabels();
43
- // Escuchar cambios en la config para actualizar etiquetas
44
- document.addEventListener('iaConfigChanged', updateModelLabels);
45
  // --- FIN: Función para Actualizar Etiquetas de Modelo ---
46
 
47
  // --- INICIO: Selección de Elementos DOM ---
48
  // Configuración
49
  const btnConfig = document.getElementById('btnConfig');
50
  const modal = document.getElementById('configModal');
51
- const iaConfigContainer = document.getElementById('iaConfigContainer');
52
-
53
  // Pestaña Análisis Médico
54
  const transcriptEl = document.getElementById('transcript');
55
  const outputEnfermedadEl = document.getElementById('output-enfermedad');
@@ -58,14 +46,10 @@ window.addEventListener('DOMContentLoaded', () => {
58
  const btnStop = document.getElementById('btnStop');
59
  const btnCopyTranscript = document.getElementById('btnCopyTranscript');
60
  const btnCopyAnalysis = document.getElementById('btnCopyAnalysis');
61
-
62
- // NUEVO: Elementos de Pestañas
63
- const tabMedicalAnalysis = document.getElementById('tab-medical-analysis');
64
- const tabLabAnalysis = document.getElementById('tab-lab-analysis');
65
- const contentMedicalAnalysis = document.getElementById('content-medical-analysis');
66
- const contentLabAnalysis = document.getElementById('content-lab-analysis');
67
-
68
- // NUEVO: Elementos Pestaña Análisis de Exámenes
69
  const labInputText = document.getElementById('lab-input-text');
70
  const btnAnalyzeLabs = document.getElementById('btnAnalyzeLabs');
71
  const labLoadingIndicator = document.getElementById('lab-loading-indicator');
@@ -74,163 +58,134 @@ window.addEventListener('DOMContentLoaded', () => {
74
  const btnCopyLabResults = document.getElementById('btnCopyLabResults');
75
  // --- FIN: Selección de Elementos DOM ---
76
 
77
- // --- INICIO: Lógica Modal Configuración ---
78
- // Renderizar formulario inicial (no debería modificar config aún)
79
- renderIaConfigForm(iaConfigContainer);
 
80
 
81
- // Abrir modal y renderizar formulario actualizado
82
  btnConfig.addEventListener('click', () => {
83
- renderIaConfigForm(iaConfigContainer); // Re-renderizar al abrir por si algo cambió
 
84
  modal.classList.add('active');
85
  });
86
- // Cerrar modal clicando fuera (manejado en módulo) o en botón X (manejado en módulo)
87
  modal.addEventListener('mousedown', e => {
88
  if (e.target === modal) modal.classList.remove('active');
89
  });
90
  // --- FIN: Lógica Modal Configuración ---
91
 
92
- // --- INICIO: Lógica Pestaña Análisis Médico (Transcripción y Análisis) ---
93
- // Evento: Transcripción lista -> Iniciar Análisis Médico
94
  document.addEventListener('transcriptionReady', async e => {
95
  const transcriptText = e.detail;
96
- if (!transcriptText) return; // No analizar si no hay transcripción
97
-
98
- // Limpiar análisis previo y mostrar estado (opcional)
99
  outputEnfermedadEl.textContent = 'Analizando...';
100
  outputExploracionEl.textContent = '';
101
-
102
  try {
103
- const result = await analyzeMedical(transcriptText); // Llama al módulo de análisis
104
- // Dividir en dos párrafos (o como esté estructurado) y mostrar en UI
105
- const sections = result.split(/\n\s*\n/); // Asume división por doble salto de línea
106
  outputEnfermedadEl.textContent = sections[0]?.trim() || '(No se generó Enfermedad Actual)';
107
  outputExploracionEl.textContent = sections.slice(1).join('\n\n').trim() || '(No se generó Exploración/Actuación)';
108
  } catch (err) {
109
  console.error("Error en análisis médico:", err);
110
  outputEnfermedadEl.textContent = `Error en análisis: ${err.message}`;
111
  outputExploracionEl.textContent = '';
112
- alert(`Error en análisis médico: ${err.message}`); // Notificar al usuario
113
  }
114
  });
115
 
116
- // Inicializar Grabadora de Audio
117
- function getTranscriptionProviderUrl() { // Renombrado para claridad
118
  const cfg = getIaConfig();
119
  const providerValue = cfg?.transcription?.provider;
120
  if (providerValue) {
121
  const prov = transcriptionProviders.find(p => p.value === providerValue);
122
  if (prov?.url) return prov.url;
123
  }
124
- console.warn('No se pudo obtener la URL del proveedor de transcripción desde la configuración.');
125
- alert('Configuración de transcripción incompleta. Por favor, configura un proveedor y API key.');
126
- return ''; // Retornar vacío para prevenir error si no hay config
127
  }
128
- // Pasar elementos y función para obtener URL del proveedor al módulo de grabación
129
  initRecorder({
130
- btnStart: btnStart,
131
- btnStop: btnStop,
132
- transcriptEl: transcriptEl,
133
- getProvider: getTranscriptionProviderUrl // Pasar la función renombrada
134
  });
135
 
136
- // Copiar Transcripción
137
  if (btnCopyTranscript) {
138
  btnCopyTranscript.addEventListener('click', async () => {
139
- try {
140
- await copyText(transcriptEl.value);
141
- console.log('Transcripción copiada');
142
- // Feedback visual rápido (opcional)
143
- const originalText = btnCopyTranscript.innerHTML;
144
- btnCopyTranscript.innerHTML = '<i class="fas fa-check mr-2"></i>Copiado';
145
- setTimeout(() => { btnCopyTranscript.innerHTML = originalText; }, 1500);
146
- } catch (err) {
147
- console.error('Error al copiar transcripción:', err);
148
- alert('Error al copiar la transcripción.');
149
- }
150
  });
151
  }
152
-
153
- // Copiar Análisis Médico
154
  if (btnCopyAnalysis) {
155
  btnCopyAnalysis.addEventListener('click', async () => {
156
- try {
157
- const text = `${outputEnfermedadEl.textContent}\n\n${outputExploracionEl.textContent}`;
158
- await copyText(text);
159
- console.log('Análisis médico copiado');
160
- // Feedback visual rápido (opcional)
161
- const originalText = btnCopyAnalysis.innerHTML;
162
- btnCopyAnalysis.innerHTML = '<i class="fas fa-check mr-2"></i>Copiado';
163
- setTimeout(() => { btnCopyAnalysis.innerHTML = originalText; }, 1500);
164
- } catch (err) {
165
- console.error('Error al copiar análisis médico:', err);
166
- alert('Error al copiar el análisis médico.');
167
- }
168
  });
169
  }
170
  // --- FIN: Lógica Pestaña Análisis Médico ---
171
 
172
- // --- INICIO: Lógica de Cambio de Pestañas ---
173
- function switchTab(event) {
174
- // Obtener el botón de pestaña que fue clickeado
175
- const clickedTab = event.currentTarget;
176
- // Obtener el ID del contenido asociado desde el atributo data-target
177
- const targetContentId = clickedTab.dataset.target;
178
  const targetContent = document.getElementById(targetContentId);
179
 
180
- // Si el target existe y no está ya activo
181
- if (targetContent && !clickedTab.classList.contains('active')) {
182
- // 1. Quitar 'active' de todos los botones de pestaña y contenidos
183
- document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
184
- document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
185
-
186
- // 2. Añadir 'active' al botón clickeado y a su contenido asociado
187
- clickedTab.classList.add('active');
188
- targetContent.classList.add('active');
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  console.log(`Cambiado a pestaña: ${targetContentId}`);
191
  }
192
  }
193
 
194
- // Añadir el event listener a cada botón de pestaña
195
- if (tabMedicalAnalysis) tabMedicalAnalysis.addEventListener('click', switchTab);
196
- if (tabLabAnalysis) tabLabAnalysis.addEventListener('click', switchTab);
 
 
 
197
  // --- FIN: Lógica de Cambio de Pestañas ---
198
 
199
- // --- INICIO: Lógica Pestaña Análisis de Exámenes ---
200
- // Event Listener para el botón "Analizar Resultados"
201
  if (btnAnalyzeLabs) {
202
  btnAnalyzeLabs.addEventListener('click', async () => {
203
  const inputText = labInputText.value;
204
- if (!inputText.trim()) {
205
- alert("Por favor, pega los resultados del laboratorio en el área de texto.");
206
- return;
207
- }
208
 
209
- // Mostrar indicador de carga y deshabilitar controles
210
  labLoadingIndicator.style.display = 'flex';
211
  btnAnalyzeLabs.disabled = true;
212
  filterAlteredLabsCheckbox.disabled = true;
213
- labResultsOutput.innerHTML = ''; // Limpiar salida anterior
214
- lastLabResultText = ''; // Limpiar resultado anterior guardado
215
 
216
  try {
217
- // Llamar al módulo para analizar
218
  const resultText = await analyzeLabResults(inputText);
219
- lastLabResultText = resultText; // Guardar resultado completo para filtrar después
220
-
221
- // Obtener estado actual del filtro
222
  const filterIsActive = filterAlteredLabsCheckbox.checked;
223
- // Mostrar resultado (filtrado o completo)
224
  displayLabResults(resultText, labResultsOutput, filterIsActive);
225
- // Actualizar etiqueta del modelo usado
226
  updateModelLabels();
227
-
228
  } catch (error) {
229
  console.error("Error en análisis de laboratorio:", error);
230
  labResultsOutput.innerHTML = `<p class="text-red-600">Error al analizar: ${error.message}</p>`;
231
  alert(`Error al analizar los resultados: ${error.message}`);
232
  } finally {
233
- // Ocultar indicador de carga y habilitar controles
234
  labLoadingIndicator.style.display = 'none';
235
  btnAnalyzeLabs.disabled = false;
236
  filterAlteredLabsCheckbox.disabled = false;
@@ -238,39 +193,22 @@ window.addEventListener('DOMContentLoaded', () => {
238
  });
239
  }
240
 
241
- // Event Listener para el checkbox "Mostrar solo alterados"
242
  if (filterAlteredLabsCheckbox) {
243
  filterAlteredLabsCheckbox.addEventListener('change', () => {
244
- // Si no hay resultado previo guardado, no hacer nada
245
  if (!lastLabResultText) return;
246
-
247
- // Obtener estado actual del filtro
248
  const filterIsActive = filterAlteredLabsCheckbox.checked;
249
- // Volver a mostrar los resultados (usando el texto completo guardado) con el nuevo estado del filtro
250
  displayLabResults(lastLabResultText, labResultsOutput, filterIsActive);
251
  });
252
  }
253
 
254
- // Event Listener para el botón "Copiar Resultados" de Laboratorio
255
  if (btnCopyLabResults) {
256
  btnCopyLabResults.addEventListener('click', async () => {
257
- // Copiar el contenido *actualmente visible* en el div de resultados
258
  const textToCopy = labResultsOutput.textContent;
259
  if (!textToCopy || textToCopy.startsWith('Error') || textToCopy.includes('No se generaron resultados')) {
260
- alert('No hay resultados válidos para copiar.');
261
- return;
262
- }
263
- try {
264
- await copyText(textToCopy);
265
- console.log('Resultados de laboratorio copiados');
266
- // Feedback visual rápido (opcional)
267
- const originalText = btnCopyLabResults.innerHTML;
268
- btnCopyLabResults.innerHTML = '<i class="fas fa-check mr-2"></i>Copiado';
269
- setTimeout(() => { btnCopyLabResults.innerHTML = originalText; }, 1500);
270
- } catch (err) {
271
- console.error('Error al copiar resultados de laboratorio:', err);
272
- alert('Error al copiar los resultados.');
273
  }
 
 
274
  });
275
  }
276
  // --- FIN: Lógica Pestaña Análisis de Exámenes ---
 
1
  // js/main.js
2
 
3
  // --- INICIO: Importaciones de Módulos ---
4
+ import { renderIaConfigForm, transcriptionProviders, getIaConfig } from './iaConfigModule.js';
5
  import { initRecorder } from './recordingModule.js';
6
  import { analyzeMedical } from './analysisModule.js';
7
  import { copyText } from './clipboardModule.js';
8
+ import { analyzeLabResults, displayLabResults } from './labAnalysisModule.js'; // Asegúrate que esta línea está presente
 
9
  // --- FIN: Importaciones de Módulos ---
10
 
11
  // Esperar a que el DOM esté completamente cargado
12
  window.addEventListener('DOMContentLoaded', () => {
13
 
14
+ // --- INICIO: Variables y Estado Global ---
15
+ let lastLabResultText = ''; // Para guardar último resultado lab (filtrado)
 
16
  // --- FIN: Variables y Estado Global ---
17
 
18
  // --- INICIO: Función para Actualizar Etiquetas de Modelo ---
 
19
  function updateModelLabels() {
20
+ const cfg = getIaConfig();
21
  const transProvider = cfg?.transcription?.provider;
22
  const transModel = cfg?.transcription?.models?.[transProvider] || 'N/A';
23
  const llmProvider = cfg?.llm?.provider;
24
+ const llmModel = cfg?.llm?.model || 'N/A';
25
 
 
26
  const transLabel = document.getElementById('trans-model-label');
27
  if (transLabel) transLabel.textContent = transModel ? `(${transModel})` : '';
 
 
28
  const analysisLabel = document.getElementById('analysis-model-label');
29
  if (analysisLabel) analysisLabel.textContent = llmModel ? `(${llmModel})` : '';
 
 
30
  const labLabel = document.getElementById('lab-model-label');
31
  if (labLabel) labLabel.textContent = llmModel ? `(${llmModel})` : '';
32
  }
33
+ updateModelLabels(); // Llamada inicial
34
+ document.addEventListener('iaConfigChanged', updateModelLabels); // Listener para cambios
 
 
35
  // --- FIN: Función para Actualizar Etiquetas de Modelo ---
36
 
37
  // --- INICIO: Selección de Elementos DOM ---
38
  // Configuración
39
  const btnConfig = document.getElementById('btnConfig');
40
  const modal = document.getElementById('configModal');
 
 
41
  // Pestaña Análisis Médico
42
  const transcriptEl = document.getElementById('transcript');
43
  const outputEnfermedadEl = document.getElementById('output-enfermedad');
 
46
  const btnStop = document.getElementById('btnStop');
47
  const btnCopyTranscript = document.getElementById('btnCopyTranscript');
48
  const btnCopyAnalysis = document.getElementById('btnCopyAnalysis');
49
+ // Elementos Pestañas
50
+ const allTabButtons = document.querySelectorAll('.tab-button'); // Seleccionar todos los botones de pestaña
51
+ const allTabContents = document.querySelectorAll('.tab-content'); // Seleccionar todos los contenidos de pestaña
52
+ // Elementos Pestaña Análisis de Exámenes
 
 
 
 
53
  const labInputText = document.getElementById('lab-input-text');
54
  const btnAnalyzeLabs = document.getElementById('btnAnalyzeLabs');
55
  const labLoadingIndicator = document.getElementById('lab-loading-indicator');
 
58
  const btnCopyLabResults = document.getElementById('btnCopyLabResults');
59
  // --- FIN: Selección de Elementos DOM ---
60
 
61
+ // --- INICIO: Lógica Modal Configuración (CORREGIDA) ---
62
+ // Renderizar formulario inicial
63
+ // !! CORRECCIÓN IMPORTANTE: Pasar el ID como string !!
64
+ renderIaConfigForm('iaConfigContainer');
65
 
66
+ // Abrir modal
67
  btnConfig.addEventListener('click', () => {
68
+ // !! CORRECCIÓN IMPORTANTE: Pasar el ID como string !!
69
+ renderIaConfigForm('iaConfigContainer');
70
  modal.classList.add('active');
71
  });
72
+ // Cerrar modal
73
  modal.addEventListener('mousedown', e => {
74
  if (e.target === modal) modal.classList.remove('active');
75
  });
76
  // --- FIN: Lógica Modal Configuración ---
77
 
78
+ // --- INICIO: Lógica Pestaña Análisis Médico (Sin cambios funcionales) ---
 
79
  document.addEventListener('transcriptionReady', async e => {
80
  const transcriptText = e.detail;
81
+ if (!transcriptText) return;
 
 
82
  outputEnfermedadEl.textContent = 'Analizando...';
83
  outputExploracionEl.textContent = '';
 
84
  try {
85
+ const result = await analyzeMedical(transcriptText);
86
+ const sections = result.split(/\n\s*\n/);
 
87
  outputEnfermedadEl.textContent = sections[0]?.trim() || '(No se generó Enfermedad Actual)';
88
  outputExploracionEl.textContent = sections.slice(1).join('\n\n').trim() || '(No se generó Exploración/Actuación)';
89
  } catch (err) {
90
  console.error("Error en análisis médico:", err);
91
  outputEnfermedadEl.textContent = `Error en análisis: ${err.message}`;
92
  outputExploracionEl.textContent = '';
93
+ alert(`Error en análisis médico: ${err.message}`);
94
  }
95
  });
96
 
97
+ function getTranscriptionProviderUrl() {
 
98
  const cfg = getIaConfig();
99
  const providerValue = cfg?.transcription?.provider;
100
  if (providerValue) {
101
  const prov = transcriptionProviders.find(p => p.value === providerValue);
102
  if (prov?.url) return prov.url;
103
  }
104
+ console.warn('No se pudo obtener la URL del proveedor de transcripción.');
105
+ alert('Configuración de transcripción incompleta.');
106
+ return '';
107
  }
 
108
  initRecorder({
109
+ btnStart: btnStart, btnStop: btnStop, transcriptEl: transcriptEl,
110
+ getProvider: getTranscriptionProviderUrl
 
 
111
  });
112
 
 
113
  if (btnCopyTranscript) {
114
  btnCopyTranscript.addEventListener('click', async () => {
115
+ try { await copyText(transcriptEl.value); console.log('Transcripción copiada'); /* Add feedback */ }
116
+ catch (err) { console.error('Error al copiar transcripción:', err); alert('Error al copiar transcripción.'); }
 
 
 
 
 
 
 
 
 
117
  });
118
  }
 
 
119
  if (btnCopyAnalysis) {
120
  btnCopyAnalysis.addEventListener('click', async () => {
121
+ try { const text = `${outputEnfermedadEl.textContent}\n\n${outputExploracionEl.textContent}`; await copyText(text); console.log('Análisis médico copiado'); /* Add feedback */ }
122
+ catch (err) { console.error('Error al copiar análisis médico:', err); alert('Error al copiar análisis médico.'); }
 
 
 
 
 
 
 
 
 
 
123
  });
124
  }
125
  // --- FIN: Lógica Pestaña Análisis Médico ---
126
 
127
+ // --- INICIO: Lógica de Cambio de Pestañas (CORREGIDA) ---
128
+ function switchTab(clickedTab) { // Recibe el botón clickeado directamente
129
+ const targetContentId = clickedTab.dataset.contentId;
 
 
 
130
  const targetContent = document.getElementById(targetContentId);
131
 
132
+ // Si el contenido existe y NO está ya activo
133
+ if (targetContent && !targetContent.classList.contains('active')) {
134
+
135
+ // 1. Desactivar TODOS los botones y contenidos
136
+ allTabButtons.forEach(btn => {
137
+ const activeCls = btn.dataset.activeClasses.split(' ');
138
+ const inactiveCls = btn.dataset.inactiveClasses.split(' ');
139
+ // Quitar clases activas y añadir inactivas
140
+ btn.classList.remove(...activeCls.filter(c => c));
141
+ btn.classList.add(...inactiveCls.filter(c => c));
142
+ });
143
+ allTabContents.forEach(content => {
144
+ content.classList.remove('active');
145
+ });
146
+
147
+ // 2. Activar la pestaña clickeada y su contenido
148
+ const activeClsToAdd = clickedTab.dataset.activeClasses.split(' ');
149
+ const inactiveClsToRemove = clickedTab.dataset.inactiveClasses.split(' ');
150
+ clickedTab.classList.remove(...inactiveClsToRemove.filter(c => c));
151
+ clickedTab.classList.add(...activeClsToAdd.filter(c => c));
152
+ targetContent.classList.add('active'); // Mostrar contenido
153
 
154
  console.log(`Cambiado a pestaña: ${targetContentId}`);
155
  }
156
  }
157
 
158
+ // Añadir event listener a cada botón de pestaña
159
+ allTabButtons.forEach(button => {
160
+ button.addEventListener('click', (event) => {
161
+ switchTab(event.currentTarget); // Pasar el botón clickeado a la función
162
+ });
163
+ });
164
  // --- FIN: Lógica de Cambio de Pestañas ---
165
 
166
+ // --- INICIO: Lógica Pestaña Análisis de Exámenes (Sin cambios funcionales aquí) ---
 
167
  if (btnAnalyzeLabs) {
168
  btnAnalyzeLabs.addEventListener('click', async () => {
169
  const inputText = labInputText.value;
170
+ if (!inputText.trim()) { alert("Por favor, pega los resultados."); return; }
 
 
 
171
 
 
172
  labLoadingIndicator.style.display = 'flex';
173
  btnAnalyzeLabs.disabled = true;
174
  filterAlteredLabsCheckbox.disabled = true;
175
+ labResultsOutput.innerHTML = '';
176
+ lastLabResultText = '';
177
 
178
  try {
 
179
  const resultText = await analyzeLabResults(inputText);
180
+ lastLabResultText = resultText;
 
 
181
  const filterIsActive = filterAlteredLabsCheckbox.checked;
 
182
  displayLabResults(resultText, labResultsOutput, filterIsActive);
 
183
  updateModelLabels();
 
184
  } catch (error) {
185
  console.error("Error en análisis de laboratorio:", error);
186
  labResultsOutput.innerHTML = `<p class="text-red-600">Error al analizar: ${error.message}</p>`;
187
  alert(`Error al analizar los resultados: ${error.message}`);
188
  } finally {
 
189
  labLoadingIndicator.style.display = 'none';
190
  btnAnalyzeLabs.disabled = false;
191
  filterAlteredLabsCheckbox.disabled = false;
 
193
  });
194
  }
195
 
 
196
  if (filterAlteredLabsCheckbox) {
197
  filterAlteredLabsCheckbox.addEventListener('change', () => {
 
198
  if (!lastLabResultText) return;
 
 
199
  const filterIsActive = filterAlteredLabsCheckbox.checked;
 
200
  displayLabResults(lastLabResultText, labResultsOutput, filterIsActive);
201
  });
202
  }
203
 
 
204
  if (btnCopyLabResults) {
205
  btnCopyLabResults.addEventListener('click', async () => {
 
206
  const textToCopy = labResultsOutput.textContent;
207
  if (!textToCopy || textToCopy.startsWith('Error') || textToCopy.includes('No se generaron resultados')) {
208
+ alert('No hay resultados válidos para copiar.'); return;
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
+ try { await copyText(textToCopy); console.log('Resultados laboratorio copiados'); /* Add feedback */ }
211
+ catch (err) { console.error('Error al copiar resultados laboratorio:', err); alert('Error al copiar resultados.'); }
212
  });
213
  }
214
  // --- FIN: Lógica Pestaña Análisis de Exámenes ---