Agrega un overlay de pantalla completa (circulo girando + porcentaje estimado +
"Actualizando...") que bloquea todo clic mientras corre la auto-actualizacion, y recarga la
pagina sola al terminar. De paso corrige un bug real: el useEffect de mountedRef no
reactivaba el flag en su setup, asi que con React.StrictMode (dev) el polling de estado
nunca llegaba a correr -- no afectaba produccion (StrictMode no duplica efectos ahi) pero
ocultaba el problema al probar en dev. Tambien se corrigen un par de voseos que quedaron
en los textos de Actualizaciones.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Superadmin ve un popup con acceso directo a Configuracion >
Actualizaciones si hay una version nueva disponible, en vez de tener
que acordarse de revisar el panel manualmente.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
POST /api/updater/apply dispara scripts/apply-update.sh (backup +
git pull + migraciones + build + restart) en un proceso separado.
Apagado por defecto: solo se activa si el servidor define
UPDATE_APP_DIR/UPDATE_BACKEND_SERVICE/UPDATE_FRONTEND_SERVICE en su
backend/.env. Sin esa config, el panel se comporta igual que antes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Se asigna sola, sin etiquetar nada a mano: backend y errores de render de React (rompen toda la
pantalla) = alta; errores JS sueltos = media; promesas rechazadas sin manejar = baja (suelen ser
fallos de red puntuales, no criticos). Nuevo filtro en el panel ademas del de origen ya existente.
Migracion 014_error_logs_severity.sql (ALTER TABLE ADD COLUMN IF NOT EXISTS). Probado en dev: las
tres severidades se asignan correctamente segun el origen, un valor invalido del frontend cae en
media, y el filtro combinado con origen funciona.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Panel nuevo en Configuracion ("Errores del sistema", solo superadmin): errores de backend
(excepciones 500 no manejadas, con stack/ruta/metodo/usuario) y de frontend (errores JS y
promesas sin catch en el navegador del usuario, antes invisibles para cualquiera mas alla de esa
persona). Filtro por origen, boton para limpiar el historial.
Error Boundary de React: si algo rompe el render, ya no queda pantalla en blanco -- aviso simple
con boton de recargar, y el error queda reportado igual.
Nuevo error_logs (tabla, se recorta sola a 1000 filas) + GET/DELETE /api/error-logs (superadmin)
+ POST /api/error-logs (publico, rate-limited, para reportar incluso sin sesion).
Probado de punta a punta en dev: error real de backend (500) capturado con stack completo y
usuario correcto, error JS global y promesa rechazada capturados desde el navegador, panel
muestra ambos y el boton de limpiar funciona. Se encontro y corrigio un bug real en el camino: la
identificacion de usuario opcional leia jwt.payload.id en vez de .sub (como realmente lo firma
auth/service.js), asi que nunca identificaba a nadie.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Compara la version instalada contra el CHANGELOG.md de main en Gitea (lectura anonima por HTTPS,
sin token) y muestra cada version pendiente con su criticidad -- derivada del digito que cambio
(1ro=Alta, 2do=Media, 3ro=Baja, sin etiquetar nada a mano, regla ya documentada en
VERSIONADO.md), una descripcion breve, y si trae migraciones de base de datos nuevas. Banner en
rojo avisando que la actualizacion requiere reiniciar el sistema.
No instala nada automaticamente -- solo informa. Aplicar la actualizacion (git pull, build,
migrar, reiniciar servicios) sigue siendo manual, a criterio del administrador de cada
instalacion, tal cual DEPLOY_WEB.md ya documenta.
Nuevo GET /api/updater/status (solo superadmin). Requiere que el repo en Gitea sea publico con
vista anonima habilitada -- documentado en DEPLOY_WEB.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Nueva marca configurable desde Configuracion -> Empresa (solo superadmin): nombre y logo,
guardados en system_settings (mismo patron que SMTP, sin tabla nueva). Nuevo endpoint publico
GET /api/company (+ /logo) para que login/registro/recuperacion de clave puedan mostrar la marca
antes de iniciar sesion. Sin configurar, se ve "LinkedCorp" y el logo estatico de siempre.
Probado en dev (build + Playwright: panel de Configuracion, sidebar expandido/compacto, login
publico), sin desplegar a produccion por pedido explicito del usuario -- este commit sirve de
punto de control en git antes de decidir el despliegue.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Un proyecto puede tener uno o mas administradores (role_in_project=
'admin' en project_members), independiente del rol global del usuario.
Nuevo PUT /api/projects/:id/members/:userId/role; canWriteProject lo
reconoce ademas del comportamiento previo (admin/superadmin/it_manager
global, o project_lead miembro). UI: casilla al agregar miembro + boton
de escudo para promover/degradar (nunca toca al Dueño).
- Menu "Configuracion" (apariencia, catalogos, SMTP) visible y accesible
solo para el superadmin: oculto en el nav y con guarda en la ruta
/configuracion para quien la teclee directo. Escritura de catalogos
restringida de [superadmin, admin] a solo superadmin para que el
backend sea consistente con la UI.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Componente reutilizable PasswordInput (icono de ojo) usado en login, registro,
restablecer clave, cambiar clave, crear usuario y la clave SMTP de
Configuracion, para que el usuario pueda verificar que la esta escribiendo
correctamente.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Agrega POST /api/settings/smtp/test (superadmin, rate-limited) que envia un
correo real usando los valores actuales del formulario y propaga el error
real del servidor SMTP, a diferencia de sendMail() que nunca lanza. El panel
de SMTP ahora tiene un campo de destinatario y boton "Enviar prueba" con el
resultado real (exito o error), en vez de solo "Guardar" sin forma de
verificar que la configuracion funcione.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- POST /api/auth/register: primer usuario del sistema -> superadmin con sesion
directa (bootstrap, sin correo ni aprobacion); los siguientes quedan
pending_verification y reciben un correo con enlace de un solo uso (24h).
- GET /api/auth/verify-email: pasa a pending_approval y notifica in-app a los
admin/superadmin activos.
- admin/superadmin aprueban (POST /api/users/:id/approve, eligen rol y
departamento) o rechazan (POST /api/users/:id/reject, borra y audita el
email) desde Organizacion -> Usuarios (nuevo badge de estado + acciones).
- login() da mensaje especifico segun el status de la cuenta en vez del
generico "credenciales invalidas".
- Recuperacion de clave (forgot/reset-password): respuesta siempre generica,
token de 1 hora de un solo uso.
- Nuevo modulo settings: SMTP configurable SOLO por superadmin desde
Configuracion -> Correo (SMTP); clave cifrada AES-256-GCM (ENCRYPTION_KEY)
en la tabla system_settings, nunca expuesta por la API. sendMail() degrada
a modo dev (loguea en vez de enviar) si no hay SMTP configurado, y nunca
deja que un fallo de envio tumbe al que lo llama.
- Migracion 002_registration_and_smtp.sql: rol superadmin, columnas de ciclo
de vida en users (status, tokens), tabla system_settings, y promueve al
usuario mas antiguo del sistema a superadmin.
- Fix: React.StrictMode disparaba el efecto de VerifyEmailPage dos veces; la
segunda llamada (token ya consumido) mostraba "enlace invalido" pese a que
la verificacion ya habia funcionado. Se agrega una guarda por token.
- Fix (deuda tecnica v1.0.0): se reordena el montaje de rutas en app.js —
subtasksRoutes (mount generico en /api) va al final, para no interceptar
con su requireAuth interno una request sin token dirigida a un router mas
especifico registrado despues.
- Documentacion actualizada: AGENTS.md, SPEC.md, API.md, BASE_DE_DATOS.md,
CONFIGURACION.md, ESTADO.md, CHANGELOG.md. Version 1.1.0.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Las 11 pantallas con listado/detalle bloqueaban TODO su render con
"if (loading || !data) return <Cargando/>". Como useAsync pone loading=true en
cada reload (no solo en la carga inicial), cualquier accion dentro de un
DetailPopup abierto que dispara un reload del padre (agregar subtarea, agregar
dependencia, guardar comentario, etc.) desmontaba momentaneamente TODA la
pantalla -y con ella el popup y su estado interno (pestaña activa)-, que volvia
a montarse desde cero con sus valores iniciales.
Sintoma reportado: al agregar una subtarea (Enter o boton "Nuevo"), el popup de
la actividad volvia a la pestaña Info en vez de quedarse en Subtareas.
Fix: la guarda ahora es solo "if (!data)" -bloquea unicamente la carga
inicial, cuando todavia no hay nada que mostrar-. Las recargas subsiguientes
actualizan el contenido en el sitio sin desmontar nada.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
El detalle de un item de listado (actividad, departamento, usuario, proyecto nuevo,
catalogo) ya no se apila debajo ni al costado del listado: ahora abre en un popup que
cubre la ventana, deja el listado inactivo detras, nunca se cierra con clic afuera (solo
con el boton Cerrar o una accion explicita del formulario) y mantiene un alto FIJO por
instancia para que cambiar de pestaña interna (Info/Subtareas/Dependencias/Comentarios/
Documentos) no reajuste su tamaño.
De paso corrige un bug real de stacking: ContentDialog no usaba portal a document.body,
así que un dialogo de confirmacion abierto desde dentro de un DetailPopup quedaba oculto
detras de este. Ahora ambos son portales con una jerarquia de z-index explicita
(Flyout 100 < DetailPopup 200 < ContentDialog 250 < Toast 300).
En el detalle de actividad, Subtareas y Dependencias pasan de estar apiladas dentro de la
pestaña Info a ser cada una su propia pestaña.
Regla documentada en project-md-templates/UI-UX-WINDOWS.md (componente DetailPopup) para
que aplique a futuros proyectos de escritorio Windows del grupo.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>