@object-ui/plugin-report 4.0.12 → 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.
- package/CHANGELOG.md +73 -0
- package/README.md +104 -22
- package/dist/index.js +1695 -551
- package/dist/index.umd.cjs +18 -18
- package/dist/packages/plugin-report/src/JoinedReportRenderer.d.ts +47 -0
- package/dist/packages/plugin-report/src/LegacyReportRenderer.d.ts +14 -0
- package/dist/packages/plugin-report/src/MatrixRenderer.d.ts +51 -0
- package/dist/packages/plugin-report/src/MatrixRenderer.stories.d.ts +24 -0
- package/dist/packages/plugin-report/src/ReportRenderer.d.ts +41 -11
- package/dist/packages/plugin-report/src/SpecReportGrid.d.ts +49 -0
- package/dist/packages/plugin-report/src/SpecReportGrid.stories.d.ts +31 -0
- package/dist/packages/plugin-report/src/buildChartData.d.ts +38 -0
- package/dist/packages/plugin-report/src/drill.d.ts +63 -0
- package/dist/packages/plugin-report/src/hooks/useReportData.d.ts +131 -0
- package/dist/packages/plugin-report/src/index.d.ts +14 -1
- package/package.json +9 -6
|
@@ -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
|
-
|
|
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.
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.umd.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -21,12 +21,15 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"clsx": "^2.1.1",
|
|
23
23
|
"lucide-react": "^1.16.0",
|
|
24
|
+
"react-i18next": "^17.0.7",
|
|
24
25
|
"tailwind-merge": "^3.6.0",
|
|
25
|
-
"@object-ui/components": "4.
|
|
26
|
-
"@object-ui/core": "4.
|
|
27
|
-
"@object-ui/fields": "4.
|
|
28
|
-
"@object-ui/
|
|
29
|
-
"@object-ui/
|
|
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",
|