Gestionar Dependencias
Exportar contenido
Establece relaciones entre tareas para definir orden de ejecución.
Tipos de Dependencia
Sección titulada «Tipos de Dependencia»| Tipo | Descripción | Uso Común |
|---|---|---|
| Fin a Inicio (Finish-to-Start) | La tarea B solo puede iniciar cuando termine A | Secuencia estándar de tareas |
| Inicio a Inicio (Start-to-Start) | Las tareas A y B deben iniciar juntas | Tareas paralelas coordinadas |
| Fin a Fin (Finish-to-Finish) | Las tareas A y B deben finalizar juntas | Tareas con entregables relacionados |
| Inicio a Fin (Start-to-Finish) | La tarea B solo puede terminar cuando inicie A | Transiciones de fase |
Funcionalidad
Sección titulada «Funcionalidad»Agregar Dependencia:
- Seleccionar tarea de la cual depende
- Elegir tipo de dependencia
- Click en ”+” para agregar
Validaciones:
- Prevención de ciclos: No permite dependencias circulares
- Mismo proyecto: Solo tareas del mismo proyecto
- Excluir subtareas: No puede depender de sus propias subtareas
- Sin duplicados: No permite agregar misma dependencia dos veces
Indicadores Visuales:
- Estado de la tarea dependiente (completada/pendiente)
- Color según estado
- Alerta si hay dependencias incompletas
Restricciones
Sección titulada «Restricciones»Una tarea con dependencias incompletas:
- Muestra advertencia en color rojo
- Puede marcarse como completada (no bloqueante, solo informativo)
- Indica claramente qué tareas faltan
Detalles Técnicos
Componente
Sección titulada «Componente»- Archivo:
TaskDependencyManager.tsx - Ruta:
src/features/projects/sub-modules/tasks/components/TaskDependencyManager.tsx
type DependencyType = 'finish_to_start' | 'start_to_start' | 'finish_to_finish' | 'start_to_finish';
interface TaskDependency { id: string; task_id: string; depends_on_task_id: string; dependency_type: DependencyType; created_at: string; updated_at: string; // Relación depends_on_task?: Task;}Query Tareas Disponibles
Sección titulada «Query Tareas Disponibles»const { data: availableTasks } = useQuery({ queryKey: ["available-tasks", projectId, taskId], queryFn: async () => { if (!projectId) return [];
const { data, error } = await supabase .from("tasks") .select("id, title, status, parent_task_id") .eq("project_id", projectId) .neq("id", taskId) // Excluir tarea actual .neq("parent_task_id", taskId) // Excluir subtareas .order("title");
return data; }});Detección de Ciclos Circulares
Sección titulada «Detección de Ciclos Circulares»const checkCircularDependency = async (targetTaskId: string): Promise<boolean> => { const visited = new Set<string>();
const checkPath = async (currentTaskId: string): Promise<boolean> => { // Si llegamos a la tarea original, hay ciclo if (currentTaskId === taskId) return true; if (visited.has(currentTaskId)) return false; visited.add(currentTaskId);
// Obtener dependencias de la tarea actual const { data } = await supabase .from("task_dependencies") .select("depends_on_task_id") .eq("task_id", currentTaskId);
if (!data) return false;
// Revisar recursivamente cada dependencia for (const dep of data) { if (await checkPath(dep.depends_on_task_id)) return true; } return false; };
return await checkPath(targetTaskId);};Mutación Agregar Dependencia
Sección titulada «Mutación Agregar Dependencia»const addDependency = useMutation({ mutationFn: async () => { if (!selectedTaskId) throw new Error("Selecciona una tarea");
// Validar ciclos const isCircular = await checkCircularDependency(selectedTaskId); if (isCircular) { throw new Error("Esta dependencia crearía un ciclo circular"); }
// Insertar dependencia const { error } = await supabase .from("task_dependencies") .insert({ task_id: taskId, depends_on_task_id: selectedTaskId, dependency_type: dependencyType, });
if (error) throw error; }, onSuccess: () => { toast({ title: "Dependencia agregada" }); refetchDependencies(); }});Query Dependencias Existentes
Sección titulada «Query Dependencias Existentes»const { data: dependencies } = useQuery({ queryKey: ["task-dependencies", taskId], queryFn: async () => { const { data, error } = await supabase .from("task_dependencies") .select(` *, depends_on_task:tasks!task_dependencies_depends_on_task_id_fkey( id, title, status ) `) .eq("task_id", taskId);
if (error) throw error; return data as TaskDependency[]; },});Etiquetas de Tipos
Sección titulada «Etiquetas de Tipos»const getDependencyTypeLabel = (type: DependencyType) => { const labels: Record<DependencyType, string> = { finish_to_start: "Fin a Inicio", start_to_start: "Inicio a Inicio", finish_to_finish: "Fin a Fin", start_to_finish: "Inicio a Fin", }; return labels[type];};Colores de Estado
Sección titulada «Colores de Estado»const getStatusColor = (status: string) => { const colors: Record<string, string> = { pending: "bg-yellow-500", in_progress: "bg-blue-500", completed: "bg-green-500", cancelled: "bg-gray-500", on_hold: "bg-orange-500", }; return colors[status] || "bg-gray-500";};Tabla: task_dependencies
Sección titulada «Tabla: task_dependencies»CREATE TABLE task_dependencies ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, depends_on_task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, dependency_type VARCHAR(20) NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT no_self_dependency CHECK (task_id != depends_on_task_id), UNIQUE(task_id, depends_on_task_id));Alerta de Dependencias Incompletas
Sección titulada «Alerta de Dependencias Incompletas»{dependencies.some(d => d.depends_on_task?.status !== 'completed') && ( <Alert variant="destructive"> <AlertTriangle className="h-4 w-4" /> <AlertDescription> Esta tarea tiene dependencias incompletas. No podrá marcarse como completada hasta que todas las tareas dependientes estén finalizadas. </AlertDescription> </Alert>)}