@topconsultnpm/sdkui-react 6.21.0-dev2.9 → 6.21.0-dev3.2

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 (80) hide show
  1. package/lib/components/base/TMAccordionNew.js +1 -0
  2. package/lib/components/base/TMAreaManager.js +19 -3
  3. package/lib/components/base/TMDataGrid.js +2 -2
  4. package/lib/components/base/TMModal.d.ts +1 -0
  5. package/lib/components/base/TMModal.js +2 -2
  6. package/lib/components/base/TMPanel.d.ts +7 -4
  7. package/lib/components/base/TMPanel.js +58 -26
  8. package/lib/components/base/TMTreeView.js +12 -2
  9. package/lib/components/base/TMWaitPanel.d.ts +3 -1
  10. package/lib/components/base/TMWaitPanel.js +14 -9
  11. package/lib/components/choosers/TMDistinctValues.js +35 -21
  12. package/lib/components/choosers/TMUserChooser.d.ts +4 -0
  13. package/lib/components/choosers/TMUserChooser.js +7 -5
  14. package/lib/components/editors/TMFormulaEditor.d.ts +2 -0
  15. package/lib/components/editors/TMFormulaEditor.js +75 -21
  16. package/lib/components/editors/TMMetadataValues.js +2 -1
  17. package/lib/components/editors/TMRadioButton.js +7 -5
  18. package/lib/components/editors/TMTextBox.d.ts +2 -0
  19. package/lib/components/editors/TMTextBox.js +3 -3
  20. package/lib/components/features/archive/TMArchive.js +1 -1
  21. package/lib/components/features/documents/TMCopyToFolderForm.d.ts +24 -0
  22. package/lib/components/features/documents/TMCopyToFolderForm.js +401 -0
  23. package/lib/components/features/documents/TMDcmtForm.d.ts +1 -0
  24. package/lib/components/features/documents/TMDcmtForm.js +126 -38
  25. package/lib/components/features/documents/TMDcmtPreview.d.ts +1 -0
  26. package/lib/components/features/documents/TMDcmtPreview.js +2 -2
  27. package/lib/components/features/documents/TMDcmtTasks.d.ts +1 -0
  28. package/lib/components/features/documents/TMDcmtTasks.js +2 -2
  29. package/lib/components/features/documents/TMDownloadRelationViewerSection.d.ts +23 -0
  30. package/lib/components/features/documents/TMDownloadRelationViewerSection.js +173 -0
  31. package/lib/components/features/documents/TMFileUploader.js +1 -1
  32. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  33. package/lib/components/features/documents/TMMasterDetailDcmts.js +28 -9
  34. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +24 -0
  35. package/lib/components/features/documents/TMMergeToPdfForm.js +309 -0
  36. package/lib/components/features/documents/TMRelationViewer.d.ts +13 -0
  37. package/lib/components/features/documents/TMRelationViewer.js +75 -6
  38. package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +71 -0
  39. package/lib/components/features/documents/copyAndMergeDcmtsShared.js +304 -0
  40. package/lib/components/features/search/SignatureParamsManager.d.ts +70 -0
  41. package/lib/components/features/search/SignatureParamsManager.js +145 -0
  42. package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
  43. package/lib/components/features/search/TMSavedQuerySelector.js +3 -2
  44. package/lib/components/features/search/TMSearch.d.ts +4 -1
  45. package/lib/components/features/search/TMSearch.js +16 -10
  46. package/lib/components/features/search/TMSearchQueryPanel.js +1 -1
  47. package/lib/components/features/search/TMSearchResult.d.ts +2 -0
  48. package/lib/components/features/search/TMSearchResult.js +117 -22
  49. package/lib/components/features/workflow/diagram/queryDescriptorParser.js +3 -6
  50. package/lib/components/forms/Login/TMLoginForm.d.ts +9 -0
  51. package/lib/components/forms/Login/TMLoginForm.js +61 -0
  52. package/lib/components/forms/TMResultDialog.d.ts +1 -1
  53. package/lib/components/forms/TMResultDialog.js +4 -2
  54. package/lib/components/index.d.ts +1 -0
  55. package/lib/components/index.js +1 -0
  56. package/lib/components/pages/TMPage.js +3 -1
  57. package/lib/components/query/TMQueryEditor.js +1 -1
  58. package/lib/components/viewers/TMTidViewer.js +1 -1
  59. package/lib/helper/MergePdfManager.d.ts +45 -0
  60. package/lib/helper/MergePdfManager.js +145 -0
  61. package/lib/helper/SDKUI_Globals.d.ts +15 -0
  62. package/lib/helper/SDKUI_Globals.js +15 -1
  63. package/lib/helper/SDKUI_Localizator.d.ts +107 -2
  64. package/lib/helper/SDKUI_Localizator.js +1070 -12
  65. package/lib/helper/TMPdfViewer.js +25 -24
  66. package/lib/helper/TMUtils.d.ts +1 -0
  67. package/lib/helper/TMUtils.js +17 -0
  68. package/lib/helper/ZipManager.d.ts +56 -0
  69. package/lib/helper/ZipManager.js +127 -0
  70. package/lib/helper/index.d.ts +1 -0
  71. package/lib/helper/index.js +1 -0
  72. package/lib/hooks/useDataUserIdItem.js +6 -4
  73. package/lib/hooks/useDcmtOperations.d.ts +9 -2
  74. package/lib/hooks/useDcmtOperations.js +77 -34
  75. package/lib/hooks/useDocumentOperations.d.ts +3 -0
  76. package/lib/hooks/useDocumentOperations.js +233 -24
  77. package/lib/hooks/useForm.js +5 -2
  78. package/lib/hooks/useResizeObserver.d.ts +1 -1
  79. package/lib/hooks/useResizeObserver.js +16 -15
  80. package/package.json +4 -2
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
- import { DcmtTypeListCacheService, LayoutModes, SDK_Globals, SDK_Localizator } from '@topconsultnpm/sdk-ts';
3
+ import { DcmtTypeListCacheService, LayoutModes, ObjectClasses, SDK_Globals, SDK_Localizator } from '@topconsultnpm/sdk-ts';
4
4
  import TMRelationViewer from './TMRelationViewer';
5
5
  import TMContextMenu from '../../NewComponents/ContextMenu/TMContextMenu';
6
6
  import { IconMultipleSelection, IconCheckFile, IconDetailDcmts, SDKUI_Localizator, IconMenuVertical, IconDataList, IconPreview, IconSearchCheck, IconBoard, IconDcmtTypeSys, IconShow, getMoreInfoTasksForDocument, isApprovalWorkflowView, searchResultToMetadataValues, IconRefresh } from '../../../helper';
@@ -16,7 +16,7 @@ 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
+ 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, }) => {
20
20
  const floatingBarContainerRef = useRef(null);
21
21
  const [focusedItem, setFocusedItem] = useState();
22
22
  const [selectedItems, setSelectedItems] = useState([]);
@@ -117,7 +117,26 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
117
117
  createTaskFromDocumentOrWorkItem();
118
118
  }
119
119
  };
120
- const { operationItems, renderFloatingBar, renderDcmtOperations, features } = useDocumentOperations({
120
+ const handleNavigateToReference = useCallback((ref) => {
121
+ switch (ref.objClass) {
122
+ case ObjectClasses.Dossier:
123
+ if (handleNavigateToDossiers && ref.objID)
124
+ handleNavigateToDossiers(ref.objID);
125
+ else
126
+ console.log("Dossier reference missing objID");
127
+ break;
128
+ case ObjectClasses.WorkingGroup:
129
+ if (handleNavigateToWGs && ref.objID)
130
+ handleNavigateToWGs(ref.objID);
131
+ else
132
+ console.log("Working Group reference missing objID");
133
+ break;
134
+ // Handle other object types as needed
135
+ default:
136
+ console.warn(`Unhandled object type: ${ref.objClass}`);
137
+ }
138
+ }, [handleNavigateToDossiers, handleNavigateToWGs]);
139
+ const { operationItems, renderFloatingBar, renderDcmtOperations, features, } = useDocumentOperations({
121
140
  context: SearchResultContext.MASTER_DETAIL,
122
141
  documentData: {
123
142
  dtd: dtdFocused,
@@ -178,7 +197,7 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
178
197
  onNavigateHandler, */
179
198
  handleNavigateToWGs,
180
199
  handleNavigateToDossiers,
181
- /* onReferenceClick, */
200
+ onReferenceClick: handleNavigateToReference,
182
201
  // Document forms/operations
183
202
  /* openAddDocumentForm,
184
203
  openCommentFormCallback,
@@ -285,7 +304,7 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
285
304
  position: contextMenuPosition,
286
305
  onClose: () => setContextMenuVisible(false)
287
306
  } })] }) }), [inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, allowMultipleSelection, focusedItem, selectedItems, handleFocusedItemChanged, handleSelectedItemsChanged, handleNoRelationsFound, onItemContextMenu, contextMenuVisible, contextMenuPosition, refreshKey, focusedItemFormData]);
288
- const tmFormOrResult = useMemo(() => _jsx(TMFormOrResultWrapper, { refreshKey: refreshKeyFormOrResult, deviceType: deviceType, focusedItem: focusedItem, onTaskCreateRequest: onTaskCreateRequest, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onRefreshSearchResults: onRefreshAllPanels }), [focusedItem, deviceType, allTasks, handleNavigateToWGs, handleNavigateToDossiers, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshAfterAddDcmtToFavs, refreshKeyFormOrResult]);
307
+ const tmFormOrResult = useMemo(() => _jsx(TMFormOrResultWrapper, { refreshKey: refreshKeyFormOrResult, deviceType: deviceType, focusedItem: focusedItem, onTaskCreateRequest: onTaskCreateRequest, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, handleNavigateToReference: handleNavigateToReference, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onRefreshSearchResults: onRefreshAllPanels }), [focusedItem, deviceType, allTasks, handleNavigateToWGs, handleNavigateToDossiers, handleNavigateToReference, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshAfterAddDcmtToFavs, refreshKeyFormOrResult]);
289
308
  const initialPanelDimensions = {
290
309
  'tmTreeView': { width: '50%', height: '100%' },
291
310
  'tmFormOrResult': { width: '50%', height: '100%' },
@@ -402,13 +421,13 @@ const TMRelationViewerWrapper = ({ refreshKey, inputDcmts, isForMaster, showCurr
402
421
  onItemContextMenu?.(item, e);
403
422
  }, 100);
404
423
  }, [onItemContextMenu, handleFocusedItemChanged]);
405
- 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, onNoRelationsFound: onNoRelationsFound, onItemContextMenu: onContextMenu, focusedItemFormData: focusedItemFormData }, refreshKey));
424
+ 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));
406
425
  };
407
- const TMFormOrResultWrapper = ({ refreshKey, deviceType, focusedItem, onTaskCreateRequest, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshSearchAsyncDatagrid, onRefreshSearchResults }) => {
426
+ const TMFormOrResultWrapper = ({ refreshKey, deviceType, focusedItem, onTaskCreateRequest, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshSearchAsyncDatagrid, onRefreshSearchResults, handleNavigateToReference }) => {
408
427
  const { setPanelVisibilityById } = useTMPanelManagerContext();
409
428
  return (_jsx(_Fragment, { children: focusedItem?.isDcmt ?
410
- _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, moreInfoTasks: getMoreInfoTasksForDocument(allTasks, focusedItem?.tid, focusedItem?.did), openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, datagridUtility: {
429
+ _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: {
411
430
  onRefreshSearchAsyncDatagrid,
412
431
  } }, refreshKey) :
413
- _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 }, 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) }));
414
433
  };
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { DcmtInfo } from '../../../ts';
3
+ import { HomeBlogPost, TaskDescriptor } from '@topconsultnpm/sdk-ts';
4
+ import { TMCopyToFolderMode } from '../../../hooks/useDocumentOperations';
5
+ interface ITMMergeToPdfFormProps {
6
+ mode: TMCopyToFolderMode;
7
+ selectedDcmtInfos: Array<DcmtInfo>;
8
+ onClose: () => void;
9
+ showTMRelationViewer: boolean;
10
+ allTasks?: Array<TaskDescriptor>;
11
+ getAllTasks?: () => Promise<void>;
12
+ deleteTaskByIdsCallback?: (deletedTaskIds: Array<number>) => Promise<void>;
13
+ addTaskCallback?: (task: TaskDescriptor) => Promise<void>;
14
+ editTaskCallback?: (task: TaskDescriptor) => Promise<void>;
15
+ handleNavigateToWGs?: (value: HomeBlogPost | number) => Promise<void>;
16
+ handleNavigateToDossiers?: (value: HomeBlogPost | number) => Promise<void>;
17
+ }
18
+ /**
19
+ * Form per l'unione di più documenti PDF in un singolo file.
20
+ * Condivide TMDownloadRelationViewerSection e gli helper in copyAndMergeDcmtsShared
21
+ * con TMCopyToFolderForm.
22
+ */
23
+ declare const TMMergeToPdfForm: React.FC<ITMMergeToPdfFormProps>;
24
+ export default TMMergeToPdfForm;
@@ -0,0 +1,309 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from 'react';
3
+ import { DownloadTypes } from '../../../ts';
4
+ import { calcResponsiveSizes, IconFolderOpen, IconPlay, IconUndo, isConvertibleToPdfExt, SDKUI_Globals, SDKUI_Localizator, } from '../../../helper';
5
+ import { DcmtOpers, FileFormats, GeneralRetrieveFormats, ResultTypes, RetrieveFileOptions, ValidationItem, } from '@topconsultnpm/sdk-ts';
6
+ import TMModal from '../../base/TMModal';
7
+ import { useDeviceType } from '../../base/TMDeviceProvider';
8
+ import TMTextBox from '../../editors/TMTextBox';
9
+ import { ButtonNames, TMMessageBoxManager } from '../../base/TMPopUp';
10
+ import { useDcmtOperations } from '../../../hooks/useDcmtOperations';
11
+ import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
12
+ import { TMSplitterLayout } from '../../base/TMLayout';
13
+ import { TMColors } from '../../../utils/theme';
14
+ import TMDownloadRelationViewerSection from './TMDownloadRelationViewerSection';
15
+ import { getDcmtInfosToDownload, getFloatingLabelStyle, isDirectoryPickerSupported, isPdfExt, MIN_PDF_FOR_MERGE, } from './copyAndMergeDcmtsShared';
16
+ import ShowAlert from '../../base/TMAlert';
17
+ import TMTooltip from '../../base/TMTooltip';
18
+ import MergePdfManager from '../../../helper/MergePdfManager';
19
+ /**
20
+ * Form per l'unione di più documenti PDF in un singolo file.
21
+ * Condivide TMDownloadRelationViewerSection e gli helper in copyAndMergeDcmtsShared
22
+ * con TMCopyToFolderForm.
23
+ */
24
+ const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationViewer, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers }) => {
25
+ const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, } = useDcmtOperations();
26
+ const deviceType = useDeviceType();
27
+ const [pdfFileName, setPdfFileName] = useState('merged.pdf');
28
+ const [destinationFolder, setDestinationFolder] = useState('Download');
29
+ const [selectedItemsRelationViewer, setSelectedItemsRelationViewer] = useState([]);
30
+ // ---- Stato per il merge PDF (progress bar) ----
31
+ const [isMergingPdf, setIsMergingPdf] = useState(false);
32
+ const [mergeProgressText, setMergeProgressText] = useState('');
33
+ const [mergeProgressValue, setMergeProgressValue] = useState(0);
34
+ const [mergeProgressMax, setMergeProgressMax] = useState(0);
35
+ // ---- Ref per la selezione cartella (File System Access API) ----
36
+ const folderHandleRef = useRef(null);
37
+ const skipSelectFolderRef = useRef(false);
38
+ const isPickerActiveRef = useRef(false);
39
+ const isUsingDefaultDownloads = destinationFolder === 'Download' && !folderHandleRef.current;
40
+ // ---- Handler dei campi cartella ----
41
+ const handleFolderValueChange = (e) => {
42
+ setDestinationFolder(e.target.value);
43
+ folderHandleRef.current = null;
44
+ };
45
+ const handleSelectFolder = async () => {
46
+ if (skipSelectFolderRef.current) {
47
+ skipSelectFolderRef.current = false;
48
+ return;
49
+ }
50
+ if (isPickerActiveRef.current)
51
+ return;
52
+ try {
53
+ isPickerActiveRef.current = true;
54
+ const dirHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
55
+ folderHandleRef.current = dirHandle;
56
+ setDestinationFolder(dirHandle.name);
57
+ }
58
+ catch (err) {
59
+ if (err.name !== 'AbortError') {
60
+ console.error('Error selecting folder::', err);
61
+ }
62
+ }
63
+ finally {
64
+ isPickerActiveRef.current = false;
65
+ }
66
+ };
67
+ const getTitle = () => {
68
+ const count = ` (${selectedDcmtInfos.length})`;
69
+ const modeLabelMap = {
70
+ onlySelected: SDKUI_Localizator.SelectedDocuments,
71
+ customized: SDKUI_Localizator.SelectedDocumentsAndRelated,
72
+ };
73
+ const modeLabel = modeLabelMap[mode] || modeLabelMap.onlySelected;
74
+ return `${SDKUI_Localizator.MergeToPdf} - ${modeLabel}${count}`;
75
+ };
76
+ // ---- Validazione ----
77
+ const pdfValidationItems = [];
78
+ if (!pdfFileName.trim()) {
79
+ pdfValidationItems.push(new ValidationItem(ResultTypes.ERROR, SDKUI_Localizator.PdfFileName, SDKUI_Localizator.RequiredField));
80
+ }
81
+ // ---- 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;
112
+ const isFormValid = () => pdfValidationItems.length === 0;
113
+ // ---- Recupero dei file PDF da unire (condiviso tra Esegui e Anteprima) ----
114
+ const collectPdfFilesToMerge = async () => {
115
+ const dcmtInfosToDownload = getDcmtInfosToDownload(selectedDcmtInfos, selectedItemsRelationViewer, showTMRelationViewer);
116
+ if (dcmtInfosToDownload.length === 0) {
117
+ TMMessageBoxManager.show({ message: SDKUI_Localizator.NoDcmtSelected, buttons: [ButtonNames.OK] });
118
+ return null;
119
+ }
120
+ const nonPdfKeys = new Set(nonPdfSelectedItems.map(i => i.key));
121
+ const pdfDcmtInfosToDownload = dcmtInfosToDownload.filter(d => !nonPdfKeys.has(`${d.TID}_${d.DID}`));
122
+ if (pdfDcmtInfosToDownload.length === 0) {
123
+ TMMessageBoxManager.show({
124
+ message: SDKUI_Localizator.NoPdfOrConvertibleFilesAmongSelectedMergeBlocked,
125
+ buttons: [ButtonNames.OK]
126
+ });
127
+ return null;
128
+ }
129
+ if (pdfDcmtInfosToDownload.length < MIN_PDF_FOR_MERGE) {
130
+ TMMessageBoxManager.show({
131
+ message: SDKUI_Localizator.OnlyOnePdfOrConvertibleFileMergeBlocked.replaceParams(MIN_PDF_FOR_MERGE.toString()),
132
+ buttons: [ButtonNames.OK]
133
+ });
134
+ return null;
135
+ }
136
+ 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(pdfDcmtInfosToDownload, DownloadTypes.Dcmt, 'download', collectFileForMerge, undefined, true, rfo, false);
147
+ if (pdfFiles.length === 0) {
148
+ TMMessageBoxManager.show({ message: SDKUI_Localizator.NoFilesAvailableForMerge, buttons: [ButtonNames.OK] });
149
+ return null;
150
+ }
151
+ return pdfFiles;
152
+ };
153
+ // ---- Esecuzione: scarica i PDF e li unisce ----
154
+ const run = async () => {
155
+ if (pdfValidationItems.length > 0) {
156
+ TMMessageBoxManager.show({ message: SDKUI_Localizator.PleaseCorrectErrorsBeforeProceeding, buttons: [ButtonNames.OK] });
157
+ return;
158
+ }
159
+ const pdfFiles = await collectPdfFilesToMerge();
160
+ if (!pdfFiles)
161
+ return;
162
+ const finalName = pdfFileName.trim().toLowerCase().endsWith('.pdf') ? pdfFileName.trim() : pdfFileName.trim() + '.pdf';
163
+ // Inizializza progress bar per il merge
164
+ setIsMergingPdf(true);
165
+ setMergeProgressMax(pdfFiles.length);
166
+ setMergeProgressValue(0);
167
+ setMergeProgressText(SDKUI_Localizator.MergeToPdf);
168
+ try {
169
+ const mergeOptions = {
170
+ onProgress: (current, total, file) => {
171
+ setMergeProgressValue(current);
172
+ setMergeProgressText(`${SDKUI_Localizator.MergeToPdf}: ${file.name}`);
173
+ }
174
+ };
175
+ // Se c'è una cartella selezionata (non Download di default), salva direttamente nella cartella
176
+ if (folderHandleRef.current) {
177
+ const mergedFile = await MergePdfManager.mergeToFile(pdfFiles, finalName, mergeOptions);
178
+ const fileHandle = await folderHandleRef.current.getFileHandle(finalName, { create: true });
179
+ const writable = await fileHandle.createWritable();
180
+ await writable.write(mergedFile);
181
+ await writable.close();
182
+ ShowAlert({ message: SDKUI_Localizator.PdfMergedSavedInFolder.replaceParams(folderHandleRef.current.name), mode: 'success', duration: 5000, title: SDKUI_Localizator.MergeToPdf });
183
+ }
184
+ else {
185
+ // Altrimenti usa il download standard del browser
186
+ await MergePdfManager.mergeAndDownload(pdfFiles, finalName, mergeOptions);
187
+ ShowAlert({ message: SDKUI_Localizator.PdfMergedSavedInFolder.replaceParams('Download'), mode: 'success', duration: 5000, title: SDKUI_Localizator.MergeToPdf });
188
+ }
189
+ }
190
+ catch (err) {
191
+ console.error('Error merging PDF or convertible files:', err);
192
+ TMMessageBoxManager.show({ message: SDKUI_Localizator.PdfMergeError, buttons: [ButtonNames.OK] });
193
+ return;
194
+ }
195
+ finally {
196
+ setIsMergingPdf(false);
197
+ }
198
+ onClose();
199
+ };
200
+ // ---- Render della sezione configurazione (solo PDF file name + warning) ----
201
+ const configSection = (_jsxs("div", { style: {
202
+ position: 'relative',
203
+ border: `1px solid ${TMColors.border_normal}`,
204
+ borderRadius: '8px',
205
+ padding: '8px',
206
+ height: showTMRelationViewer ? 'calc(100% - 7px)' : undefined,
207
+ marginTop: '7px',
208
+ flex: showTMRelationViewer ? undefined : 1,
209
+ minHeight: 0,
210
+ display: 'flex',
211
+ flexDirection: 'column',
212
+ boxSizing: 'border-box'
213
+ }, children: [_jsx("span", { style: getFloatingLabelStyle(), title: `${SDKUI_Localizator.ConfigurationParameters} (${SDKUI_Localizator.MergeToPdf})`, children: SDKUI_Localizator.ConfigurationParameters }), _jsxs("div", { style: {
214
+ display: 'flex',
215
+ flexDirection: 'column',
216
+ flex: 1,
217
+ overflowY: 'auto',
218
+ minHeight: 0,
219
+ }, children: [_jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', columnGap: '10px' }, children: [isDirectoryPickerSupported() ? (_jsx("div", { style: { flex: '1 1 280px', minWidth: '280px' }, children: _jsx(TMTextBox, { label: SDKUI_Localizator.FolderName, value: destinationFolder, onValueChanged: handleFolderValueChange, readOnly: true, placeHolder: "Download", onClick: handleSelectFolder, buttons: [
220
+ {
221
+ icon: _jsx(IconFolderOpen, {}),
222
+ text: SDKUI_Localizator.SelectFolder,
223
+ onClick: handleSelectFolder
224
+ },
225
+ ...(!isUsingDefaultDownloads ? [{
226
+ icon: _jsx(IconUndo, {}),
227
+ text: SDKUI_Localizator.RestoreDownload,
228
+ onClick: () => {
229
+ skipSelectFolderRef.current = true;
230
+ setDestinationFolder('Download');
231
+ folderHandleRef.current = null;
232
+ }
233
+ }] : [])
234
+ ] }) })) : (_jsx("div", { style: { flex: '1 1 280px', minWidth: '280px' }, children: _jsxs("div", { style: { position: 'relative', marginTop: '16px' }, children: [_jsx("span", { style: {
235
+ position: 'absolute',
236
+ top: '-10px',
237
+ left: '12px',
238
+ fontSize: '0.9rem',
239
+ color: TMColors.label_normal,
240
+ padding: '0 3px',
241
+ userSelect: 'none',
242
+ WebkitTouchCallout: 'none',
243
+ WebkitUserSelect: 'none',
244
+ width: 'max-content',
245
+ backgroundColor: TMColors.default_background,
246
+ 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: {
248
+ display: 'flex',
249
+ flexDirection: 'column',
250
+ gap: '6px',
251
+ padding: '8px 12px',
252
+ border: '1px solid #c0c0c0',
253
+ borderRadius: '5px',
254
+ backgroundColor: '#f8f8f8',
255
+ fontSize: '0.85rem',
256
+ marginTop: '12px',
257
+ }, children: [_jsx("div", { style: {
258
+ display: 'flex',
259
+ alignItems: 'center',
260
+ gap: '6px',
261
+ fontWeight: 600,
262
+ color: '#555',
263
+ fontSize: '0.9rem',
264
+ borderBottom: '1px solid #e0e0e0',
265
+ paddingBottom: '6px',
266
+ 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)));
269
+ const extLabel = extSet.length > 0
270
+ ? ` (${extSet.map(e => '.' + e).join(', ')})`
271
+ : '';
272
+ return convertibleSelectedItems.length === 1
273
+ ? SDKUI_Localizator.DocumentWillBeConvertedDuringMerge.replaceParams(extLabel)
274
+ : SDKUI_Localizator.DocumentsWillBeConvertedDuringMerge.replaceParams(convertibleSelectedItems.length.toString(), extLabel);
275
+ })() })), !hasEnoughPdfForMerge && (_jsx("li", { style: { color: '#7a5d00' }, children: mergeableSelectedCount === 0
276
+ ? SDKUI_Localizator.NoPdfOrConvertibleFilesSelected_Param.replaceParams(MIN_PDF_FOR_MERGE.toString())
277
+ : SDKUI_Localizator.OnlyOnePdfOrConvertibleFileSelected.replaceParams(MIN_PDF_FOR_MERGE.toString()) })), hasNonPdfSelected && (_jsx("li", { style: { color: '#7a5d00' }, children: (() => {
278
+ const extSet = Array.from(new Set(nonPdfSelectedItems.map(i => (i.ext ?? '').toString().trim().toLowerCase().replace(/^\./, '')).filter(e => e.length > 0)));
279
+ const extLabel = extSet.length > 0 ? ` (${extSet.map(e => '.' + e).join(', ')})` : '';
280
+ return nonPdfSelectedItems.length === 1
281
+ ? SDKUI_Localizator.FileNotPdfWillBeExcluded.replaceParams(extLabel)
282
+ : SDKUI_Localizator.FilesNotPdfWillBeExcluded.replaceParams(nonPdfSelectedItems.length.toString(), extLabel);
283
+ })() }))] })] }))] })] }));
284
+ return (_jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel || isMergingPdf, showWaitPanelPrimary: isMergingPdf ? true : showPrimary, showWaitPanelSecondary: isMergingPdf ? false : showSecondary, waitPanelTitle: isMergingPdf ? SDKUI_Localizator.MergeToPdf : waitPanelTitle, waitPanelTextPrimary: isMergingPdf ? mergeProgressText : waitPanelTextPrimary, waitPanelValuePrimary: isMergingPdf ? mergeProgressValue : waitPanelValuePrimary, waitPanelMaxValuePrimary: isMergingPdf ? mergeProgressMax : waitPanelMaxValuePrimary, waitPanelTextSecondary: isMergingPdf ? '' : waitPanelTextSecondary, waitPanelValueSecondary: isMergingPdf ? 0 : waitPanelValueSecondary, waitPanelMaxValueSecondary: isMergingPdf ? 0 : waitPanelMaxValueSecondary, isCancelable: !isMergingPdf, abortController: abortController, usePortal: true, children: _jsx(TMModal, { width: calcResponsiveSizes(deviceType, showTMRelationViewer ? '90%' : '500px', showTMRelationViewer ? '90%' : '500px', '95%'), height: showTMRelationViewer ? '95%' : 'auto', title: getTitle(), onClose: onClose, showCloseButton: true, children: _jsxs("div", { onContextMenu: (e) => e.preventDefault(), style: { display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px', width: '100%', height: '100%', boxSizing: 'border-box', overflow: 'hidden' }, children: [showTMRelationViewer ? (_jsx("div", { style: { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }, children: _jsxs(TMSplitterLayout, { direction: 'vertical', showSeparator: true, separatorSize: 8, separatorColor: 'transparent', separatorActiveColor: 'transparent', overflow: 'hidden', min: ["50", "50"], start: ["70%", "30%"], children: [_jsx(TMDownloadRelationViewerSection, { selectedDcmtInfos: selectedDcmtInfos, onSelectionChanged: setSelectedItemsRelationViewer, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), configSection] }, "TMMergeToPdf-relation-config") })) : (configSection), _jsx("div", { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', flexShrink: 0, marginTop: '12px' }, children: _jsxs("button", { disabled: !isFormValid() || (showTMRelationViewer && selectedItemsRelationViewer.filter(i => i.isDcmt).length === 0) || !hasEnoughPdfForMerge, onClick: run, style: {
285
+ display: 'flex',
286
+ alignItems: 'center',
287
+ justifyContent: 'center',
288
+ gap: '6px',
289
+ padding: '8px 20px',
290
+ fontSize: '0.95rem',
291
+ fontWeight: 600,
292
+ color: '#fff',
293
+ background: (!isFormValid() || (showTMRelationViewer && selectedItemsRelationViewer.filter(i => i.isDcmt).length === 0) || !hasEnoughPdfForMerge)
294
+ ? '#b0b0b0'
295
+ : 'linear-gradient(135deg, #28a745 0%, #218838 100%)',
296
+ border: 'none',
297
+ borderRadius: '6px',
298
+ cursor: (!isFormValid() || (showTMRelationViewer && selectedItemsRelationViewer.filter(i => i.isDcmt).length === 0) || !hasEnoughPdfForMerge) ? 'not-allowed' : 'pointer',
299
+ boxShadow: (!isFormValid() || (showTMRelationViewer && selectedItemsRelationViewer.filter(i => i.isDcmt).length === 0) || !hasEnoughPdfForMerge)
300
+ ? 'none'
301
+ : '0 2px 8px rgba(40, 167, 69, 0.35)',
302
+ transition: 'all 0.2s ease',
303
+ }, children: [_jsx(IconPlay, {}), _jsx("span", { children: mergeableSelectedCount === 0
304
+ ? SDKUI_Localizator.MergeToPdf
305
+ : mergeableSelectedCount > 999
306
+ ? _jsx(TMTooltip, { content: mergeableSelectedCount.toString(), children: SDKUI_Localizator.MergeFilesToPdf.replaceParams('999+') })
307
+ : SDKUI_Localizator.MergeFilesToPdf.replaceParams(mergeableSelectedCount.toString()) })] }) })] }) }) }));
308
+ };
309
+ export default TMMergeToPdfForm;
@@ -50,6 +50,8 @@ export interface TMRelationViewerProps {
50
50
  onFocusedItemChanged?: (item: RelationTreeItem | null) => void;
51
51
  /** Callback when selected items change */
52
52
  onSelectedItemsChanged?: (items: RelationTreeItem[]) => void;
53
+ /** Callback fired when the internal tree data changes, providing a flattened list of all items currently rendered */
54
+ onAllItemsChanged?: (items: RelationTreeItem[]) => void;
53
55
  /** Callback when a document is double-clicked */
54
56
  onDocumentDoubleClick?: (tid: number, did: number, name?: string) => void;
55
57
  /** Custom item renderer (full control). Return undefined to use default renderer. */
@@ -109,6 +111,17 @@ export interface TMRelationViewerProps {
109
111
  * (used in master-detail context)
110
112
  */
111
113
  focusedItemFormData?: MetadataValueDescriptorEx[];
114
+ /**
115
+ * If true, shows a toolbar button to expand/collapse all tree nodes.
116
+ * Default: false
117
+ */
118
+ showExpandAllButton?: boolean;
119
+ /**
120
+ * If true, all tree nodes are expanded by default on initial load.
121
+ * If false (default), uses the standard initial expansion behavior
122
+ * (root container + first document + first correlation folder).
123
+ */
124
+ defaultExpandAll?: boolean;
112
125
  }
113
126
  /**
114
127
  * Check if document type has detail relations
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataFormats, SystemMIDs, MetadataDataDomains, RelationCacheService, RelationTypes, UserListCacheService } from "@topconsultnpm/sdk-ts";
4
- import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus } from '../../../helper';
4
+ import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus, IconChevronDown, IconChevronRight, SDKUI_Localizator } from '../../../helper';
5
5
  import { TMColors } from '../../../utils/theme';
6
- import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
6
+ import { StyledDivHorizontal, StyledBadge, StyledToolbarForm } from '../../base/Styled';
7
7
  import TMTreeView from '../../base/TMTreeView';
8
8
  import { TMWaitPanel } from '../../base/TMWaitPanel';
9
9
  import TMDataListItemViewer from '../../viewers/TMDataListItemViewer';
@@ -136,12 +136,14 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata) =>
136
136
  }
137
137
  return output;
138
138
  };
139
- const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onDocumentDoubleClick, customItemRender, customDocumentStyle, customMainContainerContent, customDocumentContent, showMetadataNames = false, maxDepthLevel = 2, invertMasterNavigation = true, additionalStaticItems, showMainDocument = true, labelMainContainer, onNoRelationsFound, onItemContextMenu, focusedItemFormData = [] }) => {
139
+ 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 }) => {
140
140
  // State
141
141
  const [dcmtTypes, setDcmtTypes] = useState([]);
142
142
  const [treeData, setTreeData] = useState([]);
143
143
  const [showZeroDcmts, setShowZeroDcmts] = useState(initialShowZeroDcmts);
144
144
  const [staticItemsState, setStaticItemsState] = useState([]);
145
+ const [allExpanded, setAllExpanded] = useState(defaultExpandAll);
146
+ const initialExpandAllAppliedRef = React.useRef(false);
145
147
  const [showWaitPanel, setShowWaitPanel] = useState(false);
146
148
  const [waitPanelTextPrimary, setWaitPanelTextPrimary] = useState('');
147
149
  const [waitPanelValuePrimary, setWaitPanelValuePrimary] = useState(0);
@@ -271,7 +273,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
271
273
  console.error('❌ Error loading detail documents:', error);
272
274
  }
273
275
  return items;
274
- }, [allowedTIDs, focusedItemFormData, focusedItem?.dtd?.id]);
276
+ }, [allowedTIDs]);
275
277
  /**
276
278
  * Recursively retrieve master documents
277
279
  */
@@ -734,6 +736,24 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
734
736
  // and may contain custom properties that would be lost by the spread operator
735
737
  return [...treeData, ...itemsToMerge];
736
738
  }, [treeData, additionalStaticItems, staticItemsState]);
739
+ /**
740
+ * Notify parent with a flattened list of all items whenever the merged tree data changes.
741
+ */
742
+ useEffect(() => {
743
+ if (!onAllItemsChanged)
744
+ return;
745
+ const flatten = (items) => {
746
+ const out = [];
747
+ for (const it of items) {
748
+ out.push(it);
749
+ if (it.items && Array.isArray(it.items)) {
750
+ out.push(...flatten(it.items));
751
+ }
752
+ }
753
+ return out;
754
+ };
755
+ onAllItemsChanged(flatten(mergedTreeData));
756
+ }, [mergedTreeData, onAllItemsChanged]);
737
757
  /**
738
758
  * Load data when inputs change
739
759
  */
@@ -1118,7 +1138,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1118
1138
  }
1119
1139
  }
1120
1140
  return (_jsxs("div", { onDoubleClick: handleDoubleClick, style: documentStyle, children: [item.did && item.tid && showCurrentDcmtIndicator && inputDcmts?.some(d => d.DID === item.did && d.TID === item.tid) ? _jsx(IconBackhandIndexPointingRight, { fontSize: 22, overflow: 'visible' }) : _jsx(_Fragment, {}), item.values && (_jsx(TMDcmtIcon, { tid: item.values?.TID?.value, did: item.values?.DID?.value, fileExtension: item.values?.FILEEXT?.value, fileCount: item.values?.FILECOUNT?.value, isLexProt: item.values?.IsLexProt?.value, isMail: item.values?.ISMAIL?.value, isShared: item.values?.ISSHARED?.value, isSigned: item.values?.ISSIGNED?.value, downloadMode: 'openInNewWindow' })), checkoutStatusIcon, metadataContent] }));
1121
- }, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument, focusedItemFormData, focusedItem?.dtd, allUsers]);
1141
+ }, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument, allUsers]);
1122
1142
  /**
1123
1143
  * Wrapper renderer that handles custom rendering if provided
1124
1144
  */
@@ -1148,6 +1168,42 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1148
1168
  const handleSelectedItemsChanged = useCallback((items) => {
1149
1169
  onSelectedItemsChanged?.(items);
1150
1170
  }, [onSelectedItemsChanged]);
1171
+ /**
1172
+ * Recursively set the `expanded` property on every node that has children loaded.
1173
+ * Nodes without `items` are left untouched (will be loaded lazily on user click).
1174
+ */
1175
+ const setExpandedRecursively = useCallback((nodes, expanded) => {
1176
+ if (!nodes || !Array.isArray(nodes))
1177
+ return [];
1178
+ return nodes.map(node => {
1179
+ const hasChildren = Array.isArray(node.items) && node.items.length > 0;
1180
+ const newItems = hasChildren ? setExpandedRecursively(node.items, expanded) : node.items;
1181
+ // Only toggle expansion if the node is expandable (container/doc with children or marked expandible)
1182
+ const canExpand = hasChildren || node.isExpandible;
1183
+ return { ...node, expanded: canExpand ? expanded : node.expanded, items: newItems };
1184
+ });
1185
+ }, []);
1186
+ const handleToggleExpandAll = useCallback(() => {
1187
+ const next = !allExpanded;
1188
+ setAllExpanded(next);
1189
+ setTreeData(prev => setExpandedRecursively(prev, next));
1190
+ setStaticItemsState(prev => setExpandedRecursively(prev, next));
1191
+ userInteractedWithStaticItemsRef.current = true;
1192
+ }, [allExpanded, setExpandedRecursively]);
1193
+ /**
1194
+ * Apply defaultExpandAll once on initial tree load.
1195
+ */
1196
+ useEffect(() => {
1197
+ if (!defaultExpandAll)
1198
+ return;
1199
+ if (initialExpandAllAppliedRef.current)
1200
+ return;
1201
+ if (!treeData || treeData.length === 0)
1202
+ return;
1203
+ initialExpandAllAppliedRef.current = true;
1204
+ setTreeData(prev => setExpandedRecursively(prev, true));
1205
+ setStaticItemsState(prev => setExpandedRecursively(prev, true));
1206
+ }, [defaultExpandAll, treeData, setExpandedRecursively]);
1151
1207
  /**
1152
1208
  * Handle data changed - separate static items from dynamic tree data
1153
1209
  */
@@ -1169,7 +1225,20 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1169
1225
  return null;
1170
1226
  return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: "Nessuna relazione disponibile." });
1171
1227
  }
1172
- return (_jsxs(_Fragment, { 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 }), showExpansionWaitPanel && (_jsx(TMWaitPanel, { title: isForMaster ? 'Caricamento documenti master' : 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: expansionWaitPanelText, valuePrimary: expansionWaitPanelValue, maxValuePrimary: expansionWaitPanelMaxValue, isCancelable: true, abortController: expansionAbortController, onAbortClick: (abortController) => {
1228
+ 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
+ display: 'inline-flex',
1230
+ alignItems: 'center',
1231
+ gap: '4px',
1232
+ padding: '4px 8px',
1233
+ fontSize: '0.8rem',
1234
+ color: TMColors.primary,
1235
+ background: 'transparent',
1236
+ border: `1px solid ${TMColors.border_normal}`,
1237
+ borderRadius: '4px',
1238
+ 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 }) }), showExpansionWaitPanel && (_jsx(TMWaitPanel, { title: isForMaster ? 'Caricamento documenti master' : 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: expansionWaitPanelText, valuePrimary: expansionWaitPanelValue, maxValuePrimary: expansionWaitPanelMaxValue, isCancelable: true, abortController: expansionAbortController, onAbortClick: (abortController) => {
1173
1242
  setTimeout(() => {
1174
1243
  abortController?.abort();
1175
1244
  }, 100);