Dashboard Widget Customization (Programmatic API)
Who is this for? Package authors and app developers who programmatically register custom widgets, disable built-ins, or control the dashboard layout via code.
TL;DR: Use
CapellAdmin::registerOverviewStat()for small package metrics that belong inside the Capell overview widget. UseCapellAdmin::registerDashboardWidget()only for widgets that need their own UI, such as tables, charts, calendars, or health panels.
When to use this
Section titled “When to use this”This guide covers programmatic registration and control of dashboard widgets and overview stats — the code-level APIs for declaring what appears on each dashboard.
For end-users toggling widgets in the admin UI, see Dashboard widgets. The two APIs are layered:
- The
setEnabledWidgets()API sets defaults at app boot time - End-users can override those defaults per-role via Settings → Dashboard
Core Built-in Widgets (WidgetEnum)
Section titled “Core Built-in Widgets (WidgetEnum)”Capell Admin ships a small set of core dashboard widgets. Optional reporting,
insights, workflow, search, SEO, campaign, deployment, and operational widgets
live in their owning packages and register themselves with
CapellAdmin::registerDashboardWidget() only when they need a standalone
dashboard surface. Small counts and status metrics should contribute to the
Capell overview widget instead.
<?php
declare(strict_types=1);
namespace Capell\Admin\Enums;
enum WidgetEnum: string implements HasLabel{ case CapellInfoWidget = AbstractCapellInfoWidget::class; case ListPagesWidget = ListPagesWidget::class; case MyWorkQueueWidget = MyWorkQueueWidget::class; case PageStatusWidget = PageStatusWidget::class; case RecentlyPublishedWidget = RecentlyPublishedWidget::class; case SiteStatsOverviewWidget = SiteStatsOverviewWidget::class; case UpdateAdvisoryWidget = UpdateAdvisoryWidget::class;}Each case’s value is the fully-qualified widget class name. The getLabel() method returns a translatable string for the Filament UI.
To add a new core built-in widget, add a case here and update the getLabel() match. This is a core change and should be coordinated with the Capell maintainers. For package-owned widgets, prefer a package service provider and dashboard settings contributor instead.
Examples:
capell-app/dashboard-reportsowns generic report widgets such as publishing trend and content health.capell-app/diagnosticsowns setup, cache, registry, migration, and package health widgets.capell-app/insights,capell-app/search,capell-app/seo-suite, andcapell-app/campaign-studioown their domain reporting widgets.
Public API (CapellAdminManager / CapellAdmin facade)
Section titled “Public API (CapellAdminManager / CapellAdmin facade)”| Method | Parameters | Returns | Purpose |
|---|---|---|---|
registerOverviewStat(...) | key, label, value, optional group, description, url, color, sort, settingsKey, settingsLabel, settingsDescription | void | Register a small metric inside the Capell overview widget. The metric gets a settings toggle but does not become a standalone dashboard widget. |
getOverviewStats(bool $onlyEnabled = true) | $onlyEnabled: whether to filter by dashboard settings | array<CapellOverviewStatData> | Resolve registered overview stats for rendering. |
registerDashboardWidget(string $widgetClass, DashboardEnum ...$dashboards) | $widgetClass: fully-qualified widget class; $dashboards: one or more DashboardEnum values (e.g., DashboardEnum::Main, DashboardEnum::SystemHealth) | void | Register a custom widget class to one or more dashboard pages. The widget is available for toggling in Settings but is not automatically enabled — you must call setEnabledWidgets() or let the user enable it in the UI. |
getDashboardWidgets(DashboardEnum $dashboard) | $dashboard: which dashboard to query | array<string> (list of widget class names) | Get the list of registered widget classes for a dashboard. |
getWidgets(null|bool|Closure $filter = null) | $filter: null (all built-ins), true (enabled by current user/role), false (disabled by current user/role), or a callable filter | array<string|WidgetEnum> | Fetch built-in widgets from WidgetEnum::cases(). When $filter is null, returns all built-in widget classes as strings. When $filter is a boolean, returns filtered WidgetEnum cases based on user settings. When $filter is callable, applies the callable and returns matching cases. |
setEnabledWidgets(array<string|WidgetEnum> $widgets) | $widgets: array of widget classes or WidgetEnum cases to enable | void | Replace the enabled widget set in the database. Any built-in or custom widget NOT in this list will be marked disabled for the active role/context. Omitting a widget hides it by default (but users can re-enable it in Settings). |
Registering Capell Overview Stats
Section titled “Registering Capell Overview Stats”Use overview stats for compact package metrics: counts, percentages, current status values, or links into a package resource. This keeps the dashboard from filling up with one-off stats widgets.
<?php
declare(strict_types=1);
namespace Capell\Blog\Providers;
use Capell\Admin\Facades\CapellAdmin;use Capell\Blog\Models\Article;use Illuminate\Support\ServiceProvider;
final class AdminServiceProvider extends ServiceProvider{ public function boot(): void { CapellAdmin::registerOverviewStat( key: 'blog_overview.articles', label: fn (): string => __('capell-blog::dashboard.articles'), value: fn (): int => Article::query()->count(), group: fn (): string => __('capell-blog::dashboard.group'), sort: 100, settingsKey: 'blog_overview', settingsLabel: fn (): string => __('capell-blog::dashboard.blog_overview'), ); }}Multiple stats can share the same settingsKey. In that case, Settings →
Dashboard shows one toggle for the package overview while the Capell overview
widget renders each enabled stat.
Use registerDashboardWidget() instead when the package needs a table, chart,
calendar, form, setup panel, or richer interaction that cannot fit into a
single overview stat.
Registering a Custom Widget
Section titled “Registering a Custom Widget”Step 1: Create a Filament Widget
Section titled “Step 1: Create a Filament Widget”Create a widget class that extends Filament’s base widget. You can subclass CapellWidget for Capell-specific features (role-gating, settings integration):
<?php
declare(strict_types=1);
namespace App\Filament\Widgets;
use Capell\Admin\Filament\Widgets\CapellWidget;
final class CustomSalesWidget extends CapellWidget{ protected static ?string $heading = 'Sales This Month';
// Visible to these roles (config keys, resolved via capell.roles.<key>). protected static array $rolesConfigKeys = ['admin', 'developer'];
// Settings key for the enable / disable toggle on the Settings page. protected static string $settingsKey = 'custom_sales_widget';
protected static string $view = 'app::widgets.custom-sales';
// getData() and other Filament methods...}See docs/admin/dashboard-widgets.md for the full widget-authoring guide.
Step 2: Register the Widget
Section titled “Step 2: Register the Widget”Call registerDashboardWidget() in your service provider’s boot() method:
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Filament\Widgets\CustomSalesWidget;use Capell\Admin\Enums\DashboardEnum;use Capell\Admin\Facades\CapellAdmin;use Illuminate\Support\ServiceProvider;
final class AppServiceProvider extends ServiceProvider{ public function boot(): void { // Register the widget on the main dashboard CapellAdmin::registerDashboardWidget( CustomSalesWidget::class, DashboardEnum::Main, );
// Or on multiple dashboards: CapellAdmin::registerDashboardWidget( CustomSalesWidget::class, DashboardEnum::Main, DashboardEnum::SystemHealth, );
// Extension operational widgets belong on /admin/extensions: CapellAdmin::registerDashboardWidget( CustomExtensionHealthWidget::class, DashboardEnum::Extensions, ); }}Important: This registers the widget as available — it does not automatically enable it. Users (or you) must toggle it in Settings → Dashboard, or use setEnabledWidgets() to set defaults (see next section).
Disabling Built-in Widgets
Section titled “Disabling Built-in Widgets”Use setEnabledWidgets() to control which built-in widgets are enabled by default for the active role:
<?php
declare(strict_types=1);
namespace App\Providers;
use Capell\Admin\Enums\WidgetEnum;use Capell\Admin\Facades\CapellAdmin;use Illuminate\Support\ServiceProvider;
final class AppServiceProvider extends ServiceProvider{ public function boot(): void { // Enable only these built-ins; omit any you want disabled by default CapellAdmin::setEnabledWidgets([ WidgetEnum::SiteStatsOverviewWidget, WidgetEnum::ListPagesWidget, // Omit WidgetEnum::AlertsWidget, etc. to hide them by default ]); }}Semantics: setEnabledWidgets() replaces the enabled widget set in the AdminSettings table. Any widget not listed is marked disabled in the database, though users can re-enable it from Settings → Dashboard at any time.
DashboardEnum: Where Widgets Live
Section titled “DashboardEnum: Where Widgets Live”Widgets are associated with dashboard pages via DashboardEnum:
<?php
declare(strict_types=1);
namespace Capell\Admin\Enums;
enum DashboardEnum: string{ case Main = 'main'; // Primary dashboard (/admin) case Extensions = 'extensions'; // Extensions dashboard (/admin/extensions) case SystemHealth = 'system_health'; // System health dashboard case NotInstalled = 'not_installed'; // Installation dashboard}When registering a widget, specify which dashboard(s) it should appear on by passing the enum case(s) to registerDashboardWidget().
Gotchas
Section titled “Gotchas”- Boot timing:
registerDashboardWidget()should run inboot()so the admin panel can resolve all service providers before rendering. - Widget base class: Custom widgets must extend Filament’s base
Widgetclass (or a subclass likeCapellWidget). Simply registering an arbitrary class won’t work. - User overrides: End-users can toggle any registered widget in Settings → Dashboard.
setEnabledWidgets()sets the initial state, not a lock. - Settings table: If the
admin_settingstable doesn’t exist yet (e.g., during fresh install),getWidgets()gracefully returns all built-in widgets to avoid bootstrap errors.
Examples
Section titled “Examples”Disable all health check widgets
Section titled “Disable all health check widgets”CapellAdmin::setEnabledWidgets([ WidgetEnum::SiteStatsOverviewWidget, WidgetEnum::ListPagesWidget, WidgetEnum::PageStatusWidget,]);Register a custom widget and enable it by default
Section titled “Register a custom widget and enable it by default”// In AppServiceProvider::boot()CapellAdmin::registerDashboardWidget(CustomSalesWidget::class, DashboardEnum::Main);
// Set up initial enabled state$enabledWidgets = array_map( fn (WidgetEnum $widget): string => $widget->value, WidgetEnum::cases());$enabledWidgets[] = CustomSalesWidget::class; // Add the new widget
CapellAdmin::setEnabledWidgets($enabledWidgets);Related
Section titled “Related”docs/admin/dashboard-widgets.md— user-facing widget configuration, widget authoring, settings contributorspackages/admin/src/Support/CapellAdminManager.php— manager class implementationpackages/admin/src/Enums/WidgetEnum.php— built-in widget enumpackages/admin/src/Enums/DashboardEnum.php— dashboard page enumpackages/admin/src/Settings/AdminSettings.php— settings model- Resource registration
- Event registry