Flash Toast Bridge
Kode Laravel biasanya sudah menggunakan redirect()->with('success', 'Saved.'). Frontend Panel juga sudah mendengarkan satu channel toast, yaitu flash.toast. Flash bridge menghubungkan keduanya agar aplikasi memiliki satu mekanisme toast, bukan dua sistem yang saling bersaing. Anda tidak pernah memanggilnya secara langsung — ini adalah middleware pada group web — tetapi memahami aturannya penting untuk mengetahui pesan mana yang akan diprioritaskan.
Contoh minimal yang berfungsi
<?php
use Illuminate\Http\RedirectResponse;
Route::post('/orders/{order}/approve', function (Order $order): RedirectResponse {
$order->approve();
return back()->with('success', 'Order approved.');
})->middleware('web');2
3
4
5
6
7
8
9
Page berikutnya dirender dengan toast hijau. Kode aplikasi tidak perlu mengimpor apa pun dari Panel.
Middleware
PandaPanel\Http\Middleware\ShareFlashToast berjalan pada setiap request web:
public function handle(Request $request, Closure $next): ResponseMiddleware membaca empat session key konvensional dan menulis nilai pertama yang tidak kosong ke flash bag Inertia sebagai toast:
private const TYPES = ['error', 'warning', 'success', 'info'];Urutannya berdasarkan severity, bukan alfabet. Jika sebuah request secara tidak sengaja mem-flash success sekaligus error, error yang ditampilkan:
return redirect('/')
->with('success', 'Saved.')
->with('error', 'Went wrong.');
// The page renders: ['type' => 'error', 'message' => 'Went wrong.']2
3
4
5
Hanya match pertama yang ditulis karena loop langsung berhenti, sehingga satu request menghasilkan maksimal satu toast.
Tiga kondisi membuat middleware tidak melakukan apa pun:
| Kondisi | Perilaku |
|---|---|
| Request tidak memiliki session | langsung diteruskan |
Inertia::getFlashed($request)['toast'] sudah terisi | dibiarkan apa adanya |
Tidak ada key error/warning/success/info yang berisi string non-kosong | tidak menulis apa pun |
Mem-flash toast secara eksplisit
Toast eksplisit selalu menang atas conventional key. Ini juga cara mengirim toast yang memiliki link:
use Inertia\Inertia;
Inertia::flash('toast', [
'type' => 'success',
'message' => 'Your export of 1,204 records is ready.',
'url' => route('panel.admin.export-file', ['file' => $file, 'exporter' => $exporter]),
'urlLabel' => 'Download',
]);
return back();2
3
4
5
6
7
8
9
10
| Key | Type | Wajib |
|---|---|---|
type | 'success'|'error'|'warning'|'info' | ya |
message | string | ya |
url | string|null | tidak |
urlLabel | string|null | tidak — fallback ke Open |
Frontend mengabaikan URL flash kecuali URL tersebut relatif atau menggunakan scheme http, https, mailto, atau tel. Guard yang sama dipakai untuk notification link, table cell link, URL hasil pencarian, dan action link, sehingga flash value lama atau berbahaya tidak dapat berubah menjadi navigasi javascript:.
Shape tersebut adalah FlashToast di frontend/host/types/ui.ts, satu-satunya host-seam type yang diimpor Panel:
export interface FlashToast {
type: 'success' | 'error' | 'warning' | 'info';
message: string;
url?: string | null;
urlLabel?: string | null;
}2
3
4
5
6
Conventional key hanya dapat menghasilkan type dan message. url dan urlLabel membutuhkan pemanggilan eksplisit karena session string tidak memiliki tempat untuk menyimpan keduanya.
Tempat Panel menggunakannya
Ada dua penggunaan utama yang layak dijadikan pola:
Semua action. PanelActionController menjawab action yang sukses dengan back()->with('success', $action->getSuccessMessage()), sehingga successMessage() pada action berubah menjadi toast melalui bridge ini dan tidak membutuhkan mekanisme lain.
Synchronous export. ExportAction mem-persist notifikasi agar file dapat ditemukan lagi, tetapi menggunakan flash untuk toast daripada broadcasting karena response-nya sedang tersedia:
Notification::make('export-ready')
->title($exporter::completedMessage($result['records']))
->success()
->icon('download')
->persistent()
->broadcast(false) // the flash below is the toast
->actions([
NotificationAction::make('download')->label('Download')->url($url),
])
->send($user);
Inertia::flash('toast', [
'type' => 'success',
'message' => $exporter::completedMessage($result['records']),
'url' => $url,
'urlLabel' => 'Download',
]);2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ImportAction menggunakan pola yang sama dan mem-flash toast info — "Your import has started. You will be notified when it finishes." — ketika pekerjaan diserahkan ke queue.
Sisi frontend
resources/js/lib/flashToast.ts mendaftarkan satu Inertia listener:
import { initializeFlashToast } from '@/lib/flashToast';
initializeFlashToast();2
3
import { safeUrl } from '@/lib/utils';
export function initializeFlashToast(): void {
router.on('flash', (event) => {
const data = (event as CustomEvent).detail?.flash?.toast as FlashToast | undefined;
if (!data) {
return;
}
const url = safeUrl(data.url);
toast[data.type](data.message, {
action: url
? { label: data.urlLabel ?? 'Open', onClick: () => { window.location.href = url; } }
: undefined,
});
});
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Panggil satu kali di resources/js/app.ts milik aplikasi. Listener mendengarkan event flash milik Inertia, sehingga berlaku untuk seluruh page aplikasi, bukan hanya page Panel. Toast dirender oleh <Toaster /> yang dipasang shell Panel.
Registrasi dan opt-out
Middleware didaftarkan oleh PandaPanel\PandaPanelServiceProvider ke seluruh group web, bersama ResetPanelContext, RedirectPanelHome, dan SharePanelData:
private const WEB_MIDDLEWARE = [
ResetPanelContext::class,
RedirectPanelHome::class,
ShareFlashToast::class,
SharePanelData::class,
];2
3
4
5
6
Middleware berada pada group web, bukan pada route group Panel, karena harus tetap berjalan pada redirect yang keluar dari Panel — request yang menghasilkan message berbeda dengan request berikutnya yang merender message.
Matikan registrasi otomatis di config/panda-panel.php jika Anda ingin memasangnya sendiri:
'register_web_middleware' => false,Lalu tambahkan di bootstrap/app.php:
use PandaPanel\Http\Middleware\ShareFlashToast;
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [ShareFlashToast::class]);
})2
3
4
5
Catatan
- Hanya string yang dibaca.
->with('success', ['a', 'b'])tidak menghasilkan toast: value harus berupa string non-kosong. Nilai lain diabaikan, bukan di-coerce. with()pada response yang bukan redirect tidak berguna untuk alur ini. Bridge membaca session, sehingga message harus bertahan sampai request berikutnya — seperti yang dilakukanredirect()->with()danback()->with().- Flash bag bukan props. Inertia meletakkan flash data di samping
propspada page object. Dalam test, baca$response->viewData('page')['flash']['toast'], bukan memakai assertion prop Inertia. - Broadcast toast tidak melewati bridge ini. Notifikasi dari queued job datang melalui websocket dan ditangani
usePanelBroadcasting, bukan listener ini. Keduanya sengaja menghasilkan tampilan toast yang sama.
Lihat juga
- Toast notifications — channel transient yang menerima hasil bridge ini
- Broadcasting — jalur lain toast dapat tiba
- Actions overview —
successMessage()yang berubah menjadi flash - Import and export actions
- Konfigurasi middleware
- Testing notifications