Dashboard Widgets

Navigation: WP Admin > NimblyMail > Dashboard

The dashboard renders an extension slot below the Recent Activity table. A Pro add-on injects widgets by filtering nimbly_mail_dashboard_widgets. Six built-in widget types cover the majority of use cases without any Vue code. A custom type is available when a bespoke component is required.

Widget Descriptor Reference

Key Type Required Description
id string Yes Unique widget identifier.
type string Yes Widget type. See types below.
label string Yes Widget heading.
icon string No Ant Design icon name (e.g., WarningOutlined).
position string No Insertion hint: after-stats, after-activity (default), before-stats.
data array No Inline data object. Used when no endpoint is needed.
data_endpoint string No REST endpoint path the widget fetches on mount (relative to wp-json/).
refresh_interval int No Seconds between automatic re-fetches. Requires data_endpoint.
renderer string No Custom renderer slug. Required when type is custom.

Use either data or data_endpoint — not both. If both are present, data_endpoint takes precedence.

Widget Types

stat-card

Renders a single metric with an optional trend indicator.

Expected data shape:

array(
    'value' => 98.6,     // numeric value
    'unit'  => '%',      // string appended after the value
    'trend' => 'up',     // 'up' | 'down' | 'neutral' (optional)
)

Example:

add_filter(
    'nimbly_mail_dashboard_widgets',
    function ( array $widgets ): array {
        $widgets[] = array(
            'id'               => 'bounce-rate',
            'type'             => 'stat-card',
            'label'            => __( 'Bounce Rate (24h)', 'nimbly-mail-pro' ),
            'icon'             => 'WarningOutlined',
            'position'         => 'after-activity',
            'data_endpoint'    => 'nimbly-mail-pro/v1/stats/bounce-rate',
            'refresh_interval' => 300,
        );
        return $widgets;
    }
);

@since 1.0.0 — nimbly_mail_dashboard_widgets filter.

Signature: apply_filters( 'nimbly_mail_dashboard_widgets', array $widgets, DashboardController $controller )

  • $widgets — the current widgets array.
  • $controller — the DashboardController instance, available for context (e.g., reading request parameters).

Returns: array

table

Renders a scrollable Ant Design table.

Expected data shape:

array(
    'columns' => array(
        array( 'key' => 'email',  'title' => 'Email Address' ),
        array( 'key' => 'reason', 'title' => 'Bounce Reason' ),
        array( 'key' => 'date',   'title' => 'Date' ),
    ),
    'rows' => array(
        array( 'email' => '[email protected]', 'reason' => 'Hard bounce', 'date' => '2025-04-20' ),
    ),
)

Example:

$widgets[] = array(
    'id'            => 'recent-bounces',
    'type'          => 'table',
    'label'         => __( 'Recent Bounces', 'nimbly-mail-pro' ),
    'icon'          => 'TableOutlined',
    'position'      => 'after-activity',
    'data_endpoint' => 'nimbly-mail-pro/v1/bounces/recent',
);

activity-feed

Renders a chronological list of events with an icon, label, and timestamp per row.

Expected data shape:

array(
    'items' => array(
        array(
            'icon'      => 'CheckCircleOutlined',
            'label'     => 'Webhook received for [email protected]',
            'timestamp' => '2025-04-20 14:32:01',
        ),
    ),
)

Example:

$widgets[] = array(
    'id'               => 'webhook-feed',
    'type'             => 'activity-feed',
    'label'            => __( 'Webhook Activity', 'nimbly-mail-pro' ),
    'icon'             => 'BellOutlined',
    'position'         => 'after-activity',
    'data_endpoint'    => 'nimbly-mail-pro/v1/webhooks/recent',
    'refresh_interval' => 60,
);

alert-banner

Renders a dismissible Ant Design alert. Useful for licence warnings, quota notices, or connectivity errors.

Expected data shape:

array(
    'type'    => 'warning',  // 'success' | 'info' | 'warning' | 'error'
    'message' => 'Your Postmark sending quota is 80% used.',
    'detail'  => 'Upgrade your plan to avoid delivery interruptions.',  // optional
)

Example:

$widgets[] = array(
    'id'            => 'quota-alert',
    'type'          => 'alert-banner',
    'label'         => __( 'Sending Quota', 'nimbly-mail-pro' ),
    'position'      => 'after-stats',
    'data_endpoint' => 'nimbly-mail-pro/v1/quota/status',
);

status-list

Renders a list of named services with a status indicator (ok / warning / error) per row.

Expected data shape:

array(
    'items' => array(
        array( 'label' => 'Postmark API',        'status' => 'ok' ),
        array( 'label' => 'Bounce Webhook',      'status' => 'warning' ),
        array( 'label' => 'Smart Routing Rules', 'status' => 'ok' ),
    ),
)

Example:

$widgets[] = array(
    'id'               => 'pro-status',
    'type'             => 'status-list',
    'label'            => __( 'Pro Service Status', 'nimbly-mail-pro' ),
    'icon'             => 'CheckSquareOutlined',
    'position'         => 'after-stats',
    'data_endpoint'    => 'nimbly-mail-pro/v1/status',
    'refresh_interval' => 120,
);

chart (stub)

The chart type is a reserved stub. The free plugin renders a placeholder; the first Pro widget that needs a chart is responsible for pulling its charting library in via its own enqueued asset. A full chart widget implementation is tracked as a follow-up.

custom

Opt out of generic rendering entirely. Set type to custom, provide a renderer slug, and register the Vue component from JavaScript.

PHP side:

$widgets[] = array(
    'id'       => 'smart-routing-map',
    'type'     => 'custom',
    'label'    => __( 'Routing Map', 'nimbly-mail-pro' ),
    'position' => 'after-activity',
    'renderer' => 'routing-map',
    'data'     => array( /* any shape your component expects */ ),
);

JavaScript side:

import RoutingMap from './components/RoutingMap.vue';

window.nimblyMail.registerDashboardWidget( 'routing-map', RoutingMap );

Network Dashboard

The same pattern applies to the network admin dashboard. Use nimbly_mail_network_dashboard_widgets instead.

Signature: apply_filters( 'nimbly_mail_network_dashboard_widgets', array $widgets, NetworkDashboardController $controller )

@since 1.0.0 — nimbly_mail_network_dashboard_widgets filter.

add_filter(
    'nimbly_mail_network_dashboard_widgets',
    function ( array $widgets ): array {
        $widgets[] = array(
            'id'            => 'network-bounce-rate',
            'type'          => 'stat-card',
            'label'         => __( 'Network Bounce Rate (24h)', 'nimbly-mail-pro' ),
            'icon'          => 'GlobalOutlined',
            'position'      => 'after-activity',
            'data_endpoint' => 'nimbly-mail-pro/v1/network/stats/bounce-rate',
        );
        return $widgets;
    }
);

Polling

Widgets that declare both data_endpoint and refresh_interval are automatically re-fetched every refresh_interval seconds. Polling is paused while the browser tab is hidden (document.visibilityState === 'hidden') and resumes when the tab becomes visible again. This prevents unnecessary background requests when the user is not actively viewing the dashboard.

Setting refresh_interval without data_endpoint has no effect.