> ## Documentation Index
> Fetch the complete documentation index at: https://dench.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CRM analytics dashboards, KPIs, and charts

> Build KPI stats, bar and line charts, funnels, forecasts, and world maps over any Dench CRM object from the app, chat, CLI, and API.

Every CRM object has a dashboard. It sits above the table as a strip — KPI
stats in the header, a bento of charts underneath — and opens in a full sheet
from the toolbar's **Analytics** toggle. The math is one query,
`computePanels`; the UI, the CLI, the API, the chat card and the App SDK all
draw from it, so a number is the same number everywhere.

## What a dashboard is

A dashboard is a list of **panels**. A panel names a metric, an optional
group-by, an optional date bucketing and optional filters in the same
`FilterGroup` shape saved views use. No SQL.

| Panel          | Draws                                                                  |
| -------------- | ---------------------------------------------------------------------- |
| `stat`         | one KPI (count, sum, or a derived metric) with a previous-period delta |
| `bar`, `donut` | counts or sums by an enum / user / relation / boolean / text field     |
| `funnel`       | the pipeline, stage by stage                                           |
| `leaderboard`  | top 10 by owner or any relation                                        |
| `line`, `area` | a trend by day / week / month / quarter, optionally split by a group   |
| `forecast`     | weighted open pipeline by close month                                  |
| `map`          | a world map, drilled into the country or state where the data is       |

Derived metrics (`conversion_rate`, `win_rate`, `won_amount`, `lost_amount`,
`open_pipeline`, `pipeline_forecast`, `mrr`, `arr`) read the object's **field
roles** — amount, stage + won/lost/converted values, close date, owner,
location, recurrence, stage probabilities — so they work on any custom
object once the roles are mapped. Templates map them at install.

Two synthetic fields work anywhere a field name goes:

* `$createdAt` — the row's creation time. "New leads this week" needs no date
  column.
* `$location` — the location role, resolved through one relation hop
  (`Account.HQ Location`) so a deal inherits its account's HQ.

## Period, views, drills

* The header's **period pill** ("This month", "Last quarter", …) scopes
  every chart **and** the rows under the strip, and fills each stat's
  previous-period delta. It is never saved onto a view.
* Dashboards are **per object or per view**. Every view shows the object's
  shared dashboard until it gets its own (`scope --mode own` starts it as a
  copy). Any active view follows its own filters by default.
* Clicking a chart segment filters the table (chips in the header, one per
  dimension); the chart you clicked keeps its whole picture.

Deep link: `/?path=<object>&analytics=1[&view=<view>][&analyticsPeriod=this_month]`
opens the sheet with the pill set.

## Charts in chat

Ask Dench a quantitative question ("pipeline by stage this quarter", "where
are our customers", "win rate by rep") and the answer is a **live chart
card** in the thread — the object's own `computePanels`, updating as rows
land. Open takes you to the dashboard; an ad-hoc chart offers **Save to
dashboard**. When the panel model cannot express the question (a join across
objects, a custom formula, a cohort), Dench embeds a small App SDK widget
instead, fed by `dench.analytics.compute`; **Keep as app** turns it into a
workspace app. Off the web UI (Slack, iMessage, comments) you get the
headline number and the deep link.

## CLI

```bash theme={null}
# Read the dashboard (panels, roles, fields you may bucket on)
dench crm analytics get deal --json
dench crm analytics get company --view Customers --json

# Compute — the whole dashboard, a subset, or ad-hoc panels
dench crm analytics compute deal --period this_month --json
dench crm analytics compute deal --panel tpl:sales:open-pipeline --json
dench crm analytics compute lead --panels '[
  {"title":"Leads by source","type":"donut","groupBy":"Source"},
  {"title":"Leads per week","type":"line","dateField":"$createdAt","dateBucket":"week"}
]' --period this_quarter --json

# Edit
dench crm analytics save deal open-pipeline --panel '{"title":"Open pipeline","type":"stat","derived":"open_pipeline"}'
dench crm analytics delete deal tpl:sales:deals-by-stage
dench crm analytics reorder deal open-pipeline win-rate by-stage
dench crm analytics roles deal --roles '{"amount":"Amount","stage":"Stage","wonValues":["Closed Won"],"lostValues":["Closed Lost"],"closeDate":"Close Date","owner":"Owner"}'
dench crm analytics collapse deal
dench crm analytics scope company Customers --mode own
```

`--period` is `this_week | this_month | last_month | this_quarter |
last_quarter | this_year`. Each panel in a `compute` response says where its
numbers came from (`source`): `aggregate` — pre-computed buckets, exact at
any table size (counts, sums, averages, group-bys, monthly or coarser series,
won/lost/open \$, MRR/ARR, forecast, win rate); `scan` — the newest rows; or
`snapshot` — a background pass over every row that replaces a clipped scan.
`truncated: true` appears only while a `scan` panel could not see every row;
the snapshot is requested automatically and the next response is exact.
`link` is the deep link above.

## API

| Method           | Path                                                      |
| ---------------- | --------------------------------------------------------- |
| `GET`            | `/v1/crm/objects/{object}/analytics`                      |
| `POST`           | `/v1/crm/objects/{object}/analytics/compute`              |
| `PUT` / `DELETE` | `/v1/crm/objects/{object}/analytics/panels/{panelId}`     |
| `POST`           | `/v1/crm/objects/{object}/analytics/reorder`              |
| `PUT`            | `/v1/crm/objects/{object}/analytics/roles`                |
| `POST`           | `/v1/crm/objects/{object}/analytics/collapse` · `/expand` |
| `POST`           | `/v1/crm/objects/{object}/views/{view}/analytics/scope`   |

Apps call the same query through the bridge:
`dench.analytics.compute({ objectName, viewName?, panels?, panelIds?,
period?, extraFilters? })` — see the Apps reference.
