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>
This commit is contained in:
@@ -8,7 +8,7 @@ const LIST_SELECT = `
|
||||
p.requesting_department_id, rd.name AS requesting_department_name,
|
||||
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.risk_level, p.notes, p.created_by, p.created_at, p.updated_at, p.deleted_at, p.deleted_by
|
||||
FROM projects p
|
||||
JOIN priorities pr ON pr.id = p.priority_id
|
||||
JOIN project_statuses ps ON ps.id = p.status_id
|
||||
@@ -18,22 +18,35 @@ const LIST_SELECT = `
|
||||
LEFT JOIN departments sd ON sd.id = p.responsible_department_id
|
||||
`;
|
||||
|
||||
export async function findAll({ statusId, priorityId, ownerUserId } = {}) {
|
||||
const clauses = [];
|
||||
export async function findAll({ statusId, priorityId, ownerUserId, trash } = {}) {
|
||||
const clauses = [trash ? 'p.deleted_at IS NOT NULL' : 'p.deleted_at IS NULL'];
|
||||
const params = [];
|
||||
if (statusId) { clauses.push('p.status_id = ?'); params.push(statusId); }
|
||||
if (priorityId) { clauses.push('p.priority_id = ?'); params.push(priorityId); }
|
||||
if (ownerUserId) { clauses.push('p.owner_user_id = ?'); params.push(ownerUserId); }
|
||||
const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
|
||||
const where = `WHERE ${clauses.join(' AND ')}`;
|
||||
const [rows] = await pool.query(`${LIST_SELECT} ${where} ORDER BY p.created_at DESC`, params);
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function findById(id) {
|
||||
const [rows] = await pool.query(`${LIST_SELECT} WHERE p.id = ?`, [id]);
|
||||
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;
|
||||
}
|
||||
|
||||
export async function softDelete(id, userId) {
|
||||
await pool.query('UPDATE projects SET deleted_at = NOW(), deleted_by = ? WHERE id = ? AND deleted_at IS NULL', [userId, id]);
|
||||
}
|
||||
|
||||
export async function restore(id) {
|
||||
await pool.query('UPDATE projects SET deleted_at = NULL, deleted_by = NULL WHERE id = ?', [id]);
|
||||
}
|
||||
|
||||
export async function hardDelete(id) {
|
||||
await pool.query('DELETE FROM projects WHERE id = ?', [id]);
|
||||
}
|
||||
|
||||
export async function create(data) {
|
||||
const [result] = await pool.query(
|
||||
`INSERT INTO projects
|
||||
|
||||
Reference in New Issue
Block a user