Commit Graph

92 Commits

Author SHA1 Message Date
Milton Gonzalez 08e6db0b6f feat: app instalable desde el navegador (PWA)
vite-plugin-pwa genera manifest.webmanifest + service worker en el build. Iconos nuevos
generados a partir del logo actual (192, 512, maskable para Android). El service worker
precachea solo assets estaticos (JS/CSS/iconos) -- /api nunca se cachea, siempre va a la red, asi
los datos no quedan desactualizados. Se actualiza solo (autoUpdate) sin pedirle nada al usuario.

Probado: build genera sw.js/manifest correctamente, el service worker registra y activa en un
browser real, y la app (login, listado de proyectos, llamadas a /api) sigue funcionando igual
que antes -- sin regresiones.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 15:25:04 -05:00
Milton Gonzalez feadd2e715 feat: filtro de severidad (Alta/Media/Baja) en el log de errores
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>
2026-09-17 14:50:36 -05:00
Milton Gonzalez dec8891506 feat: log de errores para el admin (backend + frontend)
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>
2026-09-17 14:31:40 -05:00
Milton Gonzalez 043433bf9b feat: convertir una actividad en proyecto (con sus tareas)
Una tarea siempre necesita una actividad contenedora, asi que convertir crea DOS cosas: el
proyecto nuevo y, dentro, una actividad nueva que recibe las tareas -- y comentarios/adjuntos --
de la actividad original. Se piden ambos nombres (precargados con el nombre actual) antes de
confirmar. La actividad original se borra al final, ya vacia de tareas (no choca con el bloqueo
de removeActivity). Exige el mismo permiso que crear un proyecto, no solo escribir la actividad.

Nuevo POST /api/activities/:id/convert-to-project. Probado de punta a punta en dev: prioridad
heredada, tareas y comentario movidos correctamente, actividad original eliminada, navega al
proyecto nuevo al terminar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 08:59:16 -05:00
Milton Gonzalez 59aadc6a35 feat: panel de Actualizaciones en Configuracion (solo informativo)
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>
2026-09-16 17:11:58 -05:00
Milton Gonzalez 67cb58ed0b feat: el buscador de Proyectos y Actividades tambien busca en tareas
Buscador de Proyectos: ahora busca tambien en actividades y tareas de cualquier proyecto (antes
solo nombre/codigo del proyecto). Resultados en un panel flotante con la ruta completa
"Proyecto - Actividad - Tarea"; clic navega al proyecto y abre la actividad encontrada
(pestaña Tareas si el resultado era una tarea).

Buscador de Actividades (dentro de un proyecto): busca tambien en las tareas de esa actividad,
mismo panel con "Actividad - Tarea".

Nuevo GET /api/search?q=...&projectId= (global o acotado a un proyecto). Nuevos componentes/hooks
reutilizables: SearchResultsDropdown, useDebouncedValue.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 15:04:53 -05:00
Milton Gonzalez 53570bd4f8 feat: mover una actividad (con sus tareas) a otro proyecto
Menu de tres puntos en el listado de Actividades -> elegir proyecto destino -> confirmar
("Se movera la actividad... (junto con sus tareas)... Aceptar/Cancelar) antes de ejecutar.

Tareas, comentarios y adjuntos se mueven solos (referencian la actividad, no el proyecto). Las
dependencias existentes se eliminan al mover -- solo tienen sentido dentro del mismo proyecto
(addDependency ya lo exigia). Nuevo PUT /api/activities/:id/move, exige permiso de escritura en
ambos proyectos (origen y destino).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 14:47:57 -05:00
Milton Gonzalez 9d76e1b79b feat: columna Actualizado (fecha y hora) en el listado de Actividades
Mismo formato que ya existia en Proyectos. Ordenable por columna; no cambia el orden por
defecto (sigue siendo el orden manual arrastrado con el mouse).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 14:38:44 -05:00
Milton Gonzalez 696ab0d092 fix: arrastrar tareas para reordenar dejo de funcionar
Regresion del fix del checkbox (v1.15.2): draggable/onDragStart quedaron directo sobre el <svg>
del icono de agarre, que solo capta el mousedown sobre lo pintado (huecos entre sus formas), no
sobre todo su recuadro -- el navegador no siempre detectaba el inicio del arrastre. Envuelto en
un <span> con area de agarre solida, mismo patron ya usado en el listado de Actividades.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 14:22:26 -05:00
Milton Gonzalez 60347fab72 feat: contador de Tareas muestra pendientes/total (ej. 2/3)
Solo la pestaña Tareas -- Comentarios y Documentos siguen con el total simple. Se recalcula al
agregar, quitar o marcar/desmarcar tareas.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 13:02:34 -05:00
Milton Gonzalez 22e09c989e style: contador de pestañas en rojo en vez de azul
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 12:56:37 -05:00
Milton Gonzalez 1b23da7151 feat: contador en las pestañas de Tareas/Comentarios/Documentos de una actividad
Circulo con el numero al lado del nombre de la pestaña: Tareas muestra las pendientes (sin
marcar), Comentarios y Documentos el total. Soporte agregado al componente Tabs reutilizable
(tab.count), no solo al detalle de actividad.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 12:52:00 -05:00
Milton Gonzalez c84a07968f fix: checkbox de tarea (hecho) no se actualizaba tras guardar
useDragReorder resincronizaba su copia local solo cuando cambiaba el conjunto de ids, no cuando
se editaba un campo de una tarea existente (marcar hecho, renombrar) -- la fila se quedaba
mostrando el snapshot viejo para siempre despues de guardar. Ahora compara el contenido completo.

De paso: draggable/onDragStart pasan a vivir solo en el icono de agarre, nunca en toda la fila --
un ancestro draggable=true le gana el mousedown a los controles interactivos que tenga adentro en
algunos navegadores (no era la causa de este bug puntual, pero es un problema real del mismo
patron que conviene cerrar ya que se reutiliza en cualquier lista futura).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 12:33:35 -05:00
Milton Gonzalez f8e42d4377 feat: instalador para servidor nuevo + fix migracion 006 en BD vacia
scripts/install.sh automatiza el primer despliegue en un servidor Debian/Ubuntu nuevo: verifica
requisitos (Node.js, npm, cliente mysql, systemd, espacio, puertos libres), pregunta dominio,
puertos y base de datos (MariaDB local auto-instalada o un servidor existente), genera
backend/.env con secretos aleatorios, corre migraciones + catalogos, compila el frontend y
crea/arranca los servicios systemd. No crea usuarios: el primer registro publico se vuelve
superadmin solo (bootstrap ya existente).

Probando el instalador de punta a punta contra una base de datos recien creada goo un bug real:
la migracion 006_seed_it_baseline.sql (datos de ejemplo especificos de LinkedCorp) fallaba por
foreign key al no existir todavia catalogos ni usuario. Corregido para que detecte esa situacion
y quede inerte en instalaciones nuevas, sin tocar el comportamiento ya probado en dev/prod.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 15:43:11 -05:00
Milton Gonzalez 2a1bdf1ba9 feat: nombre y logo de empresa configurables (solo dev, no desplegado a prod)
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>
2026-09-15 14:42:15 -05:00
Milton Gonzalez 7ba4a89d49 fix: version real en pie del menu + logo abajo-izquierda
El pie del menu lateral mostraba el texto fijo "v0.1.0" en vez de la version real (se lee ahora
de package.json via import JSON de Vite). El logo de LinkedCorp se mueve del encabezado del menu
al pie, sobre el numero de version, en modo expandido y compacto.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 13:08:55 -05:00
Milton Gonzalez 80981e9063 feat: arrastrar con el mouse para reordenar actividades y tareas
Hook reutilizable useDragReorder (drag & drop nativo HTML5, sin
libreria nueva) usado en dos lugares: ListView (opt-in via onReorder,
deshabilitado mientras haya un ordenamiento por columna activo) para
el listado de actividades de un proyecto, y el listado de tareas de
una actividad. Orden persistido en sort_order (activities y tasks,
migracion 012, posiciones siempre 1..N).

Fix real encontrado durante las pruebas: sincronizar el estado interno
del hook por la REFERENCIA del arreglo de entrada entraba en loop
infinito de renders cuando el llamador pasa "algo || []" (un arreglo
nuevo en cada render mientras los datos cargan). Corregido para
sincronizar por el contenido (ids), no por referencia.

Probado en dev con curl (persistencia del orden) y Playwright
(arrastrar filas en el navegador, orden visible y guardado
correctamente, sin errores de consola tras el fix).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 12:41:19 -05:00
Milton Gonzalez 5e0bc0809e feat: eliminar actividad, bloqueado si tiene tareas pendientes
No existia forma de borrar una actividad. Agregado DELETE
/api/activities/:id con boton "Eliminar" en el listado (popup de
confirmacion). Si tiene tareas, se bloquea con 400 y mensaje claro en
vez de arrastrar todo en cascada sin avisar.

De paso: la logica de borrar adjunto (fila + archivo en disco) se
extrajo a un attachments/service.js reutilizable, usado tanto por el
borrado individual como por el borrado en cascada de una actividad
(evita archivos huerfanos). Comentarios de la actividad tambien se
limpian (sin FK fisica cruzada, entity_type/entity_id es polimorfico).

Probado en dev con curl (bloquea con tarea, permite sin tareas, 404
tras borrar) y Playwright (mensaje de advertencia visible).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 12:30:53 -05:00
Milton Gonzalez 8fa45de6d0 feat: reemplaza "Creado" por "Actualizado" en el listado de Proyectos
Columna con fecha y hora, orden por defecto (mas reciente primero) en
vez de fecha de creacion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 17:26:41 -05:00
Milton Gonzalez a9904074e5 feat: boton editar (lapiz) en cada tarea de una actividad
El backend ya soportaba renombrar una tarea (PUT /api/tasks/:id), solo
faltaba el boton en la UI. Edicion en linea: input + guardar (Enter o
icono) / cancelar (Escape o icono).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 09:29:49 -05:00
Milton Gonzalez ab30dfe53e feat: muestra la fecha del documento en la lista de adjuntos
El backend ya devolvia created_at, solo faltaba mostrarlo junto a
quien lo subio y el tamaño.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 09:21:01 -05:00
Milton Gonzalez d3ef86c1db fix: icono de calendario invisible en campos de fecha con tema oscuro
Reportado en produccion: el icono nativo del input type="date" se
dibujaba negro sobre fondo oscuro. Se agrega color-scheme: dark para
esos inputs cuando data-theme="dark", asi el navegador elige un icono
claro automaticamente. Verificado en dev con Playwright.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 08:51:40 -05:00
Milton Gonzalez 13a69aadc3 feat: campo "hasta" opcional en Tareas recurrentes
Vacio = se repite sin fin (comportamiento previo). Al completar un
ciclo cuyo proximo vencimiento supera "hasta", la tarea se desactiva
sola. Probado en dev con curl (3 ciclos seguidos, se desactiva justo
al superar la fecha) y validacion de que "hasta" no puede ser anterior
al proximo vencimiento.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 10:29:09 -05:00
Milton Gonzalez 72b616bd02 feat: Tareas recurrentes con recordatorio por correo
Menu propio en la barra lateral, independiente de Proyectos:
- Periodicidad configurable (cada N dias/meses/años), proximo
  vencimiento, responsable, proyecto de origen opcional.
- "Completar ciclo" avanza el proximo vencimiento sumando la
  periodicidad y limpia el recordatorio ya enviado.
- Recordatorio por correo automatico N dias antes del vencimiento
  (default 7), a una hora del dia configurable. Un job en el propio
  proceso del backend (setInterval cada 15 min) revisa y envia, sin
  infraestructura nueva (cron del SO, cola, etc).
- Desde la pestaña Tareas de una actividad, cada tarea tiene un boton
  "Convertir en recurrente" que la promueve (la tarea original no se
  borra).

Probado en dev con curl (crear, completar ciclo avanza next_due_date,
promover desde una tarea real, disparo real del job de recordatorios
verificado forzando fecha/hora, segunda corrida no reenvia) y Playwright
(listado, popup de creacion, popup de promocion anidado sobre el detalle
de actividad) sin errores de consola.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 10:17:02 -05:00
Milton Gonzalez 3ab0656a3e feat: filtros de Estado, Prioridad y proximos a vencer en Proyectos
El backend ya soportaba filtrar por statusId/priorityId; solo faltaba
exponerlo en la UI. "Proximos a vencer" es client-side, ventana de 7
dias (misma definicion que el KPI del dashboard).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 09:56:09 -05:00
Milton Gonzalez ccf5cbeb13 feat: ordenar proyectos por prioridad real + separa paginas web de dominios
canWriteProject/ListView ya soportaba ordenar por cualquier columna con
clic en el encabezado; solo Prioridad ordenaba alfabetico por el texto
en vez de por severidad real. Se agrega pr.level a la consulta y
sortValue en la columna.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 09:41:07 -05:00
Milton Gonzalez bf31182425 data: agrega actividad de inventario de servicios de tecnologia
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 09:34:06 -05:00
Milton Gonzalez 472e0be886 data: aclara revision de equipos y agrega actividad de cableado de red
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 09:30:19 -05:00
Milton Gonzalez 6087f5659d data: reset de Proyectos y baja de un usuario, pedido por el superadmin
Migracion 006 de una sola vez (marcador en system_settings, no se repite
en deploys futuros): reemplaza el proyecto de prueba "Sistema ERP" por
los 6 proyectos reales de TI clasificados a partir del listado que
compartio el usuario, y borra la cuenta pflores@hotmail.es (autorregistro
nunca verificado). Probado en dev incluyendo la idempotencia (un proyecto
creado despues de correr la migracion sobrevive a un segundo "npm run
migrate").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 09:22:14 -05:00
Milton Gonzalez f8b80d9bb4 feat: permite rechazar (eliminar) usuarios en pending_verification
Antes solo se podia rechazar un usuario en pending_approval. Una cuenta
autorregistrada que nunca confirmo su correo quedaba en
pending_verification para siempre, sin ninguna accion disponible en
Usuarios para quitarla.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 16:00:00 -05:00
Milton Gonzalez 92591f5c44 fix: mensaje claro cuando no hay actividades para agregar como dependencia
"Agregar dependencia" no daba ninguna señal cuando el combo quedaba vacio
(proyecto con una sola actividad, o ya se agregaron todas las demas) --
se podia percibir como que el boton "no hacia nada". El mecanismo en si
funcionaba bien (verificado con curl y Playwright con 2+ actividades).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 11:40:26 -05:00
Milton Gonzalez bb6bd6865c refactor: renombra Subtareas a Tareas en todo el sistema
Actividad -> Tarea, sin nivel "sub" intermedio (no hay una entidad "Tarea"
de la que estas fueran sub-, asi que el prefijo sobraba). Tabla subtasks
-> tasks (RENAME TABLE idempotente en la migracion 005; 001_initial.sql
tambien actualizado para que una instalacion nueva cree la tabla ya con
el nombre final, sin pasar por el nombre viejo). Modulo backend, rutas,
servicio frontend, pestaña del detalle de actividad, i18n y documentacion
actualizados. Sin cambios de comportamiento, solo nombres.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 08:55:58 -05:00
Milton Gonzalez d7478d0f21 feat: invitar a un proyecto por correo a alguien que aun no es usuario
Al agregar un miembro, el popup ahora permite elegir un usuario existente
o escribir un correo nuevo. Si ese correo no pertenece a nadie, se crea
una cuenta en estado 'invited' (nombre/clave placeholder) y se le manda
un correo con un enlace de un solo uso para que complete su registro
(elige su nombre y clave). La fila de project_members se crea de una vez
al invitar, asi que en cuanto acepta ya es miembro sin pasos adicionales.
A diferencia del autorregistro publico, no pasa por aprobacion de un
admin -- quien lo agrego a su proyecto ya lo vetó.

Nuevos endpoints publicos GET /api/auth/invite y POST /api/auth/accept-invite.
Nueva pagina AcceptInvitePage (/aceptar-invitacion). Migracion 004 agrega
el status 'invited' al enum de users.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 23:55:02 -05:00
Milton Gonzalez 90945ad7b1 feat: nuevo logo/icono de la app y favicon
Icono derivado del original de LinkedCorp (circulo azul #1C55FF, red de
nodos) con una insignia en el acento de la app (#0078D4) para identificar
el modulo de Proyectos. Actualiza logoLinked1.png, logoLinkedCorpTxt.png
y logoLinkedCorpTxtBlack.png (sidebar y login, temas oscuro/claro) y
agrega favicon.svg/png + apple-touch-icon.png (antes no habia favicon).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 22:49:51 -05:00
Milton Gonzalez f96391d360 feat: administradores por proyecto y Configuracion solo para superadmin
- 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>
2026-09-09 09:41:06 -05:00
Milton Gonzalez 5a25ed066d feat: eliminar proyecto (logico), papelera para superadmin y orden por creacion
- DELETE /api/projects/🆔 eliminacion logica (deleted_at/deleted_by), mismo
  permiso que editar el proyecto. Popup de confirmacion antes de aplicarla.
- Papelera (GET /api/projects?trash=true, POST /:id/restore, DELETE
  /:id/permanent) restringida al superadmin; el borrado fisico exige que el
  proyecto ya este en la papelera y arrastra actividades/comentarios/adjuntos
  por ON DELETE CASCADE.
- Dashboard (KPIs, actividades pendientes/calendario, proyectos por estado)
  excluye proyectos eliminados logicamente.
- Columna "Creado" en el listado de proyectos, ordenado por defecto de mas
  nuevo a mas antiguo (ListView admite initialSort ademas del orden por clic
  en cabecera que ya tenia).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 09:26:35 -05:00
Milton Gonzalez 869094c622 feat: boton de mostrar/ocultar clave en todos los campos de contraseña
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>
2026-09-09 09:00:06 -05:00
Milton Gonzalez 6fab4be39f feat: boton de prueba real en Configuracion > Correo (SMTP)
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>
2026-09-06 13:55:34 -05:00
Milton Gonzalez 8c06d20720 feat: cambiar clave desde la app (usuario logueado)
Nuevo PUT /api/auth/change-password: exige la clave actual (a diferencia de
reset-password, que usa un token de correo sin sesion). Opcion "Cambiar clave"
agregada al menu de usuario (popup), disponible para cualquier rol.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 12:35:25 -05:00
Milton Gonzalez 7557dffc52 feat: autorregistro con verificacion de correo + aprobacion, superadmin y SMTP
- 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>
2026-09-05 23:28:46 -05:00
Milton Gonzalez 556bbc7314 chore(release): v1.0.0 — primer despliegue a producción
project.linkedcorp.net operativo en https://project.linkedcorp.net (servidor
192.168.222.3, BD 192.168.222.32, NPM con SSL forzado). Documenta la
topología real y el flujo de deploy en PRODUCCION.md/DEPLOY_WEB.md, y
actualiza AGENTS.md/ESTADO.md/README.md con el estado actual.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 22:47:43 -05:00
Milton Gonzalez 1d22adc42e chore: estructura inicial del MVP
Backend (Express + MariaDB) y frontend (React + Vite, identidad Fluent/Windows) con el
nucleo funcional: seguridad, organizacion, proyectos, actividades, subtareas, dependencias,
dashboard, calendario, gantt, comentarios, adjuntos, notificaciones in-app y catalogos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 08:37:58 -05:00