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,379 @@
|
|
|
1
|
+
# `fetchReportDataByPageId` — Standalone Reference (Host-App Usage)
|
|
2
|
+
|
|
3
|
+
> **Scope.** This document is for calling `fetchReportDataByPageId` directly from host-app code — pages, hooks, action handlers, server-side logic, anywhere outside the UI Builder.
|
|
4
|
+
>
|
|
5
|
+
> If you're emitting builder Calculation code, use [`training/48-fetchReportData.md`](../training/48-fetchReportData.md) instead — that variant explains the auto-injected `fetchReportData` symbol, the Calculation scope, and the conventions of in-builder action code.
|
|
6
|
+
|
|
7
|
+
A pure data fetcher by report `pageId`. No AG Grid. No React. Resolves the report's builder model (cached for the session), applies your filter on top of the report's own, optionally evaluates `customFilterCode`, calls `GenericGet`, and returns a normalized result.
|
|
8
|
+
|
|
9
|
+
Use it whenever a host page needs **rows from a server-defined report** but you're not embedding a `<ReportViewer>` — e.g. seeding a form, computing a KPI counter, exporting to PDF, prefetching lookup tables, building a custom chart on top of report data.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Quick start
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { fetchReportDataByPageId } from 'robobyte-front-builder'
|
|
17
|
+
|
|
18
|
+
const result = await fetchReportDataByPageId({ pageId: 'OPEN_INVOICES' })
|
|
19
|
+
if (result.success) {
|
|
20
|
+
console.log(`Got ${result.rows.length} of ${result.total} rows`)
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
That's the minimum. `pageId` is the only required parameter.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Prerequisites
|
|
29
|
+
|
|
30
|
+
### 2.1 Import paths — recommended vs legacy
|
|
31
|
+
|
|
32
|
+
**Recommended** (since v1.0.23):
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { fetchReportDataByPageId } from 'robobyte-front-builder'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Legacy bare-alias path** (still supported):
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import fetchReportDataByPageId from 'services/reportData/fetchReportData'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The bare-alias path only works because the host's `next.config.js` includes a `NormalModuleReplacementPlugin` rule (`packageOwnedServices`) that routes `services/reportData/fetchReportData` to the package's source. Both paths resolve to **the same module instance**, so the in-memory `builderModelCache` is shared regardless of which one you use. Pick the package import for new code — it doesn't depend on the special webpack rule and is easier to read.
|
|
45
|
+
|
|
46
|
+
### 2.2 The provider supplies API URL + auth
|
|
47
|
+
|
|
48
|
+
`fetchReportDataByPageId` calls `Services.PostService` internally, which reads the API base URL and `accessToken` from `<RoboByteFrontBuilderProvider>` (set up at `_app.js` level). Without the provider, requests go nowhere useful and the call returns `{ success: false }`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 3. Signature
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { fetchReportDataByPageId } from 'robobyte-front-builder'
|
|
56
|
+
|
|
57
|
+
await fetchReportDataByPageId({
|
|
58
|
+
pageId, // string | number — REQUIRED
|
|
59
|
+
filter = {}, // see section 5
|
|
60
|
+
isDataOnly = false, // true → return rows array directly, no wrapper
|
|
61
|
+
dataAsObject = false, // true → expand underscore-paths into nested objects
|
|
62
|
+
isPagination = true, // false → fetch all matching rows
|
|
63
|
+
isSingle = false, // true → return first row only (row, not array)
|
|
64
|
+
globalParams = null, // { 0: value, 1: value, ... } overrides selectionParams by index
|
|
65
|
+
page = 1, // 1-based page number
|
|
66
|
+
pageSize = 50, // rows per page
|
|
67
|
+
authContext = null, // see section 5.3
|
|
68
|
+
context = null, // see section 5.3
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Returns a `Promise`. **Always `await`** — see pitfalls.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 4. Return shape
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// Default — full wrapper
|
|
80
|
+
{
|
|
81
|
+
success: true,
|
|
82
|
+
rows: RowType[] | RowType, // array OR single row (if isSingle)
|
|
83
|
+
total: number, // total rows on server (paginated mode)
|
|
84
|
+
rawResponse: any, // exact backend response
|
|
85
|
+
finalRequest: { // useful for debugging — what was actually sent
|
|
86
|
+
endpoint: string,
|
|
87
|
+
getType: 'Pagination' | 'NoPagination' | 'SqlPagination' | 'SqlNoPagination',
|
|
88
|
+
filter: object,
|
|
89
|
+
sourceModel: string,
|
|
90
|
+
params: { page, pageSize },
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// On error
|
|
95
|
+
{ success: false, error: 'Report detailsOld not found' /* or Error object */ }
|
|
96
|
+
|
|
97
|
+
// With isDataOnly: true
|
|
98
|
+
RowType[] | RowType // bare rows; no wrapper at all
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Variants:
|
|
102
|
+
|
|
103
|
+
| Options | Returns |
|
|
104
|
+
|---|---|
|
|
105
|
+
| Defaults | `{ success, rows: Row[], total, ... }` |
|
|
106
|
+
| `isSingle: true` | `{ success, rows: Row, total, ... }` — `rows` is a single object |
|
|
107
|
+
| `isDataOnly: true` | `Row[]` (or `Row` if `isSingle`) — no wrapper |
|
|
108
|
+
| `isPagination: false` | All matching rows fetched in one request |
|
|
109
|
+
| `dataAsObject: true` | `Foo_Bar` flat keys expanded into `Foo.Bar` nested objects |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 5. The `filter` parameter
|
|
114
|
+
|
|
115
|
+
Plain JS object. Three special slots; anything else at the top level is forwarded to the backend as-is.
|
|
116
|
+
|
|
117
|
+
| Slot | Type | Purpose |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `Tfilter` | `FilterItem[]` | Filters merged with the report's own. |
|
|
120
|
+
| `TFilter` | `FilterItem[]` | Alias accepted for legacy callers. |
|
|
121
|
+
| `customFilterCode` | `string` | JS source evaluated at fetch time. |
|
|
122
|
+
|
|
123
|
+
### 5.1 `FilterItem` shape
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
{
|
|
127
|
+
path: string, // dotted field path, e.g. "Customer.RegionId"
|
|
128
|
+
friendlyName?: string,
|
|
129
|
+
value: any, // scalar | array (for In/NotIn/Between)
|
|
130
|
+
method: 'Equal' | 'NotEqual' | 'Greater' | 'GreaterOrEqual' |
|
|
131
|
+
'Less' | 'LessOrEqual' | 'Contains' | 'StartsWith' |
|
|
132
|
+
'EndsWith' | 'In' | 'NotIn' | 'IsNull' | 'IsNotNull' | 'Between',
|
|
133
|
+
orGroupName?: string, // group multiple items into an OR-block
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 5.2 Null-value stripping
|
|
138
|
+
|
|
139
|
+
Items where `value === null || value === undefined || value === ''` are dropped before the request is sent. So you can build a filter object from optional state without conditional guards:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
const filter = {
|
|
143
|
+
Tfilter: [
|
|
144
|
+
{ path: 'Status', value: status, method: 'Equal' }, // dropped if status is null
|
|
145
|
+
{ path: 'OwnerId', value: ownerId, method: 'Equal' }, // dropped if ownerId is null
|
|
146
|
+
{ path: 'Year', value: 2026, method: 'Equal' }, // kept
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 5.3 `customFilterCode` (advanced)
|
|
152
|
+
|
|
153
|
+
A code string evaluated server-side at request time. Useful when the filter rule depends on the auth context and you want the rule to follow the report regardless of the caller. Available scope:
|
|
154
|
+
|
|
155
|
+
| Var | Source |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `authContext` | The `authContext` arg you pass to `fetchReportDataByPageId`. |
|
|
158
|
+
| `context` | The `context` arg you pass. |
|
|
159
|
+
| `filters` | The merged `Tfilter` array. Read-only. |
|
|
160
|
+
| `customFilter` | Empty `[]` to push items into. |
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const result = await fetchReportDataByPageId({
|
|
164
|
+
pageId: 'MY_ORDERS',
|
|
165
|
+
filter: {
|
|
166
|
+
customFilterCode: `
|
|
167
|
+
if (authContext?.user?.shopId) {
|
|
168
|
+
customFilter.push({ path: 'ShopId', value: authContext.user.shopId, method: 'Equal' })
|
|
169
|
+
}
|
|
170
|
+
`,
|
|
171
|
+
},
|
|
172
|
+
authContext: { user }, // pass it explicitly
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
> **In host-app code, you usually don't need `customFilterCode`.** Just build the filter object in plain JS from your React state and pass it. `customFilterCode` is for cases where the filter rule should travel with the report (e.g. server-side report definitions that include security constraints).
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 6. Recipes
|
|
181
|
+
|
|
182
|
+
### 6.1 Fetch once on mount
|
|
183
|
+
|
|
184
|
+
```jsx
|
|
185
|
+
import { useEffect, useState } from 'react'
|
|
186
|
+
import { fetchReportDataByPageId } from 'robobyte-front-builder'
|
|
187
|
+
|
|
188
|
+
export function useTickets() {
|
|
189
|
+
const [data, setData] = useState(null)
|
|
190
|
+
const [loading, setLoading] = useState(true)
|
|
191
|
+
const [error, setError] = useState(null)
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
let cancelled = false
|
|
195
|
+
setLoading(true)
|
|
196
|
+
fetchReportDataByPageId({ pageId: 'OPEN_TICKETS', isPagination: false })
|
|
197
|
+
.then(r => {
|
|
198
|
+
if (cancelled) return
|
|
199
|
+
if (r.success) setData(r.rows)
|
|
200
|
+
else setError(r.error)
|
|
201
|
+
})
|
|
202
|
+
.finally(() => { if (!cancelled) setLoading(false) })
|
|
203
|
+
|
|
204
|
+
return () => { cancelled = true }
|
|
205
|
+
}, [])
|
|
206
|
+
|
|
207
|
+
return { data, loading, error }
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 6.2 KPI counter (just the total, no rows)
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
async function loadOpenInvoiceCount() {
|
|
215
|
+
const r = await fetchReportDataByPageId({
|
|
216
|
+
pageId: 'OPEN_INVOICES',
|
|
217
|
+
isPagination: true,
|
|
218
|
+
pageSize: 1, // we only need `total`, not the rows
|
|
219
|
+
})
|
|
220
|
+
return r.success ? r.total : 0
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 6.3 Filter built from React state, memoized
|
|
225
|
+
|
|
226
|
+
```jsx
|
|
227
|
+
import { useMemo, useEffect, useState } from 'react'
|
|
228
|
+
|
|
229
|
+
function FilteredReportData({ regionId, status }) {
|
|
230
|
+
const [rows, setRows] = useState([])
|
|
231
|
+
|
|
232
|
+
const filter = useMemo(() => ({
|
|
233
|
+
Tfilter: [
|
|
234
|
+
{ path: 'Status', value: status, method: 'Equal' },
|
|
235
|
+
{ path: 'RegionId', value: regionId, method: 'Equal' },
|
|
236
|
+
],
|
|
237
|
+
}), [regionId, status])
|
|
238
|
+
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
let cancelled = false
|
|
241
|
+
fetchReportDataByPageId({ pageId: 'ORDERS', filter, isPagination: false })
|
|
242
|
+
.then(r => { if (!cancelled && r.success) setRows(r.rows) })
|
|
243
|
+
return () => { cancelled = true }
|
|
244
|
+
}, [filter])
|
|
245
|
+
|
|
246
|
+
return <Table rows={rows} />
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
> `useMemo` matters — a fresh `filter` object on every parent render would refetch every render.
|
|
251
|
+
|
|
252
|
+
### 6.4 Single record by id (for an Edit dialog)
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
async function loadCustomer(id) {
|
|
256
|
+
const r = await fetchReportDataByPageId({
|
|
257
|
+
pageId: 'CUSTOMER_BY_ID',
|
|
258
|
+
isSingle: true,
|
|
259
|
+
filter: { Tfilter: [{ path: 'Id', value: id, method: 'Equal' }] },
|
|
260
|
+
})
|
|
261
|
+
if (!r.success || !r.rows) throw new Error('not found')
|
|
262
|
+
return r.rows // single object, not an array
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 6.5 Bulk export to CSV / Excel
|
|
267
|
+
|
|
268
|
+
```js
|
|
269
|
+
import * as XLSX from 'xlsx'
|
|
270
|
+
|
|
271
|
+
async function exportAll() {
|
|
272
|
+
const r = await fetchReportDataByPageId({
|
|
273
|
+
pageId: 'ORDERS',
|
|
274
|
+
isPagination: false, // fetch every row
|
|
275
|
+
dataAsObject: true, // expand nested keys for readable headers
|
|
276
|
+
isDataOnly: true, // skip the wrapper
|
|
277
|
+
})
|
|
278
|
+
const ws = XLSX.utils.json_to_sheet(r)
|
|
279
|
+
const wb = XLSX.utils.book_new()
|
|
280
|
+
XLSX.utils.book_append_sheet(wb, ws, 'Orders')
|
|
281
|
+
XLSX.writeFile(wb, 'orders.xlsx')
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 6.6 Pagination cursor in a hook
|
|
286
|
+
|
|
287
|
+
```jsx
|
|
288
|
+
function usePaginatedReport(pageId, filter) {
|
|
289
|
+
const [page, setPage] = useState(1)
|
|
290
|
+
const [rows, setRows] = useState([])
|
|
291
|
+
const [total, setTotal] = useState(0)
|
|
292
|
+
const [loading, setLoading] = useState(false)
|
|
293
|
+
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
let cancelled = false
|
|
296
|
+
setLoading(true)
|
|
297
|
+
fetchReportDataByPageId({ pageId, filter, page, pageSize: 50 })
|
|
298
|
+
.then(r => {
|
|
299
|
+
if (cancelled) return
|
|
300
|
+
if (r.success) { setRows(r.rows); setTotal(r.total) }
|
|
301
|
+
})
|
|
302
|
+
.finally(() => { if (!cancelled) setLoading(false) })
|
|
303
|
+
return () => { cancelled = true }
|
|
304
|
+
}, [pageId, filter, page])
|
|
305
|
+
|
|
306
|
+
return { rows, total, loading, page, setPage }
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 6.7 Server-side selection-param override
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
const r = await fetchReportDataByPageId({
|
|
314
|
+
pageId: 'SALES_BY_REGION',
|
|
315
|
+
globalParams: { 0: year, 1: regionId }, // 0-based positional, NOT keyed by name
|
|
316
|
+
})
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 6.8 Forwarding auth context for `customFilterCode`
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
import { useAuth } from 'your-auth-lib'
|
|
323
|
+
|
|
324
|
+
function Component() {
|
|
325
|
+
const auth = useAuth()
|
|
326
|
+
// ...
|
|
327
|
+
const r = await fetchReportDataByPageId({
|
|
328
|
+
pageId: 'MY_SHOP_REPORT',
|
|
329
|
+
filter: {
|
|
330
|
+
customFilterCode: `
|
|
331
|
+
if (authContext?.user?.shopId) {
|
|
332
|
+
customFilter.push({ path: 'ShopId', value: authContext.user.shopId, method: 'Equal' })
|
|
333
|
+
}
|
|
334
|
+
`,
|
|
335
|
+
},
|
|
336
|
+
authContext: { user: auth.user },
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 7. Pitfalls
|
|
344
|
+
|
|
345
|
+
- **Forgetting `await`.** Returns a `Promise`. Calling `r.success` on the unresolved Promise is always `undefined`.
|
|
346
|
+
- **Importing from the wrong path.** Use `import { fetchReportDataByPageId } from 'robobyte-front-builder'`. The legacy bare-alias `'services/reportData/fetchReportData'` also works (the `NormalModuleReplacementPlugin` routes it to the same file), but anything else — relative paths, deep-import paths into `node_modules` — bypasses the dedupe rules and risks loading a second module instance with its own empty `builderModelCache`.
|
|
347
|
+
- **`isSingle: true` returns the row, not an array.** `r.rows[0]` will crash; use `r.rows` directly.
|
|
348
|
+
- **`isPagination: false` on a huge report.** All rows in one request. Use it for lookups (≤ a few thousand rows). For unbounded datasets, paginate.
|
|
349
|
+
- **Filter `null`/`''`/`undefined` items are dropped.** This is usually convenient (you can pass partial state with no conditional guards). But if you literally want to filter for null, use `method: 'IsNull'` — don't pass `value: null`.
|
|
350
|
+
- **`globalParams` is index-keyed.** Keys are 0-based positional indices into `selectionParams`, not names.
|
|
351
|
+
- **The builder-model cache lives for the page session.** First call to a given `pageId` hits the metadata endpoint; subsequent calls reuse it. If you change the report definition mid-session, the cache stays stale until full reload. Hard-refresh the tab if you need fresh metadata.
|
|
352
|
+
- **`filter` object identity in React.** When passing `filter` to a `useEffect`, memoize it with `useMemo`. A fresh literal each render triggers a refetch each render.
|
|
353
|
+
- **`customFilterCode` cannot reference React state.** It's a code string evaluated server-side with `authContext`, `context`, `filters`, `customFilter` in scope — nothing else. If you need state-based filtering, build the filter array in JS before the call.
|
|
354
|
+
- **Missing provider.** Without `<RoboByteFrontBuilderProvider>` wrapping your app, `Services.PostService` has no base URL or auth token; the request goes to the wrong host or 401s.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## 8. Cheat sheet
|
|
359
|
+
|
|
360
|
+
1. `import { fetchReportDataByPageId } from 'robobyte-front-builder'` (recommended). The legacy bare-alias `'services/reportData/fetchReportData'` still works and resolves to the same module.
|
|
361
|
+
2. `await fetchReportDataByPageId({ pageId: '...' })` — that's the minimum.
|
|
362
|
+
3. Build the `filter` object in plain JS — `{ Tfilter: [{ path, value, method }] }`. Null values are auto-stripped.
|
|
363
|
+
4. Memoize the `filter` with `useMemo` when passing into `useEffect`.
|
|
364
|
+
5. `isDataOnly: true` returns the bare rows — skip the `r.success` ceremony when you trust the call.
|
|
365
|
+
6. `isSingle: true` returns one row. `r.rows` is that row.
|
|
366
|
+
7. `isPagination: false` for full datasets; combine with `isDataOnly` for clean export logic.
|
|
367
|
+
8. `dataAsObject: true` to expand flat `Foo_Bar` keys into nested `Foo.Bar` objects.
|
|
368
|
+
9. `globalParams: { 0: ..., 1: ... }` overrides selection params by index.
|
|
369
|
+
10. Wrap the call with cancellation in `useEffect` (`let cancelled = false; … return () => { cancelled = true }`).
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Cross-references
|
|
374
|
+
|
|
375
|
+
- **In-builder Calculation usage of the same function** (auto-injected as `fetchReportData`): [`training/48-fetchReportData.md`](../training/48-fetchReportData.md).
|
|
376
|
+
- **`<ReportViewer>` host-app component** (when you want the data **rendered** rather than fetched): [`docs/ReportViewer.md`](./ReportViewer.md).
|
|
377
|
+
- **Package installation, peer deps, `next.config.js`**: [`INTEGRATION.md`](../INTEGRATION.md).
|
|
378
|
+
- **All `RoboByteFrontBuilderProvider` props**: [`README.md`](../README.md).
|
|
379
|
+
- **AG Grid theme customization** (only matters if the fetched rows end up rendered in a grid downstream): README → *AG Grid theme*.
|