@visns-studio/visns-components 5.6.14 → 5.7.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.
- package/package.json +1 -5
- package/src/components/cms/DataGrid.jsx +70 -7
- package/src/components/cms/DropZone.jsx +3 -4
- package/src/components/cms/Form.jsx +3 -4
- package/src/components/cms/generic/CmsDetail.jsx +0 -2
- package/src/components/crm/Autocomplete.jsx +161 -127
- package/src/components/crm/DataGrid.jsx +218 -75
- package/src/components/crm/Download.jsx +15 -4
- package/src/components/crm/Fetch.jsx +69 -16
- package/src/components/crm/Form.jsx +3 -3
- package/src/components/crm/Notification.jsx +237 -106
- package/src/components/crm/QuickAction.jsx +318 -91
- package/src/components/crm/generic/GenericDashboard.jsx +3 -3
- package/src/components/crm/generic/GenericDetail.jsx +46 -130
- package/src/components/crm/generic/GenericDynamic.jsx +3 -3
- package/src/components/crm/generic/GenericEditableTable.jsx +3 -4
- package/src/components/crm/generic/GenericFormBuilder.jsx +3 -3
- package/src/components/crm/generic/NotificationList.jsx +76 -7
- package/src/components/crm/generic/styles/NotificationList.module.scss +90 -2
- package/src/components/crm/generic/styles/SweetAlertCustom.css +27 -3
- package/src/components/crm/styles/DataGrid.module.scss +5 -0
- package/src/components/crm/styles/Notification.module.scss +313 -96
- package/src/components/crm/styles/QuickAction.module.scss +168 -46
- package/src/components/styles/global.css +5 -0
- package/src/components/utils/ConfirmDialog.js +155 -0
- package/src/utils/fetchUtil.js +240 -0
|
@@ -10,7 +10,6 @@ import React, {
|
|
|
10
10
|
useState,
|
|
11
11
|
} from 'react';
|
|
12
12
|
import { useNavigate } from 'react-router-dom';
|
|
13
|
-
import axios from 'axios';
|
|
14
13
|
import debounce from 'lodash.debounce';
|
|
15
14
|
import moment from 'moment';
|
|
16
15
|
import numeral from 'numeral';
|
|
@@ -22,11 +21,12 @@ import NumberFilter from '@visns-studio/visns-datagrid-enterprise/NumberFilter';
|
|
|
22
21
|
import SelectFilter from '@visns-studio/visns-datagrid-enterprise/SelectFilter';
|
|
23
22
|
import DateFilter from '@visns-studio/visns-datagrid-enterprise/DateFilter';
|
|
24
23
|
import ReactDataGrid from '@visns-studio/visns-datagrid-enterprise';
|
|
25
|
-
import { confirmAlert } from 'react-confirm-alert';
|
|
26
24
|
import { toast } from 'react-toastify';
|
|
27
25
|
import imageCompression from 'browser-image-compression';
|
|
28
26
|
import Vapor from 'laravel-vapor';
|
|
29
27
|
import Lightbox from 'yet-another-react-lightbox';
|
|
28
|
+
import fetchUtil from '../../utils/fetchUtil';
|
|
29
|
+
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
30
30
|
import {
|
|
31
31
|
Alarm,
|
|
32
32
|
ArrowCounterClockwise,
|
|
@@ -59,7 +59,6 @@ import {
|
|
|
59
59
|
import styles from './styles/DataGrid.module.scss';
|
|
60
60
|
|
|
61
61
|
import '@visns-studio/visns-datagrid-enterprise/index.css';
|
|
62
|
-
import 'react-confirm-alert/src/react-confirm-alert.css';
|
|
63
62
|
import 'yet-another-react-lightbox/styles.css';
|
|
64
63
|
|
|
65
64
|
import CustomFetch from './Fetch';
|
|
@@ -119,7 +118,7 @@ const loadData = async (
|
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
try {
|
|
122
|
-
const response = await
|
|
121
|
+
const response = await fetchUtil.post(url, params);
|
|
123
122
|
const { data, total } = response.data;
|
|
124
123
|
return { data, count: total };
|
|
125
124
|
} catch (error) {
|
|
@@ -493,43 +492,15 @@ const DataGrid = forwardRef(
|
|
|
493
492
|
|
|
494
493
|
const handleTimer = async (id, key, data) => {
|
|
495
494
|
try {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
key === 'galvanizing_start_2'
|
|
500
|
-
) {
|
|
501
|
-
// For galvanizing_start_1 and galvanizing_start_2, we need to generate dip numbers
|
|
502
|
-
const payload = {
|
|
503
|
-
[key]: moment().toDate(),
|
|
504
|
-
timer_type: key, // Pass the timer type to the backend
|
|
505
|
-
};
|
|
506
|
-
|
|
507
|
-
const res = await CustomFetch(
|
|
508
|
-
`${form.url}/${id}/timer`,
|
|
509
|
-
'PUT',
|
|
510
|
-
payload
|
|
511
|
-
);
|
|
495
|
+
const res = await CustomFetch(`${form.url}/${id}`, 'PUT', {
|
|
496
|
+
[key]: moment().toDate(),
|
|
497
|
+
});
|
|
512
498
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
);
|
|
517
|
-
handleReload();
|
|
518
|
-
} else {
|
|
519
|
-
toast.error(res.data.error);
|
|
520
|
-
}
|
|
499
|
+
if (res.data.error === '') {
|
|
500
|
+
toast.success('Timer has been updated.');
|
|
501
|
+
handleReload();
|
|
521
502
|
} else {
|
|
522
|
-
|
|
523
|
-
const res = await CustomFetch(`${form.url}/${id}`, 'PUT', {
|
|
524
|
-
[key]: moment().toDate(),
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
if (res.data.error === '') {
|
|
528
|
-
toast.success('Timer has been updated.');
|
|
529
|
-
handleReload();
|
|
530
|
-
} else {
|
|
531
|
-
toast.error(res.data.error);
|
|
532
|
-
}
|
|
503
|
+
toast.error(res.data.error);
|
|
533
504
|
}
|
|
534
505
|
} catch (error) {
|
|
535
506
|
console.info(error);
|
|
@@ -538,32 +509,74 @@ const DataGrid = forwardRef(
|
|
|
538
509
|
|
|
539
510
|
const handleSelectionChange = useCallback(
|
|
540
511
|
(param) => {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
param.
|
|
545
|
-
|
|
546
|
-
|
|
512
|
+
// If this is a new selection (not a deselection)
|
|
513
|
+
if (
|
|
514
|
+
param.selected === true ||
|
|
515
|
+
(typeof param.selected === 'object' &&
|
|
516
|
+
Object.keys(param.selected).length > 0)
|
|
517
|
+
) {
|
|
518
|
+
// Show confirmation dialog for selection
|
|
519
|
+
const selectedCount =
|
|
520
|
+
param.selected === true ? param.data.length : 1;
|
|
521
|
+
const itemText = selectedCount === 1 ? 'item' : 'items';
|
|
522
|
+
|
|
523
|
+
confirmDialog({
|
|
524
|
+
title: 'Selection Confirmation',
|
|
525
|
+
message: `You have selected ${selectedCount} ${itemText}. Do you want to proceed with this selection?`,
|
|
526
|
+
data:
|
|
527
|
+
param.selected === true
|
|
528
|
+
? param.data
|
|
529
|
+
: param.selected, // Pass the selected data
|
|
530
|
+
buttons: [
|
|
531
|
+
{
|
|
532
|
+
label: 'Yes',
|
|
533
|
+
onClick: () => {
|
|
534
|
+
// Process the selection as before
|
|
535
|
+
if (param.selected === true) {
|
|
536
|
+
const s = {};
|
|
537
|
+
param.data.forEach((obj) => {
|
|
538
|
+
s[obj.id] = obj;
|
|
539
|
+
});
|
|
540
|
+
setSelected(s);
|
|
541
|
+
} else if (
|
|
542
|
+
typeof param.selected === 'object'
|
|
543
|
+
) {
|
|
544
|
+
const selectedKey = Object.keys(
|
|
545
|
+
param.selected
|
|
546
|
+
)[0];
|
|
547
|
+
const isSelectedAlready =
|
|
548
|
+
selected.hasOwnProperty(
|
|
549
|
+
selectedKey
|
|
550
|
+
);
|
|
547
551
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
552
|
+
if (isSelectedAlready) {
|
|
553
|
+
setSelected({});
|
|
554
|
+
} else {
|
|
555
|
+
setSelected((prevSelected) => ({
|
|
556
|
+
...prevSelected,
|
|
557
|
+
[selectedKey]:
|
|
558
|
+
param.selected[selectedKey],
|
|
559
|
+
}));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
554
562
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
563
|
+
// Show a success message
|
|
564
|
+
toast.success(
|
|
565
|
+
`Selection of ${selectedCount} ${itemText} confirmed`
|
|
566
|
+
);
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
label: 'No',
|
|
571
|
+
onClick: () => {
|
|
572
|
+
// Clear selection if user cancels
|
|
573
|
+
setSelected({});
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
],
|
|
577
|
+
});
|
|
565
578
|
} else {
|
|
566
|
-
// Handle
|
|
579
|
+
// Handle deselection directly without confirmation
|
|
567
580
|
setSelected(param.selected);
|
|
568
581
|
}
|
|
569
582
|
},
|
|
@@ -645,18 +658,86 @@ const DataGrid = forwardRef(
|
|
|
645
658
|
case 'file':
|
|
646
659
|
case 'image':
|
|
647
660
|
case 'undo':
|
|
648
|
-
|
|
649
|
-
|
|
661
|
+
// Create a user-friendly action name from the ID
|
|
662
|
+
const actionName =
|
|
663
|
+
s.title || s.id.charAt(0).toUpperCase() + s.id.slice(1);
|
|
664
|
+
|
|
665
|
+
// Create a message based on the action type
|
|
666
|
+
let message = `Are you sure you want to ${actionName.toLowerCase()}?`;
|
|
667
|
+
|
|
668
|
+
// Add item identifier if available
|
|
669
|
+
const itemName = d.name || d.label || d.title || d.id;
|
|
670
|
+
if (itemName) {
|
|
671
|
+
message += ` Item: "${itemName}"`;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Show confirmation dialog
|
|
675
|
+
confirmDialog({
|
|
676
|
+
title: actionName,
|
|
677
|
+
message: message,
|
|
678
|
+
data: d, // Pass the row data for context
|
|
679
|
+
buttons: [
|
|
680
|
+
{
|
|
681
|
+
label: 'Yes',
|
|
682
|
+
onClick: () => {
|
|
683
|
+
CustomFetch(
|
|
684
|
+
s.url,
|
|
685
|
+
'POST',
|
|
686
|
+
{ id: d[s.key] },
|
|
687
|
+
(result) => {
|
|
688
|
+
toast.success(result.message);
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
label: 'No',
|
|
695
|
+
onClick: () => {},
|
|
696
|
+
},
|
|
697
|
+
],
|
|
650
698
|
});
|
|
651
699
|
break;
|
|
652
700
|
case 'clone':
|
|
653
701
|
if (s.url && s.url !== '') {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
702
|
+
// Create a message for clone action
|
|
703
|
+
let cloneMessage = `Are you sure you want to clone this item?`;
|
|
704
|
+
|
|
705
|
+
// Add item identifier if available
|
|
706
|
+
const cloneItemName =
|
|
707
|
+
d.name || d.label || d.title || d.id;
|
|
708
|
+
if (cloneItemName) {
|
|
709
|
+
cloneMessage += ` Item: "${cloneItemName}"`;
|
|
710
|
+
}
|
|
658
711
|
|
|
659
|
-
|
|
712
|
+
// Show confirmation dialog
|
|
713
|
+
confirmDialog({
|
|
714
|
+
title: 'Clone Item',
|
|
715
|
+
message: cloneMessage,
|
|
716
|
+
data: d, // Pass the row data for context
|
|
717
|
+
buttons: [
|
|
718
|
+
{
|
|
719
|
+
label: 'Yes',
|
|
720
|
+
onClick: () => {
|
|
721
|
+
const cloneUrl = `${s.url}/${
|
|
722
|
+
d[s.key]
|
|
723
|
+
}/clone`;
|
|
724
|
+
|
|
725
|
+
CustomFetch(
|
|
726
|
+
cloneUrl,
|
|
727
|
+
'POST',
|
|
728
|
+
{},
|
|
729
|
+
(result) => {
|
|
730
|
+
toast.success(result.message);
|
|
731
|
+
handleReload();
|
|
732
|
+
}
|
|
733
|
+
);
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
label: 'No',
|
|
738
|
+
onClick: () => {},
|
|
739
|
+
},
|
|
740
|
+
],
|
|
660
741
|
});
|
|
661
742
|
}
|
|
662
743
|
break;
|
|
@@ -748,9 +829,10 @@ const DataGrid = forwardRef(
|
|
|
748
829
|
}?`;
|
|
749
830
|
}
|
|
750
831
|
|
|
751
|
-
|
|
832
|
+
confirmDialog({
|
|
752
833
|
title: 'Delete Item',
|
|
753
834
|
message: confirmMessage,
|
|
835
|
+
data: d, // Pass the row data
|
|
754
836
|
buttons: [
|
|
755
837
|
{
|
|
756
838
|
label: 'Yes',
|
|
@@ -889,9 +971,10 @@ const DataGrid = forwardRef(
|
|
|
889
971
|
modalOpen('update', d.id);
|
|
890
972
|
break;
|
|
891
973
|
default:
|
|
892
|
-
|
|
974
|
+
confirmDialog({
|
|
893
975
|
title: s.title,
|
|
894
976
|
message: '',
|
|
977
|
+
data: d, // Pass the row data
|
|
895
978
|
buttons: [
|
|
896
979
|
{
|
|
897
980
|
label: 'Yes',
|
|
@@ -1509,11 +1592,71 @@ const DataGrid = forwardRef(
|
|
|
1509
1592
|
e.preventDefault();
|
|
1510
1593
|
e.stopPropagation();
|
|
1511
1594
|
|
|
1512
|
-
// Ensure both s and d are defined before
|
|
1513
|
-
if (s
|
|
1514
|
-
handleSettingClick(s, d);
|
|
1515
|
-
} else {
|
|
1595
|
+
// Ensure both s and d are defined before proceeding
|
|
1596
|
+
if (!s || !d) {
|
|
1516
1597
|
console.warn('Missing settings or data for icon click');
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// Actions that should have confirmation dialogs
|
|
1602
|
+
const actionsRequiringConfirmation = [
|
|
1603
|
+
'activate',
|
|
1604
|
+
'arrowCycle',
|
|
1605
|
+
'cloudUpload',
|
|
1606
|
+
'clone',
|
|
1607
|
+
'envelope',
|
|
1608
|
+
'file',
|
|
1609
|
+
'image',
|
|
1610
|
+
'undo',
|
|
1611
|
+
'restore',
|
|
1612
|
+
];
|
|
1613
|
+
|
|
1614
|
+
// If this action requires confirmation, show a dialog
|
|
1615
|
+
if (actionsRequiringConfirmation.includes(s.id)) {
|
|
1616
|
+
// Get a user-friendly action name from the title or ID
|
|
1617
|
+
const actionName =
|
|
1618
|
+
s.title || s.id.charAt(0).toUpperCase() + s.id.slice(1);
|
|
1619
|
+
|
|
1620
|
+
// Create a message based on the action type
|
|
1621
|
+
let message = '';
|
|
1622
|
+
if (s.id === 'delete') {
|
|
1623
|
+
// Delete already has its own confirmation message
|
|
1624
|
+
handleSettingClick(s, d);
|
|
1625
|
+
return;
|
|
1626
|
+
} else {
|
|
1627
|
+
// For other actions, create a message based on the tooltip content
|
|
1628
|
+
const tooltipContent =
|
|
1629
|
+
s.active && iconStyle.color
|
|
1630
|
+
? s.active.title
|
|
1631
|
+
: s.title;
|
|
1632
|
+
message = `Are you sure you want to ${tooltipContent.toLowerCase()}?`;
|
|
1633
|
+
|
|
1634
|
+
// Add item identifier if available
|
|
1635
|
+
const itemName = d.name || d.label || d.title || d.id;
|
|
1636
|
+
if (itemName) {
|
|
1637
|
+
message += ` Item: "${itemName}"`;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// Show the confirmation dialog
|
|
1642
|
+
confirmDialog({
|
|
1643
|
+
title: actionName,
|
|
1644
|
+
message: message,
|
|
1645
|
+
data: d, // Pass the row data for context
|
|
1646
|
+
buttons: [
|
|
1647
|
+
{
|
|
1648
|
+
label: 'Yes',
|
|
1649
|
+
onClick: () => handleSettingClick(s, d),
|
|
1650
|
+
},
|
|
1651
|
+
{
|
|
1652
|
+
label: 'No',
|
|
1653
|
+
onClick: () => {},
|
|
1654
|
+
},
|
|
1655
|
+
],
|
|
1656
|
+
});
|
|
1657
|
+
} else {
|
|
1658
|
+
// For actions that don't need confirmation, proceed directly
|
|
1659
|
+
handleSettingClick(s, d);
|
|
1517
1660
|
}
|
|
1518
1661
|
};
|
|
1519
1662
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import axios from 'axios';
|
|
3
2
|
import { trackPromise } from 'react-promise-tracker';
|
|
4
3
|
import { toast } from 'react-toastify';
|
|
5
4
|
import parse from 'html-react-parser';
|
|
5
|
+
import fetchUtil from '../../utils/fetchUtil';
|
|
6
6
|
|
|
7
7
|
const CustomDownload = (
|
|
8
8
|
url,
|
|
@@ -11,7 +11,18 @@ const CustomDownload = (
|
|
|
11
11
|
successCallback = null,
|
|
12
12
|
errorCallback = null
|
|
13
13
|
) => {
|
|
14
|
-
let headers = {
|
|
14
|
+
let headers = {
|
|
15
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Add CSRF token if available
|
|
19
|
+
const csrfToken = document
|
|
20
|
+
.querySelector('meta[name="csrf-token"]')
|
|
21
|
+
?.getAttribute('content');
|
|
22
|
+
if (csrfToken) {
|
|
23
|
+
headers['X-CSRF-TOKEN'] = csrfToken;
|
|
24
|
+
headers['X-XSRF-TOKEN'] = csrfToken;
|
|
25
|
+
}
|
|
15
26
|
let options = {};
|
|
16
27
|
|
|
17
28
|
if (method.toUpperCase() === 'PUT' || method.toUpperCase() === 'PATCH') {
|
|
@@ -39,7 +50,7 @@ const CustomDownload = (
|
|
|
39
50
|
return trackPromise(
|
|
40
51
|
new Promise((resolve, reject) => {
|
|
41
52
|
// First, make a request without responseType: 'blob' to check for errors
|
|
42
|
-
|
|
53
|
+
fetchUtil({
|
|
43
54
|
method: method,
|
|
44
55
|
data: formData,
|
|
45
56
|
headers: headers,
|
|
@@ -64,7 +75,7 @@ const CustomDownload = (
|
|
|
64
75
|
resolve(response);
|
|
65
76
|
} else {
|
|
66
77
|
// If no error, make the actual blob request
|
|
67
|
-
|
|
78
|
+
fetchUtil({
|
|
68
79
|
method: method,
|
|
69
80
|
data: formData,
|
|
70
81
|
headers: headers,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import axios from 'axios';
|
|
3
2
|
import { trackPromise } from 'react-promise-tracker';
|
|
4
3
|
import { toast } from 'react-toastify';
|
|
5
4
|
import parse from 'html-react-parser';
|
|
@@ -14,7 +13,17 @@ const CustomFetch = (
|
|
|
14
13
|
const baseUrl = '';
|
|
15
14
|
const headers = {
|
|
16
15
|
'Content-Type': 'application/json',
|
|
16
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
17
17
|
};
|
|
18
|
+
|
|
19
|
+
// Add CSRF token if available
|
|
20
|
+
const csrfToken = document
|
|
21
|
+
.querySelector('meta[name="csrf-token"]')
|
|
22
|
+
?.getAttribute('content');
|
|
23
|
+
if (csrfToken) {
|
|
24
|
+
headers['X-CSRF-TOKEN'] = csrfToken;
|
|
25
|
+
headers['X-XSRF-TOKEN'] = csrfToken;
|
|
26
|
+
}
|
|
18
27
|
const MAX_PAYLOAD_SIZE_MB =
|
|
19
28
|
parseFloat(import.meta.env.VITE_MAX_PAYLOAD_SIZE_MB) || 4.5;
|
|
20
29
|
const MAX_PAYLOAD_SIZE = MAX_PAYLOAD_SIZE_MB * 1024 * 1024; // Convert MB to bytes
|
|
@@ -48,36 +57,80 @@ const CustomFetch = (
|
|
|
48
57
|
};
|
|
49
58
|
}
|
|
50
59
|
|
|
51
|
-
|
|
60
|
+
// Configure fetch options
|
|
61
|
+
const fetchOptions = {
|
|
52
62
|
method: method,
|
|
53
|
-
data: formData,
|
|
54
63
|
headers: headers,
|
|
55
|
-
url: baseUrl + url,
|
|
56
64
|
};
|
|
57
65
|
|
|
66
|
+
// For GET and HEAD requests, convert formData to query parameters
|
|
67
|
+
if (method.toUpperCase() === 'GET' || method.toUpperCase() === 'HEAD') {
|
|
68
|
+
if (formData && Object.keys(formData).length > 0) {
|
|
69
|
+
// Convert formData to query string
|
|
70
|
+
const queryParams = new URLSearchParams();
|
|
71
|
+
Object.entries(formData).forEach(([key, value]) => {
|
|
72
|
+
if (value !== undefined && value !== null) {
|
|
73
|
+
queryParams.append(key, value);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
const queryString = queryParams.toString();
|
|
77
|
+
if (queryString) {
|
|
78
|
+
url += (url.includes('?') ? '&' : '?') + queryString;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
// For other methods, add body
|
|
83
|
+
fetchOptions.body = formData ? JSON.stringify(formData) : undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
58
86
|
return trackPromise(
|
|
59
87
|
new Promise((resolve, reject) => {
|
|
60
|
-
|
|
61
|
-
.then((response) => {
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
fetch(baseUrl + url, fetchOptions)
|
|
89
|
+
.then(async (response) => {
|
|
90
|
+
// Parse the response data
|
|
91
|
+
const contentType = response.headers.get('content-type');
|
|
92
|
+
let data;
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
contentType &&
|
|
96
|
+
contentType.includes('application/json')
|
|
97
|
+
) {
|
|
98
|
+
data = await response.json();
|
|
64
99
|
} else {
|
|
65
|
-
|
|
100
|
+
data = await response.text();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Create a response object similar to axios for compatibility
|
|
104
|
+
const responseObj = {
|
|
105
|
+
data: data,
|
|
106
|
+
status: response.status,
|
|
107
|
+
statusText: response.statusText,
|
|
108
|
+
headers: response.headers,
|
|
109
|
+
ok: response.ok,
|
|
110
|
+
};
|
|
66
111
|
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
// Handle error responses (4xx, 5xx)
|
|
114
|
+
const error = new Error(response.statusText);
|
|
115
|
+
error.response = responseObj;
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Handle success
|
|
120
|
+
if (successCallback) {
|
|
121
|
+
successCallback(data);
|
|
122
|
+
} else {
|
|
123
|
+
const error = data.error;
|
|
67
124
|
if (error && error !== '') {
|
|
68
125
|
toast.error(<div>{parse(error)}</div>);
|
|
69
126
|
}
|
|
70
127
|
}
|
|
71
|
-
resolve(
|
|
128
|
+
resolve(responseObj);
|
|
72
129
|
})
|
|
73
130
|
.catch((error) => {
|
|
74
131
|
let errorMessage = '';
|
|
75
132
|
|
|
76
|
-
if (
|
|
77
|
-
error.response &&
|
|
78
|
-
error.response.data &&
|
|
79
|
-
error.response.data.message
|
|
80
|
-
) {
|
|
133
|
+
if (error.response && error.response.data) {
|
|
81
134
|
const { data } = error.response;
|
|
82
135
|
|
|
83
136
|
if (data.errors) {
|
|
@@ -88,7 +141,7 @@ const CustomFetch = (
|
|
|
88
141
|
errorMessage += `${errorMsg}<br />`;
|
|
89
142
|
});
|
|
90
143
|
});
|
|
91
|
-
} else {
|
|
144
|
+
} else if (data.message) {
|
|
92
145
|
if (data.message === 'CSRF token mismatch.') {
|
|
93
146
|
window.location.replace('/login');
|
|
94
147
|
} else {
|
|
@@ -10,11 +10,10 @@ import parse from 'html-react-parser';
|
|
|
10
10
|
import _ from 'lodash';
|
|
11
11
|
import ReactDataGrid from '@visns-studio/visns-datagrid-enterprise';
|
|
12
12
|
import { CircleX } from 'akar-icons';
|
|
13
|
-
import { confirmAlert } from 'react-confirm-alert';
|
|
14
13
|
import imageCompression from 'browser-image-compression';
|
|
14
|
+
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
15
15
|
|
|
16
16
|
import '@visns-studio/visns-datagrid-enterprise/index.css';
|
|
17
|
-
import 'react-confirm-alert/src/react-confirm-alert.css';
|
|
18
17
|
|
|
19
18
|
import CustomFetch from './Fetch';
|
|
20
19
|
import Download from './Download';
|
|
@@ -598,11 +597,12 @@ function Form({
|
|
|
598
597
|
|
|
599
598
|
try {
|
|
600
599
|
if (s.customButton.confirmation) {
|
|
601
|
-
|
|
600
|
+
confirmDialog({
|
|
602
601
|
title: '',
|
|
603
602
|
message:
|
|
604
603
|
s.customButton.confirmation.message ||
|
|
605
604
|
'Are you sure you want to proceed?',
|
|
605
|
+
data: formData, // Add context data
|
|
606
606
|
buttons: [
|
|
607
607
|
{
|
|
608
608
|
label: 'Yes',
|