@topconsultnpm/sdkui-react 6.21.0-dev3.9 → 6.21.0-dev4.11

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.
Files changed (60) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +28 -2
  2. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +6 -6
  3. package/lib/components/base/TMAreaManager.js +11 -0
  4. package/lib/components/base/TMDataGrid.js +12 -2
  5. package/lib/components/base/TMDataGridExportForm.js +19 -8
  6. package/lib/components/base/TMModal.js +2 -2
  7. package/lib/components/base/TMPopUp.d.ts +1 -0
  8. package/lib/components/base/TMPopUp.js +59 -2
  9. package/lib/components/base/TMSpinner.d.ts +4 -2
  10. package/lib/components/base/TMSpinner.js +33 -6
  11. package/lib/components/choosers/TMDistinctValues.js +1 -1
  12. package/lib/components/choosers/TMGroupChooser.js +1 -1
  13. package/lib/components/editors/TMEditorStyled.d.ts +4 -4
  14. package/lib/components/editors/TMFormulaEditor.d.ts +1 -0
  15. package/lib/components/editors/TMFormulaEditor.js +98 -49
  16. package/lib/components/editors/TMMetadataValues.js +23 -6
  17. package/lib/components/editors/TMTextBox.d.ts +1 -0
  18. package/lib/components/editors/TMTextBox.js +2 -1
  19. package/lib/components/features/documents/TMDcmtForm.d.ts +2 -0
  20. package/lib/components/features/documents/TMDcmtForm.js +2 -1
  21. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  22. package/lib/components/features/documents/TMMasterDetailDcmts.js +7 -4
  23. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +2 -2
  24. package/lib/components/features/documents/TMMergeToPdfForm.js +91 -48
  25. package/lib/components/features/documents/TMRelationViewer.js +56 -5
  26. package/lib/components/features/documents/mergePdfUtils.d.ts +52 -0
  27. package/lib/components/features/documents/mergePdfUtils.js +268 -0
  28. package/lib/components/features/search/TMMetadataOutputForm.d.ts +17 -0
  29. package/lib/components/features/search/TMMetadataOutputForm.js +225 -0
  30. package/lib/components/features/search/TMMetadataSorterForm.d.ts +17 -0
  31. package/lib/components/features/search/TMMetadataSorterForm.js +243 -0
  32. package/lib/components/features/search/TMSearch.d.ts +2 -0
  33. package/lib/components/features/search/TMSearch.js +2 -2
  34. package/lib/components/features/search/TMSearchQueryPanel.js +249 -58
  35. package/lib/components/features/search/TMSearchResult.d.ts +3 -0
  36. package/lib/components/features/search/TMSearchResult.js +77 -22
  37. package/lib/components/features/search/metadataFormHelper.d.ts +16 -0
  38. package/lib/components/features/search/metadataFormHelper.js +77 -0
  39. package/lib/components/forms/Login/TMLoginForm.js +15 -3
  40. package/lib/components/wizard/TMWizard.d.ts +1 -0
  41. package/lib/components/wizard/TMWizard.js +5 -3
  42. package/lib/helper/Enum_Localizator.js +2 -0
  43. package/lib/helper/SDKUI_Localizator.d.ts +14 -0
  44. package/lib/helper/SDKUI_Localizator.js +152 -12
  45. package/lib/helper/certificateImportHelper.d.ts +43 -0
  46. package/lib/helper/certificateImportHelper.js +403 -0
  47. package/lib/helper/checkinCheckoutManager.js +10 -2
  48. package/lib/helper/helpers.d.ts +2 -1
  49. package/lib/helper/helpers.js +1 -0
  50. package/lib/helper/index.d.ts +1 -0
  51. package/lib/helper/index.js +1 -0
  52. package/lib/hooks/useDataUserIdItem.js +1 -1
  53. package/lib/hooks/useDcmtOperations.d.ts +2 -1
  54. package/lib/hooks/useDcmtOperations.js +177 -4
  55. package/lib/hooks/useDocumentOperations.d.ts +2 -0
  56. package/lib/hooks/useDocumentOperations.js +47 -6
  57. package/lib/services/platform_services.d.ts +4 -4
  58. package/lib/ts/types.d.ts +3 -1
  59. package/lib/ts/types.js +2 -0
  60. package/package.json +13 -7
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { HomeBlogPost, SearchResultDescriptor, TaskDescriptor } from '@topconsultnpm/sdk-ts';
3
3
  import { RelationTreeItem } from './TMRelationViewer';
4
+ import { IntesiCertificateData } from '../../../helper';
4
5
  import { DcmtInfo, TaskContext, MetadataValueDescriptorEx } from '../../../ts';
5
6
  import { DeviceContextProps } from '../../base/TMDeviceProvider';
6
7
  export type IRelatedDcmt = RelationTreeItem;
@@ -28,6 +29,7 @@ interface ITMMasterDetailDcmtsProps extends DeviceContextProps {
28
29
  openS4TViewer?: boolean;
29
30
  onOpenS4TViewerRequest?: (dcmtInfo: Array<DcmtInfo>, refreshDocumentPreview?: (() => Promise<void>)) => void;
30
31
  onOpenPdfEditorRequest?: ((dcmtInfo: Array<DcmtInfo>, refreshDocumentPreview?: () => Promise<void>) => void);
32
+ fetchRemoteCertificates?: (email: string) => Promise<IntesiCertificateData[]>;
31
33
  datagridUtility?: {
32
34
  onRefreshSearchAsyncDatagrid?: () => Promise<void>;
33
35
  onRefreshDataRowsAsync?: (() => Promise<void>);
@@ -16,7 +16,9 @@ import TMDcmtForm from './TMDcmtForm';
16
16
  import { TMNothingToShow } from './TMDcmtPreview';
17
17
  import { Spinner, TMButton } from '../..';
18
18
  import { useDocumentOperations } from '../../../hooks/useDocumentOperations';
19
- const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, deviceType, inputDcmts, isForMaster, showCurrentDcmtIndicator = true, allowNavigation, canNext, canPrev, onNext, onPrev, onBack, appendMasterDcmts, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, datagridUtility, dcmtUtility, }) => {
19
+ import TMToppyMessage from '../../../helper/TMToppyMessage';
20
+ import TMPanel from '../../base/TMPanel';
21
+ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, deviceType, inputDcmts, isForMaster, showCurrentDcmtIndicator = true, allowNavigation, canNext, canPrev, onNext, onPrev, onBack, appendMasterDcmts, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, datagridUtility, dcmtUtility, fetchRemoteCertificates }) => {
20
22
  const floatingBarContainerRef = useRef(null);
21
23
  const [focusedItem, setFocusedItem] = useState();
22
24
  const [selectedItems, setSelectedItems] = useState([]);
@@ -145,6 +147,7 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
145
147
  currentSearchResults: [],
146
148
  currentMetadataValues: [],
147
149
  allUsers: [],
150
+ fetchRemoteCertificates,
148
151
  // searchResult: selectedSearchResult,
149
152
  datagridUtility: {
150
153
  visibleItems: [],
@@ -423,11 +426,11 @@ const TMRelationViewerWrapper = ({ refreshKey, inputDcmts, isForMaster, showCurr
423
426
  }, [onItemContextMenu, handleFocusedItemChanged]);
424
427
  return (_jsx(TMRelationViewer, { inputDcmts: inputDcmts, isForMaster: isForMaster, showCurrentDcmtIndicator: showCurrentDcmtIndicator, initialShowZeroDcmts: showZeroDcmts, customItemRender: customItemRender, allowMultipleSelection: allowMultipleSelection, focusedItem: focusedItem, selectedItems: selectedItems, onFocusedItemChanged: handleFocusedItemChanged, onSelectedItemsChanged: onSelectedItemsChanged, maxDepthLevel: 1, invertMasterNavigation: false, showExpandAllButton: true, onNoRelationsFound: onNoRelationsFound, onItemContextMenu: onContextMenu, focusedItemFormData: focusedItemFormData }, refreshKey));
425
428
  };
426
- const TMFormOrResultWrapper = ({ refreshKey, deviceType, focusedItem, onTaskCreateRequest, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshSearchAsyncDatagrid, onRefreshSearchResults, handleNavigateToReference }) => {
429
+ const TMFormOrResultWrapper = ({ refreshKey, deviceType, focusedItem, onTaskCreateRequest, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshSearchAsyncDatagrid, onRefreshSearchResults, handleNavigateToReference, fetchRemoteCertificates }) => {
427
430
  const { setPanelVisibilityById } = useTMPanelManagerContext();
428
431
  return (_jsx(_Fragment, { children: focusedItem?.isDcmt ?
429
432
  _jsx(TMDcmtForm, { groupId: 'tmFormOrResult', TID: focusedItem?.tid, DID: focusedItem.did, allowButtonsRefs: true, isClosable: deviceType !== DeviceType.MOBILE, allowNavigation: false, allowRelations: deviceType !== DeviceType.MOBILE, showDcmtFormSidebar: false, onClose: () => { setPanelVisibilityById('tmTreeView', true); }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, onReferenceClick: handleNavigateToReference, moreInfoTasks: getMoreInfoTasksForDocument(allTasks, focusedItem?.tid, focusedItem?.did), openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, datagridUtility: {
430
433
  onRefreshSearchAsyncDatagrid,
431
- } }, refreshKey) :
432
- _jsx(TMSearchResult, { groupId: 'tmFormOrResult', isClosable: deviceType !== DeviceType.MOBILE, context: SearchResultContext.METADATA_SEARCH, allowFloatingBar: false, allowRelations: false, openDcmtFormAsModal: true, searchResults: focusedItem?.searchResult ?? [], showSearchResultSidebar: false, showDcmtFormSidebar: false, onTaskCreateRequest: onTaskCreateRequest, onClose: () => { setPanelVisibilityById('tmTreeView', true); }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, editPdfForm: editPdfForm, onOpenPdfEditorRequest: onOpenPdfEditorRequest, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, enablePinIcons: false, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, showBackButton: false, onRefreshSearchAsyncDatagrid: onRefreshSearchResults, onReferenceClick: handleNavigateToReference }, refreshKey) }));
434
+ }, fetchRemoteCertificates: fetchRemoteCertificates }, refreshKey) :
435
+ focusedItem?.searchResult === undefined ? (_jsx(TMPanel, { title: SDKUI_Localizator.SearchResult, children: _jsx(TMToppyMessage, { message: SDKUI_Localizator.SelectDocumentToViewSearchResults }) })) : (_jsx(TMSearchResult, { groupId: 'tmFormOrResult', isClosable: deviceType !== DeviceType.MOBILE, context: SearchResultContext.METADATA_SEARCH, allowFloatingBar: false, allowRelations: false, openDcmtFormAsModal: true, searchResults: focusedItem?.searchResult ?? [], showSearchResultSidebar: false, showDcmtFormSidebar: false, autoFocusFirstRow: false, onTaskCreateRequest: onTaskCreateRequest, onClose: () => { setPanelVisibilityById('tmTreeView', true); }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, editPdfForm: editPdfForm, onOpenPdfEditorRequest: onOpenPdfEditorRequest, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, enablePinIcons: false, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, showBackButton: false, onRefreshSearchAsyncDatagrid: onRefreshSearchResults, onReferenceClick: handleNavigateToReference, fetchRemoteCertificates: fetchRemoteCertificates }, refreshKey)) }));
433
436
  };
@@ -1,10 +1,10 @@
1
- import React from 'react';
2
1
  import { DcmtInfo } from '../../../ts';
3
2
  import { HomeBlogPost, TaskDescriptor } from '@topconsultnpm/sdk-ts';
4
3
  import { TMCopyToFolderMode } from '../../../hooks/useDocumentOperations';
5
4
  interface ITMMergeToPdfFormProps {
6
5
  mode: TMCopyToFolderMode;
7
6
  selectedDcmtInfos: Array<DcmtInfo>;
7
+ selectedItemsFull: Array<any>;
8
8
  onClose: () => void;
9
9
  showTMRelationViewer: boolean;
10
10
  allTasks?: Array<TaskDescriptor>;
@@ -20,5 +20,5 @@ interface ITMMergeToPdfFormProps {
20
20
  * Condivide TMDownloadRelationViewerSection e gli helper in copyAndMergeDcmtsShared
21
21
  * con TMCopyToFolderForm.
22
22
  */
23
- declare const TMMergeToPdfForm: React.FC<ITMMergeToPdfFormProps>;
23
+ declare const TMMergeToPdfForm: (props: ITMMergeToPdfFormProps) => import("react/jsx-runtime").JSX.Element;
24
24
  export default TMMergeToPdfForm;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRef, useState } from 'react';
3
3
  import { DownloadTypes } from '../../../ts';
4
- import { calcResponsiveSizes, IconFolderOpen, IconPlay, IconUndo, isConvertibleToPdfExt, SDKUI_Globals, SDKUI_Localizator, } from '../../../helper';
4
+ import { calcResponsiveSizes, getExceptionMessage, IconFolderOpen, IconPlay, IconUndo, SDKUI_Globals, SDKUI_Localizator, } from '../../../helper';
5
5
  import { DcmtOpers, FileFormats, GeneralRetrieveFormats, ResultTypes, RetrieveFileOptions, ValidationItem, } from '@topconsultnpm/sdk-ts';
6
6
  import TMModal from '../../base/TMModal';
7
7
  import { useDeviceType } from '../../base/TMDeviceProvider';
@@ -12,16 +12,19 @@ import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
12
12
  import { TMSplitterLayout } from '../../base/TMLayout';
13
13
  import { TMColors } from '../../../utils/theme';
14
14
  import TMDownloadRelationViewerSection from './TMDownloadRelationViewerSection';
15
- import { getDcmtInfosToDownload, getFloatingLabelStyle, isDirectoryPickerSupported, isPdfExt, MIN_PDF_FOR_MERGE, } from './copyAndMergeDcmtsShared';
15
+ import { getDcmtInfosToDownload, getFloatingLabelStyle, isDirectoryPickerSupported, MIN_PDF_FOR_MERGE, } from './copyAndMergeDcmtsShared';
16
+ import { createMetadataPdfFromDocument, getMergePdfSelectionStats, isMetadataOnlyDcmt } from './mergePdfUtils';
16
17
  import ShowAlert from '../../base/TMAlert';
17
18
  import TMTooltip from '../../base/TMTooltip';
18
19
  import MergePdfManager from '../../../helper/MergePdfManager';
20
+ import { TMResultManager } from '../../forms/TMResultDialog';
19
21
  /**
20
22
  * Form per l'unione di più documenti PDF in un singolo file.
21
23
  * Condivide TMDownloadRelationViewerSection e gli helper in copyAndMergeDcmtsShared
22
24
  * con TMCopyToFolderForm.
23
25
  */
24
- const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationViewer, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers }) => {
26
+ const TMMergeToPdfForm = (props) => {
27
+ const { mode, selectedDcmtInfos, selectedItemsFull, onClose, showTMRelationViewer, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers } = props;
25
28
  const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, } = useDcmtOperations();
26
29
  const deviceType = useDeviceType();
27
30
  const [pdfFileName, setPdfFileName] = useState(`${SDKUI_Localizator.Result.toLowerCase()}.pdf`);
@@ -79,36 +82,7 @@ const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationView
79
82
  pdfValidationItems.push(new ValidationItem(ResultTypes.ERROR, SDKUI_Localizator.PdfFileName, SDKUI_Localizator.RequiredField));
80
83
  }
81
84
  // ---- Statistiche selezione PDF / convertibili / non-PDF ----
82
- const convertibleSelectedItems = (() => {
83
- if (showTMRelationViewer) {
84
- return selectedItemsRelationViewer
85
- .filter(i => i.isDcmt && isConvertibleToPdfExt(i.fileExt))
86
- .map(i => ({ key: `${i.tid}_${i.did}`, ext: i.fileExt ?? undefined }));
87
- }
88
- return selectedDcmtInfos
89
- .filter(d => isConvertibleToPdfExt(d.FILEEXT))
90
- .map(d => ({ key: `${d.TID}_${d.DID}`, ext: d.FILEEXT }));
91
- })();
92
- const hasConvertibleSelected = convertibleSelectedItems.length > 0;
93
- const nonPdfSelectedItems = (() => {
94
- if (showTMRelationViewer) {
95
- return selectedItemsRelationViewer
96
- .filter(i => i.isDcmt && !isPdfExt(i.fileExt) && !isConvertibleToPdfExt(i.fileExt))
97
- .map(i => ({ key: `${i.tid}_${i.did}`, ext: i.fileExt ?? undefined }));
98
- }
99
- return selectedDcmtInfos
100
- .filter(d => !isPdfExt(d.FILEEXT) && !isConvertibleToPdfExt(d.FILEEXT))
101
- .map(d => ({ key: `${d.TID}_${d.DID}`, ext: d.FILEEXT }));
102
- })();
103
- const hasNonPdfSelected = nonPdfSelectedItems.length > 0;
104
- // Conteggio file unibili: PDF nativi + file convertibili
105
- const mergeableSelectedCount = (() => {
106
- if (showTMRelationViewer) {
107
- return selectedItemsRelationViewer.filter(i => i.isDcmt && (isPdfExt(i.fileExt) || isConvertibleToPdfExt(i.fileExt))).length;
108
- }
109
- return selectedDcmtInfos.filter(d => isPdfExt(d.FILEEXT) || isConvertibleToPdfExt(d.FILEEXT)).length;
110
- })();
111
- const hasEnoughPdfForMerge = mergeableSelectedCount >= MIN_PDF_FOR_MERGE;
85
+ const { convertibleSelectedItems, hasConvertibleSelected, nonPdfSelectedItems, hasNonPdfSelected, metadataOnlySelectedItems, hasMetadataOnlySelected, mergeableSelectedCount, hasEnoughPdfForMerge, } = getMergePdfSelectionStats(selectedDcmtInfos, selectedItemsFull, selectedItemsRelationViewer, showTMRelationViewer);
112
86
  const isFormValid = () => pdfValidationItems.length === 0;
113
87
  // ---- Recupero dei file PDF da unire (condiviso tra Esegui e Anteprima) ----
114
88
  const collectPdfFilesToMerge = async () => {
@@ -133,17 +107,81 @@ const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationView
133
107
  });
134
108
  return null;
135
109
  }
110
+ // Mappa per lookup veloce di FILECOUNT e FILEEXT da selectedItemsFull
111
+ const fileInfoMap = new Map(selectedItemsFull.map(d => [`${d.TID}_${d.DID}`, { fileExt: d.FILEEXT, fileCount: d.FILECOUNT }]));
112
+ // Mappa per i risultati: mantiene l'ordine originale
113
+ const pdfFilesMap = new Map();
114
+ // Identifica documenti di soli metadati e documenti con file
115
+ const metadataOnlyDcmts = [];
116
+ const dcmtsWithFile = [];
117
+ for (const dcmt of pdfDcmtInfosToDownload) {
118
+ const key = `${dcmt.TID}_${dcmt.DID}`;
119
+ const info = fileInfoMap.get(key);
120
+ if (isMetadataOnlyDcmt(info?.fileExt, info?.fileCount)) {
121
+ metadataOnlyDcmts.push(dcmt);
122
+ }
123
+ else {
124
+ dcmtsWithFile.push(dcmt);
125
+ }
126
+ }
127
+ // Per i documenti di soli metadati, genera PDF con createMetadataPdfFromDocument
128
+ // Raccoglie gli errori come downloadDcmtsAsync
129
+ const metadataConversionResults = [];
130
+ if (metadataOnlyDcmts.length > 0) {
131
+ setIsMergingPdf(true);
132
+ setMergeProgressMax(metadataOnlyDcmts.length);
133
+ setMergeProgressValue(0);
134
+ setMergeProgressText('Creazione PDF metadati...');
135
+ for (let i = 0; i < metadataOnlyDcmts.length; i++) {
136
+ const dcmt = metadataOnlyDcmts[i];
137
+ try {
138
+ setMergeProgressValue(i);
139
+ setMergeProgressText(`Creazione PDF metadati... (${i + 1}/${metadataOnlyDcmts.length})`);
140
+ const conversionResult = await createMetadataPdfFromDocument(dcmt);
141
+ if (conversionResult) {
142
+ pdfFilesMap.set(`${dcmt.TID}_${dcmt.DID}`, conversionResult.file);
143
+ metadataConversionResults.push({ rowIndex: i, id1: dcmt.TID, id2: dcmt.DID, resultType: ResultTypes.SUCCESS });
144
+ }
145
+ else {
146
+ // createMetadataPdfFromDocument ha restituito null (errore interno loggato)
147
+ metadataConversionResults.push({ rowIndex: i, id1: dcmt.TID, id2: dcmt.DID, resultType: ResultTypes.ERROR, description: 'Creazione PDF metadati fallita' });
148
+ }
149
+ }
150
+ catch (error) {
151
+ console.error(`Errore creazione PDF metadati per TID=${dcmt.TID}, DID=${dcmt.DID}`, error);
152
+ metadataConversionResults.push({ rowIndex: i, id1: dcmt.TID, id2: dcmt.DID, resultType: ResultTypes.ERROR, description: getExceptionMessage(error) });
153
+ }
154
+ }
155
+ setMergeProgressValue(metadataOnlyDcmts.length);
156
+ setIsMergingPdf(false);
157
+ // Mostra riepilogo errori se presenti (come downloadDcmtsAsync)
158
+ const hasErrors = metadataConversionResults.some(r => r.resultType === ResultTypes.ERROR);
159
+ if (hasErrors) {
160
+ TMResultManager.show(metadataConversionResults, 'Conversione PDF metadati', 'TID', 'DID', undefined, undefined, false);
161
+ }
162
+ }
163
+ // Per i documenti con file, usa il download normale
164
+ if (dcmtsWithFile.length > 0) {
165
+ const rfo = new RetrieveFileOptions();
166
+ rfo.retrieveReason = DcmtOpers.None;
167
+ rfo.generalRetrieveFormat = GeneralRetrieveFormats.OriginalUnsigned;
168
+ rfo.cvtFormat = FileFormats.PDF;
169
+ rfo.invoiceRetrieveFormat = SDKUI_Globals.userSettings?.searchSettings.invoiceRetrieveFormat;
170
+ rfo.orderRetrieveFormat = SDKUI_Globals.userSettings?.searchSettings.orderRetrieveFormat;
171
+ const collectFileForMerge = async (file, dcmtInfo) => {
172
+ pdfFilesMap.set(`${dcmtInfo.TID}_${dcmtInfo.DID}`, file);
173
+ };
174
+ await downloadDcmtsAsync({ inputDcmts: dcmtsWithFile, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForMerge, skipConfirmation: true, retrieveOptions: rfo, useCache: false });
175
+ }
176
+ // Ricostruisci l'array pdfFiles nell'ordine originale di selezione
136
177
  const pdfFiles = [];
137
- const rfo = new RetrieveFileOptions();
138
- rfo.retrieveReason = DcmtOpers.None;
139
- rfo.generalRetrieveFormat = GeneralRetrieveFormats.OriginalUnsigned;
140
- rfo.cvtFormat = FileFormats.PDF;
141
- rfo.invoiceRetrieveFormat = SDKUI_Globals.userSettings?.searchSettings.invoiceRetrieveFormat;
142
- rfo.orderRetrieveFormat = SDKUI_Globals.userSettings?.searchSettings.orderRetrieveFormat;
143
- const collectFileForMerge = async (file, _dcmtInfo) => {
144
- pdfFiles.push(file);
145
- };
146
- await downloadDcmtsAsync({ inputDcmts: pdfDcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForMerge, skipConfirmation: true, retrieveOptions: rfo, useCache: false });
178
+ for (const dcmt of pdfDcmtInfosToDownload) {
179
+ const key = `${dcmt.TID}_${dcmt.DID}`;
180
+ const file = pdfFilesMap.get(key);
181
+ if (file) {
182
+ pdfFiles.push(file);
183
+ }
184
+ }
147
185
  if (pdfFiles.length === 0) {
148
186
  TMMessageBoxManager.show({ message: SDKUI_Localizator.NoFilesAvailableForMerge, buttons: [ButtonNames.OK] });
149
187
  return null;
@@ -244,7 +282,7 @@ const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationView
244
282
  width: 'max-content',
245
283
  backgroundColor: TMColors.default_background,
246
284
  zIndex: 1,
247
- }, children: SDKUI_Localizator.Path }), _jsxs("div", { style: { border: `1px solid ${TMColors.border_normal}`, borderRadius: '5px', padding: '4px 10px 4px 13px', display: 'flex', alignItems: 'center', gap: '8px', backgroundColor: '#fafafa' }, children: [_jsx(IconFolderOpen, {}), _jsx("span", { style: { fontSize: '0.9rem', color: '#333' }, children: "Download" })] }), _jsx("span", { style: { fontSize: '0.8rem', color: '#888', fontStyle: 'italic', marginTop: '4px', display: 'block' }, children: SDKUI_Localizator.BrowserDoesNotSupportFolderSelection })] }) })), _jsx("div", { style: { flex: '1.5 1 420px', minWidth: '350px', width: '100%' }, children: _jsx(TMTextBox, { label: SDKUI_Localizator.PdfFileName, value: pdfFileName, validationItems: pdfValidationItems, autoComplete: "one-time-code", onValueChanged: (e) => setPdfFileName(e.target.value) }) })] }), (hasConvertibleSelected || !hasEnoughPdfForMerge || hasNonPdfSelected) && (_jsxs("div", { style: {
285
+ }, children: SDKUI_Localizator.Path }), _jsxs("div", { style: { border: `1px solid ${TMColors.border_normal}`, borderRadius: '5px', padding: '4px 10px 4px 13px', display: 'flex', alignItems: 'center', gap: '8px', backgroundColor: '#fafafa' }, children: [_jsx(IconFolderOpen, {}), _jsx("span", { style: { fontSize: '0.9rem', color: '#333' }, children: "Download" })] }), _jsx("span", { style: { fontSize: '0.8rem', color: '#888', fontStyle: 'italic', marginTop: '4px', display: 'block' }, children: SDKUI_Localizator.BrowserDoesNotSupportFolderSelection })] }) })), _jsx("div", { style: { flex: '1.5 1 420px', minWidth: '350px', width: '100%' }, children: _jsx(TMTextBox, { label: SDKUI_Localizator.PdfFileName, value: pdfFileName, validationItems: pdfValidationItems, autoComplete: "one-time-code", onValueChanged: (e) => setPdfFileName(e.target.value) }) })] }), (hasConvertibleSelected || hasMetadataOnlySelected || !hasEnoughPdfForMerge || hasNonPdfSelected) && (_jsxs("div", { style: {
248
286
  display: 'flex',
249
287
  flexDirection: 'column',
250
288
  gap: '6px',
@@ -264,14 +302,19 @@ const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationView
264
302
  borderBottom: '1px solid #e0e0e0',
265
303
  paddingBottom: '6px',
266
304
  marginBottom: '2px'
267
- }, children: SDKUI_Localizator.NotesAndWarnings }), _jsxs("ul", { style: { margin: 0, paddingLeft: '18px', display: 'flex', flexDirection: 'column', gap: '4px' }, children: [hasConvertibleSelected && (_jsx("li", { style: { color: '#00527a' }, children: (() => {
268
- const extSet = Array.from(new Set(convertibleSelectedItems.map(i => (i.ext ?? '').toString().trim().toLowerCase().replace(/^\./, '')).filter(e => e.length > 0)));
305
+ }, children: SDKUI_Localizator.NotesAndWarnings }), _jsxs("ul", { style: { margin: 0, paddingLeft: '18px', display: 'flex', flexDirection: 'column', gap: '4px' }, children: [(hasConvertibleSelected || hasMetadataOnlySelected) && (_jsx("li", { style: { color: '#00527a' }, children: (() => {
306
+ // Combina convertibili + metadataOnly
307
+ const allConvertible = [...convertibleSelectedItems, ...metadataOnlySelectedItems];
308
+ const extSet = Array.from(new Set([
309
+ ...convertibleSelectedItems.map(i => (i.ext ?? '').toString().trim().toLowerCase().replace(/^\./, '')).filter(e => e.length > 0),
310
+ ...(hasMetadataOnlySelected ? ['metadati'] : [])
311
+ ]));
269
312
  const extLabel = extSet.length > 0
270
313
  ? ` (${extSet.map(e => '.' + e).join(', ')})`
271
314
  : '';
272
- return convertibleSelectedItems.length === 1
315
+ return allConvertible.length === 1
273
316
  ? SDKUI_Localizator.DocumentWillBeConvertedDuringMerge.replaceParams(extLabel)
274
- : SDKUI_Localizator.DocumentsWillBeConvertedDuringMerge.replaceParams(convertibleSelectedItems.length.toString(), extLabel);
317
+ : SDKUI_Localizator.DocumentsWillBeConvertedDuringMerge.replaceParams(allConvertible.length.toString(), extLabel);
275
318
  })() })), !hasEnoughPdfForMerge && (_jsx("li", { style: { color: '#7a5d00' }, children: mergeableSelectedCount === 0
276
319
  ? SDKUI_Localizator.NoPdfOrConvertibleFilesSelected_Param.replaceParams(MIN_PDF_FOR_MERGE.toString())
277
320
  : SDKUI_Localizator.OnlyOnePdfOrConvertibleFileSelected.replaceParams(MIN_PDF_FOR_MERGE.toString()) })), hasNonPdfSelected && (_jsx("li", { style: { color: '#7a5d00' }, children: (() => {
@@ -2,6 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataFormats, SystemMIDs, MetadataDataDomains, RelationCacheService, RelationTypes, UserListCacheService } from "@topconsultnpm/sdk-ts";
4
4
  import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus, IconChevronDown, IconChevronRight, SDKUI_Localizator } from '../../../helper';
5
+ import ShowAlert from '../../base/TMAlert';
6
+ import TMToppyMessage from '../../../helper/TMToppyMessage';
5
7
  import { TMColors } from '../../../utils/theme';
6
8
  import { StyledDivHorizontal, StyledBadge, StyledToolbarForm } from '../../base/Styled';
7
9
  import TMTreeView from '../../base/TMTreeView';
@@ -158,6 +160,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
158
160
  const lastLoadedInputRef = React.useRef('');
159
161
  // State to track loaded input key - triggers re-render for focus selection
160
162
  const [loadedInputKey, setLoadedInputKey] = React.useState('');
163
+ // State to track how many GetMetadataAsync calls failed
164
+ const [metadataErrorCount, setMetadataErrorCount] = React.useState(0);
165
+ // State to track total master documents found (used when isForMaster is true)
166
+ const [totalMasterDocuments, setTotalMasterDocuments] = React.useState(0);
161
167
  // Ref to track if user has manually expanded/collapsed static items
162
168
  const userInteractedWithStaticItemsRef = React.useRef(false);
163
169
  // Ref to track the last inputKey for which we set the focused item
@@ -341,6 +347,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
341
347
  isExpandible: canExpand, // Can this doc be expanded?
342
348
  values: row,
343
349
  searchResult: [searchResult],
350
+ fileExt: row?.FILEEXT?.value,
344
351
  // Leave items and itemsCount undefined so TMTreeView shows expand arrow based on isExpandible
345
352
  // Children will be loaded lazily by calculateItemsForNode when expanded
346
353
  expanded: false,
@@ -497,6 +504,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
497
504
  }
498
505
  const tree = [];
499
506
  let processedCount = 0;
507
+ let errorCount = 0;
500
508
  for (const dcmt of inputDcmts) {
501
509
  // Check for abort
502
510
  if (abortController.signal.aborted) {
@@ -509,7 +517,24 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
509
517
  if (!searchEngine)
510
518
  continue;
511
519
  // Load document metadata
512
- const result = await searchEngine.GetMetadataAsync(dcmt.TID, dcmt.DID, false);
520
+ let result;
521
+ try {
522
+ result = await searchEngine.GetMetadataAsync(dcmt.TID, dcmt.DID, false);
523
+ }
524
+ catch (metadataError) {
525
+ console.warn('[TMRelationViewer] GetMetadataAsync failed for TID:', dcmt.TID, 'DID:', dcmt.DID, metadataError);
526
+ errorCount++;
527
+ ShowAlert({
528
+ title: SDKUI_Localizator.Warning,
529
+ message: `Il documento (TID: ${dcmt.TID}, DID: ${dcmt.DID}) non esiste più o non è accessibile.`,
530
+ mode: 'warning',
531
+ duration: 5000
532
+ });
533
+ // Skip this document and continue with the next one
534
+ processedCount++;
535
+ setWaitPanelValuePrimary(processedCount);
536
+ continue;
537
+ }
513
538
  const dtd = await DcmtTypeListCacheService.GetAsync(dcmt.TID);
514
539
  // Parse document values
515
540
  const docValues = await searchResultToDataSource(result);
@@ -714,6 +739,24 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
714
739
  if (hasNoRelations && onNoRelationsFound) {
715
740
  onNoRelationsFound();
716
741
  }
742
+ // Update metadata error count state
743
+ setMetadataErrorCount(errorCount);
744
+ // Count total master documents (only relevant when isForMaster is true)
745
+ if (isForMaster) {
746
+ const countMasterDocuments = (items) => {
747
+ let count = 0;
748
+ for (const item of items) {
749
+ if (item.isDcmt && !item.isZero) {
750
+ count++;
751
+ }
752
+ if (item.items && Array.isArray(item.items)) {
753
+ count += countMasterDocuments(item.items);
754
+ }
755
+ }
756
+ return count;
757
+ };
758
+ setTotalMasterDocuments(countMasterDocuments(tree));
759
+ }
717
760
  // FIRST setup initial expansion state (root container expanded, first document expanded with focus, first correlation folder expanded)
718
761
  // This must run BEFORE updateHiddenProperty so that infoMessage logic sees expanded=true
719
762
  const expandedTree = setupInitialTreeExpansion(tree);
@@ -1220,10 +1263,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1220
1263
  return _jsx("div", { style: { padding: '20px', textAlign: 'center' }, children: _jsx(TMWaitPanel, { title: 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: waitPanelTextPrimary, valuePrimary: waitPanelValuePrimary, maxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, onAbortClick: (abortController) => { setTimeout(() => { abortController?.abort(); }, 1000); } }) });
1221
1264
  }
1222
1265
  if (mergedTreeData.length === 0) {
1266
+ // Se NON isForMaster e tutti i GetMetadataAsync sono falliti
1267
+ if (!isForMaster && metadataErrorCount === inputDcmts.length) {
1268
+ return _jsx(TMToppyMessage, { message: SDKUI_Localizator.DocumentsNotAvailableOrNoCorrelations });
1269
+ }
1223
1270
  // If parent handles no-relations via callback, render nothing to avoid UI flash
1224
1271
  if (onNoRelationsFound)
1225
1272
  return null;
1226
- return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: "Nessuna relazione disponibile." });
1273
+ return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: SDKUI_Localizator.NoRelationsAvailable });
1274
+ }
1275
+ // Se isForMaster e non ci sono master documents disponibili
1276
+ if (isForMaster && totalMasterDocuments === 0) {
1277
+ return _jsx(TMToppyMessage, { message: SDKUI_Localizator.NoMasterDocumentsAvailable });
1227
1278
  }
1228
1279
  return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, width: '100%' }, children: [showExpandAllButton && (_jsx(StyledToolbarForm, { style: { flexShrink: 0 }, children: _jsxs("button", { type: "button", onClick: handleToggleExpandAll, title: allExpanded ? SDKUI_Localizator.CollapseAll : SDKUI_Localizator.ExpandAll, style: {
1229
1280
  display: 'inline-flex',
@@ -1236,9 +1287,9 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1236
1287
  border: `1px solid ${TMColors.border_normal}`,
1237
1288
  borderRadius: '4px',
1238
1289
  cursor: 'pointer',
1239
- lineHeight: 1,
1240
- whiteSpace: 'nowrap'
1241
- }, children: [allExpanded ? _jsx(IconChevronRight, { fontSize: 14 }) : _jsx(IconChevronDown, { fontSize: 14 }), _jsx("span", { children: allExpanded ? SDKUI_Localizator.CollapseAll : SDKUI_Localizator.ExpandAll })] }) })), _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: onItemContextMenu, shouldDelayFocusOnEvent: (node, e) => {
1290
+ whiteSpace: 'nowrap',
1291
+ outline: 'none'
1292
+ }, children: [_jsx("span", { style: { display: 'flex', alignItems: 'center' }, children: allExpanded ? _jsx(IconChevronRight, { fontSize: 14 }) : _jsx(IconChevronDown, { fontSize: 14 }) }), _jsx("span", { children: allExpanded ? SDKUI_Localizator.CollapseAll : SDKUI_Localizator.ExpandAll })] }) })), _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: onItemContextMenu, shouldDelayFocusOnEvent: (node, e) => {
1242
1293
  // Ritarda il focus quando si clicca sull'icona del documento
1243
1294
  // per permettere al doppio click di funzionare
1244
1295
  const target = e.target;
@@ -0,0 +1,52 @@
1
+ import { DcmtInfo } from '../../../ts';
2
+ import { IRelatedDcmt } from './TMMasterDetailDcmts';
3
+ /** Tipo per gli elementi con chiave e estensione */
4
+ export interface ISelectedItemInfo {
5
+ key: string;
6
+ ext: string | undefined;
7
+ }
8
+ /** Risultato delle statistiche di selezione per il merge PDF */
9
+ export interface IMergePdfSelectionStats {
10
+ /** Elementi con estensioni convertibili in PDF (non nativamente PDF) */
11
+ convertibleSelectedItems: Array<ISelectedItemInfo>;
12
+ /** True se ci sono elementi convertibili selezionati */
13
+ hasConvertibleSelected: boolean;
14
+ /** Elementi che NON sono PDF e NON sono convertibili */
15
+ nonPdfSelectedItems: Array<ISelectedItemInfo>;
16
+ /** True se ci sono elementi non-PDF selezionati */
17
+ hasNonPdfSelected: boolean;
18
+ /** Documenti di soli metadati (senza file allegato) */
19
+ metadataOnlySelectedItems: Array<ISelectedItemInfo>;
20
+ /** True se ci sono documenti di soli metadati selezionati */
21
+ hasMetadataOnlySelected: boolean;
22
+ /** Numero totale di file unibili (PDF nativi + convertibili) */
23
+ mergeableSelectedCount: number;
24
+ /** True se ci sono abbastanza file per il merge (>= MIN_PDF_FOR_MERGE) */
25
+ hasEnoughPdfForMerge: boolean;
26
+ }
27
+ /** Documento di soli metadati: FILEEXT null/undefined oppure FILECOUNT 0 (verrà convertito frontend) */
28
+ export declare const isMetadataOnlyDcmt: (fileExt: string | null | undefined, fileCount: string | number | null | undefined) => boolean;
29
+ /**
30
+ * Calcola tutte le statistiche di selezione necessarie per il merge PDF.
31
+ * Restituisce info su file convertibili, non-PDF e conteggio file unibili.
32
+ *
33
+ * @param selectedDcmtInfos - Lista dei DcmtInfo selezionati (usata se showTMRelationViewer è false)
34
+ * @param selectedItemsFull - Lista completa con FILEEXT e FILECOUNT per check documenti di soli metadati
35
+ * @param selectedItemsRelationViewer - Lista degli IRelatedDcmt selezionati nel RelationViewer
36
+ * @param showTMRelationViewer - Se true, usa selectedItemsRelationViewer, altrimenti selectedDcmtInfos
37
+ */
38
+ export declare const getMergePdfSelectionStats: (selectedDcmtInfos: Array<DcmtInfo>, selectedItemsFull: Array<any>, selectedItemsRelationViewer: Array<IRelatedDcmt>, showTMRelationViewer: boolean) => IMergePdfSelectionStats;
39
+ export interface IMetadataKeyValue {
40
+ label: string;
41
+ value: string;
42
+ }
43
+ export interface IMetadataPdfResult {
44
+ file: File;
45
+ blob: Blob;
46
+ bytes: Uint8Array;
47
+ }
48
+ /** Crea PDF con metadati documento (header + lista label=valore). Supporta wrapping e paginazione.
49
+ * @param dcmtInfo - Informazioni del documento
50
+ * @param showSystemMetadata - Se true, include i metadati di sistema (mid < 100). Default: false
51
+ */
52
+ export declare const createMetadataPdfFromDocument: (dcmtInfo: DcmtInfo, showSystemMetadata?: boolean) => Promise<IMetadataPdfResult | null>;