@topconsultnpm/sdkui-react 6.22.0-dev3.2 → 6.22.0-dev3.21
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/styles.d.ts +43 -17
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +78 -19
- package/lib/components/base/Styled.d.ts +79 -27
- package/lib/components/base/Styled.js +7 -5
- 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.d.ts +2 -0
- package/lib/components/base/TMDataGrid.js +34 -6
- package/lib/components/base/TMDataGridExportForm.d.ts +2 -1
- package/lib/components/base/TMFileManager.d.ts +2 -1
- package/lib/components/base/TMFileManagerDataGridView.d.ts +2 -1
- package/lib/components/base/TMFileManagerThumbnailItems.d.ts +1 -1
- package/lib/components/base/TMFileManagerThumbnailsView.d.ts +1 -1
- package/lib/components/base/TMFileManagerUtils.d.ts +3 -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/TMPanel.js +167 -167
- package/lib/components/base/TMSpinner.d.ts +2 -1
- package/lib/components/base/TMTab.d.ts +4 -3
- package/lib/components/base/TMTabControl.d.ts +106 -0
- package/lib/components/base/TMTabControl.js +664 -0
- package/lib/components/base/TMTooltip.d.ts +1 -1
- package/lib/components/base/TMTreeView.d.ts +13 -5
- package/lib/components/base/TMTreeView.js +19 -1
- 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 +3 -2
- package/lib/components/base/TMWaitPanel.js +1 -1
- package/lib/components/choosers/TMDistinctValues.js +27 -18
- package/lib/components/choosers/TMDynDataListItemChooser.d.ts +1 -1
- package/lib/components/choosers/TMInvoiceRetrieveFormats.d.ts +2 -1
- package/lib/components/choosers/TMOrderRetrieveFormats.d.ts +2 -1
- package/lib/components/choosers/TMQuickSearchInfo.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 +15 -5
- package/lib/components/editors/TMFormulaEditor.d.ts +1 -1
- package/lib/components/editors/TMHtmlContentDisplay.d.ts +2 -1
- package/lib/components/editors/TMHtmlEditor.d.ts +2 -1
- package/lib/components/editors/TMMetadataValues.js +5 -5
- package/lib/components/editors/TMTreeDropDown.d.ts +1 -1
- package/lib/components/features/archive/TMArchive.js +7 -2
- package/lib/components/features/assistant/TMToppyDraggableHelpCenter.d.ts +1 -1
- package/lib/components/features/blog/TMBlogCommentForm.d.ts +2 -1
- package/lib/components/features/documents/TMCopyToFolderForm.js +18 -7
- package/lib/components/features/documents/TMDcmtForm.js +3 -2
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +1 -1
- package/lib/components/features/documents/TMDcmtIcon.d.ts +2 -1
- package/lib/components/features/documents/TMDcmtPreview.d.ts +7 -3
- 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 +26 -26
- package/lib/components/features/documents/TMMasterDetailDcmts.js +40 -13
- package/lib/components/features/documents/TMMasterInfoFields.d.ts +2 -1
- package/lib/components/features/documents/TMMergeToPdfForm.d.ts +2 -1
- package/lib/components/features/documents/TMMergeToPdfForm.js +8 -7
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +2 -1
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +13 -15
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +4 -4
- package/lib/components/features/documents/TMRelationViewer.d.ts +21 -3
- package/lib/components/features/documents/TMRelationViewer.js +140 -104
- package/lib/components/features/documents/relationTreeState.d.ts +32 -0
- package/lib/components/features/documents/relationTreeState.js +89 -0
- package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +2 -1
- package/lib/components/features/search/TMMetadataOutputForm.d.ts +2 -1
- package/lib/components/features/search/TMMetadataSorterForm.d.ts +2 -1
- package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
- package/lib/components/features/search/TMSearch.d.ts +1 -1
- package/lib/components/features/search/TMSearch.js +68 -9
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +9 -3
- package/lib/components/features/search/TMSearchResult.d.ts +5 -0
- package/lib/components/features/search/TMSearchResult.js +13 -4
- 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/TMTreeSelector.js +37 -35
- package/lib/components/features/search/TMViewHistoryDcmt.d.ts +2 -1
- 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/tasks/TMTasksView.js +6 -12
- package/lib/components/features/wg/TMWGsCopyMoveForm.d.ts +3 -2
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +8 -7
- 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/Chooser.d.ts +2 -2
- package/lib/components/forms/TMChooserForm.d.ts +2 -1
- package/lib/components/forms/TMResultDialog.js +1 -1
- package/lib/components/forms/TMSaveForm.d.ts +4 -4
- package/lib/components/grids/TMBlogAttachments.d.ts +2 -1
- package/lib/components/grids/TMBlogHeader.d.ts +1 -1
- package/lib/components/grids/TMBlogsPost.d.ts +1 -1
- package/lib/components/grids/TMBlogsPostUtils.d.ts +9 -9
- package/lib/components/grids/TMValidationItemsList.d.ts +2 -1
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- 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 +4 -2
- 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/query/TMQueryEditor.d.ts +12 -4
- package/lib/components/settings/SettingsAppearance.d.ts +2 -1
- package/lib/components/sidebar/TMAboutApp.d.ts +2 -1
- package/lib/components/sidebar/TMCommandsPanel.d.ts +3 -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/helper/Enum_Localizator.js +1 -0
- package/lib/helper/SDKUI_Localizator.d.ts +6 -0
- package/lib/helper/SDKUI_Localizator.js +60 -0
- package/lib/helper/TMAppUpdatedModal.d.ts +2 -1
- package/lib/helper/TMAppUpdatedModal.js +93 -93
- package/lib/helper/TMCommandsContextMenu.d.ts +1 -1
- package/lib/helper/TMIcons.d.ts +280 -280
- package/lib/helper/TMPdfViewer.d.ts +2 -1
- package/lib/helper/TMToppyMessage.d.ts +2 -2
- package/lib/helper/TMUtils.d.ts +10 -4
- package/lib/helper/helpers.d.ts +1 -1
- package/lib/hooks/useDcmtOperations.js +94 -56
- package/lib/hooks/useDocumentOperations.d.ts +1 -0
- package/lib/hooks/useDocumentOperations.js +2 -2
- package/lib/hooks/useInputDialog.d.ts +2 -1
- package/lib/hooks/usePreventFileDrop.js +9 -0
- package/lib/services/caseflow/CaseFlowService.d.ts +32 -1
- package/lib/services/caseflow/CaseFlowService.js +58 -1
- package/lib/services/platform_services.d.ts +3 -2
- package/package.json +22 -22
|
@@ -13,6 +13,7 @@ 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
|
*/
|
|
@@ -67,8 +68,10 @@ export const getDisplayValueByColumn = (col, value) => {
|
|
|
67
68
|
};
|
|
68
69
|
/**
|
|
69
70
|
* Convert SearchResultDescriptor to structured data source with metadata
|
|
70
|
-
*
|
|
71
|
-
*
|
|
71
|
+
* I valori dei documenti sono GIÀ presenti nelle righe restituite dalla ricerca:
|
|
72
|
+
* qui vengono solo abbinati ai rispettivi MetadataDescriptor (perm,
|
|
73
|
+
* isSpecialSearchOutput, ...), risolti UNA SOLA VOLTA per tipo documento.
|
|
74
|
+
* Nessuna chiamata al server per riga.
|
|
72
75
|
*/
|
|
73
76
|
export const searchResultToDataSource = async (searchResult, hideSysMetadata, signal) => {
|
|
74
77
|
const rows = searchResult?.dtdResult?.rows ?? [];
|
|
@@ -77,27 +80,28 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata, si
|
|
|
77
80
|
const columns = searchResult?.dtdResult?.columns ?? [];
|
|
78
81
|
const tidColIndex = columns.findIndex(col => col.caption?.toUpperCase() === 'TID');
|
|
79
82
|
const didColIndex = columns.findIndex(col => col.caption?.toUpperCase() === 'DID');
|
|
83
|
+
const firstRow = rows[0] ?? [];
|
|
84
|
+
const tid = tidColIndex >= 0 && firstRow.length > 0 ? Number(firstRow[tidColIndex]) : searchResult?.fromTID;
|
|
85
|
+
const firstDid = didColIndex >= 0 && firstRow.length > 0 ? Number(firstRow[didColIndex]) : undefined;
|
|
86
|
+
const dtdWithMetadata = await DcmtTypeListCacheService.GetWithNotGrantedAsync(tid, firstDid, searchResult);
|
|
87
|
+
const mdList = dtdWithMetadata?.metadata ?? [];
|
|
88
|
+
// Posizione di ogni MID nelle colonne del risultato: identica per tutte le righe,
|
|
89
|
+
// quindi calcolata una volta sola invece di una find() per metadato per riga.
|
|
90
|
+
const mids = columns.map(col => Number(col.extendedProperties?.["MID"] ?? "0"));
|
|
91
|
+
const columnByMid = new Map();
|
|
92
|
+
columns.forEach((col, i) => { if (!columnByMid.has(mids[i]))
|
|
93
|
+
columnByMid.set(mids[i], col); });
|
|
94
|
+
const dtdResult = searchResult?.dtdResult;
|
|
80
95
|
for (let index = 0; index < rows.length; index++) {
|
|
81
96
|
// Interruzione richiesta dall'utente: restituisce i documenti già elaborati
|
|
82
97
|
if (signal?.aborted)
|
|
83
98
|
break;
|
|
84
99
|
const row = rows[index];
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const did = didColIndex >= 0 ? Number(row[didColIndex]) : undefined;
|
|
88
|
-
// Fetch complete metadata for this specific document (like getFilteredMetadata does)
|
|
89
|
-
const metadata = await SDK_Globals.tmSession?.NewSearchEngine().GetMetadataAsync(tid, did, true);
|
|
90
|
-
if (metadata) {
|
|
91
|
-
// Get full DTD with metadata for this document
|
|
92
|
-
const dtdResult = metadata.dtdResult;
|
|
93
|
-
const metadataRows = dtdResult?.rows?.[0] ?? [];
|
|
94
|
-
const mids = metadata.selectMIDs;
|
|
95
|
-
const dtdWithMetadata = await DcmtTypeListCacheService.GetWithNotGrantedAsync(tid, did, metadata);
|
|
96
|
-
const mdList = dtdWithMetadata?.metadata ?? [];
|
|
100
|
+
const item = { rowIndex: index };
|
|
101
|
+
if (mdList.length > 0) {
|
|
97
102
|
// Convert to MetadataValueDescriptorEx array
|
|
98
|
-
const metadataList = searchResultToMetadataValues(tid, dtdResult,
|
|
103
|
+
const metadataList = searchResultToMetadataValues(tid, dtdResult, row, mids, mdList, LayoutModes.Update);
|
|
99
104
|
// Convert to DcmtMetadataMap format (like getFilteredMetadata does)
|
|
100
|
-
const item = { rowIndex: index };
|
|
101
105
|
for (const mvd of metadataList) {
|
|
102
106
|
if (hideSysMetadata && mvd.mid !== undefined && mvd.mid < 100)
|
|
103
107
|
continue;
|
|
@@ -106,23 +110,18 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata, si
|
|
|
106
110
|
: (mvd.md?.name ?? '');
|
|
107
111
|
if (key) {
|
|
108
112
|
// Find the corresponding column by MID to format the value
|
|
109
|
-
const column = dtdResult?.columns?.find(c => Number(c.extendedProperties?.["MID"] ?? "0") === mvd.mid);
|
|
110
113
|
item[key] = {
|
|
111
114
|
md: mvd.md,
|
|
112
|
-
value: getDisplayValueByColumn(
|
|
115
|
+
value: getDisplayValueByColumn(columnByMid.get(mvd.mid ?? 0), mvd.value)
|
|
113
116
|
};
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
|
-
output.push(item);
|
|
117
119
|
}
|
|
118
120
|
else {
|
|
119
|
-
// Fallback:
|
|
120
|
-
const item = { rowIndex: index };
|
|
121
|
-
const fallbackTid = searchResult?.fromTID;
|
|
122
|
-
const dtd = await DcmtTypeListCacheService.GetWithNotGrantedAsync(fallbackTid, undefined, searchResult);
|
|
121
|
+
// Fallback: il DTD non è risolvibile, usa le colonne grezze del risultato
|
|
123
122
|
for (let i = 0; i < row.length; i++) {
|
|
124
|
-
const column =
|
|
125
|
-
const mid =
|
|
123
|
+
const column = columns[i];
|
|
124
|
+
const mid = mids[i];
|
|
126
125
|
if (hideSysMetadata && mid < 100)
|
|
127
126
|
continue;
|
|
128
127
|
let key = column?.caption ?? '';
|
|
@@ -134,20 +133,18 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata, si
|
|
|
134
133
|
key = `${key}_${mid}`;
|
|
135
134
|
}
|
|
136
135
|
}
|
|
137
|
-
const value = row[i];
|
|
138
|
-
const md = dtd?.metadata?.find(o => o.id == mid);
|
|
139
136
|
item[key] = {
|
|
140
|
-
md:
|
|
141
|
-
value: getDisplayValueByColumn(column,
|
|
137
|
+
md: undefined,
|
|
138
|
+
value: getDisplayValueByColumn(column, row[i])
|
|
142
139
|
};
|
|
143
140
|
}
|
|
144
|
-
output.push(item);
|
|
145
141
|
}
|
|
142
|
+
output.push(item);
|
|
146
143
|
}
|
|
147
144
|
return output;
|
|
148
145
|
};
|
|
149
146
|
export const DEFAULT_RELATION_EXPAND_LEVEL = 4; // Default maximum depth level for expansion (can be adjusted)
|
|
150
|
-
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, onStopCalculationReady, onCalculationStopped }) => {
|
|
147
|
+
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, onLoadingProgress, onStopCalculationReady, onCalculationStopped, initialTreeState, onTreeStateChange }) => {
|
|
151
148
|
// State
|
|
152
149
|
const [dcmtTypes, setDcmtTypes] = useState([]);
|
|
153
150
|
const [treeData, setTreeData] = useState([]);
|
|
@@ -181,6 +178,14 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
181
178
|
const userInteractedWithStaticItemsRef = React.useRef(false);
|
|
182
179
|
// Ref to track the last inputKey for which we set the focused item
|
|
183
180
|
const lastFocusedInputRef = React.useRef('');
|
|
181
|
+
// Catturato al mount (il padre lo aggiorna di continuo) e azzerato dopo l'uso,
|
|
182
|
+
// così vale solo per il primo caricamento
|
|
183
|
+
const stateToRestoreRef = React.useRef(initialTreeState);
|
|
184
|
+
// Parte dallo stato ricevuto: il padre azzera il fuoco prima di rimontare, quindi
|
|
185
|
+
// la prima notifica dopo il rimontaggio lo perderebbe
|
|
186
|
+
const lastReportedStateRef = React.useRef(initialTreeState);
|
|
187
|
+
// Percorso su cui rimettere il fuoco dopo un ripristino
|
|
188
|
+
const pendingFocusPathRef = React.useRef(undefined);
|
|
184
189
|
// State for all users (used for checkout status display)
|
|
185
190
|
const [allUsers, setAllUsers] = useState([]);
|
|
186
191
|
// Load all users for checkout status resolution
|
|
@@ -196,6 +201,11 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
196
201
|
const isLoading = showWaitPanel || showExpansionWaitPanel;
|
|
197
202
|
onLoadingStateChanged?.(isLoading);
|
|
198
203
|
}, [showWaitPanel, showExpansionWaitPanel, onLoadingStateChanged]);
|
|
204
|
+
// Notifica al padre l'avanzamento del caricamento iniziale (documenti processati
|
|
205
|
+
// sul totale), così può mostrarlo nel proprio overlay che copre il wait panel interno
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
onLoadingProgress?.(waitPanelValuePrimary, waitPanelMaxValuePrimary);
|
|
208
|
+
}, [waitPanelValuePrimary, waitPanelMaxValuePrimary, onLoadingProgress]);
|
|
199
209
|
// Mantiene l'AbortController dell'espansione in corso accessibile da callback stabili
|
|
200
210
|
const expansionAbortControllerRef = React.useRef(undefined);
|
|
201
211
|
useEffect(() => {
|
|
@@ -478,6 +488,26 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
478
488
|
return { ...node, hidden: shouldHide, items: updatedItems };
|
|
479
489
|
});
|
|
480
490
|
}, [showZeroDcmts]);
|
|
491
|
+
/** Messaggio informativo in testa se tutti i figli sono nascosti dal filtro; senza figli il nodo resta vuoto (nessuna freccina). */
|
|
492
|
+
const applyZeroFilterInfoMessage = useCallback((nodeKey, items) => {
|
|
493
|
+
const cleaned = items.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
494
|
+
if (showZeroDcmts)
|
|
495
|
+
return cleaned;
|
|
496
|
+
const realChildren = cleaned.filter(child => !child.isInfoMessage);
|
|
497
|
+
if (realChildren.length === 0 || !realChildren.every(child => child.hidden))
|
|
498
|
+
return cleaned;
|
|
499
|
+
return [
|
|
500
|
+
{
|
|
501
|
+
key: `__info__${nodeKey}`,
|
|
502
|
+
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
503
|
+
isContainer: false,
|
|
504
|
+
isDcmt: false,
|
|
505
|
+
isInfoMessage: true,
|
|
506
|
+
isExpandible: false
|
|
507
|
+
},
|
|
508
|
+
...cleaned // Keep hidden containers so they can be shown when toggling
|
|
509
|
+
];
|
|
510
|
+
}, [showZeroDcmts]);
|
|
481
511
|
/**
|
|
482
512
|
* Helper function to set up initial tree expansion state.
|
|
483
513
|
* Called after all data is loaded but before setTreeData.
|
|
@@ -565,6 +595,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
565
595
|
});
|
|
566
596
|
return result;
|
|
567
597
|
};
|
|
598
|
+
/**
|
|
599
|
+
* Carica i figli di un documento durante il ripristino. Usa i loader e non
|
|
600
|
+
* calculateItemsForNode, che accenderebbe il wait panel di espansione mentre
|
|
601
|
+
* è già attivo quello del caricamento principale.
|
|
602
|
+
*/
|
|
603
|
+
const loadChildrenForRestore = async (node) => {
|
|
604
|
+
if (!node.tid || !node.did)
|
|
605
|
+
return [];
|
|
606
|
+
if (isForMaster && invertMasterNavigation)
|
|
607
|
+
return await getMasterDcmtsAsync(node.tid, node.did, 1, abortController.signal);
|
|
608
|
+
return await getDetailDcmtsAsync(node.tid, node.did, 1, abortController.signal);
|
|
609
|
+
};
|
|
568
610
|
/**
|
|
569
611
|
* Main data loading function
|
|
570
612
|
*/
|
|
@@ -638,6 +680,11 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
638
680
|
}
|
|
639
681
|
}
|
|
640
682
|
}
|
|
683
|
+
// Interruzione richiesta dall'utente: il documento corrente non è stato
|
|
684
|
+
// caricato per intero, quindi non viene aggiunto all'albero. Il parziale
|
|
685
|
+
// resta così a granularità di documento (es. 3 completi su 10).
|
|
686
|
+
if (abortController.signal.aborted)
|
|
687
|
+
break;
|
|
641
688
|
// Aggiungi i master come root, raggruppando per TID e relationId
|
|
642
689
|
for (const masterContainer of masterDocs) {
|
|
643
690
|
const existingContainer = tree.find(c => c.tid === masterContainer.tid && c.relationId === masterContainer.relationId && c.isContainer);
|
|
@@ -666,6 +713,9 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
666
713
|
const relatedDocs = isForMaster
|
|
667
714
|
? await getMasterDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel, abortController.signal)
|
|
668
715
|
: await getDetailDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel, abortController.signal);
|
|
716
|
+
// Interruzione richiesta dall'utente
|
|
717
|
+
if (abortController.signal.aborted)
|
|
718
|
+
break;
|
|
669
719
|
// Se showMainDocument è false, mostra i container dei dettagli direttamente sotto il container principale
|
|
670
720
|
if (!showMainDocument) {
|
|
671
721
|
// Check if a container for this TID already exists in the tree
|
|
@@ -817,7 +867,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
817
867
|
const wasStopped = abortController.signal.aborted;
|
|
818
868
|
if (wasStopped) {
|
|
819
869
|
setLoadStoppedByUser(true);
|
|
820
|
-
onCalculationStopped?.();
|
|
870
|
+
onCalculationStopped?.(processedCount, inputDcmts.length);
|
|
821
871
|
}
|
|
822
872
|
// If no relations found, notify parent
|
|
823
873
|
if (!wasStopped && hasNoRelations && onNoRelationsFound) {
|
|
@@ -843,12 +893,20 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
843
893
|
}
|
|
844
894
|
// FIRST setup initial expansion state (root container expanded, first document expanded with focus, first correlation folder expanded)
|
|
845
895
|
// This must run BEFORE updateHiddenProperty so that infoMessage logic sees expanded=true
|
|
846
|
-
|
|
896
|
+
let expandedTree = setupInitialTreeExpansion(tree);
|
|
897
|
+
// Stato precedente da ripristinare (rimontaggio dopo un'operazione): si riapplica
|
|
898
|
+
// sopra l'espansione iniziale, che resta necessaria perché marca isRoot.
|
|
899
|
+
const stateToRestore = stateToRestoreRef.current;
|
|
900
|
+
stateToRestoreRef.current = undefined;
|
|
901
|
+
if (stateToRestore && stateToRestore.inputKey === getInputKey()) {
|
|
902
|
+
expandedTree = await applyRelationTreeState(expandedTree, stateToRestore, loadChildrenForRestore, () => abortController.signal.aborted);
|
|
903
|
+
pendingFocusPathRef.current = stateToRestore.focusedPath;
|
|
904
|
+
}
|
|
847
905
|
// THEN apply hidden property transformations (creates new objects, adds infoMessage where needed)
|
|
848
906
|
// Now it will correctly detect expanded nodes and add infoMessage if all children are hidden
|
|
849
907
|
const processedTree = updateHiddenProperty(expandedTree);
|
|
850
908
|
setTreeData(processedTree);
|
|
851
|
-
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty, showMainDocument, labelMainContainer, onNoRelationsFound, onCalculationStopped]);
|
|
909
|
+
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty, showMainDocument, labelMainContainer, onNoRelationsFound, onCalculationStopped, getInputKey]);
|
|
852
910
|
/**
|
|
853
911
|
* Merge main tree data with additional static items
|
|
854
912
|
*/
|
|
@@ -863,6 +921,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
863
921
|
// and may contain custom properties that would be lost by the spread operator
|
|
864
922
|
return [...treeData, ...itemsToMerge];
|
|
865
923
|
}, [treeData, additionalStaticItems, staticItemsState]);
|
|
924
|
+
/** Notifica al padre lo stato dell'albero, così che possa restituirlo dopo un refresh. */
|
|
925
|
+
useEffect(() => {
|
|
926
|
+
if (!onTreeStateChange)
|
|
927
|
+
return;
|
|
928
|
+
// Ad albero vuoto (caricamento in corso) si sovrascriverebbe lo stato da ripristinare
|
|
929
|
+
if (treeData.length === 0)
|
|
930
|
+
return;
|
|
931
|
+
// Il padre azzera il fuoco all'avvio di un refresh: si conserva l'ultimo noto
|
|
932
|
+
const state = collectRelationTreeState(treeData, getInputKey(), focusedItem?.key, lastReportedStateRef.current?.focusedPath);
|
|
933
|
+
lastReportedStateRef.current = state;
|
|
934
|
+
onTreeStateChange(state);
|
|
935
|
+
}, [treeData, focusedItem, getInputKey, onTreeStateChange]);
|
|
866
936
|
/**
|
|
867
937
|
* Notify parent with a flattened list of all items whenever the merged tree data changes.
|
|
868
938
|
*/
|
|
@@ -942,6 +1012,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
942
1012
|
if (lastFocusedInputRef.current === currentInputKey) {
|
|
943
1013
|
return;
|
|
944
1014
|
}
|
|
1015
|
+
// Ripristino dopo un refresh. Se il nodo non esiste più (es. documento cancellato
|
|
1016
|
+
// fisicamente) si prosegue con il fuoco predefinito sul root.
|
|
1017
|
+
const focusPathToRestore = pendingFocusPathRef.current;
|
|
1018
|
+
pendingFocusPathRef.current = undefined;
|
|
1019
|
+
if (focusPathToRestore) {
|
|
1020
|
+
const nodeToFocus = findRelationNodeByPath(treeData, focusPathToRestore);
|
|
1021
|
+
if (nodeToFocus) {
|
|
1022
|
+
onFocusedItemChanged(nodeToFocus);
|
|
1023
|
+
lastFocusedInputRef.current = currentInputKey;
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
945
1027
|
// Helper function to recursively find the first document with isRoot=true
|
|
946
1028
|
const findFirstRootDocument = (items) => {
|
|
947
1029
|
for (const item of items) {
|
|
@@ -1019,29 +1101,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1019
1101
|
if (node.isLoaded && node.items) {
|
|
1020
1102
|
// Apply current show/hide logic based on showZeroDcmts
|
|
1021
1103
|
const updatedItems = updateHiddenProperty(node.items);
|
|
1022
|
-
|
|
1023
|
-
if (node.isContainer) {
|
|
1024
|
-
// Remove any existing info messages
|
|
1025
|
-
const filteredItems = updatedItems.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
1026
|
-
// Check if all real children are hidden
|
|
1027
|
-
const realChildren = filteredItems.filter(child => !child.isInfoMessage);
|
|
1028
|
-
const allChildrenHidden = realChildren.length > 0 && realChildren.every(child => child.hidden);
|
|
1029
|
-
// If showZeroDcmts is false and all items are hidden, show info message + keep hidden containers
|
|
1030
|
-
if (!showZeroDcmts && allChildrenHidden) {
|
|
1031
|
-
return [
|
|
1032
|
-
{
|
|
1033
|
-
key: `__info__${node.key}`,
|
|
1034
|
-
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
1035
|
-
isContainer: false,
|
|
1036
|
-
isDcmt: false,
|
|
1037
|
-
isInfoMessage: true,
|
|
1038
|
-
isExpandible: false
|
|
1039
|
-
},
|
|
1040
|
-
...filteredItems // Keep hidden containers so they can be shown when toggling
|
|
1041
|
-
];
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
return updatedItems;
|
|
1104
|
+
return applyZeroFilterInfoMessage(node.key, updatedItems);
|
|
1045
1105
|
}
|
|
1046
1106
|
// ============================================
|
|
1047
1107
|
// CONTAINER: Show items immediately (no API calls)
|
|
@@ -1055,21 +1115,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1055
1115
|
if (!node.items)
|
|
1056
1116
|
return [];
|
|
1057
1117
|
const updatedItems = updateHiddenProperty(node.items);
|
|
1058
|
-
|
|
1059
|
-
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
1060
|
-
return [
|
|
1061
|
-
{
|
|
1062
|
-
key: `__info__${node.key}`,
|
|
1063
|
-
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
1064
|
-
isContainer: false,
|
|
1065
|
-
isDcmt: false,
|
|
1066
|
-
isInfoMessage: true,
|
|
1067
|
-
isExpandible: false
|
|
1068
|
-
},
|
|
1069
|
-
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
1070
|
-
];
|
|
1071
|
-
}
|
|
1072
|
-
return updatedItems;
|
|
1118
|
+
return applyZeroFilterInfoMessage(node.key, updatedItems);
|
|
1073
1119
|
}
|
|
1074
1120
|
// ============================================
|
|
1075
1121
|
// DOCUMENT: Load its children (relation containers) lazily
|
|
@@ -1110,21 +1156,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1110
1156
|
// - Relation type containers are always visible (even if empty)
|
|
1111
1157
|
// - Other containers with zero items can be hidden based on showZeroDcmts
|
|
1112
1158
|
const updatedItems = updateHiddenProperty(loadedItems);
|
|
1113
|
-
|
|
1114
|
-
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
1115
|
-
return [
|
|
1116
|
-
{
|
|
1117
|
-
key: `__info__${node.key}`,
|
|
1118
|
-
name: SDKUI_Localizator.NoRelatedDocumentsToDisplay,
|
|
1119
|
-
isContainer: false,
|
|
1120
|
-
isDcmt: false,
|
|
1121
|
-
isInfoMessage: true,
|
|
1122
|
-
isExpandible: false
|
|
1123
|
-
},
|
|
1124
|
-
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
1125
|
-
];
|
|
1126
|
-
}
|
|
1127
|
-
return updatedItems;
|
|
1159
|
+
return applyZeroFilterInfoMessage(node.key, updatedItems);
|
|
1128
1160
|
}
|
|
1129
1161
|
catch (error) {
|
|
1130
1162
|
console.error('Error loading document relations:', error);
|
|
@@ -1137,7 +1169,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1137
1169
|
}
|
|
1138
1170
|
// Default: return existing items
|
|
1139
1171
|
return node.items;
|
|
1140
|
-
}, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, showZeroDcmts]);
|
|
1172
|
+
}, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, applyZeroFilterInfoMessage, showZeroDcmts]);
|
|
1141
1173
|
/**
|
|
1142
1174
|
* Default item renderer with metadata display (adapted from TMMasterDetailDcmts.tsx)
|
|
1143
1175
|
*/
|
|
@@ -1170,8 +1202,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1170
1202
|
gap: '10px',
|
|
1171
1203
|
height: '32px',
|
|
1172
1204
|
padding: '6px 0',
|
|
1173
|
-
opacity: item.isZero ? 0.
|
|
1174
|
-
transition: 'opacity 0.2s ease-in-out'
|
|
1205
|
+
opacity: item.isZero ? 0.4 : 1,
|
|
1175
1206
|
};
|
|
1176
1207
|
// Se è il container principale (root) e showMainDocument è false,
|
|
1177
1208
|
// non mostrare il badge perché conterrebbe il numero di sotto-container, non di documenti
|
|
@@ -1201,8 +1232,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1201
1232
|
alignItems: 'center',
|
|
1202
1233
|
cursor: 'pointer',
|
|
1203
1234
|
userSelect: 'none',
|
|
1204
|
-
opacity: item.isZero ? 0.
|
|
1205
|
-
transition: 'opacity 0.2s ease-in-out',
|
|
1235
|
+
opacity: item.isZero ? 0.4 : 1,
|
|
1206
1236
|
textDecoration: isLogicallyDeleted ? 'line-through' : 'none'
|
|
1207
1237
|
};
|
|
1208
1238
|
const documentStyle = customDocumentStyle ? { ...defaultDocumentStyle, ...customDocumentStyle(item) } : defaultDocumentStyle;
|
|
@@ -1377,14 +1407,17 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1377
1407
|
const targetLevel = expandLevel;
|
|
1378
1408
|
const newAbortController = new AbortController();
|
|
1379
1409
|
setExpansionAbortController(newAbortController);
|
|
1410
|
+
// Denominatore subito: con maxValue = 0 TMWaitPanel disegna una barra ferma a 0%
|
|
1411
|
+
setExpansionWaitPanelMaxValue(100);
|
|
1412
|
+
setExpansionWaitPanelValue(0);
|
|
1380
1413
|
// CONTATORI DINAMICI:
|
|
1381
1414
|
// - totalNodes: cresce quando scopriamo nuovi nodi (evita che percentuale > 100%)
|
|
1382
1415
|
// - processedNodes: quanti nodi abbiamo già processato
|
|
1383
|
-
// -
|
|
1416
|
+
// - shownProgress: percentuale mostrata, che non torna mai indietro
|
|
1384
1417
|
let totalNodes = 0;
|
|
1385
1418
|
let processedNodes = 0;
|
|
1386
1419
|
let hasApiCalls = false;
|
|
1387
|
-
let
|
|
1420
|
+
let shownProgress = 0;
|
|
1388
1421
|
/**
|
|
1389
1422
|
* Conta i nodi espandibili in un array (solo primo livello, non ricorsivo).
|
|
1390
1423
|
* Usato per aggiornare totalNodes man mano che scopriamo nuovi figli.
|
|
@@ -1400,6 +1433,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1400
1433
|
};
|
|
1401
1434
|
// Conta iniziale: solo i nodi al livello root
|
|
1402
1435
|
totalNodes = countNodesAtLevel([...workingTreeData, ...workingStaticData]);
|
|
1436
|
+
const initialTotalNodes = totalNodes;
|
|
1403
1437
|
/**
|
|
1404
1438
|
* FUNZIONE RICORSIVA DI ESPANSIONE
|
|
1405
1439
|
*
|
|
@@ -1470,7 +1504,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1470
1504
|
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1, newAbortController.signal);
|
|
1471
1505
|
}
|
|
1472
1506
|
// Applica le regole di visibilità (showZeroDcmts)
|
|
1473
|
-
|
|
1507
|
+
// Stesse regole dell'espansione manuale (messaggio informativo se tutto nascosto)
|
|
1508
|
+
expandedNode.items = applyZeroFilterInfoMessage(node.key, updateHiddenProperty(loadedItems));
|
|
1474
1509
|
expandedNode.isLoaded = true;
|
|
1475
1510
|
expandedNode.itemsCount = loadedItems.length;
|
|
1476
1511
|
// IMPORTANTE: Aggiungi i nuovi nodi scoperti al contatore totale
|
|
@@ -1497,14 +1532,15 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1497
1532
|
}
|
|
1498
1533
|
// Aggiorna il progresso DOPO aver processato questo nodo
|
|
1499
1534
|
processedNodes++;
|
|
1500
|
-
//
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
setExpansionWaitPanelText(SDKUI_Localizator.ExpansionProgress.replaceParams(processedNodes.toString(), totalNodes.toString())
|
|
1535
|
+
// La barra non torna mai indietro: segue il valore reale quando sale, altrimenti avanza verso il 99%
|
|
1536
|
+
const realProgress = totalNodes > 0 ? (processedNodes / totalNodes) * 100 : 0;
|
|
1537
|
+
shownProgress = Math.min(99, realProgress > shownProgress
|
|
1538
|
+
? realProgress
|
|
1539
|
+
: shownProgress + (99 - shownProgress) * 0.01);
|
|
1540
|
+
setExpansionWaitPanelValue(shownProgress);
|
|
1541
|
+
// Seconda riga: spiega perché percentuale e conteggio non vanno di pari passo
|
|
1542
|
+
setExpansionWaitPanelText(SDKUI_Localizator.ExpansionProgress.replaceParams(processedNodes.toString(), totalNodes.toString())
|
|
1543
|
+
+ (totalNodes > initialTotalNodes ? `\n${SDKUI_Localizator.ExpansionProgressIsEstimated}` : ''));
|
|
1508
1544
|
// ═══════════════════════════════════════════════════════════════════
|
|
1509
1545
|
// PASSO 3: CHIAMATA RICORSIVA SUI FIGLI
|
|
1510
1546
|
// Questo è il punto chiave: i nuovi nodi appena caricati vengono
|
|
@@ -1535,7 +1571,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1535
1571
|
setShowExpansionWaitPanel(false);
|
|
1536
1572
|
setExpansionAbortController(undefined);
|
|
1537
1573
|
}
|
|
1538
|
-
}, [expandLevel, treeData, staticItemsState, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty]);
|
|
1574
|
+
}, [expandLevel, treeData, staticItemsState, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, applyZeroFilterInfoMessage]);
|
|
1539
1575
|
/**
|
|
1540
1576
|
* Apply defaultExpandAll once on initial tree load.
|
|
1541
1577
|
*/
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { RelationTreeItem } from './TMRelationViewer';
|
|
2
|
+
/**
|
|
3
|
+
* Conserva lo stato di un albero delle correlazioni (nodi espansi e fuoco) attraverso
|
|
4
|
+
* un refresh che ricarica i dati da zero. I nodi non sono confrontabili per `key`, che
|
|
5
|
+
* contiene un GUID rigenerato ad ogni caricamento: si usa un percorso costruito sui campi.
|
|
6
|
+
*/
|
|
7
|
+
export interface RelationTreeState {
|
|
8
|
+
/** Documenti di input a cui lo stato si riferisce: non va applicato ad altri */
|
|
9
|
+
inputKey: string;
|
|
10
|
+
expandedPaths: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Documenti espansi che avevano già i figli caricati. Quelli espansi ma vuoti
|
|
13
|
+
* (es. il root) non vanno ricaricati: mostrerebbero più di quanto c'era prima.
|
|
14
|
+
*/
|
|
15
|
+
loadedPaths: string[];
|
|
16
|
+
focusedPath?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Carica i figli di un documento durante il ripristino. */
|
|
19
|
+
export type LoadChildrenFn = (node: RelationTreeItem) => Promise<RelationTreeItem[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Fotografa lo stato dell'albero.
|
|
22
|
+
* `previousFocusedPath` viene usato quando il fuoco è già stato azzerato dall'esterno.
|
|
23
|
+
*/
|
|
24
|
+
export declare const collectRelationTreeState: (nodes: RelationTreeItem[], inputKey: string, focusedKey: string | undefined, previousFocusedPath?: string) => RelationTreeState;
|
|
25
|
+
/**
|
|
26
|
+
* Riapplica lo stato all'albero appena caricato. I nodi che non compaiono nello stato,
|
|
27
|
+
* o che non esistono più (es. documento cancellato), restano chiusi: il confronto per
|
|
28
|
+
* percorso è tollerante per costruzione.
|
|
29
|
+
*/
|
|
30
|
+
export declare const applyRelationTreeState: (nodes: RelationTreeItem[], state: RelationTreeState, loadChildren: LoadChildrenFn, isAborted: () => boolean) => Promise<RelationTreeItem[]>;
|
|
31
|
+
/** Cerca un nodo per percorso, per rimettere il fuoco dov'era. */
|
|
32
|
+
export declare const findRelationNodeByPath: (nodes: RelationTreeItem[], path: string) => RelationTreeItem | null;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** I nodi generati (messaggi informativi, separatori) non hanno identità stabile. */
|
|
2
|
+
const hasStableIdentity = (node) => !node.isInfoMessage && !node.isSeparator;
|
|
3
|
+
const getNodeIdentity = (node) => node.isDcmt
|
|
4
|
+
? `d:${node.tid ?? ''}:${node.did ?? ''}:${node.relationId ?? ''}`
|
|
5
|
+
: `c:${node.tid ?? ''}:${node.relationId ?? ''}:${node.name ?? ''}`;
|
|
6
|
+
/** Il percorso include i padri: lo stesso documento può comparire sotto padri diversi. */
|
|
7
|
+
const getNodePath = (node, parentPath) => {
|
|
8
|
+
const identity = getNodeIdentity(node);
|
|
9
|
+
return parentPath ? `${parentPath}>${identity}` : identity;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Fotografa lo stato dell'albero.
|
|
13
|
+
* `previousFocusedPath` viene usato quando il fuoco è già stato azzerato dall'esterno.
|
|
14
|
+
*/
|
|
15
|
+
export const collectRelationTreeState = (nodes, inputKey, focusedKey, previousFocusedPath) => {
|
|
16
|
+
const expandedPaths = [];
|
|
17
|
+
const loadedPaths = [];
|
|
18
|
+
let focusedPath;
|
|
19
|
+
const collect = (items, parentPath) => {
|
|
20
|
+
for (const node of items) {
|
|
21
|
+
if (!hasStableIdentity(node))
|
|
22
|
+
continue;
|
|
23
|
+
const path = getNodePath(node, parentPath);
|
|
24
|
+
if (node.expanded)
|
|
25
|
+
expandedPaths.push(path);
|
|
26
|
+
if (node.expanded && node.isDcmt && node.items)
|
|
27
|
+
loadedPaths.push(path);
|
|
28
|
+
// Entro la stessa istanza montata la key è univoca
|
|
29
|
+
if (focusedKey && node.key === focusedKey)
|
|
30
|
+
focusedPath = path;
|
|
31
|
+
if (node.items)
|
|
32
|
+
collect(node.items, path);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
collect(nodes, '');
|
|
36
|
+
return { inputKey, expandedPaths, loadedPaths, focusedPath: focusedPath ?? previousFocusedPath };
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Riapplica lo stato all'albero appena caricato. I nodi che non compaiono nello stato,
|
|
40
|
+
* o che non esistono più (es. documento cancellato), restano chiusi: il confronto per
|
|
41
|
+
* percorso è tollerante per costruzione.
|
|
42
|
+
*/
|
|
43
|
+
export const applyRelationTreeState = async (nodes, state, loadChildren, isAborted) => {
|
|
44
|
+
const expandedPaths = new Set(state.expandedPaths);
|
|
45
|
+
const loadedPaths = new Set(state.loadedPaths);
|
|
46
|
+
const apply = async (items, parentPath) => {
|
|
47
|
+
const result = [];
|
|
48
|
+
for (const node of items) {
|
|
49
|
+
if (!hasStableIdentity(node)) {
|
|
50
|
+
result.push(node);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const path = getNodePath(node, parentPath);
|
|
54
|
+
const wasExpanded = expandedPaths.has(path);
|
|
55
|
+
let children = node.items;
|
|
56
|
+
let isLoaded = node.isLoaded;
|
|
57
|
+
let itemsCount = node.itemsCount;
|
|
58
|
+
// I figli dei documenti sono lazy: vanno ricaricati se l'utente li aveva aperti
|
|
59
|
+
if (wasExpanded && node.isDcmt && !children && loadedPaths.has(path) && !isAborted()) {
|
|
60
|
+
children = await loadChildren(node);
|
|
61
|
+
isLoaded = true;
|
|
62
|
+
itemsCount = children.length;
|
|
63
|
+
}
|
|
64
|
+
const updatedItems = children ? await apply(children, path) : children;
|
|
65
|
+
result.push({ ...node, expanded: wasExpanded, items: updatedItems, isLoaded, itemsCount });
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
};
|
|
69
|
+
return await apply(nodes, '');
|
|
70
|
+
};
|
|
71
|
+
/** Cerca un nodo per percorso, per rimettere il fuoco dov'era. */
|
|
72
|
+
export const findRelationNodeByPath = (nodes, path) => {
|
|
73
|
+
const find = (items, parentPath) => {
|
|
74
|
+
for (const node of items) {
|
|
75
|
+
if (!hasStableIdentity(node))
|
|
76
|
+
continue;
|
|
77
|
+
const currentPath = getNodePath(node, parentPath);
|
|
78
|
+
if (currentPath === path)
|
|
79
|
+
return node;
|
|
80
|
+
if (node.items) {
|
|
81
|
+
const found = find(node.items, currentPath);
|
|
82
|
+
if (found)
|
|
83
|
+
return found;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
88
|
+
return find(nodes, '');
|
|
89
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { DcmtInfo } from "../../../ts";
|
|
2
3
|
interface TMDcmtCheckoutInfoFormProps {
|
|
3
4
|
dtdName: string;
|
|
4
5
|
selectedDcmtOrFocused: DcmtInfo;
|
|
5
6
|
onClose: () => void;
|
|
6
7
|
}
|
|
7
|
-
declare const TMDcmtCheckoutInfoForm: (props: TMDcmtCheckoutInfoFormProps) =>
|
|
8
|
+
declare const TMDcmtCheckoutInfoForm: (props: TMDcmtCheckoutInfoFormProps) => React.JSX.Element;
|
|
8
9
|
export default TMDcmtCheckoutInfoForm;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { MetadataDescriptor, QueryDescriptor, SelectItem } from "@topconsultnpm/sdk-ts";
|
|
2
3
|
interface TMMetadataOutputFormProps {
|
|
3
4
|
/** QueryDescriptor da cui estrarre i metadati disponibili */
|
|
@@ -13,5 +14,5 @@ interface TMMetadataOutputFormProps {
|
|
|
13
14
|
/** Callback chiamata quando l'utente conferma la selezione dei SelectItem */
|
|
14
15
|
onChoose?: (selectItems: SelectItem[] | undefined) => void;
|
|
15
16
|
}
|
|
16
|
-
declare const TMMetadataOutputForm: (props: TMMetadataOutputFormProps) =>
|
|
17
|
+
declare const TMMetadataOutputForm: (props: TMMetadataOutputFormProps) => React.JSX.Element;
|
|
17
18
|
export default TMMetadataOutputForm;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { MetadataDescriptor, OrderByItem, QueryDescriptor } from "@topconsultnpm/sdk-ts";
|
|
2
3
|
interface TMMetadataSorterFormProps {
|
|
3
4
|
/** QueryDescriptor da cui estrarre i metadati disponibili */
|
|
@@ -13,5 +14,5 @@ interface TMMetadataSorterFormProps {
|
|
|
13
14
|
/** Callback chiamata quando l'utente conferma la selezione degli OrderByItem */
|
|
14
15
|
onChoose?: (orderByItems: OrderByItem[] | undefined) => void;
|
|
15
16
|
}
|
|
16
|
-
declare const TMMetadataSorterForm: (props: TMMetadataSorterFormProps) =>
|
|
17
|
+
declare const TMMetadataSorterForm: (props: TMMetadataSorterFormProps) => React.JSX.Element;
|
|
17
18
|
export default TMMetadataSorterForm;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SavedQueryDescriptor } from '@topconsultnpm/sdk-ts';
|
|
3
|
-
export declare const getTooltipBySqd: (sqd: SavedQueryDescriptor | undefined) =>
|
|
3
|
+
export declare const getTooltipBySqd: (sqd: SavedQueryDescriptor | undefined) => React.JSX.Element | null;
|
|
4
4
|
interface ITMSavedQuerySelectorProps {
|
|
5
5
|
height?: string;
|
|
6
6
|
items?: SavedQueryDescriptor[];
|
|
@@ -12,5 +12,5 @@ interface ITMSavedQuerySelectorProps {
|
|
|
12
12
|
refreshFavoriteSavedQueries?: (sqd: SavedQueryDescriptor) => void;
|
|
13
13
|
onRefreshData?: () => void;
|
|
14
14
|
}
|
|
15
|
-
declare const TMSavedQuerySelector: React.MemoExoticComponent<({ items, selectedId, allowShowSearch, height, manageDefault, onItemClick, onDeleted, refreshFavoriteSavedQueries, onRefreshData }: ITMSavedQuerySelectorProps) =>
|
|
15
|
+
declare const TMSavedQuerySelector: React.MemoExoticComponent<({ items, selectedId, allowShowSearch, height, manageDefault, onItemClick, onDeleted, refreshFavoriteSavedQueries, onRefreshData }: ITMSavedQuerySelectorProps) => React.JSX.Element>;
|
|
16
16
|
export default TMSavedQuerySelector;
|
|
@@ -46,5 +46,5 @@ interface ITMSearchProps {
|
|
|
46
46
|
fetchRemoteCertificates?: (email: string) => Promise<IntesiCertificateData[]>;
|
|
47
47
|
graphometricManager?: IGraphometricManagerProp;
|
|
48
48
|
}
|
|
49
|
-
declare const TMSearch: React.
|
|
49
|
+
declare const TMSearch: (props: ITMSearchProps) => React.JSX.Element;
|
|
50
50
|
export default TMSearch;
|