@visns-studio/visns-components 5.9.4 → 5.9.6
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 +5 -5
- package/src/components/crm/DataGrid.jsx +21 -8
- package/src/components/crm/Form.jsx +386 -350
- package/src/components/crm/generic/GenericIndex.jsx +167 -12
- package/src/components/crm/generic/styles/GenericIndex.module.scss +9 -1
- package/src/components/crm/styles/Notification.module.scss +0 -1
- package/src/components/crm/styles/global-datagrid.css +1 -1
- package/src/components/test/TooltipTest.jsx +104 -0
|
@@ -15,10 +15,12 @@ import parse from 'html-react-parser';
|
|
|
15
15
|
import moment from 'moment';
|
|
16
16
|
import numeral from 'numeral';
|
|
17
17
|
import { CloudDownload } from 'akar-icons';
|
|
18
|
+
import Popup from 'reactjs-popup';
|
|
18
19
|
|
|
19
20
|
import CustomFetch from '../Fetch';
|
|
20
21
|
import Download from '../Download';
|
|
21
22
|
import Field from '../Field';
|
|
23
|
+
import Form from '../Form';
|
|
22
24
|
import Table from '../DataGrid';
|
|
23
25
|
import TableFilter from '../TableFilter';
|
|
24
26
|
|
|
@@ -1485,6 +1487,11 @@ function GenericIndex({
|
|
|
1485
1487
|
const [tableData, setTableData] = useState([]);
|
|
1486
1488
|
const [tableInfo, setTableInfo] = useState({});
|
|
1487
1489
|
const [total, setTotal] = useState(0);
|
|
1490
|
+
|
|
1491
|
+
/** Custom Action Modal States */
|
|
1492
|
+
const [customActionModalShow, setCustomActionModalShow] = useState(false);
|
|
1493
|
+
const [customActionFormData, setCustomActionFormData] = useState({});
|
|
1494
|
+
const [customActionConfig, setCustomActionConfig] = useState(null);
|
|
1488
1495
|
const windowHeight = useWindowHeight();
|
|
1489
1496
|
const { config, setConfig, subnav, setSubnav } = useConfig(
|
|
1490
1497
|
setting,
|
|
@@ -1526,20 +1533,110 @@ function GenericIndex({
|
|
|
1526
1533
|
[setting, config]
|
|
1527
1534
|
);
|
|
1528
1535
|
|
|
1536
|
+
/** Custom Action Modal Functions */
|
|
1537
|
+
const handleCustomActionModal = useCallback(
|
|
1538
|
+
(customAction) => {
|
|
1539
|
+
try {
|
|
1540
|
+
// Get form configuration from root level (config.form)
|
|
1541
|
+
const rootFormConfig = config.form;
|
|
1542
|
+
|
|
1543
|
+
if (!rootFormConfig || !rootFormConfig.fields) {
|
|
1544
|
+
toast.error('Form configuration not found.');
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// Filter out hidden fields from root-level form configuration
|
|
1549
|
+
const visibleFields = rootFormConfig.fields.filter(
|
|
1550
|
+
(field) => field.type !== 'hidden'
|
|
1551
|
+
);
|
|
1552
|
+
|
|
1553
|
+
// Add hidden field for selected rows
|
|
1554
|
+
const selectedRowIds = Object.keys(rowsSelected);
|
|
1555
|
+
const fieldsWithRows = [
|
|
1556
|
+
...visibleFields,
|
|
1557
|
+
{
|
|
1558
|
+
id: 'selected_rows',
|
|
1559
|
+
type: 'hidden',
|
|
1560
|
+
value: selectedRowIds,
|
|
1561
|
+
},
|
|
1562
|
+
];
|
|
1563
|
+
|
|
1564
|
+
// Prepare form data for the modal using root-level form config
|
|
1565
|
+
const modalFormData = {
|
|
1566
|
+
...rootFormConfig,
|
|
1567
|
+
fields: fieldsWithRows,
|
|
1568
|
+
// Use custom URL and method for submission
|
|
1569
|
+
customUrl: customAction.url,
|
|
1570
|
+
customMethod: customAction.method || 'POST',
|
|
1571
|
+
customDataId: false, // Don't append ID to URL
|
|
1572
|
+
create: {
|
|
1573
|
+
title: customAction.label,
|
|
1574
|
+
url: customAction.url,
|
|
1575
|
+
method: customAction.method || 'POST',
|
|
1576
|
+
},
|
|
1577
|
+
primaryKey: 'id',
|
|
1578
|
+
};
|
|
1579
|
+
|
|
1580
|
+
setCustomActionConfig(customAction);
|
|
1581
|
+
setCustomActionFormData(modalFormData);
|
|
1582
|
+
setCustomActionModalShow(true);
|
|
1583
|
+
} catch (error) {
|
|
1584
|
+
console.error('Error opening custom action modal:', error);
|
|
1585
|
+
toast.error('Error opening form. Please try again.');
|
|
1586
|
+
}
|
|
1587
|
+
},
|
|
1588
|
+
[config.form, rowsSelected]
|
|
1589
|
+
);
|
|
1590
|
+
|
|
1591
|
+
const closeCustomActionModal = useCallback(() => {
|
|
1592
|
+
setCustomActionModalShow(false);
|
|
1593
|
+
setCustomActionFormData({});
|
|
1594
|
+
setCustomActionConfig(null);
|
|
1595
|
+
}, []);
|
|
1596
|
+
|
|
1597
|
+
const handleCustomActionSubmit = useCallback(() => {
|
|
1598
|
+
// This is called after successful form submission by the Form component
|
|
1599
|
+
// Since we provide closeModal prop, the Form component won't show success message
|
|
1600
|
+
// So we need to show it here, then reload grid and close modal
|
|
1601
|
+
|
|
1602
|
+
console.log('Custom action submitted', customActionConfig);
|
|
1603
|
+
if (customActionConfig) {
|
|
1604
|
+
toast.success(
|
|
1605
|
+
customActionConfig.message?.success ||
|
|
1606
|
+
'Operation completed successfully.'
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1609
|
+
gridRef.current?.reload();
|
|
1610
|
+
closeCustomActionModal();
|
|
1611
|
+
}, [customActionConfig, closeCustomActionModal]);
|
|
1612
|
+
|
|
1529
1613
|
const handleCustomAction = useCallback(async () => {
|
|
1530
1614
|
try {
|
|
1531
1615
|
// Get the custom action configuration using our helper
|
|
1532
1616
|
const customActionConfig = getFunctionConfig('customAction');
|
|
1533
1617
|
|
|
1534
1618
|
if (customActionConfig) {
|
|
1535
|
-
//
|
|
1536
|
-
|
|
1619
|
+
// Check if this is a modal type custom action
|
|
1620
|
+
if (customActionConfig.type === 'modal') {
|
|
1621
|
+
// Check if rows are selected for modal type actions
|
|
1622
|
+
if (!Object.keys(rowsSelected).length) {
|
|
1623
|
+
toast.warning(
|
|
1624
|
+
customActionConfig.message?.warning ||
|
|
1625
|
+
'Please select at least one row.'
|
|
1626
|
+
);
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
handleCustomActionModal(customActionConfig);
|
|
1630
|
+
} else {
|
|
1631
|
+
// Original implementation for non-modal custom actions
|
|
1632
|
+
console.log('Custom action triggered', customActionConfig);
|
|
1633
|
+
}
|
|
1537
1634
|
}
|
|
1538
1635
|
} catch (err) {
|
|
1539
1636
|
console.error(err);
|
|
1540
1637
|
toast.error('An error occurred while performing the custom action');
|
|
1541
1638
|
}
|
|
1542
|
-
}, [getFunctionConfig]);
|
|
1639
|
+
}, [getFunctionConfig, handleCustomActionModal, rowsSelected]);
|
|
1543
1640
|
|
|
1544
1641
|
const handleCheckboxUpdate = useCallback(async () => {
|
|
1545
1642
|
try {
|
|
@@ -2064,15 +2161,36 @@ function GenericIndex({
|
|
|
2064
2161
|
)}
|
|
2065
2162
|
|
|
2066
2163
|
{/* Render Custom Action button if customAction function is configured in any location */}
|
|
2067
|
-
{getFunctionConfig('customAction') &&
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2164
|
+
{getFunctionConfig('customAction') &&
|
|
2165
|
+
(() => {
|
|
2166
|
+
const customActionConfig =
|
|
2167
|
+
getFunctionConfig('customAction');
|
|
2168
|
+
const isModalType =
|
|
2169
|
+
customActionConfig.type === 'modal';
|
|
2170
|
+
const hasSelectedRows =
|
|
2171
|
+
Object.keys(rowsSelected).length > 0;
|
|
2172
|
+
const isDisabled =
|
|
2173
|
+
isModalType && !hasSelectedRows;
|
|
2174
|
+
|
|
2175
|
+
return (
|
|
2176
|
+
<button
|
|
2177
|
+
className={styles.btn}
|
|
2178
|
+
onClick={handleCustomAction}
|
|
2179
|
+
disabled={isDisabled}
|
|
2180
|
+
{...(isDisabled && {
|
|
2181
|
+
'data-tooltip-id':
|
|
2182
|
+
'system-tooltip',
|
|
2183
|
+
'data-tooltip-content':
|
|
2184
|
+
customActionConfig.message
|
|
2185
|
+
?.warning ||
|
|
2186
|
+
'Please select at least one row to perform this action',
|
|
2187
|
+
})}
|
|
2188
|
+
>
|
|
2189
|
+
{customActionConfig.label ||
|
|
2190
|
+
'Custom Action'}
|
|
2191
|
+
</button>
|
|
2192
|
+
);
|
|
2193
|
+
})()}
|
|
2076
2194
|
|
|
2077
2195
|
{setting.export?.label && (
|
|
2078
2196
|
<button
|
|
@@ -2086,6 +2204,43 @@ function GenericIndex({
|
|
|
2086
2204
|
);
|
|
2087
2205
|
})()}
|
|
2088
2206
|
</div>
|
|
2207
|
+
|
|
2208
|
+
{/* Custom Action Modal */}
|
|
2209
|
+
<Popup
|
|
2210
|
+
open={customActionModalShow}
|
|
2211
|
+
onClose={closeCustomActionModal}
|
|
2212
|
+
closeOnDocumentClick={false}
|
|
2213
|
+
contentStyle={{
|
|
2214
|
+
width: '80vw',
|
|
2215
|
+
maxWidth: '1200px',
|
|
2216
|
+
...(customActionFormData.verticalAlign === 'top' && {
|
|
2217
|
+
position: 'fixed',
|
|
2218
|
+
top: '5vh',
|
|
2219
|
+
left: '50%',
|
|
2220
|
+
transform: 'translateX(-50%)',
|
|
2221
|
+
margin: '0',
|
|
2222
|
+
}),
|
|
2223
|
+
}}
|
|
2224
|
+
>
|
|
2225
|
+
<div
|
|
2226
|
+
className={`modalwrap top--modal modalWide ${
|
|
2227
|
+
customActionFormData.verticalAlign === 'top'
|
|
2228
|
+
? 'modal-top-aligned'
|
|
2229
|
+
: ''
|
|
2230
|
+
}`}
|
|
2231
|
+
>
|
|
2232
|
+
<div className="modal">
|
|
2233
|
+
<Form
|
|
2234
|
+
closeModal={closeCustomActionModal}
|
|
2235
|
+
formSettings={customActionFormData}
|
|
2236
|
+
formType="create"
|
|
2237
|
+
fetchTable={handleCustomActionSubmit}
|
|
2238
|
+
style={userProfile?.settings?.style || {}}
|
|
2239
|
+
userProfile={userProfile}
|
|
2240
|
+
/>
|
|
2241
|
+
</div>
|
|
2242
|
+
</div>
|
|
2243
|
+
</Popup>
|
|
2089
2244
|
</>
|
|
2090
2245
|
);
|
|
2091
2246
|
}
|
|
@@ -214,12 +214,20 @@
|
|
|
214
214
|
font-size: 1.25em;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
.btn:hover {
|
|
217
|
+
.btn:hover:not(:disabled) {
|
|
218
218
|
color: var(--primary-color);
|
|
219
219
|
background: var(--highlight-color);
|
|
220
220
|
border: 1px solid rgba(var(--highlight-color-rgb), 1.05);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
.btn:disabled {
|
|
224
|
+
opacity: 0.5;
|
|
225
|
+
cursor: not-allowed;
|
|
226
|
+
background: var(--primary-color);
|
|
227
|
+
color: var(--tertiary-color);
|
|
228
|
+
border: 1px solid rgba(var(--primary-color--rgb), 0.5);
|
|
229
|
+
}
|
|
230
|
+
|
|
223
231
|
.polActions {
|
|
224
232
|
position: fixed;
|
|
225
233
|
bottom: 15px;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import SystemTooltip from '../utils/SystemTooltip';
|
|
3
|
+
|
|
4
|
+
const TooltipTest = () => {
|
|
5
|
+
return (
|
|
6
|
+
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
|
|
7
|
+
<h1>SystemTooltip Test Component</h1>
|
|
8
|
+
|
|
9
|
+
<p>This component tests the SystemTooltip functionality. Hover over the elements below:</p>
|
|
10
|
+
|
|
11
|
+
<div style={{ margin: '20px 0' }}>
|
|
12
|
+
<button
|
|
13
|
+
style={{
|
|
14
|
+
background: '#007bff',
|
|
15
|
+
color: 'white',
|
|
16
|
+
border: 'none',
|
|
17
|
+
padding: '10px 20px',
|
|
18
|
+
borderRadius: '4px',
|
|
19
|
+
cursor: 'pointer',
|
|
20
|
+
margin: '10px'
|
|
21
|
+
}}
|
|
22
|
+
data-tooltip-content="This is a test button tooltip"
|
|
23
|
+
>
|
|
24
|
+
Test Button
|
|
25
|
+
</button>
|
|
26
|
+
|
|
27
|
+
<div
|
|
28
|
+
style={{
|
|
29
|
+
width: '30px',
|
|
30
|
+
height: '30px',
|
|
31
|
+
background: '#28a745',
|
|
32
|
+
borderRadius: '50%',
|
|
33
|
+
display: 'inline-block',
|
|
34
|
+
margin: '10px',
|
|
35
|
+
cursor: 'pointer'
|
|
36
|
+
}}
|
|
37
|
+
data-tooltip-content="This is a test icon tooltip"
|
|
38
|
+
></div>
|
|
39
|
+
|
|
40
|
+
<span
|
|
41
|
+
style={{
|
|
42
|
+
background: '#f8f9fa',
|
|
43
|
+
padding: '10px',
|
|
44
|
+
border: '1px solid #dee2e6',
|
|
45
|
+
borderRadius: '4px',
|
|
46
|
+
margin: '10px',
|
|
47
|
+
display: 'inline-block',
|
|
48
|
+
cursor: 'pointer'
|
|
49
|
+
}}
|
|
50
|
+
data-tooltip-content="This is a test text tooltip with some longer content to see how it wraps"
|
|
51
|
+
>
|
|
52
|
+
Test Text Element
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div style={{ margin: '20px 0' }}>
|
|
57
|
+
<p data-tooltip-content="This paragraph has a tooltip">
|
|
58
|
+
Paragraph with tooltip
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
<div data-tooltip-content="Nested element test">
|
|
62
|
+
<span>Nested content</span>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div style={{ margin: '20px 0' }}>
|
|
67
|
+
<input
|
|
68
|
+
type="text"
|
|
69
|
+
placeholder="Input field"
|
|
70
|
+
data-tooltip-content="This is an input field tooltip"
|
|
71
|
+
style={{ margin: '10px' }}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<select
|
|
75
|
+
data-tooltip-content="This is a select dropdown tooltip"
|
|
76
|
+
style={{ margin: '10px' }}
|
|
77
|
+
>
|
|
78
|
+
<option>Option 1</option>
|
|
79
|
+
<option>Option 2</option>
|
|
80
|
+
</select>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<div style={{ margin: '20px 0' }}>
|
|
84
|
+
<div
|
|
85
|
+
data-tooltip-content="HTML content test: <strong>Bold text</strong><br/>Line break<br/>Another line"
|
|
86
|
+
style={{
|
|
87
|
+
background: '#ffc107',
|
|
88
|
+
padding: '10px',
|
|
89
|
+
borderRadius: '4px',
|
|
90
|
+
cursor: 'pointer',
|
|
91
|
+
display: 'inline-block'
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
Element with HTML content tooltip
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{/* Include SystemTooltip component */}
|
|
99
|
+
<SystemTooltip />
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default TooltipTest;
|