Requirement Frontend
Setiap screen Panel merupakan Vue component yang masuk ke aplikasi melalui vendor:publish dan dibuild oleh Vite milik aplikasi. Agar proses ini bekerja, tiga hal harus benar dan semuanya merupakan tanggung jawab host application: npm dependency, module di bawah @/ yang memang dimiliki aplikasi, dan Vite config. Jika salah satunya salah, kegagalan biasanya muncul saat npm run build sebagai error tentang module specifier — pesan error tersebut benar, tetapi tidak langsung menjelaskan akar masalahnya.
Tanyakan ke Package Apa yang Masih Kurang
php artisan panel:install --no-panel --no-user --no-interactionAtau jalankan pemeriksaan individual secara langsung:
php artisan tinkeruse PandaPanel\Support\Installer\FrontendRequirements;
FrontendRequirements::missingInertia(); // []
FrontendRequirements::hasVite(); // true
FrontendRequirements::missingNpmPackages(); // ['reka-ui@^2.0.0']
FrontendRequirements::missingHostModules(); // ['@/routes/login', …]
FrontendRequirements::layoutOverrides(); // []2
3
4
5
6
7
Seluruh bagian berikut menjelaskan arti dari kelima hasil tersebut.
1. npm Dependency
/** @return list<string> `name@range` pairs, ready for npm install */
public static function npmPackages(): array
/** @return list<string> the same pairs, filtered to the ones you do not declare */
public static function missingNpmPackages(): array2
3
4
5
npmPackages() membaca block dependencies dari package.json milik package ini sendiri lalu mengembalikan pasangan name@range. Tidak ada copy kedua dari daftar tersebut. Ini penting karena daftar yang ditulis ulang di tempat lain akan langsung menjadi stale saat component mulai mengimpor dependency baru.
FrontendRequirements::npmPackages();
// [
// '@inertiajs/vue3@^3.0.0',
// '@internationalized/date@^3.12.0',
// '@laravel/echo-vue@^2.4.0',
// '@laravel/passkeys@^0.4.0',
// '@lucide/vue@^1.31.0',
// '@tailwindcss/vite@^4.1.0',
// '@tanstack/vue-table@^9.0.0',
// '@vueuse/core@^14.0.0',
// 'class-variance-authority@^0.7.0',
// 'clsx@^2.1.0',
// 'reka-ui@^2.0.0',
// 'tailwind-merge@^3.0.0',
// 'tailwindcss@^4.1.0',
// 'tw-animate-css@^1.2.0',
// 'vue-input-otp@^0.4.0',
// 'vue-sonner@^2.0.0',
// 'vue@^3.5.0',
// ]2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
missingNpmPackages() membandingkan daftar tersebut dengan package.json aplikasi — baik dependencies maupun devDependencies — lalu mengembalikan package yang belum dideklarasikan. Pemeriksaan dilakukan terhadap manifest, bukan node_modules, karena yang penting adalah dependency tersebut benar-benar dideklarasikan oleh project. Transitive dependency yang kebetulan tersedia di disk hari ini dapat hilang setelah dependency lain di-upgrade besok.
npm install \
@inertiajs/vue3@^3.0.0 \
reka-ui@^2.0.0
npm run build2
3
4
Aplikasi yang sama sekali belum memiliki package.json akan mendapatkan seluruh daftar sebagai missing dependency.
2. Host Seam
/** @return list<string> the missing modules, as `@/…` specifiers */
public static function missingHostModules(): array2
Component yang dipublish mengimpor sejumlah module yang tidak dikirim package karena module tersebut merupakan bagian dari aplikasi. Ada dua jenis: generated route helper dan component yang menjadi tempat project menyimpan account UI miliknya sendiri.
| Specifier | Berasal dari |
|---|---|
@/routes | Wayfinder |
@/routes/login | Wayfinder |
@/routes/register | Wayfinder |
@/routes/password | Wayfinder |
@/routes/two-factor | Wayfinder |
@/routes/verification | Wayfinder |
@/actions/App/Http/Controllers/Settings/ProfileController | Wayfinder |
@/actions/App/Http/Controllers/Settings/SecurityController | Wayfinder |
@/actions/Laravel/Passkeys/Http/Controllers/PasskeyRegistrationController | Wayfinder |
@/components/Heading | Starter kit |
@/components/UserInfo | Starter kit |
@/components/UserMenuContent | Starter kit |
@/components/PasskeyItem | Starter kit |
@/components/PasskeyRegister | Starter kit |
@/components/TwoFactorRecoveryCodes | Starter kit |
@/components/TwoFactorSetupModal | Starter kit |
@/composables/useTwoFactorAuth | Starter kit |
@/types | Starter kit |
@/types/ui | Starter kit — diimpor oleh bridge broadcasting dan flash milik Panel |
Specifier @/x berarti resources/js/x. Sebuah specifier dianggap tersedia jika salah satu bentuk berikut ada di disk, diperiksa sesuai urutan:
.ts .vue .d.ts /index.ts /index.vue /index.d.tsBare path sengaja tidak dianggap sebagai match. File::exists() mengembalikan true untuk directory, sehingga jika bare path diterima maka entry seperti @/types dianggap tersedia hanya karena folder types ada, walaupun module yang sebenarnya diimpor tidak ada. .d.ts disertakan karena starter kit menyimpan shared type pada resources/js/types/index.d.ts, yang merupakan implementasi valid dari @/types.
Untuk bagian generated:
php artisan wayfinder:generateUntuk sisanya, Laravel Vue starter kit sudah menyediakannya. Jika aplikasi Anda bukan starter kit, repository ini menyediakan minimal stand-in untuk setiap module di frontend/host/. Directory tersebut export-ignore, sehingga composer require tidak membawanya ke vendor/, tetapi source-nya dapat dibaca sebagai referensi contract: setiap file mendeklarasikan props, emit, dan export yang benar-benar digunakan component Panel.
Test suite menurunkan daftar ini dari import yang ada pada published tree lalu membandingkannya dengan FrontendRequirements. Dengan demikian module baru yang diimpor component tetapi belum masuk requirement akan gagal di test package, bukan baru ditemukan ketika build aplikasi user gagal.
3. Vite, Inertia, dan Root View
public static function hasVite(): bool // vite.config.ts or vite.config.js at the app root
/** @return list<string> what is missing, in words */
public static function missingInertia(): array2
3
4
missingInertia() mencari tepat dua file dan menjelaskan yang hilang:
| File yang Hilang | Dilaporkan sebagai |
|---|---|
resources/views/app.blade.php | an Inertia root view at resources/views/app.blade.php |
app/Http/Middleware/HandleInertiaRequests.php | Inertia's middleware (php artisan inertia:middleware) |
Tanpa salah satunya, URL Panel pertama menghasilkan HTTP 500 karena setiap screen Panel adalah Inertia response.
Root view juga menjadi tempat entrypoint Vite milik Panel ditambahkan. Bentuk pada example application:
@vite([
'resources/css/app.css',
'resources/js/app.ts',
"resources/js/pages/{$page['component']}.vue",
...(panel()?->getAssets() ?? []),
])2
3
4
5
6
panel() mengembalikan Panel untuk current request atau null di luar Panel. Karena itu spread tersebut kosong pada Page non-Panel dan pada Panel yang tidak mendeklarasikan asset tambahan.
HandleInertiaRequests milik aplikasi tidak membutuhkan logic khusus PandaBear. Prop panel, navigation, panels, search, notifications, broadcasting, dan tenancy semuanya dibagikan oleh middleware package PandaPanel\Http\Middleware\SharePanelData. Jika versi baru menambahkan prop, aplikasi tidak perlu melakukan manual merge ke file middleware miliknya sendiri.
4. Aturan Layout
/** @return list<array{file: string, line: int, code: string}> */
public static function layoutOverrides(): array2
Setiap Page Panel yang dipublish mendeklarasikan layout-nya sendiri:
defineOptions({ layout: PanelLayout }); // PanelBlankLayout on the auth pagesKarena itu host entry yang tidak menyebut panel/ sama sekali tetap benar dan tidak dilaporkan. Yang bermasalah adalah assignment layout secara unconditional di Inertia resolver, karena assignment tersebut menimpa pilihan layout Page setelah Page sudah mendeklarasikannya:
page.default.layout = AppLayout; // reported
page.default.layout ??= AppLayout; // fine
page.default.layout ||= AppLayout; // fine
page.default.layout = page.default.layout || AppLayout; // fine2
3
4
Jika dibiarkan, seluruh screen Panel dirender di dalam shell aplikasi sendiri — sidebar host muncul, navigation Panel hilang — tetapi response tetap HTTP 200 dan tidak ada error log. Karena problem ini tidak dapat diperbaiki dari dalam package, installer memeriksanya secara otomatis daripada hanya mendokumentasikannya.
File yang diperiksa berurutan adalah resources/js/app.ts, app.js, ssr.ts, dan ssr.js. Setiap baris bermasalah dikembalikan dengan line number satu-based dan source yang sudah di-trim.
Apa yang Dipublish dan Ke Mana
use PandaPanel\Support\Installer\PublishedAssets;
PublishedAssets::map(); // absolute source => absolute destination
PublishedAssets::files(); // absolute destination => absolute source, per file
PublishedAssets::relative($absolute); // the destination as it reads in a report2
3
4
5
| Source Package | Destination Aplikasi |
|---|---|
resources/js/panel | FrontendPaths::panel() — default resources/js/panel |
resources/js/components | resources/js/components |
resources/js/composables | resources/js/composables |
resources/js/lib | resources/js/lib |
resources/js/pages | resources/js/pages |
resources/js/types | resources/js/types |
resources/css/panda-panel.css | resources/css/panda-panel.css |
Salah satu destination tersebut configurable, yaitu panel_path, sehingga map dibangun setiap kali dipanggil dan tidak dibekukan sebagai constant:
// config/panda-panel.php
'frontend' => [
'panel_path' => 'js/panel', // PandaPanel\Support\FrontendPaths::panel()
'pages_path' => 'js/pages/Panels', // PandaPanel\Support\FrontendPaths::pages()
],2
3
4
5
pages_path bukan destination publish. Path ini merupakan lokasi generator menulis custom component dan root dari import.meta.glob yang digunakan frontend untuk me-resolve component name. Jika dipindahkan, glob tersebut juga harus diubah.
Stylesheet
resources/css/panda-panel.css adalah stylesheet Tailwind 4 yang lengkap, bukan fragment. File dimulai dengan @import 'tailwindcss' dan @import 'tw-animate-css', lalu mendeklarasikan dark variant, @theme inline token map, light/dark custom property termasuk seluruh token --sidebar-* yang dibaca shell, serta component class .panel-table-frozen-edge untuk menggambar seam di samping frozen column.
Laravel Vue starter kit memiliki app.css yang hampir identik. Bagian khusus Panel terutama adalah sidebar token dan frozen-column seam. Karena itu ada dua pendekatan yang sama-sama valid:
- Tetap gunakan
app.cssaplikasi dan salin bagian yang belum tersedia. Tidak diperlukan konfigurasi lain karena component Panel menggunakan utility theme biasa. - Build
panda-panel.csssebagai entrypoint terpisah, lalu deklarasikan pada Panel agar CSS hanya dimuat di Page Panel tersebut:
$panel->assets('resources/css/panda-panel.css');// vite.config.ts
input: ['resources/css/app.css', 'resources/js/app.ts', 'resources/css/panda-panel.css'],2
Diperlukan dua edit secara sengaja. Panel::assets() menerima source path, bukan built file, sehingga entry yang sama juga harus berada pada Vite input. Jika tidak, Page akan gagal dengan Vite manifest error. Error ini memang benar karena asset dideklarasikan tetapi tidak pernah dibuild; inilah alasan konfigurasi ini tidak bisa menjadi perubahan satu baris saja.
Entrypoint bersifat akumulatif antar-call, dan daftar source path tidak pernah dikirim ke frontend. Browser hanya menerima tag hasil build, bukan informasi proses pembuatannya.
Tailwind 3 tidak dapat mengompilasi stylesheet ini karena @theme, @custom-variant, dan @source merupakan directive Tailwind 4.
Build
npm install
npm run build # or npm run dev2
Component Panel di-resolve melalui import.meta.glob terhadap tree aplikasi sendiri — sengaja berupa build-time allowlist. Component yang tidak pernah terlihat build tidak dapat di-resolve, sehingga custom Column, Widget, dan Page berada di bawah resources/js/pages/Panels/**, bukan di dalam package.
Setelah package upgrade, published copy tidak berubah otomatis:
php artisan panel:assets # what is behind, what you changed, what conflicts
php artisan panel:assets --update # write only the files you have never touched
npm run build2
3
Catatan
- Missing dependency dan missing host module menghasilkan build error yang hampir sama. Keduanya terlihat sebagai "cannot resolve module".
panel:installyang membedakan kategori masalah tersebut untuk Anda. missingNpmPackages()hanya membandingkan nama package yang dideklarasikan, bukan versi. Dependency dengan range yang lebih tua daripada requirement Panel tidak ditandai di sini; incompatibility-nya akan muncul kemudian saat build atau runtime.missingHostModules()tidak memahami custom alias. Pemeriksaan hanya me-resolve specifier terhadapresources/js. Jika alias@/aplikasi menunjuk ke tempat lain, installer dapat melaporkan module missing padahal sebenarnya tersedia melalui alias custom.- Component name yang tidak terdaftar tidak melempar exception. Pada development Panel memberi warning sekali per nama dan menyebut directory tempat component harus berada; pada production component hanya tidak muncul. Behavior serupa berlaku untuk icon — jalankan
panel:iconssetelah mendeklarasikan icon baru.
Lihat Juga
- Laravel Vue starter kit setup — aplikasi yang sudah memenuhi seluruh requirement ini
- Running panel:install — tempat seluruh check dijalankan otomatis
- Compatibility — versi Vue, Vite, Tailwind, dan Node yang didukung
- Frontend: host modules, Wayfinder
- Frontend: Tailwind theme, updating assets
- Concepts: component registries, frontend assets
- Troubleshooting: Vite, host modules, Tailwind