# Author an operational dashboard

> Source: https://docs.clonepartner.com/guides/dashboard-authoring/

Envoy dashboards are YAML-defined, job-bound applications. Use them to combine run status, curated data, operator guidance, configuration forms, and controlled actions around one job.

A dashboard template belongs to a job template. When a job is created, active dashboard templates are snapshotted as dashboard instances. Editing the template affects future jobs, not existing instances.

## Start with the user journey

Before writing YAML, define:

- who will use the dashboard;
- which decisions they need to make;
- which data must always remain scoped;
- which settings they may change;
- which actions are safe;
- which pages, if any, may be public.

Prefer a few purpose-based pages over one dense wall of widgets. Keep diagnostic and remediation tools on authenticated pages.

## Minimal multi-page dashboard

```yaml
dashboard:
  title: Ticket migration
  description: Monitor progress and review exceptions.
  refresh_interval_seconds: 30
  allow_public_input: false

  pages:
    - id: overview
      title: Overview
      widgets:
        - id: overview_help
          type: heading
          variant: callout
          width: 4
          markdown: |
            ## Before you run
            Review the field mapping and unresolved records.

        - id: ticket_progress
          type: task_progress
          title: Ticket migration
          task: migrate_tickets
          refresh_interval_seconds: 5

    - id: diagnostics
      title: Diagnostics
      public: false
      widgets:
        - id: recent_errors
          type: table
          title: Recent errors
          data:
            connector: staging
            method: find
            args:
              table: migration_errors
              limit: 50
              order_by:
                created_at: desc
          columns:
            - { field: created_at, label: Time, format: datetime }
            - { field: message, label: Message, format: truncate }
```

Widget IDs must be unique across all pages.

## Choose the right widget

- `stat` for one number.
- `progress` for a current/total ratio.
- `bar_chart`, `line_chart`, or `pie_chart` for bounded grouped results.
- `table` for a small fixed result set.
- `explorer` for pageable, filterable, exportable data.
- `task_progress` for the latest run of one job task key.
- `steps` for a process stage.
- `people` for ownership and contact information.
- `heading` for section labels or instructional callouts.
- `form` and `field_mapping` for job configuration.
- `action` for a confirmed server-side operation.

Use [Dashboard schema and widgets](/reference/dashboards) for complete fields.

## Scope data at the server

Every data widget executes a read-only connector method. Add an author-controlled base query that viewers cannot remove:

```yaml
- id: failed_tickets
  type: explorer
  title: Failed tickets
  data:
    connector: staging
    resource: tickets
    base_query:
      filter:
        migration_status: failed
  default_sort:
    updated_at: -1
  page_size: 25
  features: [filter, sort, columns, export]
  columns:
    - { field: id, label: ID, sortable: true }
    - { field: subject, label: Subject }
    - { field: updated_at, label: Updated, sortable: true }
```

Declared columns are also the viewer filter and projection allowlist. Mark only indexed fields sortable. Prefer an Explorer widget over a table when the result may exceed a few hundred rows.

## Reuse expensive reads

Place a read-only call in `shared_queries` when several widgets need the same result. Each refresh resolves a needed shared query once, then widgets derive their values with JSONata.

Keep chart and stat inputs aggregated and bounded at the connector. Do not fetch raw collections merely to count or group them in the browser.

## Add editable configuration

Define the canonical schema in the job template's `job_config`, then use dashboard `form` or `field_mapping` widgets when the same value needs a guided or public-facing editor.

For public editing, both the deployment switch and `dashboard.allow_public_input: true` must be enabled. See [Public dashboard widget execution](/guides/public-dashboard-widget-execution).

## Add actions last

An action may run a task, run the job, or invoke an approved connector method. Every action presents confirmation and creates an audit row.

```yaml
- id: run_validation
  type: action
  label: Run validation
  confirm_message: Run validation against the current configuration?
  action:
    kind: job_run
    tasks: [validate]
    skip_deps: false
  cooldown_seconds: 30
```

Public actions require additional explicit gates. Never accept raw sensitive connector arguments from a public form.

## Test before sharing

1. Validate and save the dashboard template.
2. Create a test job so template snapshot behavior is exercised.
3. Open every page as an authenticated user.
4. Verify data scope with records inside and outside the intended boundary.
5. Test empty, loading, error, and large-result states.
6. Save each input and confirm the job configuration changed.
7. Trigger each action and inspect its run and audit status.
8. If sharing, inspect the public URL in a private browser session.

Treat the share URL as a bearer credential. Revoke it when the audience changes.
