@visns-studio/visns-components 5.26.5 → 6.0.0
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 +8 -7
- package/src/components/DataGrid.jsx +86 -38
- package/src/components/Form.jsx +32 -0
- package/src/components/Navigation.jsx +2 -3
- package/src/components/TableFilter.jsx +13 -1
- package/src/components/auth/Login.jsx +1 -1
- package/src/components/auth/Reset.jsx +1 -1
- package/src/components/auth/TwoFactorAuth.jsx +1 -1
- package/src/components/auth/Verify.jsx +1 -1
- package/src/components/generic/CmsDetail.jsx +1 -1
- package/src/components/generic/CmsIndex.jsx +1 -1
- package/src/components/generic/CmsSort.jsx +1 -1
- package/src/components/generic/GenericDetail.jsx +24 -0
- package/src/components/styles/GenericIndex.module.scss +0 -1
- package/src/components/utils/Reveal.jsx +19 -0
- package/src/components/utils/debugLog.js +20 -0
package/package.json
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
"@dnd-kit/utilities": "^3.2.2",
|
|
6
6
|
"@emotion/is-prop-valid": "^1.4.0",
|
|
7
7
|
"@fontsource/barlow": "^5.2.8",
|
|
8
|
-
"@inovua/reactdatagrid-community": "^5.10.2",
|
|
9
|
-
"@inovua/reactdatagrid-enterprise": "^5.10.2",
|
|
10
8
|
"@mapbox/mapbox-gl-geocoder": "^5.1.2",
|
|
11
9
|
"@nivo/bar": "^0.87.0",
|
|
12
10
|
"@nivo/core": "^0.87.0",
|
|
@@ -17,8 +15,8 @@
|
|
|
17
15
|
"@tinymce/miniature": "^6.0.0",
|
|
18
16
|
"@tinymce/tinymce-react": "^6.3.0",
|
|
19
17
|
"@uiw/react-color": "^2.10.1",
|
|
20
|
-
"@visns-studio/visns-datagrid-community": "1.
|
|
21
|
-
"@visns-studio/visns-datagrid-enterprise": "1.
|
|
18
|
+
"@visns-studio/visns-datagrid-community": "1.2.0",
|
|
19
|
+
"@visns-studio/visns-datagrid-enterprise": "1.2.0",
|
|
22
20
|
"@vitejs/plugin-react": "^4.7.0",
|
|
23
21
|
"add": "^2.0.6",
|
|
24
22
|
"array-move": "^4.0.0",
|
|
@@ -50,7 +48,6 @@
|
|
|
50
48
|
"react-password-strength-bar": "^0.4.1",
|
|
51
49
|
"react-promise-tracker": "^2.1.1",
|
|
52
50
|
"react-quill": "^2.0.0",
|
|
53
|
-
"react-reveal": "^1.2.2",
|
|
54
51
|
"react-router-dom": "^6.30.3",
|
|
55
52
|
"react-select": "^5.10.2",
|
|
56
53
|
"react-signature-pad-wrapper": "^4.3.2",
|
|
@@ -94,7 +91,7 @@
|
|
|
94
91
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
95
92
|
},
|
|
96
93
|
"name": "@visns-studio/visns-components",
|
|
97
|
-
"version": "
|
|
94
|
+
"version": "6.0.0",
|
|
98
95
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
99
96
|
"main": "src/index.js",
|
|
100
97
|
"files": [
|
|
@@ -115,5 +112,9 @@
|
|
|
115
112
|
"url": "https://github.com/visnsstudio/visns-components/issues"
|
|
116
113
|
},
|
|
117
114
|
"homepage": "https://github.com/visnsstudio/visns-components#readme",
|
|
118
|
-
"packageManager": "yarn@4.9.2"
|
|
115
|
+
"packageManager": "yarn@4.9.2",
|
|
116
|
+
"resolutions": {
|
|
117
|
+
"@visns-studio/visns-datagrid-community": "portal:../visns-datagrid/lib/community-edition",
|
|
118
|
+
"@visns-studio/visns-datagrid-enterprise": "portal:../visns-datagrid/lib/enterprise-edition"
|
|
119
|
+
}
|
|
119
120
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import debugLog from './utils/debugLog';
|
|
1
2
|
import './styles/global.css';
|
|
2
3
|
import './styles/global-datagrid.css';
|
|
3
4
|
|
|
@@ -301,21 +302,39 @@ const DataGrid = forwardRef(
|
|
|
301
302
|
const [showImportModal, setShowImportModal] = useState(false);
|
|
302
303
|
|
|
303
304
|
/** Modal Functions */
|
|
305
|
+
// Mirrors modalShow for use inside the dataSource closure (state
|
|
306
|
+
// would be stale there); used by the requireFirstEntry flow.
|
|
307
|
+
const modalShowRef = useRef(false);
|
|
308
|
+
|
|
304
309
|
const modalOpen = (formType, formId) => {
|
|
310
|
+
modalShowRef.current = true;
|
|
305
311
|
setModalShow(true);
|
|
306
312
|
setFormType(formType);
|
|
307
313
|
setFormId(formId);
|
|
308
314
|
};
|
|
309
315
|
|
|
310
316
|
const modalClose = () => {
|
|
317
|
+
modalShowRef.current = false;
|
|
311
318
|
setModalShow(false);
|
|
312
319
|
setBulkEditMode(false);
|
|
313
320
|
setBulkEditGroupData(null);
|
|
321
|
+
|
|
322
|
+
// Opt-in (form.create.requireFirstEntry): this grid must not be
|
|
323
|
+
// left empty. Closing the form while no rows exist triggers a
|
|
324
|
+
// reload, whose empty result reopens the create form — the only
|
|
325
|
+
// way out is saving at least one entry (or leaving the page).
|
|
326
|
+
if (
|
|
327
|
+
form?.create?.requireFirstEntry &&
|
|
328
|
+
Array.isArray(dataRef.current) &&
|
|
329
|
+
dataRef.current.length === 0
|
|
330
|
+
) {
|
|
331
|
+
handleReload();
|
|
332
|
+
}
|
|
314
333
|
};
|
|
315
334
|
|
|
316
335
|
/** Import Modal Functions */
|
|
317
336
|
const handleBulkUploadClick = () => {
|
|
318
|
-
|
|
337
|
+
debugLog('[DataGrid] Opening import modal with:', {
|
|
319
338
|
paramValue,
|
|
320
339
|
pageData,
|
|
321
340
|
routeParams,
|
|
@@ -555,6 +574,35 @@ const DataGrid = forwardRef(
|
|
|
555
574
|
}
|
|
556
575
|
dataRef.current = res.data;
|
|
557
576
|
|
|
577
|
+
// Opt-in (form.create.requireFirstEntry): when a clean
|
|
578
|
+
// load (no search/filter narrowing) finds no rows, open
|
|
579
|
+
// the create form automatically so the user is walked
|
|
580
|
+
// straight into entering the first record. Combined with
|
|
581
|
+
// modalClose's reload-when-empty, dismissing the form
|
|
582
|
+
// just brings it back until one entry is saved.
|
|
583
|
+
if (
|
|
584
|
+
form?.create?.requireFirstEntry &&
|
|
585
|
+
Array.isArray(res.data) &&
|
|
586
|
+
res.data.length === 0 &&
|
|
587
|
+
!modalShowRef.current &&
|
|
588
|
+
!dSearch &&
|
|
589
|
+
!(
|
|
590
|
+
Array.isArray(params.filterValue) &&
|
|
591
|
+
params.filterValue.some(
|
|
592
|
+
(f) =>
|
|
593
|
+
f?.value !== null &&
|
|
594
|
+
f?.value !== undefined &&
|
|
595
|
+
f?.value !== ''
|
|
596
|
+
)
|
|
597
|
+
)
|
|
598
|
+
) {
|
|
599
|
+
toast.warning(
|
|
600
|
+
form.create.requireFirstEntryMessage ||
|
|
601
|
+
'At least one entry is required — please add the first record.'
|
|
602
|
+
);
|
|
603
|
+
modalOpen('create', null);
|
|
604
|
+
}
|
|
605
|
+
|
|
558
606
|
// Opt-in grouped display (ajaxSetting.treeGroupBy {key,
|
|
559
607
|
// column, parentFields, countLabel}): consecutive rows
|
|
560
608
|
// sharing a non-empty `key` value get a synthetic summary
|
|
@@ -687,7 +735,7 @@ const DataGrid = forwardRef(
|
|
|
687
735
|
|
|
688
736
|
// Get default limit from userProfile preferences, tableSetting, or default to 25
|
|
689
737
|
const getDefaultLimit = () => {
|
|
690
|
-
|
|
738
|
+
debugLog('[DataGrid] getDefaultLimit called:', {
|
|
691
739
|
userProfile_rows_per_page: userProfile?.rows_per_page,
|
|
692
740
|
userProfile_settings_rows_per_page: userProfile?.settings?.rows_per_page,
|
|
693
741
|
tableSetting_pageSize: tableSetting?.pageSize,
|
|
@@ -701,7 +749,7 @@ const DataGrid = forwardRef(
|
|
|
701
749
|
|
|
702
750
|
const [limit, setLimit] = useState(() => {
|
|
703
751
|
const initialLimit = getDefaultLimit();
|
|
704
|
-
|
|
752
|
+
debugLog('[DataGrid] Initial limit set to:', initialLimit);
|
|
705
753
|
return initialLimit;
|
|
706
754
|
});
|
|
707
755
|
const [search, setSearch] = useState('');
|
|
@@ -713,7 +761,7 @@ const DataGrid = forwardRef(
|
|
|
713
761
|
|
|
714
762
|
// Effect to update limit when userProfile becomes available (for async loaded profiles)
|
|
715
763
|
useEffect(() => {
|
|
716
|
-
|
|
764
|
+
debugLog('[DataGrid] useEffect triggered:', {
|
|
717
765
|
initialLimitApplied: initialLimitApplied.current,
|
|
718
766
|
userProfile_rows_per_page: userProfile?.rows_per_page,
|
|
719
767
|
userProfile_settings_rows_per_page: userProfile?.settings?.rows_per_page,
|
|
@@ -722,9 +770,9 @@ const DataGrid = forwardRef(
|
|
|
722
770
|
// Only apply the user's preference once, when userProfile first becomes available
|
|
723
771
|
if (!initialLimitApplied.current) {
|
|
724
772
|
const userLimit = userProfile?.rows_per_page || userProfile?.settings?.rows_per_page;
|
|
725
|
-
|
|
773
|
+
debugLog('[DataGrid] Checking userLimit:', userLimit);
|
|
726
774
|
if (userLimit && userLimit > 0) {
|
|
727
|
-
|
|
775
|
+
debugLog('[DataGrid] Setting limit to userLimit:', userLimit);
|
|
728
776
|
setLimit(userLimit);
|
|
729
777
|
initialLimitApplied.current = true;
|
|
730
778
|
}
|
|
@@ -1699,17 +1747,17 @@ const DataGrid = forwardRef(
|
|
|
1699
1747
|
|
|
1700
1748
|
switch (iconConfig.id) {
|
|
1701
1749
|
case 'clock':
|
|
1702
|
-
|
|
1750
|
+
debugLog(
|
|
1703
1751
|
`Starting timer action for group: ${groupValue}`
|
|
1704
1752
|
);
|
|
1705
1753
|
break;
|
|
1706
1754
|
case 'alarm':
|
|
1707
|
-
|
|
1755
|
+
debugLog(
|
|
1708
1756
|
`Setting alarm action for group: ${groupValue}`
|
|
1709
1757
|
);
|
|
1710
1758
|
break;
|
|
1711
1759
|
case 'check':
|
|
1712
|
-
|
|
1760
|
+
debugLog(
|
|
1713
1761
|
`Mark complete action for group: ${groupValue}`
|
|
1714
1762
|
);
|
|
1715
1763
|
break;
|
|
@@ -1734,18 +1782,18 @@ const DataGrid = forwardRef(
|
|
|
1734
1782
|
// Open modal for bulk editing
|
|
1735
1783
|
modalOpen('update', null);
|
|
1736
1784
|
} else {
|
|
1737
|
-
|
|
1785
|
+
debugLog(
|
|
1738
1786
|
`Bulk edit action for group: ${groupValue}`
|
|
1739
1787
|
);
|
|
1740
1788
|
}
|
|
1741
1789
|
break;
|
|
1742
1790
|
case 'envelope':
|
|
1743
|
-
|
|
1791
|
+
debugLog(
|
|
1744
1792
|
`Send notification action for group: ${groupValue}`
|
|
1745
1793
|
);
|
|
1746
1794
|
break;
|
|
1747
1795
|
default:
|
|
1748
|
-
|
|
1796
|
+
debugLog(
|
|
1749
1797
|
`Custom action ${iconConfig.id} for group: ${groupValue}`
|
|
1750
1798
|
);
|
|
1751
1799
|
break;
|
|
@@ -1917,7 +1965,7 @@ const DataGrid = forwardRef(
|
|
|
1917
1965
|
*/
|
|
1918
1966
|
const onRowClick = useCallback(
|
|
1919
1967
|
(rowProps, event) => {
|
|
1920
|
-
|
|
1968
|
+
debugLog('🔍 DataGrid onRowClick triggered:', {
|
|
1921
1969
|
rowData: rowProps.data,
|
|
1922
1970
|
event: event,
|
|
1923
1971
|
target: event.target,
|
|
@@ -1928,7 +1976,7 @@ const DataGrid = forwardRef(
|
|
|
1928
1976
|
'.InovuaReactDataGrid__cell'
|
|
1929
1977
|
);
|
|
1930
1978
|
|
|
1931
|
-
|
|
1979
|
+
debugLog('🔍 Cell element found:', cellElement);
|
|
1932
1980
|
|
|
1933
1981
|
// Add null check to prevent errors when cellElement is null
|
|
1934
1982
|
if (!cellElement) {
|
|
@@ -1942,7 +1990,7 @@ const DataGrid = forwardRef(
|
|
|
1942
1990
|
cellElement.parentNode.children
|
|
1943
1991
|
).indexOf(cellElement);
|
|
1944
1992
|
|
|
1945
|
-
|
|
1993
|
+
debugLog('🔍 Column analysis:', {
|
|
1946
1994
|
columnIndex: columnIndex,
|
|
1947
1995
|
totalColumns: rowProps.columns.length,
|
|
1948
1996
|
isLastColumn: columnIndex === rowProps.columns.length - 1,
|
|
@@ -1956,11 +2004,11 @@ const DataGrid = forwardRef(
|
|
|
1956
2004
|
rowProps.columns.length > 1 &&
|
|
1957
2005
|
columnIndex === rowProps.columns.length - 1
|
|
1958
2006
|
) {
|
|
1959
|
-
|
|
2007
|
+
debugLog('🔍 Last column clicked - no action');
|
|
1960
2008
|
// Handle last column click
|
|
1961
2009
|
} else {
|
|
1962
2010
|
const column = rowProps.columns[columnIndex];
|
|
1963
|
-
|
|
2011
|
+
debugLog('🔍 Column clicked:', {
|
|
1964
2012
|
columnId: column?.id,
|
|
1965
2013
|
columnType: column?.type,
|
|
1966
2014
|
hasLink: !!column?.link,
|
|
@@ -2041,7 +2089,7 @@ const DataGrid = forwardRef(
|
|
|
2041
2089
|
'width=1440,height=1024,menubar=1,resizable=1,status=1,titlebar=1,toolbar=1'
|
|
2042
2090
|
);
|
|
2043
2091
|
} else {
|
|
2044
|
-
|
|
2092
|
+
debugLog('🔍 Checking for update setting:', {
|
|
2045
2093
|
settings: settingsRef.current,
|
|
2046
2094
|
form: formRef.current,
|
|
2047
2095
|
primaryKey: formRef.current?.primaryKey,
|
|
@@ -2054,13 +2102,13 @@ const DataGrid = forwardRef(
|
|
|
2054
2102
|
(setting) => setting.id === 'update'
|
|
2055
2103
|
);
|
|
2056
2104
|
|
|
2057
|
-
|
|
2105
|
+
debugLog(
|
|
2058
2106
|
'🔍 Update setting found:',
|
|
2059
2107
|
updateSetting
|
|
2060
2108
|
);
|
|
2061
2109
|
|
|
2062
2110
|
if (updateSetting) {
|
|
2063
|
-
|
|
2111
|
+
debugLog('✅ Opening modal with:', {
|
|
2064
2112
|
type: 'update',
|
|
2065
2113
|
id: rowProps.data[
|
|
2066
2114
|
formRef.current.primaryKey
|
|
@@ -2091,7 +2139,7 @@ const DataGrid = forwardRef(
|
|
|
2091
2139
|
const { onClick } = rowProps;
|
|
2092
2140
|
|
|
2093
2141
|
rowProps.onClick = (event) => {
|
|
2094
|
-
|
|
2142
|
+
debugLog('🔍 Row onClick triggered:', {
|
|
2095
2143
|
rowId: rowProps.data?.id,
|
|
2096
2144
|
rowData: rowProps.data,
|
|
2097
2145
|
event: event,
|
|
@@ -2110,10 +2158,10 @@ const DataGrid = forwardRef(
|
|
|
2110
2158
|
'.InovuaReactDataGrid__group-cell'
|
|
2111
2159
|
);
|
|
2112
2160
|
|
|
2113
|
-
|
|
2161
|
+
debugLog('🔍 Group row check:', isGroupRow);
|
|
2114
2162
|
|
|
2115
2163
|
if (isGroupRow) {
|
|
2116
|
-
|
|
2164
|
+
debugLog(
|
|
2117
2165
|
'🔍 Group row detected - calling original onClick only'
|
|
2118
2166
|
);
|
|
2119
2167
|
// For group rows, only allow the original onClick handler (for expand/collapse)
|
|
@@ -2128,7 +2176,7 @@ const DataGrid = forwardRef(
|
|
|
2128
2176
|
'.InovuaReactDataGrid__cell'
|
|
2129
2177
|
);
|
|
2130
2178
|
|
|
2131
|
-
|
|
2179
|
+
debugLog('🔍 Cell element in row handler:', cellElement);
|
|
2132
2180
|
|
|
2133
2181
|
// Add null check to prevent errors when cellElement is null
|
|
2134
2182
|
if (!cellElement) {
|
|
@@ -2144,14 +2192,14 @@ const DataGrid = forwardRef(
|
|
|
2144
2192
|
|
|
2145
2193
|
const column = rowProps.columns[columnIndex];
|
|
2146
2194
|
|
|
2147
|
-
|
|
2195
|
+
debugLog('🔍 Row handler column analysis:', {
|
|
2148
2196
|
columnIndex: columnIndex,
|
|
2149
2197
|
columnId: column?.id,
|
|
2150
2198
|
isCheckboxColumn: column?.id === '__checkbox-column',
|
|
2151
2199
|
});
|
|
2152
2200
|
|
|
2153
2201
|
if (column.id === '__checkbox-column') {
|
|
2154
|
-
|
|
2202
|
+
debugLog(
|
|
2155
2203
|
'🔍 Checkbox column clicked - handling checkbox logic'
|
|
2156
2204
|
);
|
|
2157
2205
|
// For checkbox column clicks, we need to handle them specially
|
|
@@ -2199,14 +2247,14 @@ const DataGrid = forwardRef(
|
|
|
2199
2247
|
onClick(event);
|
|
2200
2248
|
}
|
|
2201
2249
|
} else {
|
|
2202
|
-
|
|
2250
|
+
debugLog(
|
|
2203
2251
|
'🔍 Non-checkbox column - calling onRowClick'
|
|
2204
2252
|
);
|
|
2205
2253
|
// For non-checkbox columns, handle normal row clicks
|
|
2206
2254
|
// But do NOT trigger checkbox selection
|
|
2207
2255
|
onRowClick(rowProps, event);
|
|
2208
2256
|
|
|
2209
|
-
|
|
2257
|
+
debugLog(
|
|
2210
2258
|
'🔍 Calling original onClick handler:',
|
|
2211
2259
|
!!onClick
|
|
2212
2260
|
);
|
|
@@ -2787,12 +2835,12 @@ const DataGrid = forwardRef(
|
|
|
2787
2835
|
// Check function-based condition if it exists
|
|
2788
2836
|
if (allow && s.condition && typeof s.condition === 'function') {
|
|
2789
2837
|
try {
|
|
2790
|
-
|
|
2838
|
+
debugLog('🔍 DataGrid: Evaluating condition for setting:', s.id, 'with data:', d);
|
|
2791
2839
|
const conditionResult = s.condition(d);
|
|
2792
|
-
|
|
2840
|
+
debugLog('🔍 DataGrid: Condition result:', conditionResult, 'for setting:', s.id);
|
|
2793
2841
|
if (!conditionResult) {
|
|
2794
2842
|
allow = false;
|
|
2795
|
-
|
|
2843
|
+
debugLog('🔍 DataGrid: Setting blocked by condition:', s.id);
|
|
2796
2844
|
}
|
|
2797
2845
|
} catch (error) {
|
|
2798
2846
|
console.warn('🔍 DataGrid: Condition function error for setting:', s.id, error);
|
|
@@ -2852,7 +2900,7 @@ const DataGrid = forwardRef(
|
|
|
2852
2900
|
return null;
|
|
2853
2901
|
}
|
|
2854
2902
|
} else {
|
|
2855
|
-
|
|
2903
|
+
debugLog('🔍 Setting NOT rendered (allow=false):', {
|
|
2856
2904
|
settingId: s.id,
|
|
2857
2905
|
reason: 'filtered out by permissions or active conditions',
|
|
2858
2906
|
});
|
|
@@ -3420,7 +3468,7 @@ const DataGrid = forwardRef(
|
|
|
3420
3468
|
shouldAutoEnableWordWrap &&
|
|
3421
3469
|
column.wordWrap !== true
|
|
3422
3470
|
) {
|
|
3423
|
-
|
|
3471
|
+
debugLog(
|
|
3424
3472
|
`DataGrid: Auto-enabled wordWrap for column "${columnId}" (${column.label})`
|
|
3425
3473
|
);
|
|
3426
3474
|
}
|
|
@@ -3445,7 +3493,7 @@ const DataGrid = forwardRef(
|
|
|
3445
3493
|
|
|
3446
3494
|
// Debug logging for wordWrap cell rendering
|
|
3447
3495
|
if (column.wordWrap === true) {
|
|
3448
|
-
|
|
3496
|
+
debugLog(
|
|
3449
3497
|
'DataGrid DEBUG - Rendering WordWrap Cell:',
|
|
3450
3498
|
{
|
|
3451
3499
|
columnId,
|
|
@@ -3827,7 +3875,7 @@ const DataGrid = forwardRef(
|
|
|
3827
3875
|
|
|
3828
3876
|
if (userPreferredLimit && userPreferredLimit > 0) {
|
|
3829
3877
|
// User has a preference, don't override it
|
|
3830
|
-
|
|
3878
|
+
debugLog('[DataGrid] Respecting user preference, not auto-calculating limit:', userPreferredLimit);
|
|
3831
3879
|
} else if (ajaxSetting?.take >= 35) {
|
|
3832
3880
|
setLimit(ajaxSetting.take);
|
|
3833
3881
|
} else {
|
|
@@ -4033,7 +4081,7 @@ const DataGrid = forwardRef(
|
|
|
4033
4081
|
} else {
|
|
4034
4082
|
newState[groupValue] = true;
|
|
4035
4083
|
}
|
|
4036
|
-
|
|
4084
|
+
debugLog(
|
|
4037
4085
|
'Updated collapsed groups:',
|
|
4038
4086
|
newState
|
|
4039
4087
|
);
|
|
@@ -4152,7 +4200,7 @@ const DataGrid = forwardRef(
|
|
|
4152
4200
|
if (
|
|
4153
4201
|
groupClickTimeoutRef.current
|
|
4154
4202
|
) {
|
|
4155
|
-
|
|
4203
|
+
debugLog(
|
|
4156
4204
|
'🖱️ Ignoring rapid click'
|
|
4157
4205
|
);
|
|
4158
4206
|
return;
|
|
@@ -4167,7 +4215,7 @@ const DataGrid = forwardRef(
|
|
|
4167
4215
|
'input[type="checkbox"]'
|
|
4168
4216
|
)
|
|
4169
4217
|
) {
|
|
4170
|
-
|
|
4218
|
+
debugLog(
|
|
4171
4219
|
'🖱️ Ignoring click on expand button or checkbox'
|
|
4172
4220
|
);
|
|
4173
4221
|
return;
|
package/src/components/Form.jsx
CHANGED
|
@@ -2416,6 +2416,38 @@ function Form({
|
|
|
2416
2416
|
toast.error('Failed to fetch data:', error);
|
|
2417
2417
|
}
|
|
2418
2418
|
} else {
|
|
2419
|
+
// Bulk edit opens without a record fetch (columnId is null), so
|
|
2420
|
+
// seed each field with the value the group rows already share —
|
|
2421
|
+
// otherwise saved values (e.g. an assigned user) render blank and
|
|
2422
|
+
// required fields force a re-pick on every open.
|
|
2423
|
+
if (bulkEditMode && bulkEditGroupData?.groupRows?.length) {
|
|
2424
|
+
const modalFields =
|
|
2425
|
+
bulkEditGroupData.iconConfig?.formModal?.fields || [];
|
|
2426
|
+
|
|
2427
|
+
formSettings.fields.forEach((item) => {
|
|
2428
|
+
const { id, type, parent } = item;
|
|
2429
|
+
|
|
2430
|
+
if (parent) return;
|
|
2431
|
+
if (modalFields.length && !modalFields.includes(id)) return;
|
|
2432
|
+
|
|
2433
|
+
const values = bulkEditGroupData.groupRows.map(
|
|
2434
|
+
(row) => row?.[id]
|
|
2435
|
+
);
|
|
2436
|
+
const first = values[0];
|
|
2437
|
+
const isShared =
|
|
2438
|
+
first !== undefined &&
|
|
2439
|
+
first !== null &&
|
|
2440
|
+
first !== '' &&
|
|
2441
|
+
values.every((value) => value === first);
|
|
2442
|
+
|
|
2443
|
+
if (isShared) {
|
|
2444
|
+
_formData[id] = ['date', 'datetime'].includes(type)
|
|
2445
|
+
? moment(first).toDate()
|
|
2446
|
+
: first;
|
|
2447
|
+
}
|
|
2448
|
+
});
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2419
2451
|
setFormData(_formData);
|
|
2420
2452
|
}
|
|
2421
2453
|
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
UserSearch,
|
|
12
12
|
ClipboardCheck,
|
|
13
13
|
LogOut,
|
|
14
|
+
Settings,
|
|
14
15
|
Shield,
|
|
15
16
|
Image,
|
|
16
17
|
Phone,
|
|
@@ -238,6 +239,7 @@ function Navigation({
|
|
|
238
239
|
phone: Phone,
|
|
239
240
|
shield: Shield,
|
|
240
241
|
signOut: LogOut,
|
|
242
|
+
settings: Settings,
|
|
241
243
|
search: Search,
|
|
242
244
|
userSearch: UserSearch,
|
|
243
245
|
clipboardCheck: ClipboardCheck,
|
|
@@ -259,7 +261,6 @@ function Navigation({
|
|
|
259
261
|
return (
|
|
260
262
|
<li>
|
|
261
263
|
<button
|
|
262
|
-
title="Scan Business Card"
|
|
263
264
|
data-tooltip-id="system-tooltip"
|
|
264
265
|
data-tooltip-content="Scan Business Card"
|
|
265
266
|
onClick={() => setShowBusinessCardOcr(true)}
|
|
@@ -284,7 +285,6 @@ function Navigation({
|
|
|
284
285
|
return (
|
|
285
286
|
<li>
|
|
286
287
|
<button
|
|
287
|
-
title={n.label}
|
|
288
288
|
data-tooltip-id="system-tooltip"
|
|
289
289
|
data-tooltip-content={n.label}
|
|
290
290
|
onClick={handleLogout}
|
|
@@ -305,7 +305,6 @@ function Navigation({
|
|
|
305
305
|
data-tooltip-content={n.label}
|
|
306
306
|
className={`${activeClass}`}
|
|
307
307
|
target={n.target ? n.target : '_self'}
|
|
308
|
-
title={n.label}
|
|
309
308
|
>
|
|
310
309
|
{IconComponent && (
|
|
311
310
|
<IconComponent size={19} strokeWidth={2} />
|
|
@@ -373,7 +373,19 @@ function TableFilter({
|
|
|
373
373
|
)
|
|
374
374
|
)
|
|
375
375
|
) {
|
|
376
|
-
|
|
376
|
+
// Items with target "_self" navigate in the same tab; the
|
|
377
|
+
// default remains a new window for backward compatibility.
|
|
378
|
+
const clickedItem =
|
|
379
|
+
filters.find((f) => f.id === id) ||
|
|
380
|
+
filters
|
|
381
|
+
.flatMap((f) => f.children || [])
|
|
382
|
+
.find((c) => c.id === id);
|
|
383
|
+
|
|
384
|
+
if (clickedItem?.target === '_self') {
|
|
385
|
+
window.location.assign(url);
|
|
386
|
+
} else {
|
|
387
|
+
window.open(url);
|
|
388
|
+
}
|
|
377
389
|
}
|
|
378
390
|
|
|
379
391
|
// Calculate the updated settings
|
|
@@ -2,7 +2,7 @@ import '../styles/global.css';
|
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
4
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
|
5
|
-
import Reveal from '
|
|
5
|
+
import Reveal from '../utils/Reveal';
|
|
6
6
|
import { toast } from 'react-toastify';
|
|
7
7
|
import { User, Lock } from 'lucide-react';
|
|
8
8
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../styles/global.css';
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
|
-
import Reveal from '
|
|
4
|
+
import Reveal from '../utils/Reveal';
|
|
5
5
|
import { toast } from 'react-toastify';
|
|
6
6
|
import parse from 'html-react-parser';
|
|
7
7
|
import { User } from 'lucide-react';
|
|
@@ -2,7 +2,7 @@ import '../styles/global.css';
|
|
|
2
2
|
|
|
3
3
|
import React, { useState, useEffect } from 'react';
|
|
4
4
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
5
|
-
import Reveal from '
|
|
5
|
+
import Reveal from '../utils/Reveal';
|
|
6
6
|
import { toast } from 'react-toastify';
|
|
7
7
|
import { Lock } from 'lucide-react';
|
|
8
8
|
|
|
@@ -5,7 +5,7 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|
|
5
5
|
import { toast } from 'react-toastify';
|
|
6
6
|
import parse from 'html-react-parser';
|
|
7
7
|
import validator from 'validator';
|
|
8
|
-
import Reveal from '
|
|
8
|
+
import Reveal from '../utils/Reveal';
|
|
9
9
|
|
|
10
10
|
import CustomFetch from '../Fetch';
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { Outlet } from 'react-router-dom';
|
|
3
|
-
import Reveal from '
|
|
3
|
+
import Reveal from '../utils/Reveal';
|
|
4
4
|
import DataGrid from '../DataGrid';
|
|
5
5
|
|
|
6
6
|
function CmsIndex({ setting, userProfile }) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { useLocation } from 'react-router-dom';
|
|
3
|
-
import Reveal from '
|
|
3
|
+
import Reveal from '../utils/Reveal';
|
|
4
4
|
import { arrayMoveImmutable } from 'array-move';
|
|
5
5
|
import { toast } from 'react-toastify';
|
|
6
6
|
import CustomFetch from '../Fetch';
|
|
@@ -2994,6 +2994,14 @@ function GenericDetail({
|
|
|
2994
2994
|
{activeTabConfig.export && (
|
|
2995
2995
|
<button
|
|
2996
2996
|
className={styles.btn}
|
|
2997
|
+
style={{
|
|
2998
|
+
display: 'inline-flex',
|
|
2999
|
+
alignItems: 'center',
|
|
3000
|
+
justifyContent: 'center',
|
|
3001
|
+
gap: '7px',
|
|
3002
|
+
minWidth: '92px',
|
|
3003
|
+
height: '42px',
|
|
3004
|
+
}}
|
|
2997
3005
|
onClick={() =>
|
|
2998
3006
|
handleExport(
|
|
2999
3007
|
activeTabConfig.export
|
|
@@ -3008,6 +3016,14 @@ function GenericDetail({
|
|
|
3008
3016
|
{activeTabConfig.customAction && (
|
|
3009
3017
|
<button
|
|
3010
3018
|
className={styles.btn}
|
|
3019
|
+
style={{
|
|
3020
|
+
display: 'inline-flex',
|
|
3021
|
+
alignItems: 'center',
|
|
3022
|
+
justifyContent: 'center',
|
|
3023
|
+
gap: '7px',
|
|
3024
|
+
minWidth: '92px',
|
|
3025
|
+
height: '42px',
|
|
3026
|
+
}}
|
|
3011
3027
|
onClick={() =>
|
|
3012
3028
|
handleCustomAction(
|
|
3013
3029
|
activeTabConfig.customAction
|
|
@@ -3025,6 +3041,14 @@ function GenericDetail({
|
|
|
3025
3041
|
<button
|
|
3026
3042
|
key={index}
|
|
3027
3043
|
className={styles.btn}
|
|
3044
|
+
style={{
|
|
3045
|
+
display: 'inline-flex',
|
|
3046
|
+
alignItems: 'center',
|
|
3047
|
+
justifyContent: 'center',
|
|
3048
|
+
gap: '7px',
|
|
3049
|
+
minWidth: '92px',
|
|
3050
|
+
height: '42px',
|
|
3051
|
+
}}
|
|
3028
3052
|
onClick={() =>
|
|
3029
3053
|
handleCustomAction(customAction)
|
|
3030
3054
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drop-in replacement for react-reveal's <Reveal effect="...">, which is
|
|
5
|
+
* unmaintained and depends on legacy React lifecycles/context removed in
|
|
6
|
+
* React 19. The effect class (e.g. "fadeInUp") is expected to define its
|
|
7
|
+
* own @keyframes/duration in the consuming app's CSS; its animation runs
|
|
8
|
+
* when the element mounts.
|
|
9
|
+
*/
|
|
10
|
+
const Reveal = ({ effect = '', className = '', children, ...rest }) => (
|
|
11
|
+
<div
|
|
12
|
+
className={[effect, className].filter(Boolean).join(' ')}
|
|
13
|
+
{...rest}
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export default Reveal;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in debug logger. Silent by default so component internals don't spam
|
|
3
|
+
* the console; enable in a browser with:
|
|
4
|
+
* localStorage.setItem('visns-debug', '1')
|
|
5
|
+
* and disable with localStorage.removeItem('visns-debug').
|
|
6
|
+
*/
|
|
7
|
+
const debugLog = (...args) => {
|
|
8
|
+
try {
|
|
9
|
+
if (
|
|
10
|
+
typeof window !== 'undefined' &&
|
|
11
|
+
window.localStorage?.getItem('visns-debug')
|
|
12
|
+
) {
|
|
13
|
+
console.log(...args);
|
|
14
|
+
}
|
|
15
|
+
} catch (e) {
|
|
16
|
+
// Storage unavailable (private mode / SSR) — stay silent
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default debugLog;
|