Jose Salazar commited on
Commit
1fec72e
·
1 Parent(s): 6ab7946

Agrego boton para el syncing de dispositivos de laboratorio en la UI de desktop

Browse files
Files changed (3) hide show
  1. css/styles.css +46 -5
  2. frontend/src/lab-import.ts +66 -0
  3. index.html +29 -6
css/styles.css CHANGED
@@ -224,11 +224,13 @@ header .barra-paciente select option {
224
  }
225
 
226
  header .boton-tema,
 
227
  header .boton-usuario {
228
  color: var(--header-text-action);
229
  }
230
 
231
  header .boton-tema:hover,
 
232
  header .boton-usuario:hover {
233
  color: var(--header-text-hover);
234
  }
@@ -1652,8 +1654,9 @@ footer {
1652
  background: var(--surface-2);
1653
  }
1654
 
1655
- /* THEME TOGGLE */
1656
- .boton-tema {
 
1657
  background: transparent;
1658
  color: var(--text-2);
1659
  border: 1px solid var(--border-1);
@@ -1664,12 +1667,25 @@ footer {
1664
  min-height: 32px;
1665
  }
1666
 
1667
- .boton-tema:hover {
 
1668
  background: var(--surface-2);
1669
  color: var(--text-1);
1670
  border-color: var(--border-2);
1671
  }
1672
 
 
 
 
 
 
 
 
 
 
 
 
 
1673
  .icono-sol {
1674
  display: none;
1675
  width: 14px;
@@ -2108,6 +2124,12 @@ main {
2108
  align-self: center;
2109
  }
2110
 
 
 
 
 
 
 
2111
  .acciones-cabecera .boton {
2112
  padding: 6px 12px;
2113
  font-size: var(--fs-sm);
@@ -2328,7 +2350,8 @@ main {
2328
  }
2329
 
2330
  /* Modal*/
2331
- .modal-auth {
 
2332
  position: fixed;
2333
  top: 50%;
2334
  left: 50%;
@@ -2344,11 +2367,29 @@ main {
2344
  transition: opacity var(--dur-base), transform var(--dur-base);
2345
  }
2346
 
2347
- .modal-auth.visible {
 
2348
  opacity: 1;
2349
  transform: translate(-50%, -50%);
2350
  }
2351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2352
  .modal-cerrar svg {
2353
  width: 16px;
2354
  height: 16px;
 
224
  }
225
 
226
  header .boton-tema,
227
+ header .boton-lab,
228
  header .boton-usuario {
229
  color: var(--header-text-action);
230
  }
231
 
232
  header .boton-tema:hover,
233
+ header .boton-lab:hover,
234
  header .boton-usuario:hover {
235
  color: var(--header-text-hover);
236
  }
 
1654
  background: var(--surface-2);
1655
  }
1656
 
1657
+ /* THEME TOGGLE + ANALIZADOR (mismos botones de icono de la cabecera) */
1658
+ .boton-tema,
1659
+ .boton-lab {
1660
  background: transparent;
1661
  color: var(--text-2);
1662
  border: 1px solid var(--border-1);
 
1667
  min-height: 32px;
1668
  }
1669
 
1670
+ .boton-tema:hover,
1671
+ .boton-lab:hover {
1672
  background: var(--surface-2);
1673
  color: var(--text-1);
1674
  border-color: var(--border-2);
1675
  }
1676
 
1677
+ .boton-lab svg {
1678
+ width: 14px;
1679
+ height: 14px;
1680
+ fill: currentColor;
1681
+ }
1682
+
1683
+ .boton-lab[aria-expanded="true"] {
1684
+ background: var(--surface-2);
1685
+ border-color: var(--accent);
1686
+ color: var(--accent);
1687
+ }
1688
+
1689
  .icono-sol {
1690
  display: none;
1691
  width: 14px;
 
2124
  align-self: center;
2125
  }
2126
 
2127
+ /* En móvil los controles del analizador están en línea dentro del panel de paciente: el
2128
+ botón de la cabecera sobraría y abriría un modal con el mismo contenido. */
2129
+ .boton-lab {
2130
+ display: none;
2131
+ }
2132
+
2133
  .acciones-cabecera .boton {
2134
  padding: 6px 12px;
2135
  font-size: var(--fs-sm);
 
2350
  }
2351
 
2352
  /* Modal*/
2353
+ .modal-auth,
2354
+ .modal-lab {
2355
  position: fixed;
2356
  top: 50%;
2357
  left: 50%;
 
2367
  transition: opacity var(--dur-base), transform var(--dur-base);
2368
  }
2369
 
2370
+ .modal-auth.visible,
2371
+ .modal-lab.visible {
2372
  opacity: 1;
2373
  transform: translate(-50%, -50%);
2374
  }
2375
 
2376
+ .modal-lab-ayuda {
2377
+ margin: 0 0 var(--space-4);
2378
+ font-size: var(--fs-sm);
2379
+ color: var(--text-3);
2380
+ line-height: 1.5;
2381
+ }
2382
+
2383
+ /* Los controles del analizador se dimensionan para la columna estrecha del panel móvil; en el
2384
+ modal comparten ancho, así que el ID de muestra ocupa la fila entera. */
2385
+ #modal-lab-cuerpo .fila-importar-lab {
2386
+ margin-top: 0;
2387
+ }
2388
+
2389
+ #modal-lab-cuerpo #lab-muestra-id {
2390
+ width: 100%;
2391
+ }
2392
+
2393
  .modal-cerrar svg {
2394
  width: 16px;
2395
  height: 16px;
frontend/src/lab-import.ts CHANGED
@@ -174,6 +174,72 @@ export function inicializarImportLab(evaluar: () => void): void {
174
  }));
175
 
176
  inicializarCola(cargarYAplicar, detener);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
 
179
  // Cola de resultados recibidos: lista para elegir sin teclear el ID de muestra.
 
174
  }));
175
 
176
  inicializarCola(cargarYAplicar, detener);
177
+ inicializarModalLab(detener);
178
+ }
179
+
180
+ // El botón del analizador en la cabecera (sólo escritorio, donde #panel-paciente está oculto).
181
+ // En vez de duplicar los controles —que duplicaría los IDs y, peor, el estado del sondeo, con
182
+ // dos búsquedas vivas a la vez— se MUEVE el nodo #lab-controles entre su sitio en el panel de
183
+ // paciente (móvil) y el modal (escritorio). Al mover un elemento se conservan sus listeners,
184
+ // así que el cableado de arriba sigue valiendo para los dos casos.
185
+ function inicializarModalLab(detenerBusqueda: () => void): void {
186
+ const btn = document.getElementById('btn-lab');
187
+ const modal = document.getElementById('modal-lab');
188
+ const overlay = document.getElementById('modal-lab-overlay');
189
+ const cuerpo = document.getElementById('modal-lab-cuerpo');
190
+ const controles = document.getElementById('lab-controles');
191
+ const anclaje = document.getElementById('lab-anclaje-mob');
192
+ if (!btn || !modal || !overlay || !cuerpo || !controles || !anclaje) return;
193
+
194
+ const esEscritorio = (): boolean => window.innerWidth > 1100;
195
+
196
+ // Idempotente: si ya está donde toca no toca el DOM (se llama en cada `resize`).
197
+ const ubicar = (): void => {
198
+ if (esEscritorio()) {
199
+ if (controles.parentElement !== cuerpo) cuerpo.appendChild(controles);
200
+ } else if (controles.previousElementSibling !== anclaje) {
201
+ anclaje.after(controles);
202
+ }
203
+ };
204
+
205
+ const cerrar = (): void => {
206
+ modal.classList.remove('visible');
207
+ overlay.classList.remove('activo');
208
+ btn.setAttribute('aria-expanded', 'false');
209
+ setTimeout(() => modal.setAttribute('hidden', ''), 250);
210
+ };
211
+
212
+ const abrir = (): void => {
213
+ ubicar();
214
+ modal.removeAttribute('hidden');
215
+ overlay.classList.add('activo');
216
+ btn.setAttribute('aria-expanded', 'true');
217
+ requestAnimationFrame(() => modal.classList.add('visible'));
218
+ (document.getElementById('lab-muestra-id') as HTMLInputElement | null)?.focus();
219
+ };
220
+
221
+ btn.addEventListener('click', () => {
222
+ if (modal.hasAttribute('hidden')) abrir();
223
+ else cerrar();
224
+ });
225
+ overlay.addEventListener('click', cerrar);
226
+ document.getElementById('modal-lab-cerrar')?.addEventListener('click', cerrar);
227
+ document.addEventListener('keydown', (e) => {
228
+ if (e.key === 'Escape' && !modal.hasAttribute('hidden')) cerrar();
229
+ });
230
+
231
+ // Al cruzar el breakpoint hacia móvil los controles vuelven al panel de paciente. Si el modal
232
+ // estaba abierto se cierra, y con él cualquier búsqueda en curso: su botón dejaría de estar a
233
+ // la vista y el sondeo seguiría vivo en segundo plano sin forma de cancelarlo.
234
+ window.addEventListener('resize', () => {
235
+ if (!esEscritorio() && !modal.hasAttribute('hidden')) {
236
+ detenerBusqueda();
237
+ cerrar();
238
+ }
239
+ ubicar();
240
+ });
241
+
242
+ ubicar();
243
  }
244
 
245
  // Cola de resultados recibidos: lista para elegir sin teclear el ID de muestra.
index.html CHANGED
@@ -51,6 +51,11 @@
51
  </select>
52
  </div>
53
  <div class="acciones-cabecera">
 
 
 
 
 
54
  <button class="boton boton-tema" id="btn-tema" type="button" aria-label="Cambiar tema" data-tooltip="Cambiar tema">
55
  <svg class="icono-sol" viewBox="0 -960 960 960" aria-hidden="true"><path d="M565-395q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Zm-226.5 56.5Q280-397 280-480t58.5-141.5Q397-680 480-680t141.5 58.5Q680-563 680-480t-58.5 141.5Q563-280 480-280t-141.5-58.5ZM200-440H40v-80h160v80Zm720 0H760v-80h160v80ZM440-760v-160h80v160h-80Zm0 720v-160h80v160h-80ZM256-650l-101-97 57-59 96 100-52 56Zm492 496-97-101 53-55 101 97-57 59Zm-98-550 97-101 59 57-100 96-56-52ZM154-212l101-97 55 53-97 101-59-57Zm326-268Z"/></svg>
56
  <svg class="icono-luna" viewBox="0 -960 960 960" aria-hidden="true"><path d="M480-120q-150 0-255-105T120-480q0-150 105-255t255-105q14 0 27.5 1t26.5 3q-41 29-65.5 75.5T444-660q0 90 63 153t153 63q55 0 101-24.5t75-65.5q2 13 3 26.5t1 27.5q0 150-105 255T480-120Zm0-80q88 0 158-48.5T740-375q-20 5-40 8t-40 3q-123 0-209.5-86.5T364-660q0-20 3-40t8-40q-78 32-126.5 102T200-480q0 116 82 198t198 82Zm-10-270Z"/></svg>
@@ -106,13 +111,19 @@
106
  <option value="Hembra">Hembra</option>
107
  </select>
108
  </div>
109
- <div class="fila-paciente fila-importar-lab">
110
- <label for="lab-muestra-id">ID de muestra</label>
111
- <input id="lab-muestra-id" type="text" placeholder="escanea o teclea el ID" autocomplete="off">
 
 
 
 
 
 
 
 
 
112
  </div>
113
- <button class="btn-importar-lab" id="btn-importar-lab" type="button">Importar del analizador</button>
114
- <button class="btn-importar-lab btn-pendientes-lab" id="btn-pendientes-lab" type="button">Ver resultados recibidos</button>
115
- <ul class="lab-pendientes" id="lab-pendientes" hidden></ul>
116
  <input type="file" id="pdf-input-paciente" class="pdf-input" accept=".pdf" hidden>
117
  <button class="btn-importar-pdf btn-adjuntar-mob" type="button" data-panel="paciente" aria-label="Adjuntar PDF de exámenes">
118
  <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350V-700q0-42-29-71t-71-29q-42 0-71 29t-29 71v370q0 71 49.5 120.5T470-160q71 0 120.5-49.5T640-330v-390h80v390Z"/></svg>
@@ -1111,6 +1122,18 @@
1111
  <div id="papers-paginacion" class="papers-paginacion"></div>
1112
  </div>
1113
 
 
 
 
 
 
 
 
 
 
 
 
 
1114
  <!-- MODAL DE AUTENTICACIÓN -->
1115
  <div id="modal-auth-overlay" class="modal-overlay" aria-hidden="true"></div>
1116
  <div id="modal-auth" class="modal-auth" role="dialog" aria-modal="true" aria-labelledby="modal-auth-titulo" hidden>
 
51
  </select>
52
  </div>
53
  <div class="acciones-cabecera">
54
+ <!-- Sólo escritorio: en móvil los mismos controles van en línea dentro del panel de
55
+ paciente, que aquí está oculto (>1100px). -->
56
+ <button class="boton boton-lab" id="btn-lab" type="button" aria-label="Conectar con el analizador" data-tooltip="Analizador de laboratorio" aria-haspopup="dialog" aria-expanded="false">
57
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M200-120q-51 0-72.5-45.5T138-250l222-270v-240h-40q-17 0-28.5-11.5T280-800q0-17 11.5-28.5T320-840h320q17 0 28.5 11.5T680-800q0 17-11.5 28.5T640-760h-40v240l222 270q32 39 10.5 84.5T760-120H200Zm0-80h560L520-492v-268h-80v268L200-200Zm280-280Z"/></svg>
58
+ </button>
59
  <button class="boton boton-tema" id="btn-tema" type="button" aria-label="Cambiar tema" data-tooltip="Cambiar tema">
60
  <svg class="icono-sol" viewBox="0 -960 960 960" aria-hidden="true"><path d="M565-395q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Zm-226.5 56.5Q280-397 280-480t58.5-141.5Q397-680 480-680t141.5 58.5Q680-563 680-480t-58.5 141.5Q563-280 480-280t-141.5-58.5ZM200-440H40v-80h160v80Zm720 0H760v-80h160v80ZM440-760v-160h80v160h-80Zm0 720v-160h80v160h-80ZM256-650l-101-97 57-59 96 100-52 56Zm492 496-97-101 53-55 101 97-57 59Zm-98-550 97-101 59 57-100 96-56-52ZM154-212l101-97 55 53-97 101-59-57Zm326-268Z"/></svg>
61
  <svg class="icono-luna" viewBox="0 -960 960 960" aria-hidden="true"><path d="M480-120q-150 0-255-105T120-480q0-150 105-255t255-105q14 0 27.5 1t26.5 3q-41 29-65.5 75.5T444-660q0 90 63 153t153 63q55 0 101-24.5t75-65.5q2 13 3 26.5t1 27.5q0 150-105 255T480-120Zm0-80q88 0 158-48.5T740-375q-20 5-40 8t-40 3q-123 0-209.5-86.5T364-660q0-20 3-40t8-40q-78 32-126.5 102T200-480q0 116 82 198t198 82Zm-10-270Z"/></svg>
 
111
  <option value="Hembra">Hembra</option>
112
  </select>
113
  </div>
114
+ <!-- Controles del analizador. En móvil viven aquí; en escritorio este mismo nodo se
115
+ traslada al modal #modal-lab (lab-import.ts). Se MUEVE, no se duplica: los IDs
116
+ son únicos y el estado del sondeo es uno solo. -->
117
+ <div id="lab-anclaje-mob"></div>
118
+ <div id="lab-controles">
119
+ <div class="fila-paciente fila-importar-lab">
120
+ <label for="lab-muestra-id">ID de muestra</label>
121
+ <input id="lab-muestra-id" type="text" placeholder="escanea o teclea el ID" autocomplete="off">
122
+ </div>
123
+ <button class="btn-importar-lab" id="btn-importar-lab" type="button">Importar del analizador</button>
124
+ <button class="btn-importar-lab btn-pendientes-lab" id="btn-pendientes-lab" type="button">Ver resultados recibidos</button>
125
+ <ul class="lab-pendientes" id="lab-pendientes" hidden></ul>
126
  </div>
 
 
 
127
  <input type="file" id="pdf-input-paciente" class="pdf-input" accept=".pdf" hidden>
128
  <button class="btn-importar-pdf btn-adjuntar-mob" type="button" data-panel="paciente" aria-label="Adjuntar PDF de exámenes">
129
  <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350V-700q0-42-29-71t-71-29q-42 0-71 29t-29 71v370q0 71 49.5 120.5T470-160q71 0 120.5-49.5T640-330v-390h80v390Z"/></svg>
 
1122
  <div id="papers-paginacion" class="papers-paginacion"></div>
1123
  </div>
1124
 
1125
+ <!-- MODAL DEL ANALIZADOR (sólo escritorio; en móvil los controles van en el panel de paciente) -->
1126
+ <div id="modal-lab-overlay" class="modal-overlay" aria-hidden="true"></div>
1127
+ <div id="modal-lab" class="modal-lab" role="dialog" aria-modal="true" aria-labelledby="modal-lab-titulo" hidden>
1128
+ <button id="modal-lab-cerrar" class="modal-cerrar" type="button" aria-label="Cerrar" data-tooltip="Cerrar">
1129
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="m336-280-56-56 144-144-144-143 56-56 144 144 143-144 56 56-144 143 144 144-56 56-143-144-144 144Z"/></svg>
1130
+ </button>
1131
+ <h2 id="modal-lab-titulo" class="modal-titulo">Analizador de laboratorio</h2>
1132
+ <p class="modal-lab-ayuda">Escanea o teclea el ID de muestra: en cuanto el equipo envíe ese resultado, sus valores se cargan en el formulario. También puedes elegir de los ya recibidos.</p>
1133
+ <!-- Aquí se traslada #lab-controles en escritorio. -->
1134
+ <div id="modal-lab-cuerpo"></div>
1135
+ </div>
1136
+
1137
  <!-- MODAL DE AUTENTICACIÓN -->
1138
  <div id="modal-auth-overlay" class="modal-overlay" aria-hidden="true"></div>
1139
  <div id="modal-auth" class="modal-auth" role="dialog" aria-modal="true" aria-labelledby="modal-auth-titulo" hidden>