@visns-studio/visns-components 1.5.7 → 1.5.9

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