From 878f383f7af4b11b3c44b179f923d0fcd06b5d06 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Wed, 17 Jun 2026 09:47:24 +0100 Subject: [PATCH] workign comments strucuture --- .../TicketCommentStoreController.php | 60 +++++++++---- Modules/Helpdesk/Livewire/SettingsManager.php | 27 ++++++ .../views/livewire/settings-manager.blade.php | 4 +- .../views/livewire/ticket-show.blade.php | 86 ++++++++++++++++--- .../views/partials/comment-thread.blade.php | 33 ++++--- .../Browser/HelpdeskTicketBrowserTest.php | 72 ++++++++++++---- Modules/Helpdesk/changelog.yaml | 10 +-- 7 files changed, 222 insertions(+), 70 deletions(-) diff --git a/Modules/Helpdesk/Http/Controllers/TicketCommentStoreController.php b/Modules/Helpdesk/Http/Controllers/TicketCommentStoreController.php index 4aabe880..54f6c886 100644 --- a/Modules/Helpdesk/Http/Controllers/TicketCommentStoreController.php +++ b/Modules/Helpdesk/Http/Controllers/TicketCommentStoreController.php @@ -4,54 +4,76 @@ declare(strict_types=1); namespace Modules\Helpdesk\Http\Controllers; +use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; -use Illuminate\Validation\ValidationException; +use Illuminate\Validation\Rule; +use Modules\Helpdesk\Models\CannedResponse; use Modules\Helpdesk\Models\Ticket; +use Modules\Helpdesk\Services\CannedResponseRenderer; use Modules\Helpdesk\Services\TicketService; final class TicketCommentStoreController extends Controller { /** - * Store a ticket comment using a normal HTTP POST request. + * Store a ticket conversation comment or nested reply using a normal HTTP POST. + * + * This keeps the conversation workflow reliable when rich-editor, Flux, or + * Livewire JavaScript event handling does not invoke the Livewire method. */ - public function __invoke(Request $request, Ticket $ticket, TicketService $tickets): RedirectResponse + public function __invoke(Request $request, Ticket $ticket, TicketService $tickets, CannedResponseRenderer $renderer): RedirectResponse { Gate::authorize('comment', $ticket); + $maxUploadKilobytes = (int) config('helpdesk.max_upload_kb', 10240); + $allowedMimes = (array) config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip']); + $validated = $request->validate([ - 'commentBody' => ['required', 'string', 'min:2'], - 'attachments' => ['nullable', 'array'], - 'attachments.*' => [ - 'file', - 'max:'.config('helpdesk.max_upload_kb', 10240), - 'mimes:'.implode(',', config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip'])), + 'commentBody' => ['nullable', 'string'], + 'internal' => ['nullable', 'boolean'], + 'parent_id' => [ + 'nullable', + 'integer', + Rule::exists('helpdesk_comments', 'id')->where('ticket_id', $ticket->id), ], + 'canned_response_id' => ['nullable', 'integer', Rule::exists('helpdesk_canned_responses', 'id')], + 'attachments' => ['nullable', 'array'], + 'attachments.*' => ['file', 'max:'.$maxUploadKilobytes, 'mimes:'.implode(',', $allowedMimes)], ]); $user = Auth::user(); + abort_if($user === null, 403); - if ($user === null) { - throw ValidationException::withMessages([ - 'commentBody' => 'You must be signed in to comment on this ticket.', - ]); + $body = trim((string) ($validated['commentBody'] ?? '')); + + if ($body === '' && filled($validated['canned_response_id'] ?? null)) { + $response = CannedResponse::query() + ->where('is_active', true) + ->findOrFail((int) $validated['canned_response_id']); + + $body = trim($renderer->render($response->body, $ticket, $user)); } - $internal = $request->user()?->can('update', $ticket) === true && $request->boolean('internal'); + validator(['commentBody' => $body], [ + 'commentBody' => ['required', 'string', 'min:2'], + ])->validate(); + + $uploadedFiles = $request->file('attachments', []); + $uploadedFiles = is_array($uploadedFiles) ? $uploadedFiles : [$uploadedFiles]; $tickets->comment( ticket: $ticket, user: $user, - body: (string) $validated['commentBody'], - internal: $internal, - files: $request->file('attachments', []), + body: $body, + internal: (bool) ($validated['internal'] ?? false), + files: $uploadedFiles, + parentId: filled($validated['parent_id'] ?? null) ? (int) $validated['parent_id'] : null, ); return redirect() ->route('helpdesk.tickets.show', $ticket) - ->with('ticket-comment-added', 'Reply posted.'); + ->with('ticket-comment-added', filled($validated['parent_id'] ?? null) ? 'Reply posted.' : 'Comment posted.'); } } diff --git a/Modules/Helpdesk/Livewire/SettingsManager.php b/Modules/Helpdesk/Livewire/SettingsManager.php index ddc92a3e..05bef509 100644 --- a/Modules/Helpdesk/Livewire/SettingsManager.php +++ b/Modules/Helpdesk/Livewire/SettingsManager.php @@ -75,6 +75,7 @@ final class SettingsManager extends Component public function mount(): void { + $this->section = $this->validSection((string) request()->query('section', $this->section)); $this->loadDefaultHelpdeskCompany(); $this->statusForm += ['id' => null, 'name' => '', 'slug' => '', 'color' => 'zinc', 'sort_order' => 0, 'is_closed' => false, 'is_default' => false]; $this->priorityForm += ['id' => null, 'name' => '', 'slug' => '', 'color' => 'zinc', 'sort_order' => 0, 'is_default' => false]; @@ -92,6 +93,32 @@ final class SettingsManager extends Component $this->checklistTemplateForm += ['id' => null, 'company_id' => null, 'category_id' => null, 'project_id' => null, 'name' => '', 'slug' => '', 'items_text' => '', 'is_active' => true]; } + /** + * Keep settings section navigation constrained to known panels. + */ + private function validSection(string $section): string + { + $allowed = [ + 'statuses', + 'priorities', + 'categories', + 'teams', + 'projects', + 'boards', + 'git', + 'workflow', + 'canned', + 'sla', + 'automation', + 'checklists', + 'tags', + 'products', + 'defaults', + ]; + + return in_array($section, $allowed, true) ? $section : 'statuses'; + } + public function saveStatus(): void { $data = $this->validate([ diff --git a/Modules/Helpdesk/Resources/views/livewire/settings-manager.blade.php b/Modules/Helpdesk/Resources/views/livewire/settings-manager.blade.php index 5c2efa34..99421e0f 100644 --- a/Modules/Helpdesk/Resources/views/livewire/settings-manager.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/settings-manager.blade.php @@ -231,10 +231,10 @@ ? 'border-color: '.$sectionPalette['border'].'; background-color: '.$sectionPalette['active'].'; color: #ffffff; box-shadow: 0 16px 36px rgba(0,0,0,.32);' : 'border-color: '.$sectionPalette['border'].'; background-color: '.$sectionPalette['bg'].'; color: #ffffff; box-shadow: 0 10px 24px rgba(0,0,0,.20);'; @endphp - + @endforeach diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php index 98c7b2f2..e6660ff6 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php @@ -74,13 +74,13 @@

Conversation

@if ($ticket->comments->isNotEmpty()) - @include('helpdesk::partials.comment-thread', ['comments' => $ticket->comments, 'level' => 0]) + @include('helpdesk::partials.comment-thread', ['comments' => $ticket->comments, 'level' => 0, 'ticket' => $ticket]) @else
No responses yet.
@endif -
+ @csrf

Add reply

@if (session('ticket-comment-added')) @@ -88,26 +88,42 @@ {{ session('ticket-comment-added') }} @endif + @if ($errors->any()) +
+ {{ $errors->first() }} +
+ @endif + @if ($cannedResponses->isNotEmpty())
- - - @foreach ($cannedResponses as $response) - - @endforeach - +
+ + +
- Insert response +
@endif - +
+ + +
@can('update', $ticket) -
- -
+ @endcan
@@ -119,10 +135,52 @@
+ +