board_id = KanbanBoardModel::query() ->where('is_default', true) ->orderBy('sort_order') ->value('id') ?: KanbanBoardModel::query()->orderBy('sort_order')->value('id'); } /** * Move a ticket to another workflow status. */ public function moveTicket(int|string $ticketId, int|string $statusId, WorkflowNotificationService $workflow): void { $ticket = Ticket::query()->visibleTo(Auth::user())->findOrFail((int) $ticketId); $status = Status::query()->findOrFail((int) $statusId); $this->authorize('update', $ticket); $ticket->forceFill([ 'status_id' => $status->id, 'completed_at' => $status->is_closed ? now() : null, 'last_activity_at' => now(), ])->save(); $workflow->dispatch( $ticket->fresh(['owner', 'agent', 'status', 'priority', 'category', 'project', 'team']) ?? $ticket, $status->is_closed ? 'ticket.closed' : 'ticket.updated', Auth::user() ); session()->flash('success', 'Ticket moved to ' . $status->name . '.'); } /** * Render the Kanban board. */ public function render(): View { $boards = KanbanBoardModel::query() ->with('statuses') ->orderBy('sort_order') ->orderBy('name') ->get(); $board = $boards->firstWhere('id', $this->board_id) ?? $boards->first(); $statuses = $board?->statuses ?? collect(); if ($statuses->isEmpty()) { $statuses = Status::query()->orderBy('sort_order')->orderBy('name')->get(); } return view('helpdesk::livewire.kanban-board', [ 'boards' => $boards, 'board' => $board, 'statuses' => $statuses, 'ticketsByStatus' => $this->ticketsByStatus(), ])->layout('layouts.app'); } /** * Get visible tickets grouped by status. * * @return array> */ private function ticketsByStatus(): array { return Ticket::query() ->with(['owner', 'agent', 'status', 'priority', 'category', 'project', 'team']) ->visibleTo(Auth::user()) ->latest('last_activity_at') ->limit(300) ->get() ->groupBy('status_id') ->all(); } }