Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 6 additions & 25 deletions content/docs/references/ui/dashboard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Color variant for dashboard widgets (e.g., KPI cards).
## TypeScript Usage

```typescript
import { Dashboard, DashboardHeader, DashboardHeaderAction, DashboardWidget, GlobalFilter, GlobalFilterOptionsFrom, WidgetActionType, WidgetColorVariant, WidgetMeasure } from '@objectstack/spec/ui';
import type { Dashboard, DashboardHeader, DashboardHeaderAction, DashboardWidget, GlobalFilter, GlobalFilterOptionsFrom, WidgetActionType, WidgetColorVariant, WidgetMeasure } from '@objectstack/spec/ui';
import { Dashboard, DashboardHeader, DashboardHeaderAction, DashboardWidget, GlobalFilter, GlobalFilterOptionsFrom, WidgetActionType, WidgetColorVariant } from '@objectstack/spec/ui';
import type { Dashboard, DashboardHeader, DashboardHeaderAction, DashboardWidget, GlobalFilter, GlobalFilterOptionsFrom, WidgetActionType, WidgetColorVariant } from '@objectstack/spec/ui';

// Validate data
const result = Dashboard.parse(data);
Expand Down Expand Up @@ -101,14 +101,11 @@ Dashboard header action
| **actionUrl** | `string` | optional | URL or target for the widget action button |
| **actionType** | `Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api'>` | optional | Type of action for the widget action button |
| **actionIcon** | `string` | optional | Icon identifier for the widget action button |
| **object** | `string` | optional | Data source object name |
| **filter** | `[__schema0](./__schema0)` | optional | Data filter criteria |
| **filter** | `[__schema0](./__schema0)` | optional | Presentation-scope filter (runtimeFilter) |
| **compareTo** | `string \| string \| Object` | optional | Period-over-period comparison window |
| **categoryField** | `string` | optional | Field for grouping (X-Axis) |
| **categoryGranularity** | `Enum<'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>` | optional | Bucket categoryField date values into day/week/month/quarter/year periods |
| **valueField** | `string` | optional | Field for values (Y-Axis) |
| **aggregate** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max'>` | ✅ | Aggregate function |
| **measures** | `Object[]` | optional | Multiple measures for pivot/matrix analysis |
| **dataset** | `string` | ✅ | Dataset name to bind (ADR-0021) |
| **dimensions** | `string[]` | optional | Dimension names — X/group/split |
| **values** | `string[]` | ✅ | Measure names — Y (at least one) |
| **layout** | `Object` | ✅ | Grid layout position |
| **options** | `any` | optional | Widget specific configuration |
| **responsive** | `Object` | optional | Responsive layout configuration |
Expand Down Expand Up @@ -184,19 +181,3 @@ Widget color variant

---

## WidgetMeasure

Widget measure definition

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **valueField** | `string` | ✅ | Field to aggregate |
| **aggregate** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max'>` | ✅ | Aggregate function |
| **label** | `string` | optional | Measure display label |
| **format** | `string` | optional | Number format string |


---

128 changes: 128 additions & 0 deletions content/docs/references/ui/dataset.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
title: Dataset
description: Dataset protocol schemas
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs go in content/docs/guides/. */}

Analytics Dataset — the one semantic layer (ADR-0021).

A `dataset` is a named, reusable analytical definition: a base object, the

relationships to include (joins are *derived* from the object graph — the

author never writes an `ON` clause), and the declared **dimensions**

(groupable axes) and **measures** (aggregatable values). It is deliberately

SMALLER than `QuerySchema`: no raw SQL, no hand-authored join predicates,

no window/having grammar in the author surface.

Presentations (`report` / `dashboard`) bind to a dataset by reference and

pick dimensions/measures *by name*. The dataset compiles to the existing

Cube analytics runtime (ADR-0021 D-A=(c)); RLS / tenant scoping is enforced

by the runtime per joined object (D-C), never declared here.

Naming: this module owns the high-prior `dataset` / `dimension` / `measure`

vocabulary (LookML / dbt / Cube / PowerBI). The Zod export identifiers are

`Dataset`-prefixed (`DatasetDimensionSchema`, `DatasetMeasureSchema`) so they

do not clash with the Cube layer's `DimensionSchema` / `MetricSchema` in

`data/analytics.zod.ts` while the two layers coexist (Phase 1). The Cube

layer is absorbed/retired in a later phase (D-A).

<Callout type="info">
**Source:** `packages/spec/src/ui/dataset.zod.ts`
</Callout>

## TypeScript Usage

```typescript
import { Dataset, DatasetDimension, DatasetMeasure, DerivedMeasureOp } from '@objectstack/spec/ui';
import type { Dataset, DatasetDimension, DatasetMeasure, DerivedMeasureOp } from '@objectstack/spec/ui';

// Validate data
const result = Dataset.parse(data);
```

---

## Dataset

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | ✅ | Dataset unique name |
| **label** | `string` | ✅ | Dataset label |
| **description** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
| **object** | `string` | ✅ | Base object name |
| **include** | `string[]` | optional | Relationship names to join (derived from object graph) |
| **filter** | `[__schema0](./__schema0)` | optional | Intrinsic dataset scope filter |
| **dimensions** | `Object[]` | ✅ | Groupable axes |
| **measures** | `Object[]` | ✅ | Aggregatable values |
| **protection** | `Object` | optional | Package author protection block — lock policy for this dataset. |
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
| **_lockReason** | `string` | optional | Human-readable reason shown when a write is refused by _lock. |
| **_lockSource** | `Enum<'artifact' \| 'package' \| 'env-forced'>` | optional | Layer that set _lock (artifact | package | env-forced). |
| **_provenance** | `Enum<'package' \| 'org' \| 'env-forced'>` | optional | Origin of the item (package | org | env-forced). |
| **_packageId** | `string` | optional | Owning package machine id. |
| **_packageVersion** | `string` | optional | Owning package version. |
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |


---

## DatasetDimension

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | ✅ | Dimension name — referenced by presentations |
| **label** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
| **field** | `string` | ✅ | Base field or `relationship.field` path |
| **type** | `Enum<'string' \| 'number' \| 'date' \| 'boolean' \| 'lookup'>` | optional | |
| **dateGranularity** | `Enum<'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>` | optional | |


---

## DatasetMeasure

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | ✅ | Measure name — e.g. "revenue"; defined once |
| **label** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
| **aggregate** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max' \| 'count_distinct' \| 'array_agg' \| 'string_agg'>` | ✅ | Aggregation (sum/avg/count/...) |
| **field** | `string` | optional | Aggregated field; optional for count(*) |
| **filter** | `[__schema0](./__schema0)` | optional | |
| **format** | `string` | optional | |
| **certified** | `boolean` | ✅ | Blessed metric (governance checkpoint) |
| **derived** | `Object` | optional | |


---

## DerivedMeasureOp

### Allowed Values

* `ratio`
* `sum`
* `difference`
* `product`


---

1 change: 1 addition & 0 deletions content/docs/references/ui/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This section contains all protocol schemas for the ui layer of ObjectStack.
<Card href="/docs/references/ui/chart" title="Chart" description="Source: packages/spec/src/ui/chart.zod.ts" />
<Card href="/docs/references/ui/component" title="Component" description="Source: packages/spec/src/ui/component.zod.ts" />
<Card href="/docs/references/ui/dashboard" title="Dashboard" description="Source: packages/spec/src/ui/dashboard.zod.ts" />
<Card href="/docs/references/ui/dataset" title="Dataset" description="Source: packages/spec/src/ui/dataset.zod.ts" />
<Card href="/docs/references/ui/dnd" title="Dnd" description="Source: packages/spec/src/ui/dnd.zod.ts" />
<Card href="/docs/references/ui/i18n" title="I18n" description="Source: packages/spec/src/ui/i18n.zod.ts" />
<Card href="/docs/references/ui/keyboard" title="Keyboard" description="Source: packages/spec/src/ui/keyboard.zod.ts" />
Expand Down
1 change: 1 addition & 0 deletions content/docs/references/ui/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"chart",
"component",
"dashboard",
"dataset",
"dnd",
"http",
"i18n",
Expand Down
18 changes: 8 additions & 10 deletions content/docs/references/ui/report.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ const result = JoinedReportBlock.parse(data);
| **label** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
| **description** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
| **type** | `Enum<'tabular' \| 'summary' \| 'matrix'>` | ✅ | |
| **objectName** | `string` | optional | |
| **columns** | `Object[]` | ✅ | |
| **groupingsDown** | `Object[]` | optional | |
| **groupingsAcross** | `Object[]` | optional | |
| **filter** | `[__schema0](./__schema0)` | optional | |
| **chart** | `Object` | optional | |
| **dataset** | `string` | optional | Dataset name to bind (ADR-0021) |
| **rows** | `string[]` | optional | Dimension names down (dataset-bound) |
| **values** | `string[]` | optional | Measure names to show (dataset-bound) |
| **runtimeFilter** | `[__schema0](./__schema0)` | optional | Render-time scope filter (dataset-bound) |


---
Expand All @@ -52,12 +51,11 @@ const result = JoinedReportBlock.parse(data);
| **name** | `string` | ✅ | Report unique name |
| **label** | `string` | ✅ | Report label |
| **description** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
| **objectName** | `string` | ✅ | Primary object |
| **type** | `Enum<'tabular' \| 'summary' \| 'matrix' \| 'joined'>` | ✅ | Report format type |
| **columns** | `Object[]` | | Columns to display |
| **groupingsDown** | `Object[]` | optional | Row groupings |
| **groupingsAcross** | `Object[]` | optional | Column groupings (Matrix only) |
| **filter** | `[__schema0](./__schema0)` | optional | Filter criteria |
| **dataset** | `string` | optional | Dataset name to bind (ADR-0021) |
| **rows** | `string[]` | optional | Dimension names down |
| **values** | `string[]` | optional | Measure names to show |
| **runtimeFilter** | `[__schema0](./__schema0)` | optional | Render-time scope filter |
| **chart** | `Object` | optional | Embedded chart configuration |
| **aria** | `Object` | optional | ARIA accessibility attributes |
| **performance** | `Object` | optional | Performance optimization settings |
Expand Down
7 changes: 3 additions & 4 deletions content/docs/references/ui/view.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,9 @@ List chart view configuration
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **chartType** | `Enum<'bar' \| 'line' \| 'pie' \| 'area' \| 'scatter'>` | ✅ | Chart visualisation type |
| **xAxisField** | `string` | ✅ | Field used as the X axis / category dimension |
| **yAxisFields** | `string[]` | ✅ | Field(s) used as the Y axis / measures |
| **aggregation** | `Enum<'sum' \| 'avg' \| 'count' \| 'min' \| 'max'>` | optional | Aggregation function applied to Y axis fields |
| **groupByField** | `string` | optional | Optional field used to split / stack the chart |
| **dataset** | `string` | ✅ | Dataset name to bind (ADR-0021) |
| **dimensions** | `string[]` | optional | Dimension names — X/group/split |
| **values** | `string[]` | ✅ | Measure names — Y (at least one) |


---
Expand Down
20 changes: 0 additions & 20 deletions examples/app-crm/src/dashboards/pipeline.dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ export const PipelineDashboard: Dashboard = {
type: 'metric',
title: 'Total Pipeline ($)',
description: 'Sum of opportunity amounts across open stages.',
object: 'crm_opportunity',
aggregate: 'sum',
valueField: 'amount',
filter: { stage: { $nin: ['closed_won', 'closed_lost'] } },
dataset: 'opportunity_metrics',
values: ['total_amount'],
Expand All @@ -48,9 +45,6 @@ export const PipelineDashboard: Dashboard = {
type: 'metric',
title: 'Won This Quarter',
description: 'Revenue closed-won in the current quarter, compared to the previous quarter.',
object: 'crm_opportunity',
aggregate: 'sum',
valueField: 'amount',
filter: {
stage: 'closed_won',
close_date: {
Expand All @@ -69,9 +63,6 @@ export const PipelineDashboard: Dashboard = {
type: 'metric',
title: 'Avg Deal Size (YoY)',
description: 'Average won-deal value this year vs the same window last year.',
object: 'crm_opportunity',
aggregate: 'avg',
valueField: 'amount',
filter: {
stage: 'closed_won',
close_date: {
Expand All @@ -92,10 +83,6 @@ export const PipelineDashboard: Dashboard = {
type: 'line',
title: 'Pipeline Trend (12 months)',
description: 'Opportunity count bucketed by close-month for the last year, with a sliding overlay of the prior year for compareTo.',
object: 'crm_opportunity',
aggregate: 'count',
categoryField: 'close_date',
categoryGranularity: 'month',
filter: {
close_date: { $gte: '{1_years_ago}', $lte: '{today}' },
},
Expand All @@ -110,9 +97,6 @@ export const PipelineDashboard: Dashboard = {
type: 'bar',
title: 'Opportunities by Stage',
description: 'Count grouped by stage with previous-quarter overlay (compareTo).',
object: 'crm_opportunity',
aggregate: 'count',
categoryField: 'stage',
filter: {
close_date: {
$gte: '{current_quarter_start}',
Expand All @@ -132,10 +116,6 @@ export const PipelineDashboard: Dashboard = {
type: 'pie',
title: 'Open Pipeline by Stage ($)',
description: 'Open-pipeline revenue split by pipeline stage. Pie/donut/funnel ignore `compareTo`.',
object: 'crm_opportunity',
aggregate: 'sum',
valueField: 'amount',
categoryField: 'stage',
filter: { stage: { $nin: ['closed_won', 'closed_lost'] } },
dataset: 'opportunity_metrics',
dimensions: ['stage'],
Expand Down
4 changes: 3 additions & 1 deletion examples/app-crm/src/datasets/opportunity.dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const OpportunityDataset = defineDataset({

dimensions: [
{ name: 'stage', label: 'Stage', field: 'stage', type: 'string' },
{ name: 'close_date', label: 'Close Date', field: 'close_date', type: 'date' },
// ADR-0021 single-form: the monthly bucketing the trend widget used to carry
// as `categoryGranularity: 'month'` now lives on the dimension itself.
{ name: 'close_date', label: 'Close Date', field: 'close_date', type: 'date', dateGranularity: 'month' },
],

measures: [
Expand Down
6 changes: 0 additions & 6 deletions examples/app-crm/src/reports/sales-by-stage.report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@ export const SalesByStageReport: UI.Report = {
name: 'crm_sales_by_stage',
label: 'Sales by Stage',
description: 'Total opportunity amount grouped by sales stage.',
objectName: 'crm_opportunity',
type: 'summary',
columns: [
{ field: 'stage', label: 'Stage' },
{ field: 'amount', label: 'Amount', aggregate: 'sum' },
],
groupingsDown: [{ field: 'stage', sortOrder: 'asc' }],
dataset: 'opportunity_metrics',
rows: ['stage'],
values: ['total_amount'],
Expand Down
9 changes: 6 additions & 3 deletions examples/app-crm/test/smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ describe('Pipeline dashboard', () => {
const w: any = byId.get('pipeline_trend_90d');
expect(w.compareTo).toBe('previousYear');
expect(w.type).toBe('line');
expect(w.categoryGranularity).toBe('month');
// ADR-0021 single-form: the date axis is a dataset dimension (its monthly
// bucketing lives on the dataset's close_date dimension, not the widget).
expect(w.dimensions).toContain('close_date');
});

it('omits compareTo on widgets that do not need it (pie, total)', () => {
Expand All @@ -178,9 +180,10 @@ describe('Pipeline dashboard', () => {
expect(w.type).toBe('bar');
});

it('widgets bind to the opportunity object', () => {
it('widgets bind to the opportunity dataset', () => {
// ADR-0021 single-form: widgets reference the semantic dataset, not a raw object.
for (const w of PipelineDashboard.widgets) {
expect((w as any).object).toBe('crm_opportunity');
expect((w as any).dataset).toBe('opportunity_metrics');
}
});

Expand Down
Loading