@topconsultnpm/sdkui-react 6.21.0-t2 → 6.21.0-t4
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 +28 -2
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +4 -4
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +6 -6
- package/lib/components/base/Styled.d.ts +1 -1
- package/lib/components/base/TMAreaManager.js +11 -0
- package/lib/components/base/TMCounterBar.d.ts +2 -2
- package/lib/components/base/TMCounterContainer.d.ts +2 -1
- package/lib/components/base/TMCustomButton.d.ts +1 -1
- package/lib/components/base/TMDataGrid.js +12 -2
- package/lib/components/base/TMDataGridExportForm.d.ts +2 -1
- package/lib/components/base/TMDataGridExportForm.js +19 -8
- package/lib/components/base/TMFileManager.d.ts +2 -1
- package/lib/components/base/TMFileManagerDataGridView.d.ts +2 -1
- package/lib/components/base/TMFileManagerDataGridView.js +4 -4
- package/lib/components/base/TMFileManagerThumbnailItems.d.ts +1 -1
- package/lib/components/base/TMFileManagerThumbnailItems.js +3 -3
- package/lib/components/base/TMFileManagerThumbnailsView.d.ts +1 -1
- package/lib/components/base/TMFileManagerUtils.d.ts +7 -0
- package/lib/components/base/TMFileManagerUtils.js +14 -1
- package/lib/components/base/TMLayout.d.ts +4 -4
- package/lib/components/base/TMList.d.ts +2 -1
- package/lib/components/base/TMListView.d.ts +1 -1
- package/lib/components/base/TMModal.js +2 -2
- package/lib/components/base/TMPopUp.d.ts +1 -0
- package/lib/components/base/TMPopUp.js +59 -2
- package/lib/components/base/TMSpinner.d.ts +5 -2
- package/lib/components/base/TMSpinner.js +33 -6
- package/lib/components/base/TMTab.d.ts +4 -3
- package/lib/components/base/TMTooltip.d.ts +1 -1
- package/lib/components/base/TMTreeView.d.ts +16 -13
- package/lib/components/base/TMTreeView.js +242 -79
- package/lib/components/base/TMUserAvatar.d.ts +2 -1
- package/lib/components/base/TMVilViewer.d.ts +2 -1
- package/lib/components/base/TMWaitPanel.d.ts +5 -2
- package/lib/components/base/TMWaitPanel.js +8 -6
- package/lib/components/choosers/TMDistinctValues.js +1 -1
- package/lib/components/choosers/TMDynDataListItemChooser.d.ts +1 -1
- package/lib/components/choosers/TMDynDataListItemChooser.js +6 -1
- package/lib/components/choosers/TMGroupChooser.js +1 -1
- package/lib/components/choosers/TMInvoiceRetrieveFormats.d.ts +2 -1
- package/lib/components/choosers/TMOrderRetrieveFormats.d.ts +2 -1
- package/lib/components/choosers/TMRelationChooser.d.ts +3 -3
- package/lib/components/choosers/TMUserChooser.d.ts +2 -2
- package/lib/components/editors/TMEditorStyled.d.ts +6 -6
- package/lib/components/editors/TMFormulaEditor.d.ts +2 -1
- package/lib/components/editors/TMFormulaEditor.js +98 -49
- package/lib/components/editors/TMHtmlContentDisplay.d.ts +2 -1
- package/lib/components/editors/TMHtmlEditor.d.ts +2 -1
- package/lib/components/editors/TMMetadataEditor.js +6 -2
- package/lib/components/editors/TMMetadataValues.js +23 -6
- package/lib/components/editors/TMTextBox.d.ts +1 -0
- package/lib/components/editors/TMTextBox.js +2 -1
- package/lib/components/editors/TMTreeDropDown.d.ts +1 -1
- package/lib/components/features/assistant/TMToppyDraggableHelpCenter.d.ts +1 -1
- package/lib/components/features/blog/TMBlogCommentForm.d.ts +2 -1
- package/lib/components/features/blog/TMBlogCommentForm.js +5 -2
- package/lib/components/features/documents/TMCopyToFolderForm.js +46 -24
- package/lib/components/features/documents/TMDcmtForm.d.ts +2 -0
- package/lib/components/features/documents/TMDcmtForm.js +21 -10
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +1 -1
- package/lib/components/features/documents/TMDcmtIcon.d.ts +4 -1
- package/lib/components/features/documents/TMDcmtIcon.js +6 -33
- package/lib/components/features/documents/TMDcmtPreview.d.ts +1 -1
- package/lib/components/features/documents/TMDcmtTasks.d.ts +2 -1
- package/lib/components/features/documents/TMDragDropOverlay.d.ts +1 -1
- package/lib/components/features/documents/TMFileUploader.js +1 -1
- package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -2
- package/lib/components/features/documents/TMMasterDetailDcmts.js +55 -16
- package/lib/components/features/documents/TMMergeToPdfForm.d.ts +2 -3
- package/lib/components/features/documents/TMMergeToPdfForm.js +115 -56
- package/lib/components/features/documents/TMRelationViewer.d.ts +12 -10
- package/lib/components/features/documents/TMRelationViewer.js +405 -95
- package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +4 -16
- package/lib/components/features/documents/copyAndMergeDcmtsShared.js +47 -61
- package/lib/components/features/documents/mergePdfUtils.d.ts +52 -0
- package/lib/components/features/documents/mergePdfUtils.js +268 -0
- package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +2 -1
- package/lib/components/features/search/TMMetadataOutputForm.d.ts +18 -0
- package/lib/components/features/search/TMMetadataOutputForm.js +225 -0
- package/lib/components/features/search/TMMetadataSorterForm.d.ts +18 -0
- package/lib/components/features/search/TMMetadataSorterForm.js +243 -0
- package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
- package/lib/components/features/search/TMSearch.d.ts +2 -2
- package/lib/components/features/search/TMSearch.js +2 -2
- package/lib/components/features/search/TMSearchQueryEditor.js +14 -8
- package/lib/components/features/search/TMSearchQueryPanel.js +249 -58
- package/lib/components/features/search/TMSearchResult.d.ts +3 -2
- package/lib/components/features/search/TMSearchResult.js +94 -25
- package/lib/components/features/search/TMSearchResultFloatingActionButton.d.ts +1 -1
- package/lib/components/features/search/TMSignatureInfoContent.d.ts +2 -1
- package/lib/components/features/search/TMViewHistoryDcmt.d.ts +2 -1
- package/lib/components/features/search/TMViewHistoryDcmt.js +1 -1
- package/lib/components/features/search/metadataFormHelper.d.ts +16 -0
- package/lib/components/features/search/metadataFormHelper.js +77 -0
- package/lib/components/features/tasks/TMTaskForm.d.ts +1 -1
- package/lib/components/features/tasks/TMTaskFormUtils.d.ts +10 -10
- package/lib/components/features/tasks/TMTasksAgenda.d.ts +1 -1
- package/lib/components/features/tasks/TMTasksCalendar.d.ts +1 -1
- package/lib/components/features/tasks/TMTasksHeader.d.ts +1 -1
- package/lib/components/features/tasks/TMTasksPanelContent.d.ts +1 -1
- package/lib/components/features/tasks/TMTasksUtilsView.d.ts +5 -5
- package/lib/components/features/tasks/TMTasksView.d.ts +1 -1
- package/lib/components/features/wg/TMWGsCopyMoveForm.d.ts +3 -2
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +8 -7
- package/lib/components/forms/Login/Chooser.d.ts +2 -2
- package/lib/components/forms/Login/TMLoginForm.js +15 -3
- package/lib/components/forms/TMChooserForm.d.ts +2 -1
- package/lib/components/forms/TMSaveForm.d.ts +4 -4
- package/lib/components/grids/TMBlogAttachments.d.ts +2 -1
- package/lib/components/grids/TMBlogAttachments.js +2 -2
- package/lib/components/grids/TMBlogHeader.d.ts +1 -1
- package/lib/components/grids/TMBlogsPost.d.ts +1 -1
- package/lib/components/grids/TMBlogsPost.js +5 -3
- package/lib/components/grids/TMBlogsPostUtils.d.ts +10 -9
- package/lib/components/grids/TMBlogsPostUtils.js +3 -1
- package/lib/components/grids/TMValidationItemsList.d.ts +2 -1
- package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +2 -1
- package/lib/components/layout/panelManager/TMPanelManagerContext.d.ts +2 -2
- package/lib/components/layout/panelManager/TMPanelManagerToolbar.d.ts +1 -1
- package/lib/components/layout/panelManager/TMPanelManagerWithPersistenceProvider.d.ts +2 -2
- package/lib/components/layout/panelManager/TMPanelWrapper.d.ts +2 -2
- package/lib/components/pages/TMPage.d.ts +1 -1
- package/lib/components/settings/SettingsAppearance.d.ts +2 -1
- package/lib/components/sidebar/TMAboutApp.d.ts +2 -1
- package/lib/components/sidebar/TMHeader.d.ts +3 -3
- package/lib/components/viewers/TMDataListItemViewer.d.ts +3 -2
- package/lib/components/viewers/TMDataUserIdItemViewer.d.ts +3 -2
- package/lib/components/viewers/TMMidViewer.d.ts +2 -2
- package/lib/components/viewers/TMTidViewer.d.ts +2 -2
- package/lib/components/viewers/TMTidViewer.js +14 -2
- package/lib/components/wizard/TMWizard.d.ts +1 -0
- package/lib/components/wizard/TMWizard.js +5 -3
- package/lib/helper/Enum_Localizator.js +2 -0
- package/lib/helper/MergePdfManager.d.ts +45 -0
- package/lib/helper/MergePdfManager.js +148 -0
- package/lib/helper/SDKUI_Globals.d.ts +1 -0
- package/lib/helper/SDKUI_Globals.js +3 -1
- package/lib/helper/SDKUI_Localizator.d.ts +40 -0
- package/lib/helper/SDKUI_Localizator.js +412 -12
- package/lib/helper/TMCommandsContextMenu.d.ts +1 -1
- package/lib/helper/TMIcons.d.ts +278 -278
- package/lib/helper/TMPdfViewer.d.ts +2 -1
- package/lib/helper/TMToppyMessage.d.ts +2 -2
- package/lib/helper/TMUtils.d.ts +57 -21
- package/lib/helper/TMUtils.js +159 -1
- package/lib/helper/certificateImportHelper.d.ts +43 -0
- package/lib/helper/certificateImportHelper.js +403 -0
- package/lib/helper/checkinCheckoutManager.d.ts +4 -3
- package/lib/helper/checkinCheckoutManager.js +29 -11
- package/lib/helper/helpers.d.ts +3 -2
- package/lib/helper/helpers.js +10 -0
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/hooks/useCheckInOutOperations.d.ts +4 -3
- package/lib/hooks/useDataUserIdItem.js +1 -1
- package/lib/hooks/useDcmtOperations.d.ts +20 -2
- package/lib/hooks/useDcmtOperations.js +244 -25
- package/lib/hooks/useDocumentOperations.d.ts +2 -2
- package/lib/hooks/useDocumentOperations.js +52 -13
- package/lib/hooks/useInputDialog.d.ts +2 -1
- package/lib/hooks/useRelatedDocuments.js +4 -4
- package/lib/services/platform_services.d.ts +7 -6
- package/lib/ts/types.d.ts +3 -1
- package/lib/ts/types.js +2 -0
- package/package.json +15 -7
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
-
import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataFormats,
|
|
4
|
-
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus, IconChevronDown, IconChevronRight, SDKUI_Localizator } from '../../../helper';
|
|
3
|
+
import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataFormats, MetadataDataDomains, RelationCacheService, RelationTypes, UserListCacheService } from "@topconsultnpm/sdk-ts";
|
|
4
|
+
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus, IconChevronDown, IconChevronRight, SDKUI_Localizator, buildDcmtDisplayName, SDKUI_Globals } from '../../../helper';
|
|
5
|
+
import ShowAlert from '../../base/TMAlert';
|
|
6
|
+
import TMToppyMessage from '../../../helper/TMToppyMessage';
|
|
5
7
|
import { TMColors } from '../../../utils/theme';
|
|
6
|
-
import { StyledDivHorizontal, StyledBadge
|
|
8
|
+
import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
|
|
7
9
|
import TMTreeView from '../../base/TMTreeView';
|
|
8
10
|
import { TMWaitPanel } from '../../base/TMWaitPanel';
|
|
9
11
|
import TMDataListItemViewer from '../../viewers/TMDataListItemViewer';
|
|
10
12
|
import TMDcmtIcon from './TMDcmtIcon';
|
|
11
13
|
import TMDataUserIdItemViewer from '../../viewers/TMDataUserIdItemViewer';
|
|
14
|
+
import TMTooltip from '../../base/TMTooltip';
|
|
15
|
+
import TMSpinner from '../../base/TMSpinner';
|
|
12
16
|
/**
|
|
13
17
|
* Check if document type has detail relations
|
|
14
18
|
*/
|
|
@@ -41,31 +45,6 @@ const isManyToManyRelation = async (relationID) => {
|
|
|
41
45
|
const rdlManyToMany = allRelations.filter(o => o.relationType == RelationTypes.ManyToMany && o.id === relationID) ?? [];
|
|
42
46
|
return rdlManyToMany.length > 0;
|
|
43
47
|
};
|
|
44
|
-
/**
|
|
45
|
-
* Get metadata keys excluding system metadata
|
|
46
|
-
*/
|
|
47
|
-
export const getMetadataKeys = (obj) => {
|
|
48
|
-
if (!obj)
|
|
49
|
-
return [];
|
|
50
|
-
const keys = Object.keys(obj);
|
|
51
|
-
// Escludi metadati di sistema (MID < 100 che sono uppercase) e altri campi tecnici
|
|
52
|
-
const sysMIDs = Object.values(SystemMIDs).map(o => o.toUpperCase());
|
|
53
|
-
return keys.filter(k => obj?.[k].value && !sysMIDs.includes(k)).filter(o => o !== "rowIndex" && o !== "ISLEXPROT");
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* Get display value keys for a document (max 5, prioritize SYS_Abstract)
|
|
57
|
-
*/
|
|
58
|
-
export const getDcmtDisplayValue = (obj) => {
|
|
59
|
-
if (!obj)
|
|
60
|
-
return [];
|
|
61
|
-
// Prima cerca SYS_Abstract
|
|
62
|
-
if (obj['SYS_Abstract']?.value) {
|
|
63
|
-
return ['SYS_Abstract'];
|
|
64
|
-
}
|
|
65
|
-
// Altrimenti prendi i primi 5 metadati non di sistema
|
|
66
|
-
const mdKeys = getMetadataKeys(obj);
|
|
67
|
-
return mdKeys.slice(0, 5);
|
|
68
|
-
};
|
|
69
48
|
/**
|
|
70
49
|
* Get display value formatted by column type
|
|
71
50
|
*/
|
|
@@ -113,36 +92,50 @@ export const getDisplayValueByColumn = (col, value) => {
|
|
|
113
92
|
export const searchResultToDataSource = async (searchResult, hideSysMetadata) => {
|
|
114
93
|
const rows = searchResult?.dtdResult?.rows ?? [];
|
|
115
94
|
const tid = searchResult?.fromTID;
|
|
116
|
-
|
|
95
|
+
// IMPORTANT: Pass true to get full metadata with all properties (isSpecialSearchOutput, etc.)
|
|
96
|
+
const dtd = await DcmtTypeListCacheService.GetAsync(tid, true);
|
|
117
97
|
const output = [];
|
|
118
98
|
for (let index = 0; index < rows.length; index++) {
|
|
119
99
|
const item = { rowIndex: index };
|
|
120
100
|
const row = rows[index];
|
|
121
101
|
for (let i = 0; i < row.length; i++) {
|
|
122
|
-
const
|
|
102
|
+
const column = searchResult?.dtdResult?.columns?.[i];
|
|
103
|
+
const mid = Number(column?.extendedProperties?.["MID"] ?? "0");
|
|
123
104
|
if (hideSysMetadata && mid < 100)
|
|
124
105
|
continue;
|
|
125
|
-
|
|
126
|
-
|
|
106
|
+
// For system metadata (MID <= 100), use UPPERCASE caption as key
|
|
107
|
+
// For custom metadata (MID > 100), use caption as key but handle duplicates
|
|
108
|
+
let key = column?.caption ?? '';
|
|
109
|
+
if (mid <= 100) {
|
|
127
110
|
key = key.toUpperCase();
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// If key already exists with a different MID, append MID to make it unique
|
|
114
|
+
if (item[key] !== undefined && item[key]?.md?.id !== mid) {
|
|
115
|
+
key = `${key}_${mid}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
128
118
|
const value = row[i];
|
|
129
119
|
const md = dtd?.metadata?.find(o => o.id == mid);
|
|
130
120
|
item[key] = {
|
|
131
121
|
md: md,
|
|
132
|
-
value: getDisplayValueByColumn(
|
|
122
|
+
value: getDisplayValueByColumn(column, value)
|
|
133
123
|
};
|
|
134
124
|
}
|
|
135
125
|
output.push(item);
|
|
136
126
|
}
|
|
137
127
|
return output;
|
|
138
128
|
};
|
|
139
|
-
const
|
|
129
|
+
export const DEFAULT_RELATION_EXPAND_LEVEL = 4; // Default maximum depth level for expansion (can be adjusted)
|
|
130
|
+
const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onAllItemsChanged, onDocumentDoubleClick, customItemRender, customDocumentStyle, customMainContainerContent, customDocumentContent, showMetadataNames = false, maxDepthLevel = 2, invertMasterNavigation = true, additionalStaticItems, showMainDocument = true, labelMainContainer, onNoRelationsFound, onItemContextMenu, focusedItemFormData = [], showExpandAllButton = false, defaultExpandAll = false, onLoadingStateChanged }) => {
|
|
140
131
|
// State
|
|
141
132
|
const [dcmtTypes, setDcmtTypes] = useState([]);
|
|
142
133
|
const [treeData, setTreeData] = useState([]);
|
|
143
134
|
const [showZeroDcmts, setShowZeroDcmts] = useState(initialShowZeroDcmts);
|
|
144
135
|
const [staticItemsState, setStaticItemsState] = useState([]);
|
|
145
|
-
const
|
|
136
|
+
const getInitialExpandLevel = () => SDKUI_Globals.userSettings?.searchSettings?.relationExpandLevel ?? DEFAULT_RELATION_EXPAND_LEVEL;
|
|
137
|
+
const [expandLevel, setExpandLevel] = useState(getInitialExpandLevel);
|
|
138
|
+
const [isAllCollapsed, setIsAllCollapsed] = useState(false);
|
|
146
139
|
const initialExpandAllAppliedRef = React.useRef(false);
|
|
147
140
|
const [showWaitPanel, setShowWaitPanel] = useState(false);
|
|
148
141
|
const [waitPanelTextPrimary, setWaitPanelTextPrimary] = useState('');
|
|
@@ -158,6 +151,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
158
151
|
const lastLoadedInputRef = React.useRef('');
|
|
159
152
|
// State to track loaded input key - triggers re-render for focus selection
|
|
160
153
|
const [loadedInputKey, setLoadedInputKey] = React.useState('');
|
|
154
|
+
// State to track how many GetMetadataAsync calls failed
|
|
155
|
+
const [metadataErrorCount, setMetadataErrorCount] = React.useState(0);
|
|
156
|
+
// State to track total master documents found (used when isForMaster is true)
|
|
157
|
+
const [totalMasterDocuments, setTotalMasterDocuments] = React.useState(0);
|
|
161
158
|
// Ref to track if user has manually expanded/collapsed static items
|
|
162
159
|
const userInteractedWithStaticItemsRef = React.useRef(false);
|
|
163
160
|
// Ref to track the last inputKey for which we set the focused item
|
|
@@ -172,6 +169,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
172
169
|
};
|
|
173
170
|
fetchAllUsers();
|
|
174
171
|
}, []);
|
|
172
|
+
// Notify parent when loading state changes
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
const isLoading = showWaitPanel || showExpansionWaitPanel;
|
|
175
|
+
onLoadingStateChanged?.(isLoading);
|
|
176
|
+
}, [showWaitPanel, showExpansionWaitPanel, onLoadingStateChanged]);
|
|
177
|
+
// Sincronizza expandLevel quando userSettings.searchSettings.relationExpandLevel cambia
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
const globalExpandLevel = SDKUI_Globals.userSettings?.searchSettings?.relationExpandLevel;
|
|
180
|
+
if (globalExpandLevel !== undefined && globalExpandLevel !== expandLevel) {
|
|
181
|
+
setExpandLevel(globalExpandLevel);
|
|
182
|
+
}
|
|
183
|
+
}, [SDKUI_Globals.userSettings?.searchSettings?.relationExpandLevel]);
|
|
175
184
|
/**
|
|
176
185
|
* Generate a stable key from inputDcmts to detect real changes
|
|
177
186
|
*/
|
|
@@ -200,7 +209,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
200
209
|
return [];
|
|
201
210
|
if (!mDID)
|
|
202
211
|
return [];
|
|
203
|
-
|
|
212
|
+
// maxLevel = 0 means unlimited depth (expand as much as possible)
|
|
213
|
+
// maxLevel > 0 means specific depth limit
|
|
214
|
+
// maxLevel < 0 means stop recursion
|
|
215
|
+
if (maxLevel < 0)
|
|
204
216
|
return [];
|
|
205
217
|
const dcmtTypeHasRel = await hasDetailRelations(mTID);
|
|
206
218
|
if (!dcmtTypeHasRel)
|
|
@@ -283,7 +295,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
283
295
|
return [];
|
|
284
296
|
if (!dDID)
|
|
285
297
|
return [];
|
|
286
|
-
|
|
298
|
+
// maxLevel = 0 means unlimited depth (expand as much as possible)
|
|
299
|
+
// maxLevel > 0 means specific depth limit
|
|
300
|
+
// maxLevel < 0 means stop recursion
|
|
301
|
+
if (maxLevel < 0)
|
|
287
302
|
return [];
|
|
288
303
|
const dcmtTypeHasRel = await hasMasterRelations(dTID);
|
|
289
304
|
if (!dcmtTypeHasRel)
|
|
@@ -341,6 +356,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
341
356
|
isExpandible: canExpand, // Can this doc be expanded?
|
|
342
357
|
values: row,
|
|
343
358
|
searchResult: [searchResult],
|
|
359
|
+
fileExt: row?.FILEEXT?.value,
|
|
344
360
|
// Leave items and itemsCount undefined so TMTreeView shows expand arrow based on isExpandible
|
|
345
361
|
// Children will be loaded lazily by calculateItemsForNode when expanded
|
|
346
362
|
expanded: false,
|
|
@@ -386,7 +402,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
386
402
|
updatedItems = [
|
|
387
403
|
{
|
|
388
404
|
key: `__info__${node.key}`,
|
|
389
|
-
name:
|
|
405
|
+
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
390
406
|
isContainer: false,
|
|
391
407
|
isDcmt: false,
|
|
392
408
|
isInfoMessage: true,
|
|
@@ -497,6 +513,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
497
513
|
}
|
|
498
514
|
const tree = [];
|
|
499
515
|
let processedCount = 0;
|
|
516
|
+
let errorCount = 0;
|
|
500
517
|
for (const dcmt of inputDcmts) {
|
|
501
518
|
// Check for abort
|
|
502
519
|
if (abortController.signal.aborted) {
|
|
@@ -509,7 +526,24 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
509
526
|
if (!searchEngine)
|
|
510
527
|
continue;
|
|
511
528
|
// Load document metadata
|
|
512
|
-
|
|
529
|
+
let result;
|
|
530
|
+
try {
|
|
531
|
+
result = await searchEngine.GetMetadataAsync(dcmt.TID, dcmt.DID, false);
|
|
532
|
+
}
|
|
533
|
+
catch (metadataError) {
|
|
534
|
+
console.warn('[TMRelationViewer] GetMetadataAsync failed for TID:', dcmt.TID, 'DID:', dcmt.DID, metadataError);
|
|
535
|
+
errorCount++;
|
|
536
|
+
ShowAlert({
|
|
537
|
+
title: SDKUI_Localizator.Warning,
|
|
538
|
+
message: `Il documento (TID: ${dcmt.TID}, DID: ${dcmt.DID}) non esiste più o non è accessibile.`,
|
|
539
|
+
mode: 'warning',
|
|
540
|
+
duration: 5000
|
|
541
|
+
});
|
|
542
|
+
// Skip this document and continue with the next one
|
|
543
|
+
processedCount++;
|
|
544
|
+
setWaitPanelValuePrimary(processedCount);
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
513
547
|
const dtd = await DcmtTypeListCacheService.GetAsync(dcmt.TID);
|
|
514
548
|
// Parse document values
|
|
515
549
|
const docValues = await searchResultToDataSource(result);
|
|
@@ -714,6 +748,24 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
714
748
|
if (hasNoRelations && onNoRelationsFound) {
|
|
715
749
|
onNoRelationsFound();
|
|
716
750
|
}
|
|
751
|
+
// Update metadata error count state
|
|
752
|
+
setMetadataErrorCount(errorCount);
|
|
753
|
+
// Count total master documents (only relevant when isForMaster is true)
|
|
754
|
+
if (isForMaster) {
|
|
755
|
+
const countMasterDocuments = (items) => {
|
|
756
|
+
let count = 0;
|
|
757
|
+
for (const item of items) {
|
|
758
|
+
if (item.isDcmt && !item.isZero) {
|
|
759
|
+
count++;
|
|
760
|
+
}
|
|
761
|
+
if (item.items && Array.isArray(item.items)) {
|
|
762
|
+
count += countMasterDocuments(item.items);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return count;
|
|
766
|
+
};
|
|
767
|
+
setTotalMasterDocuments(countMasterDocuments(tree));
|
|
768
|
+
}
|
|
717
769
|
// FIRST setup initial expansion state (root container expanded, first document expanded with focus, first correlation folder expanded)
|
|
718
770
|
// This must run BEFORE updateHiddenProperty so that infoMessage logic sees expanded=true
|
|
719
771
|
const expandedTree = setupInitialTreeExpansion(tree);
|
|
@@ -904,7 +956,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
904
956
|
return [
|
|
905
957
|
{
|
|
906
958
|
key: `__info__${node.key}`,
|
|
907
|
-
name:
|
|
959
|
+
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
908
960
|
isContainer: false,
|
|
909
961
|
isDcmt: false,
|
|
910
962
|
isInfoMessage: true,
|
|
@@ -933,7 +985,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
933
985
|
return [
|
|
934
986
|
{
|
|
935
987
|
key: `__info__${node.key}`,
|
|
936
|
-
name:
|
|
988
|
+
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
937
989
|
isContainer: false,
|
|
938
990
|
isDcmt: false,
|
|
939
991
|
isInfoMessage: true,
|
|
@@ -988,7 +1040,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
988
1040
|
return [
|
|
989
1041
|
{
|
|
990
1042
|
key: `__info__${node.key}`,
|
|
991
|
-
name:
|
|
1043
|
+
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
992
1044
|
isContainer: false,
|
|
993
1045
|
isDcmt: false,
|
|
994
1046
|
isInfoMessage: true,
|
|
@@ -1083,34 +1135,36 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1083
1135
|
: defaultDocumentStyle;
|
|
1084
1136
|
const textDecoration = isLogicallyDeleted ? 'line-through' : 'none';
|
|
1085
1137
|
const textColor = isLogicallyDeleted ? 'gray' : undefined;
|
|
1086
|
-
const defaultMetadataContent = item.values && (
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1138
|
+
const defaultMetadataContent = item.values && (() => {
|
|
1139
|
+
const displayKeys = buildDcmtDisplayName(item.values);
|
|
1140
|
+
return (_jsx(StyledDivHorizontal, { style: {
|
|
1141
|
+
fontSize: '1rem',
|
|
1142
|
+
overflow: 'hidden',
|
|
1143
|
+
flex: 1,
|
|
1144
|
+
minWidth: 0,
|
|
1145
|
+
whiteSpace: 'nowrap',
|
|
1146
|
+
textDecoration: textDecoration,
|
|
1147
|
+
color: textColor
|
|
1148
|
+
}, children: displayKeys.map((key, index) => {
|
|
1149
|
+
const md = item.values?.[key]?.md;
|
|
1150
|
+
const value = item.values?.[key]?.value;
|
|
1151
|
+
const isLast = index === displayKeys.length - 1;
|
|
1152
|
+
return (_jsxs(StyledDivHorizontal, { style: {
|
|
1153
|
+
flexShrink: isLast ? 1 : 0,
|
|
1154
|
+
minWidth: isLast ? 0 : 'auto',
|
|
1155
|
+
overflow: isLast ? 'hidden' : 'visible',
|
|
1156
|
+
textDecoration: textDecoration,
|
|
1157
|
+
color: textColor
|
|
1158
|
+
}, children: [index > 0 && _jsx("span", { style: { margin: '0 5px', color: textColor || '#999', textDecoration: textDecoration }, children: "\u2022" }), showMetadataNames && (_jsxs("span", { style: { color: textColor || '#666', marginRight: '5px', textDecoration: textDecoration }, children: [md?.name || key, ":"] })), md?.dataDomain === MetadataDataDomains.DataList ? (_jsx("span", { style: { textDecoration: textDecoration, color: textColor }, children: _jsx(TMDataListItemViewer, { dataListId: md.dataListID, viewMode: md.dataListViewMode, value: value }) })) : md?.dataDomain === MetadataDataDomains.UserID ? (_jsx("span", { style: { textDecoration: textDecoration, color: textColor }, children: _jsx(TMDataUserIdItemViewer, { userId: value, showIcon: true }) })) : (_jsx("span", { style: {
|
|
1159
|
+
fontWeight: 500,
|
|
1160
|
+
overflow: isLast ? 'hidden' : 'visible',
|
|
1161
|
+
textOverflow: isLast ? 'ellipsis' : 'clip',
|
|
1162
|
+
whiteSpace: 'nowrap',
|
|
1163
|
+
textDecoration: textDecoration,
|
|
1164
|
+
color: textColor
|
|
1165
|
+
}, children: value }))] }, `${key}_${index}`));
|
|
1166
|
+
}) }));
|
|
1167
|
+
})();
|
|
1114
1168
|
const metadataContent = customDocumentContent
|
|
1115
1169
|
? customDocumentContent(item, defaultMetadataContent || _jsx(_Fragment, {}))
|
|
1116
1170
|
: defaultMetadataContent;
|
|
@@ -1137,7 +1191,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1137
1191
|
checkoutStatusIcon = checkoutStatus.icon;
|
|
1138
1192
|
}
|
|
1139
1193
|
}
|
|
1140
|
-
return (_jsxs("div", { onDoubleClick: handleDoubleClick, style: documentStyle, children: [item.did && item.tid && showCurrentDcmtIndicator && inputDcmts?.some(d => d.DID === item.did && d.TID === item.tid) ? _jsx(IconBackhandIndexPointingRight, { fontSize: 22, overflow: 'visible' }) : _jsx(_Fragment, {}), item.values && (_jsx(TMDcmtIcon, { tid: item.values?.TID?.value, did: item.values?.DID?.value, fileExtension: item.values?.FILEEXT?.value, fileCount: item.values?.FILECOUNT?.value, isLexProt: item.values?.IsLexProt?.value, isMail: item.values?.ISMAIL?.value, isShared: item.values?.ISSHARED?.value, isSigned: item.values?.ISSIGNED?.value, downloadMode: 'openInNewWindow' })), checkoutStatusIcon, metadataContent] }));
|
|
1194
|
+
return (_jsxs("div", { onDoubleClick: handleDoubleClick, style: documentStyle, children: [item.did && item.tid && showCurrentDcmtIndicator && inputDcmts?.some(d => d.DID === item.did && d.TID === item.tid) ? _jsx(IconBackhandIndexPointingRight, { fontSize: 22, overflow: 'visible' }) : _jsx(_Fragment, {}), item.values && (_jsx(TMDcmtIcon, { tid: item.values?.TID?.value, did: item.values?.DID?.value, fileExtension: item.values?.FILEEXT?.value, fileCount: item.values?.FILECOUNT?.value, isLexProt: item.values?.IsLexProt?.value, isMail: item.values?.ISMAIL?.value, isShared: item.values?.ISSHARED?.value, isSigned: item.values?.ISSIGNED?.value, downloadMode: 'openInNewWindow', usePortal: true })), checkoutStatusIcon, metadataContent] }));
|
|
1141
1195
|
}, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument, allUsers]);
|
|
1142
1196
|
/**
|
|
1143
1197
|
* Wrapper renderer that handles custom rendering if provided
|
|
@@ -1183,13 +1237,231 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1183
1237
|
return { ...node, expanded: canExpand ? expanded : node.expanded, items: newItems };
|
|
1184
1238
|
});
|
|
1185
1239
|
}, []);
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1240
|
+
// Collassa l'albero impostando tutti i nodi come non espansi
|
|
1241
|
+
const handleCollapseTree = useCallback(() => {
|
|
1242
|
+
// Funzione helper per collassare ricorsivamente tutti i nodi
|
|
1243
|
+
const collapseAllNodes = (nodes) => {
|
|
1244
|
+
return nodes.map(node => ({
|
|
1245
|
+
...node,
|
|
1246
|
+
expanded: false,
|
|
1247
|
+
items: node.items ? collapseAllNodes(node.items) : node.items
|
|
1248
|
+
}));
|
|
1249
|
+
};
|
|
1250
|
+
// Collassa tutti i nodi dell'albero
|
|
1251
|
+
setTreeData(prevData => collapseAllNodes(prevData));
|
|
1252
|
+
setStaticItemsState(prevData => collapseAllNodes(prevData));
|
|
1253
|
+
setIsAllCollapsed(true);
|
|
1254
|
+
}, []);
|
|
1255
|
+
/**
|
|
1256
|
+
* Espande ricorsivamente l'albero fino a `expandLevel`.
|
|
1257
|
+
* Carica i figli lazy (API) solo se non già in memoria (isLoaded).
|
|
1258
|
+
* Il contatore totalNodes cresce dinamicamente per una % progresso accurata.
|
|
1259
|
+
*
|
|
1260
|
+
* @param forceReload - Se true (Ctrl+click), ricarica tutto ignorando la cache
|
|
1261
|
+
*/
|
|
1262
|
+
const handleExpandToLevel = useCallback(async (forceReload = false) => {
|
|
1263
|
+
// Mostra spinner per feedback immediato all'utente
|
|
1264
|
+
TMSpinner.show({ description: forceReload ? SDKUI_Localizator.FullReloadInProgress : SDKUI_Localizator.ExpansionInProgress });
|
|
1265
|
+
// Piccolo delay per permettere allo spinner di renderizzarsi prima del processing pesante
|
|
1266
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1267
|
+
/**
|
|
1268
|
+
* Se forceReload è true, prima resettiamo tutti i nodi come se non fossero mai stati caricati.
|
|
1269
|
+
* Questo significa rimuovere isLoaded e items da tutti i nodi documento,
|
|
1270
|
+
* così l'espansione successiva li ricaricherà tutti da API.
|
|
1271
|
+
*/
|
|
1272
|
+
const resetLoadedState = (nodes) => {
|
|
1273
|
+
return nodes.map(node => {
|
|
1274
|
+
const resetNode = { ...node };
|
|
1275
|
+
// Reset isLoaded per forzare il ricaricamento
|
|
1276
|
+
if (resetNode.isDcmt || resetNode.isExpandible) {
|
|
1277
|
+
resetNode.isLoaded = false;
|
|
1278
|
+
// Manteniamo items solo per i container (cartelle),
|
|
1279
|
+
// perché i container non vengono ricaricati via API
|
|
1280
|
+
// I documenti invece devono ricaricare i loro figli (correlazioni)
|
|
1281
|
+
if (resetNode.isDcmt) {
|
|
1282
|
+
resetNode.items = undefined;
|
|
1283
|
+
resetNode.itemsCount = undefined;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
// Reset ricorsivo sui figli
|
|
1287
|
+
if (resetNode.items && Array.isArray(resetNode.items)) {
|
|
1288
|
+
resetNode.items = resetLoadedState(resetNode.items);
|
|
1289
|
+
}
|
|
1290
|
+
return resetNode;
|
|
1291
|
+
});
|
|
1292
|
+
};
|
|
1293
|
+
// Se forceReload, reset dello stato prima di iniziare
|
|
1294
|
+
let workingTreeData = treeData;
|
|
1295
|
+
let workingStaticData = staticItemsState;
|
|
1296
|
+
if (forceReload) {
|
|
1297
|
+
workingTreeData = resetLoadedState(treeData);
|
|
1298
|
+
workingStaticData = resetLoadedState(staticItemsState);
|
|
1299
|
+
// Aggiorna subito lo stato per riflettere il reset
|
|
1300
|
+
setTreeData(workingTreeData);
|
|
1301
|
+
setStaticItemsState(workingStaticData);
|
|
1302
|
+
}
|
|
1303
|
+
const targetLevel = expandLevel;
|
|
1304
|
+
const newAbortController = new AbortController();
|
|
1305
|
+
setExpansionAbortController(newAbortController);
|
|
1306
|
+
// CONTATORI DINAMICI:
|
|
1307
|
+
// - totalNodes: cresce quando scopriamo nuovi nodi (evita che percentuale > 100%)
|
|
1308
|
+
// - processedNodes: quanti nodi abbiamo già processato
|
|
1309
|
+
// - maxReachedProgress: percentuale massima raggiunta (evita che la barra torni indietro)
|
|
1310
|
+
let totalNodes = 0;
|
|
1311
|
+
let processedNodes = 0;
|
|
1312
|
+
let hasApiCalls = false;
|
|
1313
|
+
let maxReachedProgress = 0;
|
|
1314
|
+
/**
|
|
1315
|
+
* Conta i nodi espandibili in un array (solo primo livello, non ricorsivo).
|
|
1316
|
+
* Usato per aggiornare totalNodes man mano che scopriamo nuovi figli.
|
|
1317
|
+
*/
|
|
1318
|
+
const countNodesAtLevel = (nodes) => {
|
|
1319
|
+
let count = 0;
|
|
1320
|
+
for (const node of nodes) {
|
|
1321
|
+
if (node.isContainer || node.isExpandible || node.isDcmt) {
|
|
1322
|
+
count++;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
return count;
|
|
1326
|
+
};
|
|
1327
|
+
// Conta iniziale: solo i nodi al livello root
|
|
1328
|
+
totalNodes = countNodesAtLevel([...workingTreeData, ...workingStaticData]);
|
|
1329
|
+
/**
|
|
1330
|
+
* FUNZIONE RICORSIVA DI ESPANSIONE
|
|
1331
|
+
*
|
|
1332
|
+
* @param nodes - Array di nodi da processare
|
|
1333
|
+
* @param currentLevel - Livello corrente (0 = root)
|
|
1334
|
+
* @returns Array di nodi con expanded=true e figli caricati
|
|
1335
|
+
*
|
|
1336
|
+
* FLUSSO:
|
|
1337
|
+
* 1. Se targetLevel > 0 e currentLevel >= targetLevel → STOP, ritorna i nodi così come sono
|
|
1338
|
+
* (Se targetLevel = 0 → espandi senza limiti, non applicare mai il limite)
|
|
1339
|
+
* 2. Per ogni nodo espandibile:
|
|
1340
|
+
* a) Imposta expanded = true
|
|
1341
|
+
* b) Se i figli non sono caricati → chiama API per caricarli
|
|
1342
|
+
* c) Aggiorna totalNodes con i nuovi figli scoperti
|
|
1343
|
+
* d) CHIAMATA RICORSIVA su expandedNode.items con currentLevel + 1
|
|
1344
|
+
* → QUESTO È IL PUNTO CHIAVE: i nuovi nodi vengono espansi!
|
|
1345
|
+
*/
|
|
1346
|
+
const expandToLevel = async (nodes, currentLevel) => {
|
|
1347
|
+
// CONDIZIONE DI USCITA: abbiamo raggiunto il livello target
|
|
1348
|
+
// targetLevel = 0 significa espandi senza limiti (non applicare mai il limite)
|
|
1349
|
+
// targetLevel > 0 significa applica il limite normalmente
|
|
1350
|
+
if (targetLevel > 0 && currentLevel >= targetLevel)
|
|
1351
|
+
return nodes;
|
|
1352
|
+
// Controlla se l'utente ha annullato l'operazione
|
|
1353
|
+
if (newAbortController.signal.aborted)
|
|
1354
|
+
return nodes;
|
|
1355
|
+
const result = [];
|
|
1356
|
+
for (const node of nodes) {
|
|
1357
|
+
// Controlla abort per ogni nodo (permette cancellazione veloce)
|
|
1358
|
+
if (newAbortController.signal.aborted) {
|
|
1359
|
+
result.push(node);
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
// Crea copia del nodo (immutabilità React)
|
|
1363
|
+
let expandedNode = { ...node };
|
|
1364
|
+
// Verifica se questo nodo può essere espanso
|
|
1365
|
+
const canExpand = node.isContainer || node.isExpandible || node.isDcmt;
|
|
1366
|
+
if (canExpand) {
|
|
1367
|
+
// PASSO 1: Imposta il nodo come espanso
|
|
1368
|
+
expandedNode.expanded = true;
|
|
1369
|
+
// PASSO 2: Verifica se i figli sono già caricati o vanno caricati via API
|
|
1370
|
+
// Se forceReload è true, ricarica sempre (ignora isLoaded e items esistenti)
|
|
1371
|
+
const hasLoadedItems = !forceReload && node.items && Array.isArray(node.items) && node.items.length > 0;
|
|
1372
|
+
const actualHasItems = node.items && Array.isArray(node.items) && node.items.length > 0;
|
|
1373
|
+
const needsLoading = (forceReload || (!hasLoadedItems && !node.isLoaded)) && (node.isExpandible || node.isDcmt);
|
|
1374
|
+
if (needsLoading && node.tid && node.did) {
|
|
1375
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1376
|
+
// CASO A: I figli NON sono in memoria → CHIAMATA API
|
|
1377
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1378
|
+
// Mostra pannello di attesa solo quando ci sono chiamate API
|
|
1379
|
+
if (!hasApiCalls) {
|
|
1380
|
+
hasApiCalls = true;
|
|
1381
|
+
TMSpinner.hide();
|
|
1382
|
+
setShowExpansionWaitPanel(true);
|
|
1383
|
+
setExpansionWaitPanelMaxValue(100);
|
|
1384
|
+
setExpansionWaitPanelValue(0);
|
|
1385
|
+
}
|
|
1386
|
+
try {
|
|
1387
|
+
let loadedItems = [];
|
|
1388
|
+
// Carica i documenti correlati (detail o master in base alla modalità)
|
|
1389
|
+
if (isForMaster && !invertMasterNavigation) {
|
|
1390
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
1391
|
+
}
|
|
1392
|
+
else if (isForMaster) {
|
|
1393
|
+
loadedItems = await getMasterDcmtsAsync(node.tid, node.did, 1);
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
1397
|
+
}
|
|
1398
|
+
// Applica le regole di visibilità (showZeroDcmts)
|
|
1399
|
+
expandedNode.items = updateHiddenProperty(loadedItems);
|
|
1400
|
+
expandedNode.isLoaded = true;
|
|
1401
|
+
expandedNode.itemsCount = loadedItems.length;
|
|
1402
|
+
// IMPORTANTE: Aggiungi i nuovi nodi scoperti al contatore totale
|
|
1403
|
+
// Questo viene fatto SOLO se non siamo all'ultimo livello
|
|
1404
|
+
// (perché i nodi dell'ultimo livello non verranno espansi)
|
|
1405
|
+
// targetLevel = 0 significa illimitato, quindi aggiungi sempre
|
|
1406
|
+
if (targetLevel === 0 || currentLevel + 1 < targetLevel) {
|
|
1407
|
+
const newExpandableCount = countNodesAtLevel(loadedItems);
|
|
1408
|
+
totalNodes += newExpandableCount;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
catch (error) {
|
|
1412
|
+
console.error('Errore nel caricamento dei figli del nodo:', error);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
else if (actualHasItems && (targetLevel === 0 || currentLevel + 1 < targetLevel)) {
|
|
1416
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1417
|
+
// CASO B: I figli esistono già in memoria (non ricaricati via API)
|
|
1418
|
+
// Aggiungiamo comunque il loro conteggio a totalNodes
|
|
1419
|
+
// Usiamo actualHasItems invece di hasLoadedItems per gestire anche forceReload
|
|
1420
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1421
|
+
const childCount = countNodesAtLevel(node.items);
|
|
1422
|
+
totalNodes += childCount;
|
|
1423
|
+
}
|
|
1424
|
+
// Aggiorna il progresso DOPO aver processato questo nodo
|
|
1425
|
+
processedNodes++;
|
|
1426
|
+
// Calcola percentuale e aggiorna solo se è più alta di prima
|
|
1427
|
+
// (evita che la barra di progresso torni indietro)
|
|
1428
|
+
const currentProgress = totalNodes > 0 ? Math.floor((processedNodes / totalNodes) * 100) : 0;
|
|
1429
|
+
if (currentProgress > maxReachedProgress) {
|
|
1430
|
+
maxReachedProgress = currentProgress;
|
|
1431
|
+
setExpansionWaitPanelValue(maxReachedProgress);
|
|
1432
|
+
}
|
|
1433
|
+
setExpansionWaitPanelText(SDKUI_Localizator.ExpansionProgress.replaceParams(processedNodes.toString(), totalNodes.toString()));
|
|
1434
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
1435
|
+
// PASSO 3: CHIAMATA RICORSIVA SUI FIGLI
|
|
1436
|
+
// Questo è il punto chiave: i nuovi nodi appena caricati vengono
|
|
1437
|
+
// passati a expandToLevel con currentLevel + 1, quindi anche loro
|
|
1438
|
+
// verranno espansi (se non abbiamo raggiunto il livello target)
|
|
1439
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
1440
|
+
if (expandedNode.items && Array.isArray(expandedNode.items) && expandedNode.items.length > 0) {
|
|
1441
|
+
expandedNode.items = await expandToLevel(expandedNode.items, currentLevel + 1);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
result.push(expandedNode);
|
|
1445
|
+
}
|
|
1446
|
+
return result;
|
|
1447
|
+
};
|
|
1448
|
+
try {
|
|
1449
|
+
// Avvia l'espansione dal livello 0 (root)
|
|
1450
|
+
const expandedTree = await expandToLevel(workingTreeData, 0);
|
|
1451
|
+
setTreeData(expandedTree);
|
|
1452
|
+
// Espandi anche gli item statici (se presenti)
|
|
1453
|
+
const expandedStatic = await expandToLevel(workingStaticData, 0);
|
|
1454
|
+
setStaticItemsState(expandedStatic);
|
|
1455
|
+
setIsAllCollapsed(false);
|
|
1456
|
+
userInteractedWithStaticItemsRef.current = true;
|
|
1457
|
+
}
|
|
1458
|
+
finally {
|
|
1459
|
+
// Cleanup: nascondi spinner e pannello di attesa
|
|
1460
|
+
TMSpinner.hide();
|
|
1461
|
+
setShowExpansionWaitPanel(false);
|
|
1462
|
+
setExpansionAbortController(undefined);
|
|
1463
|
+
}
|
|
1464
|
+
}, [expandLevel, treeData, staticItemsState, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty]);
|
|
1193
1465
|
/**
|
|
1194
1466
|
* Apply defaultExpandAll once on initial tree load.
|
|
1195
1467
|
*/
|
|
@@ -1215,32 +1487,70 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1215
1487
|
setStaticItemsState(staticItems); // Preserve static items state (expanded/collapsed)
|
|
1216
1488
|
// Mark that user has interacted with the tree (expanded/collapsed nodes)
|
|
1217
1489
|
userInteractedWithStaticItemsRef.current = true;
|
|
1490
|
+
// Reset collapsed state when user manually expands a node
|
|
1491
|
+
setIsAllCollapsed(false);
|
|
1218
1492
|
}, []);
|
|
1219
1493
|
if (showWaitPanel) {
|
|
1220
|
-
return _jsx("div", { style: { padding: '20px', textAlign: 'center' }, children: _jsx(TMWaitPanel, { title:
|
|
1494
|
+
return _jsx("div", { style: { padding: '20px', textAlign: 'center' }, children: _jsx(TMWaitPanel, { title: SDKUI_Localizator.LoadingDetailDocuments, showPrimary: true, textPrimary: waitPanelTextPrimary, valuePrimary: waitPanelValuePrimary, maxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, onAbortClick: () => { setTimeout(() => { abortController.abort(); }, 1000); } }) });
|
|
1221
1495
|
}
|
|
1222
1496
|
if (mergedTreeData.length === 0) {
|
|
1497
|
+
// Se NON isForMaster e tutti i GetMetadataAsync sono falliti
|
|
1498
|
+
if (!isForMaster && metadataErrorCount === inputDcmts.length) {
|
|
1499
|
+
return _jsx(TMToppyMessage, { message: SDKUI_Localizator.DocumentsNotAvailableOrNoCorrelations });
|
|
1500
|
+
}
|
|
1223
1501
|
// If parent handles no-relations via callback, render nothing to avoid UI flash
|
|
1224
1502
|
if (onNoRelationsFound)
|
|
1225
1503
|
return null;
|
|
1226
|
-
return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children:
|
|
1504
|
+
return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: SDKUI_Localizator.NoRelationsAvailable });
|
|
1505
|
+
}
|
|
1506
|
+
// Se isForMaster e non ci sono master documents disponibili
|
|
1507
|
+
if (isForMaster && totalMasterDocuments === 0) {
|
|
1508
|
+
return _jsx(TMToppyMessage, { message: SDKUI_Localizator.NoMasterDocumentsAvailable });
|
|
1227
1509
|
}
|
|
1228
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, width: '100%' }, children: [showExpandAllButton && (
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1510
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, width: '100%' }, children: [showExpandAllButton && (_jsxs("div", { style: { flexShrink: 0, display: 'flex', alignItems: 'center', gap: '6px', height: '40px', paddingLeft: '10px', backgroundColor: TMColors.toolbar_background }, children: [_jsx(TMTooltip, { content: expandLevel === 0
|
|
1511
|
+
? `${SDKUI_Localizator.ExpandAllLevels} (${SDKUI_Localizator.CtrlClickToReloadAll})`
|
|
1512
|
+
: `${SDKUI_Localizator.ExpandToLevel.replaceParams(expandLevel.toString())} (${SDKUI_Localizator.CtrlClickToReloadAll})`, children: _jsxs("button", { type: "button", onClick: (e) => handleExpandToLevel(e.ctrlKey), style: {
|
|
1513
|
+
display: 'inline-flex',
|
|
1514
|
+
alignItems: 'center',
|
|
1515
|
+
justifyContent: 'center',
|
|
1516
|
+
gap: '5px',
|
|
1517
|
+
height: '28px',
|
|
1518
|
+
padding: '0 10px',
|
|
1519
|
+
fontSize: '0.8rem',
|
|
1520
|
+
fontWeight: 500,
|
|
1521
|
+
color: 'white',
|
|
1522
|
+
background: TMColors.primary,
|
|
1523
|
+
border: 'none',
|
|
1524
|
+
borderRadius: '4px',
|
|
1525
|
+
cursor: 'pointer',
|
|
1526
|
+
whiteSpace: 'nowrap',
|
|
1527
|
+
outline: 'none',
|
|
1528
|
+
transition: 'background 0.2s'
|
|
1529
|
+
}, children: [_jsx(IconChevronDown, { fontSize: 14 }), _jsx("span", { children: expandLevel === 0 ? SDKUI_Localizator.ExpandAllLevels : SDKUI_Localizator.ExpandLevels.replaceParams(expandLevel.toString()) })] }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.CollapseTree, children: _jsxs("button", { type: "button", onClick: handleCollapseTree, disabled: isAllCollapsed, style: {
|
|
1530
|
+
display: 'inline-flex',
|
|
1531
|
+
alignItems: 'center',
|
|
1532
|
+
justifyContent: 'center',
|
|
1533
|
+
gap: '5px',
|
|
1534
|
+
height: '28px',
|
|
1535
|
+
padding: '0 10px',
|
|
1536
|
+
fontSize: '0.8rem',
|
|
1537
|
+
fontWeight: 500,
|
|
1538
|
+
color: isAllCollapsed ? '#999' : 'white',
|
|
1539
|
+
background: isAllCollapsed ? '#e0e0e0' : TMColors.secondary || '#6c757d',
|
|
1540
|
+
border: 'none',
|
|
1541
|
+
borderRadius: '4px',
|
|
1542
|
+
cursor: isAllCollapsed ? 'not-allowed' : 'pointer',
|
|
1543
|
+
whiteSpace: 'nowrap',
|
|
1544
|
+
outline: 'none',
|
|
1545
|
+
transition: 'background 0.2s'
|
|
1546
|
+
}, children: [_jsx(IconChevronRight, { fontSize: 14 }), _jsx("span", { children: SDKUI_Localizator.CollapseAll })] }) })] })), _jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, itemsPerPage: 100, onSelectionChanged: handleSelectedItemsChanged, onItemContextMenu: showExpansionWaitPanel ? undefined : onItemContextMenu, enableVirtualization: true, shouldDelayFocusOnEvent: (node, e) => {
|
|
1547
|
+
// Ritarda il focus quando si clicca sull'icona del documento
|
|
1548
|
+
// per permettere al doppio click di funzionare
|
|
1549
|
+
const target = e.target;
|
|
1550
|
+
return !!target.closest('.tm-dcmt-icon');
|
|
1551
|
+
} }) }), showExpansionWaitPanel && (_jsx(TMWaitPanel, { title: isForMaster ? SDKUI_Localizator.LoadingMasterDocuments : SDKUI_Localizator.LoadingDetailDocuments, showPrimary: true, textPrimary: expansionWaitPanelText, valuePrimary: expansionWaitPanelValue, maxValuePrimary: expansionWaitPanelMaxValue, isCancelable: true, abortController: expansionAbortController, onAbortClick: () => {
|
|
1242
1552
|
setTimeout(() => {
|
|
1243
|
-
|
|
1553
|
+
expansionAbortController?.abort();
|
|
1244
1554
|
}, 100);
|
|
1245
1555
|
} }))] }));
|
|
1246
1556
|
};
|