select('id', 'ulid', 'name', 'status', 'start_date', 'end_date', 'priority') ->orderBy('start_date') ->get(); $dependencies = TaskDependency::whereIn('task_id', $tasks->pluck('id')) ->get() ->groupBy('task_id'); $ganttTasks = $tasks->map(function (Task $task) use ($dependencies) { $deps = $dependencies->get($task->id, collect()); $depStrings = $deps->map(fn($d) => "task-{$d->predecessor_id}")->implode(', '); return [ 'id' => "task-{$task->id}", 'name' => $task->name, 'start' => $task->start_date?->format('Y-m-d') ?? now()->format('Y-m-d'), 'end' => $task->end_date?->format('Y-m-d') ?? now()->addDays(7)->format('Y-m-d'), 'progress' => $this->statusToProgress($task->status), 'dependencies' => $depStrings ?: null, 'custom_class' => $this->statusToClass($task->status), 'meta' => [ 'task_id' => $task->id, 'status' => $task->status, 'priority' => $task->priority, ], ]; }); return $ganttTasks->values()->toArray(); } private function statusToProgress(string $status): int { return match ($status) { 'completed' => 100, 'in_progress' => 50, 'on_hold' => 25, default => 0, }; } private function statusToClass(string $status): string { return match ($status) { 'completed' => 'bar-completed', 'in_progress' => 'bar-in-progress', 'on_hold' => 'bar-on-hold', default => 'bar-pending', }; } }