Diagrama de Gantt

{{ $project->nombre_proyecto }}

Fecha: {{ $fechaActual }}

𐃊 Barras sólidas = fechas planificadas | Barras delgadas = progreso real basado en evidencias | 𐄴 Punto rojo = retraso | 𐏢 Punto verde = adelanto

@php use Carbon\Carbon; // Obtener el rango total del proyecto $primerDia = null; $ultimoDia = null; foreach ($project->sprints as $s) { $inicio = Carbon::parse($s->fecha_inicio)->startOfDay(); $fin = Carbon::parse($s->fecha_fin)->startOfDay(); if (!$primerDia || $inicio < $primerDia) { $primerDia = $inicio; } if (!$ultimoDia || $fin > $ultimoDia) { $ultimoDia = $fin; } } // Rango total del proyecto $totalDias = $primerDia && $ultimoDia ? $primerDia->diffInDays($ultimoDia) + 1 : 1; // Generar columnas de tiempo (por semanas) $semanas = []; $currentDate = $primerDia->copy(); while ($currentDate <= $ultimoDia) { $semanas[] = $currentDate->copy(); $currentDate->addWeek(); } @endphp @foreach ($project->sprints as $sprint)

{{ $sprint->nombre }}

Duración: {{ Carbon::parse($sprint->fecha_inicio)->format('d/m/Y') }} - {{ Carbon::parse($sprint->fecha_fin)->format('d/m/Y') }}

@if ($sprint->tasks->isNotEmpty()) @foreach ($sprint->tasks as $task) @php // Filtrar tareas canceladas $estado = strtolower($task->estado); if ($estado === 'cancelado') { continue; } $inicio = Carbon::parse($task->fecha_inicio_asignada)->startOfDay(); $fin = Carbon::parse($task->fecha_fin_asignada)->startOfDay(); $sprintInicio = Carbon::parse($sprint->fecha_inicio)->startOfDay(); $sprintFin = Carbon::parse($sprint->fecha_fin)->startOfDay(); $sprintDias = $sprintInicio->diffInDays($sprintFin) + 1; $offset = $sprintInicio->diffInDays($inicio); $duracion = max($inicio->diffInDays($fin) + 1, 1); $left = ($offset / $sprintDias) * 100; $width = ($duracion / $sprintDias) * 100; // Color según estado (reabierto y en revisión se consideran pendientes) $class = match ($estado) { 'completado' => 'done', 'en_revision' => 'pending', 'reabierto' => 'pending', 'pendiente' => 'pending', default => 'pending', }; @endphp @endforeach @else @endif
Tasks
@php $sprintInicio = Carbon::parse($sprint->fecha_inicio)->startOfDay(); $sprintFin = Carbon::parse($sprint->fecha_fin)->startOfDay(); $sprintDias = $sprintInicio->diffInDays($sprintFin) + 1; $currentDay = $sprintInicio->copy(); @endphp @for ($i = 0; $i < $sprintDias; $i++)
{{ $currentDay->locale('es')->isoFormat('ddd D') }}
@php $currentDay->addDay(); @endphp @endfor
{{ $task->nombre_tarea }}
@if($task->users->isNotEmpty()) @if($task->users->count() === 1) 👤 {{ $task->users->first()->name }} @else 👥 {{ $task->users->count() }} asignados: {{ $task->users->pluck('name')->join(', ') }} @endif @else ⚠️ Sin asignar @endif
@php // Calcular rango real basado en evidencias $tieneReal = false; $indicador = null; $inicioReal = null; $finReal = null; $mostrarPuntoInicio = false; $mostrarPuntoFin = false; if ($task->evidencias && $task->evidencias->isNotEmpty()) { // Obtener la fecha más temprana y más tardía de las evidencias $fechasEvidencias = $task->evidencias->pluck('fecha_subida')->sort(); $inicioRealOriginal = Carbon::parse($fechasEvidencias->first())->startOfDay(); $finRealOriginal = Carbon::parse($fechasEvidencias->last())->startOfDay(); $inicioReal = $inicioRealOriginal->copy(); $finReal = $finRealOriginal->copy(); $tieneReal = true; // Calcular offset y duración real $offsetReal = $sprintInicio->diffInDays($inicioReal); $duracionReal = max($inicioReal->diffInDays($finReal) + 1, 1); // Determinar si mostrar puntos de inicio/fin // Mostrar punto de inicio si la evidencia inició dentro o en el sprint if ($inicioReal >= $sprintInicio && $inicioReal <= $sprintFin) { $mostrarPuntoInicio = true; } // Mostrar punto de fin si la última evidencia está dentro del sprint if ($finReal >= $sprintInicio && $finReal <= $sprintFin) { $mostrarPuntoFin = true; } // Si la primera evidencia fue antes del sprint, ajustar if ($inicioReal < $sprintInicio) { $diasFueraSprint = $inicioReal->diffInDays($sprintInicio); $duracionReal = max($duracionReal - $diasFueraSprint, 1); $offsetReal = 0; } // Si la última evidencia fue después del sprint, ajustar if ($finReal > $sprintFin) { $diasDespuesSprint = $sprintFin->diffInDays($finReal); $duracionReal = max($duracionReal - $diasDespuesSprint, 1); } $leftReal = ($offsetReal / $sprintDias) * 100; $widthReal = ($duracionReal / $sprintDias) * 100; // Determinar si hubo retraso o adelanto comparando con fecha planificada if ($finRealOriginal > $fin) { $indicador = 'delay'; } elseif ($finRealOriginal < $fin && $estado === 'completado') { $indicador = 'early'; } } @endphp
{{ $duracion }}d @if ($indicador === 'delay') @elseif ($indicador === 'early') @endif
@if ($tieneReal)
@if ($mostrarPuntoInicio) @endif @if ($mostrarPuntoFin) @endif
@endif
No hay tareas registradas en este sprint.
Pendiente / Reabierto
En Revisión
Completado
💡 Nota: La barra delgada inferior representa el progreso real basado en las evidencias subidas (desde la primera hasta la última evidencia). La barra crece conforme se suben evidencias en diferentes días. Los puntos de colores indican adelantos (verde) o retrasos (rojo) respecto a lo planificado.
@endforeach