@visns-studio/visns-components 5.11.20 → 5.11.22

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 (159) hide show
  1. package/package.json +5 -5
  2. package/src/components/{crm/Autocomplete.jsx → Autocomplete.jsx} +1 -1
  3. package/src/components/{crm/BusinessCardOcr.jsx → BusinessCardOcr.jsx} +1 -1
  4. package/src/components/{crm/DataGrid.jsx → DataGrid.jsx} +27 -4
  5. package/src/components/{crm/Download.jsx → Download.jsx} +1 -1
  6. package/src/components/{cms/DropZone.jsx → DropZone.jsx} +1 -1
  7. package/src/components/{crm/Field.jsx → Field.jsx} +2 -2
  8. package/src/components/{crm/Form.jsx → Form.jsx} +1 -1
  9. package/src/components/{crm/auth → auth}/ClientLogin.jsx +2 -2
  10. package/src/components/{crm/auth → auth}/ClientOTPVerify.jsx +2 -2
  11. package/src/components/{crm/auth → auth}/Login.jsx +2 -2
  12. package/src/components/{crm/auth → auth}/Profile.jsx +3 -3
  13. package/src/components/{crm/auth → auth}/Reset.jsx +2 -2
  14. package/src/components/{crm/auth → auth}/TwoFactorAuth.jsx +2 -2
  15. package/src/components/{crm/auth → auth}/Verify.jsx +2 -2
  16. package/src/components/{crm/client → client}/ClientDashboard.jsx +1 -1
  17. package/src/components/{crm/client → client}/ClientPortal.jsx +1 -1
  18. package/src/components/{crm/columns → columns}/ColumnRenderers.jsx +171 -3
  19. package/src/components/{crm/generic → generic}/ActionButtons.jsx +1 -1
  20. package/src/components/{crm/generic → generic}/AlternativeActionModal.jsx +2 -2
  21. package/src/components/{crm/generic → generic}/AuditLog.jsx +1 -1
  22. package/src/components/{crm/generic → generic}/AuditLogs.jsx +1 -1
  23. package/src/components/{cms/generic → generic}/CmsDetail.jsx +2 -2
  24. package/src/components/{cms/generic → generic}/CmsSort.jsx +1 -1
  25. package/src/components/{crm/generic → generic}/ConfirmationDialog.jsx +1 -1
  26. package/src/components/{crm/generic → generic}/ContactSelectorModal.jsx +2 -2
  27. package/src/components/{crm/generic → generic}/DatePickerDialog.jsx +2 -2
  28. package/src/components/{crm/generic → generic}/DateRangeSelectorModal.jsx +2 -2
  29. package/src/components/{crm/generic → generic}/GenericClientPortal.jsx +1 -1
  30. package/src/components/{crm/generic → generic}/GenericDashboard.jsx +4 -4
  31. package/src/components/{crm/generic → generic}/GenericDetail.jsx +5 -5
  32. package/src/components/{crm/generic → generic}/GenericDynamic.jsx +1 -1
  33. package/src/components/{crm/generic → generic}/GenericEditableTable.jsx +1 -1
  34. package/src/components/{crm/generic → generic}/GenericFormBuilder.jsx +2 -2
  35. package/src/components/{crm/generic → generic}/GenericGrid.jsx +2 -2
  36. package/src/components/{crm/generic → generic}/GenericIndex.jsx +2 -2
  37. package/src/components/{crm/generic → generic}/GenericMain.jsx +2 -2
  38. package/src/components/{crm/generic → generic}/GenericQuote.jsx +4 -4
  39. package/src/components/{crm/generic → generic}/GenericReport.jsx +2 -2
  40. package/src/components/{crm/generic → generic}/GenericReportForm.jsx +1 -1
  41. package/src/components/{crm/generic → generic}/GenericSort.jsx +2 -2
  42. package/src/components/{crm/generic → generic}/NotificationList.jsx +1 -1
  43. package/src/components/{crm/generic → generic}/ReasonCollectorModal.jsx +2 -2
  44. package/src/components/{crm/generic → generic}/SortableQuoteItems.jsx +1 -1
  45. package/src/components/proposal/SectionEditor.jsx +1 -1
  46. package/src/components/{crm/sorting → sorting}/Item.jsx +1 -1
  47. package/src/components/{crm/sorting → sorting}/List.jsx +1 -1
  48. package/src/index.js +57 -59
  49. package/src/components/cms/DataGrid.jsx +0 -1652
  50. package/src/components/cms/Field.jsx +0 -802
  51. package/src/components/cms/Form.jsx +0 -475
  52. package/src/components/cms/sorting/Item.jsx +0 -121
  53. package/src/components/cms/sorting/List.jsx +0 -32
  54. package/src/components/cms/sorting/styles/Item.module.scss +0 -38
  55. package/src/components/crm/sorting/styles/List.module.scss +0 -74
  56. /package/src/components/{crm/AssociationManager.jsx → AssociationManager.jsx} +0 -0
  57. /package/src/components/{crm/AsyncSelect.jsx → AsyncSelect.jsx} +0 -0
  58. /package/src/components/{crm/Breadcrumb.jsx → Breadcrumb.jsx} +0 -0
  59. /package/src/components/{crm/Call.jsx → Call.jsx} +0 -0
  60. /package/src/components/{crm/ClientAssociationManager.jsx → ClientAssociationManager.jsx} +0 -0
  61. /package/src/components/{crm/Fetch.jsx → Fetch.jsx} +0 -0
  62. /package/src/components/{cms/Gallery.jsx → Gallery.jsx} +0 -0
  63. /package/src/components/{crm/Loader.jsx → Loader.jsx} +0 -0
  64. /package/src/components/{crm/MergeEntity.jsx → MergeEntity.jsx} +0 -0
  65. /package/src/components/{crm/MultiSelect.jsx → MultiSelect.jsx} +0 -0
  66. /package/src/components/{crm/Navigation.jsx → Navigation.jsx} +0 -0
  67. /package/src/components/{crm/Notification.jsx → Notification.jsx} +0 -0
  68. /package/src/components/{crm/QrCode.jsx → QrCode.jsx} +0 -0
  69. /package/src/components/{crm/QuickAction.jsx → QuickAction.jsx} +0 -0
  70. /package/src/components/{crm/SectionHeader.jsx → SectionHeader.jsx} +0 -0
  71. /package/src/components/{crm/Select.jsx → Select.jsx} +0 -0
  72. /package/src/components/{crm/SelectList.jsx → SelectList.jsx} +0 -0
  73. /package/src/components/{crm/SwitchAccount.jsx → SwitchAccount.jsx} +0 -0
  74. /package/src/components/{crm/TableFilter.jsx → TableFilter.jsx} +0 -0
  75. /package/src/components/{crm/cells → cells}/CellWithTooltip.jsx +0 -0
  76. /package/src/components/{crm/controls → controls}/AudioPlayer.jsx +0 -0
  77. /package/src/components/{crm/controls → controls}/AutoRefreshControls.jsx +0 -0
  78. /package/src/components/{crm/controls → controls}/DataGridSearch.jsx +0 -0
  79. /package/src/components/{crm/examples → examples}/FilterExample.jsx +0 -0
  80. /package/src/components/{crm/examples → examples}/FormStylingGuide.md +0 -0
  81. /package/src/components/{crm/examples → examples}/FormWithSectionHeaders.jsx +0 -0
  82. /package/src/components/{crm/examples → examples}/README.md +0 -0
  83. /package/src/components/{cms/generic → generic}/CmsIndex.jsx +0 -0
  84. /package/src/components/{crm/generic → generic}/GenericAuth.jsx +0 -0
  85. /package/src/components/{crm/generic → generic}/shared/formatters.js +0 -0
  86. /package/src/components/{crm/generic → generic}/shared/gridUtils.js +0 -0
  87. /package/src/components/{crm/generic → generic}/utils/formatters.js +0 -0
  88. /package/src/components/{crm/modals → modals}/GalleryModal.jsx +0 -0
  89. /package/src/components/{crm/sketch → sketch}/SketchField.jsx +0 -0
  90. /package/src/components/{crm/sketch → sketch}/arrow.jsx +0 -0
  91. /package/src/components/{crm/sketch → sketch}/circle.jsx +0 -0
  92. /package/src/components/{crm/sketch → sketch}/default-tool.jsx +0 -0
  93. /package/src/components/{crm/sketch → sketch}/fabrictool.jsx +0 -0
  94. /package/src/components/{crm/sketch → sketch}/history.jsx +0 -0
  95. /package/src/components/{crm/sketch → sketch}/json/config.json +0 -0
  96. /package/src/components/{crm/sketch → sketch}/line.jsx +0 -0
  97. /package/src/components/{crm/sketch → sketch}/pan.jsx +0 -0
  98. /package/src/components/{crm/sketch → sketch}/pencil.jsx +0 -0
  99. /package/src/components/{crm/sketch → sketch}/rectangle-label-object.jsx +0 -0
  100. /package/src/components/{crm/sketch → sketch}/rectangle-label.jsx +0 -0
  101. /package/src/components/{crm/sketch → sketch}/rectangle.jsx +0 -0
  102. /package/src/components/{crm/sketch → sketch}/select.jsx +0 -0
  103. /package/src/components/{crm/sketch → sketch}/tools.jsx +0 -0
  104. /package/src/components/{crm/sketch → sketch}/utils.jsx +0 -0
  105. /package/src/components/{crm/generic/styles → styles}/ActionButtons.module.scss +0 -0
  106. /package/src/components/{crm/generic/styles → styles}/AlternativeActionModal.css +0 -0
  107. /package/src/components/{crm/styles → styles}/AssociationManager.module.scss +0 -0
  108. /package/src/components/{crm/styles → styles}/AsyncSelect.module.scss +0 -0
  109. /package/src/components/{crm/generic/styles → styles}/AuditLog.module.scss +0 -0
  110. /package/src/components/{crm/generic/styles → styles}/AuditLogs.module.scss +0 -0
  111. /package/src/components/{crm/auth/styles → styles}/AuthForm.module.scss +0 -0
  112. /package/src/components/{crm/styles → styles}/Autocomplete.module.scss +0 -0
  113. /package/src/components/{crm/styles → styles}/BusinessCardOcr.module.scss +0 -0
  114. /package/src/components/{crm/styles → styles}/ClientAssociationManager.module.scss +0 -0
  115. /package/src/components/{crm/auth/styles → styles}/ClientAuth.module.scss +0 -0
  116. /package/src/components/{crm/client/styles → styles}/ClientDashboard.module.scss +0 -0
  117. /package/src/components/{crm/client/styles → styles}/ClientPortal.module.scss +0 -0
  118. /package/src/components/{crm/generic/styles → styles}/ContactSelectorModal.css +0 -0
  119. /package/src/components/{crm/styles → styles}/DataGrid.module.scss +0 -0
  120. /package/src/components/{crm/generic/styles → styles}/DatePickerDialog.css +0 -0
  121. /package/src/components/{crm/generic/styles → styles}/DateRangeSelectorModal.css +0 -0
  122. /package/src/components/{cms/styles → styles}/DropZone.module.css +0 -0
  123. /package/src/components/{crm/styles → styles}/Field.module.scss +0 -0
  124. /package/src/components/{crm/styles → styles}/Form.module.scss +0 -0
  125. /package/src/components/{cms/styles → styles}/Gallery.module.css +0 -0
  126. /package/src/components/{crm/generic/styles → styles}/GenericClientPortal.module.scss +0 -0
  127. /package/src/components/{crm/generic/styles → styles}/GenericDashboard.module.scss +0 -0
  128. /package/src/components/{crm/generic/styles → styles}/GenericDetail.module.scss +0 -0
  129. /package/src/components/{crm/generic/styles → styles}/GenericDynamic.module.scss +0 -0
  130. /package/src/components/{crm/generic/styles → styles}/GenericEditableTable.module.scss +0 -0
  131. /package/src/components/{crm/generic/styles → styles}/GenericFormBuilder.module.scss +0 -0
  132. /package/src/components/{crm/generic/styles → styles}/GenericIndex.module.scss +0 -0
  133. /package/src/components/{crm/generic/styles → styles}/GenericMain.module.scss +0 -0
  134. /package/src/components/{crm/generic/styles → styles}/GenericQuote.module.scss +0 -0
  135. /package/src/components/{crm/generic/styles → styles}/GenericReport.module.scss +0 -0
  136. /package/src/components/{crm/generic/styles → styles}/GenericSort.module.scss +0 -0
  137. /package/src/components/{crm/sorting/styles → styles}/Item.module.scss +0 -0
  138. /package/src/components/{cms/sorting/styles → styles}/List.module.scss +0 -0
  139. /package/src/components/{crm/styles → styles}/Loader.module.scss +0 -0
  140. /package/src/components/{crm/auth/styles → styles}/Login.module.scss +0 -0
  141. /package/src/components/{crm/styles → styles}/MergeEntity.module.scss +0 -0
  142. /package/src/components/{crm/styles → styles}/MultiSelect.module.scss +0 -0
  143. /package/src/components/{crm/styles → styles}/Navigation.module.scss +0 -0
  144. /package/src/components/{crm/styles → styles}/Notification.module.scss +0 -0
  145. /package/src/components/{crm/generic/styles → styles}/NotificationList.module.scss +0 -0
  146. /package/src/components/{crm/auth/styles → styles}/Profile.module.scss +0 -0
  147. /package/src/components/{crm/styles → styles}/QrCode.module.scss +0 -0
  148. /package/src/components/{crm/styles → styles}/QuickAction.module.scss +0 -0
  149. /package/src/components/{crm/generic/styles → styles}/ReasonCollectorModal.css +0 -0
  150. /package/src/components/{crm/generic/styles → styles}/ReportForm.module.scss +0 -0
  151. /package/src/components/{crm/auth/styles → styles}/Reset.module.scss +0 -0
  152. /package/src/components/{crm/generic/styles → styles}/SortableQuoteItems.module.scss +0 -0
  153. /package/src/components/{crm/generic/styles → styles}/SweetAlert.module.css +0 -0
  154. /package/src/components/{crm/styles → styles}/SwitchAccount.module.scss +0 -0
  155. /package/src/components/{crm/styles → styles}/TableFilter.module.scss +0 -0
  156. /package/src/components/{crm/auth/styles → styles}/TwoFactorAuth.module.scss +0 -0
  157. /package/src/components/{crm/auth/styles → styles}/Verify.module.scss +0 -0
  158. /package/src/components/{crm/styles → styles}/global-datagrid.css +0 -0
  159. /package/src/utils/{ConfirmDialog.js → ConfirmDialog.jsx} +0 -0
@@ -1,1652 +0,0 @@
1
- import React, { useCallback, useEffect, useState } from 'react';
2
- import { useNavigate } from 'react-router-dom';
3
- import debounce from 'lodash.debounce';
4
- import moment from 'moment';
5
- import numeral from 'numeral';
6
- import Popup from 'reactjs-popup';
7
- import parse from 'html-react-parser';
8
- import NumberFilter from '@visns-studio/visns-datagrid-enterprise/NumberFilter';
9
- import SelectFilter from '@visns-studio/visns-datagrid-enterprise/SelectFilter';
10
- import DateFilter from '@visns-studio/visns-datagrid-enterprise/DateFilter';
11
- import ReactDataGrid from '@visns-studio/visns-datagrid-enterprise';
12
- import { toast } from 'react-toastify';
13
- import Toggle from 'react-toggle';
14
- import fetchUtil from '../../utils/fetchUtil';
15
- import { confirmDialog } from '../utils/ConfirmDialog';
16
- import {
17
- Trash2 as Backspace,
18
- Check,
19
- Clock,
20
- Download as CloudDownload,
21
- Copy,
22
- Edit,
23
- Folder,
24
- Inbox,
25
- Network,
26
- Award as Ribbon,
27
- Search,
28
- Package as ShippingBoxV1,
29
- ToggleLeft as ToggleOff,
30
- ToggleRight as ToggleOnFill,
31
- AlertTriangle as TriangleAlert,
32
- } from 'lucide-react';
33
-
34
- import '@visns-studio/visns-datagrid-enterprise/index.css';
35
- import 'react-toggle/style.css';
36
-
37
- import CustomFetch from '../crm/Fetch';
38
- import Form from './Form';
39
- import { extractColumnsMetadata, isColumnSortable } from '../../utils/columnsMetadataUtils';
40
-
41
- const loadData = async (
42
- { skip, limit, sortInfo, currentData, filterValue, groupBy },
43
- search,
44
- ajaxSetting
45
- ) => {
46
- const url = ajaxSetting.url;
47
- const page = Math.floor(skip / limit) + 1;
48
- const params = { page, sortInfo, take: limit, search: search, where: [] };
49
-
50
- if (sortInfo) {
51
- params.sortBy = sortInfo.name;
52
- params.sort = sortInfo.dir === 1 ? 'asc' : 'desc';
53
- }
54
-
55
- if (ajaxSetting && ajaxSetting.where) {
56
- params.where = ajaxSetting.where;
57
- }
58
-
59
- if (filterValue) {
60
- filterValue.forEach((fv) => {
61
- const identifier = fv.key ? fv.key : fv.name;
62
- const existingIndex = params.where.findIndex(
63
- (w) => w.id === identifier
64
- );
65
-
66
- if (fv.value !== null && fv.value !== '') {
67
- if (existingIndex !== -1) {
68
- params.where[existingIndex].value = fv.value;
69
- } else {
70
- params.where.push({
71
- id: identifier,
72
- value: fv.value,
73
- operator: fv.operator,
74
- });
75
- }
76
- } else {
77
- if (existingIndex !== -1) {
78
- params.where.splice(existingIndex, 1);
79
- }
80
- }
81
- });
82
- }
83
-
84
- try {
85
- const response = await fetchUtil.post(url, params);
86
- const { data, total } = response.data;
87
-
88
- // Extract columns metadata if available
89
- const metadata = extractColumnsMetadata(response);
90
-
91
- return { data, count: total, metadata };
92
- } catch (error) {
93
- console.error('Error fetching data:', error);
94
- return { data: [], count: 0, metadata: {} };
95
- }
96
- };
97
-
98
- const DataGrid = ({
99
- ajaxSetting,
100
- columns,
101
- form,
102
- gridHeight,
103
- settings,
104
- setFilterData,
105
- setConfig,
106
- style,
107
- }) => {
108
- const navigate = useNavigate();
109
-
110
- /** Modal States */
111
- const [formData, setFormData] = useState(form);
112
- const [formType, setFormType] = useState('');
113
- const [formId, setFormId] = useState(0);
114
- const [modalShow, setModalShow] = useState(false);
115
-
116
- /** Modal Functions */
117
- const childDropdownCallback = (data) => {
118
- let _form = { ...formData };
119
-
120
- if (formData.fields.length > 0) {
121
- formData.fields.forEach((a, b) => {
122
- if (a.id === data.id) {
123
- _form.fields[b].options = data.data;
124
- }
125
- });
126
- }
127
-
128
- setFormData(_form);
129
- };
130
-
131
- const modalOpen = (formType, formId) => {
132
- setModalShow(true);
133
- setFormType(formType);
134
- setFormId(formId);
135
- };
136
-
137
- const modalClose = () => {
138
- setModalShow(false);
139
- };
140
-
141
- useEffect(() => {
142
- setFormData(form);
143
- }, [form]);
144
-
145
- /** Table States */
146
- const [dataReload, setDataReload] = useState(0);
147
- const [dSearch, setDSearch] = useState('');
148
- const data = useCallback(
149
- (params) => {
150
- const sqlResult = loadData(params, dSearch, ajaxSetting);
151
-
152
- sqlResult.then((res) => {
153
- setDataCount(res.count);
154
- // Update metadata when data is loaded
155
- if (res.metadata) {
156
- setColumnsMetadata(res.metadata);
157
- }
158
- });
159
-
160
- return sqlResult.then(res => ({
161
- data: res.data,
162
- count: res.count
163
- }));
164
- },
165
- [ajaxSetting, dataReload, dSearch]
166
- );
167
- const [filterDataSource, setFilterDataSource] = useState([]);
168
- const [filterValue, setFilterValue] = useState([]);
169
- const [gridColumns, setGridColumns] = useState([]);
170
- const [limit, setLimit] = useState(ajaxSetting.take || 10);
171
- const [search, setSearch] = useState('');
172
- const [columnsMetadata, setColumnsMetadata] = useState({});
173
-
174
- /** Table Functions */
175
- const handleChangeSearch = (e) => {
176
- const { value } = e.target;
177
- setSearch(value);
178
- };
179
-
180
- const handleSettingClick = (s, d) => {
181
- switch (s.id) {
182
- case 'update':
183
- modalOpen('update', d.id);
184
- break;
185
- case 'delete':
186
- confirmDialog({
187
- title: 'Delete Item',
188
- message: `Are you sure you want to delete ${
189
- d.name ? d.name : d.label ? d.label : 'this item'
190
- }?`,
191
- data: d, // Pass the row data
192
- buttons: [
193
- {
194
- label: 'Yes',
195
- onClick: () =>
196
- CustomFetch(
197
- form.url + '/' + d[form.primaryKey],
198
- 'DELETE',
199
- {},
200
- (result) => {
201
- if (result.error === '') {
202
- const min = 0;
203
- const max = 999999999;
204
- const randomNumber = Math.floor(
205
- Math.random() *
206
- (max - min + 1) +
207
- min
208
- );
209
-
210
- setDataReload(randomNumber);
211
-
212
- toast.success(
213
- `The selected item has been deleted.`
214
- );
215
- } else {
216
- toast.error(String(result.error));
217
- }
218
- }
219
- ),
220
- },
221
- {
222
- label: 'No',
223
- onClick: () => close(),
224
- },
225
- ],
226
- });
227
- break;
228
- default:
229
- confirmDialog({
230
- title: s.title,
231
- message: '',
232
- data: d, // Pass the row data
233
- buttons: [
234
- {
235
- label: 'Yes',
236
- onClick: () =>
237
- CustomFetch(
238
- s.url + '/' + d[form.primaryKey],
239
- s.method,
240
- {},
241
- function (result) {
242
- if (result.error === '') {
243
- const min = 0;
244
- const max = 999999999;
245
- const randomNumber = Math.floor(
246
- Math.random() *
247
- (max - min + 1) +
248
- min
249
- );
250
-
251
- setDataReload(randomNumber);
252
-
253
- toast.success(String(s.title));
254
- } else {
255
- toast.error(String(result.error));
256
- }
257
- }
258
- ),
259
- },
260
- {
261
- label: 'No',
262
- onClick: () => close(),
263
- },
264
- ],
265
- });
266
- break;
267
- }
268
- };
269
-
270
- const handleReload = () => {
271
- const min = 0;
272
- const max = 999999999;
273
- const randomNumber = Math.floor(Math.random() * (max - min + 1) + min);
274
-
275
- setDataReload(randomNumber);
276
- };
277
-
278
- const handleSort = (value) => {
279
- setConfig((prevState) => {
280
- const { id, dir } = value || {}; // Destructure id and dir from value (if provided)
281
- const sortBy = id || null;
282
- const sort = dir === 1 ? 'asc' : dir === -1 ? 'desc' : null;
283
-
284
- return {
285
- ...prevState,
286
- ajaxSetting: {
287
- ...prevState.ajaxSetting,
288
- sortBy,
289
- sort,
290
- },
291
- };
292
- });
293
- };
294
-
295
- const onRowClick = useCallback(
296
- (rowProps, event) => {
297
- // Safely get cell element and column index
298
- const cellElement = event.target.closest(
299
- '.InovuaReactDataGrid__cell'
300
- );
301
-
302
- // Return early if we can't find the cell element
303
- if (!cellElement || !cellElement.parentNode) {
304
- // Fallback: open edit modal if available
305
- const updateSetting = settings.find(setting => setting.id === 'update');
306
- if (updateSetting && form.primaryKey && rowProps.data[form.primaryKey]) {
307
- modalOpen('update', rowProps.data[form.primaryKey]);
308
- }
309
- return;
310
- }
311
-
312
- const columnIndex = Array.from(
313
- cellElement.parentNode.children
314
- ).indexOf(cellElement);
315
-
316
- // Return early if column index is invalid
317
- if (columnIndex === -1 || !rowProps.columns[columnIndex]) {
318
- // Fallback: open edit modal if available
319
- const updateSetting = settings.find(setting => setting.id === 'update');
320
- if (updateSetting && form.primaryKey && rowProps.data[form.primaryKey]) {
321
- modalOpen('update', rowProps.data[form.primaryKey]);
322
- }
323
- return;
324
- }
325
-
326
- let proceed = true;
327
-
328
- // Check if clicked column is a toggle type
329
- columns.forEach((a, b) => {
330
- if (
331
- a.id === rowProps.columns[columnIndex].id &&
332
- a.type === 'toggle'
333
- ) {
334
- proceed = false;
335
- }
336
- });
337
-
338
- if (proceed) {
339
- // Check if column has navigation link
340
- if (
341
- rowProps.columns[columnIndex].link &&
342
- rowProps.columns[columnIndex].link.url
343
- ) {
344
- if (rowProps.columns[columnIndex].link.folder) {
345
- window.location.href =
346
- rowProps.columns[columnIndex].link.url +
347
- rowProps.data[
348
- rowProps.columns[columnIndex].link.key
349
- ] +
350
- '/' +
351
- rowProps.columns[columnIndex].link.folder;
352
- } else {
353
- navigate(
354
- `${rowProps.columns[columnIndex].link.url}${
355
- rowProps.data[
356
- rowProps.columns[columnIndex].link.key
357
- ]
358
- }`,
359
- {
360
- state: {
361
- form: form,
362
- },
363
- }
364
- );
365
- }
366
- } else {
367
- // Fallback: open edit modal if no navigation link is configured
368
- const updateSetting = settings.find(setting => setting.id === 'update');
369
- if (updateSetting && form.primaryKey && rowProps.data[form.primaryKey]) {
370
- modalOpen('update', rowProps.data[form.primaryKey]);
371
- }
372
- }
373
- }
374
- },
375
- [form, settings, modalOpen]
376
- );
377
-
378
- const onRenderRow = useCallback((rowProps) => {
379
- // save the original handlers to be called later
380
- const { onClick } = rowProps;
381
-
382
- rowProps.onClick = (event) => {
383
- onRowClick(rowProps, event);
384
- if (onClick) {
385
- onClick(event);
386
- }
387
- };
388
- }, []);
389
-
390
- const sortTrigger = () => {
391
- const url = form.url.replace('/ajax', '');
392
- const newState = {
393
- form: form,
394
- };
395
-
396
- navigate(`${url}/sort`, {
397
- state: newState,
398
- });
399
- };
400
-
401
- /** Table Hooks */
402
- useEffect(() => {
403
- const delayedSetDSearch = debounce(() => {
404
- setDSearch(search);
405
- }, 300); // Adjust the delay (in milliseconds) according to your needs
406
-
407
- delayedSetDSearch();
408
-
409
- return delayedSetDSearch.cancel; // Cleanup the debounce timer on unmount
410
- }, [search, setDSearch]);
411
-
412
- const renderSetting = (s, d) => {
413
- let iconStyle = {};
414
-
415
- if (s.active) {
416
- if (
417
- d.hasOwnProperty(s.active.id) &&
418
- !s.active.hasOwnProperty('type') &&
419
- d[s.active.id] === s.active.value
420
- ) {
421
- iconStyle = {
422
- color: s.active.colour,
423
- };
424
- } else if (s.active.hasOwnProperty('type')) {
425
- switch (s.active.type) {
426
- case 'greater':
427
- if (d[s.active.id] > s.active.value) {
428
- iconStyle = {
429
- color: s.active.colour,
430
- };
431
- }
432
- break;
433
- case 'not null':
434
- if (d[s.active.id] !== null) {
435
- iconStyle = {
436
- color: s.active.colour,
437
- };
438
- }
439
- break;
440
- default:
441
- break;
442
- }
443
- }
444
- }
445
-
446
- const getIconComponent = (IconComponent) => (
447
- <IconComponent
448
- data-tooltip-id="system-tooltip"
449
- data-tooltip-content={
450
- iconStyle && iconStyle.color ? s.active.title : s.title
451
- }
452
- strokeWidth={2}
453
- size={18}
454
- className="tdaction"
455
- style={iconStyle}
456
- />
457
- );
458
-
459
- switch (s.id) {
460
- case 'complete':
461
- return getIconComponent(Check);
462
- case 'delete':
463
- return getIconComponent(Backspace);
464
- case 'primary':
465
- return getIconComponent(Ribbon);
466
- case 'ssa':
467
- return getIconComponent(Clock);
468
- case 'update':
469
- return getIconComponent(Edit);
470
- default:
471
- return null;
472
- }
473
- };
474
-
475
- const renderColumnContextMenu = useCallback((menuProps, { cellProps }) => {
476
- const filteredItems = menuProps.items
477
- .map((item, key) => {
478
- if (key >= 6 && key <= 9) {
479
- return null; // Skip items with keys 6-9 [lock columns setting]
480
- }
481
- return item;
482
- })
483
- .filter(Boolean); // Filter out any null items
484
-
485
- menuProps.items = filteredItems;
486
- }, []);
487
-
488
- useEffect(() => {
489
- const renderColumn = (column) => {
490
- let childValue;
491
- let date;
492
- let filterEditor = null;
493
- let filterEditorProps = null;
494
- let icons = [];
495
- let relationName;
496
- let selectedDataSource = null;
497
- let stage;
498
- let style;
499
- let value;
500
-
501
- // Check if column is sortable based on metadata
502
- const columnKey = column.id;
503
- const sortable = isColumnSortable(columnKey, columnsMetadata);
504
-
505
- const commonProps = {
506
- header: column.label,
507
- defaultFlex: 1,
508
- sortable: sortable,
509
- link: column.link ? column.link : {},
510
- minWidth:
511
- column.minWidth && column.minWidth > 0
512
- ? column.minWidth
513
- : undefined,
514
- maxWidth:
515
- column.maxWidth && column.maxWidth > 0
516
- ? column.maxWidth
517
- : undefined,
518
- };
519
-
520
- switch (column.type) {
521
- case 'address':
522
- return {
523
- ...commonProps,
524
- name: `${column.type}.${column.id}`,
525
- render: ({ data }) => {
526
- if (data.address1) {
527
- value += `${data.address1} `;
528
- }
529
-
530
- if (data.address2) {
531
- value += `${data.address2} `;
532
- }
533
-
534
- if (data.suburb) {
535
- value += `${data.suburb} `;
536
- }
537
-
538
- if (data.postcode) {
539
- value += `${data.postcode} `;
540
- }
541
- },
542
- };
543
- case 'age':
544
- return {
545
- ...commonProps,
546
- name: `${column.type}.${column.id}`,
547
- render: ({ data }) => {
548
- value = column.id.reduce((acc, id) => {
549
- if (acc === '') {
550
- return data[id];
551
- } else {
552
- return acc[id];
553
- }
554
- }, '');
555
-
556
- if (value !== '' && value !== null) {
557
- date = moment(value);
558
-
559
- return (
560
- <span>
561
- {moment().diff(
562
- date,
563
- column.duration
564
- ? column.duration
565
- : 'years'
566
- )}
567
- </span>
568
- );
569
- } else {
570
- return null;
571
- }
572
- },
573
- };
574
- case 'arrayCount':
575
- return {
576
- ...commonProps,
577
- name: `${column.type}.${column.id}`,
578
- sortable: false,
579
- render: ({ data }) => {
580
- if (data && data[column.id]) {
581
- return data[column.id].length;
582
- } else {
583
- return null;
584
- }
585
- },
586
- };
587
- case 'coding':
588
- return {
589
- ...commonProps,
590
- name: 'audits.name',
591
- sortable: false,
592
- render: ({ data }) => {
593
- if (data) {
594
- return handleCoding(column, data);
595
- } else {
596
- return null;
597
- }
598
- },
599
- };
600
- case 'created_by':
601
- return {
602
- ...commonProps,
603
- name: 'audits.name',
604
- sortable: false,
605
- render: ({ data }) => {
606
- if (
607
- data.audits[0] &&
608
- data.audits[0].user &&
609
- data.audits[0].user.name
610
- ) {
611
- value = data.audits[0].user.name;
612
-
613
- return <span>{value}</span>;
614
- } else {
615
- return null;
616
- }
617
- },
618
- };
619
- case 'currency':
620
- return {
621
- ...commonProps,
622
- name: column.id,
623
- render: ({ data }) => {
624
- if (data) {
625
- data = numeral(data[column.id]).format(
626
- '$0,0.00'
627
- );
628
-
629
- return <span>{data}</span>;
630
- } else {
631
- return null;
632
- }
633
- },
634
- };
635
- case 'date':
636
- if (column.filter && column.filter.type) {
637
- filterEditor = DateFilter;
638
- filterEditorProps = (props, { index }) => {
639
- return {
640
- dateFormat: 'DD-MM-YYYY',
641
- placeholder: 'All Dates',
642
- };
643
- };
644
- } else {
645
- filterEditor = null;
646
- filterEditorProps = null;
647
- }
648
-
649
- return {
650
- ...commonProps,
651
- name: column.id,
652
- filterEditor: filterEditor,
653
- filterEditorProps: filterEditorProps,
654
- render: ({ data }) => {
655
- if (
656
- data &&
657
- data[column.id] &&
658
- moment(data[column.id]).isValid() &&
659
- moment(data[column.id]).format('YYYY-MM-DD') !==
660
- '1970-01-01'
661
- ) {
662
- const value = moment(data[column.id]).format(
663
- column.format || 'DD-MM-YYYY'
664
- );
665
- style = {};
666
-
667
- if (
668
- column.active &&
669
- column.active.id &&
670
- column.active.value === 'expired'
671
- ) {
672
- if (
673
- data[column.active.id] &&
674
- moment(data[column.active.id]).isBefore(
675
- moment()
676
- )
677
- ) {
678
- style = {
679
- color: column.active.colour,
680
- };
681
- }
682
- }
683
-
684
- return <span style={style}>{value}</span>;
685
- } else {
686
- return null;
687
- }
688
- },
689
- };
690
- case 'datetime':
691
- if (column.filter && column.filter.type) {
692
- filterEditor = DateFilter;
693
- filterEditorProps = (props, { index }) => {
694
- return {
695
- dateFormat: 'DD-MM-YYYY',
696
- placeholder: 'All Dates',
697
- };
698
- };
699
- } else {
700
- filterEditor = null;
701
- filterEditorProps = null;
702
- }
703
-
704
- return {
705
- ...commonProps,
706
- name: column.id,
707
- filterEditor: filterEditor,
708
- filterEditorProps: filterEditorProps,
709
- render: ({ data }) => {
710
- if (
711
- data &&
712
- data[column.id] &&
713
- moment(data[column.id]).isValid() &&
714
- moment(data[column.id]).format('YYYY-MM-DD') !==
715
- '1970-01-01'
716
- ) {
717
- data = moment(data[column.id]).format(
718
- column.format
719
- ? column.format
720
- : 'DD-MM-YYYY hh:mm A'
721
- );
722
-
723
- return <span>{data}</span>;
724
- } else {
725
- return null;
726
- }
727
- },
728
- };
729
- case 'file':
730
- return {
731
- ...commonProps,
732
- name: column.id,
733
- sortable: false,
734
- render: ({ data }) => {
735
- if (data) {
736
- return (
737
- <span>
738
- <CloudDownload
739
- data-tooltip-id="system-tooltip"
740
- data-tooltip-content="Download File"
741
- strokeWidth={2}
742
- size={18}
743
- className="tdaction"
744
- />
745
- </span>
746
- );
747
- } else {
748
- return null;
749
- }
750
- },
751
- };
752
- case 'icons':
753
- return {
754
- ...commonProps,
755
- name: column.id,
756
- sortable: false,
757
- render: ({ data }) => {
758
- icons = [];
759
-
760
- if (column.icons && column.icons.length > 0) {
761
- column.icons.forEach((icon) => {
762
- icons.push(
763
- <img
764
- src={icon.url}
765
- alt={icon.name}
766
- data-tooltip-id="system-tooltip"
767
- data-tooltip-content={icon.name}
768
- />
769
- );
770
- });
771
- }
772
-
773
- if (data) {
774
- return <span>{icons}</span>;
775
- } else {
776
- return null;
777
- }
778
- },
779
- };
780
- case 'image':
781
- return {
782
- ...commonProps,
783
- name: column.id,
784
- render: ({ data }) => {
785
- if (data && data[column.id]) {
786
- const fileUrl =
787
- data[column.id][0]?.file_url ||
788
- data[column.id]?.file_url;
789
-
790
- if (fileUrl) {
791
- return (
792
- <span>
793
- {parse(
794
- `<img width="150" src="${fileUrl}" />`
795
- )}
796
- </span>
797
- );
798
- }
799
- }
800
-
801
- return null;
802
- },
803
- };
804
- case 'json':
805
- return {
806
- ...commonProps,
807
- name: `${column.id}-${column.jsonData}`,
808
- render: ({ data }) => {
809
- if (
810
- data &&
811
- data[column.id] &&
812
- data[column.id][column.jsonData]
813
- ) {
814
- const jsonValue = data[column.id][column.jsonData];
815
-
816
- if (!jsonValue) return null;
817
-
818
- try {
819
- const jsonData = typeof jsonValue === 'string' ? JSON.parse(jsonValue) : jsonValue;
820
-
821
- // Check if this looks like a question/answer format
822
- const isQuestionAnswerFormat = Object.values(jsonData).every(item =>
823
- item && typeof item === 'object' &&
824
- (item.hasOwnProperty('question') || item.hasOwnProperty('answer'))
825
- );
826
-
827
- if (isQuestionAnswerFormat) {
828
- return (
829
- <div style={{ fontSize: '0.8rem' }}>
830
- {Object.entries(jsonData).map(([key, item]) => {
831
- if (!item || typeof item !== 'object') return null;
832
-
833
- const question = item.question || key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
834
- const answer = item.answer !== undefined ?
835
- (typeof item.answer === 'boolean' ? (item.answer ? 'Yes' : 'No') : String(item.answer)) :
836
- 'N/A';
837
-
838
- return (
839
- <div key={key} style={{ marginBottom: '0.3rem' }}>
840
- <div style={{ fontWeight: '600', color: 'var(--primary-color)', marginBottom: '0.1rem', fontSize: '0.75rem' }}>
841
- {question.length > 60 ? question.substring(0, 60) + '...' : question}
842
- </div>
843
- <div style={{ marginLeft: '0.5rem', color: 'var(--paragraph-color)', fontSize: '0.75rem' }}>
844
- {answer}
845
- </div>
846
- </div>
847
- );
848
- })}
849
- </div>
850
- );
851
- }
852
-
853
- // Check if this is a simple key-value object
854
- const isSimpleKeyValue = Object.values(jsonData).every(val =>
855
- typeof val !== 'object' || val === null
856
- );
857
-
858
- if (isSimpleKeyValue) {
859
- return (
860
- <div style={{ fontSize: '0.8rem' }}>
861
- {Object.entries(jsonData).slice(0, 3).map(([key, val]) => (
862
- <div key={key} style={{ marginBottom: '0.2rem', display: 'flex', gap: '0.3rem' }}>
863
- <strong style={{ color: 'var(--primary-color)', fontSize: '0.7rem' }}>
864
- {key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:
865
- </strong>
866
- <span style={{ color: 'var(--paragraph-color)', fontSize: '0.7rem' }}>
867
- {val !== null && val !== undefined ?
868
- (typeof val === 'boolean' ? (val ? 'Yes' : 'No') : String(val).substring(0, 20)) :
869
- 'N/A'}
870
- </span>
871
- </div>
872
- ))}
873
- {Object.keys(jsonData).length > 3 && (
874
- <div style={{ fontSize: '0.65rem', color: 'var(--secondary-color)', fontStyle: 'italic' }}>
875
- +{Object.keys(jsonData).length - 3} more...
876
- </div>
877
- )}
878
- </div>
879
- );
880
- }
881
-
882
- // For complex nested objects, show a summary
883
- const keyCount = Object.keys(jsonData).length;
884
- return (
885
- <div style={{ fontSize: '0.75rem', color: 'var(--paragraph-color)', fontStyle: 'italic' }}>
886
- JSON ({keyCount} {keyCount === 1 ? 'property' : 'properties'})
887
- </div>
888
- );
889
-
890
- } catch (error) {
891
- // If parsing fails, show raw value truncated
892
- const displayValue = typeof jsonValue === 'string' && jsonValue.length > 30
893
- ? jsonValue.substring(0, 30) + '...'
894
- : jsonValue;
895
- return <span style={{ fontSize: '0.75rem', fontFamily: 'monospace', color: 'var(--paragraph-color)' }}>{displayValue}</span>;
896
- }
897
- } else {
898
- return null;
899
- }
900
- },
901
- };
902
- case 'number':
903
- return {
904
- ...commonProps,
905
- name: column.id,
906
- type: 'number',
907
- };
908
- case 'option':
909
- selectedDataSource = filterDataSource.reduce((acc, fds) => {
910
- if (fds.id === column.id) {
911
- return fds.dataset;
912
- }
913
- return acc;
914
- }, null);
915
-
916
- if (
917
- column.filter &&
918
- column.filter.type &&
919
- column.filter.type === 'select'
920
- ) {
921
- filterEditor = SelectFilter;
922
- filterEditorProps = {
923
- placeholder: column.filter.placeholder
924
- ? column.filter.placeholder
925
- : 'All Options',
926
- dataSource: selectedDataSource,
927
- };
928
- } else {
929
- filterEditor = null;
930
- filterEditorProps = null;
931
- }
932
-
933
- return {
934
- ...commonProps,
935
- name: column.id,
936
- filterEditor: filterEditor,
937
- filterEditorProps: filterEditorProps,
938
- render: ({ data }) => {
939
- if (
940
- data &&
941
- column.options &&
942
- column.options[data[column.id]]
943
- ) {
944
- data = column.options[data[column.id]];
945
-
946
- return <span>{data}</span>;
947
- } else {
948
- return null;
949
- }
950
- },
951
- };
952
- case 'placeholder':
953
- return {
954
- ...commonProps,
955
- name: column.id,
956
- sortable: false,
957
- render: ({ data }) => {
958
- if (data) {
959
- return <span>{column.placeholder}</span>;
960
- } else {
961
- return null;
962
- }
963
- },
964
- };
965
- case 'relation':
966
- relationName = column.id.reduce(
967
- (acc, id) => acc + `${id}.`,
968
- ''
969
- );
970
- relationName += column.nameFrom;
971
-
972
- selectedDataSource = filterDataSource.reduce((acc, fds) => {
973
- if (fds.id === relationName) {
974
- return fds.dataset;
975
- }
976
- return acc;
977
- }, null);
978
-
979
- if (
980
- column.filter &&
981
- column.filter.type &&
982
- column.filter.type === 'select'
983
- ) {
984
- filterEditor = SelectFilter;
985
- filterEditorProps = {
986
- placeholder: column.filter.placeholder
987
- ? column.filter.placeholder
988
- : 'All Options',
989
- dataSource: selectedDataSource,
990
- };
991
- } else {
992
- filterEditor = null;
993
- filterEditorProps = null;
994
- }
995
-
996
- return {
997
- ...commonProps,
998
- name: relationName,
999
- sortable: false,
1000
- filterEditor: filterEditor,
1001
- filterEditorProps: filterEditorProps,
1002
- render: ({ data }) => {
1003
- value = column.id.reduce((acc, id) => {
1004
- if (acc === '') {
1005
- return data[id];
1006
- } else {
1007
- if (acc && acc[id]) {
1008
- return acc[id];
1009
- } else {
1010
- return '';
1011
- }
1012
- }
1013
- }, '');
1014
-
1015
- if (value) {
1016
- if (Array.isArray(column.nameFrom)) {
1017
- value = column.nameFrom
1018
- .map((nf) => value[nf])
1019
- .join(' ');
1020
- } else {
1021
- value = value[column.nameFrom];
1022
- }
1023
-
1024
- if (
1025
- column.placeholder &&
1026
- column.placeholder !== ''
1027
- ) {
1028
- return <span>{column.placeholder}</span>;
1029
- } else {
1030
- return <span>{value}</span>;
1031
- }
1032
- } else {
1033
- return null;
1034
- }
1035
- },
1036
- };
1037
- case 'relationArray':
1038
- relationName =
1039
- column.id.reduce((acc, id) => acc + `${id}.`, '') +
1040
- column.nameFrom;
1041
-
1042
- selectedDataSource = filterDataSource.reduce((acc, fds) => {
1043
- if (fds.id === relationName) {
1044
- return fds.dataset;
1045
- }
1046
- return acc;
1047
- }, null);
1048
-
1049
- if (
1050
- column.filter &&
1051
- column.filter.type &&
1052
- column.filter.type === 'select'
1053
- ) {
1054
- filterEditor = SelectFilter;
1055
- filterEditorProps = {
1056
- placeholder: column.filter.placeholder
1057
- ? column.filter.placeholder
1058
- : 'All Options',
1059
- dataSource: selectedDataSource,
1060
- };
1061
- } else {
1062
- filterEditor = null;
1063
- filterEditorProps = null;
1064
- }
1065
-
1066
- return {
1067
- ...commonProps,
1068
- name: relationName,
1069
- sortable: false,
1070
- filterEditor: filterEditor,
1071
- filterEditorProps: filterEditorProps,
1072
- render: ({ data }) => {
1073
- const relationValue = column.id.reduce(
1074
- (acc, id) =>
1075
- acc === ''
1076
- ? data[id]
1077
- : acc && acc[id]
1078
- ? acc[id]
1079
- : '',
1080
- ''
1081
- );
1082
-
1083
- if (relationValue && Array.isArray(relationValue)) {
1084
- const nameProperties = Array.isArray(
1085
- column.nameFrom
1086
- )
1087
- ? column.nameFrom
1088
- : [column.nameFrom];
1089
-
1090
- const values = relationValue
1091
- .map((rv) => {
1092
- if (
1093
- column.childId &&
1094
- Array.isArray(column.childId)
1095
- ) {
1096
- childValue = rv;
1097
-
1098
- column.childId.forEach((a) => {
1099
- if (childValue[a]) {
1100
- childValue = childValue[a];
1101
- }
1102
- });
1103
-
1104
- if (childValue) {
1105
- const propertyValues =
1106
- nameProperties
1107
- .map(
1108
- (prop) =>
1109
- childValue[prop]
1110
- )
1111
- .filter(
1112
- (value) =>
1113
- value !==
1114
- undefined &&
1115
- value !== null
1116
- );
1117
- return propertyValues.join(' ');
1118
- }
1119
- } else {
1120
- const propertyValues =
1121
- nameProperties
1122
- .map((prop) => rv[prop])
1123
- .filter(
1124
- (value) =>
1125
- value !==
1126
- undefined &&
1127
- value !== null
1128
- );
1129
- return propertyValues.join(' ');
1130
- }
1131
- })
1132
- .filter((value) => value !== '');
1133
-
1134
- if (values.length > 0) {
1135
- let result = '';
1136
-
1137
- if (
1138
- column.relation?.id &&
1139
- column.relation?.key
1140
- ) {
1141
- const relationValue =
1142
- column.relation.key.reduce(
1143
- (acc, id) =>
1144
- acc === ''
1145
- ? data[id]
1146
- : acc && acc[id]
1147
- ? acc[id]
1148
- : '',
1149
- ''
1150
- );
1151
-
1152
- result = `${
1153
- relationValue[column.relation.id]
1154
- }<br />`;
1155
- }
1156
-
1157
- if (
1158
- column.where &&
1159
- column.where.id &&
1160
- column.where.value
1161
- ) {
1162
- if (
1163
- data[column.where.id] ===
1164
- column.where.value
1165
- ) {
1166
- result += values.join('<br />');
1167
- }
1168
- } else {
1169
- result += values.join('<br />');
1170
- }
1171
-
1172
- return <span>{parse(result)}</span>;
1173
- }
1174
- }
1175
-
1176
- return null;
1177
- },
1178
- };
1179
- case 'richtext':
1180
- return {
1181
- ...commonProps,
1182
- name: column.id,
1183
- render: ({ data }) => {
1184
- if (data && data[column.id]) {
1185
- return <span>{parse(data[column.id])}</span>;
1186
- } else {
1187
- return null;
1188
- }
1189
- },
1190
- };
1191
- case 'stage':
1192
- return {
1193
- ...commonProps,
1194
- name: column.id,
1195
- sortable: false,
1196
- render: ({ data }) => {
1197
- stage = '';
1198
-
1199
- column.keyIds.forEach((a, b) => {
1200
- if (data[a] === 1) {
1201
- stage = column.valueIds[b];
1202
- }
1203
- });
1204
-
1205
- return <span>{stage}</span>;
1206
- },
1207
- };
1208
- case 'time':
1209
- return {
1210
- ...commonProps,
1211
- name: column.id,
1212
- render: ({ data }) => {
1213
- if (
1214
- data &&
1215
- data[column.id] &&
1216
- moment(data[column.id]).isValid() &&
1217
- moment(data[column.id]).format('YYYY-MM-DD') !==
1218
- '1970-01-01'
1219
- ) {
1220
- data = moment(data[column.id]).format(
1221
- column.format ? column.format : 'hh:mm A'
1222
- );
1223
-
1224
- return <span>{data}</span>;
1225
- } else {
1226
- return null;
1227
- }
1228
- },
1229
- };
1230
- case 'toggle':
1231
- return {
1232
- ...commonProps,
1233
- name: column.id,
1234
- render: ({ data }) => {
1235
- return (
1236
- <Toggle
1237
- name={column.id}
1238
- checked={
1239
- data[column.id] === 1 ? true : false
1240
- }
1241
- onChange={() => {
1242
- CustomFetch(
1243
- `${column.url}/${data.id}`,
1244
- 'POST',
1245
- {
1246
- [column.id]:
1247
- data[column.id] === 1
1248
- ? 0
1249
- : 1,
1250
- },
1251
- (res) => {
1252
- if (res.error === '') {
1253
- handleReload();
1254
- } else {
1255
- toast.error(res.error);
1256
- }
1257
- }
1258
- );
1259
- }}
1260
- />
1261
- );
1262
- },
1263
- };
1264
- case 'url':
1265
- return {
1266
- ...commonProps,
1267
- name: column.id,
1268
- render: ({ data }) => {
1269
- if (data && data[column.id]) {
1270
- return (
1271
- <span>
1272
- <a
1273
- href={data[column.id]}
1274
- target="_blank"
1275
- >
1276
- Link
1277
- </a>
1278
- </span>
1279
- );
1280
- } else {
1281
- return null;
1282
- }
1283
- },
1284
- };
1285
- default:
1286
- selectedDataSource = filterDataSource.reduce((acc, fds) => {
1287
- if (fds.id === column.id) {
1288
- return fds.dataset;
1289
- }
1290
- return acc;
1291
- }, null);
1292
-
1293
- if (column.filter && column.filter.type) {
1294
- switch (column.filter.type) {
1295
- case 'select':
1296
- filterEditor = SelectFilter;
1297
- filterEditorProps = {
1298
- placeholder: column.filter.placeholder
1299
- ? column.filter.placeholder
1300
- : 'All Options',
1301
- dataSource: selectedDataSource,
1302
- };
1303
- break;
1304
- default:
1305
- filterEditor = null;
1306
- filterEditorProps = null;
1307
- break;
1308
- }
1309
- } else {
1310
- filterEditor = null;
1311
- filterEditorProps = null;
1312
- }
1313
-
1314
- return {
1315
- ...commonProps,
1316
- name: column.id,
1317
- filterEditor: filterEditor,
1318
- filterEditorProps: filterEditorProps,
1319
- };
1320
- }
1321
- };
1322
-
1323
- const newColumns = columns.map(renderColumn);
1324
-
1325
- if (settings.length > 0) {
1326
- newColumns.push({
1327
- name: 'setting',
1328
- header: 'Action',
1329
- defaultWidth: 100,
1330
- textAlign: 'center',
1331
- render: ({ data }) => {
1332
- return (
1333
- <div className="tdactions">
1334
- {settings.map((setting) => (
1335
- <span
1336
- key={`setting-${setting.id}`}
1337
- onClick={(e) => {
1338
- e.preventDefault();
1339
- e.stopPropagation();
1340
-
1341
- // Actions that should have confirmation dialogs
1342
- const actionsRequiringConfirmation = [
1343
- 'complete',
1344
- 'primary',
1345
- 'ssa',
1346
- ];
1347
-
1348
- // If this action requires confirmation and is not delete (which has its own confirmation)
1349
- if (
1350
- actionsRequiringConfirmation.includes(
1351
- setting.id
1352
- ) &&
1353
- setting.id !== 'delete'
1354
- ) {
1355
- // Get tooltip content for the message
1356
- const tooltipContent =
1357
- setting.active &&
1358
- data.hasOwnProperty(
1359
- setting.active.id
1360
- ) &&
1361
- data[setting.active.id] ===
1362
- setting.active.value
1363
- ? setting.active.title
1364
- : setting.title;
1365
-
1366
- // Create a message based on the tooltip
1367
- let message = `Are you sure you want to ${tooltipContent.toLowerCase()}?`;
1368
-
1369
- // Add item identifier if available
1370
- const itemName =
1371
- data.name ||
1372
- data.label ||
1373
- data.title ||
1374
- data.id;
1375
- if (itemName) {
1376
- message += ` Item: "${itemName}"`;
1377
- }
1378
-
1379
- // Show confirmation dialog
1380
- confirmDialog({
1381
- title: tooltipContent,
1382
- message: message,
1383
- data: data,
1384
- buttons: [
1385
- {
1386
- label: 'Yes',
1387
- onClick: () =>
1388
- handleSettingClick(
1389
- setting,
1390
- data
1391
- ),
1392
- },
1393
- {
1394
- label: 'No',
1395
- onClick: () => {},
1396
- },
1397
- ],
1398
- });
1399
- } else {
1400
- // For actions that don't need confirmation or already have it (delete)
1401
- handleSettingClick(setting, data);
1402
- }
1403
- }}
1404
- >
1405
- {renderSetting(setting, data)}
1406
- </span>
1407
- ))}
1408
- </div>
1409
- );
1410
- },
1411
- });
1412
- }
1413
-
1414
- setGridColumns(newColumns);
1415
-
1416
- const newFilterValue = columns
1417
- .filter((column) => column.filter && column.filter.type) // Only consider columns with a filter type
1418
- .map((column) => {
1419
- // Map these filtered columns to a new object array
1420
- const filterName = Array.isArray(column.id)
1421
- ? column.id.reduce((acc, id) => acc + `${id}.`, '') +
1422
- column.nameFrom
1423
- : column.id;
1424
-
1425
- return {
1426
- key: column.filter.key ? column.filter.key : null,
1427
- name: filterName,
1428
- operator: column.filter.operator,
1429
- type: column.filter.type,
1430
- value: column.filter.value ? column.filter.value : '',
1431
- };
1432
- });
1433
-
1434
- setFilterValue(newFilterValue);
1435
- }, [columns, filterDataSource, columnsMetadata]);
1436
-
1437
- useEffect(() => {
1438
- if (setFilterData) {
1439
- setFilterData(filterValue);
1440
- }
1441
- }, [filterValue]);
1442
-
1443
- useEffect(() => {
1444
- columns.forEach((column) => {
1445
- if (column.filter && column.filter.url) {
1446
- CustomFetch(
1447
- column.filter.url,
1448
- 'POST',
1449
- column.filter.where ? { where: column.filter.where } : {},
1450
- (res) => {
1451
- const filterName = Array.isArray(column.id)
1452
- ? column.id.reduce(
1453
- (acc, id) => acc + `${id}.`,
1454
- ''
1455
- ) + column.nameFrom
1456
- : column.id;
1457
-
1458
- const filterIndex = filterDataSource.findIndex(
1459
- (filter) => filter.id === filterName
1460
- );
1461
-
1462
- if (filterIndex >= 0) {
1463
- const updatedFilter = {
1464
- ...filterDataSource[filterIndex],
1465
- dataset: res.data,
1466
- };
1467
- const updatedDataSource = [
1468
- ...filterDataSource.slice(0, filterIndex),
1469
- updatedFilter,
1470
- ...filterDataSource.slice(filterIndex + 1),
1471
- ];
1472
- setFilterDataSource(updatedDataSource);
1473
- } else {
1474
- const newFilter = {
1475
- id: filterName,
1476
- dataset: res.data,
1477
- };
1478
- setFilterDataSource((prevState) => [
1479
- ...prevState,
1480
- newFilter,
1481
- ]);
1482
- }
1483
- }
1484
- );
1485
- } else if (column.filter && column.filter.options) {
1486
- const filterName = Array.isArray(column.id)
1487
- ? column.id.reduce((acc, id) => acc + `${id}.`, '') +
1488
- column.nameFrom
1489
- : column.id;
1490
-
1491
- const newFilter = {
1492
- id: filterName,
1493
- dataset: column.filter.options,
1494
- };
1495
-
1496
- setFilterDataSource((prevState) => [...prevState, newFilter]);
1497
- }
1498
- });
1499
- }, []);
1500
-
1501
- // Data Grid Styling
1502
- const [windowHeight, setWindowHeight] = useState(window.innerHeight);
1503
- const [dataCount, setDataCount] = useState(0);
1504
- const [gridStyle, setGridStyle] = useState({
1505
- minHeight:
1506
- gridHeight && gridHeight > 0
1507
- ? gridHeight
1508
- : dataCount > 5
1509
- ? dataCount > 8
1510
- ? windowHeight * 0.7 > 550
1511
- ? windowHeight * 0.7
1512
- : 550
1513
- : 450
1514
- : 400,
1515
- boxShadow: 'none',
1516
- });
1517
- const headerProps = {
1518
- style: style ? style : {},
1519
- };
1520
-
1521
- useEffect(() => {
1522
- setGridStyle((prevState) => ({
1523
- ...prevState,
1524
- minHeight:
1525
- gridHeight && gridHeight > 0
1526
- ? gridHeight
1527
- : dataCount > 5
1528
- ? dataCount > 8
1529
- ? windowHeight * 0.7 > 550
1530
- ? windowHeight * 0.7
1531
- : 550
1532
- : 450
1533
- : 400,
1534
- }));
1535
-
1536
- let dgHeight =
1537
- gridHeight && gridHeight > 0
1538
- ? gridHeight
1539
- : dataCount > 5
1540
- ? dataCount > 8
1541
- ? windowHeight * 0.7 > 550
1542
- ? windowHeight * 0.7
1543
- : 550
1544
- : 450
1545
- : 400;
1546
-
1547
- let rowCount = Math.floor((dgHeight - 100) / 40);
1548
- setLimit(rowCount);
1549
- }, [dataCount]);
1550
-
1551
- useEffect(() => {
1552
- const handleResize = () => {
1553
- setWindowHeight(window.innerHeight);
1554
- };
1555
-
1556
- window.addEventListener('resize', handleResize);
1557
-
1558
- // Clean up the event listener
1559
- return () => {
1560
- window.removeEventListener('resize', handleResize);
1561
- };
1562
- }, []);
1563
-
1564
- return (
1565
- <>
1566
- <div className="filterInput--alt">
1567
- <div className="icon-search">
1568
- <Search strokeWidth={2} size={18} />
1569
- </div>
1570
- <input
1571
- type="text"
1572
- placeholder="Search"
1573
- value={search}
1574
- onChange={handleChangeSearch}
1575
- />
1576
- </div>
1577
- <div className="filterAction--alt">
1578
- {form.sort && form.sort.title && (
1579
- <button className="btn" onClick={sortTrigger}>
1580
- <div className="icon-plus"></div> Sort Order
1581
- </button>
1582
- )}
1583
-
1584
- {form.create && form.create.title && (
1585
- <button
1586
- className="btn"
1587
- onClick={() => {
1588
- let _url = form.url.replace('/ajax', '');
1589
-
1590
- navigate(_url + '/create', {
1591
- state: {
1592
- form: formData,
1593
- },
1594
- });
1595
- }}
1596
- >
1597
- <div className="icon-plus"></div> Create
1598
- </button>
1599
- )}
1600
- </div>
1601
- <ReactDataGrid
1602
- columns={gridColumns}
1603
- dataSource={data}
1604
- filterValue={filterValue}
1605
- enableColumnAutosize={false}
1606
- enableColumnFilterContextMenu={false}
1607
- enableSelection={false}
1608
- headerProps={headerProps}
1609
- idProperty={`visns-datagrid-${ajaxSetting.url.replace(
1610
- /\//g,
1611
- '-'
1612
- )}`}
1613
- limit={limit}
1614
- minRowHeight={40}
1615
- onFilterValueChange={setFilterValue}
1616
- onLimitChange={setLimit}
1617
- onSortInfoChange={handleSort}
1618
- onRenderRow={onRenderRow}
1619
- pagination
1620
- renderColumnContextMenu={renderColumnContextMenu}
1621
- rowHeight={null}
1622
- showZebraRows={true}
1623
- sortInfo={
1624
- ajaxSetting.sortBy && ajaxSetting.sort
1625
- ? {
1626
- name: ajaxSetting.sortBy,
1627
- dir: ajaxSetting.sort === 'asc' ? 1 : -1,
1628
- }
1629
- : null
1630
- }
1631
- style={gridStyle}
1632
- />
1633
- <Popup
1634
- open={modalShow}
1635
- onClose={modalClose}
1636
- closeOnDocumentClick={false}
1637
- >
1638
- <Form
1639
- closeModal={modalClose}
1640
- fetchTable={handleReload}
1641
- formSettings={formData}
1642
- formType={formType}
1643
- updateForm={setFormData}
1644
- columnId={formId}
1645
- childDropdownCallback={childDropdownCallback}
1646
- />
1647
- </Popup>
1648
- </>
1649
- );
1650
- };
1651
-
1652
- export default DataGrid;