feat: version 2.17.8 -- Panel: medidor y detalle en "Actividades pendientes"

Base = actividades no eliminadas de proyectos no cancelados; el conteo de
pendientes pasa a usar el mismo criterio (antes incluia eliminadas).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-10-21 08:01:45 -05:00
parent 10952beea5
commit 22b287fcd8
8 changed files with 86 additions and 8 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "project-linkedcorp-backend",
"version": "2.17.7",
"version": "2.17.8",
"private": true,
"type": "module",
"main": "src/server.js",
+3 -1
View File
@@ -14,12 +14,13 @@ function memberFilterFor(currentUser) {
export const getSummary = asyncHandler(async (req, res) => {
const memberUserId = memberFilterFor(req.currentUser);
const [kpis, myActivities, byStatus, overdueProjects, activeProjects] = await Promise.all([
const [kpis, myActivities, byStatus, overdueProjects, activeProjects, pendingActivities] = await Promise.all([
repo.summary(memberUserId),
repo.myPendingActivities(req.currentUser.id, memberUserId),
repo.projectsByStatus(memberUserId),
repo.overdueProjectsList(memberUserId),
repo.activeProjectsList(memberUserId),
repo.pendingActivitiesList(memberUserId),
]);
ok(res, {
kpis,
@@ -27,6 +28,7 @@ export const getSummary = asyncHandler(async (req, res) => {
projectsByStatus: byStatus,
overdueProjects,
activeProjects: activeProjects.map((p) => ({ ...p, dueStatus: dueStatus(p.estimated_end_date) })),
pendingActivities: pendingActivities.map((a) => ({ ...a, dueStatus: dueStatus(a.due_date) })),
});
});
+28 -2
View File
@@ -31,10 +31,18 @@ export async function summary(memberUserId) {
AND p.estimated_end_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY) ${memberClause}`,
memberParams
);
// Pendientes y su base (medidor): sin actividades eliminadas ni de proyectos cancelados.
const [[pendingActivities]] = await pool.query(
`SELECT COUNT(*) AS c FROM activities a JOIN activity_statuses st ON st.id = a.status_id
JOIN projects p ON p.id = a.project_id
WHERE st.is_closed = 0 AND p.deleted_at IS NULL ${memberClause}`,
JOIN projects p ON p.id = a.project_id JOIN project_statuses ps ON ps.id = p.status_id
WHERE st.is_closed = 0 AND a.deleted_at IS NULL AND p.deleted_at IS NULL
AND ps.name NOT LIKE 'cancel%' ${memberClause}`,
memberParams
);
const [[nonCancelledActivities]] = await pool.query(
`SELECT COUNT(*) AS c FROM activities a
JOIN projects p ON p.id = a.project_id JOIN project_statuses ps ON ps.id = p.status_id
WHERE a.deleted_at IS NULL AND p.deleted_at IS NULL AND ps.name NOT LIKE 'cancel%' ${memberClause}`,
memberParams
);
const [[overdueActivities]] = await pool.query(
@@ -61,12 +69,30 @@ export async function summary(memberUserId) {
overdueProjects: overdueProjects.c,
dueSoonProjects: dueSoonProjects.c,
pendingActivities: pendingActivities.c,
nonCancelledActivities: nonCancelledActivities.c,
overdueActivities: overdueActivities.c,
todayActivities: todayActivities.c,
averageProgress: avgProgress.avg_pct === null ? 0 : Math.round(Number(avgProgress.avg_pct) * 100) / 100,
};
}
// Mismo criterio que el KPI pendingActivities de summary().
export async function pendingActivitiesList(memberUserId) {
const memberClause = memberUserId ? 'AND EXISTS (SELECT 1 FROM project_members pm WHERE pm.project_id = p.id AND pm.user_id = ?)' : '';
const [rows] = await pool.query(
`SELECT a.id, a.code, a.name, a.due_date, a.progress_pct, p.id AS project_id, p.code AS project_code,
p.name AS project_name, u.name AS responsible_name
FROM activities a JOIN activity_statuses st ON st.id = a.status_id
JOIN projects p ON p.id = a.project_id JOIN project_statuses ps ON ps.id = p.status_id
LEFT JOIN users u ON u.id = a.responsible_user_id
WHERE st.is_closed = 0 AND a.deleted_at IS NULL AND p.deleted_at IS NULL
AND ps.name NOT LIKE 'cancel%' ${memberClause}
ORDER BY (a.due_date IS NULL), a.due_date, p.code, a.code`,
memberUserId ? [memberUserId] : []
);
return rows;
}
// Mismo criterio que el KPI activeProjects de summary().
export async function activeProjectsList(memberUserId) {
const memberClause = memberUserId ? 'AND EXISTS (SELECT 1 FROM project_members pm WHERE pm.project_id = p.id AND pm.user_id = ?)' : '';