# PROMPT — Aceptar PDF como identificación · lado FRONT (`CL032FRONT`)

> **Ejecutar en:** `D:\FOURSIDES\CL001ASTU\PRE-PRODUCCION\VILLASTUR\CL032RESERVACIONESENLINEA\CL032FRONT`
> **⚠️ REQUISITO PREVIO:** `PROMPT_IMPLEMENTACION_PDF_TLALPAN.md` y `PROMPT_IMPLEMENTACION_PDF_PORTAL.md` desplegados. Sin el back, el front ofrecería subir un PDF que el servidor rechaza con 422.
> **Antecedente obligado:** este componente **acaba de recibir** la optimización de imágenes (`src/utils/imagen.js`). Léelo completo antes de tocar nada.

Actúa como desarrollador senior de este proyecto. El negocio pidió que el socio pueda adjuntar la identificación **también en PDF**. Este es el lado más delicado de los tres, por la razón que explica la sección siguiente.

---

## El problema de fondo: un PDF no se puede optimizar aquí

Toda la estrategia de este flujo se apoya en `optimizarImagen()`: decodifica, reescala a 1600 px, re-codifica a JPEG 0.85 y con eso consigue tres cosas —peso de ~400 KB, **eliminación del EXIF con el GPS**, y que la descarga hacia Tlalpan nunca se acerque a su timeout—.

**Con un PDF no se puede hacer nada de eso.** `createImageBitmap` no decodifica PDF, y comprimirlo exigiría una librería de PDF en el navegador, que está prohibido por las restricciones de este proyecto.

Consecuencia directa, y hay que aceptarla explícitamente: **un PDF viaja tal como el socio lo eligió, hasta 12 MB.** Vuelve a existir para esa ruta el escenario que la compresión había cerrado:

- Subida lenta sobre 4G, sin indicador de progreso real
- Descarga hacia Tlalpan cerca del timeout de 120 s, con el *circuit breaker* de por medio (al quinto fallo detiene la descarga de **todos** los documentos 10 minutos)
- Almacenamiento duplicado en los dos servidores

**No intentes resolverlo con una librería.** Lo que sí debes hacer es no fingir que el PDF pasa por el mismo camino que la imagen, y avisar al socio cuando su archivo sea grande (ver punto 5).

---

## Estado actual — verifícalo, cambió hace poco

| Pieza | Ubicación aprox. | Situación frente al PDF |
|---|---|---|
| `accept="image/*"` | `PasoDatosInvitados.vue` ~426 | ❌ El selector **ni siquiera deja elegir** un PDF |
| Guardia de tipo `if (!archivo.type.startsWith('image/'))` | ~124 | ❌ **Rechaza el PDF explícitamente**, con el mensaje "El documento debe ser una imagen." |
| `optimizarImagen(archivo)` | ~134 → `src/utils/imagen.js:56` | ❌ `createImageBitmap` lanza excepción con PDF; hoy se traduce a "No pudimos procesar esta imagen" |
| Preview del dropzone | `<img :src="documentoPreview">` ~396-401 | ❌ Un object URL de PDF en un `<img>` = **icono roto** |
| Miniatura en la tabla | `<img :src="inv.documentoPreview">` ~471-472 | ❌ Mismo problema |
| Miniatura en las tarjetas (móvil) | ~492-493 | ❌ Mismo problema |
| Rehidratación al reanudar | `src/stores/invitados/solicitudStore.js` · `fetchPendiente()` ~85 | ❌ Hace `URL.createObjectURL(respuesta.data)` **sin mirar el tipo** del blob: al reanudar una captura con PDF, miniatura rota |
| Validación de tamaño (12 MB) | ~153 y `TAMANO_MAXIMO_BYTES` | ✅ Sirve igual para PDF |
| Punto único de entrada `setArchivo` | ~116 | ✅ **Consérvalo.** Toda la lógica nueva se ramifica ahí dentro |
| Carrera con `peticionArchivo` | ~119, ~142 | ✅ Aplica igual al PDF |
| Revocación de object URLs | `fijarPreview()` ~89, `onBeforeUnmount` ~200 | ✅ No depende del formato |

**Las cuatro miniaturas son el trabajo real de este prompt**, no la validación. Un PDF no se puede pintar en un `<img>`, y hay tres lugares en el componente más uno en el store.

---

## Lo que hay que construir

### 1. `accept` del input

Permitir PDF además de imágenes. **No agregues `capture`**: el input actual hace que iOS y Android abran su menú nativo (*Tomar foto / Fototeca / Explorar archivos*), y ese menú es justo el que da acceso al PDF guardado en el teléfono.

### 2. Ramificar `setArchivo` por tipo, sin romper el punto único

La estructura debe quedar: **detectar tipo → si es imagen, el camino actual intacto → si es PDF, saltar la optimización → validar tamaño (común) → fijar archivo y preview**.

- Rechaza lo que no sea imagen ni PDF, con un mensaje espejo del back nuevo.
- **El camino de la imagen no debe cambiar en nada.** Si al terminar una foto se comporta distinto que hoy, introdujiste una regresión.
- `procesandoImagen` no debe activarse para un PDF: no hay nada que procesar y el spinner sería mentira. Considera si el nombre de ese `ref` sigue siendo adecuado.

### 3. Previsualización del PDF — las cuatro ubicaciones

Un `<img>` no puede renderizar un PDF. Necesitas una representación alterna: un icono de documento con el **nombre del archivo** y su **peso**, para que el socio confirme que adjuntó lo que quería.

Aplica en:
1. El dropzone (~396)
2. La tabla de invitados (~471)
3. Las tarjetas de móvil (~492)
4. **`solicitudStore.fetchPendiente()`** — aquí el blob llega del servidor; hay que mirar `respuesta.data.type` para saber si es PDF. **Es el que se olvida**, porque solo se manifiesta al reanudar una captura, no al capturar por primera vez

Decide la forma concreta (un componente pequeño reutilizable evitaría repetir el marcado tres veces) y **justifica la decisión**. El proyecto usa `bootstrap-icons` y Font Awesome: ya hay iconos de PDF disponibles, no agregues assets.

> No embebas el PDF con `<iframe>`, `<embed>` ni `<object>` para hacer una miniatura: es pesado, se comporta distinto en cada navegador y en iOS suele no renderizar. Un icono con nombre y peso resuelve la necesidad real, que es **confirmar que se adjuntó el archivo correcto**.

### 4. Textos

Hoy todo dice "imagen": el `alt`, el spinner ("Procesando imagen…"), los mensajes de error, y la ayuda del paso *("Adjunte una imagen del documento de identificación…")*. Con PDF admitido, esos textos son incorrectos.

Ajústalos a "documento" o "archivo" **y mantenlos espejo de los del back** (`GuardarFormRequest::messages()`), que se actualizaron en el prompt hermano. Que el socio lea lo mismo venga el error de donde venga es una regla ya establecida en este componente (ver el bloque `REGLAS` y su comentario).

**La ayuda del paso debe seguir diciendo que la fotografía del invitado tiene que ser visible y legible.** Es regla de negocio (D3 p.10) y no cambia porque el contenedor sea un PDF.

### 5. Avisar cuando el PDF sea grande (recomendado, decide y justifica)

Como el PDF no se comprime, un archivo de 9 MB pasa la validación y produce una subida larguísima sin que el socio entienda por qué. Evalúa un aviso **no bloqueante** por encima de cierto umbral, sugiriendo adjuntar una foto en su lugar.

Si lo implementas, que sea un aviso y no un error: el archivo es válido. Si decides no hacerlo, di por qué.

### 6. `src/utils/imagen.js`

Su docblock afirma cosas que dejan de ser universales (*"el archivo se conserva en los dos servidores… 400 KB en vez de 3 MB"*) porque ahora hay una ruta que no pasa por ahí. Deja anotado que la optimización **aplica solo a imágenes** y que el PDF sigue otro camino.

No renombres el archivo ni sus funciones: siguen tratando imágenes y el renombre ensuciaría el diff sin aportar.

---

## Casos borde

| Caso | Comportamiento correcto |
|---|---|
| PDF > 12 MB | Rechazo antes de subir, con el mismo texto del back |
| PDF de 0 bytes | Rechazo. Un `File` vacío no llega a `finfo` del servidor con nada que detectar |
| Socio elige PDF y luego una foto (o al revés) | El preview y el archivo deben corresponder al **último** elegido. La guardia `peticionArchivo` ya existe: confirma que también cubre la rama del PDF |
| Archivo sin extensión pero `type` correcto | Se decide por `type`, no por el nombre |
| `type` vacío (algunos navegadores en drag & drop) | Decide qué hacer y **déjalo escrito**. Rechazar con mensaje claro es aceptable; fallar en silencio no |
| Reanudar captura mixta (un invitado con foto, otro con PDF) | Cada miniatura según su tipo. **Pruébalo: es el caso que rompe `fetchPendiente`** |

---

## Lo que NO debes hacer

- ❌ Agregar dependencias (`pdf.js`, `pdf-lib`, librerías de compresión). Cero dependencias nuevas
- ❌ Intentar comprimir, rasterizar o generar una miniatura real del PDF
- ❌ Romper o "simplificar" el camino de la imagen: la optimización, `imageOrientation: 'from-image'`, la guardia de carrera, la revocación de object URLs y el spinner **se quedan tal cual**
- ❌ Poner `capture` en el input ni restringir `accept` a una sola opción
- ❌ Subir el archivo sin validar el tamaño en el front
- ❌ Duplicar la lógica de tipo fuera de `setArchivo`

---

## Verificación

**En dispositivo real, no solo en la PC.**

1. **PDF desde el explorador (PC)**: se adjunta, se ve el icono con nombre y peso, se sube y el back lo acepta
2. **PDF por drag & drop**
3. **PDF desde iPhone** (menú *Explorar* → Archivos) y **desde Android**
4. **Foto desde iPhone y Android**: **sin ningún cambio de comportamiento**. Orientación correcta en vertical y horizontal, compresión aplicada. Es la prueba de no-regresión más importante
5. **Reanudar una captura con un invitado con foto y otro con PDF**: las dos miniaturas correctas
6. **PDF > 12 MB**: rechazo con mensaje, sin request
7. **Archivo no admitido** (`.docx`): rechazo con mensaje, sin request
8. **Elegir PDF e inmediatamente una foto**: gana la foto, sin preview cruzado
9. `npm run lint` y `npm run build` limpios

## Al terminar, reporta

1. Archivos creados y modificados, con las líneas
2. Cómo resolviste la previsualización del PDF y por qué esa forma
3. **Confirmación explícita del punto 4** (que la foto no cambió de comportamiento) y del punto 5 (la captura mixta al reanudar)
4. Qué decidiste sobre el aviso de PDF grande y sobre el `type` vacío
5. Casos que no pudiste probar por falta de dispositivo — **dilo, no lo omitas**
6. Cualquier decisión donde te apartaste de este prompt, con el motivo
