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 new file mode 100644 index 00000000..9436569a --- /dev/null +++ b/Modules/Helpdesk/Database/Migrations/2026_06_17_123000_add_deletion_audit_fields_to_helpdesk_comments.php @@ -0,0 +1,36 @@ +foreignId('deleted_by')->nullable()->after('tombstoned_at')->constrained('users')->nullOnDelete(); + } + + if (! Schema::hasColumn('helpdesk_comments', 'delete_reason')) { + $table->string('delete_reason', 255)->nullable()->after('deleted_by'); + } + }); + } + + public function down(): void + { + Schema::table('helpdesk_comments', static function (Blueprint $table): void { + if (Schema::hasColumn('helpdesk_comments', 'deleted_by')) { + $table->dropConstrainedForeignId('deleted_by'); + } + + if (Schema::hasColumn('helpdesk_comments', 'delete_reason')) { + $table->dropColumn('delete_reason'); + } + }); + } +}; diff --git a/Modules/Helpdesk/Http/Controllers/TicketCommentDeleteController.php b/Modules/Helpdesk/Http/Controllers/TicketCommentDeleteController.php new file mode 100644 index 00000000..4f8f578e --- /dev/null +++ b/Modules/Helpdesk/Http/Controllers/TicketCommentDeleteController.php @@ -0,0 +1,75 @@ +user(); + + abort_unless($user !== null && method_exists($user, 'isMpbaPlatformAdmin') && $user->isMpbaPlatformAdmin(), 403); + abort_unless((int) $comment->ticket_id === (int) $ticket->id, 404); + + DB::transaction(function () use ($comment, $ticket, $user): void { + $updates = []; + + if (Schema::hasColumn('helpdesk_comments', 'tombstoned_at')) { + $updates['tombstoned_at'] = now(); + } + + if (Schema::hasColumn('helpdesk_comments', 'deleted_by')) { + $updates['deleted_by'] = (int) $user->id; + } + + if (Schema::hasColumn('helpdesk_comments', 'delete_reason')) { + $updates['delete_reason'] = 'Deleted by MPBA platform administrator.'; + } + + if (Schema::hasColumn('helpdesk_comments', 'sync_sequence')) { + $updates['sync_sequence'] = ((int) ($comment->sync_sequence ?? 0)) + 1; + } + + if (Schema::hasColumn('helpdesk_comments', 'sync_hash')) { + $updates['sync_hash'] = hash('sha256', implode('|', [ + (string) ($comment->guid ?? $comment->id), + 'deleted', + now()->toISOString(), + ])); + } + + if (Schema::hasColumn('helpdesk_comments', 'updated_at')) { + $updates['updated_at'] = now(); + } + + if ($updates !== []) { + DB::table('helpdesk_comments') + ->where('id', $comment->id) + ->update($updates); + } else { + $comment->delete(); + } + + if (Schema::hasColumn('helpdesk_tickets', 'last_activity_at')) { + $ticket->forceFill(['last_activity_at' => now()])->save(); + } + }); + + return redirect() + ->route('helpdesk.tickets.show', $ticket) + ->with('ticket-comment-deleted', 'Response deleted.'); + } +} diff --git a/Modules/Helpdesk/Livewire/TicketShow.php b/Modules/Helpdesk/Livewire/TicketShow.php index c057ebcd..5e0979d1 100644 --- a/Modules/Helpdesk/Livewire/TicketShow.php +++ b/Modules/Helpdesk/Livewire/TicketShow.php @@ -303,6 +303,9 @@ final class TicketShow extends Component $comments = Comment::query() ->where('ticket_id', $ticket->id) + ->when(Schema::hasColumn('helpdesk_comments', 'tombstoned_at'), static function ($query): void { + $query->whereNull('tombstoned_at'); + }) ->when(! $this->canSeeInternalNotes(), static function ($query): void { $query->where('is_internal', false); }) diff --git a/Modules/Helpdesk/Models/Comment.php b/Modules/Helpdesk/Models/Comment.php index 6d33816a..1229a72b 100644 --- a/Modules/Helpdesk/Models/Comment.php +++ b/Modules/Helpdesk/Models/Comment.php @@ -35,6 +35,8 @@ final class Comment extends Model 'replicated_at', 'synced_at', 'tombstoned_at', + 'deleted_by', + 'delete_reason', ]; /** @var array */ @@ -44,6 +46,7 @@ final class Comment extends Model 'replicated_at' => 'datetime', 'synced_at' => 'datetime', 'tombstoned_at' => 'datetime', + 'deleted_by' => 'integer', ]; /** @return BelongsTo */ @@ -64,6 +67,12 @@ final class Comment extends Model return $this->hasMany(self::class, 'parent_id')->oldest(); } + /** @return BelongsTo */ + public function deleter(): BelongsTo + { + return $this->belongsTo(User::class, 'deleted_by'); + } + /** @return BelongsTo */ public function user(): BelongsTo { diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php index 92997585..4fdc931d 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php @@ -3,11 +3,6 @@ 'resources/css/app.css', 'resources/js/app.js', ]) - @vite([ - 'Resources/assets/css/app.css', - 'Resources/assets/js/app.js', - ], 'build/helpdesk') - @include('helpdesk::partials.page-header', [ 'eyebrow' => 'Helpdesk', 'title' => 'Create ticket', diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php index 6a75da1f..6a7b8be6 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-show.blade.php @@ -181,34 +181,31 @@ .hd-helpdesk-surface [data-helpdesk-file-button] { display: inline-flex !important; - width: auto !important; - min-width: 144px !important; - height: 42px !important; align-items: center !important; justify-content: center !important; - align-self: center !important; - gap: 9px !important; - border-radius: 12px !important; - border: 1px solid rgba(34, 197, 94, 0.55) !important; - background: linear-gradient(135deg, #16a34a, #22c55e) !important; + gap: 10px !important; + min-width: 168px !important; + height: 48px !important; + padding: 0 22px !important; + border: 1px solid rgba(134, 239, 172, 0.70) !important; + border-radius: 14px !important; + background: linear-gradient(135deg, #15803d 0%, #16a34a 55%, #22c55e 100%) !important; color: #ffffff !important; - box-shadow: 0 10px 22px rgba(22, 163, 74, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.18) !important; - font-size: 0.88rem !important; - font-weight: 800 !important; + box-shadow: 0 14px 28px rgba(22, 163, 74, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.20) !important; + font-size: 0.92rem !important; + font-weight: 900 !important; line-height: 1 !important; - letter-spacing: 0.005em !important; - padding: 0 16px !important; + text-align: center !important; white-space: nowrap !important; - vertical-align: middle !important; - transition: border-color 150ms ease, background 150ms ease, color 150ms ease, transform 150ms ease, box-shadow 150ms ease !important; + cursor: pointer !important; + transition: transform 150ms ease, box-shadow 150ms ease, background 150ms ease !important; } .hd-helpdesk-surface [data-helpdesk-file-button]:hover { transform: translateY(-1px) !important; - border-color: rgba(134, 239, 172, 0.85) !important; - background: linear-gradient(135deg, #15803d, #16a34a) !important; + background: linear-gradient(135deg, #166534 0%, #15803d 55%, #16a34a 100%) !important; color: #ffffff !important; - box-shadow: 0 12px 26px rgba(22, 163, 74, 0.32), inset 0 1px 0 rgba(255, 255, 255, 0.20) !important; + box-shadow: 0 16px 32px rgba(22, 163, 74, 0.36), inset 0 1px 0 rgba(255, 255, 255, 0.22) !important; } .hd-helpdesk-surface [data-helpdesk-file-button] i, @@ -217,15 +214,18 @@ align-items: center !important; justify-content: center !important; line-height: 1 !important; + margin: 0 !important; + padding: 0 !important; + vertical-align: middle !important; } .hd-helpdesk-surface [data-helpdesk-file-button] i { - height: 18px !important; - width: 18px !important; - border-radius: 6px !important; - background: rgba(255, 255, 255, 0.16) !important; + width: 22px !important; + height: 22px !important; + border-radius: 8px !important; + background: rgba(255, 255, 255, 0.18) !important; color: #ffffff !important; - font-size: 0.74rem !important; + font-size: 0.8rem !important; } .hd-helpdesk-surface .hd-select { @@ -505,45 +505,44 @@ @endif @endcan -
- - - +
+ + + - Attachments - Pin screenshots, logs and supporting documents. + Attachments + Supporting files + Pin screenshots, logs and supporting documents. - 0 files - + 0 files + -
-
-
-
- - +
+
+
+
+ + - - Attach supporting files + Attach supporting evidence No files attached
- +
+ +
@error('attachments.*')
{{ $message }}
@enderror diff --git a/Modules/Helpdesk/Resources/views/partials/comment-thread.blade.php b/Modules/Helpdesk/Resources/views/partials/comment-thread.blade.php index 913496af..dd25b6e0 100644 --- a/Modules/Helpdesk/Resources/views/partials/comment-thread.blade.php +++ b/Modules/Helpdesk/Resources/views/partials/comment-thread.blade.php @@ -118,11 +118,22 @@ @endif @if ($currentTicket !== null) -
+
+ + @if (auth()->user()?->isMpbaPlatformAdmin()) +
+ @csrf + @method('DELETE') + +
+ @endif