228 lines
6.2 KiB
TypeScript
228 lines
6.2 KiB
TypeScript
// lib/tasksService.ts
|
|
import { dolibarrFetch } from "./dolibarrClient";
|
|
import {
|
|
DolibarrTask,
|
|
Task,
|
|
mapDolibarrTask,
|
|
CreateTaskData,
|
|
UpdateTaskData
|
|
} from "@/types/task";
|
|
|
|
/**
|
|
* Obtener todas las tareas de un proyecto específico
|
|
*
|
|
* Nota: El endpoint /projects/{id}/tasks de Dolibarr no devuelve tareas correctamente,
|
|
* por lo que obtenemos todas las tareas y filtramos por fk_project en el cliente.
|
|
*/
|
|
export async function getTasksByProjectId(projectId: number): Promise<Task[]> {
|
|
try {
|
|
// Obtener todas las tareas (Dolibarr no filtra bien por proyecto)
|
|
const dolibarrTasks: DolibarrTask[] = await dolibarrFetch('tasks');
|
|
|
|
// Si no hay tareas, devolver array vacío
|
|
if (!dolibarrTasks || !Array.isArray(dolibarrTasks)) {
|
|
return [];
|
|
}
|
|
|
|
// Filtrar tareas que pertenecen a este proyecto
|
|
const projectTasks = dolibarrTasks.filter(
|
|
task => String(task.fk_project) === String(projectId)
|
|
);
|
|
|
|
// Mapear las tareas al formato de la UI
|
|
return projectTasks.map(mapDolibarrTask);
|
|
} catch (error) {
|
|
console.error('Error fetching tasks for project:', projectId, error);
|
|
// Si el error es 404 (no hay tareas), devolver array vacío
|
|
if (error instanceof Error && error.message.includes('404')) {
|
|
return [];
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtener todas las tareas (sin filtro de proyecto)
|
|
* Endpoint: /tasks
|
|
*/
|
|
export async function getAllTasks(): Promise<Task[]> {
|
|
try {
|
|
const dolibarrTasks: DolibarrTask[] = await dolibarrFetch('tasks');
|
|
|
|
if (!dolibarrTasks || !Array.isArray(dolibarrTasks)) {
|
|
return [];
|
|
}
|
|
|
|
return dolibarrTasks.map(mapDolibarrTask);
|
|
} catch (error) {
|
|
console.error('Error fetching all tasks:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtener una tarea específica por ID
|
|
* Endpoint: /tasks/{id}
|
|
*/
|
|
export async function getTaskById(taskId: number): Promise<Task | null> {
|
|
try {
|
|
const dolibarrTask: DolibarrTask = await dolibarrFetch(`tasks/${taskId}`);
|
|
return mapDolibarrTask(dolibarrTask);
|
|
} catch (error) {
|
|
console.error('Error fetching task:', taskId, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtener datos crudos de Dolibarr para una tarea
|
|
*/
|
|
export async function getDolibarrTaskById(taskId: number): Promise<DolibarrTask | null> {
|
|
try {
|
|
return await dolibarrFetch(`tasks/${taskId}`);
|
|
} catch (error) {
|
|
console.error('Error fetching Dolibarr task:', taskId, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calcular estadísticas de las tareas de un proyecto
|
|
*/
|
|
export function calculateTaskStats(tasks: Task[]) {
|
|
const total = tasks.length;
|
|
const completed = tasks.filter(t => t.status === '2').length;
|
|
const inProgress = tasks.filter(t => t.status === '1').length;
|
|
const draft = tasks.filter(t => t.status === '0').length;
|
|
|
|
const totalPlannedHours = tasks.reduce((sum, t) => sum + t.plannedHours, 0);
|
|
const totalWorkedHours = tasks.reduce((sum, t) => sum + t.workedHours, 0);
|
|
|
|
const avgProgress = total > 0
|
|
? Math.round(tasks.reduce((sum, t) => sum + t.progress, 0) / total)
|
|
: 0;
|
|
|
|
const highPriority = tasks.filter(t => t.priority === '3').length;
|
|
const overdue = tasks.filter(t => {
|
|
if (!t.endDate && !t.plannedEndDate) return false;
|
|
const endDate = t.endDate || t.plannedEndDate;
|
|
return endDate && new Date(endDate) < new Date() && t.status !== '2';
|
|
}).length;
|
|
|
|
return {
|
|
total,
|
|
completed,
|
|
inProgress,
|
|
draft,
|
|
totalPlannedHours,
|
|
totalWorkedHours,
|
|
avgProgress,
|
|
highPriority,
|
|
overdue,
|
|
completionRate: total > 0 ? Math.round((completed / total) * 100) : 0,
|
|
};
|
|
}
|
|
|
|
// ==================== FUNCIONES DE ESCRITURA (CREATE/UPDATE/DELETE) ====================
|
|
|
|
/**
|
|
* Crear una nueva tarea
|
|
* Endpoint: POST /tasks
|
|
* @returns La tarea creada con su ID
|
|
*/
|
|
export async function createTask(data: CreateTaskData): Promise<Task> {
|
|
try {
|
|
// Limpiar datos undefined/null para la API
|
|
const cleanData = Object.fromEntries(
|
|
Object.entries(data).filter(([, v]) => v !== undefined && v !== null && v !== '')
|
|
);
|
|
|
|
const response = await dolibarrFetch('tasks', {
|
|
method: 'POST',
|
|
body: JSON.stringify(cleanData),
|
|
});
|
|
|
|
// Dolibarr devuelve el ID de la tarea creada
|
|
const taskId = typeof response === 'object' ? response.id || response : response;
|
|
|
|
// Obtener la tarea completa
|
|
const createdTask = await getTaskById(Number(taskId));
|
|
if (!createdTask) {
|
|
throw new Error('No se pudo obtener la tarea creada');
|
|
}
|
|
|
|
return createdTask;
|
|
} catch (error) {
|
|
console.error('Error creating task:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Actualizar una tarea existente
|
|
* Endpoint: PUT /tasks/{id}
|
|
* @returns La tarea actualizada
|
|
*/
|
|
export async function updateTask(taskId: number, data: UpdateTaskData): Promise<Task> {
|
|
try {
|
|
// Limpiar datos undefined/null para la API
|
|
const cleanData = Object.fromEntries(
|
|
Object.entries(data).filter(([, v]) => v !== undefined && v !== null)
|
|
);
|
|
|
|
await dolibarrFetch(`tasks/${taskId}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(cleanData),
|
|
});
|
|
|
|
// Obtener la tarea actualizada
|
|
const updatedTask = await getTaskById(taskId);
|
|
if (!updatedTask) {
|
|
throw new Error('No se pudo obtener la tarea actualizada');
|
|
}
|
|
|
|
return updatedTask;
|
|
} catch (error) {
|
|
console.error('Error updating task:', taskId, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Eliminar una tarea
|
|
* Endpoint: DELETE /tasks/{id}
|
|
*/
|
|
export async function deleteTask(taskId: number): Promise<void> {
|
|
try {
|
|
await dolibarrFetch(`tasks/${taskId}`, {
|
|
method: 'DELETE',
|
|
});
|
|
} catch (error) {
|
|
console.error('Error deleting task:', taskId, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validar (cambiar estado a 1) una tarea
|
|
* Endpoint: POST /tasks/{id}/validate
|
|
*/
|
|
export async function validateTask(taskId: number): Promise<Task> {
|
|
try {
|
|
await dolibarrFetch(`tasks/${taskId}/validate`, {
|
|
method: 'POST',
|
|
body: JSON.stringify({ notrigger: 0 }),
|
|
});
|
|
|
|
const task = await getTaskById(taskId);
|
|
if (!task) {
|
|
throw new Error('No se pudo obtener la tarea validada');
|
|
}
|
|
|
|
return task;
|
|
} catch (error) {
|
|
console.error('Error validating task:', taskId, error);
|
|
throw error;
|
|
}
|
|
}
|