Jose Salazar Claude Opus 5 commited on
Commit
e4a79ec
·
1 Parent(s): 7dba92c

Estado vacío de los paneles de exámenes en escritorio

Browse files

`panel-vacio.ts` sustituye el formulario por una zona de adjuntar (arrastrar /
explorar / «Insertar resultados manualmente») mientras el `<section>` lleva la
clase `.sin-datos`. Sólo en escritorio: el CSS que lo enciende vive dentro de
`@media (min-width: 1101px)`.

Depende sólo de `dom.ts` (`esGridEscritorio`, extraído ahí para que lo compartan
`ui.ts`, `lab-import.ts` y este módulo). Lo importa `form-inject.ts`, base común
de los dos importadores, que llama a `revelarPanelDeCampo()` por cada valor
inyectado: un panel que recibe datos sale del estado vacío solo. Colgarlo de
`ui.ts` —que toca el DOM al cargarse— rompería sus pruebas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

css/styles.css CHANGED
@@ -450,6 +450,62 @@ main {
450
  scrollbar-color: var(--border-2) transparent;
451
  }
452
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  /* ENCABEZADOS DE COLUMNA */
454
  .cabecera-columnas {
455
  display: flex;
@@ -1966,6 +2022,38 @@ main {
1966
 
1967
  /* SOLO DESKTOP */
1968
  @media (min-width: 1101px) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1969
  .cabecera-columnas--mobile-only {
1970
  display: none;
1971
  }
 
450
  scrollbar-color: var(--border-2) transparent;
451
  }
452
 
453
+ /* ESTADO VACÍO DE LOS PANELES DE EXÁMENES (sólo escritorio)
454
+ En móvil los paneles son pestañas a pantalla completa y el formulario va directo: la zona de
455
+ arrastre no aporta (no se arrastra con el dedo) y metería un paso de más. Por eso el bloque
456
+ nace oculto y sólo la media query de escritorio lo enciende. */
457
+ .panel-vacio {
458
+ display: none;
459
+ }
460
+
461
+ .panel-vacio svg {
462
+ width: 34px;
463
+ height: 34px;
464
+ fill: var(--text-4);
465
+ }
466
+
467
+ .panel-vacio-texto {
468
+ margin: 0;
469
+ font-size: var(--fs-sm);
470
+ line-height: 1.6;
471
+ color: var(--text-3);
472
+ text-align: center;
473
+ text-wrap: balance;
474
+ }
475
+
476
+ .panel-vacio-explorar {
477
+ padding: 0;
478
+ background: none;
479
+ border: none;
480
+ font: inherit;
481
+ color: var(--accent);
482
+ text-decoration: underline;
483
+ text-underline-offset: 2px;
484
+ cursor: pointer;
485
+ }
486
+
487
+ .panel-vacio-explorar:hover {
488
+ color: var(--accent-hover);
489
+ }
490
+
491
+ .panel-vacio-manual {
492
+ padding: var(--space-2) var(--space-4);
493
+ background: transparent;
494
+ color: var(--text-2);
495
+ border: 1px solid var(--border-2);
496
+ border-radius: var(--radius-md);
497
+ font: inherit;
498
+ font-size: var(--fs-sm);
499
+ cursor: pointer;
500
+ transition: background var(--dur-fast), color var(--dur-fast), border-color var(--dur-fast);
501
+ }
502
+
503
+ .panel-vacio-manual:hover {
504
+ background: var(--surface-2);
505
+ color: var(--text-1);
506
+ border-color: var(--accent);
507
+ }
508
+
509
  /* ENCABEZADOS DE COLUMNA */
510
  .cabecera-columnas {
511
  display: flex;
 
2022
 
2023
  /* SOLO DESKTOP */
2024
  @media (min-width: 1101px) {
2025
+ /* Mientras el panel no tenga datos, su cuerpo es la zona de adjuntar: se oculta TODO lo
2026
+ demás (cabecera de columnas y grupos de campos) en vez de enumerarlo, para que añadir un
2027
+ grupo nuevo al formulario no obligue a tocar esta regla. */
2028
+ .subpanel.sin-datos .panel-cuerpo > :not(.panel-vacio),
2029
+ .subpanel.sin-datos .subpanel-cuerpo > :not(.panel-vacio) {
2030
+ display: none;
2031
+ }
2032
+
2033
+ .subpanel.sin-datos .panel-vacio {
2034
+ display: flex;
2035
+ flex-direction: column;
2036
+ align-items: center;
2037
+ justify-content: center;
2038
+ gap: var(--space-3);
2039
+ /* Los dos paneles altos (hema/bioquim) centran el bloque en el alto disponible; los
2040
+ colapsables se ajustan al contenido, que es lo que anima su altura. */
2041
+ height: 100%;
2042
+ min-height: 8rem;
2043
+ padding: var(--space-5) var(--space-4);
2044
+ }
2045
+
2046
+ /* Realce al arrastrar un fichero por encima. Va en el <section> porque el drop se escucha
2047
+ ahí: también funciona sobre un panel que ya muestra el formulario. */
2048
+ .subpanel.arrastrando {
2049
+ outline: 2px dashed var(--accent);
2050
+ outline-offset: -4px;
2051
+ }
2052
+
2053
+ .subpanel.arrastrando .panel-vacio svg {
2054
+ fill: var(--accent);
2055
+ }
2056
+
2057
  .cabecera-columnas--mobile-only {
2058
  display: none;
2059
  }
frontend/src/dom.ts CHANGED
@@ -19,3 +19,10 @@ export function qs<T extends Element = Element>(sel: string, root: ParentNode =
19
  export function qsa<T extends Element = Element>(sel: string, root: ParentNode = document): T[] {
20
  return Array.from(root.querySelectorAll<T>(sel));
21
  }
 
 
 
 
 
 
 
 
19
  export function qsa<T extends Element = Element>(sel: string, root: ParentNode = document): T[] {
20
  return Array.from(root.querySelectorAll<T>(sel));
21
  }
22
+
23
+ // Punto de corte del grid de escritorio. Vive aquí, en el módulo hoja, porque lo consultan
24
+ // varios módulos que no deben depender unos de otros, y debe coincidir con la media query
25
+ // `min-width: 1101px` de css/styles.css.
26
+ export function esGridEscritorio(): boolean {
27
+ return window.innerWidth > 1100;
28
+ }
frontend/src/form-inject.ts CHANGED
@@ -2,6 +2,8 @@
2
  // y el de analizadores (lab-import.ts). La clave de unión es el atributo `name` del input
3
  // (== clave canónica de valores_referencia.json). Fuente única para no duplicar la lógica.
4
 
 
 
5
  export type ValoresInyectables = Record<string, number | string>;
6
 
7
  export interface PacienteInyectable {
@@ -32,6 +34,8 @@ export function aplicarValoresAFormulario(
32
  for (const [campo, value] of Object.entries(resultados)) {
33
  const el = document.querySelector(`[name="${campo}"]`) as HTMLInputElement | HTMLSelectElement | null;
34
  if (!el) continue;
 
 
35
  const valorCadena = String(value);
36
  if (el.tagName === 'SELECT') {
37
  const select = el as HTMLSelectElement;
 
2
  // y el de analizadores (lab-import.ts). La clave de unión es el atributo `name` del input
3
  // (== clave canónica de valores_referencia.json). Fuente única para no duplicar la lógica.
4
 
5
+ import { revelarPanelDeCampo } from './panel-vacio.js';
6
+
7
  export type ValoresInyectables = Record<string, number | string>;
8
 
9
  export interface PacienteInyectable {
 
34
  for (const [campo, value] of Object.entries(resultados)) {
35
  const el = document.querySelector(`[name="${campo}"]`) as HTMLInputElement | HTMLSelectElement | null;
36
  if (!el) continue;
37
+ // Un valor importado a un panel en estado vacío quedaría escondido tras la zona de arrastre.
38
+ revelarPanelDeCampo(el);
39
  const valorCadena = String(value);
40
  if (el.tagName === 'SELECT') {
41
  const select = el as HTMLSelectElement;
frontend/src/lab-import.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
  type PacienteInyectable,
14
  } from './form-inject.js';
15
  import { manejadorAsync } from './async.js';
 
16
 
17
  interface ValorAnalitoResp {
18
  clave: string;
@@ -191,7 +192,7 @@ function inicializarModalLab(detenerBusqueda: () => void): void {
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 => {
 
13
  type PacienteInyectable,
14
  } from './form-inject.js';
15
  import { manejadorAsync } from './async.js';
16
+ import { esGridEscritorio } from './dom.js';
17
 
18
  interface ValorAnalitoResp {
19
  clave: string;
 
192
  const anclaje = document.getElementById('lab-anclaje-mob');
193
  if (!btn || !modal || !overlay || !cuerpo || !controles || !anclaje) return;
194
 
195
+ const esEscritorio = esGridEscritorio;
196
 
197
  // Idempotente: si ya está donde toca no toca el DOM (se llama en cada `resize`).
198
  const ubicar = (): void => {
frontend/src/panel-vacio.ts ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Estado vacío de los paneles de exámenes (sólo escritorio).
2
+ //
3
+ // Cada panel arranca con `.sin-datos`: en vez del formulario muestra una zona para soltar el
4
+ // PDF y un botón que devuelve al modo de captura manual. La entrada manual no desaparece, sólo
5
+ // deja de ser lo primero que se ve: en la mayoría de los casos el veterinario llega con un PDF
6
+ // del laboratorio, y teclear 90 analitos a mano es la excepción.
7
+ //
8
+ // El estado vive en una clase del <section>, no en JS, para que el CSS decida cuándo aplica
9
+ // (el móvil lo ignora entero: allí los paneles son pestañas y el formulario va directo).
10
+
11
+ // Sólo depende de `dom.ts` a propósito: `form-inject.ts` importa este módulo, y form-inject es
12
+ // la base compartida de los dos importadores. Colgar de aquí a `ui.ts` —que toca el DOM al
13
+ // cargarse— arrastraría esa dependencia hasta ellos y hasta sus tests.
14
+ import { esGridEscritorio } from './dom.js';
15
+
16
+ // Saca un panel del estado vacío. Idempotente: se llama tanto desde el botón como desde cada
17
+ // valor inyectado por los importadores, que pueden ser decenas en una sola importación.
18
+ export function revelarPanel(panel: HTMLElement | null): void {
19
+ if (!panel || !panel.classList.contains('sin-datos')) return;
20
+ panel.classList.remove('sin-datos');
21
+ reajustarAltura(panel);
22
+ }
23
+
24
+ // Los cuatro paneles colapsables llevan la altura de su animación fijada en píxeles inline (ver
25
+ // ui.ts); sin reajustarla al cambiar el contenido, el formulario recién mostrado sale recortado.
26
+ // No toca los colapsados —su altura debe seguir siendo 0— ni el móvil, donde no hay animación.
27
+ function reajustarAltura(panel: HTMLElement): void {
28
+ if (!esGridEscritorio() || panel.classList.contains('collapsed')) return;
29
+ const animEl = panel.querySelector<HTMLElement>('.subpanel-anim');
30
+ if (!animEl || !animEl.style.height) return;
31
+ animEl.style.height = `${animEl.scrollHeight}px`;
32
+ }
33
+
34
+ // Revela el panel al que pertenece un campo. La usan los importadores (PDF y analizador) para
35
+ // que un valor inyectado nunca quede escondido detrás del estado vacío.
36
+ export function revelarPanelDeCampo(el: Element): void {
37
+ revelarPanel(el.closest<HTMLElement>('.subpanel'));
38
+ }
39
+
40
+ export function inicializarPanelesVacios(): void {
41
+ document.querySelectorAll<HTMLElement>('.panel-vacio-manual').forEach((btn) => {
42
+ btn.addEventListener('click', () => {
43
+ const panel = btn.closest<HTMLElement>('.subpanel');
44
+ revelarPanel(panel);
45
+ // El foco salta al primer campo: quien pulsa "manual" viene a teclear.
46
+ panel?.querySelector<HTMLInputElement>('.fila-campo input, .fila-campo select')?.focus();
47
+ });
48
+ });
49
+
50
+ document.querySelectorAll<HTMLElement>('.panel-vacio-explorar').forEach((btn) => {
51
+ btn.addEventListener('click', () => {
52
+ (document.getElementById(`pdf-input-${btn.dataset.panel}`) as HTMLInputElement | null)?.click();
53
+ });
54
+ });
55
+ }
frontend/src/ui.ts CHANGED
@@ -1,7 +1,7 @@
1
  // Navegación, gestos, paneles colapsables, sincronización móvil, imágenes y cámara.
2
  // Puerto TS de js/ui.js — comportamiento idéntico; sólo tipado + helpers de DOM.
3
 
4
- import { elId, qs, qsa } from './dom.js';
5
 
6
  // Navegación-Pestañas
7
 
@@ -126,7 +126,8 @@ const mainEl = qs<HTMLElement>('main')!;
126
  let filaColapsada = '';
127
  let filaExpandida = '';
128
 
129
- const esGridEscritorio = (): boolean => window.innerWidth > 1100;
 
130
 
131
  function inicializarFilasGrid(): void {
132
  if (!esGridEscritorio()) return;
 
1
  // Navegación, gestos, paneles colapsables, sincronización móvil, imágenes y cámara.
2
  // Puerto TS de js/ui.js — comportamiento idéntico; sólo tipado + helpers de DOM.
3
 
4
+ import { elId, esGridEscritorio, qs, qsa } from './dom.js';
5
 
6
  // Navegación-Pestañas
7
 
 
126
  let filaColapsada = '';
127
  let filaExpandida = '';
128
 
129
+ // Reexportado desde dom.ts: el mismo corte lo consultan panel-vacio.ts y lab-import.ts, que no
130
+ // deben depender de este módulo (toca el DOM al cargarse).
131
 
132
  function inicializarFilasGrid(): void {
133
  if (!esGridEscritorio()) return;
index.html CHANGED
@@ -137,7 +137,7 @@
137
  </section>
138
 
139
  <!-- HEMATOLOGÍA -->
140
- <section class="panel subpanel" id="panel-hema">
141
  <h2 class="panel-titulo">Hematología</h2>
142
  <div class="panel-cabecera">
143
  Hematología
@@ -150,6 +150,11 @@
150
  </button>
151
  </div>
152
  <div class="subpanel-anim"><div class="panel-cuerpo" id="cuerpo-hema">
 
 
 
 
 
153
  <div class="cabecera-columnas" aria-hidden="true">
154
  <span></span>
155
  <span class="cabecera-col-label">Valor</span>
@@ -299,7 +304,7 @@
299
  </section>
300
 
301
  <!-- PERFIL ENDOCRINO -->
302
- <section class="panel subpanel" id="panel-endo">
303
  <h2 class="panel-titulo">Perfil Endocrino</h2>
304
  <div class="panel-cabecera">
305
  Perfil Endocrino
@@ -316,6 +321,11 @@
316
  </button>
317
  </div>
318
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-endo">
 
 
 
 
 
319
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
320
  <span></span>
321
  <button class="btn-limpiar-panel" type="button" data-panel="endo" aria-label="Limpiar campos Perfil Endocrino" data-tooltip="Limpiar formulario">
@@ -384,7 +394,7 @@
384
  </section>
385
 
386
  <!-- BIOQUÍMICA -->
387
- <section class="panel subpanel" id="panel-bioquim">
388
  <h2 class="panel-titulo">Bioquímica sanguínea</h2>
389
  <div class="panel-cabecera">
390
  Bioquímica sanguínea
@@ -397,6 +407,11 @@
397
  </button>
398
  </div>
399
  <div class="subpanel-anim"><div class="panel-cuerpo" id="cuerpo-bioquim">
 
 
 
 
 
400
  <div class="cabecera-columnas" aria-hidden="true">
401
  <span></span>
402
  <span class="cabecera-col-label">Valor</span>
@@ -609,7 +624,7 @@
609
  </section>
610
 
611
  <!-- URIANÁLISIS -->
612
- <section class="panel subpanel" id="panel-uri">
613
  <h2 class="panel-titulo">Urianálisis</h2>
614
  <div class="panel-cabecera">
615
  Urianálisis
@@ -626,6 +641,11 @@
626
  </button>
627
  </div>
628
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-uri">
 
 
 
 
 
629
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
630
  <span></span>
631
  <button class="btn-limpiar-panel" type="button" data-panel="uri" aria-label="Limpiar campos Urianálisis" data-tooltip="Limpiar formulario">
@@ -753,7 +773,7 @@
753
  </section>
754
 
755
  <!-- FACTORES DE COAGULACIÓN -->
756
- <section class="panel subpanel" id="panel-coag">
757
  <h2 class="panel-titulo">Factores de Coagulación</h2>
758
  <div class="panel-cabecera">
759
  Factores de Coagulación
@@ -770,6 +790,11 @@
770
  </button>
771
  </div>
772
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-coag">
 
 
 
 
 
773
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
774
  <span></span>
775
  <button class="btn-limpiar-panel" type="button" data-panel="coag" aria-label="Limpiar campos Coagulación" data-tooltip="Limpiar formulario">
@@ -823,7 +848,7 @@
823
  </section>
824
 
825
  <!-- GASOMETRÍA -->
826
- <section class="panel subpanel" id="panel-gas">
827
  <h2 class="panel-titulo">Gasometría</h2>
828
  <div class="panel-cabecera">
829
  Gasometría
@@ -840,6 +865,11 @@
840
  </button>
841
  </div>
842
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-gas">
 
 
 
 
 
843
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
844
  <span></span>
845
  <button class="btn-limpiar-panel" type="button" data-panel="gas" aria-label="Limpiar campos Gasometría" data-tooltip="Limpiar formulario">
 
137
  </section>
138
 
139
  <!-- HEMATOLOGÍA -->
140
+ <section class="panel subpanel sin-datos" id="panel-hema">
141
  <h2 class="panel-titulo">Hematología</h2>
142
  <div class="panel-cabecera">
143
  Hematología
 
150
  </button>
151
  </div>
152
  <div class="subpanel-anim"><div class="panel-cuerpo" id="cuerpo-hema">
153
+ <div class="panel-vacio">
154
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M440-320v-326L336-542l-56-58 200-200 200 200-56 58-104-104v326h-80ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
155
+ <p class="panel-vacio-texto">Arrastra aquí el PDF de hematología<br>o <button class="panel-vacio-explorar" type="button" data-panel="hema">explora tus archivos</button></p>
156
+ <button class="panel-vacio-manual" type="button" data-panel="hema">Insertar resultados manualmente</button>
157
+ </div>
158
  <div class="cabecera-columnas" aria-hidden="true">
159
  <span></span>
160
  <span class="cabecera-col-label">Valor</span>
 
304
  </section>
305
 
306
  <!-- PERFIL ENDOCRINO -->
307
+ <section class="panel subpanel sin-datos" id="panel-endo">
308
  <h2 class="panel-titulo">Perfil Endocrino</h2>
309
  <div class="panel-cabecera">
310
  Perfil Endocrino
 
321
  </button>
322
  </div>
323
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-endo">
324
+ <div class="panel-vacio">
325
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M440-320v-326L336-542l-56-58 200-200 200 200-56 58-104-104v326h-80ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
326
+ <p class="panel-vacio-texto">Arrastra aquí el PDF de perfil endocrino<br>o <button class="panel-vacio-explorar" type="button" data-panel="endo">explora tus archivos</button></p>
327
+ <button class="panel-vacio-manual" type="button" data-panel="endo">Insertar resultados manualmente</button>
328
+ </div>
329
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
330
  <span></span>
331
  <button class="btn-limpiar-panel" type="button" data-panel="endo" aria-label="Limpiar campos Perfil Endocrino" data-tooltip="Limpiar formulario">
 
394
  </section>
395
 
396
  <!-- BIOQUÍMICA -->
397
+ <section class="panel subpanel sin-datos" id="panel-bioquim">
398
  <h2 class="panel-titulo">Bioquímica sanguínea</h2>
399
  <div class="panel-cabecera">
400
  Bioquímica sanguínea
 
407
  </button>
408
  </div>
409
  <div class="subpanel-anim"><div class="panel-cuerpo" id="cuerpo-bioquim">
410
+ <div class="panel-vacio">
411
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M440-320v-326L336-542l-56-58 200-200 200 200-56 58-104-104v326h-80ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
412
+ <p class="panel-vacio-texto">Arrastra aquí el PDF de bioquímica sanguínea<br>o <button class="panel-vacio-explorar" type="button" data-panel="bioquim">explora tus archivos</button></p>
413
+ <button class="panel-vacio-manual" type="button" data-panel="bioquim">Insertar resultados manualmente</button>
414
+ </div>
415
  <div class="cabecera-columnas" aria-hidden="true">
416
  <span></span>
417
  <span class="cabecera-col-label">Valor</span>
 
624
  </section>
625
 
626
  <!-- URIANÁLISIS -->
627
+ <section class="panel subpanel sin-datos" id="panel-uri">
628
  <h2 class="panel-titulo">Urianálisis</h2>
629
  <div class="panel-cabecera">
630
  Urianálisis
 
641
  </button>
642
  </div>
643
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-uri">
644
+ <div class="panel-vacio">
645
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M440-320v-326L336-542l-56-58 200-200 200 200-56 58-104-104v326h-80ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
646
+ <p class="panel-vacio-texto">Arrastra aquí el PDF de urianálisis<br>o <button class="panel-vacio-explorar" type="button" data-panel="uri">explora tus archivos</button></p>
647
+ <button class="panel-vacio-manual" type="button" data-panel="uri">Insertar resultados manualmente</button>
648
+ </div>
649
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
650
  <span></span>
651
  <button class="btn-limpiar-panel" type="button" data-panel="uri" aria-label="Limpiar campos Urianálisis" data-tooltip="Limpiar formulario">
 
773
  </section>
774
 
775
  <!-- FACTORES DE COAGULACIÓN -->
776
+ <section class="panel subpanel sin-datos" id="panel-coag">
777
  <h2 class="panel-titulo">Factores de Coagulación</h2>
778
  <div class="panel-cabecera">
779
  Factores de Coagulación
 
790
  </button>
791
  </div>
792
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-coag">
793
+ <div class="panel-vacio">
794
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M440-320v-326L336-542l-56-58 200-200 200 200-56 58-104-104v326h-80ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
795
+ <p class="panel-vacio-texto">Arrastra aquí el PDF de factores de coagulación<br>o <button class="panel-vacio-explorar" type="button" data-panel="coag">explora tus archivos</button></p>
796
+ <button class="panel-vacio-manual" type="button" data-panel="coag">Insertar resultados manualmente</button>
797
+ </div>
798
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
799
  <span></span>
800
  <button class="btn-limpiar-panel" type="button" data-panel="coag" aria-label="Limpiar campos Coagulación" data-tooltip="Limpiar formulario">
 
848
  </section>
849
 
850
  <!-- GASOMETRÍA -->
851
+ <section class="panel subpanel sin-datos" id="panel-gas">
852
  <h2 class="panel-titulo">Gasometría</h2>
853
  <div class="panel-cabecera">
854
  Gasometría
 
865
  </button>
866
  </div>
867
  <div class="subpanel-anim"><div class="subpanel-cuerpo" id="cuerpo-gas">
868
+ <div class="panel-vacio">
869
+ <svg viewBox="0 -960 960 960" aria-hidden="true"><path d="M440-320v-326L336-542l-56-58 200-200 200 200-56 58-104-104v326h-80ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
870
+ <p class="panel-vacio-texto">Arrastra aquí el PDF de gasometría<br>o <button class="panel-vacio-explorar" type="button" data-panel="gas">explora tus archivos</button></p>
871
+ <button class="panel-vacio-manual" type="button" data-panel="gas">Insertar resultados manualmente</button>
872
+ </div>
873
  <div class="cabecera-columnas cabecera-columnas--mobile-only" aria-hidden="true">
874
  <span></span>
875
  <button class="btn-limpiar-panel" type="button" data-panel="gas" aria-label="Limpiar campos Gasometría" data-tooltip="Limpiar formulario">