Bloqueo optimista para herramientas admin: evita sobrescrituras silenciosas
Aprende bloqueo optimista para herramientas admin con columnas de versión y comprobaciones `updated_at`, más patrones de UI simples para resolver conflictos sin sobrescrituras silenciosas.

El problema: sobrescrituras silenciosas cuando varias personas editan
Una “sobrescritura silenciosa” ocurre cuando dos personas abren el mismo registro, ambos hacen cambios y la última persona en hacer clic en Guardar gana. Los cambios de la primera persona desaparecen sin aviso y, a menudo, sin una forma fácil de recuperarlos.
En un panel administrativo con mucho movimiento, esto puede ocurrir todo el día sin que nadie lo note. La gente mantiene varias pestañas abiertas, salta entre tickets y vuelve a un formulario que ha estado allí 20 minutos. Cuando finalmente guardan, no están actualizando la versión más reciente del registro. Lo están sobrescribiendo.
Esto aparece más en herramientas internas que en apps públicas porque el trabajo es colaborativo y basado en registros. Los equipos internos editan los mismos clientes, pedidos, productos y solicitudes repetidamente, a menudo en ráfagas cortas. En las apps públicas lo más habitual es “un usuario edita sus propios datos”, mientras que en herramientas administrativas es “muchos usuarios editan cosas compartidas”.
El daño raramente es dramático en el momento, pero se acumula rápido:
- El precio de un producto vuelve a un valor antiguo justo después de una actualización promocional.
- La nota interna de un agente de soporte desaparece y el siguiente agente repite el mismo diagnóstico.
- El estado de un pedido retrocede (por ejemplo, de “Shipped” a “Packed”), disparando una acción incorrecta.
- El teléfono o la dirección de un cliente se reemplazan por información desactualizada.
Las sobrescrituras silenciosas duelen porque todos piensan que el sistema guardó correctamente. No hay un momento claro de “algo salió mal”, solo confusión después cuando los informes no cuadran o un compañero pregunta: “¿Quién cambió esto?”
Los conflictos como este son normales. Son una señal de que la herramienta es compartida y útil, no de que tu equipo esté haciendo algo mal. La meta no es impedir que dos personas editen. Es detectar cuando el registro cambió mientras alguien lo editaba y manejar ese momento de forma segura.
Si estás construyendo una herramienta interna en una plataforma no-code como AppMaster, vale la pena planear esto desde temprano. Las herramientas admin tienden a crecer rápido y, cuando los equipos dependen de ellas, perder datos “a veces” se convierte en una fuente constante de desconfianza.
Bloqueo optimista en lenguaje llano
Cuando dos personas abren el mismo registro y ambos hacen clic en Guardar, tienes concurrencia. Cada persona partió de una instantánea anterior, pero solo una puede ser “la más reciente” cuando se hacen los guardados.
Sin protección, gana el último guardado. Ahí es donde aparecen las sobrescrituras silenciosas: el segundo guardado reemplaza discretamente los cambios del primero.
El bloqueo optimista es una regla sencilla: “Guardaré mis cambios solo si el registro sigue en el mismo estado que cuando empecé a editarlo.” Si el registro cambió entre tanto, el guardado se rechaza y el usuario ve un conflicto.
Esto es diferente del bloqueo pesimista, que sería más bien “estoy editando esto, así que nadie más puede”. El bloqueo pesimista suele implicar bloqueos duros, timeouts y gente bloqueada. Puede ser útil en casos raros (por ejemplo, mover dinero entre cuentas), pero suele resultar frustrante en herramientas administrativas concurridas donde ocurren muchas ediciones pequeñas cada día.
El bloqueo optimista suele ser el mejor valor por defecto porque mantiene el flujo de trabajo. La gente puede editar en paralelo y el sistema solo interviene cuando hay una colisión real.
Encaja mejor cuando:
- Los conflictos son posibles pero no constantes.
- Las ediciones son rápidas (unos pocos campos, un formulario corto).
- Bloquear a otros ralentizaría al equipo.
- Puedes mostrar un mensaje claro de “alguien actualizó esto”.
- Tu API puede comprobar una versión (o marca de tiempo) en cada actualización.
Lo que evita es el problema del “sobrescritura silenciosa”. En lugar de perder datos, obtienes una parada clara: “Este registro cambió desde que lo abriste.”
Lo que no hace también importa. No impedirá que dos personas tomen decisiones diferentes (pero válidas) basadas en la misma información vieja, y no fusionará cambios automáticamente por ti. Y si omites la comprobación en el servidor, realmente no has solucionado nada.
Límites comunes a tener en cuenta:
- No resuelve conflictos automáticamente (todavía necesitas una decisión).
- No ayuda si los usuarios editan offline y sincronizan después sin comprobaciones.
- No arregla permisos malos (alguien aún puede editar lo que no debería).
- No detecta conflictos si solo compruebas en el cliente.
En la práctica, el bloqueo optimista es solo un valor extra que lleva la edición, más una regla en el servidor de “solo actualizar si coincide”. Si construyes un panel admin en AppMaster, esa comprobación suele vivir en la lógica de negocio justo donde se hacen las actualizaciones.
Dos enfoques comunes: columna de versión vs updated_at
Para detectar que un registro cambió mientras alguien lo editaba, normalmente eliges una de dos señales: un número de versión o una marca de tiempo updated_at.
Enfoque 1: columna version (entero incremental)
Añade un campo version (normalmente un entero). Cuando cargas el formulario de edición, también traes la version actual. Al guardar, envías ese mismo valor.
La actualización solo tiene éxito si la versión almacenada todavía coincide con la que tenía el usuario al empezar. Si coincide, actualizas el registro e incrementas version en 1. Si no coincide, devuelves un conflicto en vez de sobrescribir.
Esto es fácil de razonar: la versión 12 significa “este es el cambio número 12”. También evita casos borde relacionados con el tiempo.
Enfoque 2: updated_at (comparación de timestamp)
La mayoría de tablas ya tiene un campo updated_at. La idea es la misma: leer updated_at cuando se abre el formulario e incluirlo al guardar. El servidor solo actualiza si updated_at no cambió.
Puede funcionar bien, pero las marcas de tiempo tienen trampas. Diferentes bases de datos almacenan distinta precisión. Algunas redondean a segundos, lo que puede perder ediciones rápidas. Si varios sistemas escriben en la misma base de datos, la deriva de reloj y el manejo de zonas horarias también pueden crear casos confusos.
Una forma simple de comparar:
- Columna
version: comportamiento claro, portable entre bases de datos, sin problemas de reloj. updated_at: a menudo “gratis” porque ya existe, pero la precisión y el manejo de relojes pueden causar problemas.
Para la mayoría de equipos, una columna version es la mejor señal primaria. Es explícita, predecible y fácil de referenciar en logs y tickets de soporte.
Si construyes en AppMaster, esto normalmente significa añadir un campo entero version en el Data Designer y asegurarte de que tu lógica de actualización lo comprueba antes de guardar. Puedes mantener updated_at para auditoría, pero deja que el número de versión decida si una edición es segura de aplicar.
Qué almacenar y qué enviar con cada edición
El bloqueo optimista solo funciona si cada edición lleva un marcador “visto por última vez” del momento en que el usuario abrió el formulario. Ese marcador puede ser un número version o una marca updated_at. Sin él, el servidor no puede saber si el registro cambió mientras el usuario escribía.
En el propio registro, conserva los campos de negocio normales y además un campo de concurrencia que controle el servidor. El conjunto mínimo es:
id(identificador estable)- campos de negocio (nombre, estado, precio, notas, etc.)
version(entero que se incrementa en cada actualización exitosa) oupdated_at(timestamp escrito por el servidor)
Cuando la pantalla de edición se carga, el formulario debe almacenar el valor visto por última vez de ese campo de concurrencia. Los usuarios no deberían editarlo, así que mantenlo como un campo oculto o en el estado del formulario. Ejemplo: la API devuelve version: 12 y el formulario conserva 12 hasta Guardar.
Cuando el usuario haga clic en Guardar, envía dos cosas: los cambios y el marcador visto por última vez. La forma más simple es incluirlo en el cuerpo de la petición de actualización, como id, campos cambiados y expected_version (o expected_updated_at). Si creas la UI en AppMaster, trátalo como cualquier otro valor ligado: cárgalo con el registro, mantenlo sin cambios y envíalo con la actualización.
En el servidor, la actualización debe ser condicional. Solo actualizas si el marcador esperado coincide con lo que hay en la base de datos. Si no coincide, no “hagas merge” silenciosamente.
Una respuesta de conflicto debe ser clara y fácil de manejar en la UI. Una respuesta práctica incluye:
- Estado HTTP
409 Conflict - un mensaje corto como “Este registro fue actualizado por otra persona.”
- el valor actual del servidor (
current_versionocurrent_updated_at) - opcionalmente, el registro actual del servidor (para que la UI muestre qué cambió)
Ejemplo: Sam abre un registro de Cliente en versión 12. Priya guarda un cambio, pasando a versión 13. Sam más tarde hace Guardar con expected_version: 12. El servidor devuelve 409 con el registro actual en versión 13. Ahora la UI puede pedir a Sam que revise los valores más recientes en vez de sobrescribir la edición de Priya.
Paso a paso: implementar bloqueo optimista de extremo a extremo
El bloqueo optimista se reduce a una regla: cada edición debe probar que se basa en la última versión guardada del registro.
1) Añadir un campo de concurrencia
Elige un campo que cambie en cada escritura.
Un version entero dedicado es lo más sencillo. Empieza en 1 e incrementa en cada actualización. Si ya tienes un updated_at fiable que siempre cambia, puedes usarlo, pero asegúrate de que se actualice en cada escritura (incluyendo jobs en background).
2) Enviar ese valor al cliente al leer
Cuando la UI abre una pantalla de edición, incluye la version actual (o updated_at) en la respuesta. Guárdalo en el estado del formulario como un valor oculto.
Piénsalo como un recibo que dice: “estoy editando lo que leí por última vez”.
3) Requerir el valor al actualizar
Al guardar, el cliente envía los campos editados más el valor de concurrencia visto por última vez.
En el servidor, haz la actualización condicional. En términos SQL, sería:
UPDATE tickets
SET status = $1,
version = version + 1
WHERE id = $2
AND version = $3;
Si la actualización afecta 1 fila, el guardado tuvo éxito. Si afecta 0 filas, alguien más ya cambió el registro.
4) Devolver el nuevo valor tras el éxito
Después de un guardado exitoso, devuelve el registro actualizado con la nueva version (o nuevo updated_at). El cliente debe reemplazar el estado del formulario con lo que devuelva el servidor. Esto previene “dobles guardados” usando una versión antigua.
5) Tratar los conflictos como un resultado normal
Cuando la actualización condicional falla, devuelve una respuesta de conflicto clara (a menudo HTTP 409) que incluya:
- el registro actual tal como existe ahora
- los cambios que el cliente intentó (o la información suficiente para reconstruirlos)
- qué campos difieren (si puedes calcularlo)
En AppMaster, esto se mapea bien a un campo de modelo de PostgreSQL en el Data Designer, a un endpoint de lectura que devuelve la versión, y a un Business Process que realiza la actualización condicional y bifurca en éxito o conflicto.
Patrones de UI que manejan conflictos sin molestar a los usuarios
El bloqueo optimista es solo la mitad del trabajo. La otra mitad es lo que la persona ve cuando su guardado es rechazado porque alguien más cambió el registro.
Una buena UI de conflicto tiene dos objetivos: evitar sobrescrituras silenciosas y ayudar al usuario a terminar su tarea rápido. Bien hecho, se siente como una barrera útil, no como un obstáculo.
Patrón 1: Diálogo bloqueante simple (más rápido)
Úsalo cuando las ediciones son pequeñas y los usuarios pueden reaplicar sus cambios tras recargar.
Mantén el mensaje corto y específico: “Este registro cambió mientras lo editabas. Recarga para ver la versión más reciente.” Luego da dos acciones claras:
- Recargar y continuar (primaria)
- Copiar mis cambios (opcional pero útil)
“Copiar mis cambios” puede poner los valores no guardados en el portapapeles o mantenerlos en el formulario tras la recarga, para que la gente no tenga que recordar lo que escribió.
Funciona bien para actualizaciones de campo único, toggles, cambios de estado o notas cortas. También es lo más fácil de implementar en la mayoría de constructores, incluyendo pantallas admin basadas en AppMaster.
Patrón 2: “Revisar cambios” (mejor para registros de alto valor)
Úsalo cuando el registro es importante (precios, permisos, pagos) o el formulario es largo. En vez de un error sin salida, lleva al usuario a una pantalla de conflicto que compare:
- “Tus ediciones” (lo que intentó guardar)
- “Valores actuales” (lo último en la base de datos)
- “Qué cambió desde que lo abriste” (los campos en conflicto)
Mantenlo enfocado. No muestres todos los campos si solo hay tres en conflicto.
Para cada campo en conflicto, ofrece opciones simples:
- Mantener lo mío
- Tomar lo de ellos
- Fusionar (solo cuando tenga sentido, como tags o notas)
Tras resolver los conflictos, guarda de nuevo usando la versión más reciente. Si soportas texto enriquecido o notas largas, muestra un diff pequeño (texto añadido/eliminado) para que los usuarios decidan rápido.
Cuándo permitir forzar sobrescritura (y quién puede hacerlo)
A veces se necesita forzar una sobrescritura, pero debe ser rara y controlada. Si la añades, que sea deliberada: exige una razón corta, registra quién lo hizo y limítalo a roles como admins o supervisores.
Para usuarios regulares, por defecto “Revisar cambios”. Forzar la sobrescritura se justifica cuando el usuario es el propietario del registro, el registro es de bajo riesgo o el sistema corrige datos erróneos bajo supervisión.
Escenario de ejemplo: dos compañeros editan el mismo registro
Dos agentes de soporte, Maya y Jordan, trabajan en la misma herramienta admin. Ambos abren el mismo perfil de cliente para actualizar el estado y añadir notas tras llamadas diferentes.
Cronología (con bloqueo optimista activado usando version o comprobación updated_at):
- 10:02 - Maya abre Cliente #4821. El formulario carga Status = "Needs follow-up", Notes = "Called yesterday" y Version = 7.
- 10:03 - Jordan abre el mismo cliente. Ve los mismos datos, también Version = 7.
- 10:05 - Maya cambia Status a "Resolved" y añade una nota: "Issue fixed, confirmed by customer." Hace clic en Guardar.
- 10:05 - El servidor actualiza el registro, incrementa Version a 8 (o actualiza
updated_at) y guarda una entrada de auditoría: quién cambió qué y cuándo. - 10:09 - Jordan escribe otra nota: "Customer asked for a receipt" y hace clic en Guardar.
Sin una comprobación de concurrencia, el guardado de Jordan podría sobrescribir silenciosamente el estado y la nota de Maya, según cómo esté construido el update. Con bloqueo optimista, el servidor rechaza el guardado de Jordan porque está intentando guardar con Version = 7 mientras que el registro ya está en Version = 8.
Jordan ve un mensaje de conflicto claro. La UI muestra lo que pasó y le da un paso seguro:
- Recargar el registro más reciente (descartar mis cambios)
- Aplicar mis cambios encima del registro más reciente (recomendado cuando sea posible)
- Revisar diferencias (mostrar "Mío" vs "Último") y elegir qué conservar
Una pantalla simple puede mostrar:
- “Este cliente fue actualizado por Maya a las 10:05”
- Los campos que cambiaron (Status y Notes)
- Una vista previa de la nota no guardada de Jordan, para que pueda copiarla o re-aplicarla
Jordan selecciona “Revisar diferencias”, mantiene el Status de Maya = "Resolved" y añade su nota a las notas existentes. Guarda de nuevo, esta vez usando Version = 8, y la actualización tiene éxito (ahora Version = 9).
Estado final: sin pérdida de datos, sin adivinar quién sobrescribió a quién y con una auditoría clara que muestra el cambio de Maya y ambas notas como ediciones separadas y rastreables. En una herramienta construida con AppMaster, esto se traduce en una comprobación en la actualización más un diálogo pequeño de resolución en la UI.
Errores comunes que todavía causan pérdida de datos
La mayoría de bugs de “bloqueo optimista” no son sobre la idea sino sobre la coordinación entre UI, API y base de datos. Si alguna capa olvida las reglas, aún puedes obtener sobrescrituras silenciosas.
Un error clásico es leer la versión (o timestamp) cuando se carga la pantalla, pero no enviarla al guardar. Suele ocurrir cuando un formulario se reutiliza en varias páginas y el campo oculto se pierde, o cuando un cliente API solo envía "campos cambiados".
Otra trampa común es hacer las comprobaciones solo en el navegador. El usuario puede ver una advertencia, pero si el servidor acepta la actualización de todos modos, otro cliente (o un reintento) puede sobrescribir los datos. El servidor debe ser el guardián final.
Patrones que causan más pérdida de datos:
- Falta del token de concurrencia en la petición de guardado (
version,updated_ato ETag), así el servidor no tiene nada para comparar. - Aceptar actualizaciones sin una condición atómica, por ejemplo actualizando solo por
iden lugar de porid + version. - Uso de
updated_atcon baja precisión (por ejemplo, segundos). Dos ediciones en el mismo segundo pueden parecer idénticas. - Reemplazar campos grandes (notas, descripciones) o arrays enteros (tags, líneas de pedido) sin mostrar qué cambió.
- Tratar cualquier conflicto como “solo reintentar”, que puede volver a aplicar valores obsoletos encima de datos más nuevos.
Un ejemplo concreto: dos leads de soporte abren el mismo registro de cliente. Uno añade una nota larga, el otro cambia el estado y guarda. Si tu guardado reemplaza todo el payload del registro, el cambio de estado puede borrar accidentalmente la nota.
Cuando ocurre un conflicto, los equipos aún pierden datos si la respuesta de la API es demasiado escueta. No devuelvas solo “409 Conflict”. Devuelve suficiente información para que un humano lo arregle:
- La versión actual del servidor (o
updated_at) - Los últimos valores del servidor para los campos implicados
- Una lista clara de campos que difieren (aunque sea solo sus nombres)
- Quién lo cambió y cuándo (si lo registras)
Si implementas esto en AppMaster, aplica la misma disciplina: mantiene la versión en el estado de la UI, envíala con la actualización y aplica la comprobación dentro de la lógica backend antes de escribir en PostgreSQL.
Comprobaciones rápidas antes de lanzar
Antes de desplegar, repasa rápido los modos de fallo que crean el efecto “se guardó bien” mientras se sobrescribe el trabajo de otro.
Comprobaciones de datos y API
Asegúrate de que el registro lleve un token de concurrencia de extremo a extremo. Ese token puede ser un entero version o un timestamp updated_at, pero debe tratarse como parte del registro, no meta opcional.
- Las lecturas incluyen el token (y la UI lo almacena en el estado del formulario, no solo en pantalla).
- Cada actualización envía el token visto por última vez y el servidor lo verifica antes de escribir.
- En éxito, el servidor devuelve el nuevo token para que la UI se mantenga sincronizada.
- Las ediciones masivas y las ediciones inline siguen la misma regla, no un atajo especial.
- Los jobs en background que editan las mismas filas también comprueban el token (o crearán conflictos que parecen aleatorios).
Si construyes esto en AppMaster, verifica que el campo exista en el Data Designer (version o updated_at) y que tu Business Process compare ese valor antes de ejecutar la actualización real.
Comprobaciones de UI
Un conflicto solo es “seguro” si el siguiente paso es obvio.
Cuando el servidor rechaza una actualización, muestra un mensaje claro como: “Este registro cambió desde que lo abriste.” Luego ofrece una acción segura primero: recargar los datos más recientes. Si es posible, añade una ruta de “recargar y reaplicar” que conserve las entradas no guardadas del usuario y las vuelva a aplicar al registro actualizado, para que un arreglo pequeño no se vuelva en reescribir todo.
Si tu equipo realmente lo necesita, añade una opción controlada de “forzar guardado”. Protégela por rol, confírmala y registra quién forzó y qué cambió. Eso mantiene las emergencias posibles sin convertir la pérdida de datos en la opción por defecto.
Próximos pasos: añade bloqueo a un flujo y expande
Empieza pequeño. Elige una pantalla admin donde la gente suele chocarse y añade bloqueo optimista ahí primero. Las áreas de alta colisión suelen ser tickets, pedidos, precios e inventario. Si haces que los conflictos sean seguros en una pantalla ocupada, verás rápido el patrón para repetir en otras.
Elige el comportamiento por defecto de los conflictos desde el principio, porque moldea tanto la lógica backend como la UI:
- Bloquear-y-recargar: detener el guardado, recargar el registro más reciente y pedir al usuario que reaplique su cambio.
- Revisar-y-fusionar: mostrar “tus cambios” vs “últimos cambios” y dejar que el usuario decida qué conservar.
Bloquear-y-recargar es más rápido de construir y funciona bien cuando las ediciones son cortas (cambios de estado, asignaciones, notas pequeñas). Revisar-y-fusionar merece la pena cuando los registros son largos o de alto riesgo (tablas de precios, ediciones multi-campo de pedidos).
Luego implementa y prueba un flujo completo antes de expandir:
- Elige una pantalla y lista los campos que más editan los usuarios.
- Añade un valor version (o
updated_at) al payload del formulario y exígelo al guardar. - Haz la actualización condicional en la base de datos (solo update si la version coincide).
- Diseña el mensaje de conflicto y la siguiente acción (recargar, copiar mi texto, abrir vista de comparación).
- Prueba con dos navegadores: guarda en la pestaña A y luego intenta guardar datos obsoletos en la pestaña B.
Añade logging ligero para los conflictos. Incluso un evento simple “conflicto ocurrido” con tipo de registro, nombre de pantalla y rol de usuario te ayuda a detectar puntos calientes.
Si construyes herramientas admin con AppMaster (appmaster.io), las piezas principales encajan bien: modela un campo version en el Data Designer, aplica actualizaciones condicionales en Business Processes y añade un pequeño diálogo de conflicto en el UI builder. Una vez que el primer flujo esté estable, repite el patrón pantalla por pantalla y mantén la UI de conflicto consistente para que los usuarios lo aprendan una vez y confíen en ello en todas partes.
FAQ
Un sobrescritura silenciosa ocurre cuando dos personas editan el mismo registro desde pestañas o sesiones diferentes y la última guardada reemplaza los cambios anteriores sin aviso. Lo arriesgado es que ambos usuarios ven un “guardado exitoso”, por lo que los cambios faltantes solo se notan después.
El bloqueo optimista significa que la aplicación solo guarda tus cambios si el registro no ha cambiado desde que lo abriste. Si alguien guardó antes, tu guardado se rechaza con un conflicto para que puedas revisar los datos más recientes en vez de sobrescribirlos.
El bloqueo pesimista impide que otros editen mientras tú trabajas, lo que suele generar esperas, timeouts y la típica pregunta “¿quién lo bloqueó?”. En paneles administrativos concurridos, el bloqueo optimista suele encajar mejor porque deja que la gente trabaje en paralelo y solo interviene cuando hay una colisión real.
Una columna de version suele ser la opción más simple y predecible porque evita problemas de precisión horaria y diferencias entre sistemas. Una comprobación con updated_at puede funcionar, pero puede fallar si la marca de tiempo tiene baja precisión o hay incoherencias entre relojes.
Necesitas un token de concurrencia controlado por el servidor en el registro, normalmente version (entero) o updated_at (timestamp). El cliente debe leerlo al abrir el formulario, mantenerlo sin cambios mientras se edita y enviarlo de vuelta al guardar como el valor “esperado”.
Porque el cliente no puede garantizar la protección de datos compartidos. El servidor debe aplicar una actualización condicional como “update where id = X and version = Y”; si no, otro cliente, un reintento o un job en background puede sobrescribir cambios silenciosamente.
Un buen comportamiento por defecto es mostrar un mensaje bloqueante que diga que el registro cambió y ofrecer una acción segura: recargar los datos más recientes. Si el usuario escribió algo largo, guarda su entrada no guardada para re-aplicarla después de la recarga en vez de obligarle a reescribirlo.
Devuelve una respuesta de conflicto clara (a menudo 409) junto con contexto suficiente para recuperarse: la versión actual del servidor y los últimos valores del registro. Si puedes, indica quién lo modificó y cuándo para que el usuario entienda por qué se rechazó su guardado.
Cuidado con tokens faltantes en la solicitud de guardado, actualizaciones que solo filtran por id en vez de por id + version, y comprobaciones con updated_at de baja precisión. Otro error frecuente es reemplazar campos grandes (notas, arrays) en vez de actualizar solo lo necesario, lo que aumenta la probabilidad de borrar el trabajo de alguien más.
En AppMaster, añade un campo version en el Data Designer e inclúyelo en lo que la UI lee hacia el estado del formulario. Luego, aplica una actualización condicional dentro del Business Process para que la escritura solo tenga éxito cuando la versión esperada coincida, y maneja la rama de conflicto en la UI con una opción de recargar/revisar.


