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-61). Komponen ticket-comment-form baru dipasang ketika syarat itu benar (resources/views/filament/resources/ticket-resource/comments-section.blade.php:97-992).
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-353).
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-614). Event itu didengar oleh comments-section untuk memuat ulang daftar komentar (resources/views/filament/resources/ticket-resource/comments-section.blade.php:15).
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.Kolom yang boleh diisi pada model hanya ticket_id, user_id, dan comment (app/Models/TicketComment.php:11-156), dengan relasi ticket() dan user() (app/Models/TicketComment.php:30-387). 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-208).
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-289). 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-8410). 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-3911).
⚠️ Pesan notifikasi dibangun dari
{$ticket->title}(app/Services/NotificationService.php:18-3911), sedangkan kolom nama ticket pada model adalahname(app/Models/Ticket.php:16-2712). 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-5913).
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.Karena hook berada di updating, status yang diisi saat pembuatan ticket tidak menghasilkan baris riwayat (app/Models/Ticket.php:51-5913). 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-39214).
Model TicketHistory hanya mengizinkan ticket_id, user_id, dan ticket_status_id (app/Models/TicketHistory.php:13-1715), dan relasi status() dipetakan ke TicketStatus melalui kolom ticket_status_id (app/Models/TicketHistory.php:29-3216). 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-2017).
⚠️ Hook memakai
auth()->id()tanpa penjagaan (app/Models/Ticket.php:51-5913). Jika perubahan status terjadi tanpa user login — misalnya dariartisan tinker, job antrean, atau seeder — nilaiuser_idmenjadinulldan insert ditolak karena kolomnya tidaknullable(database/migrations/2025_03_28_144500_create_ticket_histories_table.php:14-2017).
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-9018). 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-29419).
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-6320). Nama user diberi fallback 'System', sedangkan nama status diakses langsung tanpa fallback (resources/views/filament/resources/ticket-resource/timeline-history.blade.php:52-6021).
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-8722). 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-12723). 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-7124).
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-4225). Portal eksternal memakai pola serupa per project dengan paginate(10, ['*'], 'activities') (app/Livewire/ExternalDashboard.php:147-15526). Perhitungan status_changes pada halaman leaderboard juga membaca TicketHistory::where('user_id', $userId) (app/Filament/Pages/Leaderboard.php:11227); rincian agregasinya ada di Dashboard, Analitik, dan Leaderboard dan Portal Eksternal Klien.
Ringkasan kolom
| Tabel | Kolom | Aturan |
|---|---|---|
ticket_comments | id, ticket_id, user_id, comment, timestamps | comment bertipe longtext; kedua foreign key cascadeOnDelete (database/migrations/2025_04_11_173545_create_ticket_comments_table.php:14-208) |
ticket_histories | id, ticket_id, user_id, ticket_status_id, timestamps | ketiga foreign key cascadeOnDelete; ticket_status_id mengarah ke ticket_statuses (database/migrations/2025_03_28_144500_create_ticket_histories_table.php:14-2017) |
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.