robobyte-front-builder 1.0.17 → 1.0.21

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.
Files changed (516) hide show
  1. package/README.md +211 -0
  2. package/next.config.js +19 -48
  3. package/package.json +39 -85
  4. package/src/context/BuilderContext.jsx +134 -6
  5. package/src/context/SystemContext.js +2 -2
  6. package/src/hooks/useGlobalStore.js +36 -0
  7. package/src/hooks/useTimerEngine.js +54 -0
  8. package/src/lib/index.js +11 -3
  9. package/src/lib/layouts/BlankLayout.jsx +13 -0
  10. package/src/lib/providers/RoboByteFrontBuilderProvider.jsx +59 -1
  11. package/src/lib/themes/builderTheme.js +41 -0
  12. package/src/pages/_app.js +32 -134
  13. package/src/pages/api/ai.js +87 -0
  14. package/src/pages/builders/report/index.js +1 -0
  15. package/src/pages/builders/report/list/index.js +1 -0
  16. package/src/pages/builders/report/viewer/index.js +1 -0
  17. package/src/pages/index.js +88 -37
  18. package/src/pages/printBuilder/index.jsx +263 -0
  19. package/src/pages/printBuilder/layouts/index.jsx +298 -0
  20. package/src/pages/reportModule/reportBuilder/index.js +723 -563
  21. package/src/pages/reportModule/reportBuilder/reportViewer/index.js +137 -71
  22. package/src/pages/reportModule/reportBuilder/reports/index.js +51 -13
  23. package/src/pages/reportModule/reportBuilder/reportsPermissions/index.js +126 -0
  24. package/src/pages/viewBuilder/index.jsx +117 -32
  25. package/src/pages/viewBuilder/views/index.js +3 -3
  26. package/src/pages/viewer/[id]/index.js +2 -1
  27. package/src/services/DeleteService.js +31 -60
  28. package/src/services/Endpoints/PrintLayoutEndpoints.js +42 -0
  29. package/src/services/Endpoints.js +2 -0
  30. package/src/services/GetService.js +33 -54
  31. package/src/services/PatchService.js +38 -65
  32. package/src/services/PostService.js +37 -63
  33. package/src/services/UpdateService.js +39 -65
  34. package/src/services/builderHelper/actionExecutor.js +141 -25
  35. package/src/services/builderHelper/builderHelper.js +92 -0
  36. package/src/services/builderHelper/colorSchema.js +95 -0
  37. package/src/services/builderHelper/iconResolver.js +50 -0
  38. package/src/services/builderHelper/jsExecutor.js +212 -46
  39. package/src/services/builderHelper/nodeFactory.js +32 -15
  40. package/src/services/builderHelper/numberFormat.js +123 -0
  41. package/src/services/builderHelper/resolveProps.js +73 -4
  42. package/src/services/builderHelper/thresholdEngine.js +77 -0
  43. package/src/services/builderHelper/tree.js +31 -0
  44. package/src/services/components/agGridAutoComplete.js +5 -9
  45. package/src/services/config.js +9 -1
  46. package/src/services/globalStore.js +80 -0
  47. package/src/services/helper/multiSelectEditor.js +5 -9
  48. package/src/services/helper/multiSelectEditorByBuilder.js +241 -0
  49. package/src/services/helper/reportSessionHelper.js +83 -0
  50. package/src/services/reportData/fetchReportData.js +69 -28
  51. package/src/services/routerRef.js +35 -0
  52. package/src/views/ConfirmDialog.js +2 -2
  53. package/src/views/builder/JSEditor.js +105 -107
  54. package/src/views/builder/inspector/Inspector.jsx +6 -9
  55. package/src/views/builder/inspector/Tabs/ComponentActionsTab.jsx +7 -13
  56. package/src/views/builder/inspector/Tabs/MainTab.jsx +143 -25
  57. package/src/views/builder/inspector/Tabs/RulesTab.jsx +9 -24
  58. package/src/views/builder/inspector/Tabs/StyleTab.jsx +9 -24
  59. package/src/views/builder/inspector/definitions/autocomplete/main.js +4 -6
  60. package/src/views/builder/inspector/definitions/banner/actions.js +7 -0
  61. package/src/views/builder/inspector/definitions/banner/main.js +22 -0
  62. package/src/views/builder/inspector/definitions/banner/rules.js +1 -0
  63. package/src/views/builder/inspector/definitions/banner/style.js +1 -0
  64. package/src/views/builder/inspector/definitions/breadcrumb/main.js +43 -6
  65. package/src/views/builder/inspector/definitions/button/main.js +11 -12
  66. package/src/views/builder/inspector/definitions/button/style.js +18 -30
  67. package/src/views/builder/inspector/definitions/checkbox/actions.js +3 -1
  68. package/src/views/builder/inspector/definitions/checkbox/main.js +4 -6
  69. package/src/views/builder/inspector/definitions/common/main.js +13 -2
  70. package/src/views/builder/inspector/definitions/dataGrid/main.js +23 -0
  71. package/src/views/builder/inspector/definitions/dataTableViewer/main.js +46 -0
  72. package/src/views/builder/inspector/definitions/datepicker/actions.js +3 -1
  73. package/src/views/builder/inspector/definitions/datepicker/main.js +6 -14
  74. package/src/views/builder/inspector/definitions/dialog/main.js +36 -0
  75. package/src/views/builder/inspector/definitions/dropdown/main.js +5 -8
  76. package/src/views/builder/inspector/definitions/excelUpload/actions.js +23 -0
  77. package/src/views/builder/inspector/definitions/excelUpload/main.js +17 -0
  78. package/src/views/builder/inspector/definitions/excelUpload/rules.js +1 -0
  79. package/src/views/builder/inspector/definitions/excelUpload/style.js +45 -0
  80. package/src/views/builder/inspector/definitions/header/main.js +10 -1
  81. package/src/views/builder/inspector/definitions/index.js +106 -19
  82. package/src/views/builder/inspector/definitions/input/actions.js +4 -1
  83. package/src/views/builder/inspector/definitions/input/main.js +20 -11
  84. package/src/views/builder/inspector/definitions/kpi/avatarGroup.js +22 -0
  85. package/src/views/builder/inspector/definitions/kpi/badge.js +17 -0
  86. package/src/views/builder/inspector/definitions/kpi/bulletChart.js +47 -0
  87. package/src/views/builder/inspector/definitions/kpi/chart.js +55 -0
  88. package/src/views/builder/inspector/definitions/kpi/colorScale.js +60 -0
  89. package/src/views/builder/inspector/definitions/kpi/comparisonBars.js +41 -0
  90. package/src/views/builder/inspector/definitions/kpi/countdown.js +46 -0
  91. package/src/views/builder/inspector/definitions/kpi/donut.js +51 -0
  92. package/src/views/builder/inspector/definitions/kpi/funnel.js +25 -0
  93. package/src/views/builder/inspector/definitions/kpi/gauge.js +39 -0
  94. package/src/views/builder/inspector/definitions/kpi/heatmapGrid.js +96 -0
  95. package/src/views/builder/inspector/definitions/kpi/iconBox.js +20 -0
  96. package/src/views/builder/inspector/definitions/kpi/metric.js +45 -0
  97. package/src/views/builder/inspector/definitions/kpi/rating.js +27 -0
  98. package/src/views/builder/inspector/definitions/kpi/statusDot.js +18 -0
  99. package/src/views/builder/inspector/definitions/kpi/stepStage.js +65 -0
  100. package/src/views/builder/inspector/definitions/kpi/tagList.js +32 -0
  101. package/src/views/builder/inspector/definitions/kpi/timeline.js +80 -0
  102. package/src/views/builder/inspector/definitions/kpi/trend.js +20 -0
  103. package/src/views/builder/inspector/definitions/label/main.js +10 -1
  104. package/src/views/builder/inspector/definitions/layout/main.js +27 -3
  105. package/src/views/builder/inspector/definitions/number/main.js +6 -14
  106. package/src/views/builder/inspector/definitions/pageNumber/main.js +21 -0
  107. package/src/views/builder/inspector/definitions/popover/main.js +71 -0
  108. package/src/views/builder/inspector/definitions/radio/main.js +5 -8
  109. package/src/views/builder/inspector/definitions/repeater/main.js +31 -0
  110. package/src/views/builder/inspector/definitions/reportViewer/main.js +15 -1
  111. package/src/views/builder/inspector/definitions/richtext/main.js +5 -8
  112. package/src/views/builder/inspector/definitions/signature/main.js +4 -1
  113. package/src/views/builder/inspector/definitions/tag/main.js +5 -8
  114. package/src/views/builder/inspector/definitions/textarea/actions.js +4 -1
  115. package/src/views/builder/inspector/definitions/textarea/main.js +5 -7
  116. package/src/views/builder/inspector/definitions/time/main.js +5 -8
  117. package/src/views/builder/inspector/definitions/toggle/main.js +5 -19
  118. package/src/views/builder/inspector/definitions/treeView/main.js +61 -0
  119. package/src/views/builder/inspector/definitions/viewRenderer/main.js +53 -0
  120. package/src/views/builder/inspector/definitions/wizard/main.js +68 -0
  121. package/src/views/builder/inspector/definitions/wizard-step/main.js +25 -0
  122. package/src/views/builder/inspector/fields/ActionsConfigEditor.jsx +426 -0
  123. package/src/views/builder/inspector/fields/ColorSchemaField.jsx +140 -0
  124. package/src/views/builder/inspector/fields/ColumnFunctionEditor.jsx +238 -0
  125. package/src/views/builder/inspector/fields/ColumnMappingEditor.jsx +105 -0
  126. package/src/views/builder/inspector/fields/ColumnsConfigEditor.jsx +506 -0
  127. package/src/views/builder/inspector/fields/DonutRingsEditorField.jsx +337 -0
  128. package/src/views/builder/inspector/fields/ExtraColsEditor.jsx +618 -0
  129. package/src/views/builder/inspector/fields/FunctionHelpPopover.jsx +295 -0
  130. package/src/views/builder/inspector/fields/IconEditor.jsx +64 -0
  131. package/src/views/builder/inspector/fields/KpiActionField.jsx +223 -0
  132. package/src/views/builder/inspector/fields/MarkersEditorField.jsx +173 -0
  133. package/src/views/builder/inspector/fields/SelectEditor.jsx +9 -5
  134. package/src/views/builder/inspector/fields/SeriesEditorField.jsx +363 -0
  135. package/src/views/builder/inspector/fields/TableColumnsEditor.jsx +104 -0
  136. package/src/views/builder/inspector/fields/ThresholdsEditor.jsx +247 -0
  137. package/src/views/builder/inspector/fields/ValueFunctionsRefPanel.jsx +217 -0
  138. package/src/views/builder/inspector/fields/columnEditorShared.jsx +217 -0
  139. package/src/views/builder/sidebar/Sidebar.jsx +4 -2
  140. package/src/views/builder/sidebar/SidebarTabs.jsx +28 -17
  141. package/src/views/builder/sidebar/tabs/ActionsTab.jsx +7 -3
  142. package/src/views/builder/sidebar/tabs/AiTab/AiPreviewDialog.jsx +193 -0
  143. package/src/views/builder/sidebar/tabs/AiTab/aiProvider.js +49 -0
  144. package/src/views/builder/sidebar/tabs/AiTab/index.jsx +409 -0
  145. package/src/views/builder/sidebar/tabs/AiTab/schemaTransformer.js +102 -0
  146. package/src/views/builder/sidebar/tabs/AiTab/schemaValidator.js +64 -0
  147. package/src/views/builder/sidebar/tabs/AiTab/systemPrompt.js +1151 -0
  148. package/src/views/builder/sidebar/tabs/Components/ComponentsTab.jsx +31 -31
  149. package/src/views/builder/sidebar/tabs/Components/componentCatalog.js +43 -21
  150. package/src/views/builder/sidebar/tabs/Components/printComponentCatalog.js +81 -0
  151. package/src/views/builder/sidebar/tabs/TimersTab.jsx +338 -0
  152. package/src/views/builder/sidebar/tabs/TreeTab.jsx +13 -4
  153. package/src/views/builder/sidebar/tabs/ViewTab.jsx +1 -1
  154. package/src/views/builder/viewer/AdornedLabel.jsx +82 -0
  155. package/src/views/builder/viewer/ComponentRenderer.jsx +98 -24
  156. package/src/views/builder/viewer/DialogsZone.jsx +259 -0
  157. package/src/views/builder/viewer/FieldLabel.jsx +106 -0
  158. package/src/views/builder/viewer/PrintDialog.jsx +481 -0
  159. package/src/views/builder/viewer/ProductionViewer.jsx +80 -5
  160. package/src/views/builder/viewer/Viewer.jsx +106 -8
  161. package/src/views/builder/viewer/ViewerComponentWrapper.jsx +61 -4
  162. package/src/views/builder/viewer/ViewerToolbar.jsx +273 -59
  163. package/src/views/builder/viewer/renderers/AutoCompleteRenderer.jsx +26 -22
  164. package/src/views/builder/viewer/renderers/AvatarGroupRenderer.jsx +112 -0
  165. package/src/views/builder/viewer/renderers/BadgeRenderer.jsx +79 -0
  166. package/src/views/builder/viewer/renderers/BannerRenderer.jsx +62 -0
  167. package/src/views/builder/viewer/renderers/BreadcrumbRenderer.jsx +203 -15
  168. package/src/views/builder/viewer/renderers/BulletChartRenderer.jsx +147 -0
  169. package/src/views/builder/viewer/renderers/ButtonRenderer.jsx +98 -39
  170. package/src/views/builder/viewer/renderers/CardRenderer.jsx +1 -1
  171. package/src/views/builder/viewer/renderers/ChartRenderer.jsx +388 -0
  172. package/src/views/builder/viewer/renderers/CheckboxRenderer.jsx +17 -9
  173. package/src/views/builder/viewer/renderers/ColorScaleRenderer.jsx +300 -0
  174. package/src/views/builder/viewer/renderers/ComparisonBarsRenderer.jsx +133 -0
  175. package/src/views/builder/viewer/renderers/ContainerRenderer.jsx +3 -1
  176. package/src/views/builder/viewer/renderers/CountdownRenderer.jsx +249 -0
  177. package/src/views/builder/viewer/renderers/DataGridRenderer.jsx +380 -0
  178. package/src/views/builder/viewer/renderers/DataTableViewerRenderer.jsx +240 -0
  179. package/src/views/builder/viewer/renderers/DatePickerRenderer.jsx +25 -24
  180. package/src/views/builder/viewer/renderers/DialogRenderer.jsx +327 -0
  181. package/src/views/builder/viewer/renderers/DividerRenderer.jsx +1 -1
  182. package/src/views/builder/viewer/renderers/DonutRenderer.jsx +294 -0
  183. package/src/views/builder/viewer/renderers/DropdownRenderer.jsx +36 -44
  184. package/src/views/builder/viewer/renderers/ExcelUploadRenderer.jsx +639 -0
  185. package/src/views/builder/viewer/renderers/FunnelRenderer.jsx +93 -0
  186. package/src/views/builder/viewer/renderers/GaugeRenderer.jsx +159 -0
  187. package/src/views/builder/viewer/renderers/HeaderRenderer.jsx +31 -9
  188. package/src/views/builder/viewer/renderers/HeatmapGridRenderer.jsx +432 -0
  189. package/src/views/builder/viewer/renderers/IconBoxRenderer.jsx +59 -0
  190. package/src/views/builder/viewer/renderers/ImageRenderer.jsx +1 -1
  191. package/src/views/builder/viewer/renderers/InputRenderer.jsx +75 -18
  192. package/src/views/builder/viewer/renderers/LabelRenderer.jsx +35 -9
  193. package/src/views/builder/viewer/renderers/LayoutCellRenderer.jsx +102 -40
  194. package/src/views/builder/viewer/renderers/LayoutContextMenu.jsx +8 -8
  195. package/src/views/builder/viewer/renderers/LayoutRenderer.jsx +48 -6
  196. package/src/views/builder/viewer/renderers/LinkRenderer.jsx +1 -1
  197. package/src/views/builder/viewer/renderers/MenuRenderer.jsx +2 -2
  198. package/src/views/builder/viewer/renderers/MetricRenderer.jsx +80 -0
  199. package/src/views/builder/viewer/renderers/NumberFormatRenderer.jsx +21 -30
  200. package/src/views/builder/viewer/renderers/PageNumberRenderer.jsx +76 -0
  201. package/src/views/builder/viewer/renderers/PopoverRenderer.jsx +350 -0
  202. package/src/views/builder/viewer/renderers/ProgressCircleRenderer.jsx +1 -1
  203. package/src/views/builder/viewer/renderers/ProgressLineRenderer.jsx +1 -1
  204. package/src/views/builder/viewer/renderers/RadioGroupRenderer.jsx +28 -39
  205. package/src/views/builder/viewer/renderers/RatingRenderer.jsx +80 -0
  206. package/src/views/builder/viewer/renderers/RepeaterRenderer.jsx +297 -38
  207. package/src/views/builder/viewer/renderers/ReportViewerRenderer.jsx +219 -5
  208. package/src/views/builder/viewer/renderers/RichTextRenderer.jsx +60 -66
  209. package/src/views/builder/viewer/renderers/RowActionsCell.jsx +308 -0
  210. package/src/views/builder/viewer/renderers/SignatureRenderer.jsx +33 -62
  211. package/src/views/builder/viewer/renderers/StatusDotRenderer.jsx +75 -0
  212. package/src/views/builder/viewer/renderers/StepStageRenderer.jsx +348 -0
  213. package/src/views/builder/viewer/renderers/TagListRenderer.jsx +115 -0
  214. package/src/views/builder/viewer/renderers/TagPickerRenderer.jsx +31 -45
  215. package/src/views/builder/viewer/renderers/TextAreaRenderer.jsx +25 -18
  216. package/src/views/builder/viewer/renderers/TextRenderer.jsx +7 -1
  217. package/src/views/builder/viewer/renderers/TimePickerRenderer.jsx +25 -24
  218. package/src/views/builder/viewer/renderers/TimelineRenderer.jsx +525 -0
  219. package/src/views/builder/viewer/renderers/ToggleRenderer.jsx +21 -27
  220. package/src/views/builder/viewer/renderers/TreeViewRenderer.jsx +832 -0
  221. package/src/views/builder/viewer/renderers/TrendRenderer.jsx +66 -0
  222. package/src/views/builder/viewer/renderers/ViewRendererRenderer.jsx +315 -0
  223. package/src/views/builder/viewer/renderers/WizardRenderer.jsx +380 -64
  224. package/src/views/builder/viewer/renderers/WizardStepRenderer.jsx +21 -12
  225. package/src/views/builder/viewer/renderers/dataGridComponents.jsx +824 -0
  226. package/src/views/customFilter/CustomFilterDialog.js +1023 -660
  227. package/src/views/customFilter/FixedFilterDialog.js +649 -0
  228. package/src/views/customFilter/SearchFilterDialog.js +248 -0
  229. package/src/views/genericTable/BuilderExpressionParams.js +3 -3
  230. package/src/views/genericTable/ColumnConfiguratorDialog.js +771 -0
  231. package/src/views/genericTable/FixedFilterDialog.js +3 -2
  232. package/src/views/genericTable/FormattingSettingsDialog.js +551 -0
  233. package/src/views/genericTable/ReportSettingsDialog.js +151 -0
  234. package/src/views/genericTable/SGrid.js +1061 -247
  235. package/src/views/genericTable/SearchFilterDialog.js +3 -2
  236. package/src/views/genericTable/TAGGrid.js +83 -69
  237. package/src/views/genericTable/cellEditors/autocompleteEditor.js +5 -9
  238. package/src/views/genericTable/convertStringFunctions.js +336 -0
  239. package/src/views/genericTable/statusBar/rowCountStatusBar.js +3 -1
  240. package/src/views/genericTable/updateRefHelpers.js +424 -0
  241. package/src/views/printBuilder/PrintBuilderViewer.jsx +607 -0
  242. package/src/views/printBuilder/PrintPreviewCanvas.jsx +157 -0
  243. package/src/views/rolePermissions/UpdateReportPermissionDialog.js +316 -0
  244. package/src/@core/components/auth/AclGuard.js +0 -55
  245. package/src/@core/components/auth/AuthGuard.js +0 -40
  246. package/src/@core/components/auth/GuestGuard.js +0 -30
  247. package/src/@core/components/custom-inputs/Horizontal.jsx +0 -143
  248. package/src/@core/components/custom-inputs/Image.jsx +0 -78
  249. package/src/@core/components/custom-inputs/Vertical.jsx +0 -113
  250. package/src/@core/components/customizer/index.jsx +0 -470
  251. package/src/@core/components/customizer/styles.module.css +0 -169
  252. package/src/@core/components/mui/Avatar.jsx +0 -41
  253. package/src/@core/components/mui/Badge.jsx +0 -20
  254. package/src/@core/components/mui/IconButton.jsx +0 -74
  255. package/src/@core/components/mui/TabList.jsx +0 -60
  256. package/src/@core/components/option-menu/index.jsx +0 -137
  257. package/src/@core/components/scroll-to-top/index.jsx +0 -43
  258. package/src/@core/components/spinner/index.js +0 -26
  259. package/src/@core/components/window-wrapper/index.js +0 -27
  260. package/src/@core/contexts/settingsContext.jsx +0 -98
  261. package/src/@core/hooks/useBgColor.js +0 -63
  262. package/src/@core/hooks/useImageVariant.js +0 -27
  263. package/src/@core/hooks/useLayoutInit.js +0 -37
  264. package/src/@core/hooks/useObjectCookie.js +0 -18
  265. package/src/@core/hooks/useSettings.jsx +0 -15
  266. package/src/@core/layouts/BlankLayout.js +0 -37
  267. package/src/@core/layouts/BlankLayoutWithAppBar.js +0 -51
  268. package/src/@core/layouts/HorizontalLayout.jsx +0 -151
  269. package/src/@core/layouts/Layout.js +0 -39
  270. package/src/@core/layouts/VerticalLayout.jsx +0 -124
  271. package/src/@core/layouts/components/blank-layout-with-appBar/index.js +0 -115
  272. package/src/@core/layouts/components/horizontal/app-bar-content/index.js +0 -67
  273. package/src/@core/layouts/components/horizontal/navigation/HorizontalNavGroup.js +0 -352
  274. package/src/@core/layouts/components/horizontal/navigation/HorizontalNavItems.js +0 -21
  275. package/src/@core/layouts/components/horizontal/navigation/HorizontalNavLink.js +0 -195
  276. package/src/@core/layouts/components/horizontal/navigation/index.js +0 -31
  277. package/src/@core/layouts/components/shared-components/LanguageDropdown.js +0 -96
  278. package/src/@core/layouts/components/shared-components/ModeToggler.js +0 -32
  279. package/src/@core/layouts/components/shared-components/NotificationDropdown.js +0 -226
  280. package/src/@core/layouts/components/shared-components/UserDropdown.js +0 -177
  281. package/src/@core/layouts/components/shared-components/footer/FooterContent.js +0 -46
  282. package/src/@core/layouts/components/shared-components/footer/index.js +0 -61
  283. package/src/@core/layouts/components/vertical/appBar/index.js +0 -74
  284. package/src/@core/layouts/components/vertical/navigation/Drawer.js +0 -122
  285. package/src/@core/layouts/components/vertical/navigation/VerticalNavGroup.js +0 -435
  286. package/src/@core/layouts/components/vertical/navigation/VerticalNavHeader.js +0 -180
  287. package/src/@core/layouts/components/vertical/navigation/VerticalNavItems.js +0 -26
  288. package/src/@core/layouts/components/vertical/navigation/VerticalNavLink.js +0 -258
  289. package/src/@core/layouts/components/vertical/navigation/VerticalNavSectionTitle.js +0 -102
  290. package/src/@core/layouts/components/vertical/navigation/index.js +0 -169
  291. package/src/@core/layouts/utils.js +0 -69
  292. package/src/@core/styles/Table.module.css +0 -93
  293. package/src/@core/styles/horizontal/menuItemStyles.js +0 -100
  294. package/src/@core/styles/horizontal/menuRootStyles.js +0 -19
  295. package/src/@core/styles/libs/fullcalendar/index.js +0 -461
  296. package/src/@core/styles/libs/keen-slider/index.js +0 -111
  297. package/src/@core/styles/libs/react-apexcharts/index.js +0 -107
  298. package/src/@core/styles/libs/react-cleave/index.js +0 -33
  299. package/src/@core/styles/libs/react-credit-cards/index.js +0 -11
  300. package/src/@core/styles/libs/react-datepicker/index.js +0 -388
  301. package/src/@core/styles/libs/react-draft-wysiwyg/index.js +0 -144
  302. package/src/@core/styles/libs/react-dropzone/index.js +0 -76
  303. package/src/@core/styles/libs/react-hot-toast/index.js +0 -37
  304. package/src/@core/styles/libs/recharts/index.js +0 -47
  305. package/src/@core/styles/stepper.js +0 -103
  306. package/src/@core/styles/vertical/menuItemStyles.js +0 -138
  307. package/src/@core/styles/vertical/menuSectionStyles.js +0 -54
  308. package/src/@core/styles/vertical/navigationCustomStyles.js +0 -62
  309. package/src/@core/svg/ContentCompact.jsx +0 -17
  310. package/src/@core/svg/ContentWide.jsx +0 -17
  311. package/src/@core/svg/DirectionLtr.jsx +0 -93
  312. package/src/@core/svg/DirectionRtl.jsx +0 -93
  313. package/src/@core/svg/LayoutCollapsed.jsx +0 -59
  314. package/src/@core/svg/LayoutHorizontal.jsx +0 -42
  315. package/src/@core/svg/LayoutVertical.jsx +0 -59
  316. package/src/@core/svg/Logo.jsx +0 -76
  317. package/src/@core/svg/SkinBordered.jsx +0 -54
  318. package/src/@core/svg/SkinDefault.jsx +0 -59
  319. package/src/@core/tailwind/plugin.js +0 -78
  320. package/src/@core/theme/ThemeComponent.js +0 -63
  321. package/src/@core/theme/ThemeOptions.js +0 -71
  322. package/src/@core/theme/breakpoints/index.js +0 -11
  323. package/src/@core/theme/colorSchemes.js +0 -326
  324. package/src/@core/theme/customShadows.js +0 -11
  325. package/src/@core/theme/globalStyles.js +0 -81
  326. package/src/@core/theme/index.js +0 -42
  327. package/src/@core/theme/overrides/accordion.js +0 -51
  328. package/src/@core/theme/overrides/accordion.jsx +0 -85
  329. package/src/@core/theme/overrides/alerts.js +0 -110
  330. package/src/@core/theme/overrides/alerts.jsx +0 -180
  331. package/src/@core/theme/overrides/autocomplete.js +0 -14
  332. package/src/@core/theme/overrides/autocomplete.jsx +0 -68
  333. package/src/@core/theme/overrides/avatar.js +0 -38
  334. package/src/@core/theme/overrides/avatars.js +0 -27
  335. package/src/@core/theme/overrides/backdrop.js +0 -22
  336. package/src/@core/theme/overrides/badges.js +0 -16
  337. package/src/@core/theme/overrides/breadcrumbs.js +0 -11
  338. package/src/@core/theme/overrides/button-group.js +0 -84
  339. package/src/@core/theme/overrides/button.js +0 -93
  340. package/src/@core/theme/overrides/buttonGroup.js +0 -9
  341. package/src/@core/theme/overrides/card.js +0 -83
  342. package/src/@core/theme/overrides/checkbox.jsx +0 -95
  343. package/src/@core/theme/overrides/chip.js +0 -72
  344. package/src/@core/theme/overrides/dataGrid.js +0 -114
  345. package/src/@core/theme/overrides/dateTimePicker.js +0 -65
  346. package/src/@core/theme/overrides/dialog.js +0 -120
  347. package/src/@core/theme/overrides/divider.js +0 -13
  348. package/src/@core/theme/overrides/drawer.js +0 -20
  349. package/src/@core/theme/overrides/fab.js +0 -13
  350. package/src/@core/theme/overrides/form-control-label.js +0 -19
  351. package/src/@core/theme/overrides/icon-button.js +0 -145
  352. package/src/@core/theme/overrides/index.js +0 -103
  353. package/src/@core/theme/overrides/input.js +0 -72
  354. package/src/@core/theme/overrides/link.js +0 -9
  355. package/src/@core/theme/overrides/list.js +0 -44
  356. package/src/@core/theme/overrides/menu.js +0 -25
  357. package/src/@core/theme/overrides/pagination.js +0 -41
  358. package/src/@core/theme/overrides/paper.js +0 -9
  359. package/src/@core/theme/overrides/popover.js +0 -16
  360. package/src/@core/theme/overrides/progress.js +0 -38
  361. package/src/@core/theme/overrides/radio.jsx +0 -80
  362. package/src/@core/theme/overrides/rating.js +0 -16
  363. package/src/@core/theme/overrides/rating.jsx +0 -32
  364. package/src/@core/theme/overrides/select.js +0 -19
  365. package/src/@core/theme/overrides/select.jsx +0 -52
  366. package/src/@core/theme/overrides/slider.js +0 -97
  367. package/src/@core/theme/overrides/snackbar.js +0 -19
  368. package/src/@core/theme/overrides/switch.js +0 -73
  369. package/src/@core/theme/overrides/switches.js +0 -25
  370. package/src/@core/theme/overrides/table-pagination.js +0 -39
  371. package/src/@core/theme/overrides/table.js +0 -81
  372. package/src/@core/theme/overrides/tabs.js +0 -30
  373. package/src/@core/theme/overrides/timeline.js +0 -80
  374. package/src/@core/theme/overrides/toggle-button.js +0 -33
  375. package/src/@core/theme/overrides/toggleButton.js +0 -16
  376. package/src/@core/theme/overrides/tooltip.js +0 -21
  377. package/src/@core/theme/overrides/typography.js +0 -13
  378. package/src/@core/theme/palette/index.js +0 -107
  379. package/src/@core/theme/shadows/index.js +0 -61
  380. package/src/@core/theme/shadows.js +0 -12
  381. package/src/@core/theme/spacing/index.js +0 -3
  382. package/src/@core/theme/spacing.js +0 -5
  383. package/src/@core/theme/typography/index.js +0 -65
  384. package/src/@core/theme/typography.js +0 -84
  385. package/src/@core/utils/create-emotion-cache.js +0 -5
  386. package/src/@core/utils/hex-to-rgba.js +0 -11
  387. package/src/@core/utils/serverHelpers.js +0 -45
  388. package/src/@menu/components/RouterLink.jsx +0 -18
  389. package/src/@menu/components/horizontal-menu/HorizontalNav.jsx +0 -88
  390. package/src/@menu/components/horizontal-menu/Menu.jsx +0 -83
  391. package/src/@menu/components/horizontal-menu/MenuButton.jsx +0 -100
  392. package/src/@menu/components/horizontal-menu/MenuItem.jsx +0 -183
  393. package/src/@menu/components/horizontal-menu/SubMenu.jsx +0 -418
  394. package/src/@menu/components/horizontal-menu/SubMenuContent.jsx +0 -41
  395. package/src/@menu/components/horizontal-menu/VerticalNavInHorizontal.jsx +0 -20
  396. package/src/@menu/components/vertical-menu/Menu.jsx +0 -161
  397. package/src/@menu/components/vertical-menu/MenuButton.jsx +0 -95
  398. package/src/@menu/components/vertical-menu/MenuItem.jsx +0 -180
  399. package/src/@menu/components/vertical-menu/MenuSection.jsx +0 -124
  400. package/src/@menu/components/vertical-menu/NavCollapseIcons.jsx +0 -70
  401. package/src/@menu/components/vertical-menu/NavHeader.jsx +0 -39
  402. package/src/@menu/components/vertical-menu/SubMenu.jsx +0 -420
  403. package/src/@menu/components/vertical-menu/SubMenuContent.jsx +0 -101
  404. package/src/@menu/components/vertical-menu/VerticalNav.jsx +0 -216
  405. package/src/@menu/contexts/horizontalNavContext.jsx +0 -29
  406. package/src/@menu/contexts/verticalNavContext.jsx +0 -65
  407. package/src/@menu/defaultConfigs.js +0 -12
  408. package/src/@menu/hooks/useHorizontalMenu.jsx +0 -19
  409. package/src/@menu/hooks/useHorizontalNav.jsx +0 -19
  410. package/src/@menu/hooks/useMediaQuery.jsx +0 -29
  411. package/src/@menu/hooks/useVerticalMenu.jsx +0 -19
  412. package/src/@menu/hooks/useVerticalNav.jsx +0 -19
  413. package/src/@menu/horizontal-menu/index.jsx +0 -8
  414. package/src/@menu/styles/StyledBackdrop.jsx +0 -15
  415. package/src/@menu/styles/StyledMenuIcon.jsx +0 -12
  416. package/src/@menu/styles/StyledMenuLabel.jsx +0 -16
  417. package/src/@menu/styles/StyledMenuPrefix.jsx +0 -10
  418. package/src/@menu/styles/StyledMenuSectionLabel.jsx +0 -21
  419. package/src/@menu/styles/StyledMenuSuffix.jsx +0 -10
  420. package/src/@menu/styles/StyledSubMenuContent.jsx +0 -43
  421. package/src/@menu/styles/horizontal/StyledHorizontalMenu.jsx +0 -13
  422. package/src/@menu/styles/horizontal/StyledHorizontalMenuItem.jsx +0 -26
  423. package/src/@menu/styles/horizontal/StyledHorizontalNav.jsx +0 -11
  424. package/src/@menu/styles/horizontal/StyledHorizontalNavExpandIcon.jsx +0 -33
  425. package/src/@menu/styles/horizontal/StyledHorizontalSubMenuContent.jsx +0 -18
  426. package/src/@menu/styles/horizontal/StyledHorizontalSubMenuContentWrapper.jsx +0 -10
  427. package/src/@menu/styles/horizontal/horizontalUl.module.css +0 -15
  428. package/src/@menu/styles/styles.module.css +0 -5
  429. package/src/@menu/styles/vertical/StyledVerticalMenu.jsx +0 -16
  430. package/src/@menu/styles/vertical/StyledVerticalMenuItem.jsx +0 -28
  431. package/src/@menu/styles/vertical/StyledVerticalMenuSection.jsx +0 -23
  432. package/src/@menu/styles/vertical/StyledVerticalNav.jsx +0 -67
  433. package/src/@menu/styles/vertical/StyledVerticalNavBgColorContainer.jsx +0 -15
  434. package/src/@menu/styles/vertical/StyledVerticalNavContainer.jsx +0 -23
  435. package/src/@menu/styles/vertical/StyledVerticalNavExpandIcon.jsx +0 -25
  436. package/src/@menu/styles/vertical/verticalNavBgImage.module.css +0 -10
  437. package/src/@menu/svg/ChevronRight.jsx +0 -9
  438. package/src/@menu/svg/Close.jsx +0 -12
  439. package/src/@menu/svg/RadioCircle.jsx +0 -12
  440. package/src/@menu/svg/RadioCircleMarked.jsx +0 -13
  441. package/src/@menu/utils/menuClasses.js +0 -44
  442. package/src/@menu/utils/menuUtils.jsx +0 -145
  443. package/src/@menu/vertical-menu/index.jsx +0 -11
  444. package/src/configs/acl.js +0 -115
  445. package/src/configs/auth.js +0 -5
  446. package/src/configs/aws-exports.js +0 -30
  447. package/src/configs/firebase.js +0 -25
  448. package/src/configs/i18n.js +0 -34
  449. package/src/configs/primaryColorConfig.js +0 -35
  450. package/src/configs/themeConfig.js +0 -44
  451. package/src/layouts/UserLayout.js +0 -94
  452. package/src/layouts/UserThemeOptions.js +0 -191
  453. package/src/layouts/components/Direction.js +0 -30
  454. package/src/layouts/components/HtmlTooltip.js +0 -15
  455. package/src/layouts/components/Translations.js +0 -11
  456. package/src/layouts/components/UserDropdown.js +0 -217
  457. package/src/layouts/components/UserIcon.js +0 -40
  458. package/src/layouts/components/acl/Can.js +0 -6
  459. package/src/layouts/components/acl/CanViewNavGroup.js +0 -36
  460. package/src/layouts/components/acl/CanViewNavLink.js +0 -17
  461. package/src/layouts/components/acl/CanViewNavSectionTitle.js +0 -17
  462. package/src/layouts/components/horizontal/AppBarContent.js +0 -39
  463. package/src/layouts/components/horizontal/ServerSideNavItems.js +0 -44
  464. package/src/layouts/components/mui/StepperComps.js +0 -55
  465. package/src/layouts/components/vertical/AppBarContent.js +0 -35
  466. package/src/layouts/components/vertical/ServerSideNavItems.js +0 -44
  467. package/src/libs/ApexCharts.jsx +0 -5
  468. package/src/libs/ReactPlayer.jsx +0 -5
  469. package/src/libs/Recharts.jsx +0 -4
  470. package/src/libs/auth.js +0 -124
  471. package/src/libs/styles/AppFullCalendar.js +0 -505
  472. package/src/libs/styles/AppKeenSlider.js +0 -116
  473. package/src/libs/styles/AppReactApexCharts.jsx +0 -110
  474. package/src/libs/styles/AppReactDatepicker.jsx +0 -470
  475. package/src/libs/styles/AppReactDropzone.js +0 -76
  476. package/src/libs/styles/AppReactToastify.jsx +0 -108
  477. package/src/libs/styles/AppRecharts.js +0 -55
  478. package/src/libs/styles/inputOtp.module.css +0 -39
  479. package/src/libs/styles/tiptapEditor.css +0 -72
  480. package/src/navigation/horizontal/index.js +0 -246
  481. package/src/navigation/vertical/index.js +0 -253
  482. package/src/pages/401.js +0 -70
  483. package/src/pages/404.js +0 -67
  484. package/src/pages/500.js +0 -68
  485. package/src/pages/[slug].js +0 -115
  486. package/src/pages/_document.js +0 -72
  487. package/src/pages/api/navigation/regenerate-registry.js +0 -116
  488. package/src/pages/api/navigation/save.js +0 -218
  489. package/src/pages/authModule/acl/index.js +0 -48
  490. package/src/pages/authModule/forgot-password/index.js +0 -228
  491. package/src/pages/authModule/permissions/rolePermissions/[id]/rolePermissionsUser/index.js +0 -392
  492. package/src/pages/authModule/permissions/rolePermissions/index.js +0 -343
  493. package/src/pages/authModule/permissions/systemPermissions/index.js +0 -354
  494. package/src/pages/authModule/privacy/index.js +0 -721
  495. package/src/pages/authModule/users/index.js +0 -210
  496. package/src/pages/login/index.js +0 -328
  497. package/src/pages/mainHome/index.js +0 -181
  498. package/src/views/builder/inspector/definitions/cell/main.js +0 -4
  499. package/src/views/builder/inspector/definitions/column/main.js +0 -9
  500. package/src/views/builder/inspector/definitions/column-group/main.js +0 -18
  501. package/src/views/builder/inspector/definitions/header-cell/main.js +0 -5
  502. package/src/views/builder/inspector/definitions/table/main.js +0 -9
  503. package/src/views/builder/viewer/renderers/CellRenderer.jsx +0 -71
  504. package/src/views/builder/viewer/renderers/ColumnGroupRenderer.jsx +0 -96
  505. package/src/views/builder/viewer/renderers/ColumnRenderer.jsx +0 -71
  506. package/src/views/builder/viewer/renderers/HeaderCellRenderer.jsx +0 -78
  507. package/src/views/builder/viewer/renderers/TabRenderer.jsx +0 -82
  508. package/src/views/builder/viewer/renderers/TableRenderer.jsx +0 -92
  509. package/src/views/pages/auth/FooterIllustrationsV2.js +0 -40
  510. package/src/views/pages/misc/FooterIllustrations.js +0 -47
  511. package/src/views/pages/misc/muiTable/CustomPagination.js +0 -34
  512. package/src/views/pages/users/UserManageDialog.js +0 -283
  513. package/src/views/pages/users/UserViewPage.js +0 -199
  514. package/src/views/users/AddUserNameDialog.js +0 -162
  515. package/src/views/users/ContactManage.js +0 -449
  516. package/src/views/users/ResetPasswordDialog.js +0 -242
@@ -26,7 +26,7 @@
26
26
  * - Adds search term filters based on searchTermRef and searchFieldsRef metadata.
27
27
  * - Translates group/pivot/value columns, sort model, and pagination into the expected backend format.
28
28
  * 3) Data is requested via ReportBuilderEndpoints.Post.GenericGet. Responses may contain rows and aggregations.
29
- * 4) Grid updates, status bar shows counts/aggregations, and export functions can read current viewer state.
29
+ * 4) Grid updates, status bar shows counts/aggregations, and export functions can read current view state.
30
30
  *
31
31
  * Quick search behavior (important)
32
32
  * - searchTermRef holds the current string. If it parses to a finite number, we search numeric (Int) fields with a numeric value.
@@ -58,25 +58,44 @@ import Grid from '@mui/material/Grid'
58
58
  // ** Icons Imports
59
59
  // ** Store Imports
60
60
  // ** Custom Components Imports
61
- import {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'
61
+ import {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'
62
+
63
+ // Global styles for flashing effect
64
+ if (typeof document !== 'undefined') {
65
+ const styleId = 'ag-grid-flashing-effect';
66
+ if (!document.getElementById(styleId)) {
67
+ const style = document.createElement('style');
68
+ style.id = styleId;
69
+ style.innerHTML = `
70
+ @keyframes flashing-bg {
71
+ 0% { opacity: 1; }
72
+ 50% { opacity: 0.5; }
73
+ 100% { opacity: 1; }
74
+ }
75
+ `;
76
+ document.head.appendChild(style);
77
+ }
78
+ }
79
+
62
80
  import {Endpoints, Services} from 'src/services/Endpoints'
63
81
  import {
64
- Autocomplete, Checkbox,
82
+ Autocomplete, Button, Checkbox,
65
83
  CircularProgress,
66
- Dialog,
84
+ Dialog, Divider,
67
85
  FormControl, FormControlLabel,
68
86
  IconButton,
69
87
  MenuItem,
70
88
  Select,
71
89
  TextField,
72
- Tooltip,
90
+ Tooltip, Typography,
73
91
  Chip,
74
92
  Stack,
75
93
  Popper,
76
94
  Paper,
77
95
  List,
78
96
  ListItemButton,
79
- ClickAwayListener
97
+ ClickAwayListener,
98
+ Menu
80
99
  } from '@mui/material'
81
100
  import {AgGridReact} from "ag-grid-react";
82
101
  import {Edit, FilterAlt, PrintOutlined, RefreshOutlined, Save, SaveAs, Close} from "@mui/icons-material";
@@ -101,6 +120,23 @@ import jsPDF from "jspdf";
101
120
  import "jspdf-autotable";
102
121
  import arabicFontBase64 from "../../../public/fonts/font";
103
122
  import {FilterFormat} from "services/helper/FilterFormat";
123
+ import {saveReportSession} from 'src/services/helper/reportSessionHelper'
124
+ import {
125
+ setUpdateRefValue,
126
+ setUpdateRefRow,
127
+ getUpdateRefValue,
128
+ hasUpdateRefValue,
129
+ clearUpdateRefRow,
130
+ clearAllUpdateRef,
131
+ getAllUpdates,
132
+ removeUpdateRefByRowId,
133
+ getRowId as getRowIdFromSettings,
134
+ normalizeUpdateRef,
135
+ cloneUpdateRefToOriginal,
136
+ restoreUpdateRefFromOriginal
137
+ } from './updateRefHelpers'
138
+ import {processColumnDefinitions, processColumnsConfig} from './convertStringFunctions'
139
+ import { AG_COMPONENTS } from 'views/builder/viewer/renderers/dataGridComponents'
104
140
 
105
141
  // ** Utils Import
106
142
 
@@ -113,6 +149,122 @@ const defaultFinalRequest = {
113
149
  filter: {},
114
150
  params: {}
115
151
  }
152
+
153
+ // ── Templates tool panel ──────────────────────────────────────────────────────
154
+ // Defined outside SGrid so the reference is stable (no remounts on parent renders).
155
+ // State flows in via React Context (AG Grid v33 renders custom components in the
156
+ // same React tree, so context is fully supported).
157
+ const TemplateStateContext = createContext(null)
158
+
159
+ function SettingsSection({ title, children }) {
160
+ return (
161
+ <Box style={{ display: 'block', width: '100%' }}>
162
+ <Box sx={{
163
+ px: 2, py: 0.75,
164
+ bgcolor: 'grey.100',
165
+ borderTop: '1px solid', borderBottom: '1px solid', borderColor: 'divider',
166
+ }}>
167
+ <Typography sx={{ fontSize: '0.6875rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.8, color: 'text.secondary' }}>
168
+ {title}
169
+ </Typography>
170
+ </Box>
171
+ <Box sx={{ p: 2, boxSizing: 'border-box', width: '100%' }}>
172
+ {children}
173
+ </Box>
174
+ </Box>
175
+ )
176
+ }
177
+
178
+ function TemplatesToolPanel() {
179
+ const ctx = useContext(TemplateStateContext)
180
+ if (!ctx) return null
181
+ const {
182
+ templates, selectedTemplate, setSelectedTemplate,
183
+ handleGetTemplates, handleSaveTemplate,
184
+ handleToggleDialogs, setIsTemplateEditing,
185
+ builderData,
186
+ } = ctx
187
+
188
+ return (
189
+ <Box style={{ display: 'flex', flexDirection: 'column', height: '100%', width: 250 }}>
190
+ {/* Panel header */}
191
+ <Box sx={{
192
+ px: 2, py: 1.5,
193
+ borderBottom: '1px solid', borderColor: 'divider',
194
+ bgcolor: 'background.paper',
195
+ position: 'sticky', top: 0, zIndex: 1,
196
+ }}>
197
+ <Typography variant='subtitle2' sx={{ fontWeight: 700, color: 'text.primary' }}>
198
+ Settings
199
+ </Typography>
200
+ </Box>
201
+
202
+ {/* Template section */}
203
+ <SettingsSection title='Template'>
204
+ {builderData?.id == null ? (
205
+ <Typography variant='caption' sx={{ color: 'text.disabled' }}>No report loaded</Typography>
206
+ ) : (
207
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: '100%' }}>
208
+ <Autocomplete
209
+ size='small'
210
+ value={selectedTemplate}
211
+ options={templates}
212
+ onChange={(_, value) => setSelectedTemplate(value)}
213
+ getOptionLabel={option => option.name}
214
+ renderInput={params => (
215
+ <TextField
216
+ label='Active Template'
217
+ {...params}
218
+ onMouseDown={handleGetTemplates}
219
+ />
220
+ )}
221
+ />
222
+
223
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, width: '100%' }}>
224
+ <Button
225
+ size='small'
226
+ variant='outlined'
227
+ startIcon={<Save fontSize='small' />}
228
+ disabled={selectedTemplate == null}
229
+ onClick={handleSaveTemplate}
230
+ fullWidth
231
+ sx={{ justifyContent: 'flex-start' }}
232
+ >
233
+ Save
234
+ </Button>
235
+
236
+ <Button
237
+ size='small'
238
+ variant='outlined'
239
+ startIcon={<SaveAs fontSize='small' />}
240
+ onClick={() => { setIsTemplateEditing(false); handleToggleDialogs('addTemplate') }}
241
+ fullWidth
242
+ sx={{ justifyContent: 'flex-start' }}
243
+ >
244
+ Save As New
245
+ </Button>
246
+
247
+ <Button
248
+ size='small'
249
+ variant='outlined'
250
+ startIcon={<Edit fontSize='small' />}
251
+ disabled={selectedTemplate == null}
252
+ onClick={() => { setIsTemplateEditing(true); handleToggleDialogs('addTemplate') }}
253
+ fullWidth
254
+ sx={{ justifyContent: 'flex-start' }}
255
+ >
256
+ Edit
257
+ </Button>
258
+ </Box>
259
+ </Box>
260
+ )}
261
+ </SettingsSection>
262
+ </Box>
263
+ )
264
+ }
265
+
266
+ // ─────────────────────────────────────────────────────────────────────────────
267
+
116
268
  const SGrid = props => {
117
269
  // ** State
118
270
  const appContext = useContext(SystemContext);
@@ -137,6 +289,7 @@ const SGrid = props => {
137
289
  setOutGridApi,
138
290
  setEventRowSelected,
139
291
  pageName,
292
+ pageId,
140
293
  paramsPage,
141
294
  fixedTIncludes,
142
295
  groupBy,
@@ -144,14 +297,26 @@ const SGrid = props => {
144
297
  reportTitle,
145
298
  uniqueIdPath,
146
299
  columnsConfig,
147
- dataAsObject = false
300
+ dataAsObject = false,
301
+ isRerender = false,
302
+ updateRef,
303
+ // nodeId — builder schema node ID for this ReportViewer (set by ReportViewerRenderer)
304
+ // reportRefs — shared registry { [nodeId]: updateRef } for all reports on the page
305
+ nodeId,
306
+ reportRefs,
307
+ // Viewer state — passed through to column string functions via convertStringFunctions ctx
308
+ data: viewerData,
309
+ dataRef: viewerDataRef,
310
+ openDialog,
311
+ closeDialog,
148
312
  } = props
149
313
  const groupEndPoint = ReportBuilderEndpoints.Post.GenericGet;
150
314
  const streamEndPoint = ReportBuilderEndpoints.Post.GenericGet;
151
315
  const pagedEndPoint = ReportBuilderEndpoints.Post.GenericGet;
152
316
  const [responseType, setResponseType] = useState()
153
317
  const [pagedAgg, setPagedAgg] = useState()
154
- const [colDefs, setColDefs] = useState([])
318
+ const colDefs = useRef([])
319
+ const originalRefData = useRef([])
155
320
  const [gridApi, setGridApi] = useState(null)
156
321
  const [includes, setIncludes] = useState([])
157
322
  const [isPagination, setIsPagination] = useState(true)
@@ -163,6 +328,7 @@ const SGrid = props => {
163
328
  const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
164
329
  const router = useRouter()
165
330
  const authValues = useContext(AuthContext)
331
+ const contextValues = useContext(SystemContext)
166
332
  const [isTemplateEditing, setIsTemplateEditing] = useState(false)
167
333
  const [isDownloading, setIsDownloading] = useState(false)
168
334
  const [finalRequestObject, setFinalRequestObject] = useState(defaultFinalRequest)
@@ -191,6 +357,87 @@ const SGrid = props => {
191
357
  Tfilter: [],
192
358
  LocalTfilter: [],
193
359
  })
360
+
361
+ // Persistence Key helper
362
+ const getSessionKey = useCallback(() => {
363
+ const reportId = builderData?.id || builderModel?.id;
364
+ const pId = pageId || router.query?.pageId;
365
+ if (pId) return `RB_SESSION_FILTERS_PAGE_${pId}`;
366
+ if (reportId) return `RB_SESSION_FILTERS_REP_${reportId}`;
367
+ return null;
368
+ }, [builderData?.id, builderModel?.id, pageId, router.query?.pageId]);
369
+
370
+ // Clone updateRef to originalRefData when it first gets data
371
+ useEffect(() => {
372
+ if (updateRef && updateRef.current && Array.isArray(updateRef.current) && updateRef.current.length > 0) {
373
+ // Only clone if originalRefData is empty (first time updateRef has data)
374
+ if (!originalRefData.current || originalRefData.current.length === 0) {
375
+ cloneUpdateRefToOriginal(updateRef, originalRefData);
376
+ }
377
+ }
378
+ }, [updateRef?.current?.length]); // Watch for when updateRef gets data
379
+
380
+ // Load filters from sessionStorage on mount
381
+ useEffect(() => {
382
+ const key = getSessionKey();
383
+ if (!key) return;
384
+
385
+ try {
386
+ // Check if it's a refresh
387
+ let isRefresh = false;
388
+ try {
389
+ const navEntries = performance.getEntriesByType('navigation');
390
+ isRefresh = navEntries.length > 0 && navEntries[0].type === 'reload';
391
+ } catch (e) {
392
+ console.warn('SGrid: performance.getEntriesByType not supported', e);
393
+ }
394
+
395
+ if (isRefresh) {
396
+ sessionStorage.removeItem(key);
397
+ return;
398
+ }
399
+
400
+ const stored = sessionStorage.getItem(key);
401
+ if (stored) {
402
+ // If externalFilter is provided via props (routing payload), it should probably take precedence
403
+ // but let's see if it's actually passed. In ReportViewer, it's passed as 'filter' prop.
404
+ if (externalFilter) {
405
+ return;
406
+ }
407
+
408
+ const parsed = JSON.parse(stored);
409
+
410
+ if (parsed.Filter) setFilter(parsed.Filter);
411
+ if (parsed.selectedSearchObjects) setSelectedSearchObjects(parsed.selectedSearchObjects);
412
+ if (parsed.selectParams) setSelectParams(parsed.selectParams);
413
+ }
414
+ } catch (e) {
415
+ console.error('SGrid: Failed to restore session filters', e);
416
+ }
417
+ }, [getSessionKey, externalFilter]);
418
+
419
+ // Save filters to sessionStorage whenever they change
420
+ useEffect(() => {
421
+ const key = getSessionKey();
422
+ if (!key) return;
423
+
424
+ const timeout = setTimeout(() => {
425
+ try {
426
+ const toStore = {
427
+ Filter,
428
+ selectedSearchObjects,
429
+ selectParams
430
+ };
431
+ // Always store current state. If user cleared all filters, we want to remember it's empty in this session.
432
+ // We only avoid storing if the state is exactly the default/initial state and we haven't modified it yet.
433
+ sessionStorage.setItem(key, JSON.stringify(toStore));
434
+ } catch (e) {
435
+ console.error('SGrid: Failed to save session filters', e);
436
+ }
437
+ }, 1000); // debounce saving
438
+
439
+ return () => clearTimeout(timeout);
440
+ }, [Filter, selectedSearchObjects, selectParams, getSessionKey]);
194
441
  const [openDialogs, setOpenDialogs] = useState({
195
442
  addTemplate: false,
196
443
  CustomFilter: false,
@@ -277,7 +524,7 @@ const SGrid = props => {
277
524
  if (c.colId === "IZ_groupCount") {
278
525
  return {field: "IZ_groupCount", headerName: "Group Count"};
279
526
  }
280
- return colDefs.find(cd => cd.field === c.colId);
527
+ return colDefs.current.find(cd => cd.field === c.colId);
281
528
  })
282
529
  .filter(Boolean);
283
530
 
@@ -552,7 +799,15 @@ const SGrid = props => {
552
799
  async function handleResetColsToStudio() {
553
800
  try {
554
801
  setBuilderTFilter(builderData?.filter?.Tfilter ?? [])
555
- console.log(builderModel)
802
+ setFilter(pre => ({
803
+ ...pre,
804
+ LocalTfilter: [
805
+ ...(builderData?.filter?.fixedTFilter || []).map(f => ({...f})),
806
+ ...(builderData?.filter?.LocalTfilter || []).map(f => ({...f}))
807
+ ],
808
+ Tfilter: builderData?.filter?.Tfilter || [],
809
+ customFilterCode: builderData?.filter?.customFilterCode || ''
810
+ }))
556
811
  const allowedAggFuncs = ["sum", "avg", "min", "max"];
557
812
  const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
558
813
  let defaultCols = getDefaultColDefs()
@@ -567,7 +822,8 @@ const SGrid = props => {
567
822
  const propertyType = col.propertyType ?? col.field?.propertyType;
568
823
  const propPath = col.headerName ?? col.path;
569
824
  const fieldName = col.headerName ?? col.path.replace(/\./g, '_');
570
- const externalColConfig = (columnsConfig ?? []).find(x => x.field === fieldName)?.config ?? {}
825
+ const processedColumnsConfig = processColumnsConfig(columnsConfig ?? [], { reportRefs, nodeName: nodeId, data: viewerData, dataRef: viewerDataRef, setData, openDialog, closeDialog });
826
+ const externalColConfig = processedColumnsConfig.find(x => x.field === fieldName)?.config ?? {}
571
827
  const headerName = col.title ?? col.headerName ?? col.path;
572
828
  const hasRouting = routingSettings.some(r => r.fieldName === fieldName);
573
829
  let valueFormatter = null;
@@ -583,8 +839,15 @@ const SGrid = props => {
583
839
  }
584
840
 
585
841
  if (propertyType === 'Double') {
842
+ const decimals = col.formatting?.decimals ?? 0;
843
+ valueFormatter =
844
+ `return params?.value != null ? numeral(params.value.toFixed(${decimals})).format('0,0.${'0'.repeat(decimals)}') : ''`;
845
+ }
846
+
847
+ if (propertyType === 'Int' && col.formatting?.decimals > 0) {
848
+ const decimals = col.formatting.decimals;
586
849
  valueFormatter =
587
- `return params?.value != null ? numeral(params.value.toFixed(2)).format('0,0') : ''`;
850
+ `return params?.value != null ? numeral(params.value.toFixed(${decimals})).format('0,0.${'0'.repeat(decimals)}') : ''`;
588
851
  }
589
852
 
590
853
  if (propertyType === 'Enum') {
@@ -593,7 +856,8 @@ const SGrid = props => {
593
856
  `return params?.value != null ? enumTrans('${enumName}', params.value) : ''`;
594
857
  }
595
858
 
596
- const isNumericAgg = (propertyType === 'Int' || propertyType === 'Double') &&
859
+ const isNumeric = propertyType === 'Int' || propertyType === 'Double';
860
+ const isNumericAgg = isNumeric &&
597
861
  !col.field?.path?.toLowerCase().endsWith('id');
598
862
 
599
863
  const isImage = col.image === true;
@@ -608,7 +872,173 @@ const SGrid = props => {
608
872
  ? "agCheckboxCellRenderer"
609
873
  : null)),
610
874
  width: isImage ? 90 : undefined,
611
- cellStyle: isImage ? {display: 'flex', alignItems: 'center', justifyContent: 'center'} : undefined,
875
+ cellStyle: (params) => {
876
+ let style = isImage ? {display: 'flex', alignItems: 'center', justifyContent: 'center'} : {};
877
+ const formatting = col.formatting;
878
+ if (formatting) {
879
+ if (formatting.fixedStyle) {
880
+ style = {...style, ...formatting.fixedStyle};
881
+ if (formatting.fixedStyle.flashing) {
882
+ style.animation = 'flashing-bg 1s infinite';
883
+ }
884
+ }
885
+ if (formatting.conditions && formatting.conditions.length > 0) {
886
+ const val = params.value;
887
+ for (const cond of formatting.conditions) {
888
+ let match = false;
889
+ const baseField = cond.baseField || 'self';
890
+ let currentVal;
891
+ if (baseField === 'self') {
892
+ currentVal = val;
893
+ } else {
894
+ // Resolve field path from data
895
+ // First try as is
896
+ currentVal = params.node.data[baseField];
897
+
898
+ // If not found and contains dots, try nested path
899
+ if ((currentVal === undefined || currentVal === null) && baseField.includes('.')) {
900
+ currentVal = baseField.split('.').reduce((obj, key) => (obj && obj[key] !== undefined) ? obj[key] : undefined, params.node.data);
901
+ }
902
+ }
903
+
904
+ const isNumericValue = typeof currentVal === 'number' || (!isNaN(parseFloat(currentVal)) && isFinite(currentVal));
905
+ let condVal;
906
+ if (cond.valueType === 'field' && cond.compareField) {
907
+ const compareField = cond.compareField;
908
+ // Resolve field path from data
909
+ // First try as is
910
+ condVal = params.node.data[compareField];
911
+
912
+ // If not found and contains dots, try nested path
913
+ if ((condVal === undefined || condVal === null) && compareField.includes('.')) {
914
+ condVal = compareField.split('.').reduce((obj, key) => (obj && obj[key] !== undefined) ? obj[key] : undefined, params.node.data);
915
+ }
916
+ } else if (cond.valueType === 'expression' && cond.expression) {
917
+ try {
918
+ // Math Expression evaluation
919
+ // Replace "FieldPath" with actual values from row data
920
+ let expr = cond.expression;
921
+ const fieldRegex = /"([^"]+)"/g;
922
+ let match;
923
+ let hasMissingValue = false;
924
+ const fieldValues = {};
925
+
926
+ // Find all fields in quotes
927
+ while ((match = fieldRegex.exec(cond.expression)) !== null) {
928
+ const fullMatch = match[0];
929
+ const path = match[1];
930
+ let val;
931
+ let fieldFound = false;
932
+
933
+ if (path === 'self') {
934
+ val = params.value;
935
+ fieldFound = true;
936
+ } else {
937
+ // Resolve field path from data
938
+ // First try as is (handles fieldName with underscores)
939
+ val = params.node.data[path];
940
+
941
+ if (val !== undefined) {
942
+ fieldFound = true;
943
+ } else if (path.includes('.')) {
944
+ // If not found and contains dots, try nested path
945
+ val = path.split('.').reduce((obj, key) => (obj && obj[key] !== undefined) ? obj[key] : undefined, params.node.data);
946
+ if (val !== undefined) {
947
+ fieldFound = true;
948
+ }
949
+ }
950
+ }
951
+
952
+ if (fieldFound) {
953
+ // If it's a string that is not a number, wrap it in single quotes to make it a string literal in the expression
954
+ // unless it's already a number
955
+ if (typeof val === 'string' && isNaN(parseFloat(val))) {
956
+ fieldValues[fullMatch] = `'${val.replace(/'/g, "\\'")}'`;
957
+ } else if (typeof val === 'number') {
958
+ fieldValues[fullMatch] = val;
959
+ } else if (!isNaN(parseFloat(val)) && isFinite(val)) {
960
+ fieldValues[fullMatch] = parseFloat(val);
961
+ } else if (val === null) {
962
+ fieldValues[fullMatch] = 'null';
963
+ } else if (typeof val === 'boolean') {
964
+ fieldValues[fullMatch] = val;
965
+ } else {
966
+ // Default to string if we can't be sure
967
+ fieldValues[fullMatch] = `'${String(val).replace(/'/g, "\\'")}'`;
968
+ }
969
+ }
970
+ }
971
+
972
+ // Replace fields with their values
973
+ // Sort keys by length descending to avoid partial replacements (e.g., "Field" before "FieldLonger")
974
+ const sortedKeys = Object.keys(fieldValues).sort((a, b) => b.length - a.length);
975
+ sortedKeys.forEach(key => {
976
+ // Escape special characters in key for RegExp
977
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
978
+ expr = expr.replace(new RegExp(escapedKey, 'g'), fieldValues[key]);
979
+ });
980
+
981
+ // Use Function constructor for evaluation
982
+ // Strip potentially dangerous characters while allowing math, comparison, ternary, and basic logical operators
983
+ // We allow: numbers, operators (+-*/), comparison (><=!|&), ternary (?:), parens, dots, spaces, commas, and some math words
984
+ // Updated to allow keywords for if and switch statements
985
+ // Also added ' for string literals
986
+ const safeExpr = expr.replace(/[^0-9+\-*/().\s><=!|&?:,;{}'a-zA-Z\\]/g, '');
987
+ // Check if it already contains 'return', otherwise wrap it if it looks like a simple expression
988
+ const finalCode = safeExpr.trim().startsWith('if') || safeExpr.trim().startsWith('switch') || safeExpr.includes('return')
989
+ ? safeExpr
990
+ : `return ${safeExpr}`;
991
+ condVal = new Function(finalCode)();
992
+ } catch (e) {
993
+ console.error("Math expression evaluation failed:", e);
994
+ condVal = undefined;
995
+ }
996
+ } else {
997
+ condVal = cond.value;
998
+ }
999
+
1000
+ const isCondNumeric = typeof condVal === 'number' || (!isNaN(parseFloat(condVal)) && isFinite(condVal));
1001
+ const finalCondVal = isCondNumeric ? parseFloat(condVal) : condVal;
1002
+ const compareVal = isNumericValue ? parseFloat(currentVal) : currentVal;
1003
+
1004
+ if (cond.valueType === 'expression') {
1005
+ match = !!condVal;
1006
+ } else {
1007
+ switch (cond.operator) {
1008
+ case '==':
1009
+ match = compareVal == finalCondVal;
1010
+ break;
1011
+ case '!=':
1012
+ match = compareVal != finalCondVal;
1013
+ break;
1014
+ case '>':
1015
+ match = compareVal > finalCondVal;
1016
+ break;
1017
+ case '<':
1018
+ match = compareVal < finalCondVal;
1019
+ break;
1020
+ case '>=':
1021
+ match = compareVal >= finalCondVal;
1022
+ break;
1023
+ case '<=':
1024
+ match = compareVal <= finalCondVal;
1025
+ break;
1026
+ case 'contains':
1027
+ match = String(compareVal).toLowerCase().includes(String(finalCondVal).toLowerCase());
1028
+ break;
1029
+ }
1030
+ }
1031
+ if (match) {
1032
+ style = {...style, ...cond.style};
1033
+ if (cond.style.flashing) {
1034
+ style.animation = 'flashing-bg 1s infinite';
1035
+ }
1036
+ }
1037
+ }
1038
+ }
1039
+ }
1040
+ return style;
1041
+ },
612
1042
  allowedAggFuncs: isNumericAgg ? allowedAggFuncs : [],
613
1043
  valueFormatter: valueFormatter
614
1044
  ? applyValueFormatter(valueFormatter)
@@ -655,10 +1085,11 @@ const SGrid = props => {
655
1085
  }))
656
1086
  setSelectParams(localSelectParams);
657
1087
  if (extraCols != null) {
658
- localColDefs = [...extraCols, ...localColDefs];
1088
+ const processedExtraCols = processColumnDefinitions(extraCols, { reportRefs, nodeName: nodeId, data: viewerData, dataRef: viewerDataRef, setData, openDialog, closeDialog });
1089
+ localColDefs = [...processedExtraCols, ...localColDefs];
659
1090
  }
660
1091
  localColDefs = [...localColDefs, ...defaultCols];
661
- setColDefs([
1092
+ colDefs.current = ([
662
1093
  {
663
1094
  field: 'IZ_groupCount',
664
1095
  headerName: 'Group Count',
@@ -705,7 +1136,8 @@ const SGrid = props => {
705
1136
  tableCols = processColumns(tableCols)
706
1137
  let localColDefs = [...tableCols, ...defaultCols]
707
1138
  if (extraCols != null) {
708
- localColDefs = [...extraCols, ...localColDefs]
1139
+ const processedExtraCols = processColumnDefinitions(extraCols, { reportRefs, nodeName: nodeId, data: viewerData, dataRef: viewerDataRef, setData, openDialog, closeDialog });
1140
+ localColDefs = [...processedExtraCols, ...localColDefs]
709
1141
  }
710
1142
  const localSelectTFilter = response.data.map(x => ({
711
1143
  friendlyName: x.fieldName,
@@ -714,9 +1146,9 @@ const SGrid = props => {
714
1146
  propertyType: x.type,
715
1147
  }))
716
1148
  setSelectTFilter(localSelectTFilter)
717
- setColDefs(localColDefs)
1149
+ colDefs.current = localColDefs
718
1150
  } else {
719
- setColDefs([])
1151
+ colDefs.current = []
720
1152
  }
721
1153
  } catch (error) {
722
1154
 
@@ -753,92 +1185,161 @@ const SGrid = props => {
753
1185
  const {tRouting} = context;
754
1186
  const fieldName = colDef.field;
755
1187
 
756
- const routing = tRouting?.find(r => r.fieldName === fieldName || r.headerName === fieldName);
757
- if (!routing) return <span>{value}</span>;
1188
+ const [anchorEl, setAnchorEl] = useState(null);
1189
+ const open = Boolean(anchorEl);
758
1190
 
759
- // Determine behavior based on routing.type
760
- const type = routing.type || 'Route';
1191
+ const routes = tRouting?.filter(r => r.fieldName === fieldName || r.headerName === fieldName) || [];
1192
+ if (routes.length === 0) return <span>{value}</span>;
761
1193
 
762
- // Build finalRoute for Route type
763
- let finalRoute = null;
764
- if (type === 'Route') {
765
- if (routing.script) {
766
- try {
767
- const fn = new Function("row", routing.script);
768
- finalRoute = fn(data);
769
- } catch (err) {
770
- console.error("Routing script error:", err);
771
- finalRoute = "#";
772
- }
773
- } else if (routing.pattern) {
774
- finalRoute = routing.pattern.replace(/\{\{(.*?)\}\}/g, (_, key) => {
775
- const val = data?.[key];
776
- return val != null ? val : `:${key}`;
777
- });
778
- }
779
- }
1194
+ const handleRouteClick = (routing) => {
1195
+ setAnchorEl(null);
1196
+ // Determine behavior based on routing.type
1197
+ const type = routing.type || 'Route';
780
1198
 
781
- const handleClick = () => {
782
1199
  if (type === 'Filter') {
783
1200
  try {
784
1201
  // Prepare payload to pass via window.name (works across new tabs without session)
785
- const raw = routing.externalFilterJson || '';
786
- const replaced = raw.replace(/\{\{(.*?)\}\}/g, (_, key) => {
787
- const v = data?.[key];
788
- return v != null ? String(v) : '';
789
- });
790
1202
  let externalFilter = null;
791
- try {
792
- externalFilter = replaced ? JSON.parse(replaced) : null;
793
- } catch (e) {
794
- console.error('Invalid externalFilterJson after replacement', e);
1203
+
1204
+ if (routing.customFilterCode) {
1205
+ try {
1206
+ // Create function with access to authContext (if needed) and row data
1207
+ const executeCode = new Function('context', 'authContext', 'row', `
1208
+ let customFilter = [];
1209
+ try {
1210
+ ${routing.customFilterCode}
1211
+ } catch (e) {
1212
+ console.error('Inner error in routing customFilterCode:', e);
1213
+ }
1214
+ return customFilter;
1215
+ `);
1216
+ const customFilterResult = executeCode(contextValues, authValues, data);
1217
+
1218
+ if (customFilterResult && Array.isArray(customFilterResult)) {
1219
+ externalFilter = {Tfilter: FilterFormat(customFilterResult)};
1220
+ } else if (customFilterResult && typeof customFilterResult === 'object') {
1221
+ // If it returned { Tfilter: [...] } directly
1222
+ externalFilter = customFilterResult;
1223
+ }
1224
+ } catch (error) {
1225
+ console.error('Error executing routing custom filter code:', error);
1226
+ }
1227
+ }
1228
+
1229
+ if (!externalFilter) {
1230
+ const raw = routing.externalFilterJson || '';
1231
+ const replaced = raw.replace(/\{\{(.*?)\}\}/g, (_, key) => {
1232
+ const v = data?.[key];
1233
+ return v != null ? String(v) : '';
1234
+ });
1235
+ try {
1236
+ externalFilter = replaced ? JSON.parse(replaced) : null;
1237
+ } catch (e) {
1238
+ console.error('Invalid externalFilterJson after replacement', e);
1239
+ }
1240
+ }
1241
+
1242
+ if (externalFilter && routing.customFilterCode) {
1243
+ externalFilter.customFilterCode = routing.customFilterCode;
795
1244
  }
796
1245
 
797
1246
  const builderId = context?.builderId;
1247
+ const targetPageId = routing.pageId;
798
1248
  const payload = {
799
- pageId: routing.pageId ?? null,
1249
+ pageId: targetPageId ?? null,
800
1250
  externalFilter,
801
- id: builderId ?? null
1251
+ id: targetPageId ? null : (builderId ?? null)
802
1252
  };
803
1253
 
804
- const base = '/reportBuilder/reportViewer';
805
- const url = builderId ? `${base}?id=${encodeURIComponent(builderId)}&useWindowName=1` : `${base}?useWindowName=1`;
806
- // Open the new tab and set its window.name with a prefixed base64 JSON payload
807
- const child = window.open(url, "_blank"); // do not use noopener because we need to set name
808
- if (child) {
809
- try {
1254
+ const base = '/builders/report/viewer';
1255
+ let url = base;
1256
+ if (targetPageId) {
1257
+ url += `?pageId=${encodeURIComponent(targetPageId)}`;
1258
+ } else if (builderId) {
1259
+ url += `?id=${encodeURIComponent(builderId)}`;
1260
+ }
1261
+
1262
+ try {
1263
+ const sessionId = saveReportSession(payload);
1264
+ if (sessionId) {
1265
+ url += (url.includes('?') ? '&' : '?') + `sessionId=${sessionId}`;
1266
+ } else {
1267
+ // fallback to session storage if localStorage fails
810
1268
  const json = JSON.stringify(payload);
811
- const b64 = typeof btoa !== 'undefined' ? btoa(unescape(encodeURIComponent(json))) : json;
812
- child.name = `RB:${b64}`;
813
- } catch (e) {
814
- // as a fallback, put minimal info in URL (may be long for large filters)
815
- try {
816
- const fallback = encodeURIComponent(JSON.stringify(payload));
817
- child.location.href = builderId ? `${base}?id=${encodeURIComponent(builderId)}&payload=${fallback}` : `${base}?payload=${fallback}`;
818
- } catch {}
1269
+ sessionStorage.setItem('RB_PAYLOAD', json);
819
1270
  }
1271
+ } catch (e) {
1272
+ console.error('Failed to set session payload', e);
820
1273
  }
1274
+ router.push(url).then(() => {
1275
+ });
821
1276
  } catch (e) {
822
1277
  console.error('Filter routing error', e);
823
1278
  }
824
1279
  } else {
1280
+ // Build finalRoute for Route type
1281
+ let finalRoute = null;
1282
+ if (routing.script) {
1283
+ try {
1284
+ const fn = new Function("row", routing.script);
1285
+ finalRoute = fn(data);
1286
+ } catch (err) {
1287
+ console.error("Routing script error:", err);
1288
+ finalRoute = "#";
1289
+ }
1290
+ } else if (routing.pattern) {
1291
+ finalRoute = routing.pattern.replace(/\{\{(.*?)\}\}/g, (_, key) => {
1292
+ const val = data?.[key];
1293
+ return val != null ? val : `:${key}`;
1294
+ });
1295
+ }
1296
+
825
1297
  if (finalRoute && finalRoute !== '#') {
826
- window.open(finalRoute, "_blank", "noopener,noreferrer");
1298
+ const target = routing.target || '_self';
1299
+
1300
+ if (target === '_blank' || target === '_parent' || target === '_top') {
1301
+ // Open in new tab/window or specific frame
1302
+ window.open(finalRoute, target);
1303
+ } else {
1304
+ // Default: navigate in same tab using Next.js router
1305
+ router.push(finalRoute);
1306
+ }
827
1307
  }
828
1308
  }
829
1309
  };
830
1310
 
1311
+ const handleClick = (event) => {
1312
+ if (routes.length > 1) {
1313
+ setAnchorEl(event.currentTarget);
1314
+ } else {
1315
+ handleRouteClick(routes[0]);
1316
+ }
1317
+ };
1318
+
831
1319
  return (
832
- <span
833
- style={{
834
- color: '#1976d2',
835
- fontWeight: 600,
836
- cursor: 'pointer',
837
- }}
838
- onClick={handleClick}
839
- >
840
- {value}
841
- </span>
1320
+ <>
1321
+ <span
1322
+ style={{
1323
+ color: '#1976d2',
1324
+ fontWeight: 600,
1325
+ cursor: 'pointer',
1326
+ }}
1327
+ onClick={handleClick}
1328
+ >
1329
+ {value}
1330
+ </span>
1331
+ <Menu
1332
+ anchorEl={anchorEl}
1333
+ open={open}
1334
+ onClose={() => setAnchorEl(null)}
1335
+ >
1336
+ {routes.map((r, idx) => (
1337
+ <MenuItem key={idx} onClick={() => handleRouteClick(r)}>
1338
+ {r.name || `Route ${idx + 1}`}
1339
+ </MenuItem>
1340
+ ))}
1341
+ </Menu>
1342
+ </>
842
1343
  );
843
1344
  };
844
1345
 
@@ -926,26 +1427,35 @@ const SGrid = props => {
926
1427
  }
927
1428
  }
928
1429
 
929
- const sideBarConfig = useMemo(() => ({
930
- toolPanels: [
931
- {
932
- id: 'columns',
933
- labelDefault: 'Columns',
934
- labelKey: 'columns',
935
- iconKey: 'columns',
936
- toolPanel: 'agColumnsToolPanel',
937
- toolPanelParams: {
938
- suppressRowGroups: groupBy != null,
939
- suppressValues: groupBy != null,
1430
+ const sideBarConfig = useMemo(
1431
+ () => ({
1432
+ toolPanels: [
1433
+ {
1434
+ id: 'columns',
1435
+ labelDefault: 'Columns',
1436
+ labelKey: 'columns',
1437
+ iconKey: 'columns',
1438
+ toolPanel: 'agColumnsToolPanel',
1439
+ toolPanelParams: {
1440
+ suppressRowGroups: groupBy != null,
1441
+ suppressValues: groupBy != null
1442
+ },
1443
+ minWidth: 225
940
1444
  },
941
- minWidth: 225,
942
- },
943
-
944
- ],
945
- position: 'left',
946
- // ← optionally open this panel by default on init
947
- defaultToolPanel: 'columns',
948
- }), [groupBy]); // only re-create if groupBy changes
1445
+ {
1446
+ id: 'templates',
1447
+ labelDefault: 'Templates',
1448
+ labelKey: 'templates',
1449
+ iconKey: 'menu',
1450
+ toolPanel: 'templatesToolPanel',
1451
+ minWidth: 250,
1452
+ }
1453
+ ],
1454
+ position: 'right',
1455
+ defaultToolPanel: '' // closed by default
1456
+ }),
1457
+ [groupBy]
1458
+ ) // only re-create if groupBy changes
949
1459
  const handleGetAggregationObject = (agg) => {
950
1460
 
951
1461
  const matchedCol = builderData.selectedFields.find(col => {
@@ -1059,13 +1569,30 @@ const SGrid = props => {
1059
1569
  setFinalRequestObject(defaultFinalRequest)
1060
1570
  const fixedTFilter = externalFilter?.fixedTFilter ?? externalFilter?.fixedTfilter ?? []
1061
1571
  const localFixedTFilter = Filter?.fixedTFilter ?? []
1062
- let localFilter = externalFilter ? {...externalFilter} : {}
1063
- localFilter = {...localFilter, ...Filter}
1064
-
1065
- let localTFilter = localFilter?.Tfilter ? [...localFilter?.Tfilter] : []
1066
- localTFilter = localFilter?.TFilter ? [...localTFilter, ...localFilter?.TFilter] : [...localTFilter]
1572
+ let localFilter = externalFilter && !Array.isArray(externalFilter) ? {...externalFilter} : {}
1573
+ // Merge with Filter but be careful not to overwrite Tfilter/TFilter with empty ones
1574
+ const {Tfilter: _tf, TFilter: _tF, customFilterCode: _cfc, ...otherInternalFilter} = Filter || {}
1575
+ localFilter = {...localFilter, ...otherInternalFilter}
1576
+
1577
+ let localTFilter = []
1578
+ if (externalFilter?.Tfilter) localTFilter.push(...externalFilter.Tfilter)
1579
+ if (externalFilter?.TFilter) localTFilter.push(...externalFilter.TFilter)
1580
+ if (Filter?.Tfilter) localTFilter.push(...Filter.Tfilter)
1581
+ if (Filter?.TFilter) localTFilter.push(...Filter.TFilter)
1582
+
1583
+ // Also support LocalTfilter (this is where CustomFilterDialog often stores its filters)
1584
+ if (Filter?.LocalTfilter) localTFilter.push(...Filter.LocalTfilter)
1585
+ if (externalFilter?.LocalTfilter) localTFilter.push(...externalFilter.LocalTfilter)
1586
+
1587
+ // Support for routing-passed externalFilter that might be directly inside externalFilter (not under Tfilter)
1588
+ if (Array.isArray(externalFilter) && localTFilter.length === 0) {
1589
+ localTFilter = [...externalFilter];
1590
+ }
1067
1591
  localTFilter = [...localTFilter, ...fixedTFilter, ...localFixedTFilter, ...builderTFilter]
1068
1592
 
1593
+ // Extract customFilterCode from externalFilter if present (e.g. from routing)
1594
+ let currentCustomFilterCode = Filter?.customFilterCode || externalFilter?.customFilterCode || "";
1595
+
1069
1596
  // Apply explicit user-selected search filters (selectedSearchObjects), grouped by path
1070
1597
  if ((selectedSearchObjects?.length ?? 0) > 0) {
1071
1598
  const byPath = selectedSearchObjects.reduce((acc, item) => {
@@ -1099,8 +1626,6 @@ const SGrid = props => {
1099
1626
  localTFilter = [...localTFilter, ...grouped];
1100
1627
  }
1101
1628
 
1102
- localTFilter = localTFilter.filter(x => x.value !== null && x.value !== undefined && x.value !== '')
1103
-
1104
1629
  delete localFilter.Tfilter
1105
1630
  delete localFilter.fixedTfilter
1106
1631
  delete localFilter.fixedTFilter
@@ -1112,8 +1637,8 @@ const SGrid = props => {
1112
1637
  const pageSize = request.endRow - request.startRow // Calculate the page size
1113
1638
  let aggregators = request.valueCols.filter(x => x.id != 'groupCount').map(agg => handleGetAggregationObject(agg))
1114
1639
  let updateAggregation = true
1115
- if (colDefs && gridApi) {
1116
- let resultColDef = [...colDefs]
1640
+ if (colDefs.current && gridApi) {
1641
+ let resultColDef = [...colDefs.current]
1117
1642
  if (request.rowGroupCols.length > 0) {
1118
1643
  }
1119
1644
  // gridApi.setGridOption('columnDefs', resultColDef)
@@ -1121,7 +1646,7 @@ const SGrid = props => {
1121
1646
  }
1122
1647
  // Build tFilter safely: remove fixed from preformatted lists and merge a single fixed source
1123
1648
  // 1) Strip fixed items from local Tfilter/TFilter (they may already be formatted by CustomFilterDialog)
1124
- let nonFixedLocal = Array.isArray(localTFilter) ? localTFilter.filter(it => !(it && it.fixed === true && it.value === null)) : []
1649
+ let nonFixedLocal = Array.isArray(localTFilter) ? localTFilter.filter(it => it != null && !(it.fixed === true && it.value === null)) : []
1125
1650
 
1126
1651
  // 2) Choose ONE fixed source by priority: component state -> external -> builder
1127
1652
  const extFixed = Array.isArray(externalFilter?.fixedTFilter)
@@ -1129,12 +1654,80 @@ const SGrid = props => {
1129
1654
  : (Array.isArray(externalFilter?.fixedTfilter) ? externalFilter.fixedTfilter : [])
1130
1655
  const fixedSource = (Array.isArray(Filter?.fixedTFilter) && Filter.fixedTFilter.length > 0)
1131
1656
  ? Filter.fixedTFilter
1132
- : (extFixed.length > 0 ? extFixed : (Array.isArray(builderTFilter) ? builderTFilter : []))
1657
+ : (extFixed.length > 0 ? extFixed : (Array.isArray(builderTFilter) && builderTFilter.length > 0 ? builderTFilter : []))
1133
1658
 
1134
1659
  // 3) Merge and format once
1135
- const mergedForFormat = [...nonFixedLocal]
1136
- console.log("Merged for format:", mergedForFormat)
1137
- let formattedT = FilterFormat(mergedForFormat)
1660
+ const mergedForFormat = [...nonFixedLocal, ...fixedSource]
1661
+ let formattedT = FilterFormat(mergedForFormat).filter(x => !x?.isCustom)
1662
+
1663
+ // Dynamic placeholder replacement for authValues
1664
+ if (formattedT && Array.isArray(formattedT)) {
1665
+ formattedT = formattedT.map(f => {
1666
+ if (typeof f.value === 'string' && f.value.includes('{{auth.')) {
1667
+ const replaced = String(f.value).replace(/\{\{auth\.(.*?)\}\}/g, (_, path) => {
1668
+ return path.split('.').reduce((acc, key) => acc && acc[key], authValues) ?? '';
1669
+ });
1670
+ return {...f, value: replaced};
1671
+ }
1672
+ return f;
1673
+ });
1674
+ }
1675
+
1676
+ // Dynamic placeholder replacement for authValues
1677
+ if (formattedT && Array.isArray(formattedT)) {
1678
+ formattedT = formattedT.map(f => {
1679
+ if (typeof f.value === 'string' && f.value.includes('{{context.')) {
1680
+ const replaced = String(f.value).replace(/\{\{context\.(.*?)\}\}/g, (_, path) => {
1681
+ return path.split('.').reduce((acc, key) => acc && acc[key], contextValues) ?? '';
1682
+ });
1683
+ return {...f, value: replaced};
1684
+ }
1685
+ return f;
1686
+ });
1687
+ }
1688
+
1689
+ // 4) Execute custom filter code if present
1690
+ let customFilterResult = null
1691
+ if (currentCustomFilterCode && currentCustomFilterCode.trim()) {
1692
+ try {
1693
+ // Wrap code in a function that provides contextValues
1694
+ const executeCode = new Function('context', 'authContext', 'filters', 'localFilter', `
1695
+ let customFilter = [];
1696
+ try {
1697
+ ${currentCustomFilterCode}
1698
+ } catch (e) {
1699
+ console.error('Inner error in customFilterCode:', e);
1700
+ }
1701
+ return customFilter;
1702
+ `);
1703
+ customFilterResult = executeCode(contextValues, authValues, formattedT, localFilter)
1704
+
1705
+ // Validate that result is an array
1706
+ if (customFilterResult && !Array.isArray(customFilterResult)) {
1707
+ console.error('Custom filter must return an array, got:', typeof customFilterResult)
1708
+ customFilterResult = null
1709
+ }
1710
+ } catch (error) {
1711
+ console.error('Error executing custom filter code in SGrid:', error)
1712
+ customFilterResult = null
1713
+ }
1714
+ }
1715
+
1716
+ // Merge custom filter array with formattedT if present
1717
+ if (customFilterResult && Array.isArray(customFilterResult)) {
1718
+ formattedT = [...formattedT, ...customFilterResult]
1719
+ }
1720
+
1721
+ // Final Deduplication after all merges and custom code execution
1722
+ if (Array.isArray(formattedT)) {
1723
+ formattedT = formattedT.filter((f, idx, self) =>
1724
+ idx === self.findIndex(t => (
1725
+ t.path === f.path &&
1726
+ t.method === f.method &&
1727
+ JSON.stringify(t.value) === JSON.stringify(f.value)
1728
+ ))
1729
+ );
1730
+ }
1138
1731
 
1139
1732
  let data = {
1140
1733
  ...localFilter,
@@ -1144,7 +1737,7 @@ const SGrid = props => {
1144
1737
  }
1145
1738
  // Exclude any tFilter entries with null value before sending the request (per requirement)
1146
1739
  if (Array.isArray(data.tFilter)) {
1147
- data.tFilter = data.tFilter.filter(f => f == null ? false : f.value !== null);
1740
+ data.tFilter = data.tFilter.filter(f => f == null ? false : (f.value !== null && !f.isCustom));
1148
1741
  }
1149
1742
  try {
1150
1743
  if (request.rowGroupCols.length !== request.groupKeys.length) {
@@ -1175,6 +1768,33 @@ const SGrid = props => {
1175
1768
  }));
1176
1769
  const aggIds = request.valueCols.map(vc => vc.id);
1177
1770
 
1771
+ // Dynamic placeholder replacement for authValues in selectParams
1772
+ let dynamicSelectParams = selectParams;
1773
+ if (Array.isArray(dynamicSelectParams)) {
1774
+ dynamicSelectParams = dynamicSelectParams.map(p => {
1775
+ if (typeof p.value === 'string' && p.value.includes('{{auth.')) {
1776
+ const replaced = p.value.replace(/\{\{auth\.(.*?)\}\}/g, (_, path) => {
1777
+ return path.split('.').reduce((acc, key) => acc && acc[key], authValues) ?? '';
1778
+ });
1779
+ return {...p, value: replaced};
1780
+ }
1781
+ return p;
1782
+ });
1783
+ }
1784
+
1785
+ // Dynamic placeholder replacement for contextValues in selectParams
1786
+ if (Array.isArray(dynamicSelectParams)) {
1787
+ dynamicSelectParams = dynamicSelectParams.map(p => {
1788
+ if (typeof p.value === 'string' && p.value.includes('{{context.')) {
1789
+ const replaced = p.value.replace(/\{\{context\.(.*?)\}\}/g, (_, path) => {
1790
+ return path.split('.').reduce((acc, key) => acc && acc[key], contextValues) ?? '';
1791
+ });
1792
+ return {...p, value: replaced};
1793
+ }
1794
+ return p;
1795
+ });
1796
+ }
1797
+
1178
1798
  const groupOrderColdIds = request.sortModel.filter(x => groupOrderCols.includes(x.colId) || ((x.colId == "IZ_groupCount" || x.colId.endsWith("groupCount") || aggIds.some(id => x.colId.toLowerCase().endsWith(id.toLowerCase()))) && isGroupCountOrder))
1179
1799
  let orderBy = groupOrderColdIds.map(sort => handleGetSortObject(sort.colId, sort.sort, true, aggIds.some(id => sort.colId.toLowerCase().endsWith(id.toLowerCase()))))
1180
1800
  data = {
@@ -1183,11 +1803,22 @@ const SGrid = props => {
1183
1803
  orderBy: orderBy
1184
1804
  }
1185
1805
 
1806
+ // Final deduplication for group keys vs base filters
1807
+ if (Array.isArray(data.tFilter)) {
1808
+ data.tFilter = data.tFilter.filter((f, idx, self) =>
1809
+ idx === self.findIndex(t => (
1810
+ t.path === f.path &&
1811
+ t.method === f.method &&
1812
+ JSON.stringify(t.value) === JSON.stringify(f.value)
1813
+ ))
1814
+ );
1815
+ }
1816
+
1186
1817
  // if (updateAggregation !== true) {
1187
1818
  // data.aggregators = []
1188
1819
  // }
1189
1820
  data.selectFields = selectedFields
1190
- data.selectParams = selectParams
1821
+ data.selectParams = dynamicSelectParams
1191
1822
  data.rawSql = builderData.rawSql
1192
1823
 
1193
1824
  const response = await Services.PostService(groupEndPoint, false, data, {
@@ -1259,8 +1890,46 @@ const SGrid = props => {
1259
1890
  ...data,
1260
1891
  tFilter: [...tFilters, ...data.tFilter]
1261
1892
  }
1893
+
1894
+ // Final deduplication for group keys vs base filters
1895
+ if (Array.isArray(data.tFilter)) {
1896
+ data.tFilter = data.tFilter.filter((f, idx, self) =>
1897
+ idx === self.findIndex(t => (
1898
+ t.path === f.path &&
1899
+ t.method === f.method &&
1900
+ JSON.stringify(t.value) === JSON.stringify(f.value)
1901
+ ))
1902
+ );
1903
+ }
1262
1904
  }
1263
1905
  let orderBy = request.sortModel.filter(x => !x.colId.endsWith("IZ_groupCount")).map(sort => handleGetSortObject(sort.colId, sort.sort, false))
1906
+
1907
+ // Dynamic placeholder replacement for authValues in selectParams
1908
+ let dynamicSelectParams = selectParams;
1909
+ if (Array.isArray(dynamicSelectParams)) {
1910
+ dynamicSelectParams = dynamicSelectParams.map(p => {
1911
+ if (typeof p.value === 'string' && p.value.includes('{{auth.')) {
1912
+ const replaced = p.value.replace(/\{\{auth\.(.*?)\}\}/g, (_, path) => {
1913
+ return path.split('.').reduce((acc, key) => acc && acc[key], authValues) ?? '';
1914
+ });
1915
+ return {...p, value: replaced};
1916
+ }
1917
+ return p;
1918
+ });
1919
+ }
1920
+
1921
+ if (Array.isArray(dynamicSelectParams)) {
1922
+ dynamicSelectParams = dynamicSelectParams.map(p => {
1923
+ if (typeof p.value === 'string' && p.value.includes('{{context.')) {
1924
+ const replaced = p.value.replace(/\{\{context\.(.*?)\}\}/g, (_, path) => {
1925
+ return path.split('.').reduce((acc, key) => acc && acc[key], contextValues) ?? '';
1926
+ });
1927
+ return {...p, value: replaced};
1928
+ }
1929
+ return p;
1930
+ });
1931
+ }
1932
+
1264
1933
  if (data.tIncludes != null)
1265
1934
  data = {
1266
1935
  ...data,
@@ -1281,7 +1950,7 @@ const SGrid = props => {
1281
1950
  data.aggregators = []
1282
1951
  }
1283
1952
  data.selectFields = selectedFields
1284
- data.selectParams = selectParams
1953
+ data.selectParams = dynamicSelectParams
1285
1954
  data.lastRowParams = lastRowRef.current[pageNumber];
1286
1955
 
1287
1956
  data.isPaginationByFilter = builderData?.isRaw === false
@@ -1292,7 +1961,6 @@ const SGrid = props => {
1292
1961
  rowCount: undefined // <== This keeps the grid in "loading" state
1293
1962
  }
1294
1963
  data.rawSql = builderData?.rawSql
1295
- console.log(builderData)
1296
1964
  const response = await Services.PostService(pagedEndPoint, false, data, {
1297
1965
  ...paramsPage,
1298
1966
  page: isSingle ? 1 : pageNumber,
@@ -1341,7 +2009,7 @@ const SGrid = props => {
1341
2009
  return {success: false}
1342
2010
  }
1343
2011
  },
1344
- [externalFilter, includes, paramsPage, Filter, selectedFields, isPagination, selectedSearchObjects, builderData]
2012
+ [externalFilter, includes, paramsPage, Filter, selectedFields, isPagination, selectedSearchObjects, builderData, authValues, contextValues]
1345
2013
  )
1346
2014
  const datasource = useMemo(() => createServerSideDatasource(getRowsFromApi), [getRowsFromApi]);
1347
2015
 
@@ -1416,7 +2084,19 @@ const SGrid = props => {
1416
2084
  }
1417
2085
 
1418
2086
  function handleFilterChange(field, value) {
1419
- handleChange(setFilter, field, value)
2087
+ if (field === 'LocalTfilter') {
2088
+ // When updating LocalTfilter, preserve main filters from builderModel
2089
+ setFilter(preValue => {
2090
+ const mainFilters = (preValue.LocalTfilter || []).filter(f => f.isMainFilter === true)
2091
+ const newFilters = Array.isArray(value) ? value : []
2092
+ return {
2093
+ ...preValue,
2094
+ LocalTfilter: [...mainFilters, ...newFilters]
2095
+ }
2096
+ })
2097
+ } else {
2098
+ handleChange(setFilter, field, value)
2099
+ }
1420
2100
  }
1421
2101
 
1422
2102
  async function handleGetTemplates() {
@@ -1524,14 +2204,53 @@ const SGrid = props => {
1524
2204
  }
1525
2205
 
1526
2206
  const restoreState = () => {
1527
- if (gridApi !== null && colDefs !== null && selectedTemplate != null) {
2207
+ let builderMainFilter = builderModel?.filter?.LocalTfilter ?? []
2208
+
2209
+ if (gridApi !== null && colDefs.current !== null && selectedTemplate != null) {
2210
+ console.log(selectedTemplate)
1528
2211
  gridApi.applyColumnState({
1529
2212
  state: selectedTemplate.value,
1530
2213
  applyOrder: true,
1531
2214
  });
1532
- }
1533
- if (selectedTemplate.filterValue != null && (Filter.Tfilter == null || Filter.Tfilter.length == 0)) {
1534
- setFilter(selectedTemplate.filterValue);
2215
+ if (selectedTemplate.filterValue != null) {
2216
+ let fixedLocalFilters = builderModel?.filter?.fixedTFilter ?? []
2217
+ fixedLocalFilters = fixedLocalFilters.filter(x => x.fixed === true)
2218
+ fixedLocalFilters = fixedLocalFilters.map(x => ({
2219
+ ...x,
2220
+ isMainFilter: x.fixed === true ? false : x.isMainFilter
2221
+ }))
2222
+ let templateFilter = selectedTemplate?.filterValue?.LocalTfilter ?? []
2223
+ let nonExistsFilter = fixedLocalFilters.filter(x => !templateFilter.some(y => y.path === x.path && y.method === x.method && y.isOrList === x.isOrList))
2224
+ let finalLocalFilter = [...templateFilter, ...nonExistsFilter]
2225
+
2226
+ // Handle Tfilter from template
2227
+ const templateTfilter = selectedTemplate?.filterValue?.Tfilter ?? []
2228
+ const templateCustomFilterCode = selectedTemplate?.filterValue?.customFilterCode ?? ''
2229
+
2230
+ setFilter(preValue => ({
2231
+ ...preValue,
2232
+ LocalTfilter: [...finalLocalFilter, ...builderMainFilter],
2233
+ Tfilter: templateTfilter,
2234
+ customFilterCode: templateCustomFilterCode
2235
+ }));
2236
+ }
2237
+ } else {
2238
+ let fixedLocalFilters = builderModel?.filter?.fixedTFilter ?? []
2239
+ fixedLocalFilters = fixedLocalFilters.filter(x => x.fixed === true)
2240
+
2241
+ let finalLocalFilter = [...fixedLocalFilters]
2242
+
2243
+ // When no template, use builderModel filters
2244
+ const builderTfilter = builderModel?.filter?.Tfilter ?? []
2245
+ const builderCustomFilterCode = builderModel?.filter?.customFilterCode ?? ''
2246
+
2247
+ setFilter(preValue => ({
2248
+ ...preValue,
2249
+ LocalTfilter: [...finalLocalFilter, ...builderMainFilter],
2250
+ Tfilter: builderTfilter,
2251
+ customFilterCode: builderCustomFilterCode
2252
+ }));
2253
+
1535
2254
  }
1536
2255
  };
1537
2256
 
@@ -1566,7 +2285,6 @@ const SGrid = props => {
1566
2285
  };
1567
2286
 
1568
2287
  setSelectedSearchObjects(prev => [...prev, newObj]);
1569
- console.log(newObj)
1570
2288
  setSearchTerm('');
1571
2289
  setSearchPopoverOpen(false);
1572
2290
  debouncedRefresh();
@@ -1575,18 +2293,16 @@ const SGrid = props => {
1575
2293
 
1576
2294
  useEffect(() => {
1577
2295
  if (builderData) {
1578
-
1579
2296
  if (builderData.isRaw) {
1580
2297
  handleGetRawColumns(builderData.rawSql)
1581
2298
  } else {
1582
2299
  handleResetColsToStudio()
1583
2300
  }
1584
2301
  }
1585
- }, [builderData]);
2302
+ }, [builderData, isRerender]);
1586
2303
 
1587
2304
  useEffect(() => {
1588
2305
  if (builderModel != null) {
1589
- console.log(builderModel)
1590
2306
  let rawSql = builderModel.rawSql;
1591
2307
  if (builderModel.isRaw === true) {
1592
2308
  const localSelectParams = builderModel.selectionParams.map((x, index) => ({
@@ -1607,11 +2323,44 @@ const SGrid = props => {
1607
2323
  return param.value;
1608
2324
  });
1609
2325
  }
1610
- setBuilderData({...builderModel, rawSql: rawSql})
1611
- setFilter(preValue => ({
1612
- ...preValue,
1613
- LocalTfilter: [...(builderModel?.filter?.fixedTFilter || [])]
1614
- }))
2326
+
2327
+ setBuilderData(prev => {
2328
+ // Only update if builderModel data changed or rawSql changed
2329
+ const isSameModel = prev && JSON.stringify(prev) === JSON.stringify({...builderModel, rawSql});
2330
+ if (isSameModel) return prev;
2331
+ return {...builderModel, rawSql: rawSql};
2332
+ });
2333
+
2334
+ // We explicitly skip updating setFilter if builderModel triggered it but we just restored from session.
2335
+ // We rely on the fact that if a session restore happened, Filter.LocalTfilter will likely be populated.
2336
+
2337
+ setFilter(preValue => {
2338
+ const fixedTFilter = builderModel?.filter?.fixedTFilter || [];
2339
+ const localTFilter = builderModel?.filter?.LocalTfilter || [];
2340
+ const tFilter = builderModel?.filter?.Tfilter || [];
2341
+ const customFilterCode = builderModel?.filter?.customFilterCode || '';
2342
+
2343
+ // If we already have filters (possibly restored from session), don't overwrite with just fixed filters
2344
+ // unless it's a completely fresh state or fixed filters changed.
2345
+ const isSameFilter = JSON.stringify(preValue.LocalTfilter) === JSON.stringify([...fixedTFilter, ...localTFilter]);
2346
+ const isSameCode = preValue.customFilterCode === customFilterCode;
2347
+
2348
+ if (preValue.LocalTfilter.length > (fixedTFilter.length + localTFilter.length) && isSameCode) {
2349
+ return preValue;
2350
+ }
2351
+
2352
+ if (isSameFilter && isSameCode) return preValue;
2353
+
2354
+ return {
2355
+ ...preValue,
2356
+ Tfilter: tFilter,
2357
+ LocalTfilter: [
2358
+ ...fixedTFilter.map(f => ({...f})),
2359
+ ...localTFilter.map(f => ({...f}))
2360
+ ],
2361
+ customFilterCode: customFilterCode
2362
+ };
2363
+ })
1615
2364
  }
1616
2365
  }, [builderModel]);
1617
2366
 
@@ -1640,39 +2389,38 @@ const SGrid = props => {
1640
2389
  }, [selectParams, builderModel?.isRaw]);
1641
2390
 
1642
2391
  useEffect(() => {
1643
-
1644
2392
  if (gridApi !== null) {
1645
2393
  gridApi.refreshServerSide({purge: !noPurge})
1646
2394
  lastRowRef.current = {}
1647
2395
  }
1648
- }, [refresh, externalFilter, localRefresh, Filter]);
2396
+ }, [refresh, externalFilter, localRefresh, Filter.Tfilter, Filter.LocalTfilter, Filter.customFilterCode, externalFilter?.customFilterCode]);
1649
2397
 
1650
2398
  useEffect(() => {
1651
- if (gridApi != null && colDefs != null)
2399
+ if (gridApi != null && colDefs.current != null)
1652
2400
  handleGetTemplates()
1653
- }, [gridApi, colDefs])
2401
+ }, [gridApi, colDefs.current])
1654
2402
 
1655
2403
  useEffect(() => {
1656
- if (colDefs.length > 0 && selectedTemplate != null) {
2404
+ if (colDefs.current.length > 0) {
1657
2405
  restoreState()
1658
2406
  }
1659
2407
  }, [selectedTemplate])
1660
- useEffect(() => {
1661
- if (selectedTemplate == null && templates.length > 0 && colDefs.length > 0) {
1662
- const userDefined = templates.find(x => x.isDefault === true)
1663
- if (userDefined != null)
1664
- setSelectedTemplate(userDefined)
1665
- else {
1666
- const systemDefined = templates.find(x => x.type === "UserPredefined")
1667
- if (systemDefined != null)
1668
- setSelectedTemplate(systemDefined)
1669
- else {
1670
- const anyTemplate = templates[0]
1671
- setSelectedTemplate(anyTemplate)
1672
- }
1673
- }
1674
- }
1675
- }, [templates, colDefs])
2408
+ // useEffect(() => {
2409
+ // if (templates.length > 0 && colDefs.length > 0) {
2410
+ // const userDefined = templates.find(x => x.isDefault === true)
2411
+ // if (userDefined != null)
2412
+ // setSelectedTemplate(userDefined)
2413
+ // else {
2414
+ // const systemDefined = templates.find(x => x.type === "UserPredefined")
2415
+ // if (systemDefined != null)
2416
+ // setSelectedTemplate(systemDefined)
2417
+ // else {
2418
+ // const anyTemplate = templates[0]
2419
+ // setSelectedTemplate(anyTemplate)
2420
+ // }
2421
+ // }
2422
+ // }
2423
+ // }, [])
1676
2424
 
1677
2425
  useEffect(() => {
1678
2426
  if (!timerValue || isNaN(timerValue) || timerValue == 0) {
@@ -1705,47 +2453,18 @@ const SGrid = props => {
1705
2453
  }, [externalTimer]); // re// n after each refresh or change in value
1706
2454
 
1707
2455
  return (
1708
- <Grid item container size={12}>
1709
- <Grid container item
2456
+ <Grid container size={{ xs: 12 }}>
2457
+ <Grid container
1710
2458
  sx={{backgroundColor: '#fafafb', justifyContent: 'space-between'}} padding={2}
1711
- size={12}
1712
- >
2459
+ size={{ xs: 12 }}>
1713
2460
  <Box sx={{display: 'flex'}}>
1714
- {(builderData?.id != null && minimized !== true) &&
1715
- <Box sx={{minWidth: '250px'}}>
1716
-
1717
- <FormControl fullWidth>
1718
- <Autocomplete
1719
- size={'small'}
1720
- value={selectedTemplate}
1721
- fullWidth
1722
- disableClearable
1723
- options={templates}
1724
- onChange={(e, value) => {
1725
- setSelectedTemplate(value)
1726
- }
1727
- }
1728
- getOptionLabel={option => option.name}
1729
- renderInput={params => (
1730
- <TextField
1731
- label='شكل النموذج'
1732
- {...params}
1733
- onMouseDown={() => {
1734
- handleGetTemplates()
1735
- }}
1736
- />
1737
- )}
1738
- />
1739
- </FormControl>
1740
- </Box>
1741
- }
1742
2461
  <Box sx={{ml: '5px'}}>
1743
2462
  <Tooltip title='مؤقت' placement={'top'}>
1744
2463
  <Select variant={'outlined'} value={timerValue} onChange={(e) => setTimerValue(e.target.value)}
1745
2464
  defaultValue={0} size={'small'}
1746
2465
  color='primary' sx={{width: '60px'}}>
1747
2466
  <MenuItem key={1} value={0}>No</MenuItem>
1748
- <MenuItem key={2} value={0.1667}>10s</MenuItem>
2467
+ {/*<MenuItem key={2} value={0.1667}>10s</MenuItem>*/}
1749
2468
  <MenuItem key={2} value={0.5}>30s</MenuItem>
1750
2469
  <MenuItem key={2} value={1}>1m</MenuItem>
1751
2470
  <MenuItem key={2} value={2}>2m</MenuItem>
@@ -1912,43 +2631,6 @@ const SGrid = props => {
1912
2631
  </Tooltip>
1913
2632
  </Box>
1914
2633
  }
1915
- {
1916
- builderData?.id != null && minimized !== true && <>
1917
- <Box>
1918
- <Tooltip title='حفظ النموذج'>
1919
- <IconButton disabled={selectedTemplate == null} onClick={() => {
1920
- handleSaveTemplate()
1921
- }} color='primary'>
1922
- <Save/>
1923
- </IconButton>
1924
- </Tooltip>
1925
- </Box>
1926
- <Box>
1927
- <Tooltip title='حفظ بأسم'>
1928
- <IconButton onClick={() => {
1929
- setIsTemplateEditing(false)
1930
- handleToggleDialogs('addTemplate')
1931
- }} color='primary'>
1932
- <SaveAs/>
1933
- </IconButton>
1934
- </Tooltip>
1935
- </Box>
1936
-
1937
- <Box>
1938
- <Tooltip title='تعديل'>
1939
- <IconButton disabled={selectedTemplate == null}
1940
- onClick={() => {
1941
- setIsTemplateEditing(true)
1942
- handleToggleDialogs('addTemplate')
1943
- }}
1944
- color='primary'
1945
- >
1946
- <Edit/>
1947
- </IconButton>
1948
- </Tooltip>
1949
- </Box>
1950
- </>
1951
- }
1952
2634
  {<Box>
1953
2635
 
1954
2636
  <IconButton color={'primary'} onClick={() => handleToggleDialogs('CustomFilter')}>
@@ -1966,6 +2648,16 @@ const SGrid = props => {
1966
2648
 
1967
2649
  </Box>
1968
2650
  </Grid>
2651
+ <TemplateStateContext.Provider value={{
2652
+ templates,
2653
+ selectedTemplate,
2654
+ setSelectedTemplate,
2655
+ handleGetTemplates,
2656
+ handleSaveTemplate,
2657
+ handleToggleDialogs,
2658
+ setIsTemplateEditing,
2659
+ builderData,
2660
+ }}>
1969
2661
  <div style={{width: "100%", height: minimized === true ? "0px" : height ?? "70vh", direction: 'ltr'}}>
1970
2662
  <AgGridReact
1971
2663
 
@@ -1973,7 +2665,7 @@ const SGrid = props => {
1973
2665
  columnHoverHighlight={true}
1974
2666
  theme={agTheme}
1975
2667
  enableRtl={true}
1976
- columnDefs={colDefs}
2668
+ columnDefs={colDefs.current}
1977
2669
  rowModelType={"serverSide"}
1978
2670
  onGridReady={onGridReady}
1979
2671
  maxConcurrentDatasourceRequests={0}
@@ -1981,10 +2673,107 @@ const SGrid = props => {
1981
2673
  onRowSelected={onRowSelected}
1982
2674
  // getChildCount={getChildCount}
1983
2675
  sideBar={sideBarConfig}
1984
- context={{tRouting, builderId: builderData?.id}} // pass routing and builder id
2676
+ context={{
2677
+ tRouting,
2678
+ builderId: builderData?.id,
2679
+ updateRef: updateRef,
2680
+ settings: builderModel?.settings,
2681
+ // Node identity — available inside all column functions as params.context.nodeId / nodeName
2682
+ nodeId: nodeId ?? null,
2683
+ nodeName: nodeId ?? null, // alias for convenience
2684
+ // Shared registry — access any report's ref: params.context.reportRefs.current['nodeId']
2685
+ reportRefs: reportRefs ?? null,
2686
+ // Returns the latest viewer state snapshot — used by MultiSelectEditor to commit values
2687
+ getViewerContext: () => ({
2688
+ data: viewerData,
2689
+ setData: setData ?? (() => {}),
2690
+ dataRef: viewerDataRef,
2691
+ reportRefs: reportRefs ?? null,
2692
+ }),
2693
+ }} // pass routing, builder id, updateRef, node identity and reportRefs
1985
2694
  gridOptions={{
1986
2695
  enableRangeSelection: true,
1987
2696
  enableCharts: true,
2697
+ getContextMenuItems: (params) => {
2698
+ const rowUniqueId = builderModel?.settings?.rowUniqueId
2699
+ const rowData = params.node?.data
2700
+ const defaultItems = params.defaultItems || []
2701
+
2702
+ if (!updateRef || !rowUniqueId || !rowData) {
2703
+ return defaultItems
2704
+ }
2705
+
2706
+ const rowIdValue = getRowIdFromSettings(rowData, rowUniqueId)
2707
+ const hasUpdate = updateRef.current && Array.isArray(updateRef.current) &&
2708
+ updateRef.current.some(item => item.rowId === rowIdValue)
2709
+
2710
+ const customItems = []
2711
+
2712
+ if (hasUpdate) {
2713
+ customItems.push({
2714
+ name: 'Remove Row from Update Ref',
2715
+ icon: '<span class="ag-icon ag-icon-cancel" unselectable="on" role="presentation"></span>',
2716
+ action: () => {
2717
+ removeUpdateRefByRowId(updateRef, rowData, rowUniqueId)
2718
+ params.api.refreshCells({force: true})
2719
+ }
2720
+ })
2721
+ }
2722
+
2723
+ customItems.push({
2724
+ name: 'Clone Update Ref (Backup)',
2725
+ icon: '<span class="ag-icon ag-icon-copy" unselectable="on" role="presentation"></span>',
2726
+ action: () => {
2727
+ cloneUpdateRefToOriginal(updateRef, originalRefData)
2728
+ params.api.refreshCells({force: true})
2729
+ },
2730
+ disabled: !updateRef.current || updateRef.current.length === 0
2731
+ })
2732
+
2733
+ customItems.push({
2734
+ name: 'Restore Update Ref (from Backup)',
2735
+ icon: '<span class="ag-icon ag-icon-undo" unselectable="on" role="presentation"></span>',
2736
+ action: () => {
2737
+ restoreUpdateRefFromOriginal(updateRef, originalRefData)
2738
+ params.api.refreshCells({force: true})
2739
+ },
2740
+ disabled: !originalRefData.current || originalRefData.current.length === 0
2741
+ })
2742
+
2743
+ customItems.push({
2744
+ name: 'Normalize Update Ref (Merge Duplicates)',
2745
+ icon: '<span class="ag-icon ag-icon-columns" unselectable="on" role="presentation"></span>',
2746
+ action: () => {
2747
+ normalizeUpdateRef(updateRef)
2748
+ params.api.refreshCells({force: true})
2749
+ },
2750
+ disabled: !updateRef.current || updateRef.current.length === 0
2751
+ })
2752
+
2753
+ customItems.push({
2754
+ name: 'Clear All Update Ref',
2755
+ icon: '<span class="ag-icon ag-icon-cancel" unselectable="on" role="presentation"></span>',
2756
+ action: () => {
2757
+ if (updateRef.current && Array.isArray(updateRef.current)) {
2758
+ const count = updateRef.current.length
2759
+ clearAllUpdateRef(updateRef)
2760
+ params.api.refreshCells({force: true})
2761
+ console.log(`Cleared ${count} rows from updateRef`)
2762
+ }
2763
+ },
2764
+ disabled: !updateRef.current || updateRef.current.length === 0
2765
+ })
2766
+
2767
+ if (customItems.length > 0) {
2768
+ return [
2769
+ ...customItems,
2770
+ 'separator',
2771
+ ...defaultItems
2772
+ ]
2773
+ }
2774
+
2775
+ return defaultItems
2776
+ }
1988
2777
  }}
1989
2778
  rowSelection="multiple"
1990
2779
  statusBar={{
@@ -2033,14 +2822,21 @@ const SGrid = props => {
2033
2822
  }}
2034
2823
 
2035
2824
  components={{
2825
+ ...AG_COMPONENTS,
2036
2826
  routingCell: RoutingCell,
2037
2827
  imageCell: ImageCell,
2038
2828
  customStatusBar: CustomStatusBar,
2039
- // agColumnsToolPanel: CustomColumnsToolPanel
2040
-
2829
+ templatesToolPanel: TemplatesToolPanel,
2041
2830
  }}
2042
2831
 
2043
2832
 
2833
+ onCellValueChanged={params => {
2834
+ // Guard: skip undefined phantom reverts produced by MultiSelectEditor's stopEditing(true) cancel path
2835
+ if (params.newValue === undefined && params.colDef?.cellEditor === 'MultiSelectEditor') {
2836
+ return
2837
+ }
2838
+ }}
2839
+
2044
2840
  onSortChanged={params => {
2045
2841
  const sm = params.columns;
2046
2842
  if (sm.length == 1 && sm.some(s => s.colId === 'IZ_groupCount')) {
@@ -2049,6 +2845,7 @@ const SGrid = props => {
2049
2845
  }}
2050
2846
  />
2051
2847
  </div>
2848
+ </TemplateStateContext.Provider>
2052
2849
  <Dialog
2053
2850
  fullWidth
2054
2851
  open={openDialogs?.addTemplate && builderData?.id != null}
@@ -2073,29 +2870,34 @@ const SGrid = props => {
2073
2870
  scroll='body'
2074
2871
  // onClose={() => handleToggleDialogs('CustomFilter')}
2075
2872
  >
2076
- <CustomFilterDialog
2077
- handleToggleDialogs={handleToggleDialogs}
2078
- Filter={[...(Filter?.LocalTfilter || [])]}
2079
- handleFilterChange={handleFilterChange}
2080
- className={builderData?.reportSource?.fullName}
2081
- LocalFilter={false}
2082
- selectTFilter={selectTFilter}
2083
- selectParamsMeta={builderData?.selectionParams || []}
2084
- selectParamsValues={selectParams}
2085
- onSelectParamsSave={(list) => {
2086
- // list is array of { index, value, type }
2087
- setSelectParams(prev => {
2088
- const arr = [...(prev || [])];
2089
- (list || []).forEach(item => {
2090
- const i = item.index;
2091
- const type = item.type;
2092
- const current = arr[i] || {id: i, PropertyType: type, value: null};
2093
- arr[i] = {...current, PropertyType: current.PropertyType || type, value: item.value};
2873
+ {openDialogs.CustomFilter && (
2874
+ <CustomFilterDialog
2875
+ handleToggleDialogs={handleToggleDialogs}
2876
+ Filter={(Filter?.LocalTfilter || []).filter(f => (f.isMainFilter !== true))}
2877
+ customFilterCode={Filter?.customFilterCode}
2878
+ handleFilterChange={handleFilterChange}
2879
+ className={builderData?.reportSource?.fullName}
2880
+ LocalFilter={false}
2881
+
2882
+ isViewer={router.pathname.includes('report/viewer')}
2883
+ selectTFilter={selectTFilter}
2884
+ selectParamsMeta={builderData?.selectionParams || []}
2885
+ selectParamsValues={selectParams}
2886
+ onSelectParamsSave={(list) => {
2887
+ // list is array of { index, value, type }
2888
+ setSelectParams(prev => {
2889
+ const arr = [...(prev || [])];
2890
+ (list || []).forEach(item => {
2891
+ const i = item.index;
2892
+ const type = item.type;
2893
+ const current = arr[i] || {id: i, PropertyType: type, value: null};
2894
+ arr[i] = {...current, PropertyType: current.PropertyType || type, value: item.value};
2895
+ });
2896
+ return arr;
2094
2897
  });
2095
- return arr;
2096
- });
2097
- }}
2098
- />
2898
+ }}
2899
+ />
2900
+ )}
2099
2901
  </Dialog>
2100
2902
 
2101
2903
 
@@ -2166,3 +2968,15 @@ function toNestedByUnderscore(input) {
2166
2968
  return result;
2167
2969
  }
2168
2970
 
2971
+ // Export helper functions for use in column configurations
2972
+ export {
2973
+ setUpdateRefValue,
2974
+ setUpdateRefRow,
2975
+ getUpdateRefValue,
2976
+ hasUpdateRefValue,
2977
+ clearUpdateRefRow,
2978
+ clearAllUpdateRef,
2979
+ getAllUpdates,
2980
+ removeUpdateRefByRowId
2981
+ }
2982
+