@topconsultnpm/sdkui-react 6.20.0-dev1.3 → 6.20.0-dev1.31
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/lib/components/NewComponents/ContextMenu/TMContextMenu.js +29 -11
- package/lib/components/NewComponents/ContextMenu/hooks.d.ts +1 -0
- package/lib/components/NewComponents/ContextMenu/hooks.js +8 -4
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +5 -1
- package/lib/components/NewComponents/ContextMenu/styles.js +56 -28
- package/lib/components/NewComponents/ContextMenu/types.d.ts +10 -0
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +39 -51
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +8 -0
- package/lib/components/NewComponents/FloatingMenuBar/styles.js +29 -19
- package/lib/components/NewComponents/FloatingMenuBar/types.d.ts +0 -1
- package/lib/components/base/TMAccordion.js +2 -2
- package/lib/components/base/TMCustomButton.js +61 -17
- package/lib/components/base/TMDataGrid.d.ts +5 -2
- package/lib/components/base/TMDataGrid.js +98 -7
- package/lib/components/editors/TMHtmlEditor.js +1 -1
- package/lib/components/editors/TMMetadataValues.js +20 -2
- package/lib/components/features/documents/TMDcmtBlog.d.ts +1 -7
- package/lib/components/features/documents/TMDcmtBlog.js +29 -2
- package/lib/components/features/documents/TMDcmtForm.js +268 -168
- package/lib/components/features/documents/TMDcmtPreview.js +37 -66
- package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +8 -0
- package/lib/components/features/search/{TMSearchResultCheckoutInfoForm.js → TMDcmtCheckoutInfoForm.js} +6 -11
- package/lib/components/features/search/TMSearchQueryPanel.js +13 -12
- package/lib/components/features/search/TMSearchResult.js +66 -117
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +3 -3
- package/lib/components/features/search/TMSearchResultsMenuItems.js +163 -180
- package/lib/components/features/search/TMSignatureInfoContent.d.ts +6 -0
- package/lib/components/features/search/TMSignatureInfoContent.js +140 -0
- package/lib/components/forms/Login/LoginValidatorService.d.ts +2 -0
- package/lib/components/forms/Login/LoginValidatorService.js +7 -2
- package/lib/components/forms/Login/TMLoginForm.js +34 -6
- package/lib/css/tm-sdkui.css +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +12 -14
- package/lib/helper/SDKUI_Globals.js +8 -0
- package/lib/helper/SDKUI_Localizator.d.ts +8 -0
- package/lib/helper/SDKUI_Localizator.js +98 -0
- package/lib/helper/TMPdfViewer.d.ts +8 -0
- package/lib/helper/TMPdfViewer.js +187 -0
- package/lib/helper/TMUtils.d.ts +3 -1
- package/lib/helper/TMUtils.js +51 -0
- package/lib/helper/checkinCheckoutManager.d.ts +85 -0
- package/lib/helper/checkinCheckoutManager.js +348 -0
- package/lib/helper/devextremeCustomMessages.d.ts +30 -0
- package/lib/helper/devextremeCustomMessages.js +30 -0
- package/lib/helper/helpers.js +7 -1
- package/lib/helper/index.d.ts +2 -0
- package/lib/helper/index.js +2 -0
- package/lib/helper/queryHelper.js +29 -0
- package/lib/hooks/useCheckInOutOperations.d.ts +28 -0
- package/lib/hooks/useCheckInOutOperations.js +223 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +5 -2
- package/lib/components/NewComponents/Notification/Notification.d.ts +0 -4
- package/lib/components/NewComponents/Notification/Notification.js +0 -60
- package/lib/components/NewComponents/Notification/NotificationContainer.d.ts +0 -8
- package/lib/components/NewComponents/Notification/NotificationContainer.js +0 -33
- package/lib/components/NewComponents/Notification/index.d.ts +0 -2
- package/lib/components/NewComponents/Notification/index.js +0 -2
- package/lib/components/NewComponents/Notification/styles.d.ts +0 -21
- package/lib/components/NewComponents/Notification/styles.js +0 -180
- package/lib/components/NewComponents/Notification/types.d.ts +0 -18
- package/lib/components/NewComponents/Notification/types.js +0 -1
- package/lib/components/features/search/TMSearchResultCheckoutInfoForm.d.ts +0 -8
- package/lib/helper/cicoHelper.d.ts +0 -31
- package/lib/helper/cicoHelper.js +0 -155
|
@@ -55,7 +55,7 @@ export const FloatingContainer = styled.div.attrs(props => ({
|
|
|
55
55
|
transition: none;
|
|
56
56
|
|
|
57
57
|
&:hover {
|
|
58
|
-
background: linear-gradient(135deg, #
|
|
58
|
+
background: linear-gradient(135deg, #0071BC 0%, #1B1464 100%);
|
|
59
59
|
border: 1px solid #667eea;
|
|
60
60
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3),
|
|
61
61
|
0 6px 16px rgba(0, 0, 0, 0.2);
|
|
@@ -98,6 +98,13 @@ export const GripHandle = styled.div `
|
|
|
98
98
|
height: 14px;
|
|
99
99
|
}
|
|
100
100
|
`;
|
|
101
|
+
export const Separator = styled.div `
|
|
102
|
+
background: rgba(255, 255, 255, 0.25);
|
|
103
|
+
width: ${props => props.$orientation === 'horizontal' ? '1px' : '100%'};
|
|
104
|
+
height: ${props => props.$orientation === 'horizontal' ? '24px' : '1px'};
|
|
105
|
+
margin: ${props => props.$orientation === 'horizontal' ? '0 4px' : '4px 0'};
|
|
106
|
+
flex-shrink: 0;
|
|
107
|
+
`;
|
|
101
108
|
export const MenuButton = styled.button `
|
|
102
109
|
display: flex;
|
|
103
110
|
align-items: center;
|
|
@@ -110,23 +117,21 @@ export const MenuButton = styled.button `
|
|
|
110
117
|
color: white;
|
|
111
118
|
font-size: 16px;
|
|
112
119
|
cursor: pointer;
|
|
113
|
-
transition:
|
|
120
|
+
transition: background 0.2s ease;
|
|
114
121
|
position: relative;
|
|
115
122
|
|
|
116
123
|
&:hover:not(:disabled) {
|
|
117
124
|
background: rgba(255, 255, 255, 0.2);
|
|
118
|
-
transform: scale(1.1);
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
&:active:not(:disabled) {
|
|
122
|
-
|
|
128
|
+
opacity: 0.8;
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
&:disabled {
|
|
126
|
-
opacity: 0.
|
|
132
|
+
opacity: 0.5;
|
|
127
133
|
cursor: not-allowed;
|
|
128
|
-
|
|
129
|
-
color: rgba(255, 255, 255, 0.4);
|
|
134
|
+
color: rgba(255, 255, 255, 0.6);
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
svg {
|
|
@@ -138,8 +143,8 @@ export const ConfigButton = styled.button `
|
|
|
138
143
|
display: flex;
|
|
139
144
|
align-items: center;
|
|
140
145
|
justify-content: center;
|
|
141
|
-
width:
|
|
142
|
-
height:
|
|
146
|
+
width: 28px;
|
|
147
|
+
height: 28px;
|
|
143
148
|
background: ${props => props.$isActive
|
|
144
149
|
? 'rgba(0, 0, 0, 0.2)'
|
|
145
150
|
: 'rgba(0, 0, 0, 0.1)'};
|
|
@@ -148,9 +153,9 @@ export const ConfigButton = styled.button `
|
|
|
148
153
|
: 'rgba(255, 255, 255, 0.15)'};
|
|
149
154
|
border-radius: 8px;
|
|
150
155
|
color: white;
|
|
151
|
-
font-size:
|
|
156
|
+
font-size: 14px;
|
|
152
157
|
cursor: pointer;
|
|
153
|
-
transition:
|
|
158
|
+
transition: background 0.2s ease, border-color 0.2s ease;
|
|
154
159
|
position: relative;
|
|
155
160
|
|
|
156
161
|
&:hover {
|
|
@@ -158,16 +163,20 @@ export const ConfigButton = styled.button `
|
|
|
158
163
|
? 'rgba(0, 0, 0, 0.25)'
|
|
159
164
|
: 'rgba(0, 0, 0, 0.15)'};
|
|
160
165
|
border-color: rgba(255, 255, 255, 0.35);
|
|
161
|
-
transform: scale(1.05);
|
|
162
166
|
}
|
|
163
167
|
|
|
164
168
|
&:active {
|
|
165
|
-
|
|
169
|
+
opacity: 0.8;
|
|
166
170
|
}
|
|
167
171
|
|
|
168
172
|
svg {
|
|
169
|
-
width:
|
|
170
|
-
height:
|
|
173
|
+
width: 16px;
|
|
174
|
+
height: 16px;
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
export const ContextMenuButton = styled(MenuButton) `
|
|
178
|
+
svg {
|
|
179
|
+
transform: translateY(0);
|
|
171
180
|
}
|
|
172
181
|
`;
|
|
173
182
|
export const RemoveButton = styled.button `
|
|
@@ -177,25 +186,26 @@ export const RemoveButton = styled.button `
|
|
|
177
186
|
width: 20px;
|
|
178
187
|
height: 20px;
|
|
179
188
|
background: #ef4444;
|
|
180
|
-
border:
|
|
189
|
+
border: none;
|
|
181
190
|
border-radius: 50%;
|
|
182
191
|
color: white;
|
|
183
|
-
font-size:
|
|
192
|
+
font-size: 14px;
|
|
184
193
|
font-weight: bold;
|
|
194
|
+
line-height: 1;
|
|
185
195
|
cursor: pointer;
|
|
186
196
|
display: flex;
|
|
187
197
|
align-items: center;
|
|
188
198
|
justify-content: center;
|
|
199
|
+
padding: 0;
|
|
189
200
|
transition: all 0.2s ease;
|
|
190
201
|
z-index: 1;
|
|
191
202
|
|
|
192
203
|
&:hover {
|
|
193
204
|
background: #dc2626;
|
|
194
|
-
transform: scale(1.15);
|
|
195
205
|
}
|
|
196
206
|
|
|
197
207
|
&:active {
|
|
198
|
-
|
|
208
|
+
background: #b91c1c;
|
|
199
209
|
}
|
|
200
210
|
`;
|
|
201
211
|
export const OrientationToggle = styled.button `
|
|
@@ -11,7 +11,6 @@ export interface TMFloatingMenuItem {
|
|
|
11
11
|
export interface TMFloatingMenuBarProps {
|
|
12
12
|
containerRef: React.RefObject<HTMLElement | null>;
|
|
13
13
|
contextMenuItems?: TMContextMenuItemProps[];
|
|
14
|
-
storageKey?: string;
|
|
15
14
|
isConstrained?: boolean;
|
|
16
15
|
defaultPosition?: Position;
|
|
17
16
|
maxItems?: number;
|
|
@@ -47,12 +47,12 @@ const StyledGroupTemplate = styled.div `
|
|
|
47
47
|
&::after {
|
|
48
48
|
content: '';
|
|
49
49
|
display: block;
|
|
50
|
-
width:
|
|
50
|
+
width: calc(100% - 35px);
|
|
51
51
|
margin: 0 auto;
|
|
52
52
|
border-bottom: 1px solid #00A99D;
|
|
53
53
|
margin-top: 8px;
|
|
54
54
|
position: absolute;
|
|
55
|
-
left:
|
|
55
|
+
left: 35px;
|
|
56
56
|
bottom: 0;
|
|
57
57
|
}
|
|
58
58
|
`;
|
|
@@ -4,31 +4,68 @@ import TMModal from './TMModal';
|
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import { SDKUI_Localizator, TMLayoutWaitingContainer } from '../..';
|
|
6
6
|
import { getButtonAttributes, getSelectedItem } from '../../helper/dcmtsHelper';
|
|
7
|
+
import { DeviceType, useDeviceType } from './TMDeviceProvider';
|
|
7
8
|
const IframeContainer = styled.div `
|
|
8
9
|
display: flex;
|
|
9
10
|
height: 100%;
|
|
10
11
|
flex-direction: column;
|
|
11
|
-
|
|
12
|
+
position: relative;
|
|
12
13
|
`;
|
|
13
14
|
const StyledIframe = styled.iframe `
|
|
14
15
|
border: none;
|
|
15
16
|
flex: 1;
|
|
16
17
|
`;
|
|
18
|
+
const LoadingOverlay = styled.div `
|
|
19
|
+
position: absolute;
|
|
20
|
+
top: 0;
|
|
21
|
+
left: 0;
|
|
22
|
+
right: 0;
|
|
23
|
+
bottom: 0;
|
|
24
|
+
background-color: rgba(128, 128, 128, 0.3);
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
z-index: 1000;
|
|
29
|
+
|
|
30
|
+
&::after {
|
|
31
|
+
content: '';
|
|
32
|
+
width: 40px;
|
|
33
|
+
height: 40px;
|
|
34
|
+
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
35
|
+
border-top-color: #fff;
|
|
36
|
+
border-radius: 50%;
|
|
37
|
+
animation: spin 0.8s linear infinite;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@keyframes spin {
|
|
41
|
+
to {
|
|
42
|
+
transform: rotate(360deg);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
17
46
|
const TMCustomButton = (props) => {
|
|
18
47
|
const { button, isModal = true, formData, selectedItems, onClose } = props;
|
|
19
48
|
const { appName: scriptUrl, arguments: args } = button;
|
|
49
|
+
const Device = useDeviceType();
|
|
20
50
|
const iframeRef = useRef(null);
|
|
21
51
|
const attributes = useMemo(() => getButtonAttributes(args, formData, selectedItems), [args, formData, selectedItems]);
|
|
22
52
|
const RunOnce = button.mode === "RunOnce";
|
|
23
53
|
const [loading, setLoading] = useState(true);
|
|
24
54
|
const [error, setError] = useState(false);
|
|
25
|
-
const
|
|
55
|
+
const itemsToProcess = useMemo(() => selectedItems && selectedItems.length > 0 ? selectedItems : [attributes], [selectedItems, attributes]);
|
|
26
56
|
// Stati per il wait panel
|
|
27
|
-
const [showWaitPanel, setShowWaitPanel] = useState(
|
|
28
|
-
const [waitPanelText, setWaitPanelText] = useState(SDKUI_Localizator.CustomButtonActions.replaceParams(1,
|
|
57
|
+
const [showWaitPanel, setShowWaitPanel] = useState(itemsToProcess.length > 0 && !RunOnce);
|
|
58
|
+
const [waitPanelText, setWaitPanelText] = useState(SDKUI_Localizator.CustomButtonActions.replaceParams(1, itemsToProcess.length));
|
|
29
59
|
const [waitPanelValue, setWaitPanelValue] = useState(0);
|
|
30
|
-
const [waitPanelMaxValue, setWaitPanelMaxValue] = useState(
|
|
60
|
+
const [waitPanelMaxValue, setWaitPanelMaxValue] = useState(itemsToProcess.length);
|
|
31
61
|
const [abortController, setAbortController] = useState(undefined);
|
|
62
|
+
// Aggiungi timestamp all'URL per evitare cache
|
|
63
|
+
const iframeUrl = useMemo(() => {
|
|
64
|
+
if (!scriptUrl)
|
|
65
|
+
return '';
|
|
66
|
+
const separator = scriptUrl.includes('?') ? '&' : '?';
|
|
67
|
+
return `${scriptUrl}${separator}t=${Date.now()}`;
|
|
68
|
+
}, [scriptUrl]);
|
|
32
69
|
const targetOrigin = useMemo(() => {
|
|
33
70
|
if (!scriptUrl)
|
|
34
71
|
return '*';
|
|
@@ -41,26 +78,35 @@ const TMCustomButton = (props) => {
|
|
|
41
78
|
}
|
|
42
79
|
}, [scriptUrl]);
|
|
43
80
|
const handleLoad = () => setLoading(false);
|
|
81
|
+
const isMobile = Device === DeviceType.MOBILE;
|
|
44
82
|
const handleError = () => {
|
|
45
83
|
setLoading(false);
|
|
46
84
|
setError(true);
|
|
47
85
|
};
|
|
48
86
|
const executeSequentially = async (controller) => {
|
|
49
|
-
if (!
|
|
87
|
+
if (!itemsToProcess)
|
|
50
88
|
return;
|
|
51
|
-
for (const [index, item] of
|
|
89
|
+
for (const [index, item] of itemsToProcess.entries()) {
|
|
52
90
|
if (controller.signal.aborted)
|
|
53
91
|
break;
|
|
54
|
-
setWaitPanelText(SDKUI_Localizator.CustomButtonActions.replaceParams(index + 1,
|
|
92
|
+
setWaitPanelText(SDKUI_Localizator.CustomButtonActions.replaceParams(index + 1, itemsToProcess.length));
|
|
55
93
|
setWaitPanelValue(index);
|
|
56
94
|
// Attendi che l'iframe sia pronto e invia il messaggio
|
|
57
95
|
await new Promise((resolve) => {
|
|
58
96
|
const checkIframe = setInterval(() => {
|
|
59
97
|
if (iframeRef.current?.contentWindow) {
|
|
60
98
|
clearInterval(checkIframe);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
99
|
+
if (selectedItems && selectedItems.length > 0) {
|
|
100
|
+
//devo convertire item in formData
|
|
101
|
+
const processedItem = getSelectedItem(args, formData, item);
|
|
102
|
+
postMessageIframe(processedItem);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
postMessageIframe(item);
|
|
106
|
+
}
|
|
107
|
+
//imposta 100% se sono all'ultimo item
|
|
108
|
+
if (index === itemsToProcess.length - 1)
|
|
109
|
+
setWaitPanelValue(index + 1);
|
|
64
110
|
// Attendi prima di passare al prossimo
|
|
65
111
|
setTimeout(() => {
|
|
66
112
|
setWaitPanelValue(index + 1);
|
|
@@ -82,8 +128,7 @@ const TMCustomButton = (props) => {
|
|
|
82
128
|
useEffect(() => {
|
|
83
129
|
if (loading || error)
|
|
84
130
|
return;
|
|
85
|
-
|
|
86
|
-
if (!RunOnce && selectedItemsCount > 0) {
|
|
131
|
+
if (!RunOnce && itemsToProcess.length > 0) {
|
|
87
132
|
// esegui per ogni item selezionato
|
|
88
133
|
const controller = new AbortController();
|
|
89
134
|
controller.signal.addEventListener('abort', () => {
|
|
@@ -91,7 +136,7 @@ const TMCustomButton = (props) => {
|
|
|
91
136
|
onClose?.();
|
|
92
137
|
});
|
|
93
138
|
setAbortController(controller);
|
|
94
|
-
setWaitPanelMaxValue(
|
|
139
|
+
setWaitPanelMaxValue(itemsToProcess.length);
|
|
95
140
|
executeSequentially(controller);
|
|
96
141
|
}
|
|
97
142
|
else {
|
|
@@ -103,7 +148,6 @@ const TMCustomButton = (props) => {
|
|
|
103
148
|
onClose?.();
|
|
104
149
|
}, 2000);
|
|
105
150
|
}
|
|
106
|
-
//clearTimeout(timeoutIframe);
|
|
107
151
|
}
|
|
108
152
|
}, [loading, error, RunOnce]);
|
|
109
153
|
useEffect(() => {
|
|
@@ -112,7 +156,7 @@ const TMCustomButton = (props) => {
|
|
|
112
156
|
onClose?.();
|
|
113
157
|
}
|
|
114
158
|
}, []);
|
|
115
|
-
const iframeContent = (_jsxs(IframeContainer, { style: !RunOnce ? { visibility: 'hidden' } : {}, children: [error && _jsx("div", { children: "Si \u00E8 verificato un errore nel caricamento del contenuto." }), !error && _jsx(StyledIframe, { ref: iframeRef, loading: 'lazy', onLoad: handleLoad, onError: handleError, src:
|
|
116
|
-
return isModal && RunOnce ? (_jsx(TMModal, { title: button.title, width: '60%', height: '70%', resizable: true, expandable: true, onClose: onClose, children: iframeContent })) : !RunOnce && (_jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { showWaitPanel: showWaitPanel, waitPanelTitle: SDKUI_Localizator.CustomButtonAction, showWaitPanelPrimary: true, waitPanelTextPrimary: waitPanelText, waitPanelValuePrimary: waitPanelValue, waitPanelMaxValuePrimary: waitPanelMaxValue, showWaitPanelSecondary: false, isCancelable: true, abortController: abortController, children: undefined }), iframeContent] }));
|
|
159
|
+
const iframeContent = (_jsxs(IframeContainer, { style: !RunOnce ? { visibility: 'hidden' } : {}, children: [loading && _jsx(LoadingOverlay, {}), error && _jsx("div", { children: "Si \u00E8 verificato un errore nel caricamento del contenuto." }), !error && _jsx(StyledIframe, { ref: iframeRef, loading: 'lazy', onLoad: handleLoad, onError: handleError, src: iframeUrl })] }));
|
|
160
|
+
return isModal && RunOnce ? (_jsx(TMModal, { title: button.title, width: isMobile ? '95%' : '60%', height: isMobile ? '95%' : '70%', resizable: isMobile ? false : true, expandable: isMobile ? false : true, onClose: onClose, children: iframeContent })) : !RunOnce && (_jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { showWaitPanel: showWaitPanel, waitPanelTitle: SDKUI_Localizator.CustomButtonAction, showWaitPanelPrimary: true, waitPanelTextPrimary: waitPanelText, waitPanelValuePrimary: waitPanelValue, waitPanelMaxValuePrimary: waitPanelMaxValue, showWaitPanelSecondary: false, isCancelable: true, abortController: abortController, children: undefined }), iframeContent] }));
|
|
117
161
|
};
|
|
118
162
|
export default TMCustomButton;
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { IColumnProps, IDataGridOptions, IMasterDetailProps } from 'devextreme-react/data-grid';
|
|
3
3
|
import dxDataGrid from 'devextreme/ui/data_grid';
|
|
4
4
|
import { ITMCounterContainerProps } from './TMCounterContainer';
|
|
5
|
+
import { TMContextMenuItemProps } from '../NewComponents/ContextMenu/types';
|
|
5
6
|
export interface TMDataGridContextMenuItem {
|
|
6
7
|
text: string;
|
|
7
8
|
icon: string;
|
|
@@ -39,8 +40,8 @@ export interface TMDataGridProps<T> extends IDataGridOptions {
|
|
|
39
40
|
showFilterPanel?: boolean;
|
|
40
41
|
/** Show the load panel */
|
|
41
42
|
showLoadPanel?: boolean;
|
|
42
|
-
/** Show the column chooser */
|
|
43
|
-
|
|
43
|
+
/** Show the header column chooser in context menu */
|
|
44
|
+
showHeaderColumnChooser?: boolean;
|
|
44
45
|
/** Show the search panel */
|
|
45
46
|
showSearchPanel?: boolean;
|
|
46
47
|
/** Show the group panel */
|
|
@@ -51,6 +52,8 @@ export interface TMDataGridProps<T> extends IDataGridOptions {
|
|
|
51
52
|
masterDetail?: IMasterDetailProps;
|
|
52
53
|
/** On Has Filters Change */
|
|
53
54
|
onHasFiltersChange?: (hasFilters: boolean) => void;
|
|
55
|
+
/** Custom context menu items - when provided, replaces DevExtreme's native context menu with TMContextMenu */
|
|
56
|
+
customContextMenuItems?: TMContextMenuItemProps[];
|
|
54
57
|
}
|
|
55
58
|
declare const TMDataGrid: React.ForwardRefExoticComponent<TMDataGridProps<unknown> & React.RefAttributes<dxDataGrid<any, any>>>;
|
|
56
59
|
export default TMDataGrid;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
3
|
-
import DataGrid, { Column, HeaderFilter, Selection, Scrolling, LoadPanel, SearchPanel, Pager, Sorting, Paging, FilterPanel, ColumnChooser, Grouping, GroupPanel, Summary, Editing, FilterRow, StateStoring, RowDragging, MasterDetail } from 'devextreme-react/data-grid';
|
|
3
|
+
import DataGrid, { Column, HeaderFilter, Selection, Scrolling, LoadPanel, SearchPanel, Pager, Sorting, Paging, FilterPanel, ColumnChooser, Grouping, GroupPanel, Summary, Editing, FilterRow, StateStoring, RowDragging, MasterDetail, Position, ColumnChooserSearch, ColumnChooserSelection } from 'devextreme-react/data-grid';
|
|
4
4
|
import DataSource from 'devextreme/data/data_source';
|
|
5
5
|
import { IconAll, IconSelected, IconVisible, SDKUI_Globals, SDKUI_Localizator } from '../../helper';
|
|
6
6
|
import TMCounterContainer, { CounterItemKey } from './TMCounterContainer';
|
|
7
|
+
import TMContextMenu from '../NewComponents/ContextMenu/TMContextMenu';
|
|
7
8
|
;
|
|
8
9
|
export var TMDataGridPageSize;
|
|
9
10
|
(function (TMDataGridPageSize) {
|
|
@@ -16,9 +17,9 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
16
17
|
// main properties
|
|
17
18
|
keyExpr = 'id', dataSource, focusedRowEnabled = true, hoverStateEnabled = true, focusedRowKey, selectedRowKeys = [],
|
|
18
19
|
// custom options
|
|
19
|
-
dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusStarting = false, counterConfig = { show: false, items: new Map() }, onHasFiltersChange,
|
|
20
|
+
dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showHeaderColumnChooser = false, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusStarting = false, counterConfig = { show: false, items: new Map() }, onHasFiltersChange, customContextMenuItems,
|
|
20
21
|
// events and callbacks
|
|
21
|
-
onSelectionChanged, onFocusedRowChanged, onRowDblClick, onRowClick, onCellClick, onCellDblClick, onOptionChanged, onContentReady, onContextMenuPreparing, onInitialized, onEditorPreparing, onCellPrepared, onRowPrepared, onRowUpdating, onRowExpanded, onRowCollapsed, onRowUpdated, onSaved, onEditCanceled, onEditingStart, onEditingChange, customizeColumns, onKeyDown, scrolling = { mode: 'standard', useNative: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1 }, paging = { enabled: true, pageSize: pageSize }, pager = { visible: true, showInfo: true, showNavigationButtons: true }, selection = { mode: 'multiple', showCheckBoxesMode: "always", selectAllMode: "allPages" }, sorting, summary, stateStoring,
|
|
22
|
+
onSelectionChanged, onFocusedRowChanged, onRowDblClick, onRowClick, onCellClick, onCellDblClick, onOptionChanged, onContentReady, onContextMenuPreparing, onInitialized, onEditorPreparing, onCellPrepared, onRowPrepared, onRowUpdating, onRowExpanded, onRowCollapsed, onRowUpdated, onSaved, onEditCanceled, onEditingStart, onEditingChange, customizeColumns, onKeyDown, scrolling = { mode: 'standard', useNative: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1 }, paging = { enabled: true, pageSize: pageSize }, pager = { visible: true, showInfo: true, showNavigationButtons: true }, selection = { mode: 'multiple', showCheckBoxesMode: "always", selectAllMode: "allPages" }, sorting, summary, stateStoring, grouping, groupPanel, filterRow, headerFilter, editing, rowDragging, masterDetail,
|
|
22
23
|
// other properties
|
|
23
24
|
disabled = false, autoNavigateToFocusedRow = true, columnResizingMode = 'widget', columnHidingEnabled = true, columnAutoWidth = true, allowColumnResizing = true, allowColumnReordering = true, showBorders = true, showRowLines = SDKUI_Globals.userSettings?.themeSettings.gridSettings.showRowLines === 1, showColumnLines = SDKUI_Globals.userSettings?.themeSettings.gridSettings.showColumnLines === 1, showColumnHeaders = true, rowAlternationEnabled = false, wordWrapEnabled = false, noDataText,
|
|
24
25
|
// styles
|
|
@@ -29,10 +30,42 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
29
30
|
const [totalRecordCount, setTotalRecordCount] = useState(0);
|
|
30
31
|
const [visibleItemsCount, setVisibleItemsCount] = useState(0);
|
|
31
32
|
const [hasFilters, setHasFilters] = useState(false);
|
|
33
|
+
// Custom context menu states
|
|
34
|
+
const [customContextMenuVisible, setCustomContextMenuVisible] = useState(false);
|
|
35
|
+
const [customContextMenuPosition, setCustomContextMenuPosition] = useState({ x: 0, y: 0 });
|
|
36
|
+
const [customContextMenuRowKey, setCustomContextMenuRowKey] = useState(undefined);
|
|
37
|
+
const gridContainerRef = useRef(null);
|
|
32
38
|
useEffect(() => {
|
|
33
39
|
const count = getRecordCount(dataSource);
|
|
34
40
|
setTotalRecordCount(count);
|
|
35
41
|
}, [dataSource]);
|
|
42
|
+
// Handle custom context menu (only when customContextMenuItems is provided)
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!customContextMenuItems || !gridContainerRef.current)
|
|
45
|
+
return;
|
|
46
|
+
const gridContainer = gridContainerRef.current;
|
|
47
|
+
const handleContextMenu = (e) => {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
e.stopPropagation();
|
|
50
|
+
// Get the clicked row
|
|
51
|
+
const target = e.target;
|
|
52
|
+
const rowElement = target.closest('.dx-data-row');
|
|
53
|
+
if (rowElement && internalRef.current) {
|
|
54
|
+
const rowIndex = Array.from(rowElement.parentElement?.children || []).indexOf(rowElement);
|
|
55
|
+
const rowKey = internalRef.current.instance().getKeyByRowIndex(rowIndex);
|
|
56
|
+
// Change focused row
|
|
57
|
+
internalRef.current.instance().option('focusedRowKey', rowKey);
|
|
58
|
+
// Show custom context menu
|
|
59
|
+
setCustomContextMenuVisible(true);
|
|
60
|
+
setCustomContextMenuPosition({ x: e.clientX, y: e.clientY });
|
|
61
|
+
setCustomContextMenuRowKey(rowKey);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
gridContainer.addEventListener('contextmenu', handleContextMenu);
|
|
65
|
+
return () => {
|
|
66
|
+
gridContainer.removeEventListener('contextmenu', handleContextMenu);
|
|
67
|
+
};
|
|
68
|
+
}, [customContextMenuItems]);
|
|
36
69
|
// Creating a ref to store the timestamp of the last selection change
|
|
37
70
|
const lastSelectionChangeTime = useRef(Date.now());
|
|
38
71
|
useEffect(() => {
|
|
@@ -101,6 +134,8 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
101
134
|
}
|
|
102
135
|
return {
|
|
103
136
|
...item,
|
|
137
|
+
// Ensure icon is not null/undefined to prevent DevExtreme errors
|
|
138
|
+
icon: item.icon || '',
|
|
104
139
|
disabled: disabled || disabledCalculation, // An item is disabled if it's explicitly set to `true` or if the calculation above determines so
|
|
105
140
|
// Define the behavior when the menu item is clicked
|
|
106
141
|
onClick: () => {
|
|
@@ -132,10 +167,51 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
132
167
|
};
|
|
133
168
|
});
|
|
134
169
|
}, [focusedRowEnabled, focusedRowKey, selectedRowKeys]);
|
|
170
|
+
// Process custom context menu items (for TMContextMenuItemProps)
|
|
171
|
+
const processCustomContextMenuItems = useCallback((items, rowID) => {
|
|
172
|
+
return items.map(item => {
|
|
173
|
+
let disabled = item.disabled ?? false;
|
|
174
|
+
let disabledCalculation = false;
|
|
175
|
+
const id = focusedRowEnabled ? focusedRowKey : rowID;
|
|
176
|
+
if (item.operationType === 'singleRow') {
|
|
177
|
+
disabledCalculation = selectedRowKeys.length > 1 || id === undefined;
|
|
178
|
+
}
|
|
179
|
+
if (item.operationType === 'multiRow') {
|
|
180
|
+
disabledCalculation = selectedRowKeys.length === 0 && id === undefined;
|
|
181
|
+
}
|
|
182
|
+
const originalOnClick = item.onClick;
|
|
183
|
+
return {
|
|
184
|
+
...item,
|
|
185
|
+
disabled: disabled || disabledCalculation,
|
|
186
|
+
onClick: originalOnClick ? () => {
|
|
187
|
+
if (item.operationType === 'singleRow' && id !== undefined) {
|
|
188
|
+
originalOnClick(id);
|
|
189
|
+
}
|
|
190
|
+
else if (item.operationType === 'multiRow' && id !== undefined) {
|
|
191
|
+
if (selectedRowKeys.length > 0) {
|
|
192
|
+
originalOnClick(selectedRowKeys);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
originalOnClick([id]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
originalOnClick();
|
|
200
|
+
}
|
|
201
|
+
} : undefined,
|
|
202
|
+
submenu: item.submenu ? processCustomContextMenuItems(item.submenu, id) : undefined,
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
}, [focusedRowEnabled, focusedRowKey, selectedRowKeys]);
|
|
135
206
|
// Handle context menu preparation
|
|
136
207
|
const onContextMenuPreparingCallback = useCallback((e) => {
|
|
137
208
|
if (e === undefined)
|
|
138
209
|
return;
|
|
210
|
+
// If custom context menu is enabled, completely disable DevExtreme's native context menu
|
|
211
|
+
if (customContextMenuItems && e.target === 'content') {
|
|
212
|
+
e.items = undefined;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
139
215
|
if (onContextMenuPreparing)
|
|
140
216
|
onContextMenuPreparing(e);
|
|
141
217
|
if (e.target === 'content') {
|
|
@@ -146,7 +222,18 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
146
222
|
e.items = [...updatedContextMenuItems];
|
|
147
223
|
}
|
|
148
224
|
}
|
|
149
|
-
|
|
225
|
+
// Add column chooser to header context menu
|
|
226
|
+
if (e.target === 'header' && showHeaderColumnChooser) {
|
|
227
|
+
e.items = e.items || [];
|
|
228
|
+
e.items.push({
|
|
229
|
+
text: SDKUI_Localizator.ShowColumnSelection,
|
|
230
|
+
icon: 'columnchooser',
|
|
231
|
+
onItemClick: () => {
|
|
232
|
+
internalRef.current?.instance().showColumnChooser();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}, [updateContextMenuItems, onContextMenuPreparing, showHeaderColumnChooser, customContextMenuItems]);
|
|
150
237
|
// Handle toolbar preparation, especially for the search panel
|
|
151
238
|
const onToolbarPreparingCallback = useCallback((e) => {
|
|
152
239
|
if (e === undefined || e.toolbarOptions === undefined || e.toolbarOptions.items === undefined)
|
|
@@ -215,15 +302,19 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
215
302
|
// Propaga l'evento originale
|
|
216
303
|
onOptionChanged?.(e);
|
|
217
304
|
}, [onOptionChanged, onHasFiltersChange]);
|
|
218
|
-
return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
|
|
305
|
+
return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { ref: gridContainerRef, style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
|
|
219
306
|
// main properties
|
|
220
307
|
keyExpr: keyExpr, dataSource: dataSource, selectedRowKeys: selectedRowKeys, focusedRowEnabled: focusedRowEnabled, hoverStateEnabled: hoverStateEnabled,
|
|
221
308
|
// events and callbacks
|
|
222
|
-
onSelectionChanged: onSelectionChangedCallback, onRowDblClick: onRowDblClickCallback, onRowPrepared: onRowPrepared, onContextMenuPreparing: onContextMenuPreparingCallback, onToolbarPreparing: onToolbarPreparingCallback, onFocusedRowChanged: onFocusedRowChanged, onRowClick: onRowClick, onCellClick: onCellClick, onCellDblClick: onCellDblClick, onOptionChanged: onOptionChangedCallback, onContentReady: onContentReadyCallback, onInitialized: onInitialized, customizeColumns: customizeColumns, onEditorPreparing: onEditorPreparing, onCellPrepared: onCellPrepared, onRowUpdating: onRowUpdating, onRowExpanded: onRowExpanded, onRowCollapsed: onRowCollapsed, onRowUpdated: onRowUpdated, onSaved: onSaved, onEditCanceled: onEditCanceled, onEditingStart: onEditingStart, onEditingChange: onEditingChange, onKeyDown: onKeyDown,
|
|
309
|
+
onSelectionChanged: onSelectionChangedCallback, onRowDblClick: onRowDblClickCallback, onRowPrepared: onRowPrepared, onContextMenuPreparing: customContextMenuItems ? undefined : onContextMenuPreparingCallback, onToolbarPreparing: onToolbarPreparingCallback, onFocusedRowChanged: onFocusedRowChanged, onRowClick: onRowClick, onCellClick: onCellClick, onCellDblClick: onCellDblClick, onOptionChanged: onOptionChangedCallback, onContentReady: onContentReadyCallback, onInitialized: onInitialized, customizeColumns: customizeColumns, onEditorPreparing: onEditorPreparing, onCellPrepared: onCellPrepared, onRowUpdating: onRowUpdating, onRowExpanded: onRowExpanded, onRowCollapsed: onRowCollapsed, onRowUpdated: onRowUpdated, onSaved: onSaved, onEditCanceled: onEditCanceled, onEditingStart: onEditingStart, onEditingChange: onEditingChange, onKeyDown: onKeyDown,
|
|
223
310
|
// other properties
|
|
224
311
|
disabled: disabled, autoNavigateToFocusedRow: autoNavigateToFocusedRow, focusedRowKey: focusedRowKey, columnHidingEnabled: columnHidingEnabled, columnResizingMode: columnResizingMode, columnAutoWidth: columnAutoWidth, allowColumnResizing: allowColumnResizing, allowColumnReordering: allowColumnReordering, showBorders: showBorders, showRowLines: showRowLines, showColumnLines: showColumnLines, showColumnHeaders: showColumnHeaders, rowAlternationEnabled: rowAlternationEnabled, wordWrapEnabled: wordWrapEnabled, noDataText: noDataText,
|
|
225
312
|
// styles
|
|
226
|
-
width: width, height: height, style: { userSelect: 'none' }, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }),
|
|
313
|
+
width: width, height: height, style: { userSelect: 'none' }, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), pager && _jsx(Pager, { ...pager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) }), customContextMenuItems && (_jsx(TMContextMenu, { items: processCustomContextMenuItems(customContextMenuItems, customContextMenuRowKey), externalControl: {
|
|
314
|
+
visible: customContextMenuVisible,
|
|
315
|
+
position: customContextMenuPosition,
|
|
316
|
+
onClose: () => setCustomContextMenuVisible(false)
|
|
317
|
+
} }))] });
|
|
227
318
|
});
|
|
228
319
|
export default TMDataGrid;
|
|
229
320
|
const getRecordCount = (dataSource) => {
|
|
@@ -170,7 +170,7 @@ const TMHtmlEditor = (props) => {
|
|
|
170
170
|
justifyContent: 'flex-end',
|
|
171
171
|
fontSize: 12,
|
|
172
172
|
color: '#6c757d',
|
|
173
|
-
marginTop: 4,
|
|
173
|
+
marginTop: showInfoIcon ? 0 : 4,
|
|
174
174
|
gap: 4,
|
|
175
175
|
}, children: [`${Math.max(charactersRemaining, 0)} ${SDKUI_Localizator.CharactersRemaining}`, showInfoIcon && (_jsx(TMTooltip, { content: 'Markup HTML', children: _jsx("span", { className: "dx-icon-codeblock", style: { fontSize: 22, cursor: 'pointer' }, onClick: () => {
|
|
176
176
|
TMMessageBoxManager.show({
|
|
@@ -389,17 +389,35 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
|
|
|
389
389
|
return (_jsxs("div", { style: { width: '100%' }, children: [draftData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.Draft, children: draftData.map(item => renderMetadataItem(item, isReadOnly)) }), checkOutData.length > 0 && _jsx(TMAccordion, { title: `${SDKUI_Localizator.CheckIn}/${SDKUI_Localizator.CheckOut}`, children: checkOutData.map(item => renderMetadataItem(item, true)) })] }));
|
|
390
390
|
}, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
391
391
|
const layoutChronology = useMemo(() => {
|
|
392
|
-
|
|
392
|
+
// Definiamo l'ordine desiderato: Version, Tipo, Dimensione, Autore, Data Ultima modifica
|
|
393
|
+
const desiredChronologyOrder = [
|
|
394
|
+
ChronologyMIDs.Ver,
|
|
395
|
+
SystemMIDsAsNumber.FileExt,
|
|
396
|
+
SystemMIDsAsNumber.FileSize,
|
|
397
|
+
ChronologyMIDs.AuthorID,
|
|
398
|
+
ChronologyMIDs.CheckInTime,
|
|
399
|
+
];
|
|
400
|
+
const tempChronologyDataMap = {};
|
|
393
401
|
metadataValues.forEach(item => {
|
|
394
402
|
switch (item.md?.id) {
|
|
395
403
|
case ChronologyMIDs.Ver:
|
|
396
404
|
case ChronologyMIDs.AuthorID:
|
|
397
|
-
|
|
405
|
+
case ChronologyMIDs.CheckInTime:
|
|
406
|
+
case SystemMIDsAsNumber.FileExt:
|
|
407
|
+
case SystemMIDsAsNumber.FileSize:
|
|
408
|
+
tempChronologyDataMap[item.md.id] = item;
|
|
398
409
|
break;
|
|
399
410
|
default:
|
|
400
411
|
break;
|
|
401
412
|
}
|
|
402
413
|
});
|
|
414
|
+
// Visualizziamo nell'ordine desiderato
|
|
415
|
+
const chronologyData = [];
|
|
416
|
+
desiredChronologyOrder.forEach(id => {
|
|
417
|
+
if (tempChronologyDataMap[id]) {
|
|
418
|
+
chronologyData.push(tempChronologyDataMap[id]);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
403
421
|
return (_jsx("div", { style: { width: '100%' }, children: chronologyData.length > 0 && chronologyData.map(item => renderMetadataItem(item, isReadOnly)) }));
|
|
404
422
|
}, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
|
|
405
423
|
const layoutDsAttachs = useMemo(() => {
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { HomeBlogPost, TaskDescriptor } from '@topconsultnpm/sdk-ts';
|
|
3
3
|
interface ITMDcmtBlogProps {
|
|
4
|
-
blogsDatasource: HomeBlogPost[];
|
|
5
|
-
setBlogsDatasource: (posts: HomeBlogPost[]) => void;
|
|
6
|
-
hasLoadedDataOnce: boolean;
|
|
7
|
-
setHasLoadedDataOnce: (loaded: boolean) => void;
|
|
8
|
-
lastLoadedDid: number | undefined;
|
|
9
|
-
setLastLoadedDid: (did: number | undefined) => void;
|
|
10
4
|
tid: number | undefined;
|
|
11
5
|
did: number | undefined;
|
|
12
|
-
fetchBlogDataAsync: (tid: number | undefined, did: number | undefined) => Promise<void>;
|
|
13
6
|
isVisible?: boolean;
|
|
7
|
+
fetchBlogDataTrigger?: number;
|
|
14
8
|
allTasks?: Array<TaskDescriptor>;
|
|
15
9
|
getAllTasks?: () => Promise<void>;
|
|
16
10
|
deleteTaskByIdsCallback?: (deletedTaskIds: Array<number>) => Promise<void>;
|
|
@@ -6,13 +6,40 @@ import { TMNothingToShow } from './TMDcmtPreview';
|
|
|
6
6
|
import { IconBoard, SDKUI_Localizator } from '../../../helper';
|
|
7
7
|
import TMBlogCommentForm from '../blog/TMBlogCommentForm';
|
|
8
8
|
import TMBlogsPost from '../../grids/TMBlogsPost';
|
|
9
|
-
|
|
9
|
+
import TMSpinner from '../../base/TMSpinner';
|
|
10
|
+
import { TMExceptionBoxManager } from '../../base/TMPopUp';
|
|
11
|
+
const TMDcmtBlog = ({ tid, did, isVisible, fetchBlogDataTrigger, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers }) => {
|
|
12
|
+
const [blogsDatasource, setBlogsDatasource] = useState([]);
|
|
13
|
+
const [hasLoadedDataOnce, setHasLoadedDataOnce] = useState(false); //traccia se *qualsiasi* dato è stato caricato per la prima volta
|
|
14
|
+
const [lastLoadedDid, setLastLoadedDid] = useState(undefined); // `lastLoadedDid` tiene traccia dell'ultimo `did` per cui abbiamo caricato i dati
|
|
10
15
|
// State to manage show comment form selected file
|
|
11
16
|
const [showCommentForm, setShowCommentForm] = useState(false);
|
|
12
17
|
const [externalBlogPost, setExternalBlogPost] = useState(undefined);
|
|
18
|
+
const fetchBlogDataAsync = useCallback(async (tid, did) => {
|
|
19
|
+
try {
|
|
20
|
+
TMSpinner.show({ description: 'Caricamento - Bacheca...' });
|
|
21
|
+
const res = await SDK_Globals.tmSession?.NewSearchEngine().BlogRetrieveAsync(tid, did);
|
|
22
|
+
setBlogsDatasource(res ?? []);
|
|
23
|
+
setHasLoadedDataOnce(true);
|
|
24
|
+
setLastLoadedDid(did);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
let err = e;
|
|
28
|
+
TMExceptionBoxManager.show({ exception: err });
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
TMSpinner.hide();
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
13
34
|
const showCommentFormCallback = useCallback(() => {
|
|
14
35
|
setShowCommentForm(true);
|
|
15
36
|
}, []);
|
|
37
|
+
// useEffect per triggerare il fetch dall'esterno tramite props
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (fetchBlogDataTrigger !== undefined && fetchBlogDataTrigger > 0) {
|
|
40
|
+
fetchBlogDataAsync(tid, did);
|
|
41
|
+
}
|
|
42
|
+
}, [fetchBlogDataTrigger, fetchBlogDataAsync, tid, did]);
|
|
16
43
|
useEffect(() => {
|
|
17
44
|
if (!tid || !did) {
|
|
18
45
|
setBlogsDatasource([]);
|
|
@@ -21,7 +48,7 @@ const TMDcmtBlog = ({ blogsDatasource, setBlogsDatasource, hasLoadedDataOnce, se
|
|
|
21
48
|
}
|
|
22
49
|
// Condizione per eseguire il fetch:
|
|
23
50
|
// 1. Il pannello è visibile
|
|
24
|
-
// 2. E (non abbiamo ancora caricato dati
|
|
51
|
+
// 2. E (non abbiamo ancora caricato dati o il `did` è cambiato rispetto all'ultima volta)
|
|
25
52
|
const shouldFetch = isVisible && (!hasLoadedDataOnce || did !== lastLoadedDid);
|
|
26
53
|
// Esegui la chiamata API solo se il pannello è visibile E i dati non sono già stati caricati
|
|
27
54
|
// O, se vuoi ricaricare ogni volta che diventa visibile (ma è meno efficiente per "pesante")
|