# DashboardSectionProvider plugins

How to contribute a section to `/admin/reports/wow`.

## When to use this

- Your submodule has status your operators should see (row counts, last-sync time, queue depth, something warning-worthy).
- You want administrators to trigger one-click actions (sync, resync, wipe, reset state) from the dashboard.

## The interface

`Drupal\wow\DashboardSectionProviderInterface`

Plugins return value objects, never HTML:

- `DashboardSection` — the top-level block with a title and rows.
- `DashboardRow` — one line inside a section: severity (OK / warning / error), label, value, optional actions.
- `DashboardAction` — a dropdown item on a row: ID, label, optional `destructive: TRUE` flag to force a confirmation form.

`DashboardRenderer` owns all markup. A theme refresh to the dashboard doesn't ripple out to every plugin.

## The attribute

```php
use Drupal\wow\Attribute\DashboardSectionProvider;

#[DashboardSectionProvider(
  id: 'wow_my_thing',
  label: new TranslatableMarkup('My thing'),
)]
class MyThingStatus extends DashboardSectionProviderBase { /* ... */ }
```

Plugins live in `modules/<module>/src/Plugin/DashboardSectionProvider/`.

## Two base classes

### `TaxonomyDashboardProviderBase`

For taxonomy-backed reference-data modules. You implement five metadata getters and the base class handles:

- term counts
- last-sync timestamp (`wow.last_modified.<entity>_index.<region>`)
- the standard "Sync now" action
- skipping the wipe action (taxonomy wipes would break entity_reference integrity)

Reference: `modules/wow_playable_class/src/Plugin/DashboardSectionProvider/PlayableClassStatus.php`.

### `DashboardSectionProviderBase`

The generic base. Use when your module has its own content entities, queue state, or non-standard actions to surface. Reference implementations:

- `modules/wow_achievement/src/Plugin/DashboardSectionProvider/AchievementStatus.php` — queue depth, category counts, batch sync
- `modules/wow_character/src/Plugin/DashboardSectionProvider/CharacterStatus.php` — stored count, force-resync, wipe
- `src/Plugin/DashboardSectionProvider/BattleNetStatus.php` — API connection health, credential status

## Implementing actions

Actions are IDs in your `DashboardAction` objects; when clicked, Drupal routes to your plugin's `executeAction()`:

```php
public function executeAction(string $actionId, array $parameters = []): ?BatchBuilder {
  return match ($actionId) {
    'sync_now'     => $this->buildSyncBatch($this->defaultRegion()),
    'force_resync' => $this->buildSyncBatch($this->defaultRegion(), resetState: TRUE),
    'wipe'         => $this->buildWipeBatch(),
    default        => parent::executeAction($actionId, $parameters),
  };
}
```

Return a `BatchBuilder` — the dashboard controller runs it as a Drupal batch with progress. Return `NULL` for actions that complete immediately (e.g. state reset).

Destructive actions (`destructive: TRUE`) route through `DashboardActionConfirmForm` before executing, so the operator sees a "Are you sure?" step.

## Testing expectations

Kernel tests for dashboard plugins are straightforward — see `PlayableClassStatusTest` or `AchievementStatusTest`:

1. Install the module + schemas.
2. Seed some state (stored rows, a state key).
3. Invoke the plugin's `getSection()` directly and assert severity, label, value, action presence.

Tests don't need to render markup — assert against the value objects.

## Common pitfalls

- **Hardcoded region fallback**: always fall back to `'eu'` (matches `config/install/wow.settings.yml`). Don't use `'us'` — it diverges from what other modules assume.
- **Writing HTML in the plugin**: don't. Return value objects. If you need custom markup, work with the core maintainers on expanding `DashboardRenderer` — not in-plugin markup.
- **Coupling to another module's services**: a dashboard plugin inside `wow_mount` should only use `wow_mount`'s own services + core. Cross-module dependencies in dashboard plugins tend to indicate a deeper architecture problem.
