Full Page URLs
Inertia mengubah setiap link Panel menjadi client-side visit. Beberapa destination tidak dapat bekerja dengan mekanisme tersebut: file download, route yang menetapkan header yang akan dibuang SPA, atau aplikasi lain yang bukan aplikasi ini. fullPageUrls() mendefinisikan path-path tersebut, lalu navigation yang dikirim server menandai link yang cocok sehingga shell merender plain anchor, bukan Inertia <Link>.
Keputusan dibuat di server bersama link, bukan dihitung ulang di Vue pada setiap render.
Contoh minimal yang berfungsi
<?php
declare(strict_types=1);
namespace App\Panels\Admin;
use PandaPanel\Core\Panel;
use PandaPanel\Core\PanelProvider;
final class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->path('admin')
->auth()
->fullPageUrls('/admin/exports/*', '/admin/reports/monthly');
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Setiap navigation item yang href-nya cocok dengan salah satu pattern sekarang dikirim dengan fullPage: true, lalu sidebar merendernya sebagai <a href>. Link lain tetap menggunakan client-side visit.
Method
public function fullPageUrls(string ...$patterns): self;
/** @return list<string> */
public function getFullPageUrls(): array;
public function isFullPageUrl(string $url): bool;2
3
4
5
6
use PandaPanel\Core\Panel;
$panel = Panel::make('accumulates')
->fullPageUrls('/one')
->fullPageUrls('/two', '/three');
$panel->getFullPageUrls(); // ['/one', '/two', '/three']2
3
4
5
6
7
Pemanggilan menambahkan pattern, bukan mengganti daftar sebelumnya, sehingga plugin dapat menambahkan pattern tanpa menghapus setting Panel. Duplicate digabung.
Matching
Pattern dicocokkan menggunakan Illuminate\Support\Str::is(). Karena itu * adalah satu-satunya wildcard dan semua karakter lain literal. Absolute URL dicocokkan terhadap path-nya sekaligus terhadap URL lengkap, sehingga pattern berbentuk path tetap menangkap link yang server hasilkan dengan host.
$panel = Panel::make('patterns')->fullPageUrls('/reports/*');
$panel->isFullPageUrl('/reports/monthly'); // true
$panel->isFullPageUrl('https://example.test/reports/monthly'); // true
$panel->isFullPageUrl('/reports'); // false — `*` needs a segment
$panel->isFullPageUrl('/invoices/1'); // false2
3
4
5
6
Panel yang tidak mendeklarasikan pattern melakukan short-circuit: isFullPageUrl() langsung menghasilkan false tanpa parsing.
Panel::make('none')->isFullPageUrl('/anything'); // falseTempat flag diterapkan
PandaPanel\Support\NavigationBuilder::applyFullPage() menelusuri sidebar item setelah authorization dan active state diproses, termasuk children. Keputusan berdasarkan href milik link itu sendiri, bukan parent-nya:
$item
->withChildren($this->applyFullPage($panel, $item->children))
->withFullPage($panel->isFullPageUrl($item->href));2
3
NavigationItem::withFullPage(bool $fullPage): self mengembalikan instance baru; flag diserialisasi sebagai fullPage pada setiap item.
it('marks no navigation item full page until the panel declares one', function (): void {
expect($items->pluck('fullPage')->unique()->all())->toBe([false]);
});
it('marks only the declared paths as full page', function (): void {
expect($items->firstWhere('label', 'Settings')['fullPage'])->toBeTrue();
});2
3
4
5
6
7
Pass ini dilewati sepenuhnya ketika Panel tidak memiliki pattern, sehingga common case tidak menambah pekerjaan.
Yang dilakukan frontend
export interface NavigationItem {
label: string;
href: string;
icon: string | null;
activeIcon: string | null;
badge: string | number | null;
active: boolean;
sort: number;
/** Declared by the panel: this destination needs a real browser navigation. */
fullPage: boolean;
children: NavigationItem[];
}2
3
4
5
6
7
8
9
10
11
12
PanelNavigationItem.vue melakukan branch untuk item dan seluruh child:
<a v-if="item.fullPage" :href="item.href" :aria-current="item.active ? 'page' : undefined">
<component :is="icon" v-if="icon" />
<span>{{ item.label }}</span>
</a>
<Link v-else :href="item.href" :prefetch="prefetch">
<component :is="icon" v-if="icon" />
<span>{{ item.label }}</span>
</Link>2
3
4
5
6
7
8
Full-page destination tidak pernah diprefetch karena destination tersebut harus meninggalkan SPA, sedangkan prefetch hanya akan mengambil document yang tidak dapat digunakan client. Lihat Prefetching.
Membangun URL Panel
Semua destination yang di-link Panel dibangun dari route name, bukan string concatenation. Jika path Panel berubah, seluruh link ikut berpindah. Sebaliknya URL untuk class yang tidak pernah registered akan gagal dengan jelas ketika dibangun daripada menghasilkan 404 nanti.
Route name
public function getRouteNamePrefix(): string; // "panel.{id}."
public function routeName(string $name): string;2
panel('admin')->routeName('dashboard'); // 'panel.admin.dashboard'
panel('admin')->routeName('pages.settings'); // 'panel.admin.pages.settings'2
Page
public static function slug(): string;
public static function routePath(): string;
public static function routeName(Panel|string|null $panel = null): string;
public static function url(Panel|string|null $panel = null): string;2
3
4
use App\Panels\Admin\Pages\Settings;
Settings::routeName('admin'); // 'panel.admin.pages.settings'
Settings::url('admin'); // '/admin/settings'
Settings::url(); // same, using the panel resolved for this request
Settings::url(panel('admin')); // same, given the object2
3
4
5
6
url() mengembalikan URL relatif menggunakan route(..., absolute: false), sesuai kebutuhan Inertia visit.
slug() dan routePath() terpisah: slug adalah route name dan registry key, sedangkan path yang terlihat di address bar. routePath() Page yang menjadi member cluster membawa cluster prefix, tetapi route name tetap tidak berubah.
ClusteredReportPage::slug(); // 'throughput'
ClusteredReportPage::routePath(); // 'ops/throughput'
ClusteredReportPage::url($panel); // '/cluster-host/ops/throughput'2
3
Resource
public static function routeName(string $page = 'index', Panel|string|null $panel = null): string;
public static function url(
string $page = 'index',
Model|int|string|null $record = null,
Panel|string|null $panel = null,
Model|int|string|null $parent = null,
): string;2
3
4
5
6
7
8
use App\Panels\Admin\Resources\Users\UserResource;
UserResource::url(); // '/admin/users'
UserResource::url('create'); // '/admin/users/create'
UserResource::url('view', $record); // '/admin/users/3'
UserResource::url('edit', $record, 'admin');// '/admin/users/3/edit'2
3
4
5
6
url() memastikan Resource registered pada Panel yang di-resolve sebelum membangun URL. Nested Resource mengambil parent dari request saat ini kecuali parent diberikan secara eksplisit. Lihat URLs and routes.
Me-resolve Panel
Ketiga helper menerima Panel|string|null:
| Argument | Perilaku |
|---|---|
Panel | digunakan apa adanya |
string | dicari berdasarkan id; id tidak dikenal melempar PanelRegistrationException |
null | Panel yang di-resolve untuk request saat ini; melempar noCurrentPanel() di luar request Panel |
Helper panel()
panel(); // the current panel, or null outside one
panel('admin'); // that panel, or PanelRegistrationException2
Menggunakan full-page URL di luar navigation
fullPageUrls() hanya menandai navigation item. Link yang Anda tulis sendiri dalam Vue component menjadi tanggung jawab component tersebut:
<script setup lang="ts">
import { Link } from '@inertiajs/vue3';
</script>
<template>
<!-- Stays inside the SPA. -->
<Link href="/admin/users">Users</Link>
<!-- Leaves it: a download, or anything that sets its own headers. -->
<a href="/admin/exports/users.csv">Download CSV</a>
</template>2
3
4
5
6
7
8
9
10
11
Header action dengan type link dirender oleh ActionButton sebagai ordinary anchor. Karena itu action yang menunjuk ke download sudah memiliki behavior yang benar tanpa membutuhkan pattern fullPageUrls().
Gotchas
- Hanya sidebar navigation yang diberi flag.
NavigationBuilderyang menerapkan flag; cluster bar (ClusterNavigation::for()) dan record sub-navigation (RecordSubNavigation::for()) tidak membawafullPagedan selalu merender<Link>. Full-page destination yang harus muncul di salah satu tempat tersebut membutuhkan custom component sendiri. *tidak mencocokkan segment kosong./reports/*cocok dengan/reports/monthly, tetapi tidak dengan/reports. Deklarasikan keduanya jika keduanya dibutuhkan.- Pattern dicocokkan terhadap
hrefyang dihasilkan server. URL Panel bersifat relatif, sehingga pattern umumnya ditulis sebagai path yang diawali Panel path —/admin/exports/*, bukanexports/*. - Full-page link me-reload seluruh aplikasi. Shared props, Panel shell, dan seluruh client state dibangun ulang. Itulah tujuan fitur ini sekaligus biayanya.
url()sengaja menghasilkan relative URL. Bila digunakan pada sesuatu yang membutuhkan absolute URL — mail template atau webhook payload — bangun URL denganurl()atauroute()menggunakanabsolute: true.Resource::url()melempar exception untuk Resource yang tidak registered. Ini disengaja: dead link yang berhasil dirender lebih buruk daripada exception tepat pada baris kode yang mencoba membangun link tersebut.