robobyte-front-builder 1.0.26 → 1.0.28
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 +6 -0
- package/LICENSE +65 -0
- package/README.md +166 -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/index.js +14 -0
- package/src/lib/muiTheme.js +655 -0
- package/src/lib/providers/RoboByteFrontBuilderProvider.jsx +45 -1
- package/src/pages/_app.js +1 -0
- package/src/pages/printBuilder/index.jsx +26 -19
- package/src/pages/viewBuilder/index.jsx +29 -19
- 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,125 @@
|
|
|
1
|
+
# Component: kpi-rating
|
|
2
|
+
|
|
3
|
+
An interactive or read-only star/heart/circle rating component. Writes the selected value back to `data[valueKey]`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Props — Main Tab
|
|
8
|
+
|
|
9
|
+
### Data
|
|
10
|
+
| Prop | Type | Options / Notes |
|
|
11
|
+
|------|------|-----------------|
|
|
12
|
+
| `valueKey` | expression | Data key for the current rating value. Clicking a star writes the new rating to `data[valueKey]`. |
|
|
13
|
+
|
|
14
|
+
### Appearance
|
|
15
|
+
| Prop | Type | Options / Notes |
|
|
16
|
+
|------|------|-----------------|
|
|
17
|
+
| `max` | expression | Maximum rating (number of icons shown). E.g. `5`, `10`. |
|
|
18
|
+
| `icon` | select | `star` · `heart` · `circle` · `square` — Icon shape. |
|
|
19
|
+
| `color` | expression | Color of filled/active icons, e.g. `"#f5a623"`, `"primary.main"`. |
|
|
20
|
+
| `emptyColor` | expression | Color of unfilled icons, e.g. `"#e0e0e0"`. |
|
|
21
|
+
| `size` | expression | Icon size in px, e.g. `"28"`. |
|
|
22
|
+
| `gap` | expression | Gap between icons in px, e.g. `"4"`. |
|
|
23
|
+
| `align` | select | `left` · `center` · `right` |
|
|
24
|
+
| `readonly` | boolean | `true` prevents user interaction — display only. |
|
|
25
|
+
|
|
26
|
+
### Actions
|
|
27
|
+
| Prop | Type | Notes |
|
|
28
|
+
|------|------|-------|
|
|
29
|
+
| `onChange` | kpi-action | Fires when the user selects a rating. Available: `newValue` (new rating number), `clickedValue` (alias). The new value is automatically written to `data[valueKey]` before the action runs. |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Props — Style Tab
|
|
34
|
+
|
|
35
|
+
No dedicated style tab.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Use Cases
|
|
40
|
+
|
|
41
|
+
**When to use:**
|
|
42
|
+
- Product reviews: user rates a product 1–5 stars.
|
|
43
|
+
- Feedback forms: rate your experience.
|
|
44
|
+
- Priority indicators with visual icons.
|
|
45
|
+
- Read-only display of an average rating score.
|
|
46
|
+
|
|
47
|
+
**When NOT to use:**
|
|
48
|
+
- Sliders with continuous values → use a number input with slider.
|
|
49
|
+
- Pure numeric display → use `kpi-metric`.
|
|
50
|
+
- Status with fixed categories → use `kpi-badge`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Schema Examples
|
|
55
|
+
|
|
56
|
+
### Interactive 5-star rating
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"type": "kpi-rating",
|
|
60
|
+
"props": {
|
|
61
|
+
"valueKey": "data.userRating",
|
|
62
|
+
"max": 5,
|
|
63
|
+
"icon": "star",
|
|
64
|
+
"color": "#f5a623",
|
|
65
|
+
"emptyColor": "#e0e0e0",
|
|
66
|
+
"size": "32",
|
|
67
|
+
"gap": "4",
|
|
68
|
+
"align": "center",
|
|
69
|
+
"readonly": false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Read-only product rating display
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"type": "kpi-rating",
|
|
78
|
+
"props": {
|
|
79
|
+
"valueKey": "data.product.avgRating",
|
|
80
|
+
"max": 5,
|
|
81
|
+
"icon": "star",
|
|
82
|
+
"color": "#fbc02d",
|
|
83
|
+
"emptyColor": "#e0e0e0",
|
|
84
|
+
"size": "20",
|
|
85
|
+
"readonly": true,
|
|
86
|
+
"align": "left"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Heart rating with action
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"type": "kpi-rating",
|
|
95
|
+
"props": {
|
|
96
|
+
"valueKey": "data.satisfaction",
|
|
97
|
+
"max": 5,
|
|
98
|
+
"icon": "heart",
|
|
99
|
+
"color": "#e91e63",
|
|
100
|
+
"size": "28",
|
|
101
|
+
"align": "center",
|
|
102
|
+
"onChange": {
|
|
103
|
+
"type": "custom",
|
|
104
|
+
"code": "submitSatisfactionScore({ score: newValue, sessionId: data.sessionId })"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 10-point circle rating
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"type": "kpi-rating",
|
|
114
|
+
"props": {
|
|
115
|
+
"valueKey": "data.npsScore",
|
|
116
|
+
"max": 10,
|
|
117
|
+
"icon": "circle",
|
|
118
|
+
"color": "primary.main",
|
|
119
|
+
"emptyColor": "#e0e0e0",
|
|
120
|
+
"size": "20",
|
|
121
|
+
"gap": "2",
|
|
122
|
+
"align": "left"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Component: kpi-countdown
|
|
2
|
+
|
|
3
|
+
A live countdown timer displaying days, hours, minutes, and seconds remaining until a target date/time. Fires an `onDone` action when it reaches zero.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Props — Main Tab
|
|
8
|
+
|
|
9
|
+
### Data
|
|
10
|
+
| Prop | Type | Options / Notes |
|
|
11
|
+
|------|------|-----------------|
|
|
12
|
+
| `targetDateKey` | expression | Data key or expression returning the target date. Accepts: ISO datetime string, ISO date string, epoch ms (number), epoch seconds (number < 1e11), or JS Date object. E.g. `"dealDeadline"` → `data.dealDeadline`. |
|
|
13
|
+
| `targetDate` | expression | Static target date when not using `targetDateKey`. E.g. `"2025-12-31T23:59:59"`. |
|
|
14
|
+
| `doneLabel` | expression | Text shown when the countdown reaches zero, e.g. `"Expired"`, `"Time's up!"`. |
|
|
15
|
+
| `label` | expression | Optional heading shown above the countdown digits. |
|
|
16
|
+
|
|
17
|
+
### Segments
|
|
18
|
+
| Prop | Type | Options / Notes |
|
|
19
|
+
|------|------|-----------------|
|
|
20
|
+
| `showDays` | boolean | Show days counter. |
|
|
21
|
+
| `showHours` | boolean | Show hours counter. |
|
|
22
|
+
| `showMinutes` | boolean | Show minutes counter. |
|
|
23
|
+
| `showSeconds` | boolean | Show seconds counter. |
|
|
24
|
+
|
|
25
|
+
### Unit Label Overrides
|
|
26
|
+
| Prop | Type | Options / Notes |
|
|
27
|
+
|------|------|-----------------|
|
|
28
|
+
| `labelDays` | expression | Override "days" label, e.g. `"d"`. |
|
|
29
|
+
| `labelHours` | expression | Override "hrs" label, e.g. `"h"`. |
|
|
30
|
+
| `labelMinutes` | expression | Override "min" label. |
|
|
31
|
+
| `labelSeconds` | expression | Override "sec" label. |
|
|
32
|
+
|
|
33
|
+
### Layout
|
|
34
|
+
| Prop | Type | Options / Notes |
|
|
35
|
+
|------|------|-----------------|
|
|
36
|
+
| `size` | select | `small` · `medium` · `large` — Controls digit font scale. |
|
|
37
|
+
| `align` | select | `left` · `center` · `right` |
|
|
38
|
+
| `separator` | expression | Character between digit groups, e.g. `":"`. Default: `":"`. |
|
|
39
|
+
|
|
40
|
+
### Colors
|
|
41
|
+
| Prop | Type | Options / Notes |
|
|
42
|
+
|------|------|-----------------|
|
|
43
|
+
| `color` | expression | Digit color. Overridden by thresholds. E.g. `"#1976d2"`. |
|
|
44
|
+
| `unitColor` | expression | Color of the unit labels (days/hrs/min/sec). Default: `text.disabled`. |
|
|
45
|
+
| `separatorColor` | expression | Color of the separator character. Default: `text.disabled`. |
|
|
46
|
+
| `doneLabelColor` | expression | Color of the done label. Default: `success.main`. |
|
|
47
|
+
|
|
48
|
+
### Digit Style
|
|
49
|
+
| Prop | Type | Options / Notes |
|
|
50
|
+
|------|------|-----------------|
|
|
51
|
+
| `numberFontWeight` | select | `300` · `400` · `500` · `600` · `700` · `800` · `900` |
|
|
52
|
+
|
|
53
|
+
### Label Style
|
|
54
|
+
| Prop | Type | Options / Notes |
|
|
55
|
+
|------|------|-----------------|
|
|
56
|
+
| `labelFontSize` | expression | Heading label font size. |
|
|
57
|
+
| `labelFontWeight` | select | `300` · `400` · `500` · `600` · `700` · `800` |
|
|
58
|
+
| `labelColor` | expression | Heading label color. Default: `text.secondary`. |
|
|
59
|
+
|
|
60
|
+
### Thresholds
|
|
61
|
+
| Prop | Type | Notes |
|
|
62
|
+
|------|------|-------|
|
|
63
|
+
| `thresholds` | thresholds-editor | Applied on remaining seconds. E.g. `< 3600` → `error.main` (last hour turns red). |
|
|
64
|
+
|
|
65
|
+
### On Done
|
|
66
|
+
| Prop | Type | Notes |
|
|
67
|
+
|------|------|-------|
|
|
68
|
+
| `onDoneCode` | code | Code run when countdown reaches zero. |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Use Cases
|
|
73
|
+
|
|
74
|
+
**When to use:**
|
|
75
|
+
- Deal/offer expiry countdowns (e.g. flash sale).
|
|
76
|
+
- Appointment/meeting countdown.
|
|
77
|
+
- Deadline trackers for tasks or bids.
|
|
78
|
+
- Event date countdowns (conference, launch).
|
|
79
|
+
|
|
80
|
+
**When NOT to use:**
|
|
81
|
+
- Elapsed time (uptime) → display a label with calculated duration.
|
|
82
|
+
- Non-time-based progress → use `kpi-gauge` or `progress-line`.
|
|
83
|
+
- Static date display → use `label`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Schema Examples
|
|
88
|
+
|
|
89
|
+
### Deal expiry countdown
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"type": "kpi-countdown",
|
|
93
|
+
"props": {
|
|
94
|
+
"targetDateKey": "data.offerExpiry",
|
|
95
|
+
"label": "Offer Expires In",
|
|
96
|
+
"doneLabel": "Offer Expired",
|
|
97
|
+
"showDays": true,
|
|
98
|
+
"showHours": true,
|
|
99
|
+
"showMinutes": true,
|
|
100
|
+
"showSeconds": true,
|
|
101
|
+
"size": "large",
|
|
102
|
+
"align": "center",
|
|
103
|
+
"numberFontWeight": "700",
|
|
104
|
+
"color": "#1976d2",
|
|
105
|
+
"thresholds": [
|
|
106
|
+
{ "operator": "<", "value": 3600, "color": "error.main" },
|
|
107
|
+
{ "operator": "<", "value": 86400, "color": "warning.main" }
|
|
108
|
+
],
|
|
109
|
+
"onDoneCode": "setData({ offerExpired: true }); showBanner('offerExpiredBanner')"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Days-only event countdown
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"type": "kpi-countdown",
|
|
118
|
+
"props": {
|
|
119
|
+
"targetDate": "2025-12-31",
|
|
120
|
+
"label": "Days Until Year End",
|
|
121
|
+
"showDays": true,
|
|
122
|
+
"showHours": false,
|
|
123
|
+
"showMinutes": false,
|
|
124
|
+
"showSeconds": false,
|
|
125
|
+
"size": "medium",
|
|
126
|
+
"align": "center",
|
|
127
|
+
"color": "#1976d2"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Compact with custom separator
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"type": "kpi-countdown",
|
|
136
|
+
"props": {
|
|
137
|
+
"targetDateKey": "data.meetingTime",
|
|
138
|
+
"showDays": false,
|
|
139
|
+
"showHours": true,
|
|
140
|
+
"showMinutes": true,
|
|
141
|
+
"showSeconds": true,
|
|
142
|
+
"separator": ":",
|
|
143
|
+
"size": "small",
|
|
144
|
+
"align": "left",
|
|
145
|
+
"labelDays": "d",
|
|
146
|
+
"labelHours": "h",
|
|
147
|
+
"labelMinutes": "m",
|
|
148
|
+
"labelSeconds": "s"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Helper: `fetchReportData` — Calculation-scope Reference Manual
|
|
2
|
+
|
|
3
|
+
> **Audience.** AI sessions emitting builder Calculation code (`actionsConfig.code`, `viewerActions.code`, `onClick`, `onLoad`, column functions, etc.). `fetchReportData` is **auto-injected** into every Calculation scope — you never `import` it in a builder schema.
|
|
4
|
+
|
|
5
|
+
Fetches report data by `pageId` **without any AG Grid dependency**. Resolves the report's builder model on first call (cached for the session), applies the supplied filter on top of the report's own filter, optionally evaluates `customFilterCode`, then issues `GenericGet` and returns a normalized result.
|
|
6
|
+
|
|
7
|
+
Use this whenever a Calculation needs **rows from a server-defined report** but the rows aren't rendered by a `reportViewer` on the page (e.g. dialog seed data, exports, conditional logic, dashboard counters, hidden lookups).
|
|
8
|
+
|
|
9
|
+
> ⚠️ Different from `<reportViewer>`. `<reportViewer>` renders + paginates + filters + ag-grids the data. `fetchReportData` is a **pure data fetch** — you get an array back and decide what to do with it.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Quick start
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
const result = await fetchReportData({ pageId: 'OPEN_INVOICES' })
|
|
17
|
+
if (result.success) {
|
|
18
|
+
console.log(`Got ${result.rows.length} of ${result.total} rows`)
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Minimum: `pageId`. Everything else has sane defaults.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. Signature
|
|
27
|
+
|
|
28
|
+
`fetchReportData` is the alias bound into Calculation scope. The function it points to is the package's `fetchReportDataByPageId` (one and the same).
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
await fetchReportData({
|
|
32
|
+
pageId, // string | number — REQUIRED
|
|
33
|
+
filter = {}, // see section 3
|
|
34
|
+
isDataOnly = false, // true → return rows array directly, no wrapper
|
|
35
|
+
dataAsObject = false, // true → expand underscore-paths into nested objects
|
|
36
|
+
isPagination = true, // false → fetch all matching rows
|
|
37
|
+
isSingle = false, // true → return only the first row (row, not array)
|
|
38
|
+
globalParams = null, // { 0: value, 1: value, ... } overrides selectionParams by index
|
|
39
|
+
page = 1, // 1-based page number
|
|
40
|
+
pageSize = 50, // rows per page
|
|
41
|
+
authContext = null, // host auth, used by customFilterCode
|
|
42
|
+
context = null, // host system context, used by customFilterCode
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> **`authContext` / `context`** — pass them along when calling from inside a Calculation if your filter uses `customFilterCode` that reads `authContext.user`. The builder runtime doesn't auto-thread them through (yet) — explicitly forward what you have. Most calls don't need either.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 3. The `filter` parameter
|
|
51
|
+
|
|
52
|
+
Plain JS object. Three slots:
|
|
53
|
+
|
|
54
|
+
| Slot | Type | Purpose |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `Tfilter` | `FilterItem[]` | Filters merged with the report's own filters. |
|
|
57
|
+
| `TFilter` | `FilterItem[]` | Alias accepted for legacy callers. Concatenated with `Tfilter`. |
|
|
58
|
+
| `customFilterCode` | `string` | JS source evaluated at fetch time; pushes filters into a local `customFilter` array. |
|
|
59
|
+
|
|
60
|
+
Anything **else** at the top level of `filter` is forwarded as-is to the backend request body. Useful for backend-specific knobs.
|
|
61
|
+
|
|
62
|
+
### `FilterItem` shape
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
{
|
|
66
|
+
path: string, // dotted field path, e.g. "Customer.RegionId"
|
|
67
|
+
friendlyName?: string,
|
|
68
|
+
value: any, // scalar | array (for In/NotIn/Between)
|
|
69
|
+
method: 'Equal' | 'NotEqual' | 'Greater' | 'GreaterOrEqual' |
|
|
70
|
+
'Less' | 'LessOrEqual' | 'Contains' | 'StartsWith' |
|
|
71
|
+
'EndsWith' | 'In' | 'NotIn' | 'IsNull' | 'IsNotNull' | 'Between',
|
|
72
|
+
orGroupName?: string, // group multiple items into an OR-block
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Null filtering
|
|
77
|
+
|
|
78
|
+
Items where `value === null || value === undefined || value === ''` are stripped **before** the request is sent. So you can build a filter from optional state without conditional guards:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
const filter = {
|
|
82
|
+
Tfilter: [
|
|
83
|
+
{ path: 'Status', value: data.status, method: 'Equal' }, // dropped if data.status is null
|
|
84
|
+
{ path: 'OwnerId', value: data.ownerId, method: 'Equal' }, // same
|
|
85
|
+
{ path: 'Year', value: 2026, method: 'Equal' }, // kept
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `customFilterCode`
|
|
91
|
+
|
|
92
|
+
A JS string evaluated in a sandbox at request time. Push `FilterItem`s into `customFilter`. Scope:
|
|
93
|
+
|
|
94
|
+
| Var | Source |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `authContext` | The `authContext` arg passed to `fetchReportData`. |
|
|
97
|
+
| `context` | The `context` arg passed to `fetchReportData`. |
|
|
98
|
+
| `filters` | The merged `Tfilter` array right before custom filters are added. Read-only. |
|
|
99
|
+
| `customFilter` | Empty `[]` to push items into. |
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const filter = {
|
|
103
|
+
customFilterCode: `
|
|
104
|
+
if (authContext?.user?.shopId) {
|
|
105
|
+
customFilter.push({
|
|
106
|
+
path: 'ShopId',
|
|
107
|
+
value: authContext.user.shopId,
|
|
108
|
+
method: 'Equal',
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
`
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 4. Return shape
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// Default — full wrapper:
|
|
121
|
+
{
|
|
122
|
+
success: true,
|
|
123
|
+
rows: RowType[] | RowType, // array OR single row (if isSingle)
|
|
124
|
+
total: number, // total rows on the server (paginated mode)
|
|
125
|
+
rawResponse: any, // exact backend response (use sparingly)
|
|
126
|
+
finalRequest: { // useful for debugging — what was actually sent
|
|
127
|
+
endpoint: string,
|
|
128
|
+
getType: 'Pagination' | 'NoPagination' | 'SqlPagination' | 'SqlNoPagination',
|
|
129
|
+
filter: object, // final filter payload
|
|
130
|
+
sourceModel: string,
|
|
131
|
+
params: { page, pageSize },
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// On error:
|
|
136
|
+
{ success: false, error: 'Report detailsOld not found' /* or Error */ }
|
|
137
|
+
|
|
138
|
+
// With isDataOnly: true:
|
|
139
|
+
RowType[] | RowType // bare rows; no wrapper
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Common shape variants
|
|
143
|
+
|
|
144
|
+
| Options | Returns |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Defaults | `{ success, rows: Row[], total, ... }` |
|
|
147
|
+
| `isSingle: true` | `{ success, rows: Row, ... }` — first row, not an array |
|
|
148
|
+
| `isDataOnly: true` | `Row[]` (or `Row` if `isSingle`) — no wrapper |
|
|
149
|
+
| `isPagination: false` | All matching rows (use with care on large datasets) |
|
|
150
|
+
| `dataAsObject: true` | Rows have underscore-paths expanded to nested objects (`{ "Customer_Name": "..." }` → `{ Customer: { Name: "..." } }`) |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 5. Recipes
|
|
155
|
+
|
|
156
|
+
### 5.1 Count rows for a KPI
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
async function Calculation(form, data, setData) {
|
|
160
|
+
const r = await fetchReportData({
|
|
161
|
+
pageId: 'OPEN_TICKETS',
|
|
162
|
+
isPagination: true,
|
|
163
|
+
pageSize: 1, // we only care about `total`, not the rows
|
|
164
|
+
})
|
|
165
|
+
if (r.success) setData(prev => ({ ...prev, openTicketCount: r.total }))
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 5.2 Seed a dialog with one record
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
async function Calculation(form, data, setData, dataRef, reportRefs, openDialog) {
|
|
173
|
+
const r = await fetchReportData({
|
|
174
|
+
pageId: 'CUSTOMER_BY_ID',
|
|
175
|
+
isSingle: true,
|
|
176
|
+
filter: { Tfilter: [{ path: 'Id', value: rowData.Id, method: 'Equal' }] },
|
|
177
|
+
})
|
|
178
|
+
if (!r.success) { showToast('Lookup failed', 'error'); return }
|
|
179
|
+
openDialog('editCustomer', r.rows) // r.rows is a single object, not an array
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 5.3 Filtered list inside an onLoad action
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
async function Calculation(form, data, setData) {
|
|
187
|
+
const r = await fetchReportData({
|
|
188
|
+
pageId: 'ACTIVE_USERS',
|
|
189
|
+
filter: { Tfilter: [{ path: 'TenantId', value: data.tenantId, method: 'Equal' }] },
|
|
190
|
+
isPagination: false,
|
|
191
|
+
})
|
|
192
|
+
if (r.success) setData(prev => ({ ...prev, users: r.rows }))
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 5.4 Pivot-style "raw rows for chart"
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
async function Calculation(form, data, setData) {
|
|
200
|
+
const rows = await fetchReportData({
|
|
201
|
+
pageId: 'SALES_BY_MONTH',
|
|
202
|
+
isPagination: false,
|
|
203
|
+
isDataOnly: true, // skip the wrapper — we just want rows
|
|
204
|
+
dataAsObject: true, // expand `Region_Name` → `Region.Name`
|
|
205
|
+
})
|
|
206
|
+
setData(prev => ({ ...prev, chartData: rows }))
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 5.5 Forward auth context for tenant-scoped reports
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
async function Calculation(form, data, setData, dataRef, reportRefs, openDialog, closeDialog, urlParams, page) {
|
|
214
|
+
const r = await fetchReportData({
|
|
215
|
+
pageId: 'MY_SHOP_ORDERS',
|
|
216
|
+
filter: {
|
|
217
|
+
customFilterCode: `
|
|
218
|
+
if (authContext?.user?.shopId) {
|
|
219
|
+
customFilter.push({ path: 'ShopId', value: authContext.user.shopId, method: 'Equal' })
|
|
220
|
+
}
|
|
221
|
+
`,
|
|
222
|
+
},
|
|
223
|
+
authContext: page?.authContext, // forward whatever the parent page has
|
|
224
|
+
})
|
|
225
|
+
// ...
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 5.6 Override report selection params by index
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
const r = await fetchReportData({
|
|
233
|
+
pageId: 'SALES_BY_REGION',
|
|
234
|
+
globalParams: { 0: data.year, 1: data.region }, // 0-based positional, NOT keyed by name
|
|
235
|
+
})
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 5.7 Refresh a hidden lookup table on demand
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
async function refreshLookups(form, data, setData) {
|
|
242
|
+
const r = await fetchReportData({
|
|
243
|
+
pageId: 'CATEGORIES_LOOKUP',
|
|
244
|
+
isPagination: false,
|
|
245
|
+
isDataOnly: true,
|
|
246
|
+
dataAsObject: true,
|
|
247
|
+
})
|
|
248
|
+
setData(prev => ({ ...prev, categories: r }))
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 6. Pitfalls
|
|
255
|
+
|
|
256
|
+
- **Forgetting `await`.** `fetchReportData` returns a `Promise`. Without `await`, `r.success` is `undefined` and the next line runs before the request completes. Always `await`.
|
|
257
|
+
- **`isSingle: true` returns the row, not an array.** `r.rows[0]` will crash; use `r.rows` directly.
|
|
258
|
+
- **`isPagination: false` on huge reports.** All rows are fetched in one request. Use it for lookups (≤ a few thousand rows). For large datasets, paginate.
|
|
259
|
+
- **Builder-model cache is per-session.** First call hits the metadata endpoint; subsequent calls with the same `pageId` reuse it. If the report definition changes mid-session, the user sees stale columns until refresh.
|
|
260
|
+
- **`globalParams` is index-keyed, not name-keyed.** Keys are 0-based positional indices into `selectionParams`. Wrong index → wrong override.
|
|
261
|
+
- **`filter.LocalTfilter` is stripped.** Only `Tfilter` / `TFilter` are honored at this level. (LocalTfilter is a UI-side concept that doesn't make sense for a direct fetch.)
|
|
262
|
+
- **`null` / `''` / `undefined` values are dropped.** This is usually convenient (no need to gate on optional state), but if you literally need to filter for `null`, use `method: 'IsNull'`, not `value: null`.
|
|
263
|
+
- **`customFilterCode` is a string, not a function.** Pushed `FilterItem`s must be plain objects. You can't reference page `data` directly inside — only `authContext`, `context`, `filters` are in scope. To filter by page state, build the filter array in JS **before** calling `fetchReportData` instead.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 7. Cheat sheet
|
|
268
|
+
|
|
269
|
+
1. `await fetchReportData({ pageId: '...' })` — that's the minimum.
|
|
270
|
+
2. Build the filter as a plain JS object: `{ Tfilter: [{ path, value, method }] }`. Null values are auto-stripped.
|
|
271
|
+
3. `isDataOnly: true` returns the bare rows array — skip the `r.success` ceremony when you trust the call.
|
|
272
|
+
4. `isSingle: true` returns one row, not an array. `r.rows` is that row.
|
|
273
|
+
5. `isPagination: false` to load everything; cap with `pageSize` if needed.
|
|
274
|
+
6. `dataAsObject: true` to expand `Foo_Bar` flat keys into `Foo.Bar` nested objects.
|
|
275
|
+
7. `globalParams: { 0: ..., 1: ... }` overrides specific `selectionParams` by index.
|
|
276
|
+
8. `customFilterCode` runs server-side at request time — useful for auth-derived filters; otherwise build the filter object in JS.
|