robobyte-front-builder 1.0.16 → 1.0.19

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.
@@ -8,8 +8,8 @@ import Grid from '@mui/material/Grid'
8
8
  // ** Icons Imports
9
9
  // ** Store Imports
10
10
  // ** Custom Components Imports
11
- import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
12
- import { Endpoints, Services } from 'src/services/Endpoints'
11
+ import {useCallback, useContext, useEffect, useMemo, useState} from 'react'
12
+ import {Endpoints, Services} from 'src/services/Endpoints'
13
13
  import {
14
14
  Autocomplete,
15
15
  CircularProgress,
@@ -21,23 +21,23 @@ import {
21
21
  TextField,
22
22
  Tooltip
23
23
  } from '@mui/material'
24
- import { AgGridReact } from "ag-grid-react";
25
- import { Edit, FilterAlt, RefreshOutlined, Save, SaveAs } from "@mui/icons-material";
26
- import { getRandomInt, Helper } from "services/helper/helper";
24
+ import {AgGridReact} from "ag-grid-react";
25
+ import {Edit, FilterAlt, RefreshOutlined, Save, SaveAs} from "@mui/icons-material";
26
+ import {getRandomInt, Helper} from "services/helper/helper";
27
27
  import PostService from 'services/PostService'
28
28
  import handleChange from 'services/helper/handleChange'
29
- import { DotsVerticalCircleOutline, FileExcelOutline } from 'mdi-material-ui'
29
+ import {DotsVerticalCircleOutline, FileExcelOutline} from 'mdi-material-ui'
30
30
  import AddTemplateDialog from './template/addTemplate'
31
31
  import UpdateService from 'services/UpdateService'
32
- import { useRouter } from 'next/router'
33
- import { SystemContext } from 'context/SystemContext'
34
- import { AuthContext } from 'context/AuthContext'
32
+ import {useRouter} from 'next/router'
33
+ import {SystemContext} from 'context/SystemContext'
34
+ import {AuthContext} from 'context/AuthContext'
35
35
  import moment from "moment-timezone"
36
36
  import numeral from 'numeral'
37
37
  import CustomFilterDialog from "views/customFilter/CustomFilterDialog";
38
38
  import CustomStatusBar from "views/genericTable/statusBar/rowCountStatusBar";
39
39
  import StreamService from "services/StreamService";
40
- import { themeQuartz } from 'ag-grid-community';
40
+ import {themeQuartz} from 'ag-grid-community';
41
41
 
42
42
  // ** Utils Import
43
43
 
@@ -54,7 +54,7 @@ const TAGGrid = props => {
54
54
  // ** State
55
55
  const agTheme = themeQuartz
56
56
  .withParams({
57
- accentColor: "#00989f"
57
+ accentColor: "#FF1185"
58
58
  });
59
59
  const {
60
60
  groupEndPoint,
@@ -114,16 +114,16 @@ const TAGGrid = props => {
114
114
  enableRowGroup: false,
115
115
  cellRenderer: (params) => {
116
116
  if (params.node.group) {
117
- return <Box sx={{ display: 'flex', alignItems: 'center' }}>
117
+ return <Box sx={{display: 'flex', alignItems: 'center'}}>
118
118
  <IconButton
119
- sx={{ color: 'primary.light' }}
119
+ sx={{color: 'primary.light'}}
120
120
  onClick={() => {
121
121
  const groupKeys = getParentKeys(params.node.parent)
122
122
  groupKeys.push(params.node.key)
123
- params.api.refreshServerSide({ purge: !noPurge, route: groupKeys });
123
+ params.api.refreshServerSide({purge: !noPurge, route: groupKeys});
124
124
  }}
125
125
  >
126
- <RefreshOutlined />
126
+ <RefreshOutlined/>
127
127
  </IconButton>
128
128
  </Box>
129
129
  }
@@ -257,7 +257,7 @@ const TAGGrid = props => {
257
257
  afterSelect: true
258
258
  }
259
259
  }
260
- const field = isGrouping ? { path: colId, afterSelect: true } : flatCols.find(f => f.field === colId);
260
+ const field = isGrouping ? {path: colId, afterSelect: true} : flatCols.find(f => f.field === colId);
261
261
  return {
262
262
  columnName: isGrouping ? field.path.replace(".", "_") : field.path,
263
263
  sortDirection: sort === 'asc' ? 'Ascending' : 'Descending',
@@ -318,6 +318,8 @@ const TAGGrid = props => {
318
318
  const cleanedPath = path.substring(6); // Remove "pivot_"
319
319
  resultObject[cleanedPath] = value;
320
320
  } else {
321
+ resultObject[path] = value;
322
+
321
323
  // Otherwise, split the path and nest it
322
324
  const keys = path.split('_');
323
325
  let current = resultObject;
@@ -338,6 +340,22 @@ const TAGGrid = props => {
338
340
  return resultObject;
339
341
  }
340
342
 
343
+ function returnAdditionalPivotCols(flatCols) {
344
+ const resultFields = new Set();
345
+
346
+ flatCols.forEach(row => {
347
+ Object.keys(row).forEach(key => {
348
+ if (!key.startsWith("pivot_") && key !== "groupCount" && key !== "totalPrice") {
349
+ console.log(key)
350
+ resultFields.add(key);
351
+ }
352
+ });
353
+ });
354
+
355
+ const distinctFields = Array.from(resultFields);
356
+ return distinctFields;
357
+ }
358
+
341
359
  const createServerSideDatasource = (getRowsFromApi) => {
342
360
  return {
343
361
  getRows: (params) => {
@@ -357,6 +375,7 @@ const TAGGrid = props => {
357
375
  }
358
376
  })
359
377
  .catch((e) => {
378
+
360
379
  console.log(e)
361
380
  params.fail()
362
381
  });
@@ -368,13 +387,11 @@ const TAGGrid = props => {
368
387
  async params => {
369
388
  setFinalRequestObject(defaultFinalRequest)
370
389
  const fixedTFilter = externalFilter?.fixedTFilter ?? externalFilter?.fixedTfilter ?? []
371
- let localFilter = externalFilter ? { ...externalFilter } : {}
372
- localFilter = { ...localFilter, ...Filter }
373
- console.log(localFilter)
390
+ let localFilter = externalFilter ? {...externalFilter} : {}
391
+ localFilter = {...localFilter, ...Filter}
374
392
  let localTFilter = localFilter?.Tfilter ? [...localFilter?.Tfilter] : []
375
393
  localTFilter = localFilter?.TFilter ? [...localTFilter, ...localFilter?.TFilter] : [...localTFilter]
376
394
  localTFilter = [...localTFilter, ...fixedTFilter]
377
- console.log(localTFilter)
378
395
  delete localFilter.Tfilter
379
396
  delete localFilter.fixedTfilter
380
397
  delete localFilter.fixedTFilter
@@ -419,21 +436,17 @@ const TAGGrid = props => {
419
436
  // if (rowGroupColsIds.length > 1 && params.request.sortModel.length > 1) {
420
437
  // isGroupCountOrder = false;
421
438
  // }
422
- console.log(request.sortModel)
423
439
  request.sortModel = (request.sortModel ?? []).map(x => ({
424
440
  ...x,
425
441
  colId: x?.colId?.endsWith("_groupCount")
426
442
  ? x.colId.replace("_groupCount", "_groupCount")
427
443
  : x.colId
428
444
  }));
429
- console.log(request)
430
- console.log(request.sortModel)
445
+
431
446
  const aggIds = request.valueCols.map(vc => vc.id);
432
447
 
433
448
  const groupOrderColdIds = request.sortModel.filter(x => groupOrderCols.includes(x.colId) || ((x.colId == "IZ_groupCount" || x.colId.endsWith("groupCount") || aggIds.some(id => x.colId.endsWith(id))) && isGroupCountOrder))
434
449
  let orderBy = groupOrderColdIds.map(sort => handleGetSortObject(sort.colId, sort.sort, true, aggIds.some(id => sort.colId.endsWith(id))))
435
- console.log(groupOrderColdIds)
436
- console.log(orderBy)
437
450
  data = {
438
451
  ...data,
439
452
  tFilter: [...tFilters, ...data.tFilter],
@@ -461,6 +474,9 @@ const TAGGrid = props => {
461
474
  pivotColResult.push('groupCount')
462
475
  const responsePivotCol = response.pivotCols.map(x => x.field) ?? []
463
476
  pivotColResult.push(...responsePivotCol)
477
+ const additionalPivotCols = returnAdditionalPivotCols(response.data)
478
+ // pivotColResult.push(...additionalPivotCols)
479
+
464
480
  }
465
481
  }
466
482
  // groupKeys.push(request.rowGroupCols[request.groupKeys.length].id)
@@ -534,7 +550,6 @@ const TAGGrid = props => {
534
550
  if (response.aggregation != null && updateAggregation === true) {
535
551
  setPagedAgg([response.aggregation])
536
552
  }
537
-
538
553
  return {
539
554
  success: true,
540
555
  rows: colDefs && response.data, // Adjust based on your API's response structure
@@ -544,7 +559,7 @@ const TAGGrid = props => {
544
559
  }
545
560
  } catch (error) {
546
561
  console.log(error)
547
- return { success: false }
562
+ return {success: false}
548
563
  }
549
564
  },
550
565
  [externalFilter, includes, paramsPage, Filter]
@@ -706,7 +721,6 @@ const TAGGrid = props => {
706
721
  const cols = gridApi
707
722
  .getColumnDefs()
708
723
  const flatCols = flattenArray(cols)
709
- console.log(flatCols)
710
724
  const visibleCols = flatCols // all column definitions
711
725
  .filter(colDef =>
712
726
  gridApi.getColumnState().some(
@@ -727,10 +741,10 @@ const TAGGrid = props => {
727
741
  data.pivotCols = []
728
742
  }
729
743
  response = await StreamService(streamEndPoint, true, data, {
730
- ...finalRequestObject.params,
731
- groupBy: finalRequestObject.groupBy,
732
- getType: 'Stream'
733
- }, null,
744
+ ...finalRequestObject.params,
745
+ groupBy: finalRequestObject.groupBy,
746
+ getType: 'Stream'
747
+ }, null,
734
748
  progress => {
735
749
  console.log(`Downloaded: ${progress}%`)
736
750
  })
@@ -750,7 +764,7 @@ const TAGGrid = props => {
750
764
  applyOrder: true,
751
765
  });
752
766
  }
753
- if (selectedTemplate.filterValue != null && (Filter.Tfilter == null || Filter.Tfilter.length == 0)) {
767
+ if (selectedTemplate.filterValue != null) {
754
768
  setFilter(selectedTemplate.filterValue);
755
769
  }
756
770
  };
@@ -765,7 +779,7 @@ const TAGGrid = props => {
765
779
  useEffect(() => {
766
780
 
767
781
  if (gridApi !== null) {
768
- gridApi.refreshServerSide({ purge: !noPurge })
782
+ gridApi.refreshServerSide({purge: !noPurge})
769
783
  }
770
784
  }, [refresh, externalFilter, localRefresh, Filter]);
771
785
 
@@ -812,12 +826,11 @@ const TAGGrid = props => {
812
826
  }, [timerValue]); // re-run after each refresh or change in value
813
827
 
814
828
  return (
815
- <Grid item container size={12}>
816
- <Grid container item
817
- sx={{ borderTop: '1px solid #ccc', backgroundColor: '#fafafb', justifyContent: 'space-between' }} padding={2}
818
- size={12}>
819
- <Box sx={{ display: 'flex', minWidth: '250px' }}>
820
- <Box sx={{ minWidth: '250px' }}>
829
+ <Grid container size={{ xs: 12 }}>
830
+ <Grid container
831
+ sx={{borderTop: '1px solid #ccc', backgroundColor: '#fafafb', justifyContent: 'space-between'}} padding={2} size={{ xs: 12 }}>
832
+ <Box sx={{display: 'flex', minWidth: '250px'}}>
833
+ <Box sx={{minWidth: '250px'}}>
821
834
 
822
835
  <FormControl fullWidth>
823
836
  <Autocomplete
@@ -828,7 +841,6 @@ const TAGGrid = props => {
828
841
  options={templates}
829
842
  onChange={(e, value) => {
830
843
  setSelectedTemplate(value)
831
-
832
844
  }
833
845
  }
834
846
  getOptionLabel={option => option.name}
@@ -846,20 +858,23 @@ const TAGGrid = props => {
846
858
  </FormControl>
847
859
 
848
860
  </Box>
849
- <Box sx={{ ml: '5px' }}>
861
+ <Box sx={{ml: '5px'}}>
850
862
  <Tooltip title='مؤقت' placement={'top'}>
851
863
  <Select variant={'outlined'} value={timerValue} onChange={(e) => setTimerValue(e.target.value)}
852
- defaultValue={0} size={'small'}
853
- color='primary' sx={{ width: '60px' }}>
854
- <MenuItem key={1} value={0}>0</MenuItem>
855
- <MenuItem key={2} value={5}>5</MenuItem>
856
- <MenuItem key={3} value={15}>15</MenuItem>
857
- <MenuItem key={4} value={30}>30</MenuItem>
864
+ defaultValue={0} size={'small'}
865
+ color='primary' sx={{width: '60px'}}>
866
+ <MenuItem key={1} value={0}>No</MenuItem>
867
+ <MenuItem key={2} value={0.5}>30s</MenuItem>
868
+ <MenuItem key={2} value={1}>1m</MenuItem>
869
+ <MenuItem key={2} value={2}>2m</MenuItem>
870
+ <MenuItem key={2} value={5}>5m</MenuItem>
871
+ <MenuItem key={3} value={15}>15m</MenuItem>
872
+ <MenuItem key={4} value={30}>30m</MenuItem>
858
873
  </Select>
859
874
  </Tooltip>
860
875
  </Box>
861
876
  </Box>
862
- <Box sx={{ display: 'flex', justifyContent: 'center' }}>
877
+ <Box sx={{display: 'flex', justifyContent: 'center'}}>
863
878
  {streamEndPoint &&
864
879
  <Box>
865
880
  <Tooltip title='تصدير'>
@@ -868,7 +883,7 @@ const TAGGrid = props => {
868
883
  onClick={handleCSVExport}
869
884
  color='primary'
870
885
  >
871
- {isDownloading ? <CircularProgress size={24} /> : <FileExcelOutline />}
886
+ {isDownloading ? <CircularProgress size={24}/> : <FileExcelOutline/>}
872
887
  </IconButton>
873
888
  </Tooltip>
874
889
  </Box>
@@ -878,7 +893,7 @@ const TAGGrid = props => {
878
893
  <IconButton disabled={selectedTemplate == null} onClick={() => {
879
894
  handleSaveTemplate()
880
895
  }} color='primary'>
881
- <Save />
896
+ <Save/>
882
897
  </IconButton>
883
898
  </Tooltip>
884
899
  </Box>
@@ -888,7 +903,7 @@ const TAGGrid = props => {
888
903
  setIsTemplateEditing(false)
889
904
  handleToggleDialogs('addTemplate')
890
905
  }} color='primary'>
891
- <SaveAs />
906
+ <SaveAs/>
892
907
  </IconButton>
893
908
  </Tooltip>
894
909
  </Box>
@@ -896,39 +911,38 @@ const TAGGrid = props => {
896
911
  <Box>
897
912
  <Tooltip title='تعديل'>
898
913
  <IconButton disabled={selectedTemplate == null}
899
- onClick={() => {
900
- setIsTemplateEditing(true)
901
- handleToggleDialogs('addTemplate')
902
- }}
903
- color='primary'
914
+ onClick={() => {
915
+ setIsTemplateEditing(true)
916
+ handleToggleDialogs('addTemplate')
917
+ }}
918
+ color='primary'
904
919
  >
905
- <Edit />
920
+ <Edit/>
906
921
  </IconButton>
907
922
  </Tooltip>
908
923
  </Box>
909
924
  <Box>
910
925
  <IconButton color={'primary'} onClick={() => handleToggleDialogs('CustomFilter')}>
911
- <FilterAlt />
926
+ <FilterAlt/>
912
927
  </IconButton>
913
928
  </Box>
914
929
  <Box>
915
930
  <Tooltip title='اعادة تحميل'>
916
931
  <IconButton onClick={() => setLocalRefresh(!localRefresh)}>
917
- <RefreshOutlined />
932
+ <RefreshOutlined/>
918
933
  </IconButton>
919
934
  </Tooltip>
920
935
  </Box>
921
936
 
922
937
  </Box>
923
938
  </Grid>
924
- <div style={{ width: "100%", height: height ?? "70vh", direction: 'ltr' }}>
939
+ <div style={{width: "100%", height: height ?? "70vh", direction: 'ltr'}}>
925
940
  <AgGridReact
926
941
  debug={false}
927
942
  columnHoverHighlight={true}
928
943
  theme={agTheme}
929
944
  enableRtl={true}
930
945
  columnDefs={colDefs}
931
- stopEditingWhenCellsLoseFocus={true}
932
946
  rowModelType={"serverSide"}
933
947
  onGridReady={onGridReady}
934
948
  // defaultColDef={{
@@ -969,11 +983,11 @@ const TAGGrid = props => {
969
983
  pivotKeySeparator={"_"}
970
984
  getRowStyle={(row) => {
971
985
  if (row.node.group) {
972
- return { fontWeight: 'bold' };
986
+ return {fontWeight: 'bold'};
973
987
  }
974
988
  if (expireReport) {
975
989
  if (row.node?.data?.expireDate && new Date(row.node.data.expireDate) <= new Date()) {
976
- return { background: '#FF625F' };
990
+ return {background: '#FF625F'};
977
991
  }
978
992
  if (row.node?.data?.expireDate) {
979
993
  const expireDate = new Date(row.node.data.expireDate);
@@ -981,9 +995,9 @@ const TAGGrid = props => {
981
995
  const diffInDays = (expireDate - currentDate) / (1000 * 60 * 60 * 24); // Convert milliseconds to days
982
996
 
983
997
  if (diffInDays > 10) {
984
- return { background: '#8AFF8A' };
998
+ return {background: '#8AFF8A'};
985
999
  } else if (diffInDays > 0) {
986
- return { background: '#fcfd74' };
1000
+ return {background: '#fcfd74'};
987
1001
  }
988
1002
  }
989
1003
  }
@@ -999,7 +1013,7 @@ const TAGGrid = props => {
999
1013
  onSortChanged={params => {
1000
1014
  const sm = params.columns;
1001
1015
  if (sm.length == 1 && sm.some(s => s.colId === 'IZ_groupCount')) {
1002
- params.api.refreshServerSide({ purge: true })
1016
+ params.api.refreshServerSide({purge: true})
1003
1017
  }
1004
1018
  }}
1005
1019
  />
@@ -1019,7 +1033,7 @@ const TAGGrid = props => {
1019
1033
  pageName={router.pathname}
1020
1034
  item={isTemplateEditing === true ? selectedTemplate : null}
1021
1035
  template={gridApi?.getColumnState()}
1022
- userId={authValues?.user?.id} />
1036
+ userId={authValues?.user?.id}/>
1023
1037
  </Dialog>
1024
1038
 
1025
1039
  <Dialog
@@ -1027,7 +1041,7 @@ const TAGGrid = props => {
1027
1041
  open={openDialogs.CustomFilter && localClasName}
1028
1042
  maxWidth='xl'
1029
1043
  scroll='body'
1030
- // onClose={() => handleToggleDialogs('CustomFilter')}
1044
+ // onClose={() => handleToggleDialogs('CustomFilter')}
1031
1045
  >
1032
1046
  <CustomFilterDialog
1033
1047
  handleToggleDialogs={handleToggleDialogs}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Convert string-based functions to executable functions
3
+ * Used for extraCols and columnsConfig that come from JSON configuration
4
+ */
5
+
6
+ // Import helper functions that need to be available in the converted functions
7
+ import {
8
+ setUpdateRefValue,
9
+ setUpdateRefRow,
10
+ getUpdateRefValue,
11
+ hasUpdateRefValue,
12
+ removeUpdateRefByRowId,
13
+ clearUpdateRefRow,
14
+ clearAllUpdateRef,
15
+ cloneUpdateRefToOriginal,
16
+ restoreUpdateRefFromOriginal
17
+ } from './updateRefHelpers'
18
+
19
+ /**
20
+ * Convert a string function to an executable function
21
+ * Injects helper functions into the scope so they're available when the function executes
22
+ * @param {string} funcString - The function as a string
23
+ * @returns {Function|string} The converted function or original string if conversion fails
24
+ */
25
+ export const convertStringToFunction = (funcString) => {
26
+ if (typeof funcString !== 'string' || !funcString.trim()) {
27
+ return funcString
28
+ }
29
+
30
+ try {
31
+ // Check if it looks like a function
32
+ const trimmed = funcString.trim()
33
+
34
+ // Arrow function: (params) => { ... } or (params) => ...
35
+ if (trimmed.startsWith('(') && trimmed.includes('=>')) {
36
+ // Create a wrapper that provides helper functions in scope
37
+ // We wrap the user's function and inject the helpers as local variables
38
+ // eslint-disable-next-line no-new-func
39
+ const wrapper = new Function(
40
+ 'setUpdateRefValue',
41
+ 'setUpdateRefRow',
42
+ 'getUpdateRefValue',
43
+ 'hasUpdateRefValue',
44
+ 'removeUpdateRefByRowId',
45
+ 'clearUpdateRefRow',
46
+ 'clearAllUpdateRef',
47
+ 'cloneUpdateRefToOriginal',
48
+ 'restoreUpdateRefFromOriginal',
49
+ `"use strict";
50
+ const userFunc = ${trimmed};
51
+ return userFunc;`
52
+ )
53
+ return wrapper(setUpdateRefValue, setUpdateRefRow, getUpdateRefValue, hasUpdateRefValue, removeUpdateRefByRowId, clearUpdateRefRow, clearAllUpdateRef, cloneUpdateRefToOriginal, restoreUpdateRefFromOriginal)
54
+ }
55
+
56
+ // Regular function: function(params) { ... }
57
+ if (trimmed.startsWith('function')) {
58
+ // Create a wrapper that provides helper functions in scope
59
+ // eslint-disable-next-line no-new-func
60
+ const wrapper = new Function(
61
+ 'setUpdateRefValue',
62
+ 'setUpdateRefRow',
63
+ 'getUpdateRefValue',
64
+ 'hasUpdateRefValue',
65
+ 'removeUpdateRefByRowId',
66
+ 'clearUpdateRefRow',
67
+ 'clearAllUpdateRef',
68
+ 'cloneUpdateRefToOriginal',
69
+ 'restoreUpdateRefFromOriginal',
70
+ `"use strict";
71
+ const userFunc = ${trimmed};
72
+ return userFunc;`
73
+ )
74
+ return wrapper(setUpdateRefValue, setUpdateRefRow, getUpdateRefValue, hasUpdateRefValue, removeUpdateRefByRowId, clearUpdateRefRow, clearAllUpdateRef, cloneUpdateRefToOriginal, restoreUpdateRefFromOriginal)
75
+ }
76
+
77
+ // Expression function (old AG Grid style): return params.value
78
+ if (trimmed.startsWith('return ')) {
79
+ // For expression functions, wrap in a function with helpers available
80
+ // eslint-disable-next-line no-new-func
81
+ const wrapper = new Function(
82
+ 'setUpdateRefValue',
83
+ 'setUpdateRefRow',
84
+ 'getUpdateRefValue',
85
+ 'hasUpdateRefValue',
86
+ 'removeUpdateRefByRowId',
87
+ 'clearUpdateRefRow',
88
+ 'clearAllUpdateRef',
89
+ 'cloneUpdateRefToOriginal',
90
+ 'restoreUpdateRefFromOriginal',
91
+ `"use strict";
92
+ return function(params) {
93
+ ${trimmed}
94
+ };`
95
+ )
96
+ return wrapper(setUpdateRefValue, setUpdateRefRow, getUpdateRefValue, hasUpdateRefValue, removeUpdateRefByRowId, clearUpdateRefRow, clearAllUpdateRef, cloneUpdateRefToOriginal, restoreUpdateRefFromOriginal)
97
+ }
98
+
99
+ // If not a function format, return as is
100
+ return funcString
101
+ } catch (e) {
102
+ console.error('Failed to convert string to function:', e)
103
+ console.error('Function string:', funcString)
104
+ console.error('Stack:', e.stack)
105
+ return funcString
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Process a column definition and convert all string functions to executable functions
111
+ * @param {object} colDef - The column definition object
112
+ * @returns {object} The processed column definition with converted functions
113
+ */
114
+ export const processColumnDefinition = (colDef) => {
115
+ if (!colDef || typeof colDef !== 'object') {
116
+ return colDef
117
+ }
118
+
119
+ const processed = {...colDef}
120
+
121
+ // List of AG Grid properties that should be functions
122
+ const functionProperties = [
123
+ 'valueSetter',
124
+ 'valueGetter',
125
+ 'valueFormatter',
126
+ 'valueParser',
127
+ 'cellRenderer',
128
+ 'cellEditor',
129
+ 'cellClass',
130
+ 'cellStyle',
131
+ 'editable',
132
+ 'comparator',
133
+ 'equals',
134
+ 'keyCreator',
135
+ 'filterValueGetter',
136
+ 'filterParams',
137
+ 'cellEditorParams',
138
+ 'cellRendererParams',
139
+ 'headerValueGetter',
140
+ 'tooltipValueGetter',
141
+ 'aggFunc',
142
+ 'getQuickFilterText',
143
+ 'suppressKeyboardEvent',
144
+ 'suppressPaste',
145
+ 'cellClassRules'
146
+ ]
147
+
148
+ functionProperties.forEach(prop => {
149
+ if (prop in processed && typeof processed[prop] === 'string') {
150
+ const converted = convertStringToFunction(processed[prop])
151
+ if (converted !== processed[prop]) {
152
+ processed[prop] = converted
153
+ }
154
+ }
155
+ })
156
+
157
+ // Special handling for cellEditorParams - can be a function or object
158
+ if ('cellEditorParams' in processed) {
159
+ if (typeof processed.cellEditorParams === 'string') {
160
+ processed.cellEditorParams = convertStringToFunction(processed.cellEditorParams)
161
+ }
162
+ }
163
+
164
+ return processed
165
+ }
166
+
167
+ /**
168
+ * Process an array of column definitions
169
+ * @param {array} colDefs - Array of column definitions
170
+ * @returns {array} Processed column definitions
171
+ */
172
+ export const processColumnDefinitions = (colDefs) => {
173
+ if (!Array.isArray(colDefs)) {
174
+ return colDefs
175
+ }
176
+
177
+ return colDefs.map(colDef => processColumnDefinition(colDef))
178
+ }
179
+
180
+ /**
181
+ * Process columnsConfig array (field + config format)
182
+ * @param {array} columnsConfig - Array of column configs [{field, config}]
183
+ * @returns {array} Processed column configs
184
+ */
185
+ export const processColumnsConfig = (columnsConfig) => {
186
+ if (!Array.isArray(columnsConfig)) {
187
+ return columnsConfig
188
+ }
189
+
190
+ return columnsConfig.map(item => {
191
+ if (!item || !item.config) {
192
+ return item
193
+ }
194
+
195
+ return {
196
+ ...item,
197
+ config: processColumnDefinition(item.config)
198
+ }
199
+ })
200
+ }