robobyte-front-builder 1.0.25 → 1.0.27
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/INTEGRATION.md +94 -10
- package/LICENSE +65 -0
- package/README.md +149 -21
- package/docs/ReportViewer.md +581 -0
- package/docs/fetchReportData.md +379 -0
- package/docs/printLayout.md +405 -0
- package/package.json +29 -1
- package/src/lib/agGridTheme.js +58 -0
- package/src/lib/agGridThemeContext.jsx +42 -0
- package/src/lib/index.js +7 -0
- package/src/lib/providers/RoboByteFrontBuilderProvider.jsx +14 -0
- package/src/views/builder/viewer/renderers/DataGridRenderer.jsx +4 -4
- package/src/views/genericTable/SGrid.js +5 -5
- package/src/views/genericTable/TAGGrid.js +3 -5
- package/src/views/rolePermissions/UpdateReportPermissionDialog.js +4 -0
- package/training/00-index.md +168 -0
- package/training/01-input.md +144 -0
- package/training/02-checkbox.md +107 -0
- package/training/03-dropdown.md +135 -0
- package/training/04-datepicker.md +139 -0
- package/training/05-radio.md +123 -0
- package/training/06-number.md +133 -0
- package/training/07-textarea.md +114 -0
- package/training/08-richtext.md +112 -0
- package/training/09-tag.md +110 -0
- package/training/10-time.md +107 -0
- package/training/11-toggle.md +108 -0
- package/training/12-signature.md +107 -0
- package/training/13-autocomplete.md +134 -0
- package/training/14-button.md +168 -0
- package/training/15-label.md +138 -0
- package/training/16-header.md +128 -0
- package/training/17-divider.md +96 -0
- package/training/18-image.md +105 -0
- package/training/19-link.md +108 -0
- package/training/20-banner.md +122 -0
- package/training/21-progress-circle.md +101 -0
- package/training/22-progress-line.md +93 -0
- package/training/23-menu.md +139 -0
- package/training/24-popover.md +114 -0
- package/training/25-layout.md +116 -0
- package/training/26-layout-cell.md +143 -0
- package/training/27-card.md +87 -0
- package/training/28-wizard.md +126 -0
- package/training/29-wizard-step.md +92 -0
- package/training/30-repeater.md +123 -0
- package/training/31-dialog.md +131 -0
- package/training/32-breadcrumb.md +121 -0
- package/training/33-dataGrid.md +129 -0
- package/training/34-dataTableViewer.md +115 -0
- package/training/35-reportViewer.md +673 -0
- package/training/36-viewRenderer.md +110 -0
- package/training/37-treeView.md +170 -0
- package/training/38-kpi-metric.md +148 -0
- package/training/39-kpi-trend.md +105 -0
- package/training/40-kpi-badge.md +112 -0
- package/training/41-kpi-statusDot.md +118 -0
- package/training/42-kpi-iconBox.md +114 -0
- package/training/43-kpi-gauge.md +143 -0
- package/training/44-kpi-bulletChart.md +126 -0
- package/training/45-kpi-colorScale.md +143 -0
- package/training/46-kpi-rating.md +125 -0
- package/training/47-kpi-countdown.md +151 -0
- package/training/48-fetchReportData.md +276 -0
- package/training/49-printLayout.md +215 -0
- package/training/examples/01-login-form.json +176 -0
- package/training/examples/02-contact-form.json +141 -0
- package/training/examples/03-kpi-cards-row.json +123 -0
- package/training/examples/04-settings-toggles.json +153 -0
- package/training/examples/05-user-profile-card.json +136 -0
- package/training/examples/06-date-range-filter.json +108 -0
- package/training/examples/07-search-bar-results.json +130 -0
- package/training/examples/08-notification-settings.json +131 -0
- package/training/examples/09-employee-profile-form.json +259 -0
- package/training/examples/10-invoice-form.json +241 -0
- package/training/examples/11-dashboard-overview.json +251 -0
- package/training/examples/12-registration-wizard.json +154 -0
- package/training/examples/13-product-catalog.json +168 -0
- package/training/examples/14-data-table-with-filters.json +180 -0
- package/training/examples/15-tabbed-profile.json +92 -0
- package/training/examples/16-kpi-full-row.json +203 -0
- package/training/examples/17-tree-detail-view.json +139 -0
- package/training/examples/18-employee-management.json +233 -0
- package/training/examples/19-sales-dashboard.json +272 -0
- package/training/examples/20-checkout-wizard.json +225 -0
- package/training/examples/21-analytics-page.json +222 -0
- package/training/examples/22-hr-onboarding.json +222 -0
- package/training/examples/23-document-browser.json +241 -0
- package/training/examples/24-order-management.json +290 -0
- package/training/examples/25-crm-contact-page.json +272 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
# `<ReportViewer>` — Standalone Component Reference (Host-App Usage)
|
|
2
|
+
|
|
3
|
+
> **Scope.** This document is for embedding `<ReportViewer>` as a React component **outside the UI Builder** — inside a regular page, dialog, drawer, or dashboard tile in a host Next.js app that consumes `robobyte-front-builder`.
|
|
4
|
+
>
|
|
5
|
+
> If you're emitting a **builder schema** (`{ "type": "reportViewer", "props": {…} }`), use [`training/35-reportViewer.md`](../training/35-reportViewer.md) instead — that document covers the inspector-resolved expression scope, `actionsConfig` / `viewerActions` Calculation runtime, `reportRefs` registry, and the `THIS_NODE` sentinel, none of which apply here.
|
|
6
|
+
|
|
7
|
+
In host-app code you write plain React. There is no Calculation runtime, no cross-report ref registry, no inspector. Row actions are plain AG Grid `cellRenderer` functions, viewer actions are plain `onClick` handlers, and you wire data through props and callbacks.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Quick start
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
import { ReportViewer } from 'robobyte-front-builder'
|
|
15
|
+
|
|
16
|
+
export default function OrdersPage() {
|
|
17
|
+
return (
|
|
18
|
+
<ReportViewer
|
|
19
|
+
pageId="ORDERS_BY_REGION"
|
|
20
|
+
height="70vh"
|
|
21
|
+
title="Orders"
|
|
22
|
+
caption="All regions"
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That's the minimum. The component:
|
|
29
|
+
- Calls `Endpoints.ReportBuilder.Post.GetReportDetails` on mount with the supplied `id` / `pageId`.
|
|
30
|
+
- Renders the toolbar, AG Grid Enterprise grid, and side tool panel.
|
|
31
|
+
- Handles pagination, filtering, sorting, grouping, aggregation, templates internally.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Prerequisites
|
|
36
|
+
|
|
37
|
+
### 2.1 Provider in your `_app.js`
|
|
38
|
+
|
|
39
|
+
`<ReportViewer>` reads the API base URL, auth token, AG Grid license, **and the Quartz theme** from `<RoboByteFrontBuilderProvider>`. Without it, HTTP calls go to the wrong host and AG Grid throws a license warning. The `agGridTheme` prop (optional) lets you customize colors, header / row height, fonts, border radius, etc. across every grid the package renders.
|
|
40
|
+
|
|
41
|
+
```jsx
|
|
42
|
+
import { RoboByteFrontBuilderProvider } from 'robobyte-front-builder'
|
|
43
|
+
|
|
44
|
+
export default function App({ Component, pageProps }) {
|
|
45
|
+
const auth = useYourAuth()
|
|
46
|
+
return (
|
|
47
|
+
<RoboByteFrontBuilderProvider
|
|
48
|
+
baseURL={process.env.NEXT_PUBLIC_API_BASE_URL}
|
|
49
|
+
apiURL={process.env.NEXT_PUBLIC_API_URL}
|
|
50
|
+
user={auth.user}
|
|
51
|
+
accessToken={auth.accessToken}
|
|
52
|
+
agGridLicenseKey={process.env.NEXT_PUBLIC_AG_GRID_LICENSE_KEY}
|
|
53
|
+
// Optional: theme overrides for every <AgGridReact> in the package.
|
|
54
|
+
// agGridTheme={{ accentColor: '#3b82f6', headerHeight: 32 }}
|
|
55
|
+
>
|
|
56
|
+
<Component {...pageProps} />
|
|
57
|
+
</RoboByteFrontBuilderProvider>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2.2 Peer dependencies installed in the host app
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install \
|
|
66
|
+
react react-dom next \
|
|
67
|
+
@mui/material @mui/icons-material @mui/lab @mui/system \
|
|
68
|
+
@mui/x-data-grid @mui/x-date-pickers @mui/x-internals @mui/x-virtualizer \
|
|
69
|
+
@emotion/react @emotion/styled \
|
|
70
|
+
ag-grid-community ag-grid-enterprise ag-grid-react \
|
|
71
|
+
xlsx react-hot-toast
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2.3 `next.config.js` transpile + bare-alias plugin
|
|
75
|
+
|
|
76
|
+
See [INTEGRATION.md](../INTEGRATION.md) for the exact `next-transpile-modules` + `NormalModuleReplacementPlugin` setup. `<ReportViewer>` depends on internal bare-aliased imports (`services/*`, `views/*`) that the plugin resolves.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. Props reference
|
|
81
|
+
|
|
82
|
+
Listed alphabetically. Where types overlap with the builder schema, the **value** here is a real JS object / function, **not** an expression string.
|
|
83
|
+
|
|
84
|
+
### 3.1 Identity & data
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Required | Description |
|
|
87
|
+
|---|---|---|---|
|
|
88
|
+
| `id` | string \| number | one of | Report id from the backend. |
|
|
89
|
+
| `pageId` | string \| number | one of | Page / sub-report id. **At least one of `id` / `pageId` must be set.** |
|
|
90
|
+
| `sessionId` | string | no | Persists user filter / search state to `localStorage` under this key across navigations. |
|
|
91
|
+
| `globalParams` | object | no | Object keyed by 0-based **index** into the report's `selectionParams`, e.g. `{ 0: year, 1: region }`. Overrides defaults at request time. |
|
|
92
|
+
|
|
93
|
+
### 3.2 Filter
|
|
94
|
+
|
|
95
|
+
| Prop | Type | Description |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `filter` | object | Merged into the request. **See section 4 for the full shape.** Plain JS object — not an expression string. |
|
|
98
|
+
|
|
99
|
+
### 3.3 Presentation
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Default | Description |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| `title` | string | report's name | Bold heading on the left of the toolbar. |
|
|
104
|
+
| `caption` | string | — | Small secondary text under `title`. |
|
|
105
|
+
| `height` | string | `'85vh'` | CSS height (`'500px'`, `'70vh'`, etc.). |
|
|
106
|
+
| `minimized` | boolean | `false` | Removes toolbar + paddings. Useful inside cards / popovers. |
|
|
107
|
+
| `noHeader` | boolean | `false` | Hides the standalone-page chrome (report-name + Studio button). Toolbar (title / search / filter / refresh / viewer actions) stays. |
|
|
108
|
+
| `isSingle` | boolean | `false` | Return only the first row. |
|
|
109
|
+
| `dataAsObject` | boolean | `false` | Returns data keyed by row's unique id rather than an array. |
|
|
110
|
+
|
|
111
|
+
### 3.4 Refresh & timer
|
|
112
|
+
|
|
113
|
+
| Prop | Type | Description |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `refresh` | any | Toggle / change this value to force a reload. Use a state value (`refreshTick`) and `setRefreshTick(Date.now())` to trigger. |
|
|
116
|
+
| `externalTimer` | number | Auto-refresh interval **in minutes**. `0` or `null` disables. |
|
|
117
|
+
| `isRerender` | boolean | Toggle to fully remount the grid (resets internal state). |
|
|
118
|
+
|
|
119
|
+
### 3.5 Columns
|
|
120
|
+
|
|
121
|
+
| Prop | Type | Description |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `extraCols` | `colDef[]` | AG Grid `colDef` objects appended to the report's columns. Plain functions are fine — `valueGetter`, `valueSetter`, `cellRenderer`, `cellStyle` etc. |
|
|
124
|
+
| `columnsConfig` | array | Per-field overrides matched by `field`: `[{ field, config: { editable, valueFormatter, ... } }, …]`. |
|
|
125
|
+
|
|
126
|
+
### 3.6 Actions
|
|
127
|
+
|
|
128
|
+
| Prop | Type | Description |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `actions` | function | AG Grid `cellRenderer` function for the row's Actions cell. Receives `params` (the AG Grid params object). You write whatever JSX you want. |
|
|
131
|
+
| `viewerActions` | `ViewerAction[]` | Page-level buttons rendered after Filter / Refresh. **See section 7.** |
|
|
132
|
+
|
|
133
|
+
### 3.7 Programmatic hooks
|
|
134
|
+
|
|
135
|
+
| Prop | Type | Description |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `setData` | `(rows) => void` | Callback fired when row data loads / changes. |
|
|
138
|
+
| `setOutGridApi` | `(api) => void` | Callback receiving the AG Grid API instance once `onGridReady` fires. Use it to read selected rows, refresh server-side, export, etc. |
|
|
139
|
+
| `setEventRowSelected` | `(event) => void` | Optional callback fired on every row-selection change. |
|
|
140
|
+
| `updateRef` | `React.MutableRefObject` | Per-instance ref tracking row mutations. Allocate with `useRef([])` if you want to read pending edits. |
|
|
141
|
+
|
|
142
|
+
> **`reportRefs`, `nodeId`, `THIS_NODE`, builder-context props** — these only matter inside the UI Builder. Don't pass them.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 4. The `filter` object
|
|
147
|
+
|
|
148
|
+
Plain JS object passed each request. Three slots:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
{
|
|
152
|
+
// Filters always applied (the user cannot remove these via the UI).
|
|
153
|
+
fixedTFilter?: FilterItem[],
|
|
154
|
+
|
|
155
|
+
// Local-style filters; appear in the UI and can be removed by the user.
|
|
156
|
+
LocalTfilter?: FilterItem[],
|
|
157
|
+
|
|
158
|
+
// JS code string evaluated server-side at request time. Pushes additional
|
|
159
|
+
// FilterItems into a customFilter array. See 4.3.
|
|
160
|
+
customFilterCode?: string,
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 4.1 `FilterItem` shape
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
{
|
|
168
|
+
path: string, // dotted field path, e.g. "Customer.RegionId"
|
|
169
|
+
friendlyName?: string, // label for UI display
|
|
170
|
+
value: any, // string | number | boolean | array (for In/NotIn/Between)
|
|
171
|
+
method: FilterOp,
|
|
172
|
+
orGroupName?: string, // optional, groups multiple items into an OR-block
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 4.2 Operator values (`method`)
|
|
177
|
+
|
|
178
|
+
`Equal` · `NotEqual` · `Greater` · `GreaterOrEqual` · `Less` · `LessOrEqual` · `Contains` · `StartsWith` · `EndsWith` · `In` · `NotIn` · `IsNull` · `IsNotNull` · `Between` (value is `[from, to]`).
|
|
179
|
+
|
|
180
|
+
### 4.3 `customFilterCode` (advanced)
|
|
181
|
+
|
|
182
|
+
A code string evaluated by `SGrid` at request time. Scope:
|
|
183
|
+
|
|
184
|
+
| Var | Source |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `authContext` | The host's auth context (`{ user, accessToken, … }`) from `RoboByteFrontBuilderProvider`. |
|
|
187
|
+
| `context` | The host's system context. |
|
|
188
|
+
| `data` | An empty `{}` in standalone mode (this hook is mainly useful inside the builder where `data` is the page's reactive state). |
|
|
189
|
+
| `customFilter` | An `[]` to push items into. |
|
|
190
|
+
|
|
191
|
+
Most host-app callers don't need `customFilterCode` — just build the filter object in JS from your React state and pass it as `filter`. Use `customFilterCode` only when the same filter rules must follow the report through a deep link / templated session.
|
|
192
|
+
|
|
193
|
+
### 4.4 Building a filter from React state
|
|
194
|
+
|
|
195
|
+
```jsx
|
|
196
|
+
const [region, setRegion] = useState(null)
|
|
197
|
+
const [status, setStatus] = useState('Open')
|
|
198
|
+
|
|
199
|
+
const filter = useMemo(() => ({
|
|
200
|
+
Tfilter: [
|
|
201
|
+
{ path: 'Status', value: status, method: 'Equal' },
|
|
202
|
+
...(region ? [{ path: 'RegionId', value: region, method: 'Equal' }] : []),
|
|
203
|
+
],
|
|
204
|
+
}), [region, status])
|
|
205
|
+
|
|
206
|
+
return <ReportViewer pageId="ORDERS" filter={filter} height="60vh" />
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
> **Tip.** `useMemo` matters — passing a fresh object literal on every parent render causes the grid to refetch unnecessarily.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 5. Title / caption / toolbar layout
|
|
214
|
+
|
|
215
|
+
The toolbar is a single row:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
[Title / Caption] [Search] [Filter] [Refresh] [viewerActions…]
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
- Pass `title` and `caption` as plain strings.
|
|
222
|
+
- Search collapses to an icon button when empty; expands on focus / when filters are active. Arrow keys navigate the field-selector popover; Enter picks the highlighted field.
|
|
223
|
+
- **Display Settings** (auto-refresh interval, "Load all data", "Export to Excel") live in the side panel under the Templates tool icon — the user toggles them, not you.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 6. Row actions — custom `cellRenderer`
|
|
228
|
+
|
|
229
|
+
Inside the builder, `actionsConfig` is an array of `{ label, icon, code }`. Outside, you pass a complete `cellRenderer` function via `actions`. The function receives AG Grid's `params`; you write whatever React you want.
|
|
230
|
+
|
|
231
|
+
```jsx
|
|
232
|
+
function OrderActionsCell({ params, onEdit, onCancel }) {
|
|
233
|
+
return (
|
|
234
|
+
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
|
235
|
+
<IconButton size='small' onClick={() => onEdit(params.data)}>
|
|
236
|
+
<EditOutlined fontSize='small' />
|
|
237
|
+
</IconButton>
|
|
238
|
+
<IconButton size='small' color='error' onClick={() => onCancel(params.data.Id)}>
|
|
239
|
+
<DeleteOutlined fontSize='small' />
|
|
240
|
+
</IconButton>
|
|
241
|
+
</Box>
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export default function OrdersPage() {
|
|
246
|
+
const handleEdit = (row) => setEditing(row)
|
|
247
|
+
const handleCancel = (id) => cancelOrder(id).then(refresh)
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<ReportViewer
|
|
251
|
+
pageId="ORDERS"
|
|
252
|
+
actions={(params) => (
|
|
253
|
+
<OrderActionsCell params={params} onEdit={handleEdit} onCancel={handleCancel} />
|
|
254
|
+
)}
|
|
255
|
+
/>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## 7. Viewer actions (`viewerActions`)
|
|
263
|
+
|
|
264
|
+
Page-level buttons rendered in the toolbar after Filter / Refresh. Each item is a plain object with an `onClick` you write yourself. **No code-string Calculation runtime.**
|
|
265
|
+
|
|
266
|
+
### 7.1 `ViewerAction` shape
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
{
|
|
270
|
+
key?: string, // React key — auto-generated if omitted
|
|
271
|
+
label: string, // button text
|
|
272
|
+
icon?: string, // MUI icon name resolved against @mui/icons-material
|
|
273
|
+
// (e.g. "AddOutlined", "DownloadOutlined")
|
|
274
|
+
color?: 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info' | 'inherit',
|
|
275
|
+
variant?: 'text' | 'outlined' | 'contained', // default 'outlined'
|
|
276
|
+
disabled?: boolean,
|
|
277
|
+
onClick: () => void | Promise<void>,
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
> `confirmation` is recognized by the builder runtime; in standalone mode just handle the confirm yourself inside `onClick` (e.g. `if (!window.confirm('Sure?')) return`).
|
|
282
|
+
|
|
283
|
+
### 7.2 Example — "New" + "Export"
|
|
284
|
+
|
|
285
|
+
```jsx
|
|
286
|
+
import { ReportViewer } from 'robobyte-front-builder'
|
|
287
|
+
import { useState, useCallback } from 'react'
|
|
288
|
+
|
|
289
|
+
export default function CustomersPage() {
|
|
290
|
+
const [refreshTick, setRefreshTick] = useState(0)
|
|
291
|
+
const [createOpen, setCreateOpen] = useState(false)
|
|
292
|
+
|
|
293
|
+
const viewerActions = [
|
|
294
|
+
{
|
|
295
|
+
label: 'New Customer',
|
|
296
|
+
icon: 'AddOutlined',
|
|
297
|
+
variant: 'contained',
|
|
298
|
+
color: 'primary',
|
|
299
|
+
onClick: () => setCreateOpen(true),
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
label: 'Refresh Manually',
|
|
303
|
+
icon: 'RefreshOutlined',
|
|
304
|
+
onClick: () => setRefreshTick(Date.now()),
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
label: 'Export CSV',
|
|
308
|
+
icon: 'DownloadOutlined',
|
|
309
|
+
onClick: () => gridApi?.exportDataAsCsv({ fileName: 'customers.csv' }),
|
|
310
|
+
},
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
const [gridApi, setGridApi] = useState(null)
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<>
|
|
317
|
+
<ReportViewer
|
|
318
|
+
pageId="CUSTOMERS"
|
|
319
|
+
title="Customers"
|
|
320
|
+
caption="All accounts"
|
|
321
|
+
refresh={refreshTick}
|
|
322
|
+
viewerActions={viewerActions}
|
|
323
|
+
setOutGridApi={setGridApi}
|
|
324
|
+
/>
|
|
325
|
+
<CreateCustomerDialog open={createOpen} onClose={() => setCreateOpen(false)} />
|
|
326
|
+
</>
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## 8. Refresh patterns
|
|
334
|
+
|
|
335
|
+
### 8.1 Trigger on a button click
|
|
336
|
+
|
|
337
|
+
```jsx
|
|
338
|
+
const [tick, setTick] = useState(0)
|
|
339
|
+
|
|
340
|
+
<ReportViewer pageId="X" refresh={tick} />
|
|
341
|
+
<Button onClick={() => setTick(t => t + 1)}>Reload</Button>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### 8.2 Auto-refresh every minute
|
|
345
|
+
|
|
346
|
+
```jsx
|
|
347
|
+
<ReportViewer pageId="X" externalTimer={1} />
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 8.3 Refresh after a mutation
|
|
351
|
+
|
|
352
|
+
```jsx
|
|
353
|
+
async function saveAndRefresh() {
|
|
354
|
+
await PostService(Endpoints.X.Post.Save, true, payload)
|
|
355
|
+
setTick(t => t + 1)
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
> **Don't mix `refresh` and `externalTimer` for the same window** — both fire and you'll see double-fetches. Pick one source of truth.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## 9. Reading loaded data
|
|
364
|
+
|
|
365
|
+
Pass `setData` to get a callback every time the report loads new rows:
|
|
366
|
+
|
|
367
|
+
```jsx
|
|
368
|
+
const [rows, setRows] = useState([])
|
|
369
|
+
|
|
370
|
+
<ReportViewer
|
|
371
|
+
pageId="X"
|
|
372
|
+
setData={setRows}
|
|
373
|
+
/>
|
|
374
|
+
|
|
375
|
+
// Below the grid:
|
|
376
|
+
<Typography variant='caption'>{rows.length} rows loaded</Typography>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 9.1 Combining with `dataAsObject`
|
|
380
|
+
|
|
381
|
+
`dataAsObject: true` returns rows keyed by their server-side unique id:
|
|
382
|
+
|
|
383
|
+
```jsx
|
|
384
|
+
const [rowsById, setRowsById] = useState({})
|
|
385
|
+
<ReportViewer pageId="X" dataAsObject setData={setRowsById} />
|
|
386
|
+
|
|
387
|
+
// rowsById['ord_123'] ← O(1) lookup by id
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### 9.2 Single-record reports
|
|
391
|
+
|
|
392
|
+
`isSingle: true` returns only the first row. Useful for "current user", "summary", etc.
|
|
393
|
+
|
|
394
|
+
```jsx
|
|
395
|
+
const [me, setMe] = useState(null)
|
|
396
|
+
<ReportViewer pageId="ME" isSingle minimized setData={setMe} />
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 10. AG Grid API access
|
|
402
|
+
|
|
403
|
+
`setOutGridApi` gives you the live API. Common uses:
|
|
404
|
+
|
|
405
|
+
```jsx
|
|
406
|
+
const [api, setApi] = useState(null)
|
|
407
|
+
|
|
408
|
+
<ReportViewer pageId="X" setOutGridApi={setApi} />
|
|
409
|
+
|
|
410
|
+
// Selected rows:
|
|
411
|
+
const selected = api?.getSelectedRows() ?? []
|
|
412
|
+
|
|
413
|
+
// Refresh server-side without re-mounting:
|
|
414
|
+
api?.refreshServerSide({ purge: true })
|
|
415
|
+
|
|
416
|
+
// Export visible rows to CSV:
|
|
417
|
+
api?.exportDataAsCsv({ fileName: 'x.csv' })
|
|
418
|
+
|
|
419
|
+
// Apply a column state programmatically:
|
|
420
|
+
api?.applyColumnState({ state: [{ colId: 'CreatedAt', sort: 'desc' }] })
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 11. Templates
|
|
426
|
+
|
|
427
|
+
The Templates tool panel (side panel) is fully self-contained — the user opens it via the icon at the right edge of the grid, saves their column layout + filters + sort, and switches between templates. Nothing for the host to wire.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## 12. Common recipes
|
|
432
|
+
|
|
433
|
+
### 12.1 Report inside a Dialog
|
|
434
|
+
|
|
435
|
+
```jsx
|
|
436
|
+
import { Dialog, DialogTitle, DialogContent } from '@mui/material'
|
|
437
|
+
|
|
438
|
+
<Dialog open={open} onClose={onClose} maxWidth='xl' fullWidth>
|
|
439
|
+
<DialogTitle>Customer Orders</DialogTitle>
|
|
440
|
+
<DialogContent sx={{ p: 0 }}>
|
|
441
|
+
<ReportViewer
|
|
442
|
+
pageId="CUSTOMER_ORDERS"
|
|
443
|
+
filter={{ Tfilter: [{ path: 'CustomerId', value: customerId, method: 'Equal' }] }}
|
|
444
|
+
height='70vh'
|
|
445
|
+
noHeader
|
|
446
|
+
/>
|
|
447
|
+
</DialogContent>
|
|
448
|
+
</Dialog>
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### 12.2 Two reports, master/detail
|
|
452
|
+
|
|
453
|
+
```jsx
|
|
454
|
+
const [selectedCustomerId, setSelectedCustomerId] = useState(null)
|
|
455
|
+
|
|
456
|
+
<Grid container spacing={2}>
|
|
457
|
+
<Grid item xs={6}>
|
|
458
|
+
<ReportViewer
|
|
459
|
+
pageId="CUSTOMERS"
|
|
460
|
+
title="Customers"
|
|
461
|
+
actions={(params) => (
|
|
462
|
+
<IconButton size='small' onClick={() => setSelectedCustomerId(params.data.Id)}>
|
|
463
|
+
<OpenInNewOutlined fontSize='small' />
|
|
464
|
+
</IconButton>
|
|
465
|
+
)}
|
|
466
|
+
/>
|
|
467
|
+
</Grid>
|
|
468
|
+
<Grid item xs={6}>
|
|
469
|
+
<ReportViewer
|
|
470
|
+
pageId="ORDERS"
|
|
471
|
+
title='Orders'
|
|
472
|
+
caption={selectedCustomerId ? `Customer ${selectedCustomerId}` : 'Pick a customer'}
|
|
473
|
+
filter={selectedCustomerId
|
|
474
|
+
? { Tfilter: [{ path: 'CustomerId', value: selectedCustomerId, method: 'Equal' }] }
|
|
475
|
+
: { Tfilter: [{ path: 'Id', value: -1, method: 'Equal' }] }} // force empty
|
|
476
|
+
height='60vh'
|
|
477
|
+
/>
|
|
478
|
+
</Grid>
|
|
479
|
+
</Grid>
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### 12.3 Bound to URL query params (deep-link friendly)
|
|
483
|
+
|
|
484
|
+
```jsx
|
|
485
|
+
import { useRouter } from 'next/router'
|
|
486
|
+
|
|
487
|
+
const router = useRouter()
|
|
488
|
+
const region = router.query.region
|
|
489
|
+
|
|
490
|
+
const filter = useMemo(() => (
|
|
491
|
+
region
|
|
492
|
+
? { Tfilter: [{ path: 'Region', value: region, method: 'Equal' }] }
|
|
493
|
+
: {}
|
|
494
|
+
), [region])
|
|
495
|
+
|
|
496
|
+
return (
|
|
497
|
+
<ReportViewer
|
|
498
|
+
pageId="SALES"
|
|
499
|
+
title='Sales'
|
|
500
|
+
caption={region ? `Region: ${region}` : 'All regions'}
|
|
501
|
+
filter={filter}
|
|
502
|
+
sessionId={`sales-${region ?? 'all'}`} // separate filter state per region
|
|
503
|
+
/>
|
|
504
|
+
)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### 12.4 Custom toolbar action that triggers a refresh
|
|
508
|
+
|
|
509
|
+
```jsx
|
|
510
|
+
const [tick, setTick] = useState(0)
|
|
511
|
+
const refresh = useCallback(() => setTick(Date.now()), [])
|
|
512
|
+
|
|
513
|
+
const viewerActions = [
|
|
514
|
+
{
|
|
515
|
+
label: 'Sync Now',
|
|
516
|
+
icon: 'CloudDownloadOutlined',
|
|
517
|
+
variant: 'contained',
|
|
518
|
+
onClick: async () => {
|
|
519
|
+
try {
|
|
520
|
+
await PostService(Endpoints.Sync.Post.Trigger, true, {})
|
|
521
|
+
refresh()
|
|
522
|
+
} catch (e) {
|
|
523
|
+
console.error(e)
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
]
|
|
528
|
+
|
|
529
|
+
<ReportViewer pageId="LIVE_FEED" refresh={tick} viewerActions={viewerActions} />
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### 12.5 Embed with pre-filtered data and no chrome (tile / widget)
|
|
533
|
+
|
|
534
|
+
```jsx
|
|
535
|
+
<Card sx={{ p: 2 }}>
|
|
536
|
+
<Typography variant='h6'>Top 5 customers this week</Typography>
|
|
537
|
+
<ReportViewer
|
|
538
|
+
pageId="TOP_CUSTOMERS"
|
|
539
|
+
filter={{ Tfilter: [{ path: 'Range', value: 'week', method: 'Equal' }] }}
|
|
540
|
+
height='320px'
|
|
541
|
+
minimized
|
|
542
|
+
isSingle={false}
|
|
543
|
+
/>
|
|
544
|
+
</Card>
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
|
|
549
|
+
## 13. Pitfalls
|
|
550
|
+
|
|
551
|
+
- **Forgetting `<RoboByteFrontBuilderProvider>`** — `apiURL` / `accessToken` / AG Grid license come from there. Without it the report 404s on the API call and the grid throws a license banner. Wrap once at `_app.js` level.
|
|
552
|
+
- **`filter` object identity** — passing `filter={{ Tfilter: [...] }}` inline rebuilds the object every render and triggers refetches. Memoize with `useMemo`.
|
|
553
|
+
- **Mixing `refresh` with `externalTimer`** — both reload independently. Pick one strategy per report.
|
|
554
|
+
- **`viewerActions` icon string** — the value must match an export from `@mui/icons-material` exactly (e.g. `'AddOutlined'`, not `'add-outlined'`). Unknown icon names render the button without an icon, no error.
|
|
555
|
+
- **`actions` is a function, not an array** — in standalone mode `actions` is an AG Grid `cellRenderer` callback. If you tried passing a JSON array (`actionsConfig` style), nothing renders. Use the function form shown in section 6.
|
|
556
|
+
- **`globalParams` is index-keyed** — keys are 0-based positional indices into the report's `selectionParams`, not names. Wrong index → wrong override.
|
|
557
|
+
- **Setting `nodeId` / `reportRefs`** — these are builder-internal. Don't pass them; they'll either be ignored or interfere with the standalone grid.
|
|
558
|
+
- **Mounting two `<ReportViewer>` with the same `sessionId`** — they share saved filter state, which usually isn't what you want. Suffix with a discriminator (`sessionId={`orders-${tenantId}`}`).
|
|
559
|
+
- **Resetting `data` to refresh** — `<ReportViewer>` doesn't take a `data` prop in standalone mode. To force a reload, change `refresh` or `isRerender`. Re-rendering the parent with new props alone won't refetch.
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## 14. Cheat sheet
|
|
564
|
+
|
|
565
|
+
1. **Import**: `import { ReportViewer } from 'robobyte-front-builder'`.
|
|
566
|
+
2. **Minimum props**: `pageId` (or `id`) + `height`.
|
|
567
|
+
3. **Filter from React state**: build a plain `{ Tfilter: [...] }` object, memoize, pass via `filter`.
|
|
568
|
+
4. **Refresh on demand**: bump a state value passed as `refresh`. Auto-refresh: `externalTimer={minutes}`.
|
|
569
|
+
5. **Row actions**: pass `actions={(params) => <YourCell params={params} />}`.
|
|
570
|
+
6. **Page-level buttons**: `viewerActions={[{ label, icon, variant, onClick }, …]}`.
|
|
571
|
+
7. **Read loaded rows**: pass `setData`. Read the AG Grid API: pass `setOutGridApi`.
|
|
572
|
+
8. **Wrap your app once in `<RoboByteFrontBuilderProvider>`** at `_app.js` level — that's where the API URL, token, and AG Grid license come from.
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Cross-references
|
|
577
|
+
|
|
578
|
+
- **Schema-level / AI-builder usage** of the same component: [`training/35-reportViewer.md`](../training/35-reportViewer.md).
|
|
579
|
+
- **Package installation, peer deps, `next.config.js`**: [`INTEGRATION.md`](../INTEGRATION.md).
|
|
580
|
+
- **All `RoboByteFrontBuilderProvider` props**: [`README.md`](../README.md).
|
|
581
|
+
- **AG Grid theme customization** (the `agGridTheme` prop and `useAgGridTheme()` hook): README → *AG Grid theme*.
|