106 lines
4.1 KiB
PHP
106 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Modules\Helpdesk\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Gate;
|
|
use Illuminate\Validation\Rule;
|
|
use Modules\Helpdesk\Models\KanbanBoard;
|
|
use Modules\Helpdesk\Models\Status;
|
|
use Modules\Helpdesk\Models\Ticket;
|
|
use Modules\Helpdesk\Services\WorkflowNotificationService;
|
|
|
|
final class TicketBoardController extends Controller
|
|
{
|
|
public function __invoke(Request $request, Ticket $ticket, WorkflowNotificationService $workflow): RedirectResponse
|
|
{
|
|
Gate::authorize('update', $ticket);
|
|
|
|
$validated = $request->validate([
|
|
'board_id' => ['required', 'integer', Rule::exists('helpdesk_kanban_boards', 'id')],
|
|
'return_to_board_id' => ['nullable', 'integer', Rule::exists('helpdesk_kanban_boards', 'id')],
|
|
]);
|
|
|
|
$targetBoardId = (int) $validated['board_id'];
|
|
|
|
$targetBoard = KanbanBoard::query()
|
|
->with(['statuses' => fn ($query) => $query->orderBy('helpdesk_kanban_board_statuses.sort_order')->orderBy('helpdesk_statuses.sort_order')->orderBy('helpdesk_statuses.name')])
|
|
->findOrFail($targetBoardId);
|
|
|
|
if ($targetBoard->statuses->isEmpty()) {
|
|
$this->ensureBoardHasStatuses($targetBoardId);
|
|
$targetBoard->load(['statuses' => fn ($query) => $query->orderBy('helpdesk_kanban_board_statuses.sort_order')->orderBy('helpdesk_statuses.sort_order')->orderBy('helpdesk_statuses.name')]);
|
|
}
|
|
|
|
$fresh = DB::transaction(function () use ($ticket, $targetBoard): Ticket {
|
|
$lockedTicket = Ticket::query()
|
|
->visibleTo(Auth::user())
|
|
->lockForUpdate()
|
|
->findOrFail($ticket->id);
|
|
|
|
$targetStatus = $targetBoard->statuses->firstWhere('id', (int) $lockedTicket->status_id)
|
|
?: $targetBoard->statuses->first();
|
|
|
|
if ($targetStatus === null) {
|
|
$targetStatus = Status::query()->orderBy('sort_order')->orderBy('name')->firstOrFail();
|
|
$this->addStatusToBoard($targetBoard->id, $targetStatus->id);
|
|
}
|
|
|
|
$updates = [
|
|
'board_id' => $targetBoard->id,
|
|
'status_id' => $targetStatus->id,
|
|
'completed_at' => $targetStatus->is_closed ? now() : null,
|
|
'last_activity_at' => now(),
|
|
];
|
|
|
|
if (DB::getSchemaBuilder()->hasColumn('helpdesk_tickets', 'updated_at')) {
|
|
$updates['updated_at'] = now();
|
|
}
|
|
|
|
$affected = DB::table('helpdesk_tickets')
|
|
->where('id', $lockedTicket->id)
|
|
->update($updates);
|
|
|
|
if ($affected < 1) {
|
|
throw new \RuntimeException('Ticket board update did not persist.');
|
|
}
|
|
|
|
return Ticket::query()
|
|
->with(['status', 'priority', 'category', 'project', 'team', 'product', 'tags', 'checklistItems', 'board'])
|
|
->findOrFail($lockedTicket->id);
|
|
});
|
|
|
|
$workflow->dispatch($fresh, 'ticket.updated', Auth::user());
|
|
|
|
return redirect()
|
|
->route('helpdesk.kanban', ['board_id' => $targetBoardId])
|
|
->with('success', 'Ticket '.$fresh->reference.' moved to '.$targetBoard->name.'.');
|
|
}
|
|
|
|
private function ensureBoardHasStatuses(int $boardId): void
|
|
{
|
|
Status::query()
|
|
->orderBy('sort_order')
|
|
->orderBy('name')
|
|
->get(['id'])
|
|
->values()
|
|
->each(function (Status $status, int $index) use ($boardId): void {
|
|
$this->addStatusToBoard($boardId, (int) $status->id, ($index + 1) * 10);
|
|
});
|
|
}
|
|
|
|
private function addStatusToBoard(int $boardId, int $statusId, int $sortOrder = 999): void
|
|
{
|
|
DB::table('helpdesk_kanban_board_statuses')->updateOrInsert(
|
|
['board_id' => $boardId, 'status_id' => $statusId],
|
|
['sort_order' => $sortOrder, 'updated_at' => now(), 'created_at' => now()],
|
|
);
|
|
}
|
|
}
|