Toast Notifications
Toast adalah sisi transient dari panel notification: pesan singkat yang muncul di atas page yang sedang terbuka lalu hilang beberapa detik kemudian. Cocok untuk "Saved.", tetapi tidak cocok untuk job yang selesai sepuluh menit setelah request awal — jika tidak ada yang melihat, toast seolah tidak pernah terjadi. Gunakan toast ketika notifikasi menjawab aksi yang baru saja dilakukan user; gunakan database notifications jika pesan harus tetap ada setelah tab ditutup.
Contoh minimal yang berfungsi
<?php
use PandaPanel\Notifications\Notification;
Notification::make('saved')
->title('Saved.')
->success()
->send($request->user());2
3
4
5
6
7
8
Kode tersebut melakukan broadcast dan tidak menyimpan row. Panel yang sedang dibuka user menampilkan toast hijau; user yang sedang tidak membuka Panel tidak melihat apa pun, sesuai sifat pesan "Saved.".
Tiga destination
Notification adalah satu object dengan tiga kemungkinan destination yang dapat dikombinasikan:
| Destination | Diaktifkan oleh | Yang terjadi |
|---|---|---|
| Toast | aktif secara default | PanelNotificationSent di-dispatch ke private channel user |
| Database | ->persistent() | sebuah row ditulis ke table notifications Laravel |
| Keduanya | cukup ->persistent() | dipersist terlebih dahulu, lalu dibroadcast |
use PandaPanel\Notifications\Notification;
use PandaPanel\Notifications\NotificationAction;
Notification::make('export-ready')
->title('Your export is ready')
->body('1,204 records')
->success()
->persistent() // also store it, so it can be read later
->actions([
NotificationAction::make('download')
->label('Download')
->url('/admin/exports/users.csv'),
])
->send($user);2
3
4
5
6
7
8
9
10
11
12
13
14
URL pada notification action disanitasi sebelum disimpan atau dibroadcast. URL relatif serta scheme http, https, mailto, dan tel dipertahankan; unsafe scheme dibuang, dan component Vue notification menjalankan guard yang sama sebelum membuka link.
->persistent() nonaktif secara default. Sebagian besar notifikasi hanya menjawab hal yang baru saja dilakukan user, dan bell yang penuh dengan "Saved." akan kehilangan maknanya.
->broadcast(false) mematikan toast ketika response sudah membawa toast yang sama agar pesan tidak tampil dua kali. ExportAction menggunakan pola ini: notifikasi dipersist, lalu toast dikirim melalui flash pada response yang memang sedang dikembalikan.
Semua method pada Notification
PandaPanel\Notifications\Notification bersifat final, dan setiap setter mengembalikan self.
| Method | Signature | Default |
|---|---|---|
make | static make(?string $name = null): self | UUID ketika nama tidak diberikan |
title | title(string $title): self | Str::headline($name) |
body | body(string $body): self | null |
icon | icon(string $icon): self | icon milik color |
color | color(NotificationColor $color): self | NotificationColor::Info |
success | success(): self | — |
info | info(): self | — |
warning | warning(): self | — |
danger | danger(): self | — |
actions | actions(array $actions): self — array<array-key, NotificationAction> | [] |
persistent | persistent(bool $persistent = true): self | false |
broadcast | broadcast(bool $broadcast = true): self | true |
getName | getName(): string | — |
isPersistent | isPersistent(): bool | — |
isBroadcast | isBroadcast(): bool | — |
send | send(Authenticatable $user): self | — |
toArray | toArray(): array<string, mixed> | — |
use Illuminate\Support\Str;
use PandaPanel\Notifications\Enums\NotificationColor;
use PandaPanel\Notifications\Notification;
// The name is an identifier, not copy. Leaving it out gives you a UUID,
// which is fine for a notification nothing else refers to.
$notification = Notification::make();
// The title falls back to the headline of the name, so a well-named
// notification often needs no title at all.
Notification::make('export-ready')->toArray()['title']; // 'Export Ready'
// The four shorthands are the same call.
Notification::make('a')->color(NotificationColor::Warning);
Notification::make('a')->warning();2
3
4
5
6
7
8
9
10
11
12
13
14
15
send() adalah satu-satunya method yang menimbulkan side effect. Method ini melakukan persist terlebih dahulu lalu broadcast, sehingga user yang mengklik action toast menuju notification center akan menemukan row-nya sudah tersedia.
Color
PandaPanel\Notifications\Enums\NotificationColor merupakan closed set berisi empat case. Setiap case dipetakan ke literal frontend classes agar class tidak hilang ketika dikompilasi Tailwind.
| Case | Value | icon() | toastType() |
|---|---|---|---|
NotificationColor::Info | info | info | info |
NotificationColor::Success | success | check | success |
NotificationColor::Warning | warning | triangle-alert | warning |
NotificationColor::Danger | danger | circle-alert | error |
use PandaPanel\Notifications\Enums\NotificationColor;
NotificationColor::Danger->icon(); // 'circle-alert'
NotificationColor::Danger->toastType(); // 'error'2
3
4
toastType() diperlukan karena channel toast sama dengan yang digunakan flash message dan vocabulary-nya adalah success|info|warning|error, bukan nama color Panel. Mapping dilakukan di server agar frontend tidak perlu menentukan arti color.
Data yang benar-benar melewati wire
toArray() adalah shape yang dibawa kedua channel — satu shape yang sama agar notifikasi terlihat konsisten baik datang dari websocket maupun dibaca dari table satu jam kemudian:
[
'name' => 'export-ready',
'title' => 'Your export is ready',
'body' => '1,204 records',
'color' => 'success',
'icon' => 'download',
'actions' => [ /* each NotificationAction::toArray() */ ],
'type' => 'success', // the toast channel, resolved from the colour
'persistent' => true, // so the bell knows to refetch rather than guess
]2
3
4
5
6
7
8
9
10
Broadcast menambahkan message dan menegaskan kembali persistent di PanelNotificationSent::broadcastWith():
[
...$payload,
'message' => $payload['title'] ?? '', // what the toast reads
'persistent' => true, // whether the bell has a row to fetch
]2
3
4
5
Toast membaca message; bell membaca title dan body. Keduanya dikirim eksplisit sehingga tidak ada sisi yang perlu menebak.
Toast tanpa stored notification
Untuk pesan yang tidak perlu menjadi notifikasi lengkap — tidak perlu semantic color tambahan dan tidak perlu row persisten — dispatch broadcast event secara langsung:
use PandaPanel\Broadcasting\PanelNotification;
PanelNotification::dispatch(
$user,
'Your export is ready.',
'success',
'/admin/exports/users.csv', // optional: something to open
'Download', // optional: the link's label
);2
3
4
5
6
7
8
9
| Constructor argument | Type | Default |
|---|---|---|
$user | Illuminate\Contracts\Auth\Authenticatable | — |
$message | string | — |
$type | 'success'|'info'|'warning'|'error' | 'info' |
$url | string|null | null |
$urlLabel | string|null | null |
Ini satu-satunya cara menaruh link pada broadcast toast — toast yang dibawa response juga dapat memiliki link dengan flash. PanelNotification::broadcastWith() membawa url dan urlLabel, yang kemudian dijadikan action button oleh client. Lihat Broadcasting.
Cara toast dirender
resources/js/panel/composables/usePanelBroadcasting.ts dipanggil satu kali di PanelLayout.vue, sehingga satu subscription mencakup seluruh route Panel:
import { usePanelBroadcasting } from '@/panel/composables/usePanelBroadcasting';
usePanelBroadcasting();2
3
Composable mendengarkan .panel.notification pada private channel yang dikirim server, melakukan narrowing payload, lalu memanggil vue-sonner:
import { safeUrl } from '@/lib/utils';
toast[notification.type](notification.message, {
action: safeUrl(notification.url) === null
? undefined
: {
label: notification.urlLabel ?? 'Open',
onClick: () => { window.location.href = safeUrl(notification.url) as string; },
},
});2
3
4
5
6
7
8
9
10
Ini berupa link, bukan navigasi otomatis: toast dapat datang saat user sedang melakukan pekerjaan lain, dan memindahkan mereka tanpa persetujuan akan lebih buruk daripada membiarkan file menunggu.
<Toaster /> berada pada shell Panel — SidebarPanelLayout.vue, HeaderPanelLayout.vue, dan PanelAuthLayout.vue. Page yang menggunakan layout sendiri dan bukan salah satu shell tersebut tidak memiliki toast renderer.
Hal yang perlu diperhatikan
- Toast menampilkan title, bukan body.
messagediisi daripayload['title'], sehingga->body()hanya terlihat di bell. Jika sebuah kalimat penting, letakkan pada title. - Action milik
Notificationtidak pernah tampil pada toast. Client toast membacaurldanurlLabel, sedangkanNotification::toArray()tidak menghasilkan keduanya — arrayactionsdigunakan notification center. Untuk clickable toast, dispatchPanelNotificationdengan$url, atau gunakan flash toast denganurldanurlLabelmelalui Flash toast bridge. - Tidak ada delivery tanpa broadcaster. Channel hanya dibagikan ke frontend ketika broadcasting Panel aktif dan
BroadcastSupport::isConfigured()true. Sampai saat itusend()tetap mendispatch event tetapi tidak ada pesan yang sampai ke browser. Lihat Setup Reverb dan Echo. icon()menerima registry name, bukan Lucide class. Nama yang tidak ada diresources/js/panel/icons/registry.tstidak merender icon. Jalankanphp artisan panel:iconssetelah menambahkan icon baru.send()membutuhkan user, bukan sembarang notifiable. Parameter-nyaAuthenticatable. Sisi database dilewati tanpa exception untuk model yang tidak memilikinotify(), sehingga toast tetap dapat bekerja pada user model yang tidakNotifiable.
Lihat juga
- Flash toast bridge — mengubah
redirect()->with('success', …)menjadi toast yang sama - Database notifications —
->persistent()dan tablenotifications - Notification actions — tombol yang dibawa stored notification
- Notification center — bell dan endpoint-nya
- Broadcasting — event, channel, dan payload
- Error notifications — pesan saat request gagal
- Testing notifications