Lompat ke konten
dokumentasi.id
Commit a2522f5
v1
Star645

6.2

Komentar dan Riwayat Ticket

Ticket menyimpan dua jejak aktivitas yang terpisah. TicketComment merekam diskusi anggota project pada satu ticket, sedangkan TicketHistory merekam satu baris setiap kali ticket_status_id berubah. Keduanya memakai relasi yang sama ke Ticket dan User, tetapi dipicu mekanisme berbeda: komentar lewat komponen Livewire, riwayat lewat model event di Ticket.

Alur komentar dari comments-section ke TicketCommentForm

Blok komentar dipasang oleh comments-section.blade.php. Berkas itu menghitung $canComment dari $project->members()->where('users.id', auth()->id())->exists(), sehingga user di luar project tidak melihat form (resources/views/filament/resources/ticket-resource/comments-section.blade.php:4-6). Komponen ticket-comment-form baru dipasang ketika syarat itu benar (resources/views/filament/resources/ticket-resource/comments-section.blade.php:97-99).

TicketCommentForm hanya mendeklarasikan satu field, RichEditor bernama newComment, dan field itu wajib diisi. Lampiran disimpan di disk public dalam direktori attachments dengan tipe yang diterima image/png, image/jpeg, image/gif, image/webp, dan video/mp4 (app/Livewire/TicketCommentForm.php:27-35).

Saat addComment dijalankan, komponen mengambil state form lalu membuat baris lewat relasi $this->ticket->comments()->create([...]) dengan user_id dari auth()->id(). Setelah itu notifikasi milik user yang sedang login untuk ticket tersebut ditandai terbaca, notifikasi Filament Comment added successfully dikirim, form dikosongkan, dan event comment-added di-dispatch (app/Livewire/TicketCommentForm.php:39-61). Event itu didengar oleh comments-section untuk memuat ulang daftar komentar (resources/views/filament/resources/ticket-resource/comments-section.blade.php:1).

sequenceDiagram
  participant B as comments-section.blade.php
  participant L as TicketCommentForm
  participant T as Ticket
  participant C as TicketComment
  participant N as NotificationService
  B->>L: livewire ticket-comment-form
  L->>L: form getState
  L->>T: comments create user_id comment
  T-->>C: created event
  C->>N: notifyCommentAdded
  N-->>C: Notification per penerima
  L->>B: dispatch comment-added
%% fig: Siklus hidup satu komentar baru
%% desc: Form Livewire membuat baris lewat relasi comments pada Ticket, lalu model event TicketComment memanggil NotificationService untuk setiap penerima.
Fig. 1: Siklus hidup satu komentar baruForm Livewire membuat baris lewat relasi comments pada Ticket, lalu model event TicketComment memanggil NotificationService untuk setiap penerima.

Kolom yang boleh diisi pada model hanya ticket_id, user_id, dan comment (app/Models/TicketComment.php:11-15), dengan relasi ticket() dan user() (app/Models/TicketComment.php:30-38). Tabel ticket_comments menyimpan comment sebagai longtext dan memakai foreign key ticket_id serta user_id dengan cascadeOnDelete (database/migrations/2025_04_11_173545_create_ticket_comments_table.php:14-20).

Notifikasi komentar

TicketComment memakai hook booted() untuk memanggil NotificationService. Hook created selalu memanggil notifyCommentAdded, sedangkan hook updated hanya memanggil notifyCommentUpdated ketika wasChanged('comment') bernilai benar (app/Models/TicketComment.php:17-28). Perubahan kolom lain pada baris komentar tidak menghasilkan notifikasi.

Daftar penerima disusun getUsersToNotifyForComment: creator ticket, user yang di-assign, dan komentator lain pada ticket yang sama, semuanya kecuali penulis komentar, lalu di-unique-kan per id (app/Services/NotificationService.php:64-84). Setiap penerima mendapat baris App\Models\Notification dengan type comment_added dan data berisi ticket_id, comment_id, commenter_id, dan commenter_name (app/Services/NotificationService.php:18-39).

⚠️ Pesan notifikasi dibangun dari {$ticket->title} (app/Services/NotificationService.php:18-39), sedangkan kolom nama ticket pada model adalah name (app/Models/Ticket.php:16-27). Periksa keluaran pesan jika judul ticket tampil kosong.

Pencatatan TicketHistory saat status berubah

Riwayat status tidak ditulis oleh komponen mana pun. Penulisan terjadi di model event updating pada Ticket: ketika isDirty('ticket_status_id') benar, TicketHistory::create dipanggil dengan ticket_id, user_id dari auth()->id(), dan ticket_status_id yang baru (app/Models/Ticket.php:51-59).

graph LR
  U["Ticket update: ticket_status_id berubah"] --> D{"isDirty ticket_status_id"}
  D -->|ya| H["TicketHistory::create"]
  D -->|tidak| S["tidak ada baris riwayat"]
  H --> T[("ticket_histories")]
  T --> V["histories desc di ViewTicket"]
  style T fill:#336791,color:#fff
  style H fill:#2d7d46,color:#fff
  style S fill:#dc2626,color:#fff
  style D fill:#f59e0b,color:#000
  style V fill:#8b5cf6,color:#fff
%% fig: Pemicu pencatatan TicketHistory
%% desc: Hanya perubahan ticket_status_id pada update yang membuat baris ticket_histories, lalu baris itu dibaca berurutan menurun.
Fig. 2: Pemicu pencatatan TicketHistoryHanya perubahan ticket_status_id pada update yang membuat baris ticket_histories, lalu baris itu dibaca berurutan menurun.

Karena hook berada di updating, status yang diisi saat pembuatan ticket tidak menghasilkan baris riwayat (app/Models/Ticket.php:51-59). Bulk action updateStatus pada relation manager memanggil $record->update(['ticket_status_id' => $data['ticket_status_id']]) untuk setiap record, sehingga hook di atas berjalan satu kali per ticket yang statusnya benar-benar berubah (app/Filament/Resources/Projects/RelationManagers/TicketsRelationManager.php:365-392).

Model TicketHistory hanya mengizinkan ticket_id, user_id, dan ticket_status_id (app/Models/TicketHistory.php:13-17), dan relasi status() dipetakan ke TicketStatus melalui kolom ticket_status_id (app/Models/TicketHistory.php:29-32). Tabel ticket_histories menyimpan ketiga foreign key itu dengan cascadeOnDelete, dan user_id tidak nullable (database/migrations/2025_03_28_144500_create_ticket_histories_table.php:14-20).

⚠️ Hook memakai auth()->id() tanpa penjagaan (app/Models/Ticket.php:51-59). Jika perubahan status terjadi tanpa user login — misalnya dari artisan tinker, job antrean, atau seeder — nilai user_id menjadi null dan insert ditolak karena kolomnya tidak nullable (database/migrations/2025_03_28_144500_create_ticket_histories_table.php:14-20).

Membaca komentar dan riwayat

Kedua relasi pada Ticket mengurutkan hasilnya berbeda: comments() memakai orderBy('created_at', 'asc'), sedangkan histories() memakai orderBy('created_at', 'desc') (app/Models/Ticket.php:82-90). Halaman ViewTicket menampilkan komentar pada Section Comments dan riwayat pada Section Status History yang collapsible serta awalnya tertutup (app/Filament/Resources/Tickets/Pages/ViewTicket.php:285-294).

Timeline riwayat memuat relasi user dan status, mengurutkan ulang secara menurun, lalu menampilkan $history->status->name beserta Updated by: ... dan waktu d M H:i (resources/views/filament/resources/ticket-resource/timeline-history.blade.php:35-63). Nama user diberi fallback 'System', sedangkan nama status diakses langsung tanpa fallback (resources/views/filament/resources/ticket-resource/timeline-history.blade.php:52-60).

Hak ubah dan hapus komentar diperiksa di halaman ViewTicket. editCommentAction menolak user yang bukan pemilik komentar dan bukan super_admin dengan notifikasi You do not have permission to edit this comment, lalu menyimpan lewat $comment->update([...]) (app/Filament/Resources/Tickets/Pages/ViewTicket.php:27-87). deleteCommentAction memakai aturan yang sama dengan pesan You do not have permission to delete this comment sebelum memanggil $comment->delete() (app/Filament/Resources/Tickets/Pages/ViewTicket.php:89-127). Tombol edit dan hapus di antarmuka hanya dirender untuk super_admin atau pemilik komentar (resources/views/filament/resources/ticket-resource/comments-section.blade.php:45-71).

Konsumen data riwayat

TicketHistory juga menjadi sumber beberapa tampilan lintas ticket. Widget RecentActivityTable mengambil TicketHistory terbaru dan membatasi hasil ke project tempat user menjadi anggota kecuali super_admin (app/Filament/Widgets/RecentActivityTable.php:33-42). Portal eksternal memakai pola serupa per project dengan paginate(10, ['*'], 'activities') (app/Livewire/ExternalDashboard.php:147-155). Perhitungan status_changes pada halaman leaderboard juga membaca TicketHistory::where('user_id', $userId) (app/Filament/Pages/Leaderboard.php:112); rincian agregasinya ada di Dashboard, Analitik, dan Leaderboard dan Portal Eksternal Klien.

Ringkasan kolom

TabelKolomAturan
ticket_commentsid, ticket_id, user_id, comment, timestampscomment bertipe longtext; kedua foreign key cascadeOnDelete (database/migrations/2025_04_11_173545_create_ticket_comments_table.php:14-20)
ticket_historiesid, ticket_id, user_id, ticket_status_id, timestampsketiga foreign key cascadeOnDelete; ticket_status_id mengarah ke ticket_statuses (database/migrations/2025_03_28_144500_create_ticket_histories_table.php:14-20)

Status ticket sendiri, termasuk daftar status per project dan warna badge yang dipakai widget aktivitas, dibahas di Status, Prioritas, dan Epic. Aturan siapa yang boleh mengubah komentar terkait peran super_admin dijelaskan lebih lengkap di Kontrol Akses dan Peran.