robobyte-front-builder 1.0.25 → 1.0.26

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 CHANGED
@@ -173,6 +173,9 @@ function RoboByteBridge({ children }) {
173
173
  user={auth.user}
174
174
  accessToken={auth.accessToken}
175
175
  agGridLicenseKey={process.env.NEXT_PUBLIC_AG_GRID_LICENSE_KEY}
176
+ // Optional — Quartz theme overrides for every <AgGridReact> in the
177
+ // package. Merged on top of the package defaults.
178
+ // agGridTheme={{ accentColor: '#3b82f6', headerHeight: 32 }}
176
179
  >
177
180
  {children}
178
181
  </RoboByteFrontBuilderProvider>
@@ -200,11 +203,15 @@ export default function App({ Component, pageProps }) {
200
203
  | `user` | object | Current user object from your auth context |
201
204
  | `accessToken` | string | Bearer token — attached to every robobyte service call |
202
205
  | `agGridLicenseKey` | string | AG Grid Enterprise license key. The provider calls `LicenseManager.setLicenseKey()` and registers `AllEnterpriseModule` + `IntegratedChartsModule.with(AgChartsEnterpriseModule)` internally. |
206
+ | `agGridTheme` | object | Optional. Quartz theme overrides applied to every `<AgGridReact>` in the package. Merged on top of `DEFAULT_AG_THEME_PARAMS` (see [`src/lib/agGridTheme.js`](src/lib/agGridTheme.js)). Example: `{ accentColor: '#3b82f6', headerHeight: 32 }`. |
203
207
  | `navExtensions` | array | Static nav items to inject into the sidebar (optional) |
204
208
  | `endpoints` | object | Full-URL overrides per endpoint group + name (optional) |
205
209
 
206
210
  > **AG Grid note:** Do not call `LicenseManager` or `ModuleRegistry` in the
207
- > host app — the provider handles it entirely via `agGridLicenseKey`.
211
+ > host app — the provider handles it entirely via `agGridLicenseKey`. The
212
+ > theme is also handled internally — do not pass `theme={...}` to
213
+ > individual `<AgGridReact>` instances rendered by the package; the
214
+ > `agGridTheme` prop is the single source of truth.
208
215
 
209
216
  ---
210
217
 
@@ -308,17 +315,35 @@ Paths to stub out:
308
315
  Fetches report data by `pageId` without any AG Grid dependency. The canonical
309
316
  implementation lives in the package at `services/reportData/fetchReportData`.
310
317
 
311
- ### Sourcing always from the package
318
+ For the full host-app reference (cancellation in `useEffect`, memoizing
319
+ filters, paginated cursor hook, exporting to XLSX, all recipes and pitfalls),
320
+ see **[`docs/fetchReportData.md`](docs/fetchReportData.md)**. The companion
321
+ in-builder Calculation manual is **[`training/48-fetchReportData.md`](training/48-fetchReportData.md)**
322
+ (the auto-injected `fetchReportData` symbol).
312
323
 
313
- The `packageOwnedServices` entry in `next.config.js` (§4) routes every import
314
- of `services/reportData/fetchReportData` — from any file, host or package — to
315
- the package's copy via `NormalModuleReplacementPlugin`. The host app's local
316
- copy (if any) should be emptied out and never executed.
324
+ ### Sourcing both import paths work
317
325
 
318
- ### Usage
326
+ Since v1.0.23, the recommended import is the package surface:
327
+
328
+ ```js
329
+ import { fetchReportDataByPageId } from 'robobyte-front-builder'
330
+ ```
331
+
332
+ The legacy bare-alias path also still works and resolves to the same module
333
+ via the `NormalModuleReplacementPlugin` rule:
319
334
 
320
335
  ```js
321
336
  import fetchReportDataByPageId from 'services/reportData/fetchReportData'
337
+ ```
338
+
339
+ Both paths point to the same file, so the in-memory `builderModelCache` is
340
+ shared. Pick the package import for new code — it doesn't depend on the
341
+ webpack rule.
342
+
343
+ ### Usage
344
+
345
+ ```js
346
+ import { fetchReportDataByPageId } from 'robobyte-front-builder'
322
347
 
323
348
  const result = await fetchReportDataByPageId({
324
349
  pageId, // string | number — required
@@ -374,9 +399,18 @@ import { ReportViewer } from 'robobyte-front-builder'
374
399
  noHeader={true}
375
400
  filter={{ Tfilter: [...] }}
376
401
  height="400px"
402
+ title="Q1 Orders" // toolbar title (left side)
403
+ caption="2026-01-01 → 2026-03-31"
377
404
  />
378
405
  ```
379
406
 
407
+ For the complete host-app reference — every prop, the new `viewerActions` /
408
+ `title` / `caption` / `debug` props, `setOutGridApi` for raw AG Grid API
409
+ access, building filters from React state, master/detail patterns, dialog
410
+ embedding, all pitfalls — see **[`docs/ReportViewer.md`](docs/ReportViewer.md)**.
411
+ The schema-side AI manual for the same component is
412
+ **[`training/35-reportViewer.md`](training/35-reportViewer.md)**.
413
+
380
414
  ### Props
381
415
 
382
416
  | Prop | Type | Description |
@@ -858,9 +892,9 @@ The print builder persists layouts via four REST endpoints. The host API must im
858
892
 
859
893
  The `value` field is the schema serialised with `JSON.stringify`. The print builder deserialises it with `JSON.parse` after fetching.
860
894
 
861
- ### Step 3 — Trigger print from a view
895
+ ### Step 3 — Trigger print from a view (inside the builder)
862
896
 
863
- Inside any action code (button `onClick`, row action, etc.) in a UI Builder view:
897
+ Inside any action code (button `onClick`, row action, viewer action, form `onSubmit`, etc.) in a UI Builder view:
864
898
 
865
899
  ```js
866
900
  openPrintLayout('your-layout-id', {
@@ -871,7 +905,57 @@ openPrintLayout('your-layout-id', {
871
905
  })
872
906
  ```
873
907
 
874
- `openPrintLayout` is injected into every action's execution scope automatically by `ProductionViewer`. No import is needed.
908
+ `openPrintLayout` is injected into every action's execution scope automatically by `ProductionViewer`. No import is needed. The companion helper `closePrintLayout()` dismisses the dialog programmatically.
909
+
910
+ For the full Calculation-scope reference (data shape, all trigger points, recipes for row actions / viewer actions / form submits, pitfalls), see **[`training/49-printLayout.md`](training/49-printLayout.md)**.
911
+
912
+ ### Step 3b — Trigger print from host-app code (outside the builder)
913
+
914
+ When you need to print from regular host-app React code — a custom page, an action handler, a dialog, a hook, anywhere outside a builder Calculation — use the imperative `PrintDialog` API.
915
+
916
+ The pattern, condensed:
917
+
918
+ ```jsx
919
+ // host-app/pages/invoices.jsx (or any host React file)
920
+ import { useRef } from 'react'
921
+ import PrintDialog from 'views/builder/viewer/PrintDialog' // bare-aliased import
922
+
923
+ export default function InvoicesPage() {
924
+ const printDialogRef = useRef(null)
925
+
926
+ const viewerContext = {
927
+ printDialogRef,
928
+ data: {}, setData: () => {}, form: {}, setForm: () => {},
929
+ mode: 'preview', isEditMode: false, isPreviewMode: true, isPrintContext: true,
930
+ }
931
+
932
+ return (
933
+ <>
934
+ <Button
935
+ onClick={() => printDialogRef.current?.open('invoice-layout-id', {
936
+ invoiceNumber: 'INV-1042',
937
+ customerName: 'Acme Corp',
938
+ lines: [{ sku: 'A', qty: 2 }],
939
+ })}
940
+ >
941
+ Print Invoice
942
+ </Button>
943
+
944
+ <PrintDialog viewerContext={viewerContext} />
945
+ </>
946
+ )
947
+ }
948
+ ```
949
+
950
+ Three things to know:
951
+
952
+ 1. **The `PrintDialog` import is bare-aliased.** Either add `views/builder/viewer/PrintDialog` to your `packageOwnedServices` array in `next.config.js` (next to the existing `services/reportData/fetchReportData` entry), or use the longer path `'robobyte-front-builder/src/views/builder/viewer/PrintDialog'`. Both resolve to the same file.
953
+
954
+ 2. **The dialog is mounted-but-hidden.** It registers itself on `printDialogRef.current` after first render and exposes `{ open(layoutId, data), openWithSchema(schema, data), close() }`.
955
+
956
+ 3. **For multi-page apps, prefer a `PrintProvider` pattern** — mount the dialog once at `_app.js` level and expose `usePrintLayout()` from a React context so every page gets `openPrintLayout(id, data)` without re-mounting. The full pattern is in **[`docs/printLayout.md`](docs/printLayout.md)**.
957
+
958
+ For the complete host-app reference (the three mounting patterns, listing layouts, recipes for printing from a `<ReportViewer>` viewer action, all pitfalls), see **[`docs/printLayout.md`](docs/printLayout.md)**.
875
959
 
876
960
  ### Step 4 — Build the layout
877
961
 
package/README.md CHANGED
@@ -17,27 +17,28 @@ A low-code **UI Builder**, **Report Builder**, **Print Layout Designer**, and **
17
17
  7. [Builder pages](#builder-pages)
18
18
  8. [Navigation Extension API](#navigation-extension-api)
19
19
  9. [Provider props reference](#provider-props-reference)
20
- 10. [fetchReportDataByPageId](#fetchreportdatabypageid)
21
- 11. [ReportViewer as a component](#reportviewer-as-a-component)
22
- 12. [Data Grid component](#data-grid-component)
23
- 13. [Dialog component](#dialog-component)
24
- 14. [Popover component](#popover-component)
25
- 15. [Excel Upload component](#excel-upload-component)
26
- 16. [Wizard component](#wizard-component)
27
- 17. [Repeater component](#repeater-component)
28
- 18. [Menu component](#menu-component)
29
- 19. [View Renderer component](#view-renderer-component)
30
- 20. [Layout Grid component](#layout-grid-component)
31
- 21. [Breadcrumb component](#breadcrumb-component)
32
- 22. [Print Layout Builder](#print-layout-builder)
33
- 23. [Calculation Scope Reference](#calculation-scope-reference)
34
- 24. [KPI Component Actions](#kpi-component-actions)
35
- 25. [Global Data Store](#global-data-store)
36
- 26. [Dark / Light theme](#dark--light-theme)
37
- 27. [Syncing local changes](#syncing-local-changes)
38
- 28. [Troubleshooting](#troubleshooting)
39
- 29. [Publishing](#publishing)
40
- 30. [Changelog](#changelog)
20
+ 10. [AG Grid theme](#ag-grid-theme)
21
+ 11. [fetchReportDataByPageId](#fetchreportdatabypageid)
22
+ 12. [ReportViewer as a component](#reportviewer-as-a-component)
23
+ 13. [Data Grid component](#data-grid-component)
24
+ 14. [Dialog component](#dialog-component)
25
+ 15. [Popover component](#popover-component)
26
+ 16. [Excel Upload component](#excel-upload-component)
27
+ 17. [Wizard component](#wizard-component)
28
+ 18. [Repeater component](#repeater-component)
29
+ 19. [Menu component](#menu-component)
30
+ 20. [View Renderer component](#view-renderer-component)
31
+ 21. [Layout Grid component](#layout-grid-component)
32
+ 22. [Breadcrumb component](#breadcrumb-component)
33
+ 23. [Print Layout Builder](#print-layout-builder)
34
+ 24. [Calculation Scope Reference](#calculation-scope-reference)
35
+ 25. [KPI Component Actions](#kpi-component-actions)
36
+ 26. [Global Data Store](#global-data-store)
37
+ 27. [Dark / Light theme](#dark--light-theme)
38
+ 28. [Syncing local changes](#syncing-local-changes)
39
+ 29. [Troubleshooting](#troubleshooting)
40
+ 30. [Publishing](#publishing)
41
+ 31. [Changelog](#changelog)
41
42
 
42
43
  ---
43
44
 
@@ -171,6 +172,11 @@ function RoboByteBridge({ children }) {
171
172
  user={auth.user}
172
173
  accessToken={auth.accessToken}
173
174
  agGridLicenseKey={process.env.NEXT_PUBLIC_AG_GRID_LICENSE_KEY}
175
+ // Optional: Quartz theme overrides applied to every <AgGridReact>
176
+ // in the package. Merged on top of the package defaults — see the
177
+ // "AG Grid theme" section for the full list of params and the
178
+ // programmatic API. Omit to use the package defaults.
179
+ agGridTheme={{ accentColor: '#3b82f6' }}
174
180
  >
175
181
  {children}
176
182
  </RoboByteFrontBuilderProvider>
@@ -316,6 +322,7 @@ function MyFeaturePlugin() {
316
322
  | `user` | object | Current user object from your auth context |
317
323
  | `accessToken` | string | Bearer token attached to every service call |
318
324
  | `agGridLicenseKey` | string | AG Grid Enterprise license key — the provider calls `LicenseManager.setLicenseKey()` and registers all enterprise modules internally |
325
+ | `agGridTheme` | object | Quartz theme overrides applied to every `<AgGridReact>` in the package. Merged on top of `DEFAULT_AG_THEME_PARAMS`. Example: `{ accentColor: '#3b82f6', headerHeight: 32 }`. See [AG Grid theme](#ag-grid-theme). |
319
326
  | `navExtensions` | array | Static nav items to inject into the sidebar |
320
327
  | `endpoints` | object | Full-URL overrides per endpoint group + name |
321
328
 
@@ -323,6 +330,58 @@ function MyFeaturePlugin() {
323
330
 
324
331
  ---
325
332
 
333
+ ## AG Grid theme
334
+
335
+ Every `<AgGridReact>` in the package — `reportViewer`, `dataGrid`, the role-permission editor, the navigator picker — reads its theme from a single context. The default lives in code at [`src/lib/agGridTheme.js`](src/lib/agGridTheme.js) and can be overridden in two places:
336
+
337
+ ### 1. Host-app override via the provider
338
+
339
+ Pass an object of Quartz params to `agGridTheme`. The object is merged on top of the package defaults — keys you set win, everything else falls back.
340
+
341
+ ```jsx
342
+ <RoboByteFrontBuilderProvider
343
+ agGridTheme={{
344
+ accentColor: '#3b82f6',
345
+ headerHeight: 32,
346
+ fontFamily: 'inherit',
347
+ }}
348
+ /* …other props… */
349
+ >
350
+ <App />
351
+ </RoboByteFrontBuilderProvider>
352
+ ```
353
+
354
+ See the [AG Grid Quartz theming reference](https://www.ag-grid.com/react-data-grid/theming/) for the full list of params.
355
+
356
+ ### 2. Package-wide default
357
+
358
+ Open [`src/lib/agGridTheme.js`](src/lib/agGridTheme.js) and edit `DEFAULT_AG_THEME_PARAMS`. Affects every consumer of `robobyte-front-builder` that doesn't supply its own `agGridTheme`.
359
+
360
+ ### Programmatic use
361
+
362
+ ```js
363
+ import {
364
+ DEFAULT_AG_THEME_PARAMS, // frozen params object
365
+ DEFAULT_AG_THEME, // pre-built Quartz theme
366
+ buildAgGridTheme, // (overrides) => Quartz theme
367
+ AgGridThemeProvider, // standalone provider (no other dependencies)
368
+ useAgGridTheme, // hook returning the resolved theme
369
+ } from 'robobyte-front-builder'
370
+
371
+ // In a host component that renders its own AG Grid:
372
+ import { AgGridReact } from 'ag-grid-react'
373
+ function MyGrid() {
374
+ const theme = useAgGridTheme()
375
+ return <AgGridReact theme={theme} columnDefs={[...]} />
376
+ }
377
+ ```
378
+
379
+ The hook falls back to `DEFAULT_AG_THEME` when called outside `RoboByteFrontBuilderProvider`, so isolated grids still render correctly.
380
+
381
+ > **Future plan.** A "system settings" path will eventually layer between `DEFAULT_AG_THEME_PARAMS` and the host's `agGridTheme` prop, letting admins customize the theme without a code change. The provider API will stay the same.
382
+
383
+ ---
384
+
326
385
  ## `fetchReportDataByPageId`
327
386
 
328
387
  Fetches report data by `pageId` without any AG Grid dependency. Always imported from the package path — the `NormalModuleReplacementPlugin` in `next.config.js` routes it to the package's canonical implementation.
@@ -868,6 +927,9 @@ Hot-reload picks up all file changes automatically. Only `next.config.js` change
868
927
  **AG Grid error #200 — IntegratedChartsModule not registered**
869
928
  → The provider registers `IntegratedChartsModule.with(AgChartsEnterpriseModule)` automatically. Ensure `ag-charts-enterprise` is installed in the package.
870
929
 
930
+ **AG Grid theme override not applying**
931
+ → Pass it via `<RoboByteFrontBuilderProvider agGridTheme={{ … }}>`, not as a `theme={…}` prop on individual `<AgGridReact>` instances rendered by the package. Internal grids ignore per-instance themes; the provider's context is the single source of truth. See the *AG Grid theme* section.
932
+
871
933
  **ReportViewer renders at half width**
872
934
  → The outer `Box` in `reportViewer/index.js` must use `display: 'block'`, not `display: 'flex'`.
873
935
 
@@ -903,6 +965,15 @@ npm publish --dry-run
903
965
 
904
966
  ## Changelog
905
967
 
968
+ ### 1.0.25
969
+ - **Centralized AG Grid theme.** Every `<AgGridReact>` in the package (`reportViewer`, `dataGrid`, role-permission editor, navigator picker) now reads its Quartz theme from a single context. Host apps can override package-wide theme params (accent color, header height, fonts, border radius, etc.) via the new `agGridTheme` prop on `RoboByteFrontBuilderProvider`. Defaults live in [`src/lib/agGridTheme.js`](src/lib/agGridTheme.js). New exports: `DEFAULT_AG_THEME_PARAMS`, `DEFAULT_AG_THEME`, `buildAgGridTheme`, `AgGridThemeProvider`, `useAgGridTheme`.
970
+ - **`<ReportViewer>` redesigned toolbar.** Single-row toolbar: title + caption on the left; collapsible search, Filter (outlined button), Refresh (outlined button), and configurable viewer actions on the right. Auto-refresh interval, "Load all data" toggle, and Excel export moved into the side tool panel under *Display Settings*. Search popover navigable by keyboard (↑ ↓ Home End Enter Esc).
971
+ - **`<ReportViewer>` new inspector fields.** `title`, `caption`, `viewerActions` (array of page-level action buttons appended after Filter/Refresh), `debug` (renders a floating bug icon with a popover showing resolved `pageId`/`id`/builder model id/etc. — hidden in production by default).
972
+ - **Print Layout integration.** Host apps can now trigger print layouts directly via the imperative `PrintDialog` ref API — see [docs/printLayout.md](./docs/printLayout.md) for the recommended `PrintProvider` pattern with `usePrintLayout()`.
973
+ - **Public exports expanded.** `fetchReportDataByPageId` now exported from the package surface (`import { fetchReportDataByPageId } from 'robobyte-front-builder'`); the legacy bare-alias path still works.
974
+ - **Unsaved changes guard.** Builder pages now warn on tab close / refresh / in-app navigation when the schema has unsaved edits. `BuilderProvider` exposes `loadSchema(schema)` (clears history baseline), `markClean()` (post-save reset), and `isDirty`.
975
+ - **Session log + AI fine-tune pipeline.** Every successful save (UI Builder + Print Builder) is auto-logged to localStorage with the full schema. New *Session Logs* toolbar dialog lets developers add training prompts to entries and export a Together AI-ready JSONL. Together is now a first-class AI provider in `/api/ai`.
976
+
906
977
  ### 1.0.21
907
978
  - Added Timer Engine — configurable auto-refresh timers per view
908
979
  - Added full Undo / Redo history with keyboard shortcuts (Ctrl+Z / Ctrl+Y)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robobyte-front-builder",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "RoboByte low-code UI builder, Report builder, and navigation extension system",
5
5
  "main": "src/lib/index.js",
6
6
  "files": [
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Centralized AG Grid theme configuration.
3
+ *
4
+ * Every <AgGridReact> in the package reads its theme from a single source —
5
+ * the React context provided by `AgGridThemeProvider` (wrapped automatically
6
+ * inside RoboByteFrontBuilderProvider). The default lives here, in code.
7
+ *
8
+ * Host apps can override theme params by passing `agGridTheme` to
9
+ * RoboByteFrontBuilderProvider:
10
+ *
11
+ * <RoboByteFrontBuilderProvider
12
+ * agGridTheme={{ accentColor: '#3b82f6', headerHeight: 32 }}
13
+ * ...
14
+ * />
15
+ *
16
+ * The override object is merged on top of DEFAULT_AG_THEME_PARAMS — host
17
+ * params win for the keys they set, and fall back to defaults otherwise.
18
+ *
19
+ * Future: this default will be replaced with a system-settings fetch so
20
+ * theme params can be tweaked at runtime without a code change. Until then,
21
+ * editing this file is the way to change the package-wide default.
22
+ */
23
+
24
+ import { themeQuartz } from 'ag-grid-community'
25
+
26
+ /**
27
+ * Default Quartz theme parameters used across the package.
28
+ * See https://www.ag-grid.com/react-data-grid/theming/ for the full list.
29
+ *
30
+ * Add new keys here as the package grows. Host apps can override any of
31
+ * them by passing `agGridTheme={{ <key>: <value> }}` to the provider.
32
+ */
33
+ export const DEFAULT_AG_THEME_PARAMS = Object.freeze({
34
+ accentColor: '#FF1185',
35
+ // headerHeight: 28,
36
+ // rowHeight: 34,
37
+ // fontFamily: 'inherit',
38
+ // fontSize: 13,
39
+ // borderRadius: 4,
40
+ })
41
+
42
+ /**
43
+ * Build a Quartz theme from optional overrides. Returns a fresh theme object
44
+ * (Quartz themes are immutable). Safe to call on every render — `withParams`
45
+ * caches by params identity.
46
+ *
47
+ * @param {Object} [overrides] - Quartz theme params to merge over the defaults.
48
+ * @returns {object} A ready-to-use AG Grid theme.
49
+ */
50
+ export function buildAgGridTheme(overrides) {
51
+ return themeQuartz.withParams({
52
+ ...DEFAULT_AG_THEME_PARAMS,
53
+ ...(overrides ?? {}),
54
+ })
55
+ }
56
+
57
+ /** Pre-built default theme — convenient when no override is in play. */
58
+ export const DEFAULT_AG_THEME = buildAgGridTheme()
@@ -0,0 +1,42 @@
1
+ /**
2
+ * AgGridThemeContext — single source of truth for the package-wide AG Grid
3
+ * theme. Wrapped automatically inside RoboByteFrontBuilderProvider.
4
+ *
5
+ * Consumers call `useAgGridTheme()` to get the resolved Quartz theme and pass
6
+ * it to <AgGridReact theme={agTheme} />.
7
+ */
8
+
9
+ import { createContext, useContext, useMemo } from 'react'
10
+ import { buildAgGridTheme, DEFAULT_AG_THEME } from './agGridTheme'
11
+
12
+ const AgGridThemeContext = createContext(DEFAULT_AG_THEME)
13
+
14
+ /**
15
+ * Wraps children with the AG Grid theme context.
16
+ *
17
+ * @param {Object} [params] - Quartz theme overrides (merged over defaults).
18
+ * @param {ReactNode} children
19
+ */
20
+ export function AgGridThemeProvider({ params, children }) {
21
+ // JSON.stringify on the params object is intentional — host apps that pass
22
+ // an inline object literal would otherwise rebuild the theme on every
23
+ // parent render. Stringify keys the memo by the actual params content.
24
+ const theme = useMemo(
25
+ () => buildAgGridTheme(params),
26
+ // eslint-disable-next-line react-hooks/exhaustive-deps
27
+ [JSON.stringify(params ?? null)]
28
+ )
29
+ return (
30
+ <AgGridThemeContext.Provider value={theme}>
31
+ {children}
32
+ </AgGridThemeContext.Provider>
33
+ )
34
+ }
35
+
36
+ /**
37
+ * Returns the live AG Grid theme. Falls back to the default theme when
38
+ * called outside RoboByteFrontBuilderProvider, so isolated grids still work.
39
+ */
40
+ export function useAgGridTheme() {
41
+ return useContext(AgGridThemeContext)
42
+ }
package/src/lib/index.js CHANGED
@@ -106,3 +106,10 @@ export { AuthContext } from '../context/AuthContext'
106
106
  // — both resolve to the same module thanks to the NormalModuleReplacementPlugin
107
107
  // in next.config.js, so the in-memory builderModelCache is shared.
108
108
  export { default as fetchReportDataByPageId } from '../services/reportData/fetchReportData'
109
+
110
+ // ── AG Grid theme ────────────────────────────────────────────────────────────
111
+ // Default theme params (host can override via RoboByteFrontBuilderProvider's
112
+ // `agGridTheme` prop), a builder function for ad-hoc themes, and the hook
113
+ // every internal grid uses to read the live theme.
114
+ export { DEFAULT_AG_THEME_PARAMS, buildAgGridTheme, DEFAULT_AG_THEME } from './agGridTheme'
115
+ export { AgGridThemeProvider, useAgGridTheme } from './agGridThemeContext'
@@ -49,6 +49,7 @@ import { Toaster } from 'react-hot-toast'
49
49
  import { ModuleRegistry } from 'ag-grid-community'
50
50
  import { AllEnterpriseModule, LicenseManager } from 'ag-grid-enterprise'
51
51
  import { NavigationExtensionProvider } from '../navigation/NavigationExtensionContext'
52
+ import { AgGridThemeProvider } from '../agGridThemeContext'
52
53
  import { configureRoboByte } from '../../services/config'
53
54
  import { AuthContext } from '../../context/AuthContext'
54
55
  import { setRouter } from '../../services/routerRef'
@@ -95,6 +96,17 @@ const RoboByteFrontBuilderProvider = ({
95
96
  navExtensions = [],
96
97
  endpoints = null,
97
98
  agGridLicenseKey = null,
99
+ /**
100
+ * Optional Quartz theme overrides applied to every <AgGridReact> in the
101
+ * package. Merged on top of DEFAULT_AG_THEME_PARAMS in `lib/agGridTheme.js`.
102
+ *
103
+ * Example:
104
+ * agGridTheme={{ accentColor: '#3b82f6', headerHeight: 32 }}
105
+ *
106
+ * Future: this will accept a "load from system settings" mode. For now
107
+ * pass a plain params object.
108
+ */
109
+ agGridTheme = null,
98
110
  toasterProps = {},
99
111
  }) => {
100
112
  // Apply URL + endpoint config synchronously before any child renders.
@@ -121,6 +133,7 @@ const RoboByteFrontBuilderProvider = ({
121
133
  return (
122
134
  <AuthContext.Provider value={authValue}>
123
135
  <NavigationExtensionProvider items={navExtensions}>
136
+ <AgGridThemeProvider params={agGridTheme}>
124
137
  <RouterSync />
125
138
  {children}
126
139
  {/* Package-owned Toaster — same react-hot-toast instance used by all
@@ -157,6 +170,7 @@ const RoboByteFrontBuilderProvider = ({
157
170
  }}
158
171
  {...toasterProps}
159
172
  />
173
+ </AgGridThemeProvider>
160
174
  </NavigationExtensionProvider>
161
175
  </AuthContext.Provider>
162
176
  )
@@ -1,7 +1,6 @@
1
1
  import { Box, Button, IconButton, Tooltip, Typography } from '@mui/material'
2
2
  import { useRef, useMemo, useCallback, useEffect } from 'react'
3
3
  import { AgGridReact } from 'ag-grid-react'
4
- import { themeQuartz } from 'ag-grid-community'
5
4
  import { AddOutlined, DeleteOutlined, TableChartOutlined } from '@mui/icons-material'
6
5
  import ViewerComponentWrapper from '../ViewerComponentWrapper'
7
6
  import RowActionsCell from './RowActionsCell'
@@ -9,9 +8,7 @@ import { resolveProps } from 'services/builderHelper/resolveProps'
9
8
  import { executeJSCode } from 'services/builderHelper/jsExecutor'
10
9
  import { processColumnDefinitions, processColumnsConfig } from 'views/genericTable/convertStringFunctions'
11
10
  import { AG_COMPONENTS } from './dataGridComponents'
12
-
13
- // ── Shared AG Grid theme (same as SGrid) ──────────────────────────────────────
14
- const agTheme = themeQuartz.withParams({ accentColor: '#FF1185' })
11
+ import { useAgGridTheme } from 'src/lib/agGridThemeContext'
15
12
 
16
13
  // ── Delete-column cell renderer ───────────────────────────────────────────────
17
14
  // Defined at module level so AG Grid never receives a new component reference.
@@ -49,6 +46,9 @@ export default function DataGridRenderer({ node, viewerContext }) {
49
46
  form, data, setData, dataRef, reportRefs, openDialog, closeDialog, isEditMode,
50
47
  } = viewerContext
51
48
 
49
+ // Centralized AG Grid theme — see lib/agGridTheme.js / RoboByteFrontBuilderProvider.
50
+ const agTheme = useAgGridTheme()
51
+
52
52
  const main = resolveProps(node, 'main', viewerContext)
53
53
 
54
54
  const {
@@ -115,7 +115,7 @@ import numeral from 'numeral'
115
115
  import CustomFilterDialog from "views/customFilter/CustomFilterDialog";
116
116
  import CustomStatusBar from "views/genericTable/statusBar/rowCountStatusBar";
117
117
  import StreamService from "services/StreamService";
118
- import {themeQuartz} from 'ag-grid-community';
118
+ import { useAgGridTheme } from 'src/lib/agGridThemeContext'
119
119
  import {ReportBuilderEndpoints} from "services/Endpoints/ReportBuilderEndpoints";
120
120
  import {debounce} from "lodash";
121
121
  import jsPDF from "jspdf";
@@ -330,10 +330,10 @@ const SGrid = props => {
330
330
  // ** State
331
331
  const appContext = useContext(SystemContext);
332
332
  const imageBaseUrl = appContext?.settings?.imagesStorageServer;
333
- const agTheme = themeQuartz
334
- .withParams({
335
- accentColor: "#FF1185"
336
- });
333
+ // Theme comes from RoboByteFrontBuilderProvider's agGridTheme prop, merged
334
+ // with DEFAULT_AG_THEME_PARAMS in lib/agGridTheme.js. Falls back to the
335
+ // bare default when used outside the provider.
336
+ const agTheme = useAgGridTheme()
337
337
 
338
338
  const {
339
339
  externalTimer,
@@ -37,7 +37,7 @@ import numeral from 'numeral'
37
37
  import CustomFilterDialog from "views/customFilter/CustomFilterDialog";
38
38
  import CustomStatusBar from "views/genericTable/statusBar/rowCountStatusBar";
39
39
  import StreamService from "services/StreamService";
40
- import {themeQuartz} from 'ag-grid-community';
40
+ import { useAgGridTheme } from 'src/lib/agGridThemeContext'
41
41
 
42
42
  // ** Utils Import
43
43
 
@@ -52,10 +52,8 @@ const defaultFinalRequest = {
52
52
  }
53
53
  const TAGGrid = props => {
54
54
  // ** State
55
- const agTheme = themeQuartz
56
- .withParams({
57
- accentColor: "#FF1185"
58
- });
55
+ // Theme comes from RoboByteFrontBuilderProvider's agGridTheme prop.
56
+ const agTheme = useAgGridTheme()
59
57
  const {
60
58
  groupEndPoint,
61
59
  streamEndPoint,
@@ -21,8 +21,11 @@ import {Endpoints, Services} from 'src/services/Endpoints'
21
21
  import {toast} from 'react-hot-toast'
22
22
  import {AgGridReact} from "ag-grid-react";
23
23
  import {AutocompleteEditorWrapper} from "views/genericTable/cellEditors/autocompleteEditor";
24
+ import { useAgGridTheme } from 'src/lib/agGridThemeContext'
24
25
 
25
26
  const UpdateReportPermissionsDialog = props => {
27
+ // Centralized AG Grid theme.
28
+ const agTheme = useAgGridTheme()
26
29
  // ** Props
27
30
  const {handleToggleDialogs, report, dialog, setRefresh} = props
28
31
  const [IsSubmitting, setIsSubmitting] = useState(false)
@@ -266,6 +269,7 @@ const UpdateReportPermissionsDialog = props => {
266
269
  <Grid container size={{ xs: 12 }}>
267
270
  <div style={{width: "100%", height: "40vh", direction: 'ltr'}}>
268
271
  <AgGridReact
272
+ theme={agTheme}
269
273
  enableRtl={true}
270
274
  columnDefs={colDefs}
271
275
  rowData={ReportPermissions}