feat: version 2.11.0 -- contadores de dependencias/comentarios/documentos/miembros en listados
Actividades (dentro de un proyecto): nuevas columnas con la cantidad de dependencias, comentarios y documentos de cada fila -- subconsultas correlacionadas agregadas al LIST_SELECT existente (mismo patron que share_count de notas/proyectos), siempre numericas (0 si no hay, nunca oculto). Proyectos: nuevas columnas con la cantidad de miembros, actividades completas al 100% sobre el total (ej. "1/4", mismo patron pendientes/total que ya usa la pestaña de tareas de una actividad), comentarios y documentos. Nuevo componente CountBadge (icono + numero) reutilizado en ambos listados -- a diferencia de SharedWithIcon/Tabs que ocultan el badge en 0, este siempre lo muestra. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,12 @@ const LIST_SELECT = `
|
||||
p.responsible_department_id, sd.name AS responsible_department_name,
|
||||
p.start_date, p.estimated_end_date, p.actual_end_date, p.progress_pct,
|
||||
p.risk_level, p.notes, p.created_by, p.created_at, p.updated_at, p.deleted_at, p.deleted_by,
|
||||
(SELECT COUNT(*) FROM project_members pm WHERE pm.project_id = p.id AND pm.user_id <> p.owner_user_id) AS shared_count
|
||||
(SELECT COUNT(*) FROM project_members pm WHERE pm.project_id = p.id AND pm.user_id <> p.owner_user_id) AS shared_count,
|
||||
(SELECT COUNT(*) FROM project_members pm2 WHERE pm2.project_id = p.id) AS members_count,
|
||||
(SELECT COUNT(*) FROM activities act WHERE act.project_id = p.id AND act.deleted_at IS NULL) AS activities_total,
|
||||
(SELECT COUNT(*) FROM activities act2 WHERE act2.project_id = p.id AND act2.deleted_at IS NULL AND act2.progress_pct >= 100) AS activities_complete,
|
||||
(SELECT COUNT(*) FROM comments c WHERE c.entity_type = 'project' AND c.entity_id = p.id) AS comments_count,
|
||||
(SELECT COUNT(*) FROM attachments att WHERE att.entity_type = 'project' AND att.entity_id = p.id) AS documents_count
|
||||
FROM projects p
|
||||
JOIN priorities pr ON pr.id = p.priority_id
|
||||
JOIN project_statuses ps ON ps.id = p.status_id
|
||||
@@ -19,6 +24,20 @@ const LIST_SELECT = `
|
||||
LEFT JOIN departments sd ON sd.id = p.responsible_department_id
|
||||
`;
|
||||
|
||||
// Los COUNT(*) de subconsultas correlacionadas vuelven de mysql2 como string/BigInt-like --
|
||||
// se castean a Number aqui para que el frontend los reciba como numero real (orden, comparar).
|
||||
function castCounts(row) {
|
||||
return {
|
||||
...row,
|
||||
shared_count: Number(row.shared_count),
|
||||
members_count: Number(row.members_count),
|
||||
activities_total: Number(row.activities_total),
|
||||
activities_complete: Number(row.activities_complete),
|
||||
comments_count: Number(row.comments_count),
|
||||
documents_count: Number(row.documents_count),
|
||||
};
|
||||
}
|
||||
|
||||
export async function findAll({ statusId, priorityId, ownerUserId, memberUserId, trash } = {}) {
|
||||
const clauses = [trash ? 'p.deleted_at IS NOT NULL' : 'p.deleted_at IS NULL'];
|
||||
const params = [];
|
||||
@@ -33,13 +52,13 @@ export async function findAll({ statusId, priorityId, ownerUserId, memberUserId,
|
||||
}
|
||||
const where = `WHERE ${clauses.join(' AND ')}`;
|
||||
const [rows] = await pool.query(`${LIST_SELECT} ${where} ORDER BY p.created_at DESC`, params);
|
||||
return rows;
|
||||
return rows.map(castCounts);
|
||||
}
|
||||
|
||||
export async function findById(id, { includeDeleted = false } = {}) {
|
||||
const where = includeDeleted ? 'p.id = ?' : 'p.id = ? AND p.deleted_at IS NULL';
|
||||
const [rows] = await pool.query(`${LIST_SELECT} WHERE ${where}`, [id]);
|
||||
return rows[0] || null;
|
||||
return rows[0] ? castCounts(rows[0]) : null;
|
||||
}
|
||||
|
||||
export async function softDelete(id, userId) {
|
||||
|
||||
Reference in New Issue
Block a user