abortUnlessCanManage($request, $project); return response()->json([ 'data' => $this->buildProjectBoardSettingsPayload->handle($project), ]); } public function storeColumn(StoreBoardColumnRequest $request, Project $project): JsonResponse { $this->abortUnlessCanManage($request, $project); $boardColumn = $this->storeBoardColumn->handle($project, $request->validated()); return BoardColumnResource::make($boardColumn->load('ticketStatuses')) ->additional([ 'meta' => [ 'message' => "{$boardColumn->name} was added to the board workflow.", ], ]) ->response() ->setStatusCode(201); } public function updateColumn( UpdateBoardColumnRequest $request, Project $project, BoardColumn $boardColumn, ): JsonResponse { $this->abortUnlessCanManage($request, $project); $boardColumn = $this->updateBoardColumn->handle($project, $boardColumn, $request->validated()); return BoardColumnResource::make($boardColumn->load('ticketStatuses')) ->additional([ 'meta' => [ 'message' => "{$boardColumn->name} was updated.", ], ]) ->response(); } public function destroyColumn(Request $request, Project $project, BoardColumn $boardColumn): JsonResponse { $this->abortUnlessCanManage($request, $project); $this->deleteBoardColumn->handle($project, $boardColumn); return response()->json(null, 204); } public function storeStatus(StoreTicketStatusRequest $request, Project $project): JsonResponse { $this->abortUnlessCanManage($request, $project); $ticketStatus = $this->storeTicketStatus->handle($project, $request->validated()); return TicketStatusResource::make($ticketStatus) ->additional([ 'meta' => [ 'message' => "{$ticketStatus->name} was added to {$this->statusContextLabel($ticketStatus)}.", ], ]) ->response() ->setStatusCode(201); } public function updateStatus( UpdateTicketStatusRequest $request, Project $project, TicketStatus $ticketStatus, ): JsonResponse { $this->abortUnlessCanManage($request, $project); $ticketStatus = $this->updateTicketStatus->handle($ticketStatus, $request->validated()); return TicketStatusResource::make($ticketStatus->load('boardColumn')) ->additional([ 'meta' => [ 'message' => "{$ticketStatus->name} was updated.", ], ]) ->response(); } public function destroyStatus(Request $request, Project $project, TicketStatus $ticketStatus): JsonResponse { $this->abortUnlessCanManage($request, $project); $this->deleteTicketStatus->handle($project, $ticketStatus); return response()->json(null, 204); } public function updateStatusTransitions( UpdateTicketStatusTransitionsRequest $request, Project $project, TicketStatus $ticketStatus, ): JsonResponse { $this->abortUnlessCanManage($request, $project); $ticketStatus = $this->updateTicketStatusTransitions->handle($ticketStatus, $request->validated()); return TicketStatusResource::make($ticketStatus->load(['allowedTargetStatuses.boardColumn', 'boardColumn'])) ->additional([ 'meta' => [ 'message' => "{$ticketStatus->name} move targets were updated.", ], ]) ->response(); } private function abortUnlessCanManage(Request $request, Project $project): void { abort_unless( $this->permissionService->allows($this->authenticatedUser($request), Permission::ProjectAdmin, $project), 403, ); } private function statusContextLabel(TicketStatus $ticketStatus): string { return $ticketStatus->boardColumn?->name ?? 'Backlog'; } }