From 3ab0656a3ea7941f29a137274c4f0f1b5c41470d Mon Sep 17 00:00:00 2001 From: Milton Gonzalez Date: Fri, 11 Sep 2026 09:56:09 -0500 Subject: [PATCH] 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 --- CHANGELOG.md | 6 ++++ VERSION | 2 +- backend/package.json | 2 +- frontend/package.json | 2 +- frontend/src/i18n/messages.js | 2 ++ .../src/pages/Proyectos/ProyectosPage.jsx | 33 +++++++++++++++++-- 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dee213..4962269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog - project.linkedcorp.net +## [1.11.0] - 2026-09-11 +### Added +- Filtros en el listado de Proyectos: Estado, Prioridad (ambos ya soportados por el backend, solo + faltaba exponerlos en la UI) y "Solo próximos a vencer" (checkbox, ventana de 7 días — misma + definición que el KPI del dashboard). + ## [1.10.0] - 2026-09-11 ### Added - Ordenar proyectos por Prioridad ahora usa la severidad real (Baja→Media→Alta→Crítica), no el diff --git a/VERSION b/VERSION index 81c871d..1cac385 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.10.0 +1.11.0 diff --git a/backend/package.json b/backend/package.json index 8cb2a1d..97cabca 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "project-linkedcorp-backend", - "version": "1.10.0", + "version": "1.11.0", "private": true, "type": "module", "main": "src/server.js", diff --git a/frontend/package.json b/frontend/package.json index a8b0700..d1620d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "project-linkedcorp-frontend", "private": true, - "version": "1.10.0", + "version": "1.11.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/i18n/messages.js b/frontend/src/i18n/messages.js index 8116dfb..08ad21a 100644 --- a/frontend/src/i18n/messages.js +++ b/frontend/src/i18n/messages.js @@ -137,6 +137,7 @@ export const messages = { project_invite_sent: 'Invitacion enviada por correo', project_created_at: 'Creado', project_deleted_at: 'Eliminado', + project_filter_due_soon: 'Solo próximos a vencer (7 días)', project_delete_confirm_msg: '¿Seguro que deseas eliminar este proyecto? Se movera a la papelera y dejara de verse en el listado; un superadministrador puede restaurarlo o borrarlo definitivamente.', project_trash: 'Papelera', project_trash_title: 'Papelera de proyectos', @@ -346,6 +347,7 @@ export const messages = { project_invite_sent: 'Invite sent by email', project_created_at: 'Created', project_deleted_at: 'Deleted', + project_filter_due_soon: 'Due soon only (7 days)', project_delete_confirm_msg: 'Are you sure you want to delete this project? It will move to the trash and disappear from the list; a superadmin can restore it or delete it permanently.', project_trash: 'Trash', project_trash_title: 'Project trash', diff --git a/frontend/src/pages/Proyectos/ProyectosPage.jsx b/frontend/src/pages/Proyectos/ProyectosPage.jsx index e22ce3b..12392a8 100644 --- a/frontend/src/pages/Proyectos/ProyectosPage.jsx +++ b/frontend/src/pages/Proyectos/ProyectosPage.jsx @@ -40,7 +40,14 @@ export default function ProyectosPage() { const isSuperadmin = currentUser?.roleCode === 'superadmin'; const canDelete = CAN_DELETE_ROLES.includes(currentUser?.roleCode); - const { data: projects, reload } = useAsync(() => listProjects(showTrash ? { trash: true } : {}), [showTrash]); + const [filterStatusId, setFilterStatusId] = useState(''); + const [filterPriorityId, setFilterPriorityId] = useState(''); + const [filterDueSoon, setFilterDueSoon] = useState(false); + + const { data: projects, reload } = useAsync( + () => listProjects(showTrash ? { trash: true } : { statusId: filterStatusId, priorityId: filterPriorityId }), + [showTrash, filterStatusId, filterPriorityId] + ); const { data: priorities } = useAsync(() => listCatalog('priorities'), []); const { data: statuses } = useAsync(() => listCatalog('project-statuses'), []); const { data: types } = useAsync(() => listCatalog('project-types'), []); @@ -133,6 +140,14 @@ export default function ProyectosPage() { if (!projects) return
{t('common_loading')}
; + const visibleProjects = filterDueSoon && !showTrash + ? projects.filter((p) => { + if (p.status_is_closed || !p.estimated_end_date) return false; + const diffDays = Math.round((new Date(p.estimated_end_date) - new Date()) / 86400000); + return diffDays >= 0 && diffDays <= 7; + }) + : projects; + return (
@@ -155,11 +170,25 @@ export default function ProyectosPage() {
+ {!showTrash && ( + <> +
+ +
+
+ +
+ + + )}
`${p.code} ${p.name} ${p.owner_name} ${p.status_name}`} getRowKey={(p) => p.id}