nova-bi/nova-dashboard-manager
A Laravel Nova package for creating, organizing, and managing dashboards and their widgets directly from the Nova admin panel, with controls for layout, visibility, and access so teams can tailor dashboard experiences.
Install into an existing Laravel + Nova app (this package is a Nova add-on, not standalone):
composer require nova-bi/nova-dashboard-manager
php artisan vendor:publish --provider="NovaBi\NovaDashboardManager\DashboardManagerServiceProvider"
php artisan migrate
Confirm the actual provider namespace and publish tags from the README before running — with a small package, tags are easy to get wrong.
Register whatever the package exposes (a Nova tool, resource, or both) inside app/Nova/NovaServiceProvider.php, then php artisan nova:publish only if the README calls for asset publishing.
Look first at, in order:
README.md (install + screenshots of the UI are usually the only reliable docs)config/ after publishing — this is where card scoping, route prefixes, and model bindings usually livedatabase/migrations/ — tells you the real data model (dashboards, dashboard-card pivot, user/team ownership)src/ — Models/, Nova/, and any Http/Controllers reveal how dashboards are actually resolved at render timeFirst use case to validate: create a dashboard in the Nova UI, attach two built-in cards (e.g. a Value metric and a Partition metric you already have), assign it to a user, and confirm it renders for that user but not another. This exercises install, persistence, authorization, and card resolution in one pass — the four things most likely to be broken.
Treat dashboards as data, seed them as data. The main draw of a dashboard manager over Nova's native dashboards() array is that composition lives in the database. That means your environments should be reproducible via seeders, not clicking:
// database/seeders/DashboardSeeder.php
$dashboard = Dashboard::create(['name' => 'Ops', 'owner_id' => $admin->id]);
$dashboard->cards()->sync([
ActiveUsers::class => ['position' => 0, 'width' => '1/2'],
RevenueTrend::class => ['position' => 1, 'width' => 'full'],
]);
Pin down whether the package stores card class names or a registry key — this determines how brittle your seeders are.
Keep cards registered in Nova as well as in the DB. Database rows are metadata; Nova still needs to instantiate the card class. Register custom cards in NovaServiceProvider::cards() or the dashboard's cards() method so the manager can resolve them, then use the package only for layout/assignment.
Scope by owner or team in a single place. If the package ships a Dashboard model, extend it and add a global scope rather than filtering in views:
// app/Models/Dashboard.php
protected static function booted(): void
{
static::addGlobalScope('team', fn ($q) =>
$q->where('team_id', auth()->user()?->team_id)
);
}
Pair that with a Nova Policy for viewAny/update so the UI and the API agree.
Override the Nova home dashboard last. If the package offers a "set as default" flag, use it behind a config value and keep Nova's native Main dashboard as a fallback. This makes rollback a config change rather than a code revert.
Build cards package-agnostically. Anything you write as a standard Laravel\Nova\Card will survive if you drop this package. Avoid coupling card constructors to manager-specific models; pass IDs or query scopes in from the dashboard config instead.
Testing pattern: use the package's factories (if published) to create a dashboard, then actingAs($user)->get('/nova-api/dashboards') to assert visibility. Assert negative cases — a user from another team must get 404/403, not an empty dashboard.
composer why-not laravel/nova, and be ready to fork. Check composer.json constraints before assuming compatibility.data column that stores stable keys instead.authorizedToSee() still applies — test with a low-privilege user, otherwise you ship a dashboard full of empty or 403 panels.src/. Run php artisan vendor:publish with no arguments and pick from the list rather than trusting the snippet.php artisan config:clear && php artisan nova:clear (or cache:clear) — Nova caches its resource/card manifest and stale manifests masquerade as "the package is broken."nova-api request first. A 200 with cards: [] means resolution failed (class not registered); a 403 means policy; a 500 usually means a missing config key from an incomplete publish.Dashboard model (extend, don't edit vendor), Nova policies, and a custom card base class. Avoid patching vendor/ — with no release in over a year, you will need to composer update eventually.src/ instead of docs, pin the exact version in composer.json, and wrap your integration in a thin application-level service so swapping to hand-rolled Nova dashboards later is a contained change.How can I help you explore Laravel packages today?