function ($query) { $query ->withCount(['notes' => function ($query) { $query->where('notable_type', 'App\Models\PipelineItem'); }]) ->withCount(['todos' => function ($query) { $query->where('todoable_type', 'App\Models\PipelineItem') ->whereNotIn('status', ['completed', 'COMPLETED']); }]) ->with(['todos' => function ($query) { $query->where('todoable_type', 'App\Models\PipelineItem')->orderBy('due_date', 'asc'); }]) ->orderBy('position') ; }]) ->orderBy('position') ->get(); $lanesArray = $lanes->map(function ($lane) { $laneArray = $lane->toArray(); // Process items to add latest_todo_due_date and remove todos array $laneArray['items'] = collect($laneArray['items'])->map(function ($item) { // Find the first todo with status != 'COMPLETED' $nextTodo = collect($item['todos'])->first(function ($todo) { return strtolower($todo['status'] ?? '') !== 'completed'; }); $item['next_todo_due_date'] = $nextTodo['due_date'] ?? null; unset($item['todos']); // Remove the todos array return $item; })->toArray(); return $laneArray; }); return ApiDataTransformer::snakeToCamel($lanesArray->toArray()); } public function show() { return Inertia::render('Pipeline', [ 'pipeline' => $this->index(), ]); } /** * Update positions in bulk. Expect array of objects with camelCase keys: id, stage, position */ public function updatePositions(Request $request) { $payload = $request->all(); if (!is_array($payload)) { return response()->json(['message' => 'Invalid payload'], 422); } $items = array_map(function ($item) { return ApiDataTransformer::camelToSnake((array)$item); }, $payload); $validator = Validator::make(['items' => $items], [ 'items.*.id' => 'required|integer|exists:pipeline_items,id', 'items.*.pipeline_lane_id' => 'required|integer|exists:pipeline_lanes,id', 'items.*.title' => 'string', 'items.*.position' => 'required|integer|min:0', 'items.*.expected_revenue' => 'decimal', 'items.*.description' => 'string', ]); if ($validator->fails()) { return response()->json(['errors' => $validator->errors()], 422); } DB::beginTransaction(); try { foreach ($items as $it) { PipelineItem::where('id', $it['id'])->update([ 'pipeline_lane_id' => $it['pipeline_lane_id'], 'position' => $it['position'], ]); } DB::commit(); } catch (\Exception $e) { DB::rollBack(); Log::error('Failed to update pipeline positions: ' . $e->getMessage()); return response()->json(['message' => 'Failed to update positions'], 500); } return response()->json(['status' => 'ok']); } }