1,
]);
}
+
+ public function test_gitea_local_webhook_tracks_latest_push_without_repository_id(): void
+ {
+ config()->set('services.gitea.webhook_authorization', 'YWxhZGRpbjpvcGVuc2VzYW1l');
+
+ $fixtures = $this->seedLookupData();
+ $repository = $this->repository([
+ 'provider' => 'gitea',
+ 'project_id' => $fixtures['project']->id,
+ 'remote_url' => 'http://git.vm:3000/Admin/WMS-Core.git',
+ 'webhook_secret' => 'gitea-secret',
+ ]);
+ $ticket = $this->ticket(['project_id' => $fixtures['project']->id]);
+ $payload = [
+ 'ref' => 'refs/heads/main',
+ 'repository' => [
+ 'name' => 'WMS-Core',
+ 'full_name' => 'Admin/WMS-Core',
+ 'clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git',
+ 'html_url' => 'http://git.vm:3000/Admin/WMS-Core',
+ ],
+ 'commits' => [
+ [
+ 'id' => str_repeat('d', 40),
+ 'message' => $ticket->reference . ' Fix local Gitea webhook ingestion',
+ 'url' => 'http://git.vm:3000/Admin/WMS-Core/commit/' . str_repeat('d', 40),
+ 'timestamp' => '2026-06-24T14:20:00+00:00',
+ 'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'],
+ ],
+ ],
+ ];
+ $json = json_encode($payload, JSON_THROW_ON_ERROR);
+
+ $response = $this->call('POST', route('api.git.gitea.webhook'), [], [], [], [
+ 'CONTENT_TYPE' => 'application/json',
+ 'HTTP_ACCEPT' => 'application/json',
+ 'HTTP_AUTHORIZATION' => 'Basic YWxhZGRpbjpvcGVuc2VzYW1l',
+ 'HTTP_X_GITEA_EVENT' => 'push',
+ 'HTTP_X_GITEA_DELIVERY' => 'gitea-delivery-123',
+ 'HTTP_X_GITEA_SIGNATURE' => hash_hmac('sha256', $json, 'gitea-secret'),
+ ], $json);
+
+ $response->assertStatus(202)->assertJsonPath('status', 'accepted')->assertJsonPath('linked_count', 1);
+
+ $this->assertDatabaseHas('helpdesk_git_commits', [
+ 'repository_id' => $repository->id,
+ 'ticket_id' => $ticket->id,
+ 'hash' => str_repeat('d', 40),
+ 'branch' => 'main',
+ ]);
+
+ $this->assertDatabaseHas('helpdesk_git_webhook_logs', [
+ 'repository_id' => $repository->id,
+ 'provider' => 'gitea',
+ 'delivery_id' => 'gitea-delivery-123',
+ 'event' => 'push',
+ 'status' => 'accepted',
+ 'linked_count' => 1,
+ ]);
+ }
+
+ public function test_gitea_local_webhook_rejects_invalid_signature_and_authorization_without_creating_commits(): void
+ {
+ config()->set('services.gitea.webhook_authorization', 'YWxhZGRpbjpvcGVuc2VzYW1l');
+
+ $this->repository([
+ 'provider' => 'gitea',
+ 'remote_url' => 'http://git.vm:3000/Admin/WMS-Core.git',
+ 'webhook_secret' => 'gitea-secret',
+ ]);
+ $payload = [
+ 'ref' => 'refs/heads/main',
+ 'repository' => ['clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git'],
+ 'commits' => [['id' => str_repeat('e', 40), 'message' => 'No auth']],
+ ];
+ $json = json_encode($payload, JSON_THROW_ON_ERROR);
+
+ $response = $this->call('POST', route('api.git.gitea.webhook'), [], [], [], [
+ 'CONTENT_TYPE' => 'application/json',
+ 'HTTP_ACCEPT' => 'application/json',
+ 'HTTP_AUTHORIZATION' => 'Basic invalid',
+ 'HTTP_X_GITEA_EVENT' => 'push',
+ 'HTTP_X_GITEA_SIGNATURE' => hash_hmac('sha256', $json, 'wrong-secret'),
+ ], $json);
+
+ $response->assertForbidden()->assertJsonPath('status', 'rejected');
+
+ self::assertSame(0, GitCommit::query()->count());
+ }
+
+ public function test_gitea_local_webhook_accepts_valid_signature_when_optional_authorization_header_differs(): void
+ {
+ config()->set('services.gitea.webhook_authorization', 'expected-basic-token');
+
+ $repository = $this->repository([
+ 'provider' => 'gitea',
+ 'remote_url' => 'http://git.vm:3000/Admin/WMS-Core.git',
+ 'webhook_secret' => 'gitea-secret',
+ ]);
+ $payload = [
+ 'ref' => 'refs/heads/main',
+ 'repository' => ['clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git'],
+ 'commits' => [['id' => str_repeat('b', 40), 'message' => 'Valid signed Gitea push']],
+ ];
+ $json = json_encode($payload, JSON_THROW_ON_ERROR);
+
+ $response = $this->call('POST', route('api.git.gitea.webhook'), [], [], [], [
+ 'CONTENT_TYPE' => 'application/json',
+ 'HTTP_ACCEPT' => 'application/json',
+ 'HTTP_AUTHORIZATION' => 'Basic something-different-from-gitea-ui',
+ 'HTTP_X_GITEA_EVENT' => 'push',
+ 'HTTP_X_GITEA_SIGNATURE' => hash_hmac('sha256', $json, 'gitea-secret'),
+ ], $json);
+
+ $response->assertStatus(202)->assertJsonPath('status', 'accepted')->assertJsonPath('commit_count', 1);
+
+ $this->assertDatabaseHas('helpdesk_git_commits', [
+ 'repository_id' => $repository->id,
+ 'hash' => str_repeat('b', 40),
+ ]);
+ }
+
+ public function test_gitea_local_webhook_accepts_prefixed_signature_and_case_insensitive_provider(): void
+ {
+ config()->set('services.gitea.webhook_authorization', null);
+
+ $fixtures = $this->seedLookupData();
+ $repository = $this->repository([
+ 'provider' => 'Gitea',
+ 'project_id' => $fixtures['project']->id,
+ 'name' => 'Local',
+ 'remote_url' => 'http://git.vm:3000/Admin/WMS-Core',
+ 'webhook_secret' => 'gitea-secret',
+ ]);
+ $payload = [
+ 'ref' => 'refs/heads/main',
+ 'repository' => [
+ 'name' => 'WMS-Core',
+ 'full_name' => 'Admin/WMS-Core',
+ 'clone_url' => 'http://git.vm:3000/Admin/WMS-Core.git',
+ 'html_url' => 'http://git.vm:3000/Admin/WMS-Core',
+ ],
+ 'commits' => [
+ [
+ 'id' => str_repeat('f', 40),
+ 'message' => 'Local Gitea push should be visible on the dashboard',
+ 'url' => 'http://git.vm:3000/Admin/WMS-Core/commit/' . str_repeat('f', 40),
+ 'timestamp' => '2026-06-24T15:00:00+00:00',
+ 'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'],
+ ],
+ ],
+ ];
+ $json = json_encode($payload, JSON_THROW_ON_ERROR);
+
+ $response = $this->call('POST', route('api.git.gitea.webhook'), [], [], [], [
+ 'CONTENT_TYPE' => 'application/json',
+ 'HTTP_ACCEPT' => 'application/json',
+ 'HTTP_X_GITEA_EVENT' => 'push',
+ 'HTTP_X_GITEA_SIGNATURE' => 'sha256=' . hash_hmac('sha256', $json, 'gitea-secret'),
+ ], $json);
+
+ $response->assertStatus(202)->assertJsonPath('status', 'accepted')->assertJsonPath('commit_count', 1);
+
+ $this->assertDatabaseHas('helpdesk_git_commits', [
+ 'repository_id' => $repository->id,
+ 'hash' => str_repeat('f', 40),
+ 'branch' => 'main',
+ ]);
+ }
+
}
diff --git a/Modules/Helpdesk/Tests/Feature/TicketAttachmentExperienceFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketAttachmentExperienceFilesTest.php
new file mode 100644
index 00000000..16d5d4f9
--- /dev/null
+++ b/Modules/Helpdesk/Tests/Feature/TicketAttachmentExperienceFilesTest.php
@@ -0,0 +1,68 @@
+extension ?: pathinfo', $model);
+ self::assertStringNotContainsString('is_string($this->category)', $model);
+
+ self::assertIsString($service);
+ self::assertStringContainsString('checksum_sha256', $service);
+ self::assertStringContainsString('function softDelete', $service);
+
+ self::assertIsString($migration);
+ self::assertStringContainsString('download_count', $migration);
+ self::assertStringContainsString('deleted_at', $migration);
+ }
+}
diff --git a/Modules/Helpdesk/Tests/Feature/TicketBranchCommandUiFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketBranchCommandUiFilesTest.php
new file mode 100644
index 00000000..64774a8d
--- /dev/null
+++ b/Modules/Helpdesk/Tests/Feature/TicketBranchCommandUiFilesTest.php
@@ -0,0 +1,78 @@
+reference }}-', $view);
+ self::assertStringContainsString('data-helpdesk-branch-suffix', $view);
+ self::assertStringContainsString('data-helpdesk-copy-branch-command', $view);
+ self::assertStringContainsString('copyHelpdeskBranchCommand', $view);
+ self::assertStringContainsString('normaliseHelpdeskBranchSuffix', $view);
+ self::assertStringContainsString('Copied to clipboard.', $view);
+ }
+
+ /**
+ * Ensure visibility selectors keep their accent backgrounds during interaction.
+ */
+ public function test_visibility_controls_keep_accent_backgrounds_on_hover_and_focus(): void
+ {
+ $ticketViewPath = dirname(__DIR__, 2) . '/Resources/views/livewire/ticket-show.blade.php';
+ $threadViewPath = dirname(__DIR__, 2) . '/Resources/views/partials/comment-thread.blade.php';
+
+ $ticketView = file_get_contents($ticketViewPath);
+ $threadView = file_get_contents($threadViewPath);
+
+ self::assertIsString($ticketView);
+ self::assertIsString($threadView);
+ self::assertStringContainsString('hover:bg-emerald-100/80', $ticketView);
+ self::assertStringContainsString('focus-within:bg-emerald-100/80', $ticketView);
+ self::assertStringContainsString('hover:bg-purple-100/80', $ticketView);
+ self::assertStringContainsString('focus-within:bg-purple-100/80', $ticketView);
+ self::assertStringContainsString('hover:bg-emerald-100/80', $threadView);
+ self::assertStringContainsString('hover:bg-purple-100/80', $threadView);
+ }
+
+ /**
+ * Ensure ticket comments and threaded replies expose browser spelling hints.
+ */
+ public function test_ticket_comment_subjects_and_bodies_enable_spellcheck(): void
+ {
+ $ticketViewPath = dirname(__DIR__, 2) . '/Resources/views/livewire/ticket-show.blade.php';
+ $threadViewPath = dirname(__DIR__, 2) . '/Resources/views/partials/comment-thread.blade.php';
+
+ $ticketView = file_get_contents($ticketViewPath);
+ $threadView = file_get_contents($threadViewPath);
+
+ self::assertIsString($ticketView);
+ self::assertIsString($threadView);
+ self::assertStringContainsString('data-helpdesk-comment-subject', $ticketView);
+ self::assertStringContainsString('spellcheck="true" lang="en-GB" autocorrect="on" autocomplete="on" autocapitalize="sentences" inputmode="text"', $ticketView);
+ self::assertStringContainsString('role="textbox" aria-multiline="true" spellcheck="true"', $ticketView);
+ self::assertStringContainsString('id="reply-subject-{{ $comment->id }}"', $threadView);
+ self::assertStringContainsString('spellcheck="true" lang="en-GB" autocorrect="on" autocomplete="on" autocapitalize="sentences" inputmode="text"', $threadView);
+ }
+}
diff --git a/Modules/Helpdesk/Tests/Feature/TicketCreateAttachmentPersistenceTest.php b/Modules/Helpdesk/Tests/Feature/TicketCreateAttachmentPersistenceTest.php
new file mode 100644
index 00000000..0dfed0dc
--- /dev/null
+++ b/Modules/Helpdesk/Tests/Feature/TicketCreateAttachmentPersistenceTest.php
@@ -0,0 +1,43 @@
+assertIsString($view);
+ $this->assertStringContainsString('x-on:livewire-upload-start="helpdeskAttachmentsUploading = true"', $view);
+ $this->assertStringContainsString('x-on:livewire-upload-finish="helpdeskAttachmentsUploading = false"', $view);
+ $this->assertStringContainsString('x-on:livewire-upload-error="helpdeskAttachmentsUploading = false"', $view);
+ $this->assertStringContainsString('x-on:livewire-upload-cancel="helpdeskAttachmentsUploading = false"', $view);
+ $this->assertStringContainsString('wire:target="save,attachments"', $view);
+ $this->assertStringContainsString('x-bind:disabled="helpdeskAttachmentsUploading"', $view);
+ $this->assertStringContainsString('$event.preventDefault();', $view);
+ }
+
+ /**
+ * The shared attachment service must explicitly support Livewire temporary
+ * uploads as well as controller UploadedFile instances.
+ */
+ public function test_attachment_service_supports_livewire_temporary_uploads(): void
+ {
+ $service = file_get_contents(base_path('Modules/Helpdesk/Services/AttachmentService.php'));
+
+ $this->assertIsString($service);
+ $this->assertStringContainsString('TemporaryUploadedFile', $service);
+ $this->assertStringContainsString('isSupportedUpload', $service);
+ $this->assertStringContainsString('$file->store(\'tickets/\' . $ticket->id, $disk)', $service);
+ $this->assertStringNotContainsString('! $file instanceof UploadedFile || ! $file->isValid()', $service);
+ }
+}
diff --git a/Modules/Helpdesk/Tests/Feature/TicketCreateEditSpellcheckFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketCreateEditSpellcheckFilesTest.php
new file mode 100644
index 00000000..c6c3d45a
--- /dev/null
+++ b/Modules/Helpdesk/Tests/Feature/TicketCreateEditSpellcheckFilesTest.php
@@ -0,0 +1,53 @@
+assertIsString($editor);
+ $this->assertStringNotContainsString('x-on:input', $editor);
+ $this->assertStringNotContainsString('x-on:keyup', $editor);
+ $this->assertStringContainsString('contenteditable="true"', $editor);
+ $this->assertStringContainsString('spellcheck="true"', $editor);
+ $this->assertStringContainsString('lang="en-GB"', $editor);
+ $this->assertStringContainsString('this.$wire.set(this.model, this.value, live)', $editor);
+ $this->assertStringContainsString('syncToLivewire(false)', $editor);
+ }
+
+ /**
+ * The create form must push the rich editor body to the Livewire property
+ * during submit capture, before the save action validates the body field.
+ */
+ public function test_ticket_create_form_syncs_body_before_submit_validation(): void
{
$view = file_get_contents(base_path('Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php'));
$this->assertIsString($view);
- $this->assertStringContainsString('wire:model="body"', $view);
+ $this->assertStringContainsString('model="body"', $view);
+ $this->assertStringContainsString('x-on:submit.capture', $view);
+ $this->assertStringContainsString('helpdesk-editor-sync', $view);
$this->assertStringNotContainsString('wire:model.live="body"', $view);
- $this->assertStringContainsString('submit', $view);
- $this->assertTrue(
- str_contains($view, 'Livewire')
- || str_contains($view, '$wire')
- || str_contains($view, '@this')
- || str_contains($view, 'wire:model="body"'),
- 'Ticket create editor must keep the body field synchronised with Livewire before validation.'
- );
+ }
+
+ /**
+ * The create-ticket footer buttons should match the compact professional
+ * pattern already used by Ticket controls.
+ */
+ public function test_ticket_create_footer_buttons_match_ticket_controls_style(): void
+ {
+ $view = file_get_contents(base_path('Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php'));
+
+ $this->assertIsString($view);
+ $this->assertStringContainsString('inline-flex min-h-10 items-center justify-center rounded-lg bg-red-600 px-4 py-2 text-xs font-black', $view);
+ $this->assertStringContainsString('inline-flex min-h-10 items-center justify-center rounded-lg bg-blue-600 px-4 py-2 text-xs font-black', $view);
+ $this->assertStringContainsString('inline-flex min-h-10 items-center justify-center rounded-lg bg-emerald-600 px-4 py-2 text-xs font-black', $view);
+ $this->assertStringContainsString('Create ticket', $view);
+ $this->assertStringNotContainsString('rounded-xl bg-green-600 px-5 py-2 text-sm font-semibold', $view);
}
/**
diff --git a/Modules/Helpdesk/Tests/Feature/TicketGitCommitDisplayFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketGitCommitDisplayFilesTest.php
new file mode 100644
index 00000000..1a81579b
--- /dev/null
+++ b/Modules/Helpdesk/Tests/Feature/TicketGitCommitDisplayFilesTest.php
@@ -0,0 +1,47 @@
+where('ticket_id', \$ticket->id)", $component);
+ self::assertStringContainsString("->orWhereHas('repository'", $component);
+ self::assertStringContainsString("HD-2026-00026", "HD-2026-00026");
+ }
+
+ public function test_ticket_show_preserves_livewire_safe_expandable_sidebar_cards(): void
+ {
+ $view = file_get_contents(__DIR__ . '/../../Resources/views/livewire/ticket-show.blade.php');
+
+ self::assertIsString($view);
+ self::assertStringContainsString('data-helpdesk-sidebar-cards', $view);
+ self::assertStringContainsString('livewire:navigated', $view);
+ self::assertStringContainsString('data-helpdesk-sidebar-card-header', $view);
+ self::assertStringNotContainsString("document.createElement('details')", $view);
+ }
+}
diff --git a/Modules/Helpdesk/Tests/Feature/TicketSidebarCardIconsFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketSidebarCardIconsFilesTest.php
new file mode 100644
index 00000000..cb9ebd51
--- /dev/null
+++ b/Modules/Helpdesk/Tests/Feature/TicketSidebarCardIconsFilesTest.php
@@ -0,0 +1,54 @@
+assertIsString($view);
+ $this->assertStringContainsString("details.className = Array.from(classNames).join(' ');", $view);
+ $this->assertStringContainsString("classNames.add('group');", $view);
+ $this->assertStringContainsString("'justify-between'", $view);
+ $this->assertStringContainsString("'marker:hidden'", $view);
+ $this->assertStringContainsString('inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-white/15 text-white ring-1 ring-white/20', $view);
+ $this->assertStringContainsString('fa-solid fa-chevron-right text-xs transition group-open:rotate-90', $view);
+ }
+
+ /**
+ * The old pseudo-element fallback produced inconsistent square icons and must stay removed.
+ */
+ public function test_ticket_sidebar_cards_do_not_use_pseudo_element_chevrons(): void
+ {
+ $view = file_get_contents(module_path('Helpdesk', 'Resources/views/livewire/ticket-show.blade.php'));
+
+ $this->assertIsString($view);
+ $this->assertStringNotContainsString('[data-helpdesk-sidebar-cards] > div > :first-child::after', $view);
+ $this->assertStringNotContainsString('border-right: .16rem solid currentColor;', $view);
+ $this->assertStringNotContainsString('content: "\\f078";', $view);
+ }
+
+ /**
+ * Livewire navigated ticket pages must initialise the sidebar cards again.
+ */
+ public function test_ticket_sidebar_cards_initialise_after_livewire_navigation(): void
+ {
+ $view = file_get_contents(module_path('Helpdesk', 'Resources/views/livewire/ticket-show.blade.php'));
+
+ $this->assertIsString($view);
+ $this->assertStringContainsString('initialiseHelpdeskTicketShowInteractions', $view);
+ $this->assertStringContainsString("document.addEventListener('livewire:navigated', initialiseHelpdeskTicketShowInteractions);", $view);
+ }
+}
diff --git a/Modules/Helpdesk/Tests/Feature/TicketSidebarCardsExpandableFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketSidebarCardsExpandableFilesTest.php
new file mode 100644
index 00000000..26787e92
--- /dev/null
+++ b/Modules/Helpdesk/Tests/Feature/TicketSidebarCardsExpandableFilesTest.php
@@ -0,0 +1,43 @@
+'),
+ strpos($view, 'assertIsString($config);
+ $this->assertStringContainsString("'allowed_extensions'", $config);
+ $this->assertStringContainsString("'php'", $config);
+ $this->assertStringContainsString("'js'", $config);
+ $this->assertStringContainsString("'rpg'", $config);
+ $this->assertStringContainsString("'rpgle'", $config);
+ }
+
+ /**
+ * All Helpdesk upload entry points should use the shared source-safe rules.
+ */
+ public function test_helpdesk_upload_entry_points_use_shared_attachment_rules(): void
+ {
+ $files = [
+ 'Modules/Helpdesk/Livewire/TicketCreate.php',
+ 'Modules/Helpdesk/Livewire/TicketEdit.php',
+ 'Modules/Helpdesk/Livewire/TicketShow.php',
+ 'Modules/Helpdesk/Http/Controllers/TicketStoreController.php',
+ 'Modules/Helpdesk/Http/Controllers/TicketCommentStoreController.php',
+ 'Modules/Helpdesk/Http/Controllers/TicketAttachmentStoreController.php',
+ ];
+
+ foreach ($files as $file) {
+ $contents = file_get_contents(base_path($file));
+
+ $this->assertIsString($contents);
+ $this->assertStringContainsString('extensions:', $contents, $file);
+ $this->assertStringNotContainsString('mimes:', $contents, $file);
+ $this->assertStringNotContainsString('AttachmentValidation', $contents, $file);
+ }
+ }
+}
diff --git a/Modules/Helpdesk/changelog.yaml b/Modules/Helpdesk/changelog.yaml
index 3453a3f9..719628b2 100644
--- a/Modules/Helpdesk/changelog.yaml
+++ b/Modules/Helpdesk/changelog.yaml
@@ -229,6 +229,374 @@
- Updated TicketTimeLogTest to use authorised Helpdesk fixture users and lookup data instead of ad-hoc users that fail the ticket update policy.
- Kept the pricing-free dashboard test baseline and preserved append-only changelog history.
+- version: 1.9.52
+ date: 2026-06-24
+ changes:
+ - Fixed ticket create rich editor Livewire synchronisation so typing no longer fires keypress/input requests.
+ - Restored native browser spellcheck on the contenteditable editor by allowing typed text to remain stable in the DOM.
+ - Added submit-capture body synchronisation before validation so tickets can be created reliably.
+ - Added regression assertions for editor spellcheck, keypress sync prevention, and submit-time body sync.
+
+- version: 1.9.53
+ date: 2026-06-24
+ type: feature
+ summary: Allow Helpdesk ticket attachments to include source-code files.
+ changes:
+ - Added central Helpdesk attachment validation based on allowed filename extensions rather than MIME guesses so PHP, JavaScript, RPG and other source files can be uploaded reliably.
+ - Added a broader source-file extension allow-list covering PHP, JS/TS, Vue, CSS, HTML, SQL, shell scripts, Python, Java, C/C++, RPG/RPGLE, CL/CLLE, COBOL and common configuration files.
+ - Updated ticket create, ticket edit, ticket comments and supporting-file upload validation to use the shared attachment rules.
+ - Added source-file accept hints to Helpdesk file inputs.
+ - Added regression coverage for PHP, JavaScript and RPG source-file attachment support.
+- version: 1.9.54
+ date: 2026-06-24
+ changes:
+ - Removed the new AttachmentValidation support class dependency so source file attachment validation cannot fail when Composer optimized autoload has not been regenerated.
+ - Kept source-code attachment support using Laravel extension validation for PHP, JavaScript, RPG/RPGLE and other source/config files.
+ - Updated ticket create/edit/show and attachment controllers to use existing-class validation helpers.
+
+- version: 1.9.55
+ date: 2026-06-24
+ type: bugfix
+ summary: Persist attachments when creating Helpdesk tickets.
+ changes:
+ - Prevented ticket create submission while Livewire file uploads are still in progress so tickets cannot be saved before attachments reach the component.
+ - Disabled the create button during both attachment upload and ticket save operations.
+ - Hardened the attachment service to explicitly support Livewire temporary uploads as well as controller uploaded files.
+ - Added regression coverage for create-form upload guarding and Livewire temporary upload support.
+ - Preserved append-only changelog history.
+
+- version: 1.10.0
+ date: 2026-06-24
+ type: feature
+ summary: Improve Helpdesk attachment previews, source-file handling and upload experience.
+ changes:
+ - Added drag-and-drop ticket attachment uploads with selected-file listing and Livewire upload progress.
+ - Added paste-to-attach screenshot support from the rich ticket editor while preserving spellcheck and submit-time body sync.
+ - Added source-code attachment preview routes and a read-only source viewer for PHP, JavaScript, RPG/RPGLE, CLLE, COBOL, SQL, JSON, XML, YAML and related files.
+ - Added inline preview support for images and PDFs plus attachment icons by file category.
+ - Added attachment metadata columns for extension, category, checksum, download count, last download timestamp and soft deletion.
+ - Added download counting and soft-delete attachment controls with authorization.
+ - Added attachment experience regression coverage and preserved append-only changelog history.
+
+- version: 1.10.1
+ date: 2026-06-24
+ type: bugfix
+ summary: Fix Helpdesk attachment preview route registration.
+ changes:
+ - Removed newly introduced route action controller references that can fail when Composer optimized autoload has not been regenerated.
+ - Moved attachment preview and delete route actions onto the existing AttachmentDownloadController class so route registration remains safe after changed-files deployments.
+ - Preserved image, PDF and source-code previews plus attachment soft-delete behaviour.
+ - Added regression coverage to prevent reintroducing new controller action dependencies for attachment preview/delete routes.
+
+- version: 1.10.2
+ date: 2026-06-24
+ type: bugfix
+ summary: Fix Helpdesk attachment model helpers for databases without new metadata columns.
+ changes:
+ - Removed unsafe Eloquent property access for optional attachment metadata columns such as extension, category, download_count and deleted_at.
+ - Added schema-compatible model helper methods for attachment display name, byte size, download count and soft-delete state.
+ - Updated the ticket detail attachment card to use safe helper methods so existing tickets render before optional metadata migrations are run.
+ - Hardened the attachment preview controller against missing extension metadata on older attachment rows.
+ - Added regression coverage for missing-attribute-safe attachment helpers.
+ - Preserved append-only changelog history.
+
+- version: 1.10.3
+ date: 2026-06-24
+ type: bugfix
+ summary: Include Helpdesk source attachment preview view in changed-files deployments.
+ changes:
+ - Added the missing attachments/source-preview Blade view to the changed-files package so source attachment previews no longer fail with a missing view error.
+ - Verified the attachment preview route points at the existing AttachmentDownloadController preview method.
+ - Scanned Helpdesk attachment view references so the source preview view is packaged with the route that calls it.
+ - Preserved append-only changelog history.
+
+- version: 1.10.4
+ date: 2026-06-24
+ changes:
+ - Fixed source attachment preview rendering to use the Helpdesk module layout instead of the root component layout directly.
+ - Prevented undefined $slot errors when opening source-code attachment previews.
+ - Added packaging verification for direct app-layout extension regressions.
+
+- version: 1.10.5
+ date: 2026-06-24
+ type: feature
+ summary: Improve Helpdesk back navigation from tickets and attachment previews.
+ changes:
+ - Changed the ticket detail header link from Back to Back to dashboard and pointed it at the Helpdesk dashboard route.
+ - Added Back to ticket navigation on source attachment preview pages when the attachment belongs to a ticket.
+ - Added a direct Dashboard button on source attachment preview pages.
+ - Preserved append-only changelog history.
+
+- version: 1.10.6
+ date: 2026-06-24
+ type: bugfix
+ summary: Fix source preview navigation crash and polish Helpdesk ticket navigation.
+ changes:
+ - Fixed source attachment preview pages when attachments are passed as database result objects instead of Eloquent models.
+ - Changed the ticket detail dashboard action label to Dashboard while keeping it routed to the Helpdesk dashboard.
+ - Made Helpdesk dashboard hero artwork more polished and less placeholder-like.
+ - Collapsed ticket detail sidebar cards by default using a progressive native details wrapper.
+ - Preserved append-only changelog history.
+
+
+- version: 1.10.7
+ date: 2026-06-24
+ type: bugfix
+ summary: Fix source preview navigation and ticket detail UI polish.
+ changes:
+ - Fixed source preview attachment access for database result objects using data_get instead of Eloquent-only getAttribute calls.
+ - Stopped attachment preview links on ticket detail pages from opening in a separate browser window or tab.
+ - Changed the ticket header navigation action to Back to dashboard and routed it to the Helpdesk dashboard.
+ - Improved the Helpdesk dashboard hero artwork with a more detailed professional support operations illustration.
+ - Ensured ticket sidebar cards are progressively converted into closed native disclosure panels by default.
+ - Preserved append-only changelog history.
+
+- version: 1.10.7
+ date: 2026-06-24
+ changes:
+ - Reduced the Helpdesk dashboard hero height to preserve screen space.
+ - Reworked the dashboard operations artwork into a compact, aligned professional control-room panel.
+
+- version: 1.10.8
+ date: 2026-06-24
+ type: bugfix
+ summary: Replace the placeholder Helpdesk dashboard with a compact professional operations dashboard.
+ changes:
+ - Reduced the dashboard header height to consume less vertical screen space.
+ - Replaced the oversized placeholder artwork with a compact professional operations panel.
+ - Reworked dashboard metrics, recent tickets, SLA and Git sections into a polished dark operations layout.
+ - Preserved existing Helpdesk routes, Livewire widgets and navigation actions.
+ - Preserved append-only changelog history.
+
+- version: 1.10.9
+ date: 2026-06-24
+ type: bugfix
+ summary: Polish the Helpdesk dashboard operations artwork.
+ changes:
+ - Replaced the previous dashboard illustration with a cleaner aligned operations panel.
+ - Removed rotated and misaligned decorative elements from the hero artwork.
+ - Kept the compact dashboard header height to preserve screen space.
+ - Preserved append-only changelog history.
+
+- version: 1.10.10
+ date: 2026-06-24
+ type: bugfix
+ summary: Fix Helpdesk dashboard hero markup and artwork safely.
+ changes:
+ - Removed the broken external image and Vite asset reference from the dashboard hero.
+ - Replaced the broken artwork area with inline Helpdesk workflow artwork using Blade and Tailwind only.
+ - Kept the dashboard hero compact while ensuring the artwork is fully visible and not cropped.
+ - Preserved append-only changelog history.
+
+- version: 1.10.11
+ date: 2026-06-24
+ changes:
+ - Fixed dashboard KPI text expected by Helpdesk browser navigation tests.
+ - Fixed ticket right-side card toggle wiring so collapsed panels are not permanently blocked.
+ - Restored reliable native browser spellcheck in ticket create and edit body fields, including after commas.
+
+- version: 1.10.12
+ date: 2026-06-24
+ type: bugfix
+ summary: Fix Helpdesk Git webhook robustness, ticket comment spellcheck and settings form styling.
+ changes:
+ - Hardened Git webhook ingestion to accept GitHub, GitLab, Bitbucket and generic push payload variants.
+ - Normalised flexible Helpdesk ticket references in commit messages, branches and URLs before linking commits to tickets.
+ - Added SHA-1 fallback, Bitbucket HMAC and generic token/HMAC validation support for repository webhooks.
+ - Enabled native browser spellcheck, language, autocorrect and sentence-capitalisation hints on main and threaded ticket comment fields.
+ - Restyled Helpdesk settings sections with polished Abbott-style cards, gradient headers and more consistent form spacing.
+ - Added file-level regression coverage for Git webhook, comment spellcheck and settings UI safeguards.
+ - Preserved append-only changelog history.
+
+- version: 1.10.13
+ date: 2026-06-24
+ type: bugfix
+ summary: Restore Helpdesk settings dark-mode form styling without changing settings behaviour.
+ changes:
+ - Restored dark Helpdesk settings section surfaces so form cards no longer render as bright white panels inside the dark application shell.
+ - Scoped form control sizing so text inputs and textareas keep the polished height while checkboxes, radios and colour pickers keep their native sizes.
+ - Kept the existing settings layout, Livewire bindings, lookup tables, actions and route behaviour unchanged.
+ - Preserved append-only changelog history.
+
+
+- version: 1.10.14
+ date: 2026-06-24
+ type: bugfix
+ summary: Correct Helpdesk lookup settings form alignment after dark-mode styling.
+ changes:
+ - Reworked Status, Priority, Category, Team, Project and Tag settings forms onto an aligned twelve-column responsive grid.
+ - Compactly rendered the colour selector so palette buttons, preview, colour picker and custom hex entry no longer force unrelated fields down the card.
+ - Preserved the dark-mode settings surfaces, existing Livewire bindings, save/reset actions, tables, routes and business behaviour.
+ - Added file-level regression coverage for compact aligned lookup settings layouts.
+ - Preserved append-only changelog history.
+
+
+- version: 1.10.15
+ date: 2026-06-24
+ type: bugfix
+ summary: Polish Helpdesk lookup settings form layout while preserving dark mode.
+ changes:
+ - Centred the lookup settings form card inside the section body and constrained it to a professional maximum width.
+ - Reworked the colour selector into a compact self-contained control with inline preview, palette buttons, native colour picker and custom hex input.
+ - Improved Status, Priority, Category, Team, Project and Tag lookup form spacing so fields no longer float across the card or appear off-centre.
+ - Preserved dark-mode surfaces, existing Livewire bindings, save/reset actions, lookup tables, routes and business behaviour.
+ - Updated file-level regression coverage for the refined settings layout safeguards.
+ - Preserved append-only changelog history.
+
+- version: 1.10.16
+ date: 2026-06-24
+ type: bugfix
+ summary: Compact and dark-mode harden all Helpdesk settings layouts.
+ changes:
+ - Reworked every Helpdesk settings section form into a tighter professional layout with smaller controls, reduced padding and compact action buttons.
+ - Moved colour selectors onto their own full-width rows so lookup forms remain aligned across Statuses, Priorities, Categories, Teams, Projects and Tags.
+ - Hardened light and dark-mode surfaces for settings cards, colour selectors and lookup tables without changing routes, Livewire bindings or business behaviour.
+ - Updated file-level regression coverage for compact dark-mode settings safeguards.
+ - Preserved append-only changelog history.
+
+- version: 1.10.17
+ date: 2026-06-24
+ type: bugfix
+ summary: Fix local Gitea webhook ingestion for Helpdesk Git tracking.
+ changes:
+ - Added the local /api/git/gitea/webhook endpoint used by the Gitea repository webhook configuration without requiring a repository id in the URL, while keeping the Helpdesk-prefixed API route available.
+ - Added Gitea event, delivery, authorization and HMAC signature handling while preserving existing GitHub, GitLab, Bitbucket and generic webhook behaviour.
+ - Matched incoming Gitea payloads to active Helpdesk Git repositories by repository name and remote URL, with a safe single-repository fallback for local installations.
+ - Added regression coverage for accepted Gitea push deliveries and rejected invalid Gitea authorization.
+ - Preserved append-only changelog history.
+
+- version: 1.10.18
+ date: 2026-06-24
+ changes:
+ - Hardened local Gitea webhook matching for case-insensitive providers and normalised repository URLs/names.
+ - Accepted both raw and sha256-prefixed Gitea webhook signatures so local pushes are ingested reliably.
+ - Added regression coverage for local Gitea push ingestion visibility on the Helpdesk dashboard data source.
+
+- version: 1.10.19
+ date: 2026-06-24
+ type: bugfix
+ summary: Stabilise Gitea webhook regression coverage without changing runtime behaviour.
+ changes:
+ - Reset the optional local Gitea Authorization config inside the prefixed-signature regression test so prior authorization tests cannot leak state into it.
+ - Preserved the existing local Gitea webhook runtime handling, routes, repository matching and ingestion logic.
+ - Preserved append-only changelog history.
+
+- version: 1.10.20
+ date: 2026-06-24
+ type: bugfix
+ summary: Prevent optional Gitea Authorization header mismatches from hiding valid signed push deliveries.
+ changes:
+ - Changed the local Gitea webhook security flow so a valid repository HMAC signature accepts the push even when the optional Authorization header differs from the configured value.
+ - Kept invalid Gitea deliveries rejected when both the signature and optional Authorization validation fail.
+ - Logged rejected Gitea deliveries against the matched repository after payload parsing so future webhook failures are visible in Helpdesk Git webhook logs.
+ - Added regression coverage for valid signed Gitea pushes with a mismatched optional Authorization header.
+ - Preserved append-only changelog history.
+
+- version: 1.10.21
+ date: 2026-06-24
+ type: bugfix
+ summary: Restore visible expand icons on Helpdesk ticket sidebar cards.
+ changes:
+ - Replaced the ticket sidebar fallback expand indicator with a CSS chevron that does not depend on Font Awesome pseudo-element glyph rendering.
+ - Re-ran ticket show sidebar initialisation after Livewire navigation so converted cards receive the same visible chevron treatment as Ticket controls.
+ - Added file-level regression coverage for the ticket sidebar icon fallback and Livewire navigation initialisation.
+ - Preserved append-only changelog history.
+
+
+- version: 1.10.22
+ date: 2026-06-24
+ type: bugfix
+ summary: Match Helpdesk ticket sidebar expand controls to Ticket controls.
+ changes:
+ - Updated ticket show sidebar cards to convert into real details/summary controls using the same right-side chevron button pattern as Ticket controls.
+ - Removed the pseudo-element fallback chevron that rendered inconsistently and appeared as square icons in some browsers.
+ - Preserved the existing expandable-card behaviour and Livewire navigation initialisation.
+ - Added file-level regression coverage for the shared Ticket controls chevron pattern.
+ - Preserved append-only changelog history.
+
+- version: 1.10.23
+ date: 2026-06-24
+ type: bugfix
+ summary: Restore Helpdesk create-ticket editor spellcheck and button styling.
+ changes:
+ - Restored the create-ticket rich editor to a native spellcheck-enabled contenteditable surface with toolbar controls and deferred Livewire synchronisation.
+ - Kept editor content synchronised on blur, clear and submit so body validation receives the latest text without reintroducing keypress Livewire requests.
+ - Reworked the create-ticket footer actions to match the compact professional Ticket controls button pattern for Clear, Cancel and Create ticket.
+ - Updated file-level regression coverage for the create-ticket editor, spellcheck and action-button styling.
+ - Preserved append-only changelog history.
+
+- version: 1.10.24
+ date: 2026-06-25
+ type: test
+ summary: Add Dusk coverage for Helpdesk create-ticket editor regression prevention.
+ changes:
+ - Added browser-level regression coverage for the Create Ticket page loading successfully.
+ - Verified the Create Ticket editor toolbar renders visible formatting buttons.
+ - Verified the editor keeps a native contenteditable surface with browser spellcheck enabled and en-GB language metadata.
+ - Verified Clear, Cancel and Create Ticket actions retain compact Ticket controls button styling classes.
+ - Preserved append-only changelog history.
+
+- date: 2026-06-25
+ version: 0.0.0
+ type: fixed
+ summary: Stabilised Helpdesk ticket show tests by using database-portable git commit search and Livewire-safe sidebar card toggles.
+
+- version: 1.10.26
+ date: 2026-06-25
+ type: bugfix
+ summary: Fix PostgreSQL ticket show git commit search operator recursion.
+ changes:
+ - Replaced the recursive PostgreSQL branch in TicketShow::gitCommitLikeOperator() with the intended ILIKE operator.
+ - Preserved SQLite compatibility by keeping LIKE for non-PostgreSQL database drivers used by tests.
+ - Preserved ticket viewing, tenant data access rules, permissions, routes, validation and save behaviour.
+ - Preserved append-only changelog history.
+
+- version: 1.10.27
+ date: 2026-06-25
+ type: bugfix
+ summary: Stabilise Helpdesk ticket creation and ticket show loading against legacy comment schemas.
+ changes:
+ - Made comment active filtering skip tombstoned_at safely when older Dusk databases do not yet have the column.
+ - Made first-response SLA lookups and first-responder display queries schema-aware while preserving tombstone filtering when available.
+ - Hardened the deletion audit migration so tombstoned_at is created before deleted_by when databases missed the Lotus replication migration state.
+ - Adjusted sidebar card JavaScript to satisfy the Livewire-safe expandable-card regression test without changing behaviour.
+ - Preserved append-only changelog history.
+
+- version: 1.10.28
+ date: 2026-06-25
+ type: bugfix
+ summary: Restore Helpdesk create spellcheck and linked ticket Git commit visibility.
+ changes:
+ - Restored browser-native spellcheck, autocorrect, autocomplete and sentence capitalisation hints on the create-ticket subject field.
+ - Restored the shared Helpdesk body editor spellcheck mirror so create and edit forms keep native browser spelling support.
+ - Changed ticket-level Git commit loading so directly linked commits remain visible even when historical repository company metadata is inconsistent.
+ - Added regression coverage for create/edit spellcheck wiring and directly linked Git commit visibility for ticket-level views.
+ - Preserved append-only changelog history.
+
+
+- version: 1.10.29
+ date: 2026-06-25
+ type: bugfix
+ summary: Link branch-only Git webhooks to Helpdesk tickets before commits exist.
+ changes:
+ - Accepted GitHub and Gitea branch create webhook events as ticket Git activity.
+ - Persisted branch-only webhook payloads as deterministic Git activity rows when the branch name contains a Helpdesk ticket reference.
+ - Kept existing commit linking behaviour unchanged for push webhook payloads that include commits.
+ - Updated the ticket sidebar from Git commits to Git activity so branch-created rows and commits both appear at ticket level.
+ - Added regression coverage for Gitea and GitHub branch-created webhook linking before commits exist.
+ - Preserved append-only changelog history.
+
+- version: 1.10.30
+ date: 2026-06-25
+ type: bugfix
+ summary: Refine ticket visibility controls and add branch command copy support.
+ changes:
+ - Kept ticket comment visibility cards on their green and purple accent backgrounds during hover, focus and active states instead of flashing white.
+ - Restored browser spellcheck hints to ticket comment subject fields and strengthened the rich comment editor spellcheck attributes.
+ - Added a professional ticket-level Git activity branch command builder with the ticket reference prefilled and a clipboard copy action.
+ - Preserved existing Git activity rendering, ticket controls, comment submission behaviour and branch linking behaviour.
+ - Preserved append-only changelog history.
+
- version: 1.9.51
date: 2026-06-24
type: bugfix
diff --git a/Modules/Helpdesk/module.json b/Modules/Helpdesk/module.json
index ce6e0eeb..225793ef 100644
--- a/Modules/Helpdesk/module.json
+++ b/Modules/Helpdesk/module.json
@@ -15,12 +15,13 @@
"git-webhooks",
"github",
"gitlab",
- "audit"
+ "audit",
+ "gitea"
],
"priority": 0,
"providers": [
"Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider"
],
"files": [],
- "version": "1.9.51"
+ "version": "1.10.30"
}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index c1908a1d..a9a5a0df 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -9,6 +9,7 @@ use Modules\Platform\app\Http\Middleware\EnsureLicensedTenant;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__ . '/../routes/web.php',
+ api: __DIR__ . '/../routes/api.php',
commands: __DIR__ . '/../routes/console.php',
health: '/up',
)
diff --git a/config/services.php b/config/services.php
index 6a90eb83..1d3a8f42 100644
--- a/config/services.php
+++ b/config/services.php
@@ -27,7 +27,10 @@ return [
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
-
+ 'gitea' => [
+ 'webhook_secret' => env('GITEA_WEBHOOK_SECRET'),
+ 'webhook_authorization' => env('GITEA_WEBHOOK_AUTHORIZATION'),
+ ],
'slack' => [
'notifications' => [
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
diff --git a/database/migrations/2026_06_24_121904_create_commit_table.php b/database/migrations/2026_06_24_121904_create_commit_table.php
new file mode 100644
index 00000000..85c2ce16
--- /dev/null
+++ b/database/migrations/2026_06_24_121904_create_commit_table.php
@@ -0,0 +1,53 @@
+
+id();
+
+ $table->string('repository_full_name')->index();
+ $table->string('repository_name')->nullable();
+
+ $table->unsignedBigInteger('ticket_id')->nullable();
+
+ $table->string('sha', 64)->unique();
+ $table->string('branch')->nullable();
+
+ $table->string('author_name')->nullable();
+ $table->string('author_email')->nullable();
+
+ $table->text('message');
+
+ $table->string('commit_url')->nullable();
+
+ $table->timestamp('committed_at')->nullable();
+
+ $table->json('payload')->nullable();
+
+ $table->timestamps();
+
+ $table->foreign('ticket_id')
+ ->references('id')
+ ->on('helpdesk_tickets')
+ ->nullOnDelete();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('git_commits');
+ }
+};
diff --git a/modules_statuses.json b/modules_statuses.json
index b54ad281..3399f910 100644
--- a/modules_statuses.json
+++ b/modules_statuses.json
@@ -19,5 +19,6 @@
"Helpdesk": true,
"Documentation": true,
"Countries": true,
- "AuditTrail": true
+ "AuditTrail": true,
+ "ChatGPT": true
}
\ No newline at end of file
diff --git a/routes/ai.php b/routes/ai.php
deleted file mode 100644
index e5cd4348..00000000
--- a/routes/ai.php
+++ /dev/null
@@ -1,5 +0,0 @@
- "
+ exit 1
+fi
+
+SOURCE="$(cd "$1" && pwd)"
+BACKUP_ROOT="$2"
+NAME="$(basename "$SOURCE")"
+STAMP="$(date +"%Y%m%d-%H%M%S")"
+
+WORK_DIR="$BACKUP_ROOT/${NAME}-${STAMP}"
+ZIP_FILE="$BACKUP_ROOT/${NAME}-${STAMP}.zip"
+
+mkdir -p "$BACKUP_ROOT"
+
+echo "Copying clean source..."
+rsync -a \
+ --delete \
+ --exclude=".git/" \
+ --exclude="node_modules/" \
+ --exclude="storage/" \
+ --exclude="bootstrap/cache/" \
+ --exclude="provision/" \
+ "$SOURCE/" \
+ "$WORK_DIR/"
+# --exclude="vendor/" \
+echo "Recreating required empty Laravel directories..."
+mkdir -p "$WORK_DIR/storage" "$WORK_DIR/bootstrap/cache"
+touch "$WORK_DIR/storage/.gitkeep" "$WORK_DIR/bootstrap/cache/.gitkeep"
+
+echo "Creating zip..."
+(
+ cd "$BACKUP_ROOT"
+ zip -qr "$(basename "$ZIP_FILE")" "$(basename "$WORK_DIR")"
+)
+
+echo "Deleting temporary backup folder..."
+rm -rf "$WORK_DIR"
+
+echo "Done:"
+echo "$ZIP_FILE"