# Sesión, retorno al contexto y borradores Solución compartida para toda la app (frontend). Los proyectos y comentarios también verifican la versión en el backend (migración `029_optimistic_versions.sql`). ## Piezas | Archivo | Qué hace | |---|---| | `utils/jwt.js` | Lee `exp` y `sub` del JWT (sin verificar firma; eso lo hace el backend). | | `context/auth-context.jsx` | Vigila el vencimiento: temporizador al instante exacto, chequeo cada 30 s y al volver a la pestaña/ventana (`visibilitychange`, `focus`, `pageshow`). El navegador congela temporizadores en segundo plano, por eso el chequeo al volver es el que manda. | | `services/api.js` | Nunca envía una petición con token vencido: corta la sesión antes. Un `401` con token también corta. | | `utils/session.js` | Al vencer guarda `{ userId, ruta completa }` (`plc_return_ctx`, 24 h). Tras el login solo se usa si entra **el mismo usuario**; otro usuario la descarta. | | `utils/drafts.js` + `hooks/useDraftForm.js` | Borradores por formulario (ver abajo). | | `components/NavigationGuard.jsx` | Aviso al salir con cambios (`useBlocker`), advertencia nativa en F5/cierre, `DraftMark`, `useGuardedClose`. | | `components/DraftNotice.jsx` | Aviso dentro del formulario: cambios recuperados / conflicto con el servidor. | | `app/App.jsx` | Router de datos (`createBrowserRouter`, mismas rutas): requisito de `useBlocker`. | Pestañas y vistas que viven en la URL (para volver exactamente ahí): proyecto `?tab=`, actividad abierta `?activity=&activityTab=`, Organización `?tab=`, Equipos `?tab=&device=&deviceTab=`. ## Comportamiento - **Vencimiento:** se cierra la sesión al instante (aunque no haya peticiones) y aparece el login con el aviso "Tu sesión venció…". Al volver a entrar, el mismo usuario regresa a la misma ruta, pestaña y popup, con la edición reabierta y sus cambios. Salir con "Cerrar sesión" no guarda ruta de retorno. - **Borradores:** clave `plc_draft:v1:::` en `localStorage`, vigencia 7 días. Solo se guardan los campos permitidos de cada formulario (`draftAllowedFields`); el texto libre de notas, comentarios, descripciones y observaciones no se conserva. Sobreviven a cambiar de pestaña/menú, F5 y vencimiento. Se borran al guardar, descartar o cerrar sesión voluntariamente. Solo se restauran si el registro sigue en lo que el backend devuelve para ese usuario (si se borró o perdió acceso, no se abre nada). - **Secretos:** nunca se guardan campos cuyo nombre parezca contraseña/token/PIN, archivos ni campos fuera de la lista permitida. Al leer borradores antiguos se eliminan los campos excluidos. - **Marcas:** campo modificado en rojo con el texto "· modificado"; pestaña, sección plegable, botón "Nuevo …", tarjeta (Notas) y menú lateral con punto rojo + texto para lector de pantalla. - **Salir con cambios** (menú, pestaña, cerrar popup, plegar sección): diálogo *Seguir editando* / *Dejar pendiente* / *Descartar cambios*. Con "Dejar pendiente", al volver se reabre la edición con los valores recuperados. F5 o cerrar la pestaña: advertencia nativa del navegador. - **Integridad:** los borradores no se guardan automáticamente en el backend; Guardar envía los campos del formulario con la versión leída. Si el registro cambió, se conserva el borrador y se pide recargar para revisar la versión actual. En Info de proyecto/actividad, Equipos, SMTP y Empresa además se consulta el servidor justo antes de guardar. - **Formulario abierto desde otro** (ej. "Nuevo paciente" desde una cita): guardar, cancelar o cerrar vuelve al formulario de origen con su borrador; nunca se cierra el de origen dejando su borrador huérfano (eso hacía que la siguiente cita nueva apareciera con el paciente anterior). Descartar un registro **nuevo** cierra el popup; uno existente vuelve a lectura. ## Formularios sin recuperación (a propósito) | Formulario | Motivo | |---|---| | Contraseñas (todo el módulo, incluido PIN y grupos) | Los datos están protegidos por PIN y cifrados; guardarlos en el navegador saltaría esa protección. | | Cambiar contraseña (menú del usuario) | Solo contiene secretos. | | Cambio de correo (Mi cuenta) | Solo se guarda el correo nuevo; la clave actual nunca se persiste. | | Login, registro, olvidé/restablecer clave, aceptar invitación, restablecer PIN | Pantallas públicas: no hay usuario autenticado para aislar el borrador y contienen claves/tokens. | | Documentos (subir archivo), logo y nombre de Empresa | Archivos y texto libre no se guardan en el navegador. | | Equipos → Agentes (token de instalación) | Token secreto; generar uno es una acción de un paso. | | Mover actividad, agregar dependencia, compartir nota, compartir Equipos | Acciones de una sola elección, sin captura de datos: se repiten en un clic. | | Clave y texto libre en "Nuevo usuario"; clave SMTP | Solo se recuperan rol/departamento; las claves y el texto libre se vuelven a escribir. | | Apariencia, filtros, selector de tarjetas del Panel | Se aplican al instante; no queda nada pendiente. | ## Límites conocidos - Los borradores son de **este navegador**: no se sincronizan entre equipos. - En un equipo compartido, los borradores de un usuario quedan en el navegador hasta guardarlos, descartarlos, cerrar sesión voluntariamente o que venzan (7 días). Tras vencimiento de sesión se conservan para permitir el retorno al contexto. - Todos los formularios de edición de entidades, empresa y SMTP envían `editVersion`; el backend rechaza con `409` una versión obsoleta. El borrador se conserva y el aviso pide revisar la versión actual. Miembros no edita una entidad mediante formulario: sus acciones aplican cambios puntuales de acceso. - Los popups son modales: con uno abierto no se puede usar el menú lateral; se sale cerrándolo (pasa por el mismo aviso) o con atrás del navegador.