@visns-studio/visns-components 5.10.2 → 5.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -1
- package/package.json +3 -3
- package/src/components/crm/DataGrid.jsx +707 -2686
- package/src/components/crm/Field.jsx +0 -2
- package/src/components/crm/cells/CellWithTooltip.jsx +138 -0
- package/src/components/crm/columns/ColumnRenderers.jsx +1358 -0
- package/src/components/crm/controls/AudioPlayer.jsx +19 -0
- package/src/components/crm/controls/AutoRefreshControls.jsx +37 -0
- package/src/components/crm/controls/DataGridSearch.jsx +238 -0
- package/src/components/crm/modals/GalleryModal.jsx +405 -0
- package/src/components/crm/styles/DataGrid.module.scss +296 -0
- package/src/components/crm/styles/global-datagrid.css +21 -0
- package/src/index.js +11 -0
|
@@ -30,7 +30,6 @@ import React, {
|
|
|
30
30
|
import { useNavigate } from 'react-router-dom';
|
|
31
31
|
import debounce from 'lodash.debounce';
|
|
32
32
|
import moment from 'moment';
|
|
33
|
-
import numeral from 'numeral';
|
|
34
33
|
import Popup from 'reactjs-popup';
|
|
35
34
|
import 'reactjs-popup/dist/index.css';
|
|
36
35
|
import parse from 'html-react-parser';
|
|
@@ -40,10 +39,6 @@ import SelectFilter from '@visns-studio/visns-datagrid-enterprise/SelectFilter';
|
|
|
40
39
|
import DateFilter from '@visns-studio/visns-datagrid-enterprise/DateFilter';
|
|
41
40
|
import ReactDataGrid from '@visns-studio/visns-datagrid-enterprise';
|
|
42
41
|
import { toast } from 'react-toastify';
|
|
43
|
-
import imageCompression from 'browser-image-compression';
|
|
44
|
-
import Vapor from 'laravel-vapor';
|
|
45
|
-
import Lightbox from 'yet-another-react-lightbox';
|
|
46
|
-
import { Tooltip } from 'react-tooltip';
|
|
47
42
|
import fetchUtil from '../../utils/fetchUtil';
|
|
48
43
|
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
49
44
|
import {
|
|
@@ -54,10 +49,8 @@ import {
|
|
|
54
49
|
BookOpen,
|
|
55
50
|
Check,
|
|
56
51
|
CircleCheck,
|
|
57
|
-
CircleX,
|
|
58
52
|
Clock,
|
|
59
53
|
CloudDownload,
|
|
60
|
-
CloudUpload,
|
|
61
54
|
Copy,
|
|
62
55
|
Edit,
|
|
63
56
|
Envelope,
|
|
@@ -68,108 +61,57 @@ import {
|
|
|
68
61
|
LinkOut,
|
|
69
62
|
LockOn,
|
|
70
63
|
Network,
|
|
71
|
-
Plus,
|
|
72
64
|
Ribbon,
|
|
73
|
-
Search,
|
|
74
65
|
ShippingBoxV1,
|
|
75
|
-
Sort,
|
|
76
66
|
SoundOn,
|
|
77
67
|
TriangleAlert,
|
|
78
68
|
} from 'akar-icons';
|
|
79
69
|
import styles from './styles/DataGrid.module.scss';
|
|
80
70
|
|
|
81
71
|
import '@visns-studio/visns-datagrid-enterprise/index.css';
|
|
82
|
-
import 'yet-another-react-lightbox/styles.css';
|
|
83
72
|
|
|
84
73
|
import CustomFetch from './Fetch';
|
|
85
74
|
import Download from './Download';
|
|
86
75
|
import Form from './Form';
|
|
76
|
+
import CellWithTooltip from './cells/CellWithTooltip';
|
|
77
|
+
import DataGridSearch from './controls/DataGridSearch';
|
|
78
|
+
import AutoRefreshControls from './controls/AutoRefreshControls';
|
|
79
|
+
import AudioPlayer from './controls/AudioPlayer';
|
|
80
|
+
import GalleryModal from './modals/GalleryModal';
|
|
81
|
+
import {
|
|
82
|
+
renderBooleanColumn,
|
|
83
|
+
renderCurrencyColumn,
|
|
84
|
+
renderDateColumn,
|
|
85
|
+
renderDateTimeColumn,
|
|
86
|
+
renderArrayCountColumn,
|
|
87
|
+
renderColourColumn,
|
|
88
|
+
renderDropdownColumn,
|
|
89
|
+
renderImageColumn,
|
|
90
|
+
renderFileColumn,
|
|
91
|
+
renderIconsColumn,
|
|
92
|
+
renderJsonColumn,
|
|
93
|
+
renderNumberColumn,
|
|
94
|
+
renderOptionColumn,
|
|
95
|
+
renderPlaceholderColumn,
|
|
96
|
+
renderAgeColumn,
|
|
97
|
+
renderCodingColumn,
|
|
98
|
+
renderRelationColumn,
|
|
99
|
+
renderRelationArrayColumn,
|
|
100
|
+
renderRichTextColumn,
|
|
101
|
+
renderStageColumn,
|
|
102
|
+
renderTimeColumn,
|
|
103
|
+
renderTimerColumn,
|
|
104
|
+
renderUrlColumn,
|
|
105
|
+
renderInputTextColumn,
|
|
106
|
+
renderAddressColumn,
|
|
107
|
+
renderCreatedByColumn,
|
|
108
|
+
renderDateRangeColumn,
|
|
109
|
+
renderStageCounterColumn,
|
|
110
|
+
} from './columns/ColumnRenderers.jsx';
|
|
87
111
|
import _ from 'lodash';
|
|
88
112
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
const groupHeaderStyles = `
|
|
92
|
-
.datagrid-fixed-group-headers .group-header-fixed-height {
|
|
93
|
-
height: 34px !important;
|
|
94
|
-
min-height: 34px !important;
|
|
95
|
-
max-height: 34px !important;
|
|
96
|
-
box-sizing: border-box !important;
|
|
97
|
-
line-height: 1.1 !important;
|
|
98
|
-
flex-shrink: 0 !important;
|
|
99
|
-
overflow: hidden !important;
|
|
100
|
-
transition: background-color 0.2s ease, box-shadow 0.2s ease !important;
|
|
101
|
-
cursor: pointer !important;
|
|
102
|
-
position: relative !important;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/* Hover effect for entire group header */
|
|
106
|
-
.datagrid-fixed-group-headers .group-header-fixed-height:hover {
|
|
107
|
-
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%) !important;
|
|
108
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important;
|
|
109
|
-
transform: translateY(-1px) !important;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* Click indicator overlay */
|
|
113
|
-
.datagrid-fixed-group-headers .group-header-fixed-height::after {
|
|
114
|
-
content: '' !important;
|
|
115
|
-
position: absolute !important;
|
|
116
|
-
top: 0 !important;
|
|
117
|
-
left: 0 !important;
|
|
118
|
-
right: 0 !important;
|
|
119
|
-
bottom: 0 !important;
|
|
120
|
-
background: rgba(59, 130, 246, 0.05) !important;
|
|
121
|
-
opacity: 0 !important;
|
|
122
|
-
transition: opacity 0.2s ease !important;
|
|
123
|
-
pointer-events: none !important;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
.datagrid-fixed-group-headers .group-header-fixed-height:hover::after {
|
|
127
|
-
opacity: 1 !important;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row {
|
|
131
|
-
height: 34px !important;
|
|
132
|
-
min-height: 34px !important;
|
|
133
|
-
max-height: 34px !important;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row .InovuaReactDataGrid__group-cell {
|
|
137
|
-
height: 34px !important;
|
|
138
|
-
min-height: 34px !important;
|
|
139
|
-
max-height: 34px !important;
|
|
140
|
-
padding: 0 !important;
|
|
141
|
-
overflow: hidden !important;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row .InovuaReactDataGrid__group-cell > div {
|
|
145
|
-
height: 34px !important;
|
|
146
|
-
min-height: 34px !important;
|
|
147
|
-
max-height: 34px !important;
|
|
148
|
-
overflow: hidden !important;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/* Ensure group headers maintain consistent styling during expand/collapse */
|
|
152
|
-
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row--collapsed .group-header-fixed-height,
|
|
153
|
-
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row--expanded .group-header-fixed-height {
|
|
154
|
-
height: 34px !important;
|
|
155
|
-
min-height: 34px !important;
|
|
156
|
-
max-height: 34px !important;
|
|
157
|
-
transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease !important;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/* Force consistent height for all group headers regardless of content */
|
|
161
|
-
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row[data-group-by] {
|
|
162
|
-
height: 34px !important;
|
|
163
|
-
min-height: 34px !important;
|
|
164
|
-
max-height: 34px !important;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row[data-group-by] .InovuaReactDataGrid__group-cell {
|
|
168
|
-
height: 34px !important;
|
|
169
|
-
min-height: 34px !important;
|
|
170
|
-
max-height: 34px !important;
|
|
171
|
-
}
|
|
172
|
-
|
|
113
|
+
// Enhanced multi-line content support for data cells
|
|
114
|
+
const dataGridStyles = `
|
|
173
115
|
/* Enhanced multi-line content support for data cells */
|
|
174
116
|
.InovuaReactDataGrid__cell {
|
|
175
117
|
word-wrap: break-word;
|
|
@@ -202,7 +144,7 @@ if (typeof document !== 'undefined') {
|
|
|
202
144
|
if (!document.getElementById(styleId)) {
|
|
203
145
|
const style = document.createElement('style');
|
|
204
146
|
style.id = styleId;
|
|
205
|
-
style.textContent =
|
|
147
|
+
style.textContent = dataGridStyles;
|
|
206
148
|
document.head.appendChild(style);
|
|
207
149
|
}
|
|
208
150
|
}
|
|
@@ -268,123 +210,6 @@ const loadData = async (
|
|
|
268
210
|
}
|
|
269
211
|
};
|
|
270
212
|
|
|
271
|
-
// Utility function to check if text content is truncated
|
|
272
|
-
const isTextTruncated = (element) => {
|
|
273
|
-
if (!element) return false;
|
|
274
|
-
return (
|
|
275
|
-
element.scrollWidth > element.clientWidth ||
|
|
276
|
-
element.scrollHeight > element.clientHeight
|
|
277
|
-
);
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// Utility function to format cell value for tooltip display
|
|
281
|
-
const formatCellValueForTooltip = (value, columnType) => {
|
|
282
|
-
if (value === null || value === undefined || value === '') {
|
|
283
|
-
return '';
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
switch (columnType) {
|
|
287
|
-
case 'date':
|
|
288
|
-
case 'datetime':
|
|
289
|
-
if (
|
|
290
|
-
moment(value).isValid() &&
|
|
291
|
-
moment(value).format('YYYY-MM-DD') !== '1970-01-01'
|
|
292
|
-
) {
|
|
293
|
-
return moment(value).format('DD-MM-YYYY HH:mm');
|
|
294
|
-
}
|
|
295
|
-
return String(value);
|
|
296
|
-
case 'currency':
|
|
297
|
-
return numeral(value).format('$0,0.00');
|
|
298
|
-
case 'number':
|
|
299
|
-
return numeral(value).format('0,0');
|
|
300
|
-
case 'boolean':
|
|
301
|
-
return value ? 'Yes' : 'No';
|
|
302
|
-
case 'richtext':
|
|
303
|
-
// Strip HTML tags for tooltip
|
|
304
|
-
const tempDiv = document.createElement('div');
|
|
305
|
-
tempDiv.innerHTML = value;
|
|
306
|
-
return tempDiv.textContent || tempDiv.innerText || '';
|
|
307
|
-
default:
|
|
308
|
-
return String(value);
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// Component to wrap cell content with tooltip functionality
|
|
313
|
-
const CellWithTooltip = ({
|
|
314
|
-
children,
|
|
315
|
-
value,
|
|
316
|
-
columnType = 'text',
|
|
317
|
-
className = '',
|
|
318
|
-
}) => {
|
|
319
|
-
const cellRef = useRef(null);
|
|
320
|
-
const [showTooltip, setShowTooltip] = useState(false);
|
|
321
|
-
const [tooltipId] = useState(
|
|
322
|
-
() => `cell-tooltip-${Math.random().toString(36).substr(2, 9)}`
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
useEffect(() => {
|
|
326
|
-
const checkTruncation = () => {
|
|
327
|
-
if (cellRef.current) {
|
|
328
|
-
const isTruncated = isTextTruncated(cellRef.current);
|
|
329
|
-
setShowTooltip(isTruncated);
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
// Check truncation on mount and when content changes
|
|
334
|
-
checkTruncation();
|
|
335
|
-
|
|
336
|
-
// Also check on window resize
|
|
337
|
-
const handleResize = () => checkTruncation();
|
|
338
|
-
window.addEventListener('resize', handleResize);
|
|
339
|
-
|
|
340
|
-
return () => {
|
|
341
|
-
window.removeEventListener('resize', handleResize);
|
|
342
|
-
};
|
|
343
|
-
}, [children, value]);
|
|
344
|
-
|
|
345
|
-
const tooltipContent = formatCellValueForTooltip(value, columnType);
|
|
346
|
-
|
|
347
|
-
// Only show tooltip if content is truncated and we have meaningful content
|
|
348
|
-
const shouldShowTooltip =
|
|
349
|
-
showTooltip && tooltipContent && tooltipContent.trim() !== '';
|
|
350
|
-
|
|
351
|
-
return (
|
|
352
|
-
<div
|
|
353
|
-
ref={cellRef}
|
|
354
|
-
className={className}
|
|
355
|
-
style={{
|
|
356
|
-
overflow: 'hidden',
|
|
357
|
-
textOverflow: 'ellipsis',
|
|
358
|
-
whiteSpace: 'nowrap',
|
|
359
|
-
width: '100%',
|
|
360
|
-
}}
|
|
361
|
-
{...(shouldShowTooltip && {
|
|
362
|
-
'data-tooltip-id': tooltipId,
|
|
363
|
-
'data-tooltip-html': tooltipContent,
|
|
364
|
-
})}
|
|
365
|
-
>
|
|
366
|
-
{children}
|
|
367
|
-
{shouldShowTooltip && (
|
|
368
|
-
<Tooltip
|
|
369
|
-
id={tooltipId}
|
|
370
|
-
style={{
|
|
371
|
-
backgroundColor: 'rgba(0, 0, 0, 0.9)',
|
|
372
|
-
color: 'white',
|
|
373
|
-
borderRadius: '6px',
|
|
374
|
-
padding: '8px 12px',
|
|
375
|
-
fontSize: '13px',
|
|
376
|
-
maxWidth: '500px',
|
|
377
|
-
wordWrap: 'break-word',
|
|
378
|
-
zIndex: 1000,
|
|
379
|
-
}}
|
|
380
|
-
place="top"
|
|
381
|
-
offset={5}
|
|
382
|
-
/>
|
|
383
|
-
)}
|
|
384
|
-
</div>
|
|
385
|
-
);
|
|
386
|
-
};
|
|
387
|
-
|
|
388
213
|
const DataGrid = forwardRef(
|
|
389
214
|
(
|
|
390
215
|
{
|
|
@@ -413,21 +238,16 @@ const DataGrid = forwardRef(
|
|
|
413
238
|
const navigate = useNavigate();
|
|
414
239
|
|
|
415
240
|
/** Audio */
|
|
416
|
-
const audioRef = useRef(null);
|
|
417
241
|
const [audioSource, setAudioSource] = useState('');
|
|
418
242
|
|
|
419
|
-
useEffect(() => {
|
|
420
|
-
if (audioSource) {
|
|
421
|
-
audioRef.current.play();
|
|
422
|
-
}
|
|
423
|
-
}, [audioSource]);
|
|
424
|
-
|
|
425
243
|
/** Gallery Modal States */
|
|
426
244
|
const [modalGalleryShow, setModalGalleryShow] = useState(false);
|
|
427
245
|
const [galleryData, setGalleryData] = useState([]);
|
|
428
|
-
const [uploadProgress, setUploadProgress] = useState(0);
|
|
429
246
|
const [currentItem, setCurrentItem] = useState(null);
|
|
430
|
-
|
|
247
|
+
|
|
248
|
+
/** Touch Tooltip States */
|
|
249
|
+
const [activeTooltip, setActiveTooltip] = useState(null);
|
|
250
|
+
const [touchTimer, setTouchTimer] = useState(null);
|
|
431
251
|
|
|
432
252
|
/** Lightbox States */
|
|
433
253
|
const [lightboxOpen, setLightboxOpen] = useState(false);
|
|
@@ -442,149 +262,6 @@ const DataGrid = forwardRef(
|
|
|
442
262
|
setModalGalleryShow(false);
|
|
443
263
|
};
|
|
444
264
|
|
|
445
|
-
const handleFileUpload = async (files) => {
|
|
446
|
-
if (!currentItem || !currentItem.settings.upload) return;
|
|
447
|
-
|
|
448
|
-
setIsUploading(true);
|
|
449
|
-
const uploadPromises = [];
|
|
450
|
-
|
|
451
|
-
for (let i = 0; i < files.length; i++) {
|
|
452
|
-
const file = files[i];
|
|
453
|
-
|
|
454
|
-
try {
|
|
455
|
-
// Optimize image if needed
|
|
456
|
-
let optimizedFile = file;
|
|
457
|
-
if (file.type.startsWith('image/')) {
|
|
458
|
-
const options = {
|
|
459
|
-
maxSizeMB: 1,
|
|
460
|
-
maxWidthOrHeight: 1920,
|
|
461
|
-
useWebWorker: true,
|
|
462
|
-
};
|
|
463
|
-
try {
|
|
464
|
-
optimizedFile = await imageCompression(
|
|
465
|
-
file,
|
|
466
|
-
options
|
|
467
|
-
);
|
|
468
|
-
} catch (err) {
|
|
469
|
-
console.error('Image compression failed:', err);
|
|
470
|
-
// Continue with original file if compression fails
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Upload to Vapor
|
|
475
|
-
const uploadPromise = new Promise((resolve, reject) => {
|
|
476
|
-
Vapor.store(optimizedFile, {
|
|
477
|
-
progress: (progress) => {
|
|
478
|
-
setUploadProgress(progress * 100);
|
|
479
|
-
},
|
|
480
|
-
})
|
|
481
|
-
.then((response) => {
|
|
482
|
-
// Construct the URL with the key parameter
|
|
483
|
-
const uploadSettings =
|
|
484
|
-
currentItem.settings.upload;
|
|
485
|
-
const itemData = currentItem.data;
|
|
486
|
-
const keyValue = itemData[uploadSettings.key];
|
|
487
|
-
const url = keyValue
|
|
488
|
-
? `${uploadSettings.url}/${keyValue}`
|
|
489
|
-
: uploadSettings.url;
|
|
490
|
-
|
|
491
|
-
// Make the PUT request
|
|
492
|
-
CustomFetch(
|
|
493
|
-
url,
|
|
494
|
-
uploadSettings.method || 'POST',
|
|
495
|
-
{
|
|
496
|
-
uuid: response.uuid,
|
|
497
|
-
key: response.key,
|
|
498
|
-
bucket: response.bucket,
|
|
499
|
-
filename: file.name,
|
|
500
|
-
filesize: optimizedFile.size,
|
|
501
|
-
extension: response.extension,
|
|
502
|
-
file_relationship: uploadSettings.id,
|
|
503
|
-
},
|
|
504
|
-
(result) => {
|
|
505
|
-
if (result.error === '') {
|
|
506
|
-
resolve();
|
|
507
|
-
} else {
|
|
508
|
-
toast.error(String(result.error));
|
|
509
|
-
reject(result.error);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
);
|
|
513
|
-
})
|
|
514
|
-
.catch(reject);
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
uploadPromises.push(uploadPromise);
|
|
518
|
-
} catch (error) {
|
|
519
|
-
console.error('Error uploading file:', error);
|
|
520
|
-
toast.error(
|
|
521
|
-
`Error uploading ${file.name}: ${
|
|
522
|
-
error.message || 'Unknown error'
|
|
523
|
-
}`
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
try {
|
|
529
|
-
await Promise.all(uploadPromises);
|
|
530
|
-
toast.success('Files uploaded successfully');
|
|
531
|
-
|
|
532
|
-
// Refresh the gallery data
|
|
533
|
-
if (currentItem && currentItem.settings && currentItem.data) {
|
|
534
|
-
const s = currentItem.settings;
|
|
535
|
-
const d = currentItem.data;
|
|
536
|
-
|
|
537
|
-
// Function to access nested property using a series of keys
|
|
538
|
-
const getNestedValue = (data, keys) =>
|
|
539
|
-
keys.reduce(
|
|
540
|
-
(currentValue, key) =>
|
|
541
|
-
currentValue && currentValue[key] !== undefined
|
|
542
|
-
? currentValue[key]
|
|
543
|
-
: undefined,
|
|
544
|
-
data
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
// Fetch updated data
|
|
548
|
-
try {
|
|
549
|
-
const res = await CustomFetch(
|
|
550
|
-
`${form.url}/${d[form.primaryKey]}`,
|
|
551
|
-
'GET',
|
|
552
|
-
{}
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
if (res.data) {
|
|
556
|
-
// Extract the updated nested value
|
|
557
|
-
const updatedNestedValue = getNestedValue(
|
|
558
|
-
res.data,
|
|
559
|
-
s.relation
|
|
560
|
-
);
|
|
561
|
-
|
|
562
|
-
// Update gallery data
|
|
563
|
-
if (
|
|
564
|
-
updatedNestedValue &&
|
|
565
|
-
Array.isArray(updatedNestedValue)
|
|
566
|
-
) {
|
|
567
|
-
const updatedGalleryArray =
|
|
568
|
-
updatedNestedValue.map((item) => ({
|
|
569
|
-
src: item[s.key],
|
|
570
|
-
caption: item.file_name,
|
|
571
|
-
}));
|
|
572
|
-
|
|
573
|
-
setGalleryData(updatedGalleryArray);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
} catch (error) {
|
|
577
|
-
console.error('Error refreshing gallery data:', error);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
} catch (error) {
|
|
581
|
-
console.error('Error in upload process:', error);
|
|
582
|
-
} finally {
|
|
583
|
-
setIsUploading(false);
|
|
584
|
-
setUploadProgress(0);
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
|
|
588
265
|
/** Modal States */
|
|
589
266
|
const [formData, setFormData] = useState({});
|
|
590
267
|
const [formType, setFormType] = useState('');
|
|
@@ -689,6 +366,7 @@ const DataGrid = forwardRef(
|
|
|
689
366
|
return () => clearTimeout(timer);
|
|
690
367
|
}
|
|
691
368
|
}, [collapsedGroups, ajaxSetting?.groupBy]);
|
|
369
|
+
|
|
692
370
|
const dataSource = useCallback(
|
|
693
371
|
(params) => {
|
|
694
372
|
const sqlResult = loadData(params, dSearch, ajaxSetting);
|
|
@@ -1160,8 +838,7 @@ const DataGrid = forwardRef(
|
|
|
1160
838
|
setFilterValue(filters);
|
|
1161
839
|
}, []);
|
|
1162
840
|
|
|
1163
|
-
const handleChangeSearch = (
|
|
1164
|
-
const { value } = e.target;
|
|
841
|
+
const handleChangeSearch = (value) => {
|
|
1165
842
|
setSearch(value);
|
|
1166
843
|
};
|
|
1167
844
|
|
|
@@ -1540,6 +1217,194 @@ const DataGrid = forwardRef(
|
|
|
1540
1217
|
gridRef.current.paginationProps.reload();
|
|
1541
1218
|
};
|
|
1542
1219
|
|
|
1220
|
+
const handleGroupAction = async (groupValue, iconConfig) => {
|
|
1221
|
+
// Get all rows for this group
|
|
1222
|
+
const groupRows = getGroupRows(groupValue);
|
|
1223
|
+
|
|
1224
|
+
// Show confirmation dialog to prevent accidental clicks
|
|
1225
|
+
const confirmMessage = `Are you sure you want to perform "${iconConfig.label}" action on group "${groupValue}" (${groupRows.length} rows)?`;
|
|
1226
|
+
|
|
1227
|
+
confirmDialog({
|
|
1228
|
+
title: 'Confirm Group Action',
|
|
1229
|
+
message: confirmMessage,
|
|
1230
|
+
buttons: [
|
|
1231
|
+
{
|
|
1232
|
+
label: 'Yes',
|
|
1233
|
+
onClick: () => {
|
|
1234
|
+
executeGroupAction(groupValue, iconConfig);
|
|
1235
|
+
},
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
label: 'No',
|
|
1239
|
+
onClick: () => {
|
|
1240
|
+
// User cancelled
|
|
1241
|
+
},
|
|
1242
|
+
},
|
|
1243
|
+
],
|
|
1244
|
+
});
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
const getGroupRows = (groupValue) => {
|
|
1248
|
+
if (!dataRef.current || !ajaxSetting?.groupBy) {
|
|
1249
|
+
return [];
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
const groupField = ajaxSetting.groupBy[0];
|
|
1253
|
+
return dataRef.current.filter(
|
|
1254
|
+
(row) => row[groupField] === groupValue
|
|
1255
|
+
);
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
const shouldShowGroupAction = (iconConfig, groupValue) => {
|
|
1259
|
+
// If no show conditions, always show the icon
|
|
1260
|
+
if (!iconConfig.show || !Array.isArray(iconConfig.show)) {
|
|
1261
|
+
return true;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// Get all rows for this group
|
|
1265
|
+
const groupRows = getGroupRows(groupValue);
|
|
1266
|
+
|
|
1267
|
+
// Check if all show conditions are met
|
|
1268
|
+
return iconConfig.show.every((condition) => {
|
|
1269
|
+
const { id, value } = condition;
|
|
1270
|
+
|
|
1271
|
+
// Check if any row in the group matches the condition
|
|
1272
|
+
return groupRows.some((row) => {
|
|
1273
|
+
const rowValue = row[id];
|
|
1274
|
+
|
|
1275
|
+
// Handle null value condition
|
|
1276
|
+
if (value === null) {
|
|
1277
|
+
return (
|
|
1278
|
+
rowValue === null ||
|
|
1279
|
+
rowValue === undefined ||
|
|
1280
|
+
rowValue === ''
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Handle other value conditions
|
|
1285
|
+
return rowValue === value;
|
|
1286
|
+
});
|
|
1287
|
+
});
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
const executeGroupAction = async (groupValue, iconConfig) => {
|
|
1291
|
+
// Check if fetch configuration is available
|
|
1292
|
+
if (iconConfig.fetch && iconConfig.fetch.url) {
|
|
1293
|
+
try {
|
|
1294
|
+
const payload = {
|
|
1295
|
+
...iconConfig.fetch.data,
|
|
1296
|
+
groupLabel: groupValue,
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1299
|
+
const result = await CustomFetch(
|
|
1300
|
+
iconConfig.fetch.url,
|
|
1301
|
+
iconConfig.fetch.method || 'POST',
|
|
1302
|
+
payload
|
|
1303
|
+
);
|
|
1304
|
+
|
|
1305
|
+
if (result.error) {
|
|
1306
|
+
throw new Error(result.error);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
toast.success(
|
|
1310
|
+
`${iconConfig.label} - Group: ${groupValue} completed successfully`
|
|
1311
|
+
);
|
|
1312
|
+
|
|
1313
|
+
// Reload grid data if needed
|
|
1314
|
+
if (gridRef.current && gridRef.current.paginationProps) {
|
|
1315
|
+
gridRef.current.paginationProps.reload();
|
|
1316
|
+
}
|
|
1317
|
+
} catch (error) {
|
|
1318
|
+
console.error('Group action fetch error:', error);
|
|
1319
|
+
toast.error(
|
|
1320
|
+
`${iconConfig.label} - Group: ${groupValue} failed: ${error.message}`
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
} else {
|
|
1324
|
+
// Fallback to existing switch logic if no fetch config
|
|
1325
|
+
toast.info(`${iconConfig.label} - Group: ${groupValue}`);
|
|
1326
|
+
|
|
1327
|
+
switch (iconConfig.id) {
|
|
1328
|
+
case 'clock':
|
|
1329
|
+
console.log(
|
|
1330
|
+
`Starting timer action for group: ${groupValue}`
|
|
1331
|
+
);
|
|
1332
|
+
break;
|
|
1333
|
+
case 'alarm':
|
|
1334
|
+
console.log(
|
|
1335
|
+
`Setting alarm action for group: ${groupValue}`
|
|
1336
|
+
);
|
|
1337
|
+
break;
|
|
1338
|
+
case 'check':
|
|
1339
|
+
console.log(
|
|
1340
|
+
`Mark complete action for group: ${groupValue}`
|
|
1341
|
+
);
|
|
1342
|
+
break;
|
|
1343
|
+
case 'edit':
|
|
1344
|
+
console.log(
|
|
1345
|
+
`Bulk edit action for group: ${groupValue}`
|
|
1346
|
+
);
|
|
1347
|
+
break;
|
|
1348
|
+
case 'envelope':
|
|
1349
|
+
console.log(
|
|
1350
|
+
`Send notification action for group: ${groupValue}`
|
|
1351
|
+
);
|
|
1352
|
+
break;
|
|
1353
|
+
default:
|
|
1354
|
+
console.log(
|
|
1355
|
+
`Custom action ${iconConfig.id} for group: ${groupValue}`
|
|
1356
|
+
);
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
// Touch handlers for tooltips
|
|
1363
|
+
const handleTouchStart = (e, buttonId, label) => {
|
|
1364
|
+
// Clear any existing timer
|
|
1365
|
+
if (touchTimer) {
|
|
1366
|
+
clearTimeout(touchTimer);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
// Capture the button element and rect immediately
|
|
1370
|
+
const buttonElement = e.currentTarget;
|
|
1371
|
+
const rect = buttonElement.getBoundingClientRect();
|
|
1372
|
+
const x = rect.left + rect.width / 2; // Center of button
|
|
1373
|
+
const y = rect.top; // Top of button
|
|
1374
|
+
|
|
1375
|
+
// Set a timer for long press (500ms)
|
|
1376
|
+
const timer = setTimeout(() => {
|
|
1377
|
+
setActiveTooltip({ id: buttonId, label, x, y });
|
|
1378
|
+
|
|
1379
|
+
// Auto-close tooltip after 2 seconds
|
|
1380
|
+
setTimeout(() => {
|
|
1381
|
+
setActiveTooltip(null);
|
|
1382
|
+
}, 2000);
|
|
1383
|
+
}, 500);
|
|
1384
|
+
|
|
1385
|
+
setTouchTimer(timer);
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
const handleTouchEnd = () => {
|
|
1389
|
+
// Clear the timer if touch ends before long press
|
|
1390
|
+
if (touchTimer) {
|
|
1391
|
+
clearTimeout(touchTimer);
|
|
1392
|
+
setTouchTimer(null);
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
const handleTouchMove = () => {
|
|
1397
|
+
// Cancel tooltip if user moves finger (not a long press)
|
|
1398
|
+
if (touchTimer) {
|
|
1399
|
+
clearTimeout(touchTimer);
|
|
1400
|
+
setTouchTimer(null);
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
|
|
1404
|
+
const closeTooltip = () => {
|
|
1405
|
+
setActiveTooltip(null);
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1543
1408
|
const handleSort = (value) => {
|
|
1544
1409
|
if (setConfig) {
|
|
1545
1410
|
setConfig((prevState) => {
|
|
@@ -1767,212 +1632,6 @@ const DataGrid = forwardRef(
|
|
|
1767
1632
|
[selected, setRowsSelected, onRowClick]
|
|
1768
1633
|
);
|
|
1769
1634
|
|
|
1770
|
-
const exportTrigger = async () => {
|
|
1771
|
-
try {
|
|
1772
|
-
toast.success('Starting export please wait...');
|
|
1773
|
-
|
|
1774
|
-
let payload;
|
|
1775
|
-
|
|
1776
|
-
if (form.export.useData) {
|
|
1777
|
-
payload = { data: dataRef.current };
|
|
1778
|
-
} else {
|
|
1779
|
-
payload = {
|
|
1780
|
-
filter: filterValue,
|
|
1781
|
-
paramId: paramValue || '',
|
|
1782
|
-
};
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
const res = await Download(form.export.url, 'POST', payload);
|
|
1786
|
-
|
|
1787
|
-
if (res.data.error) {
|
|
1788
|
-
toast.error(res.data.error);
|
|
1789
|
-
} else {
|
|
1790
|
-
// Assuming the response contains a Blob or similar data
|
|
1791
|
-
if (res.data && res.data instanceof Blob) {
|
|
1792
|
-
const fileName = `${form.export.filename}`;
|
|
1793
|
-
saveAs(res.data, fileName);
|
|
1794
|
-
} else {
|
|
1795
|
-
// Handle the case where the response is not a Blob
|
|
1796
|
-
toast.error('Invalid file format received.');
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
} catch (error) {
|
|
1800
|
-
console.info(error);
|
|
1801
|
-
}
|
|
1802
|
-
};
|
|
1803
|
-
|
|
1804
|
-
const sortTrigger = () => {
|
|
1805
|
-
let baseUrl = form.url.replace('/ajax', '');
|
|
1806
|
-
let sortUrl = paramValue
|
|
1807
|
-
? `${baseUrl}/${paramValue}/sort`
|
|
1808
|
-
: `${baseUrl}/sort`;
|
|
1809
|
-
|
|
1810
|
-
if (form.sort?.url) {
|
|
1811
|
-
sortUrl = form.sort.url;
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
const newState = {
|
|
1815
|
-
form,
|
|
1816
|
-
page: {
|
|
1817
|
-
data: pageData,
|
|
1818
|
-
setting: pageSetting,
|
|
1819
|
-
},
|
|
1820
|
-
paramValue,
|
|
1821
|
-
};
|
|
1822
|
-
|
|
1823
|
-
navigate(sortUrl, { state: newState });
|
|
1824
|
-
};
|
|
1825
|
-
|
|
1826
|
-
const renderCreateButton = () => {
|
|
1827
|
-
if (form.createDisable && form.createDisable === true) {
|
|
1828
|
-
return null;
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
const isSearchEnabled = !(form.searchDisable === true);
|
|
1832
|
-
const buttonClassName = isSearchEnabled
|
|
1833
|
-
? styles.actionButtons
|
|
1834
|
-
: styles.actionButtonsStandalone;
|
|
1835
|
-
|
|
1836
|
-
return (
|
|
1837
|
-
<div className={buttonClassName}>
|
|
1838
|
-
{form.export && form.export.url && (
|
|
1839
|
-
<button className={styles.btn} onClick={exportTrigger}>
|
|
1840
|
-
<LinkOut
|
|
1841
|
-
strokeWidth={2}
|
|
1842
|
-
size={16}
|
|
1843
|
-
style={{ marginRight: '10px' }}
|
|
1844
|
-
/>{' '}
|
|
1845
|
-
Export
|
|
1846
|
-
</button>
|
|
1847
|
-
)}
|
|
1848
|
-
{form.sort && form.sort.title && (
|
|
1849
|
-
<button className={styles.btn} onClick={sortTrigger}>
|
|
1850
|
-
<Sort
|
|
1851
|
-
strokeWidth={2}
|
|
1852
|
-
size={16}
|
|
1853
|
-
style={{ marginRight: '10px' }}
|
|
1854
|
-
/>{' '}
|
|
1855
|
-
Sort Order
|
|
1856
|
-
</button>
|
|
1857
|
-
)}
|
|
1858
|
-
|
|
1859
|
-
{(form.create &&
|
|
1860
|
-
form.create.title &&
|
|
1861
|
-
form.create.title !== '') ||
|
|
1862
|
-
(form.url && form.create?.title !== '') ? (
|
|
1863
|
-
<button
|
|
1864
|
-
className={styles.btn}
|
|
1865
|
-
onClick={(e) => {
|
|
1866
|
-
e.preventDefault();
|
|
1867
|
-
|
|
1868
|
-
if (form.create) {
|
|
1869
|
-
if (form.create.url) {
|
|
1870
|
-
// Check if URL is external
|
|
1871
|
-
if (
|
|
1872
|
-
form.create.url.startsWith(
|
|
1873
|
-
'http://'
|
|
1874
|
-
) ||
|
|
1875
|
-
form.create.url.startsWith(
|
|
1876
|
-
'https://'
|
|
1877
|
-
)
|
|
1878
|
-
) {
|
|
1879
|
-
window.open(
|
|
1880
|
-
form.create.url,
|
|
1881
|
-
'_blank'
|
|
1882
|
-
);
|
|
1883
|
-
} else {
|
|
1884
|
-
navigate(form.create.url);
|
|
1885
|
-
}
|
|
1886
|
-
} else {
|
|
1887
|
-
modalOpen('create', 0);
|
|
1888
|
-
}
|
|
1889
|
-
} else {
|
|
1890
|
-
if (form.url) {
|
|
1891
|
-
window.open(
|
|
1892
|
-
form.url,
|
|
1893
|
-
'',
|
|
1894
|
-
'width=1440,height=1024,menubar=1,resizable=1,status=1,titlebar=1,toolbar=1'
|
|
1895
|
-
);
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
}}
|
|
1899
|
-
>
|
|
1900
|
-
<Plus
|
|
1901
|
-
strokeWidth={2}
|
|
1902
|
-
size={16}
|
|
1903
|
-
style={{ marginRight: '10px' }}
|
|
1904
|
-
/>
|
|
1905
|
-
Add New
|
|
1906
|
-
</button>
|
|
1907
|
-
) : form.url && form.modal === false ? (
|
|
1908
|
-
<button
|
|
1909
|
-
className={styles.btn}
|
|
1910
|
-
onClick={(e) => {
|
|
1911
|
-
e.preventDefault();
|
|
1912
|
-
|
|
1913
|
-
window.open(
|
|
1914
|
-
form.url,
|
|
1915
|
-
'',
|
|
1916
|
-
'width=1440,height=1024,menubar=1,resizable=1,status=1,titlebar=1,toolbar=1'
|
|
1917
|
-
);
|
|
1918
|
-
}}
|
|
1919
|
-
>
|
|
1920
|
-
<Plus
|
|
1921
|
-
strokeWidth={2}
|
|
1922
|
-
size={16}
|
|
1923
|
-
style={{ marginRight: '10px' }}
|
|
1924
|
-
/>
|
|
1925
|
-
Add New
|
|
1926
|
-
</button>
|
|
1927
|
-
) : null}
|
|
1928
|
-
</div>
|
|
1929
|
-
);
|
|
1930
|
-
};
|
|
1931
|
-
|
|
1932
|
-
const renderSearch = () => {
|
|
1933
|
-
const isSearchEnabled = !(form.searchDisable === true);
|
|
1934
|
-
const isCreateDisabled = form.createDisable === true;
|
|
1935
|
-
|
|
1936
|
-
// Don't render anything if both search and create are disabled
|
|
1937
|
-
if (!isSearchEnabled && isCreateDisabled) {
|
|
1938
|
-
return null;
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
// If search is disabled but create button is enabled, render only the button with its own container
|
|
1942
|
-
if (!isSearchEnabled && !isCreateDisabled) {
|
|
1943
|
-
return (
|
|
1944
|
-
<div className={styles.actionButtonsContainer}>
|
|
1945
|
-
{renderCreateButton()}
|
|
1946
|
-
</div>
|
|
1947
|
-
);
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
// If search is enabled, render the full search container with both search and buttons
|
|
1951
|
-
return (
|
|
1952
|
-
<div className={styles.searchContainer}>
|
|
1953
|
-
<div className={styles.searchInputWrapper}>
|
|
1954
|
-
<div
|
|
1955
|
-
className={styles['icon-search']}
|
|
1956
|
-
style={{ left: 25, color: '#000' }}
|
|
1957
|
-
>
|
|
1958
|
-
<Search strokeWidth={2} size={14} />
|
|
1959
|
-
</div>
|
|
1960
|
-
<input
|
|
1961
|
-
type="text"
|
|
1962
|
-
placeholder="Search"
|
|
1963
|
-
value={search}
|
|
1964
|
-
onChange={handleChangeSearch}
|
|
1965
|
-
style={{ borderRadius: 0, paddingLeft: 55 }}
|
|
1966
|
-
className={styles.searchInput}
|
|
1967
|
-
/>
|
|
1968
|
-
</div>
|
|
1969
|
-
<div className={styles.actionButtonsWrapper}>
|
|
1970
|
-
{!isCreateDisabled && renderCreateButton()}
|
|
1971
|
-
</div>
|
|
1972
|
-
</div>
|
|
1973
|
-
);
|
|
1974
|
-
};
|
|
1975
|
-
|
|
1976
1635
|
// Helper function to determine if auto refresh should be relocated
|
|
1977
1636
|
const shouldRelocateAutoRefresh = () => {
|
|
1978
1637
|
const isSearchDisabled = form.searchDisable === true;
|
|
@@ -1980,36 +1639,9 @@ const DataGrid = forwardRef(
|
|
|
1980
1639
|
const hasAutoRefresh =
|
|
1981
1640
|
ajaxSetting && ajaxSetting.autoRefresh !== undefined;
|
|
1982
1641
|
|
|
1983
|
-
//
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
const renderAutoRefreshControls = (isRelocated = false) => {
|
|
1988
|
-
// Only show auto-refresh if the setting is present in ajaxSetting
|
|
1989
|
-
if (!ajaxSetting || ajaxSetting.autoRefresh === undefined) {
|
|
1990
|
-
return null;
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
const containerClass = isRelocated
|
|
1994
|
-
? styles.autoRefreshContainerRelocated
|
|
1995
|
-
: styles.autoRefreshContainer;
|
|
1996
|
-
|
|
1997
|
-
return (
|
|
1998
|
-
<div className={containerClass}>
|
|
1999
|
-
<label className={styles.autoRefreshLabel}>
|
|
2000
|
-
<input
|
|
2001
|
-
type="checkbox"
|
|
2002
|
-
checked={autoRefreshEnabled}
|
|
2003
|
-
onChange={() =>
|
|
2004
|
-
setAutoRefreshEnabled(!autoRefreshEnabled)
|
|
2005
|
-
}
|
|
2006
|
-
className={styles.autoRefreshCheckbox}
|
|
2007
|
-
/>
|
|
2008
|
-
Auto Refresh{' '}
|
|
2009
|
-
{refreshInterval && `(${refreshInterval}s)`}
|
|
2010
|
-
</label>
|
|
2011
|
-
</div>
|
|
2012
|
-
);
|
|
1642
|
+
// Hide auto refresh when it would be the sole control (instead of relocating it)
|
|
1643
|
+
// This prevents it from appearing under the datagrid
|
|
1644
|
+
return false;
|
|
2013
1645
|
};
|
|
2014
1646
|
|
|
2015
1647
|
const renderRowContextMenu = useCallback(
|
|
@@ -2714,8 +2346,6 @@ const DataGrid = forwardRef(
|
|
|
2714
2346
|
let date;
|
|
2715
2347
|
let filterEditor = null;
|
|
2716
2348
|
let filterEditorProps = null;
|
|
2717
|
-
let fileUrl = null;
|
|
2718
|
-
let icons = [];
|
|
2719
2349
|
let relationName;
|
|
2720
2350
|
let selectedDataSource = null;
|
|
2721
2351
|
let stage;
|
|
@@ -2840,252 +2470,259 @@ const DataGrid = forwardRef(
|
|
|
2840
2470
|
|
|
2841
2471
|
switch (column.type) {
|
|
2842
2472
|
case 'address':
|
|
2843
|
-
return {
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
filterEditor
|
|
2847
|
-
filterEditorProps
|
|
2848
|
-
|
|
2849
|
-
const addressFields = [
|
|
2850
|
-
'address1',
|
|
2851
|
-
'address2',
|
|
2852
|
-
'suburb',
|
|
2853
|
-
'postcode',
|
|
2854
|
-
];
|
|
2855
|
-
|
|
2856
|
-
const addressValue = addressFields
|
|
2857
|
-
.filter((field) => data[field])
|
|
2858
|
-
.map((field) => data[field])
|
|
2859
|
-
.join(' ');
|
|
2860
|
-
|
|
2861
|
-
const stateValue =
|
|
2862
|
-
data.state?.name ||
|
|
2863
|
-
data.state ||
|
|
2864
|
-
'';
|
|
2865
|
-
|
|
2866
|
-
const value = `${addressValue} ${stateValue}`;
|
|
2867
|
-
|
|
2868
|
-
return (
|
|
2869
|
-
<CellWithTooltip
|
|
2870
|
-
value={value}
|
|
2871
|
-
columnType="address"
|
|
2872
|
-
>
|
|
2873
|
-
<span>{value}</span>
|
|
2874
|
-
</CellWithTooltip>
|
|
2875
|
-
);
|
|
2876
|
-
},
|
|
2877
|
-
};
|
|
2473
|
+
return renderAddressColumn({
|
|
2474
|
+
column,
|
|
2475
|
+
commonProps,
|
|
2476
|
+
filterEditor,
|
|
2477
|
+
filterEditorProps,
|
|
2478
|
+
});
|
|
2878
2479
|
case 'age':
|
|
2879
|
-
return {
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
filterEditor
|
|
2883
|
-
|
|
2884
|
-
render: ({ data }) => {
|
|
2885
|
-
value = column.id.reduce((acc, id) => {
|
|
2886
|
-
if (acc === '') {
|
|
2887
|
-
return data[id];
|
|
2888
|
-
} else {
|
|
2889
|
-
return acc ? acc[id] : '';
|
|
2890
|
-
}
|
|
2891
|
-
}, '');
|
|
2892
|
-
|
|
2893
|
-
if (value !== '' && value !== null) {
|
|
2894
|
-
date = moment(value);
|
|
2895
|
-
|
|
2896
|
-
return (
|
|
2897
|
-
<span>
|
|
2898
|
-
{moment().diff(
|
|
2899
|
-
date,
|
|
2900
|
-
column.duration
|
|
2901
|
-
? column.duration
|
|
2902
|
-
: 'years'
|
|
2903
|
-
)}
|
|
2904
|
-
</span>
|
|
2905
|
-
);
|
|
2906
|
-
} else {
|
|
2907
|
-
return null;
|
|
2908
|
-
}
|
|
2909
|
-
},
|
|
2910
|
-
};
|
|
2480
|
+
return renderAgeColumn({
|
|
2481
|
+
column,
|
|
2482
|
+
commonProps,
|
|
2483
|
+
filterEditor,
|
|
2484
|
+
});
|
|
2911
2485
|
case 'arrayCount':
|
|
2912
|
-
return {
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
render: ({ data }) => {
|
|
2917
|
-
if (data && data[column.id]) {
|
|
2918
|
-
return data[column.id].length;
|
|
2919
|
-
} else {
|
|
2920
|
-
return null;
|
|
2921
|
-
}
|
|
2922
|
-
},
|
|
2923
|
-
};
|
|
2486
|
+
return renderArrayCountColumn({
|
|
2487
|
+
column,
|
|
2488
|
+
commonProps,
|
|
2489
|
+
});
|
|
2924
2490
|
case 'boolean':
|
|
2925
|
-
return {
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
render: ({ data }) => {
|
|
2932
|
-
const value = data[column.id]
|
|
2933
|
-
? 'Yes'
|
|
2934
|
-
: 'No';
|
|
2935
|
-
return (
|
|
2936
|
-
<CellWithTooltip
|
|
2937
|
-
value={data[column.id]}
|
|
2938
|
-
columnType="boolean"
|
|
2939
|
-
>
|
|
2940
|
-
<span>{value}</span>
|
|
2941
|
-
</CellWithTooltip>
|
|
2942
|
-
);
|
|
2943
|
-
},
|
|
2944
|
-
};
|
|
2491
|
+
return renderBooleanColumn({
|
|
2492
|
+
column,
|
|
2493
|
+
commonProps,
|
|
2494
|
+
filterEditor,
|
|
2495
|
+
filterEditorProps,
|
|
2496
|
+
});
|
|
2945
2497
|
case 'coding':
|
|
2946
|
-
return {
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
if (data) {
|
|
2952
|
-
return handleCoding(column, data);
|
|
2953
|
-
} else {
|
|
2954
|
-
return null;
|
|
2955
|
-
}
|
|
2956
|
-
},
|
|
2957
|
-
};
|
|
2498
|
+
return renderCodingColumn({
|
|
2499
|
+
column,
|
|
2500
|
+
commonProps,
|
|
2501
|
+
handleCoding,
|
|
2502
|
+
});
|
|
2958
2503
|
case 'colour':
|
|
2959
|
-
return {
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
render: ({ data }) => {
|
|
2964
|
-
if (data && data[column.id]) {
|
|
2965
|
-
return (
|
|
2966
|
-
<div
|
|
2967
|
-
style={{
|
|
2968
|
-
width: '25px',
|
|
2969
|
-
height: '25px',
|
|
2970
|
-
borderRadius: '10px',
|
|
2971
|
-
overflow: 'hidden',
|
|
2972
|
-
}}
|
|
2973
|
-
>
|
|
2974
|
-
<div
|
|
2975
|
-
style={{
|
|
2976
|
-
background:
|
|
2977
|
-
data[
|
|
2978
|
-
column.id
|
|
2979
|
-
] +
|
|
2980
|
-
' none repeat scroll 0% 0%',
|
|
2981
|
-
height: '100%',
|
|
2982
|
-
width: '100%',
|
|
2983
|
-
cursor: 'pointer',
|
|
2984
|
-
position:
|
|
2985
|
-
'relative',
|
|
2986
|
-
outline:
|
|
2987
|
-
'currentcolor none medium',
|
|
2988
|
-
}}
|
|
2989
|
-
></div>
|
|
2990
|
-
</div>
|
|
2991
|
-
);
|
|
2992
|
-
} else {
|
|
2993
|
-
return null;
|
|
2994
|
-
}
|
|
2995
|
-
},
|
|
2996
|
-
};
|
|
2504
|
+
return renderColourColumn({
|
|
2505
|
+
column,
|
|
2506
|
+
commonProps,
|
|
2507
|
+
});
|
|
2997
2508
|
case 'created_by':
|
|
2998
|
-
return {
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
render: ({ data }) => {
|
|
3003
|
-
if (
|
|
3004
|
-
data.audits[0] &&
|
|
3005
|
-
data.audits[0].user &&
|
|
3006
|
-
data.audits[0].user.name
|
|
3007
|
-
) {
|
|
3008
|
-
value = data.audits[0].user.name;
|
|
3009
|
-
|
|
3010
|
-
return <span>{value}</span>;
|
|
3011
|
-
} else {
|
|
3012
|
-
return null;
|
|
3013
|
-
}
|
|
3014
|
-
},
|
|
3015
|
-
};
|
|
2509
|
+
return renderCreatedByColumn({
|
|
2510
|
+
column,
|
|
2511
|
+
commonProps,
|
|
2512
|
+
});
|
|
3016
2513
|
case 'currency':
|
|
3017
|
-
return {
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
if (data) {
|
|
3022
|
-
data = numeral(
|
|
3023
|
-
data[column.id]
|
|
3024
|
-
).format('$0,0.00');
|
|
3025
|
-
|
|
3026
|
-
return (
|
|
3027
|
-
<CellWithTooltip
|
|
3028
|
-
value={data[column.id]}
|
|
3029
|
-
columnType="currency"
|
|
3030
|
-
>
|
|
3031
|
-
<span>{data}</span>
|
|
3032
|
-
</CellWithTooltip>
|
|
3033
|
-
);
|
|
3034
|
-
} else {
|
|
3035
|
-
return null;
|
|
3036
|
-
}
|
|
3037
|
-
},
|
|
3038
|
-
};
|
|
2514
|
+
return renderCurrencyColumn({
|
|
2515
|
+
column,
|
|
2516
|
+
commonProps,
|
|
2517
|
+
});
|
|
3039
2518
|
case 'date':
|
|
2519
|
+
return renderDateColumn({
|
|
2520
|
+
column,
|
|
2521
|
+
commonProps,
|
|
2522
|
+
filterEditor,
|
|
2523
|
+
filterEditorProps,
|
|
2524
|
+
});
|
|
2525
|
+
case 'daterange':
|
|
2526
|
+
return renderDateRangeColumn({
|
|
2527
|
+
column,
|
|
2528
|
+
commonProps,
|
|
2529
|
+
filterEditor,
|
|
2530
|
+
filterEditorProps,
|
|
2531
|
+
});
|
|
2532
|
+
case 'datetime':
|
|
2533
|
+
return renderDateTimeColumn({
|
|
2534
|
+
column,
|
|
2535
|
+
commonProps,
|
|
2536
|
+
filterEditor,
|
|
2537
|
+
filterEditorProps,
|
|
2538
|
+
});
|
|
2539
|
+
case 'dropdown':
|
|
2540
|
+
return renderDropdownColumn({
|
|
2541
|
+
column,
|
|
2542
|
+
commonProps,
|
|
2543
|
+
filterEditor,
|
|
2544
|
+
filterEditorProps,
|
|
2545
|
+
dropdownData,
|
|
2546
|
+
handleDropdownUpdate,
|
|
2547
|
+
});
|
|
2548
|
+
case 'file':
|
|
2549
|
+
return renderFileColumn({
|
|
2550
|
+
column,
|
|
2551
|
+
commonProps,
|
|
2552
|
+
handleDownload,
|
|
2553
|
+
styles,
|
|
2554
|
+
});
|
|
2555
|
+
case 'icons':
|
|
2556
|
+
return renderIconsColumn({
|
|
2557
|
+
column,
|
|
2558
|
+
commonProps,
|
|
2559
|
+
});
|
|
2560
|
+
case 'image':
|
|
2561
|
+
return renderImageColumn({
|
|
2562
|
+
column,
|
|
2563
|
+
commonProps,
|
|
2564
|
+
});
|
|
2565
|
+
case 'json':
|
|
2566
|
+
return renderJsonColumn({
|
|
2567
|
+
column,
|
|
2568
|
+
commonProps,
|
|
2569
|
+
});
|
|
2570
|
+
case 'number':
|
|
2571
|
+
return renderNumberColumn({
|
|
2572
|
+
column,
|
|
2573
|
+
commonProps,
|
|
2574
|
+
});
|
|
2575
|
+
case 'option':
|
|
2576
|
+
return renderOptionColumn({
|
|
2577
|
+
column,
|
|
2578
|
+
commonProps,
|
|
2579
|
+
filterEditor,
|
|
2580
|
+
filterEditorProps,
|
|
2581
|
+
});
|
|
2582
|
+
case 'placeholder':
|
|
2583
|
+
return renderPlaceholderColumn({
|
|
2584
|
+
column,
|
|
2585
|
+
commonProps,
|
|
2586
|
+
});
|
|
2587
|
+
case 'relation':
|
|
2588
|
+
return renderRelationColumn({
|
|
2589
|
+
column,
|
|
2590
|
+
commonProps,
|
|
2591
|
+
filterEditor,
|
|
2592
|
+
filterEditorProps,
|
|
2593
|
+
relationName,
|
|
2594
|
+
});
|
|
2595
|
+
case 'relationArray':
|
|
2596
|
+
return renderRelationArrayColumn({
|
|
2597
|
+
column,
|
|
2598
|
+
commonProps,
|
|
2599
|
+
filterEditor,
|
|
2600
|
+
filterEditorProps,
|
|
2601
|
+
relationName,
|
|
2602
|
+
});
|
|
2603
|
+
case 'richtext':
|
|
2604
|
+
return renderRichTextColumn({
|
|
2605
|
+
column,
|
|
2606
|
+
commonProps,
|
|
2607
|
+
});
|
|
2608
|
+
case 'stage':
|
|
2609
|
+
return renderStageColumn({
|
|
2610
|
+
column,
|
|
2611
|
+
commonProps,
|
|
2612
|
+
});
|
|
2613
|
+
case 'time':
|
|
2614
|
+
return renderTimeColumn({
|
|
2615
|
+
column,
|
|
2616
|
+
commonProps,
|
|
2617
|
+
});
|
|
2618
|
+
case 'timer':
|
|
2619
|
+
return renderTimerColumn({
|
|
2620
|
+
column,
|
|
2621
|
+
commonProps,
|
|
2622
|
+
handleTimer,
|
|
2623
|
+
form,
|
|
2624
|
+
columns,
|
|
2625
|
+
styles,
|
|
2626
|
+
});
|
|
2627
|
+
case 'stageCounter':
|
|
2628
|
+
return renderStageCounterColumn({
|
|
2629
|
+
column,
|
|
2630
|
+
commonProps,
|
|
2631
|
+
navigate,
|
|
2632
|
+
});
|
|
2633
|
+
case 'url':
|
|
2634
|
+
return renderUrlColumn({ column, commonProps });
|
|
2635
|
+
case 'input_text':
|
|
2636
|
+
return renderInputTextColumn({
|
|
2637
|
+
column,
|
|
2638
|
+
commonProps,
|
|
2639
|
+
localInputValuesRef,
|
|
2640
|
+
setLocalInputValues,
|
|
2641
|
+
handleInputUpdate,
|
|
2642
|
+
});
|
|
2643
|
+
default:
|
|
2644
|
+
columnId = Array.isArray(column.id)
|
|
2645
|
+
? column.id.join('-')
|
|
2646
|
+
: column.id;
|
|
2647
|
+
|
|
3040
2648
|
return {
|
|
3041
2649
|
...commonProps,
|
|
3042
|
-
name:
|
|
2650
|
+
name: columnId,
|
|
3043
2651
|
filterEditor: filterEditor,
|
|
3044
2652
|
filterEditorProps: filterEditorProps,
|
|
3045
|
-
|
|
3046
|
-
|
|
2653
|
+
className:
|
|
2654
|
+
column.wordWrap === true
|
|
2655
|
+
? 'cell-word-wrap'
|
|
2656
|
+
: '',
|
|
3047
2657
|
render: ({ data }) => {
|
|
3048
2658
|
if (
|
|
3049
2659
|
data &&
|
|
3050
2660
|
data[column.id] &&
|
|
3051
|
-
|
|
3052
|
-
moment(data[column.id]).format(
|
|
3053
|
-
'YYYY-MM-DD'
|
|
3054
|
-
) !== '1970-01-01'
|
|
2661
|
+
data[column.id] !== ''
|
|
3055
2662
|
) {
|
|
3056
|
-
|
|
2663
|
+
let content = String(
|
|
3057
2664
|
data[column.id]
|
|
3058
|
-
).format(
|
|
3059
|
-
column.format || 'DD-MM-YYYY'
|
|
3060
2665
|
);
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
if (
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
2666
|
+
|
|
2667
|
+
// Debug logging for wordWrap cell rendering
|
|
2668
|
+
if (column.wordWrap === true) {
|
|
2669
|
+
console.log(
|
|
2670
|
+
'DataGrid DEBUG - Rendering WordWrap Cell:',
|
|
2671
|
+
{
|
|
2672
|
+
columnId,
|
|
2673
|
+
contentLength:
|
|
2674
|
+
content.length,
|
|
2675
|
+
contentPreview:
|
|
2676
|
+
content.substring(
|
|
2677
|
+
0,
|
|
2678
|
+
50
|
|
2679
|
+
) + '...',
|
|
2680
|
+
wordWrapProp:
|
|
2681
|
+
column.wordWrap,
|
|
2682
|
+
}
|
|
2683
|
+
);
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
// If removeFormat is true, strip HTML tags
|
|
2687
|
+
if (column.removeFormat) {
|
|
2688
|
+
content = content.replace(
|
|
2689
|
+
/<\/?[^>]+(>|$)/g,
|
|
2690
|
+
''
|
|
2691
|
+
); // Simple regex to strip HTML tags
|
|
2692
|
+
} else {
|
|
2693
|
+
// Replace p tags with br tags
|
|
2694
|
+
content = content
|
|
2695
|
+
.replace(/<p>/g, '')
|
|
2696
|
+
.replace(/<\/p>/g, '<br>');
|
|
2697
|
+
|
|
2698
|
+
// Ensure <br /> tags are properly handled (convert to <br> for consistency)
|
|
2699
|
+
content = content
|
|
2700
|
+
.replace(/<br \/>/g, '<br>')
|
|
2701
|
+
.replace(/<br\/>/g, '<br>');
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
// If truncate has a value, limit content to specified number of characters
|
|
2705
|
+
if (column.truncate) {
|
|
2706
|
+
content =
|
|
2707
|
+
content.length >
|
|
2708
|
+
column.truncate
|
|
2709
|
+
? content.substring(
|
|
2710
|
+
0,
|
|
2711
|
+
column.truncate
|
|
2712
|
+
) + '...'
|
|
2713
|
+
: content;
|
|
3080
2714
|
}
|
|
3081
2715
|
|
|
3082
2716
|
return (
|
|
3083
2717
|
<CellWithTooltip
|
|
3084
2718
|
value={data[column.id]}
|
|
3085
|
-
columnType="
|
|
2719
|
+
columnType="text"
|
|
2720
|
+
wordWrap={
|
|
2721
|
+
column.wordWrap === true
|
|
2722
|
+
}
|
|
3086
2723
|
>
|
|
3087
|
-
<span
|
|
3088
|
-
{
|
|
2724
|
+
<span>
|
|
2725
|
+
{parse(content)}
|
|
3089
2726
|
</span>
|
|
3090
2727
|
</CellWithTooltip>
|
|
3091
2728
|
);
|
|
@@ -3094,1417 +2731,40 @@ const DataGrid = forwardRef(
|
|
|
3094
2731
|
}
|
|
3095
2732
|
},
|
|
3096
2733
|
};
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
...commonProps,
|
|
3100
|
-
name: `${column.id[0]}.${column.id[1]}.`,
|
|
3101
|
-
filterEditor: filterEditor,
|
|
3102
|
-
filterEditorProps: filterEditorProps,
|
|
3103
|
-
defaultWidth: 200, // Set default width for daterange columns
|
|
3104
|
-
minWidth: 200, // Set minimum width for daterange columns
|
|
3105
|
-
render: ({ data }) => {
|
|
3106
|
-
if (
|
|
3107
|
-
data &&
|
|
3108
|
-
data[column.id[0]] &&
|
|
3109
|
-
moment(
|
|
3110
|
-
data[column.id[0]]
|
|
3111
|
-
).isValid() &&
|
|
3112
|
-
moment(data[column.id[0]]).format(
|
|
3113
|
-
'YYYY-MM-DD'
|
|
3114
|
-
) !== '1970-01-01'
|
|
3115
|
-
) {
|
|
3116
|
-
value = moment(
|
|
3117
|
-
data[column.id[0]]
|
|
3118
|
-
).format(
|
|
3119
|
-
column.format || 'DD-MM-YYYY'
|
|
3120
|
-
);
|
|
3121
|
-
}
|
|
2734
|
+
}
|
|
2735
|
+
};
|
|
3122
2736
|
|
|
3123
|
-
|
|
3124
|
-
data &&
|
|
3125
|
-
data[column.id[1]] &&
|
|
3126
|
-
moment(
|
|
3127
|
-
data[column.id[1]]
|
|
3128
|
-
).isValid() &&
|
|
3129
|
-
moment(data[column.id[1]]).format(
|
|
3130
|
-
'YYYY-MM-DD'
|
|
3131
|
-
) !== '1970-01-01'
|
|
3132
|
-
) {
|
|
3133
|
-
value += ` - ${moment(
|
|
3134
|
-
data[column.id[1]]
|
|
3135
|
-
).format(
|
|
3136
|
-
column.format || 'DD-MM-YYYY'
|
|
3137
|
-
)}`;
|
|
3138
|
-
}
|
|
2737
|
+
const newColumns = columns.map(renderColumn);
|
|
3139
2738
|
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
data
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
data[column.id]
|
|
3162
|
-
).format(
|
|
3163
|
-
column.format
|
|
3164
|
-
? column.format
|
|
3165
|
-
: 'DD-MM-YYYY hh:mm A'
|
|
3166
|
-
);
|
|
2739
|
+
if (settings.length > 0) {
|
|
2740
|
+
newColumns.push({
|
|
2741
|
+
name: 'setting',
|
|
2742
|
+
header: 'Action',
|
|
2743
|
+
defaultWidth: 100,
|
|
2744
|
+
textAlign: 'center',
|
|
2745
|
+
textVerticalAlign:
|
|
2746
|
+
style && style.text_vertical_align
|
|
2747
|
+
? style.text_vertical_align
|
|
2748
|
+
: 'center',
|
|
2749
|
+
render: ({ data }) => {
|
|
2750
|
+
return (
|
|
2751
|
+
<div className={styles.tdactions}>
|
|
2752
|
+
{settings.map((setting) =>
|
|
2753
|
+
renderSetting(setting, data)
|
|
2754
|
+
)}
|
|
2755
|
+
</div>
|
|
2756
|
+
);
|
|
2757
|
+
},
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
3167
2760
|
|
|
3168
|
-
|
|
3169
|
-
} else {
|
|
3170
|
-
return null;
|
|
3171
|
-
}
|
|
3172
|
-
},
|
|
3173
|
-
};
|
|
3174
|
-
case 'dropdown':
|
|
3175
|
-
columnId = Array.isArray(column.id)
|
|
3176
|
-
? column.id.join('-')
|
|
3177
|
-
: column.id;
|
|
2761
|
+
setGridColumns(newColumns);
|
|
3178
2762
|
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
render: ({ data }) => {
|
|
3185
|
-
return (
|
|
3186
|
-
<select
|
|
3187
|
-
name={column.id}
|
|
3188
|
-
value={
|
|
3189
|
-
data[column.id]?.id || ''
|
|
3190
|
-
}
|
|
3191
|
-
style={{ padding: '7px' }}
|
|
3192
|
-
onChange={(e) => {
|
|
3193
|
-
e.preventDefault();
|
|
3194
|
-
|
|
3195
|
-
if (
|
|
3196
|
-
(column.update?.key,
|
|
3197
|
-
column.update?.url,
|
|
3198
|
-
column.update?.method)
|
|
3199
|
-
) {
|
|
3200
|
-
handleDropdownUpdate(
|
|
3201
|
-
column.update.key,
|
|
3202
|
-
`${column.update.url}/${data.id}`,
|
|
3203
|
-
column.update
|
|
3204
|
-
.method,
|
|
3205
|
-
e.target.value
|
|
3206
|
-
);
|
|
3207
|
-
}
|
|
3208
|
-
}}
|
|
3209
|
-
>
|
|
3210
|
-
<option value="">
|
|
3211
|
-
Select{' '}
|
|
3212
|
-
{/^[aeioAEIO]/.test(
|
|
3213
|
-
column.label
|
|
3214
|
-
)
|
|
3215
|
-
? 'an'
|
|
3216
|
-
: 'a'}{' '}
|
|
3217
|
-
{column.label}
|
|
3218
|
-
</option>
|
|
3219
|
-
{dropdownData[column.id] &&
|
|
3220
|
-
dropdownData[column.id].length >
|
|
3221
|
-
0
|
|
3222
|
-
? dropdownData[
|
|
3223
|
-
column.id
|
|
3224
|
-
].map((option, index) => (
|
|
3225
|
-
<option
|
|
3226
|
-
value={option.id}
|
|
3227
|
-
key={index}
|
|
3228
|
-
>
|
|
3229
|
-
{option.label}
|
|
3230
|
-
</option>
|
|
3231
|
-
))
|
|
3232
|
-
: null}
|
|
3233
|
-
</select>
|
|
3234
|
-
);
|
|
3235
|
-
},
|
|
3236
|
-
};
|
|
3237
|
-
case 'file':
|
|
3238
|
-
return {
|
|
3239
|
-
...commonProps,
|
|
3240
|
-
name: column.id,
|
|
3241
|
-
sortable: false,
|
|
3242
|
-
render: ({ data }) => {
|
|
3243
|
-
if (data && data[column.id]) {
|
|
3244
|
-
// Check if the data is an array of files
|
|
3245
|
-
if (
|
|
3246
|
-
Array.isArray(data[column.id])
|
|
3247
|
-
) {
|
|
3248
|
-
// Return a span for each file in the array with added margin
|
|
3249
|
-
return data[column.id].map(
|
|
3250
|
-
(file, index) => (
|
|
3251
|
-
<span
|
|
3252
|
-
key={index} // Use index as a key; consider using unique ids if available
|
|
3253
|
-
onClick={(e) => {
|
|
3254
|
-
e.stopPropagation();
|
|
3255
|
-
e.preventDefault();
|
|
3256
|
-
handleDownload(
|
|
3257
|
-
file
|
|
3258
|
-
);
|
|
3259
|
-
}}
|
|
3260
|
-
style={{
|
|
3261
|
-
marginRight:
|
|
3262
|
-
'10px',
|
|
3263
|
-
display:
|
|
3264
|
-
'inline-block',
|
|
3265
|
-
}} // Added margin right and display style
|
|
3266
|
-
>
|
|
3267
|
-
<CloudDownload
|
|
3268
|
-
data-tooltip-id={`system-tooltip`}
|
|
3269
|
-
data-tooltip-content={`Download File ${file.file_name}`}
|
|
3270
|
-
strokeWidth={2}
|
|
3271
|
-
size={14}
|
|
3272
|
-
className={
|
|
3273
|
-
styles.tdaction
|
|
3274
|
-
}
|
|
3275
|
-
/>
|
|
3276
|
-
</span>
|
|
3277
|
-
)
|
|
3278
|
-
);
|
|
3279
|
-
} else {
|
|
3280
|
-
// Return a single span for a single file with added margin
|
|
3281
|
-
return (
|
|
3282
|
-
<span
|
|
3283
|
-
onClick={(e) => {
|
|
3284
|
-
e.stopPropagation();
|
|
3285
|
-
e.preventDefault();
|
|
3286
|
-
handleDownload(
|
|
3287
|
-
data[column.id]
|
|
3288
|
-
);
|
|
3289
|
-
}}
|
|
3290
|
-
style={{
|
|
3291
|
-
marginRight: '10px',
|
|
3292
|
-
display:
|
|
3293
|
-
'inline-block',
|
|
3294
|
-
}} // Added margin right and display style for consistency
|
|
3295
|
-
>
|
|
3296
|
-
<CloudDownload
|
|
3297
|
-
data-tooltip-id="system-tooltip"
|
|
3298
|
-
data-tooltip-content={`Download File ${
|
|
3299
|
-
data[column.id]
|
|
3300
|
-
.file_name
|
|
3301
|
-
}`}
|
|
3302
|
-
strokeWidth={2}
|
|
3303
|
-
size={14}
|
|
3304
|
-
className={
|
|
3305
|
-
styles.tdaction
|
|
3306
|
-
}
|
|
3307
|
-
/>
|
|
3308
|
-
</span>
|
|
3309
|
-
);
|
|
3310
|
-
}
|
|
3311
|
-
} else {
|
|
3312
|
-
return null;
|
|
3313
|
-
}
|
|
3314
|
-
},
|
|
3315
|
-
};
|
|
3316
|
-
case 'icons':
|
|
3317
|
-
return {
|
|
3318
|
-
...commonProps,
|
|
3319
|
-
name: column.id,
|
|
3320
|
-
sortable: false,
|
|
3321
|
-
render: ({ data }) => {
|
|
3322
|
-
icons = [];
|
|
3323
|
-
|
|
3324
|
-
if (
|
|
3325
|
-
column.icons &&
|
|
3326
|
-
column.icons.length > 0
|
|
3327
|
-
) {
|
|
3328
|
-
column.icons.forEach((icon) => {
|
|
3329
|
-
icons.push(
|
|
3330
|
-
<img
|
|
3331
|
-
src={icon.url}
|
|
3332
|
-
alt={icon.name}
|
|
3333
|
-
data-tooltip-id="system-tooltip"
|
|
3334
|
-
data-tooltip-content={
|
|
3335
|
-
icon.name
|
|
3336
|
-
}
|
|
3337
|
-
/>
|
|
3338
|
-
);
|
|
3339
|
-
});
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
if (data) {
|
|
3343
|
-
return <span>{icons}</span>;
|
|
3344
|
-
} else {
|
|
3345
|
-
return null;
|
|
3346
|
-
}
|
|
3347
|
-
},
|
|
3348
|
-
};
|
|
3349
|
-
case 'image':
|
|
3350
|
-
return {
|
|
3351
|
-
...commonProps,
|
|
3352
|
-
name: column.id,
|
|
3353
|
-
render: ({ data }) => {
|
|
3354
|
-
if (data && data[column.id]) {
|
|
3355
|
-
fileUrl =
|
|
3356
|
-
data[column.id][0]?.file_url ||
|
|
3357
|
-
data[column.id]?.file_url;
|
|
3358
|
-
|
|
3359
|
-
if (fileUrl) {
|
|
3360
|
-
return (
|
|
3361
|
-
<span>
|
|
3362
|
-
{parse(
|
|
3363
|
-
`<img width="150" src="${fileUrl}" />`
|
|
3364
|
-
)}
|
|
3365
|
-
</span>
|
|
3366
|
-
);
|
|
3367
|
-
}
|
|
3368
|
-
}
|
|
3369
|
-
|
|
3370
|
-
return null;
|
|
3371
|
-
},
|
|
3372
|
-
};
|
|
3373
|
-
case 'json':
|
|
3374
|
-
return {
|
|
3375
|
-
...commonProps,
|
|
3376
|
-
name: `${column.id}.${column.jsonData}`,
|
|
3377
|
-
render: ({ data }) => {
|
|
3378
|
-
if (
|
|
3379
|
-
data &&
|
|
3380
|
-
data[column.id] &&
|
|
3381
|
-
data[column.id][column.jsonData]
|
|
3382
|
-
) {
|
|
3383
|
-
data =
|
|
3384
|
-
data[column.id][
|
|
3385
|
-
column.jsonData
|
|
3386
|
-
];
|
|
3387
|
-
|
|
3388
|
-
return (
|
|
3389
|
-
<CellWithTooltip
|
|
3390
|
-
value={data}
|
|
3391
|
-
columnType="json"
|
|
3392
|
-
>
|
|
3393
|
-
<span>{data}</span>
|
|
3394
|
-
</CellWithTooltip>
|
|
3395
|
-
);
|
|
3396
|
-
} else {
|
|
3397
|
-
return null;
|
|
3398
|
-
}
|
|
3399
|
-
},
|
|
3400
|
-
};
|
|
3401
|
-
case 'number':
|
|
3402
|
-
return {
|
|
3403
|
-
...commonProps,
|
|
3404
|
-
name: column.id,
|
|
3405
|
-
type: 'number',
|
|
3406
|
-
};
|
|
3407
|
-
case 'option':
|
|
3408
|
-
return {
|
|
3409
|
-
...commonProps,
|
|
3410
|
-
name: column.id,
|
|
3411
|
-
filterEditor: filterEditor,
|
|
3412
|
-
filterEditorProps: filterEditorProps,
|
|
3413
|
-
render: ({ data }) => {
|
|
3414
|
-
let newData;
|
|
3415
|
-
|
|
3416
|
-
if (
|
|
3417
|
-
data &&
|
|
3418
|
-
column.options &&
|
|
3419
|
-
column.options[data[column.id]]
|
|
3420
|
-
) {
|
|
3421
|
-
if (
|
|
3422
|
-
column.key &&
|
|
3423
|
-
data[column.key] > 0 &&
|
|
3424
|
-
column.options[
|
|
3425
|
-
data[column.id]
|
|
3426
|
-
].includes('#')
|
|
3427
|
-
) {
|
|
3428
|
-
newData = column.options[
|
|
3429
|
-
data[column.id]
|
|
3430
|
-
].replace(
|
|
3431
|
-
/#/g,
|
|
3432
|
-
data[column.key]
|
|
3433
|
-
);
|
|
3434
|
-
} else {
|
|
3435
|
-
newData =
|
|
3436
|
-
column.options[
|
|
3437
|
-
data[column.id]
|
|
3438
|
-
];
|
|
3439
|
-
}
|
|
3440
|
-
|
|
3441
|
-
return (
|
|
3442
|
-
<CellWithTooltip
|
|
3443
|
-
value={newData}
|
|
3444
|
-
columnType="option"
|
|
3445
|
-
>
|
|
3446
|
-
<span>{newData}</span>
|
|
3447
|
-
</CellWithTooltip>
|
|
3448
|
-
);
|
|
3449
|
-
} else {
|
|
3450
|
-
return null;
|
|
3451
|
-
}
|
|
3452
|
-
},
|
|
3453
|
-
};
|
|
3454
|
-
case 'placeholder':
|
|
3455
|
-
return {
|
|
3456
|
-
...commonProps,
|
|
3457
|
-
name: column.id,
|
|
3458
|
-
sortable: false,
|
|
3459
|
-
render: ({ data }) => {
|
|
3460
|
-
if (data) {
|
|
3461
|
-
return (
|
|
3462
|
-
<CellWithTooltip
|
|
3463
|
-
value={column.placeholder}
|
|
3464
|
-
columnType="placeholder"
|
|
3465
|
-
>
|
|
3466
|
-
<span>
|
|
3467
|
-
{column.placeholder}
|
|
3468
|
-
</span>
|
|
3469
|
-
</CellWithTooltip>
|
|
3470
|
-
);
|
|
3471
|
-
} else {
|
|
3472
|
-
return null;
|
|
3473
|
-
}
|
|
3474
|
-
},
|
|
3475
|
-
};
|
|
3476
|
-
case 'relation':
|
|
3477
|
-
return {
|
|
3478
|
-
...commonProps,
|
|
3479
|
-
name: relationName,
|
|
3480
|
-
sortable: false,
|
|
3481
|
-
filterEditor: filterEditor,
|
|
3482
|
-
filterEditorProps: filterEditorProps,
|
|
3483
|
-
render: ({ data }) => {
|
|
3484
|
-
value = column.id.reduce((acc, id) => {
|
|
3485
|
-
if (acc === '') {
|
|
3486
|
-
if (data[id]) {
|
|
3487
|
-
return data[id];
|
|
3488
|
-
} else {
|
|
3489
|
-
return '';
|
|
3490
|
-
}
|
|
3491
|
-
} else {
|
|
3492
|
-
if (acc && acc[id]) {
|
|
3493
|
-
return acc[id];
|
|
3494
|
-
} else {
|
|
3495
|
-
return '';
|
|
3496
|
-
}
|
|
3497
|
-
}
|
|
3498
|
-
}, '');
|
|
3499
|
-
|
|
3500
|
-
if (value) {
|
|
3501
|
-
if (Array.isArray(value)) {
|
|
3502
|
-
if (value[0]) {
|
|
3503
|
-
value = value[0];
|
|
3504
|
-
}
|
|
3505
|
-
}
|
|
3506
|
-
|
|
3507
|
-
if (
|
|
3508
|
-
Array.isArray(column.nameFrom)
|
|
3509
|
-
) {
|
|
3510
|
-
value = column.nameFrom
|
|
3511
|
-
.map(
|
|
3512
|
-
(nf) =>
|
|
3513
|
-
value?.[nf] ?? ''
|
|
3514
|
-
)
|
|
3515
|
-
.join(' ');
|
|
3516
|
-
} else {
|
|
3517
|
-
if (
|
|
3518
|
-
value &&
|
|
3519
|
-
value.hasOwnProperty(
|
|
3520
|
-
column.nameFrom
|
|
3521
|
-
)
|
|
3522
|
-
) {
|
|
3523
|
-
value =
|
|
3524
|
-
value[column.nameFrom];
|
|
3525
|
-
}
|
|
3526
|
-
}
|
|
3527
|
-
|
|
3528
|
-
if (
|
|
3529
|
-
column.placeholder &&
|
|
3530
|
-
column.placeholder !== ''
|
|
3531
|
-
) {
|
|
3532
|
-
return (
|
|
3533
|
-
<span>
|
|
3534
|
-
{column.placeholder}
|
|
3535
|
-
</span>
|
|
3536
|
-
);
|
|
3537
|
-
} else {
|
|
3538
|
-
if (
|
|
3539
|
-
column.hasOwnProperty(
|
|
3540
|
-
'format'
|
|
3541
|
-
)
|
|
3542
|
-
) {
|
|
3543
|
-
if (column.format) {
|
|
3544
|
-
value =
|
|
3545
|
-
moment(
|
|
3546
|
-
value
|
|
3547
|
-
).format(
|
|
3548
|
-
'DD-MM-YYYY'
|
|
3549
|
-
);
|
|
3550
|
-
}
|
|
3551
|
-
}
|
|
3552
|
-
|
|
3553
|
-
if (
|
|
3554
|
-
typeof value === 'string' ||
|
|
3555
|
-
typeof value === 'number'
|
|
3556
|
-
) {
|
|
3557
|
-
return (
|
|
3558
|
-
<CellWithTooltip
|
|
3559
|
-
value={value}
|
|
3560
|
-
columnType="relation"
|
|
3561
|
-
>
|
|
3562
|
-
<span>{value}</span>
|
|
3563
|
-
</CellWithTooltip>
|
|
3564
|
-
);
|
|
3565
|
-
} else {
|
|
3566
|
-
return null;
|
|
3567
|
-
}
|
|
3568
|
-
}
|
|
3569
|
-
} else {
|
|
3570
|
-
return null;
|
|
3571
|
-
}
|
|
3572
|
-
},
|
|
3573
|
-
};
|
|
3574
|
-
case 'relationArray':
|
|
3575
|
-
return {
|
|
3576
|
-
...commonProps,
|
|
3577
|
-
name: relationName,
|
|
3578
|
-
sortable: false,
|
|
3579
|
-
filterEditor: filterEditor,
|
|
3580
|
-
filterEditorProps: filterEditorProps,
|
|
3581
|
-
render: ({ data }) => {
|
|
3582
|
-
const relationValue = column.id.reduce(
|
|
3583
|
-
(acc, id) =>
|
|
3584
|
-
acc === ''
|
|
3585
|
-
? data[id]
|
|
3586
|
-
: acc && acc[id]
|
|
3587
|
-
? acc[id]
|
|
3588
|
-
: '',
|
|
3589
|
-
''
|
|
3590
|
-
);
|
|
3591
|
-
|
|
3592
|
-
if (
|
|
3593
|
-
relationValue &&
|
|
3594
|
-
Array.isArray(relationValue) &&
|
|
3595
|
-
relationValue.length > 0
|
|
3596
|
-
) {
|
|
3597
|
-
const nameProperties =
|
|
3598
|
-
Array.isArray(column.nameFrom)
|
|
3599
|
-
? column.nameFrom
|
|
3600
|
-
: [column.nameFrom];
|
|
3601
|
-
|
|
3602
|
-
const values = relationValue
|
|
3603
|
-
.map((rv) => {
|
|
3604
|
-
if (
|
|
3605
|
-
column.childId &&
|
|
3606
|
-
Array.isArray(
|
|
3607
|
-
column.childId
|
|
3608
|
-
)
|
|
3609
|
-
) {
|
|
3610
|
-
childValue = rv;
|
|
3611
|
-
|
|
3612
|
-
column.childId.forEach(
|
|
3613
|
-
(a) => {
|
|
3614
|
-
if (
|
|
3615
|
-
childValue[
|
|
3616
|
-
a
|
|
3617
|
-
]
|
|
3618
|
-
) {
|
|
3619
|
-
childValue =
|
|
3620
|
-
childValue[
|
|
3621
|
-
a
|
|
3622
|
-
];
|
|
3623
|
-
}
|
|
3624
|
-
}
|
|
3625
|
-
);
|
|
3626
|
-
|
|
3627
|
-
if (childValue) {
|
|
3628
|
-
const propertyValues =
|
|
3629
|
-
nameProperties
|
|
3630
|
-
.map(
|
|
3631
|
-
(
|
|
3632
|
-
prop
|
|
3633
|
-
) =>
|
|
3634
|
-
childValue[
|
|
3635
|
-
prop
|
|
3636
|
-
]
|
|
3637
|
-
)
|
|
3638
|
-
.filter(
|
|
3639
|
-
(
|
|
3640
|
-
value
|
|
3641
|
-
) =>
|
|
3642
|
-
value !==
|
|
3643
|
-
undefined &&
|
|
3644
|
-
value !==
|
|
3645
|
-
null
|
|
3646
|
-
);
|
|
3647
|
-
return propertyValues.join(
|
|
3648
|
-
' '
|
|
3649
|
-
);
|
|
3650
|
-
}
|
|
3651
|
-
} else {
|
|
3652
|
-
const propertyValues =
|
|
3653
|
-
nameProperties
|
|
3654
|
-
.map(
|
|
3655
|
-
(prop) =>
|
|
3656
|
-
rv[prop]
|
|
3657
|
-
)
|
|
3658
|
-
.filter(
|
|
3659
|
-
(value) =>
|
|
3660
|
-
value !==
|
|
3661
|
-
undefined &&
|
|
3662
|
-
value !==
|
|
3663
|
-
null
|
|
3664
|
-
);
|
|
3665
|
-
return propertyValues.join(
|
|
3666
|
-
' '
|
|
3667
|
-
);
|
|
3668
|
-
}
|
|
3669
|
-
})
|
|
3670
|
-
.filter(
|
|
3671
|
-
(value) => value !== ''
|
|
3672
|
-
);
|
|
3673
|
-
|
|
3674
|
-
// if (values.length > 0) {
|
|
3675
|
-
let result = '';
|
|
3676
|
-
|
|
3677
|
-
if (
|
|
3678
|
-
column.relation?.id &&
|
|
3679
|
-
column.relation?.key
|
|
3680
|
-
) {
|
|
3681
|
-
const relationValue =
|
|
3682
|
-
column.relation.key.reduce(
|
|
3683
|
-
(acc, id) =>
|
|
3684
|
-
acc === ''
|
|
3685
|
-
? data[id]
|
|
3686
|
-
: acc && acc[id]
|
|
3687
|
-
? acc[id]
|
|
3688
|
-
: '',
|
|
3689
|
-
''
|
|
3690
|
-
);
|
|
3691
|
-
|
|
3692
|
-
result = `${
|
|
3693
|
-
relationValue[
|
|
3694
|
-
column.relation.id
|
|
3695
|
-
]
|
|
3696
|
-
}<br />`;
|
|
3697
|
-
}
|
|
3698
|
-
|
|
3699
|
-
if (
|
|
3700
|
-
column.where &&
|
|
3701
|
-
column.where.id &&
|
|
3702
|
-
column.where.value
|
|
3703
|
-
) {
|
|
3704
|
-
if (
|
|
3705
|
-
data[column.where.id] ===
|
|
3706
|
-
column.where.value
|
|
3707
|
-
) {
|
|
3708
|
-
result +=
|
|
3709
|
-
values.join('<br />');
|
|
3710
|
-
}
|
|
3711
|
-
} else {
|
|
3712
|
-
result += values.join('<br />');
|
|
3713
|
-
}
|
|
3714
|
-
|
|
3715
|
-
return (
|
|
3716
|
-
<CellWithTooltip
|
|
3717
|
-
value={result}
|
|
3718
|
-
columnType="relationArray"
|
|
3719
|
-
>
|
|
3720
|
-
<span>{parse(result)}</span>
|
|
3721
|
-
</CellWithTooltip>
|
|
3722
|
-
);
|
|
3723
|
-
// }
|
|
3724
|
-
} else {
|
|
3725
|
-
let result = '';
|
|
3726
|
-
|
|
3727
|
-
if (
|
|
3728
|
-
column.relation?.id &&
|
|
3729
|
-
column.relation?.key
|
|
3730
|
-
) {
|
|
3731
|
-
const relationValue =
|
|
3732
|
-
column.relation.key.reduce(
|
|
3733
|
-
(acc, id) =>
|
|
3734
|
-
acc === ''
|
|
3735
|
-
? data[id]
|
|
3736
|
-
: acc && acc[id]
|
|
3737
|
-
? acc[id]
|
|
3738
|
-
: '',
|
|
3739
|
-
''
|
|
3740
|
-
);
|
|
3741
|
-
|
|
3742
|
-
result =
|
|
3743
|
-
relationValue &&
|
|
3744
|
-
relationValue[
|
|
3745
|
-
column.relation.id
|
|
3746
|
-
]
|
|
3747
|
-
? relationValue[
|
|
3748
|
-
column.relation.id
|
|
3749
|
-
]
|
|
3750
|
-
: '';
|
|
3751
|
-
}
|
|
3752
|
-
|
|
3753
|
-
return (
|
|
3754
|
-
<CellWithTooltip
|
|
3755
|
-
value={result}
|
|
3756
|
-
columnType="relationArray"
|
|
3757
|
-
>
|
|
3758
|
-
<span>{parse(result)}</span>
|
|
3759
|
-
</CellWithTooltip>
|
|
3760
|
-
);
|
|
3761
|
-
}
|
|
3762
|
-
|
|
3763
|
-
return null;
|
|
3764
|
-
},
|
|
3765
|
-
};
|
|
3766
|
-
case 'richtext':
|
|
3767
|
-
return {
|
|
3768
|
-
...commonProps,
|
|
3769
|
-
name: column.id,
|
|
3770
|
-
render: ({ data }) => {
|
|
3771
|
-
if (data && data[column.id]) {
|
|
3772
|
-
return (
|
|
3773
|
-
<CellWithTooltip
|
|
3774
|
-
value={data[column.id]}
|
|
3775
|
-
columnType="richtext"
|
|
3776
|
-
>
|
|
3777
|
-
<span>
|
|
3778
|
-
{parse(data[column.id])}
|
|
3779
|
-
</span>
|
|
3780
|
-
</CellWithTooltip>
|
|
3781
|
-
);
|
|
3782
|
-
} else {
|
|
3783
|
-
return null;
|
|
3784
|
-
}
|
|
3785
|
-
},
|
|
3786
|
-
};
|
|
3787
|
-
case 'stage':
|
|
3788
|
-
return {
|
|
3789
|
-
...commonProps,
|
|
3790
|
-
name: column.id,
|
|
3791
|
-
sortable: false,
|
|
3792
|
-
render: ({ data }) => {
|
|
3793
|
-
stage = '';
|
|
3794
|
-
|
|
3795
|
-
column.keyIds.forEach((a, b) => {
|
|
3796
|
-
if (data[a] === 1) {
|
|
3797
|
-
stage = column.valueIds[b];
|
|
3798
|
-
}
|
|
3799
|
-
});
|
|
3800
|
-
|
|
3801
|
-
return (
|
|
3802
|
-
<CellWithTooltip
|
|
3803
|
-
value={stage}
|
|
3804
|
-
columnType="stage"
|
|
3805
|
-
>
|
|
3806
|
-
<span>{stage}</span>
|
|
3807
|
-
</CellWithTooltip>
|
|
3808
|
-
);
|
|
3809
|
-
},
|
|
3810
|
-
};
|
|
3811
|
-
case 'time':
|
|
3812
|
-
return {
|
|
3813
|
-
...commonProps,
|
|
3814
|
-
name: column.id,
|
|
3815
|
-
render: ({ data }) => {
|
|
3816
|
-
if (
|
|
3817
|
-
data &&
|
|
3818
|
-
data[column.id] &&
|
|
3819
|
-
moment(data[column.id]).isValid() &&
|
|
3820
|
-
moment(data[column.id]).format(
|
|
3821
|
-
'YYYY-MM-DD'
|
|
3822
|
-
) !== '1970-01-01'
|
|
3823
|
-
) {
|
|
3824
|
-
data = moment(
|
|
3825
|
-
data[column.id]
|
|
3826
|
-
).format(
|
|
3827
|
-
column.format
|
|
3828
|
-
? column.format
|
|
3829
|
-
: 'hh:mm A'
|
|
3830
|
-
);
|
|
3831
|
-
|
|
3832
|
-
return (
|
|
3833
|
-
<CellWithTooltip
|
|
3834
|
-
value={data[column.id]}
|
|
3835
|
-
columnType="time"
|
|
3836
|
-
>
|
|
3837
|
-
<span>{data}</span>
|
|
3838
|
-
</CellWithTooltip>
|
|
3839
|
-
);
|
|
3840
|
-
} else {
|
|
3841
|
-
return null;
|
|
3842
|
-
}
|
|
3843
|
-
},
|
|
3844
|
-
};
|
|
3845
|
-
case 'timer':
|
|
3846
|
-
return {
|
|
3847
|
-
...commonProps,
|
|
3848
|
-
name: column.id,
|
|
3849
|
-
render: ({ data }) => {
|
|
3850
|
-
if (
|
|
3851
|
-
data &&
|
|
3852
|
-
data[column.id] &&
|
|
3853
|
-
moment(data[column.id]).isValid() &&
|
|
3854
|
-
moment(data[column.id]).format(
|
|
3855
|
-
'YYYY-MM-DD'
|
|
3856
|
-
) !== '1970-01-01'
|
|
3857
|
-
) {
|
|
3858
|
-
data = moment(
|
|
3859
|
-
data[column.id]
|
|
3860
|
-
).format(
|
|
3861
|
-
column.format
|
|
3862
|
-
? column.format
|
|
3863
|
-
: 'DD-MM-YYYY hh:mm A'
|
|
3864
|
-
);
|
|
3865
|
-
|
|
3866
|
-
return (
|
|
3867
|
-
<CellWithTooltip
|
|
3868
|
-
value={data[column.id]}
|
|
3869
|
-
columnType="timer"
|
|
3870
|
-
>
|
|
3871
|
-
<span>{data}</span>
|
|
3872
|
-
</CellWithTooltip>
|
|
3873
|
-
);
|
|
3874
|
-
} else {
|
|
3875
|
-
let shouldRenderButton = false;
|
|
3876
|
-
|
|
3877
|
-
if (
|
|
3878
|
-
!column.hasOwnProperty(
|
|
3879
|
-
'require'
|
|
3880
|
-
) ||
|
|
3881
|
-
column.require.length === 0
|
|
3882
|
-
) {
|
|
3883
|
-
shouldRenderButton = true;
|
|
3884
|
-
} else {
|
|
3885
|
-
for (const req of column.require) {
|
|
3886
|
-
if (
|
|
3887
|
-
req.hasOwnProperty('id')
|
|
3888
|
-
) {
|
|
3889
|
-
const hasValue =
|
|
3890
|
-
req.hasOwnProperty(
|
|
3891
|
-
'value'
|
|
3892
|
-
);
|
|
3893
|
-
if (
|
|
3894
|
-
(hasValue &&
|
|
3895
|
-
data[req.id] ===
|
|
3896
|
-
req.value) ||
|
|
3897
|
-
(!hasValue &&
|
|
3898
|
-
data[req.id])
|
|
3899
|
-
) {
|
|
3900
|
-
shouldRenderButton = true;
|
|
3901
|
-
} else {
|
|
3902
|
-
shouldRenderButton = false;
|
|
3903
|
-
break;
|
|
3904
|
-
}
|
|
3905
|
-
}
|
|
3906
|
-
}
|
|
3907
|
-
}
|
|
3908
|
-
|
|
3909
|
-
if (shouldRenderButton) {
|
|
3910
|
-
return (
|
|
3911
|
-
<button
|
|
3912
|
-
className={
|
|
3913
|
-
styles.clockButton
|
|
3914
|
-
}
|
|
3915
|
-
onClick={(e) => {
|
|
3916
|
-
e.stopPropagation();
|
|
3917
|
-
e.preventDefault();
|
|
3918
|
-
|
|
3919
|
-
// Check if every required field in column.validate exists and is truthy in data
|
|
3920
|
-
if (
|
|
3921
|
-
column.validate &&
|
|
3922
|
-
column.validate
|
|
3923
|
-
.length > 0
|
|
3924
|
-
) {
|
|
3925
|
-
const missingFields =
|
|
3926
|
-
column.validate.filter(
|
|
3927
|
-
(v) =>
|
|
3928
|
-
!data[
|
|
3929
|
-
v
|
|
3930
|
-
]
|
|
3931
|
-
);
|
|
3932
|
-
|
|
3933
|
-
if (
|
|
3934
|
-
missingFields.length ===
|
|
3935
|
-
0
|
|
3936
|
-
) {
|
|
3937
|
-
handleTimer(
|
|
3938
|
-
data[
|
|
3939
|
-
form
|
|
3940
|
-
.primaryKey
|
|
3941
|
-
],
|
|
3942
|
-
column.id,
|
|
3943
|
-
data
|
|
3944
|
-
);
|
|
3945
|
-
} else {
|
|
3946
|
-
// Format field names to be more readable
|
|
3947
|
-
const formattedFields =
|
|
3948
|
-
missingFields.map(
|
|
3949
|
-
(
|
|
3950
|
-
field
|
|
3951
|
-
) => {
|
|
3952
|
-
// Try to find the column with this field ID to get its label
|
|
3953
|
-
const fieldColumn =
|
|
3954
|
-
columns.find(
|
|
3955
|
-
(
|
|
3956
|
-
col
|
|
3957
|
-
) =>
|
|
3958
|
-
col.id ===
|
|
3959
|
-
field
|
|
3960
|
-
);
|
|
3961
|
-
// Use the label if found, otherwise use the field ID
|
|
3962
|
-
return (
|
|
3963
|
-
fieldColumn?.label ||
|
|
3964
|
-
field
|
|
3965
|
-
);
|
|
3966
|
-
}
|
|
3967
|
-
);
|
|
3968
|
-
|
|
3969
|
-
const fieldList =
|
|
3970
|
-
formattedFields.join(
|
|
3971
|
-
', '
|
|
3972
|
-
);
|
|
3973
|
-
toast.error(
|
|
3974
|
-
`Please complete the following required field${
|
|
3975
|
-
missingFields.length >
|
|
3976
|
-
1
|
|
3977
|
-
? 's'
|
|
3978
|
-
: ''
|
|
3979
|
-
} before starting the timer: ${fieldList}`
|
|
3980
|
-
);
|
|
3981
|
-
}
|
|
3982
|
-
} else {
|
|
3983
|
-
handleTimer(
|
|
3984
|
-
data[
|
|
3985
|
-
form
|
|
3986
|
-
.primaryKey
|
|
3987
|
-
],
|
|
3988
|
-
column.id,
|
|
3989
|
-
data
|
|
3990
|
-
);
|
|
3991
|
-
}
|
|
3992
|
-
}}
|
|
3993
|
-
>
|
|
3994
|
-
<Alarm
|
|
3995
|
-
data-tooltip-id="system-tooltip"
|
|
3996
|
-
data-tooltip-content={`${column.label} Timer`}
|
|
3997
|
-
strokeWidth={2}
|
|
3998
|
-
size={14}
|
|
3999
|
-
style={{
|
|
4000
|
-
color: 'white',
|
|
4001
|
-
}}
|
|
4002
|
-
/>
|
|
4003
|
-
</button>
|
|
4004
|
-
);
|
|
4005
|
-
}
|
|
4006
|
-
|
|
4007
|
-
// Return a placeholder div to maintain consistent row height
|
|
4008
|
-
return (
|
|
4009
|
-
<div
|
|
4010
|
-
style={{
|
|
4011
|
-
width: '28px',
|
|
4012
|
-
height: '28px',
|
|
4013
|
-
display: 'flex',
|
|
4014
|
-
alignItems: 'center',
|
|
4015
|
-
justifyContent:
|
|
4016
|
-
'center',
|
|
4017
|
-
margin: '0 auto',
|
|
4018
|
-
}}
|
|
4019
|
-
>
|
|
4020
|
-
{/* Empty placeholder to maintain layout */}
|
|
4021
|
-
</div>
|
|
4022
|
-
);
|
|
4023
|
-
}
|
|
4024
|
-
},
|
|
4025
|
-
};
|
|
4026
|
-
case 'stageCounter':
|
|
4027
|
-
return {
|
|
4028
|
-
...commonProps,
|
|
4029
|
-
name: column.id,
|
|
4030
|
-
sortable: false,
|
|
4031
|
-
render: ({ data }) => {
|
|
4032
|
-
if (
|
|
4033
|
-
data &&
|
|
4034
|
-
column.stageConfig &&
|
|
4035
|
-
column.stageConfig.key
|
|
4036
|
-
) {
|
|
4037
|
-
// Check if the data has the specified key and it's an array
|
|
4038
|
-
const stageData =
|
|
4039
|
-
data[column.stageConfig.key];
|
|
4040
|
-
|
|
4041
|
-
if (
|
|
4042
|
-
stageData &&
|
|
4043
|
-
Array.isArray(stageData)
|
|
4044
|
-
) {
|
|
4045
|
-
const stageCount =
|
|
4046
|
-
stageData.length;
|
|
4047
|
-
|
|
4048
|
-
// Create a clickable element if link is provided
|
|
4049
|
-
const handleClick = (e) => {
|
|
4050
|
-
e.stopPropagation();
|
|
4051
|
-
e.preventDefault();
|
|
4052
|
-
|
|
4053
|
-
if (
|
|
4054
|
-
column.link &&
|
|
4055
|
-
column.link.url &&
|
|
4056
|
-
column.link.key
|
|
4057
|
-
) {
|
|
4058
|
-
navigate(
|
|
4059
|
-
`${
|
|
4060
|
-
column.link.url
|
|
4061
|
-
}${
|
|
4062
|
-
data[
|
|
4063
|
-
column.link
|
|
4064
|
-
.key
|
|
4065
|
-
]
|
|
4066
|
-
}`
|
|
4067
|
-
);
|
|
4068
|
-
}
|
|
4069
|
-
};
|
|
4070
|
-
|
|
4071
|
-
// Create an array of squares based on the stage data
|
|
4072
|
-
const squares = [];
|
|
4073
|
-
for (
|
|
4074
|
-
let i = 0;
|
|
4075
|
-
i < stageCount;
|
|
4076
|
-
i++
|
|
4077
|
-
) {
|
|
4078
|
-
// Get the stage item
|
|
4079
|
-
const stageItem =
|
|
4080
|
-
stageData[i];
|
|
4081
|
-
|
|
4082
|
-
// Get the label value from the stage item using stageLabelKey if provided
|
|
4083
|
-
let labelValue = '';
|
|
4084
|
-
if (
|
|
4085
|
-
column.stageConfig
|
|
4086
|
-
.stageLabelKey &&
|
|
4087
|
-
stageItem &&
|
|
4088
|
-
stageItem[
|
|
4089
|
-
column.stageConfig
|
|
4090
|
-
.stageLabelKey
|
|
4091
|
-
] !== undefined
|
|
4092
|
-
) {
|
|
4093
|
-
// Get the value from the stage item
|
|
4094
|
-
const rawValue =
|
|
4095
|
-
stageItem[
|
|
4096
|
-
column
|
|
4097
|
-
.stageConfig
|
|
4098
|
-
.stageLabelKey
|
|
4099
|
-
];
|
|
4100
|
-
|
|
4101
|
-
// If it's an object with a name property, use that
|
|
4102
|
-
if (
|
|
4103
|
-
rawValue &&
|
|
4104
|
-
typeof rawValue ===
|
|
4105
|
-
'object' &&
|
|
4106
|
-
rawValue.name
|
|
4107
|
-
) {
|
|
4108
|
-
labelValue =
|
|
4109
|
-
rawValue.name.charAt(
|
|
4110
|
-
0
|
|
4111
|
-
);
|
|
4112
|
-
}
|
|
4113
|
-
// If it's a string, use the first character
|
|
4114
|
-
else if (
|
|
4115
|
-
typeof rawValue ===
|
|
4116
|
-
'string' &&
|
|
4117
|
-
rawValue.length > 0
|
|
4118
|
-
) {
|
|
4119
|
-
labelValue =
|
|
4120
|
-
rawValue.charAt(
|
|
4121
|
-
0
|
|
4122
|
-
);
|
|
4123
|
-
}
|
|
4124
|
-
// Otherwise use the raw value if it can be converted to string
|
|
4125
|
-
else if (
|
|
4126
|
-
rawValue !== null &&
|
|
4127
|
-
rawValue !==
|
|
4128
|
-
undefined
|
|
4129
|
-
) {
|
|
4130
|
-
labelValue =
|
|
4131
|
-
String(
|
|
4132
|
-
rawValue
|
|
4133
|
-
).charAt(0);
|
|
4134
|
-
}
|
|
4135
|
-
}
|
|
4136
|
-
// If no stageLabelKey is provided or no value is found, leave it blank
|
|
4137
|
-
|
|
4138
|
-
// Create tooltip content from the full value if available
|
|
4139
|
-
let tooltipContent = `Stage ${
|
|
4140
|
-
i + 1
|
|
4141
|
-
}`;
|
|
4142
|
-
if (
|
|
4143
|
-
column.stageConfig
|
|
4144
|
-
.stageLabelKey &&
|
|
4145
|
-
stageItem &&
|
|
4146
|
-
stageItem[
|
|
4147
|
-
column.stageConfig
|
|
4148
|
-
.stageLabelKey
|
|
4149
|
-
]
|
|
4150
|
-
) {
|
|
4151
|
-
const rawValue =
|
|
4152
|
-
stageItem[
|
|
4153
|
-
column
|
|
4154
|
-
.stageConfig
|
|
4155
|
-
.stageLabelKey
|
|
4156
|
-
];
|
|
4157
|
-
if (
|
|
4158
|
-
rawValue &&
|
|
4159
|
-
typeof rawValue ===
|
|
4160
|
-
'object' &&
|
|
4161
|
-
rawValue.name
|
|
4162
|
-
) {
|
|
4163
|
-
tooltipContent = `${
|
|
4164
|
-
rawValue.name
|
|
4165
|
-
} (Stage ${i + 1})`;
|
|
4166
|
-
} else if (
|
|
4167
|
-
rawValue !== null &&
|
|
4168
|
-
rawValue !==
|
|
4169
|
-
undefined
|
|
4170
|
-
) {
|
|
4171
|
-
tooltipContent = `Stage ${
|
|
4172
|
-
i + 1
|
|
4173
|
-
})`;
|
|
4174
|
-
}
|
|
4175
|
-
}
|
|
4176
|
-
|
|
4177
|
-
// Determine the background color based on stageColour configuration
|
|
4178
|
-
let backgroundColor =
|
|
4179
|
-
'var(--primary-color, #4a90e2)';
|
|
4180
|
-
|
|
4181
|
-
if (
|
|
4182
|
-
column.stageConfig
|
|
4183
|
-
.stageColour &&
|
|
4184
|
-
column.stageConfig
|
|
4185
|
-
.stageColour.key &&
|
|
4186
|
-
column.stageConfig
|
|
4187
|
-
.stageColour
|
|
4188
|
-
.options &&
|
|
4189
|
-
stageItem
|
|
4190
|
-
) {
|
|
4191
|
-
const colourKey =
|
|
4192
|
-
column.stageConfig
|
|
4193
|
-
.stageColour
|
|
4194
|
-
.key;
|
|
4195
|
-
const colourOptions =
|
|
4196
|
-
column.stageConfig
|
|
4197
|
-
.stageColour
|
|
4198
|
-
.options;
|
|
4199
|
-
|
|
4200
|
-
// Get the value for the color key
|
|
4201
|
-
if (
|
|
4202
|
-
stageItem[
|
|
4203
|
-
colourKey
|
|
4204
|
-
] !== undefined
|
|
4205
|
-
) {
|
|
4206
|
-
const keyValue =
|
|
4207
|
-
stageItem[
|
|
4208
|
-
colourKey
|
|
4209
|
-
];
|
|
4210
|
-
|
|
4211
|
-
// Find matching option
|
|
4212
|
-
const matchingOption =
|
|
4213
|
-
colourOptions.find(
|
|
4214
|
-
(
|
|
4215
|
-
option
|
|
4216
|
-
) => {
|
|
4217
|
-
// Handle both direct value and object with id
|
|
4218
|
-
if (
|
|
4219
|
-
typeof keyValue ===
|
|
4220
|
-
'object' &&
|
|
4221
|
-
keyValue !==
|
|
4222
|
-
null
|
|
4223
|
-
) {
|
|
4224
|
-
// Use loose equality (==) to handle type coercion between string and number
|
|
4225
|
-
return (
|
|
4226
|
-
keyValue.id ==
|
|
4227
|
-
option.id
|
|
4228
|
-
);
|
|
4229
|
-
}
|
|
4230
|
-
// Use loose equality (==) to handle type coercion between string and number
|
|
4231
|
-
return (
|
|
4232
|
-
keyValue ==
|
|
4233
|
-
option.id
|
|
4234
|
-
);
|
|
4235
|
-
}
|
|
4236
|
-
);
|
|
4237
|
-
|
|
4238
|
-
// Use the color from the matching option if found
|
|
4239
|
-
if (
|
|
4240
|
-
matchingOption &&
|
|
4241
|
-
matchingOption.colour
|
|
4242
|
-
) {
|
|
4243
|
-
backgroundColor =
|
|
4244
|
-
matchingOption.colour;
|
|
4245
|
-
}
|
|
4246
|
-
}
|
|
4247
|
-
}
|
|
4248
|
-
|
|
4249
|
-
squares.push(
|
|
4250
|
-
<div
|
|
4251
|
-
key={i}
|
|
4252
|
-
style={{
|
|
4253
|
-
width: '20px',
|
|
4254
|
-
height: '20px',
|
|
4255
|
-
backgroundColor:
|
|
4256
|
-
backgroundColor,
|
|
4257
|
-
borderRadius:
|
|
4258
|
-
'3px',
|
|
4259
|
-
margin: '0 2px',
|
|
4260
|
-
display: 'flex',
|
|
4261
|
-
justifyContent:
|
|
4262
|
-
'center',
|
|
4263
|
-
alignItems:
|
|
4264
|
-
'center',
|
|
4265
|
-
color: 'white',
|
|
4266
|
-
fontSize:
|
|
4267
|
-
'11px',
|
|
4268
|
-
fontWeight:
|
|
4269
|
-
'bold',
|
|
4270
|
-
}}
|
|
4271
|
-
data-tooltip-id="system-tooltip"
|
|
4272
|
-
data-tooltip-content={
|
|
4273
|
-
tooltipContent
|
|
4274
|
-
}
|
|
4275
|
-
>
|
|
4276
|
-
{labelValue}
|
|
4277
|
-
</div>
|
|
4278
|
-
);
|
|
4279
|
-
}
|
|
4280
|
-
|
|
4281
|
-
return (
|
|
4282
|
-
<div
|
|
4283
|
-
onClick={handleClick}
|
|
4284
|
-
style={{
|
|
4285
|
-
display: 'flex',
|
|
4286
|
-
flexWrap: 'wrap',
|
|
4287
|
-
justifyContent:
|
|
4288
|
-
'center',
|
|
4289
|
-
alignItems:
|
|
4290
|
-
'center',
|
|
4291
|
-
cursor: column.link
|
|
4292
|
-
? 'pointer'
|
|
4293
|
-
: 'default',
|
|
4294
|
-
gap: '4px',
|
|
4295
|
-
padding: '2px 0',
|
|
4296
|
-
maxWidth: '100%',
|
|
4297
|
-
}}
|
|
4298
|
-
>
|
|
4299
|
-
{squares}
|
|
4300
|
-
</div>
|
|
4301
|
-
);
|
|
4302
|
-
}
|
|
4303
|
-
}
|
|
4304
|
-
return null;
|
|
4305
|
-
},
|
|
4306
|
-
};
|
|
4307
|
-
case 'url':
|
|
4308
|
-
return {
|
|
4309
|
-
...commonProps,
|
|
4310
|
-
name: column.id,
|
|
4311
|
-
render: ({ data }) => {
|
|
4312
|
-
if (data && data[column.id]) {
|
|
4313
|
-
return (
|
|
4314
|
-
<CellWithTooltip
|
|
4315
|
-
value={data[column.id]}
|
|
4316
|
-
columnType="url"
|
|
4317
|
-
>
|
|
4318
|
-
<span>
|
|
4319
|
-
<a
|
|
4320
|
-
href={
|
|
4321
|
-
data[column.id]
|
|
4322
|
-
}
|
|
4323
|
-
target="_blank"
|
|
4324
|
-
>
|
|
4325
|
-
Link
|
|
4326
|
-
</a>
|
|
4327
|
-
</span>
|
|
4328
|
-
</CellWithTooltip>
|
|
4329
|
-
);
|
|
4330
|
-
} else {
|
|
4331
|
-
return null;
|
|
4332
|
-
}
|
|
4333
|
-
},
|
|
4334
|
-
};
|
|
4335
|
-
case 'input_text':
|
|
4336
|
-
return {
|
|
4337
|
-
...commonProps,
|
|
4338
|
-
name: column.id,
|
|
4339
|
-
render: ({ data }) => {
|
|
4340
|
-
const inputKey = `${data.id}-${column.id}`;
|
|
4341
|
-
|
|
4342
|
-
// Use the ref value first, then fall back to data value
|
|
4343
|
-
const currentValue =
|
|
4344
|
-
localInputValuesRef.current[
|
|
4345
|
-
inputKey
|
|
4346
|
-
] !== undefined
|
|
4347
|
-
? localInputValuesRef.current[
|
|
4348
|
-
inputKey
|
|
4349
|
-
]
|
|
4350
|
-
: data[column.id] || '';
|
|
4351
|
-
|
|
4352
|
-
return (
|
|
4353
|
-
<input
|
|
4354
|
-
type="text"
|
|
4355
|
-
name={column.id}
|
|
4356
|
-
value={currentValue}
|
|
4357
|
-
style={{
|
|
4358
|
-
padding: '7px',
|
|
4359
|
-
width: '100%',
|
|
4360
|
-
}}
|
|
4361
|
-
onChange={(e) => {
|
|
4362
|
-
const newValue =
|
|
4363
|
-
e.target.value;
|
|
4364
|
-
|
|
4365
|
-
// Update both state and ref
|
|
4366
|
-
localInputValuesRef.current =
|
|
4367
|
-
{
|
|
4368
|
-
...localInputValuesRef.current,
|
|
4369
|
-
[inputKey]:
|
|
4370
|
-
newValue,
|
|
4371
|
-
};
|
|
4372
|
-
|
|
4373
|
-
setLocalInputValues(
|
|
4374
|
-
(prev) => ({
|
|
4375
|
-
...prev,
|
|
4376
|
-
[inputKey]:
|
|
4377
|
-
newValue,
|
|
4378
|
-
})
|
|
4379
|
-
);
|
|
4380
|
-
|
|
4381
|
-
if (
|
|
4382
|
-
column.update?.key &&
|
|
4383
|
-
column.update?.url &&
|
|
4384
|
-
column.update?.method
|
|
4385
|
-
) {
|
|
4386
|
-
handleInputUpdate(
|
|
4387
|
-
column.update.key,
|
|
4388
|
-
`${column.update.url}/${data.id}`,
|
|
4389
|
-
column.update
|
|
4390
|
-
.method,
|
|
4391
|
-
newValue,
|
|
4392
|
-
inputKey
|
|
4393
|
-
);
|
|
4394
|
-
}
|
|
4395
|
-
}}
|
|
4396
|
-
onBlur={() => {
|
|
4397
|
-
if (
|
|
4398
|
-
column.update?.key &&
|
|
4399
|
-
column.update?.url &&
|
|
4400
|
-
column.update?.method
|
|
4401
|
-
) {
|
|
4402
|
-
handleInputUpdate.flush();
|
|
4403
|
-
}
|
|
4404
|
-
}}
|
|
4405
|
-
/>
|
|
4406
|
-
);
|
|
4407
|
-
},
|
|
4408
|
-
};
|
|
4409
|
-
default:
|
|
4410
|
-
columnId = Array.isArray(column.id)
|
|
4411
|
-
? column.id.join('-')
|
|
4412
|
-
: column.id;
|
|
4413
|
-
|
|
4414
|
-
return {
|
|
4415
|
-
...commonProps,
|
|
4416
|
-
name: columnId,
|
|
4417
|
-
filterEditor: filterEditor,
|
|
4418
|
-
filterEditorProps: filterEditorProps,
|
|
4419
|
-
render: ({ data }) => {
|
|
4420
|
-
if (
|
|
4421
|
-
data &&
|
|
4422
|
-
data[column.id] &&
|
|
4423
|
-
data[column.id] !== ''
|
|
4424
|
-
) {
|
|
4425
|
-
let content = String(
|
|
4426
|
-
data[column.id]
|
|
4427
|
-
);
|
|
4428
|
-
|
|
4429
|
-
// If removeFormat is true, strip HTML tags
|
|
4430
|
-
if (column.removeFormat) {
|
|
4431
|
-
content = content.replace(
|
|
4432
|
-
/<\/?[^>]+(>|$)/g,
|
|
4433
|
-
''
|
|
4434
|
-
); // Simple regex to strip HTML tags
|
|
4435
|
-
} else {
|
|
4436
|
-
// Replace p tags with br tags
|
|
4437
|
-
content = content
|
|
4438
|
-
.replace(/<p>/g, '')
|
|
4439
|
-
.replace(/<\/p>/g, '<br>');
|
|
4440
|
-
|
|
4441
|
-
// Ensure <br /> tags are properly handled (convert to <br> for consistency)
|
|
4442
|
-
content = content
|
|
4443
|
-
.replace(/<br \/>/g, '<br>')
|
|
4444
|
-
.replace(/<br\/>/g, '<br>');
|
|
4445
|
-
}
|
|
4446
|
-
|
|
4447
|
-
// If truncate has a value, limit content to specified number of characters
|
|
4448
|
-
if (column.truncate) {
|
|
4449
|
-
content =
|
|
4450
|
-
content.length >
|
|
4451
|
-
column.truncate
|
|
4452
|
-
? content.substring(
|
|
4453
|
-
0,
|
|
4454
|
-
column.truncate
|
|
4455
|
-
) + '...'
|
|
4456
|
-
: content;
|
|
4457
|
-
}
|
|
4458
|
-
|
|
4459
|
-
return (
|
|
4460
|
-
<CellWithTooltip
|
|
4461
|
-
value={data[column.id]}
|
|
4462
|
-
columnType="text"
|
|
4463
|
-
>
|
|
4464
|
-
<span>
|
|
4465
|
-
{parse(content)}
|
|
4466
|
-
</span>
|
|
4467
|
-
</CellWithTooltip>
|
|
4468
|
-
);
|
|
4469
|
-
} else {
|
|
4470
|
-
return null;
|
|
4471
|
-
}
|
|
4472
|
-
},
|
|
4473
|
-
};
|
|
4474
|
-
}
|
|
4475
|
-
};
|
|
4476
|
-
|
|
4477
|
-
const newColumns = columns.map(renderColumn);
|
|
4478
|
-
|
|
4479
|
-
if (settings.length > 0) {
|
|
4480
|
-
newColumns.push({
|
|
4481
|
-
name: 'setting',
|
|
4482
|
-
header: 'Action',
|
|
4483
|
-
defaultWidth: 100,
|
|
4484
|
-
textAlign: 'center',
|
|
4485
|
-
textVerticalAlign:
|
|
4486
|
-
style && style.text_vertical_align
|
|
4487
|
-
? style.text_vertical_align
|
|
4488
|
-
: 'center',
|
|
4489
|
-
render: ({ data }) => {
|
|
4490
|
-
return (
|
|
4491
|
-
<div className={styles.tdactions}>
|
|
4492
|
-
{settings.map((setting) =>
|
|
4493
|
-
renderSetting(setting, data)
|
|
4494
|
-
)}
|
|
4495
|
-
</div>
|
|
4496
|
-
);
|
|
4497
|
-
},
|
|
4498
|
-
});
|
|
4499
|
-
}
|
|
4500
|
-
|
|
4501
|
-
setGridColumns(newColumns);
|
|
4502
|
-
|
|
4503
|
-
const newFilterValue = columns
|
|
4504
|
-
.filter((column) => column.filter && column.filter.type) // Only consider columns with a filter type
|
|
4505
|
-
.map((column) => {
|
|
4506
|
-
// Map these filtered columns to a new object array
|
|
4507
|
-
let filterName = '';
|
|
2763
|
+
const newFilterValue = columns
|
|
2764
|
+
.filter((column) => column.filter && column.filter.type) // Only consider columns with a filter type
|
|
2765
|
+
.map((column) => {
|
|
2766
|
+
// Map these filtered columns to a new object array
|
|
2767
|
+
let filterName = '';
|
|
4508
2768
|
|
|
4509
2769
|
if (Array.isArray(column.id)) {
|
|
4510
2770
|
filterName = column.id.reduce(
|
|
@@ -4767,16 +3027,36 @@ const DataGrid = forwardRef(
|
|
|
4767
3027
|
<>
|
|
4768
3028
|
{/* Top container with search, action buttons, and auto-refresh */}
|
|
4769
3029
|
{/* Only show top container if there are controls to display */}
|
|
4770
|
-
{(
|
|
3030
|
+
{(!form.searchDisable ||
|
|
3031
|
+
!form.createDisable ||
|
|
4771
3032
|
(!shouldRelocateAutoRefresh() &&
|
|
4772
|
-
|
|
3033
|
+
ajaxSetting &&
|
|
3034
|
+
ajaxSetting.autoRefresh !== undefined)) && (
|
|
4773
3035
|
<div className={styles.dataGridTopContainer}>
|
|
4774
3036
|
<div className={styles.dataGridTopLeftContainer}>
|
|
4775
|
-
|
|
3037
|
+
<DataGridSearch
|
|
3038
|
+
form={form}
|
|
3039
|
+
search={search}
|
|
3040
|
+
onSearchChange={handleChangeSearch}
|
|
3041
|
+
onModalOpen={modalOpen}
|
|
3042
|
+
dataRef={dataRef}
|
|
3043
|
+
filterValue={filterValue}
|
|
3044
|
+
paramValue={paramValue}
|
|
3045
|
+
pageData={pageData}
|
|
3046
|
+
pageSetting={pageSetting}
|
|
3047
|
+
/>
|
|
4776
3048
|
</div>
|
|
4777
3049
|
<div className={styles.dataGridTopRightContainer}>
|
|
4778
|
-
{!shouldRelocateAutoRefresh() &&
|
|
4779
|
-
|
|
3050
|
+
{!shouldRelocateAutoRefresh() && (
|
|
3051
|
+
<AutoRefreshControls
|
|
3052
|
+
ajaxSetting={ajaxSetting}
|
|
3053
|
+
autoRefreshEnabled={autoRefreshEnabled}
|
|
3054
|
+
setAutoRefreshEnabled={
|
|
3055
|
+
setAutoRefreshEnabled
|
|
3056
|
+
}
|
|
3057
|
+
refreshInterval={refreshInterval}
|
|
3058
|
+
/>
|
|
3059
|
+
)}
|
|
4780
3060
|
</div>
|
|
4781
3061
|
</div>
|
|
4782
3062
|
)}
|
|
@@ -4907,64 +3187,19 @@ const DataGrid = forwardRef(
|
|
|
4907
3187
|
|
|
4908
3188
|
return (
|
|
4909
3189
|
<div
|
|
4910
|
-
className=
|
|
3190
|
+
className={`group-header-clean group-header-fixed-height ${styles.groupHeaderContainer}`}
|
|
4911
3191
|
onClick={
|
|
4912
3192
|
handleGroupHeaderClick
|
|
4913
3193
|
}
|
|
4914
|
-
style={{
|
|
4915
|
-
display: 'flex',
|
|
4916
|
-
alignItems: 'center',
|
|
4917
|
-
justifyContent:
|
|
4918
|
-
'space-between',
|
|
4919
|
-
width: '100%',
|
|
4920
|
-
height: '34px', // Compact height
|
|
4921
|
-
padding: '6px 12px', // Reduced padding for compact design
|
|
4922
|
-
background:
|
|
4923
|
-
'linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)',
|
|
4924
|
-
borderLeft:
|
|
4925
|
-
'4px solid var(--primary-color, #3b82f6)',
|
|
4926
|
-
position: 'relative',
|
|
4927
|
-
overflow: 'hidden',
|
|
4928
|
-
boxShadow:
|
|
4929
|
-
'0 1px 3px rgba(0,0,0,0.1)',
|
|
4930
|
-
borderRadius:
|
|
4931
|
-
'0 4px 4px 0',
|
|
4932
|
-
cursor: 'pointer',
|
|
4933
|
-
boxSizing: 'border-box',
|
|
4934
|
-
lineHeight: '1.1', // Compact line height
|
|
4935
|
-
flexShrink: 0,
|
|
4936
|
-
transition:
|
|
4937
|
-
'background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease',
|
|
4938
|
-
}}
|
|
4939
|
-
onMouseEnter={(e) => {
|
|
4940
|
-
e.currentTarget.style.background =
|
|
4941
|
-
'linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%)';
|
|
4942
|
-
e.currentTarget.style.boxShadow =
|
|
4943
|
-
'0 2px 8px rgba(0,0,0,0.15)';
|
|
4944
|
-
e.currentTarget.style.transform =
|
|
4945
|
-
'translateY(-1px)';
|
|
4946
|
-
}}
|
|
4947
|
-
onMouseLeave={(e) => {
|
|
4948
|
-
e.currentTarget.style.background =
|
|
4949
|
-
'linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)';
|
|
4950
|
-
e.currentTarget.style.boxShadow =
|
|
4951
|
-
'0 1px 3px rgba(0,0,0,0.1)';
|
|
4952
|
-
e.currentTarget.style.transform =
|
|
4953
|
-
'translateY(0)';
|
|
4954
|
-
}}
|
|
4955
3194
|
>
|
|
4956
3195
|
{/* Left side - Group info and expand/collapse */}
|
|
4957
3196
|
<div
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
flex: 1,
|
|
4962
|
-
zIndex: 2,
|
|
4963
|
-
position: 'relative',
|
|
4964
|
-
}}
|
|
3197
|
+
className={
|
|
3198
|
+
styles.groupActionContainer
|
|
3199
|
+
}
|
|
4965
3200
|
>
|
|
4966
3201
|
<button
|
|
4967
|
-
className=
|
|
3202
|
+
className={`group-expand-btn ${styles.groupExpandBtn}`}
|
|
4968
3203
|
onClick={(e) => {
|
|
4969
3204
|
e.stopPropagation();
|
|
4970
3205
|
if (
|
|
@@ -4972,110 +3207,165 @@ const DataGrid = forwardRef(
|
|
|
4972
3207
|
'function'
|
|
4973
3208
|
) {
|
|
4974
3209
|
toggleGroup();
|
|
4975
|
-
} else {
|
|
4976
|
-
console.warn(
|
|
4977
|
-
'toggleGroup is not a function:',
|
|
4978
|
-
toggleGroup
|
|
4979
|
-
);
|
|
4980
3210
|
}
|
|
4981
3211
|
}}
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
display: 'flex',
|
|
4992
|
-
alignItems:
|
|
4993
|
-
'center',
|
|
4994
|
-
justifyContent:
|
|
4995
|
-
'center',
|
|
4996
|
-
marginRight:
|
|
4997
|
-
'8px', // Reduced margin
|
|
4998
|
-
cursor: 'pointer',
|
|
4999
|
-
fontSize: '11px', // Smaller font
|
|
5000
|
-
fontWeight:
|
|
5001
|
-
'bold',
|
|
5002
|
-
boxShadow:
|
|
5003
|
-
'0 1px 2px rgba(0,0,0,0.1)',
|
|
5004
|
-
transition:
|
|
5005
|
-
'all 0.15s ease',
|
|
5006
|
-
}}
|
|
5007
|
-
onMouseEnter={(e) => {
|
|
5008
|
-
e.target.style.transform =
|
|
5009
|
-
'scale(1.05)';
|
|
5010
|
-
e.target.style.background =
|
|
5011
|
-
'var(--secondary-color, #2563eb)';
|
|
5012
|
-
}}
|
|
5013
|
-
onMouseLeave={(e) => {
|
|
5014
|
-
e.target.style.transform =
|
|
5015
|
-
'scale(1)';
|
|
5016
|
-
e.target.style.background =
|
|
5017
|
-
'var(--primary-color, #3b82f6)';
|
|
3212
|
+
onTouchStart={(e) => {
|
|
3213
|
+
e.stopPropagation();
|
|
3214
|
+
handleTouchStart(
|
|
3215
|
+
e,
|
|
3216
|
+
'group-expand',
|
|
3217
|
+
collapsed
|
|
3218
|
+
? 'Expand group'
|
|
3219
|
+
: 'Collapse group'
|
|
3220
|
+
);
|
|
5018
3221
|
}}
|
|
3222
|
+
onTouchEnd={
|
|
3223
|
+
handleTouchEnd
|
|
3224
|
+
}
|
|
3225
|
+
onTouchMove={
|
|
3226
|
+
handleTouchMove
|
|
3227
|
+
}
|
|
3228
|
+
data-tooltip-id="system-tooltip"
|
|
3229
|
+
data-tooltip-content={
|
|
3230
|
+
collapsed
|
|
3231
|
+
? 'Expand group'
|
|
3232
|
+
: 'Collapse group'
|
|
3233
|
+
}
|
|
5019
3234
|
>
|
|
5020
3235
|
{collapsed
|
|
5021
3236
|
? '+'
|
|
5022
3237
|
: '−'}
|
|
5023
3238
|
</button>
|
|
3239
|
+
{/* Dynamic group action buttons */}
|
|
3240
|
+
{ajaxSetting?.groupBySetting?.icons?.map(
|
|
3241
|
+
(
|
|
3242
|
+
iconConfig,
|
|
3243
|
+
index
|
|
3244
|
+
) => {
|
|
3245
|
+
// Get the icon component based on the id
|
|
3246
|
+
const getIconComponent =
|
|
3247
|
+
(iconId) => {
|
|
3248
|
+
switch (
|
|
3249
|
+
iconId
|
|
3250
|
+
) {
|
|
3251
|
+
case 'clock':
|
|
3252
|
+
return Clock;
|
|
3253
|
+
case 'alarm':
|
|
3254
|
+
return Alarm;
|
|
3255
|
+
case 'check':
|
|
3256
|
+
return Check;
|
|
3257
|
+
case 'edit':
|
|
3258
|
+
return Edit;
|
|
3259
|
+
case 'envelope':
|
|
3260
|
+
return Envelope;
|
|
3261
|
+
case 'file':
|
|
3262
|
+
return File;
|
|
3263
|
+
case 'image':
|
|
3264
|
+
return Image;
|
|
3265
|
+
case 'copy':
|
|
3266
|
+
return Copy;
|
|
3267
|
+
case 'cloudUpload':
|
|
3268
|
+
return CloudDownload;
|
|
3269
|
+
case 'cloudDownload':
|
|
3270
|
+
return CloudDownload;
|
|
3271
|
+
default:
|
|
3272
|
+
return Clock; // Default fallback
|
|
3273
|
+
}
|
|
3274
|
+
};
|
|
3275
|
+
|
|
3276
|
+
// Check if this icon should be shown based on show conditions
|
|
3277
|
+
if (
|
|
3278
|
+
!shouldShowGroupAction(
|
|
3279
|
+
iconConfig,
|
|
3280
|
+
groupValue
|
|
3281
|
+
)
|
|
3282
|
+
) {
|
|
3283
|
+
return null;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
const IconComponent =
|
|
3287
|
+
getIconComponent(
|
|
3288
|
+
iconConfig.id
|
|
3289
|
+
);
|
|
3290
|
+
|
|
3291
|
+
// Generate different color classes for multiple buttons
|
|
3292
|
+
const colorClasses =
|
|
3293
|
+
[
|
|
3294
|
+
'success',
|
|
3295
|
+
'warning',
|
|
3296
|
+
'info',
|
|
3297
|
+
'purple',
|
|
3298
|
+
'pink',
|
|
3299
|
+
];
|
|
3300
|
+
const colorClass =
|
|
3301
|
+
colorClasses[
|
|
3302
|
+
index %
|
|
3303
|
+
colorClasses.length
|
|
3304
|
+
];
|
|
3305
|
+
|
|
3306
|
+
return (
|
|
3307
|
+
<button
|
|
3308
|
+
key={`group-action-${index}`}
|
|
3309
|
+
className={`group-action-btn group-${iconConfig.id}-btn ${styles.groupActionBtn} ${styles[colorClass]}`}
|
|
3310
|
+
onClick={(
|
|
3311
|
+
e
|
|
3312
|
+
) => {
|
|
3313
|
+
e.stopPropagation();
|
|
3314
|
+
handleGroupAction(
|
|
3315
|
+
groupValue,
|
|
3316
|
+
iconConfig
|
|
3317
|
+
);
|
|
3318
|
+
}}
|
|
3319
|
+
onTouchStart={(
|
|
3320
|
+
e
|
|
3321
|
+
) => {
|
|
3322
|
+
e.stopPropagation();
|
|
3323
|
+
handleTouchStart(
|
|
3324
|
+
e,
|
|
3325
|
+
`group-${iconConfig.id}-${index}`,
|
|
3326
|
+
iconConfig.label
|
|
3327
|
+
);
|
|
3328
|
+
}}
|
|
3329
|
+
onTouchEnd={
|
|
3330
|
+
handleTouchEnd
|
|
3331
|
+
}
|
|
3332
|
+
onTouchMove={
|
|
3333
|
+
handleTouchMove
|
|
3334
|
+
}
|
|
3335
|
+
data-tooltip-id="system-tooltip"
|
|
3336
|
+
data-tooltip-content={
|
|
3337
|
+
iconConfig.label
|
|
3338
|
+
}
|
|
3339
|
+
>
|
|
3340
|
+
<IconComponent
|
|
3341
|
+
strokeWidth={
|
|
3342
|
+
2
|
|
3343
|
+
}
|
|
3344
|
+
size={
|
|
3345
|
+
14
|
|
3346
|
+
}
|
|
3347
|
+
/>
|
|
3348
|
+
</button>
|
|
3349
|
+
);
|
|
3350
|
+
}
|
|
3351
|
+
) || null}
|
|
5024
3352
|
<div
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
'center', // Changed to center for compact layout
|
|
5029
|
-
gap: '8px', // Add gap between elements
|
|
5030
|
-
}}
|
|
3353
|
+
className={
|
|
3354
|
+
styles.groupInfo
|
|
3355
|
+
}
|
|
5031
3356
|
>
|
|
5032
3357
|
<strong
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
color: 'var(--secondary-color, #1f2937)',
|
|
5037
|
-
fontWeight:
|
|
5038
|
-
'600',
|
|
5039
|
-
textTransform:
|
|
5040
|
-
'uppercase',
|
|
5041
|
-
letterSpacing:
|
|
5042
|
-
'0.025em',
|
|
5043
|
-
}}
|
|
3358
|
+
className={
|
|
3359
|
+
styles.groupValue
|
|
3360
|
+
}
|
|
5044
3361
|
>
|
|
5045
3362
|
{groupValue}
|
|
5046
3363
|
</strong>
|
|
5047
|
-
{groupCount > 0 && (
|
|
5048
|
-
<span
|
|
5049
|
-
style={{
|
|
5050
|
-
color: '#6b7280',
|
|
5051
|
-
fontSize:
|
|
5052
|
-
'0.75rem', // Smaller for compact design
|
|
5053
|
-
fontWeight:
|
|
5054
|
-
'500',
|
|
5055
|
-
}}
|
|
5056
|
-
>
|
|
5057
|
-
({groupCount}{' '}
|
|
5058
|
-
{groupCount ===
|
|
5059
|
-
1
|
|
5060
|
-
? 'item'
|
|
5061
|
-
: 'items'}
|
|
5062
|
-
)
|
|
5063
|
-
</span>
|
|
5064
|
-
)}
|
|
5065
3364
|
{/* Click indicator */}
|
|
5066
3365
|
<span
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
'0.7rem',
|
|
5071
|
-
fontStyle:
|
|
5072
|
-
'italic',
|
|
5073
|
-
opacity: 0.8,
|
|
5074
|
-
marginLeft:
|
|
5075
|
-
'auto',
|
|
5076
|
-
userSelect:
|
|
5077
|
-
'none',
|
|
5078
|
-
}}
|
|
3366
|
+
className={
|
|
3367
|
+
styles.groupClickIndicator
|
|
3368
|
+
}
|
|
5079
3369
|
>
|
|
5080
3370
|
Click to
|
|
5081
3371
|
select/unselect
|
|
@@ -5084,93 +3374,11 @@ const DataGrid = forwardRef(
|
|
|
5084
3374
|
</div>
|
|
5085
3375
|
</div>
|
|
5086
3376
|
|
|
5087
|
-
{/* Right side - Group selection */}
|
|
5088
|
-
{hasCheckboxColumn && (
|
|
5089
|
-
<div
|
|
5090
|
-
style={{
|
|
5091
|
-
display: 'flex',
|
|
5092
|
-
alignItems:
|
|
5093
|
-
'center',
|
|
5094
|
-
gap: '4px', // Reduced gap for compact design
|
|
5095
|
-
fontSize:
|
|
5096
|
-
'0.75rem', // Smaller font for compact design
|
|
5097
|
-
background:
|
|
5098
|
-
'rgba(255, 255, 255, 0.9)',
|
|
5099
|
-
padding:
|
|
5100
|
-
'3px 8px', // Reduced padding for compact design
|
|
5101
|
-
borderRadius:
|
|
5102
|
-
'12px', // Smaller border radius
|
|
5103
|
-
border: '1px solid rgba(59, 130, 246, 0.3)',
|
|
5104
|
-
zIndex: 2,
|
|
5105
|
-
position:
|
|
5106
|
-
'relative',
|
|
5107
|
-
boxShadow:
|
|
5108
|
-
'0 1px 2px rgba(0,0,0,0.05)',
|
|
5109
|
-
}}
|
|
5110
|
-
>
|
|
5111
|
-
<input
|
|
5112
|
-
type="checkbox"
|
|
5113
|
-
onChange={(e) => {
|
|
5114
|
-
e.stopPropagation();
|
|
5115
|
-
handleGroupSelectionChange(
|
|
5116
|
-
groupValue,
|
|
5117
|
-
e.target
|
|
5118
|
-
.checked
|
|
5119
|
-
);
|
|
5120
|
-
}}
|
|
5121
|
-
checked={isGroupSelected(
|
|
5122
|
-
groupValue
|
|
5123
|
-
)}
|
|
5124
|
-
style={{
|
|
5125
|
-
width: '14px', // Smaller checkbox for compact design
|
|
5126
|
-
height: '14px',
|
|
5127
|
-
cursor: 'pointer',
|
|
5128
|
-
accentColor:
|
|
5129
|
-
'var(--primary-color, #3b82f6)',
|
|
5130
|
-
}}
|
|
5131
|
-
/>
|
|
5132
|
-
<span
|
|
5133
|
-
style={{
|
|
5134
|
-
cursor: 'pointer',
|
|
5135
|
-
userSelect:
|
|
5136
|
-
'none',
|
|
5137
|
-
color: 'var(--secondary-color, #1f2937)',
|
|
5138
|
-
fontWeight:
|
|
5139
|
-
'500',
|
|
5140
|
-
fontSize:
|
|
5141
|
-
'0.7rem', // Smaller font for compact design
|
|
5142
|
-
}}
|
|
5143
|
-
onClick={(e) => {
|
|
5144
|
-
e.stopPropagation();
|
|
5145
|
-
const checkbox =
|
|
5146
|
-
e.target
|
|
5147
|
-
.previousElementSibling;
|
|
5148
|
-
checkbox.checked =
|
|
5149
|
-
!checkbox.checked;
|
|
5150
|
-
handleGroupSelectionChange(
|
|
5151
|
-
groupValue,
|
|
5152
|
-
checkbox.checked
|
|
5153
|
-
);
|
|
5154
|
-
}}
|
|
5155
|
-
>
|
|
5156
|
-
Select All
|
|
5157
|
-
</span>
|
|
5158
|
-
</div>
|
|
5159
|
-
)}
|
|
5160
|
-
|
|
5161
3377
|
{/* Background pattern */}
|
|
5162
3378
|
<div
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
left: 0,
|
|
5167
|
-
right: 0,
|
|
5168
|
-
bottom: 0,
|
|
5169
|
-
background:
|
|
5170
|
-
'repeating-linear-gradient(45deg, transparent, transparent 2px, rgba(59, 130, 246, 0.03) 2px, rgba(59, 130, 246, 0.03) 4px)',
|
|
5171
|
-
zIndex: 1,
|
|
5172
|
-
pointerEvents: 'none',
|
|
5173
|
-
}}
|
|
3379
|
+
className={
|
|
3380
|
+
styles.groupHeaderPattern
|
|
3381
|
+
}
|
|
5174
3382
|
/>
|
|
5175
3383
|
</div>
|
|
5176
3384
|
);
|
|
@@ -5319,13 +3527,19 @@ const DataGrid = forwardRef(
|
|
|
5319
3527
|
</div>
|
|
5320
3528
|
|
|
5321
3529
|
{/* Relocated auto refresh control when it's the only control */}
|
|
5322
|
-
{
|
|
3530
|
+
{shouldRelocateAutoRefresh() && (
|
|
5323
3531
|
<div className={styles.dataGridBottomContainer}>
|
|
5324
|
-
|
|
3532
|
+
<AutoRefreshControls
|
|
3533
|
+
ajaxSetting={ajaxSetting}
|
|
3534
|
+
autoRefreshEnabled={autoRefreshEnabled}
|
|
3535
|
+
setAutoRefreshEnabled={setAutoRefreshEnabled}
|
|
3536
|
+
refreshInterval={refreshInterval}
|
|
3537
|
+
isRelocated={true}
|
|
3538
|
+
/>
|
|
5325
3539
|
</div>
|
|
5326
|
-
)}
|
|
3540
|
+
)}
|
|
5327
3541
|
|
|
5328
|
-
|
|
3542
|
+
<AudioPlayer audioSource={audioSource} />
|
|
5329
3543
|
<Popup
|
|
5330
3544
|
open={modalShow}
|
|
5331
3545
|
onClose={modalClose}
|
|
@@ -5373,231 +3587,38 @@ const DataGrid = forwardRef(
|
|
|
5373
3587
|
</div>
|
|
5374
3588
|
</div>
|
|
5375
3589
|
</Popup>
|
|
5376
|
-
<
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
}
|
|
5385
|
-
|
|
5386
|
-
|
|
3590
|
+
<GalleryModal
|
|
3591
|
+
modalGalleryShow={modalGalleryShow}
|
|
3592
|
+
modalGalleryClose={modalGalleryClose}
|
|
3593
|
+
galleryData={galleryData}
|
|
3594
|
+
setGalleryData={setGalleryData}
|
|
3595
|
+
currentItem={currentItem}
|
|
3596
|
+
form={form}
|
|
3597
|
+
lightboxOpen={lightboxOpen}
|
|
3598
|
+
setLightboxOpen={setLightboxOpen}
|
|
3599
|
+
lightboxIndex={lightboxIndex}
|
|
3600
|
+
setLightboxIndex={setLightboxIndex}
|
|
3601
|
+
/>
|
|
3602
|
+
|
|
3603
|
+
{/* Touch Tooltip Overlay */}
|
|
3604
|
+
{activeTooltip && (
|
|
5387
3605
|
<div
|
|
5388
|
-
className={
|
|
3606
|
+
className={styles.touchTooltip}
|
|
3607
|
+
style={{
|
|
3608
|
+
position: 'fixed',
|
|
3609
|
+
left: `${activeTooltip.x}px`,
|
|
3610
|
+
top: `${activeTooltip.y - 50}px`,
|
|
3611
|
+
transform: 'translateX(-50%)', // Center horizontally on button
|
|
3612
|
+
zIndex: 9999,
|
|
3613
|
+
pointerEvents: 'none',
|
|
3614
|
+
}}
|
|
3615
|
+
onClick={closeTooltip}
|
|
5389
3616
|
>
|
|
5390
|
-
<div className={styles.
|
|
5391
|
-
|
|
5392
|
-
<h1>Gallery</h1>
|
|
5393
|
-
<button
|
|
5394
|
-
className={styles.modal__close}
|
|
5395
|
-
onClick={modalGalleryClose}
|
|
5396
|
-
>
|
|
5397
|
-
<CircleX strokeWidth={1} size={24} />
|
|
5398
|
-
</button>
|
|
5399
|
-
</div>
|
|
5400
|
-
<div
|
|
5401
|
-
className={`${styles.modal__content} ${styles['modal__content--gallery']}`}
|
|
5402
|
-
>
|
|
5403
|
-
{currentItem?.settings?.upload && (
|
|
5404
|
-
<div className={styles.uploadSection}>
|
|
5405
|
-
<h3>
|
|
5406
|
-
<Image
|
|
5407
|
-
strokeWidth={1.5}
|
|
5408
|
-
size={18}
|
|
5409
|
-
/>
|
|
5410
|
-
Upload New Images
|
|
5411
|
-
</h3>
|
|
5412
|
-
<div
|
|
5413
|
-
className={styles.dropzoneContainer}
|
|
5414
|
-
>
|
|
5415
|
-
{isUploading && (
|
|
5416
|
-
<div
|
|
5417
|
-
className={
|
|
5418
|
-
styles.progressBar
|
|
5419
|
-
}
|
|
5420
|
-
>
|
|
5421
|
-
<div
|
|
5422
|
-
className={
|
|
5423
|
-
styles.progressBarInner
|
|
5424
|
-
}
|
|
5425
|
-
style={{
|
|
5426
|
-
width: `${uploadProgress}%`,
|
|
5427
|
-
}}
|
|
5428
|
-
></div>
|
|
5429
|
-
</div>
|
|
5430
|
-
)}
|
|
5431
|
-
<div
|
|
5432
|
-
className={styles.dropzone}
|
|
5433
|
-
onClick={() =>
|
|
5434
|
-
document
|
|
5435
|
-
.getElementById(
|
|
5436
|
-
'gallery-file-upload'
|
|
5437
|
-
)
|
|
5438
|
-
.click()
|
|
5439
|
-
}
|
|
5440
|
-
onDragOver={(e) => {
|
|
5441
|
-
e.preventDefault();
|
|
5442
|
-
e.stopPropagation();
|
|
5443
|
-
}}
|
|
5444
|
-
onDragEnter={(e) => {
|
|
5445
|
-
e.preventDefault();
|
|
5446
|
-
e.stopPropagation();
|
|
5447
|
-
}}
|
|
5448
|
-
onDragLeave={(e) => {
|
|
5449
|
-
e.preventDefault();
|
|
5450
|
-
e.stopPropagation();
|
|
5451
|
-
}}
|
|
5452
|
-
onDrop={(e) => {
|
|
5453
|
-
e.preventDefault();
|
|
5454
|
-
e.stopPropagation();
|
|
5455
|
-
const files =
|
|
5456
|
-
e.dataTransfer.files;
|
|
5457
|
-
if (files.length > 0) {
|
|
5458
|
-
handleFileUpload(files);
|
|
5459
|
-
}
|
|
5460
|
-
}}
|
|
5461
|
-
>
|
|
5462
|
-
<CloudUpload
|
|
5463
|
-
strokeWidth={1.5}
|
|
5464
|
-
size={24}
|
|
5465
|
-
/>
|
|
5466
|
-
<p>
|
|
5467
|
-
Drag and drop files here or
|
|
5468
|
-
click to select files
|
|
5469
|
-
</p>
|
|
5470
|
-
<input
|
|
5471
|
-
id="gallery-file-upload"
|
|
5472
|
-
type="file"
|
|
5473
|
-
multiple
|
|
5474
|
-
style={{ display: 'none' }}
|
|
5475
|
-
onChange={(e) => {
|
|
5476
|
-
if (
|
|
5477
|
-
e.target.files
|
|
5478
|
-
.length > 0
|
|
5479
|
-
) {
|
|
5480
|
-
handleFileUpload(
|
|
5481
|
-
e.target.files
|
|
5482
|
-
);
|
|
5483
|
-
e.target.value =
|
|
5484
|
-
null; // Reset input
|
|
5485
|
-
}
|
|
5486
|
-
}}
|
|
5487
|
-
accept="image/*"
|
|
5488
|
-
/>
|
|
5489
|
-
</div>
|
|
5490
|
-
</div>
|
|
5491
|
-
</div>
|
|
5492
|
-
)}
|
|
5493
|
-
<div className={styles.formcontainer}>
|
|
5494
|
-
{galleryData.length > 0 ? (
|
|
5495
|
-
<div
|
|
5496
|
-
className={styles.galleryContainer}
|
|
5497
|
-
>
|
|
5498
|
-
<div
|
|
5499
|
-
className={
|
|
5500
|
-
styles.galleryInstructions
|
|
5501
|
-
}
|
|
5502
|
-
>
|
|
5503
|
-
<Image
|
|
5504
|
-
strokeWidth={1.5}
|
|
5505
|
-
size={16}
|
|
5506
|
-
/>
|
|
5507
|
-
<span>
|
|
5508
|
-
Tap any image to enlarge
|
|
5509
|
-
</span>
|
|
5510
|
-
</div>
|
|
5511
|
-
<div
|
|
5512
|
-
className={
|
|
5513
|
-
styles.customGalleryGrid
|
|
5514
|
-
}
|
|
5515
|
-
>
|
|
5516
|
-
{galleryData.map(
|
|
5517
|
-
(img, index) => (
|
|
5518
|
-
<div
|
|
5519
|
-
key={`gallery-item-${index}`}
|
|
5520
|
-
className={
|
|
5521
|
-
styles.galleryItem
|
|
5522
|
-
}
|
|
5523
|
-
onClick={() => {
|
|
5524
|
-
setLightboxIndex(
|
|
5525
|
-
index
|
|
5526
|
-
);
|
|
5527
|
-
setLightboxOpen(
|
|
5528
|
-
true
|
|
5529
|
-
);
|
|
5530
|
-
}}
|
|
5531
|
-
>
|
|
5532
|
-
<img
|
|
5533
|
-
src={img.src}
|
|
5534
|
-
alt={
|
|
5535
|
-
img.caption ||
|
|
5536
|
-
`Image ${
|
|
5537
|
-
index +
|
|
5538
|
-
1
|
|
5539
|
-
}`
|
|
5540
|
-
}
|
|
5541
|
-
/>
|
|
5542
|
-
{img.caption && (
|
|
5543
|
-
<div
|
|
5544
|
-
className={
|
|
5545
|
-
styles.imageCaption
|
|
5546
|
-
}
|
|
5547
|
-
>
|
|
5548
|
-
{
|
|
5549
|
-
img.caption
|
|
5550
|
-
}
|
|
5551
|
-
</div>
|
|
5552
|
-
)}
|
|
5553
|
-
<div
|
|
5554
|
-
className={
|
|
5555
|
-
styles.galleryImageOverlay
|
|
5556
|
-
}
|
|
5557
|
-
>
|
|
5558
|
-
<span>
|
|
5559
|
-
<Image
|
|
5560
|
-
strokeWidth={
|
|
5561
|
-
1.5
|
|
5562
|
-
}
|
|
5563
|
-
size={
|
|
5564
|
-
16
|
|
5565
|
-
}
|
|
5566
|
-
style={{
|
|
5567
|
-
marginRight:
|
|
5568
|
-
'6px',
|
|
5569
|
-
}}
|
|
5570
|
-
/>
|
|
5571
|
-
Enlarge
|
|
5572
|
-
</span>
|
|
5573
|
-
</div>
|
|
5574
|
-
</div>
|
|
5575
|
-
)
|
|
5576
|
-
)}
|
|
5577
|
-
</div>
|
|
5578
|
-
</div>
|
|
5579
|
-
) : (
|
|
5580
|
-
<div className={styles.noDataMessage}>
|
|
5581
|
-
<Image
|
|
5582
|
-
strokeWidth={1.5}
|
|
5583
|
-
size={20}
|
|
5584
|
-
/>
|
|
5585
|
-
No images available in the gallery
|
|
5586
|
-
</div>
|
|
5587
|
-
)}
|
|
5588
|
-
</div>
|
|
5589
|
-
</div>
|
|
3617
|
+
<div className={styles.touchTooltipContent}>
|
|
3618
|
+
{activeTooltip.label}
|
|
5590
3619
|
</div>
|
|
5591
3620
|
</div>
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
{/* Lightbox for full-size image viewing */}
|
|
5595
|
-
<Lightbox
|
|
5596
|
-
open={lightboxOpen}
|
|
5597
|
-
close={() => setLightboxOpen(false)}
|
|
5598
|
-
index={lightboxIndex}
|
|
5599
|
-
slides={galleryData.map((img) => ({ src: img.src }))}
|
|
5600
|
-
/>
|
|
3621
|
+
)}
|
|
5601
3622
|
</>
|
|
5602
3623
|
);
|
|
5603
3624
|
}
|