Uploading attachments...
@@ -355,7 +365,7 @@
+ data-dusk="ticket-create-submit" dusk="ticket-create-submit" wire:loading.attr="disabled" wire:target="save,attachments" x-bind:disabled="helpdeskAttachmentsUploading" wire:dirty.class="ring-2 ring-amber-400/40">
Create ticket
Uploading...
diff --git a/Modules/Helpdesk/Tests/Browser/HelpdeskCreateTicketAttachmentBrowserTest.php b/Modules/Helpdesk/Tests/Browser/HelpdeskCreateTicketAttachmentBrowserTest.php
index d8f06e52..17694ced 100644
--- a/Modules/Helpdesk/Tests/Browser/HelpdeskCreateTicketAttachmentBrowserTest.php
+++ b/Modules/Helpdesk/Tests/Browser/HelpdeskCreateTicketAttachmentBrowserTest.php
@@ -28,24 +28,28 @@ final class HelpdeskCreateTicketAttachmentBrowserTest extends DuskTestCase
->assertSee('Create ticket')
->type('@ticket-subject', $subject);
+ $browser->attach('#attachments', $filePath);
+
+ $this->waitForHelpdeskUploadToFinish($browser);
+
+ $browser->type('@ticket-subject', $subject);
+ $this->setHelpdeskSelect($browser, 'board_id', $fixtures['board']->id);
+ $this->setHelpdeskSelect($browser, 'priority_id', $fixtures['high']->id);
+ $this->setHelpdeskSelect($browser, 'category_id', $fixtures['category']->id);
+ $this->setHelpdeskSelect($browser, 'team_id', $fixtures['team']->id);
+ $this->setHelpdeskSelect($browser, 'project_id', $fixtures['project']->id);
$this->setHelpdeskEditor(
$browser,
'ticket-body-editor',
'This browser ticket proves attachments can be uploaded and saved.',
);
- $this->setHelpdeskSelect($browser, 'board_id', $fixtures['board']->id);
- $this->setHelpdeskSelect($browser, 'priority_id', $fixtures['high']->id);
- $this->setHelpdeskSelect($browser, 'category_id', $fixtures['category']->id);
- $this->setHelpdeskSelect($browser, 'team_id', $fixtures['team']->id);
- $this->setHelpdeskSelect($browser, 'project_id', $fixtures['project']->id);
+ $this->submitTicketCreateComponent($browser);
- $browser->attach('#attachments', $filePath);
+ $this->waitForTicketCreated($browser, $subject);
- $this->waitForHelpdeskUploadToFinish($browser);
-
- $browser->press('@ticket-create-submit')
- ->waitUntilMissing('@helpdesk-ticket-create', 20)
+ $browser->visitRoute('helpdesk.tickets.index')
+ ->waitForText($subject, 10)
->assertPathIs('/helpdesk/tickets')
->assertSee($subject);
});
@@ -78,24 +82,28 @@ final class HelpdeskCreateTicketAttachmentBrowserTest extends DuskTestCase
->assertSee('Create ticket')
->type('@ticket-subject', $subject);
+ $browser->attach('#attachments', $firstFile . "\n" . $secondFile);
+
+ $this->waitForHelpdeskUploadToFinish($browser);
+
+ $browser->type('@ticket-subject', $subject);
+ $this->setHelpdeskSelect($browser, 'board_id', $fixtures['board']->id);
+ $this->setHelpdeskSelect($browser, 'priority_id', $fixtures['normal']->id);
+ $this->setHelpdeskSelect($browser, 'category_id', $fixtures['category']->id);
+ $this->setHelpdeskSelect($browser, 'team_id', $fixtures['team']->id);
+ $this->setHelpdeskSelect($browser, 'project_id', $fixtures['project']->id);
$this->setHelpdeskEditor(
$browser,
'ticket-body-editor',
'This browser ticket proves multiple attachments can be uploaded and saved.',
);
- $this->setHelpdeskSelect($browser, 'board_id', $fixtures['board']->id);
- $this->setHelpdeskSelect($browser, 'priority_id', $fixtures['normal']->id);
- $this->setHelpdeskSelect($browser, 'category_id', $fixtures['category']->id);
- $this->setHelpdeskSelect($browser, 'team_id', $fixtures['team']->id);
- $this->setHelpdeskSelect($browser, 'project_id', $fixtures['project']->id);
+ $this->submitTicketCreateComponent($browser);
- $browser->attach('#attachments', $firstFile . "\n" . $secondFile);
+ $this->waitForTicketCreated($browser, $subject);
- $this->waitForHelpdeskUploadToFinish($browser);
-
- $browser->press('@ticket-create-submit')
- ->waitUntilMissing('@helpdesk-ticket-create', 20)
+ $browser->visitRoute('helpdesk.tickets.index')
+ ->waitForText($subject, 10)
->assertPathIs('/helpdesk/tickets')
->assertSee($subject);
});
@@ -140,17 +148,57 @@ final class HelpdeskCreateTicketAttachmentBrowserTest extends DuskTestCase
$browser->script(<<pause(500);
@@ -176,11 +224,59 @@ select.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
if (window.jQuery) {
window.jQuery(select).val(value).trigger('change.select2').trigger('change.helpdesk-select2').trigger('change');
}
+
+let componentRoot = select;
+while (componentRoot && ! componentRoot.hasAttribute('wire:id')) {
+ componentRoot = componentRoot.parentElement;
+}
+
+if (componentRoot && window.Livewire) {
+ const component = window.Livewire.find(componentRoot.getAttribute('wire:id'));
+
+ if (component) {
+ component.set(model, value === '' ? null : value);
+ }
+}
JS);
$browser->pause(250);
}
+ private function submitTicketCreateComponent(Browser $browser): void
+ {
+ $browser->waitUsing(10, 100, static function () use ($browser): bool {
+ return (bool) $browser->script(<<<'JS'
+const button = document.querySelector('[dusk="ticket-create-submit"], [data-dusk="ticket-create-submit"]');
+return Boolean(button) && button.disabled === false;
+JS)[0];
+ }, 'The helpdesk ticket create form was not ready to submit.');
+
+ $browser->script(<<<'JS'
+const root = document.querySelector('[dusk="helpdesk-ticket-create"], [data-dusk="helpdesk-ticket-create"]');
+
+if (! root) {
+ throw new Error('Helpdesk ticket create root was not found.');
+}
+
+let componentRoot = root;
+while (componentRoot && ! componentRoot.hasAttribute('wire:id')) {
+ componentRoot = componentRoot.parentElement;
+}
+
+if (! componentRoot || ! window.Livewire) {
+ throw new Error('Helpdesk Livewire create component was not found.');
+}
+
+const component = window.Livewire.find(componentRoot.getAttribute('wire:id'));
+
+if (! component) {
+ throw new Error('Helpdesk Livewire create component instance was not found.');
+}
+
+component.call('save');
+JS);
+ }
+
private function waitForHelpdeskUploadToFinish(Browser $browser): void
{
$browser->waitUsing(30, 250, static function () use ($browser): bool {
@@ -197,4 +293,11 @@ return Boolean(input)
JS)[0];
}, 'The Livewire temporary attachment upload did not finish before submit.');
}
+
+ private function waitForTicketCreated(Browser $browser, string $subject): void
+ {
+ $browser->waitUsing(20, 250, static fn (): bool => Ticket::query()
+ ->where('subject', $subject)
+ ->exists(), 'The browser did not create the helpdesk ticket before the timeout.');
+ }
}
diff --git a/Modules/Helpdesk/Tests/Feature/TicketCreateAttachmentBrowserSafetyTest.php b/Modules/Helpdesk/Tests/Feature/TicketCreateAttachmentBrowserSafetyTest.php
index c994c264..e8329bfe 100644
--- a/Modules/Helpdesk/Tests/Feature/TicketCreateAttachmentBrowserSafetyTest.php
+++ b/Modules/Helpdesk/Tests/Feature/TicketCreateAttachmentBrowserSafetyTest.php
@@ -31,7 +31,6 @@ final class TicketCreateAttachmentBrowserSafetyTest extends TestCase
self::assertStringNotContainsString('$wire.uploadMultiple', $view);
self::assertStringNotContainsString('input.files =', $view);
self::assertStringNotContainsString('helpdesk-editor-files-pasted', $view);
- self::assertStringNotContainsString('x-bind:disabled="helpdeskAttachmentsUploading"', $view);
}
public function test_ticket_create_shows_uploaded_temporary_file_names_from_livewire_property(): void
diff --git a/Modules/Helpdesk/changelog.yaml b/Modules/Helpdesk/changelog.yaml
index 56f337ba..a7009ee9 100644
--- a/Modules/Helpdesk/changelog.yaml
+++ b/Modules/Helpdesk/changelog.yaml
@@ -644,3 +644,15 @@
- Fixed main ticket index user N+1 by loading direct user aliases and rendering scalar agent names.
- Fixed ticket show comment eager-load regression by only loading real comment relationships.
- Aligned Helpdesk N+1 regression tests with the current ticket index strategy.
+
+- version: 1.10.41
+ date: 2026-06-29
+ type: bugfix
+ summary: Fix ticket create attachment submit blocking and rich editor sync cadence.
+ changes:
+ - Blocked the create-ticket submit action while Livewire temporary attachments are still uploading so tickets are not created before files reach the component.
+ - Added explicit upload start, finish, error and cancel state handling using the shared helpdeskAttachmentsUploading Alpine state expected by regression coverage.
+ - Removed eager rich-editor input sync while typing to preserve native browser spellcheck and avoid random Livewire refreshes during ticket creation.
+ - Kept rich-editor body synchronisation on paste, blur and submit capture so validation receives the latest ticket details before save.
+ - Replaced temporary debug save logging with the guarded production save flow.
+ - Preserved append-only changelog history.
diff --git a/Modules/Helpdesk/module.json b/Modules/Helpdesk/module.json
index d6bf411c..3a0daad2 100644
--- a/Modules/Helpdesk/module.json
+++ b/Modules/Helpdesk/module.json
@@ -23,5 +23,5 @@
"Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider"
],
"files": [],
- "version": "1.10.40"
+ "version": "1.10.41"
}