webx-ui / admin
The frame a WebX UI admin panel is built on: a module contract, the manifest the front end reads, and the routes that serve it.
Requires
- php: ^8.3
- illuminate/console: ^13.0
- illuminate/contracts: ^13.0
- illuminate/filesystem: ^13.0
- illuminate/http: ^13.0
- illuminate/routing: ^13.0
- illuminate/support: ^13.0
- webx-ui/localization: ^0.6.0
Requires (Dev)
- orchestra/testbench: ^11.0
- phpunit/phpunit: ^12.0 || ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
The frame a WebX UI admin panel is built on.
It answers three questions and stays out of everything else: what modules this panel has, where the front end can ask, and what to serve when someone opens a deep link. Entities, screens and permissions belong to the modules.
Requirements
- PHP 8.3+
- Laravel 13
Install
composer require webx-ui/admin php artisan webx:install
webx:install publishes the config and the shell view, then tells you where the panel and its
manifest are. The panel is open until an auth module adds its middleware — the command says
so too, because it is the kind of thing that is easy to leave for later.
A module
A module describes itself. Its own service provider does the real work — routes, bindings, migrations — exactly as any Laravel package would; this contract is only what the front end needs in order to know the module exists.
php artisan webx:make-module Pages
namespace App\Cms\Modules; use WebxUi\Admin\AbstractModule; class PagesModule extends AbstractModule { public function id(): string { return 'pages'; } public function icon(): ?string { return 'file-text'; } public function order(): int { return 10; } public function permissions(): array { return ['pages.view', 'pages.manage']; } public function manifest(): array { return ['tree' => true]; } }
Register it from a service provider, so it is there on every request:
public function boot(ModuleRegistry $modules): void { $modules->register(new PagesModule); }
AbstractModule fills in everything but id(). Two modules may not share an id — they would
collide in URLs, in permissions and in the manifest, and the failure would surface far from its
cause, so registration refuses it outright.
The manifest
GET /api/cms/manifest
{
"data": {
"title": "WebX UI",
"path": "/cms",
"apiPath": "/api/cms",
"modules": [
{
"id": "pages",
"title": "Pages",
"icon": "file-text",
"order": 10,
"permissions": ["pages.view", "pages.manage"],
"meta": { "tree": true }
}
]
}
}
Whatever a module returns from manifest() lands under meta, in its own room, so it can never
shadow the fields around it.
Responses
Laravel already has the two shapes that matter, and the WebX UI front end is built against them:
a paginator serialises with data and meta, and a failed validation is a 422 with message
and errors. So a table endpoint returns ->paginate() as it is, and a form endpoint lets
validation fail on its own.
ApiResponse covers the third case — a plain payload, wrapped in data so it arrives like
everything else:
use WebxUi\Admin\Http\ApiResponse; return ApiResponse::data($page); return ApiResponse::message('Published.'); return ApiResponse::noContent();
The panel's front end
The panel is built by the site rather than shipped prebuilt: which modules it contains is a decision only the site can make, so there is no one bundle to ship.
php artisan webx:panel
writes resources/js/admin.ts, adds it to the laravel() plugin's inputs, and points
webx-admin.vite at it. Then install the front-end packages it names and build:
npm install @webx-ui/admin @webx-ui/module-auth
npm run build # or npm run dev while working — @vite serves from the dev server
If your Vite configuration is shaped in a way the command does not recognise, it says which line to add rather than rewriting a build it does not understand.
Building the panel outside Laravel's Vite — in CI, say, or alongside a front end that has its own toolchain — is the other supported route: name the files instead.
'assets' => ['/webx/webx.css', '/webx/webx.js'],
Give those files hashed names. With stable ones a browser keeps the panel it saw yesterday.
The shell
Every address below the panel prefix serves the same page: routing inside the admin belongs to
the front end, and a reloaded page three levels deep must not 404. The view carries the manifest
URL in a meta tag and mounts #webx-app.
To point it at your own assets, either publish it —
php artisan vendor:publish --tag=webx-admin-views
— or push onto the webx-head and webx-body stacks from a view composer.
Configuration
config/webx-admin.php covers the title, the two paths and the middleware groups. Moving the
panel means clearing the route cache afterwards.
Languages
Ten shipped: en, ru, uk, de, pl, fr, es, it, pt, tr. English is the fallback, and it is laid under the chosen language line by line, so a half-translated group shows what it has and English for the rest.
Only English, Russian and Ukrainian have been read by a speaker; the other seven are machine
translations. A correction from someone who speaks the language is welcome — they are plain PHP
arrays in lang/.
Licence
MIT.