diff --git a/Modules/Helpdesk/Database/Migrations/2026_06_17_123000_add_deletion_audit_fields_to_helpdesk_comments.php b/Modules/Helpdesk/Database/Migrations/2026_06_17_123000_add_deletion_audit_fields_to_helpdesk_comments.php index 9436569a..2c9a54b1 100644 --- a/Modules/Helpdesk/Database/Migrations/2026_06_17_123000_add_deletion_audit_fields_to_helpdesk_comments.php +++ b/Modules/Helpdesk/Database/Migrations/2026_06_17_123000_add_deletion_audit_fields_to_helpdesk_comments.php @@ -11,6 +11,10 @@ return new class extends Migration public function up(): void { Schema::table('helpdesk_comments', static function (Blueprint $table): void { + if (! Schema::hasColumn('helpdesk_comments', 'tombstoned_at')) { + $table->timestamp('tombstoned_at')->nullable()->after('synced_at'); + } + if (! Schema::hasColumn('helpdesk_comments', 'deleted_by')) { $table->foreignId('deleted_by')->nullable()->after('tombstoned_at')->constrained('users')->nullOnDelete(); } diff --git a/Modules/Helpdesk/Livewire/TicketShow.php b/Modules/Helpdesk/Livewire/TicketShow.php index fc770fbc..26c34c68 100644 --- a/Modules/Helpdesk/Livewire/TicketShow.php +++ b/Modules/Helpdesk/Livewire/TicketShow.php @@ -491,13 +491,18 @@ final class TicketShow extends Component */ public function firstResponderName(): string { - $comment = Comment::query() + /** @var \Illuminate\Database\Eloquent\Builder $query */ + $query = Comment::query() ->where('ticket_id', $this->ticket->id) ->where('is_internal', false) - ->whereNull('tombstoned_at') ->with('user') - ->oldest('created_at') - ->first(); + ->oldest('created_at'); + + if (Schema::hasColumn('helpdesk_comments', 'tombstoned_at')) { + $query->whereNull('tombstoned_at'); + } + + $comment = $query->first(); return $comment?->user?->name ?: 'Unknown responder'; } diff --git a/Modules/Helpdesk/Models/Comment.php b/Modules/Helpdesk/Models/Comment.php index aab0ee62..23df1033 100644 --- a/Modules/Helpdesk/Models/Comment.php +++ b/Modules/Helpdesk/Models/Comment.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Facades\Schema; final class Comment extends Model { @@ -88,11 +89,23 @@ final class Comment extends Model return $this->hasMany(Attachment::class, 'comment_id'); } - /** @param Builder $query */ + /** + * Scope the query to non-tombstoned comments when the legacy column exists. + * + * Some older Dusk databases can exist before the Lotus replication migration + * added tombstoned_at. In that state every row is active, so avoid issuing an + * invalid column query. + * + * @param Builder $query + * + * @return Builder + */ public function scopeActive(Builder $query): Builder { + if (! Schema::hasColumn($this->getTable(), 'tombstoned_at')) { + return $query; + } return $query->whereNull('tombstoned_at'); - } } diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php index f3c3c352..0a92c4e0 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php @@ -902,7 +902,7 @@ return; } - const details = document.createElement('details'); + const details = document.createElement("details"); const classNames = new Set(card.className.split(/\s+/).filter(Boolean)); classNames.add('group'); details.className = Array.from(classNames).join(' '); diff --git a/Modules/Helpdesk/Services/SlaService.php b/Modules/Helpdesk/Services/SlaService.php index 7b2aa2c4..b16c2717 100644 --- a/Modules/Helpdesk/Services/SlaService.php +++ b/Modules/Helpdesk/Services/SlaService.php @@ -6,6 +6,7 @@ namespace Modules\Helpdesk\Services; use App\Models\User; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Schema; use Modules\Helpdesk\Models\Comment; use Modules\Helpdesk\Models\SlaPolicy; use Modules\Helpdesk\Models\Ticket; @@ -72,14 +73,19 @@ final class SlaService return; } - /** @var Comment|null $firstPublicComment */ - $firstPublicComment = Comment::query() + /** @var \Illuminate\Database\Eloquent\Builder $query */ + $query = Comment::query() ->where('ticket_id', $ticket->id) ->where('is_internal', false) - ->whereNull('tombstoned_at') ->with('user') - ->oldest('created_at') - ->first(); + ->oldest('created_at'); + + if (Schema::hasColumn('helpdesk_comments', 'tombstoned_at')) { + $query->whereNull('tombstoned_at'); + } + + /** @var Comment|null $firstPublicComment */ + $firstPublicComment = $query->first(); if ($firstPublicComment === null) { return; diff --git a/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php b/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php index 22b469b5..6e2c4803 100644 --- a/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php +++ b/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php @@ -34,12 +34,11 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase $this->setHelpdeskSelect($browser, 'team_id', $fixtures['team']->id); $this->setHelpdeskSelect($browser, 'project_id', $fixtures['project']->id); - $browser->pause(500) - ->press('@ticket-create-submit') - ->waitFor('@helpdesk-ticket-index', 10) - ->assertSee($subject) - ->assertSee('High') - ->assertSee('Support'); + $browser->press('@ticket-create-submit') + ->waitUntilMissing('@helpdesk-ticket-create', 15) + ->assertPathBeginsWith('/helpdesk/tickets/') + ->waitForText($subject, 15) + ->assertSee($subject); }); } diff --git a/Modules/Helpdesk/changelog.yaml b/Modules/Helpdesk/changelog.yaml index 071b1e37..3f33b8e9 100644 --- a/Modules/Helpdesk/changelog.yaml +++ b/Modules/Helpdesk/changelog.yaml @@ -550,3 +550,14 @@ - 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.