@visns-studio/visns-components 5.10.1 → 5.10.3
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 +1 -1
- package/src/components/crm/DataGrid.jsx +583 -2599
- package/src/components/crm/Field.jsx +15 -3
- package/src/components/crm/cells/CellWithTooltip.jsx +123 -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/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,188 @@ 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((row) => row[groupField] === groupValue);
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
const shouldShowGroupAction = (iconConfig, groupValue) => {
|
|
1257
|
+
// If no show conditions, always show the icon
|
|
1258
|
+
if (!iconConfig.show || !Array.isArray(iconConfig.show)) {
|
|
1259
|
+
return true;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// Get all rows for this group
|
|
1263
|
+
const groupRows = getGroupRows(groupValue);
|
|
1264
|
+
|
|
1265
|
+
// Check if all show conditions are met
|
|
1266
|
+
return iconConfig.show.every(condition => {
|
|
1267
|
+
const { id, value } = condition;
|
|
1268
|
+
|
|
1269
|
+
// Check if any row in the group matches the condition
|
|
1270
|
+
return groupRows.some(row => {
|
|
1271
|
+
const rowValue = row[id];
|
|
1272
|
+
|
|
1273
|
+
// Handle null value condition
|
|
1274
|
+
if (value === null) {
|
|
1275
|
+
return rowValue === null || rowValue === undefined || rowValue === '';
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Handle other value conditions
|
|
1279
|
+
return rowValue === value;
|
|
1280
|
+
});
|
|
1281
|
+
});
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
const executeGroupAction = async (groupValue, iconConfig) => {
|
|
1285
|
+
// Check if fetch configuration is available
|
|
1286
|
+
if (iconConfig.fetch && iconConfig.fetch.url) {
|
|
1287
|
+
try {
|
|
1288
|
+
const payload = {
|
|
1289
|
+
...iconConfig.fetch.data,
|
|
1290
|
+
groupLabel: groupValue
|
|
1291
|
+
};
|
|
1292
|
+
|
|
1293
|
+
const result = await CustomFetch(
|
|
1294
|
+
iconConfig.fetch.url,
|
|
1295
|
+
iconConfig.fetch.method || 'POST',
|
|
1296
|
+
payload
|
|
1297
|
+
);
|
|
1298
|
+
|
|
1299
|
+
if (result.error) {
|
|
1300
|
+
throw new Error(result.error);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
toast.success(
|
|
1304
|
+
`${iconConfig.label} - Group: ${groupValue} completed successfully`
|
|
1305
|
+
);
|
|
1306
|
+
|
|
1307
|
+
// Reload grid data if needed
|
|
1308
|
+
if (gridRef.current && gridRef.current.paginationProps) {
|
|
1309
|
+
gridRef.current.paginationProps.reload();
|
|
1310
|
+
}
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
console.error('Group action fetch error:', error);
|
|
1313
|
+
toast.error(
|
|
1314
|
+
`${iconConfig.label} - Group: ${groupValue} failed: ${error.message}`
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
} else {
|
|
1318
|
+
// Fallback to existing switch logic if no fetch config
|
|
1319
|
+
toast.info(`${iconConfig.label} - Group: ${groupValue}`);
|
|
1320
|
+
|
|
1321
|
+
switch (iconConfig.id) {
|
|
1322
|
+
case 'clock':
|
|
1323
|
+
console.log(
|
|
1324
|
+
`Starting timer action for group: ${groupValue}`
|
|
1325
|
+
);
|
|
1326
|
+
break;
|
|
1327
|
+
case 'alarm':
|
|
1328
|
+
console.log(
|
|
1329
|
+
`Setting alarm action for group: ${groupValue}`
|
|
1330
|
+
);
|
|
1331
|
+
break;
|
|
1332
|
+
case 'check':
|
|
1333
|
+
console.log(
|
|
1334
|
+
`Mark complete action for group: ${groupValue}`
|
|
1335
|
+
);
|
|
1336
|
+
break;
|
|
1337
|
+
case 'edit':
|
|
1338
|
+
console.log(
|
|
1339
|
+
`Bulk edit action for group: ${groupValue}`
|
|
1340
|
+
);
|
|
1341
|
+
break;
|
|
1342
|
+
case 'envelope':
|
|
1343
|
+
console.log(
|
|
1344
|
+
`Send notification action for group: ${groupValue}`
|
|
1345
|
+
);
|
|
1346
|
+
break;
|
|
1347
|
+
default:
|
|
1348
|
+
console.log(
|
|
1349
|
+
`Custom action ${iconConfig.id} for group: ${groupValue}`
|
|
1350
|
+
);
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
|
|
1356
|
+
// Touch handlers for tooltips
|
|
1357
|
+
const handleTouchStart = (e, buttonId, label) => {
|
|
1358
|
+
// Clear any existing timer
|
|
1359
|
+
if (touchTimer) {
|
|
1360
|
+
clearTimeout(touchTimer);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// Capture the button element and rect immediately
|
|
1364
|
+
const buttonElement = e.currentTarget;
|
|
1365
|
+
const rect = buttonElement.getBoundingClientRect();
|
|
1366
|
+
const x = rect.left + rect.width / 2; // Center of button
|
|
1367
|
+
const y = rect.top; // Top of button
|
|
1368
|
+
|
|
1369
|
+
// Set a timer for long press (500ms)
|
|
1370
|
+
const timer = setTimeout(() => {
|
|
1371
|
+
setActiveTooltip({ id: buttonId, label, x, y });
|
|
1372
|
+
|
|
1373
|
+
// Auto-close tooltip after 2 seconds
|
|
1374
|
+
setTimeout(() => {
|
|
1375
|
+
setActiveTooltip(null);
|
|
1376
|
+
}, 2000);
|
|
1377
|
+
}, 500);
|
|
1378
|
+
|
|
1379
|
+
setTouchTimer(timer);
|
|
1380
|
+
};
|
|
1381
|
+
|
|
1382
|
+
const handleTouchEnd = () => {
|
|
1383
|
+
// Clear the timer if touch ends before long press
|
|
1384
|
+
if (touchTimer) {
|
|
1385
|
+
clearTimeout(touchTimer);
|
|
1386
|
+
setTouchTimer(null);
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
|
|
1390
|
+
const handleTouchMove = () => {
|
|
1391
|
+
// Cancel tooltip if user moves finger (not a long press)
|
|
1392
|
+
if (touchTimer) {
|
|
1393
|
+
clearTimeout(touchTimer);
|
|
1394
|
+
setTouchTimer(null);
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
|
|
1398
|
+
const closeTooltip = () => {
|
|
1399
|
+
setActiveTooltip(null);
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1543
1402
|
const handleSort = (value) => {
|
|
1544
1403
|
if (setConfig) {
|
|
1545
1404
|
setConfig((prevState) => {
|
|
@@ -1767,212 +1626,6 @@ const DataGrid = forwardRef(
|
|
|
1767
1626
|
[selected, setRowsSelected, onRowClick]
|
|
1768
1627
|
);
|
|
1769
1628
|
|
|
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
1629
|
// Helper function to determine if auto refresh should be relocated
|
|
1977
1630
|
const shouldRelocateAutoRefresh = () => {
|
|
1978
1631
|
const isSearchDisabled = form.searchDisable === true;
|
|
@@ -1980,36 +1633,9 @@ const DataGrid = forwardRef(
|
|
|
1980
1633
|
const hasAutoRefresh =
|
|
1981
1634
|
ajaxSetting && ajaxSetting.autoRefresh !== undefined;
|
|
1982
1635
|
|
|
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
|
-
);
|
|
1636
|
+
// Hide auto refresh when it would be the sole control (instead of relocating it)
|
|
1637
|
+
// This prevents it from appearing under the datagrid
|
|
1638
|
+
return false;
|
|
2013
1639
|
};
|
|
2014
1640
|
|
|
2015
1641
|
const renderRowContextMenu = useCallback(
|
|
@@ -2714,8 +2340,6 @@ const DataGrid = forwardRef(
|
|
|
2714
2340
|
let date;
|
|
2715
2341
|
let filterEditor = null;
|
|
2716
2342
|
let filterEditorProps = null;
|
|
2717
|
-
let fileUrl = null;
|
|
2718
|
-
let icons = [];
|
|
2719
2343
|
let relationName;
|
|
2720
2344
|
let selectedDataSource = null;
|
|
2721
2345
|
let stage;
|
|
@@ -2840,1572 +2464,176 @@ const DataGrid = forwardRef(
|
|
|
2840
2464
|
|
|
2841
2465
|
switch (column.type) {
|
|
2842
2466
|
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
|
-
};
|
|
2467
|
+
return renderAddressColumn({
|
|
2468
|
+
column,
|
|
2469
|
+
commonProps,
|
|
2470
|
+
filterEditor,
|
|
2471
|
+
filterEditorProps,
|
|
2472
|
+
});
|
|
2878
2473
|
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
|
-
};
|
|
2474
|
+
return renderAgeColumn({
|
|
2475
|
+
column,
|
|
2476
|
+
commonProps,
|
|
2477
|
+
filterEditor,
|
|
2478
|
+
});
|
|
2911
2479
|
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
|
-
};
|
|
2480
|
+
return renderArrayCountColumn({
|
|
2481
|
+
column,
|
|
2482
|
+
commonProps,
|
|
2483
|
+
});
|
|
2924
2484
|
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
|
-
};
|
|
2485
|
+
return renderBooleanColumn({
|
|
2486
|
+
column,
|
|
2487
|
+
commonProps,
|
|
2488
|
+
filterEditor,
|
|
2489
|
+
filterEditorProps,
|
|
2490
|
+
});
|
|
2945
2491
|
case 'coding':
|
|
2946
|
-
return {
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
if (data) {
|
|
2952
|
-
return handleCoding(column, data);
|
|
2953
|
-
} else {
|
|
2954
|
-
return null;
|
|
2955
|
-
}
|
|
2956
|
-
},
|
|
2957
|
-
};
|
|
2492
|
+
return renderCodingColumn({
|
|
2493
|
+
column,
|
|
2494
|
+
commonProps,
|
|
2495
|
+
handleCoding,
|
|
2496
|
+
});
|
|
2958
2497
|
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
|
-
};
|
|
2498
|
+
return renderColourColumn({
|
|
2499
|
+
column,
|
|
2500
|
+
commonProps,
|
|
2501
|
+
});
|
|
2997
2502
|
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
|
-
};
|
|
2503
|
+
return renderCreatedByColumn({
|
|
2504
|
+
column,
|
|
2505
|
+
commonProps,
|
|
2506
|
+
});
|
|
3016
2507
|
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
|
-
};
|
|
2508
|
+
return renderCurrencyColumn({
|
|
2509
|
+
column,
|
|
2510
|
+
commonProps,
|
|
2511
|
+
});
|
|
3039
2512
|
case 'date':
|
|
3040
|
-
return {
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
filterEditor
|
|
3044
|
-
filterEditorProps
|
|
3045
|
-
|
|
3046
|
-
minWidth: 120, // Set minimum width for date columns
|
|
3047
|
-
render: ({ data }) => {
|
|
3048
|
-
if (
|
|
3049
|
-
data &&
|
|
3050
|
-
data[column.id] &&
|
|
3051
|
-
moment(data[column.id]).isValid() &&
|
|
3052
|
-
moment(data[column.id]).format(
|
|
3053
|
-
'YYYY-MM-DD'
|
|
3054
|
-
) !== '1970-01-01'
|
|
3055
|
-
) {
|
|
3056
|
-
const value = moment(
|
|
3057
|
-
data[column.id]
|
|
3058
|
-
).format(
|
|
3059
|
-
column.format || 'DD-MM-YYYY'
|
|
3060
|
-
);
|
|
3061
|
-
columnStyle = {};
|
|
3062
|
-
|
|
3063
|
-
if (
|
|
3064
|
-
column.active &&
|
|
3065
|
-
column.active.id &&
|
|
3066
|
-
column.active.value ===
|
|
3067
|
-
'expired'
|
|
3068
|
-
) {
|
|
3069
|
-
if (
|
|
3070
|
-
data[column.active.id] &&
|
|
3071
|
-
moment(
|
|
3072
|
-
data[column.active.id]
|
|
3073
|
-
).isBefore(moment())
|
|
3074
|
-
) {
|
|
3075
|
-
columnStyle = {
|
|
3076
|
-
color: column.active
|
|
3077
|
-
.colour,
|
|
3078
|
-
};
|
|
3079
|
-
}
|
|
3080
|
-
}
|
|
3081
|
-
|
|
3082
|
-
return (
|
|
3083
|
-
<CellWithTooltip
|
|
3084
|
-
value={data[column.id]}
|
|
3085
|
-
columnType="date"
|
|
3086
|
-
>
|
|
3087
|
-
<span style={columnStyle}>
|
|
3088
|
-
{value}
|
|
3089
|
-
</span>
|
|
3090
|
-
</CellWithTooltip>
|
|
3091
|
-
);
|
|
3092
|
-
} else {
|
|
3093
|
-
return null;
|
|
3094
|
-
}
|
|
3095
|
-
},
|
|
3096
|
-
};
|
|
2513
|
+
return renderDateColumn({
|
|
2514
|
+
column,
|
|
2515
|
+
commonProps,
|
|
2516
|
+
filterEditor,
|
|
2517
|
+
filterEditorProps,
|
|
2518
|
+
});
|
|
3097
2519
|
case 'daterange':
|
|
3098
|
-
return {
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
filterEditor
|
|
3102
|
-
filterEditorProps
|
|
3103
|
-
|
|
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
|
-
}
|
|
3122
|
-
|
|
3123
|
-
if (
|
|
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
|
-
}
|
|
3139
|
-
|
|
3140
|
-
return <span>{value}</span>;
|
|
3141
|
-
},
|
|
3142
|
-
};
|
|
2520
|
+
return renderDateRangeColumn({
|
|
2521
|
+
column,
|
|
2522
|
+
commonProps,
|
|
2523
|
+
filterEditor,
|
|
2524
|
+
filterEditorProps,
|
|
2525
|
+
});
|
|
3143
2526
|
case 'datetime':
|
|
3144
|
-
return {
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
filterEditor
|
|
3148
|
-
filterEditorProps
|
|
3149
|
-
|
|
3150
|
-
minWidth: 160, // Set minimum width for datetime columns
|
|
3151
|
-
render: ({ data }) => {
|
|
3152
|
-
if (
|
|
3153
|
-
data &&
|
|
3154
|
-
data[column.id] &&
|
|
3155
|
-
moment(data[column.id]).isValid() &&
|
|
3156
|
-
moment(data[column.id]).format(
|
|
3157
|
-
'YYYY-MM-DD'
|
|
3158
|
-
) !== '1970-01-01'
|
|
3159
|
-
) {
|
|
3160
|
-
data = moment(
|
|
3161
|
-
data[column.id]
|
|
3162
|
-
).format(
|
|
3163
|
-
column.format
|
|
3164
|
-
? column.format
|
|
3165
|
-
: 'DD-MM-YYYY hh:mm A'
|
|
3166
|
-
);
|
|
3167
|
-
|
|
3168
|
-
return <span>{data}</span>;
|
|
3169
|
-
} else {
|
|
3170
|
-
return null;
|
|
3171
|
-
}
|
|
3172
|
-
},
|
|
3173
|
-
};
|
|
2527
|
+
return renderDateTimeColumn({
|
|
2528
|
+
column,
|
|
2529
|
+
commonProps,
|
|
2530
|
+
filterEditor,
|
|
2531
|
+
filterEditorProps,
|
|
2532
|
+
});
|
|
3174
2533
|
case 'dropdown':
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
filterEditorProps: filterEditorProps,
|
|
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
|
-
};
|
|
2534
|
+
return renderDropdownColumn({
|
|
2535
|
+
column,
|
|
2536
|
+
commonProps,
|
|
2537
|
+
filterEditor,
|
|
2538
|
+
filterEditorProps,
|
|
2539
|
+
dropdownData,
|
|
2540
|
+
handleDropdownUpdate,
|
|
2541
|
+
});
|
|
3237
2542
|
case 'file':
|
|
3238
|
-
return {
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
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
|
-
};
|
|
2543
|
+
return renderFileColumn({
|
|
2544
|
+
column,
|
|
2545
|
+
commonProps,
|
|
2546
|
+
handleDownload,
|
|
2547
|
+
styles,
|
|
2548
|
+
});
|
|
3316
2549
|
case 'icons':
|
|
3317
|
-
return {
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
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
|
-
};
|
|
2550
|
+
return renderIconsColumn({
|
|
2551
|
+
column,
|
|
2552
|
+
commonProps,
|
|
2553
|
+
});
|
|
3349
2554
|
case 'image':
|
|
3350
|
-
return {
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
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
|
-
};
|
|
2555
|
+
return renderImageColumn({
|
|
2556
|
+
column,
|
|
2557
|
+
commonProps,
|
|
2558
|
+
});
|
|
3373
2559
|
case 'json':
|
|
3374
|
-
return {
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
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
|
-
};
|
|
2560
|
+
return renderJsonColumn({
|
|
2561
|
+
column,
|
|
2562
|
+
commonProps,
|
|
2563
|
+
});
|
|
3401
2564
|
case 'number':
|
|
3402
|
-
return {
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
};
|
|
2565
|
+
return renderNumberColumn({
|
|
2566
|
+
column,
|
|
2567
|
+
commonProps,
|
|
2568
|
+
});
|
|
3407
2569
|
case 'option':
|
|
3408
|
-
return {
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
filterEditor
|
|
3412
|
-
filterEditorProps
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
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
|
-
};
|
|
2570
|
+
return renderOptionColumn({
|
|
2571
|
+
column,
|
|
2572
|
+
commonProps,
|
|
2573
|
+
filterEditor,
|
|
2574
|
+
filterEditorProps,
|
|
2575
|
+
});
|
|
2576
|
+
case 'placeholder':
|
|
2577
|
+
return renderPlaceholderColumn({
|
|
2578
|
+
column,
|
|
2579
|
+
commonProps,
|
|
2580
|
+
});
|
|
2581
|
+
case 'relation':
|
|
2582
|
+
return renderRelationColumn({
|
|
2583
|
+
column,
|
|
2584
|
+
commonProps,
|
|
2585
|
+
filterEditor,
|
|
2586
|
+
filterEditorProps,
|
|
2587
|
+
relationName,
|
|
2588
|
+
});
|
|
3574
2589
|
case 'relationArray':
|
|
3575
|
-
return {
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
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
|
-
};
|
|
2590
|
+
return renderRelationArrayColumn({
|
|
2591
|
+
column,
|
|
2592
|
+
commonProps,
|
|
2593
|
+
filterEditor,
|
|
2594
|
+
filterEditorProps,
|
|
2595
|
+
relationName,
|
|
2596
|
+
});
|
|
3766
2597
|
case 'richtext':
|
|
3767
|
-
return {
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
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
|
-
};
|
|
2598
|
+
return renderRichTextColumn({
|
|
2599
|
+
column,
|
|
2600
|
+
commonProps,
|
|
2601
|
+
});
|
|
3787
2602
|
case 'stage':
|
|
3788
|
-
return {
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
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
|
-
};
|
|
2603
|
+
return renderStageColumn({
|
|
2604
|
+
column,
|
|
2605
|
+
commonProps,
|
|
2606
|
+
});
|
|
3811
2607
|
case 'time':
|
|
3812
|
-
return {
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
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
|
-
};
|
|
2608
|
+
return renderTimeColumn({
|
|
2609
|
+
column,
|
|
2610
|
+
commonProps,
|
|
2611
|
+
});
|
|
3845
2612
|
case 'timer':
|
|
3846
|
-
return {
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
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
|
-
};
|
|
2613
|
+
return renderTimerColumn({
|
|
2614
|
+
column,
|
|
2615
|
+
commonProps,
|
|
2616
|
+
handleTimer,
|
|
2617
|
+
form,
|
|
2618
|
+
columns,
|
|
2619
|
+
styles,
|
|
2620
|
+
});
|
|
4026
2621
|
case 'stageCounter':
|
|
4027
|
-
return {
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
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
|
-
};
|
|
2622
|
+
return renderStageCounterColumn({
|
|
2623
|
+
column,
|
|
2624
|
+
commonProps,
|
|
2625
|
+
navigate,
|
|
2626
|
+
});
|
|
4307
2627
|
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
|
-
};
|
|
2628
|
+
return renderUrlColumn({ column, commonProps });
|
|
4335
2629
|
case 'input_text':
|
|
4336
|
-
return {
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
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
|
-
};
|
|
2630
|
+
return renderInputTextColumn({
|
|
2631
|
+
column,
|
|
2632
|
+
commonProps,
|
|
2633
|
+
localInputValuesRef,
|
|
2634
|
+
setLocalInputValues,
|
|
2635
|
+
handleInputUpdate,
|
|
2636
|
+
});
|
|
4409
2637
|
default:
|
|
4410
2638
|
columnId = Array.isArray(column.id)
|
|
4411
2639
|
? column.id.join('-')
|
|
@@ -4767,16 +2995,36 @@ const DataGrid = forwardRef(
|
|
|
4767
2995
|
<>
|
|
4768
2996
|
{/* Top container with search, action buttons, and auto-refresh */}
|
|
4769
2997
|
{/* Only show top container if there are controls to display */}
|
|
4770
|
-
{(
|
|
2998
|
+
{(!form.searchDisable ||
|
|
2999
|
+
!form.createDisable ||
|
|
4771
3000
|
(!shouldRelocateAutoRefresh() &&
|
|
4772
|
-
|
|
3001
|
+
ajaxSetting &&
|
|
3002
|
+
ajaxSetting.autoRefresh !== undefined)) && (
|
|
4773
3003
|
<div className={styles.dataGridTopContainer}>
|
|
4774
3004
|
<div className={styles.dataGridTopLeftContainer}>
|
|
4775
|
-
|
|
3005
|
+
<DataGridSearch
|
|
3006
|
+
form={form}
|
|
3007
|
+
search={search}
|
|
3008
|
+
onSearchChange={handleChangeSearch}
|
|
3009
|
+
onModalOpen={modalOpen}
|
|
3010
|
+
dataRef={dataRef}
|
|
3011
|
+
filterValue={filterValue}
|
|
3012
|
+
paramValue={paramValue}
|
|
3013
|
+
pageData={pageData}
|
|
3014
|
+
pageSetting={pageSetting}
|
|
3015
|
+
/>
|
|
4776
3016
|
</div>
|
|
4777
3017
|
<div className={styles.dataGridTopRightContainer}>
|
|
4778
|
-
{!shouldRelocateAutoRefresh() &&
|
|
4779
|
-
|
|
3018
|
+
{!shouldRelocateAutoRefresh() && (
|
|
3019
|
+
<AutoRefreshControls
|
|
3020
|
+
ajaxSetting={ajaxSetting}
|
|
3021
|
+
autoRefreshEnabled={autoRefreshEnabled}
|
|
3022
|
+
setAutoRefreshEnabled={
|
|
3023
|
+
setAutoRefreshEnabled
|
|
3024
|
+
}
|
|
3025
|
+
refreshInterval={refreshInterval}
|
|
3026
|
+
/>
|
|
3027
|
+
)}
|
|
4780
3028
|
</div>
|
|
4781
3029
|
</div>
|
|
4782
3030
|
)}
|
|
@@ -4907,64 +3155,19 @@ const DataGrid = forwardRef(
|
|
|
4907
3155
|
|
|
4908
3156
|
return (
|
|
4909
3157
|
<div
|
|
4910
|
-
className=
|
|
3158
|
+
className={`group-header-clean group-header-fixed-height ${styles.groupHeaderContainer}`}
|
|
4911
3159
|
onClick={
|
|
4912
3160
|
handleGroupHeaderClick
|
|
4913
3161
|
}
|
|
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
3162
|
>
|
|
4956
3163
|
{/* Left side - Group info and expand/collapse */}
|
|
4957
3164
|
<div
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
flex: 1,
|
|
4962
|
-
zIndex: 2,
|
|
4963
|
-
position: 'relative',
|
|
4964
|
-
}}
|
|
3165
|
+
className={
|
|
3166
|
+
styles.groupActionContainer
|
|
3167
|
+
}
|
|
4965
3168
|
>
|
|
4966
3169
|
<button
|
|
4967
|
-
className=
|
|
3170
|
+
className={`group-expand-btn ${styles.groupExpandBtn}`}
|
|
4968
3171
|
onClick={(e) => {
|
|
4969
3172
|
e.stopPropagation();
|
|
4970
3173
|
if (
|
|
@@ -4972,110 +3175,160 @@ const DataGrid = forwardRef(
|
|
|
4972
3175
|
'function'
|
|
4973
3176
|
) {
|
|
4974
3177
|
toggleGroup();
|
|
4975
|
-
} else {
|
|
4976
|
-
console.warn(
|
|
4977
|
-
'toggleGroup is not a function:',
|
|
4978
|
-
toggleGroup
|
|
4979
|
-
);
|
|
4980
3178
|
}
|
|
4981
3179
|
}}
|
|
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)';
|
|
3180
|
+
onTouchStart={(e) => {
|
|
3181
|
+
e.stopPropagation();
|
|
3182
|
+
handleTouchStart(
|
|
3183
|
+
e,
|
|
3184
|
+
'group-expand',
|
|
3185
|
+
collapsed
|
|
3186
|
+
? 'Expand group'
|
|
3187
|
+
: 'Collapse group'
|
|
3188
|
+
);
|
|
5018
3189
|
}}
|
|
3190
|
+
onTouchEnd={
|
|
3191
|
+
handleTouchEnd
|
|
3192
|
+
}
|
|
3193
|
+
onTouchMove={
|
|
3194
|
+
handleTouchMove
|
|
3195
|
+
}
|
|
3196
|
+
data-tooltip-id="system-tooltip"
|
|
3197
|
+
data-tooltip-content={
|
|
3198
|
+
collapsed
|
|
3199
|
+
? 'Expand group'
|
|
3200
|
+
: 'Collapse group'
|
|
3201
|
+
}
|
|
5019
3202
|
>
|
|
5020
3203
|
{collapsed
|
|
5021
3204
|
? '+'
|
|
5022
3205
|
: '−'}
|
|
5023
3206
|
</button>
|
|
3207
|
+
{/* Dynamic group action buttons */}
|
|
3208
|
+
{ajaxSetting?.groupBySetting?.icons?.map(
|
|
3209
|
+
(
|
|
3210
|
+
iconConfig,
|
|
3211
|
+
index
|
|
3212
|
+
) => {
|
|
3213
|
+
// Get the icon component based on the id
|
|
3214
|
+
const getIconComponent =
|
|
3215
|
+
(iconId) => {
|
|
3216
|
+
switch (
|
|
3217
|
+
iconId
|
|
3218
|
+
) {
|
|
3219
|
+
case 'clock':
|
|
3220
|
+
return Clock;
|
|
3221
|
+
case 'alarm':
|
|
3222
|
+
return Alarm;
|
|
3223
|
+
case 'check':
|
|
3224
|
+
return Check;
|
|
3225
|
+
case 'edit':
|
|
3226
|
+
return Edit;
|
|
3227
|
+
case 'envelope':
|
|
3228
|
+
return Envelope;
|
|
3229
|
+
case 'file':
|
|
3230
|
+
return File;
|
|
3231
|
+
case 'image':
|
|
3232
|
+
return Image;
|
|
3233
|
+
case 'copy':
|
|
3234
|
+
return Copy;
|
|
3235
|
+
case 'cloudUpload':
|
|
3236
|
+
return CloudDownload;
|
|
3237
|
+
case 'cloudDownload':
|
|
3238
|
+
return CloudDownload;
|
|
3239
|
+
default:
|
|
3240
|
+
return Clock; // Default fallback
|
|
3241
|
+
}
|
|
3242
|
+
};
|
|
3243
|
+
|
|
3244
|
+
// Check if this icon should be shown based on show conditions
|
|
3245
|
+
if (!shouldShowGroupAction(iconConfig, groupValue)) {
|
|
3246
|
+
return null;
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
const IconComponent =
|
|
3250
|
+
getIconComponent(
|
|
3251
|
+
iconConfig.id
|
|
3252
|
+
);
|
|
3253
|
+
|
|
3254
|
+
// Generate different color classes for multiple buttons
|
|
3255
|
+
const colorClasses =
|
|
3256
|
+
[
|
|
3257
|
+
'success',
|
|
3258
|
+
'warning',
|
|
3259
|
+
'info',
|
|
3260
|
+
'purple',
|
|
3261
|
+
'pink',
|
|
3262
|
+
];
|
|
3263
|
+
const colorClass =
|
|
3264
|
+
colorClasses[
|
|
3265
|
+
index %
|
|
3266
|
+
colorClasses.length
|
|
3267
|
+
];
|
|
3268
|
+
|
|
3269
|
+
return (
|
|
3270
|
+
<button
|
|
3271
|
+
key={`group-action-${index}`}
|
|
3272
|
+
className={`group-action-btn group-${iconConfig.id}-btn ${styles.groupActionBtn} ${styles[colorClass]}`}
|
|
3273
|
+
onClick={(
|
|
3274
|
+
e
|
|
3275
|
+
) => {
|
|
3276
|
+
e.stopPropagation();
|
|
3277
|
+
handleGroupAction(
|
|
3278
|
+
groupValue,
|
|
3279
|
+
iconConfig
|
|
3280
|
+
);
|
|
3281
|
+
}}
|
|
3282
|
+
onTouchStart={(
|
|
3283
|
+
e
|
|
3284
|
+
) => {
|
|
3285
|
+
e.stopPropagation();
|
|
3286
|
+
handleTouchStart(
|
|
3287
|
+
e,
|
|
3288
|
+
`group-${iconConfig.id}-${index}`,
|
|
3289
|
+
iconConfig.label
|
|
3290
|
+
);
|
|
3291
|
+
}}
|
|
3292
|
+
onTouchEnd={
|
|
3293
|
+
handleTouchEnd
|
|
3294
|
+
}
|
|
3295
|
+
onTouchMove={
|
|
3296
|
+
handleTouchMove
|
|
3297
|
+
}
|
|
3298
|
+
data-tooltip-id="system-tooltip"
|
|
3299
|
+
data-tooltip-content={
|
|
3300
|
+
iconConfig.label
|
|
3301
|
+
}
|
|
3302
|
+
>
|
|
3303
|
+
<IconComponent
|
|
3304
|
+
strokeWidth={
|
|
3305
|
+
2
|
|
3306
|
+
}
|
|
3307
|
+
size={
|
|
3308
|
+
14
|
|
3309
|
+
}
|
|
3310
|
+
/>
|
|
3311
|
+
</button>
|
|
3312
|
+
);
|
|
3313
|
+
}
|
|
3314
|
+
) || null}
|
|
5024
3315
|
<div
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
'center', // Changed to center for compact layout
|
|
5029
|
-
gap: '8px', // Add gap between elements
|
|
5030
|
-
}}
|
|
3316
|
+
className={
|
|
3317
|
+
styles.groupInfo
|
|
3318
|
+
}
|
|
5031
3319
|
>
|
|
5032
3320
|
<strong
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
color: 'var(--secondary-color, #1f2937)',
|
|
5037
|
-
fontWeight:
|
|
5038
|
-
'600',
|
|
5039
|
-
textTransform:
|
|
5040
|
-
'uppercase',
|
|
5041
|
-
letterSpacing:
|
|
5042
|
-
'0.025em',
|
|
5043
|
-
}}
|
|
3321
|
+
className={
|
|
3322
|
+
styles.groupValue
|
|
3323
|
+
}
|
|
5044
3324
|
>
|
|
5045
3325
|
{groupValue}
|
|
5046
3326
|
</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
3327
|
{/* Click indicator */}
|
|
5066
3328
|
<span
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
'0.7rem',
|
|
5071
|
-
fontStyle:
|
|
5072
|
-
'italic',
|
|
5073
|
-
opacity: 0.8,
|
|
5074
|
-
marginLeft:
|
|
5075
|
-
'auto',
|
|
5076
|
-
userSelect:
|
|
5077
|
-
'none',
|
|
5078
|
-
}}
|
|
3329
|
+
className={
|
|
3330
|
+
styles.groupClickIndicator
|
|
3331
|
+
}
|
|
5079
3332
|
>
|
|
5080
3333
|
Click to
|
|
5081
3334
|
select/unselect
|
|
@@ -5084,93 +3337,11 @@ const DataGrid = forwardRef(
|
|
|
5084
3337
|
</div>
|
|
5085
3338
|
</div>
|
|
5086
3339
|
|
|
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
3340
|
{/* Background pattern */}
|
|
5162
3341
|
<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
|
-
}}
|
|
3342
|
+
className={
|
|
3343
|
+
styles.groupHeaderPattern
|
|
3344
|
+
}
|
|
5174
3345
|
/>
|
|
5175
3346
|
</div>
|
|
5176
3347
|
);
|
|
@@ -5319,13 +3490,19 @@ const DataGrid = forwardRef(
|
|
|
5319
3490
|
</div>
|
|
5320
3491
|
|
|
5321
3492
|
{/* Relocated auto refresh control when it's the only control */}
|
|
5322
|
-
{
|
|
3493
|
+
{shouldRelocateAutoRefresh() && (
|
|
5323
3494
|
<div className={styles.dataGridBottomContainer}>
|
|
5324
|
-
|
|
3495
|
+
<AutoRefreshControls
|
|
3496
|
+
ajaxSetting={ajaxSetting}
|
|
3497
|
+
autoRefreshEnabled={autoRefreshEnabled}
|
|
3498
|
+
setAutoRefreshEnabled={setAutoRefreshEnabled}
|
|
3499
|
+
refreshInterval={refreshInterval}
|
|
3500
|
+
isRelocated={true}
|
|
3501
|
+
/>
|
|
5325
3502
|
</div>
|
|
5326
|
-
)}
|
|
3503
|
+
)}
|
|
5327
3504
|
|
|
5328
|
-
|
|
3505
|
+
<AudioPlayer audioSource={audioSource} />
|
|
5329
3506
|
<Popup
|
|
5330
3507
|
open={modalShow}
|
|
5331
3508
|
onClose={modalClose}
|
|
@@ -5373,231 +3550,38 @@ const DataGrid = forwardRef(
|
|
|
5373
3550
|
</div>
|
|
5374
3551
|
</div>
|
|
5375
3552
|
</Popup>
|
|
5376
|
-
<
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
}
|
|
5385
|
-
|
|
5386
|
-
|
|
3553
|
+
<GalleryModal
|
|
3554
|
+
modalGalleryShow={modalGalleryShow}
|
|
3555
|
+
modalGalleryClose={modalGalleryClose}
|
|
3556
|
+
galleryData={galleryData}
|
|
3557
|
+
setGalleryData={setGalleryData}
|
|
3558
|
+
currentItem={currentItem}
|
|
3559
|
+
form={form}
|
|
3560
|
+
lightboxOpen={lightboxOpen}
|
|
3561
|
+
setLightboxOpen={setLightboxOpen}
|
|
3562
|
+
lightboxIndex={lightboxIndex}
|
|
3563
|
+
setLightboxIndex={setLightboxIndex}
|
|
3564
|
+
/>
|
|
3565
|
+
|
|
3566
|
+
{/* Touch Tooltip Overlay */}
|
|
3567
|
+
{activeTooltip && (
|
|
5387
3568
|
<div
|
|
5388
|
-
className={
|
|
3569
|
+
className={styles.touchTooltip}
|
|
3570
|
+
style={{
|
|
3571
|
+
position: 'fixed',
|
|
3572
|
+
left: `${activeTooltip.x}px`,
|
|
3573
|
+
top: `${activeTooltip.y - 50}px`,
|
|
3574
|
+
transform: 'translateX(-50%)', // Center horizontally on button
|
|
3575
|
+
zIndex: 9999,
|
|
3576
|
+
pointerEvents: 'none',
|
|
3577
|
+
}}
|
|
3578
|
+
onClick={closeTooltip}
|
|
5389
3579
|
>
|
|
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>
|
|
3580
|
+
<div className={styles.touchTooltipContent}>
|
|
3581
|
+
{activeTooltip.label}
|
|
5590
3582
|
</div>
|
|
5591
3583
|
</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
|
-
/>
|
|
3584
|
+
)}
|
|
5601
3585
|
</>
|
|
5602
3586
|
);
|
|
5603
3587
|
}
|