@object-ui/plugin-report 4.0.11 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ import { ReportRow } from './hooks/useReportData';
2
+ import { SpecReport, SpecReportColumn } from '../../types/src';
3
+ export interface ChartAdapterLabelResolvers {
4
+ /** Resolve a friendly label for a measure column. Used for the chart's y-axis legend. */
5
+ resolveColumnLabel?: (col: SpecReportColumn) => string | undefined;
6
+ /** Resolve a friendly label for an implicit `count` measure given the yAxis field. */
7
+ resolveCountLabel?: (yField: string) => string | undefined;
8
+ /** Resolve a friendly display label for an x-axis bucket value (e.g. translate picklist values). */
9
+ resolveAxisValue?: (field: string, rawValue: unknown) => string | undefined;
10
+ }
11
+ export interface ChartAdapterInput {
12
+ report: SpecReport;
13
+ rows: ReportRow[];
14
+ labels?: ChartAdapterLabelResolvers;
15
+ }
16
+ export interface ChartAdapterOutput {
17
+ /** `{ data, xAxisKey, series, chartType }` ready for `<SchemaRenderer type="chart">`. */
18
+ schema: ChartSchema | null;
19
+ /** Human-readable diagnostic when we can't build a chart (missing xAxis, etc.). */
20
+ diagnostic?: string;
21
+ }
22
+ type ChartType = 'bar' | 'column' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'donut' | 'funnel' | 'radar' | 'scatter';
23
+ export interface ChartSchema {
24
+ type: 'chart';
25
+ chartType: ChartType;
26
+ data: Array<Record<string, unknown>>;
27
+ xAxisKey: string;
28
+ series: Array<{
29
+ dataKey: string;
30
+ label?: string;
31
+ }>;
32
+ title?: string;
33
+ showLegend?: boolean;
34
+ showDataLabels?: boolean;
35
+ className?: string;
36
+ }
37
+ export declare function buildChartData({ report, rows, labels }: ChartAdapterInput): ChartAdapterOutput;
38
+ export {};
@@ -0,0 +1,63 @@
1
+ import { ActionDef, ActionHandler, ActionRunner } from '../../core/src';
2
+ import { SpecReport } from '../../types/src';
3
+ /** Open-target for a drill. */
4
+ export type DrillOpenIn = 'current' | 'modal' | 'new-tab';
5
+ /** Drill target — either a list filtered down to the group, or a specific record. */
6
+ export type DrillView = 'list' | 'detail' | (string & {});
7
+ /** Serializable shape of a `type: 'drill'` action. */
8
+ export interface DrillActionDef extends ActionDef {
9
+ type: 'drill';
10
+ objectName: string;
11
+ filter?: Record<string, unknown>;
12
+ view?: DrillView;
13
+ recordId?: string | number;
14
+ openIn?: DrillOpenIn;
15
+ }
16
+ /** Resolved navigation target produced by a drill handler. */
17
+ export interface DrillNavigateTarget {
18
+ objectName: string;
19
+ filter?: Record<string, unknown>;
20
+ view?: DrillView;
21
+ recordId?: string | number;
22
+ openIn?: DrillOpenIn;
23
+ }
24
+ /** Options passed when creating a drill handler. */
25
+ export interface DrillHandlerOptions {
26
+ /**
27
+ * Host-supplied navigation callback. The handler invokes this with a
28
+ * fully-resolved target so the host can decide whether to push to
29
+ * router, open a modal, or open a new tab.
30
+ */
31
+ navigate: (target: DrillNavigateTarget) => void | Promise<void>;
32
+ }
33
+ /**
34
+ * Compose a `drill` action from a spec report + group key.
35
+ *
36
+ * Filter precedence (later overrides earlier where keys conflict):
37
+ * 1. `report.filter` (definition-time)
38
+ * 2. `runtimeFilter` (URL / user selection)
39
+ * 3. `groupKey` (the row/cell the user actually clicked)
40
+ *
41
+ * All three are combined via `$and` so backend filter engines see the
42
+ * full intersection.
43
+ */
44
+ export declare function buildDrillAction(report: SpecReport, groupKey: Record<string, unknown>, options?: {
45
+ runtimeFilter?: Record<string, unknown>;
46
+ view?: DrillView;
47
+ recordId?: string | number;
48
+ openIn?: DrillOpenIn;
49
+ }): DrillActionDef;
50
+ /** Type guard for drill actions. */
51
+ export declare function isDrillAction(action: ActionDef | undefined | null): action is DrillActionDef;
52
+ /**
53
+ * Build an `ActionHandler` for the `drill` action type.
54
+ *
55
+ * Register it on an `ActionRunner` (or `ActionEngine`'s runner) via
56
+ * `runner.registerHandler('drill', createDrillHandler({ navigate }))`.
57
+ */
58
+ export declare function createDrillHandler(opts: DrillHandlerOptions): ActionHandler;
59
+ /**
60
+ * Register the drill handler on an `ActionRunner` instance. Returns an
61
+ * unregister function for cleanup (useful in React effects).
62
+ */
63
+ export declare function registerDrillHandler(runner: ActionRunner, opts: DrillHandlerOptions): () => void;
@@ -0,0 +1,131 @@
1
+ import { SpecReport, SpecReportColumn, SpecReportGrouping, SpecReportDateGranularity } from '../../../types/src';
2
+ export interface ReportRow {
3
+ /** Group key path, e.g. `{ region: 'East', quarter: '2024-Q1' }`. Empty `{}` for the grand-total row. */
4
+ groupKey: Record<string, unknown>;
5
+ /** Display labels for the key path, in `[down..., across...]` order. */
6
+ groupPath: string[];
7
+ /** Aggregated values, keyed as `${field}__${aggregate}` (or just `${field}` for passthrough columns). */
8
+ values: Record<string, unknown>;
9
+ /** Number of underlying raw rows in this group. */
10
+ count: number;
11
+ /** Child rows for nested groupings. Undefined at the leaf level. */
12
+ children?: ReportRow[];
13
+ }
14
+ export interface UseReportDataResult {
15
+ rows: ReportRow[];
16
+ rawRows: Record<string, unknown>[];
17
+ totals: Record<string, unknown>;
18
+ /** Pivoted matrix shape — populated when `report.groupingsAcross` is non-empty. */
19
+ pivot: PivotMatrix | null;
20
+ loading: boolean;
21
+ error: Error | null;
22
+ refetch: () => Promise<void>;
23
+ drillDown: (groupKey: Record<string, unknown>) => Record<string, unknown>[];
24
+ }
25
+ /**
26
+ * 2D pivot output. `rowHeaders` × `colHeaders` form the matrix; `cells[rowId][colId]`
27
+ * holds the per-cell aggregated values keyed by `columnKey()`. Totals are
28
+ * pre-computed so the renderer doesn't need to re-aggregate.
29
+ */
30
+ export interface PivotHeader {
31
+ /** Group key path, e.g. `{ region: 'EMEA' }` (row) or `{ closeQuarter: '2024-Q1' }` (col). */
32
+ key: Record<string, unknown>;
33
+ /** Display labels in nesting order. */
34
+ path: string[];
35
+ /** Stable id derived from `path` — use as React key + lookup key into `cells`. */
36
+ id: string;
37
+ }
38
+ export interface PivotMatrix {
39
+ rowHeaders: PivotHeader[];
40
+ colHeaders: PivotHeader[];
41
+ /** `cells[rowId][colId] = { 'amount__sum': 1200, ... }`. Missing cells are absent (renderer fills with 0/—). */
42
+ cells: Record<string, Record<string, Record<string, unknown>>>;
43
+ /** Row totals across all columns, keyed by `rowId`. */
44
+ rowTotals: Record<string, Record<string, unknown>>;
45
+ /** Column totals across all rows, keyed by `colId`. */
46
+ colTotals: Record<string, Record<string, unknown>>;
47
+ /** Grand total across the entire matrix. */
48
+ grandTotal: Record<string, unknown>;
49
+ columns: readonly SpecReportColumn[];
50
+ downGroupings: readonly SpecReportGrouping[];
51
+ acrossGroupings: readonly SpecReportGrouping[];
52
+ }
53
+ export interface UseReportDataOptions {
54
+ /**
55
+ * Adapter exposing `find(resource, params)` and optionally `aggregate(resource, query)`.
56
+ * - When `.aggregate` is present, the hook prefers it for reports with date
57
+ * bucketing or pure aggregations — the server then runs DATE_TRUNC + GROUP
58
+ * BY natively and returns one row per bucket.
59
+ * - When only `.find` is present, the hook falls back to raw rows + client
60
+ * bucketing (see `bucketDate` / `groupingValue`).
61
+ * If the hook stays idle, no fetch happens.
62
+ */
63
+ dataSource?: {
64
+ find?: (resource: string, params?: Record<string, unknown>) => Promise<unknown>;
65
+ aggregate?: (resource: string, query: Record<string, unknown>) => Promise<unknown>;
66
+ };
67
+ /** Filter merged on top of `report.filter` via `$and` (e.g., URL params, user selections). */
68
+ runtimeFilter?: Record<string, unknown>;
69
+ /** Optional `$top` cap for the raw fetch. Defaults to 5000. */
70
+ maxRows?: number;
71
+ /** When `false`, skips fetching. Defaults to `true`. */
72
+ enabled?: boolean;
73
+ /** Pre-fetched rows. When provided, bypasses `dataSource` entirely. */
74
+ rows?: Record<string, unknown>[];
75
+ /**
76
+ * When true (default), prefer `dataSource.aggregate()` for reports that
77
+ * carry date granularity or any aggregating column. Set false to force the
78
+ * client-side path (useful for tests / debugging).
79
+ */
80
+ preferServerAggregation?: boolean;
81
+ }
82
+ /** Stable key for a column's aggregated output: `field__aggregate` (or just `field`). */
83
+ export declare function columnKey(col: SpecReportColumn): string;
84
+ /** Format a Date into a string bucket according to a date granularity. */
85
+ export declare function bucketDate(value: unknown, granularity: SpecReportDateGranularity): string | null;
86
+ /** Extract the group value for a single grouping, applying dateGranularity if set. */
87
+ export declare function groupingValue(row: Record<string, unknown>, grouping: SpecReportGrouping): string;
88
+ /**
89
+ * Compute aggregates for an array of rows against a list of columns.
90
+ * `unique` returns the count of distinct non-null values; `count` includes nulls.
91
+ */
92
+ export declare function aggregateRows(rows: Record<string, unknown>[], columns: readonly SpecReportColumn[]): Record<string, unknown>;
93
+ /**
94
+ * Recursive grouping. Each grouping level produces a nested `ReportRow[]`.
95
+ * The final leaf level still aggregates over the bucket so callers always have
96
+ * `values` available without re-walking children.
97
+ */
98
+ export declare function groupAndAggregate(rows: Record<string, unknown>[], groupings: readonly SpecReportGrouping[], columns: readonly SpecReportColumn[], parentKey?: Record<string, unknown>, parentPath?: string[]): ReportRow[];
99
+ /**
100
+ * Build a 2D pivot matrix from raw rows. Rows are bucketed by every
101
+ * `downGroupings` field path; columns by every `acrossGroupings` field path.
102
+ * Each cell is then aggregated independently with the report's columns.
103
+ *
104
+ * - When either dimension is empty, the matrix degenerates: a single header
105
+ * with empty path is emitted so the renderer can still display a 1×N or
106
+ * N×1 strip without special-casing.
107
+ * - Header ordering honours each grouping's `sort` ('asc' default, 'desc' opt-in).
108
+ * - `limit` on the *head* grouping of each dimension is respected (matches
109
+ * `groupAndAggregate`); deeper limits are ignored to preserve totals.
110
+ */
111
+ export declare function pivotRows(rows: Record<string, unknown>[], columns: readonly SpecReportColumn[], downGroupings: readonly SpecReportGrouping[], acrossGroupings: readonly SpecReportGrouping[]): PivotMatrix;
112
+ /** Combine two FilterCondition objects via `$and`, dropping empty ones. */
113
+ export declare function mergeFilters(a: Record<string, unknown> | undefined, b: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
114
+ /** Collect all fields referenced by groupings + columns + sort + filter (for $select). */
115
+ export declare function collectFields(report: SpecReport): string[];
116
+ /**
117
+ * Build a `QueryAST`-style payload for `dataSource.aggregate()`. Groupings
118
+ * with `dateGranularity` are emitted as structured objects so spec-aware
119
+ * backends can do server-side DATE_TRUNC; backends that don't recognise the
120
+ * field gracefully treat them as the bare field name (the engine's
121
+ * in-memory fallback handles the rest).
122
+ *
123
+ * Returns `null` when the report has no groupings *and* no aggregating
124
+ * columns — in that case there's no work for `aggregate()` to do and the
125
+ * hook should stick with `find()`.
126
+ */
127
+ export declare function buildAggregateQuery(report: SpecReport, runtimeFilter: Record<string, unknown> | undefined, maxRows: number): Record<string, unknown> | null;
128
+ /**
129
+ * Run a spec `Report` and return its rows, totals, and a drill-down helper.
130
+ */
131
+ export declare function useReportData(report: SpecReport | undefined, options?: UseReportDataOptions): UseReportDataResult;
@@ -1,11 +1,24 @@
1
1
  import { ReportRenderer } from './ReportRenderer';
2
+ import { LegacyReportRenderer } from './LegacyReportRenderer';
2
3
  import { ReportViewer } from './ReportViewer';
3
4
  import { ReportBuilder } from './ReportBuilder';
4
5
  import { ReportConfigPanel } from './ReportConfigPanel';
5
- export { ReportRenderer, ReportViewer, ReportBuilder, ReportConfigPanel };
6
+ import { SpecReportGrid } from './SpecReportGrid';
7
+ import { MatrixRenderer } from './MatrixRenderer';
8
+ import { JoinedReportRenderer } from './JoinedReportRenderer';
9
+ export { ReportRenderer, LegacyReportRenderer, ReportViewer, ReportBuilder, ReportConfigPanel, SpecReportGrid, MatrixRenderer, JoinedReportRenderer };
10
+ export type { ReportRendererProps, ReportRendererSchema } from './ReportRenderer';
11
+ export type { LegacyReportRendererProps } from './LegacyReportRenderer';
12
+ export type { SpecReportGridProps } from './SpecReportGrid';
13
+ export type { MatrixRendererProps, MatrixCellClickArgs } from './MatrixRenderer';
14
+ export type { JoinedReportRendererProps } from './JoinedReportRenderer';
15
+ export { buildDrillAction, createDrillHandler, registerDrillHandler, isDrillAction, } from './drill';
16
+ export type { DrillActionDef, DrillHandlerOptions, DrillNavigateTarget, DrillOpenIn, DrillView, } from './drill';
6
17
  export type { AvailableField } from './ReportConfigPanel';
7
18
  export { formatValue } from './formatValue';
8
19
  export { exportReport, exportAsCSV, exportAsJSON, exportAsHTML, exportAsPDF, exportAsExcel } from './ReportExportEngine';
9
20
  export { ScheduleConfig } from './ScheduleConfig';
10
21
  export { exportWithLiveData, exportExcelWithFormulas, createScheduleTrigger, } from './LiveReportExporter';
11
22
  export type { LiveExportOptions, LiveExportResult, ExcelColumnConfig, ScheduleTriggerCallback, } from './LiveReportExporter';
23
+ export { useReportData, columnKey, bucketDate, groupingValue, aggregateRows, groupAndAggregate, pivotRows, buildAggregateQuery, mergeFilters, collectFields, } from './hooks/useReportData';
24
+ export type { ReportRow, PivotHeader, PivotMatrix, UseReportDataResult, UseReportDataOptions, } from './hooks/useReportData';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-report",
3
- "version": "4.0.11",
3
+ "version": "4.2.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.umd.cjs",
6
6
  "module": "dist/index.js",
@@ -20,24 +20,27 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "clsx": "^2.1.1",
23
- "lucide-react": "^1.14.0",
23
+ "lucide-react": "^1.16.0",
24
+ "react-i18next": "^17.0.7",
24
25
  "tailwind-merge": "^3.6.0",
25
- "@object-ui/components": "4.0.11",
26
- "@object-ui/core": "4.0.11",
27
- "@object-ui/fields": "4.0.11",
28
- "@object-ui/react": "4.0.11",
29
- "@object-ui/types": "4.0.11"
26
+ "@object-ui/components": "4.2.1",
27
+ "@object-ui/core": "4.2.1",
28
+ "@object-ui/fields": "4.2.1",
29
+ "@object-ui/i18n": "4.2.1",
30
+ "@object-ui/plugin-grid": "4.2.1",
31
+ "@object-ui/react": "4.2.1",
32
+ "@object-ui/types": "4.2.1"
30
33
  },
31
34
  "peerDependencies": {
32
35
  "react": "^18.0.0",
33
36
  "react-dom": "^18.0.0"
34
37
  },
35
38
  "devDependencies": {
36
- "@types/node": "^25.7.0",
39
+ "@types/node": "^25.9.0",
37
40
  "@types/react": "19.2.14",
38
41
  "@types/react-dom": "19.2.3",
39
- "@vitejs/plugin-react": "^6.0.1",
40
- "vite": "^8.0.12",
42
+ "@vitejs/plugin-react": "^6.0.2",
43
+ "vite": "^8.0.13",
41
44
  "vite-plugin-dts": "^5.0.0",
42
45
  "vitest": "^4.1.6"
43
46
  },