# 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. Use `CapellAdmin::registerDashboardWidget()` only for widgets that need their own UI, such as tables, charts, calendars, or health panels.

---

## 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](../../../docs/admin/dashboard-widgets.md). 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)

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
<?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-reports` owns generic report widgets such as publishing trend and content health.
- `capell-app/diagnostics` owns setup, cache, registry, migration, and package health widgets.
- `capell-app/insights`, `capell-app/search`, `capell-app/seo-suite`, and `capell-app/campaign-studio` own their domain reporting widgets.

---

## 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

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
<?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

### 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
<?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`](../../../docs/admin/dashboard-widgets.md) for the full widget-authoring guide.

### Step 2: Register the Widget

Call `registerDashboardWidget()` in your service provider's `boot()` method:

```php
<?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

Use `setEnabledWidgets()` to control which built-in widgets are enabled by default for the active role:

```php
<?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

Widgets are associated with dashboard pages via `DashboardEnum`:

```php
<?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

- **Boot timing:** `registerDashboardWidget()` should run in `boot()` so the admin panel can resolve all service providers before rendering.
- **Widget base class:** Custom widgets must extend Filament's base `Widget` class (or a subclass like `CapellWidget`). 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_settings` table doesn't exist yet (e.g., during fresh install), `getWidgets()` gracefully returns all built-in widgets to avoid bootstrap errors.

---

## Examples

### Disable all health check widgets

```php
CapellAdmin::setEnabledWidgets([
    WidgetEnum::SiteStatsOverviewWidget,
    WidgetEnum::ListPagesWidget,
    WidgetEnum::PageStatusWidget,
]);
```

### Register a custom widget and enable it by default

```php
// 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

- [`docs/admin/dashboard-widgets.md`](../../../docs/admin/dashboard-widgets.md) — user-facing widget configuration, widget authoring, settings contributors
- [`packages/admin/src/Support/CapellAdminManager.php`](../src/Support/CapellAdminManager.php) — manager class implementation
- [`packages/admin/src/Enums/WidgetEnum.php`](../src/Enums/WidgetEnum.php) — built-in widget enum
- [`packages/admin/src/Enums/DashboardEnum.php`](../src/Enums/DashboardEnum.php) — dashboard page enum
- [`packages/admin/src/Settings/AdminSettings.php`](../src/Settings/AdminSettings.php) — settings model
- [Resource registration](resource-registration.md)
- [Event registry](event-registry.md)