@visns-studio/visns-components 5.10.2 → 5.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1358 @@
1
+ import numeral from 'numeral';
2
+ import moment from 'moment';
3
+ import parse from 'html-react-parser';
4
+ import { CloudDownload, Alarm } from 'akar-icons';
5
+ import { toast } from 'react-toastify';
6
+ import CellWithTooltip from '../cells/CellWithTooltip';
7
+
8
+ // Boolean Column Renderer
9
+ export const renderBooleanColumn = ({ column, commonProps, filterEditor, filterEditorProps }) => {
10
+ return {
11
+ ...commonProps,
12
+ filterEditor: filterEditor,
13
+ filterEditorProps: filterEditorProps,
14
+ name: `${column.type}.${column.id}`,
15
+ sortable: false,
16
+ render: ({ data }) => {
17
+ const value = data[column.id] ? 'Yes' : 'No';
18
+ return (
19
+ <CellWithTooltip
20
+ value={data[column.id]}
21
+ columnType="boolean"
22
+ >
23
+ <span>{value}</span>
24
+ </CellWithTooltip>
25
+ );
26
+ },
27
+ };
28
+ };
29
+
30
+ // Currency Column Renderer
31
+ export const renderCurrencyColumn = ({ column, commonProps }) => {
32
+ return {
33
+ ...commonProps,
34
+ name: column.id,
35
+ render: ({ data }) => {
36
+ if (data) {
37
+ const formattedValue = numeral(data[column.id]).format('$0,0.00');
38
+
39
+ return (
40
+ <CellWithTooltip
41
+ value={data[column.id]}
42
+ columnType="currency"
43
+ >
44
+ <span>{formattedValue}</span>
45
+ </CellWithTooltip>
46
+ );
47
+ } else {
48
+ return null;
49
+ }
50
+ },
51
+ };
52
+ };
53
+
54
+ // Date Column Renderer
55
+ export const renderDateColumn = ({ column, commonProps, filterEditor, filterEditorProps }) => {
56
+ return {
57
+ ...commonProps,
58
+ name: column.id,
59
+ filterEditor: filterEditor,
60
+ filterEditorProps: filterEditorProps,
61
+ defaultWidth: 120, // Set default width for date columns
62
+ minWidth: 120, // Set minimum width for date columns
63
+ render: ({ data }) => {
64
+ if (
65
+ data &&
66
+ data[column.id] &&
67
+ moment(data[column.id]).isValid() &&
68
+ moment(data[column.id]).format('YYYY-MM-DD') !== '1970-01-01'
69
+ ) {
70
+ const value = moment(data[column.id]).format(
71
+ column.format || 'DD-MM-YYYY'
72
+ );
73
+ let columnStyle = {};
74
+
75
+ if (
76
+ column.active &&
77
+ column.active.id &&
78
+ column.active.value === 'expired'
79
+ ) {
80
+ if (
81
+ data[column.active.id] &&
82
+ moment(data[column.active.id]).isBefore(moment())
83
+ ) {
84
+ columnStyle = {
85
+ color: column.active.colour,
86
+ };
87
+ }
88
+ }
89
+
90
+ return (
91
+ <CellWithTooltip
92
+ value={data[column.id]}
93
+ columnType="date"
94
+ >
95
+ <span style={columnStyle}>
96
+ {value}
97
+ </span>
98
+ </CellWithTooltip>
99
+ );
100
+ } else {
101
+ return null;
102
+ }
103
+ },
104
+ };
105
+ };
106
+
107
+ // DateTime Column Renderer
108
+ export const renderDateTimeColumn = ({ column, commonProps, filterEditor, filterEditorProps }) => {
109
+ return {
110
+ ...commonProps,
111
+ name: column.id,
112
+ filterEditor: filterEditor,
113
+ filterEditorProps: filterEditorProps,
114
+ defaultWidth: 160, // Set default width for datetime columns
115
+ minWidth: 160, // Set minimum width for datetime columns
116
+ render: ({ data }) => {
117
+ if (
118
+ data &&
119
+ data[column.id] &&
120
+ moment(data[column.id]).isValid() &&
121
+ moment(data[column.id]).format('YYYY-MM-DD') !== '1970-01-01'
122
+ ) {
123
+ const formattedValue = moment(data[column.id]).format(
124
+ column.format ? column.format : 'DD-MM-YYYY hh:mm A'
125
+ );
126
+
127
+ return <span>{formattedValue}</span>;
128
+ } else {
129
+ return null;
130
+ }
131
+ },
132
+ };
133
+ };
134
+
135
+ // Array Count Column Renderer
136
+ export const renderArrayCountColumn = ({ column, commonProps }) => {
137
+ return {
138
+ ...commonProps,
139
+ name: `${column.type}.${column.id}`,
140
+ sortable: false,
141
+ render: ({ data }) => {
142
+ if (data && data[column.id]) {
143
+ return data[column.id].length;
144
+ } else {
145
+ return null;
146
+ }
147
+ },
148
+ };
149
+ };
150
+
151
+ // Colour Column Renderer
152
+ export const renderColourColumn = ({ column, commonProps }) => {
153
+ return {
154
+ ...commonProps,
155
+ name: `${column.type}.${column.id}`,
156
+ sortable: false,
157
+ render: ({ data }) => {
158
+ if (data && data[column.id]) {
159
+ return (
160
+ <div
161
+ style={{
162
+ width: '25px',
163
+ height: '25px',
164
+ borderRadius: '10px',
165
+ overflow: 'hidden',
166
+ }}
167
+ >
168
+ <div
169
+ style={{
170
+ background:
171
+ data[column.id] + ' none repeat scroll 0% 0%',
172
+ height: '100%',
173
+ width: '100%',
174
+ cursor: 'pointer',
175
+ position: 'relative',
176
+ outline: 'currentcolor none medium',
177
+ }}
178
+ ></div>
179
+ </div>
180
+ );
181
+ } else {
182
+ return null;
183
+ }
184
+ },
185
+ };
186
+ };
187
+
188
+ // Dropdown Column Renderer
189
+ export const renderDropdownColumn = ({
190
+ column,
191
+ commonProps,
192
+ filterEditor,
193
+ filterEditorProps,
194
+ dropdownData,
195
+ handleDropdownUpdate
196
+ }) => {
197
+ const columnId = Array.isArray(column.id)
198
+ ? column.id.join('-')
199
+ : column.id;
200
+
201
+ return {
202
+ ...commonProps,
203
+ name: columnId,
204
+ filterEditor: filterEditor,
205
+ filterEditorProps: filterEditorProps,
206
+ render: ({ data }) => {
207
+ return (
208
+ <select
209
+ name={column.id}
210
+ value={data[column.id]?.id || ''}
211
+ style={{ padding: '7px' }}
212
+ onChange={(e) => {
213
+ e.preventDefault();
214
+
215
+ if (
216
+ column.update?.key &&
217
+ column.update?.url &&
218
+ column.update?.method
219
+ ) {
220
+ handleDropdownUpdate(
221
+ column.update.key,
222
+ `${column.update.url}/${data.id}`,
223
+ column.update.method,
224
+ e.target.value
225
+ );
226
+ }
227
+ }}
228
+ >
229
+ <option value="">
230
+ Select{' '}
231
+ {/^[aeioAEIO]/.test(column.label) ? 'an' : 'a'}{' '}
232
+ {column.label}
233
+ </option>
234
+ {dropdownData[column.id] &&
235
+ dropdownData[column.id].length > 0
236
+ ? dropdownData[column.id].map((option, index) => (
237
+ <option value={option.id} key={index}>
238
+ {option.label}
239
+ </option>
240
+ ))
241
+ : null}
242
+ </select>
243
+ );
244
+ },
245
+ };
246
+ };
247
+
248
+ // Image Column Renderer
249
+ export const renderImageColumn = ({ column, commonProps }) => {
250
+ return {
251
+ ...commonProps,
252
+ name: column.id,
253
+ render: ({ data }) => {
254
+ if (data && data[column.id]) {
255
+ const fileUrl = data[column.id][0]?.file_url || data[column.id]?.file_url;
256
+
257
+ if (fileUrl) {
258
+ return (
259
+ <span>
260
+ {parse(`<img width="150" src="${fileUrl}" />`)}
261
+ </span>
262
+ );
263
+ }
264
+ }
265
+
266
+ return null;
267
+ },
268
+ };
269
+ };
270
+
271
+ // File Column Renderer
272
+ export const renderFileColumn = ({ column, commonProps, handleDownload, styles }) => {
273
+ return {
274
+ ...commonProps,
275
+ name: column.id,
276
+ sortable: false,
277
+ render: ({ data }) => {
278
+ if (data && data[column.id]) {
279
+ // Check if the data is an array of files
280
+ if (Array.isArray(data[column.id])) {
281
+ // Return a span for each file in the array with added margin
282
+ return data[column.id].map((file, index) => (
283
+ <span
284
+ key={index}
285
+ onClick={(e) => {
286
+ e.stopPropagation();
287
+ e.preventDefault();
288
+ handleDownload(file);
289
+ }}
290
+ style={{
291
+ marginRight: '10px',
292
+ display: 'inline-block',
293
+ }}
294
+ >
295
+ <CloudDownload
296
+ data-tooltip-id="system-tooltip"
297
+ data-tooltip-content={`Download File ${file.file_name}`}
298
+ strokeWidth={2}
299
+ size={14}
300
+ className={styles.tdaction}
301
+ />
302
+ </span>
303
+ ));
304
+ } else {
305
+ // Return a single span for a single file with added margin
306
+ return (
307
+ <span
308
+ onClick={(e) => {
309
+ e.stopPropagation();
310
+ e.preventDefault();
311
+ handleDownload(data[column.id]);
312
+ }}
313
+ style={{
314
+ marginRight: '10px',
315
+ display: 'inline-block',
316
+ }}
317
+ >
318
+ <CloudDownload
319
+ data-tooltip-id="system-tooltip"
320
+ data-tooltip-content={`Download File ${data[column.id].file_name}`}
321
+ strokeWidth={2}
322
+ size={14}
323
+ className={styles.tdaction}
324
+ />
325
+ </span>
326
+ );
327
+ }
328
+ } else {
329
+ return null;
330
+ }
331
+ },
332
+ };
333
+ };
334
+
335
+ // Icons Column Renderer
336
+ export const renderIconsColumn = ({ column, commonProps }) => {
337
+ return {
338
+ ...commonProps,
339
+ name: column.id,
340
+ sortable: false,
341
+ render: ({ data }) => {
342
+ const icons = [];
343
+
344
+ if (column.icons && column.icons.length > 0) {
345
+ column.icons.forEach((icon, index) => {
346
+ icons.push(
347
+ <img
348
+ key={index}
349
+ src={icon.url}
350
+ alt={icon.name}
351
+ data-tooltip-id="system-tooltip"
352
+ data-tooltip-content={icon.name}
353
+ />
354
+ );
355
+ });
356
+ }
357
+
358
+ if (data) {
359
+ return <span>{icons}</span>;
360
+ } else {
361
+ return null;
362
+ }
363
+ },
364
+ };
365
+ };
366
+
367
+ // JSON Column Renderer
368
+ export const renderJsonColumn = ({ column, commonProps }) => {
369
+ return {
370
+ ...commonProps,
371
+ name: `${column.id}.${column.jsonData}`,
372
+ render: ({ data }) => {
373
+ if (
374
+ data &&
375
+ data[column.id] &&
376
+ data[column.id][column.jsonData]
377
+ ) {
378
+ const jsonValue = data[column.id][column.jsonData];
379
+
380
+ return (
381
+ <CellWithTooltip
382
+ value={jsonValue}
383
+ columnType="json"
384
+ >
385
+ <span>{jsonValue}</span>
386
+ </CellWithTooltip>
387
+ );
388
+ } else {
389
+ return null;
390
+ }
391
+ },
392
+ };
393
+ };
394
+
395
+ // Number Column Renderer
396
+ export const renderNumberColumn = ({ column, commonProps }) => {
397
+ return {
398
+ ...commonProps,
399
+ name: column.id,
400
+ type: 'number',
401
+ };
402
+ };
403
+
404
+ // Option Column Renderer
405
+ export const renderOptionColumn = ({ column, commonProps, filterEditor, filterEditorProps }) => {
406
+ return {
407
+ ...commonProps,
408
+ name: column.id,
409
+ filterEditor: filterEditor,
410
+ filterEditorProps: filterEditorProps,
411
+ render: ({ data }) => {
412
+ let newData;
413
+
414
+ if (
415
+ data &&
416
+ column.options &&
417
+ column.options[data[column.id]]
418
+ ) {
419
+ if (
420
+ column.key &&
421
+ data[column.key] > 0 &&
422
+ column.options[data[column.id]].includes('#')
423
+ ) {
424
+ newData = column.options[data[column.id]].replace(
425
+ /#/g,
426
+ data[column.key]
427
+ );
428
+ } else {
429
+ newData = column.options[data[column.id]];
430
+ }
431
+
432
+ return (
433
+ <CellWithTooltip
434
+ value={newData}
435
+ columnType="option"
436
+ >
437
+ <span>{newData}</span>
438
+ </CellWithTooltip>
439
+ );
440
+ } else {
441
+ return null;
442
+ }
443
+ },
444
+ };
445
+ };
446
+
447
+ // Placeholder Column Renderer
448
+ export const renderPlaceholderColumn = ({ column, commonProps }) => {
449
+ return {
450
+ ...commonProps,
451
+ name: column.id,
452
+ sortable: false,
453
+ render: ({ data }) => {
454
+ if (data) {
455
+ return (
456
+ <CellWithTooltip
457
+ value={column.placeholder}
458
+ columnType="placeholder"
459
+ >
460
+ <span>{column.placeholder}</span>
461
+ </CellWithTooltip>
462
+ );
463
+ } else {
464
+ return null;
465
+ }
466
+ },
467
+ };
468
+ };
469
+
470
+ // Age Column Renderer
471
+ export const renderAgeColumn = ({ column, commonProps, filterEditor }) => {
472
+ return {
473
+ ...commonProps,
474
+ type: 'number',
475
+ filterEditor: filterEditor,
476
+ name: `${column.type}.${column.id}`,
477
+ render: ({ data }) => {
478
+ const value = column.id.reduce((acc, id) => {
479
+ if (acc === '') {
480
+ return data[id];
481
+ } else {
482
+ return acc ? acc[id] : '';
483
+ }
484
+ }, '');
485
+
486
+ if (value !== '' && value !== null) {
487
+ const date = moment(value);
488
+
489
+ return (
490
+ <span>
491
+ {moment().diff(
492
+ date,
493
+ column.duration ? column.duration : 'years'
494
+ )}
495
+ </span>
496
+ );
497
+ } else {
498
+ return null;
499
+ }
500
+ },
501
+ };
502
+ };
503
+
504
+ // Coding Column Renderer
505
+ export const renderCodingColumn = ({ column, commonProps, handleCoding }) => {
506
+ return {
507
+ ...commonProps,
508
+ name: `${column.type}.${column.id}`,
509
+ sortable: false,
510
+ render: ({ data }) => {
511
+ if (data) {
512
+ return handleCoding(column, data);
513
+ } else {
514
+ return null;
515
+ }
516
+ },
517
+ };
518
+ };
519
+
520
+ // Relation Column Renderer
521
+ export const renderRelationColumn = ({ column, commonProps, filterEditor, filterEditorProps, relationName }) => {
522
+ return {
523
+ ...commonProps,
524
+ name: relationName,
525
+ sortable: false,
526
+ filterEditor: filterEditor,
527
+ filterEditorProps: filterEditorProps,
528
+ render: ({ data }) => {
529
+ let value = column.id.reduce((acc, id) => {
530
+ if (acc === '') {
531
+ if (data[id]) {
532
+ return data[id];
533
+ } else {
534
+ return '';
535
+ }
536
+ } else {
537
+ if (acc && acc[id]) {
538
+ return acc[id];
539
+ } else {
540
+ return '';
541
+ }
542
+ }
543
+ }, '');
544
+
545
+ if (value) {
546
+ if (Array.isArray(value)) {
547
+ if (value[0]) {
548
+ value = value[0];
549
+ }
550
+ }
551
+
552
+ if (Array.isArray(column.nameFrom)) {
553
+ value = column.nameFrom
554
+ .map((nf) => value?.[nf] ?? '')
555
+ .join(' ');
556
+ } else {
557
+ if (
558
+ value &&
559
+ value.hasOwnProperty(column.nameFrom)
560
+ ) {
561
+ value = value[column.nameFrom];
562
+ }
563
+ }
564
+
565
+ if (
566
+ column.placeholder &&
567
+ column.placeholder !== ''
568
+ ) {
569
+ return (
570
+ <span>
571
+ {column.placeholder}
572
+ </span>
573
+ );
574
+ } else {
575
+ if (column.hasOwnProperty('format')) {
576
+ if (column.format) {
577
+ value = moment(value).format('DD-MM-YYYY');
578
+ }
579
+ }
580
+
581
+ if (
582
+ typeof value === 'string' ||
583
+ typeof value === 'number'
584
+ ) {
585
+ return (
586
+ <CellWithTooltip
587
+ value={value}
588
+ columnType="relation"
589
+ >
590
+ <span>{value}</span>
591
+ </CellWithTooltip>
592
+ );
593
+ } else {
594
+ return null;
595
+ }
596
+ }
597
+ } else {
598
+ return null;
599
+ }
600
+ },
601
+ };
602
+ };
603
+
604
+ // RelationArray Column Renderer
605
+ export const renderRelationArrayColumn = ({ column, commonProps, filterEditor, filterEditorProps, relationName }) => {
606
+ return {
607
+ ...commonProps,
608
+ name: relationName,
609
+ sortable: false,
610
+ filterEditor: filterEditor,
611
+ filterEditorProps: filterEditorProps,
612
+ render: ({ data }) => {
613
+ const relationValue = column.id.reduce(
614
+ (acc, id) =>
615
+ acc === ''
616
+ ? data[id]
617
+ : acc && acc[id]
618
+ ? acc[id]
619
+ : '',
620
+ ''
621
+ );
622
+
623
+ if (
624
+ relationValue &&
625
+ Array.isArray(relationValue) &&
626
+ relationValue.length > 0
627
+ ) {
628
+ const nameProperties =
629
+ Array.isArray(column.nameFrom)
630
+ ? column.nameFrom
631
+ : [column.nameFrom];
632
+
633
+ const values = relationValue
634
+ .map((rv) => {
635
+ if (
636
+ column.childId &&
637
+ Array.isArray(column.childId)
638
+ ) {
639
+ let childValue = rv;
640
+
641
+ column.childId.forEach((a) => {
642
+ if (childValue[a]) {
643
+ childValue = childValue[a];
644
+ }
645
+ });
646
+
647
+ if (childValue) {
648
+ const propertyValues =
649
+ nameProperties
650
+ .map((prop) => childValue[prop])
651
+ .filter(
652
+ (value) =>
653
+ value !== undefined &&
654
+ value !== null
655
+ );
656
+ return propertyValues.join(' ');
657
+ }
658
+ } else {
659
+ const propertyValues =
660
+ nameProperties
661
+ .map((prop) => rv[prop])
662
+ .filter(
663
+ (value) =>
664
+ value !== undefined &&
665
+ value !== null
666
+ );
667
+ return propertyValues.join(' ');
668
+ }
669
+ })
670
+ .filter((value) => value !== '');
671
+
672
+ let result = '';
673
+
674
+ if (
675
+ column.relation?.id &&
676
+ column.relation?.key
677
+ ) {
678
+ const relationValue =
679
+ column.relation.key.reduce(
680
+ (acc, id) =>
681
+ acc === ''
682
+ ? data[id]
683
+ : acc && acc[id]
684
+ ? acc[id]
685
+ : '',
686
+ ''
687
+ );
688
+
689
+ result = `${relationValue[column.relation.id]}<br />`;
690
+ }
691
+
692
+ if (
693
+ column.where &&
694
+ column.where.id &&
695
+ column.where.value
696
+ ) {
697
+ if (
698
+ data[column.where.id] ===
699
+ column.where.value
700
+ ) {
701
+ result += values.join('<br />');
702
+ }
703
+ } else {
704
+ result += values.join('<br />');
705
+ }
706
+
707
+ return (
708
+ <CellWithTooltip
709
+ value={result}
710
+ columnType="relationArray"
711
+ >
712
+ <span>{parse(result)}</span>
713
+ </CellWithTooltip>
714
+ );
715
+ } else {
716
+ let result = '';
717
+
718
+ if (
719
+ column.relation?.id &&
720
+ column.relation?.key
721
+ ) {
722
+ const relationValue =
723
+ column.relation.key.reduce(
724
+ (acc, id) =>
725
+ acc === ''
726
+ ? data[id]
727
+ : acc && acc[id]
728
+ ? acc[id]
729
+ : '',
730
+ ''
731
+ );
732
+
733
+ result =
734
+ relationValue &&
735
+ relationValue[column.relation.id]
736
+ ? relationValue[column.relation.id]
737
+ : '';
738
+ }
739
+
740
+ return (
741
+ <CellWithTooltip
742
+ value={result}
743
+ columnType="relationArray"
744
+ >
745
+ <span>{parse(result)}</span>
746
+ </CellWithTooltip>
747
+ );
748
+ }
749
+
750
+ return null;
751
+ },
752
+ };
753
+ };
754
+
755
+ // RichText Column Renderer
756
+ export const renderRichTextColumn = ({ column, commonProps }) => {
757
+ return {
758
+ ...commonProps,
759
+ name: column.id,
760
+ render: ({ data }) => {
761
+ if (data && data[column.id]) {
762
+ return (
763
+ <CellWithTooltip
764
+ value={data[column.id]}
765
+ columnType="richtext"
766
+ >
767
+ <span>
768
+ {parse(data[column.id])}
769
+ </span>
770
+ </CellWithTooltip>
771
+ );
772
+ } else {
773
+ return null;
774
+ }
775
+ },
776
+ };
777
+ };
778
+
779
+ // Stage Column Renderer
780
+ export const renderStageColumn = ({ column, commonProps }) => {
781
+ return {
782
+ ...commonProps,
783
+ name: column.id,
784
+ sortable: false,
785
+ render: ({ data }) => {
786
+ let stage = '';
787
+
788
+ column.keyIds.forEach((a, b) => {
789
+ if (data[a] === 1) {
790
+ stage = column.valueIds[b];
791
+ }
792
+ });
793
+
794
+ return (
795
+ <CellWithTooltip
796
+ value={stage}
797
+ columnType="stage"
798
+ >
799
+ <span>{stage}</span>
800
+ </CellWithTooltip>
801
+ );
802
+ },
803
+ };
804
+ };
805
+
806
+ // Time Column Renderer
807
+ export const renderTimeColumn = ({ column, commonProps }) => {
808
+ return {
809
+ ...commonProps,
810
+ name: column.id,
811
+ render: ({ data }) => {
812
+ if (
813
+ data &&
814
+ data[column.id] &&
815
+ moment(data[column.id]).isValid() &&
816
+ moment(data[column.id]).format('YYYY-MM-DD') !== '1970-01-01'
817
+ ) {
818
+ const formattedTime = moment(data[column.id]).format(
819
+ column.format ? column.format : 'hh:mm A'
820
+ );
821
+
822
+ return (
823
+ <CellWithTooltip
824
+ value={data[column.id]}
825
+ columnType="time"
826
+ >
827
+ <span>{formattedTime}</span>
828
+ </CellWithTooltip>
829
+ );
830
+ } else {
831
+ return null;
832
+ }
833
+ },
834
+ };
835
+ };
836
+
837
+ // Timer Column Renderer
838
+ export const renderTimerColumn = ({ column, commonProps, handleTimer, form, columns, styles }) => {
839
+ return {
840
+ ...commonProps,
841
+ name: column.id,
842
+ render: ({ data }) => {
843
+ if (
844
+ data &&
845
+ data[column.id] &&
846
+ moment(data[column.id]).isValid() &&
847
+ moment(data[column.id]).format('YYYY-MM-DD') !== '1970-01-01'
848
+ ) {
849
+ const formattedData = moment(data[column.id]).format(
850
+ column.format ? column.format : 'DD-MM-YYYY hh:mm A'
851
+ );
852
+
853
+ return (
854
+ <CellWithTooltip
855
+ value={data[column.id]}
856
+ columnType="timer"
857
+ >
858
+ <span>{formattedData}</span>
859
+ </CellWithTooltip>
860
+ );
861
+ } else {
862
+ let shouldRenderButton = false;
863
+
864
+ if (
865
+ !column.hasOwnProperty('require') ||
866
+ column.require.length === 0
867
+ ) {
868
+ shouldRenderButton = true;
869
+ } else {
870
+ for (const req of column.require) {
871
+ if (req.hasOwnProperty('id')) {
872
+ const hasValue = req.hasOwnProperty('value');
873
+ if (
874
+ (hasValue && data[req.id] === req.value) ||
875
+ (!hasValue && data[req.id])
876
+ ) {
877
+ shouldRenderButton = true;
878
+ } else {
879
+ shouldRenderButton = false;
880
+ break;
881
+ }
882
+ }
883
+ }
884
+ }
885
+
886
+ if (shouldRenderButton) {
887
+ return (
888
+ <button
889
+ className={styles.clockButton}
890
+ onClick={(e) => {
891
+ e.stopPropagation();
892
+ e.preventDefault();
893
+
894
+ // Check if every required field in column.validate exists and is truthy in data
895
+ if (
896
+ column.validate &&
897
+ column.validate.length > 0
898
+ ) {
899
+ const missingFields = column.validate.filter(
900
+ (v) => !data[v]
901
+ );
902
+
903
+ if (missingFields.length === 0) {
904
+ handleTimer(
905
+ data[form.primaryKey],
906
+ column.id,
907
+ data
908
+ );
909
+ } else {
910
+ // Format field names to be more readable
911
+ const formattedFields = missingFields.map(
912
+ (field) => {
913
+ // Try to find the column with this field ID to get its label
914
+ const fieldColumn = columns.find(
915
+ (col) => col.id === field
916
+ );
917
+ // Use the label if found, otherwise use the field ID
918
+ return fieldColumn?.label || field;
919
+ }
920
+ );
921
+
922
+ const fieldList = formattedFields.join(', ');
923
+ toast.error(
924
+ `Please complete the following required field${
925
+ missingFields.length > 1 ? 's' : ''
926
+ } before starting the timer: ${fieldList}`
927
+ );
928
+ }
929
+ } else {
930
+ handleTimer(
931
+ data[form.primaryKey],
932
+ column.id,
933
+ data
934
+ );
935
+ }
936
+ }}
937
+ >
938
+ <Alarm
939
+ data-tooltip-id="system-tooltip"
940
+ data-tooltip-content={`${column.label} Timer`}
941
+ strokeWidth={2}
942
+ size={14}
943
+ style={{
944
+ color: 'white',
945
+ }}
946
+ />
947
+ </button>
948
+ );
949
+ }
950
+
951
+ // Return a placeholder div to maintain consistent row height
952
+ return (
953
+ <div
954
+ style={{
955
+ width: '28px',
956
+ height: '28px',
957
+ display: 'flex',
958
+ alignItems: 'center',
959
+ justifyContent: 'center',
960
+ margin: '0 auto',
961
+ }}
962
+ >
963
+ {/* Empty placeholder to maintain layout */}
964
+ </div>
965
+ );
966
+ }
967
+ },
968
+ };
969
+ };
970
+
971
+ // URL Column Renderer
972
+ export const renderUrlColumn = ({ column, commonProps }) => {
973
+ return {
974
+ ...commonProps,
975
+ name: column.id,
976
+ render: ({ data }) => {
977
+ if (data && data[column.id]) {
978
+ return (
979
+ <CellWithTooltip
980
+ value={data[column.id]}
981
+ columnType="url"
982
+ >
983
+ <span>
984
+ <a
985
+ href={data[column.id]}
986
+ target="_blank"
987
+ >
988
+ Link
989
+ </a>
990
+ </span>
991
+ </CellWithTooltip>
992
+ );
993
+ } else {
994
+ return null;
995
+ }
996
+ },
997
+ };
998
+ };
999
+
1000
+ // Input Text Column Renderer
1001
+ export const renderInputTextColumn = ({
1002
+ column,
1003
+ commonProps,
1004
+ localInputValuesRef,
1005
+ setLocalInputValues,
1006
+ handleInputUpdate
1007
+ }) => {
1008
+ return {
1009
+ ...commonProps,
1010
+ name: column.id,
1011
+ render: ({ data }) => {
1012
+ const inputKey = `${data.id}-${column.id}`;
1013
+
1014
+ // Use the ref value first, then fall back to data value
1015
+ const currentValue =
1016
+ localInputValuesRef.current[inputKey] !== undefined
1017
+ ? localInputValuesRef.current[inputKey]
1018
+ : data[column.id] || '';
1019
+
1020
+ return (
1021
+ <input
1022
+ type="text"
1023
+ name={column.id}
1024
+ value={currentValue}
1025
+ style={{
1026
+ padding: '7px',
1027
+ width: '100%',
1028
+ }}
1029
+ onChange={(e) => {
1030
+ const newValue = e.target.value;
1031
+
1032
+ // Update both state and ref
1033
+ localInputValuesRef.current = {
1034
+ ...localInputValuesRef.current,
1035
+ [inputKey]: newValue,
1036
+ };
1037
+
1038
+ setLocalInputValues((prev) => ({
1039
+ ...prev,
1040
+ [inputKey]: newValue,
1041
+ }));
1042
+
1043
+ if (
1044
+ column.update?.key &&
1045
+ column.update?.url &&
1046
+ column.update?.method
1047
+ ) {
1048
+ handleInputUpdate(
1049
+ column.update.key,
1050
+ `${column.update.url}/${data.id}`,
1051
+ column.update.method,
1052
+ newValue,
1053
+ inputKey
1054
+ );
1055
+ }
1056
+ }}
1057
+ onBlur={() => {
1058
+ if (
1059
+ column.update?.key &&
1060
+ column.update?.url &&
1061
+ column.update?.method
1062
+ ) {
1063
+ handleInputUpdate.flush();
1064
+ }
1065
+ }}
1066
+ />
1067
+ );
1068
+ },
1069
+ };
1070
+ };
1071
+
1072
+ // Address Column Renderer
1073
+ export const renderAddressColumn = ({ column, commonProps, filterEditor, filterEditorProps }) => {
1074
+ return {
1075
+ ...commonProps,
1076
+ name: `${column.type}.${column.id}`,
1077
+ filterEditor: filterEditor,
1078
+ filterEditorProps: filterEditorProps,
1079
+ render: ({ data }) => {
1080
+ const addressFields = [
1081
+ 'address1',
1082
+ 'address2',
1083
+ 'suburb',
1084
+ 'postcode',
1085
+ ];
1086
+
1087
+ const addressValue = addressFields
1088
+ .filter((field) => data[field])
1089
+ .map((field) => data[field])
1090
+ .join(' ');
1091
+
1092
+ const stateValue =
1093
+ data.state?.name ||
1094
+ data.state ||
1095
+ '';
1096
+
1097
+ const value = `${addressValue} ${stateValue}`;
1098
+
1099
+ return (
1100
+ <CellWithTooltip
1101
+ value={value}
1102
+ columnType="address"
1103
+ >
1104
+ <span>{value}</span>
1105
+ </CellWithTooltip>
1106
+ );
1107
+ },
1108
+ };
1109
+ };
1110
+
1111
+ // Created By Column Renderer
1112
+ export const renderCreatedByColumn = ({ column, commonProps }) => {
1113
+ return {
1114
+ ...commonProps,
1115
+ name: `${column.type}.${column.id}`,
1116
+ sortable: false,
1117
+ render: ({ data }) => {
1118
+ if (
1119
+ data.audits[0] &&
1120
+ data.audits[0].user &&
1121
+ data.audits[0].user.name
1122
+ ) {
1123
+ const value = data.audits[0].user.name;
1124
+
1125
+ return <span>{value}</span>;
1126
+ } else {
1127
+ return null;
1128
+ }
1129
+ },
1130
+ };
1131
+ };
1132
+
1133
+ // Date Range Column Renderer
1134
+ export const renderDateRangeColumn = ({ column, commonProps, filterEditor, filterEditorProps }) => {
1135
+ return {
1136
+ ...commonProps,
1137
+ name: `${column.id[0]}.${column.id[1]}.`,
1138
+ filterEditor: filterEditor,
1139
+ filterEditorProps: filterEditorProps,
1140
+ defaultWidth: 200, // Set default width for daterange columns
1141
+ minWidth: 200, // Set minimum width for daterange columns
1142
+ render: ({ data }) => {
1143
+ let value = '';
1144
+
1145
+ if (
1146
+ data &&
1147
+ data[column.id[0]] &&
1148
+ moment(data[column.id[0]]).isValid() &&
1149
+ moment(data[column.id[0]]).format('YYYY-MM-DD') !== '1970-01-01'
1150
+ ) {
1151
+ value = moment(data[column.id[0]]).format(
1152
+ column.format || 'DD-MM-YYYY'
1153
+ );
1154
+ }
1155
+
1156
+ if (
1157
+ data &&
1158
+ data[column.id[1]] &&
1159
+ moment(data[column.id[1]]).isValid() &&
1160
+ moment(data[column.id[1]]).format('YYYY-MM-DD') !== '1970-01-01'
1161
+ ) {
1162
+ value += ` - ${moment(data[column.id[1]]).format(
1163
+ column.format || 'DD-MM-YYYY'
1164
+ )}`;
1165
+ }
1166
+
1167
+ return <span>{value}</span>;
1168
+ },
1169
+ };
1170
+ };
1171
+
1172
+ // Stage Counter Column Renderer
1173
+ export const renderStageCounterColumn = ({ column, commonProps, navigate }) => {
1174
+ return {
1175
+ ...commonProps,
1176
+ name: column.id,
1177
+ sortable: false,
1178
+ render: ({ data }) => {
1179
+ if (
1180
+ data &&
1181
+ column.stageConfig &&
1182
+ column.stageConfig.key
1183
+ ) {
1184
+ // Check if the data has the specified key and it's an array
1185
+ const stageData = data[column.stageConfig.key];
1186
+
1187
+ if (
1188
+ stageData &&
1189
+ Array.isArray(stageData)
1190
+ ) {
1191
+ const stageCount = stageData.length;
1192
+
1193
+ // Create a clickable element if link is provided
1194
+ const handleClick = (e) => {
1195
+ e.stopPropagation();
1196
+ e.preventDefault();
1197
+
1198
+ if (
1199
+ column.link &&
1200
+ column.link.url &&
1201
+ column.link.key &&
1202
+ navigate
1203
+ ) {
1204
+ navigate(
1205
+ `${column.link.url}${data[column.link.key]}`
1206
+ );
1207
+ }
1208
+ };
1209
+
1210
+ // Create an array of squares based on the stage data
1211
+ const squares = [];
1212
+ for (let i = 0; i < stageCount; i++) {
1213
+ // Get the stage item
1214
+ const stageItem = stageData[i];
1215
+
1216
+ // Get the label value from the stage item using stageLabelKey if provided
1217
+ let labelValue = '';
1218
+ if (
1219
+ column.stageConfig.stageLabelKey &&
1220
+ stageItem &&
1221
+ stageItem[column.stageConfig.stageLabelKey] !== undefined
1222
+ ) {
1223
+ // Get the value from the stage item
1224
+ const rawValue = stageItem[column.stageConfig.stageLabelKey];
1225
+
1226
+ // If it's an object with a name property, use that
1227
+ if (
1228
+ rawValue &&
1229
+ typeof rawValue === 'object' &&
1230
+ rawValue.name
1231
+ ) {
1232
+ labelValue = rawValue.name.charAt(0);
1233
+ }
1234
+ // If it's a string, use the first character
1235
+ else if (
1236
+ typeof rawValue === 'string' &&
1237
+ rawValue.length > 0
1238
+ ) {
1239
+ labelValue = rawValue.charAt(0);
1240
+ }
1241
+ // Otherwise use the raw value if it can be converted to string
1242
+ else if (
1243
+ rawValue !== null &&
1244
+ rawValue !== undefined
1245
+ ) {
1246
+ labelValue = String(rawValue).charAt(0);
1247
+ }
1248
+ }
1249
+
1250
+ // Create tooltip content from the full value if available
1251
+ let tooltipContent = `Stage ${i + 1}`;
1252
+ if (
1253
+ column.stageConfig.stageLabelKey &&
1254
+ stageItem &&
1255
+ stageItem[column.stageConfig.stageLabelKey]
1256
+ ) {
1257
+ const rawValue = stageItem[column.stageConfig.stageLabelKey];
1258
+ if (
1259
+ rawValue &&
1260
+ typeof rawValue === 'object' &&
1261
+ rawValue.name
1262
+ ) {
1263
+ tooltipContent = `${rawValue.name} (Stage ${i + 1})`;
1264
+ } else if (
1265
+ rawValue !== null &&
1266
+ rawValue !== undefined
1267
+ ) {
1268
+ tooltipContent = `Stage ${i + 1})`;
1269
+ }
1270
+ }
1271
+
1272
+ // Determine the background color based on stageColour configuration
1273
+ let backgroundColor = 'var(--primary-color, #4a90e2)';
1274
+
1275
+ if (
1276
+ column.stageConfig.stageColour &&
1277
+ column.stageConfig.stageColour.key &&
1278
+ column.stageConfig.stageColour.options &&
1279
+ stageItem
1280
+ ) {
1281
+ const colourKey = column.stageConfig.stageColour.key;
1282
+ const colourOptions = column.stageConfig.stageColour.options;
1283
+
1284
+ // Get the value for the color key
1285
+ if (stageItem[colourKey] !== undefined) {
1286
+ const keyValue = stageItem[colourKey];
1287
+
1288
+ // Find matching option
1289
+ const matchingOption = colourOptions.find((option) => {
1290
+ // Handle both direct value and object with id
1291
+ if (
1292
+ typeof keyValue === 'object' &&
1293
+ keyValue !== null
1294
+ ) {
1295
+ // Use loose equality (==) to handle type coercion between string and number
1296
+ return keyValue.id == option.id;
1297
+ }
1298
+ // Use loose equality (==) to handle type coercion between string and number
1299
+ return keyValue == option.id;
1300
+ });
1301
+
1302
+ // Use the color from the matching option if found
1303
+ if (
1304
+ matchingOption &&
1305
+ matchingOption.colour
1306
+ ) {
1307
+ backgroundColor = matchingOption.colour;
1308
+ }
1309
+ }
1310
+ }
1311
+
1312
+ squares.push(
1313
+ <div
1314
+ key={i}
1315
+ style={{
1316
+ width: '20px',
1317
+ height: '20px',
1318
+ backgroundColor: backgroundColor,
1319
+ borderRadius: '3px',
1320
+ margin: '0 2px',
1321
+ display: 'flex',
1322
+ justifyContent: 'center',
1323
+ alignItems: 'center',
1324
+ color: 'white',
1325
+ fontSize: '11px',
1326
+ fontWeight: 'bold',
1327
+ }}
1328
+ data-tooltip-id="system-tooltip"
1329
+ data-tooltip-content={tooltipContent}
1330
+ >
1331
+ {labelValue}
1332
+ </div>
1333
+ );
1334
+ }
1335
+
1336
+ return (
1337
+ <div
1338
+ onClick={handleClick}
1339
+ style={{
1340
+ display: 'flex',
1341
+ flexWrap: 'wrap',
1342
+ justifyContent: 'center',
1343
+ alignItems: 'center',
1344
+ cursor: column.link ? 'pointer' : 'default',
1345
+ gap: '4px',
1346
+ padding: '2px 0',
1347
+ maxWidth: '100%',
1348
+ }}
1349
+ >
1350
+ {squares}
1351
+ </div>
1352
+ );
1353
+ }
1354
+ }
1355
+ return null;
1356
+ },
1357
+ };
1358
+ };