@topconsultnpm/sdkui-react 6.22.0-t1 → 6.22.0-t3
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 +3 -3
- package/lib/components/NewComponents/ContextMenu/hooks.d.ts +1 -1
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +45 -43
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +6 -6
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +80 -73
- package/lib/components/base/Styled.d.ts +92 -76
- package/lib/components/base/Styled.js +7 -5
- package/lib/components/base/TMAccordionNew.js +7 -7
- package/lib/components/base/TMAlert.d.ts +32 -32
- package/lib/components/base/TMAlert.js +6 -8
- package/lib/components/base/TMAreaManager.js +53 -55
- package/lib/components/base/TMButton.d.ts +13 -0
- package/lib/components/base/TMButton.js +139 -7
- package/lib/components/base/TMClosableList.js +3 -3
- package/lib/components/base/TMCustomButton.js +40 -48
- package/lib/components/base/TMDataGrid.d.ts +4 -0
- package/lib/components/base/TMDataGrid.js +292 -12
- package/lib/components/base/TMDataGridExportForm.js +39 -30
- package/lib/components/base/TMDropDownMenu.js +1 -1
- package/lib/components/base/TMEditorBase.d.ts +2 -0
- package/lib/components/base/TMFileManager.js +5 -3
- package/lib/components/base/TMFileManagerDataGridView.js +12 -3
- package/lib/components/base/TMFileManagerThumbnailItems.js +20 -10
- package/lib/components/base/TMFileManagerUtils.d.ts +6 -6
- package/lib/components/base/TMFileManagerUtils.js +37 -1
- package/lib/components/base/TMLayout.d.ts +2 -2
- package/lib/components/base/TMLayout.js +3 -3
- package/lib/components/base/TMList.js +1 -1
- package/lib/components/base/TMListView.js +3 -3
- package/lib/components/base/TMModal.d.ts +11 -0
- package/lib/components/base/TMModal.js +47 -13
- package/lib/components/base/TMPanel.d.ts +6 -0
- package/lib/components/base/TMPanel.js +141 -28
- package/lib/components/base/TMPopUp.js +5 -2
- package/lib/components/base/TMResizableMenu.js +2 -2
- package/lib/components/base/TMRightSidebar.js +1 -1
- package/lib/components/base/TMShowAllOrMaxItemsButton.js +1 -1
- package/lib/components/base/TMSpinner.d.ts +2 -1
- package/lib/components/base/TMSpinner.js +9 -7
- package/lib/components/base/TMTab.js +2 -2
- package/lib/components/base/TMTabControl.d.ts +106 -0
- package/lib/components/base/TMTabControl.js +664 -0
- package/lib/components/base/TMToggleButton.js +2 -2
- package/lib/components/base/TMToolbarCard.js +3 -3
- package/lib/components/base/TMTooltip.d.ts +2 -1
- package/lib/components/base/TMTooltip.js +24 -8
- package/lib/components/base/TMTreeView.d.ts +13 -7
- package/lib/components/base/TMTreeView.js +143 -70
- package/lib/components/base/TMWaitPanel.js +3 -3
- package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
- package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
- package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
- package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
- package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
- package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
- package/lib/components/base/waitPanel/index.d.ts +1 -0
- package/lib/components/base/waitPanel/index.js +1 -0
- package/lib/components/choosers/TMCultureIDPicker.js +2 -2
- package/lib/components/choosers/TMDataListItemChooser.js +1 -1
- package/lib/components/choosers/TMDataListItemPicker.js +2 -2
- package/lib/components/choosers/TMDcmtTypeChooser.js +5 -4
- package/lib/components/choosers/TMDiskChooser.js +1 -1
- package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
- package/lib/components/choosers/TMDistinctValues.js +157 -141
- package/lib/components/choosers/TMDynDataListItemChooser.js +1 -1
- package/lib/components/choosers/TMImageIDChooser.js +1 -1
- package/lib/components/choosers/TMMetadataChooser.js +3 -3
- package/lib/components/choosers/TMPathChooser.js +17 -34
- package/lib/components/choosers/TMQuickSearchInfo.d.ts +13 -0
- package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
- package/lib/components/choosers/TMRelationChooser.js +1 -1
- package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
- package/lib/components/choosers/TMSelectedValuesSummary.js +89 -0
- package/lib/components/choosers/TMUserChooser.js +1 -1
- package/lib/components/editors/TMCheckBox.js +1 -1
- package/lib/components/editors/TMDateBox.d.ts +16 -1
- package/lib/components/editors/TMDateBox.js +90 -22
- package/lib/components/editors/TMDropDown.d.ts +3 -0
- package/lib/components/editors/TMDropDown.js +197 -5
- package/lib/components/editors/TMEditorStyled.d.ts +21 -43
- package/lib/components/editors/TMFormulaEditor.js +1 -1
- package/lib/components/editors/TMHtmlContentDisplay.js +1 -1
- package/lib/components/editors/TMLocalizedTextBox.js +1 -1
- package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
- package/lib/components/editors/TMMetadataEditor.js +27 -23
- package/lib/components/editors/TMMetadataValues.js +6 -6
- package/lib/components/editors/TMRadioButton.js +2 -2
- package/lib/components/editors/TMSummary.js +3 -3
- package/lib/components/editors/TMTextArea.d.ts +1 -0
- package/lib/components/editors/TMTextArea.js +25 -17
- package/lib/components/editors/TMTextBox.d.ts +2 -0
- package/lib/components/editors/TMTextBox.js +32 -21
- package/lib/components/editors/TMTextExpression.js +1 -1
- package/lib/components/features/archive/TMArchive.js +24 -5
- package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +29 -0
- package/lib/components/features/assistant/TMHelpOnLineChat.js +1384 -0
- package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +4 -4
- package/lib/components/features/assistant/TMToppySpeechBubble.js +1 -1
- package/lib/components/features/assistant/dataset/Designer-Suggested-Questions.json +102 -0
- package/lib/components/features/assistant/dataset/Login-Suggested-Questions.json +32 -0
- package/lib/components/features/assistant/dataset/Surfer-Suggested-Questions.json +102 -0
- package/lib/components/features/documents/TMBatchUpdateForm.js +22 -9
- package/lib/components/features/documents/TMCopyToFolderForm.js +50 -42
- package/lib/components/features/documents/TMDcmtBlog.d.ts +3 -0
- package/lib/components/features/documents/TMDcmtBlog.js +4 -4
- package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
- package/lib/components/features/documents/TMDcmtForm.js +281 -113
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +79 -9
- package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -3
- package/lib/components/features/documents/TMDcmtIcon.js +8 -8
- package/lib/components/features/documents/TMDcmtPreview.d.ts +7 -5
- package/lib/components/features/documents/TMDcmtPreview.js +16 -10
- package/lib/components/features/documents/TMDcmtTasks.js +2 -9
- package/lib/components/features/documents/TMDownloadRelationViewerSection.js +1 -1
- package/lib/components/features/documents/TMFileUploader.js +7 -7
- package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
- package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -108
- package/lib/components/features/documents/TMMasterInfoFields.d.ts +20 -0
- package/lib/components/features/documents/TMMasterInfoFields.js +185 -0
- package/lib/components/features/documents/TMMergeToPdfForm.d.ts +1 -2
- package/lib/components/features/documents/TMMergeToPdfForm.js +83 -56
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +46 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +320 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +370 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +283 -0
- package/lib/components/features/documents/TMRelationViewer.d.ts +29 -9
- package/lib/components/features/documents/TMRelationViewer.js +310 -208
- package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +1 -1
- package/lib/components/features/documents/relationTreeState.d.ts +32 -0
- package/lib/components/features/documents/relationTreeState.js +89 -0
- package/lib/components/features/search/TMMetadataOutputForm.js +1 -1
- package/lib/components/features/search/TMMetadataSorterForm.js +1 -1
- package/lib/components/features/search/TMSavedQuerySelector.js +2 -2
- package/lib/components/features/search/TMSearch.d.ts +1 -1
- package/lib/components/features/search/TMSearch.js +172 -34
- package/lib/components/features/search/TMSearchQueryEditor.js +7 -5
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +14 -4
- package/lib/components/features/search/TMSearchQueryPanel.js +153 -62
- package/lib/components/features/search/TMSearchResult.d.ts +21 -0
- package/lib/components/features/search/TMSearchResult.js +461 -152
- package/lib/components/features/search/TMSignatureInfoContent.js +1 -1
- package/lib/components/features/search/TMTreeSelector.js +130 -83
- package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
- package/lib/components/features/tasks/TMTaskForm.js +1 -1
- package/lib/components/features/tasks/TMTaskFormUtils.js +3 -3
- package/lib/components/features/tasks/TMTasksAgenda.js +1 -1
- package/lib/components/features/tasks/TMTasksCalendar.js +1 -1
- package/lib/components/features/tasks/TMTasksHeader.js +1 -1
- package/lib/components/features/tasks/TMTasksUtilsView.js +1 -1
- package/lib/components/features/tasks/TMTasksView.js +97 -102
- package/lib/components/features/wg/TMWGsCopyMoveForm.js +92 -92
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
- package/lib/components/features/workflow/TMWorkflowPopup.js +9 -5
- package/lib/components/features/workflow/diagram/ConnectionComponent.js +3 -3
- package/lib/components/features/workflow/diagram/DiagramItemComponent.js +3 -3
- package/lib/components/features/workflow/diagram/DiagramItemForm.js +1 -1
- package/lib/components/features/workflow/diagram/DiagramItemSvgContent.js +1 -1
- package/lib/components/features/workflow/diagram/RecipientList.js +1 -1
- package/lib/components/features/workflow/diagram/WFDiagram.js +4 -4
- package/lib/components/features/workflow/diagram/WorkitemRecipientsEditor.d.ts +3 -1
- package/lib/components/forms/Login/ChangePasswordInputs.d.ts +3 -1
- package/lib/components/forms/Login/Menu.js +1 -1
- package/lib/components/forms/Login/OTPReader.js +1 -1
- package/lib/components/forms/Login/PasswordStrengthChecker.js +1 -1
- package/lib/components/forms/Login/RapidAccessLogin.js +2 -2
- package/lib/components/forms/Login/RecoverPasswordFlow.js +2 -2
- package/lib/components/forms/Login/SelectBox.js +1 -1
- package/lib/components/forms/Login/StepIndicator.js +1 -1
- package/lib/components/forms/Login/TMLoginForm.js +7 -7
- package/lib/components/forms/Login/TextBox.js +1 -1
- package/lib/components/forms/TMApplyForm.js +3 -3
- package/lib/components/forms/TMChooserForm.js +1 -1
- package/lib/components/forms/TMResultDialog.js +1 -1
- package/lib/components/forms/TMSaveForm.js +3 -3
- package/lib/components/grids/TMBlogAttachments.js +14 -3
- package/lib/components/grids/TMBlogHeader.js +5 -1
- package/lib/components/grids/TMBlogsPost.d.ts +2 -0
- package/lib/components/grids/TMBlogsPost.js +122 -99
- package/lib/components/grids/TMBlogsPostUtils.d.ts +1 -0
- package/lib/components/grids/TMBlogsPostUtils.js +15 -4
- package/lib/components/grids/TMRecentsManager.js +4 -4
- package/lib/components/grids/TMValidationItemsList.js +1 -1
- package/lib/components/index.d.ts +6 -0
- package/lib/components/index.js +5 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.js +25 -15
- package/lib/components/layout/panelManager/TMPanelManagerContext.js +68 -93
- package/lib/components/layout/panelManager/TMPanelManagerToolbar.d.ts +4 -5
- package/lib/components/layout/panelManager/TMPanelManagerToolbar.js +1 -1
- package/lib/components/layout/panelManager/TMPanelWrapper.js +3 -3
- package/lib/components/layout/panelManager/types.d.ts +3 -0
- package/lib/components/layout/panelManager/utils.js +13 -11
- package/lib/components/pages/TMPage.js +30 -32
- package/lib/components/query/TMQueryCountButton.js +1 -1
- package/lib/components/query/TMQueryEditor.d.ts +17 -10
- package/lib/components/query/TMQueryEditor.js +9 -8
- package/lib/components/sidebar/TMCommandsPanel.d.ts +4 -4
- package/lib/components/sidebar/TMCommandsPanel.js +1 -1
- package/lib/components/sidebar/TMHeader.d.ts +1 -0
- package/lib/components/sidebar/TMHeader.js +37 -27
- package/lib/components/sidebar/TMSidebar.js +1 -1
- package/lib/components/sidebar/TMSidebarItem.js +1 -1
- package/lib/components/viewers/TMMidViewer.js +6 -3
- package/lib/components/viewers/TMTidViewer.js +89 -28
- package/lib/components/wizard/TMStepIndicator.js +3 -3
- package/lib/css/tm-sdkui.css +1 -1
- package/lib/devextreme-license.d.ts +1 -0
- package/lib/devextreme-license.js +3 -0
- package/lib/helper/Enum_Localizator.d.ts +4 -4
- package/lib/helper/Enum_Localizator.js +1 -0
- package/lib/helper/Globalization.d.ts +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +110 -0
- package/lib/helper/SDKUI_Globals.js +151 -0
- package/lib/helper/SDKUI_Localizator.d.ts +756 -677
- package/lib/helper/SDKUI_Localizator.js +790 -0
- package/lib/helper/TMAppUpdatedModal.d.ts +2 -0
- package/lib/helper/TMAppUpdatedModal.js +134 -0
- package/lib/helper/TMIcons.d.ts +1 -0
- package/lib/helper/TMIcons.js +4 -3
- package/lib/helper/TMPdfViewer.js +1 -1
- package/lib/helper/TMToppyMessage.js +1 -1
- package/lib/helper/TMUtils.d.ts +51 -4
- package/lib/helper/TMUtils.js +273 -12
- package/lib/helper/ZipManager.d.ts +1 -1
- package/lib/helper/ZipManager.js +2 -1
- package/lib/helper/checkinCheckoutManager.d.ts +1 -1
- package/lib/helper/checkinCheckoutManager.js +1 -1
- package/lib/helper/dataGridSearchHelper.d.ts +28 -0
- package/lib/helper/dataGridSearchHelper.js +51 -0
- package/lib/helper/dcmtsHelper.d.ts +22 -0
- package/lib/helper/dcmtsHelper.js +60 -1
- package/lib/helper/helpers.d.ts +24 -2
- package/lib/helper/helpers.js +59 -3
- package/lib/helper/index.d.ts +2 -0
- package/lib/helper/index.js +2 -0
- package/lib/helper/queryHelper.d.ts +6 -1
- package/lib/helper/queryHelper.js +8 -3
- package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
- package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
- package/lib/hooks/useArchiveListForm.d.ts +39 -0
- package/lib/hooks/useArchiveListForm.js +46 -0
- package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
- package/lib/hooks/useCaseFlowApprove.js +157 -0
- package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
- package/lib/hooks/useCheckInOutOperations.js +14 -34
- package/lib/hooks/useDataUserIdItem.d.ts +1 -0
- package/lib/hooks/useDataUserIdItem.js +1 -0
- package/lib/hooks/useDcmtOperations.d.ts +3 -12
- package/lib/hooks/useDcmtOperations.js +187 -179
- package/lib/hooks/useDocumentOperations.d.ts +5 -0
- package/lib/hooks/useDocumentOperations.js +64 -33
- package/lib/hooks/useInputDialog.js +1 -1
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +98 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +758 -0
- package/lib/hooks/usePreventFileDrop.js +9 -0
- package/lib/hooks/useQueryParametersDialog.js +1 -1
- package/lib/hooks/useRelatedDocuments.d.ts +1 -0
- package/lib/hooks/useRelatedDocuments.js +22 -27
- package/lib/hooks/useShowDossiersV5.d.ts +12 -0
- package/lib/hooks/useShowDossiersV5.js +37 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
- package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
- package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
- package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
- package/lib/hooks/useTMDistinctValuesSource.js +105 -0
- package/lib/hooks/useWorkflowApprove.js +33 -17
- package/lib/index.d.ts +2 -0
- package/lib/index.js +5 -2
- package/lib/services/caseflow/CaseFlowService.d.ts +277 -0
- package/lib/services/caseflow/CaseFlowService.js +761 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
- package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
- package/lib/services/caseflow/caseFlowNameCache.js +116 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
- package/lib/services/index.d.ts +4 -0
- package/lib/services/index.js +4 -0
- package/lib/services/platform_services.d.ts +4 -4
- package/package.json +16 -13
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
-
import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes,
|
|
4
|
-
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus, IconChevronDown, IconChevronRight, SDKUI_Localizator, buildDcmtDisplayName, SDKUI_Globals, searchResultToMetadataValues } from '../../../helper';
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataDataDomains, RelationCacheService, RelationTypes, UserListCacheService, LayoutModes } from "@topconsultnpm/sdk-ts";
|
|
4
|
+
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus, IconChevronDown, IconChevronRight, SDKUI_Localizator, buildDcmtDisplayName, SDKUI_Globals, searchResultToMetadataValues, getColumnFormatInfo, formatDateTimeByMetadataFormat, formatNumberByMetadataFormat } from '../../../helper';
|
|
5
5
|
import ShowAlert from '../../base/TMAlert';
|
|
6
6
|
import TMToppyMessage from '../../../helper/TMToppyMessage';
|
|
7
7
|
import { TMColors } from '../../../utils/theme';
|
|
8
8
|
import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
|
|
9
9
|
import TMTreeView from '../../base/TMTreeView';
|
|
10
|
-
import
|
|
10
|
+
import TMWaitPanelManager from '../../base/waitPanel/TMWaitPanelManager';
|
|
11
11
|
import TMDataListItemViewer from '../../viewers/TMDataListItemViewer';
|
|
12
12
|
import TMDcmtIcon from './TMDcmtIcon';
|
|
13
13
|
import TMDataUserIdItemViewer from '../../viewers/TMDataUserIdItemViewer';
|
|
14
14
|
import TMTooltip from '../../base/TMTooltip';
|
|
15
15
|
import TMSpinner from '../../base/TMSpinner';
|
|
16
|
+
import { applyRelationTreeState, collectRelationTreeState, findRelationNodeByPath } from './relationTreeState';
|
|
16
17
|
/**
|
|
17
18
|
* Check if document type has detail relations
|
|
18
19
|
*/
|
|
@@ -55,67 +56,64 @@ export const getDisplayValueByColumn = (col, value) => {
|
|
|
55
56
|
return value;
|
|
56
57
|
if (col.dataType === DataColumnTypes.Text)
|
|
57
58
|
return value;
|
|
58
|
-
|
|
59
|
-
const formatCulture = col
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
case MetadataFormats.ShortDateShortTime: return date.toLocaleString(formatCulture, formatCulture == "it-IT" ? { year: "numeric", month: "2-digit", day: "2-digit", hour: '2-digit', minute: '2-digit' } : { dateStyle: 'short', timeStyle: 'short' }).replace(',', '');
|
|
67
|
-
case MetadataFormats.LongDate: return date.toLocaleString(formatCulture, { weekday: "long", year: "numeric", month: "long", day: "numeric" });
|
|
68
|
-
case MetadataFormats.LongTime: return date.toLocaleString(formatCulture, { timeStyle: 'medium' });
|
|
69
|
-
case MetadataFormats.LongDateLongTime: return date.toLocaleString(formatCulture, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
70
|
-
case MetadataFormats.LongDateShortTime: return date.toLocaleString(formatCulture, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: '2-digit', minute: '2-digit' });
|
|
71
|
-
default: return date.toLocaleString(formatCulture, { dateStyle: 'short' });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (col.dataType === DataColumnTypes.Number) {
|
|
75
|
-
return value.toLocaleString(formatCulture, { useGrouping: format == MetadataFormats.NumberWithThousandsSeparator });
|
|
76
|
-
}
|
|
77
|
-
if (format == MetadataFormats.None)
|
|
78
|
-
return value;
|
|
79
|
-
if (format == MetadataFormats.CurrencyEuro)
|
|
80
|
-
return value.toLocaleString(formatCulture, { style: 'currency', currency: "EUR" });
|
|
81
|
-
if (format == MetadataFormats.CurrencyDollar)
|
|
82
|
-
return value.toLocaleString(formatCulture, { style: 'currency', currency: "USD" });
|
|
83
|
-
if (format == MetadataFormats.CurrencyPound)
|
|
84
|
-
return value.toLocaleString(formatCulture, { style: 'currency', currency: "GBP" });
|
|
85
|
-
if (format == MetadataFormats.CurrencyYen)
|
|
86
|
-
return value.toLocaleString(formatCulture, { style: 'currency', currency: "JPY" });
|
|
59
|
+
// Formato e cultura di visualizzazione definiti sulle proprietà estese della colonna
|
|
60
|
+
const { format, formatCulture } = getColumnFormatInfo(col);
|
|
61
|
+
// Date: il valore arriva come stringa e va prima convertito in Date
|
|
62
|
+
if (col.dataType === DataColumnTypes.DateTime)
|
|
63
|
+
return formatDateTimeByMetadataFormat(new Date(value), format, formatCulture);
|
|
64
|
+
// Numeri (incluse valute): stessa formattazione usata da TMSearchResult
|
|
65
|
+
if (col.dataType === DataColumnTypes.Number)
|
|
66
|
+
return formatNumberByMetadataFormat(value, format, formatCulture);
|
|
87
67
|
return value;
|
|
88
68
|
};
|
|
69
|
+
/**
|
|
70
|
+
* Attende la chiamata, ma smette di aspettarla (restituendo undefined) appena l'utente interrompe.
|
|
71
|
+
* Serve perché il SearchEngine dell'SDK non accetta un AbortSignal: senza, "Interrompi" aspetterebbe la fine della chiamata in corso.
|
|
72
|
+
*/
|
|
73
|
+
const untilAborted = (promise, signal) => {
|
|
74
|
+
if (!signal)
|
|
75
|
+
return promise;
|
|
76
|
+
if (signal.aborted)
|
|
77
|
+
return Promise.resolve(undefined);
|
|
78
|
+
const aborted = new Promise(resolve => signal.addEventListener('abort', () => resolve(undefined), { once: true }));
|
|
79
|
+
// Vince chi termina prima: la risposta del server oppure l'interruzione (la chiamata prosegue in background e viene ignorata)
|
|
80
|
+
return Promise.race([promise, aborted]);
|
|
81
|
+
};
|
|
89
82
|
/**
|
|
90
83
|
* Convert SearchResultDescriptor to structured data source with metadata
|
|
91
|
-
*
|
|
92
|
-
*
|
|
84
|
+
* I valori dei documenti sono già presenti nelle righe restituite dalla ricerca:
|
|
85
|
+
* qui vengono solo abbinati ai rispettivi MetadataDescriptor (perm,
|
|
86
|
+
* isSpecialSearchOutput, ...), risolti una sola volta per tipo documento.
|
|
93
87
|
*/
|
|
94
|
-
export const searchResultToDataSource = async (searchResult, hideSysMetadata) => {
|
|
88
|
+
export const searchResultToDataSource = async (searchResult, hideSysMetadata, signal) => {
|
|
95
89
|
const rows = searchResult?.dtdResult?.rows ?? [];
|
|
96
90
|
const output = [];
|
|
97
91
|
// Find TID and DID column indices
|
|
98
92
|
const columns = searchResult?.dtdResult?.columns ?? [];
|
|
99
93
|
const tidColIndex = columns.findIndex(col => col.caption?.toUpperCase() === 'TID');
|
|
100
94
|
const didColIndex = columns.findIndex(col => col.caption?.toUpperCase() === 'DID');
|
|
95
|
+
const firstRow = rows[0] ?? [];
|
|
96
|
+
const tid = tidColIndex >= 0 && firstRow.length > 0 ? Number(firstRow[tidColIndex]) : searchResult?.fromTID;
|
|
97
|
+
const firstDid = didColIndex >= 0 && firstRow.length > 0 ? Number(firstRow[didColIndex]) : undefined;
|
|
98
|
+
const dtdWithMetadata = await DcmtTypeListCacheService.GetWithNotGrantedAsync(tid, firstDid, searchResult);
|
|
99
|
+
const mdList = dtdWithMetadata?.metadata ?? [];
|
|
100
|
+
// Posizione di ogni MID nelle colonne del risultato: identica per tutte le righe,
|
|
101
|
+
// quindi calcolata una volta sola invece di una find() per metadato per riga.
|
|
102
|
+
const mids = columns.map(col => Number(col.extendedProperties?.["MID"] ?? "0"));
|
|
103
|
+
const columnByMid = new Map();
|
|
104
|
+
columns.forEach((col, i) => { if (!columnByMid.has(mids[i]))
|
|
105
|
+
columnByMid.set(mids[i], col); });
|
|
106
|
+
const dtdResult = searchResult?.dtdResult;
|
|
101
107
|
for (let index = 0; index < rows.length; index++) {
|
|
108
|
+
// Interruzione richiesta dall'utente: restituisce i documenti già elaborati
|
|
109
|
+
if (signal?.aborted)
|
|
110
|
+
break;
|
|
102
111
|
const row = rows[index];
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const did = didColIndex >= 0 ? Number(row[didColIndex]) : undefined;
|
|
106
|
-
// Fetch complete metadata for this specific document (like getFilteredMetadata does)
|
|
107
|
-
const metadata = await SDK_Globals.tmSession?.NewSearchEngine().GetMetadataAsync(tid, did, true);
|
|
108
|
-
if (metadata) {
|
|
109
|
-
// Get full DTD with metadata for this document
|
|
110
|
-
const dtdResult = metadata.dtdResult;
|
|
111
|
-
const metadataRows = dtdResult?.rows?.[0] ?? [];
|
|
112
|
-
const mids = metadata.selectMIDs;
|
|
113
|
-
const dtdWithMetadata = await DcmtTypeListCacheService.GetWithNotGrantedAsync(tid, did, metadata);
|
|
114
|
-
const mdList = dtdWithMetadata?.metadata ?? [];
|
|
112
|
+
const item = { rowIndex: index };
|
|
113
|
+
if (mdList.length > 0) {
|
|
115
114
|
// Convert to MetadataValueDescriptorEx array
|
|
116
|
-
const metadataList = searchResultToMetadataValues(tid, dtdResult,
|
|
115
|
+
const metadataList = searchResultToMetadataValues(tid, dtdResult, row, mids, mdList, LayoutModes.Update);
|
|
117
116
|
// Convert to DcmtMetadataMap format (like getFilteredMetadata does)
|
|
118
|
-
const item = { rowIndex: index };
|
|
119
117
|
for (const mvd of metadataList) {
|
|
120
118
|
if (hideSysMetadata && mvd.mid !== undefined && mvd.mid < 100)
|
|
121
119
|
continue;
|
|
@@ -123,22 +121,19 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata) =>
|
|
|
123
121
|
? (mvd.md?.name ?? '').toUpperCase()
|
|
124
122
|
: (mvd.md?.name ?? '');
|
|
125
123
|
if (key) {
|
|
124
|
+
// Find the corresponding column by MID to format the value
|
|
126
125
|
item[key] = {
|
|
127
126
|
md: mvd.md,
|
|
128
|
-
value: mvd.value
|
|
127
|
+
value: getDisplayValueByColumn(columnByMid.get(mvd.mid ?? 0), mvd.value)
|
|
129
128
|
};
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
|
-
output.push(item);
|
|
133
131
|
}
|
|
134
132
|
else {
|
|
135
|
-
// Fallback:
|
|
136
|
-
const item = { rowIndex: index };
|
|
137
|
-
const fallbackTid = searchResult?.fromTID;
|
|
138
|
-
const dtd = await DcmtTypeListCacheService.GetWithNotGrantedAsync(fallbackTid, undefined, searchResult);
|
|
133
|
+
// Fallback: il DTD non è risolvibile, usa le colonne grezze del risultato
|
|
139
134
|
for (let i = 0; i < row.length; i++) {
|
|
140
|
-
const column =
|
|
141
|
-
const mid =
|
|
135
|
+
const column = columns[i];
|
|
136
|
+
const mid = mids[i];
|
|
142
137
|
if (hideSysMetadata && mid < 100)
|
|
143
138
|
continue;
|
|
144
139
|
let key = column?.caption ?? '';
|
|
@@ -150,20 +145,18 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata) =>
|
|
|
150
145
|
key = `${key}_${mid}`;
|
|
151
146
|
}
|
|
152
147
|
}
|
|
153
|
-
const value = row[i];
|
|
154
|
-
const md = dtd?.metadata?.find(o => o.id == mid);
|
|
155
148
|
item[key] = {
|
|
156
|
-
md:
|
|
157
|
-
value: getDisplayValueByColumn(column,
|
|
149
|
+
md: undefined,
|
|
150
|
+
value: getDisplayValueByColumn(column, row[i])
|
|
158
151
|
};
|
|
159
152
|
}
|
|
160
|
-
output.push(item);
|
|
161
153
|
}
|
|
154
|
+
output.push(item);
|
|
162
155
|
}
|
|
163
156
|
return output;
|
|
164
157
|
};
|
|
165
158
|
export const DEFAULT_RELATION_EXPAND_LEVEL = 4; // Default maximum depth level for expansion (can be adjusted)
|
|
166
|
-
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,
|
|
159
|
+
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, showExpandAllButton = false, defaultExpandAll = false, onLoadingStateChanged, onStopCalculationReady, onCalculationStopped, initialTreeState, onTreeStateChange }) => {
|
|
167
160
|
// State
|
|
168
161
|
const [dcmtTypes, setDcmtTypes] = useState([]);
|
|
169
162
|
const [treeData, setTreeData] = useState([]);
|
|
@@ -172,19 +165,21 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
172
165
|
const getInitialExpandLevel = () => SDKUI_Globals.userSettings?.searchSettings?.relationExpandLevel ?? DEFAULT_RELATION_EXPAND_LEVEL;
|
|
173
166
|
const [expandLevel, setExpandLevel] = useState(getInitialExpandLevel);
|
|
174
167
|
const [isAllCollapsed, setIsAllCollapsed] = useState(false);
|
|
175
|
-
const initialExpandAllAppliedRef =
|
|
176
|
-
|
|
177
|
-
const [
|
|
178
|
-
const
|
|
179
|
-
const [waitPanelMaxValuePrimary, setWaitPanelMaxValuePrimary] = useState(0);
|
|
168
|
+
const initialExpandAllAppliedRef = useRef(false);
|
|
169
|
+
// Calcolo iniziale: il pannello e' imperativo, qui resta solo il flag per il render.
|
|
170
|
+
const [isInitialLoading, setIsInitialLoading] = useState(false);
|
|
171
|
+
const loadPanelRef = useRef(undefined);
|
|
180
172
|
const [abortController] = useState(new AbortController());
|
|
181
|
-
|
|
182
|
-
const [
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const [
|
|
173
|
+
// True quando l'utente ha interrotto il calcolo delle correlazioni (pulsante "Interrompi")
|
|
174
|
+
const [loadStoppedByUser, setLoadStoppedByUser] = useState(false);
|
|
175
|
+
// Espansione di un nodo: il pannello e' imperativo, qui resta solo il flag per il render.
|
|
176
|
+
// qui resta solo il flag che serve al render e alla notifica al padre.
|
|
177
|
+
const [isExpansionLoading, setIsExpansionLoading] = useState(false);
|
|
178
|
+
// AbortController dell'espansione in corso, accessibile da callback stabili
|
|
179
|
+
const expansionAbortControllerRef = useRef(undefined);
|
|
180
|
+
const expansionPanelRef = useRef(undefined);
|
|
186
181
|
// Ref to track last loaded input to prevent unnecessary reloads
|
|
187
|
-
const lastLoadedInputRef =
|
|
182
|
+
const lastLoadedInputRef = useRef('');
|
|
188
183
|
// State to track loaded input key - triggers re-render for focus selection
|
|
189
184
|
const [loadedInputKey, setLoadedInputKey] = React.useState('');
|
|
190
185
|
// State to track how many GetMetadataAsync calls failed
|
|
@@ -192,9 +187,17 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
192
187
|
// State to track total master documents found (used when isForMaster is true)
|
|
193
188
|
const [totalMasterDocuments, setTotalMasterDocuments] = React.useState(0);
|
|
194
189
|
// Ref to track if user has manually expanded/collapsed static items
|
|
195
|
-
const userInteractedWithStaticItemsRef =
|
|
190
|
+
const userInteractedWithStaticItemsRef = useRef(false);
|
|
196
191
|
// Ref to track the last inputKey for which we set the focused item
|
|
197
|
-
const lastFocusedInputRef =
|
|
192
|
+
const lastFocusedInputRef = useRef('');
|
|
193
|
+
// Catturato al mount (il padre lo aggiorna di continuo) e azzerato dopo l'uso,
|
|
194
|
+
// così vale solo per il primo caricamento
|
|
195
|
+
const stateToRestoreRef = useRef(initialTreeState);
|
|
196
|
+
// Parte dallo stato ricevuto: il padre azzera il fuoco prima di rimontare, quindi
|
|
197
|
+
// la prima notifica dopo il rimontaggio lo perderebbe
|
|
198
|
+
const lastReportedStateRef = useRef(initialTreeState);
|
|
199
|
+
// Percorso su cui rimettere il fuoco dopo un ripristino
|
|
200
|
+
const pendingFocusPathRef = useRef(undefined);
|
|
198
201
|
// State for all users (used for checkout status display)
|
|
199
202
|
const [allUsers, setAllUsers] = useState([]);
|
|
200
203
|
// Load all users for checkout status resolution
|
|
@@ -207,9 +210,29 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
207
210
|
}, []);
|
|
208
211
|
// Notify parent when loading state changes
|
|
209
212
|
useEffect(() => {
|
|
210
|
-
const isLoading =
|
|
213
|
+
const isLoading = isInitialLoading || isExpansionLoading;
|
|
211
214
|
onLoadingStateChanged?.(isLoading);
|
|
212
|
-
}, [
|
|
215
|
+
}, [isInitialLoading, isExpansionLoading, onLoadingStateChanged]);
|
|
216
|
+
// I pannelli vivono fuori dall'albero React: se il viewer viene smontato mentre un
|
|
217
|
+
// caricamento e' in corso vanno chiusi a mano, altrimenti resterebbero a schermo.
|
|
218
|
+
useEffect(() => () => {
|
|
219
|
+
loadPanelRef.current?.close();
|
|
220
|
+
expansionPanelRef.current?.close();
|
|
221
|
+
}, []);
|
|
222
|
+
/**
|
|
223
|
+
* Interrompe il calcolo delle correlazioni (iniziale e/o espansione in corso):
|
|
224
|
+
* non vengono eseguite ulteriori chiamate GetMetadata e vengono mantenute
|
|
225
|
+
* solo le correlazioni già calcolate.
|
|
226
|
+
*/
|
|
227
|
+
const stopCalculation = useCallback(() => {
|
|
228
|
+
if (!abortController.signal.aborted)
|
|
229
|
+
abortController.abort();
|
|
230
|
+
expansionAbortControllerRef.current?.abort();
|
|
231
|
+
}, [abortController]);
|
|
232
|
+
// Espone la funzione di interruzione al componente padre
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
onStopCalculationReady?.(stopCalculation);
|
|
235
|
+
}, [onStopCalculationReady, stopCalculation]);
|
|
213
236
|
// Sincronizza expandLevel quando userSettings.searchSettings.relationExpandLevel cambia
|
|
214
237
|
useEffect(() => {
|
|
215
238
|
const globalExpandLevel = SDKUI_Globals.userSettings?.searchSettings?.relationExpandLevel;
|
|
@@ -239,7 +262,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
239
262
|
/**
|
|
240
263
|
* Recursively retrieve detail documents (adapted from TMMasterDetailDcmts.tsx)
|
|
241
264
|
*/
|
|
242
|
-
const getDetailDcmtsAsync = useCallback(async (mTID, mDID, maxLevel) => {
|
|
265
|
+
const getDetailDcmtsAsync = useCallback(async (mTID, mDID, maxLevel, signal) => {
|
|
243
266
|
const items = [];
|
|
244
267
|
if (!mTID)
|
|
245
268
|
return [];
|
|
@@ -257,8 +280,11 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
257
280
|
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
258
281
|
if (!searchEngine)
|
|
259
282
|
return items;
|
|
260
|
-
const results = await searchEngine.GetAllDetailDcmtsAsync(mTID, mDID) ?? [];
|
|
283
|
+
const results = await untilAborted(searchEngine.GetAllDetailDcmtsAsync(mTID, mDID), signal) ?? [];
|
|
261
284
|
for (const searchResult of results) {
|
|
285
|
+
// Interruzione richiesta dall'utente: restituisce le correlazioni già calcolate
|
|
286
|
+
if (signal?.aborted)
|
|
287
|
+
break;
|
|
262
288
|
// Skip if allowedTIDs is set and this TID is not allowed
|
|
263
289
|
if (allowedTIDs && searchResult.fromTID && !allowedTIDs.includes(searchResult.fromTID)) {
|
|
264
290
|
continue;
|
|
@@ -280,7 +306,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
280
306
|
expanded: false,
|
|
281
307
|
hidden: false
|
|
282
308
|
};
|
|
283
|
-
const source = await searchResultToDataSource(searchResult);
|
|
309
|
+
const source = await searchResultToDataSource(searchResult, undefined, signal);
|
|
284
310
|
if (source && source.length > 0) {
|
|
285
311
|
const dcmtDetails = [];
|
|
286
312
|
// Check once if this document type can have detail relations
|
|
@@ -326,7 +352,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
326
352
|
/**
|
|
327
353
|
* Recursively retrieve master documents
|
|
328
354
|
*/
|
|
329
|
-
const getMasterDcmtsAsync = useCallback(async (dTID, dDID, maxLevel) => {
|
|
355
|
+
const getMasterDcmtsAsync = useCallback(async (dTID, dDID, maxLevel, signal) => {
|
|
330
356
|
const items = [];
|
|
331
357
|
if (!dTID)
|
|
332
358
|
return [];
|
|
@@ -344,8 +370,11 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
344
370
|
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
345
371
|
if (!searchEngine)
|
|
346
372
|
return items;
|
|
347
|
-
const results = await searchEngine.GetAllMasterDcmtsAsync(dTID, dDID) ?? [];
|
|
373
|
+
const results = await untilAborted(searchEngine.GetAllMasterDcmtsAsync(dTID, dDID), signal) ?? [];
|
|
348
374
|
for (const searchResult of results) {
|
|
375
|
+
// Interruzione richiesta dall'utente: restituisce le correlazioni già calcolate
|
|
376
|
+
if (signal?.aborted)
|
|
377
|
+
break;
|
|
349
378
|
// Skip if allowedTIDs is set and this TID is not allowed
|
|
350
379
|
if (allowedTIDs && searchResult.fromTID && !allowedTIDs.includes(searchResult.fromTID)) {
|
|
351
380
|
continue;
|
|
@@ -367,7 +396,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
367
396
|
expanded: false,
|
|
368
397
|
hidden: false
|
|
369
398
|
};
|
|
370
|
-
const source = await searchResultToDataSource(searchResult);
|
|
399
|
+
const source = await searchResultToDataSource(searchResult, undefined, signal);
|
|
371
400
|
if (source && source.length > 0) {
|
|
372
401
|
const dcmtMasters = [];
|
|
373
402
|
// Check once if this document type can have master relations (for inverted mode)
|
|
@@ -465,6 +494,26 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
465
494
|
return { ...node, hidden: shouldHide, items: updatedItems };
|
|
466
495
|
});
|
|
467
496
|
}, [showZeroDcmts]);
|
|
497
|
+
/** Messaggio informativo in testa se tutti i figli sono nascosti dal filtro; senza figli il nodo resta vuoto (nessuna freccina). */
|
|
498
|
+
const applyZeroFilterInfoMessage = useCallback((nodeKey, items) => {
|
|
499
|
+
const cleaned = items.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
500
|
+
if (showZeroDcmts)
|
|
501
|
+
return cleaned;
|
|
502
|
+
const realChildren = cleaned.filter(child => !child.isInfoMessage);
|
|
503
|
+
if (realChildren.length === 0 || !realChildren.every(child => child.hidden))
|
|
504
|
+
return cleaned;
|
|
505
|
+
return [
|
|
506
|
+
{
|
|
507
|
+
key: `__info__${nodeKey}`,
|
|
508
|
+
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
509
|
+
isContainer: false,
|
|
510
|
+
isDcmt: false,
|
|
511
|
+
isInfoMessage: true,
|
|
512
|
+
isExpandible: false
|
|
513
|
+
},
|
|
514
|
+
...cleaned // Keep hidden containers so they can be shown when toggling
|
|
515
|
+
];
|
|
516
|
+
}, [showZeroDcmts]);
|
|
468
517
|
/**
|
|
469
518
|
* Helper function to set up initial tree expansion state.
|
|
470
519
|
* Called after all data is loaded but before setTreeData.
|
|
@@ -552,10 +601,22 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
552
601
|
});
|
|
553
602
|
return result;
|
|
554
603
|
};
|
|
604
|
+
/**
|
|
605
|
+
* Carica i figli di un documento durante il ripristino. Usa i loader e non
|
|
606
|
+
* calculateItemsForNode, che accenderebbe il wait panel di espansione mentre
|
|
607
|
+
* è già attivo quello del caricamento principale.
|
|
608
|
+
*/
|
|
609
|
+
const loadChildrenForRestore = async (node) => {
|
|
610
|
+
if (!node.tid || !node.did)
|
|
611
|
+
return [];
|
|
612
|
+
if (isForMaster && invertMasterNavigation)
|
|
613
|
+
return await getMasterDcmtsAsync(node.tid, node.did, 1, abortController.signal);
|
|
614
|
+
return await getDetailDcmtsAsync(node.tid, node.did, 1, abortController.signal);
|
|
615
|
+
};
|
|
555
616
|
/**
|
|
556
617
|
* Main data loading function
|
|
557
618
|
*/
|
|
558
|
-
const loadData = useCallback(async () => {
|
|
619
|
+
const loadData = useCallback(async (panel) => {
|
|
559
620
|
if (!inputDcmts || inputDcmts.length === 0 || dcmtTypes.length === 0) {
|
|
560
621
|
setTreeData([]);
|
|
561
622
|
return;
|
|
@@ -570,14 +631,14 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
570
631
|
break;
|
|
571
632
|
}
|
|
572
633
|
// Update wait panel progress
|
|
573
|
-
|
|
634
|
+
panel.setSubtitle(`Caricamento correlazione ${processedCount + 1} di ${inputDcmts.length}`);
|
|
574
635
|
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
575
636
|
if (!searchEngine)
|
|
576
637
|
continue;
|
|
577
638
|
// Load document metadata
|
|
578
639
|
let result;
|
|
579
640
|
try {
|
|
580
|
-
result = await searchEngine.GetMetadataAsync(dcmt.TID, dcmt.DID, false);
|
|
641
|
+
result = await untilAborted(searchEngine.GetMetadataAsync(dcmt.TID, dcmt.DID, false), abortController.signal);
|
|
581
642
|
}
|
|
582
643
|
catch (metadataError) {
|
|
583
644
|
console.warn('[TMRelationViewer] GetMetadataAsync failed for TID:', dcmt.TID, 'DID:', dcmt.DID, metadataError);
|
|
@@ -590,12 +651,15 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
590
651
|
});
|
|
591
652
|
// Skip this document and continue with the next one
|
|
592
653
|
processedCount++;
|
|
593
|
-
|
|
654
|
+
await panel.step();
|
|
594
655
|
continue;
|
|
595
656
|
}
|
|
657
|
+
// Interrotto durante la chiamata: esce con le correlazioni gia' calcolate
|
|
658
|
+
if (abortController.signal.aborted)
|
|
659
|
+
break;
|
|
596
660
|
const dtd = await DcmtTypeListCacheService.GetAsync(dcmt.TID);
|
|
597
661
|
// Parse document values
|
|
598
|
-
const docValues = await searchResultToDataSource(result);
|
|
662
|
+
const docValues = await searchResultToDataSource(result, undefined, abortController.signal);
|
|
599
663
|
const docRow = docValues.length > 0 ? docValues[0] : undefined;
|
|
600
664
|
// Generate unique GUID for this container
|
|
601
665
|
const containerGUID = genUniqueId();
|
|
@@ -604,22 +668,32 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
604
668
|
// ========================================================================
|
|
605
669
|
if (isForMaster && !invertMasterNavigation) {
|
|
606
670
|
// Carica i master documents
|
|
607
|
-
const masterDocs = await getMasterDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel);
|
|
671
|
+
const masterDocs = await getMasterDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel, abortController.signal);
|
|
608
672
|
// Per ogni master, carica i suoi detail (incluso il documento corrente)
|
|
609
673
|
for (const masterContainer of masterDocs) {
|
|
674
|
+
if (abortController.signal.aborted)
|
|
675
|
+
break;
|
|
610
676
|
if (!masterContainer.items)
|
|
611
677
|
continue;
|
|
612
678
|
// Per ogni master document nel container, carica i detail
|
|
613
679
|
for (const masterDoc of masterContainer.items) {
|
|
680
|
+
// Interruzione richiesta dall'utente: mantiene i dati già calcolati
|
|
681
|
+
if (abortController.signal.aborted)
|
|
682
|
+
break;
|
|
614
683
|
if (masterDoc.tid && masterDoc.did) {
|
|
615
684
|
// Carica i detail del master (incluso il documento di partenza)
|
|
616
|
-
const detailDocs = await getDetailDcmtsAsync(masterDoc.tid, masterDoc.did, 1);
|
|
685
|
+
const detailDocs = await getDetailDcmtsAsync(masterDoc.tid, masterDoc.did, 1, abortController.signal);
|
|
617
686
|
masterDoc.items = detailDocs;
|
|
618
687
|
masterDoc.itemsCount = detailDocs.length;
|
|
619
688
|
masterDoc.isLoaded = true;
|
|
620
689
|
}
|
|
621
690
|
}
|
|
622
691
|
}
|
|
692
|
+
// Interruzione richiesta dall'utente: il documento corrente non è stato
|
|
693
|
+
// caricato per intero, quindi non viene aggiunto all'albero. Il parziale
|
|
694
|
+
// resta così a granularità di documento (es. 3 completi su 10).
|
|
695
|
+
if (abortController.signal.aborted)
|
|
696
|
+
break;
|
|
623
697
|
// Aggiungi i master come root, raggruppando per TID e relationId
|
|
624
698
|
for (const masterContainer of masterDocs) {
|
|
625
699
|
const existingContainer = tree.find(c => c.tid === masterContainer.tid && c.relationId === masterContainer.relationId && c.isContainer);
|
|
@@ -646,8 +720,11 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
646
720
|
else {
|
|
647
721
|
// Load related documents (detail or master based on isForMaster flag)
|
|
648
722
|
const relatedDocs = isForMaster
|
|
649
|
-
? await getMasterDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel)
|
|
650
|
-
: await getDetailDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel);
|
|
723
|
+
? await getMasterDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel, abortController.signal)
|
|
724
|
+
: await getDetailDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel, abortController.signal);
|
|
725
|
+
// Interruzione richiesta dall'utente
|
|
726
|
+
if (abortController.signal.aborted)
|
|
727
|
+
break;
|
|
651
728
|
// Se showMainDocument è false, mostra i container dei dettagli direttamente sotto il container principale
|
|
652
729
|
if (!showMainDocument) {
|
|
653
730
|
// Check if a container for this TID already exists in the tree
|
|
@@ -755,7 +832,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
755
832
|
}
|
|
756
833
|
// Update progress counter
|
|
757
834
|
processedCount++;
|
|
758
|
-
|
|
835
|
+
// step() attende anche la conferma di interruzione: col dialogo aperto il ciclo resta fermo
|
|
836
|
+
await panel.step();
|
|
759
837
|
}
|
|
760
838
|
/**
|
|
761
839
|
* Helper function to check if a node has any actual correlated documents
|
|
@@ -794,8 +872,15 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
794
872
|
// Check recursively if there are any actual documents
|
|
795
873
|
return hasNoActualDocuments(container.items);
|
|
796
874
|
});
|
|
875
|
+
// Calcolo interrotto dall'utente: mostra solo ciò che è stato calcolato,
|
|
876
|
+
// senza notificare l'assenza di correlazioni (il risultato è parziale, non definitivo)
|
|
877
|
+
const wasStopped = abortController.signal.aborted;
|
|
878
|
+
if (wasStopped) {
|
|
879
|
+
setLoadStoppedByUser(true);
|
|
880
|
+
onCalculationStopped?.(processedCount, inputDcmts.length);
|
|
881
|
+
}
|
|
797
882
|
// If no relations found, notify parent
|
|
798
|
-
if (hasNoRelations && onNoRelationsFound) {
|
|
883
|
+
if (!wasStopped && hasNoRelations && onNoRelationsFound) {
|
|
799
884
|
onNoRelationsFound();
|
|
800
885
|
}
|
|
801
886
|
// Update metadata error count state
|
|
@@ -818,12 +903,20 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
818
903
|
}
|
|
819
904
|
// FIRST setup initial expansion state (root container expanded, first document expanded with focus, first correlation folder expanded)
|
|
820
905
|
// This must run BEFORE updateHiddenProperty so that infoMessage logic sees expanded=true
|
|
821
|
-
|
|
906
|
+
let expandedTree = setupInitialTreeExpansion(tree);
|
|
907
|
+
// Stato precedente da ripristinare (rimontaggio dopo un'operazione): si riapplica
|
|
908
|
+
// sopra l'espansione iniziale, che resta necessaria perché marca isRoot.
|
|
909
|
+
const stateToRestore = stateToRestoreRef.current;
|
|
910
|
+
stateToRestoreRef.current = undefined;
|
|
911
|
+
if (stateToRestore && stateToRestore.inputKey === getInputKey()) {
|
|
912
|
+
expandedTree = await applyRelationTreeState(expandedTree, stateToRestore, loadChildrenForRestore, () => abortController.signal.aborted);
|
|
913
|
+
pendingFocusPathRef.current = stateToRestore.focusedPath;
|
|
914
|
+
}
|
|
822
915
|
// THEN apply hidden property transformations (creates new objects, adds infoMessage where needed)
|
|
823
916
|
// Now it will correctly detect expanded nodes and add infoMessage if all children are hidden
|
|
824
917
|
const processedTree = updateHiddenProperty(expandedTree);
|
|
825
918
|
setTreeData(processedTree);
|
|
826
|
-
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty, showMainDocument, labelMainContainer, onNoRelationsFound]);
|
|
919
|
+
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty, showMainDocument, labelMainContainer, onNoRelationsFound, onCalculationStopped, getInputKey]);
|
|
827
920
|
/**
|
|
828
921
|
* Merge main tree data with additional static items
|
|
829
922
|
*/
|
|
@@ -838,6 +931,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
838
931
|
// and may contain custom properties that would be lost by the spread operator
|
|
839
932
|
return [...treeData, ...itemsToMerge];
|
|
840
933
|
}, [treeData, additionalStaticItems, staticItemsState]);
|
|
934
|
+
/** Notifica al padre lo stato dell'albero, così che possa restituirlo dopo un refresh. */
|
|
935
|
+
useEffect(() => {
|
|
936
|
+
if (!onTreeStateChange)
|
|
937
|
+
return;
|
|
938
|
+
// Ad albero vuoto (caricamento in corso) si sovrascriverebbe lo stato da ripristinare
|
|
939
|
+
if (treeData.length === 0)
|
|
940
|
+
return;
|
|
941
|
+
// Il padre azzera il fuoco all'avvio di un refresh: si conserva l'ultimo noto
|
|
942
|
+
const state = collectRelationTreeState(treeData, getInputKey(), focusedItem?.key, lastReportedStateRef.current?.focusedPath);
|
|
943
|
+
lastReportedStateRef.current = state;
|
|
944
|
+
onTreeStateChange(state);
|
|
945
|
+
}, [treeData, focusedItem, getInputKey, onTreeStateChange]);
|
|
841
946
|
/**
|
|
842
947
|
* Notify parent with a flattened list of all items whenever the merged tree data changes.
|
|
843
948
|
*/
|
|
@@ -878,20 +983,28 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
878
983
|
lastLoadedInputRef.current = currentKey;
|
|
879
984
|
// Reset interaction flag when loading new data
|
|
880
985
|
userInteractedWithStaticItemsRef.current = false;
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
986
|
+
setIsInitialLoading(true);
|
|
987
|
+
const panel = TMWaitPanelManager.show({
|
|
988
|
+
title: isForMaster ? SDKUI_Localizator.LoadingMasterDocuments : SDKUI_Localizator.LoadingDetailDocuments,
|
|
989
|
+
max: inputDcmts.length,
|
|
990
|
+
showCounters: false,
|
|
991
|
+
cancelable: true,
|
|
992
|
+
onAbort: () => abortController.abort(),
|
|
993
|
+
});
|
|
994
|
+
loadPanelRef.current = panel;
|
|
884
995
|
// Call loadData and use .then() instead of await to allow React to render
|
|
885
|
-
loadData().then(() => {
|
|
996
|
+
loadData(panel).then(() => {
|
|
886
997
|
// Mark as loaded AFTER data is ready - state update triggers re-render for focus selection
|
|
887
998
|
lastLoadedInputRef.current = currentKey;
|
|
888
999
|
setLoadedInputKey(currentKey);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1000
|
+
}).finally(() => {
|
|
1001
|
+
// Il pannello resta aperto per tutto loadData, ripristini inclusi
|
|
1002
|
+
panel.close();
|
|
1003
|
+
if (loadPanelRef.current === panel)
|
|
1004
|
+
loadPanelRef.current = undefined;
|
|
1005
|
+
setIsInitialLoading(false);
|
|
893
1006
|
});
|
|
894
|
-
}, [inputDcmts, dcmtTypes, maxDepthLevel, getInputKey, loadData, treeData.length]);
|
|
1007
|
+
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, abortController, getInputKey, loadData, treeData.length]);
|
|
895
1008
|
/**
|
|
896
1009
|
* Update tree when showZeroDcmts changes
|
|
897
1010
|
* Updates the visualization without re-fetching data (isLoaded stays true)
|
|
@@ -917,6 +1030,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
917
1030
|
if (lastFocusedInputRef.current === currentInputKey) {
|
|
918
1031
|
return;
|
|
919
1032
|
}
|
|
1033
|
+
// Ripristino dopo un refresh. Se il nodo non esiste più (es. documento cancellato
|
|
1034
|
+
// fisicamente) si prosegue con il fuoco predefinito sul root.
|
|
1035
|
+
const focusPathToRestore = pendingFocusPathRef.current;
|
|
1036
|
+
pendingFocusPathRef.current = undefined;
|
|
1037
|
+
if (focusPathToRestore) {
|
|
1038
|
+
const nodeToFocus = findRelationNodeByPath(treeData, focusPathToRestore);
|
|
1039
|
+
if (nodeToFocus) {
|
|
1040
|
+
onFocusedItemChanged(nodeToFocus);
|
|
1041
|
+
lastFocusedInputRef.current = currentInputKey;
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
920
1045
|
// Helper function to recursively find the first document with isRoot=true
|
|
921
1046
|
const findFirstRootDocument = (items) => {
|
|
922
1047
|
for (const item of items) {
|
|
@@ -994,29 +1119,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
994
1119
|
if (node.isLoaded && node.items) {
|
|
995
1120
|
// Apply current show/hide logic based on showZeroDcmts
|
|
996
1121
|
const updatedItems = updateHiddenProperty(node.items);
|
|
997
|
-
|
|
998
|
-
if (node.isContainer) {
|
|
999
|
-
// Remove any existing info messages
|
|
1000
|
-
const filteredItems = updatedItems.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
1001
|
-
// Check if all real children are hidden
|
|
1002
|
-
const realChildren = filteredItems.filter(child => !child.isInfoMessage);
|
|
1003
|
-
const allChildrenHidden = realChildren.length > 0 && realChildren.every(child => child.hidden);
|
|
1004
|
-
// If showZeroDcmts is false and all items are hidden, show info message + keep hidden containers
|
|
1005
|
-
if (!showZeroDcmts && allChildrenHidden) {
|
|
1006
|
-
return [
|
|
1007
|
-
{
|
|
1008
|
-
key: `__info__${node.key}`,
|
|
1009
|
-
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
1010
|
-
isContainer: false,
|
|
1011
|
-
isDcmt: false,
|
|
1012
|
-
isInfoMessage: true,
|
|
1013
|
-
isExpandible: false
|
|
1014
|
-
},
|
|
1015
|
-
...filteredItems // Keep hidden containers so they can be shown when toggling
|
|
1016
|
-
];
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
return updatedItems;
|
|
1122
|
+
return applyZeroFilterInfoMessage(node.key, updatedItems);
|
|
1020
1123
|
}
|
|
1021
1124
|
// ============================================
|
|
1022
1125
|
// CONTAINER: Show items immediately (no API calls)
|
|
@@ -1030,21 +1133,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1030
1133
|
if (!node.items)
|
|
1031
1134
|
return [];
|
|
1032
1135
|
const updatedItems = updateHiddenProperty(node.items);
|
|
1033
|
-
|
|
1034
|
-
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
1035
|
-
return [
|
|
1036
|
-
{
|
|
1037
|
-
key: `__info__${node.key}`,
|
|
1038
|
-
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
1039
|
-
isContainer: false,
|
|
1040
|
-
isDcmt: false,
|
|
1041
|
-
isInfoMessage: true,
|
|
1042
|
-
isExpandible: false
|
|
1043
|
-
},
|
|
1044
|
-
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
1045
|
-
];
|
|
1046
|
-
}
|
|
1047
|
-
return updatedItems;
|
|
1136
|
+
return applyZeroFilterInfoMessage(node.key, updatedItems);
|
|
1048
1137
|
}
|
|
1049
1138
|
// ============================================
|
|
1050
1139
|
// DOCUMENT: Load its children (relation containers) lazily
|
|
@@ -1052,11 +1141,19 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1052
1141
|
// ============================================
|
|
1053
1142
|
if (node.isDcmt && node.tid && node.did) {
|
|
1054
1143
|
const newAbortController = new AbortController();
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1144
|
+
expansionAbortControllerRef.current = newAbortController;
|
|
1145
|
+
setIsExpansionLoading(true);
|
|
1146
|
+
// Un solo nodo: niente contatori ne' stima dei tempi, solo l'attesa annullabile.
|
|
1147
|
+
const panel = TMWaitPanelManager.show({
|
|
1148
|
+
title: isForMaster ? SDKUI_Localizator.LoadingMasterDocuments : SDKUI_Localizator.LoadingDetailDocuments,
|
|
1149
|
+
subtitle: 'Caricamento documenti correlati...',
|
|
1150
|
+
max: 1,
|
|
1151
|
+
showCounters: false,
|
|
1152
|
+
showEta: false,
|
|
1153
|
+
cancelable: true,
|
|
1154
|
+
onAbort: () => newAbortController.abort(),
|
|
1155
|
+
});
|
|
1156
|
+
expansionPanelRef.current = panel;
|
|
1060
1157
|
try {
|
|
1061
1158
|
// Check for abort
|
|
1062
1159
|
if (newAbortController.signal.aborted) {
|
|
@@ -1066,15 +1163,15 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1066
1163
|
let loadedItems = [];
|
|
1067
1164
|
if (isForMaster && !invertMasterNavigation) {
|
|
1068
1165
|
// Original mode: detail documents load detail documents (natural detail→detail navigation)
|
|
1069
|
-
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
1166
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1, newAbortController.signal);
|
|
1070
1167
|
}
|
|
1071
1168
|
else if (isForMaster) {
|
|
1072
1169
|
// Inverted master mode: load master documents
|
|
1073
|
-
loadedItems = await getMasterDcmtsAsync(node.tid, node.did, 1);
|
|
1170
|
+
loadedItems = await getMasterDcmtsAsync(node.tid, node.did, 1, newAbortController.signal);
|
|
1074
1171
|
}
|
|
1075
1172
|
else {
|
|
1076
1173
|
// Standard mode: load detail documents
|
|
1077
|
-
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
1174
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1, newAbortController.signal);
|
|
1078
1175
|
}
|
|
1079
1176
|
// Espandi automaticamente il primo container se ci sono documenti correlati
|
|
1080
1177
|
if (loadedItems.length > 0 && loadedItems[0].isContainer) {
|
|
@@ -1085,34 +1182,22 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1085
1182
|
// - Relation type containers are always visible (even if empty)
|
|
1086
1183
|
// - Other containers with zero items can be hidden based on showZeroDcmts
|
|
1087
1184
|
const updatedItems = updateHiddenProperty(loadedItems);
|
|
1088
|
-
|
|
1089
|
-
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
1090
|
-
return [
|
|
1091
|
-
{
|
|
1092
|
-
key: `__info__${node.key}`,
|
|
1093
|
-
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
1094
|
-
isContainer: false,
|
|
1095
|
-
isDcmt: false,
|
|
1096
|
-
isInfoMessage: true,
|
|
1097
|
-
isExpandible: false
|
|
1098
|
-
},
|
|
1099
|
-
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
1100
|
-
];
|
|
1101
|
-
}
|
|
1102
|
-
return updatedItems;
|
|
1185
|
+
return applyZeroFilterInfoMessage(node.key, updatedItems);
|
|
1103
1186
|
}
|
|
1104
1187
|
catch (error) {
|
|
1105
1188
|
console.error('Error loading document relations:', error);
|
|
1106
1189
|
return [];
|
|
1107
1190
|
}
|
|
1108
1191
|
finally {
|
|
1109
|
-
|
|
1110
|
-
|
|
1192
|
+
panel.close();
|
|
1193
|
+
setIsExpansionLoading(false);
|
|
1194
|
+
expansionAbortControllerRef.current = undefined;
|
|
1195
|
+
expansionPanelRef.current = undefined;
|
|
1111
1196
|
}
|
|
1112
1197
|
}
|
|
1113
1198
|
// Default: return existing items
|
|
1114
1199
|
return node.items;
|
|
1115
|
-
}, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, showZeroDcmts]);
|
|
1200
|
+
}, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, applyZeroFilterInfoMessage, showZeroDcmts]);
|
|
1116
1201
|
/**
|
|
1117
1202
|
* Default item renderer with metadata display (adapted from TMMasterDetailDcmts.tsx)
|
|
1118
1203
|
*/
|
|
@@ -1145,8 +1230,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1145
1230
|
gap: '10px',
|
|
1146
1231
|
height: '32px',
|
|
1147
1232
|
padding: '6px 0',
|
|
1148
|
-
opacity: item.isZero ? 0.
|
|
1149
|
-
transition: 'opacity 0.2s ease-in-out'
|
|
1233
|
+
opacity: item.isZero ? 0.4 : 1,
|
|
1150
1234
|
};
|
|
1151
1235
|
// Se è il container principale (root) e showMainDocument è false,
|
|
1152
1236
|
// non mostrare il badge perché conterrebbe il numero di sotto-container, non di documenti
|
|
@@ -1156,7 +1240,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1156
1240
|
textOverflow: 'ellipsis',
|
|
1157
1241
|
fontSize: '1rem',
|
|
1158
1242
|
color: 'inherit'
|
|
1159
|
-
}, children: item.name }), shouldShowBadge && (_jsx(StyledBadge, {
|
|
1243
|
+
}, children: item.name }), shouldShowBadge && (_jsx(StyledBadge, { $backgroundColor: TMColors.info, children: item.items?.length ?? 0 }))] })] }));
|
|
1160
1244
|
// Se è il container principale (root) e abbiamo un custom renderer, usalo
|
|
1161
1245
|
const content = (item.isRoot && customMainContainerContent)
|
|
1162
1246
|
? customMainContainerContent(item, defaultContent)
|
|
@@ -1176,8 +1260,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1176
1260
|
alignItems: 'center',
|
|
1177
1261
|
cursor: 'pointer',
|
|
1178
1262
|
userSelect: 'none',
|
|
1179
|
-
opacity: item.isZero ? 0.
|
|
1180
|
-
transition: 'opacity 0.2s ease-in-out',
|
|
1263
|
+
opacity: item.isZero ? 0.4 : 1,
|
|
1181
1264
|
textDecoration: isLogicallyDeleted ? 'line-through' : 'none'
|
|
1182
1265
|
};
|
|
1183
1266
|
const documentStyle = customDocumentStyle ? { ...defaultDocumentStyle, ...customDocumentStyle(item) } : defaultDocumentStyle;
|
|
@@ -1237,7 +1320,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1237
1320
|
checkoutStatusIcon = checkoutStatus.icon;
|
|
1238
1321
|
}
|
|
1239
1322
|
}
|
|
1240
|
-
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'
|
|
1323
|
+
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] }));
|
|
1241
1324
|
}, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument, allUsers]);
|
|
1242
1325
|
/**
|
|
1243
1326
|
* Wrapper renderer that handles custom rendering if provided
|
|
@@ -1351,15 +1434,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1351
1434
|
}
|
|
1352
1435
|
const targetLevel = expandLevel;
|
|
1353
1436
|
const newAbortController = new AbortController();
|
|
1354
|
-
|
|
1437
|
+
expansionAbortControllerRef.current = newAbortController;
|
|
1438
|
+
// Aperto solo se servono chiamate API (vedi hasApiCalls): con i figli già in
|
|
1439
|
+
// memoria l'espansione è immediata e il pannello lampeggerebbe soltanto.
|
|
1440
|
+
let panel;
|
|
1355
1441
|
// CONTATORI DINAMICI:
|
|
1356
1442
|
// - totalNodes: cresce quando scopriamo nuovi nodi (evita che percentuale > 100%)
|
|
1357
1443
|
// - processedNodes: quanti nodi abbiamo già processato
|
|
1358
|
-
// -
|
|
1444
|
+
// - shownProgress: percentuale mostrata, che non torna mai indietro
|
|
1359
1445
|
let totalNodes = 0;
|
|
1360
1446
|
let processedNodes = 0;
|
|
1361
1447
|
let hasApiCalls = false;
|
|
1362
|
-
let
|
|
1448
|
+
let shownProgress = 0;
|
|
1363
1449
|
/**
|
|
1364
1450
|
* Conta i nodi espandibili in un array (solo primo livello, non ricorsivo).
|
|
1365
1451
|
* Usato per aggiornare totalNodes man mano che scopriamo nuovi figli.
|
|
@@ -1375,6 +1461,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1375
1461
|
};
|
|
1376
1462
|
// Conta iniziale: solo i nodi al livello root
|
|
1377
1463
|
totalNodes = countNodesAtLevel([...workingTreeData, ...workingStaticData]);
|
|
1464
|
+
const initialTotalNodes = totalNodes;
|
|
1378
1465
|
/**
|
|
1379
1466
|
* FUNZIONE RICORSIVA DI ESPANSIONE
|
|
1380
1467
|
*
|
|
@@ -1428,24 +1515,33 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1428
1515
|
if (!hasApiCalls) {
|
|
1429
1516
|
hasApiCalls = true;
|
|
1430
1517
|
TMSpinner.hide();
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1518
|
+
setIsExpansionLoading(true);
|
|
1519
|
+
// Avanzamento in centesimi: il totale cambia mentre si scoprono i figli, niente ETA.
|
|
1520
|
+
panel = TMWaitPanelManager.show({
|
|
1521
|
+
title: isForMaster ? SDKUI_Localizator.LoadingMasterDocuments : SDKUI_Localizator.LoadingDetailDocuments,
|
|
1522
|
+
max: 100,
|
|
1523
|
+
showCounters: false,
|
|
1524
|
+
showEta: false,
|
|
1525
|
+
cancelable: true,
|
|
1526
|
+
onAbort: () => newAbortController.abort(),
|
|
1527
|
+
});
|
|
1528
|
+
expansionPanelRef.current = panel;
|
|
1434
1529
|
}
|
|
1435
1530
|
try {
|
|
1436
1531
|
let loadedItems = [];
|
|
1437
1532
|
// Carica i documenti correlati (detail o master in base alla modalità)
|
|
1438
1533
|
if (isForMaster && !invertMasterNavigation) {
|
|
1439
|
-
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
1534
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1, newAbortController.signal);
|
|
1440
1535
|
}
|
|
1441
1536
|
else if (isForMaster) {
|
|
1442
|
-
loadedItems = await getMasterDcmtsAsync(node.tid, node.did, 1);
|
|
1537
|
+
loadedItems = await getMasterDcmtsAsync(node.tid, node.did, 1, newAbortController.signal);
|
|
1443
1538
|
}
|
|
1444
1539
|
else {
|
|
1445
|
-
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
1540
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1, newAbortController.signal);
|
|
1446
1541
|
}
|
|
1447
1542
|
// Applica le regole di visibilità (showZeroDcmts)
|
|
1448
|
-
|
|
1543
|
+
// Stesse regole dell'espansione manuale (messaggio informativo se tutto nascosto)
|
|
1544
|
+
expandedNode.items = applyZeroFilterInfoMessage(node.key, updateHiddenProperty(loadedItems));
|
|
1449
1545
|
expandedNode.isLoaded = true;
|
|
1450
1546
|
expandedNode.itemsCount = loadedItems.length;
|
|
1451
1547
|
// IMPORTANTE: Aggiungi i nuovi nodi scoperti al contatore totale
|
|
@@ -1472,14 +1568,15 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1472
1568
|
}
|
|
1473
1569
|
// Aggiorna il progresso DOPO aver processato questo nodo
|
|
1474
1570
|
processedNodes++;
|
|
1475
|
-
//
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1571
|
+
// La barra non torna mai indietro: segue il valore reale quando sale, altrimenti avanza verso il 99%
|
|
1572
|
+
const realProgress = totalNodes > 0 ? (processedNodes / totalNodes) * 100 : 0;
|
|
1573
|
+
shownProgress = Math.min(99, realProgress > shownProgress
|
|
1574
|
+
? realProgress
|
|
1575
|
+
: shownProgress + (99 - shownProgress) * 0.01);
|
|
1576
|
+
panel?.setValue(shownProgress);
|
|
1577
|
+
// Seconda riga: spiega perché percentuale e conteggio non vanno di pari passo
|
|
1578
|
+
panel?.setSubtitle(SDKUI_Localizator.ExpansionProgress.replaceParams(processedNodes.toString(), totalNodes.toString())
|
|
1579
|
+
+ (totalNodes > initialTotalNodes ? `\n${SDKUI_Localizator.ExpansionProgressIsEstimated}` : ''));
|
|
1483
1580
|
// ═══════════════════════════════════════════════════════════════════
|
|
1484
1581
|
// PASSO 3: CHIAMATA RICORSIVA SUI FIGLI
|
|
1485
1582
|
// Questo è il punto chiave: i nuovi nodi appena caricati vengono
|
|
@@ -1507,10 +1604,12 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1507
1604
|
finally {
|
|
1508
1605
|
// Cleanup: nascondi spinner e pannello di attesa
|
|
1509
1606
|
TMSpinner.hide();
|
|
1510
|
-
|
|
1511
|
-
|
|
1607
|
+
panel?.close();
|
|
1608
|
+
setIsExpansionLoading(false);
|
|
1609
|
+
expansionAbortControllerRef.current = undefined;
|
|
1610
|
+
expansionPanelRef.current = undefined;
|
|
1512
1611
|
}
|
|
1513
|
-
}, [expandLevel, treeData, staticItemsState, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty]);
|
|
1612
|
+
}, [expandLevel, treeData, staticItemsState, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, applyZeroFilterInfoMessage]);
|
|
1514
1613
|
/**
|
|
1515
1614
|
* Apply defaultExpandAll once on initial tree load.
|
|
1516
1615
|
*/
|
|
@@ -1539,10 +1638,16 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1539
1638
|
// Reset collapsed state when user manually expands a node
|
|
1540
1639
|
setIsAllCollapsed(false);
|
|
1541
1640
|
}, []);
|
|
1542
|
-
|
|
1543
|
-
|
|
1641
|
+
// Il pannello di attesa è montato dal manager fuori dall'albero React: qui resta
|
|
1642
|
+
// solo il posto dell'albero, che compare a calcolo finito.
|
|
1643
|
+
if (isInitialLoading) {
|
|
1644
|
+
return _jsx("div", { style: { width: '100%', height: '100%' } });
|
|
1544
1645
|
}
|
|
1545
1646
|
if (mergedTreeData.length === 0) {
|
|
1647
|
+
// Calcolo interrotto dall'utente prima di aver calcolato qualsiasi correlazione
|
|
1648
|
+
if (loadStoppedByUser) {
|
|
1649
|
+
return _jsx(TMToppyMessage, { message: SDKUI_Localizator.RelationsCalculationStopped });
|
|
1650
|
+
}
|
|
1546
1651
|
// Se NON isForMaster e tutti i GetMetadataAsync sono falliti
|
|
1547
1652
|
if (!isForMaster && metadataErrorCount === inputDcmts.length) {
|
|
1548
1653
|
return _jsx(TMToppyMessage, { message: SDKUI_Localizator.DocumentsNotAvailableOrNoCorrelations });
|
|
@@ -1553,7 +1658,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1553
1658
|
return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: SDKUI_Localizator.NoRelationsAvailable });
|
|
1554
1659
|
}
|
|
1555
1660
|
// Se isForMaster e non ci sono master documents disponibili
|
|
1556
|
-
|
|
1661
|
+
// (se il calcolo è stato interrotto l'albero parziale viene comunque mostrato)
|
|
1662
|
+
if (isForMaster && totalMasterDocuments === 0 && !loadStoppedByUser) {
|
|
1557
1663
|
return _jsx(TMToppyMessage, { message: SDKUI_Localizator.NoMasterDocumentsAvailable });
|
|
1558
1664
|
}
|
|
1559
1665
|
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
|
|
@@ -1592,15 +1698,11 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1592
1698
|
whiteSpace: 'nowrap',
|
|
1593
1699
|
outline: 'none',
|
|
1594
1700
|
transition: 'background 0.2s'
|
|
1595
|
-
}, 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:
|
|
1701
|
+
}, 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: isExpansionLoading ? undefined : onItemContextMenu, enableVirtualization: true, shouldDelayFocusOnEvent: (node, e) => {
|
|
1596
1702
|
// Ritarda il focus quando si clicca sull'icona del documento
|
|
1597
1703
|
// per permettere al doppio click di funzionare
|
|
1598
1704
|
const target = e.target;
|
|
1599
1705
|
return !!target.closest('.tm-dcmt-icon');
|
|
1600
|
-
} }) })
|
|
1601
|
-
setTimeout(() => {
|
|
1602
|
-
expansionAbortController?.abort();
|
|
1603
|
-
}, 100);
|
|
1604
|
-
} }))] }));
|
|
1706
|
+
} }) })] }));
|
|
1605
1707
|
};
|
|
1606
1708
|
export default TMRelationViewer;
|