@visns-studio/visns-components 1.1.19 → 1.2.1

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.
@@ -9,14 +9,14 @@ import ReactDataGrid from "@inovua/reactdatagrid-community";
9
9
  import { confirmAlert } from "react-confirm-alert";
10
10
  import { toast } from "react-toastify";
11
11
  import {
12
- Backspace,
13
- Check,
14
- Clock,
15
- CloudDownload,
16
- Copy,
17
- Edit,
18
- Ribbon,
19
- Search,
12
+ Backspace,
13
+ Check,
14
+ Clock,
15
+ CloudDownload,
16
+ Copy,
17
+ Edit,
18
+ Ribbon,
19
+ Search,
20
20
  } from "akar-icons";
21
21
 
22
22
  import "@inovua/reactdatagrid-community/index.css";
@@ -26,794 +26,825 @@ import CustomFetch from "./visns-fetch";
26
26
  import Form from "./visns-form";
27
27
 
28
28
  const loadData = async (
29
- { skip, limit, sortInfo, currentData, filterValue, groupBy },
30
- search,
31
- ajaxSetting
29
+ { skip, limit, sortInfo, currentData, filterValue, groupBy },
30
+ search,
31
+ ajaxSetting
32
32
  ) => {
33
- const url = ajaxSetting.url;
34
-
35
- let page = Math.floor(skip / limit) + 1;
36
- let params = { page, sortInfo, take: limit, search: search };
37
-
38
- if (sortInfo) {
39
- params.sortBy = sortInfo.name;
40
- params.sort = sortInfo.dir === 1 ? "asc" : "desc";
41
- }
42
-
43
- if (ajaxSetting && ajaxSetting.where) {
44
- params.where = ajaxSetting.where;
45
- }
46
-
47
- try {
48
- const response = await axios.post(url, params);
49
- const { data, total } = response.data;
50
- return { data, count: total };
51
- } catch (error) {
52
- console.error("Error fetching data:", error);
53
- return { data: [], count: 0 };
54
- }
33
+ const url = ajaxSetting.url;
34
+
35
+ let page = Math.floor(skip / limit) + 1;
36
+ let params = { page, sortInfo, take: limit, search: search };
37
+
38
+ if (sortInfo) {
39
+ params.sortBy = sortInfo.name;
40
+ params.sort = sortInfo.dir === 1 ? "asc" : "desc";
41
+ }
42
+
43
+ if (ajaxSetting && ajaxSetting.where) {
44
+ params.where = ajaxSetting.where;
45
+ }
46
+
47
+ try {
48
+ const response = await axios.post(url, params);
49
+ const { data, total } = response.data;
50
+ return { data, count: total };
51
+ } catch (error) {
52
+ console.error("Error fetching data:", error);
53
+ return { data: [], count: 0 };
54
+ }
55
55
  };
56
56
 
57
57
  const DataGrid = (props) => {
58
- const {
59
- ajaxSetting,
60
- form,
61
- columns,
62
- settings,
63
- style,
64
- tableFilters,
65
- historySearch,
66
- historyUrl,
67
- } = props;
68
- const navigate = useNavigate();
69
-
70
- /** Modal States */
71
- const [formData, setFormData] = useState(form);
72
- const [formType, setFormType] = useState("");
73
- const [formId, setFormId] = useState(0);
74
- const [modalShow, setModalShow] = useState(false);
75
-
76
- /** Modal Functions */
77
- const childDropdownCallback = (data) => {
78
- let _form = { ...formData };
79
-
80
- if (formData.fields.length > 0) {
81
- formData.fields.forEach((a, b) => {
82
- if (a.id === data.id) {
83
- _form.fields[b].options = data.data;
84
- }
85
- });
86
- }
87
-
88
- setFormData(_form);
89
- };
90
-
91
- const modalOpen = (formType, formId) => {
92
- setModalShow(true);
93
- setFormType(formType);
94
- setFormId(formId);
95
- };
96
-
97
- const modalClose = () => {
98
- setModalShow(false);
99
- };
100
-
101
- /** Table States */
102
- const [dataReload, setDataReload] = useState(0);
103
- const [dSearch, setDSearch] = useState("");
104
- const data = useCallback(
105
- (params) => loadData(params, dSearch, ajaxSetting),
106
- [ajaxSetting, dataReload, dSearch]
107
- );
108
- const [gridColumns, setGridColumns] = useState([]);
109
- const limit = ajaxSetting.take || 10;
110
- const [search, setSearch] = useState("");
111
- const sortInfo =
112
- ajaxSetting.sortBy && ajaxSetting.sort
113
- ? {
114
- name: ajaxSetting.sortBy,
115
- dir: ajaxSetting.sort === "asc" ? 1 : -1,
116
- }
117
- : null;
118
-
119
- /** Table Functions */
120
- const handleChangeSearch = (e) => {
121
- const { value } = e.target;
122
- setSearch(value);
123
- };
124
-
125
- const handleSettingClick = (s, d) => {
126
- switch (s.id) {
127
- case "update":
128
- modalOpen("update", d.id);
129
- break;
130
- case "delete":
131
- confirmAlert({
132
- title: "Delete Item",
133
- message: `Are you sure you want to delete ${
134
- d.name ? d.name : d.label ? d.label : "this item"
135
- }?`,
136
- buttons: [
137
- {
138
- label: "Yes",
139
- onClick: () =>
140
- CustomFetch(
141
- form.url + "/" + d[form.primaryKey],
142
- "DELETE",
143
- {},
144
- (result) => {
145
- if (result.error === "") {
146
- const min = 0;
147
- const max = 999999999;
148
- const randomNumber = Math.floor(
149
- Math.random() *
150
- (max - min + 1) +
151
- min
152
- );
153
-
154
- setDataReload(randomNumber);
155
-
156
- toast.success(
157
- `The selected item has been deleted.`
158
- );
159
- } else {
160
- toast.error(String(result.error));
161
- }
162
- }
163
- ),
164
- },
165
- {
166
- label: "No",
167
- onClick: () => close(),
168
- },
169
- ],
170
- });
171
- break;
172
- default:
173
- confirmAlert({
174
- title: s.title,
175
- message: "",
176
- buttons: [
177
- {
178
- label: "Yes",
179
- onClick: () =>
180
- CustomFetch(
181
- s.url + "/" + d[form.primaryKey],
182
- s.method,
183
- {},
184
- function (result) {
185
- if (result.error === "") {
186
- const min = 0;
187
- const max = 999999999;
188
- const randomNumber = Math.floor(
189
- Math.random() *
190
- (max - min + 1) +
191
- min
192
- );
193
-
194
- setDataReload(randomNumber);
195
-
196
- toast.success(String(s.title));
197
- } else {
198
- toast.error(String(result.error));
199
- }
200
- }
201
- ),
202
- },
203
- {
204
- label: "No",
205
- onClick: () => close(),
206
- },
207
- ],
208
- });
209
- break;
210
- }
211
- };
212
-
213
- const handleReload = () => {
214
- setDataReload(dataReload + 1);
215
- };
216
-
217
- const onRowClick = useCallback((rowProps, event) => {
218
- const cellElement = event.target.closest(".InovuaReactDataGrid__cell");
219
- const columnIndex = Array.from(cellElement.parentNode.children).indexOf(
220
- cellElement
221
- );
222
-
223
- if (columnIndex === rowProps.columns.length - 1) {
224
- } else {
225
- if (
226
- rowProps.columns[columnIndex].link &&
227
- rowProps.columns[columnIndex].link.url
228
- ) {
229
- if (rowProps.columns[columnIndex].link.folder) {
230
- window.location.href =
231
- rowProps.columns[columnIndex].link.url +
232
- rowProps.data[rowProps.columns[columnIndex].link.key] +
233
- "/" +
234
- rowProps.columns[columnIndex].link.folder;
235
- } else {
236
- navigate(
237
- `${rowProps.columns[columnIndex].link.url}${
238
- rowProps.data[
239
- rowProps.columns[columnIndex].link.key
240
- ]
241
- }`
242
- );
243
- }
244
- } else {
245
- modalOpen("update", rowProps.data[form.primaryKey]);
246
- }
247
- }
248
- }, []);
249
-
250
- const onRenderRow = useCallback((rowProps) => {
251
- // save the original handlers to be called later
252
- const { onClick } = rowProps;
253
-
254
- rowProps.onClick = (event) => {
255
- onRowClick(rowProps, event);
256
- if (onClick) {
257
- onClick(event);
258
- }
259
- };
260
- }, []);
261
-
262
- /** Table Hooks */
263
- useEffect(() => {
264
- const delayedSetDSearch = debounce(() => {
265
- setDSearch(search);
266
- }, 300); // Adjust the delay (in milliseconds) according to your needs
267
-
268
- delayedSetDSearch();
269
-
270
- return delayedSetDSearch.cancel; // Cleanup the debounce timer on unmount
271
- }, [search, setDSearch]);
272
-
273
- const renderSetting = (s, d) => {
274
- let iconStyle = {};
275
-
276
- if (s.active) {
277
- if (
278
- d.hasOwnProperty(s.active.id) &&
279
- !s.active.hasOwnProperty("type") &&
280
- d[s.active.id] === s.active.value
281
- ) {
282
- iconStyle = {
283
- color: s.active.colour,
284
- };
285
- } else if (s.active.hasOwnProperty("type")) {
286
- switch (s.active.type) {
287
- case "greater":
288
- if (d[s.active.id] > s.active.value) {
289
- iconStyle = {
290
- color: s.active.colour,
291
- };
292
- }
293
- break;
294
- case "not null":
295
- if (d[s.active.id] !== null) {
296
- iconStyle = {
297
- color: s.active.colour,
298
- };
299
- }
300
- break;
301
- default:
302
- break;
303
- }
304
- }
305
- }
306
-
307
- const getIconComponent = (IconComponent) => (
308
- <IconComponent
309
- data-tooltip-id="system-tooltip"
310
- data-tooltip-content={
311
- iconStyle && iconStyle.color ? s.active.title : s.title
312
- }
313
- strokeWidth={2}
314
- size={18}
315
- className="tdaction"
316
- style={iconStyle}
317
- />
318
- );
319
-
320
- switch (s.id) {
321
- case "complete":
322
- return getIconComponent(Check);
323
- case "copy":
324
- return getIconComponent(Copy);
325
- case "delete":
326
- return getIconComponent(Backspace);
327
- case "primary":
328
- return getIconComponent(Ribbon);
329
- case "ssa":
330
- return getIconComponent(Clock);
331
- case "update":
332
- return getIconComponent(Edit);
333
- default:
334
- return null;
335
- }
336
- };
337
-
338
- const renderColumnContextMenu = useCallback((menuProps, { cellProps }) => {
339
- const filteredItems = menuProps.items
340
- .map((item, key) => {
341
- if (key >= 6 && key <= 9) {
342
- return null; // Skip items with keys 6-9 [lock columns setting]
343
- }
344
- return item;
345
- })
346
- .filter(Boolean); // Filter out any null items
347
-
348
- menuProps.items = filteredItems;
349
- }, []);
350
-
351
- useEffect(() => {
352
- const renderColumn = (column) => {
353
- switch (column.type) {
354
- case "created_by":
355
- return {
356
- name: "audits.name",
357
- header: column.label,
358
- defaultFlex: 1,
359
- link: column.link ? column.link : {},
360
- minWidth:
361
- column.width && column.width > 0
362
- ? column.width
363
- : undefined,
364
- render: ({ data }) => {
365
- if (
366
- data.audits[0] &&
367
- data.audits[0].user &&
368
- data.audits[0].user.name
369
- ) {
370
- let value = data.audits[0].user.name;
371
-
372
- return <span>{value}</span>;
373
- } else {
374
- return null;
375
- }
376
- },
377
- };
378
- case "currency":
379
- return {
380
- name: column.id,
381
- header: column.label,
382
- defaultFlex: 1,
383
- minWidth:
384
- column.width && column.width > 0
385
- ? column.width
386
- : undefined,
387
- link: column.link ? column.link : {},
388
- render: ({ data }) => {
389
- if (data) {
390
- data = numeral(data[column.id]).format(
391
- "$0,0.00"
392
- );
393
-
394
- return <span>{data}</span>;
395
- } else {
396
- return null;
397
- }
398
- },
399
- };
400
- case "date":
401
- return {
402
- name: column.id,
403
- header: column.label,
404
- defaultFlex: 1,
405
- minWidth:
406
- column.width && column.width > 0
407
- ? column.width
408
- : undefined,
409
- link: column.link ? column.link : {},
410
- render: ({ data }) => {
411
- if (data && data[column.id]) {
412
- data = moment(data[column.id]).format(
413
- column.format ? column.format : "DD-MM-YYYY"
414
- );
415
-
416
- return <span>{data}</span>;
417
- } else {
418
- return null;
419
- }
420
- },
421
- };
422
- case "datetime":
423
- return {
424
- name: column.id,
425
- header: column.label,
426
- defaultFlex: 1,
427
- minWidth:
428
- column.width && column.width > 0
429
- ? column.width
430
- : undefined,
431
- link: column.link ? column.link : {},
432
- render: ({ data }) => {
433
- if (data && data[column.id]) {
434
- data = moment(data[column.id]).format(
435
- column.format
436
- ? column.format
437
- : "DD-MM-YYYY hh:mm A"
438
- );
439
-
440
- return <span>{data}</span>;
441
- } else {
442
- return null;
443
- }
444
- },
445
- };
446
- case "file":
447
- return {
448
- name: column.id,
449
- header: column.label,
450
- defaultFlex: 1,
451
- minWidth:
452
- column.width && column.width > 0
453
- ? column.width
454
- : undefined,
455
- link: column.link ? column.link : {},
456
- render: ({ data }) => {
457
- if (data) {
458
- return (
459
- <span>
460
- <CloudDownload
461
- data-tooltip-id="system-tooltip"
462
- data-tooltip-content="Download File"
463
- strokeWidth={2}
464
- size={18}
465
- className="tdaction"
466
- />
467
- </span>
468
- );
469
- } else {
470
- return null;
471
- }
472
- },
473
- };
474
- case "icons":
475
- return {
476
- name: column.id,
477
- header: column.label,
478
- defaultFlex: 1,
479
- minWidth:
480
- column.width && column.width > 0
481
- ? column.width
482
- : undefined,
483
- link: column.link ? column.link : {},
484
- render: ({ data }) => {
485
- let icons = [];
486
-
487
- if (column.icons && column.icons.length > 0) {
488
- column.icons.forEach((icon) => {
489
- icons.push(
490
- <img
491
- src={icon.url}
492
- alt={icon.name}
493
- data-tooltip-id="system-tooltip"
494
- data-tooltip-content={icon.name}
495
- />
496
- );
497
- });
498
- }
499
-
500
- return <span>{icons}</span>;
501
- },
502
- };
503
- case "json":
504
- return {
505
- name: column.id,
506
- header: column.label,
507
- defaultFlex: 1,
508
- minWidth:
509
- column.width && column.width > 0
510
- ? column.width
511
- : undefined,
512
- link: column.link ? column.link : {},
513
- render: ({ data }) => {
514
- if (
515
- data &&
516
- data[column.id] &&
517
- data[column.id][column.jsonData]
518
- ) {
519
- data = data[column.id][column.jsonData];
520
-
521
- return <span>{data}</span>;
522
- } else {
523
- return null;
524
- }
525
- },
526
- };
527
- case "number":
528
- return {
529
- name: column.id,
530
- header: column.label,
531
- defaultFlex: 1,
532
- minWidth:
533
- column.width && column.width > 0
534
- ? column.width
535
- : undefined,
536
- link: column.link ? column.link : {},
537
- type: "number",
538
- };
539
- case "option":
540
- return {
541
- name: column.id,
542
- header: column.label,
543
- defaultFlex: 1,
544
- minWidth:
545
- column.width && column.width > 0
546
- ? column.width
547
- : undefined,
548
- link: column.link ? column.link : {},
549
- render: ({ data }) => {
550
- if (
551
- data &&
552
- column.options &&
553
- column.options[data[column.id]]
554
- ) {
555
- data = column.options[data[column.id]];
556
-
557
- return <span>{data}</span>;
558
- } else {
559
- return null;
560
- }
561
- },
562
- };
563
- case "placeholder":
564
- return {
565
- name: column.id,
566
- header: column.label,
567
- defaultFlex: 1,
568
- minWidth:
569
- column.width && column.width > 0
570
- ? column.width
571
- : undefined,
572
- link: column.link ? column.link : {},
573
- render: ({ data }) => {
574
- return <span>{column.placeholder}</span>;
575
- },
576
- };
577
- case "relation":
578
- let relationName = column.id.reduce(
579
- (acc, id) => acc + `${id}.`,
580
- ""
581
- );
582
- relationName += column.nameFrom;
583
-
584
- return {
585
- name: relationName,
586
- header: column.label,
587
- defaultFlex: 1,
588
- minWidth:
589
- column.width && column.width > 0
590
- ? column.width
591
- : undefined,
592
- link: column.link ? column.link : {},
593
- render: ({ data }) => {
594
- let value = column.id.reduce((acc, id) => {
595
- if (acc === "") {
596
- return data[id];
597
- } else {
598
- return acc[id];
599
- }
600
- }, "");
601
-
602
- if (value) {
603
- if (Array.isArray(column.nameFrom)) {
604
- value = column.nameFrom
605
- .map((nf) => value[nf])
606
- .join(" ");
607
- } else {
608
- value = value[column.nameFrom];
609
- }
610
-
611
- if (
612
- column.placeholder &&
613
- column.placeholder !== ""
614
- ) {
615
- return <span>{column.placeholder}</span>;
616
- } else {
617
- return <span>{value}</span>;
618
- }
619
- } else {
620
- return null;
621
- }
622
- },
623
- };
624
- case "stage":
625
- return {
626
- name: column.id,
627
- header: column.label,
628
- defaultFlex: 1,
629
- minWidth:
630
- column.width && column.width > 0
631
- ? column.width
632
- : undefined,
633
- link: column.link ? column.link : {},
634
- render: ({ data }) => {
635
- let stage = "";
636
-
637
- column.keyIds.forEach((a, b) => {
638
- if (data[a] === 1) {
639
- stage = column.valueIds[b];
640
- }
641
- });
642
-
643
- return <span>{stage}</span>;
644
- },
645
- };
646
- case "time":
647
- return {
648
- name: column.id,
649
- header: column.label,
650
- defaultFlex: 1,
651
- minWidth:
652
- column.width && column.width > 0
653
- ? column.width
654
- : undefined,
655
- link: column.link ? column.link : {},
656
- render: ({ data }) => {
657
- if (data && data[column.id]) {
658
- data = moment(data[column.id]).format(
659
- column.format ? column.format : "hh:mm A"
660
- );
661
-
662
- return <span>{data}</span>;
663
- } else {
664
- return null;
665
- }
666
- },
667
- };
668
- case "url":
669
- return {
670
- name: column.id,
671
- header: column.label,
672
- defaultFlex: 1,
673
- minWidth:
674
- column.width && column.width > 0
675
- ? column.width
676
- : undefined,
677
- link: column.link ? column.link : {},
678
- render: ({ data }) => {
679
- if (data) {
680
- return (
681
- <span>
682
- <a href={data} target="_blank">
683
- Link
684
- </a>
685
- </span>
686
- );
687
- } else {
688
- return null;
689
- }
690
- },
691
- };
692
- default:
693
- return {
694
- name: column.id,
695
- header: column.label,
696
- defaultFlex: 1,
697
- minWidth:
698
- column.width && column.width > 0
699
- ? column.width
700
- : undefined,
701
- link: column.link ? column.link : {},
702
- };
703
- }
704
- };
705
-
706
- const newColumns = columns.map(renderColumn);
707
-
708
- if (settings.length > 0) {
709
- newColumns.push({
710
- name: "setting",
711
- header: "Action",
712
- defaultWidth: 100,
713
- textAlign: "center",
714
- render: ({ data }) => {
715
- return (
716
- <div className="tdactions">
717
- {settings.map((setting) => (
718
- <span
719
- key={`setting-${setting.id}`}
720
- onClick={(e) => {
721
- e.preventDefault();
722
- e.stopPropagation();
723
- handleSettingClick(setting, data);
724
- }}
725
- >
726
- {renderSetting(setting, data)}
727
- </span>
728
- ))}
729
- </div>
730
- );
731
- },
732
- });
733
- }
734
-
735
- setGridColumns(newColumns);
736
- }, [columns]);
737
-
738
- // Data Grid Styling
739
- const [windowHeight, setWindowHeight] = useState(window.innerHeight);
740
- const gridStyle = {
741
- minHeight: windowHeight * 0.6 > 550 ? windowHeight * 0.6 : 550,
742
- };
743
- const headerProps = {
744
- style: style ? style : {},
745
- };
746
-
747
- useEffect(() => {
748
- const handleResize = () => {
749
- setWindowHeight(window.innerHeight);
750
- };
751
-
752
- window.addEventListener("resize", handleResize);
753
-
754
- // Clean up the event listener
755
- return () => {
756
- window.removeEventListener("resize", handleResize);
757
- };
758
- }, []);
759
-
760
- return (
761
- <>
762
- <div className="filterInput--alt">
763
- <div className="icon-search">
764
- <Search strokeWidth={2} size={18} />
765
- </div>
766
- <input
767
- type="text"
768
- placeholder="Search"
769
- value={search}
770
- onChange={handleChangeSearch}
771
- />
772
- </div>
773
- <div className="filterAction--alt">
774
- <button
775
- className="btn"
776
- onClick={() => {
777
- modalOpen("create", 0);
778
- }}
779
- >
780
- <div className="icon-plus"></div> Create
781
- </button>
782
- </div>
783
- <ReactDataGrid
784
- columns={gridColumns}
785
- dataSource={data}
786
- defaultLimit={limit}
787
- defaultSortInfo={sortInfo}
788
- enableColumnAutosize={false}
789
- headerProps={headerProps}
790
- idProperty={`visns-datagrid-${ajaxSetting.url.replace(
791
- /\//g,
792
- "-"
793
- )}`}
794
- style={gridStyle}
795
- onRenderRow={onRenderRow}
796
- pagination
797
- renderColumnContextMenu={renderColumnContextMenu}
798
- showZebraRows={true}
799
- />
800
- <Popup
801
- open={modalShow}
802
- onClose={modalClose}
803
- closeOnDocumentClick={false}
804
- >
805
- <Form
806
- closeModal={modalClose}
807
- fetchTable={handleReload}
808
- formSettings={formData}
809
- formType={formType}
810
- updateForm={setFormData}
811
- columnId={formId}
812
- childDropdownCallback={childDropdownCallback}
813
- />
814
- </Popup>
815
- </>
816
- );
58
+ const {
59
+ ajaxSetting,
60
+ form,
61
+ columns,
62
+ settings,
63
+ style,
64
+ tableFilters,
65
+ historySearch,
66
+ historyUrl,
67
+ } = props;
68
+ const navigate = useNavigate();
69
+
70
+ /** Modal States */
71
+ const [formData, setFormData] = useState(form);
72
+ const [formType, setFormType] = useState("");
73
+ const [formId, setFormId] = useState(0);
74
+ const [modalShow, setModalShow] = useState(false);
75
+
76
+ /** Modal Functions */
77
+ const childDropdownCallback = (data) => {
78
+ let _form = { ...formData };
79
+
80
+ if (formData.fields.length > 0) {
81
+ formData.fields.forEach((a, b) => {
82
+ if (a.id === data.id) {
83
+ _form.fields[b].options = data.data;
84
+ }
85
+ });
86
+ }
87
+
88
+ setFormData(_form);
89
+ };
90
+
91
+ const modalOpen = (formType, formId) => {
92
+ setModalShow(true);
93
+ setFormType(formType);
94
+ setFormId(formId);
95
+ };
96
+
97
+ const modalClose = () => {
98
+ setModalShow(false);
99
+ };
100
+
101
+ /** Table States */
102
+ const [dataReload, setDataReload] = useState(0);
103
+ const [dSearch, setDSearch] = useState("");
104
+ const data = useCallback(
105
+ (params) => loadData(params, dSearch, ajaxSetting),
106
+ [ajaxSetting, dataReload, dSearch]
107
+ );
108
+ const [gridColumns, setGridColumns] = useState([]);
109
+ const limit = ajaxSetting.take || 10;
110
+ const [search, setSearch] = useState("");
111
+ const sortInfo =
112
+ ajaxSetting.sortBy && ajaxSetting.sort
113
+ ? {
114
+ name: ajaxSetting.sortBy,
115
+ dir: ajaxSetting.sort === "asc" ? 1 : -1,
116
+ }
117
+ : null;
118
+
119
+ /** Table Functions */
120
+ const handleChangeSearch = (e) => {
121
+ const { value } = e.target;
122
+ setSearch(value);
123
+ };
124
+
125
+ const handleSettingClick = (s, d) => {
126
+ switch (s.id) {
127
+ case "update":
128
+ modalOpen("update", d.id);
129
+ break;
130
+ case "delete":
131
+ confirmAlert({
132
+ title: "Delete Item",
133
+ message: `Are you sure you want to delete ${
134
+ d.name ? d.name : d.label ? d.label : "this item"
135
+ }?`,
136
+ buttons: [
137
+ {
138
+ label: "Yes",
139
+ onClick: () =>
140
+ CustomFetch(
141
+ form.url + "/" + d[form.primaryKey],
142
+ "DELETE",
143
+ {},
144
+ (result) => {
145
+ if (result.error === "") {
146
+ const min = 0;
147
+ const max = 999999999;
148
+ const randomNumber = Math.floor(
149
+ Math.random() *
150
+ (max - min + 1) +
151
+ min
152
+ );
153
+
154
+ setDataReload(randomNumber);
155
+
156
+ toast.success(
157
+ `The selected item has been deleted.`
158
+ );
159
+ } else {
160
+ toast.error(String(result.error));
161
+ }
162
+ }
163
+ ),
164
+ },
165
+ {
166
+ label: "No",
167
+ onClick: () => close(),
168
+ },
169
+ ],
170
+ });
171
+ break;
172
+ default:
173
+ confirmAlert({
174
+ title: s.title,
175
+ message: "",
176
+ buttons: [
177
+ {
178
+ label: "Yes",
179
+ onClick: () =>
180
+ CustomFetch(
181
+ s.url + "/" + d[form.primaryKey],
182
+ s.method,
183
+ {},
184
+ function (result) {
185
+ if (result.error === "") {
186
+ const min = 0;
187
+ const max = 999999999;
188
+ const randomNumber = Math.floor(
189
+ Math.random() *
190
+ (max - min + 1) +
191
+ min
192
+ );
193
+
194
+ setDataReload(randomNumber);
195
+
196
+ toast.success(String(s.title));
197
+ } else {
198
+ toast.error(String(result.error));
199
+ }
200
+ }
201
+ ),
202
+ },
203
+ {
204
+ label: "No",
205
+ onClick: () => close(),
206
+ },
207
+ ],
208
+ });
209
+ break;
210
+ }
211
+ };
212
+
213
+ const handleReload = () => {
214
+ setDataReload(dataReload + 1);
215
+ };
216
+
217
+ const onRowClick = useCallback((rowProps, event) => {
218
+ const cellElement = event.target.closest(".InovuaReactDataGrid__cell");
219
+ const columnIndex = Array.from(cellElement.parentNode.children).indexOf(
220
+ cellElement
221
+ );
222
+
223
+ if (columnIndex === rowProps.columns.length - 1) {
224
+ } else {
225
+ if (
226
+ rowProps.columns[columnIndex].link &&
227
+ rowProps.columns[columnIndex].link.url
228
+ ) {
229
+ if (rowProps.columns[columnIndex].link.folder) {
230
+ window.location.href =
231
+ rowProps.columns[columnIndex].link.url +
232
+ rowProps.data[rowProps.columns[columnIndex].link.key] +
233
+ "/" +
234
+ rowProps.columns[columnIndex].link.folder;
235
+ } else {
236
+ navigate(
237
+ `${rowProps.columns[columnIndex].link.url}${
238
+ rowProps.data[
239
+ rowProps.columns[columnIndex].link.key
240
+ ]
241
+ }`
242
+ );
243
+ }
244
+ } else {
245
+ modalOpen("update", rowProps.data[form.primaryKey]);
246
+ }
247
+ }
248
+ }, []);
249
+
250
+ const onRenderRow = useCallback((rowProps) => {
251
+ // save the original handlers to be called later
252
+ const { onClick } = rowProps;
253
+
254
+ rowProps.onClick = (event) => {
255
+ onRowClick(rowProps, event);
256
+ if (onClick) {
257
+ onClick(event);
258
+ }
259
+ };
260
+ }, []);
261
+
262
+ /** Table Hooks */
263
+ useEffect(() => {
264
+ const delayedSetDSearch = debounce(() => {
265
+ setDSearch(search);
266
+ }, 300); // Adjust the delay (in milliseconds) according to your needs
267
+
268
+ delayedSetDSearch();
269
+
270
+ return delayedSetDSearch.cancel; // Cleanup the debounce timer on unmount
271
+ }, [search, setDSearch]);
272
+
273
+ const renderSetting = (s, d) => {
274
+ let iconStyle = {};
275
+
276
+ if (s.active) {
277
+ if (
278
+ d.hasOwnProperty(s.active.id) &&
279
+ !s.active.hasOwnProperty("type") &&
280
+ d[s.active.id] === s.active.value
281
+ ) {
282
+ iconStyle = {
283
+ color: s.active.colour,
284
+ };
285
+ } else if (s.active.hasOwnProperty("type")) {
286
+ switch (s.active.type) {
287
+ case "greater":
288
+ if (d[s.active.id] > s.active.value) {
289
+ iconStyle = {
290
+ color: s.active.colour,
291
+ };
292
+ }
293
+ break;
294
+ case "not null":
295
+ if (d[s.active.id] !== null) {
296
+ iconStyle = {
297
+ color: s.active.colour,
298
+ };
299
+ }
300
+ break;
301
+ default:
302
+ break;
303
+ }
304
+ }
305
+ }
306
+
307
+ const getIconComponent = (IconComponent) => (
308
+ <IconComponent
309
+ data-tooltip-id="system-tooltip"
310
+ data-tooltip-content={
311
+ iconStyle && iconStyle.color ? s.active.title : s.title
312
+ }
313
+ strokeWidth={2}
314
+ size={18}
315
+ className="tdaction"
316
+ style={iconStyle}
317
+ />
318
+ );
319
+
320
+ switch (s.id) {
321
+ case "complete":
322
+ return getIconComponent(Check);
323
+ case "copy":
324
+ return getIconComponent(Copy);
325
+ case "delete":
326
+ return getIconComponent(Backspace);
327
+ case "primary":
328
+ return getIconComponent(Ribbon);
329
+ case "ssa":
330
+ return getIconComponent(Clock);
331
+ case "update":
332
+ return getIconComponent(Edit);
333
+ default:
334
+ return null;
335
+ }
336
+ };
337
+
338
+ const renderColumnContextMenu = useCallback((menuProps, { cellProps }) => {
339
+ const filteredItems = menuProps.items
340
+ .map((item, key) => {
341
+ if (key >= 6 && key <= 9) {
342
+ return null; // Skip items with keys 6-9 [lock columns setting]
343
+ }
344
+ return item;
345
+ })
346
+ .filter(Boolean); // Filter out any null items
347
+
348
+ menuProps.items = filteredItems;
349
+ }, []);
350
+
351
+ useEffect(() => {
352
+ const renderColumn = (column) => {
353
+ switch (column.type) {
354
+ case "created_by":
355
+ return {
356
+ name: "audits.name",
357
+ header: column.label,
358
+ defaultFlex: 1,
359
+ link: column.link ? column.link : {},
360
+ minWidth:
361
+ column.width && column.width > 0
362
+ ? column.width
363
+ : undefined,
364
+ render: ({ data }) => {
365
+ if (
366
+ data.audits[0] &&
367
+ data.audits[0].user &&
368
+ data.audits[0].user.name
369
+ ) {
370
+ let value = data.audits[0].user.name;
371
+
372
+ return <span>{value}</span>;
373
+ } else {
374
+ return null;
375
+ }
376
+ },
377
+ };
378
+ case "currency":
379
+ return {
380
+ name: column.id,
381
+ header: column.label,
382
+ defaultFlex: 1,
383
+ minWidth:
384
+ column.width && column.width > 0
385
+ ? column.width
386
+ : undefined,
387
+ link: column.link ? column.link : {},
388
+ render: ({ data }) => {
389
+ if (data) {
390
+ data = numeral(data[column.id]).format(
391
+ "$0,0.00"
392
+ );
393
+
394
+ return <span>{data}</span>;
395
+ } else {
396
+ return null;
397
+ }
398
+ },
399
+ };
400
+ case "date":
401
+ return {
402
+ name: column.id,
403
+ header: column.label,
404
+ defaultFlex: 1,
405
+ minWidth:
406
+ column.width && column.width > 0
407
+ ? column.width
408
+ : undefined,
409
+ link: column.link ? column.link : {},
410
+ render: ({ data }) => {
411
+ if (data && data[column.id]) {
412
+ data = moment(data[column.id]).format(
413
+ column.format ? column.format : "DD-MM-YYYY"
414
+ );
415
+
416
+ return <span>{data}</span>;
417
+ } else {
418
+ return null;
419
+ }
420
+ },
421
+ };
422
+ case "datetime":
423
+ return {
424
+ name: column.id,
425
+ header: column.label,
426
+ defaultFlex: 1,
427
+ minWidth:
428
+ column.width && column.width > 0
429
+ ? column.width
430
+ : undefined,
431
+ link: column.link ? column.link : {},
432
+ render: ({ data }) => {
433
+ if (data && data[column.id]) {
434
+ data = moment(data[column.id]).format(
435
+ column.format
436
+ ? column.format
437
+ : "DD-MM-YYYY hh:mm A"
438
+ );
439
+
440
+ return <span>{data}</span>;
441
+ } else {
442
+ return null;
443
+ }
444
+ },
445
+ };
446
+ case "file":
447
+ return {
448
+ name: column.id,
449
+ header: column.label,
450
+ defaultFlex: 1,
451
+ minWidth:
452
+ column.width && column.width > 0
453
+ ? column.width
454
+ : undefined,
455
+ link: column.link ? column.link : {},
456
+ render: ({ data }) => {
457
+ if (data) {
458
+ return (
459
+ <span>
460
+ <CloudDownload
461
+ data-tooltip-id="system-tooltip"
462
+ data-tooltip-content="Download File"
463
+ strokeWidth={2}
464
+ size={18}
465
+ className="tdaction"
466
+ />
467
+ </span>
468
+ );
469
+ } else {
470
+ return null;
471
+ }
472
+ },
473
+ };
474
+ case "icons":
475
+ return {
476
+ name: column.id,
477
+ header: column.label,
478
+ defaultFlex: 1,
479
+ minWidth:
480
+ column.width && column.width > 0
481
+ ? column.width
482
+ : undefined,
483
+ link: column.link ? column.link : {},
484
+ render: ({ data }) => {
485
+ let icons = [];
486
+
487
+ if (column.icons && column.icons.length > 0) {
488
+ column.icons.forEach((icon) => {
489
+ icons.push(
490
+ <img
491
+ src={icon.url}
492
+ alt={icon.name}
493
+ data-tooltip-id="system-tooltip"
494
+ data-tooltip-content={icon.name}
495
+ />
496
+ );
497
+ });
498
+ }
499
+
500
+ return <span>{icons}</span>;
501
+ },
502
+ };
503
+ case "json":
504
+ return {
505
+ name: column.id,
506
+ header: column.label,
507
+ defaultFlex: 1,
508
+ minWidth:
509
+ column.width && column.width > 0
510
+ ? column.width
511
+ : undefined,
512
+ link: column.link ? column.link : {},
513
+ render: ({ data }) => {
514
+ if (
515
+ data &&
516
+ data[column.id] &&
517
+ data[column.id][column.jsonData]
518
+ ) {
519
+ data = data[column.id][column.jsonData];
520
+
521
+ return <span>{data}</span>;
522
+ } else {
523
+ return null;
524
+ }
525
+ },
526
+ };
527
+ case "number":
528
+ return {
529
+ name: column.id,
530
+ header: column.label,
531
+ defaultFlex: 1,
532
+ minWidth:
533
+ column.width && column.width > 0
534
+ ? column.width
535
+ : undefined,
536
+ link: column.link ? column.link : {},
537
+ type: "number",
538
+ };
539
+ case "option":
540
+ return {
541
+ name: column.id,
542
+ header: column.label,
543
+ defaultFlex: 1,
544
+ minWidth:
545
+ column.width && column.width > 0
546
+ ? column.width
547
+ : undefined,
548
+ link: column.link ? column.link : {},
549
+ render: ({ data }) => {
550
+ if (
551
+ data &&
552
+ column.options &&
553
+ column.options[data[column.id]]
554
+ ) {
555
+ data = column.options[data[column.id]];
556
+
557
+ return <span>{data}</span>;
558
+ } else {
559
+ return null;
560
+ }
561
+ },
562
+ };
563
+ case "placeholder":
564
+ return {
565
+ name: column.id,
566
+ header: column.label,
567
+ defaultFlex: 1,
568
+ minWidth:
569
+ column.width && column.width > 0
570
+ ? column.width
571
+ : undefined,
572
+ link: column.link ? column.link : {},
573
+ render: ({ data }) => {
574
+ return <span>{column.placeholder}</span>;
575
+ },
576
+ };
577
+ case "relation":
578
+ let relationName = column.id.reduce(
579
+ (acc, id) => acc + `${id}.`,
580
+ ""
581
+ );
582
+ relationName += column.nameFrom;
583
+
584
+ return {
585
+ name: relationName,
586
+ header: column.label,
587
+ defaultFlex: 1,
588
+ minWidth:
589
+ column.width && column.width > 0
590
+ ? column.width
591
+ : undefined,
592
+ link: column.link ? column.link : {},
593
+ render: ({ data }) => {
594
+ let value = column.id.reduce((acc, id) => {
595
+ if (acc === "") {
596
+ return data[id];
597
+ } else {
598
+ return acc[id];
599
+ }
600
+ }, "");
601
+
602
+ if (value) {
603
+ if (Array.isArray(column.nameFrom)) {
604
+ value = column.nameFrom
605
+ .map((nf) => value[nf])
606
+ .join(" ");
607
+ } else {
608
+ value = value[column.nameFrom];
609
+ }
610
+
611
+ if (
612
+ column.placeholder &&
613
+ column.placeholder !== ""
614
+ ) {
615
+ return <span>{column.placeholder}</span>;
616
+ } else {
617
+ return <span>{value}</span>;
618
+ }
619
+ } else {
620
+ return null;
621
+ }
622
+ },
623
+ };
624
+ case "relationArray":
625
+ const relationArrayName =
626
+ column.id.reduce((acc, id) => acc + `${id}.`, "") +
627
+ column.nameFrom;
628
+
629
+ return {
630
+ name: relationArrayName,
631
+ header: column.label,
632
+ defaultFlex: 1,
633
+ minWidth:
634
+ column.width && column.width > 0
635
+ ? column.width
636
+ : undefined,
637
+ link: column.link ? column.link : {},
638
+ render: ({ data }) => {
639
+ const relationValue = column.id.reduce(
640
+ (acc, id) => (acc === "" ? data[id] : acc[id]),
641
+ ""
642
+ );
643
+
644
+ if (relationValue && Array.isArray(relationValue)) {
645
+ const value = relationValue
646
+ .map((rv) => rv[column.nameFrom])
647
+ .join(", ");
648
+ return <span>{value}</span>;
649
+ } else {
650
+ return null;
651
+ }
652
+ },
653
+ };
654
+
655
+ case "stage":
656
+ return {
657
+ name: column.id,
658
+ header: column.label,
659
+ defaultFlex: 1,
660
+ minWidth:
661
+ column.width && column.width > 0
662
+ ? column.width
663
+ : undefined,
664
+ link: column.link ? column.link : {},
665
+ render: ({ data }) => {
666
+ let stage = "";
667
+
668
+ column.keyIds.forEach((a, b) => {
669
+ if (data[a] === 1) {
670
+ stage = column.valueIds[b];
671
+ }
672
+ });
673
+
674
+ return <span>{stage}</span>;
675
+ },
676
+ };
677
+ case "time":
678
+ return {
679
+ name: column.id,
680
+ header: column.label,
681
+ defaultFlex: 1,
682
+ minWidth:
683
+ column.width && column.width > 0
684
+ ? column.width
685
+ : undefined,
686
+ link: column.link ? column.link : {},
687
+ render: ({ data }) => {
688
+ if (data && data[column.id]) {
689
+ data = moment(data[column.id]).format(
690
+ column.format ? column.format : "hh:mm A"
691
+ );
692
+
693
+ return <span>{data}</span>;
694
+ } else {
695
+ return null;
696
+ }
697
+ },
698
+ };
699
+ case "url":
700
+ return {
701
+ name: column.id,
702
+ header: column.label,
703
+ defaultFlex: 1,
704
+ minWidth:
705
+ column.width && column.width > 0
706
+ ? column.width
707
+ : undefined,
708
+ link: column.link ? column.link : {},
709
+ render: ({ data }) => {
710
+ if (data) {
711
+ return (
712
+ <span>
713
+ <a href={data} target="_blank">
714
+ Link
715
+ </a>
716
+ </span>
717
+ );
718
+ } else {
719
+ return null;
720
+ }
721
+ },
722
+ };
723
+ default:
724
+ return {
725
+ name: column.id,
726
+ header: column.label,
727
+ defaultFlex: 1,
728
+ minWidth:
729
+ column.width && column.width > 0
730
+ ? column.width
731
+ : undefined,
732
+ link: column.link ? column.link : {},
733
+ };
734
+ }
735
+ };
736
+
737
+ const newColumns = columns.map(renderColumn);
738
+
739
+ if (settings.length > 0) {
740
+ newColumns.push({
741
+ name: "setting",
742
+ header: "Action",
743
+ defaultWidth: 100,
744
+ textAlign: "center",
745
+ render: ({ data }) => {
746
+ return (
747
+ <div className="tdactions">
748
+ {settings.map((setting) => (
749
+ <span
750
+ key={`setting-${setting.id}`}
751
+ onClick={(e) => {
752
+ e.preventDefault();
753
+ e.stopPropagation();
754
+ handleSettingClick(setting, data);
755
+ }}
756
+ >
757
+ {renderSetting(setting, data)}
758
+ </span>
759
+ ))}
760
+ </div>
761
+ );
762
+ },
763
+ });
764
+ }
765
+
766
+ setGridColumns(newColumns);
767
+ }, [columns]);
768
+
769
+ // Data Grid Styling
770
+ const [windowHeight, setWindowHeight] = useState(window.innerHeight);
771
+ const gridStyle = {
772
+ minHeight: windowHeight * 0.6 > 550 ? windowHeight * 0.6 : 550,
773
+ };
774
+ const headerProps = {
775
+ style: style ? style : {},
776
+ };
777
+
778
+ useEffect(() => {
779
+ const handleResize = () => {
780
+ setWindowHeight(window.innerHeight);
781
+ };
782
+
783
+ window.addEventListener("resize", handleResize);
784
+
785
+ // Clean up the event listener
786
+ return () => {
787
+ window.removeEventListener("resize", handleResize);
788
+ };
789
+ }, []);
790
+
791
+ return (
792
+ <>
793
+ <div className="filterInput--alt">
794
+ <div className="icon-search">
795
+ <Search strokeWidth={2} size={18} />
796
+ </div>
797
+ <input
798
+ type="text"
799
+ placeholder="Search"
800
+ value={search}
801
+ onChange={handleChangeSearch}
802
+ />
803
+ </div>
804
+ <div className="filterAction--alt">
805
+ <button
806
+ className="btn"
807
+ onClick={() => {
808
+ modalOpen("create", 0);
809
+ }}
810
+ >
811
+ <div className="icon-plus"></div> Create
812
+ </button>
813
+ </div>
814
+ <ReactDataGrid
815
+ columns={gridColumns}
816
+ dataSource={data}
817
+ defaultLimit={limit}
818
+ defaultSortInfo={sortInfo}
819
+ enableColumnAutosize={false}
820
+ headerProps={headerProps}
821
+ idProperty={`visns-datagrid-${ajaxSetting.url.replace(
822
+ /\//g,
823
+ "-"
824
+ )}`}
825
+ style={gridStyle}
826
+ onRenderRow={onRenderRow}
827
+ pagination
828
+ renderColumnContextMenu={renderColumnContextMenu}
829
+ showZebraRows={true}
830
+ />
831
+ <Popup
832
+ open={modalShow}
833
+ onClose={modalClose}
834
+ closeOnDocumentClick={false}
835
+ >
836
+ <Form
837
+ closeModal={modalClose}
838
+ fetchTable={handleReload}
839
+ formSettings={formData}
840
+ formType={formType}
841
+ updateForm={setFormData}
842
+ columnId={formId}
843
+ childDropdownCallback={childDropdownCallback}
844
+ />
845
+ </Popup>
846
+ </>
847
+ );
817
848
  };
818
849
 
819
850
  export default DataGrid;