@@ -248,7 +248,9 @@ an `aggregate` measure or chart spec, and a `layout: {x,y,w,h}`.
248248
249249See the ** Production Pattern** section below for the full
250250` Dashboard ` shape with ` refreshInterval ` , header actions, date range,
251- global filters, and widget options.
251+ global filters, widget options, and the period-over-period (` compareTo ` )
252+ / date-bucketing (` categoryGranularity ` ) modifiers available on every
253+ data-bound widget.
252254
253255---
254256
@@ -491,15 +493,80 @@ no second `filter` is required.
491493| ` { offset: '7d' } ` | Shift by an explicit duration. Units: ` d ` (days), ` w ` (weeks), ` M ` (months), ` y ` (years). |
492494
493495* ** Metric widgets** — the prior-period value renders as a small caption
494- beneath the headline number, alongside a green/red delta arrow.
495- Authors should * not* hand-author ` options.trend ` when ` compareTo ` is
496- set; the renderer wins and overwrites it.
497- * ** Chart widgets** — the comparison series is appended after the
498- primary series with ` variant: 'comparison' ` and styled as a muted /
499- dashed overlay. Single-series charts (bar / line / area) get an
500- automatic second series; multi-series pivots are not yet supported.
496+ beneath the headline number, alongside a green/red delta arrow and an
497+ i18n label (` 较上期 ` / ` vs previous period ` , ` 较去年 ` / ` vs last year ` ,
498+ ` 较前7天 ` / ` vs previous 7d ` , etc.). Authors should * not* hand-author
499+ ` options.trend ` when ` compareTo ` is set; the renderer wins and overwrites
500+ it.
501+ * ** Cartesian charts** (` line ` / ` area ` / ` bar ` / ` horizontal-bar ` /
502+ ` scatter ` ) — the comparison series is appended after the primary series
503+ with ` variant: 'comparison' ` and styled as a muted overlay (` opacity: 0.5 `
504+ + ` strokeDasharray: '4 4' ` for line/area/scatter; ` opacity: 0.4 ` for
505+ bars). Override per-series with ` series.dashArray ` / ` series.opacity ` .
506+ * ** Pie / donut / funnel** — ` compareTo ` is silently ignored; there is no
507+ meaningful "two-period" composition for part-of-whole charts.
501508* ** Requirements** — ` compareTo ` is a no-op when the filter contains no
502- resolvable date macros and no global ` dateRange ` is configured.
509+ resolvable date macros and no global ` dateRange ` is configured. The
510+ shifted query reuses the original ` filter ` shape and replaces only the
511+ date-bound clauses.
512+
513+ ``` typescript
514+ // Metric — WoW delta
515+ { id : ' done_this_week' , type : ' metric' , object : ' task' ,
516+ filter : { assignee : ' {current_user_id}' , status : ' done' ,
517+ completed_at : { $gte : ' {week_start}' } },
518+ aggregate : ' count' ,
519+ compareTo : ' previousPeriod' }
520+
521+ // Bar — YoY overlay on a stable category set
522+ { id : ' headcount_by_dept' , type : ' bar' , object : ' employee' ,
523+ filter : { status : { $ne : ' terminated' } },
524+ aggregate : ' count' , categoryField : ' department' ,
525+ compareTo : ' previousYear' }
526+ ```
527+
528+ ### Server-side date bucketing — ` categoryGranularity `
529+
530+ For any chart with ` categoryField ` pointing at a date/datetime field, set
531+ ` categoryGranularity ` to bucket values server-side. Without it every
532+ distinct timestamp becomes its own category, which collapses a 12-row
533+ seed dataset into a 12-point flat-line chart.
534+
535+ | Value | Bucket key |
536+ | :--| :--|
537+ | ` 'day' ` | Calendar day (` YYYY-MM-DD ` ) |
538+ | ` 'week' ` | ISO week (` YYYY-Www ` ) |
539+ | ` 'month' ` | Calendar month (` YYYY-MM ` ) |
540+ | ` 'quarter' ` | Calendar quarter (` YYYY-Qn ` ) |
541+ | ` 'year' ` | Calendar year (` YYYY ` ) |
542+
543+ * ** Engine support** — Postgres ` date_trunc ` , MySQL ` date_format ` , SQLite
544+ ` strftime ` , MongoDB ` $dateTrunc ` , in-memory ` bucketDateValue ` fallback.
545+ All emitted by the analytics service, not the client.
546+ * ** Rule of thumb** — ` day ` for ≤30d windows, ` week ` for ~ 90d, ` month `
547+ for 6–12 months, ` quarter ` for multi-year, ` year ` for retention /
548+ compliance scopes.
549+ * ** Combines with ` compareTo ` ** — the comparison query is issued with the
550+ same granularity, so the muted overlay aligns bucket-for-bucket.
551+ * ** xAxis format** — pair with a matching ` chartConfig.xAxis.format `
552+ (` %b %d ` for day, ` %b %Y ` for month, etc.) so the rendered labels
553+ match the bucket grain.
554+
555+ ``` typescript
556+ // Line chart — monthly trend with YoY overlay
557+ { id : ' signed_by_month' , type : ' line' , object : ' contract' ,
558+ filter : { signed_date : { $gte : ' {12_months_ago}' } },
559+ aggregate : ' count' ,
560+ categoryField : ' signed_date' ,
561+ categoryGranularity : ' month' ,
562+ compareTo : ' previousYear' ,
563+ chartConfig : {
564+ type : ' line' ,
565+ xAxis : { field : ' signed_date' , format : ' %b %Y' },
566+ yAxis : [{ field: ' value' , format: ' 0,0' }],
567+ },
568+ }
569+ ```
503570
504571---
505572
0 commit comments