atannex / panels
Many back-office panels in one Laravel app: one panel per system on its own host, a control panel over all of them, host-aware routing and URLs, a panel switcher, and cached panel summaries refreshed on the queue so an overview never builds every panel live.
Requires
- php: ^8.2
- illuminate/bus: ^11.0|^12.0|^13.0
- illuminate/cache: ^11.0|^12.0|^13.0
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/queue: ^11.0|^12.0|^13.0
- illuminate/routing: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^11.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-09 11:31:26 UTC
README
Many back-office panels in one Laravel app. Each system gets its own panel on its own host, and a control panel oversees them all. Panel routes move to their own host, route() URLs point there directly, a switcher lists the panels a person may enter, and an overview reads cached summaries rather than building every panel.
use Atannex\Panels\Facades\Panels;
use Atannex\Panels\Panel;
Panels::register(
Panel::make('control')->control()->label('Control')
->host('atannex.com')->home('/panels/dashboard')->routes(['panels.*'])
->access(fn ($user) => $user->can('panel.admin')),
Panel::make('news')->label('News')->accent('#b91c1c')->icon('newspaper')
->host(fn () => config('hosts.news'))->home('/panels/newsroom')
->routes(['panels.newsroom.*', 'panels.media.*'])
->desks([NewsroomDesk::class, MediaDesk::class])
->access(fn ($user) => $user->can('panel.newsroom'))
->summary(NewsSummary::class),
);
Add panels.resolve to the web middleware group. Point URLs at each panel's host with URL::formatHostUsing(fn ($root, $route) => Panels::root($root, $route)), combined with any root callback the app already has. Schedule panels:refresh so summaries are ready before anyone opens the overview.
Access is denied by default: a panel without access() admits no one.