@visns-studio/visns-components 5.14.14 → 5.14.16
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 -1
- package/src/components/DataGrid.jsx +378 -132
- package/src/components/Form.jsx +99 -52
package/package.json
CHANGED
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
90
90
|
},
|
|
91
91
|
"name": "@visns-studio/visns-components",
|
|
92
|
-
"version": "5.14.
|
|
92
|
+
"version": "5.14.16",
|
|
93
93
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
94
94
|
"main": "src/index.js",
|
|
95
95
|
"files": [
|
|
@@ -24,6 +24,7 @@ import React, {
|
|
|
24
24
|
useCallback,
|
|
25
25
|
useEffect,
|
|
26
26
|
useImperativeHandle,
|
|
27
|
+
useMemo,
|
|
27
28
|
useRef,
|
|
28
29
|
useState,
|
|
29
30
|
} from 'react';
|
|
@@ -250,6 +251,29 @@ const DataGrid = forwardRef(
|
|
|
250
251
|
) => {
|
|
251
252
|
const navigate = useNavigate();
|
|
252
253
|
|
|
254
|
+
/**
|
|
255
|
+
* DEBUG: Settings Configuration Tracking
|
|
256
|
+
* Monitor changes to settings prop to identify config update issues
|
|
257
|
+
*/
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
console.log('🔍 DataGrid settings prop changed:', {
|
|
260
|
+
settings: settings,
|
|
261
|
+
settingsLength: settings?.length || 0,
|
|
262
|
+
hasUpdateSetting:
|
|
263
|
+
settings?.some((s) => s.id === 'update') || false,
|
|
264
|
+
settingsIds: settings?.map((s) => s.id) || [],
|
|
265
|
+
timestamp: new Date().toISOString(),
|
|
266
|
+
});
|
|
267
|
+
}, [settings]);
|
|
268
|
+
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
console.log('🔍 DataGrid form prop changed:', {
|
|
271
|
+
form: form,
|
|
272
|
+
primaryKey: form?.primaryKey,
|
|
273
|
+
timestamp: new Date().toISOString(),
|
|
274
|
+
});
|
|
275
|
+
}, [form]);
|
|
276
|
+
|
|
253
277
|
/** Audio */
|
|
254
278
|
const [audioSource, setAudioSource] = useState('');
|
|
255
279
|
|
|
@@ -285,6 +309,11 @@ const DataGrid = forwardRef(
|
|
|
285
309
|
|
|
286
310
|
/** Modal Functions */
|
|
287
311
|
const modalOpen = (formType, formId) => {
|
|
312
|
+
console.log('🔍 modalOpen called:', {
|
|
313
|
+
formType: formType,
|
|
314
|
+
formId: formId,
|
|
315
|
+
timestamp: new Date().toISOString(),
|
|
316
|
+
});
|
|
288
317
|
setModalShow(true);
|
|
289
318
|
setFormType(formType);
|
|
290
319
|
setFormId(formId);
|
|
@@ -304,6 +333,32 @@ const DataGrid = forwardRef(
|
|
|
304
333
|
const dataRef = useRef(null);
|
|
305
334
|
const [dSearch, setDSearch] = useState('');
|
|
306
335
|
|
|
336
|
+
/** Optimized refs for settings and form to avoid unnecessary re-renders */
|
|
337
|
+
const settingsRef = useRef(settings);
|
|
338
|
+
const formRef = useRef(form);
|
|
339
|
+
|
|
340
|
+
// Keep refs updated with current values
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
settingsRef.current = settings;
|
|
343
|
+
}, [settings]);
|
|
344
|
+
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
formRef.current = form;
|
|
347
|
+
}, [form]);
|
|
348
|
+
|
|
349
|
+
// Memoize settings to avoid unnecessary re-renders when array reference changes but content is same
|
|
350
|
+
const memoizedSettings = useMemo(
|
|
351
|
+
() => settings,
|
|
352
|
+
[
|
|
353
|
+
settings?.length,
|
|
354
|
+
settings
|
|
355
|
+
?.map(
|
|
356
|
+
(s) => `${s.id}-${s.roles?.join(',') || ''}-${s.active}`
|
|
357
|
+
)
|
|
358
|
+
.join('|'),
|
|
359
|
+
]
|
|
360
|
+
);
|
|
361
|
+
|
|
307
362
|
/** Group expansion state */
|
|
308
363
|
const [collapsedGroups, setCollapsedGroups] = useState({});
|
|
309
364
|
|
|
@@ -1209,7 +1264,7 @@ const DataGrid = forwardRef(
|
|
|
1209
1264
|
|
|
1210
1265
|
const handleReload = () => {
|
|
1211
1266
|
gridRef.current.paginationProps.reload();
|
|
1212
|
-
|
|
1267
|
+
|
|
1213
1268
|
// Force grid to recalculate row heights after reload
|
|
1214
1269
|
// This fixes the row height rendering issue after filter changes
|
|
1215
1270
|
setTimeout(() => {
|
|
@@ -1321,7 +1376,7 @@ const DataGrid = forwardRef(
|
|
|
1321
1376
|
// Reload grid data if needed
|
|
1322
1377
|
if (gridRef.current && gridRef.current.paginationProps) {
|
|
1323
1378
|
gridRef.current.paginationProps.reload();
|
|
1324
|
-
|
|
1379
|
+
|
|
1325
1380
|
// Force grid to recalculate row heights after reload
|
|
1326
1381
|
setTimeout(() => {
|
|
1327
1382
|
if (gridRef.current?.api) {
|
|
@@ -1463,112 +1518,198 @@ const DataGrid = forwardRef(
|
|
|
1463
1518
|
}
|
|
1464
1519
|
};
|
|
1465
1520
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1521
|
+
/**
|
|
1522
|
+
* DEBUG: Row Click Analysis
|
|
1523
|
+
*
|
|
1524
|
+
* When row clicks aren't working, check the console for these debug messages:
|
|
1525
|
+
*
|
|
1526
|
+
* 1. "Row onClick triggered" - Initial row click detection
|
|
1527
|
+
* 2. "Group row check" - Whether it's a group row (should be false for data rows)
|
|
1528
|
+
* 3. "Cell element in row handler" - Cell element detection
|
|
1529
|
+
* 4. "Row handler column analysis" - Column identification
|
|
1530
|
+
* 5. "Non-checkbox column - calling onRowClick" - Normal row click path
|
|
1531
|
+
* 6. "DataGrid onRowClick triggered" - Main onRowClick function entry
|
|
1532
|
+
* 7. "Column analysis" - Column detection and type checking
|
|
1533
|
+
* 8. "Checking for update setting" - Modal opening prerequisites
|
|
1534
|
+
* 9. "Update setting found" - Whether update setting exists
|
|
1535
|
+
* 10. "Opening modal with" / "No update setting found" - Final modal decision
|
|
1536
|
+
* 11. "modalOpen called" - Modal function execution
|
|
1537
|
+
*
|
|
1538
|
+
* Common issues:
|
|
1539
|
+
* - Missing cell element: Check if click target is within grid
|
|
1540
|
+
* - Wrong column type: Check column configuration
|
|
1541
|
+
* - No update setting: Check config.settings for update permission
|
|
1542
|
+
* - Group row interference: Check if data is being treated as group row
|
|
1543
|
+
*/
|
|
1544
|
+
const onRowClick = useCallback(
|
|
1545
|
+
(rowProps, event) => {
|
|
1546
|
+
console.log('🔍 DataGrid onRowClick triggered:', {
|
|
1547
|
+
rowData: rowProps.data,
|
|
1548
|
+
event: event,
|
|
1549
|
+
target: event.target,
|
|
1550
|
+
timestamp: new Date().toISOString(),
|
|
1551
|
+
});
|
|
1470
1552
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
}
|
|
1553
|
+
const cellElement = event.target.closest(
|
|
1554
|
+
'.InovuaReactDataGrid__cell'
|
|
1555
|
+
);
|
|
1475
1556
|
|
|
1476
|
-
|
|
1477
|
-
cellElement.parentNode.children
|
|
1478
|
-
).indexOf(cellElement);
|
|
1557
|
+
console.log('🔍 Cell element found:', cellElement);
|
|
1479
1558
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1559
|
+
// Add null check to prevent errors when cellElement is null
|
|
1560
|
+
if (!cellElement) {
|
|
1561
|
+
console.warn(
|
|
1562
|
+
'⚠️ No cell element found - row click aborted'
|
|
1563
|
+
);
|
|
1564
|
+
return; // Exit early if no cell element was found
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
const columnIndex = Array.from(
|
|
1568
|
+
cellElement.parentNode.children
|
|
1569
|
+
).indexOf(cellElement);
|
|
1570
|
+
|
|
1571
|
+
console.log('🔍 Column analysis:', {
|
|
1572
|
+
columnIndex: columnIndex,
|
|
1573
|
+
totalColumns: rowProps.columns.length,
|
|
1574
|
+
isLastColumn: columnIndex === rowProps.columns.length - 1,
|
|
1575
|
+
columns: rowProps.columns.map((col) => ({
|
|
1576
|
+
id: col.id,
|
|
1577
|
+
type: col.type,
|
|
1578
|
+
})),
|
|
1579
|
+
});
|
|
1487
1580
|
|
|
1488
1581
|
if (
|
|
1489
|
-
|
|
1490
|
-
|
|
1582
|
+
rowProps.columns.length > 1 &&
|
|
1583
|
+
columnIndex === rowProps.columns.length - 1
|
|
1491
1584
|
) {
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
acc ? acc[part] : undefined,
|
|
1502
|
-
obj
|
|
1503
|
-
);
|
|
1585
|
+
console.log('🔍 Last column clicked - no action');
|
|
1586
|
+
// Handle last column click
|
|
1587
|
+
} else {
|
|
1588
|
+
const column = rowProps.columns[columnIndex];
|
|
1589
|
+
console.log('🔍 Column clicked:', {
|
|
1590
|
+
columnId: column?.id,
|
|
1591
|
+
columnType: column?.type,
|
|
1592
|
+
hasLink: !!column?.link,
|
|
1593
|
+
});
|
|
1504
1594
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
)
|
|
1595
|
+
if (
|
|
1596
|
+
column.type !== 'dropdown' &&
|
|
1597
|
+
column.type !== 'input_text'
|
|
1598
|
+
) {
|
|
1599
|
+
if (column.id !== '__checkbox-column') {
|
|
1600
|
+
if (
|
|
1601
|
+
column.link &&
|
|
1602
|
+
column.link.hasOwnProperty('url')
|
|
1603
|
+
) {
|
|
1604
|
+
if (column.link.url) {
|
|
1605
|
+
// Helper function to safely retrieve nested values
|
|
1606
|
+
const getNestedValue = (obj, key) =>
|
|
1607
|
+
key
|
|
1608
|
+
.split('.')
|
|
1609
|
+
.reduce(
|
|
1610
|
+
(acc, part) =>
|
|
1611
|
+
acc ? acc[part] : undefined,
|
|
1612
|
+
obj
|
|
1613
|
+
);
|
|
1513
1614
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1615
|
+
const fileRelationArray =
|
|
1616
|
+
column.id.split('.');
|
|
1617
|
+
const linkUrl = column.link.url;
|
|
1618
|
+
const linkKey =
|
|
1619
|
+
rowProps.columns[columnIndex].link.key;
|
|
1620
|
+
const rowData = getNestedValue(
|
|
1621
|
+
rowProps.data,
|
|
1622
|
+
linkKey
|
|
1623
|
+
);
|
|
1518
1624
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
)
|
|
1530
|
-
|
|
1531
|
-
|
|
1625
|
+
if (column.link.hasOwnProperty('folder')) {
|
|
1626
|
+
const linkFolder =
|
|
1627
|
+
rowProps.columns[columnIndex].link
|
|
1628
|
+
.folder;
|
|
1629
|
+
|
|
1630
|
+
// Adjusted to use the nested value for checking the file relation
|
|
1631
|
+
if (
|
|
1632
|
+
getNestedValue(
|
|
1633
|
+
rowProps.data,
|
|
1634
|
+
fileRelationArray[0]
|
|
1635
|
+
)
|
|
1636
|
+
) {
|
|
1637
|
+
if (linkFolder !== '') {
|
|
1638
|
+
window.open(
|
|
1639
|
+
`${linkUrl}${rowData}/${linkFolder}`
|
|
1640
|
+
);
|
|
1641
|
+
} else {
|
|
1642
|
+
window.open(
|
|
1643
|
+
`${linkUrl}${rowData}`
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1532
1646
|
}
|
|
1647
|
+
} else {
|
|
1648
|
+
navigate(`${linkUrl}${rowData}`);
|
|
1533
1649
|
}
|
|
1534
1650
|
} else {
|
|
1535
|
-
|
|
1651
|
+
if (rowProps.data[column.id]) {
|
|
1652
|
+
window.open(
|
|
1653
|
+
rowProps.data[column.id],
|
|
1654
|
+
'_blank'
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1536
1657
|
}
|
|
1658
|
+
} else if (column.link && column.link.popup) {
|
|
1659
|
+
const linkPopup = column.link.popup;
|
|
1660
|
+
const linkKey =
|
|
1661
|
+
rowProps.columns[columnIndex].link.key;
|
|
1662
|
+
const rowData = rowProps.data[linkKey];
|
|
1663
|
+
|
|
1664
|
+
window.open(
|
|
1665
|
+
`${linkPopup}/${rowData}`,
|
|
1666
|
+
'',
|
|
1667
|
+
'width=1440,height=1024,menubar=1,resizable=1,status=1,titlebar=1,toolbar=1'
|
|
1668
|
+
);
|
|
1537
1669
|
} else {
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
const linkPopup = column.link.popup;
|
|
1547
|
-
const linkKey =
|
|
1548
|
-
rowProps.columns[columnIndex].link.key;
|
|
1549
|
-
const rowData = rowProps.data[linkKey];
|
|
1550
|
-
|
|
1551
|
-
window.open(
|
|
1552
|
-
`${linkPopup}/${rowData}`,
|
|
1553
|
-
'',
|
|
1554
|
-
'width=1440,height=1024,menubar=1,resizable=1,status=1,titlebar=1,toolbar=1'
|
|
1555
|
-
);
|
|
1556
|
-
} else {
|
|
1557
|
-
const updateSetting = settings.find(
|
|
1558
|
-
(setting) => setting.id === 'update'
|
|
1559
|
-
);
|
|
1670
|
+
console.log('🔍 Checking for update setting:', {
|
|
1671
|
+
settings: settingsRef.current,
|
|
1672
|
+
form: formRef.current,
|
|
1673
|
+
primaryKey: formRef.current?.primaryKey,
|
|
1674
|
+
rowId: rowProps.data[
|
|
1675
|
+
formRef.current?.primaryKey
|
|
1676
|
+
],
|
|
1677
|
+
});
|
|
1560
1678
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
'update',
|
|
1564
|
-
rowProps.data[form.primaryKey]
|
|
1679
|
+
const updateSetting = settingsRef.current.find(
|
|
1680
|
+
(setting) => setting.id === 'update'
|
|
1565
1681
|
);
|
|
1682
|
+
|
|
1683
|
+
console.log(
|
|
1684
|
+
'🔍 Update setting found:',
|
|
1685
|
+
updateSetting
|
|
1686
|
+
);
|
|
1687
|
+
|
|
1688
|
+
if (updateSetting) {
|
|
1689
|
+
console.log('✅ Opening modal with:', {
|
|
1690
|
+
type: 'update',
|
|
1691
|
+
id: rowProps.data[
|
|
1692
|
+
formRef.current.primaryKey
|
|
1693
|
+
],
|
|
1694
|
+
});
|
|
1695
|
+
modalOpen(
|
|
1696
|
+
'update',
|
|
1697
|
+
rowProps.data[
|
|
1698
|
+
formRef.current.primaryKey
|
|
1699
|
+
]
|
|
1700
|
+
);
|
|
1701
|
+
} else {
|
|
1702
|
+
console.warn(
|
|
1703
|
+
'⚠️ No update setting found - modal will not open'
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1566
1706
|
}
|
|
1567
1707
|
}
|
|
1568
1708
|
}
|
|
1569
1709
|
}
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1710
|
+
},
|
|
1711
|
+
[modalOpen, navigate]
|
|
1712
|
+
);
|
|
1572
1713
|
|
|
1573
1714
|
const onRenderRow = useCallback(
|
|
1574
1715
|
(rowProps) => {
|
|
@@ -1576,6 +1717,14 @@ const DataGrid = forwardRef(
|
|
|
1576
1717
|
const { onClick } = rowProps;
|
|
1577
1718
|
|
|
1578
1719
|
rowProps.onClick = (event) => {
|
|
1720
|
+
console.log('🔍 Row onClick triggered:', {
|
|
1721
|
+
rowId: rowProps.data?.id,
|
|
1722
|
+
rowData: rowProps.data,
|
|
1723
|
+
event: event,
|
|
1724
|
+
target: event.target,
|
|
1725
|
+
timestamp: new Date().toISOString(),
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1579
1728
|
// Check if this is a group row - group rows should only handle expand/collapse, not onRowClick
|
|
1580
1729
|
const isGroupRow =
|
|
1581
1730
|
rowProps.isGroupRow ||
|
|
@@ -1587,7 +1736,12 @@ const DataGrid = forwardRef(
|
|
|
1587
1736
|
'.InovuaReactDataGrid__group-cell'
|
|
1588
1737
|
);
|
|
1589
1738
|
|
|
1739
|
+
console.log('🔍 Group row check:', isGroupRow);
|
|
1740
|
+
|
|
1590
1741
|
if (isGroupRow) {
|
|
1742
|
+
console.log(
|
|
1743
|
+
'🔍 Group row detected - calling original onClick only'
|
|
1744
|
+
);
|
|
1591
1745
|
// For group rows, only allow the original onClick handler (for expand/collapse)
|
|
1592
1746
|
// Do NOT call onRowClick as it should only apply to data rows
|
|
1593
1747
|
if (onClick) {
|
|
@@ -1600,8 +1754,13 @@ const DataGrid = forwardRef(
|
|
|
1600
1754
|
'.InovuaReactDataGrid__cell'
|
|
1601
1755
|
);
|
|
1602
1756
|
|
|
1757
|
+
console.log('🔍 Cell element in row handler:', cellElement);
|
|
1758
|
+
|
|
1603
1759
|
// Add null check to prevent errors when cellElement is null
|
|
1604
1760
|
if (!cellElement) {
|
|
1761
|
+
console.warn(
|
|
1762
|
+
'⚠️ No cell element found in row handler - exiting'
|
|
1763
|
+
);
|
|
1605
1764
|
return; // Exit early if no cell element was found
|
|
1606
1765
|
}
|
|
1607
1766
|
|
|
@@ -1611,7 +1770,16 @@ const DataGrid = forwardRef(
|
|
|
1611
1770
|
|
|
1612
1771
|
const column = rowProps.columns[columnIndex];
|
|
1613
1772
|
|
|
1773
|
+
console.log('🔍 Row handler column analysis:', {
|
|
1774
|
+
columnIndex: columnIndex,
|
|
1775
|
+
columnId: column?.id,
|
|
1776
|
+
isCheckboxColumn: column?.id === '__checkbox-column',
|
|
1777
|
+
});
|
|
1778
|
+
|
|
1614
1779
|
if (column.id === '__checkbox-column') {
|
|
1780
|
+
console.log(
|
|
1781
|
+
'🔍 Checkbox column clicked - handling checkbox logic'
|
|
1782
|
+
);
|
|
1615
1783
|
// For checkbox column clicks, we need to handle them specially
|
|
1616
1784
|
// to ensure consistent behavior whether clicking checkbox or cell area
|
|
1617
1785
|
|
|
@@ -1657,10 +1825,17 @@ const DataGrid = forwardRef(
|
|
|
1657
1825
|
onClick(event);
|
|
1658
1826
|
}
|
|
1659
1827
|
} else {
|
|
1828
|
+
console.log(
|
|
1829
|
+
'🔍 Non-checkbox column - calling onRowClick'
|
|
1830
|
+
);
|
|
1660
1831
|
// For non-checkbox columns, handle normal row clicks
|
|
1661
1832
|
// But do NOT trigger checkbox selection
|
|
1662
1833
|
onRowClick(rowProps, event);
|
|
1663
1834
|
|
|
1835
|
+
console.log(
|
|
1836
|
+
'🔍 Calling original onClick handler:',
|
|
1837
|
+
!!onClick
|
|
1838
|
+
);
|
|
1664
1839
|
// Call the original onClick handler
|
|
1665
1840
|
if (onClick) {
|
|
1666
1841
|
onClick(event);
|
|
@@ -2283,6 +2458,10 @@ const DataGrid = forwardRef(
|
|
|
2283
2458
|
return null;
|
|
2284
2459
|
}
|
|
2285
2460
|
} else {
|
|
2461
|
+
console.log('🔍 Setting NOT rendered (allow=false):', {
|
|
2462
|
+
settingId: s.id,
|
|
2463
|
+
reason: 'filtered out by permissions or active conditions',
|
|
2464
|
+
});
|
|
2286
2465
|
return null;
|
|
2287
2466
|
}
|
|
2288
2467
|
|
|
@@ -2874,14 +3053,22 @@ const DataGrid = forwardRef(
|
|
|
2874
3053
|
|
|
2875
3054
|
// Function to check if any settings would be visible for any row
|
|
2876
3055
|
const hasVisibleSettings = () => {
|
|
2877
|
-
if (
|
|
2878
|
-
|
|
3056
|
+
if (memoizedSettings.length === 0) return false;
|
|
3057
|
+
|
|
2879
3058
|
// Check if any setting would be visible based on role permissions
|
|
2880
|
-
return
|
|
3059
|
+
return memoizedSettings.some((setting) => {
|
|
2881
3060
|
// Check role-based permissions
|
|
2882
|
-
if (
|
|
2883
|
-
|
|
2884
|
-
|
|
3061
|
+
if (
|
|
3062
|
+
setting.roles &&
|
|
3063
|
+
Array.isArray(setting.roles) &&
|
|
3064
|
+
setting.roles.length > 0
|
|
3065
|
+
) {
|
|
3066
|
+
const hasRequiredRole = setting.roles.some(
|
|
3067
|
+
(requiredRole) =>
|
|
3068
|
+
userProfile?.roles?.some(
|
|
3069
|
+
(userRole) =>
|
|
3070
|
+
userRole.name === requiredRole
|
|
3071
|
+
)
|
|
2885
3072
|
);
|
|
2886
3073
|
return hasRequiredRole;
|
|
2887
3074
|
}
|
|
@@ -2903,7 +3090,7 @@ const DataGrid = forwardRef(
|
|
|
2903
3090
|
render: ({ data }) => {
|
|
2904
3091
|
return (
|
|
2905
3092
|
<div className={styles.tdactions}>
|
|
2906
|
-
{
|
|
3093
|
+
{memoizedSettings.map((setting) =>
|
|
2907
3094
|
renderSetting(setting, data)
|
|
2908
3095
|
)}
|
|
2909
3096
|
</div>
|
|
@@ -2986,7 +3173,7 @@ const DataGrid = forwardRef(
|
|
|
2986
3173
|
.catch((error) => {
|
|
2987
3174
|
console.error('Error in fetching dropdown data: ', error);
|
|
2988
3175
|
});
|
|
2989
|
-
}, [columns, filterDataSource]);
|
|
3176
|
+
}, [columns, filterDataSource, memoizedSettings]);
|
|
2990
3177
|
|
|
2991
3178
|
useEffect(() => {
|
|
2992
3179
|
if (setFilterData) {
|
|
@@ -3146,7 +3333,7 @@ const DataGrid = forwardRef(
|
|
|
3146
3333
|
// Force reload of data
|
|
3147
3334
|
if (gridRef.current && gridRef.current.paginationProps) {
|
|
3148
3335
|
gridRef.current.paginationProps.reload();
|
|
3149
|
-
|
|
3336
|
+
|
|
3150
3337
|
// Force grid to recalculate row heights after reload
|
|
3151
3338
|
setTimeout(() => {
|
|
3152
3339
|
if (gridRef.current?.api) {
|
|
@@ -3284,37 +3471,69 @@ const DataGrid = forwardRef(
|
|
|
3284
3471
|
'';
|
|
3285
3472
|
|
|
3286
3473
|
// Check for important marker
|
|
3287
|
-
const checkGroupImportantMarker = (
|
|
3288
|
-
|
|
3474
|
+
const checkGroupImportantMarker = (
|
|
3475
|
+
groupValue,
|
|
3476
|
+
importantMarkerConfig
|
|
3477
|
+
) => {
|
|
3478
|
+
if (
|
|
3479
|
+
!importantMarkerConfig ||
|
|
3480
|
+
!dataRef.current?.length ||
|
|
3481
|
+
!ajaxSetting?.groupBy
|
|
3482
|
+
) {
|
|
3289
3483
|
return false;
|
|
3290
3484
|
}
|
|
3291
|
-
|
|
3292
|
-
const {
|
|
3293
|
-
|
|
3294
|
-
|
|
3485
|
+
|
|
3486
|
+
const {
|
|
3487
|
+
id: fieldId,
|
|
3488
|
+
value: expectedValue,
|
|
3489
|
+
} = importantMarkerConfig;
|
|
3490
|
+
const groupField =
|
|
3491
|
+
ajaxSetting.groupBy[0];
|
|
3492
|
+
|
|
3295
3493
|
// Get all rows that belong to this group
|
|
3296
|
-
const groupRows =
|
|
3297
|
-
(
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3494
|
+
const groupRows =
|
|
3495
|
+
dataRef.current.filter(
|
|
3496
|
+
(row) =>
|
|
3497
|
+
row[groupField] ===
|
|
3498
|
+
groupValue
|
|
3499
|
+
);
|
|
3500
|
+
|
|
3501
|
+
return groupRows.some((row) => {
|
|
3502
|
+
const fieldValue =
|
|
3503
|
+
row[fieldId];
|
|
3504
|
+
|
|
3303
3505
|
// Handle different value types
|
|
3304
|
-
if (
|
|
3305
|
-
|
|
3506
|
+
if (
|
|
3507
|
+
typeof expectedValue ===
|
|
3508
|
+
'boolean'
|
|
3509
|
+
) {
|
|
3510
|
+
return (
|
|
3511
|
+
Boolean(
|
|
3512
|
+
fieldValue
|
|
3513
|
+
) === expectedValue
|
|
3514
|
+
);
|
|
3306
3515
|
}
|
|
3307
|
-
|
|
3308
|
-
return
|
|
3516
|
+
|
|
3517
|
+
return (
|
|
3518
|
+
fieldValue ===
|
|
3519
|
+
expectedValue
|
|
3520
|
+
);
|
|
3309
3521
|
});
|
|
3310
3522
|
};
|
|
3311
3523
|
|
|
3312
|
-
const hasImportantMarker =
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3524
|
+
const hasImportantMarker =
|
|
3525
|
+
checkGroupImportantMarker(
|
|
3526
|
+
groupValue,
|
|
3527
|
+
ajaxSetting?.groupBySetting
|
|
3528
|
+
?.importantMarker
|
|
3529
|
+
);
|
|
3530
|
+
const importantMarkerColor =
|
|
3531
|
+
ajaxSetting?.groupBySetting
|
|
3532
|
+
?.importantMarker?.colour;
|
|
3533
|
+
const importantMarkerTooltip =
|
|
3534
|
+
ajaxSetting?.groupBySetting
|
|
3535
|
+
?.importantMarker?.tooltip ||
|
|
3536
|
+
'This group contains items that require attention';
|
|
3318
3537
|
|
|
3319
3538
|
// Use ReactDataGrid's built-in toggle functionality
|
|
3320
3539
|
const collapsed =
|
|
@@ -3395,11 +3614,29 @@ const DataGrid = forwardRef(
|
|
|
3395
3614
|
|
|
3396
3615
|
return (
|
|
3397
3616
|
<div
|
|
3398
|
-
className={`group-header-clean group-header-fixed-height ${
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3617
|
+
className={`group-header-clean group-header-fixed-height ${
|
|
3618
|
+
styles.groupHeaderContainer
|
|
3619
|
+
} ${
|
|
3620
|
+
hasImportantMarker
|
|
3621
|
+
? styles.groupHeaderImportant
|
|
3622
|
+
: ''
|
|
3623
|
+
}`}
|
|
3624
|
+
style={
|
|
3625
|
+
hasImportantMarker
|
|
3626
|
+
? {
|
|
3627
|
+
borderLeftColor:
|
|
3628
|
+
importantMarkerColor ||
|
|
3629
|
+
'#FF6961',
|
|
3630
|
+
background: `linear-gradient(135deg, ${
|
|
3631
|
+
importantMarkerColor ||
|
|
3632
|
+
'#FF6961'
|
|
3633
|
+
}15 0%, ${
|
|
3634
|
+
importantMarkerColor ||
|
|
3635
|
+
'#FF6961'
|
|
3636
|
+
}08 100%)`,
|
|
3637
|
+
}
|
|
3638
|
+
: {}
|
|
3639
|
+
}
|
|
3403
3640
|
onClick={
|
|
3404
3641
|
handleGroupHeaderClick
|
|
3405
3642
|
}
|
|
@@ -3577,16 +3814,25 @@ const DataGrid = forwardRef(
|
|
|
3577
3814
|
<AlertCircle
|
|
3578
3815
|
className="important-marker"
|
|
3579
3816
|
style={{
|
|
3580
|
-
display:
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3817
|
+
display:
|
|
3818
|
+
'inline-block',
|
|
3819
|
+
marginLeft:
|
|
3820
|
+
'8px',
|
|
3821
|
+
color:
|
|
3822
|
+
importantMarkerColor ||
|
|
3823
|
+
'#FF6961',
|
|
3824
|
+
position:
|
|
3825
|
+
'relative',
|
|
3826
|
+
top: '1px',
|
|
3585
3827
|
}}
|
|
3586
3828
|
size={16}
|
|
3587
|
-
title={
|
|
3829
|
+
title={
|
|
3830
|
+
importantMarkerTooltip
|
|
3831
|
+
}
|
|
3588
3832
|
data-tooltip-id="system-tooltip"
|
|
3589
|
-
data-tooltip-content={
|
|
3833
|
+
data-tooltip-content={
|
|
3834
|
+
importantMarkerTooltip
|
|
3835
|
+
}
|
|
3590
3836
|
/>
|
|
3591
3837
|
)}
|
|
3592
3838
|
</strong>
|
package/src/components/Form.jsx
CHANGED
|
@@ -125,45 +125,49 @@ function Form({
|
|
|
125
125
|
|
|
126
126
|
if (e.target.files && e.target.files.length > 0) {
|
|
127
127
|
// Find the field settings to check if multiple is enabled
|
|
128
|
-
const fieldSettings = formSettings?.fields?.find(
|
|
128
|
+
const fieldSettings = formSettings?.fields?.find(
|
|
129
|
+
(field) => field.id === id
|
|
130
|
+
);
|
|
129
131
|
const isMultiple = fieldSettings?.multiple === true;
|
|
130
|
-
|
|
132
|
+
|
|
131
133
|
// Debug logging for development
|
|
132
134
|
if (process.env.NODE_ENV === 'development') {
|
|
133
135
|
console.log('File input change:', {
|
|
134
136
|
id,
|
|
135
137
|
filesCount: e.target.files.length,
|
|
136
138
|
isMultiple,
|
|
137
|
-
fieldSettings
|
|
139
|
+
fieldSettings,
|
|
138
140
|
});
|
|
139
141
|
}
|
|
140
|
-
|
|
142
|
+
|
|
141
143
|
if (isMultiple) {
|
|
142
144
|
// For multiple file inputs, append new files to existing ones
|
|
143
145
|
setFormData((prevState) => {
|
|
144
146
|
const existingFiles = prevState[id] || [];
|
|
145
147
|
const newFiles = Array.from(e.target.files);
|
|
146
|
-
|
|
148
|
+
|
|
147
149
|
// Ensure existingFiles is always an array for multiple inputs
|
|
148
|
-
const currentFiles = Array.isArray(existingFiles)
|
|
149
|
-
|
|
150
|
+
const currentFiles = Array.isArray(existingFiles)
|
|
151
|
+
? existingFiles
|
|
152
|
+
: [];
|
|
153
|
+
|
|
150
154
|
const updatedFiles = [...currentFiles, ...newFiles];
|
|
151
|
-
|
|
155
|
+
|
|
152
156
|
// Debug logging for development
|
|
153
157
|
if (process.env.NODE_ENV === 'development') {
|
|
154
158
|
console.log('Multiple file update:', {
|
|
155
159
|
existingCount: currentFiles.length,
|
|
156
160
|
newCount: newFiles.length,
|
|
157
|
-
totalCount: updatedFiles.length
|
|
161
|
+
totalCount: updatedFiles.length,
|
|
158
162
|
});
|
|
159
163
|
}
|
|
160
|
-
|
|
164
|
+
|
|
161
165
|
return {
|
|
162
166
|
...prevState,
|
|
163
167
|
[id]: updatedFiles,
|
|
164
168
|
};
|
|
165
169
|
});
|
|
166
|
-
|
|
170
|
+
|
|
167
171
|
// Clear the input so the same files can be selected again
|
|
168
172
|
setTimeout(() => {
|
|
169
173
|
if (e.target) {
|
|
@@ -318,15 +322,19 @@ function Form({
|
|
|
318
322
|
const handleSelectOption = () => {
|
|
319
323
|
updateFormData({ [id]: inputValue });
|
|
320
324
|
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
325
|
+
// Batch update version
|
|
326
|
+
const excludedKeys = new Set(['id', 'label', 'description']);
|
|
327
|
+
const updates = {};
|
|
328
|
+
|
|
329
|
+
for (const key in inputValue) {
|
|
330
|
+
if (key in formData && !excludedKeys.has(key)) {
|
|
331
|
+
updates[key] = inputValue[key];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (Object.keys(updates).length > 0) {
|
|
336
|
+
updateFormData(updates);
|
|
337
|
+
}
|
|
330
338
|
|
|
331
339
|
formSettings.fields.forEach((field) => {
|
|
332
340
|
if (field.id === id && field.trigger) {
|
|
@@ -495,7 +503,7 @@ function Form({
|
|
|
495
503
|
const handleFileDownload = async (fileData) => {
|
|
496
504
|
try {
|
|
497
505
|
console.info('File download data:', fileData);
|
|
498
|
-
|
|
506
|
+
|
|
499
507
|
if (!fileData) {
|
|
500
508
|
toast.error('No file data available for download');
|
|
501
509
|
return;
|
|
@@ -503,7 +511,11 @@ function Form({
|
|
|
503
511
|
|
|
504
512
|
// Handle different file data structures
|
|
505
513
|
let downloadUrl = null;
|
|
506
|
-
let fileName =
|
|
514
|
+
let fileName =
|
|
515
|
+
fileData.filename ||
|
|
516
|
+
fileData.name ||
|
|
517
|
+
fileData.file_name ||
|
|
518
|
+
'download';
|
|
507
519
|
|
|
508
520
|
// Priority order for download methods:
|
|
509
521
|
// 1. Direct file URL (S3, CDN, etc.)
|
|
@@ -518,7 +530,9 @@ function Form({
|
|
|
518
530
|
downloadUrl = filePath;
|
|
519
531
|
} else {
|
|
520
532
|
// Construct URL - adjust this based on your API structure
|
|
521
|
-
downloadUrl = `/api/files/download?path=${encodeURIComponent(
|
|
533
|
+
downloadUrl = `/api/files/download?path=${encodeURIComponent(
|
|
534
|
+
filePath
|
|
535
|
+
)}`;
|
|
522
536
|
}
|
|
523
537
|
}
|
|
524
538
|
// 3. Legacy key/uuid method
|
|
@@ -529,7 +543,7 @@ function Form({
|
|
|
529
543
|
else if (fileData.id) {
|
|
530
544
|
downloadUrl = `/api/files/download/${fileData.id}`;
|
|
531
545
|
}
|
|
532
|
-
|
|
546
|
+
|
|
533
547
|
if (!downloadUrl) {
|
|
534
548
|
toast.error('Unable to determine download URL for this file');
|
|
535
549
|
return;
|
|
@@ -540,14 +554,13 @@ function Form({
|
|
|
540
554
|
link.href = downloadUrl;
|
|
541
555
|
link.download = fileName;
|
|
542
556
|
link.target = '_blank';
|
|
543
|
-
|
|
557
|
+
|
|
544
558
|
// Add to DOM, click, and remove
|
|
545
559
|
document.body.appendChild(link);
|
|
546
560
|
link.click();
|
|
547
561
|
document.body.removeChild(link);
|
|
548
|
-
|
|
562
|
+
|
|
549
563
|
toast.success(`Downloading ${fileName}...`);
|
|
550
|
-
|
|
551
564
|
} catch (error) {
|
|
552
565
|
console.error('File download error:', error);
|
|
553
566
|
toast.error('Failed to download file');
|
|
@@ -925,11 +938,19 @@ function Form({
|
|
|
925
938
|
|
|
926
939
|
// Check if formData[item.id] is null, undefined, or an empty string AND there's no auto value
|
|
927
940
|
// Note: 0 is considered a valid value for dropdowns/options
|
|
928
|
-
if (
|
|
941
|
+
if (
|
|
942
|
+
(formData[item.id] === null ||
|
|
943
|
+
formData[item.id] === undefined ||
|
|
944
|
+
formData[item.id] === '') &&
|
|
945
|
+
!autoValue
|
|
946
|
+
) {
|
|
929
947
|
// Check for required_check dependency
|
|
930
948
|
if (
|
|
931
949
|
item.required_check &&
|
|
932
|
-
(formData[item.required_check] === null ||
|
|
950
|
+
(formData[item.required_check] === null ||
|
|
951
|
+
formData[item.required_check] ===
|
|
952
|
+
undefined ||
|
|
953
|
+
formData[item.required_check] === '')
|
|
933
954
|
) {
|
|
934
955
|
validation += `${item.label} is a required field.<br/>`;
|
|
935
956
|
_inputClass[item.id] = styles.inputError;
|
|
@@ -943,7 +964,9 @@ function Form({
|
|
|
943
964
|
} else if (
|
|
944
965
|
item.required_rely &&
|
|
945
966
|
formData[item.required_rely] &&
|
|
946
|
-
(formData[item.id] === null ||
|
|
967
|
+
(formData[item.id] === null ||
|
|
968
|
+
formData[item.id] === undefined ||
|
|
969
|
+
formData[item.id] === '')
|
|
947
970
|
) {
|
|
948
971
|
// Check if the dependent required field is empty when it should be filled
|
|
949
972
|
validation += `${item.label} is a required field.<br/>`;
|
|
@@ -994,9 +1017,13 @@ function Form({
|
|
|
994
1017
|
method = formSettings.customMethod;
|
|
995
1018
|
} else {
|
|
996
1019
|
// Check if this is bulk edit mode
|
|
997
|
-
if (
|
|
1020
|
+
if (
|
|
1021
|
+
bulkEditMode &&
|
|
1022
|
+
bulkEditGroupData?.iconConfig?.formModal
|
|
1023
|
+
) {
|
|
998
1024
|
// Use bulk edit endpoint configuration
|
|
999
|
-
const formModalConfig =
|
|
1025
|
+
const formModalConfig =
|
|
1026
|
+
bulkEditGroupData.iconConfig.formModal;
|
|
1000
1027
|
url = formModalConfig.url || url;
|
|
1001
1028
|
method = formModalConfig.method || 'POST';
|
|
1002
1029
|
} else {
|
|
@@ -1020,7 +1047,9 @@ function Form({
|
|
|
1020
1047
|
}`;
|
|
1021
1048
|
} else {
|
|
1022
1049
|
url += `/${
|
|
1023
|
-
formData[
|
|
1050
|
+
formData[
|
|
1051
|
+
formSettings.primaryKey
|
|
1052
|
+
]
|
|
1024
1053
|
}`;
|
|
1025
1054
|
}
|
|
1026
1055
|
method =
|
|
@@ -1179,9 +1208,11 @@ function Form({
|
|
|
1179
1208
|
...payload,
|
|
1180
1209
|
bulkEdit: true,
|
|
1181
1210
|
groupValue: bulkEditGroupData.groupValue,
|
|
1182
|
-
groupKey:
|
|
1211
|
+
groupKey:
|
|
1212
|
+
bulkEditGroupData.iconConfig.formModal
|
|
1213
|
+
?.groupKey || ajaxSetting?.groupBy?.[0],
|
|
1183
1214
|
rowIds: bulkEditGroupData.groupRowIds,
|
|
1184
|
-
...bulkEditGroupData.iconConfig.formModal?.data
|
|
1215
|
+
...bulkEditGroupData.iconConfig.formModal?.data,
|
|
1185
1216
|
};
|
|
1186
1217
|
}
|
|
1187
1218
|
|
|
@@ -1237,7 +1268,8 @@ function Form({
|
|
|
1237
1268
|
if (resSaveAnother.data.error === '') {
|
|
1238
1269
|
toast.success(saveAnother.message);
|
|
1239
1270
|
if (fetchTable) {
|
|
1240
|
-
const createdItem =
|
|
1271
|
+
const createdItem =
|
|
1272
|
+
res.data.data || res.data;
|
|
1241
1273
|
fetchTable(createdItem);
|
|
1242
1274
|
}
|
|
1243
1275
|
}
|
|
@@ -1846,28 +1878,36 @@ function Form({
|
|
|
1846
1878
|
/>
|
|
1847
1879
|
</div>
|
|
1848
1880
|
{tableData?.dataSource?.length > 0 && (
|
|
1849
|
-
<div
|
|
1881
|
+
<div
|
|
1850
1882
|
className={`${styles.formItem} ${styles.fwItem}`}
|
|
1851
1883
|
style={{
|
|
1852
|
-
width: formSettings?.save?.return?.width
|
|
1853
|
-
? `min(${parseInt(
|
|
1884
|
+
width: formSettings?.save?.return?.width
|
|
1885
|
+
? `min(${parseInt(
|
|
1886
|
+
formSettings.save.return.width
|
|
1887
|
+
)}px, 100vw - 40px)`
|
|
1854
1888
|
: '100%',
|
|
1855
|
-
maxWidth: formSettings?.save?.return?.width
|
|
1856
|
-
? `${parseInt(
|
|
1889
|
+
maxWidth: formSettings?.save?.return?.width
|
|
1890
|
+
? `${parseInt(
|
|
1891
|
+
formSettings.save.return.width
|
|
1892
|
+
)}px`
|
|
1857
1893
|
: '100%',
|
|
1858
1894
|
overflow: 'auto',
|
|
1859
|
-
margin: formSettings?.save?.return?.width
|
|
1895
|
+
margin: formSettings?.save?.return?.width
|
|
1896
|
+
? '0 auto'
|
|
1897
|
+
: undefined,
|
|
1860
1898
|
}}
|
|
1861
1899
|
>
|
|
1862
1900
|
<ReactDataGrid
|
|
1863
1901
|
idProperty="form-table-result"
|
|
1864
1902
|
columns={tableData.columns}
|
|
1865
1903
|
dataSource={tableData.dataSource}
|
|
1866
|
-
style={{
|
|
1904
|
+
style={{
|
|
1867
1905
|
minHeight: 550,
|
|
1868
|
-
width: formSettings?.save?.return?.width
|
|
1869
|
-
? `${parseInt(
|
|
1870
|
-
|
|
1906
|
+
width: formSettings?.save?.return?.width
|
|
1907
|
+
? `${parseInt(
|
|
1908
|
+
formSettings.save.return.width
|
|
1909
|
+
)}px`
|
|
1910
|
+
: '100%',
|
|
1871
1911
|
}}
|
|
1872
1912
|
showActiveRowIndicator={false}
|
|
1873
1913
|
selected={selected}
|
|
@@ -2361,9 +2401,13 @@ function Form({
|
|
|
2361
2401
|
<div className={styles.modal__header}>
|
|
2362
2402
|
<h1>
|
|
2363
2403
|
{bulkEditMode && bulkEditGroupData
|
|
2364
|
-
? `${
|
|
2365
|
-
|
|
2366
|
-
|
|
2404
|
+
? `${
|
|
2405
|
+
bulkEditGroupData.iconConfig.formModal
|
|
2406
|
+
?.title || 'Bulk Edit'
|
|
2407
|
+
} - ${bulkEditGroupData.groupValue} (${
|
|
2408
|
+
bulkEditGroupData.groupRows.length
|
|
2409
|
+
} items)`
|
|
2410
|
+
: formSettings[formType]?.title}
|
|
2367
2411
|
</h1>
|
|
2368
2412
|
<button
|
|
2369
2413
|
className={styles.modal__close}
|
|
@@ -2398,9 +2442,12 @@ function Form({
|
|
|
2398
2442
|
}
|
|
2399
2443
|
// In bulk edit mode, only show fields specified in the formModal configuration
|
|
2400
2444
|
if (
|
|
2401
|
-
bulkEditMode &&
|
|
2402
|
-
bulkEditGroupData?.iconConfig?.formModal
|
|
2403
|
-
|
|
2445
|
+
bulkEditMode &&
|
|
2446
|
+
bulkEditGroupData?.iconConfig?.formModal
|
|
2447
|
+
?.fields &&
|
|
2448
|
+
!bulkEditGroupData.iconConfig.formModal.fields.includes(
|
|
2449
|
+
item.id
|
|
2450
|
+
)
|
|
2404
2451
|
) {
|
|
2405
2452
|
return null;
|
|
2406
2453
|
}
|