@visns-studio/visns-components 1.6.1 → 1.6.3

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